1 /* Libart_LGPL - library of basic graphic primitives
2 * Copyright (C) 2001 Raph Levien
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 /* This file contains a testbed implementation of the new intersection
27 #include "art_svp_intersect.h"
29 #include <math.h> /* for sqrt */
31 /* Sanitychecking verifies the main invariant on every priority queue
32 point. Do not use in production, as it slows things down way too
36 /* This can be used in production, to prevent hangs. Eventually, it
37 should not be necessary. */
38 #define CHEAP_SANITYCHECK
42 #define noABORT_ON_ERROR
46 /* A priority queue - perhaps move to a separate file if it becomes
47 needed somewhere else */
49 #define ART_PRIQ_USE_HEAP
51 typedef struct _ArtPriQ ArtPriQ;
52 typedef struct _ArtPriPoint ArtPriPoint;
69 ArtPriQ *result = art_new (ArtPriQ, 1);
72 result->n_items_max = 16;
73 result->items = art_new (ArtPriPoint *, result->n_items_max);
78 art_pri_free (ArtPriQ *pq)
85 art_pri_empty (ArtPriQ *pq)
87 return pq->n_items == 0;
90 /* This heap implementation is based on Vasek Chvatal's course notes:
91 http://www.cs.rutgers.edu/~chvatal/notes/pq.html#heap */
94 art_pri_bubble_up (ArtPriQ *pq, int vacant, ArtPriPoint *missing)
96 ArtPriPoint **items = pq->items;
99 parent = (vacant - 1) >> 1;
100 while (vacant > 0 && (missing->y < items[parent]->y ||
101 (missing->y == items[parent]->y &&
102 missing->x < items[parent]->x)))
104 items[vacant] = items[parent];
106 parent = (vacant - 1) >> 1;
109 items[vacant] = missing;
113 art_pri_insert (ArtPriQ *pq, ArtPriPoint *point)
116 art_dprint("insert into pq %08x: %.16f,%.16f %08x\n", pq, point->x, point->y, point->user_data);
118 if (pq->n_items == pq->n_items_max)
119 art_expand (pq->items, ArtPriPoint *, pq->n_items_max);
121 art_pri_bubble_up (pq, pq->n_items++, point);
125 art_pri_sift_down_from_root (ArtPriQ *pq, ArtPriPoint *missing)
127 ArtPriPoint **items = pq->items;
128 int vacant = 0, child = 2;
133 if (items[child - 1]->y < items[child]->y ||
134 (items[child - 1]->y == items[child]->y &&
135 items[child - 1]->x < items[child]->x))
137 items[vacant] = items[child];
139 child = (vacant + 1) << 1;
143 items[vacant] = items[n - 1];
147 art_pri_bubble_up (pq, vacant, missing);
151 art_pri_choose (ArtPriQ *pq)
153 ArtPriPoint *result = pq->items[0];
155 art_pri_sift_down_from_root (pq, pq->items[--pq->n_items]);
159 /* TODO: this is *not* thread save */
160 const ArtSVP* current_svp = 0;
161 int art_error_in_intersector=0;
163 void art_report_error()
165 art_error_in_intersector = 1;
166 #ifdef ABORT_ON_ERROR
168 fprintf(stderr, "internal error: no current polygon\n");
171 const ArtSVP*svp = current_svp;
172 FILE*fi = fopen("svp.ps", "wb");
174 fprintf(fi, "%% begin\n");
175 for (i = 0; i < svp->n_segs; i++)
177 fprintf(fi, "%g setgray\n", svp->segs[i].dir ? 0.7 : 0);
178 for (j = 0; j < svp->segs[i].n_points; j++)
180 fprintf(fi, "%.32f %.32f %s\n",
181 svp->segs[i].points[j].x,
182 svp->segs[i].points[j].y,
183 j ? "lineto" : "moveto");
185 fprintf(fi, "stroke\n");
187 fprintf(fi, "showpage\n");
190 fprintf(stderr, "There was an error during polygon processing.\n");
191 fprintf(stderr, "I saved a debug file, called svp.ps, to the current directory.\n");
192 fprintf(stderr, "Please help making this tool more stable by mailing\n");
193 fprintf(stderr, "this file to debug@swftools.org. Thank you!\n");
198 /* A virtual class for an "svp writer". A client of this object creates an
199 SVP by repeatedly calling "add segment" and "add point" methods on it.
202 typedef struct _ArtSvpWriterRewind ArtSvpWriterRewind;
204 /* An implementation of the svp writer virtual class that applies the
207 struct _ArtSvpWriterRewind {
216 art_svp_writer_rewind_add_segment (ArtSvpWriter *self, int wind_left,
217 int delta_wind, double x, double y)
219 ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
222 art_boolean left_filled, right_filled;
223 int wind_right = wind_left + delta_wind;
225 const int init_n_points_max = 4;
229 case ART_WIND_RULE_NONZERO:
230 left_filled = (wind_left != 0);
231 right_filled = (wind_right != 0);
233 case ART_WIND_RULE_INTERSECT:
234 left_filled = (wind_left > 1);
235 right_filled = (wind_right > 1);
237 case ART_WIND_RULE_ODDEVEN:
238 left_filled = (wind_left & 1);
239 right_filled = (wind_right & 1);
241 case ART_WIND_RULE_POSITIVE:
242 left_filled = (wind_left > 0);
243 right_filled = (wind_right > 0);
246 art_die ("Unknown wind rule %d\n", swr->rule);
250 art_dprint("New svp segment %d: %.32f %.32f left_filled=%d, right_filled=%d\n", swr->svp->n_segs, x, y, left_filled, right_filled);
253 if (left_filled == right_filled)
255 /* discard segment now */
257 art_dprint (" discarding segment: %d += %d (%.16f, %.16f)\n",
258 wind_left, delta_wind, x, y);
264 seg_num = svp->n_segs++;
265 if (swr->n_segs_max == seg_num)
267 swr->n_segs_max += 10;
268 svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
269 (swr->n_segs_max - 1) *
272 swr->n_points_max = art_renew (swr->n_points_max, int,
275 seg = &svp->segs[seg_num];
277 seg->dir = right_filled;
278 swr->n_points_max[seg_num] = init_n_points_max;
283 seg->points = art_new (ArtPoint, init_n_points_max);
284 seg->points[0].x = x;
285 seg->points[0].y = y;
287 art_dprint ("swr add_segment: %d += %d (%.16f, %.16f) --> %d (%s)\n",
288 wind_left, delta_wind, x, y, seg_num,
289 seg->dir ? "filled" : "non-filled");
295 art_svp_writer_rewind_add_point (ArtSvpWriter *self, int seg_id,
299 ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
304 art_dprint ("swr add_point: %d (%.16f, %.16f)\n", seg_id, x, y);
307 /* omitted segment */
310 seg = &swr->svp->segs[seg_id];
313 seg->points[seg->n_points-1].x == x &&
314 seg->points[seg->n_points-1].y == y) {
315 //art_warn("duplicate point %.16f,%.16f in segment %08x\n",
320 n_points = seg->n_points++;
321 if (swr->n_points_max[seg_id] == n_points)
322 art_expand (seg->points, ArtPoint, swr->n_points_max[seg_id]);
324 if(0 && n_points>=2) {
325 double dx1 = seg->points[n_points-1].x - seg->points[n_points-2].x;
326 double dy1 = seg->points[n_points-1].y - seg->points[n_points-2].y;
327 double dx2 = x - seg->points[n_points-2].x;
328 double dy2 = y - seg->points[n_points-2].y;
329 if(fabs(dx1*dy2 - dx2*dy1) < 1e-10) {
331 n_points--; // remove previous point pointing in the same direction
333 //art_warn("redundant point %.16f,%.16f in segment %08x\n",
334 // seg->points[n_points-1].x, seg->points[n_points-1].y,
339 if(n_points && seg->points[n_points-1].y > y) {
340 art_warn("non-increasing segment (%.16f -> %.16f)\n", seg->points[n_points-1].y, y);
344 seg->points[n_points].x = x;
345 seg->points[n_points].y = y;
346 if (x < seg->bbox.x0)
348 if (x > seg->bbox.x1)
354 art_svp_writer_rewind_close_segment (ArtSvpWriter *self, int seg_id)
356 /* Not needed for this simple implementation. A potential future
357 optimization is to merge segments that can be merged safely. */
358 #ifdef CHEAP_SANITYCHECK
359 ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
364 seg = &swr->svp->segs[seg_id];
365 if (seg->n_points < 2)
366 art_warn ("*** closing segment %d with only %d point%s\n",
367 seg_id, seg->n_points, seg->n_points == 1 ? "" : "s");
372 art_dprint ("swr close_segment: %d\n", seg_id);
377 art_svp_writer_rewind_reap (ArtSvpWriter *self)
379 ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
380 ArtSVP *result = swr->svp;
382 art_free (swr->n_points_max);
388 art_svp_writer_rewind_new (ArtWindRule rule)
390 ArtSvpWriterRewind *result = art_new (ArtSvpWriterRewind, 1);
392 result->super.add_segment = art_svp_writer_rewind_add_segment;
393 result->super.add_point = art_svp_writer_rewind_add_point;
394 result->super.close_segment = art_svp_writer_rewind_close_segment;
397 result->n_segs_max = 16;
398 result->svp = (ArtSVP*)art_alloc (sizeof(ArtSVP) +
399 (result->n_segs_max - 1) * sizeof(ArtSVPSeg));
400 result->svp->n_segs = 0;
401 result->n_points_max = (int*)art_new (int, result->n_segs_max);
403 return &result->super;
406 /* Now, data structures for the active list.
415 -------------+--------------------
423 typedef struct _ArtActiveSeg ArtActiveSeg;
425 /* Note: BNEG is 1 for \ lines, and 0 for /. Thus,
426 x[(flags & BNEG) ^ 1] <= x[flags & BNEG] */
427 #define ART_ACTIVE_FLAGS_BNEG 1
429 /* This flag is set if the segment has been inserted into the active
431 #define ART_ACTIVE_FLAGS_IN_ACTIVE 2
433 /* This flag is set when the segment is to be deleted in the
434 horiz commit process. */
435 #define ART_ACTIVE_FLAGS_DEL 4
437 /* This flag is set if the seg_id is a valid output segment. */
438 #define ART_ACTIVE_FLAGS_OUT 8
440 /* This flag is set if the segment is in the horiz list. */
441 #define ART_ACTIVE_FLAGS_IN_HORIZ 16
443 struct _ArtActiveSeg {
445 int wind_left, delta_wind;
446 ArtActiveSeg *left, *right; /* doubly linked list structure */
448 const ArtSVPSeg *in_seg;
453 double a, b, c; /* line equation; ax+by+c = 0 for the line, a^2 + b^2 = 1,
456 /* bottom point and intersection point stack */
461 /* horiz commit list */
462 ArtActiveSeg *horiz_left, *horiz_right;
464 int horiz_delta_wind;
468 typedef struct _ArtIntersectCtx ArtIntersectCtx;
470 struct _ArtIntersectCtx {
476 ArtActiveSeg *active_head;
479 ArtActiveSeg *horiz_first;
480 ArtActiveSeg *horiz_last;
482 /* segment index of next input segment to be added to pri q */
486 #define EPSILON_A 1e-5 /* Threshold for breaking lines at point insertions */
489 * art_svp_intersect_setup_seg: Set up an active segment from input segment.
490 * @seg: Active segment.
491 * @pri_pt: Priority queue point to initialize.
493 * Sets the x[], a, b, c, flags, and stack fields according to the
494 * line from the current cursor value. Sets the priority queue point
495 * to the bottom point of this line. Also advances the input segment
499 art_svp_intersect_setup_seg (ArtActiveSeg *seg, ArtPriPoint *pri_pt)
501 const ArtSVPSeg *in_seg = seg->in_seg;
503 double x0, y0, x1, y1;
508 in_curs = seg->in_curs++;
509 //} while(in_seg->points[in_curs].x == in_seg->points[in_curs + 1].x &&
510 // in_seg->points[in_curs].y == in_seg->points[in_curs + 1].y &&
511 // in_curs < in_seg->n_points-1
514 x0 = in_seg->points[in_curs].x;
515 y0 = in_seg->points[in_curs].y;
516 x1 = in_seg->points[in_curs + 1].x;
517 y1 = in_seg->points[in_curs + 1].y;
522 r2 = dx * dx + dy * dy;
524 //art_warn("segment %08x has zero length\n", seg);
526 s = r2 == 0 ? 1 : 1 / sqrt (r2);
528 seg->b = b = -dx * s;
529 seg->c = -(a * x0 + b * y0);
530 seg->flags = (seg->flags & ~ART_ACTIVE_FLAGS_BNEG) | (dx > 0);
536 seg->stack[0].x = x1;
537 seg->stack[0].y = y1;
540 art_warn("art_svp_intersect.c: bad input polygon %08x, contains decreasing y values\n", seg->in_seg);
544 art_dprint("segment %08x (top: %.16f,%.16f) starts with endpoint at %.16f,%.16f\n", seg,
551 * art_svp_intersect_add_horiz: Add point to horizontal list.
552 * @ctx: Intersector context.
553 * @seg: Segment with point to insert into horizontal list.
555 * Inserts @seg into horizontal list, keeping it in ascending horiz_x
558 * Note: the horiz_commit routine processes "clusters" of segs in the
559 * horiz list, all sharing the same horiz_x value. The cluster is
560 * processed in active list order, rather than horiz list order. Thus,
561 * the order of segs in the horiz list sharing the same horiz_x
562 * _should_ be irrelevant. Even so, we use b as a secondary sorting key,
563 * as a "belt and suspenders" defensive coding tactic.
566 art_svp_intersect_add_horiz (ArtIntersectCtx *ctx, ArtActiveSeg *seg)
568 ArtActiveSeg **pp = &ctx->horiz_last;
570 ArtActiveSeg *place_right = NULL;
572 #ifdef CHEAP_SANITYCHECK
573 if (seg->flags & ART_ACTIVE_FLAGS_IN_HORIZ)
575 double dx = seg->x[1] - seg->x[0];
576 double dy = seg->y1 - seg->y0;
577 double len = sqrt(dx*dx+dy*dy);
578 art_warn("attempt to put segment %08x %.16f,%.16f (len:%.16f) in horiz list twice\n", seg, seg->x[0], seg->y0, len);
581 seg->flags |= ART_ACTIVE_FLAGS_IN_HORIZ;
585 art_dprint ("add_horiz %08x, x = %.16f\n", (unsigned long) seg, seg->horiz_x);
587 for (place = *pp; place != NULL && (place->horiz_x > seg->horiz_x ||
588 (place->horiz_x == seg->horiz_x &&
593 pp = &place->horiz_left;
596 seg->horiz_left = place;
597 seg->horiz_right = place_right;
600 ctx->horiz_first = seg;
602 place->horiz_right = seg;
605 art_dprint("horiz_list:\n");
606 ArtActiveSeg*s = ctx->horiz_first;
608 art_dprint(" %08x x=%.16f wind_left=%d delta_wind=%d horiz_delta_wind=%d\n", s, s->horiz_x,
609 s->wind_left, s->delta_wind, s->horiz_delta_wind);
616 art_svp_intersect_push_pt (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
620 int n_stack = seg->n_stack;
622 if (n_stack == seg->n_stack_max)
623 art_expand (seg->stack, ArtPoint, seg->n_stack_max);
626 seg->stack[n_stack].x = x;
627 seg->stack[n_stack].y = y;
632 art_dprint("art_svp_intersect_push_pt %08x |top %.16f,%.16f\n", seg, seg->x[0], seg->y0);
633 for(t=seg->n_stack-1;t>=0;t--) {
634 if(t!=seg->n_stack-1)
635 art_dprint("art_svp_intersect_push_pt %08x |pt %.16f,%.16f\n", seg, seg->stack[t].x, seg->stack[t].y);
637 art_dprint("art_svp_intersect_push_pt %08x |new %.16f,%.16f\n", seg, seg->stack[t].x, seg->stack[t].y);
641 art_dprint("(shortening segment %08x to %.16f,%.16f)\n", seg, x, y);
644 if(seg->stack[seg->n_stack-1].y == seg->y0) {
645 art_warn("duplicate y coordinate (=y0) in point stack\n");
649 if(seg->stack[seg->n_stack-2].y < seg->stack[seg->n_stack-1].y) {
650 art_warn("bad shortening- segment got *longer*\n");
659 pri_pt = art_new (ArtPriPoint, 1);
662 pri_pt->user_data = seg;
663 art_pri_insert (ctx->pq, pri_pt);
666 #define ART_BREAK_LEFT 1
667 #define ART_BREAK_RIGHT 2
670 * art_svp_intersect_break: Break an active segment.
672 * Note: y must be greater than the top point's y, and less than
675 * Return value: x coordinate of break point.
678 art_svp_intersect_break (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
679 double x_ref, double y, int break_flags)
681 double x0, y0, x1, y1;
682 const ArtSVPSeg *in_seg = seg->in_seg;
683 int in_curs = seg->in_curs;
686 x0 = in_seg->points[in_curs - 1].x;
687 y0 = in_seg->points[in_curs - 1].y;
688 x1 = in_seg->points[in_curs].x;
689 y1 = in_seg->points[in_curs].y;
691 x = x0 + (x1 - x0) * ((y - y0) / (y1 - y0));
693 //printf("%d %.16f %.16f %d\n", break_flags&ART_BREAK_LEFT, x, x_ref, x > x_ref);
694 //printf("%d %.16f %.16f %d\n", break_flags&ART_BREAK_RIGHT, x, x_ref, x < x_ref);
696 if ((break_flags == ART_BREAK_LEFT && x > x_ref) ||
697 (break_flags == ART_BREAK_RIGHT && x < x_ref))
700 art_dprint ("art_svp_intersect_break %08x: limiting x to %.16f, was %.16f, %s\n", seg,
701 x_ref, x, break_flags == ART_BREAK_LEFT ? "left" : "right");
703 //x = x_ref; //used to be *within* the VERBOSE comment
706 if(y < y0 || y > y1) {
707 art_warn("!! bad break %.16f of %.16f-%.16f\n", y, y0, y1);
712 /* I think we can count on min(x0, x1) <= x <= max(x0, x1) with sane
713 arithmetic, but it might be worthwhile to check just in case. */
715 /* TODO: should we check seg instead of in_seg ? */
718 art_warn("bad x value %.16f in intersect_break: not between %.16f and %.16f\n", x, x0, x1);
723 art_warn("bad x value %.16f in intersect_break: not between %.16f and %.16f\n", x, x1, x0);
730 art_svp_intersect_push_pt (ctx, seg, x, y);
734 art_warn("intersect_break at a y above the current scanline (%.16f < %.16f)", y, ctx->y);
740 art_svp_intersect_add_horiz (ctx, seg);
747 * art_svp_intersect_add_point: Add a point, breaking nearby neighbors.
748 * @ctx: Intersector context.
749 * @x: X coordinate of point to add.
750 * @y: Y coordinate of point to add.
751 * @seg: "nearby" segment, or NULL if leftmost.
753 * Return value: Segment immediately to the left of the new point, or
754 * NULL if the new point is leftmost.
756 static ArtActiveSeg *
757 art_svp_intersect_add_point (ArtIntersectCtx *ctx, double x, double y,
758 ArtActiveSeg *seg, int break_flags)
760 ArtActiveSeg *left, *right;
761 double x_min = x, x_max = x;
762 art_boolean left_live, right_live;
765 ArtActiveSeg *test, *result = NULL;
770 right = ctx->active_head;
773 left_live = (break_flags & ART_BREAK_LEFT) && (left != NULL);
774 right_live = (break_flags & ART_BREAK_RIGHT) && (right != NULL);
779 dd = seg->a*x + seg->b*y + seg->c;
780 art_dprint("add_point seg=%08x %f,%f%s%s (left=%08x, right=%08x) d=%f\n", seg, x, y,
781 break_flags&ART_BREAK_LEFT?" BREAK_LEFT":"",
782 break_flags&ART_BREAK_LEFT?" BREAK_RIGHT":"",
783 seg?seg->left:0, seg?seg->right:0, dd);
785 /* add_point relies on the fact that the active list is ascending-
786 no checks are done for lines which are not in strict order.
788 a point position (x,y) is tested against left (if break_flags&ART_BREAK_LEFT)
789 and right (if break_flags&ART_BREAK_RIGHT) neighboring segments which are
790 within EPSILON_A distance of the point. If they are, they are split at y.
791 For ART_BREAK_LEFT, the "intersection point" on the line (which is to the left
792 of (x,y) will never be to the right of "our" (x,y)- new_x will be shifted
793 by _break to make sure of that. (Which should only happen for horizontal
794 line segments) Likewise for ART_BREAK_RIGHT.
796 The horizontal area around (x,y) (x_min, x_max) will be extended into the
797 break direction for every cut we make.
800 //new_x = art_svp_intersect_break (ctx, left, x_min, y, ART_BREAK_LEFT);
802 while (left_live || right_live)
805 art_dprint(" left: %08x (%d) right: %08x (%d)\n", left, left_live, right, right_live);
809 if (x <= left->x[left->flags & ART_ACTIVE_FLAGS_BNEG] &&
810 /* It may be that one of these conjuncts turns out to be always
811 true. We test both anyway, to be defensive. */
812 y != left->y0 && y < left->y1)
814 d = x_min * left->a + y * left->b + left->c;
817 new_x = art_svp_intersect_break (ctx, left, x_min, y,
820 art_dprint("break %08x at x<=%.16f,y=%.16f, new_x=%f\n", left, x_min, y, new_x);
825 right_live = (right != NULL);
827 else if (new_x < x_min)
830 left_live = (left != NULL);
833 left_live = ART_FALSE;
836 left_live = ART_FALSE;
840 if (x >= right->x[(right->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] &&
841 /* It may be that one of these conjuncts turns out to be always
842 true. We test both anyway, to be defensive. */
843 y != right->y0 && y < right->y1)
845 d = x_max * right->a + y * right->b + right->c;
848 new_x = art_svp_intersect_break (ctx, right, x_max, y,
851 art_dprint("break %08x at x>=%.16f,y=%.16f, new_x=%f\n", right, x_max, y, new_x);
856 left_live = (left != NULL);
858 else if (new_x >= x_max)
860 right = right->right;
861 right_live = (right != NULL);
864 right_live = ART_FALSE;
867 right_live = ART_FALSE;
871 /* Ascending order is guaranteed by break_flags. Thus, we don't need
872 to actually fix up non-ascending pairs. */
874 /* Now, (left, right) defines an interval of segments broken. Sort
875 into ascending x order. (find segment to the left of the new point) */
876 test = left == NULL ? ctx->active_head : left->right;
878 if (test != NULL && test != right)
882 else if(y == test->y1)
885 art_warn ("internal error in add_point: y=%.16f is between %.16f and %.16f\n", y, test->y0, test->y1);
898 /* FIXME the following code doesn't do anything (?) */
902 art_warn ("art_svp_intersect_add_point: non-ascending x\n");
911 art_svp_intersect_swap_active (ArtIntersectCtx *ctx,
912 ArtActiveSeg *left_seg, ArtActiveSeg *right_seg)
914 if((left_seg && left_seg->right != right_seg) ||
915 (right_seg && right_seg->left != left_seg)) {
916 art_warn("error: active list in disarray\n");
920 right_seg->left = left_seg->left;
921 if (right_seg->left != NULL)
922 right_seg->left->right = right_seg;
924 ctx->active_head = right_seg;
925 left_seg->right = right_seg->right;
926 if (left_seg->right != NULL)
927 left_seg->right->left = left_seg;
928 left_seg->left = right_seg;
929 right_seg->right = left_seg;
932 volatile char add0 = 0;
933 static double double_precision(double x)
935 /* make sure x has exactly 11 bits exponent and 52 bit mantissa-
936 there is probably a more elegant (or faster) way to trick the compiler
937 into doing this, but this works for now. */
940 xx[0]+=add0; xx[1]+=add0; xx[2]+=add0; xx[3]+=add0;
941 xx[4]+=add0; xx[5]+=add0; xx[6]+=add0; xx[7]+=add0;
946 * art_svp_intersect_test_cross: Test crossing of a pair of active segments.
947 * @ctx: Intersector context.
948 * @left_seg: Left segment of the pair.
949 * @right_seg: Right segment of the pair.
950 * @break_flags: Flags indicating whether to break neighbors.
952 * Tests crossing of @left_seg and @right_seg. If there is a crossing,
953 * inserts the intersection point into both segments.
955 * Return value: True if the intersection took place at the current
956 * scan line, indicating further iteration is needed.
959 art_svp_intersect_test_cross (ArtIntersectCtx *ctx,
960 ArtActiveSeg *left_seg, ArtActiveSeg *right_seg,
963 double left_x0, left_y0, left_x1;
964 double left_y1 = left_seg->y1;
965 double right_y1 = right_seg->y1;
968 const ArtSVPSeg *in_seg;
971 double x, y; /* intersection point */
974 static int count = 0;
976 art_dprint ("art_svp_intersect_test_cross %08x <-> %08x: count=%d\n",
977 (unsigned long)left_seg, (unsigned long)right_seg, count++);
978 art_dprint ("%08x = %.16f,%.16f -> %.16f %.16f\n", left_seg,
979 left_seg->x[0], left_seg->y0,
980 left_seg->x[1], left_seg->y1);
981 art_dprint ("(full: %.16f,%.16f -> %.16f %.16f)\n",
982 left_seg->in_seg->points[left_seg->in_curs - 1].x, left_seg->in_seg->points[left_seg->in_curs - 1].y,
983 left_seg->in_seg->points[left_seg->in_curs].x, left_seg->in_seg->points[left_seg->in_curs].y
986 art_dprint ("%08x = %.16f,%.16f -> %.16f %.16f\n", right_seg,
987 right_seg->x[0], right_seg->y0,
988 right_seg->x[1], right_seg->y1);
989 art_dprint ("(full: %.16f,%.16f -> %.16f %.16f)\n",
990 right_seg->in_seg->points[right_seg->in_curs - 1].x, right_seg->in_seg->points[right_seg->in_curs - 1].y,
991 right_seg->in_seg->points[right_seg->in_curs].x, right_seg->in_seg->points[right_seg->in_curs].y
995 #ifdef CHEAP_SANITYCHECK
996 if (right_seg->x[0] < left_seg->x[(left_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1]) {
997 /* notice: if we test *only* the line equation here, dd might be < 0, even though
998 right_seg was inserted to the right of left_seg correctly, due to numerical
1000 double dd = right_seg->x[0] * left_seg->a + right_seg->y0 * left_seg->b + left_seg->c;
1002 //art_warn ("segment %08x[%d] isn't to the left of %08x[%d]. d=%.16f\n",
1003 // left_seg, left_seg->n_stack,
1004 // right_seg, right_seg->n_stack,
1010 if (left_seg->y0 == right_seg->y0 && left_seg->x[0] == right_seg->x[0])
1012 /* Top points of left and right segments coincide. This case
1013 feels like a bit of duplication - we may want to merge it
1014 with the cases below. However, this way, we're sure that this
1015 logic makes only localized changes. */
1017 if (left_y1 < right_y1)
1019 /* Test left (x1, y1) against right segment */
1020 double left_x1 = left_seg->x[1];
1023 right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
1024 left_y1 == right_seg->y0)
1026 d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
1030 else if (d < EPSILON_A) /* should we use zero here? */
1033 art_dprint("break %08x because of common top point, left_y1 < right_y1\n", right_seg);
1035 /* I'm unsure about the break flags here. */
1036 double right_x1 = art_svp_intersect_break (ctx, right_seg,
1040 /* this is always true due to ART_BREAK_RIGHT right_x1>=left_x1 if
1041 _break uses x_ref clipping */
1042 if (left_x1 <= right_x1) {
1047 else if (left_y1 > right_y1)
1049 /* Test right (x1, y1) against left segment */
1050 double right_x1 = right_seg->x[1];
1052 if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
1053 right_y1 == left_seg->y0)
1056 d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
1059 else if (d > -EPSILON_A) /* should we use zero here? */
1062 art_dprint("break %08x because of common top point, left_y1 > right_y1\n", left_seg);
1064 /* See above regarding break flags. */
1065 double left_x1 = art_svp_intersect_break (ctx, left_seg,
1069 /* this is always true, due to ART_BREAK_LEFT left_x1<=right_x1, if
1070 _break uses x_ref clipping
1072 if (left_x1 <= right_x1) {
1077 else /* left_y1 == right_y1 */
1079 double left_x1 = left_seg->x[1];
1080 double right_x1 = right_seg->x[1];
1083 if (left_x1 <= right_x1)
1087 //int wind_left = left_seg->wind_left;
1088 //int wind_right = right_seg->wind_left;
1089 art_svp_intersect_swap_active (ctx, left_seg, right_seg);
1090 //left_seg->wind_left = wind_right;
1091 //right_seg->wind_left = wind_left;
1096 if (left_y1 < right_y1)
1098 /* Test left (x1, y1) against right segment */
1099 double left_x1 = left_seg->x[1];
1102 right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
1103 left_y1 == right_seg->y0)
1106 if(left_y1 < right_seg->y0) {
1107 art_warn("left_y1 < right_seg->y0\n");
1111 /* we might want to output a warning for left_y1 < right_seg->y0 */
1113 d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
1116 else if (d < EPSILON_A)
1119 art_dprint("break %08x at %.16f because left_y1 < right_y1\n", right_seg, left_y1);
1120 art_dprint ("%08x = %.16f,%.16f -> %.16f %.16f\n", right_seg,
1121 right_seg->x[0], right_seg->y0,
1122 right_seg->x[1], right_seg->y1);
1124 double right_x1 = art_svp_intersect_break (ctx, right_seg,
1128 art_dprint("after break:\n", right_seg);
1129 art_dprint ("%08x = %.16f,%.16f -> %.16f %.16f\n", right_seg,
1130 right_seg->x[0], right_seg->y0,
1131 right_seg->x[1], right_seg->y1);
1133 /* this is always true if _break uses x_ref clipping */
1134 if (left_x1 <= right_x1)
1138 else if (left_y1 > right_y1)
1140 /* Test right (x1, y1) against left segment */
1141 double right_x1 = right_seg->x[1];
1143 if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
1144 right_y1 == left_seg->y0)
1147 if(right_y1 < left_seg->y0) {
1148 art_warn("left_y1 < right_seg->y0\n");
1152 /* we might want to output a warning for right_y1 < left_seg->y0 */
1154 d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
1157 else if (d > -EPSILON_A)
1160 art_dprint("break %08x because left_y1 > right_y1\n", right_seg);
1162 double left_x1 = art_svp_intersect_break (ctx, left_seg,
1165 /* this is always true if _break uses x_ref clipping */
1166 if (left_x1 <= right_x1)
1170 else /* left_y1 == right_y1 */
1172 double left_x1 = left_seg->x[1];
1173 double right_x1 = right_seg->x[1];
1175 if (left_x1 <= right_x1)
1180 /* The segments cross. Find the intersection point. */
1182 in_seg = left_seg->in_seg;
1183 in_curs = left_seg->in_curs;
1184 left_x0 = in_seg->points[in_curs - 1].x;
1185 left_y0 = in_seg->points[in_curs - 1].y;
1186 left_x1 = in_seg->points[in_curs].x;
1187 left_y1 = in_seg->points[in_curs].y;
1190 /* check for endpoint = firstpoint cases */
1191 if(left_seg->y0 == right_seg->y1 && left_seg->x[0] == right_seg->x[1])
1193 if(left_seg->y1 == right_seg->y0 && left_seg->x[1] == right_seg->x[0])
1197 d0 = left_x0 * right_seg->a + left_y0 * right_seg->b + right_seg->c;
1198 d1 = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
1206 /* Is this division always safe? It could possibly overflow. */
1220 x = left_x0 + t * (left_x1 - left_x0);
1221 y = left_y0 + t * (left_y1 - left_y0);
1225 /* Make sure intersection point is within bounds of right seg. */
1226 if (y < right_seg->y0)
1228 x = right_seg->x[0];
1231 else if (y > right_seg->y1)
1233 x = right_seg->x[1];
1236 else if (x < right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1])
1237 x = right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1];
1238 else if (x > right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG])
1239 x = right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG];
1241 x = double_precision(x);
1242 y = double_precision(y);
1244 assert(ctx->y >= left_seg->y0);
1246 art_dprint ("intersect at %.16f %.16f\n", x, y);
1251 /* as we use the full segment length (not just the subsegment currently
1252 under evaluation), intersection points may be above the current scanline.
1253 As we're not able to process these anymore, we also don't need to add
1254 anything to the active list or pq.
1256 Intersection points above the current scanline happen if an
1257 intersection is handled twice- once when the line is inserted, and
1258 once when e.g. some other intersection point triggers insert_cross.
1260 art_warn ("previously unhandled intersection point %.16f %.16f (dy=%.16f)\n", x, y, ctx->y - y);
1264 if(y > left_seg->y1) {
1265 /* not within the subsegment we're currently looking into- this is not an intersection */
1269 if (y == left_seg->y0)
1271 if (y != right_seg->y0)
1274 art_dprint ("art_svp_intersect_test_cross: intersection (%.16f, %.16f) matches former y0 of %08x, [%08x]\n",
1275 x, y, (unsigned long)left_seg, (unsigned long)right_seg);
1277 art_svp_intersect_push_pt (ctx, right_seg, x, y);
1278 if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
1279 art_svp_intersect_add_point (ctx, x, y, right_seg->right,
1285 art_dprint ("art_svp_intersect_test_cross: intersection (%.16f, %.16f) at current scan line (y=ly0=ry0)\n",
1286 x, y, (unsigned long)left_seg, (unsigned long)right_seg);
1288 /* Intersection takes place at current scan line, with
1289 left->x0 <= x <= right->x0, left->x0 != right->x0.
1291 This happens if one of the lines is horizontal, or very near
1292 horizontal. (true horizontal lines are processed by _horiz())
1294 Process immediately rather than queueing intersection point into
1296 ArtActiveSeg *winner, *loser;
1298 /* Choose "most vertical" segement */
1299 if (left_seg->a > right_seg->a)
1310 art_dprint (" x = %.16f\n", x);
1311 art_dprint (" loser->x[0] = %.16f\n", loser->x[0]);
1312 art_dprint ("winner->x[0] = %.16f\n", winner->x[0]);
1314 loser->x[0] = winner->x[0];
1315 loser->horiz_x = loser->x[0];
1316 loser->horiz_delta_wind += loser->delta_wind;
1317 winner->horiz_delta_wind -= loser->delta_wind;
1319 art_svp_intersect_swap_active (ctx, left_seg, right_seg);
1323 else if (y == right_seg->y0)
1326 art_dprint ("*** art_svp_intersect_test_cross: intersection (%.16f, %.16f) matches latter y0 of [%08x], %08x\n",
1327 x, y, (unsigned long)left_seg, (unsigned long)right_seg);
1328 art_dprint ("%08x = %.16f,%.16f -> %.16f %.16f\n", left_seg,
1329 left_seg->x[0], left_seg->y0,
1330 left_seg->x[1], left_seg->y1);
1331 art_dprint ("%08x = %.16f,%.16f -> %.16f %.16f\n", right_seg,
1332 right_seg->x[0], right_seg->y0,
1333 right_seg->x[1], right_seg->y1);
1336 art_svp_intersect_push_pt (ctx, left_seg, x, y);
1337 if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
1338 art_svp_intersect_add_point (ctx, x, y, left_seg->left,
1344 art_dprint ("Inserting (%.16f, %.16f) into %08x, %08x\n",
1345 x, y, (unsigned long)left_seg, (unsigned long)right_seg);
1347 /* Insert the intersection point into both segments. */
1348 art_svp_intersect_push_pt (ctx, left_seg, x, y);
1349 art_svp_intersect_push_pt (ctx, right_seg, x, y);
1350 if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
1351 art_svp_intersect_add_point (ctx, x, y, left_seg->left, ART_BREAK_LEFT | ART_BREAK_RIGHT);
1352 if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
1353 art_svp_intersect_add_point (ctx, x, y, right_seg->right, ART_BREAK_LEFT | ART_BREAK_RIGHT);
1359 * art_svp_intersect_active_delete: Delete segment from active list.
1360 * @ctx: Intersection context.
1361 * @seg: Segment to delete.
1363 * Deletes @seg from the active list.
1365 static /* todo inline */ void
1366 art_svp_intersect_active_delete (ArtIntersectCtx *ctx, ArtActiveSeg *seg)
1368 ArtActiveSeg *left = seg->left, *right = seg->right;
1371 left->right = right;
1373 ctx->active_head = right;
1379 * art_svp_intersect_active_free: Free an active segment.
1380 * @seg: Segment to delete.
1384 static /* todo inline */ void
1385 art_svp_intersect_active_free (ArtActiveSeg *seg)
1387 art_free (seg->stack);
1389 art_dprint ("Freeing %08x\n", (unsigned long) seg);
1397 * art_svp_intersect_insert_cross: Test crossings of newly inserted line.
1399 * Tests @seg against its left and right neighbors for intersections.
1400 * Precondition: the line in @seg is not purely horizontal.
1403 art_svp_intersect_insert_cross (ArtIntersectCtx *ctx,
1406 ArtActiveSeg *left = seg, *right = seg;
1412 ArtActiveSeg *leftc;
1414 for (leftc = left->left; leftc != NULL; leftc = leftc->left)
1415 if (!(leftc->flags & ART_ACTIVE_FLAGS_DEL))
1417 if (leftc != NULL &&
1418 art_svp_intersect_test_cross (ctx, leftc, left,
1421 if (left == right || right == NULL)
1422 right = left->right;
1429 else if (right != NULL && right->right != NULL)
1431 ArtActiveSeg *rightc;
1433 for (rightc = right->right; rightc != NULL; rightc = rightc->right)
1434 if (!(rightc->flags & ART_ACTIVE_FLAGS_DEL))
1436 if (rightc != NULL &&
1437 art_svp_intersect_test_cross (ctx, right, rightc,
1440 if (left == right || left == NULL)
1454 * art_svp_intersect_horiz: Add horizontal line segment.
1455 * @ctx: Intersector context.
1456 * @seg: Segment on which to add horizontal line.
1457 * @x0: Old x position.
1458 * @x1: New x position.
1460 * Adds a horizontal line from @x0 to @x1, and updates the current
1461 * location of @seg to @x1.
1464 art_svp_intersect_horiz (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
1465 double x0, double x1)
1473 art_dprint("horiz: seg=%08x x0=%f x1=%f segid=%08x flags=%s delta_wind=%d\n", seg, x0, x1, seg->seg_id,
1474 seg->flags & ART_ACTIVE_FLAGS_OUT?"out":"", seg->delta_wind);
1477 hs = art_new (ArtActiveSeg, 1);
1479 hs->flags = ART_ACTIVE_FLAGS_DEL | (seg->flags & ART_ACTIVE_FLAGS_OUT);
1480 if (seg->flags & ART_ACTIVE_FLAGS_OUT)
1482 ArtSvpWriter *swr = ctx->out;
1484 swr->add_point (swr, seg->seg_id, x0, ctx->y);
1486 hs->seg_id = seg->seg_id;
1488 hs->horiz_delta_wind = seg->delta_wind;
1491 /* Ideally, the (a, b, c) values will never be read. However, there
1492 are probably some tests remaining that don't check for _DEL
1493 before evaluating the line equation. For those, these
1494 initializations will at least prevent a UMR of the values, which
1495 can crash on some platforms. */
1500 seg->horiz_delta_wind -= seg->delta_wind;
1502 art_svp_intersect_add_horiz (ctx, hs);
1507 art_boolean first = ART_TRUE;
1509 for (left = seg->left; left != NULL; left = seg->left)
1511 int left_bneg = left->flags & ART_ACTIVE_FLAGS_BNEG;
1513 if (left->x[left_bneg] <= x1)
1515 if (left->x[left_bneg ^ 1] <= x1 &&
1516 x1 * left->a + ctx->y * left->b + left->c >= 0)
1518 if (left->y0 != ctx->y && left->y1 != ctx->y)
1520 art_svp_intersect_break (ctx, left, x1, ctx->y, ART_BREAK_LEFT);
1523 art_dprint ("x0=%.16f > x1=%.16f, swapping %08x, %08x\n",
1524 x0, x1, (unsigned long)left, (unsigned long)seg);
1526 art_svp_intersect_swap_active (ctx, left, seg);
1527 if (first && left->right != NULL)
1529 art_svp_intersect_test_cross (ctx, left, left->right,
1537 ArtActiveSeg *right;
1538 art_boolean first = ART_TRUE;
1540 for (right = seg->right; right != NULL; right = seg->right)
1542 int right_bneg = right->flags & ART_ACTIVE_FLAGS_BNEG;
1544 if (right->x[right_bneg ^ 1] >= x1)
1546 if (right->x[right_bneg] >= x1 &&
1547 x1 * right->a + ctx->y * right->b + right->c <= 0)
1549 if (right->y0 != ctx->y && right->y1 != ctx->y)
1551 art_svp_intersect_break (ctx, right, x1, ctx->y,
1555 art_dprint ("[right]x0=%.16f < x1=%.16f, swapping %08x, %08x\n",
1556 x0, x1, (unsigned long)seg, (unsigned long)right);
1558 art_svp_intersect_swap_active (ctx, seg, right);
1559 if (first && right->left != NULL)
1561 art_svp_intersect_test_cross (ctx, right->left, right,
1571 seg->flags &= ~ART_ACTIVE_FLAGS_OUT;
1575 * art_svp_intersect_insert_line: Insert a line into the active list.
1576 * @ctx: Intersector context.
1577 * @seg: Segment containing line to insert.
1579 * Inserts the line into the intersector context, taking care of any
1580 * intersections, and adding the appropriate horizontal points to the
1584 art_svp_intersect_insert_line (ArtIntersectCtx *ctx, ArtActiveSeg *seg)
1586 if (seg->y1 == seg->y0)
1589 art_dprint ("art_svp_intersect_insert_line(%d): %08x is horizontal\n", seg->flags&ART_ACTIVE_FLAGS_IN_ACTIVE, (unsigned long)seg);
1591 art_svp_intersect_horiz (ctx, seg, seg->x[0], seg->x[1]);
1596 art_dprint ("art_svp_intersect_insert_line(%d): %08x is non-horizontal\n", seg->flags&ART_ACTIVE_FLAGS_IN_ACTIVE, (unsigned long)seg);
1598 art_svp_intersect_insert_cross (ctx, seg);
1599 art_svp_intersect_add_horiz (ctx, seg);
1602 seg->flags |= ART_ACTIVE_FLAGS_IN_ACTIVE; //q
1606 art_svp_intersect_process_intersection (ArtIntersectCtx *ctx,
1609 int n_stack = --seg->n_stack;
1610 seg->x[1] = seg->stack[n_stack - 1].x;
1611 seg->y1 = seg->stack[n_stack - 1].y;
1612 seg->x[0] = seg->stack[n_stack].x;
1613 seg->y0 = seg->stack[n_stack].y;
1614 seg->horiz_x = seg->x[0];
1616 art_dprint("process intersection %08x (%.16f,%.16f-%.16f,%.16f)\n", seg, seg->x[0], seg->y0, seg->x[1], seg->y1);
1618 art_svp_intersect_insert_line (ctx, seg);
1622 art_svp_intersect_advance_cursor (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
1623 ArtPriPoint *pri_pt)
1625 const ArtSVPSeg *in_seg = seg->in_seg;
1626 int in_curs = seg->in_curs;
1627 ArtSvpWriter *swr = seg->flags & ART_ACTIVE_FLAGS_OUT ? ctx->out : NULL;
1630 swr->add_point (swr, seg->seg_id, seg->x[1], seg->y1);
1631 if (in_curs + 1 >= in_seg->n_points)
1633 ArtActiveSeg *left = seg->left, *right = seg->right;
1637 swr->close_segment (swr, seg->seg_id);
1638 seg->flags &= ~ART_ACTIVE_FLAGS_OUT;
1640 seg->flags |= ART_ACTIVE_FLAGS_DEL;
1641 art_svp_intersect_add_horiz (ctx, seg);
1642 art_svp_intersect_active_delete (ctx, seg);
1643 if (left != NULL && right != NULL)
1644 art_svp_intersect_test_cross (ctx, left, right,
1645 ART_BREAK_LEFT | ART_BREAK_RIGHT);
1650 seg->horiz_x = seg->x[1];
1652 art_svp_intersect_setup_seg (seg, pri_pt);
1653 art_pri_insert (ctx->pq, pri_pt);
1654 art_svp_intersect_insert_line (ctx, seg);
1659 art_svp_intersect_add_seg (ArtIntersectCtx *ctx, const ArtSVPSeg *in_seg)
1661 ArtActiveSeg *seg = art_new (ArtActiveSeg, 1);
1664 ArtActiveSeg *beg_range;
1665 ArtActiveSeg *last = NULL;
1666 ArtActiveSeg *left, *right;
1667 ArtPriPoint *pri_pt = art_new (ArtPriPoint, 1);
1670 seg->in_seg = in_seg;
1673 seg->n_stack_max = 4;
1674 seg->stack = art_new (ArtPoint, seg->n_stack_max);
1676 seg->horiz_delta_wind = 0;
1680 pri_pt->user_data = seg;
1681 art_svp_intersect_setup_seg (seg, pri_pt);
1682 art_pri_insert (ctx->pq, pri_pt);
1684 /* Find insertion place for new segment */
1685 /* This is currently a left-to-right scan, but should be replaced
1686 with a binary search as soon as it's validated. */
1688 x0 = in_seg->points[0].x;
1689 y0 = in_seg->points[0].y;
1691 for (test = ctx->active_head; test != NULL; test = test->right)
1694 int test_bneg = test->flags & ART_ACTIVE_FLAGS_BNEG;
1696 if (x0 < test->x[test_bneg])
1698 if (x0 < test->x[test_bneg ^ 1]) {
1700 art_dprint("inserting segment %08x before %08x (x0 < xmin)\n", seg, test);
1704 d = x0 * test->a + y0 * test->b + test->c;
1707 art_dprint("inserting segment %08x before %08x (d = %.16f)\n", seg, test, d);
1712 art_dprint("segment %08x is to the right of %08x d=%.16f\n", seg, test, d);
1716 d = x0 * test->a + y0 * test->b + test->c;
1717 art_dprint("segment %08x is to the right of %08x d=%.16f (not used)\n", seg, test, d);
1723 left = art_svp_intersect_add_point (ctx, x0, y0, last, ART_BREAK_LEFT | ART_BREAK_RIGHT);
1726 art_dprint("left=%08x x0=%.16f y=%.16f\n", left, x0, y0);
1732 right = ctx->active_head;
1733 ctx->active_head = seg;
1737 right = left->right;
1744 seg->delta_wind = in_seg->dir ? 1 : -1;
1747 art_svp_intersect_insert_line (ctx, seg);
1752 art_svp_intersect_sanitycheck_winding (ArtIntersectCtx *ctx)
1755 /* At this point, we seem to be getting false positives, so it's
1756 turned off for now. */
1759 int winding_number = 0;
1761 for (seg = ctx->active_head; seg != NULL; seg = seg->right)
1763 /* Check winding number consistency. */
1764 if (seg->flags & ART_ACTIVE_FLAGS_OUT)
1766 if (winding_number != seg->wind_left)
1767 art_warn ("*** art_svp_intersect_sanitycheck_winding: seg %08x has wind_left of %d, expected %d\n",
1768 (unsigned long) seg, seg->wind_left, winding_number);
1769 winding_number = seg->wind_left + seg->delta_wind;
1772 if (winding_number != 0)
1773 art_warn ("*** art_svp_intersect_sanitycheck_winding: non-balanced winding number %d\n",
1780 * art_svp_intersect_horiz_commit: Commit points in horiz list to output.
1781 * @ctx: Intersection context.
1783 * The main function of the horizontal commit is to output new
1784 * points to the output writer.
1786 * This "commit" pass is also where winding numbers are assigned,
1787 * because doing it here provides much greater tolerance for inputs
1788 * which are not in strict SVP order.
1790 * Each cluster in the horiz_list contains both segments that are in
1791 * the active list (ART_ACTIVE_FLAGS_DEL is false) and that are not,
1792 * and are scheduled to be deleted (ART_ACTIVE_FLAGS_DEL is true). We
1793 * need to deal with both.
1796 art_svp_intersect_horiz_commit (ArtIntersectCtx *ctx)
1799 int winding_number = 0; /* initialization just to avoid warning */
1801 double last_x = 0; /* initialization just to avoid warning */
1804 art_dprint ("art_svp_intersect_horiz_commit: y=%.16f\n", ctx->y);
1805 for (seg = ctx->horiz_first; seg != NULL; seg = seg->horiz_right)
1806 art_dprint (" %08x: %.16f %+d segid=%d\n",
1807 (unsigned long)seg, seg->horiz_x, seg->horiz_delta_wind, seg->seg_id);
1810 /* Output points to svp writer. */
1811 for (seg = ctx->horiz_first; seg != NULL;)
1813 /* Find a cluster with common horiz_x, */
1815 double x = seg->horiz_x;
1817 /* Generate any horizontal segments. */
1818 if (horiz_wind != 0)
1820 ArtSvpWriter *swr = ctx->out;
1824 art_dprint ("generate horizontal segment: y=%.16f x=%.16f-%.16f wind=%d horiz_wind=%d\n", ctx->y, last_x, x, winding_number, horiz_wind);
1826 seg_id = swr->add_segment (swr, winding_number, horiz_wind,
1828 swr->add_point (swr, seg_id, x, ctx->y);
1829 swr->close_segment (swr, seg_id);
1832 /* Find first active segment in cluster. */
1834 for (curs = seg; curs != NULL && curs->horiz_x == x;
1835 curs = curs->horiz_right)
1836 if (!(curs->flags & ART_ACTIVE_FLAGS_DEL))
1839 if (curs != NULL && curs->horiz_x == x)
1841 /* There exists at least one active segment in this cluster. */
1843 /* Find beginning of cluster. */
1844 for (; curs->left != NULL; curs = curs->left)
1845 if (curs->left->horiz_x != x)
1848 if (curs->left != NULL)
1849 winding_number = curs->left->wind_left + curs->left->delta_wind;
1856 art_dprint (" curs %08x: winding_number = %d += %d\n", (unsigned long)curs, winding_number, curs->delta_wind);
1858 if (!(curs->flags & ART_ACTIVE_FLAGS_OUT) ||
1859 curs->wind_left != winding_number)
1861 ArtSvpWriter *swr = ctx->out;
1863 if (curs->flags & ART_ACTIVE_FLAGS_OUT)
1865 swr->add_point (swr, curs->seg_id,
1866 curs->horiz_x, ctx->y);
1867 swr->close_segment (swr, curs->seg_id);
1870 curs->seg_id = swr->add_segment (swr, winding_number,
1873 curs->flags |= ART_ACTIVE_FLAGS_OUT;
1875 curs->wind_left = winding_number;
1876 winding_number += curs->delta_wind;
1879 while (curs != NULL && curs->horiz_x == x);
1882 /* Skip past cluster. */
1885 ArtActiveSeg *next = seg->horiz_right;
1887 seg->flags &= ~ART_ACTIVE_FLAGS_IN_HORIZ;
1888 horiz_wind += seg->horiz_delta_wind;
1889 seg->horiz_delta_wind = 0;
1890 if (seg->flags & ART_ACTIVE_FLAGS_DEL)
1892 if (seg->flags & ART_ACTIVE_FLAGS_OUT)
1894 ArtSvpWriter *swr = ctx->out;
1895 swr->close_segment (swr, seg->seg_id);
1897 art_svp_intersect_active_free (seg);
1901 while (seg != NULL && seg->horiz_x == x);
1905 ctx->horiz_first = NULL;
1906 ctx->horiz_last = NULL;
1908 art_svp_intersect_sanitycheck_winding (ctx);
1914 art_svp_intersect_sanitycheck (ArtIntersectCtx *ctx, double y)
1917 ArtActiveSeg *last = NULL;
1921 art_dprint ("Active list (y = %.16f (new: %.16f)):\n", ctx->y, y);
1924 for (seg = ctx->active_head; seg != NULL; seg = seg->right)
1927 char c1=' ',c2=' ',e1=' ';
1928 if(seg->y0 > ctx->y) c1='!';
1929 if(seg->y0 > y) c2='!';
1930 if(seg->y0 > seg->y1) e1='E';
1933 if (seg->left != last)
1935 art_warn ("*** art_svp_intersect_sanitycheck: last=%08x, seg->left=%08x\n",
1936 (unsigned long)last, (unsigned long)seg->left);
1940 /* pairwise compare with previous seg */
1942 /* First the top. */
1943 if (last->y0 < seg->y0)
1950 /* Then the bottom. */
1951 if (last->y1 < seg->y1)
1954 seg->x[(seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1]) ||
1955 last->y1 == seg->y0))
1957 d = last->x[1] * seg->a + last->y1 * seg->b + seg->c;
1958 if (d >= EPSILON_A) {
1959 art_warn ("*** bottom (%.16f, %.16f) of %08x is not clear of %08x to right (d = %g)\n",
1960 last->x[1], last->y1, (unsigned long) last,
1961 (unsigned long) seg, d);
1965 art_dprint("is to the left (d=%.16f of previous x1,y1) of\n", d);
1970 art_dprint("is to the left (previous x1 < next x%d) of\n", (seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1);
1974 else if (last->y1 > seg->y1)
1978 last->x[last->flags & ART_ACTIVE_FLAGS_BNEG]) ||
1979 seg->y1 == last->y0))
1981 d = seg->x[1] * last->a + seg->y1 * last->b + last->c;
1982 if (d < -EPSILON_A) {
1983 art_warn ("*** bottom (%.16f, %.16f) of %08x is not clear of %08x to left (d = %.16f)\n",
1984 seg->x[1], seg->y1, (unsigned long) seg,
1985 (unsigned long) last, d);
1989 art_dprint("is to the left (d=%.16f of next x1,y1) of\n", d);
1994 art_dprint("is to the left (next x1 > previous x%d) of\n", last->flags & ART_ACTIVE_FLAGS_BNEG);
2000 if (last->x[1] - seg->x[1] > EPSILON_A) {
2001 art_warn ("*** bottoms (%.16f, %.16f) of %08x and (%.16f, %.16f) of %08x out of order\n",
2002 last->x[1], last->y1, (unsigned long)last,
2003 seg->x[1], seg->y1, (unsigned long)seg);
2007 art_dprint("is to the left (y1s equal, previous x1 < next x1)\n");
2014 art_dprint ("%c%08x: %c%c %02d (%.16f, %.16f)-(%.16f, %.16f) ",
2016 (unsigned long)seg, c1, c2, seg->flags,
2017 seg->x[0], seg->y0, seg->x[1], seg->y1);
2029 art_svp_intersector (const ArtSVP *in, ArtSvpWriter *out)
2031 ArtIntersectCtx *ctx;
2033 ArtPriPoint *first_point;
2038 if (in->n_segs == 0)
2045 art_dprint("Segments: %d\n", in->n_segs);
2046 for(t=0;t<in->n_segs;t++) {
2047 const ArtSVPSeg* seg = &in->segs[t];
2048 art_dprint("Segment %08x(%d): %d points, %s, BBox: (%f,%f,%f,%f)\n",
2049 seg, t, seg->n_points, seg->dir==0?"UP ":"DOWN",
2050 seg->bbox.x0, seg->bbox.y0, seg->bbox.x1, seg->bbox.y1);
2052 for(p=0;p<seg->n_points;p++) {
2053 ArtPoint* point = &seg->points[p];
2054 art_dprint(" (%.16f,%.16f)\n", point->x, point->y);
2060 ctx = art_new (ArtIntersectCtx, 1);
2063 pq = art_pri_new ();
2066 ctx->active_head = NULL;
2068 ctx->horiz_first = NULL;
2069 ctx->horiz_last = NULL;
2072 first_point = art_new (ArtPriPoint, 1);
2073 first_point->x = in->segs[0].points[0].x;
2074 first_point->y = in->segs[0].points[0].y;
2075 first_point->user_data = NULL;
2076 ctx->y = first_point->y;
2077 art_pri_insert (pq, first_point);
2079 double lasty = -HUGE_VAL;
2080 while (!art_pri_empty (pq))
2082 ArtPriPoint *pri_point = art_pri_choose (pq);
2083 ArtActiveSeg *seg = (ArtActiveSeg *)pri_point->user_data;
2086 art_dprint ("\nIntersector step %d (%d items in pq)\n", count++, pq->n_items);
2089 art_svp_intersect_sanitycheck(ctx, pri_point->y);
2092 art_dprint ("priq choose (%.16f, %.16f) %08x\n", pri_point->x, pri_point->y,
2093 (unsigned long)pri_point->user_data);
2095 art_dprint (" %08x = %.16f,%.16f -> %.16f %.16f\n", seg, seg->x[0], seg->y0, seg->x[1], seg->y1);
2098 //for(t=0;t<pq->n_items;t++) {
2099 // art_dprint("pq[%d] %.16f,%.16f %08x\n",
2103 // pq->items[t]->user_data);
2107 //art_dprint("y=%f %08x\n", pri_point->y, pri_point->user_data);
2108 if (ctx->y != pri_point->y)
2110 art_svp_intersect_horiz_commit (ctx);
2111 ctx->y = pri_point->y;
2114 if(ctx->y < lasty) {
2115 art_warn("y decreased\n");
2121 /* Insert new segment from input */
2122 const ArtSVPSeg *in_seg = 0;
2125 in_seg = &in->segs[ctx->in_curs++];
2126 if(in_seg->n_points > 1)
2128 if(ctx->in_curs == in->n_segs) {
2134 art_dprint("ignoring input segment %08x- it only contains %d point(s)\n",
2135 in_seg, in_seg->n_points);
2142 for(t=0;t<in_seg->n_points-1;t++) {
2143 if(!(in_seg->points[t].y <= in_seg->points[t+1].y)) {
2148 art_warn("bad input: contains a segment with descending y\n");
2149 for(t=0;t<in_seg->n_points;t++) {
2150 art_dprint("%d) %.16f %.16f\n", t, in_seg->points[t].x, in_seg->points[t].y);
2156 art_dprint("insert new segment from input (%.16f,%.16f) dir=%d\n", in_seg->points[0].x, in_seg->points[0].y, in_seg->dir);
2158 art_svp_intersect_add_seg (ctx, in_seg);
2159 if (ctx->in_curs < in->n_segs)
2161 const ArtSVPSeg *next_seg = &in->segs[ctx->in_curs];
2162 pri_point->x = next_seg->points[0].x;
2163 pri_point->y = next_seg->points[0].y;
2164 /* user_data is already NULL */
2165 art_pri_insert (pq, pri_point);
2168 art_free(pri_point);pri_point =0;
2170 art_free(pri_point);pri_point = 0;
2175 int n_stack = seg->n_stack;
2179 art_svp_intersect_process_intersection (ctx, seg);
2180 art_free (pri_point);
2184 art_svp_intersect_advance_cursor (ctx, seg, pri_point);
2190 art_svp_intersect_horiz_commit (ctx);