14 static gfxpoly_t*current_polygon = 0;
15 void gfxpoly_fail(char*expr, char*file, int line, const char*function)
17 if(!current_polygon) {
18 fprintf(stderr, "assert(%s) failed in %s in line %d: %s\n", expr, file, line, function);
22 void*md5 = initialize_md5();
25 gfxpolystroke_t*stroke = current_polygon->strokes;
26 for(;stroke;stroke=stroke->next) {
27 for(t=0;t<stroke->num_points;t++) {
28 update_md5(md5, (unsigned char*)&stroke->points[t].x, sizeof(stroke->points[t].x));
29 update_md5(md5, (unsigned char*)&stroke->points[t].y, sizeof(stroke->points[t].y));
33 char filename[32+4+1];
35 sprintf(filename, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x.ps",
36 h[0],h[1],h[2],h[3],h[4],h[5],h[6],h[7],h[8],h[9],h[10],h[11],h[12],h[13],h[14],h[15]);
38 fprintf(stderr, "assert(%s) failed in %s in line %d: %s\n", expr, file, line, function);
39 fprintf(stderr, "I'm saving a debug file \"%s\" to the current directory.\n", filename);
41 gfxpoly_save(current_polygon, filename);
45 static char point_equals(const void*o1, const void*o2)
47 const point_t*p1 = o1;
48 const point_t*p2 = o2;
49 return p1->x == p2->x && p1->y == p2->y;
51 static unsigned int point_hash(const void*o)
56 static void* point_dup(const void*o)
59 point_t*n = malloc(sizeof(point_t));
64 static void point_free(void*o)
78 typedef struct _event {
85 /* compare_events_simple differs from compare_events in that it schedules
86 events from left to right regardless of type. It's only used in horizontal
87 processing, in order to get an x-wise sorting of the current scanline */
88 static inline int compare_events_simple(const void*_a,const void*_b)
90 event_t* a = (event_t*)_a;
91 event_t* b = (event_t*)_b;
92 int d = b->p.y - a->p.y;
99 static inline int compare_events(const void*_a,const void*_b)
101 event_t* a = (event_t*)_a;
102 event_t* b = (event_t*)_b;
103 int d = b->p.y - a->p.y;
105 /* we need to schedule end after intersect (so that a segment about
106 to end has a chance to tear up a few other segs first) and start
107 events after end (in order not to confuse the intersection check, which
108 assumes there's an actual y overlap between active segments, and
109 because ending segments in the active list make it difficult to insert
110 starting segments at the right position)).
111 Horizontal lines come last, because the only purpose
112 they have is to create snapping coordinates for the segments (still)
113 existing in this scanline.
115 d = b->type - a->type;
119 /* I don't see any reason why we would need to order by x- at least as long
120 as we do horizontal lines in a seperate pass */
121 //d = b->p.x - a->p.x;
125 #define COMPARE_EVENTS(x,y) (compare_events(x,y)>0)
126 #define COMPARE_EVENTS_SIMPLE(x,y) (compare_events_simple(x,y)>0)
127 HEAP_DEFINE(queue,event_t,COMPARE_EVENTS);
128 HEAP_DEFINE(hqueue,event_t,COMPARE_EVENTS_SIMPLE);
130 typedef struct _horizontal {
138 typedef struct _horizdata {
144 typedef struct _status {
151 windcontext_t*context;
152 segment_t*ending_segments;
156 gfxpolystroke_t*strokes;
158 dict_t*seen_crossings; //list of crossing we saw so far
159 dict_t*intersecting_segs; //list of segments intersecting in this scanline
160 dict_t*segs_with_point; //lists of segments that received a point in this scanline
165 int gfxpoly_num_segments(gfxpoly_t*poly)
167 gfxpolystroke_t*stroke = poly->strokes;
169 for(;stroke;stroke=stroke->next) {
174 int gfxpoly_size(gfxpoly_t*poly)
178 gfxpolystroke_t*stroke = poly->strokes;
179 for(;stroke;stroke=stroke->next) {
180 edges += stroke->num_points-1;
185 char gfxpoly_check(gfxpoly_t*poly, char updown)
187 current_polygon = poly;
188 dict_t*d1 = dict_new2(&point_type);
189 dict_t*d2 = dict_new2(&point_type);
191 gfxpolystroke_t*stroke = poly->strokes;
192 for(;stroke;stroke=stroke->next) {
193 /* In order to not confuse the fill/wind logic, existing segments must have
194 a non-zero edge style */
197 /* put all the segments into dictionaries so that we can check
198 that the endpoint multiplicity is two */
199 for(s=0;s<stroke->num_points;s++) {
200 point_t p = stroke->points[s];
201 int num_xor = (s>=1 && s<stroke->num_points-1)?2:1; // mid points are two points (start+end)
202 int num_circ = (s>=1 && s<stroke->num_points-1)?0:(s==0?1:-1);
203 if(stroke->dir==DIR_UP)
206 if(!dict_contains(d1, &p)) {
207 dict_put(d1, &p, (void*)(ptroff_t)num_xor);
209 assert(!dict_contains(d2, &p));
210 dict_put(d2, &p, (void*)(ptroff_t)num_circ);
213 int count = (ptroff_t)dict_lookup(d1, &p);
216 dict_put(d1, &p, (void*)(ptroff_t)count);
219 assert(dict_contains(d2, &p));
220 count = (ptroff_t)dict_lookup(d2, &p);
223 dict_put(d2, &p, (void*)(ptroff_t)count);
228 DICT_ITERATE_ITEMS(d1, point_t*, p1, void*, c1) {
229 int count = (ptroff_t)c1;
231 fprintf(stderr, "Error: Point (%.2f,%.2f) occurs %d times\n", p1->x * poly->gridsize, p1->y * poly->gridsize, count);
239 DICT_ITERATE_ITEMS(d2, point_t*, p2, void*, c2) {
240 int count = (ptroff_t)c2;
242 if(count>0) fprintf(stderr, "Error: Point (%d,%d) has %d more incoming than outgoing segments\n", p2->x, p2->y, count);
243 if(count<0) fprintf(stderr, "Error: Point (%d,%d) has %d more outgoing than incoming segments\n", p2->x, p2->y, -count);
253 void gfxpoly_dump(gfxpoly_t*poly)
256 double g = poly->gridsize;
257 fprintf(stderr, "polyon %p (gridsize: %.2f)\n", poly, poly->gridsize);
258 gfxpolystroke_t*stroke = poly->strokes;
259 for(;stroke;stroke=stroke->next) {
260 fprintf(stderr, "%11p", stroke);
261 if(stroke->dir==DIR_UP) {
262 for(s=stroke->num_points-1;s>=1;s--) {
263 point_t a = stroke->points[s];
264 point_t b = stroke->points[s-1];
265 fprintf(stderr, "%s (%.2f,%.2f) -> (%.2f,%.2f)%s%s\n", s!=stroke->num_points-1?" ":"", a.x*g, a.y*g, b.x*g, b.y*g,
266 s==1?"]":"", a.y==b.y?"H":"");
269 for(s=0;s<stroke->num_points-1;s++) {
270 point_t a = stroke->points[s];
271 point_t b = stroke->points[s+1];
272 fprintf(stderr, "%s (%.2f,%.2f) -> (%.2f,%.2f)%s%s\n", s?" ":"", a.x*g, a.y*g, b.x*g, b.y*g,
273 s==stroke->num_points-2?"]":"", a.y==b.y?"H":"");
279 void gfxpoly_save(gfxpoly_t*poly, const char*filename)
281 FILE*fi = fopen(filename, "wb");
282 fprintf(fi, "%% gridsize %f\n", poly->gridsize);
283 fprintf(fi, "%% begin\n");
285 gfxpolystroke_t*stroke = poly->strokes;
286 for(;stroke;stroke=stroke->next) {
287 fprintf(fi, "%g setgray\n", stroke->dir==DIR_UP ? 0.7 : 0);
288 point_t p = stroke->points[0];
289 fprintf(fi, "%d %d moveto\n", p.x, p.y);
290 for(s=1;s<stroke->num_points;s++) {
291 p = stroke->points[s];
292 fprintf(fi, "%d %d lineto\n", p.x, p.y);
294 fprintf(fi, "stroke\n");
296 fprintf(fi, "showpage\n");
300 void gfxpoly_save_arrows(gfxpoly_t*poly, const char*filename)
302 FILE*fi = fopen(filename, "wb");
303 fprintf(fi, "%% gridsize %f\n", poly->gridsize);
304 fprintf(fi, "%% begin\n");
306 double l = 5.0 / poly->gridsize;
307 double g = poly->gridsize;
308 gfxpolystroke_t*stroke = poly->strokes;
309 for(;stroke;stroke=stroke->next) {
310 fprintf(fi, "%g setgray\n", 0);
312 int s = stroke->dir==DIR_UP?stroke->num_points-1:0;
313 int end = stroke->dir==DIR_UP?-1:stroke->num_points;
314 int dir = stroke->dir==DIR_UP?-1:1;
316 point_t p = stroke->points[s];
319 fprintf(fi, "%f %f moveto\n", p.x * g, p.y * g);
320 for(;s!=end;s+=dir) {
321 p = stroke->points[s];
324 double d = sqrt(lx*lx+ly*ly);
328 fprintf(fi, "%f %f lineto\n", (p.x - lx*d2) * g, (p.y - ly*d2) * g);
329 fprintf(fi, "%f %f lineto\n", (p.x - lx*d2 + (ly*d))*g,
330 (p.y - ly*d2 - (lx*d))*g);
331 fprintf(fi, "%f %f lineto\n", p.x * g, p.y * g);
332 fprintf(fi, "%f %f lineto\n", (p.x - lx*d2 - (ly*d))*g,
333 (p.y - ly*d2 + (lx*d))*g);
334 fprintf(fi, "%f %f lineto\n", (p.x - lx*d2) * g, (p.y - ly*d2) * g);
335 fprintf(fi, "%f %f moveto\n", p.x * g, p.y * g);
338 fprintf(fi, "stroke\n");
340 fprintf(fi, "showpage\n");
344 inline static event_t* event_new()
346 event_t*e = rfx_calloc(sizeof(event_t));
349 inline static void event_free(event_t*e)
354 static void event_dump(status_t*status, event_t*e)
356 if(e->type == EVENT_HORIZONTAL) {
357 fprintf(stderr, "Horizontal [%d] (%.2f,%.2f) -> (%.2f,%.2f)\n", (int)e->s1->nr,
358 e->s1->a.x * status->gridsize, e->s1->a.y * status->gridsize, e->s1->b.x * status->gridsize, e->s1->b.y * status->gridsize);
359 } else if(e->type == EVENT_START) {
360 fprintf(stderr, "event: segment [%d] starts at (%.2f,%.2f)\n", (int)e->s1->nr,
361 e->p.x * status->gridsize, e->p.y * status->gridsize);
362 } else if(e->type == EVENT_END) {
363 fprintf(stderr, "event: segment [%d] ends at (%.2f,%.2f)\n", (int)e->s1->nr,
364 e->p.x * status->gridsize, e->p.y * status->gridsize);
365 } else if(e->type == EVENT_CROSS) {
366 fprintf(stderr, "event: segment [%d] and [%d] intersect at (%.2f,%.2f)\n", (int)e->s1->nr, (int)e->s2->nr,
367 e->p.x * status->gridsize, e->p.y * status->gridsize);
373 static inline int32_t max32(int32_t v1, int32_t v2) {return v1>v2?v1:v2;}
374 static inline int32_t min32(int32_t v1, int32_t v2) {return v1<v2?v1:v2;}
376 static void segment_dump(segment_t*s)
378 fprintf(stderr, "[%d] (%d,%d)->(%d,%d) ", (int)s->nr, s->a.x, s->a.y, s->b.x, s->b.y);
379 fprintf(stderr, " dx:%d dy:%d k:%f dx/dy=%f fs=%p\n", s->delta.x, s->delta.y, s->k,
380 (double)s->delta.x / s->delta.y, s->fs);
383 static void segment_init(segment_t*s, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int polygon_nr, segment_dir_t dir)
389 /* We need to make sure horizontal segments always go from left to right.
390 "up/down" for horizontal segments is handled by "rotating"
391 them 90° counterclockwise in screen coordinates (tilt your head to
392 the right). In other words, the "normal" direction (what's positive dy for
393 vertical segments) is positive dx for horizontal segments.
396 s->dir = DIR_INVERT(s->dir);
397 int32_t x = x1;x1=x2;x2=x;
398 int32_t y = y1;y1=y2;y2=y;
405 s->k = (double)x1*y2-(double)x2*y1;
406 s->left = s->right = 0;
409 s->minx = min32(x1,x2);
410 s->maxx = max32(x1,x2);
413 s->polygon_nr = polygon_nr;
414 static int segment_count=0;
415 s->nr = segment_count++;
418 /* notice: on some systems (with some compilers), for the line
419 (1073741823,-1073741824)->(1073741823,1073741823)
420 we get LINE_EQ(s->a, s) == 1.
421 That's why we now clamp to 26 bit.
423 assert(LINE_EQ(s->a, s) == 0);
424 assert(LINE_EQ(s->b, s) == 0);
426 /* check that all signs are in order:
434 p.x = min32(s->a.x, s->b.x);
435 assert(LINE_EQ(p, s) <= 0);
436 p.x = max32(s->a.x, s->b.x);
437 assert(LINE_EQ(p, s) >= 0);
440 #ifndef DONT_REMEMBER_CROSSINGS
441 dict_init2(&s->scheduled_crossings, &ptr_type, 0);
445 static segment_t* segment_new(point_t a, point_t b, int polygon_nr, segment_dir_t dir)
447 segment_t*s = (segment_t*)rfx_calloc(sizeof(segment_t));
448 segment_init(s, a.x, a.y, b.x, b.y, polygon_nr, dir);
452 static void segment_clear(segment_t*s)
454 #ifndef DONT_REMEMBER_CROSSINGS
455 dict_clear(&s->scheduled_crossings);
458 static void segment_destroy(segment_t*s)
464 static void advance_stroke(queue_t*queue, hqueue_t*hqueue, gfxpolystroke_t*stroke, int polygon_nr, int pos, double gridsize)
469 /* we need to queue multiple segments at once because we need to process start events
470 before horizontal events */
471 while(pos < stroke->num_points-1) {
472 assert(stroke->points[pos].y <= stroke->points[pos+1].y);
473 s = segment_new(stroke->points[pos], stroke->points[pos+1], polygon_nr, stroke->dir);
481 fprintf(stderr, "[%d] (%.2f,%.2f) -> (%.2f,%.2f) %s (stroke %p, %d more to come)\n",
482 s->nr, s->a.x * gridsize, s->a.y * gridsize,
483 s->b.x * gridsize, s->b.y * gridsize,
484 s->dir==DIR_UP?"up":"down", stroke, stroke->num_points - 1 - pos);
486 event_t* e = event_new();
487 e->type = s->delta.y ? EVENT_START : EVENT_HORIZONTAL;
492 if(queue) queue_put(queue, e);
493 else hqueue_put(hqueue, e);
495 if(e->type != EVENT_HORIZONTAL) {
505 static void gfxpoly_enqueue(gfxpoly_t*p, queue_t*queue, hqueue_t*hqueue, int polygon_nr)
508 gfxpolystroke_t*stroke = p->strokes;
509 for(;stroke;stroke=stroke->next) {
510 assert(stroke->num_points > 1);
514 for(s=0;s<stroke->num_points-1;s++) {
515 assert(stroke->points[s].y <= stroke->points[s+1].y);
518 advance_stroke(queue, hqueue, stroke, polygon_nr, 0, p->gridsize);
522 static void schedule_endpoint(status_t*status, segment_t*s)
524 // schedule end point of segment
525 assert(s->b.y > status->y);
526 event_t*e = event_new();
531 queue_put(&status->queue, e);
534 static void schedule_crossing(status_t*status, segment_t*s1, segment_t*s2)
536 /* the code that's required (and the checks you can perform) before
537 it can be said with 100% certainty that we indeed have a valid crossing
538 amazes me every time. -mk */
541 assert(s1->right == s2);
542 assert(s2->left == s1);
543 int32_t miny1 = min32(s1->a.y,s1->b.y);
544 int32_t maxy1 = max32(s1->a.y,s1->b.y);
545 int32_t miny2 = min32(s2->a.y,s2->b.y);
546 int32_t maxy2 = max32(s2->a.y,s2->b.y);
547 int32_t minx1 = min32(s1->a.x,s1->b.x);
548 int32_t minx2 = min32(s2->a.x,s2->b.x);
549 int32_t maxx1 = max32(s1->a.x,s1->b.x);
550 int32_t maxx2 = max32(s2->a.x,s2->b.x);
551 /* check that precomputation is sane */
552 assert(minx1 == s1->minx && minx2 == s2->minx);
553 assert(maxx1 == s1->maxx && maxx2 == s2->maxx);
554 /* both segments are active, so this can't happen */
555 assert(!(maxy1 <= miny2 || maxy2 <= miny1));
556 /* we know that right now, s2 is to the right of s1, so there's
557 no way the complete bounding box of s1 is to the right of s1 */
558 assert(!(s1->minx > s2->maxx));
559 assert(s1->minx != s2->maxx || (!s1->delta.x && !s2->delta.x));
562 if(s1->maxx <= s2->minx) {
564 fprintf(stderr, "[%d] doesn't intersect with [%d] because: bounding boxes don't intersect\n", s1->nr, s2->nr);
566 /* bounding boxes don't intersect */
570 #ifndef DONT_REMEMBER_CROSSINGS
571 if(dict_contains(&s1->scheduled_crossings, (void*)(ptroff_t)s2->nr)) {
572 /* FIXME: this whole segment hashing thing is really slow */
574 fprintf(stderr, "[%d] doesn't intersect with [%d] because: we already scheduled this intersection\n", s1->nr, s2->nr);
575 // DICT_ITERATE_KEY(&s1->scheduled_crossings, void*, x) {
576 // fprintf(stderr, "[%d]<->[%d]\n", s1->nr, (int)(ptroff_t)x);
579 return; // we already know about this one
583 double det = (double)s1->delta.x*s2->delta.y - (double)s1->delta.y*s2->delta.x;
586 // lines are exactly on top of each other (ignored)
588 fprintf(stderr, "Notice: segments [%d] and [%d] are exactly on top of each other\n", s1->nr, s2->nr);
593 fprintf(stderr, "[%d] doesn't intersect with [%d] because: they are parallel to each other\n", s1->nr, s2->nr);
595 /* lines are parallel */
600 double asign2 = LINE_EQ(s1->a, s2);
602 // segment1 touches segment2 in a single point (ignored)
604 fprintf(stderr, "Notice: segment [%d]'s start point touches segment [%d]\n", s1->nr, s2->nr);
608 double bsign2 = LINE_EQ(s1->b, s2);
610 // segment1 touches segment2 in a single point (ignored)
612 fprintf(stderr, "Notice: segment [%d]'s end point touches segment [%d]\n", s1->nr, s2->nr);
617 if(asign2<0 && bsign2<0) {
618 // segment1 is completely to the left of segment2
620 fprintf(stderr, "[%d] doesn't intersect with [%d] because: [%d] is completely to the left of [%d]\n", s1->nr, s2->nr, s1->nr, s2->nr);
624 if(asign2>0 && bsign2>0) {
625 // segment1 is completely to the right of segment2
626 #ifndef DONT_REMEMBER_CROSSINGS
630 fprintf(stderr, "[%d] doesn't intersect with [%d] because: [%d] is completely to the left of [%d]\n", s1->nr, s2->nr, s2->nr, s1->nr);
635 double asign1 = LINE_EQ(s2->a, s1);
637 // segment2 touches segment1 in a single point (ignored)
639 fprintf(stderr, "Notice: segment [%d]'s start point touches segment [%d]\n", s2->nr, s1->nr);
643 double bsign1 = LINE_EQ(s2->b, s1);
645 // segment2 touches segment1 in a single point (ignored)
647 fprintf(stderr, "Notice: segment [%d]'s end point touches segment [%d]\n", s2->nr, s1->nr);
652 if(asign1<0 && bsign1<0) {
653 // segment2 is completely to the left of segment1
654 #ifndef DONT_REMEMBER_CROSSINGS
658 fprintf(stderr, "[%d] doesn't intersect with [%d] because: [%d] is completely to the left of [%d]\n", s1->nr, s2->nr, s1->nr, s2->nr);
662 if(asign1>0 && bsign1>0) {
663 // segment2 is completely to the right of segment1
665 fprintf(stderr, "[%d] doesn't intersect with [%d] because: [%d] is completely to the left of [%d]\n", s1->nr, s2->nr, s2->nr, s1->nr);
670 #ifdef DONT_REMEMBER_CROSSINGS
671 /* s2 crosses s1 from *left* to *right*. This is a crossing we already processed-
672 there's not way s2 would be to the left of s1 otherwise */
673 if(asign1<0 && bsign1>0) return;
674 if(asign2>0 && bsign2<0) return;
677 assert(!(asign1<0 && bsign1>0));
678 assert(!(asign2>0 && bsign2<0));
680 /* TODO: should we precompute these? */
681 double la = (double)s1->a.x*(double)s1->b.y - (double)s1->a.y*(double)s1->b.x;
682 double lb = (double)s2->a.x*(double)s2->b.y - (double)s2->a.y*(double)s2->b.x;
685 p.x = (int32_t)ceil((-la*s2->delta.x + lb*s1->delta.x) / det);
686 p.y = (int32_t)ceil((+lb*s1->delta.y - la*s2->delta.y) / det);
688 assert(p.y >= status->y);
690 assert(p.x >= s1->minx && p.x <= s1->maxx);
691 assert(p.x >= s2->minx && p.x <= s2->maxx);
696 #ifndef DONT_REMEMBER_CROSSINGS
697 assert(!dict_contains(status->seen_crossings, &pair));
698 dict_put(status->seen_crossings, &pair, 0);
702 fprintf(stderr, "schedule crossing between [%d] and [%d] at (%d,%d)\n", s1->nr, s2->nr, p.x, p.y);
705 #ifndef DONT_REMEMBER_CROSSINGS
706 /* we insert into each other's intersection history because these segments might switch
707 places and we still want to look them up quickly after they did */
708 dict_put(&s1->scheduled_crossings, (void*)(ptroff_t)(s2->nr), 0);
709 dict_put(&s2->scheduled_crossings, (void*)(ptroff_t)(s1->nr), 0);
712 event_t* e = event_new();
713 e->type = EVENT_CROSS;
717 queue_put(&status->queue, e);
721 static void exchange_two(status_t*status, event_t*e)
723 //exchange two segments in list
724 segment_t*s1 = e->s1;
725 segment_t*s2 = e->s2;
727 if(!dict_contains(status->intersecting_segs, s1))
728 dict_put(status->intersecting_segs, s1, 0);
729 if(!dict_contains(status->intersecting_segs, s2))
730 dict_put(status->intersecting_segs, s2, 0);
732 assert(s2->left == s1);
733 assert(s1->right == s2);
734 actlist_swap(status->actlist, s1, s2);
735 assert(s2->right == s1);
736 assert(s1->left == s2);
737 segment_t*left = s2->left;
738 segment_t*right = s1->right;
740 schedule_crossing(status, left, s2);
742 schedule_crossing(status, s1, right);
745 typedef struct _box {
746 point_t left1, left2, right1, right2;
748 static inline box_t box_new(int32_t x, int32_t y)
751 box.right1.x = box.right2.x = x;
752 box.left1.x = box.left2.x = x-1;
753 box.left1.y = box.right1.y = y-1;
754 box.left2.y = box.right2.y = y;
758 static void store_horizontal(status_t*status, point_t p1, point_t p2, edgestyle_t*fs, segment_dir_t dir, int polygon_nr);
760 static void append_stroke(status_t*status, point_t a, point_t b, segment_dir_t dir, edgestyle_t*fs)
762 gfxpolystroke_t*stroke = status->strokes;
763 /* find a stoke to attach this segment to. It has to have an endpoint
764 matching our start point, and a matching edgestyle */
766 point_t p = stroke->points[stroke->num_points-1];
767 if(p.x == a.x && p.y == a.y && stroke->fs == fs && stroke->dir == dir)
769 stroke = stroke->next;
772 stroke = rfx_calloc(sizeof(gfxpolystroke_t));
775 stroke->next = status->strokes;
776 status->strokes = stroke;
777 stroke->points_size = 2;
778 stroke->points = rfx_calloc(sizeof(point_t)*stroke->points_size);
779 stroke->points[0] = a;
780 stroke->num_points = 1;
781 } else if(stroke->num_points == stroke->points_size) {
783 stroke->points_size *= 2;
784 stroke->points = rfx_realloc(stroke->points, sizeof(point_t)*stroke->points_size);
786 stroke->points[stroke->num_points++] = b;
789 static void insert_point_into_segment(status_t*status, segment_t*s, point_t p)
791 assert(s->pos.x != p.x || s->pos.y != p.y);
794 if(!dict_contains(status->segs_with_point, s))
795 dict_put(status->segs_with_point, s, 0);
796 assert(s->fs_out_ok);
799 if(s->pos.y != p.y) {
800 /* non horizontal line- copy to output */
803 fprintf(stderr, "[%d] receives next point (%.2f,%.2f)->(%.2f,%.2f) (drawing)\n", s->nr,
804 s->pos.x * status->gridsize, s->pos.y * status->gridsize,
805 p.x * status->gridsize, p.y * status->gridsize);
807 segment_dir_t dir = s->wind.is_filled?DIR_DOWN:DIR_UP;
808 assert(s->pos.y != p.y);
809 append_stroke(status, s->pos, p, dir, s->fs_out);
812 fprintf(stderr, "[%d] receives next point (%.2f,%.2f) (omitting)\n", s->nr,
813 p.x * status->gridsize,
814 p.y * status->gridsize);
818 /* horizontal line. we need to look at this more closely at the end of this
820 store_horizontal(status, s->pos, p, s->fs, s->dir, s->polygon_nr);
826 typedef struct _segrange {
833 static void segrange_adjust_endpoints(segrange_t*range, int32_t y)
835 #define XPOS_EQ(s1,s2,ypos) (XPOS((s1),(ypos))==XPOS((s2),(ypos)))
836 segment_t*min = range->segmin;
837 segment_t*max = range->segmax;
839 /* we need this because if two segments intersect exactly on
840 the scanline, segrange_test_segment_{min,max} can't tell which
841 one is smaller/larger */
842 if(min) while(min->left && XPOS_EQ(min, min->left, y)) {
845 if(max) while(max->right && XPOS_EQ(max, max->right, y)) {
851 static void segrange_test_segment_min(segrange_t*range, segment_t*seg, int32_t y)
854 /* we need to calculate the xpos anew (and can't use start coordinate or
855 intersection coordinate), because we need the xpos exactly at the end of
858 double x = XPOS(seg, y);
859 if(!range->segmin || x<range->xmin) {
864 static void segrange_test_segment_max(segrange_t*range, segment_t*seg, int32_t y)
867 double x = XPOS(seg, y);
868 if(!range->segmax || x>range->xmax) {
883 static void add_points_to_positively_sloped_segments(status_t*status, int32_t y, segrange_t*range)
885 segment_t*first=0, *last = 0;
887 for(t=0;t<status->xrow->num;t++) {
888 box_t box = box_new(status->xrow->x[t], y);
889 segment_t*seg = actlist_find(status->actlist, box.left2, box.left2);
891 seg = actlist_right(status->actlist, seg);
894 // this segment started in this scanline, ignore it
895 seg->changed = 1;last = seg;if(!first) {first=seg;}
896 } else if(seg->delta.x <= 0) {
897 // ignore segment w/ negative slope
899 last = seg;if(!first) {first=seg;}
900 double d1 = LINE_EQ(box.right1, seg);
901 double d2 = LINE_EQ(box.right2, seg);
904 insert_point_into_segment(status, seg, box.right2);
906 /* we unfortunately can't break here- the active list is sorted according
907 to the *bottom* of the scanline. hence pretty much everything that's still
908 coming might reach into our box */
915 segrange_test_segment_min(range, first, y);
916 segrange_test_segment_max(range, last, y);
926 static void add_points_to_negatively_sloped_segments(status_t*status, int32_t y, segrange_t*range)
928 segment_t*first=0, *last = 0;
930 for(t=status->xrow->num-1;t>=0;t--) {
931 box_t box = box_new(status->xrow->x[t], y);
932 segment_t*seg = actlist_find(status->actlist, box.right2, box.right2);
936 // this segment started in this scanline, ignore it
937 seg->changed = 1;last = seg;if(!first) {first=seg;}
938 } else if(seg->delta.x > 0) {
939 // ignore segment w/ positive slope
941 last = seg;if(!first) {first=seg;}
942 double d1 = LINE_EQ(box.left1, seg);
943 double d2 = LINE_EQ(box.left2, seg);
946 insert_point_into_segment(status, seg, box.right2);
954 segrange_test_segment_min(range, last, y);
955 segrange_test_segment_max(range, first, y);
958 /* segments ending in the current scanline need xrow treatment like everything else.
959 (consider an intersection taking place just above a nearly horizontal segment
960 ending on the current scanline- the intersection would snap down *below* the
961 ending segment if we don't add the intersection point to the latter right away)
962 we need to treat ending segments seperately, however. we have to delete them from
963 the active list right away to make room for intersect operations (which might
964 still be in the current scanline- consider two 45° polygons and a vertical polygon
965 intersecting on an integer coordinate). but once they're no longer in the active list,
966 we can't use the add_points_to_*_sloped_segments() functions anymore, and re-adding
967 them to the active list just for point snapping would be overkill.
968 (One other option to consider, however, would be to create a new active list only
971 static void add_points_to_ending_segments(status_t*status, int32_t y)
973 segment_t*seg = status->ending_segments;
975 segment_t*next = seg->right;seg->right=0;
977 assert(seg->b.y == status->y);
979 if(status->xrow->num == 1) {
981 assert(seg->b.x == status->xrow->x[0]);
982 point_t p = {status->xrow->x[0], y};
983 insert_point_into_segment(status, seg, p);
986 int start=0,end=status->xrow->num,dir=1;
987 if(seg->delta.x < 0) {
988 start = status->xrow->num-1;
991 for(t=start;t!=end;t+=dir) {
992 box_t box = box_new(status->xrow->x[t], y);
993 double d0 = LINE_EQ(box.left1, seg);
994 double d1 = LINE_EQ(box.left2, seg);
995 double d2 = LINE_EQ(box.right1, seg);
996 double d3 = LINE_EQ(box.right2, seg);
997 if(!(d0>=0 && d1>=0 && d2>=0 && d3>0 ||
998 d0<=0 && d1<=0 && d2<=0 && d3<0)) {
999 insert_point_into_segment(status, seg, box.right2);
1005 /* we *need* to find a point to insert. the segment's own end point
1006 is in that list, for Pete's sake. */
1010 // now that this is done, too, we can also finally free this segment
1011 segment_destroy(seg);
1014 status->ending_segments = 0;
1017 static void recalculate_windings(status_t*status, segrange_t*range)
1020 fprintf(stderr, "range: [%d]..[%d]\n", SEGNR(range->segmin), SEGNR(range->segmax));
1022 segrange_adjust_endpoints(range, status->y);
1024 segment_t*s = range->segmin;
1025 segment_t*end = range->segmax;
1029 s = actlist_leftmost(status->actlist);
1031 fprintf(stderr, "[%d]%d%s ", s->nr, s->changed,
1032 s == range->segmin?"S":(
1033 s == range->segmax?"E":""));
1036 fprintf(stderr, "\n");
1040 /* test sanity: verify that we don't have changed segments
1041 outside of the given range */
1042 s = actlist_leftmost(status->actlist);
1043 while(s && s!=range->segmin) {
1044 assert(!s->changed);
1047 s = actlist_rightmost(status->actlist);
1048 while(s && s!=range->segmax) {
1049 assert(!s->changed);
1052 /* in check mode, go through the whole interval so we can test
1053 that all polygons where the edgestyle changed also have seg->changed=1 */
1054 s = actlist_leftmost(status->actlist);
1065 segment_t* left = actlist_left(status->actlist, s);
1066 windstate_t wind = left?left->wind:status->windrule->start(status->context);
1067 s->wind = status->windrule->add(status->context, wind, s->fs, s->dir, s->polygon_nr);
1068 edgestyle_t*fs_old = s->fs_out;
1069 s->fs_out = status->windrule->diff(&wind, &s->wind);
1072 fprintf(stderr, "[%d] dir=%s wind=%d wind.filled=%s fs_old/new=%s/%s %s\n", s->nr, s->dir==DIR_UP?"up":"down", s->wind.wind_nr, s->wind.is_filled?"fill":"nofill",
1073 fs_old?"draw":"omit", s->fs_out?"draw":"omit",
1074 fs_old!=s->fs_out?"CHANGED":"");
1076 assert(!(!s->changed && fs_old!=s->fs_out));
1087 /* we need to handle horizontal lines in order to add points to segments
1088 we otherwise would miss during the windrule re-evaluation */
1089 static void intersect_with_horizontal(status_t*status, segment_t*h)
1091 segment_t* left = actlist_find(status->actlist, h->a, h->a);
1092 segment_t* right = actlist_find(status->actlist, h->b, h->b);
1094 /* not strictly necessary, also done by the event */
1095 xrow_add(status->xrow, h->a.x);
1102 left = actlist_right(status->actlist, left); //first seg to the right of h->a
1103 right = right->right; //first seg to the right of h->b
1104 segment_t* s = left;
1109 int32_t x = XPOS_INT(s, status->y);
1110 point_t p = {x, status->y};
1112 fprintf(stderr, "...intersecting with [%d] (%.2f,%.2f) -> (%.2f,%.2f) at (%.2f,%.2f)\n",
1114 s->a.x * status->gridsize, s->a.y * status->gridsize,
1115 s->b.x * status->gridsize, s->b.y * status->gridsize,
1116 x * status->gridsize, status->y * status->gridsize
1119 assert(x >= h->a.x);
1120 assert(x <= h->b.x);
1121 assert(s->delta.x > 0 && x >= s->a.x || s->delta.x <= 0 && x <= s->a.x);
1122 assert(s->delta.x > 0 && x <= s->b.x || s->delta.x <= 0 && x >= s->b.x);
1123 xrow_add(status->xrow, x);
1130 /* while, for a scanline, we need both starting as well as ending segments in order
1131 to *reconstruct* horizontal lines, we only need one or the other to *process*
1132 horizontal lines from the input data.
1134 So horizontal lines are processed twice: first they create hotpixels by intersecting
1135 all segments on the scanline (EVENT_HORIZTONAL). Secondly, they are processed for
1136 their actual content. The second also happens for all segments that received more than
1137 one point in this scanline.
1139 void horiz_reset(horizdata_t*horiz)
1144 void horiz_destroy(horizdata_t*horiz)
1146 if(horiz->data) rfx_free(horiz->data);
1150 static int compare_horizontals(const void *_h1, const void *_h2)
1152 horizontal_t*h1 = (horizontal_t*)_h1;
1153 horizontal_t*h2 = (horizontal_t*)_h2;
1154 return h1->x1 - h2->x1;
1157 static void process_horizontals(status_t*status)
1159 horizdata_t*horiz = &status->horiz;
1160 qsort(horiz->data, horiz->num, sizeof(horizontal_t), compare_horizontals);
1163 for(t=0;t<horiz->num;t++) {
1164 horizontal_t*h = &horiz->data[t];
1166 fprintf(stderr, "horizontal (y=%.2f): %.2f -> %.2f dir=%s fs=%p\n",
1167 h->y * status->gridsize,
1168 h->x1 * status->gridsize,
1169 h->x2 * status->gridsize,
1170 h->dir==DIR_UP?"up":"down", h->fs);
1172 assert(h->y == status->y);
1173 assert(xrow_contains(status->xrow, h->x1));
1174 assert(xrow_contains(status->xrow, h->x2));
1176 int pos = xrow_find(status->xrow, h->x1);
1178 assert(pos <= status->xrow->num);
1179 assert(pos == status->xrow->num || status->xrow->x[pos] > x);
1182 int next_x = pos < status->xrow->num ? status->xrow->x[pos] : h->x2;
1187 point_t p1 = {x,h->y};
1188 point_t p2 = {next_x,h->y};
1189 segment_t* left = actlist_find(status->actlist, p1, p2);
1190 assert(!left || left->fs_out_ok);
1192 fprintf(stderr, " fragment %.2f..%.2f\n",
1193 x * status->gridsize,
1194 next_x * status->gridsize);
1196 fprintf(stderr, " segment [%d] (%.2f,%.2f -> %.2f,%2f, at %.2f,%.2f) is to the left\n",
1198 left->a.x * status->gridsize,
1199 left->a.y * status->gridsize,
1200 left->b.x * status->gridsize,
1201 left->b.y * status->gridsize,
1202 left->pos.x * status->gridsize,
1203 left->pos.y * status->gridsize
1205 /* this segment might be a distance away from the left point
1206 of the horizontal line if the horizontal line belongs to a stroke
1207 with segments that just ended (so this horizontal line appears to
1208 be "floating in space" from our current point of view)
1209 assert(left->pos.y == h->y && left->pos.x == h->x1);
1213 windstate_t below = left?left->wind:status->windrule->start(status->context);
1214 windstate_t above = status->windrule->add(status->context, below, h->fs, h->dir, h->polygon_nr);
1215 edgestyle_t*fs = status->windrule->diff(&above, &below);
1218 fprintf(stderr, " ...storing\n");
1220 append_stroke(status, p1, p2, h->dir, fs);
1223 fprintf(stderr, " ...ignoring\n");
1231 static void store_horizontal(status_t*status, point_t p1, point_t p2, edgestyle_t*fs, segment_dir_t dir, int polygon_nr)
1233 assert(p1.y == p2.y);
1234 assert(p1.x != p2.x); // TODO: can this happen?
1237 dir = DIR_INVERT(dir);
1244 if(status->horiz.size == status->horiz.num) {
1245 if(!status->horiz.size)
1246 status->horiz.size = 16;
1247 status->horiz.size *= 2;
1248 status->horiz.data = rfx_realloc(status->horiz.data, sizeof(status->horiz.data[0])*status->horiz.size);
1250 horizontal_t*h = &status->horiz.data[status->horiz.num++];
1256 h->polygon_nr = polygon_nr;
1260 static void event_apply(status_t*status, event_t*e)
1263 event_dump(status, e);
1267 case EVENT_HORIZONTAL: {
1268 segment_t*s = e->s1;
1269 intersect_with_horizontal(status, s);
1270 store_horizontal(status, s->a, s->b, s->fs, s->dir, s->polygon_nr);
1271 advance_stroke(&status->queue, 0, s->stroke, s->polygon_nr, s->stroke_pos, status->gridsize);
1272 segment_destroy(s);e->s1=0;
1276 //delete segment from list
1277 segment_t*s = e->s1;
1279 dict_del(status->intersecting_segs, s);
1280 dict_del(status->segs_with_point, s);
1281 assert(!dict_contains(status->intersecting_segs, s));
1282 assert(!dict_contains(status->segs_with_point, s));
1284 segment_t*left = s->left;
1285 segment_t*right = s->right;
1286 actlist_delete(status->actlist, s);
1288 schedule_crossing(status, left, right);
1290 /* schedule segment for xrow handling */
1291 s->left = 0; s->right = status->ending_segments;
1292 status->ending_segments = s;
1293 advance_stroke(&status->queue, 0, s->stroke, s->polygon_nr, s->stroke_pos, status->gridsize);
1297 //insert segment into list
1298 segment_t*s = e->s1;
1299 assert(e->p.x == s->a.x && e->p.y == s->a.y);
1300 actlist_insert(status->actlist, s->a, s->b, s);
1301 segment_t*left = s->left;
1302 segment_t*right = s->right;
1304 schedule_crossing(status, left, s);
1306 schedule_crossing(status, s, right);
1307 schedule_endpoint(status, s);
1311 // exchange two segments
1312 if(e->s1->right == e->s2) {
1313 assert(e->s2->left == e->s1);
1314 exchange_two(status, e);
1316 assert(e->s2->left != e->s1);
1318 fprintf(stderr, "Ignore this crossing ([%d] not next to [%d])\n", e->s1->nr, e->s2->nr);
1320 #ifndef DONT_REMEMBER_CROSSINGS
1321 /* ignore this crossing for now (there are some line segments in between).
1322 it'll get rescheduled as soon as the "obstacles" are gone */
1323 char del1 = dict_del(&e->s1->scheduled_crossings, (void*)(ptroff_t)e->s2->nr);
1324 char del2 = dict_del(&e->s2->scheduled_crossings, (void*)(ptroff_t)e->s1->nr);
1325 assert(del1 && del2);
1331 #ifndef DONT_REMEMBER_CROSSINGS
1332 assert(dict_contains(status->seen_crossings, &pair));
1333 dict_del(status->seen_crossings, &pair);
1342 static void check_status(status_t*status)
1344 DICT_ITERATE_KEY(status->intersecting_segs, segment_t*, s) {
1345 if((s->pos.x != s->b.x ||
1346 s->pos.y != s->b.y) &&
1347 !dict_contains(status->segs_with_point, s)) {
1348 fprintf(stderr, "Error: segment [%d] (%sslope) intersects in scanline %d, but it didn't receive a point\n",
1350 s->delta.x<0?"-":"+",
1358 gfxpoly_t* gfxpoly_process(gfxpoly_t*poly1, gfxpoly_t*poly2, windrule_t*windrule, windcontext_t*context)
1360 current_polygon = poly1;
1363 memset(&status, 0, sizeof(status_t));
1364 status.gridsize = poly1->gridsize;
1366 queue_init(&status.queue);
1367 gfxpoly_enqueue(poly1, &status.queue, 0, /*polygon nr*/0);
1369 assert(poly1->gridsize == poly2->gridsize);
1370 gfxpoly_enqueue(poly2, &status.queue, 0, /*polygon nr*/1);
1373 status.windrule = windrule;
1374 status.context = context;
1375 status.actlist = actlist_new();
1378 status.seen_crossings = dict_new2(&point_type);
1379 int32_t lasty=-0x80000000;
1382 status.xrow = xrow_new();
1384 event_t*e = queue_get(&status.queue);
1389 assert(status.y>=lasty);
1391 status.intersecting_segs = dict_new2(&ptr_type);
1392 status.segs_with_point = dict_new2(&ptr_type);
1396 fprintf(stderr, "----------------------------------- %.2f\n", status.y * status.gridsize);
1397 actlist_dump(status.actlist, status.y-1, status.gridsize);
1400 actlist_verify(status.actlist, status.y-1);
1402 xrow_reset(status.xrow);
1403 horiz_reset(&status.horiz);
1406 xrow_add(status.xrow, e->p.x);
1407 event_apply(&status, e);
1409 e = queue_get(&status.queue);
1410 } while(e && status.y == e->p.y);
1412 xrow_sort(status.xrow);
1414 memset(&range, 0, sizeof(range));
1416 actlist_dump(status.actlist, status.y, status.gridsize);
1418 xrow_dump(status.xrow, status.gridsize);
1419 add_points_to_positively_sloped_segments(&status, status.y, &range);
1420 add_points_to_negatively_sloped_segments(&status, status.y, &range);
1421 add_points_to_ending_segments(&status, status.y);
1423 recalculate_windings(&status, &range);
1424 process_horizontals(&status);
1426 check_status(&status);
1427 dict_destroy(status.intersecting_segs);
1428 dict_destroy(status.segs_with_point);
1432 dict_destroy(status.seen_crossings);
1434 actlist_destroy(status.actlist);
1435 queue_destroy(&status.queue);
1436 horiz_destroy(&status.horiz);
1437 xrow_destroy(status.xrow);
1439 gfxpoly_t*p = (gfxpoly_t*)malloc(sizeof(gfxpoly_t));
1440 p->gridsize = poly1->gridsize;
1441 p->strokes = status.strokes;
1444 /* we only add segments with non-empty edgestyles to strokes in
1445 recalculate_windings, but better safe than sorry */
1446 gfxpolystroke_t*stroke = p->strokes;
1449 stroke = stroke->next;
1455 static windcontext_t twopolygons = {2};
1456 gfxpoly_t* gfxpoly_intersect(gfxpoly_t*p1, gfxpoly_t*p2)
1458 return gfxpoly_process(p1, p2, &windrule_intersect, &twopolygons);
1460 gfxpoly_t* gfxpoly_union(gfxpoly_t*p1, gfxpoly_t*p2)
1462 return gfxpoly_process(p1, p2, &windrule_union, &twopolygons);