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);
238 DICT_ITERATE_ITEMS(d2, point_t*, p2, void*, c2) {
239 int count = (ptroff_t)c2;
240 assert(dict_contains(d1, p2));
241 int ocount = (ptroff_t)dict_lookup(d1, p2);
243 if(count>0) fprintf(stderr, "Error: Point (%.2f,%.2f) has %d more incoming than outgoing segments (%d incoming, %d outgoing)\n", p2->x * poly->gridsize, p2->y * poly->gridsize, count, (ocount+count)/2, (ocount-count)/2);
244 if(count<0) fprintf(stderr, "Error: Point (%.2f,%.2f) has %d more outgoing than incoming segments (%d incoming, %d outgoing)\n", p2->x * poly->gridsize, p2->y * poly->gridsize, -count, (ocount+count)/2, (ocount-count)/2);
255 void gfxpoly_dump(gfxpoly_t*poly)
258 double g = poly->gridsize;
259 fprintf(stderr, "polyon %p (gridsize: %.2f)\n", poly, poly->gridsize);
260 gfxpolystroke_t*stroke = poly->strokes;
261 for(;stroke;stroke=stroke->next) {
262 fprintf(stderr, "%11p", stroke);
263 if(stroke->dir==DIR_UP) {
264 for(s=stroke->num_points-1;s>=1;s--) {
265 point_t a = stroke->points[s];
266 point_t b = stroke->points[s-1];
267 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,
268 s==1?"]":"", a.y==b.y?"H":"");
271 for(s=0;s<stroke->num_points-1;s++) {
272 point_t a = stroke->points[s];
273 point_t b = stroke->points[s+1];
274 fprintf(stderr, "%s (%.2f,%.2f) -> (%.2f,%.2f)%s%s\n", s?" ":"", a.x*g, a.y*g, b.x*g, b.y*g,
275 s==stroke->num_points-2?"]":"", a.y==b.y?"H":"");
281 void gfxpoly_save(gfxpoly_t*poly, const char*filename)
283 FILE*fi = fopen(filename, "wb");
284 fprintf(fi, "%% gridsize %f\n", poly->gridsize);
285 fprintf(fi, "%% begin\n");
287 gfxpolystroke_t*stroke = poly->strokes;
288 for(;stroke;stroke=stroke->next) {
289 fprintf(fi, "%g setgray\n", stroke->dir==DIR_UP ? 0.7 : 0);
290 point_t p = stroke->points[0];
291 fprintf(fi, "%d %d moveto\n", p.x, p.y);
292 for(s=1;s<stroke->num_points;s++) {
293 p = stroke->points[s];
294 fprintf(fi, "%d %d lineto\n", p.x, p.y);
296 fprintf(fi, "stroke\n");
298 fprintf(fi, "showpage\n");
302 void gfxpoly_save_arrows(gfxpoly_t*poly, const char*filename)
304 FILE*fi = fopen(filename, "wb");
305 fprintf(fi, "%% gridsize %f\n", poly->gridsize);
306 fprintf(fi, "%% begin\n");
308 double l = 5.0 / poly->gridsize;
309 double g = poly->gridsize;
310 gfxpolystroke_t*stroke = poly->strokes;
311 for(;stroke;stroke=stroke->next) {
312 fprintf(fi, "%g setgray\n", 0);
314 int s = stroke->dir==DIR_UP?stroke->num_points-1:0;
315 int end = stroke->dir==DIR_UP?-1:stroke->num_points;
316 int dir = stroke->dir==DIR_UP?-1:1;
318 point_t p = stroke->points[s];
321 fprintf(fi, "%f %f moveto\n", p.x * g, p.y * g);
322 for(;s!=end;s+=dir) {
323 p = stroke->points[s];
326 double d = sqrt(lx*lx+ly*ly);
330 fprintf(fi, "%f %f lineto\n", (p.x - lx*d2) * g, (p.y - ly*d2) * g);
331 fprintf(fi, "%f %f lineto\n", (p.x - lx*d2 + (ly*d))*g,
332 (p.y - ly*d2 - (lx*d))*g);
333 fprintf(fi, "%f %f lineto\n", p.x * g, p.y * g);
334 fprintf(fi, "%f %f lineto\n", (p.x - lx*d2 - (ly*d))*g,
335 (p.y - ly*d2 + (lx*d))*g);
336 fprintf(fi, "%f %f lineto\n", (p.x - lx*d2) * g, (p.y - ly*d2) * g);
337 fprintf(fi, "%f %f moveto\n", p.x * g, p.y * g);
340 fprintf(fi, "stroke\n");
342 fprintf(fi, "showpage\n");
346 inline static event_t* event_new()
348 event_t*e = rfx_calloc(sizeof(event_t));
351 inline static void event_free(event_t*e)
356 static void event_dump(status_t*status, event_t*e)
358 if(e->type == EVENT_HORIZONTAL) {
359 fprintf(stderr, "Horizontal [%d] (%.2f,%.2f) -> (%.2f,%.2f)\n", (int)e->s1->nr,
360 e->s1->a.x * status->gridsize, e->s1->a.y * status->gridsize, e->s1->b.x * status->gridsize, e->s1->b.y * status->gridsize);
361 } else if(e->type == EVENT_START) {
362 fprintf(stderr, "event: segment [%d] starts at (%.2f,%.2f)\n", (int)e->s1->nr,
363 e->p.x * status->gridsize, e->p.y * status->gridsize);
364 } else if(e->type == EVENT_END) {
365 fprintf(stderr, "event: segment [%d] ends at (%.2f,%.2f)\n", (int)e->s1->nr,
366 e->p.x * status->gridsize, e->p.y * status->gridsize);
367 } else if(e->type == EVENT_CROSS) {
368 fprintf(stderr, "event: segment [%d] and [%d] intersect at (%.2f,%.2f)\n", (int)e->s1->nr, (int)e->s2->nr,
369 e->p.x * status->gridsize, e->p.y * status->gridsize);
375 static inline int32_t max32(int32_t v1, int32_t v2) {return v1>v2?v1:v2;}
376 static inline int32_t min32(int32_t v1, int32_t v2) {return v1<v2?v1:v2;}
378 static void segment_dump(segment_t*s)
380 fprintf(stderr, "[%d] (%d,%d)->(%d,%d) ", (int)s->nr, s->a.x, s->a.y, s->b.x, s->b.y);
381 fprintf(stderr, " dx:%d dy:%d k:%f dx/dy=%f fs=%p\n", s->delta.x, s->delta.y, s->k,
382 (double)s->delta.x / s->delta.y, s->fs);
385 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)
387 static int segment_count=0;
388 s->nr = segment_count++;
393 /* We need to make sure horizontal segments always go from left to right.
394 "up/down" for horizontal segments is handled by "rotating"
395 them 90° counterclockwise in screen coordinates (tilt your head to
396 the right). In other words, the "normal" direction (what's positive dy for
397 vertical segments) is positive dx for horizontal segments ("down" is right).
400 s->dir = DIR_INVERT(s->dir);
401 int32_t x = x1;x1=x2;x2=x;
402 int32_t y = y1;y1=y2;y2=y;
404 fprintf(stderr, "Scheduling horizontal segment [%d] (%.2f,%.2f) -> (%.2f,%.2f) %s\n",
406 x1 * 0.05, y1 * 0.05, x2 * 0.05, y2 * 0.05, s->dir==DIR_UP?"up":"down");
412 s->k = (double)x1*y2-(double)x2*y1;
413 s->left = s->right = 0;
416 s->minx = min32(x1,x2);
417 s->maxx = max32(x1,x2);
420 s->polygon_nr = polygon_nr;
423 /* notice: on some systems (with some compilers), for the line
424 (1073741823,-1073741824)->(1073741823,1073741823)
425 we get LINE_EQ(s->a, s) == 1.
426 That's why we now clamp to 26 bit.
428 assert(LINE_EQ(s->a, s) == 0);
429 assert(LINE_EQ(s->b, s) == 0);
431 /* check that all signs are in order:
439 p.x = min32(s->a.x, s->b.x);
440 assert(LINE_EQ(p, s) <= 0);
441 p.x = max32(s->a.x, s->b.x);
442 assert(LINE_EQ(p, s) >= 0);
445 #ifndef DONT_REMEMBER_CROSSINGS
446 dict_init2(&s->scheduled_crossings, &ptr_type, 0);
450 static segment_t* segment_new(point_t a, point_t b, int polygon_nr, segment_dir_t dir)
452 segment_t*s = (segment_t*)rfx_calloc(sizeof(segment_t));
453 segment_init(s, a.x, a.y, b.x, b.y, polygon_nr, dir);
457 static void segment_clear(segment_t*s)
459 #ifndef DONT_REMEMBER_CROSSINGS
460 dict_clear(&s->scheduled_crossings);
463 static void segment_destroy(segment_t*s)
469 static void advance_stroke(queue_t*queue, hqueue_t*hqueue, gfxpolystroke_t*stroke, int polygon_nr, int pos, double gridsize)
474 /* we need to queue multiple segments at once because we need to process start events
475 before horizontal events */
476 while(pos < stroke->num_points-1) {
477 assert(stroke->points[pos].y <= stroke->points[pos+1].y);
478 s = segment_new(stroke->points[pos], stroke->points[pos+1], polygon_nr, stroke->dir);
486 fprintf(stderr, "[%d] (%.2f,%.2f) -> (%.2f,%.2f) %s (stroke %p, %d more to come)\n",
487 s->nr, s->a.x * gridsize, s->a.y * gridsize,
488 s->b.x * gridsize, s->b.y * gridsize,
489 s->dir==DIR_UP?"up":"down", stroke, stroke->num_points - 1 - pos);
491 event_t* e = event_new();
492 e->type = s->delta.y ? EVENT_START : EVENT_HORIZONTAL;
497 if(queue) queue_put(queue, e);
498 else hqueue_put(hqueue, e);
500 if(e->type != EVENT_HORIZONTAL) {
510 static void gfxpoly_enqueue(gfxpoly_t*p, queue_t*queue, hqueue_t*hqueue, int polygon_nr)
513 gfxpolystroke_t*stroke = p->strokes;
514 for(;stroke;stroke=stroke->next) {
515 assert(stroke->num_points > 1);
519 for(s=0;s<stroke->num_points-1;s++) {
520 assert(stroke->points[s].y <= stroke->points[s+1].y);
523 advance_stroke(queue, hqueue, stroke, polygon_nr, 0, p->gridsize);
527 static void schedule_endpoint(status_t*status, segment_t*s)
529 // schedule end point of segment
530 assert(s->b.y > status->y);
531 event_t*e = event_new();
536 queue_put(&status->queue, e);
539 static void schedule_crossing(status_t*status, segment_t*s1, segment_t*s2)
541 /* the code that's required (and the checks you can perform) before
542 it can be said with 100% certainty that we indeed have a valid crossing
543 amazes me every time. -mk */
546 assert(s1->right == s2);
547 assert(s2->left == s1);
548 int32_t miny1 = min32(s1->a.y,s1->b.y);
549 int32_t maxy1 = max32(s1->a.y,s1->b.y);
550 int32_t miny2 = min32(s2->a.y,s2->b.y);
551 int32_t maxy2 = max32(s2->a.y,s2->b.y);
552 int32_t minx1 = min32(s1->a.x,s1->b.x);
553 int32_t minx2 = min32(s2->a.x,s2->b.x);
554 int32_t maxx1 = max32(s1->a.x,s1->b.x);
555 int32_t maxx2 = max32(s2->a.x,s2->b.x);
556 /* check that precomputation is sane */
557 assert(minx1 == s1->minx && minx2 == s2->minx);
558 assert(maxx1 == s1->maxx && maxx2 == s2->maxx);
559 /* both segments are active, so this can't happen */
560 assert(!(maxy1 <= miny2 || maxy2 <= miny1));
561 /* we know that right now, s2 is to the right of s1, so there's
562 no way the complete bounding box of s1 is to the right of s1 */
563 assert(!(s1->minx > s2->maxx));
564 assert(s1->minx != s2->maxx || (!s1->delta.x && !s2->delta.x));
567 if(s1->maxx <= s2->minx) {
569 fprintf(stderr, "[%d] doesn't intersect with [%d] because: bounding boxes don't intersect\n", s1->nr, s2->nr);
571 /* bounding boxes don't intersect */
575 #ifndef DONT_REMEMBER_CROSSINGS
576 if(dict_contains(&s1->scheduled_crossings, (void*)(ptroff_t)s2->nr)) {
577 /* FIXME: this whole segment hashing thing is really slow */
579 fprintf(stderr, "[%d] doesn't intersect with [%d] because: we already scheduled this intersection\n", s1->nr, s2->nr);
580 // DICT_ITERATE_KEY(&s1->scheduled_crossings, void*, x) {
581 // fprintf(stderr, "[%d]<->[%d]\n", s1->nr, (int)(ptroff_t)x);
584 return; // we already know about this one
588 double det = (double)s1->delta.x*s2->delta.y - (double)s1->delta.y*s2->delta.x;
591 // lines are exactly on top of each other (ignored)
593 fprintf(stderr, "Notice: segments [%d] and [%d] are exactly on top of each other\n", s1->nr, s2->nr);
598 fprintf(stderr, "[%d] doesn't intersect with [%d] because: they are parallel to each other\n", s1->nr, s2->nr);
600 /* lines are parallel */
605 double asign2 = LINE_EQ(s1->a, s2);
607 // segment1 touches segment2 in a single point (ignored)
609 fprintf(stderr, "Notice: segment [%d]'s start point touches segment [%d]\n", s1->nr, s2->nr);
613 double bsign2 = LINE_EQ(s1->b, s2);
615 // segment1 touches segment2 in a single point (ignored)
617 fprintf(stderr, "Notice: segment [%d]'s end point touches segment [%d]\n", s1->nr, s2->nr);
622 if(asign2<0 && bsign2<0) {
623 // segment1 is completely to the left of segment2
625 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);
629 if(asign2>0 && bsign2>0) {
630 // segment1 is completely to the right of segment2
631 #ifndef DONT_REMEMBER_CROSSINGS
635 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);
640 double asign1 = LINE_EQ(s2->a, s1);
642 // segment2 touches segment1 in a single point (ignored)
644 fprintf(stderr, "Notice: segment [%d]'s start point touches segment [%d]\n", s2->nr, s1->nr);
648 double bsign1 = LINE_EQ(s2->b, s1);
650 // segment2 touches segment1 in a single point (ignored)
652 fprintf(stderr, "Notice: segment [%d]'s end point touches segment [%d]\n", s2->nr, s1->nr);
657 if(asign1<0 && bsign1<0) {
658 // segment2 is completely to the left of segment1
659 #ifndef DONT_REMEMBER_CROSSINGS
663 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);
667 if(asign1>0 && bsign1>0) {
668 // segment2 is completely to the right of segment1
670 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);
675 #ifdef DONT_REMEMBER_CROSSINGS
676 /* s2 crosses s1 from *left* to *right*. This is a crossing we already processed-
677 there's not way s2 would be to the left of s1 otherwise */
678 if(asign1<0 && bsign1>0) return;
679 if(asign2>0 && bsign2<0) return;
682 assert(!(asign1<0 && bsign1>0));
683 assert(!(asign2>0 && bsign2<0));
685 /* TODO: should we precompute these? */
686 double la = (double)s1->a.x*(double)s1->b.y - (double)s1->a.y*(double)s1->b.x;
687 double lb = (double)s2->a.x*(double)s2->b.y - (double)s2->a.y*(double)s2->b.x;
690 p.x = (int32_t)ceil((-la*s2->delta.x + lb*s1->delta.x) / det);
691 p.y = (int32_t)ceil((+lb*s1->delta.y - la*s2->delta.y) / det);
693 assert(p.y >= status->y);
695 assert(p.x >= s1->minx && p.x <= s1->maxx);
696 assert(p.x >= s2->minx && p.x <= s2->maxx);
701 #ifndef DONT_REMEMBER_CROSSINGS
702 assert(!dict_contains(status->seen_crossings, &pair));
703 dict_put(status->seen_crossings, &pair, 0);
707 fprintf(stderr, "schedule crossing between [%d] and [%d] at (%d,%d)\n", s1->nr, s2->nr, p.x, p.y);
710 #ifndef DONT_REMEMBER_CROSSINGS
711 /* we insert into each other's intersection history because these segments might switch
712 places and we still want to look them up quickly after they did */
713 dict_put(&s1->scheduled_crossings, (void*)(ptroff_t)(s2->nr), 0);
714 dict_put(&s2->scheduled_crossings, (void*)(ptroff_t)(s1->nr), 0);
717 event_t* e = event_new();
718 e->type = EVENT_CROSS;
722 queue_put(&status->queue, e);
726 static void exchange_two(status_t*status, event_t*e)
728 //exchange two segments in list
729 segment_t*s1 = e->s1;
730 segment_t*s2 = e->s2;
732 if(!dict_contains(status->intersecting_segs, s1))
733 dict_put(status->intersecting_segs, s1, 0);
734 if(!dict_contains(status->intersecting_segs, s2))
735 dict_put(status->intersecting_segs, s2, 0);
737 assert(s2->left == s1);
738 assert(s1->right == s2);
739 actlist_swap(status->actlist, s1, s2);
740 assert(s2->right == s1);
741 assert(s1->left == s2);
742 segment_t*left = s2->left;
743 segment_t*right = s1->right;
745 schedule_crossing(status, left, s2);
747 schedule_crossing(status, s1, right);
750 typedef struct _box {
751 point_t left1, left2, right1, right2;
753 static inline box_t box_new(int32_t x, int32_t y)
756 box.right1.x = box.right2.x = x;
757 box.left1.x = box.left2.x = x-1;
758 box.left1.y = box.right1.y = y-1;
759 box.left2.y = box.right2.y = y;
763 static void store_horizontal(status_t*status, point_t p1, point_t p2, edgestyle_t*fs, segment_dir_t dir, int polygon_nr);
765 static void append_stroke(status_t*status, point_t a, point_t b, segment_dir_t dir, edgestyle_t*fs)
767 gfxpolystroke_t*stroke = status->strokes;
768 /* find a stoke to attach this segment to. It has to have an endpoint
769 matching our start point, and a matching edgestyle */
771 point_t p = stroke->points[stroke->num_points-1];
772 if(p.x == a.x && p.y == a.y && stroke->fs == fs && stroke->dir == dir)
774 stroke = stroke->next;
777 stroke = rfx_calloc(sizeof(gfxpolystroke_t));
780 stroke->next = status->strokes;
781 status->strokes = stroke;
782 stroke->points_size = 2;
783 stroke->points = rfx_calloc(sizeof(point_t)*stroke->points_size);
784 stroke->points[0] = a;
785 stroke->num_points = 1;
786 } else if(stroke->num_points == stroke->points_size) {
788 stroke->points_size *= 2;
789 stroke->points = rfx_realloc(stroke->points, sizeof(point_t)*stroke->points_size);
791 stroke->points[stroke->num_points++] = b;
794 static void insert_point_into_segment(status_t*status, segment_t*s, point_t p)
796 assert(s->pos.x != p.x || s->pos.y != p.y);
799 if(!dict_contains(status->segs_with_point, s))
800 dict_put(status->segs_with_point, s, 0);
801 assert(s->fs_out_ok);
804 if(s->pos.y != p.y) {
805 /* non horizontal line- copy to output */
808 fprintf(stderr, "[%d] receives next point (%.2f,%.2f)->(%.2f,%.2f) (drawing)\n", s->nr,
809 s->pos.x * status->gridsize, s->pos.y * status->gridsize,
810 p.x * status->gridsize, p.y * status->gridsize);
812 segment_dir_t dir = s->wind.is_filled?DIR_DOWN:DIR_UP;
813 assert(s->pos.y != p.y);
814 append_stroke(status, s->pos, p, dir, s->fs_out);
817 fprintf(stderr, "[%d] receives next point (%.2f,%.2f) (omitting)\n", s->nr,
818 p.x * status->gridsize,
819 p.y * status->gridsize);
823 /* horizontal line. we need to look at this more closely at the end of this
825 store_horizontal(status, s->pos, p, s->fs, s->dir, s->polygon_nr);
831 typedef struct _segrange {
838 static void segrange_adjust_endpoints(segrange_t*range, int32_t y)
840 #define XPOS_EQ(s1,s2,ypos) (XPOS((s1),(ypos))==XPOS((s2),(ypos)))
841 segment_t*min = range->segmin;
842 segment_t*max = range->segmax;
844 /* we need this because if two segments intersect exactly on
845 the scanline, segrange_test_segment_{min,max} can't tell which
846 one is smaller/larger */
847 if(min) while(min->left && XPOS_EQ(min, min->left, y)) {
850 if(max) while(max->right && XPOS_EQ(max, max->right, y)) {
856 static void segrange_test_segment_min(segrange_t*range, segment_t*seg, int32_t y)
859 /* we need to calculate the xpos anew (and can't use start coordinate or
860 intersection coordinate), because we need the xpos exactly at the end of
863 double x = XPOS(seg, y);
864 if(!range->segmin || x<range->xmin) {
869 static void segrange_test_segment_max(segrange_t*range, segment_t*seg, int32_t y)
872 double x = XPOS(seg, y);
873 if(!range->segmax || x>range->xmax) {
888 static void add_points_to_positively_sloped_segments(status_t*status, int32_t y, segrange_t*range)
890 segment_t*first=0, *last = 0;
892 for(t=0;t<status->xrow->num;t++) {
893 box_t box = box_new(status->xrow->x[t], y);
894 segment_t*seg = actlist_find(status->actlist, box.left2, box.left2);
896 seg = actlist_right(status->actlist, seg);
899 // this segment started in this scanline, ignore it
900 seg->changed = 1;last = seg;if(!first) {first=seg;}
901 } else if(seg->delta.x <= 0) {
902 // ignore segment w/ negative slope
904 last = seg;if(!first) {first=seg;}
905 double d1 = LINE_EQ(box.right1, seg);
906 double d2 = LINE_EQ(box.right2, seg);
909 insert_point_into_segment(status, seg, box.right2);
911 /* we unfortunately can't break here- the active list is sorted according
912 to the *bottom* of the scanline. hence pretty much everything that's still
913 coming might reach into our box */
920 segrange_test_segment_min(range, first, y);
921 segrange_test_segment_max(range, last, y);
931 static void add_points_to_negatively_sloped_segments(status_t*status, int32_t y, segrange_t*range)
933 segment_t*first=0, *last = 0;
935 for(t=status->xrow->num-1;t>=0;t--) {
936 box_t box = box_new(status->xrow->x[t], y);
937 segment_t*seg = actlist_find(status->actlist, box.right2, box.right2);
941 // this segment started in this scanline, ignore it
942 seg->changed = 1;last = seg;if(!first) {first=seg;}
943 } else if(seg->delta.x > 0) {
944 // ignore segment w/ positive slope
946 last = seg;if(!first) {first=seg;}
947 double d1 = LINE_EQ(box.left1, seg);
948 double d2 = LINE_EQ(box.left2, seg);
951 insert_point_into_segment(status, seg, box.right2);
959 segrange_test_segment_min(range, last, y);
960 segrange_test_segment_max(range, first, y);
963 /* segments ending in the current scanline need xrow treatment like everything else.
964 (consider an intersection taking place just above a nearly horizontal segment
965 ending on the current scanline- the intersection would snap down *below* the
966 ending segment if we don't add the intersection point to the latter right away)
967 we need to treat ending segments seperately, however. we have to delete them from
968 the active list right away to make room for intersect operations (which might
969 still be in the current scanline- consider two 45° polygons and a vertical polygon
970 intersecting on an integer coordinate). but once they're no longer in the active list,
971 we can't use the add_points_to_*_sloped_segments() functions anymore, and re-adding
972 them to the active list just for point snapping would be overkill.
973 (One other option to consider, however, would be to create a new active list only
976 static void add_points_to_ending_segments(status_t*status, int32_t y)
978 segment_t*seg = status->ending_segments;
980 segment_t*next = seg->right;seg->right=0;
982 assert(seg->b.y == status->y);
984 if(status->xrow->num == 1) {
986 assert(seg->b.x == status->xrow->x[0]);
987 point_t p = {status->xrow->x[0], y};
988 insert_point_into_segment(status, seg, p);
991 int start=0,end=status->xrow->num,dir=1;
992 if(seg->delta.x < 0) {
993 start = status->xrow->num-1;
996 for(t=start;t!=end;t+=dir) {
997 box_t box = box_new(status->xrow->x[t], y);
998 double d0 = LINE_EQ(box.left1, seg);
999 double d1 = LINE_EQ(box.left2, seg);
1000 double d2 = LINE_EQ(box.right1, seg);
1001 double d3 = LINE_EQ(box.right2, seg);
1002 if(!(d0>=0 && d1>=0 && d2>=0 && d3>0 ||
1003 d0<=0 && d1<=0 && d2<=0 && d3<0)) {
1004 insert_point_into_segment(status, seg, box.right2);
1010 /* we *need* to find a point to insert. the segment's own end point
1011 is in that list, for Pete's sake. */
1015 // now that this is done, too, we can also finally free this segment
1016 segment_destroy(seg);
1019 status->ending_segments = 0;
1022 static void recalculate_windings(status_t*status, segrange_t*range)
1025 fprintf(stderr, "range: [%d]..[%d]\n", SEGNR(range->segmin), SEGNR(range->segmax));
1027 segrange_adjust_endpoints(range, status->y);
1029 segment_t*s = range->segmin;
1030 segment_t*end = range->segmax;
1034 s = actlist_leftmost(status->actlist);
1036 fprintf(stderr, "[%d]%d%s ", s->nr, s->changed,
1037 s == range->segmin?"S":(
1038 s == range->segmax?"E":""));
1041 fprintf(stderr, "\n");
1045 /* test sanity: verify that we don't have changed segments
1046 outside of the given range */
1047 s = actlist_leftmost(status->actlist);
1048 while(s && s!=range->segmin) {
1049 assert(!s->changed);
1052 s = actlist_rightmost(status->actlist);
1053 while(s && s!=range->segmax) {
1054 assert(!s->changed);
1057 /* in check mode, go through the whole interval so we can test
1058 that all polygons where the edgestyle changed also have seg->changed=1 */
1059 s = actlist_leftmost(status->actlist);
1070 segment_t* left = actlist_left(status->actlist, s);
1071 windstate_t wind = left?left->wind:status->windrule->start(status->context);
1072 s->wind = status->windrule->add(status->context, wind, s->fs, s->dir, s->polygon_nr);
1073 edgestyle_t*fs_old = s->fs_out;
1074 s->fs_out = status->windrule->diff(&wind, &s->wind);
1077 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",
1078 fs_old?"draw":"omit", s->fs_out?"draw":"omit",
1079 fs_old!=s->fs_out?"CHANGED":"");
1081 assert(!(!s->changed && fs_old!=s->fs_out));
1092 /* we need to handle horizontal lines in order to add points to segments
1093 we otherwise would miss during the windrule re-evaluation */
1094 static void intersect_with_horizontal(status_t*status, segment_t*h)
1096 segment_t* left = actlist_find(status->actlist, h->a, h->a);
1097 segment_t* right = actlist_find(status->actlist, h->b, h->b);
1099 /* h->a.x is not strictly necessary, as it's also done by the event */
1100 xrow_add(status->xrow, h->a.x);
1101 xrow_add(status->xrow, h->b.x);
1108 left = actlist_right(status->actlist, left); //first seg to the right of h->a
1109 right = right->right; //first seg to the right of h->b
1110 segment_t* s = left;
1115 int32_t x = XPOS_INT(s, status->y);
1116 point_t p = {x, status->y};
1118 fprintf(stderr, "...intersecting with [%d] (%.2f,%.2f) -> (%.2f,%.2f) at (%.2f,%.2f)\n",
1120 s->a.x * status->gridsize, s->a.y * status->gridsize,
1121 s->b.x * status->gridsize, s->b.y * status->gridsize,
1122 x * status->gridsize, status->y * status->gridsize
1125 assert(x >= h->a.x);
1126 assert(x <= h->b.x);
1127 assert(s->delta.x > 0 && x >= s->a.x || s->delta.x <= 0 && x <= s->a.x);
1128 assert(s->delta.x > 0 && x <= s->b.x || s->delta.x <= 0 && x >= s->b.x);
1129 xrow_add(status->xrow, x);
1136 /* while, for a scanline, we need both starting as well as ending segments in order
1137 to *reconstruct* horizontal lines, we only need one or the other to *process*
1138 horizontal lines from the input data.
1140 So horizontal lines are processed twice: first they create hotpixels by intersecting
1141 all segments on the scanline (EVENT_HORIZTONAL). Secondly, they are processed for
1142 their actual content. The second also happens for all segments that received more than
1143 one point in this scanline.
1145 void horiz_reset(horizdata_t*horiz)
1150 void horiz_destroy(horizdata_t*horiz)
1152 if(horiz->data) rfx_free(horiz->data);
1156 static int compare_horizontals(const void *_h1, const void *_h2)
1158 horizontal_t*h1 = (horizontal_t*)_h1;
1159 horizontal_t*h2 = (horizontal_t*)_h2;
1160 return h1->x1 - h2->x1;
1163 static void process_horizontals(status_t*status)
1165 horizdata_t*horiz = &status->horiz;
1166 qsort(horiz->data, horiz->num, sizeof(horizontal_t), compare_horizontals);
1169 for(t=0;t<horiz->num;t++) {
1170 horizontal_t*h = &horiz->data[t];
1172 fprintf(stderr, "horizontal (y=%.2f): %.2f -> %.2f dir=%s fs=%p\n",
1173 h->y * status->gridsize,
1174 h->x1 * status->gridsize,
1175 h->x2 * status->gridsize,
1176 h->dir==DIR_UP?"up":"down", h->fs);
1178 assert(h->y == status->y);
1179 assert(xrow_contains(status->xrow, h->x1));
1180 assert(xrow_contains(status->xrow, h->x2));
1182 int pos = xrow_find(status->xrow, h->x1);
1184 assert(pos <= status->xrow->num);
1185 assert(pos == status->xrow->num || status->xrow->x[pos] > x);
1188 int next_x = pos < status->xrow->num ? status->xrow->x[pos] : h->x2;
1193 point_t p1 = {x,h->y};
1194 point_t p2 = {next_x,h->y};
1195 segment_t* left = actlist_find(status->actlist, p1, p2);
1196 assert(!left || left->fs_out_ok);
1198 fprintf(stderr, " fragment %.2f..%.2f edge=%08x\n",
1199 x * status->gridsize,
1200 next_x * status->gridsize,
1203 fprintf(stderr, " segment [%d] (%.2f,%.2f -> %.2f,%2f, at %.2f,%.2f) is to the left\n",
1205 left->a.x * status->gridsize,
1206 left->a.y * status->gridsize,
1207 left->b.x * status->gridsize,
1208 left->b.y * status->gridsize,
1209 left->pos.x * status->gridsize,
1210 left->pos.y * status->gridsize
1212 /* this segment might be a distance away from the left point
1213 of the horizontal line if the horizontal line belongs to a stroke
1214 with segments that just ended (so this horizontal line appears to
1215 be "floating in space" from our current point of view)
1216 assert(left->pos.y == h->y && left->pos.x == h->x1);
1220 windstate_t below = left?left->wind:status->windrule->start(status->context);
1221 windstate_t above = status->windrule->add(status->context, below, h->fs, h->dir, h->polygon_nr);
1222 edgestyle_t*fs = status->windrule->diff(&above, &below);
1224 segment_dir_t dir = above.is_filled?DIR_DOWN:DIR_UP;
1226 //append_stroke(status, p1, p2, DIR_INVERT(h->dir), fs);
1227 append_stroke(status, p1, p2, dir, fs);
1230 fprintf(stderr, " ...%s (below: (wind_nr=%d, filled=%d), above: (wind_nr=%d, filled=%d) %s\n",
1231 fs?"storing":"ignoring",
1232 below.wind_nr, below.is_filled,
1233 above.wind_nr, above.is_filled,
1234 dir==DIR_UP?"up":"down");
1241 static void store_horizontal(status_t*status, point_t p1, point_t p2, edgestyle_t*fs, segment_dir_t dir, int polygon_nr)
1243 assert(p1.y == p2.y);
1244 assert(p1.x != p2.x); // TODO: can this happen?
1247 dir = DIR_INVERT(dir);
1254 if(status->horiz.size == status->horiz.num) {
1255 if(!status->horiz.size)
1256 status->horiz.size = 16;
1257 status->horiz.size *= 2;
1258 status->horiz.data = rfx_realloc(status->horiz.data, sizeof(status->horiz.data[0])*status->horiz.size);
1260 horizontal_t*h = &status->horiz.data[status->horiz.num++];
1266 h->polygon_nr = polygon_nr;
1270 static void event_apply(status_t*status, event_t*e)
1273 event_dump(status, e);
1277 case EVENT_HORIZONTAL: {
1278 segment_t*s = e->s1;
1279 intersect_with_horizontal(status, s);
1280 store_horizontal(status, s->a, s->b, s->fs, s->dir, s->polygon_nr);
1281 advance_stroke(&status->queue, 0, s->stroke, s->polygon_nr, s->stroke_pos, status->gridsize);
1282 segment_destroy(s);e->s1=0;
1286 //delete segment from list
1287 segment_t*s = e->s1;
1289 dict_del(status->intersecting_segs, s);
1290 dict_del(status->segs_with_point, s);
1291 assert(!dict_contains(status->intersecting_segs, s));
1292 assert(!dict_contains(status->segs_with_point, s));
1294 segment_t*left = s->left;
1295 segment_t*right = s->right;
1296 actlist_delete(status->actlist, s);
1298 schedule_crossing(status, left, right);
1300 /* schedule segment for xrow handling */
1301 s->left = 0; s->right = status->ending_segments;
1302 status->ending_segments = s;
1303 advance_stroke(&status->queue, 0, s->stroke, s->polygon_nr, s->stroke_pos, status->gridsize);
1307 //insert segment into list
1308 segment_t*s = e->s1;
1309 assert(e->p.x == s->a.x && e->p.y == s->a.y);
1310 actlist_insert(status->actlist, s->a, s->b, s);
1311 segment_t*left = s->left;
1312 segment_t*right = s->right;
1314 schedule_crossing(status, left, s);
1316 schedule_crossing(status, s, right);
1317 schedule_endpoint(status, s);
1321 // exchange two segments
1322 if(e->s1->right == e->s2) {
1323 assert(e->s2->left == e->s1);
1324 exchange_two(status, e);
1326 assert(e->s2->left != e->s1);
1328 fprintf(stderr, "Ignore this crossing ([%d] not next to [%d])\n", e->s1->nr, e->s2->nr);
1330 #ifndef DONT_REMEMBER_CROSSINGS
1331 /* ignore this crossing for now (there are some line segments in between).
1332 it'll get rescheduled as soon as the "obstacles" are gone */
1333 char del1 = dict_del(&e->s1->scheduled_crossings, (void*)(ptroff_t)e->s2->nr);
1334 char del2 = dict_del(&e->s2->scheduled_crossings, (void*)(ptroff_t)e->s1->nr);
1335 assert(del1 && del2);
1341 #ifndef DONT_REMEMBER_CROSSINGS
1342 assert(dict_contains(status->seen_crossings, &pair));
1343 dict_del(status->seen_crossings, &pair);
1352 static void check_status(status_t*status)
1354 DICT_ITERATE_KEY(status->intersecting_segs, segment_t*, s) {
1355 if((s->pos.x != s->b.x ||
1356 s->pos.y != s->b.y) &&
1357 !dict_contains(status->segs_with_point, s)) {
1358 fprintf(stderr, "Error: segment [%d] (%sslope) intersects in scanline %d, but it didn't receive a point\n",
1360 s->delta.x<0?"-":"+",
1368 gfxpoly_t* gfxpoly_process(gfxpoly_t*poly1, gfxpoly_t*poly2, windrule_t*windrule, windcontext_t*context)
1370 current_polygon = poly1;
1373 memset(&status, 0, sizeof(status_t));
1374 status.gridsize = poly1->gridsize;
1376 gfxpoly_dump(poly1);
1378 queue_init(&status.queue);
1379 gfxpoly_enqueue(poly1, &status.queue, 0, /*polygon nr*/0);
1381 assert(poly1->gridsize == poly2->gridsize);
1382 gfxpoly_enqueue(poly2, &status.queue, 0, /*polygon nr*/1);
1385 status.windrule = windrule;
1386 status.context = context;
1387 status.actlist = actlist_new();
1390 status.seen_crossings = dict_new2(&point_type);
1391 int32_t lasty=-0x80000000;
1394 status.xrow = xrow_new();
1396 event_t*e = queue_get(&status.queue);
1401 assert(status.y>=lasty);
1403 status.intersecting_segs = dict_new2(&ptr_type);
1404 status.segs_with_point = dict_new2(&ptr_type);
1408 fprintf(stderr, "----------------------------------- %.2f\n", status.y * status.gridsize);
1409 actlist_dump(status.actlist, status.y-1, status.gridsize);
1412 actlist_verify(status.actlist, status.y-1);
1414 xrow_reset(status.xrow);
1415 horiz_reset(&status.horiz);
1418 xrow_add(status.xrow, e->p.x);
1419 event_apply(&status, e);
1421 e = queue_get(&status.queue);
1422 } while(e && status.y == e->p.y);
1424 xrow_sort(status.xrow);
1426 memset(&range, 0, sizeof(range));
1428 actlist_dump(status.actlist, status.y, status.gridsize);
1430 xrow_dump(status.xrow, status.gridsize);
1431 add_points_to_positively_sloped_segments(&status, status.y, &range);
1432 add_points_to_negatively_sloped_segments(&status, status.y, &range);
1433 add_points_to_ending_segments(&status, status.y);
1435 recalculate_windings(&status, &range);
1436 process_horizontals(&status);
1438 check_status(&status);
1439 dict_destroy(status.intersecting_segs);
1440 dict_destroy(status.segs_with_point);
1444 dict_destroy(status.seen_crossings);
1446 actlist_destroy(status.actlist);
1447 queue_destroy(&status.queue);
1448 horiz_destroy(&status.horiz);
1449 xrow_destroy(status.xrow);
1451 gfxpoly_t*p = (gfxpoly_t*)malloc(sizeof(gfxpoly_t));
1452 p->gridsize = poly1->gridsize;
1453 p->strokes = status.strokes;
1456 /* we only add segments with non-empty edgestyles to strokes in
1457 recalculate_windings, but better safe than sorry */
1458 gfxpolystroke_t*stroke = p->strokes;
1461 stroke = stroke->next;
1467 static windcontext_t twopolygons = {2};
1468 gfxpoly_t* gfxpoly_intersect(gfxpoly_t*p1, gfxpoly_t*p2)
1470 return gfxpoly_process(p1, p2, &windrule_intersect, &twopolygons);
1472 gfxpoly_t* gfxpoly_union(gfxpoly_t*p1, gfxpoly_t*p2)
1474 return gfxpoly_process(p1, p2, &windrule_union, &twopolygons);