Graphviz 16.1.0~dev.20260902.0144
Loading...
Searching...
No Matches
trapezoid.c
Go to the documentation of this file.
1
9/*************************************************************************
10 * Copyright (c) 2011 AT&T Intellectual Property
11 * All rights reserved. This program and the accompanying materials
12 * are made available under the terms of the Eclipse Public License v2.0
13 * which accompanies this distribution, and is available at
14 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
15 *
16 * Contributors: Details at https://graphviz.org
17 *************************************************************************/
18
19
20#include "config.h"
21#include <string.h>
22#include <assert.h>
23#include <float.h>
24#include <stdbool.h>
25#include <stddef.h>
26#include <stdint.h>
27#include <stdio.h>
28#include <math.h>
29#include <common/geom.h>
30#include <common/types.h>
31#include <ortho/trap.h>
32#include <util/alloc.h>
33#include <util/gv_math.h>
34#include <util/list.h>
35#include <util/unreachable.h>
36#include <util/unused.h>
37
38/* Node types */
39
40#define T_X 1
41#define T_Y 2
42#define T_SINK 3
43
44#define FIRSTPT 1 /* checking whether pt. is inserted */
45#define LASTPT 2
46
47#define S_LEFT 1 /* for merge-direction */
48#define S_RIGHT 2
49
50static double cross(pointf v0, pointf v1, pointf v2) {
51 return (v1.x - v0.x) * (v2.y - v0.y) - (v1.y - v0.y) * (v2.x - v0.x);
52}
53
54typedef struct {
55 int nodetype; /* Y-node or S-node */
56 int segnum;
58 size_t trnum;
59 size_t parent;
60 size_t left, right;
61} qnode_t;
62
64typedef LIST(qnode_t) qnodes_t;
65
66/* Return a new node to be added into the query tree */
67static size_t newnode(qnodes_t *qs) {
68 LIST_APPEND(qs, (qnode_t){0});
69 return LIST_SIZE(qs) - 1;
70}
71
72/* Return a free trapezoid */
73static size_t newtrap(traps_t *tr) {
74 LIST_APPEND(tr, (trap_t){0});
75 return LIST_SIZE(tr) - 1;
76}
77
79static pointf max_(pointf v0, pointf v1) {
80 if (v0.y > v1.y + C_EPS)
81 return v0;
82 if (fp_equal(v0.y, v1.y)) {
83 if (v0.x > v1.x + C_EPS)
84 return v0;
85 return v1;
86 }
87 return v1;
88}
89
91static pointf min_(pointf v0, pointf v1) {
92 if (v0.y < v1.y - C_EPS)
93 return v0;
94 if (fp_equal(v0.y, v1.y)) {
95 if (v0.x < v1.x)
96 return v0;
97 return v1;
98 }
99 return v1;
100}
101
103 return greater_than(v0, v1) || equal_to(v0, v1);
104}
105
106static bool less_than(pointf v0, pointf v1) {
107 return !greater_than_equal_to(v0, v1);
108}
109
110/* Initialize the query structure (Q) and the trapezoid table (T)
111 * when the first segment is added to start the trapezoidation. The
112 * query-tree starts out with 4 trapezoids, one S-node and 2 Y-nodes
113 *
114 * 4
115 * -----------------------------------
116 * \
117 * 1 \ 2
118 * \
119 * -----------------------------------
120 * 3
121 */
122
123static size_t init_query_structure(int segnum, segment_t *seg, traps_t *tr,
124 qnodes_t *qs) {
125 segment_t *s = &seg[segnum];
126
127 const size_t i1 = newnode(qs);
128 LIST_AT(qs, i1)->nodetype = T_Y;
129 LIST_AT(qs, i1)->yval = max_(s->v0, s->v1); // root
130 const size_t root = i1;
131
132 const size_t i2 = newnode(qs);
133 LIST_AT(qs, i1)->right = i2;
134 LIST_AT(qs, i2)->nodetype = T_SINK;
135 LIST_AT(qs, i2)->parent = i1;
136
137 const size_t i3 = newnode(qs);
138 LIST_AT(qs, i1)->left = i3;
139 LIST_AT(qs, i3)->nodetype = T_Y;
140 LIST_AT(qs, i3)->yval = min_(s->v0, s->v1); // root
141 LIST_AT(qs, i3)->parent = i1;
142
143 const size_t i4 = newnode(qs);
144 LIST_AT(qs, i3)->left = i4;
145 LIST_AT(qs, i4)->nodetype = T_SINK;
146 LIST_AT(qs, i4)->parent = i3;
147
148 const size_t i5 = newnode(qs);
149 LIST_AT(qs, i3)->right = i5;
150 LIST_AT(qs, i5)->nodetype = T_X;
151 LIST_AT(qs, i5)->segnum = segnum;
152 LIST_AT(qs, i5)->parent = i3;
153
154 const size_t i6 = newnode(qs);
155 LIST_AT(qs, i5)->left = i6;
156 LIST_AT(qs, i6)->nodetype = T_SINK;
157 LIST_AT(qs, i6)->parent = i5;
158
159 const size_t i7 = newnode(qs);
160 LIST_AT(qs, i5)->right = i7;
161 LIST_AT(qs, i7)->nodetype = T_SINK;
162 LIST_AT(qs, i7)->parent = i5;
163
164 const size_t t1 = newtrap(tr); // middle left
165 const size_t t2 = newtrap(tr); // middle right
166 const size_t t3 = newtrap(tr); // bottom-most
167 const size_t t4 = newtrap(tr); // topmost
168
169 LIST_AT(tr, t1)->hi = LIST_GET(qs, i1).yval;
170 LIST_AT(tr, t2)->hi = LIST_GET(qs, i1).yval;
171 LIST_AT(tr, t4)->lo = LIST_GET(qs, i1).yval;
172 LIST_AT(tr, t1)->lo = LIST_GET(qs, i3).yval;
173 LIST_AT(tr, t2)->lo = LIST_GET(qs, i3).yval;
174 LIST_AT(tr, t3)->hi = LIST_GET(qs, i3).yval;
175 LIST_AT(tr, t4)->hi.y = DBL_MAX;
176 LIST_AT(tr, t4)->hi.x = DBL_MAX;
177 LIST_AT(tr, t3)->lo.y = -DBL_MAX;
178 LIST_AT(tr, t3)->lo.x = -DBL_MAX;
179 LIST_AT(tr, t1)->rseg = segnum;
180 LIST_AT(tr, t2)->lseg = segnum;
181 LIST_AT(tr, t1)->u0 = t4;
182 LIST_AT(tr, t2)->u0 = t4;
183 LIST_AT(tr, t1)->d0 = t3;
184 LIST_AT(tr, t2)->d0 = t3;
185 LIST_AT(tr, t4)->d0 = t1;
186 LIST_AT(tr, t3)->u0 = t1;
187 LIST_AT(tr, t4)->d1 = t2;
188 LIST_AT(tr, t3)->u1 = t2;
189
190 LIST_AT(tr, t1)->sink = i6;
191 LIST_AT(tr, t2)->sink = i7;
192 LIST_AT(tr, t3)->sink = i4;
193 LIST_AT(tr, t4)->sink = i2;
194
195 LIST_AT(tr, t1)->is_valid = true;
196 LIST_AT(tr, t2)->is_valid = true;
197 LIST_AT(tr, t3)->is_valid = true;
198 LIST_AT(tr, t4)->is_valid = true;
199
200 LIST_AT(qs, i2)->trnum = t4;
201 LIST_AT(qs, i4)->trnum = t3;
202 LIST_AT(qs, i6)->trnum = t1;
203 LIST_AT(qs, i7)->trnum = t2;
204
205 s->is_inserted = true;
206 return root;
207}
208
209/* Return true if the vertex v is to the left of line segment no.
210 * segnum. Takes care of the degenerate cases when both the vertices
211 * have the same y--cood, etc.
212 */
213static bool
214is_left_of (int segnum, segment_t* seg, pointf *v)
215{
216 segment_t *s = &seg[segnum];
217 double area;
218
219 if (greater_than(s->v1, s->v0)) { // segment going upwards
220 if (fp_equal(s->v1.y, v->y)) {
221 if (v->x < s->v1.x)
222 area = 1.0;
223 else
224 area = -1.0;
225 }
226 else if (fp_equal(s->v0.y, v->y)) {
227 if (v->x < s->v0.x)
228 area = 1.0;
229 else
230 area = -1.0;
231 }
232 else
233 area = cross(s->v0, s->v1, *v);
234 }
235 else /* v0 > v1 */
236 {
237 if (fp_equal(s->v1.y, v->y)) {
238 if (v->x < s->v1.x)
239 area = 1.0;
240 else
241 area = -1.0;
242 }
243 else if (fp_equal(s->v0.y, v->y)) {
244 if (v->x < s->v0.x)
245 area = 1.0;
246 else
247 area = -1.0;
248 }
249 else
250 area = cross(s->v1, s->v0, *v);
251 }
252
253 return area > 0.0;
254}
255
256/* Returns true if the corresponding endpoint of the given segment is */
257/* already inserted into the segment tree. Use the simple test of */
258/* whether the segment which shares this endpoint is already inserted */
259static bool inserted (int segnum, segment_t* seg, int whichpt)
260{
261 if (whichpt == FIRSTPT)
262 return seg[seg[segnum].prev].is_inserted;
263 else
264 return seg[seg[segnum].next].is_inserted;
265}
266
267/* This is query routine which determines which trapezoid does the
268 * point v lie in. The return value is the trapezoid number.
269 */
270static size_t locate_endpoint(pointf *v, pointf *vo, size_t r, segment_t *seg,
271 qnodes_t *qs) {
272 qnode_t *rptr = LIST_AT(qs, r);
273
274 switch (rptr->nodetype) {
275 case T_SINK:
276 return rptr->trnum;
277
278 case T_Y:
279 if (greater_than(*v, rptr->yval)) // above
280 return locate_endpoint(v, vo, rptr->right, seg, qs);
281 if (equal_to(*v, rptr->yval)) { // the point is already inserted
282 if (greater_than(*vo, rptr->yval)) // above
283 return locate_endpoint(v, vo, rptr->right, seg, qs);
284 return locate_endpoint(v, vo, rptr->left, seg, qs); // below
285 }
286 return locate_endpoint(v, vo, rptr->left, seg, qs); // below
287
288 case T_X:
289 if (equal_to(*v, seg[rptr->segnum].v0) ||
290 equal_to(*v, seg[rptr->segnum].v1)) {
291 if (fp_equal(v->y, vo->y)) { // horizontal segment
292 if (vo->x < v->x)
293 return locate_endpoint(v, vo, rptr->left, seg, qs); /* left */
294 return locate_endpoint(v, vo, rptr->right, seg, qs); // right
295 }
296
297 if (is_left_of(rptr->segnum, seg, vo))
298 return locate_endpoint(v, vo, rptr->left, seg, qs); /* left */
299 return locate_endpoint(v, vo, rptr->right, seg, qs); // right
300 }
301 if (is_left_of(rptr->segnum, seg, v))
302 return locate_endpoint(v, vo, rptr->left, seg, qs); /* left */
303 return locate_endpoint(v, vo, rptr->right, seg, qs); // right
304
305 default:
306 break;
307 }
308 UNREACHABLE();
309}
310
311/* Thread in the segment into the existing trapezoidation. The
312 * limiting trapezoids are given by tfirst and tlast (which are the
313 * trapezoids containing the two endpoints of the segment. Merges all
314 * possible trapezoids which flank this segment and have been recently
315 * divided because of its insertion
316 */
317static void merge_trapezoids(int segnum, size_t tfirst, size_t tlast, int side,
318 traps_t *tr, qnodes_t *qs) {
319 /* First merge polys on the LHS */
320 size_t t = tfirst;
321 while (is_valid_trap(t) &&
322 greater_than_equal_to(LIST_GET(tr, t).lo, LIST_GET(tr, tlast).lo)) {
323 size_t tnext;
324 bool cond;
325 if (side == S_LEFT)
326 cond = (is_valid_trap(tnext = LIST_GET(tr, t).d0) && LIST_GET(tr, tnext).rseg == segnum) ||
327 (is_valid_trap(tnext = LIST_GET(tr, t).d1) && LIST_GET(tr, tnext).rseg == segnum);
328 else
329 cond = (is_valid_trap(tnext = LIST_GET(tr, t).d0) && LIST_GET(tr, tnext).lseg == segnum) ||
330 (is_valid_trap(tnext = LIST_GET(tr, t).d1) && LIST_GET(tr, tnext).lseg == segnum);
331
332 if (cond)
333 {
334 if (LIST_GET(tr, t).lseg == LIST_GET(tr, tnext).lseg &&
335 LIST_GET(tr, t).rseg == LIST_GET(tr, tnext).rseg) // good neighbors
336 { /* merge them */
337 /* Use the upper node as the new node i.e. t */
338
339 const size_t ptnext = LIST_GET(qs, LIST_GET(tr, tnext).sink).parent;
340
341 if (LIST_GET(qs, ptnext).left == LIST_GET(tr, tnext).sink)
342 LIST_AT(qs, ptnext)->left = LIST_GET(tr, t).sink;
343 else
344 LIST_AT(qs, ptnext)->right = LIST_GET(tr, t).sink; // redirect parent
345
346
347 /* Change the upper neighbours of the lower trapezoids */
348
349 if (is_valid_trap(LIST_AT(tr, t)->d0 = LIST_GET(tr, tnext).d0)) {
350 if (LIST_GET(tr, LIST_GET(tr, t).d0).u0 == tnext)
351 LIST_AT(tr, LIST_GET(tr, t).d0)->u0 = t;
352 else if (LIST_GET(tr, LIST_GET(tr, t).d0).u1 == tnext)
353 LIST_AT(tr, LIST_GET(tr, t).d0)->u1 = t;
354 }
355
356 if (is_valid_trap(LIST_AT(tr, t)->d1 = LIST_GET(tr, tnext).d1)) {
357 if (LIST_GET(tr, LIST_GET(tr, t).d1).u0 == tnext)
358 LIST_AT(tr, LIST_GET(tr, t).d1)->u0 = t;
359 else if (LIST_GET(tr, LIST_GET(tr, t).d1).u1 == tnext)
360 LIST_AT(tr, LIST_GET(tr, t).d1)->u1 = t;
361 }
362
363 LIST_AT(tr, t)->lo = LIST_GET(tr, tnext).lo;
364 LIST_AT(tr, tnext)->is_valid = false; // invalidate the lower
365 /* trapezium */
366 }
367 else /* not good neighbours */
368 t = tnext;
369 }
370 else /* do not satisfy the outer if */
371 t = tnext;
372
373 } /* end-while */
374
375}
376
377static void update_trapezoid(segment_t *s, segment_t *seg, traps_t *tr,
378 size_t t, size_t tn) {
379 if (is_valid_trap(LIST_GET(tr, t).u0) && is_valid_trap(LIST_GET(tr, t).u1))
380 { /* continuation of a chain from abv. */
381 if (is_valid_trap(LIST_GET(tr, t).usave)) { // three upper neighbours
382 if (LIST_GET(tr, t).uside == S_LEFT)
383 {
384 LIST_AT(tr, tn)->u0 = LIST_GET(tr, t).u1;
385 LIST_AT(tr, t)->u1 = SIZE_MAX;
386 LIST_AT(tr, tn)->u1 = LIST_GET(tr, t).usave;
387
388 LIST_AT(tr, LIST_GET(tr, t).u0)->d0 = t;
389 LIST_AT(tr, LIST_GET(tr, tn).u0)->d0 = tn;
390 LIST_AT(tr, LIST_GET(tr, tn).u1)->d0 = tn;
391 }
392 else /* intersects in the right */
393 {
394 LIST_AT(tr, tn)->u1 = SIZE_MAX;
395 LIST_AT(tr, tn)->u0 = LIST_GET(tr, t).u1;
396 LIST_AT(tr, t)->u1 = LIST_GET(tr, t).u0;
397 LIST_AT(tr, t)->u0 = LIST_GET(tr, t).usave;
398
399 LIST_AT(tr, LIST_GET(tr, t).u0)->d0 = t;
400 LIST_AT(tr, LIST_GET(tr, t).u1)->d0 = t;
401 LIST_AT(tr, LIST_GET(tr, tn).u0)->d0 = tn;
402 }
403
404 LIST_AT(tr, t)->usave = 0;
405 LIST_AT(tr, tn)->usave = 0;
406 }
407 else /* No usave.... simple case */
408 {
409 LIST_AT(tr, tn)->u0 = LIST_GET(tr, t).u1;
410 LIST_AT(tr, t)->u1 = SIZE_MAX;
411 LIST_AT(tr, tn)->u1 = SIZE_MAX;
412 LIST_AT(tr, LIST_GET(tr, tn).u0)->d0 = tn;
413 }
414 }
415 else
416 { /* fresh seg. or upward cusp */
417 const size_t tmp_u = LIST_GET(tr, t).u0;
418 size_t td0;
419 if (is_valid_trap(td0 = LIST_GET(tr, tmp_u).d0) && is_valid_trap(LIST_GET(tr, tmp_u).d1))
420 { /* upward cusp */
421 if (LIST_GET(tr, td0).rseg > 0 && !is_left_of(LIST_GET(tr, td0).rseg, seg, &s->v1))
422 {
423 LIST_AT(tr, t)->u0 = SIZE_MAX;
424 LIST_AT(tr, t)->u1 = SIZE_MAX;
425 LIST_AT(tr, tn)->u1 = SIZE_MAX;
426 LIST_AT(tr, LIST_GET(tr, tn).u0)->d1 = tn;
427 }
428 else /* cusp going leftwards */
429 {
430 LIST_AT(tr, tn)->u0 = SIZE_MAX;
431 LIST_AT(tr, tn)->u1 = SIZE_MAX;
432 LIST_AT(tr, t)->u1 = SIZE_MAX;
433 LIST_AT(tr, LIST_GET(tr, t).u0)->d0 = t;
434 }
435 }
436 else /* fresh segment */
437 {
438 LIST_AT(tr, LIST_GET(tr, t).u0)->d0 = t;
439 LIST_AT(tr, LIST_GET(tr, t).u0)->d1 = tn;
440 }
441 }
442}
443
444/* Add in the new segment into the trapezoidation and update Q and T
445 * structures. First locate the two endpoints of the segment in the
446 * Q-structure. Then start from the topmost trapezoid and go down to
447 * the lower trapezoid dividing all the trapezoids in between .
448 *
449 * @return 0 on success
450 */
451static WUR int add_segment(int segnum, segment_t *seg, traps_t *tr,
452 qnodes_t *qs) {
453 segment_t s;
454 size_t tfirst, tlast;
455 size_t tfirstr = 0, tlastr = 0;
456 bool tribot = false;
457 bool is_swapped;
458 int tmptriseg;
459
460 s = seg[segnum];
461 if (greater_than(s.v1, s.v0)) { // Get higher vertex in v0
462 SWAP(&s.v0, &s.v1);
463 SWAP(&s.root0, &s.root1);
464 is_swapped = true;
465 }
466 else is_swapped = false;
467
468 if (!inserted(segnum, seg, is_swapped ? LASTPT : FIRSTPT))
469 /* insert v0 in the tree */
470 {
471 size_t tmp_d;
472
473 const size_t tu = locate_endpoint(&s.v0, &s.v1, s.root0, seg, qs);
474 const size_t tl = newtrap(tr); // tl is the new lower trapezoid
475 LIST_SET(tr, tl, LIST_GET(tr, tu));
476 LIST_AT(tr, tu)->lo = s.v0;
477 LIST_AT(tr, tl)->hi = s.v0;
478 LIST_AT(tr, tu)->d0 = tl;
479 LIST_AT(tr, tu)->d1 = 0;
480 LIST_AT(tr, tl)->u0 = tu;
481 LIST_AT(tr, tl)->u1 = 0;
482
483 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d0) && LIST_GET(tr, tmp_d).u0 == tu)
484 LIST_AT(tr, tmp_d)->u0 = tl;
485 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d0) && LIST_GET(tr, tmp_d).u1 == tu)
486 LIST_AT(tr, tmp_d)->u1 = tl;
487
488 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d1) && LIST_GET(tr, tmp_d).u0 == tu)
489 LIST_AT(tr, tmp_d)->u0 = tl;
490 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d1) && LIST_GET(tr, tmp_d).u1 == tu)
491 LIST_AT(tr, tmp_d)->u1 = tl;
492
493 /* Now update the query structure and obtain the sinks for the */
494 /* two trapezoids */
495
496 const size_t i1 = newnode(qs); // Upper trapezoid sink
497 const size_t i2 = newnode(qs); // Lower trapezoid sink
498 const size_t sk = LIST_GET(tr, tu).sink;
499
500 LIST_AT(qs, sk)->nodetype = T_Y;
501 LIST_AT(qs, sk)->yval = s.v0;
502 LIST_AT(qs, sk)->segnum = segnum; // not really required … maybe later
503 LIST_AT(qs, sk)->left = i2;
504 LIST_AT(qs, sk)->right = i1;
505
506 LIST_AT(qs, i1)->nodetype = T_SINK;
507 LIST_AT(qs, i1)->trnum = tu;
508 LIST_AT(qs, i1)->parent = sk;
509
510 LIST_AT(qs, i2)->nodetype = T_SINK;
511 LIST_AT(qs, i2)->trnum = tl;
512 LIST_AT(qs, i2)->parent = sk;
513
514 LIST_AT(tr, tu)->sink = i1;
515 LIST_AT(tr, tl)->sink = i2;
516 tfirst = tl;
517 }
518 else /* v0 already present */
519 { /* Get the topmost intersecting trapezoid */
520 tfirst = locate_endpoint(&s.v0, &s.v1, s.root0, seg, qs);
521 }
522
523
524 if (!inserted(segnum, seg, is_swapped ? FIRSTPT : LASTPT))
525 /* insert v1 in the tree */
526 {
527 size_t tmp_d;
528
529 const size_t tu = locate_endpoint(&s.v1, &s.v0, s.root1, seg, qs);
530
531 const size_t tl = newtrap(tr); // tl is the new lower trapezoid
532 LIST_SET(tr, tl, LIST_GET(tr, tu));
533 LIST_AT(tr, tu)->lo = s.v1;
534 LIST_AT(tr, tl)->hi = s.v1;
535 LIST_AT(tr, tu)->d0 = tl;
536 LIST_AT(tr, tu)->d1 = 0;
537 LIST_AT(tr, tl)->u0 = tu;
538 LIST_AT(tr, tl)->u1 = 0;
539
540 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d0) && LIST_GET(tr, tmp_d).u0 == tu)
541 LIST_AT(tr, tmp_d)->u0 = tl;
542 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d0) && LIST_GET(tr, tmp_d).u1 == tu)
543 LIST_AT(tr, tmp_d)->u1 = tl;
544
545 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d1) && LIST_GET(tr, tmp_d).u0 == tu)
546 LIST_AT(tr, tmp_d)->u0 = tl;
547 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d1) && LIST_GET(tr, tmp_d).u1 == tu)
548 LIST_AT(tr, tmp_d)->u1 = tl;
549
550 /* Now update the query structure and obtain the sinks for the */
551 /* two trapezoids */
552
553 const size_t i1 = newnode(qs); // Upper trapezoid sink
554 const size_t i2 = newnode(qs); // Lower trapezoid sink
555 const size_t sk = LIST_GET(tr, tu).sink;
556
557 LIST_AT(qs, sk)->nodetype = T_Y;
558 LIST_AT(qs, sk)->yval = s.v1;
559 LIST_AT(qs, sk)->segnum = segnum; // not really required … maybe later
560 LIST_AT(qs, sk)->left = i2;
561 LIST_AT(qs, sk)->right = i1;
562
563 LIST_AT(qs, i1)->nodetype = T_SINK;
564 LIST_AT(qs, i1)->trnum = tu;
565 LIST_AT(qs, i1)->parent = sk;
566
567 LIST_AT(qs, i2)->nodetype = T_SINK;
568 LIST_AT(qs, i2)->trnum = tl;
569 LIST_AT(qs, i2)->parent = sk;
570
571 LIST_AT(tr, tu)->sink = i1;
572 LIST_AT(tr, tl)->sink = i2;
573 tlast = tu;
574 }
575 else /* v1 already present */
576 { /* Get the lowermost intersecting trapezoid */
577 tlast = locate_endpoint(&s.v1, &s.v0, s.root1, seg, qs);
578 tribot = true;
579 }
580
581 /* Thread the segment into the query tree creating a new X-node */
582 /* First, split all the trapezoids which are intersected by s into */
583 /* two */
584
585 size_t t = tfirst; // topmost trapezoid
586
587 while (is_valid_trap(t) &&
588 greater_than_equal_to(LIST_GET(tr, t).lo, LIST_GET(tr, tlast).lo))
589 /* traverse from top to bot */
590 {
591 const size_t sk = LIST_GET(tr, t).sink;
592 const size_t i1 = newnode(qs); // left trapezoid sink
593 const size_t i2 = newnode(qs); // right trapezoid sink
594
595 LIST_AT(qs, sk)->nodetype = T_X;
596 LIST_AT(qs, sk)->segnum = segnum;
597 LIST_AT(qs, sk)->left = i1;
598 LIST_AT(qs, sk)->right = i2;
599
600 LIST_AT(qs, i1)->nodetype = T_SINK; // left trapezoid (use existing one)
601 LIST_AT(qs, i1)->trnum = t;
602 LIST_AT(qs, i1)->parent = sk;
603
604 LIST_AT(qs, i2)->nodetype = T_SINK; // right trapezoid (allocate new)
605 const size_t tn = newtrap(tr);
606 LIST_AT(qs, i2)->trnum = tn;
607 LIST_AT(tr, tn)->is_valid = true;
608 LIST_AT(qs, i2)->parent = sk;
609
610 if (t == tfirst)
611 tfirstr = tn;
612 if (equal_to(LIST_GET(tr, t).lo, LIST_GET(tr, tlast).lo))
613 tlastr = tn;
614
615 LIST_SET(tr, tn, LIST_GET(tr, t));
616 LIST_AT(tr, t)->sink = i1;
617 LIST_AT(tr, tn)->sink = i2;
618 const size_t t_sav = t;
619 const size_t tn_sav = tn;
620
621 if (!is_valid_trap(LIST_GET(tr, t).d0) &&
622 !is_valid_trap(LIST_GET(tr, t).d1)) { // error
623 fprintf(stderr, "trapezoid segment construction failed\n");
624 return -1;
625 }
626
627 /* only one trapezoid below. partition t into two and make the */
628 /* two resulting trapezoids t and tn as the upper neighbours of */
629 /* the sole lower trapezoid */
630
631 else if (is_valid_trap(LIST_GET(tr, t).d0) &&
632 !is_valid_trap(LIST_GET(tr, t).d1)) { // only one trapezoid below
633 update_trapezoid(&s, seg, tr, t, tn);
634
635 if (fp_equal(LIST_GET(tr, t).lo.y, LIST_GET(tr, tlast).lo.y) &&
636 fp_equal(LIST_GET(tr, t).lo.x, LIST_GET(tr, tlast).lo.x) && tribot)
637 { /* bottom forms a triangle */
638
639 if (is_swapped)
640 tmptriseg = seg[segnum].prev;
641 else
642 tmptriseg = seg[segnum].next;
643
644 if (tmptriseg > 0 && is_left_of(tmptriseg, seg, &s.v0))
645 {
646 /* L-R downward cusp */
647 LIST_AT(tr, LIST_GET(tr, t).d0)->u0 = t;
648 LIST_AT(tr, tn)->d0 = SIZE_MAX;
649 LIST_AT(tr, tn)->d1 = SIZE_MAX;
650 }
651 else
652 {
653 /* R-L downward cusp */
654 LIST_AT(tr, LIST_GET(tr, tn).d0)->u1 = tn;
655 LIST_AT(tr, t)->d0 = SIZE_MAX;
656 LIST_AT(tr, t)->d1 = SIZE_MAX;
657 }
658 }
659 else
660 {
661 if (is_valid_trap(LIST_GET(tr, LIST_GET(tr, t).d0).u0) &&
662 is_valid_trap(LIST_GET(tr, LIST_GET(tr, t).d0).u1)) {
663 if (LIST_GET(tr, LIST_GET(tr, t).d0).u0 == t) { // passes through LHS
664 LIST_AT(tr, LIST_GET(tr, t).d0)->usave = LIST_GET(tr, LIST_GET(tr, t).d0).u1;
665 LIST_AT(tr, LIST_GET(tr, t).d0)->uside = S_LEFT;
666 }
667 else
668 {
669 LIST_AT(tr, LIST_GET(tr, t).d0)->usave = LIST_GET(tr, LIST_GET(tr, t).d0).u0;
670 LIST_AT(tr, LIST_GET(tr, t).d0)->uside = S_RIGHT;
671 }
672 }
673 LIST_AT(tr, LIST_GET(tr, t).d0)->u0 = t;
674 LIST_AT(tr, LIST_GET(tr, t).d0)->u1 = tn;
675 }
676
677 t = LIST_GET(tr, t).d0;
678 }
679
680
681 else if (!is_valid_trap(LIST_GET(tr, t).d0) &&
682 is_valid_trap(LIST_GET(tr, t).d1)) { // only one trapezoid below
683 update_trapezoid(&s, seg, tr, t, tn);
684
685 if (fp_equal(LIST_GET(tr, t).lo.y, LIST_GET(tr, tlast).lo.y) &&
686 fp_equal(LIST_GET(tr, t).lo.x, LIST_GET(tr, tlast).lo.x) && tribot)
687 { /* bottom forms a triangle */
688
689 if (is_swapped)
690 tmptriseg = seg[segnum].prev;
691 else
692 tmptriseg = seg[segnum].next;
693
694 if (tmptriseg > 0 && is_left_of(tmptriseg, seg, &s.v0))
695 {
696 /* L-R downward cusp */
697 LIST_AT(tr, LIST_GET(tr, t).d1)->u0 = t;
698 LIST_AT(tr, tn)->d0 = SIZE_MAX;
699 LIST_AT(tr, tn)->d1 = SIZE_MAX;
700 }
701 else
702 {
703 /* R-L downward cusp */
704 LIST_AT(tr, LIST_GET(tr, tn).d1)->u1 = tn;
705 LIST_AT(tr, t)->d0 = SIZE_MAX;
706 LIST_AT(tr, t)->d1 = SIZE_MAX;
707 }
708 }
709 else
710 {
711 if (is_valid_trap(LIST_GET(tr, LIST_GET(tr, t).d1).u0) &&
712 is_valid_trap(LIST_GET(tr, LIST_GET(tr, t).d1).u1)) {
713 if (LIST_GET(tr, LIST_GET(tr, t).d1).u0 == t) { // passes through LHS
714 LIST_AT(tr, LIST_GET(tr, t).d1)->usave = LIST_GET(tr, LIST_GET(tr, t).d1).u1;
715 LIST_AT(tr, LIST_GET(tr, t).d1)->uside = S_LEFT;
716 }
717 else
718 {
719 LIST_AT(tr, LIST_GET(tr, t).d1)->usave = LIST_GET(tr, LIST_GET(tr, t).d1).u0;
720 LIST_AT(tr, LIST_GET(tr, t).d1)->uside = S_RIGHT;
721 }
722 }
723 LIST_AT(tr, LIST_GET(tr, t).d1)->u0 = t;
724 LIST_AT(tr, LIST_GET(tr, t).d1)->u1 = tn;
725 }
726
727 t = LIST_GET(tr, t).d1;
728 }
729
730 /* two trapezoids below. Find out which one is intersected by */
731 /* this segment and proceed down that one */
732
733 else
734 {
735 double y0, yt;
736 pointf tmppt;
737 size_t tnext;
738 bool i_d0 = false;
739 if (fp_equal(LIST_GET(tr, t).lo.y, s.v0.y)) {
740 if (LIST_GET(tr, t).lo.x > s.v0.x)
741 i_d0 = true;
742 }
743 else
744 {
745 tmppt.y = y0 = LIST_GET(tr, t).lo.y;
746 yt = (y0 - s.v0.y)/(s.v1.y - s.v0.y);
747 tmppt.x = s.v0.x + yt * (s.v1.x - s.v0.x);
748
749 if (less_than(tmppt, LIST_GET(tr, t).lo))
750 i_d0 = true;
751 }
752
753 /* check continuity from the top so that the lower-neighbour */
754 /* values are properly filled for the upper trapezoid */
755
756 update_trapezoid(&s, seg, tr, t, tn);
757
758 if (fp_equal(LIST_GET(tr, t).lo.y, LIST_GET(tr, tlast).lo.y) &&
759 fp_equal(LIST_GET(tr, t).lo.x, LIST_GET(tr, tlast).lo.x) && tribot)
760 {
761 /* this case arises only at the lowest trapezoid.. i.e.
762 tlast, if the lower endpoint of the segment is
763 already inserted in the structure */
764
765 LIST_AT(tr, LIST_GET(tr, t).d0)->u0 = t;
766 LIST_AT(tr, LIST_GET(tr, t).d0)->u1 = SIZE_MAX;
767 LIST_AT(tr, LIST_GET(tr, t).d1)->u0 = tn;
768 LIST_AT(tr, LIST_GET(tr, t).d1)->u1 = SIZE_MAX;
769
770 LIST_AT(tr, tn)->d0 = LIST_GET(tr, t).d1;
771 LIST_AT(tr, t)->d1 = SIZE_MAX;
772 LIST_AT(tr, tn)->d1 = SIZE_MAX;
773
774 tnext = LIST_GET(tr, t).d1;
775 }
776 else if (i_d0)
777 /* intersecting d0 */
778 {
779 LIST_AT(tr, LIST_GET(tr, t).d0)->u0 = t;
780 LIST_AT(tr, LIST_GET(tr, t).d0)->u1 = tn;
781 LIST_AT(tr, LIST_GET(tr, t).d1)->u0 = tn;
782 LIST_AT(tr, LIST_GET(tr, t).d1)->u1 = SIZE_MAX;
783
784 /* new code to determine the bottom neighbours of the */
785 /* newly partitioned trapezoid */
786
787 LIST_AT(tr, t)->d1 = SIZE_MAX;
788
789 tnext = LIST_GET(tr, t).d0;
790 }
791 else /* intersecting d1 */
792 {
793 LIST_AT(tr, LIST_GET(tr, t).d0)->u0 = t;
794 LIST_AT(tr, LIST_GET(tr, t).d0)->u1 = SIZE_MAX;
795 LIST_AT(tr, LIST_GET(tr, t).d1)->u0 = t;
796 LIST_AT(tr, LIST_GET(tr, t).d1)->u1 = tn;
797
798 /* new code to determine the bottom neighbours of the */
799 /* newly partitioned trapezoid */
800
801 LIST_AT(tr, tn)->d0 = LIST_GET(tr, t).d1;
802 LIST_AT(tr, tn)->d1 = SIZE_MAX;
803
804 tnext = LIST_GET(tr, t).d1;
805 }
806
807 t = tnext;
808 }
809
810 LIST_AT(tr, t_sav)->rseg = segnum;
811 LIST_AT(tr, tn_sav)->lseg = segnum;
812 } /* end-while */
813
814 /* Now combine those trapezoids which share common segments. We can */
815 /* use the pointers to the parent to connect these together. This */
816 /* works only because all these new trapezoids have been formed */
817 /* due to splitting by the segment, and hence have only one parent */
818
819 const size_t tfirstl = tfirst;
820 const size_t tlastl = tlast;
821 merge_trapezoids(segnum, tfirstl, tlastl, S_LEFT, tr, qs);
822 merge_trapezoids(segnum, tfirstr, tlastr, S_RIGHT, tr, qs);
823
824 seg[segnum].is_inserted = true;
825 return 0;
826}
827
828/* Update the roots stored for each of the endpoints of the segment.
829 * This is done to speed up the location-query for the endpoint when
830 * the segment is inserted into the trapezoidation subsequently
831 */
832static void
833find_new_roots(int segnum, segment_t *seg, traps_t *tr, qnodes_t *qs) {
834 segment_t *s = &seg[segnum];
835
836 if (s->is_inserted) return;
837
838 s->root0 = (size_t)locate_endpoint(&s->v0, &s->v1, s->root0, seg, qs);
839 s->root0 = LIST_GET(tr, s->root0).sink;
840
841 s->root1 = (size_t)locate_endpoint(&s->v1, &s->v0, s->root1, seg, qs);
842 s->root1 = LIST_GET(tr, s->root1).sink;
843}
844
845/* Get log*n for given n */
846static int math_logstar_n(int n)
847{
848 int i = 0;
849 for (double v = n; v >= 1; i++)
850 v = log2(v);
851
852 return i - 1;
853}
854
855static int math_N(int n, int h)
856{
857 double v = n;
858 for (int i = 0; i < h; i++)
859 v = log2(v);
860
861 return (int)ceil(n / v);
862}
863
864/* Main routine to perform trapezoidation */
865traps_t construct_trapezoids(int nseg, segment_t *seg, int *permute) {
866 int i;
867 int h;
868 int segi = 0;
869
870 // we will append later nodes by expanding this on-demand
871 qnodes_t qs = {0};
872
873 // First trapezoid is reserved as a sentinel. We will append later
874 // trapezoids by expanding this on-demand.
875 traps_t tr = {0};
876 LIST_APPEND(&tr, (trap_t){0});
877
878 /* Add the first segment and get the query structure and trapezoid */
879 /* list initialised */
880
881 const size_t root = init_query_structure(permute[segi++], seg, &tr, &qs);
882
883 for (i = 1; i <= nseg; i++)
884 seg[i].root0 = seg[i].root1 = root;
885
886 const int logstar = math_logstar_n(nseg);
887 for (h = 1; h <= logstar; h++) {
888 for (i = math_N(nseg, h -1) + 1; i <= math_N(nseg, h); i++) {
889 if (add_segment(permute[segi++], seg, &tr, &qs) != 0) {
890 LIST_FREE(&tr);
891 goto done;
892 }
893 }
894
895 /* Find a new root for each of the segment endpoints */
896 for (i = 1; i <= nseg; i++)
897 find_new_roots(i, seg, &tr, &qs);
898 }
899
900 for (i = math_N(nseg, logstar) + 1; i <= nseg; i++) {
901 if (add_segment(permute[segi++], seg, &tr, &qs) != 0) {
902 LIST_FREE(&tr);
903 goto done;
904 }
905 }
906
907done:
908 LIST_FREE(&qs);
909 return tr;
910}
Memory allocation wrappers that exit on failure.
static Agnode_t * newnode(Agraph_t *g, IDTYPE id, uint64_t seq)
Definition node.c:75
#define right(i)
Definition closest.c:74
#define left
Definition dthdr.h:12
geometric types and macros (e.g. points and boxes)
#define SIZE_MAX
Definition gmlscan.c:347
Arithmetic helper functions.
#define SWAP(a, b)
Definition gv_math.h:137
type-generic dynamically expanding list
#define LIST_AT(list, index)
Definition list.h:172
#define LIST_APPEND(list,...)
Definition list.h:124
#define LIST(type)
Definition list.h:55
#define LIST_SIZE(list)
Definition list.h:80
#define LIST_FREE(list)
Definition list.h:350
#define LIST_SET(list, index, item)
Definition list.h:206
#define LIST_GET(list, index)
Definition list.h:159
double x
Definition geom.h:29
double y
Definition geom.h:29
int segnum
Definition trapezoid.c:56
size_t parent
doubly linked DAG
Definition trapezoid.c:59
size_t right
children
Definition trapezoid.c:60
size_t trnum
Definition trapezoid.c:58
int nodetype
Definition trapezoid.c:55
size_t left
Definition trapezoid.c:60
pointf yval
Definition trapezoid.c:57
pointf v0
Definition trap.h:30
int prev
Definition trap.h:34
int next
Definition trap.h:33
pointf v1
Definition trap.h:30
bool is_inserted
Definition trap.h:31
Definition trap.h:40
trapezoid elements and utilities for partition.c
static bool is_valid_trap(size_t index)
Definition trap.h:56
static bool equal_to(pointf v0, pointf v1)
Definition trap.h:88
static bool greater_than(pointf v0, pointf v1)
Definition trap.h:92
#define C_EPS
Definition trap.h:63
static bool fp_equal(double s, double t)
Definition trap.h:69
static double cross(pointf v0, pointf v1, pointf v2)
Definition trapezoid.c:50
static bool less_than(pointf v0, pointf v1)
Definition trapezoid.c:106
#define T_Y
Definition trapezoid.c:41
static size_t locate_endpoint(pointf *v, pointf *vo, size_t r, segment_t *seg, qnodes_t *qs)
Definition trapezoid.c:270
#define S_LEFT
Definition trapezoid.c:47
static WUR int add_segment(int segnum, segment_t *seg, traps_t *tr, qnodes_t *qs)
Definition trapezoid.c:451
static pointf max_(pointf v0, pointf v1)
return the maximum of the two points
Definition trapezoid.c:79
#define T_X
Definition trapezoid.c:40
static int math_logstar_n(int n)
Definition trapezoid.c:846
static int math_N(int n, int h)
Definition trapezoid.c:855
static void update_trapezoid(segment_t *s, segment_t *seg, traps_t *tr, size_t t, size_t tn)
Definition trapezoid.c:377
#define FIRSTPT
Definition trapezoid.c:44
#define LASTPT
Definition trapezoid.c:45
static bool greater_than_equal_to(pointf v0, pointf v1)
Definition trapezoid.c:102
static void merge_trapezoids(int segnum, size_t tfirst, size_t tlast, int side, traps_t *tr, qnodes_t *qs)
Definition trapezoid.c:317
static void find_new_roots(int segnum, segment_t *seg, traps_t *tr, qnodes_t *qs)
Definition trapezoid.c:833
traps_t construct_trapezoids(int nseg, segment_t *seg, int *permute)
Definition trapezoid.c:865
static pointf min_(pointf v0, pointf v1)
return the minimum of the two points
Definition trapezoid.c:91
#define S_RIGHT
Definition trapezoid.c:48
static size_t init_query_structure(int segnum, segment_t *seg, traps_t *tr, qnodes_t *qs)
Definition trapezoid.c:123
static bool inserted(int segnum, segment_t *seg, int whichpt)
Definition trapezoid.c:259
static bool is_left_of(int segnum, segment_t *seg, pointf *v)
Definition trapezoid.c:214
static size_t newtrap(traps_t *tr)
Definition trapezoid.c:73
#define T_SINK
Definition trapezoid.c:42
graphs, nodes and edges info: Agraphinfo_t, Agnodeinfo_t and Agedgeinfo_t
Definition grammar.c:90
#define UNREACHABLE()
Definition unreachable.h:30
abstraction for squashing compiler warnings for unused symbols
#define WUR
Definition unused.h:43