fix crashes reported by Debian Cylab Mayhem Team
[swftools.git] / lib / art / art_svp_intersect.c
blobeefedbef5084396e0dad9e1b4d14d000fd2fe854
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
21 code.
24 #include <stdio.h>
25 #include <assert.h>
26 #include "config.h"
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
33 much. */
34 #define noSANITYCHECK
36 /* This can be used in production, to prevent hangs. Eventually, it
37 should not be necessary. */
38 #define CHEAP_SANITYCHECK
40 #define noVERBOSE
42 #define noABORT_ON_ERROR
44 #include "art_misc.h"
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;
54 struct _ArtPriQ {
55 int n_items;
56 int n_items_max;
57 ArtPriPoint **items;
60 struct _ArtPriPoint {
61 double x;
62 double y;
63 void *user_data;
66 static ArtPriQ *
67 art_pri_new (void)
69 ArtPriQ *result = art_new (ArtPriQ, 1);
71 result->n_items = 0;
72 result->n_items_max = 16;
73 result->items = art_new (ArtPriPoint *, result->n_items_max);
74 return result;
77 static void
78 art_pri_free (ArtPriQ *pq)
80 art_free (pq->items);
81 art_free (pq);
84 static art_boolean
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 */
93 static void
94 art_pri_bubble_up (ArtPriQ *pq, int vacant, ArtPriPoint *missing)
96 ArtPriPoint **items = pq->items;
97 int parent;
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];
105 vacant = parent;
106 parent = (vacant - 1) >> 1;
109 items[vacant] = missing;
112 static void
113 art_pri_insert (ArtPriQ *pq, ArtPriPoint *point)
115 #ifdef VERBOSE
116 art_dprint("insert into pq %08x: %.16f,%.16f %08x\n", pq, point->x, point->y, point->user_data);
117 #endif
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);
124 static void
125 art_pri_sift_down_from_root (ArtPriQ *pq, ArtPriPoint *missing)
127 ArtPriPoint **items = pq->items;
128 int vacant = 0, child = 2;
129 int n = pq->n_items;
131 while (child < n)
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))
136 child--;
137 items[vacant] = items[child];
138 vacant = child;
139 child = (vacant + 1) << 1;
141 if (child == n)
143 items[vacant] = items[n - 1];
144 vacant = n - 1;
147 art_pri_bubble_up (pq, vacant, missing);
150 static ArtPriPoint *
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]);
156 return result;
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
167 if(!current_svp) {
168 fprintf(stderr, "internal error: no current polygon\n");
169 exit(1);
171 const ArtSVP*svp = current_svp;
172 FILE*fi = fopen("svp.ps", "wb");
173 int i, j;
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");
188 fclose(fi);
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");
194 exit(1);
195 #endif
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
205 winding rule. */
207 struct _ArtSvpWriterRewind {
208 ArtSvpWriter super;
209 ArtWindRule rule;
210 ArtSVP *svp;
211 int n_segs_max;
212 int *n_points_max;
215 static int
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;
220 ArtSVP *svp;
221 ArtSVPSeg *seg;
222 art_boolean left_filled, right_filled;
223 int wind_right = wind_left + delta_wind;
224 int seg_num;
225 const int init_n_points_max = 4;
227 switch (swr->rule)
229 case ART_WIND_RULE_NONZERO:
230 left_filled = (wind_left != 0);
231 right_filled = (wind_right != 0);
232 break;
233 case ART_WIND_RULE_INTERSECT:
234 left_filled = (wind_left > 1);
235 right_filled = (wind_right > 1);
236 break;
237 case ART_WIND_RULE_ODDEVEN:
238 left_filled = (wind_left & 1);
239 right_filled = (wind_right & 1);
240 break;
241 case ART_WIND_RULE_POSITIVE:
242 left_filled = (wind_left > 0);
243 right_filled = (wind_right > 0);
244 break;
245 default:
246 art_die ("Unknown wind rule %d\n", swr->rule);
249 #ifdef VERBOSE
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);
251 #endif
253 if (left_filled == right_filled)
255 /* discard segment now */
256 #ifdef VERBOSE
257 art_dprint (" discarding segment: %d += %d (%.16f, %.16f)\n",
258 wind_left, delta_wind, x, y);
259 #endif
260 return -1;
263 svp = swr->svp;
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) *
270 sizeof(ArtSVPSeg));
271 swr->svp = svp;
272 swr->n_points_max = art_renew (swr->n_points_max, int,
273 swr->n_segs_max);
275 seg = &svp->segs[seg_num];
276 seg->n_points = 1;
277 seg->dir = right_filled;
278 swr->n_points_max[seg_num] = init_n_points_max;
279 seg->bbox.x0 = x;
280 seg->bbox.y0 = y;
281 seg->bbox.x1 = x;
282 seg->bbox.y1 = y;
283 seg->points = art_new (ArtPoint, init_n_points_max);
284 seg->points[0].x = x;
285 seg->points[0].y = y;
286 #ifdef VERBOSE
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");
290 #endif
291 return seg_num;
294 static void
295 art_svp_writer_rewind_add_point (ArtSvpWriter *self, int seg_id,
296 double x, double y)
299 ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
300 ArtSVPSeg *seg;
301 int n_points;
303 #ifdef VERBOSE
304 art_dprint ("swr add_point: %d (%.16f, %.16f)\n", seg_id, x, y);
305 #endif
306 if (seg_id < 0)
307 /* omitted segment */
308 return;
310 seg = &swr->svp->segs[seg_id];
312 if(seg->n_points &&
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",
316 // x, y, seg_id);
317 return;
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) {
330 seg->n_points--;
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,
335 // seg_id);
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);
341 art_report_error();
344 seg->points[n_points].x = x;
345 seg->points[n_points].y = y;
346 if (x < seg->bbox.x0)
347 seg->bbox.x0 = x;
348 if (x > seg->bbox.x1)
349 seg->bbox.x1 = x;
350 seg->bbox.y1 = y;
353 static void
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;
360 ArtSVPSeg *seg;
362 if (seg_id >= 0)
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");
369 #endif
371 #ifdef VERBOSE
372 art_dprint ("swr close_segment: %d\n", seg_id);
373 #endif
376 ArtSVP *
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);
383 art_free (swr);
384 return result;
387 ArtSvpWriter *
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;
396 result->rule = rule;
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.
408 signs:
410 b<0 / | \ b>0, c<0
411 c>0 /\ | /\
412 / \ | / \
413 / \ | /
415 -------------+--------------------
416 /|\
417 \ / | \ /
418 \/ | \/ b<0, c<0
419 b>0 \ | /
420 c>0 \ | /
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
430 list. */
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 {
444 int flags;
445 int wind_left, delta_wind;
446 ArtActiveSeg *left, *right; /* doubly linked list structure */
448 const ArtSVPSeg *in_seg;
449 int in_curs;
451 double x[2];
452 double y0, y1;
453 double a, b, c; /* line equation; ax+by+c = 0 for the line, a^2 + b^2 = 1,
454 and a>0 */
456 /* bottom point and intersection point stack */
457 int n_stack;
458 int n_stack_max;
459 ArtPoint *stack;
461 /* horiz commit list */
462 ArtActiveSeg *horiz_left, *horiz_right;
463 double horiz_x;
464 int horiz_delta_wind;
465 int seg_id;
468 typedef struct _ArtIntersectCtx ArtIntersectCtx;
470 struct _ArtIntersectCtx {
471 const ArtSVP *in;
472 ArtSvpWriter *out;
474 ArtPriQ *pq;
476 ArtActiveSeg *active_head;
478 double y;
479 ArtActiveSeg *horiz_first;
480 ArtActiveSeg *horiz_last;
482 /* segment index of next input segment to be added to pri q */
483 int in_curs;
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
496 * cursor.
498 static void
499 art_svp_intersect_setup_seg (ArtActiveSeg *seg, ArtPriPoint *pri_pt)
501 const ArtSVPSeg *in_seg = seg->in_seg;
502 int in_curs = 0;
503 double x0, y0, x1, y1;
504 double dx, dy, s;
505 double a, b, r2;
507 //do {
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
512 // );
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;
518 pri_pt->x = x1;
519 pri_pt->y = y1;
520 dx = x1 - x0;
521 dy = y1 - y0;
522 r2 = dx * dx + dy * dy;
523 if(r2 == 0) {
524 //art_warn("segment %08x has zero length\n", seg);
526 s = r2 == 0 ? 1 : 1 / sqrt (r2);
527 seg->a = a = dy * s;
528 seg->b = b = -dx * s;
529 seg->c = -(a * x0 + b * y0);
530 seg->flags = (seg->flags & ~ART_ACTIVE_FLAGS_BNEG) | (dx > 0);
531 seg->x[0] = x0;
532 seg->x[1] = x1;
533 seg->y0 = y0;
534 seg->y1 = y1;
535 seg->n_stack = 1;
536 seg->stack[0].x = x1;
537 seg->stack[0].y = y1;
539 if(y1 < y0) {
540 art_warn("art_svp_intersect.c: bad input polygon %08x, contains decreasing y values\n", seg->in_seg);
541 art_report_error();
543 #ifdef VERBOSE
544 art_dprint("segment %08x (top: %.16f,%.16f) starts with endpoint at %.16f,%.16f\n", seg,
545 x0, y0,
546 x1, y1);
547 #endif
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
556 * order.
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.
565 static void
566 art_svp_intersect_add_horiz (ArtIntersectCtx *ctx, ArtActiveSeg *seg)
568 ArtActiveSeg **pp = &ctx->horiz_last;
569 ArtActiveSeg *place;
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);
579 return;
581 seg->flags |= ART_ACTIVE_FLAGS_IN_HORIZ;
582 #endif
584 #ifdef VERBOSE
585 art_dprint ("add_horiz %08x, x = %.16f\n", (unsigned long) seg, seg->horiz_x);
586 #endif
587 for (place = *pp; place != NULL && (place->horiz_x > seg->horiz_x ||
588 (place->horiz_x == seg->horiz_x &&
589 place->b < seg->b));
590 place = *pp)
592 place_right = place;
593 pp = &place->horiz_left;
595 *pp = seg;
596 seg->horiz_left = place;
597 seg->horiz_right = place_right;
599 if (place == NULL)
600 ctx->horiz_first = seg;
601 else
602 place->horiz_right = seg;
604 #ifdef VERBOSE
605 art_dprint("horiz_list:\n");
606 ArtActiveSeg*s = ctx->horiz_first;
607 while(s) {
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);
610 s = s->horiz_right;
612 #endif
615 static void
616 art_svp_intersect_push_pt (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
617 double x, double y)
619 ArtPriPoint *pri_pt;
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;
628 seg->n_stack++;
630 #ifdef VERBOSE
631 int t;
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);
636 else
637 art_dprint("art_svp_intersect_push_pt %08x |new %.16f,%.16f\n", seg, seg->stack[t].x, seg->stack[t].y);
639 #endif
640 #ifdef VERBOSE
641 art_dprint("(shortening segment %08x to %.16f,%.16f)\n", seg, x, y);
642 #endif
644 if(seg->stack[seg->n_stack-1].y == seg->y0) {
645 art_warn("duplicate y coordinate (=y0) in point stack\n");
648 if(n_stack) {
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");
651 art_report_error();
656 seg->x[1] = x;
657 seg->y1 = y;
659 pri_pt = art_new (ArtPriPoint, 1);
660 pri_pt->x = x;
661 pri_pt->y = y;
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
673 * the bottom's.
675 * Return value: x coordinate of break point.
677 static double
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;
684 double x;
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))
699 #ifdef VERBOSE
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");
702 #endif
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);
708 art_report_error();
709 return x;
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 ? */
716 if(x0<x1) {
717 if(x<x0 || x>x1) {
718 art_warn("bad x value %.16f in intersect_break: not between %.16f and %.16f\n", x, x0, x1);
719 art_report_error();
721 } else {
722 if(x<x1 || x>x0) {
723 art_warn("bad x value %.16f in intersect_break: not between %.16f and %.16f\n", x, x1, x0);
724 art_report_error();
729 if (y > ctx->y)
730 art_svp_intersect_push_pt (ctx, seg, x, y);
731 else
733 if(y < ctx->y) {
734 art_warn("intersect_break at a y above the current scanline (%.16f < %.16f)", y, ctx->y);
735 art_report_error();
737 seg->x[0] = x;
738 seg->y0 = y;
739 seg->horiz_x = x;
740 art_svp_intersect_add_horiz (ctx, seg);
743 return x;
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;
763 double d;
764 double new_x;
765 ArtActiveSeg *test, *result = NULL;
766 double x_test;
768 left = seg;
769 if (left == NULL)
770 right = ctx->active_head;
771 else
772 right = left->right;
773 left_live = (break_flags & ART_BREAK_LEFT) && (left != NULL);
774 right_live = (break_flags & ART_BREAK_RIGHT) && (right != NULL);
776 #ifdef VERBOSE
777 double dd = 0;
778 if(seg)
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);
784 #endif
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)
804 #ifdef VERBOSE
805 art_dprint(" left: %08x (%d) right: %08x (%d)\n", left, left_live, right, right_live);
806 #endif
807 if (left_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;
815 if (d < EPSILON_A)
817 new_x = art_svp_intersect_break (ctx, left, x_min, y,
818 ART_BREAK_LEFT);
819 #ifdef VERBOSE
820 art_dprint("break %08x at x<=%.16f,y=%.16f, new_x=%f\n", left, x_min, y, new_x);
821 #endif
822 if (new_x > x_max)
824 x_max = new_x;
825 right_live = (right != NULL);
827 else if (new_x < x_min)
828 x_min = new_x;
829 left = left->left;
830 left_live = (left != NULL);
832 else
833 left_live = ART_FALSE;
835 else
836 left_live = ART_FALSE;
838 else if (right_live)
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;
846 if (d > -EPSILON_A)
848 new_x = art_svp_intersect_break (ctx, right, x_max, y,
849 ART_BREAK_RIGHT);
850 #ifdef VERBOSE
851 art_dprint("break %08x at x>=%.16f,y=%.16f, new_x=%f\n", right, x_max, y, new_x);
852 #endif
853 if (new_x < x_min)
855 x_min = new_x;
856 left_live = (left != NULL);
858 else if (new_x >= x_max)
859 x_max = new_x;
860 right = right->right;
861 right_live = (right != NULL);
863 else
864 right_live = ART_FALSE;
866 else
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;
877 result = left;
878 if (test != NULL && test != right)
880 if (y == test->y0)
881 x_test = test->x[0];
882 else if(y == test->y1)
883 x_test = test->x[1];
884 else {
885 art_warn ("internal error in add_point: y=%.16f is between %.16f and %.16f\n", y, test->y0, test->y1);
886 x_test = test->x[1];
887 art_report_error();
890 for (;;)
892 if (x_test <= x)
893 result = test;
894 test = test->right;
895 if (test == right)
896 break;
898 /* FIXME the following code doesn't do anything (?) */
899 new_x = x_test;
900 if (new_x < x_test)
902 art_warn ("art_svp_intersect_add_point: non-ascending x\n");
904 x_test = new_x;
907 return result;
910 static void
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");
917 art_report_error();
920 right_seg->left = left_seg->left;
921 if (right_seg->left != NULL)
922 right_seg->left->right = right_seg;
923 else
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. */
938 unsigned char xx[8];
939 *(double*)xx = x;
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;
942 return *(double*)xx;
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.
958 static art_boolean
959 art_svp_intersect_test_cross (ArtIntersectCtx *ctx,
960 ArtActiveSeg *left_seg, ArtActiveSeg *right_seg,
961 int break_flags)
963 double left_x0, left_y0, left_x1;
964 double left_y1 = left_seg->y1;
965 double right_y1 = right_seg->y1;
966 double d;
968 const ArtSVPSeg *in_seg;
969 int in_curs;
970 double d0, d1, t;
971 double x, y; /* intersection point */
973 #ifdef VERBOSE
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
993 #endif
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
999 instabilities */
1000 double dd = right_seg->x[0] * left_seg->a + right_seg->y0 * left_seg->b + left_seg->c;
1001 if(dd < 0) {
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,
1005 // dd);
1008 #endif
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];
1022 if (left_x1 <
1023 right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
1024 left_y1 == right_seg->y0)
1025 return ART_FALSE;
1026 d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
1027 if (d < -EPSILON_A)
1029 return ART_FALSE;
1030 else if (d < EPSILON_A) /* should we use zero here? */
1032 #ifdef VERBOSE
1033 art_dprint("break %08x because of common top point, left_y1 < right_y1\n", right_seg);
1034 #endif
1035 /* I'm unsure about the break flags here. */
1036 double right_x1 = art_svp_intersect_break (ctx, right_seg,
1037 left_x1, left_y1,
1038 ART_BREAK_RIGHT);
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) {
1043 return ART_FALSE;
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)
1055 return ART_FALSE;
1056 d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
1057 if (d > EPSILON_A)
1058 return ART_FALSE;
1059 else if (d > -EPSILON_A) /* should we use zero here? */
1061 #ifdef VERBOSE
1062 art_dprint("break %08x because of common top point, left_y1 > right_y1\n", left_seg);
1063 #endif
1064 /* See above regarding break flags. */
1065 double left_x1 = art_svp_intersect_break (ctx, left_seg,
1066 right_x1, right_y1,
1067 ART_BREAK_LEFT);
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) {
1073 return ART_FALSE;
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)
1084 return ART_FALSE;
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;
1093 return ART_TRUE;
1096 if (left_y1 < right_y1)
1098 /* Test left (x1, y1) against right segment */
1099 double left_x1 = left_seg->x[1];
1101 if (left_x1 <
1102 right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
1103 left_y1 == right_seg->y0)
1104 return ART_FALSE;
1106 if(left_y1 < right_seg->y0) {
1107 art_warn("left_y1 < right_seg->y0\n");
1108 return ART_FALSE;
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;
1114 if (d < -EPSILON_A)
1115 return ART_FALSE;
1116 else if (d < EPSILON_A)
1118 #ifdef VERBOSE
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);
1123 #endif
1124 double right_x1 = art_svp_intersect_break (ctx, right_seg,
1125 left_x1, left_y1,
1126 ART_BREAK_RIGHT);
1127 #ifdef VERBOSE
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);
1132 #endif
1133 /* this is always true if _break uses x_ref clipping */
1134 if (left_x1 <= right_x1)
1135 return ART_FALSE;
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)
1145 return ART_FALSE;
1147 if(right_y1 < left_seg->y0) {
1148 art_warn("left_y1 < right_seg->y0\n");
1149 return ART_FALSE;
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;
1155 if (d > EPSILON_A)
1156 return ART_FALSE;
1157 else if (d > -EPSILON_A)
1159 #ifdef VERBOSE
1160 art_dprint("break %08x because left_y1 > right_y1\n", right_seg);
1161 #endif
1162 double left_x1 = art_svp_intersect_break (ctx, left_seg,
1163 right_x1, right_y1,
1164 ART_BREAK_LEFT);
1165 /* this is always true if _break uses x_ref clipping */
1166 if (left_x1 <= right_x1)
1167 return ART_FALSE;
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)
1176 return ART_FALSE;
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;
1189 #if 0
1190 /* check for endpoint = firstpoint cases */
1191 if(left_seg->y0 == right_seg->y1 && left_seg->x[0] == right_seg->x[1])
1192 return ART_FALSE;
1193 if(left_seg->y1 == right_seg->y0 && left_seg->x[1] == right_seg->x[0])
1194 return ART_FALSE;
1195 #endif
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;
1199 if (d0 == d1)
1201 x = left_x0;
1202 y = left_y0;
1204 else
1206 /* Is this division always safe? It could possibly overflow. */
1207 t = d0 / (d0 - d1);
1208 if (t <= 0)
1210 x = left_x0;
1211 y = left_y0;
1213 else if (t >= 1)
1215 x = left_x1;
1216 y = left_y1;
1218 else
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];
1229 y = right_seg->y0;
1231 else if (y > right_seg->y1)
1233 x = right_seg->x[1];
1234 y = right_seg->y1;
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);
1245 #ifdef VERBOSE
1246 art_dprint ("intersect at %.16f %.16f\n", x, y);
1247 #endif
1250 if(y < ctx->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);
1261 return ART_FALSE;
1264 if(y > left_seg->y1) {
1265 /* not within the subsegment we're currently looking into- this is not an intersection */
1266 return ART_FALSE;
1269 if (y == left_seg->y0)
1271 if (y != right_seg->y0)
1273 #ifdef VERBOSE
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);
1276 #endif
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,
1280 break_flags);
1282 else
1284 #ifdef VERBOSE
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);
1287 #endif
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
1295 priq. */
1296 ArtActiveSeg *winner, *loser;
1298 /* Choose "most vertical" segement */
1299 if (left_seg->a > right_seg->a)
1301 winner = left_seg;
1302 loser = right_seg;
1304 else
1306 winner = right_seg;
1307 loser = left_seg;
1309 #ifdef VERBOSE
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]);
1313 #endif
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);
1320 return ART_TRUE;
1323 else if (y == right_seg->y0)
1325 #ifdef VERBOSE
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);
1335 #endif
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,
1339 break_flags);
1341 else
1343 #ifdef VERBOSE
1344 art_dprint ("Inserting (%.16f, %.16f) into %08x, %08x\n",
1345 x, y, (unsigned long)left_seg, (unsigned long)right_seg);
1346 #endif
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);
1355 return ART_FALSE;
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;
1370 if (left != NULL)
1371 left->right = right;
1372 else
1373 ctx->active_head = right;
1374 if (right != NULL)
1375 right->left = left;
1379 * art_svp_intersect_active_free: Free an active segment.
1380 * @seg: Segment to delete.
1382 * Frees @seg.
1384 static /* todo inline */ void
1385 art_svp_intersect_active_free (ArtActiveSeg *seg)
1387 art_free (seg->stack);
1388 #ifdef VERBOSE
1389 art_dprint ("Freeing %08x\n", (unsigned long) seg);
1390 #else
1391 // !!!
1392 art_free (seg);
1393 #endif
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.
1402 static void
1403 art_svp_intersect_insert_cross (ArtIntersectCtx *ctx,
1404 ArtActiveSeg *seg)
1406 ArtActiveSeg *left = seg, *right = seg;
1408 for (;;)
1410 if (left != NULL)
1412 ArtActiveSeg *leftc;
1414 for (leftc = left->left; leftc != NULL; leftc = leftc->left)
1415 if (!(leftc->flags & ART_ACTIVE_FLAGS_DEL))
1416 break;
1417 if (leftc != NULL &&
1418 art_svp_intersect_test_cross (ctx, leftc, left,
1419 ART_BREAK_LEFT))
1421 if (left == right || right == NULL)
1422 right = left->right;
1424 else
1426 left = NULL;
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))
1435 break;
1436 if (rightc != NULL &&
1437 art_svp_intersect_test_cross (ctx, right, rightc,
1438 ART_BREAK_RIGHT))
1440 if (left == right || left == NULL)
1441 left = right->left;
1443 else
1445 right = NULL;
1448 else
1449 break;
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.
1463 static void
1464 art_svp_intersect_horiz (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
1465 double x0, double x1)
1467 ArtActiveSeg *hs;
1469 if (x0 == x1)
1470 return;
1472 #ifdef VERBOSE
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);
1475 #endif
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;
1487 hs->horiz_x = x0;
1488 hs->horiz_delta_wind = seg->delta_wind;
1489 hs->stack = NULL;
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. */
1496 hs->a = 0.0;
1497 hs->b = 0.0;
1498 hs->c = 0.0;
1500 seg->horiz_delta_wind -= seg->delta_wind;
1502 art_svp_intersect_add_horiz (ctx, hs);
1504 if (x0 > x1)
1506 ArtActiveSeg *left;
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)
1514 break;
1515 if (left->x[left_bneg ^ 1] <= x1 &&
1516 x1 * left->a + ctx->y * left->b + left->c >= 0)
1517 break;
1518 if (left->y0 != ctx->y && left->y1 != ctx->y)
1520 art_svp_intersect_break (ctx, left, x1, ctx->y, ART_BREAK_LEFT);
1522 #ifdef VERBOSE
1523 art_dprint ("x0=%.16f > x1=%.16f, swapping %08x, %08x\n",
1524 x0, x1, (unsigned long)left, (unsigned long)seg);
1525 #endif
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,
1530 ART_BREAK_RIGHT);
1531 first = ART_FALSE;
1535 else
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)
1545 break;
1546 if (right->x[right_bneg] >= x1 &&
1547 x1 * right->a + ctx->y * right->b + right->c <= 0)
1548 break;
1549 if (right->y0 != ctx->y && right->y1 != ctx->y)
1551 art_svp_intersect_break (ctx, right, x1, ctx->y,
1552 ART_BREAK_LEFT);
1554 #ifdef VERBOSE
1555 art_dprint ("[right]x0=%.16f < x1=%.16f, swapping %08x, %08x\n",
1556 x0, x1, (unsigned long)seg, (unsigned long)right);
1557 #endif
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,
1562 ART_BREAK_RIGHT);
1563 first = ART_FALSE;
1568 seg->x[0] = x1;
1569 seg->x[1] = x1;
1570 seg->horiz_x = x1;
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
1581 * active list.
1583 static void
1584 art_svp_intersect_insert_line (ArtIntersectCtx *ctx, ArtActiveSeg *seg)
1586 if (seg->y1 == seg->y0)
1588 #ifdef VERBOSE
1589 art_dprint ("art_svp_intersect_insert_line(%d): %08x is horizontal\n", seg->flags&ART_ACTIVE_FLAGS_IN_ACTIVE, (unsigned long)seg);
1590 #endif
1591 art_svp_intersect_horiz (ctx, seg, seg->x[0], seg->x[1]);
1593 else
1595 #ifdef VERBOSE
1596 art_dprint ("art_svp_intersect_insert_line(%d): %08x is non-horizontal\n", seg->flags&ART_ACTIVE_FLAGS_IN_ACTIVE, (unsigned long)seg);
1597 #endif
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
1605 static void
1606 art_svp_intersect_process_intersection (ArtIntersectCtx *ctx,
1607 ArtActiveSeg *seg)
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];
1615 #ifdef VERBOSE
1616 art_dprint("process intersection %08x (%.16f,%.16f-%.16f,%.16f)\n", seg, seg->x[0], seg->y0, seg->x[1], seg->y1);
1617 #endif
1618 art_svp_intersect_insert_line (ctx, seg);
1621 static void
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;
1629 if (swr != 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;
1635 #if 0
1636 if (swr != NULL)
1637 swr->close_segment (swr, seg->seg_id);
1638 seg->flags &= ~ART_ACTIVE_FLAGS_OUT;
1639 #endif
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);
1646 art_free (pri_pt);
1648 else
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);
1658 static void
1659 art_svp_intersect_add_seg (ArtIntersectCtx *ctx, const ArtSVPSeg *in_seg)
1661 ArtActiveSeg *seg = art_new (ArtActiveSeg, 1);
1662 ArtActiveSeg *test;
1663 double x0, y0;
1664 ArtActiveSeg *beg_range;
1665 ArtActiveSeg *last = NULL;
1666 ArtActiveSeg *left, *right;
1667 ArtPriPoint *pri_pt = art_new (ArtPriPoint, 1);
1669 seg->flags = 0;
1670 seg->in_seg = in_seg;
1671 seg->in_curs = 0;
1673 seg->n_stack_max = 4;
1674 seg->stack = art_new (ArtPoint, seg->n_stack_max);
1676 seg->horiz_delta_wind = 0;
1678 seg->wind_left = 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;
1690 beg_range = NULL;
1691 for (test = ctx->active_head; test != NULL; test = test->right)
1693 double d;
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]) {
1699 #ifdef VERBOSE
1700 art_dprint("inserting segment %08x before %08x (x0 < xmin)\n", seg, test);
1701 #endif
1702 break;
1704 d = x0 * test->a + y0 * test->b + test->c;
1705 if (d < 0) {
1706 #ifdef VERBOSE
1707 art_dprint("inserting segment %08x before %08x (d = %.16f)\n", seg, test, d);
1708 #endif
1709 break;
1711 #ifdef VERBOSE
1712 art_dprint("segment %08x is to the right of %08x d=%.16f\n", seg, test, d);
1713 #endif
1714 } else {
1715 #ifdef VERBOSE
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);
1718 #endif
1720 last = test;
1723 left = art_svp_intersect_add_point (ctx, x0, y0, last, ART_BREAK_LEFT | ART_BREAK_RIGHT);
1725 #ifdef VERBOSE
1726 art_dprint("left=%08x x0=%.16f y=%.16f\n", left, x0, y0);
1727 #endif
1729 seg->left = left;
1730 if (left == NULL)
1732 right = ctx->active_head;
1733 ctx->active_head = seg;
1735 else
1737 right = left->right;
1738 left->right = seg;
1740 seg->right = right;
1741 if (right != NULL)
1742 right->left = seg;
1744 seg->delta_wind = in_seg->dir ? 1 : -1;
1745 seg->horiz_x = x0;
1747 art_svp_intersect_insert_line (ctx, seg);
1750 #ifdef SANITYCHECK
1751 static void
1752 art_svp_intersect_sanitycheck_winding (ArtIntersectCtx *ctx)
1754 #if 0
1755 /* At this point, we seem to be getting false positives, so it's
1756 turned off for now. */
1758 ArtActiveSeg *seg;
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",
1774 winding_number);
1775 #endif
1777 #endif
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.
1795 static void
1796 art_svp_intersect_horiz_commit (ArtIntersectCtx *ctx)
1798 ArtActiveSeg *seg;
1799 int winding_number = 0; /* initialization just to avoid warning */
1800 int horiz_wind = 0;
1801 double last_x = 0; /* initialization just to avoid warning */
1803 #ifdef VERBOSE
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);
1808 #endif
1810 /* Output points to svp writer. */
1811 for (seg = ctx->horiz_first; seg != NULL;)
1813 /* Find a cluster with common horiz_x, */
1814 ArtActiveSeg *curs;
1815 double x = seg->horiz_x;
1817 /* Generate any horizontal segments. */
1818 if (horiz_wind != 0)
1820 ArtSvpWriter *swr = ctx->out;
1821 int seg_id;
1823 #ifdef VERBOSE
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);
1825 #endif
1826 seg_id = swr->add_segment (swr, winding_number, horiz_wind,
1827 last_x, ctx->y);
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))
1837 break;
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)
1846 break;
1848 if (curs->left != NULL)
1849 winding_number = curs->left->wind_left + curs->left->delta_wind;
1850 else
1851 winding_number = 0;
1855 #ifdef VERBOSE
1856 art_dprint (" curs %08x: winding_number = %d += %d\n", (unsigned long)curs, winding_number, curs->delta_wind);
1857 #endif
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,
1871 curs->delta_wind,
1872 x, ctx->y);
1873 curs->flags |= ART_ACTIVE_FLAGS_OUT;
1875 curs->wind_left = winding_number;
1876 winding_number += curs->delta_wind;
1877 curs = curs->right;
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);
1899 seg = next;
1901 while (seg != NULL && seg->horiz_x == x);
1903 last_x = x;
1905 ctx->horiz_first = NULL;
1906 ctx->horiz_last = NULL;
1907 #ifdef SANITYCHECK
1908 art_svp_intersect_sanitycheck_winding (ctx);
1909 #endif
1912 #ifdef SANITYCHECK
1913 static void
1914 art_svp_intersect_sanitycheck (ArtIntersectCtx *ctx, double y)
1916 ArtActiveSeg *seg;
1917 ArtActiveSeg *last = NULL;
1918 double d;
1920 #ifdef VERbOSE
1921 art_dprint ("Active list (y = %.16f (new: %.16f)):\n", ctx->y, y);
1922 #endif
1924 for (seg = ctx->active_head; seg != NULL; seg = seg->right)
1926 #ifdef VERBOSE
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';
1931 #endif
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);
1938 if (last != NULL)
1940 /* pairwise compare with previous seg */
1942 /* First the top. */
1943 if (last->y0 < seg->y0)
1946 else
1950 /* Then the bottom. */
1951 if (last->y1 < seg->y1)
1953 if (!((last->x[1] <
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);
1962 art_report_error();
1963 } else {
1964 #ifdef VERBOSE
1965 art_dprint("is to the left (d=%.16f of previous x1,y1) of\n", d);
1966 #endif
1968 } else {
1969 #ifdef VERBOSE
1970 art_dprint("is to the left (previous x1 < next x%d) of\n", (seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1);
1971 #endif
1974 else if (last->y1 > seg->y1)
1977 if (!((seg->x[1] >
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);
1986 art_report_error();
1987 } else {
1988 #ifdef VERBOSE
1989 art_dprint("is to the left (d=%.16f of next x1,y1) of\n", d);
1990 #endif
1992 } else {
1993 #ifdef VERBOSE
1994 art_dprint("is to the left (next x1 > previous x%d) of\n", last->flags & ART_ACTIVE_FLAGS_BNEG);
1995 #endif
1998 else
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);
2004 art_report_error();
2005 } else {
2006 #ifdef VERBOSE
2007 art_dprint("is to the left (y1s equal, previous x1 < next x1)\n");
2008 #endif
2013 #ifdef VERBOSE
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);
2018 #endif
2020 last = seg;
2022 #ifdef VERBOSE
2023 art_dprint("\n");
2024 #endif
2026 #endif
2028 void
2029 art_svp_intersector (const ArtSVP *in, ArtSvpWriter *out)
2031 ArtIntersectCtx *ctx;
2032 ArtPriQ *pq;
2033 ArtPriPoint *first_point;
2034 #ifdef VERBOSE
2035 int count = 0;
2036 #endif
2038 if (in->n_segs == 0)
2039 return;
2041 current_svp = in;
2043 #ifdef VERBOSE
2044 int t;
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);
2051 int p;
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);
2057 art_dprint("\n");
2058 #endif
2060 ctx = art_new (ArtIntersectCtx, 1);
2061 ctx->in = in;
2062 ctx->out = out;
2063 pq = art_pri_new ();
2064 ctx->pq = pq;
2066 ctx->active_head = NULL;
2068 ctx->horiz_first = NULL;
2069 ctx->horiz_last = NULL;
2071 ctx->in_curs = 0;
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;
2085 #ifdef VERBOSE
2086 art_dprint ("\nIntersector step %d (%d items in pq)\n", count++, pq->n_items);
2087 #endif
2088 #ifdef SANITYCHECK
2089 art_svp_intersect_sanitycheck(ctx, pri_point->y);
2090 #endif
2091 #ifdef VERBOSE
2092 art_dprint ("priq choose (%.16f, %.16f) %08x\n", pri_point->x, pri_point->y,
2093 (unsigned long)pri_point->user_data);
2094 if(seg) {
2095 art_dprint (" %08x = %.16f,%.16f -> %.16f %.16f\n", seg, seg->x[0], seg->y0, seg->x[1], seg->y1);
2097 //int t;
2098 //for(t=0;t<pq->n_items;t++) {
2099 // art_dprint("pq[%d] %.16f,%.16f %08x\n",
2100 // t,
2101 // pq->items[t]->x,
2102 // pq->items[t]->y,
2103 // pq->items[t]->user_data);
2105 #endif
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");
2116 art_report_error();
2119 if (seg == NULL)
2121 /* Insert new segment from input */
2122 const ArtSVPSeg *in_seg = 0;
2124 while(1) {
2125 in_seg = &in->segs[ctx->in_curs++];
2126 if(in_seg->n_points > 1)
2127 break;
2128 if(ctx->in_curs == in->n_segs) {
2129 in_seg = 0;
2130 break;
2133 #ifdef VERBOSE
2134 art_dprint("ignoring input segment %08x- it only contains %d point(s)\n",
2135 in_seg, in_seg->n_points);
2136 #endif
2139 if(in_seg) {
2140 int t;
2141 int error = 0;
2142 for(t=0;t<in_seg->n_points-1;t++) {
2143 if(!(in_seg->points[t].y <= in_seg->points[t+1].y)) {
2144 error = 1;
2147 if(error) {
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);
2152 art_report_error();
2155 #ifdef VERBOSE
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);
2157 #endif
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);
2167 else
2168 art_free(pri_point);pri_point =0;
2169 } else {
2170 art_free(pri_point);pri_point = 0;
2173 else
2175 int n_stack = seg->n_stack;
2177 if (n_stack > 1)
2179 art_svp_intersect_process_intersection (ctx, seg);
2180 art_free (pri_point);
2182 else
2184 art_svp_intersect_advance_cursor (ctx, seg, pri_point);
2187 lasty = ctx->y;
2190 art_svp_intersect_horiz_commit (ctx);
2192 art_pri_free (pq);
2193 art_free (ctx);
2194 current_svp = 0;