13 static gfxpoly_t
*current_polygon
= 0;
14 void gfxpoly_fail(char*expr
, char*file
, int line
, const char*function
)
16 if(!current_polygon
) {
17 fprintf(stderr
, "assert(%s) failed in %s in line %d: %s\n", expr
, file
, line
, function
);
21 void*md5
= init_md5();
23 edge_t
* s
= current_polygon
->edges
;
25 update_md5(md5
, (unsigned char*)&s
->a
.x
, sizeof(s
->a
.x
));
26 update_md5(md5
, (unsigned char*)&s
->a
.y
, sizeof(s
->a
.y
));
27 update_md5(md5
, (unsigned char*)&s
->b
.x
, sizeof(s
->b
.x
));
28 update_md5(md5
, (unsigned char*)&s
->b
.y
, sizeof(s
->b
.y
));
32 char filename
[32+4+1];
34 sprintf(filename
, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x.ps",
35 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]);
37 fprintf(stderr
, "assert(%s) failed in %s in line %d: %s\n", expr
, file
, line
, function
);
38 fprintf(stderr
, "I'm saving a debug file \"%s\" to the current directory.\n", filename
);
40 gfxpoly_save(current_polygon
, filename
);
44 static char point_equals(const void*o1
, const void*o2
)
46 const point_t
*p1
= o1
;
47 const point_t
*p2
= o2
;
48 return p1
->x
== p2
->x
&& p1
->y
== p2
->y
;
50 static unsigned int point_hash(const void*o
)
55 static void* point_dup(const void*o
)
58 point_t
*n
= malloc(sizeof(point_t
));
63 static void point_free(void*o
)
70 static type_t point_type
= {
77 typedef struct _status
{
84 windcontext_t
*context
;
85 segment_t
*ending_segments
;
87 dict_t
*seen_crossings
; //list of crossing we saw so far
88 dict_t
*intersecting_segs
; //list of segments intersecting in this scanline
89 dict_t
*segs_with_point
; //lists of segments that received a point in this scanline
93 /* compare_events_simple differs from compare_events in that it schedules
94 events from left to right regardless of type. It's only used in horizontal
95 processing, in order to get an x-wise sorting of the current scanline */
96 static int compare_events_simple(const void*_a
,const void*_b
)
98 event_t
* a
= (event_t
*)_a
;
99 event_t
* b
= (event_t
*)_b
;
100 int d
= b
->p
.y
- a
->p
.y
;
107 static int compare_events(const void*_a
,const void*_b
)
109 event_t
* a
= (event_t
*)_a
;
110 event_t
* b
= (event_t
*)_b
;
111 int d
= b
->p
.y
- a
->p
.y
;
113 /* we need to schedule end after intersect (so that a segment about
114 to end has a chance to tear up a few other segs first) and start
115 events after end (in order not to confuse the intersection check, which
116 assumes there's an actual y overlap between active segments, and
117 because ending segments in the active list make it difficult to insert
118 starting segments at the right position)).
119 Horizontal lines come last, because the only purpose
120 they have is to create snapping coordinates for the segments (still)
121 existing in this scanline.
123 d
= b
->type
- a
->type
;
127 /* I don't see any reason why we would need to order by x- at least as long
128 as we do horizontal lines in a seperate pass */
129 //d = b->p.x - a->p.x;
133 gfxpoly_t
* gfxpoly_new(double gridsize
)
135 gfxpoly_t
*p
= (gfxpoly_t
*)rfx_calloc(sizeof(gfxpoly_t
));
136 p
->gridsize
= gridsize
;
139 void gfxpoly_destroy(gfxpoly_t
*poly
)
141 edge_t
* s
= poly
->edges
;
143 edge_t
*next
= s
->next
;
149 int gfxpoly_size(gfxpoly_t
*poly
)
151 edge_t
* s
= poly
->edges
;
159 char gfxpoly_check(gfxpoly_t
*poly
)
161 edge_t
* s
= poly
->edges
;
162 dict_t
*d
= dict_new2(&point_type
);
164 if(!dict_contains(d
, &s
->a
)) {
165 dict_put(d
, &s
->a
, (void*)(ptroff_t
)1);
167 int count
= (ptroff_t
)dict_lookup(d
, &s
->a
);
170 dict_put(d
, &s
->a
, (void*)(ptroff_t
)count
);
172 if(!dict_contains(d
, &s
->b
)) {
173 dict_put(d
, &s
->b
, (void*)(ptroff_t
)1);
175 int count
= (ptroff_t
)dict_lookup(d
, &s
->b
);
178 dict_put(d
, &s
->b
, (void*)(ptroff_t
)count
);
182 DICT_ITERATE_ITEMS(d
, point_t
*, p
, void*, c
) {
183 int count
= (ptroff_t
)c
;
185 fprintf(stderr
, "Point (%f,%f) occurs %d times\n", p
->x
*poly
->gridsize
, p
->y
*poly
->gridsize
, count
);
194 void gfxpoly_dump(gfxpoly_t
*poly
)
196 edge_t
* s
= poly
->edges
;
197 double g
= poly
->gridsize
;
198 fprintf(stderr
, "polyon %08x (gridsize: %f)\n", poly
, poly
->gridsize
);
200 fprintf(stderr
, "(%f,%f) -> (%f,%f)\n", s
->a
.x
*g
, s
->a
.y
*g
, s
->b
.x
*g
, s
->b
.y
*g
);
205 gfxpoly_t
* gfxpoly_save(gfxpoly_t
*poly
, const char*filename
)
207 FILE*fi
= fopen(filename
, "wb");
208 fprintf(fi
, "%% gridsize %f\n", poly
->gridsize
);
209 fprintf(fi
, "%% begin\n");
210 edge_t
* s
= poly
->edges
;
212 fprintf(fi
, "%g setgray\n", s
->b
.y
< s
->a
.y
? 0.7 : 0);
213 fprintf(fi
, "%d %d moveto\n", s
->a
.x
, s
->a
.y
);
214 fprintf(fi
, "%d %d lineto\n", s
->b
.x
, s
->b
.y
);
215 fprintf(fi
, "stroke\n");
218 fprintf(fi
, "showpage\n");
222 inline static event_t
event_new()
225 memset(&e
, 0, sizeof(e
));
229 static void event_dump(event_t
*e
)
231 if(e
->type
== EVENT_HORIZONTAL
) {
232 fprintf(stderr
, "Horizontal [%d] (%d,%d) -> (%d,%d)\n", e
->s1
->nr
, e
->s1
->a
.x
, e
->s1
->a
.y
, e
->s1
->b
.x
, e
->s1
->b
.y
);
233 } else if(e
->type
== EVENT_START
) {
234 fprintf(stderr
, "event: segment [%d] starts at (%d,%d)\n", e
->s1
->nr
, e
->p
.x
, e
->p
.y
);
235 } else if(e
->type
== EVENT_END
) {
236 fprintf(stderr
, "event: segment [%d] ends at (%d,%d)\n", e
->s1
->nr
, e
->p
.x
, e
->p
.y
);
237 } else if(e
->type
== EVENT_CROSS
) {
238 fprintf(stderr
, "event: segment [%d] and [%d] intersect at (%d,%d)\n", e
->s1
->nr
, e
->s2
->nr
, e
->p
.x
, e
->p
.y
);
239 } else if(e
->type
== EVENT_CORNER
) {
240 fprintf(stderr
, "event: segment [%d] ends, segment [%d] starts, at (%d,%d)\n", e
->s1
->nr
, e
->s2
->nr
, e
->p
.x
, e
->p
.y
);
246 static inline max32(int32_t v1
, int32_t v2
) {return v1
>v2
?v1
:v2
;}
247 static inline min32(int32_t v1
, int32_t v2
) {return v1
<v2
?v1
:v2
;}
249 static void segment_dump(segment_t
*s
)
251 fprintf(stderr
, "[%d] (%d,%d)->(%d,%d) ", s
->nr
, s
->a
.x
, s
->a
.y
, s
->b
.x
, s
->b
.y
);
252 fprintf(stderr
, " dx:%d dy:%d k:%f dx/dy=%f\n", s
->delta
.x
, s
->delta
.y
, s
->k
,
253 (double)s
->delta
.x
/ s
->delta
.y
);
256 static void segment_init(segment_t
*s
, int32_t x1
, int32_t y1
, int32_t x2
, int32_t y2
, int polygon_nr
)
261 int32_t x
= x1
;x1
=x2
;x2
=x
;
262 int32_t y
= y1
;y1
=y2
;y2
=y
;
265 /* up/down for horizontal segments is handled by "rotating"
266 them 90° anticlockwise in screen coordinates (tilt your head to
271 int32_t x
= x1
;x1
=x2
;x2
=x
;
272 int32_t y
= y1
;y1
=y2
;y2
=y
;
279 s
->k
= (double)x1
*y2
-(double)x2
*y1
;
280 s
->left
= s
->right
= 0;
283 s
->minx
= min32(x1
,x2
);
284 s
->maxx
= max32(x1
,x2
);
287 s
->polygon_nr
= polygon_nr
;
290 static int segment_count
=0;
291 s
->nr
= segment_count
++;
295 assert(LINE_EQ(s
->a
, s
) == 0);
296 assert(LINE_EQ(s
->b
, s
) == 0);
298 /* check that all signs are in order:
306 p
.x
= min32(s
->a
.x
, s
->b
.x
);
307 assert(LINE_EQ(p
, s
) <= 0);
308 p
.x
= max32(s
->a
.x
, s
->b
.x
);
309 assert(LINE_EQ(p
, s
) >= 0);
312 dict_init2(&s
->scheduled_crossings
, &ptr_type
, 0);
315 static segment_t
* segment_new(int32_t x1
, int32_t y1
, int32_t x2
, int32_t y2
, int polygon_nr
)
317 segment_t
*s
= (segment_t
*)rfx_calloc(sizeof(segment_t
));
318 segment_init(s
, x1
, y1
, x2
, y2
, polygon_nr
);
322 static void segment_destroy(segment_t
*s
)
324 dict_clear(&s
->scheduled_crossings
);
328 static void gfxpoly_enqueue(edge_t
*list
, heap_t
*queue
, int polygon_nr
)
331 for(l
=list
;l
;l
=l
->next
) {
332 if(l
->a
.x
== l
->b
.x
&&
334 fprintf(stderr
, "Warning: intersector input contains zero-length segments\n");
337 segment_t
*s
= segment_new(l
->a
.x
, l
->a
.y
, l
->b
.x
, l
->b
.y
, polygon_nr
);
341 fprintf(stderr
, "[%d] (%d,%d) -> (%d,%d) %s\n",
342 s
->nr
, s
->a
.x
, s
->a
.y
, s
->b
.x
, s
->b
.y
,
343 s
->dir
==DIR_UP
?"up":"down");
345 event_t e
= event_new();
346 e
.type
= s
->delta
.y
? EVENT_START
: EVENT_HORIZONTAL
;
354 static void schedule_endpoint(status_t
*status
, segment_t
*s
)
356 // schedule end point of segment
357 assert(s
->b
.y
> status
->y
);
363 heap_put(status
->queue
, &e
);
366 static void schedule_crossing(status_t
*status
, segment_t
*s1
, segment_t
*s2
)
368 /* the code that's required (and the checks you can perform) before
369 it can be said with 100% certainty that we indeed have a valid crossing
370 amazes me every time. -mk */
373 assert(s1
->right
== s2
);
374 assert(s2
->left
== s1
);
375 int32_t miny1
= min32(s1
->a
.y
,s1
->b
.y
);
376 int32_t maxy1
= max32(s1
->a
.y
,s1
->b
.y
);
377 int32_t miny2
= min32(s2
->a
.y
,s2
->b
.y
);
378 int32_t maxy2
= max32(s2
->a
.y
,s2
->b
.y
);
379 int32_t minx1
= min32(s1
->a
.x
,s1
->b
.x
);
380 int32_t minx2
= min32(s2
->a
.x
,s2
->b
.x
);
381 int32_t maxx1
= max32(s1
->a
.x
,s1
->b
.x
);
382 int32_t maxx2
= max32(s2
->a
.x
,s2
->b
.x
);
383 /* check that precomputation is sane */
384 assert(minx1
== s1
->minx
&& minx2
== s2
->minx
);
385 assert(maxx1
== s1
->maxx
&& maxx2
== s2
->maxx
);
386 /* both segments are active, so this can't happen */
387 assert(!(maxy1
<= miny2
|| maxy2
<= miny1
));
388 /* we know that right now, s2 is to the right of s1, so there's
389 no way the complete bounding box of s1 is to the right of s1 */
390 assert(!(s1
->minx
> s2
->maxx
));
391 assert(s1
->minx
!= s2
->maxx
|| (!s1
->delta
.x
&& !s2
->delta
.x
));
394 if(s1
->maxx
<= s2
->minx
) {
395 /* bounding boxes don't intersect */
399 if(dict_contains(&s1
->scheduled_crossings
, s2
)) {
400 /* FIXME: this whole segment hashing thing is really slow */
401 //fprintf(stderr, "Encountered crossing between [%d] and [%d] twice\n", s1->nr, s2->nr);
402 return; // we already know about this one
405 double det
= (double)s1
->delta
.x
*s2
->delta
.y
- (double)s1
->delta
.y
*s2
->delta
.x
;
408 // lines are exactly on top of each other (ignored)
410 fprintf(stderr
, "Notice: segments [%d] and [%d] are exactly on top of each other\n", s1
->nr
, s2
->nr
);
414 /* lines are parallel */
418 double asign2
= LINE_EQ(s1
->a
, s2
);
419 double bsign2
= LINE_EQ(s1
->b
, s2
);
420 if(asign2
<0 && bsign2
<0) {
421 // segment1 is completely to the left of segment2
424 if(asign2
>0 && bsign2
>0) {
425 // segment2 is completely to the left of segment1
429 // segment1 touches segment2 in a single point (ignored)
431 fprintf(stderr
, "Notice: segment [%d]'s start point touches segment [%d]\n", s1
->nr
, s2
->nr
);
436 // segment1 touches segment2 in a single point (ignored)
438 fprintf(stderr
, "Notice: segment [%d]'s end point touches segment [%d]\n", s1
->nr
, s2
->nr
);
442 double asign1
= LINE_EQ(s2
->a
, s1
);
443 double bsign1
= LINE_EQ(s2
->b
, s1
);
444 if(asign1
<0 && bsign1
<0) {
445 // segment1 is completely to the left of segment2
448 if(asign1
>0 && bsign1
>0) {
449 // segment2 is completely to the left of segment1
453 // segment2 touches segment1 in a single point (ignored)
455 fprintf(stderr
, "Notice: segment [%d]'s start point touches segment [%d]\n", s2
->nr
, s1
->nr
);
460 // segment2 touches segment1 in a single point (ignored)
462 fprintf(stderr
, "Notice: segment [%d]'s end point touches segment [%d]\n", s2
->nr
, s1
->nr
);
467 /* TODO: should we precompute these? */
468 double la
= (double)s1
->a
.x
*(double)s1
->b
.y
- (double)s1
->a
.y
*(double)s1
->b
.x
;
469 double lb
= (double)s2
->a
.x
*(double)s2
->b
.y
- (double)s2
->a
.y
*(double)s2
->b
.x
;
472 p
.x
= (int32_t)ceil((-la
*s2
->delta
.x
+ lb
*s1
->delta
.x
) / det
);
473 p
.y
= (int32_t)ceil((+lb
*s1
->delta
.y
- la
*s2
->delta
.y
) / det
);
475 assert(p
.y
>= status
->y
);
477 assert(p
.x
>= s1
->minx
&& p
.x
<= s1
->maxx
);
478 assert(p
.x
>= s2
->minx
&& p
.x
<= s2
->maxx
);
483 assert(!dict_contains(status
->seen_crossings
, &pair
));
484 dict_put(status
->seen_crossings
, &pair
, 0);
487 fprintf(stderr
, "schedule crossing between [%d] and [%d] at (%d,%d)\n", s1
->nr
, s2
->nr
, p
.x
, p
.y
);
490 /* we insert into each other's intersection history because these segments might switch
491 places and we still want to look them up quickly after they did */
492 dict_put(&s1
->scheduled_crossings
, s2
, 0);
493 dict_put(&s2
->scheduled_crossings
, s1
, 0);
495 event_t e
= event_new();
496 e
.type
= EVENT_CROSS
;
500 heap_put(status
->queue
, &e
);
504 static void exchange_two(status_t
*status
, event_t
*e
)
506 //exchange two segments in list
507 segment_t
*s1
= e
->s1
;
508 segment_t
*s2
= e
->s2
;
510 if(!dict_contains(status
->intersecting_segs
, s1
))
511 dict_put(status
->intersecting_segs
, s1
, 0);
512 if(!dict_contains(status
->intersecting_segs
, s2
))
513 dict_put(status
->intersecting_segs
, s2
, 0);
515 assert(s2
->left
== s1
);
516 assert(s1
->right
== s2
);
517 actlist_swap(status
->actlist
, s1
, s2
);
518 assert(s2
->right
== s1
);
519 assert(s1
->left
== s2
);
520 segment_t
*left
= s2
->left
;
521 segment_t
*right
= s1
->right
;
523 schedule_crossing(status
, left
, s2
);
525 schedule_crossing(status
, s1
, right
);
528 typedef struct _box
{
529 point_t left1
, left2
, right1
, right2
;
531 static inline box_t
box_new(int32_t x
, int32_t y
)
534 box
.right1
.x
= box
.right2
.x
= x
;
535 box
.left1
.x
= box
.left2
.x
= x
-1;
536 box
.left1
.y
= box
.right1
.y
= y
-1;
537 box
.left2
.y
= box
.right2
.y
= y
;
542 static void insert_point_into_segment(status_t
*status
, segment_t
*s
, point_t p
)
544 assert(s
->pos
.x
!= p
.x
|| s
->pos
.y
!= p
.y
);
547 if(!dict_contains(status
->segs_with_point
, s
))
548 dict_put(status
->segs_with_point
, s
, 0);
549 assert(s
->fs_out_ok
);
554 fprintf(stderr
, "[%d] receives next point (%d,%d)->(%d,%d) (drawing)\n", s
->nr
,
555 s
->pos
.x
, s
->pos
.y
, p
.x
, p
.y
);
557 // omit horizontal lines
558 if(s
->pos
.y
!= p
.y
) {
559 edge_t
*e
= rfx_calloc(sizeof(edge_t
));
565 assert(e
->a
.y
!= e
->b
.y
);
566 e
->next
= status
->output
;
571 fprintf(stderr
, "[%d] receives next point (%d,%d) (omitting)\n", s
->nr
, p
.x
, p
.y
);
577 typedef struct _segrange
{
584 static void segrange_adjust_endpoints(segrange_t
*range
, int32_t y
)
586 #define XPOS_EQ(s1,s2,ypos) (XPOS((s1),(ypos))==XPOS((s2),(ypos)))
587 segment_t
*min
= range
->segmin
;
588 segment_t
*max
= range
->segmax
;
590 /* we need this because if two segments intersect exactly on
591 the scanline, segrange_test_segment_{min,max} can't tell which
592 one is smaller/larger */
593 if(min
) while(min
->left
&& XPOS_EQ(min
, min
->left
, y
)) {
596 if(max
) while(max
->right
&& XPOS_EQ(max
, max
->right
, y
)) {
602 static void segrange_test_segment_min(segrange_t
*range
, segment_t
*seg
, int32_t y
)
605 /* we need to calculate the xpos anew (and can't use start coordinate or
606 intersection coordinate), because we need the xpos exactly at the end of
609 double x
= XPOS(seg
, y
);
610 if(!range
->segmin
|| x
<range
->xmin
) {
615 static void segrange_test_segment_max(segrange_t
*range
, segment_t
*seg
, int32_t y
)
618 double x
= XPOS(seg
, y
);
619 if(!range
->segmax
|| x
>range
->xmax
) {
634 static void add_points_to_positively_sloped_segments(status_t
*status
, int32_t y
, segrange_t
*range
)
636 segment_t
*first
=0, *last
= 0;
638 for(t
=0;t
<status
->xrow
->num
;t
++) {
639 box_t box
= box_new(status
->xrow
->x
[t
], y
);
640 segment_t
*seg
= actlist_find(status
->actlist
, box
.left2
, box
.left2
);
642 seg
= actlist_right(status
->actlist
, seg
);
645 // this segment started in this scanline, ignore it
646 seg
->changed
= 1;last
= seg
;if(!first
) {first
=seg
;}
647 } else if(seg
->delta
.x
<= 0) {
648 // ignore segment w/ negative slope
650 last
= seg
;if(!first
) {first
=seg
;}
651 double d1
= LINE_EQ(box
.right1
, seg
);
652 double d2
= LINE_EQ(box
.right2
, seg
);
655 insert_point_into_segment(status
, seg
, box
.right2
);
657 /* we unfortunately can't break here- the active list is sorted according
658 to the *bottom* of the scanline. hence pretty much everything that's still
659 coming might reach into our box */
666 segrange_test_segment_min(range
, first
, y
);
667 segrange_test_segment_max(range
, last
, y
);
677 static void add_points_to_negatively_sloped_segments(status_t
*status
, int32_t y
, segrange_t
*range
)
679 segment_t
*first
=0, *last
= 0;
681 for(t
=status
->xrow
->num
-1;t
>=0;t
--) {
682 box_t box
= box_new(status
->xrow
->x
[t
], y
);
683 segment_t
*seg
= actlist_find(status
->actlist
, box
.right2
, box
.right2
);
687 // this segment started in this scanline, ignore it
688 seg
->changed
= 1;last
= seg
;if(!first
) {first
=seg
;}
689 } else if(seg
->delta
.x
> 0) {
690 // ignore segment w/ positive slope
692 last
= seg
;if(!first
) {first
=seg
;}
693 double d1
= LINE_EQ(box
.left1
, seg
);
694 double d2
= LINE_EQ(box
.left2
, seg
);
697 insert_point_into_segment(status
, seg
, box
.right2
);
705 segrange_test_segment_min(range
, last
, y
);
706 segrange_test_segment_max(range
, first
, y
);
709 /* segments ending in the current scanline need xrow treatment like everything else.
710 (consider an intersection taking place just above a nearly horizontal segment
711 ending on the current scanline- the intersection would snap down *below* the
712 ending segment if we don't add the intersection point to the latter right away)
713 we need to treat ending segments seperately, however. we have to delete them from
714 the active list right away to make room for intersect operations (which might
715 still be in the current scanline- consider two 45° polygons and a vertical polygon
716 intersecting on an integer coordinate). but once they're no longer in the active list,
717 we can't use the add_points_to_*_sloped_segments() functions anymore, and re-adding
718 them to the active list just for point snapping would be overkill.
719 (One other option to consider, however, would be to create a new active list only
722 static void add_points_to_ending_segments(status_t
*status
, int32_t y
)
724 segment_t
*seg
= status
->ending_segments
;
726 segment_t
*next
= seg
->right
;seg
->right
=0;
728 assert(seg
->b
.y
== status
->y
);
730 if(status
->xrow
->num
== 1) {
732 assert(seg
->b
.x
== status
->xrow
->x
[0]);
733 point_t p
= {status
->xrow
->x
[0], y
};
734 insert_point_into_segment(status
, seg
, p
);
737 int start
=0,end
=status
->xrow
->num
,dir
=1;
738 if(seg
->delta
.x
< 0) {
739 start
= status
->xrow
->num
-1;
742 for(t
=start
;t
!=end
;t
+=dir
) {
743 box_t box
= box_new(status
->xrow
->x
[t
], y
);
744 double d0
= LINE_EQ(box
.left1
, seg
);
745 double d1
= LINE_EQ(box
.left2
, seg
);
746 double d2
= LINE_EQ(box
.right1
, seg
);
747 double d3
= LINE_EQ(box
.right2
, seg
);
748 if(!(d0
>=0 && d1
>=0 && d2
>=0 && d3
>0 ||
749 d0
<=0 && d1
<=0 && d2
<=0 && d3
<0)) {
750 insert_point_into_segment(status
, seg
, box
.right2
);
756 /* we *need* to find a point to insert. the segment's own end point
757 is in that list, for Pete's sake. */
761 // now that this is done, too, we can also finally free this segment
762 segment_destroy(seg
);
765 status
->ending_segments
= 0;
768 static void recalculate_windings(status_t
*status
, segrange_t
*range
)
771 fprintf(stderr
, "range: [%d]..[%d]\n", SEGNR(range
->segmin
), SEGNR(range
->segmax
));
773 segrange_adjust_endpoints(range
, status
->y
);
775 segment_t
*s
= range
->segmin
;
776 segment_t
*end
= range
->segmax
;
780 s
= actlist_leftmost(status
->actlist
);
782 fprintf(stderr
, "[%d]%d%s ", s
->nr
, s
->changed
,
783 s
== range
->segmin
?"S":(
784 s
== range
->segmax
?"E":""));
787 fprintf(stderr
, "\n");
791 /* test sanity: check that we don't have changed segments
792 outside of the given range */
793 s
= actlist_leftmost(status
->actlist
);
794 while(s
&& s
!=range
->segmin
) {
798 s
= actlist_rightmost(status
->actlist
);
799 while(s
&& s
!=range
->segmax
) {
803 /* in check mode, go through the whole interval so we can test
804 that all polygons where the fillstyle changed also have seg->changed=1 */
805 s
= actlist_leftmost(status
->actlist
);
816 segment_t
* left
= actlist_left(status
->actlist
, s
);
817 windstate_t wind
= left
?left
->wind
:status
->windrule
->start(status
->context
);
818 s
->wind
= status
->windrule
->add(status
->context
, wind
, s
->fs
, s
->dir
, s
->polygon_nr
);
819 fillstyle_t
*fs_old
= s
->fs_out
;
820 s
->fs_out
= status
->windrule
->diff(&wind
, &s
->wind
);
822 assert(!(!s
->changed
&& fs_old
!=s
->fs_out
));
829 fprintf(stderr
, "[%d] %s/%d/%s/%s %s\n", s
->nr
, s
->dir
==DIR_UP
?"up":"down", s
->wind
.wind_nr
, s
->wind
.is_filled
?"fill":"nofill", s
->fs_out
?"draw":"omit",
830 fs_old
!=s
->fs_out
?"CHANGED":"");
837 /* we need to handle horizontal lines in order to add points to segments
838 we otherwise would miss during the windrule re-evaluation */
839 static void intersect_with_horizontal(status_t
*status
, segment_t
*h
)
841 segment_t
* left
= actlist_find(status
->actlist
, h
->a
, h
->a
);
842 segment_t
* right
= actlist_find(status
->actlist
, h
->b
, h
->b
);
844 /* not strictly necessary, also done by the event */
845 xrow_add(status
->xrow
, h
->a
.x
);
853 left
= actlist_right(status
->actlist
, left
); //first seg to the right of h->a
854 right
= right
->right
; //first seg to the right of h->b
859 int32_t x
= XPOS_INT(s
, status
->y
);
861 fprintf(stderr
, "...into [%d] (%d,%d) -> (%d,%d) at (%d,%d)\n", s
->nr
,
869 assert(s
->delta
.x
> 0 && x
>= s
->a
.x
|| s
->delta
.x
<= 0 && x
<= s
->a
.x
);
870 assert(s
->delta
.x
> 0 && x
<= s
->b
.x
|| s
->delta
.x
<= 0 && x
>= s
->b
.x
);
871 xrow_add(status
->xrow
, x
);
877 static void event_apply(status_t
*status
, event_t
*e
)
880 case EVENT_HORIZONTAL
: {
884 intersect_with_horizontal(status
, e
->s1
);
885 segment_destroy(e
->s1
);e
->s1
=0;
889 //delete segment from list
895 dict_del(status
->intersecting_segs
, s
);
896 dict_del(status
->segs_with_point
, s
);
897 assert(!dict_contains(status
->intersecting_segs
, s
));
898 assert(!dict_contains(status
->segs_with_point
, s
));
900 segment_t
*left
= s
->left
;
901 segment_t
*right
= s
->right
;
902 actlist_delete(status
->actlist
, s
);
904 schedule_crossing(status
, left
, right
);
906 /* schedule segment for xrow handling */
907 s
->left
= 0; s
->right
= status
->ending_segments
;
908 status
->ending_segments
= s
;
912 //insert segment into list
917 assert(e
->p
.x
== s
->a
.x
&& e
->p
.y
== s
->a
.y
);
918 actlist_insert(status
->actlist
, s
->a
, s
->b
, s
);
919 segment_t
*left
= s
->left
;
920 segment_t
*right
= s
->right
;
922 schedule_crossing(status
, left
, s
);
924 schedule_crossing(status
, s
, right
);
925 schedule_endpoint(status
, e
->s1
);
929 // exchange two segments
933 if(e
->s1
->right
== e
->s2
) {
934 assert(e
->s2
->left
== e
->s1
);
935 exchange_two(status
, e
);
937 assert(e
->s2
->left
!= e
->s1
);
939 fprintf(stderr
, "Ignore this crossing ([%d] not next to [%d])\n", e
->s1
->nr
, e
->s2
->nr
);
941 /* ignore this crossing for now (there are some line segments in between).
942 it'll get rescheduled as soon as the "obstacles" are gone */
943 char del1
= dict_del(&e
->s1
->scheduled_crossings
, e
->s2
);
944 char del2
= dict_del(&e
->s2
->scheduled_crossings
, e
->s1
);
945 assert(del1
&& del2
);
950 assert(dict_contains(status
->seen_crossings
, &pair
));
951 dict_del(status
->seen_crossings
, &pair
);
959 static void check_status(status_t
*status
)
961 DICT_ITERATE_KEY(status
->intersecting_segs
, segment_t
*, s
) {
962 if((s
->pos
.x
!= s
->b
.x
||
963 s
->pos
.y
!= s
->b
.y
) &&
964 !dict_contains(status
->segs_with_point
, s
)) {
965 fprintf(stderr
, "Error: segment [%d] (%sslope) intersects in scanline %d, but it didn't receive a point\n",
967 s
->delta
.x
<0?"-":"+",
975 static void add_horizontals(gfxpoly_t
*poly
, windrule_t
*windrule
, windcontext_t
*context
)
978 |..| |...........| | |
979 |..| |...........| | |
980 |..+ + +..| +--+ +--+
981 |...........| |..| | |
982 |...........| |..| | |
986 fprintf(stderr
, "========================================================================\n");
988 heap_t
* queue
= heap_new(sizeof(event_t
), compare_events_simple
);
989 gfxpoly_enqueue(poly
->edges
, queue
, 0);
991 actlist_t
* actlist
= actlist_new();
993 event_t
*e
= heap_chopmax(queue
);
999 fprintf(stderr
, "----------------------------------- %d\n", y
);
1000 actlist_dump(actlist
, y
-1);
1003 actlist_verify(actlist
, y
-1);
1006 if(fill
&& x
!= e
->p
.x
) {
1008 fprintf(stderr
, "%d) draw horizontal line from %d to %d\n", y
, x
, e
->p
.x
);
1011 edge_t
*l
= malloc(sizeof(edge_t
));
1012 l
->a
.y
= l
->b
.y
= y
;
1013 /* we draw from low x to high x so that left/right fillstyles add up
1014 (because the horizontal line's fill style controls the area *below* the line)
1018 l
->next
= poly
->edges
;
1021 /* the output should always be intersection free polygons, so check this horizontal
1022 line isn't hacking through any segments in the active list */
1023 segment_t
* start
= actlist_find(actlist
, l
->b
, l
->b
);
1024 segment_t
* s
= actlist_find(actlist
, l
->a
, l
->a
);
1026 assert(s
->a
.y
== y
|| s
->b
.y
== y
);
1032 segment_t
*s
= e
->s1
;
1036 assert(e
->p
.x
== s
->a
.x
&& e
->p
.y
== s
->a
.y
);
1037 actlist_insert(actlist
, s
->a
, s
->b
, s
);
1043 heap_put(queue
, &e
);
1044 left
= actlist_left(actlist
, s
);
1048 left
= actlist_left(actlist
, s
);
1049 actlist_delete(actlist
, s
);
1056 fill
^= 1;//(before.is_filled != after.is_filled);
1058 fprintf(stderr
, "%d) event=%s[%d] left:[%d] x:%d\n",
1059 y
, e
->type
==EVENT_START
?"start":"end",
1065 if(e
->type
== EVENT_END
)
1069 e
= heap_chopmax(queue
);
1070 } while(e
&& y
== e
->p
.y
);
1072 assert(!fill
); // check that polygon is not bleeding
1074 actlist_destroy(actlist
);
1075 heap_destroy(queue
);
1078 gfxpoly_t
* gfxpoly_process(gfxpoly_t
*poly
, windrule_t
*windrule
, windcontext_t
*context
)
1080 current_polygon
= poly
;
1081 heap_t
* queue
= heap_new(sizeof(event_t
), compare_events
);
1083 gfxpoly_enqueue(poly
->edges
, queue
, /*polygon nr*/0);
1086 memset(&status
, 0, sizeof(status_t
));
1087 status
.queue
= queue
;
1088 status
.windrule
= windrule
;
1089 status
.context
= context
;
1090 status
.actlist
= actlist_new();
1092 status
.seen_crossings
= dict_new2(&point_type
);
1095 status
.xrow
= xrow_new();
1097 event_t
*e
= heap_chopmax(queue
);
1101 status
.intersecting_segs
= dict_new2(&ptr_type
);
1102 status
.segs_with_point
= dict_new2(&ptr_type
);
1106 fprintf(stderr
, "----------------------------------- %d\n", status
.y
);
1107 actlist_dump(status
.actlist
, status
.y
-1);
1110 actlist_verify(status
.actlist
, status
.y
-1);
1112 xrow_reset(status
.xrow
);
1114 xrow_add(status
.xrow
, e
->p
.x
);
1115 event_apply(&status
, e
);
1117 e
= heap_chopmax(queue
);
1118 } while(e
&& status
.y
== e
->p
.y
);
1120 xrow_sort(status
.xrow
);
1122 memset(&range
, 0, sizeof(range
));
1124 actlist_dump(status
.actlist
, status
.y
);
1126 add_points_to_positively_sloped_segments(&status
, status
.y
, &range
);
1127 add_points_to_negatively_sloped_segments(&status
, status
.y
, &range
);
1128 add_points_to_ending_segments(&status
, status
.y
);
1130 recalculate_windings(&status
, &range
);
1132 check_status(&status
);
1133 dict_destroy(status
.intersecting_segs
);
1134 dict_destroy(status
.segs_with_point
);
1138 dict_destroy(status
.seen_crossings
);
1140 actlist_destroy(status
.actlist
);
1141 heap_destroy(queue
);
1142 xrow_destroy(status
.xrow
);
1144 gfxpoly_t
*p
= gfxpoly_new(poly
->gridsize
);
1145 p
->edges
= status
.output
;
1147 add_horizontals(p
, &windrule_evenodd
, context
); // output is always even/odd