4 #include "../Graphics/Bitmap.h"
5 #include "../Graphics/Color.h"
6 #include "../Containers/Image.h"
8 #include "../Geometry/PointList.h"
9 #include "../Geometry/Line.h"
10 #include "../Containers/Collection.h"
15 namespace Gorgon {
namespace CGI {
20 vertexinfo() =
default;
22 vertexinfo (Float first, Float second,
int list) :
23 first(first), second(second), list(list)
32 template<
class PL_,
class F_,
class T_>
33 void findpolylinestofill(
const PL_ &pointlist, T_ ymin, T_ ymax, F_ fn, T_ step = 1, T_ cover = 1) {
36 for(T_ y = ymin; y<ymax; y += step) {
39 std::vector<vertexinfo> xpoints;
42 for(
const auto &points : pointlist) {
43 int N = (int)points.GetSize();
50 auto line = points.GetLine(off);
52 if(line.End.Y < nexty && line.End.Y > y) {
55 if(line.Start.Y < nexty && line.Start.Y > y) {
66 bool connected =
false, wasconnected =
false;
67 int firstdir = 0, lastdir = 0;
68 for(
int i=off; i<
N+off; i++) {
69 auto line = points.GetLine(i);
71 if(line.MinY() < nexty && line.MaxY() > y) {
72 auto line = points.GetLine(i);
74 auto slope = line.SlopeInv();
75 auto offset= line.OffsetInv();
79 if(std::isinf(slope)) {
84 x1 = slope * y + offset;
85 x2 = slope * nexty + offset;
98 auto &f = xpoints.back().first;
101 auto &s = xpoints.back().second;
105 if(wasconnected && lastdir != firstdir) {
106 xpoints.back().skip=
true;
109 firstdir = line.YDirection();
110 xpoints.push_back({x1, x2, listind});
111 xpoints.back().wind = firstdir;
114 lastdir = line.YDirection();
116 wasconnected = connected;
117 connected = line.End.Y <= nexty && line.End.Y >= y;
120 if(wasconnected && lastdir != firstdir) {
121 xpoints.back().skip=
true;
128 if(wasconnected && lastdir != firstdir) {
129 xpoints.back().skip=
true;
136 std::sort(xpoints.begin(), xpoints.end(), [](
auto l,
auto r) { return l.second < r.second; });
149 fn((Float)y, xpoints);
163 template<
int S_ = 8,
int W_ = 1,
class P_= Geometry::Po
intf,
class F_ = Sol
idFill<>>
165 if(p.size() < 1)
return;
167 std::vector<Geometry::PointList<P_>> p2;
168 const std::vector<Geometry::PointList<P_>> *pp;
172 p2.push_back(pl.Duplicate());
180 const std::vector<Geometry::PointList<P_>> &points = *pp;
185 int xmin = int(target.
GetWidth()*S_ - 1);
189 for(
const auto &p : points) {
190 if(p.GetSize() <= 0)
continue;
192 auto yrange = std::minmax_element(p.begin(), p.end(), [](
auto l,
auto r) { return l.Y < r.Y; });
194 if(ymin > yrange.first->Y)
195 ymin = yrange.first->Y;
197 if(ymax < yrange.second->
Y)
198 ymax = yrange.second->Y;
200 auto xrange = std::minmax_element(p.begin(), p.end(), [](
auto l,
auto r) { return l.X < r.X; });
202 if(xmin > xrange.first->X)
203 xmin = (int)xrange.first->X;
205 if(xmax < xrange.second->
X)
206 xmax = (int)xrange.second->X;
215 Gorgon::FitInto<Float>(ymin, 0.f, (Float)target.
GetHeight()-1);
216 Gorgon::FitInto<Float>(ymax, 0.f, (Float)target.
GetHeight());
222 if(ceil(ymin) > floor(ymax))
return;
230 int ew = xmax-xmin+1;
231 std::vector<int> cnts(ew);
232 int yminint = (int)floor(ymin*S_);
234 float a = 1.f / (S_ * S_);
236 internal::findpolylinestofill(points, yminint, (
int)ceil(ymax*S_)+1, [&](
float y,
auto &xpoints) {
237 if(
int(cy) !=
int(y/S_)) {
239 for(
int x=0; x<ew; x++) {
241 Graphics::RGBA prevcol = target.
GetRGBAAt(
int(x+xmin), (
int)cy);
243 auto col = fill({(
Float)x, (Float)floor(cy-yminint)}, {x + xmin, (int)cy}, prevcol, a * cnts[x]);
245 target.
SetRGBAAt(
int(x + xmin), int(cy), col);
251 memset(&cnts[0], 0, cnts.size()*
sizeof(
int));
253 cy = (
Float)
int(y/S_);
257 for(
int i=0; i<(int)xpoints.size()-1; i++) {
261 wind += xpoints[i].wind;
263 if(wind == 0)
continue;
265 Float s = ceil(xpoints[i].second)/S_;
266 std::sort(xpoints.begin() + i + 1, xpoints.end(), [](
auto l,
auto r) { return l.first < r.first; });
271 if(i >= xpoints.size())
274 wind += xpoints[i].wind;
276 if(W_ == 0 ? (wind == 0) : (wind%2==0)) {
277 Float e = floor(xpoints[i].first)/S_;
279 FitInto<Float>(s, (Float)xmin, (Float)xmax+1);
280 FitInto<Float>(e, (Float)xmin, (Float)xmax+1);
282 for(Float x=s; x<e; x+=1.f/S_) {
287 if(xpoints[i].skip) {
288 xpoints[i].skip =
false;
289 xpoints[i].wind *= -1;
296 if(xpoints[i].skip) {
297 xpoints[i].skip =
false;
298 xpoints[i].wind *= -1;
303 if(xpoints.begin() + i == xpoints.end())
continue;
305 std::sort(xpoints.begin() + i + 1, xpoints.end(), [](
auto l,
auto r) { return l.second < r.second; });
310 internal::findpolylinestofill(points, (
int)floor(ymin), (
int)ceil(ymax), [&](
float y,
auto &xpoints) {
312 for(
int i=0; i<(int)xpoints.size()-1; i++) {
316 wind += xpoints[i].wind;
318 if(wind == 0)
continue;
320 int s = (int)ceil(xpoints[i].second);
321 std::sort(xpoints.begin() + i + 1, xpoints.end(), [](
auto l,
auto r) { return l.first < r.first; });
326 if(i >= xpoints.size())
329 wind += xpoints[i].wind;
331 if(W_ == 0 ? (wind == 0) : (wind%2 == 0)) {
332 int e = (int)floor(xpoints[i].first);
337 for(
int x=s; x<e; x++) {
338 target.
SetRGBAAt(x, (
int)y, fill({float(x-xmin), float(y-ymin)}, {(int)x, (
int)y}, target.
GetRGBAAt(x, (
int)y), 1.f));
342 if(xpoints[i].skip) {
343 xpoints[i].skip =
false;
344 xpoints[i].wind *= -1;
351 if(xpoints[i].skip) {
352 xpoints[i].skip =
false;
353 xpoints[i].wind *= -1;
358 if(xpoints.begin() + i == xpoints.end())
continue;
360 std::sort(xpoints.begin() + i + 1, xpoints.end(), [](
auto l,
auto r) { return l.second < r.second; });
370 template<
int S_ = 8,
int W_ = 1,
class P_,
class F_ = Sol
idFill<>>
373 Polyfill<S_, W_, P_, F_>(target.
GetData(), points, fill);
381 template<
int S_ = 8,
int W_ = 1,
class P_= Geometry::Po
intf,
class F_ = Sol
idFill<>>
395 const Geometry::PointList<P_> &points = *pp;
397 auto yrange = std::minmax_element(points.begin(), points.end(), [](
auto l,
auto r) { return l.Y < r.Y; });
399 Float ymin = yrange.first->Y;
400 Float ymax = yrange.second->Y;
404 Gorgon::FitInto<Float>(ymin, 0, (Float)target.
GetHeight()-1);
405 Gorgon::FitInto<Float>(ymax, 0, (Float)target.
GetHeight());
407 auto xrange = std::minmax_element(points.begin(), points.end(), [](
auto l,
auto r) { return l.X < r.X; });
409 int xmin = (int)xrange.first->X;
410 int xmax = (
int)ceil(xrange.second->X);
415 if(ceil(ymin) > floor(ymax))
return;
422 int ew = xmax-xmin+1;
423 std::vector<int> cnts(ew);
424 int yminint = (int)floor(ymin*S_);
426 float a = 1.f / (S_ * S_);
428 internal::findpolylinestofill(Containers::Collection<
const Geometry::PointList<P_>>({points}), yminint, (
int)ceil(ymax*S_)+1, [&](Float y,
auto &xpoints) {
429 if(
int(cy) !=
int(y/S_)) {
431 for(
int x=0; x<ew; x++) {
433 Graphics::RGBA prevcol = target.
GetRGBAAt(
int(x+xmin), (
int)cy);
435 auto col = fill({floorf((
float)x), floorf((
float)cy-yminint)}, {int(x + xmin), (int)cy}, prevcol, a * cnts[x]);
437 target.
SetRGBAAt(x + xmin, (
int)cy, col);
443 memset(&cnts[0], 0, cnts.size()*
sizeof(
int));
445 cy = (
Float)
int(y/S_);
450 for(
int i=0; i<(int)xpoints.size()-1; i++) {
454 wind += xpoints[i].wind;
456 if(wind == 0)
continue;
458 Float s = ceil(xpoints[i].second)/S_;
459 std::sort(xpoints.begin() + i + 1, xpoints.end(), [](
auto l,
auto r) { return l.first < r.first; });
464 if(i >= xpoints.size())
467 wind += xpoints[i].wind;
469 if(W_ == 0 ? (wind == 0) : (wind%2==0)) {
470 Float e = floor(xpoints[i].first)/S_;
472 FitInto<Float>(s, (Float)xmin, (Float)xmax+1);
473 FitInto<Float>(e, (Float)xmin, (Float)xmax+1);
475 for(Float x=s; x<e; x+=1.f/S_) {
480 if(xpoints[i].skip) {
481 xpoints[i].skip =
false;
482 xpoints[i].wind *= -1;
489 if(xpoints[i].skip) {
490 xpoints[i].skip =
false;
491 xpoints[i].wind *= -1;
496 if(xpoints.begin() + i == xpoints.end())
continue;
498 std::sort(xpoints.begin() + i + 1, xpoints.end(), [](
auto l,
auto r) { return l.second < r.second; });
503 internal::findpolylinestofill(Containers::Collection<
const Geometry::PointList<P_>>({points}), ymin, ymax, [&](
float y,
auto &xpoints) {
505 for(
int i=0; i<(int)xpoints.size()-1; i++) {
509 wind += xpoints[i].wind;
511 if(wind == 0)
continue;
513 int s = (int)ceil(xpoints[i].second);
514 std::sort(xpoints.begin() + i + 1, xpoints.end(), [](
auto l,
auto r) { return l.first < r.first; });
519 if(i >= xpoints.size())
522 wind += xpoints[i].wind;
524 if(W_ == 0 ? (wind == 0) : (wind%2 == 0)) {
525 int e = (int)floor(xpoints[i].first);
530 for(
int x=s; x<e; x++) {
531 target.
SetRGBAAt(x, (
int)y, fill({float(x-xmin), float(y-ymin)}, {(int)x, (
int)y}, target.
GetRGBAAt(x, (
int)y), 1.f));
535 if(xpoints[i].skip) {
536 xpoints[i].skip =
false;
537 xpoints[i].wind *= -1;
544 if(xpoints[i].skip) {
545 xpoints[i].skip =
false;
546 xpoints[i].wind *= -1;
551 if(xpoints.begin() + i == xpoints.end())
continue;
553 std::sort(xpoints.begin() + i + 1, xpoints.end(), [](
auto l,
auto r) { return l.second < r.second; });
563 template<
int S_ = 8,
class P_,
class F_ = Sol
idFill<>>
566 Polyfill<S_>(target.
GetData(), points, fill);