source: trunk/third/libart_lgpl/art_svp_intersect.c @ 18256

Revision 18256, 44.9 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18255, which included commits to RCS files with non-trunk default branches.
Line 
1/* Libart_LGPL - library of basic graphic primitives
2 * Copyright (C) 2001 Raph Levien
3 *
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.
8 *
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.
13 *
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.
18 */
19
20/* This file contains a testbed implementation of the new intersection
21   code.
22*/
23
24#include "config.h"
25#include "art_svp_intersect.h"
26
27#include <math.h> /* for sqrt */
28
29/* Sanitychecking verifies the main invariant on every priority queue
30   point. Do not use in production, as it slows things down way too
31   much. */
32#define noSANITYCHECK
33
34/* This can be used in production, to prevent hangs. Eventually, it
35   should not be necessary. */
36#define CHEAP_SANITYCHECK
37
38#define noVERBOSE
39
40#include "art_misc.h"
41
42/* A priority queue - perhaps move to a separate file if it becomes
43   needed somewhere else */
44
45#define ART_PRIQ_USE_HEAP
46
47typedef struct _ArtPriQ ArtPriQ;
48typedef struct _ArtPriPoint ArtPriPoint;
49
50struct _ArtPriQ {
51  int n_items;
52  int n_items_max;
53  ArtPriPoint **items;
54};
55
56struct _ArtPriPoint {
57  double x;
58  double y;
59  void *user_data;
60};
61
62static ArtPriQ *
63art_pri_new (void)
64{
65  ArtPriQ *result = art_new (ArtPriQ, 1);
66
67  result->n_items = 0;
68  result->n_items_max = 16;
69  result->items = art_new (ArtPriPoint *, result->n_items_max);
70  return result;
71}
72
73static void
74art_pri_free (ArtPriQ *pq)
75{
76  art_free (pq->items);
77  art_free (pq);
78}
79
80static art_boolean
81art_pri_empty (ArtPriQ *pq)
82{
83  return pq->n_items == 0;
84}
85
86#ifdef ART_PRIQ_USE_HEAP
87
88/* This heap implementation is based on Vasek Chvatal's course notes:
89   http://www.cs.rutgers.edu/~chvatal/notes/pq.html#heap */
90
91static void
92art_pri_bubble_up (ArtPriQ *pq, int vacant, ArtPriPoint *missing)
93{
94  ArtPriPoint **items = pq->items;
95  int parent;
96
97  parent = (vacant - 1) >> 1;
98  while (vacant > 0 && (missing->y < items[parent]->y ||
99                        (missing->y == items[parent]->y &&
100                         missing->x < items[parent]->x)))
101    {
102      items[vacant] = items[parent];
103      vacant = parent;
104      parent = (vacant - 1) >> 1;
105    }
106
107  items[vacant] = missing;
108}
109
110static void
111art_pri_insert (ArtPriQ *pq, ArtPriPoint *point)
112{
113  if (pq->n_items == pq->n_items_max)
114    art_expand (pq->items, ArtPriPoint *, pq->n_items_max);
115
116  art_pri_bubble_up (pq, pq->n_items++, point);
117}
118
119static void
120art_pri_sift_down_from_root (ArtPriQ *pq, ArtPriPoint *missing)
121{
122  ArtPriPoint **items = pq->items;
123  int vacant = 0, child = 2;
124  int n = pq->n_items;
125
126  while (child < n)
127    {
128      if (items[child - 1]->y < items[child]->y ||
129          (items[child - 1]->y == items[child]->y &&
130           items[child - 1]->x < items[child]->x))
131        child--;
132      items[vacant] = items[child];
133      vacant = child;
134      child = (vacant + 1) << 1;
135    }
136  if (child == n)
137    {
138      items[vacant] = items[n - 1];
139      vacant = n - 1;
140    }
141
142  art_pri_bubble_up (pq, vacant, missing);
143}
144
145static ArtPriPoint *
146art_pri_choose (ArtPriQ *pq)
147{
148  ArtPriPoint *result = pq->items[0];
149
150  art_pri_sift_down_from_root (pq, pq->items[--pq->n_items]);
151  return result;
152}
153
154#else
155
156/* Choose least point in queue */
157static ArtPriPoint *
158art_pri_choose (ArtPriQ *pq)
159{
160  int i;
161  int best = 0;
162  double best_x, best_y;
163  double y;
164  ArtPriPoint *result;
165
166  if (pq->n_items == 0)
167    return NULL;
168
169  best_x = pq->items[best]->x;
170  best_y = pq->items[best]->y;
171
172  for (i = 1; i < pq->n_items; i++)
173    {
174      y = pq->items[i]->y;
175      if (y < best_y || (y == best_y && pq->items[i]->x < best_x))
176        {
177          best = i;
178          best_x = pq->items[best]->x;
179          best_y = y;
180        }
181    }
182  result = pq->items[best];
183  pq->items[best] = pq->items[--pq->n_items];
184  return result;
185}
186
187static void
188art_pri_insert (ArtPriQ *pq, ArtPriPoint *point)
189{
190  if (pq->n_items == pq->n_items_max)
191    art_expand (pq->items, ArtPriPoint *, pq->n_items_max);
192
193  pq->items[pq->n_items++] = point;
194}
195
196#endif
197
198#ifdef TEST_PRIQ
199
200#include <stdlib.h> /* for rand() */
201#include <stdio.h>
202
203static double
204double_rand (double lo, double hi, int quant)
205{
206  int tmp = rand () / (RAND_MAX * (1.0 / quant)) + 0.5;
207  return lo + tmp * ((hi - lo) / quant);
208}
209
210/*
211 * This custom allocator for priority queue points is here so I can
212 * test speed. It doesn't look like it will be that significant, but
213 * if I want a small improvement later, it's something.
214 */
215
216typedef ArtPriPoint *ArtPriPtPool;
217
218static ArtPriPtPool *
219art_pri_pt_pool_new (void)
220{
221  ArtPriPtPool *result = art_new (ArtPriPtPool, 1);
222  *result = NULL;
223  return result;
224}
225
226static ArtPriPoint *
227art_pri_pt_alloc (ArtPriPtPool *pool)
228{
229  ArtPriPoint *result = *pool;
230  if (result == NULL)
231    return art_new (ArtPriPoint, 1);
232  else
233    {
234      *pool = result->user_data;
235      return result;
236    }
237}
238
239static void
240art_pri_pt_free (ArtPriPtPool *pool, ArtPriPoint *pt)
241{
242  pt->user_data = *pool;
243  *pool = pt;
244}
245
246static void
247art_pri_pt_pool_free (ArtPriPtPool *pool)
248{
249  ArtPriPoint *pt = *pool;
250  while (pt != NULL)
251    {
252      ArtPriPoint *next = pt->user_data;
253      art_free (pt);
254      pt = next;
255    }
256  art_free (pool);
257}
258
259int
260main (int argc, char **argv)
261{
262  ArtPriPtPool *pool = art_pri_pt_pool_new ();
263  ArtPriQ *pq;
264  int i, j;
265  const int n_iter = 1;
266  const int pq_size = 100;
267
268  for (j = 0; j < n_iter; j++)
269    {
270      pq = art_pri_new ();
271
272      for (i = 0; i < pq_size; i++)
273        {
274          ArtPriPoint *pt = art_pri_pt_alloc (pool);
275          pt->x = double_rand (0, 1, 100);
276          pt->y = double_rand (0, 1, 100);
277          pt->user_data = (void *)i;
278          art_pri_insert (pq, pt);
279        }
280
281      while (!art_pri_empty (pq))
282        {
283          ArtPriPoint *pt = art_pri_choose (pq);
284          if (n_iter == 1)
285            printf ("(%g, %g), %d\n", pt->x, pt->y, (int)pt->user_data);
286          art_pri_pt_free (pool, pt);
287        }
288
289      art_pri_free (pq);
290    }
291  art_pri_pt_pool_free (pool);
292  return 0;
293}
294
295#else /* TEST_PRIQ */
296
297/* A virtual class for an "svp writer". A client of this object creates an
298   SVP by repeatedly calling "add segment" and "add point" methods on it.
299*/
300
301typedef struct _ArtSvpWriterRewind ArtSvpWriterRewind;
302
303/* An implementation of the svp writer virtual class that applies the
304   winding rule. */
305
306struct _ArtSvpWriterRewind {
307  ArtSvpWriter super;
308  ArtWindRule rule;
309  ArtSVP *svp;
310  int n_segs_max;
311  int *n_points_max;
312};
313
314static int
315art_svp_writer_rewind_add_segment (ArtSvpWriter *self, int wind_left,
316                                   int delta_wind, double x, double y)
317{
318  ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
319  ArtSVP *svp;
320  ArtSVPSeg *seg;
321  art_boolean left_filled, right_filled;
322  int wind_right = wind_left + delta_wind;
323  int seg_num;
324  const int init_n_points_max = 4;
325
326  switch (swr->rule)
327    {
328    case ART_WIND_RULE_NONZERO:
329      left_filled = (wind_left != 0);
330      right_filled = (wind_right != 0);
331      break;
332    case ART_WIND_RULE_INTERSECT:
333      left_filled = (wind_left > 1);
334      right_filled = (wind_right > 1);
335      break;
336    case ART_WIND_RULE_ODDEVEN:
337      left_filled = (wind_left & 1);
338      right_filled = (wind_right & 1);
339      break;
340    case ART_WIND_RULE_POSITIVE:
341      left_filled = (wind_left > 0);
342      right_filled = (wind_right > 0);
343      break;
344    default:
345      art_die ("Unknown wind rule %d\n", swr->rule);
346    }
347  if (left_filled == right_filled)
348    {
349      /* discard segment now */
350#ifdef VERBOSE
351      art_dprint ("swr add_segment: %d += %d (%g, %g) --> -1\n",
352              wind_left, delta_wind, x, y);
353#endif
354      return -1;
355   }
356
357  svp = swr->svp;
358  seg_num = svp->n_segs++;
359  if (swr->n_segs_max == seg_num)
360    {
361      swr->n_segs_max <<= 1;
362      svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
363                                   (swr->n_segs_max - 1) *
364                                   sizeof(ArtSVPSeg));
365      swr->svp = svp;
366      swr->n_points_max = art_renew (swr->n_points_max, int,
367                                     swr->n_segs_max);
368    }
369  seg = &svp->segs[seg_num];
370  seg->n_points = 1;
371  seg->dir = right_filled;
372  swr->n_points_max[seg_num] = init_n_points_max;
373  seg->bbox.x0 = x;
374  seg->bbox.y0 = y;
375  seg->bbox.x1 = x;
376  seg->bbox.y1 = y;
377  seg->points = art_new (ArtPoint, init_n_points_max);
378  seg->points[0].x = x;
379  seg->points[0].y = y;
380#ifdef VERBOSE
381    art_dprint ("swr add_segment: %d += %d (%g, %g) --> %d(%s)\n",
382            wind_left, delta_wind, x, y, seg_num,
383            seg->dir ? "v" : "^");
384#endif
385  return seg_num;
386}
387
388static void
389art_svp_writer_rewind_add_point (ArtSvpWriter *self, int seg_id,
390                                 double x, double y)
391{
392  ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
393  ArtSVPSeg *seg;
394  int n_points;
395
396#ifdef VERBOSE
397  art_dprint ("swr add_point: %d (%g, %g)\n", seg_id, x, y);
398#endif
399  if (seg_id < 0)
400    /* omitted segment */
401    return;
402
403  seg = &swr->svp->segs[seg_id];
404  n_points = seg->n_points++;
405  if (swr->n_points_max[seg_id] == n_points)
406    art_expand (seg->points, ArtPoint, swr->n_points_max[seg_id]);
407  seg->points[n_points].x = x;
408  seg->points[n_points].y = y;
409  if (x < seg->bbox.x0)
410    seg->bbox.x0 = x;
411  if (x > seg->bbox.x1)
412    seg->bbox.x1 = x;
413  seg->bbox.y1 = y;
414}
415
416static void
417art_svp_writer_rewind_close_segment (ArtSvpWriter *self, int seg_id)
418{
419  /* Not needed for this simple implementation. A potential future
420     optimization is to merge segments that can be merged safely. */
421#ifdef SANITYCHECK
422  ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
423  ArtSVPSeg *seg;
424
425  if (seg_id >= 0)
426    {
427      seg = &swr->svp->segs[seg_id];
428      if (seg->n_points < 2)
429        art_warn ("*** closing segment %d with only %d point%s\n",
430                  seg_id, seg->n_points, seg->n_points == 1 ? "" : "s");
431    }
432#endif
433
434#ifdef VERBOSE
435  art_dprint ("swr close_segment: %d\n", seg_id);
436#endif
437}
438
439ArtSVP *
440art_svp_writer_rewind_reap (ArtSvpWriter *self)
441{
442  ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
443  ArtSVP *result = swr->svp;
444
445  art_free (swr->n_points_max);
446  art_free (swr);
447  return result;
448}
449
450ArtSvpWriter *
451art_svp_writer_rewind_new (ArtWindRule rule)
452{
453  ArtSvpWriterRewind *result = art_new (ArtSvpWriterRewind, 1);
454
455  result->super.add_segment = art_svp_writer_rewind_add_segment;
456  result->super.add_point = art_svp_writer_rewind_add_point;
457  result->super.close_segment = art_svp_writer_rewind_close_segment;
458
459  result->rule = rule;
460  result->n_segs_max = 16;
461  result->svp = art_alloc (sizeof(ArtSVP) +
462                           (result->n_segs_max - 1) * sizeof(ArtSVPSeg));
463  result->svp->n_segs = 0;
464  result->n_points_max = art_new (int, result->n_segs_max);
465
466  return &result->super;
467}
468
469/* Now, data structures for the active list */
470
471typedef struct _ArtActiveSeg ArtActiveSeg;
472
473/* Note: BNEG is 1 for \ lines, and 0 for /. Thus,
474   x[(flags & BNEG) ^ 1] <= x[flags & BNEG] */
475#define ART_ACTIVE_FLAGS_BNEG 1
476
477/* This flag is set if the segment has been inserted into the active
478   list. */
479#define ART_ACTIVE_FLAGS_IN_ACTIVE 2
480
481/* This flag is set when the segment is to be deleted in the
482   horiz commit process. */
483#define ART_ACTIVE_FLAGS_DEL 4
484
485/* This flag is set if the seg_id is a valid output segment. */
486#define ART_ACTIVE_FLAGS_OUT 8
487
488/* This flag is set if the segment is in the horiz list. */
489#define ART_ACTIVE_FLAGS_IN_HORIZ 16
490
491struct _ArtActiveSeg {
492  int flags;
493  int wind_left, delta_wind;
494  ArtActiveSeg *left, *right; /* doubly linked list structure */
495
496  const ArtSVPSeg *in_seg;
497  int in_curs;
498
499  double x[2];
500  double y0, y1;
501  double a, b, c; /* line equation; ax+by+c = 0 for the line, a^2 + b^2 = 1,
502                     and a>0 */
503
504  /* bottom point and intersection point stack */
505  int n_stack;
506  int n_stack_max;
507  ArtPoint *stack;
508
509  /* horiz commit list */
510  ArtActiveSeg *horiz_left, *horiz_right;
511  double horiz_x;
512  int horiz_delta_wind;
513  int seg_id;
514};
515
516typedef struct _ArtIntersectCtx ArtIntersectCtx;
517
518struct _ArtIntersectCtx {
519  const ArtSVP *in;
520  ArtSvpWriter *out;
521
522  ArtPriQ *pq;
523
524  ArtActiveSeg *active_head;
525
526  double y;
527  ArtActiveSeg *horiz_first;
528  ArtActiveSeg *horiz_last;
529
530  /* segment index of next input segment to be added to pri q */
531  int in_curs;
532};
533
534#define EPSILON_A 1e-5 /* Threshold for breaking lines at point insertions */
535
536/**
537 * art_svp_intersect_setup_seg: Set up an active segment from input segment.
538 * @seg: Active segment.
539 * @pri_pt: Priority queue point to initialize.
540 *
541 * Sets the x[], a, b, c, flags, and stack fields according to the
542 * line from the current cursor value. Sets the priority queue point
543 * to the bottom point of this line. Also advances the input segment
544 * cursor.
545 **/
546static void
547art_svp_intersect_setup_seg (ArtActiveSeg *seg, ArtPriPoint *pri_pt)
548{
549  const ArtSVPSeg *in_seg = seg->in_seg;
550  int in_curs = seg->in_curs++;
551  double x0, y0, x1, y1;
552  double dx, dy, s;
553  double a, b, r2;
554
555  x0 = in_seg->points[in_curs].x;
556  y0 = in_seg->points[in_curs].y;
557  x1 = in_seg->points[in_curs + 1].x;
558  y1 = in_seg->points[in_curs + 1].y;
559  pri_pt->x = x1;
560  pri_pt->y = y1;
561  dx = x1 - x0;
562  dy = y1 - y0;
563  r2 = dx * dx + dy * dy;
564  s = r2 == 0 ? 1 : 1 / sqrt (r2);
565  seg->a = a = dy * s;
566  seg->b = b = -dx * s;
567  seg->c = -(a * x0 + b * y0);
568  seg->flags = (seg->flags & ~ART_ACTIVE_FLAGS_BNEG) | (dx > 0);
569  seg->x[0] = x0;
570  seg->x[1] = x1;
571  seg->y0 = y0;
572  seg->y1 = y1;
573  seg->n_stack = 1;
574  seg->stack[0].x = x1;
575  seg->stack[0].y = y1;
576}
577
578/**
579 * art_svp_intersect_add_horiz: Add point to horizontal list.
580 * @ctx: Intersector context.
581 * @seg: Segment with point to insert into horizontal list.
582 *
583 * Inserts @seg into horizontal list, keeping it in ascending horiz_x
584 * order.
585 *
586 * Note: the horiz_commit routine processes "clusters" of segs in the
587 * horiz list, all sharing the same horiz_x value. The cluster is
588 * processed in active list order, rather than horiz list order. Thus,
589 * the order of segs in the horiz list sharing the same horiz_x
590 * _should_ be irrelevant. Even so, we use b as a secondary sorting key,
591 * as a "belt and suspenders" defensive coding tactic.
592 **/
593static void
594art_svp_intersect_add_horiz (ArtIntersectCtx *ctx, ArtActiveSeg *seg)
595{
596  ArtActiveSeg **pp = &ctx->horiz_last;
597  ArtActiveSeg *place;
598  ArtActiveSeg *place_right = NULL;
599
600
601#ifdef CHEAP_SANITYCHECK
602  if (seg->flags & ART_ACTIVE_FLAGS_IN_HORIZ)
603    {
604      art_warn ("*** attempt to put segment in horiz list twice\n");
605      return;
606    }
607  seg->flags |= ART_ACTIVE_FLAGS_IN_HORIZ;
608#endif
609
610#ifdef VERBOSE
611  art_dprint ("add_horiz %lx, x = %g\n", (unsigned long) seg, seg->horiz_x);
612#endif
613  for (place = *pp; place != NULL && (place->horiz_x > seg->horiz_x ||
614                                      (place->horiz_x == seg->horiz_x &&
615                                       place->b < seg->b));
616       place = *pp)
617    {
618      place_right = place;
619      pp = &place->horiz_left;
620    }
621  *pp = seg;
622  seg->horiz_left = place;
623  seg->horiz_right = place_right;
624  if (place == NULL)
625    ctx->horiz_first = seg;
626  else
627    place->horiz_right = seg;
628}
629
630static void
631art_svp_intersect_push_pt (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
632                           double x, double y)
633{
634  ArtPriPoint *pri_pt;
635  int n_stack = seg->n_stack;
636
637  if (n_stack == seg->n_stack_max)
638    art_expand (seg->stack, ArtPoint, seg->n_stack_max);
639  seg->stack[n_stack].x = x;
640  seg->stack[n_stack].y = y;
641  seg->n_stack++;
642
643  seg->x[1] = x;
644  seg->y1 = y;
645
646  pri_pt = art_new (ArtPriPoint, 1);
647  pri_pt->x = x;
648  pri_pt->y = y;
649  pri_pt->user_data = seg;
650  art_pri_insert (ctx->pq, pri_pt);
651}
652
653typedef enum {
654  ART_BREAK_LEFT = 1,
655  ART_BREAK_RIGHT = 2
656} ArtBreakFlags;
657
658/**
659 * art_svp_intersect_break: Break an active segment.
660 *
661 * Note: y must be greater than the top point's y, and less than
662 * the bottom's.
663 *
664 * Return value: x coordinate of break point.
665 */
666static double
667art_svp_intersect_break (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
668                         double x_ref, double y, ArtBreakFlags break_flags)
669{
670  double x0, y0, x1, y1;
671  const ArtSVPSeg *in_seg = seg->in_seg;
672  int in_curs = seg->in_curs;
673  double x;
674
675  x0 = in_seg->points[in_curs - 1].x;
676  y0 = in_seg->points[in_curs - 1].y;
677  x1 = in_seg->points[in_curs].x;
678  y1 = in_seg->points[in_curs].y;
679  x = x0 + (x1 - x0) * ((y - y0) / (y1 - y0));
680  if ((break_flags == ART_BREAK_LEFT && x > x_ref) ||
681      (break_flags == ART_BREAK_RIGHT && x < x_ref))
682    {
683#ifdef VERBOSE
684      art_dprint ("art_svp_intersect_break: limiting x to %f, was %f, %s\n",
685                  x_ref, x, break_flags == ART_BREAK_LEFT ? "left" : "right");
686      x = x_ref;
687#endif
688    }
689
690  /* I think we can count on min(x0, x1) <= x <= max(x0, x1) with sane
691     arithmetic, but it might be worthwhile to check just in case. */
692
693  if (y > ctx->y)
694    art_svp_intersect_push_pt (ctx, seg, x, y);
695  else
696    {
697      seg->x[0] = x;
698      seg->y0 = y;
699      seg->horiz_x = x;
700      art_svp_intersect_add_horiz (ctx, seg);
701    }
702
703  return x;
704}
705
706/**
707 * art_svp_intersect_add_point: Add a point, breaking nearby neighbors.
708 * @ctx: Intersector context.
709 * @x: X coordinate of point to add.
710 * @y: Y coordinate of point to add.
711 * @seg: "nearby" segment, or NULL if leftmost.
712 *
713 * Return value: Segment immediately to the left of the new point, or
714 * NULL if the new point is leftmost.
715 **/
716static ArtActiveSeg *
717art_svp_intersect_add_point (ArtIntersectCtx *ctx, double x, double y,
718                             ArtActiveSeg *seg, ArtBreakFlags break_flags)
719{
720  ArtActiveSeg *left, *right;
721  double x_min = x, x_max = x;
722  art_boolean left_live, right_live;
723  double d;
724  double new_x;
725  ArtActiveSeg *test, *result = NULL;
726  double x_test;
727
728  left = seg;
729  if (left == NULL)
730    right = ctx->active_head;
731  else
732    right = left->right;
733  left_live = (break_flags & ART_BREAK_LEFT) && (left != NULL);
734  right_live = (break_flags & ART_BREAK_RIGHT) && (right != NULL);
735  while (left_live || right_live)
736    {
737      if (left_live)
738        {
739          if (x <= left->x[left->flags & ART_ACTIVE_FLAGS_BNEG] &&
740              /* It may be that one of these conjuncts turns out to be always
741                 true. We test both anyway, to be defensive. */
742              y != left->y0 && y < left->y1)
743            {
744              d = x_min * left->a + y * left->b + left->c;
745              if (d < EPSILON_A)
746                {
747                  new_x = art_svp_intersect_break (ctx, left, x_min, y,
748                                                   ART_BREAK_LEFT);
749                  if (new_x > x_max)
750                    {
751                      x_max = new_x;
752                      right_live = (right != NULL);
753                    }
754                  else if (new_x < x_min)
755                    x_min = new_x;
756                  left = left->left;
757                  left_live = (left != NULL);
758                }
759              else
760                left_live = ART_FALSE;
761            }
762          else
763            left_live = ART_FALSE;
764        }
765      else if (right_live)
766        {
767          if (x >= right->x[(right->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] &&
768              /* It may be that one of these conjuncts turns out to be always
769                 true. We test both anyway, to be defensive. */
770              y != right->y0 && y < right->y1)
771            {
772              d = x_max * right->a + y * right->b + right->c;
773              if (d > -EPSILON_A)
774                {
775                  new_x = art_svp_intersect_break (ctx, right, x_max, y,
776                                                   ART_BREAK_RIGHT);
777                  if (new_x < x_min)
778                    {
779                      x_min = new_x;
780                      left_live = (left != NULL);
781                    }
782                  else if (new_x >= x_max)
783                    x_max = new_x;
784                  right = right->right;
785                  right_live = (right != NULL);
786                }
787              else
788                right_live = ART_FALSE;
789            }
790          else
791            right_live = ART_FALSE;
792        }
793    }
794
795  /* Ascending order is guaranteed by break_flags. Thus, we don't need
796     to actually fix up non-ascending pairs. */
797
798  /* Now, (left, right) defines an interval of segments broken. Sort
799     into ascending x order. */
800  test = left == NULL ? ctx->active_head : left->right;
801  result = left;
802  if (test != NULL && test != right)
803    {
804      if (y == test->y0)
805        x_test = test->x[0];
806      else /* assert y == test->y1, I think */
807        x_test = test->x[1];
808      for (;;)
809        {
810          if (x_test <= x)
811            result = test;
812          test = test->right;
813          if (test == right)
814            break;
815          new_x = x_test;
816          if (new_x < x_test)
817            {
818              art_warn ("art_svp_intersect_add_point: non-ascending x\n");
819            }
820          x_test = new_x;
821        }
822    }
823  return result;
824}
825
826static void
827art_svp_intersect_swap_active (ArtIntersectCtx *ctx,
828                               ArtActiveSeg *left_seg, ArtActiveSeg *right_seg)
829{
830  right_seg->left = left_seg->left;
831  if (right_seg->left != NULL)
832    right_seg->left->right = right_seg;
833  else
834    ctx->active_head = right_seg;
835  left_seg->right = right_seg->right;
836  if (left_seg->right != NULL)
837    left_seg->right->left = left_seg;
838  left_seg->left = right_seg;
839  right_seg->right = left_seg;
840}
841
842/**
843 * art_svp_intersect_test_cross: Test crossing of a pair of active segments.
844 * @ctx: Intersector context.
845 * @left_seg: Left segment of the pair.
846 * @right_seg: Right segment of the pair.
847 * @break_flags: Flags indicating whether to break neighbors.
848 *
849 * Tests crossing of @left_seg and @right_seg. If there is a crossing,
850 * inserts the intersection point into both segments.
851 *
852 * Return value: True if the intersection took place at the current
853 * scan line, indicating further iteration is needed.
854 **/
855static art_boolean
856art_svp_intersect_test_cross (ArtIntersectCtx *ctx,
857                              ArtActiveSeg *left_seg, ArtActiveSeg *right_seg,
858                              ArtBreakFlags break_flags)
859{
860  double left_x0, left_y0, left_x1;
861  double left_y1 = left_seg->y1;
862  double right_y1 = right_seg->y1;
863  double d;
864
865  const ArtSVPSeg *in_seg;
866  int in_curs;
867  double d0, d1, t;
868  double x, y; /* intersection point */
869
870#ifdef VERBOSE
871  static int count = 0;
872
873  art_dprint ("art_svp_intersect_test_cross %lx <-> %lx: count=%d\n",
874          (unsigned long)left_seg, (unsigned long)right_seg, count++);
875#endif
876
877  if (left_seg->y0 == right_seg->y0 && left_seg->x[0] == right_seg->x[0])
878    {
879      /* Top points of left and right segments coincide. This case
880         feels like a bit of duplication - we may want to merge it
881         with the cases below. However, this way, we're sure that this
882         logic makes only localized changes. */
883
884      if (left_y1 < right_y1)
885        {
886          /* Test left (x1, y1) against right segment */
887          double left_x1 = left_seg->x[1];
888
889          if (left_x1 <
890              right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
891              left_y1 == right_seg->y0)
892            return ART_FALSE;
893          d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
894          if (d < -EPSILON_A)
895            return ART_FALSE;
896          else if (d < EPSILON_A)
897            {
898              /* I'm unsure about the break flags here. */
899              double right_x1 = art_svp_intersect_break (ctx, right_seg,
900                                                         left_x1, left_y1,
901                                                         ART_BREAK_RIGHT);
902              if (left_x1 <= right_x1)
903                return ART_FALSE;
904            }
905        }
906      else if (left_y1 > right_y1)
907        {
908          /* Test right (x1, y1) against left segment */
909          double right_x1 = right_seg->x[1];
910
911          if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
912              right_y1 == left_seg->y0)
913            return ART_FALSE;
914          d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
915          if (d > EPSILON_A)
916            return ART_FALSE;
917          else if (d > -EPSILON_A)
918            {
919              /* See above regarding break flags. */
920              double left_x1 = art_svp_intersect_break (ctx, left_seg,
921                                                        right_x1, right_y1,
922                                                        ART_BREAK_LEFT);
923              if (left_x1 <= right_x1)
924                return ART_FALSE;
925            }
926        }
927      else /* left_y1 == right_y1 */
928        {
929          double left_x1 = left_seg->x[1];
930          double right_x1 = right_seg->x[1];
931
932          if (left_x1 <= right_x1)
933            return ART_FALSE;
934        }
935      art_svp_intersect_swap_active (ctx, left_seg, right_seg);
936      return ART_TRUE;
937    }
938
939  if (left_y1 < right_y1)
940    {
941      /* Test left (x1, y1) against right segment */
942      double left_x1 = left_seg->x[1];
943
944      if (left_x1 <
945          right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
946          left_y1 == right_seg->y0)
947        return ART_FALSE;
948      d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
949      if (d < -EPSILON_A)
950        return ART_FALSE;
951      else if (d < EPSILON_A)
952        {
953          double right_x1 = art_svp_intersect_break (ctx, right_seg,
954                                                     left_x1, left_y1,
955                                                     ART_BREAK_RIGHT);
956          if (left_x1 <= right_x1)
957            return ART_FALSE;
958        }
959    }
960  else if (left_y1 > right_y1)
961    {
962      /* Test right (x1, y1) against left segment */
963      double right_x1 = right_seg->x[1];
964
965      if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
966          right_y1 == left_seg->y0)
967        return ART_FALSE;
968      d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
969      if (d > EPSILON_A)
970        return ART_FALSE;
971      else if (d > -EPSILON_A)
972        {
973          double left_x1 = art_svp_intersect_break (ctx, left_seg,
974                                                    right_x1, right_y1,
975                                                    ART_BREAK_LEFT);
976          if (left_x1 <= right_x1)
977            return ART_FALSE;
978        }
979    }
980  else /* left_y1 == right_y1 */
981    {
982      double left_x1 = left_seg->x[1];
983      double right_x1 = right_seg->x[1];
984
985      if (left_x1 <= right_x1)
986        return ART_FALSE;
987    }
988
989  /* The segments cross. Find the intersection point. */
990
991  in_seg = left_seg->in_seg;
992  in_curs = left_seg->in_curs;
993  left_x0 = in_seg->points[in_curs - 1].x;
994  left_y0 = in_seg->points[in_curs - 1].y;
995  left_x1 = in_seg->points[in_curs].x;
996  left_y1 = in_seg->points[in_curs].y;
997  d0 = left_x0 * right_seg->a + left_y0 * right_seg->b + right_seg->c;
998  d1 = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
999  if (d0 == d1)
1000    {
1001      x = left_x0;
1002      y = left_y0;
1003    }
1004  else
1005    {
1006      /* Is this division always safe? It could possibly overflow. */
1007      t = d0 / (d0 - d1);
1008      if (t <= 0)
1009        {
1010          x = left_x0;
1011          y = left_y0;
1012        }
1013      else if (t >= 1)
1014        {
1015          x = left_x1;
1016          y = left_y1;
1017        }
1018      else
1019        {
1020          x = left_x0 + t * (left_x1 - left_x0);
1021          y = left_y0 + t * (left_y1 - left_y0);
1022        }
1023    }
1024
1025  /* Make sure intersection point is within bounds of right seg. */
1026  if (y < right_seg->y0)
1027    {
1028      x = right_seg->x[0];
1029      y = right_seg->y0;
1030    }
1031  else if (y > right_seg->y1)
1032    {
1033      x = right_seg->x[1];
1034      y = right_seg->y1;
1035    }
1036  else if (x < right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1])
1037    x = right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1];
1038  else if (x > right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG])
1039    x = right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG];
1040
1041  if (y == left_seg->y0)
1042    {
1043      if (y != right_seg->y0)
1044        {
1045#ifdef VERBOSE
1046          art_dprint ("art_svp_intersect_test_cross: intersection (%g, %g) matches former y0 of %lx, %lx\n",
1047                    x, y, (unsigned long)left_seg, (unsigned long)right_seg);
1048#endif
1049          art_svp_intersect_push_pt (ctx, right_seg, x, y);
1050          if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
1051            art_svp_intersect_add_point (ctx, x, y, right_seg->right,
1052                                         break_flags);
1053        }
1054      else
1055        {
1056          /* Intersection takes place at current scan line; process
1057             immediately rather than queueing intersection point into
1058             priq. */
1059          ArtActiveSeg *winner, *loser;
1060
1061          /* Choose "most vertical" segement */
1062          if (left_seg->a > right_seg->a)
1063            {
1064              winner = left_seg;
1065              loser = right_seg;
1066            }
1067          else
1068            {
1069              winner = right_seg;
1070              loser = left_seg;
1071            }
1072
1073          loser->x[0] = winner->x[0];
1074          loser->horiz_x = loser->x[0];
1075          loser->horiz_delta_wind += loser->delta_wind;
1076          winner->horiz_delta_wind -= loser->delta_wind;
1077
1078          art_svp_intersect_swap_active (ctx, left_seg, right_seg);
1079          return ART_TRUE;
1080        }
1081    }
1082  else if (y == right_seg->y0)
1083    {
1084#ifdef VERBOSE
1085      art_dprint ("*** art_svp_intersect_test_cross: intersection (%g, %g) matches latter y0 of %lx, %lx\n",
1086              x, y, (unsigned long)left_seg, (unsigned long)right_seg);
1087#endif
1088      art_svp_intersect_push_pt (ctx, left_seg, x, y);
1089      if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
1090        art_svp_intersect_add_point (ctx, x, y, left_seg->left,
1091                                     break_flags);
1092    }
1093  else
1094    {
1095#ifdef VERBOSE
1096      art_dprint ("Inserting (%g, %g) into %lx, %lx\n",
1097              x, y, (unsigned long)left_seg, (unsigned long)right_seg);
1098#endif
1099      /* Insert the intersection point into both segments. */
1100      art_svp_intersect_push_pt (ctx, left_seg, x, y);
1101      art_svp_intersect_push_pt (ctx, right_seg, x, y);
1102      if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
1103        art_svp_intersect_add_point (ctx, x, y, left_seg->left, break_flags);
1104      if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
1105        art_svp_intersect_add_point (ctx, x, y, right_seg->right, break_flags);
1106    }
1107  return ART_FALSE;
1108}
1109
1110/**
1111 * art_svp_intersect_active_delete: Delete segment from active list.
1112 * @ctx: Intersection context.
1113 * @seg: Segment to delete.
1114 *
1115 * Deletes @seg from the active list.
1116 **/
1117static /* todo inline */ void
1118art_svp_intersect_active_delete (ArtIntersectCtx *ctx, ArtActiveSeg *seg)
1119{
1120  ArtActiveSeg *left = seg->left, *right = seg->right;
1121
1122  if (left != NULL)
1123    left->right = right;
1124  else
1125    ctx->active_head = right;
1126  if (right != NULL)
1127    right->left = left;
1128}
1129
1130/**
1131 * art_svp_intersect_active_free: Free an active segment.
1132 * @seg: Segment to delete.
1133 *
1134 * Frees @seg.
1135 **/
1136static /* todo inline */ void
1137art_svp_intersect_active_free (ArtActiveSeg *seg)
1138{
1139  art_free (seg->stack);
1140#ifdef VERBOSE
1141  art_dprint ("Freeing %lx\n", (unsigned long) seg);
1142#endif
1143  art_free (seg);
1144}
1145
1146/**
1147 * art_svp_intersect_insert_cross: Test crossings of newly inserted line.
1148 *
1149 * Tests @seg against its left and right neighbors for intersections.
1150 * Precondition: the line in @seg is not purely horizontal.
1151 **/
1152static void
1153art_svp_intersect_insert_cross (ArtIntersectCtx *ctx,
1154                                ArtActiveSeg *seg)
1155{
1156  ArtActiveSeg *left = seg, *right = seg;
1157
1158  for (;;)
1159    {
1160      if (left != NULL)
1161        {
1162          ArtActiveSeg *leftc;
1163
1164          for (leftc = left->left; leftc != NULL; leftc = leftc->left)
1165            if (!(leftc->flags & ART_ACTIVE_FLAGS_DEL))
1166              break;
1167          if (leftc != NULL &&
1168              art_svp_intersect_test_cross (ctx, leftc, left,
1169                                            ART_BREAK_LEFT))
1170            {
1171              if (left == right || right == NULL)
1172                right = left->right;
1173            }
1174          else
1175            {
1176              left = NULL;
1177            }
1178        }
1179      else if (right != NULL && right->right != NULL)
1180        {
1181          ArtActiveSeg *rightc;
1182
1183          for (rightc = right->right; rightc != NULL; rightc = rightc->right)
1184            if (!(rightc->flags & ART_ACTIVE_FLAGS_DEL))
1185              break;
1186          if (rightc != NULL &&
1187              art_svp_intersect_test_cross (ctx, right, rightc,
1188                                            ART_BREAK_RIGHT))
1189            {
1190              if (left == right || left == NULL)
1191                left = right->left;
1192            }
1193          else
1194            {
1195              right = NULL;
1196            }
1197        }
1198      else
1199        break;
1200    }
1201}
1202
1203/**
1204 * art_svp_intersect_horiz: Add horizontal line segment.
1205 * @ctx: Intersector context.
1206 * @seg: Segment on which to add horizontal line.
1207 * @x0: Old x position.
1208 * @x1: New x position.
1209 *
1210 * Adds a horizontal line from @x0 to @x1, and updates the current
1211 * location of @seg to @x1.
1212 **/
1213static void
1214art_svp_intersect_horiz (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
1215                         double x0, double x1)
1216{
1217  ArtActiveSeg *hs;
1218
1219  if (x0 == x1)
1220    return;
1221
1222  hs = art_new (ArtActiveSeg, 1);
1223
1224  hs->flags = ART_ACTIVE_FLAGS_DEL | (seg->flags & ART_ACTIVE_FLAGS_OUT);
1225  if (seg->flags & ART_ACTIVE_FLAGS_OUT)
1226    {
1227      ArtSvpWriter *swr = ctx->out;
1228
1229      swr->add_point (swr, seg->seg_id, x0, ctx->y);
1230    }
1231  hs->seg_id = seg->seg_id;
1232  hs->horiz_x = x0;
1233  hs->horiz_delta_wind = seg->delta_wind;
1234  hs->stack = NULL;
1235
1236  /* Ideally, the (a, b, c) values will never be read. However, there
1237     are probably some tests remaining that don't check for _DEL
1238     before evaluating the line equation. For those, these
1239     initializations will at least prevent a UMR of the values, which
1240     can crash on some platforms. */
1241  hs->a = 0.0;
1242  hs->b = 0.0;
1243  hs->c = 0.0;
1244
1245  seg->horiz_delta_wind -= seg->delta_wind;
1246
1247  art_svp_intersect_add_horiz (ctx, hs);
1248
1249  if (x0 > x1)
1250    {
1251      ArtActiveSeg *left;
1252      art_boolean first = ART_TRUE;
1253
1254      for (left = seg->left; left != NULL; left = seg->left)
1255        {
1256          int left_bneg = left->flags & ART_ACTIVE_FLAGS_BNEG;
1257
1258          if (left->x[left_bneg] <= x1)
1259            break;
1260          if (left->x[left_bneg ^ 1] <= x1 &&
1261              x1 * left->a + ctx->y * left->b + left->c >= 0)
1262            break;
1263          if (left->y0 != ctx->y && left->y1 != ctx->y)
1264            {
1265              art_svp_intersect_break (ctx, left, x1, ctx->y, ART_BREAK_LEFT);
1266            }
1267#ifdef VERBOSE
1268          art_dprint ("x0=%g > x1=%g, swapping %lx, %lx\n",
1269                  x0, x1, (unsigned long)left, (unsigned long)seg);
1270#endif
1271          art_svp_intersect_swap_active (ctx, left, seg);
1272          if (first && left->right != NULL)
1273            {
1274              art_svp_intersect_test_cross (ctx, left, left->right,
1275                                            ART_BREAK_RIGHT);
1276              first = ART_FALSE;
1277            }
1278        }
1279    }
1280  else
1281    {
1282      ArtActiveSeg *right;
1283      art_boolean first = ART_TRUE;
1284
1285      for (right = seg->right; right != NULL; right = seg->right)
1286        {
1287          int right_bneg = right->flags & ART_ACTIVE_FLAGS_BNEG;
1288
1289          if (right->x[right_bneg ^ 1] >= x1)
1290            break;
1291          if (right->x[right_bneg] >= x1 &&
1292              x1 * right->a + ctx->y * right->b + right->c <= 0)
1293            break;
1294          if (right->y0 != ctx->y && right->y1 != ctx->y)
1295            {
1296              art_svp_intersect_break (ctx, right, x1, ctx->y,
1297                                       ART_BREAK_LEFT);
1298            }
1299#ifdef VERBOSE
1300          art_dprint ("[right]x0=%g < x1=%g, swapping %lx, %lx\n",
1301                  x0, x1, (unsigned long)seg, (unsigned long)right);
1302#endif
1303          art_svp_intersect_swap_active (ctx, seg, right);
1304          if (first && right->left != NULL)
1305            {
1306              art_svp_intersect_test_cross (ctx, right->left, right,
1307                                            ART_BREAK_RIGHT);
1308              first = ART_FALSE;
1309            }
1310        }
1311    }
1312
1313  seg->x[0] = x1;
1314  seg->x[1] = x1;
1315  seg->horiz_x = x1;
1316  seg->flags &= ~ART_ACTIVE_FLAGS_OUT;
1317}
1318
1319/**
1320 * art_svp_intersect_insert_line: Insert a line into the active list.
1321 * @ctx: Intersector context.
1322 * @seg: Segment containing line to insert.
1323 *
1324 * Inserts the line into the intersector context, taking care of any
1325 * intersections, and adding the appropriate horizontal points to the
1326 * active list.
1327 **/
1328static void
1329art_svp_intersect_insert_line (ArtIntersectCtx *ctx, ArtActiveSeg *seg)
1330{
1331  if (seg->y1 == seg->y0)
1332    {
1333#ifdef VERBOSE
1334      art_dprint ("art_svp_intersect_insert_line: %lx is horizontal\n",
1335              (unsigned long)seg);
1336#endif
1337      art_svp_intersect_horiz (ctx, seg, seg->x[0], seg->x[1]);
1338    }
1339  else
1340    {
1341      art_svp_intersect_insert_cross (ctx, seg);
1342      art_svp_intersect_add_horiz (ctx, seg);
1343    }
1344}
1345
1346static void
1347art_svp_intersect_process_intersection (ArtIntersectCtx *ctx,
1348                                        ArtActiveSeg *seg)
1349{
1350  int n_stack = --seg->n_stack;
1351  seg->x[1] = seg->stack[n_stack - 1].x;
1352  seg->y1 = seg->stack[n_stack - 1].y;
1353  seg->x[0] = seg->stack[n_stack].x;
1354  seg->y0 = seg->stack[n_stack].y;
1355  seg->horiz_x = seg->x[0];
1356  art_svp_intersect_insert_line (ctx, seg);
1357}
1358
1359static void
1360art_svp_intersect_advance_cursor (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
1361                                  ArtPriPoint *pri_pt)
1362{
1363  const ArtSVPSeg *in_seg = seg->in_seg;
1364  int in_curs = seg->in_curs;
1365  ArtSvpWriter *swr = seg->flags & ART_ACTIVE_FLAGS_OUT ? ctx->out : NULL;
1366
1367  if (swr != NULL)
1368    swr->add_point (swr, seg->seg_id, seg->x[1], seg->y1);
1369  if (in_curs + 1 == in_seg->n_points)
1370    {
1371      ArtActiveSeg *left = seg->left, *right = seg->right;
1372
1373#if 0
1374      if (swr != NULL)
1375        swr->close_segment (swr, seg->seg_id);
1376      seg->flags &= ~ART_ACTIVE_FLAGS_OUT;
1377#endif
1378      seg->flags |= ART_ACTIVE_FLAGS_DEL;
1379      art_svp_intersect_add_horiz (ctx, seg);
1380      art_svp_intersect_active_delete (ctx, seg);
1381      if (left != NULL && right != NULL)
1382        art_svp_intersect_test_cross (ctx, left, right,
1383                                      ART_BREAK_LEFT | ART_BREAK_RIGHT);
1384      art_free (pri_pt);
1385    }
1386  else
1387    {
1388      seg->horiz_x = seg->x[1];
1389
1390      art_svp_intersect_setup_seg (seg, pri_pt);
1391      art_pri_insert (ctx->pq, pri_pt);
1392      art_svp_intersect_insert_line (ctx, seg);
1393    }
1394}
1395
1396static void
1397art_svp_intersect_add_seg (ArtIntersectCtx *ctx, const ArtSVPSeg *in_seg)
1398{
1399  ArtActiveSeg *seg = art_new (ArtActiveSeg, 1);
1400  ArtActiveSeg *test;
1401  double x0, y0;
1402  ArtActiveSeg *beg_range;
1403  ArtActiveSeg *last = NULL;
1404  ArtActiveSeg *left, *right;
1405  ArtPriPoint *pri_pt = art_new (ArtPriPoint, 1);
1406
1407  seg->flags = 0;
1408  seg->in_seg = in_seg;
1409  seg->in_curs = 0;
1410
1411  seg->n_stack_max = 4;
1412  seg->stack = art_new (ArtPoint, seg->n_stack_max);
1413
1414  seg->horiz_delta_wind = 0;
1415 
1416  seg->wind_left = 0;
1417
1418  pri_pt->user_data = seg;
1419  art_svp_intersect_setup_seg (seg, pri_pt);
1420  art_pri_insert (ctx->pq, pri_pt);
1421
1422  /* Find insertion place for new segment */
1423  /* This is currently a left-to-right scan, but should be replaced
1424     with a binary search as soon as it's validated. */
1425
1426  x0 = in_seg->points[0].x;
1427  y0 = in_seg->points[0].y;
1428  beg_range = NULL;
1429  for (test = ctx->active_head; test != NULL; test = test->right)
1430    {
1431      double d;
1432      int test_bneg = test->flags & ART_ACTIVE_FLAGS_BNEG;
1433
1434      if (x0 < test->x[test_bneg])
1435        {
1436          if (x0 < test->x[test_bneg ^ 1])
1437            break;
1438          d = x0 * test->a + y0 * test->b + test->c;
1439          if (d < 0)
1440            break;
1441        }
1442      last = test;
1443    }
1444
1445  left = art_svp_intersect_add_point (ctx, x0, y0, last, ART_BREAK_LEFT | ART_BREAK_RIGHT);
1446  seg->left = left;
1447  if (left == NULL)
1448    {
1449      right = ctx->active_head;
1450      ctx->active_head = seg;
1451    }
1452  else
1453    {
1454      right = left->right;
1455      left->right = seg;
1456    }
1457  seg->right = right;
1458  if (right != NULL)
1459    right->left = seg;
1460
1461  seg->delta_wind = in_seg->dir ? 1 : -1;
1462  seg->horiz_x = x0;
1463
1464  art_svp_intersect_insert_line (ctx, seg);
1465}
1466
1467#ifdef SANITYCHECK
1468static void
1469art_svp_intersect_sanitycheck_winding (ArtIntersectCtx *ctx)
1470{
1471#if 0
1472  /* At this point, we seem to be getting false positives, so it's
1473     turned off for now. */
1474
1475  ArtActiveSeg *seg;
1476  int winding_number = 0;
1477
1478  for (seg = ctx->active_head; seg != NULL; seg = seg->right)
1479    {
1480      /* Check winding number consistency. */
1481      if (seg->flags & ART_ACTIVE_FLAGS_OUT)
1482        {
1483          if (winding_number != seg->wind_left)
1484            art_warn ("*** art_svp_intersect_sanitycheck_winding: seg %lx has wind_left of %d, expected %d\n",
1485                  (unsigned long) seg, seg->wind_left, winding_number);
1486          winding_number = seg->wind_left + seg->delta_wind;
1487        }
1488    }
1489  if (winding_number != 0)
1490    art_warn ("*** art_svp_intersect_sanitycheck_winding: non-balanced winding number %d\n",
1491              winding_number);
1492#endif
1493}
1494#endif
1495
1496/**
1497 * art_svp_intersect_horiz_commit: Commit points in horiz list to output.
1498 * @ctx: Intersection context.
1499 *
1500 * The main function of the horizontal commit is to output new
1501 * points to the output writer.
1502 *
1503 * This "commit" pass is also where winding numbers are assigned,
1504 * because doing it here provides much greater tolerance for inputs
1505 * which are not in strict SVP order.
1506 *
1507 * Each cluster in the horiz_list contains both segments that are in
1508 * the active list (ART_ACTIVE_FLAGS_DEL is false) and that are not,
1509 * and are scheduled to be deleted (ART_ACTIVE_FLAGS_DEL is true). We
1510 * need to deal with both.
1511 **/
1512static void
1513art_svp_intersect_horiz_commit (ArtIntersectCtx *ctx)
1514{
1515  ArtActiveSeg *seg;
1516  int winding_number = 0; /* initialization just to avoid warning */
1517  int horiz_wind = 0;
1518  double last_x = 0; /* initialization just to avoid warning */
1519
1520#ifdef VERBOSE
1521  art_dprint ("art_svp_intersect_horiz_commit: y=%g\n", ctx->y);
1522  for (seg = ctx->horiz_first; seg != NULL; seg = seg->horiz_right)
1523    art_dprint (" %lx: %g %+d\n",
1524            (unsigned long)seg, seg->horiz_x, seg->horiz_delta_wind);
1525#endif
1526
1527  /* Output points to svp writer. */
1528  for (seg = ctx->horiz_first; seg != NULL;)
1529    {
1530      /* Find a cluster with common horiz_x, */
1531      ArtActiveSeg *curs;
1532      double x = seg->horiz_x;
1533
1534      /* Generate any horizontal segments. */
1535      if (horiz_wind != 0)
1536        {
1537          ArtSvpWriter *swr = ctx->out;
1538          int seg_id;
1539
1540          seg_id = swr->add_segment (swr, winding_number, horiz_wind,
1541                                     last_x, ctx->y);
1542          swr->add_point (swr, seg_id, x, ctx->y);
1543          swr->close_segment (swr, seg_id);
1544        }
1545
1546      /* Find first active segment in cluster. */
1547
1548      for (curs = seg; curs != NULL && curs->horiz_x == x;
1549           curs = curs->horiz_right)
1550        if (!(curs->flags & ART_ACTIVE_FLAGS_DEL))
1551          break;
1552
1553      if (curs != NULL && curs->horiz_x == x)
1554        {
1555          /* There exists at least one active segment in this cluster. */
1556
1557          /* Find beginning of cluster. */
1558          for (; curs->left != NULL; curs = curs->left)
1559            if (curs->left->horiz_x != x)
1560              break;
1561
1562          if (curs->left != NULL)
1563            winding_number = curs->left->wind_left + curs->left->delta_wind;
1564          else
1565            winding_number = 0;
1566
1567          do
1568            {
1569#ifdef VERBOSE
1570              art_dprint (" curs %lx: winding_number = %d += %d\n",
1571                      (unsigned long)curs, winding_number, curs->delta_wind);
1572#endif
1573              if (!(curs->flags & ART_ACTIVE_FLAGS_OUT) ||
1574                  curs->wind_left != winding_number)
1575                {
1576                  ArtSvpWriter *swr = ctx->out;
1577
1578                  if (curs->flags & ART_ACTIVE_FLAGS_OUT)
1579                    {
1580                      swr->add_point (swr, curs->seg_id,
1581                                      curs->horiz_x, ctx->y);
1582                      swr->close_segment (swr, curs->seg_id);
1583                    }
1584
1585                  curs->seg_id = swr->add_segment (swr, winding_number,
1586                                                   curs->delta_wind,
1587                                                   x, ctx->y);
1588                  curs->flags |= ART_ACTIVE_FLAGS_OUT;
1589                }
1590              curs->wind_left = winding_number;
1591              winding_number += curs->delta_wind;
1592              curs = curs->right;
1593            }
1594          while (curs != NULL && curs->horiz_x == x);
1595        }
1596
1597      /* Skip past cluster. */
1598      do
1599        {
1600          ArtActiveSeg *next = seg->horiz_right;
1601
1602          seg->flags &= ~ART_ACTIVE_FLAGS_IN_HORIZ;
1603          horiz_wind += seg->horiz_delta_wind;
1604          seg->horiz_delta_wind = 0;
1605          if (seg->flags & ART_ACTIVE_FLAGS_DEL)
1606            {
1607              if (seg->flags & ART_ACTIVE_FLAGS_OUT)
1608                {
1609                  ArtSvpWriter *swr = ctx->out;
1610                  swr->close_segment (swr, seg->seg_id);
1611                }
1612              art_svp_intersect_active_free (seg);
1613            }
1614          seg = next;
1615        }
1616      while (seg != NULL && seg->horiz_x == x);
1617
1618      last_x = x;
1619    }
1620  ctx->horiz_first = NULL;
1621  ctx->horiz_last = NULL;
1622#ifdef SANITYCHECK
1623  art_svp_intersect_sanitycheck_winding (ctx);
1624#endif
1625}
1626
1627#ifdef VERBOSE
1628static void
1629art_svp_intersect_print_active (ArtIntersectCtx *ctx)
1630{
1631  ArtActiveSeg *seg;
1632
1633  art_dprint ("Active list (y = %g):\n", ctx->y);
1634  for (seg = ctx->active_head; seg != NULL; seg = seg->right)
1635    {
1636      art_dprint (" %lx: (%g, %g)-(%g, %g), (a, b, c) = (%g, %g, %g)\n",
1637              (unsigned long)seg,
1638              seg->x[0], seg->y0, seg->x[1], seg->y1,
1639              seg->a, seg->b, seg->c);
1640    }
1641}
1642#endif
1643
1644#ifdef SANITYCHECK
1645static void
1646art_svp_intersect_sanitycheck (ArtIntersectCtx *ctx)
1647{
1648  ArtActiveSeg *seg;
1649  ArtActiveSeg *last = NULL;
1650  double d;
1651
1652  for (seg = ctx->active_head; seg != NULL; seg = seg->right)
1653    {
1654      if (seg->left != last)
1655        {
1656          art_warn ("*** art_svp_intersect_sanitycheck: last=%lx, seg->left=%lx\n",
1657                    (unsigned long)last, (unsigned long)seg->left);
1658        }
1659      if (last != NULL)
1660        {
1661          /* pairwise compare with previous seg */
1662
1663          /* First the top. */
1664          if (last->y0 < seg->y0)
1665            {
1666            }
1667          else
1668            {
1669            }
1670
1671          /* Then the bottom. */
1672          if (last->y1 < seg->y1)
1673            {
1674              if (!((last->x[1] <
1675                     seg->x[(seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1]) ||
1676                    last->y1 == seg->y0))
1677                {
1678                  d = last->x[1] * seg->a + last->y1 * seg->b + seg->c;
1679                  if (d >= -EPSILON_A)
1680                    art_warn ("*** bottom (%g, %g) of %lx is not clear of %lx to right (d = %g)\n",
1681                              last->x[1], last->y1, (unsigned long) last,
1682                              (unsigned long) seg, d);
1683                }
1684            }
1685          else if (last->y1 > seg->y1)
1686
1687            {
1688              if (!((seg->x[1] >
1689                     last->x[last->flags & ART_ACTIVE_FLAGS_BNEG]) ||
1690                    seg->y1 == last->y0))
1691              {
1692                d = seg->x[1] * last->a + seg->y1 * last->b + last->c;
1693                if (d <= EPSILON_A)
1694                  art_warn ("*** bottom (%g, %g) of %lx is not clear of %lx to left (d = %g)\n",
1695                              seg->x[1], seg->y1, (unsigned long) seg,
1696                              (unsigned long) last, d);
1697              }
1698            }
1699          else
1700            {
1701              if (last->x[1] > seg->x[1])
1702                art_warn ("*** bottoms (%g, %g) of %lx and (%g, %g) of %lx out of order\n",
1703                          last->x[1], last->y1, (unsigned long)last,
1704                          seg->x[1], seg->y1, (unsigned long)seg);
1705            }
1706        }
1707      last = seg;
1708    }
1709}
1710#endif
1711
1712void
1713art_svp_intersector (const ArtSVP *in, ArtSvpWriter *out)
1714{
1715  ArtIntersectCtx *ctx;
1716  ArtPriQ *pq;
1717  ArtPriPoint *first_point;
1718#ifdef VERBOSE
1719  int count = 0;
1720#endif
1721
1722  if (in->n_segs == 0)
1723    return;
1724
1725  ctx = art_new (ArtIntersectCtx, 1);
1726  ctx->in = in;
1727  ctx->out = out;
1728  pq = art_pri_new ();
1729  ctx->pq = pq;
1730
1731  ctx->active_head = NULL;
1732
1733  ctx->horiz_first = NULL;
1734  ctx->horiz_last = NULL;
1735
1736  ctx->in_curs = 0;
1737  first_point = art_new (ArtPriPoint, 1);
1738  first_point->x = in->segs[0].points[0].x;
1739  first_point->y = in->segs[0].points[0].y;
1740  first_point->user_data = NULL;
1741  ctx->y = first_point->y;
1742  art_pri_insert (pq, first_point);
1743
1744  while (!art_pri_empty (pq))
1745    {
1746      ArtPriPoint *pri_point = art_pri_choose (pq);
1747      ArtActiveSeg *seg = (ArtActiveSeg *)pri_point->user_data;
1748
1749#ifdef VERBOSE
1750      art_dprint ("\nIntersector step %d\n", count++);
1751      art_svp_intersect_print_active (ctx);
1752      art_dprint ("priq choose (%g, %g) %lx\n", pri_point->x, pri_point->y,
1753              (unsigned long)pri_point->user_data);
1754#endif
1755#ifdef SANITYCHECK
1756      art_svp_intersect_sanitycheck(ctx);
1757#endif
1758
1759      if (ctx->y != pri_point->y)
1760        {
1761          art_svp_intersect_horiz_commit (ctx);
1762          ctx->y = pri_point->y;
1763        }
1764
1765      if (seg == NULL)
1766        {
1767          /* Insert new segment from input */
1768          const ArtSVPSeg *in_seg = &in->segs[ctx->in_curs++];
1769          art_svp_intersect_add_seg (ctx, in_seg);
1770          if (ctx->in_curs < in->n_segs)
1771            {
1772              const ArtSVPSeg *next_seg = &in->segs[ctx->in_curs];
1773              pri_point->x = next_seg->points[0].x;
1774              pri_point->y = next_seg->points[0].y;
1775              /* user_data is already NULL */
1776              art_pri_insert (pq, pri_point);
1777            }
1778          else
1779            art_free (pri_point);
1780        }
1781      else
1782        {
1783          int n_stack = seg->n_stack;
1784
1785          if (n_stack > 1)
1786            {
1787              art_svp_intersect_process_intersection (ctx, seg);
1788              art_free (pri_point);
1789            }
1790          else
1791            {
1792              art_svp_intersect_advance_cursor (ctx, seg, pri_point);
1793            }
1794        }
1795    }
1796
1797  art_svp_intersect_horiz_commit (ctx);
1798
1799  art_pri_free (pq);
1800  art_free (ctx);
1801}
1802
1803#endif /* not TEST_PRIQ */
Note: See TracBrowser for help on using the repository browser.