Graphviz 16.1.1~dev.20261101.1716
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 size_t 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(size_t 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 is_left_of(size_t segnum, segment_t *seg, pointf *v) {
214 segment_t *s = &seg[segnum];
215 double area;
216
217 if (greater_than(s->v1, s->v0)) { // segment going upwards
218 if (fp_equal(s->v1.y, v->y)) {
219 if (v->x < s->v1.x)
220 area = 1.0;
221 else
222 area = -1.0;
223 }
224 else if (fp_equal(s->v0.y, v->y)) {
225 if (v->x < s->v0.x)
226 area = 1.0;
227 else
228 area = -1.0;
229 }
230 else
231 area = cross(s->v0, s->v1, *v);
232 }
233 else /* v0 > v1 */
234 {
235 if (fp_equal(s->v1.y, v->y)) {
236 if (v->x < s->v1.x)
237 area = 1.0;
238 else
239 area = -1.0;
240 }
241 else if (fp_equal(s->v0.y, v->y)) {
242 if (v->x < s->v0.x)
243 area = 1.0;
244 else
245 area = -1.0;
246 }
247 else
248 area = cross(s->v1, s->v0, *v);
249 }
250
251 return area > 0.0;
252}
253
254/* Returns true if the corresponding endpoint of the given segment is */
255/* already inserted into the segment tree. Use the simple test of */
256/* whether the segment which shares this endpoint is already inserted */
257static bool inserted(size_t segnum, segment_t *seg, int whichpt) {
258 if (whichpt == FIRSTPT)
259 return seg[seg[segnum].prev].is_inserted;
260 else
261 return seg[seg[segnum].next].is_inserted;
262}
263
264/* This is query routine which determines which trapezoid does the
265 * point v lie in. The return value is the trapezoid number.
266 */
267static size_t locate_endpoint(pointf *v, pointf *vo, size_t r, segment_t *seg,
268 qnodes_t *qs) {
269 qnode_t *rptr = LIST_AT(qs, r);
270
271 switch (rptr->nodetype) {
272 case T_SINK:
273 return rptr->trnum;
274
275 case T_Y:
276 if (greater_than(*v, rptr->yval)) // above
277 return locate_endpoint(v, vo, rptr->right, seg, qs);
278 if (equal_to(*v, rptr->yval)) { // the point is already inserted
279 if (greater_than(*vo, rptr->yval)) // above
280 return locate_endpoint(v, vo, rptr->right, seg, qs);
281 return locate_endpoint(v, vo, rptr->left, seg, qs); // below
282 }
283 return locate_endpoint(v, vo, rptr->left, seg, qs); // below
284
285 case T_X:
286 if (equal_to(*v, seg[rptr->segnum].v0) ||
287 equal_to(*v, seg[rptr->segnum].v1)) {
288 if (fp_equal(v->y, vo->y)) { // horizontal segment
289 if (vo->x < v->x)
290 return locate_endpoint(v, vo, rptr->left, seg, qs); /* left */
291 return locate_endpoint(v, vo, rptr->right, seg, qs); // right
292 }
293
294 if (is_left_of(rptr->segnum, seg, vo))
295 return locate_endpoint(v, vo, rptr->left, seg, qs); /* left */
296 return locate_endpoint(v, vo, rptr->right, seg, qs); // right
297 }
298 if (is_left_of(rptr->segnum, seg, v))
299 return locate_endpoint(v, vo, rptr->left, seg, qs); /* left */
300 return locate_endpoint(v, vo, rptr->right, seg, qs); // right
301
302 default:
303 break;
304 }
305 UNREACHABLE();
306}
307
308/* Thread in the segment into the existing trapezoidation. The
309 * limiting trapezoids are given by tfirst and tlast (which are the
310 * trapezoids containing the two endpoints of the segment. Merges all
311 * possible trapezoids which flank this segment and have been recently
312 * divided because of its insertion
313 */
314static void merge_trapezoids(size_t segnum, size_t tfirst, size_t tlast,
315 int side, traps_t *tr, qnodes_t *qs) {
316 /* First merge polys on the LHS */
317 size_t t = tfirst;
318 while (is_valid_trap(t) &&
319 greater_than_equal_to(LIST_GET(tr, t).lo, LIST_GET(tr, tlast).lo)) {
320 size_t tnext;
321 bool cond;
322 if (side == S_LEFT)
323 cond = (is_valid_trap(tnext = LIST_GET(tr, t).d0) && LIST_GET(tr, tnext).rseg == segnum) ||
324 (is_valid_trap(tnext = LIST_GET(tr, t).d1) && LIST_GET(tr, tnext).rseg == segnum);
325 else
326 cond = (is_valid_trap(tnext = LIST_GET(tr, t).d0) && LIST_GET(tr, tnext).lseg == segnum) ||
327 (is_valid_trap(tnext = LIST_GET(tr, t).d1) && LIST_GET(tr, tnext).lseg == segnum);
328
329 if (cond)
330 {
331 if (LIST_GET(tr, t).lseg == LIST_GET(tr, tnext).lseg &&
332 LIST_GET(tr, t).rseg == LIST_GET(tr, tnext).rseg) // good neighbors
333 { /* merge them */
334 /* Use the upper node as the new node i.e. t */
335
336 const size_t ptnext = LIST_GET(qs, LIST_GET(tr, tnext).sink).parent;
337
338 if (LIST_GET(qs, ptnext).left == LIST_GET(tr, tnext).sink)
339 LIST_AT(qs, ptnext)->left = LIST_GET(tr, t).sink;
340 else
341 LIST_AT(qs, ptnext)->right = LIST_GET(tr, t).sink; // redirect parent
342
343
344 /* Change the upper neighbours of the lower trapezoids */
345
346 if (is_valid_trap(LIST_AT(tr, t)->d0 = LIST_GET(tr, tnext).d0)) {
347 if (LIST_GET(tr, LIST_GET(tr, t).d0).u0 == tnext)
348 LIST_AT(tr, LIST_GET(tr, t).d0)->u0 = t;
349 else if (LIST_GET(tr, LIST_GET(tr, t).d0).u1 == tnext)
350 LIST_AT(tr, LIST_GET(tr, t).d0)->u1 = t;
351 }
352
353 if (is_valid_trap(LIST_AT(tr, t)->d1 = LIST_GET(tr, tnext).d1)) {
354 if (LIST_GET(tr, LIST_GET(tr, t).d1).u0 == tnext)
355 LIST_AT(tr, LIST_GET(tr, t).d1)->u0 = t;
356 else if (LIST_GET(tr, LIST_GET(tr, t).d1).u1 == tnext)
357 LIST_AT(tr, LIST_GET(tr, t).d1)->u1 = t;
358 }
359
360 LIST_AT(tr, t)->lo = LIST_GET(tr, tnext).lo;
361 LIST_AT(tr, tnext)->is_valid = false; // invalidate the lower
362 /* trapezium */
363 }
364 else /* not good neighbours */
365 t = tnext;
366 }
367 else /* do not satisfy the outer if */
368 t = tnext;
369
370 } /* end-while */
371
372}
373
374static void update_trapezoid(segment_t *s, segment_t *seg, traps_t *tr,
375 size_t t, size_t tn) {
376 if (is_valid_trap(LIST_GET(tr, t).u0) && is_valid_trap(LIST_GET(tr, t).u1))
377 { /* continuation of a chain from abv. */
378 if (is_valid_trap(LIST_GET(tr, t).usave)) { // three upper neighbours
379 if (LIST_GET(tr, t).uside == S_LEFT)
380 {
381 LIST_AT(tr, tn)->u0 = LIST_GET(tr, t).u1;
382 LIST_AT(tr, t)->u1 = SIZE_MAX;
383 LIST_AT(tr, tn)->u1 = LIST_GET(tr, t).usave;
384
385 LIST_AT(tr, LIST_GET(tr, t).u0)->d0 = t;
386 LIST_AT(tr, LIST_GET(tr, tn).u0)->d0 = tn;
387 LIST_AT(tr, LIST_GET(tr, tn).u1)->d0 = tn;
388 }
389 else /* intersects in the right */
390 {
391 LIST_AT(tr, tn)->u1 = SIZE_MAX;
392 LIST_AT(tr, tn)->u0 = LIST_GET(tr, t).u1;
393 LIST_AT(tr, t)->u1 = LIST_GET(tr, t).u0;
394 LIST_AT(tr, t)->u0 = LIST_GET(tr, t).usave;
395
396 LIST_AT(tr, LIST_GET(tr, t).u0)->d0 = t;
397 LIST_AT(tr, LIST_GET(tr, t).u1)->d0 = t;
398 LIST_AT(tr, LIST_GET(tr, tn).u0)->d0 = tn;
399 }
400
401 LIST_AT(tr, t)->usave = 0;
402 LIST_AT(tr, tn)->usave = 0;
403 }
404 else /* No usave.... simple case */
405 {
406 LIST_AT(tr, tn)->u0 = LIST_GET(tr, t).u1;
407 LIST_AT(tr, t)->u1 = SIZE_MAX;
408 LIST_AT(tr, tn)->u1 = SIZE_MAX;
409 LIST_AT(tr, LIST_GET(tr, tn).u0)->d0 = tn;
410 }
411 }
412 else
413 { /* fresh seg. or upward cusp */
414 const size_t tmp_u = LIST_GET(tr, t).u0;
415 size_t td0;
416 if (is_valid_trap(td0 = LIST_GET(tr, tmp_u).d0) && is_valid_trap(LIST_GET(tr, tmp_u).d1))
417 { /* upward cusp */
418 if (LIST_GET(tr, td0).rseg > 0 && !is_left_of(LIST_GET(tr, td0).rseg, seg, &s->v1))
419 {
420 LIST_AT(tr, t)->u0 = SIZE_MAX;
421 LIST_AT(tr, t)->u1 = SIZE_MAX;
422 LIST_AT(tr, tn)->u1 = SIZE_MAX;
423 LIST_AT(tr, LIST_GET(tr, tn).u0)->d1 = tn;
424 }
425 else /* cusp going leftwards */
426 {
427 LIST_AT(tr, tn)->u0 = SIZE_MAX;
428 LIST_AT(tr, tn)->u1 = SIZE_MAX;
429 LIST_AT(tr, t)->u1 = SIZE_MAX;
430 LIST_AT(tr, LIST_GET(tr, t).u0)->d0 = t;
431 }
432 }
433 else /* fresh segment */
434 {
435 LIST_AT(tr, LIST_GET(tr, t).u0)->d0 = t;
436 LIST_AT(tr, LIST_GET(tr, t).u0)->d1 = tn;
437 }
438 }
439}
440
441/* Add in the new segment into the trapezoidation and update Q and T
442 * structures. First locate the two endpoints of the segment in the
443 * Q-structure. Then start from the topmost trapezoid and go down to
444 * the lower trapezoid dividing all the trapezoids in between .
445 *
446 * @return 0 on success
447 */
448static WUR int add_segment(size_t segnum, segment_t *seg, traps_t *tr,
449 qnodes_t *qs) {
450 segment_t s;
451 size_t tfirst, tlast;
452 size_t tfirstr = 0, tlastr = 0;
453 bool tribot = false;
454 bool is_swapped;
455 size_t tmptriseg;
456
457 s = seg[segnum];
458 if (greater_than(s.v1, s.v0)) { // Get higher vertex in v0
459 SWAP(&s.v0, &s.v1);
460 SWAP(&s.root0, &s.root1);
461 is_swapped = true;
462 }
463 else is_swapped = false;
464
465 if (!inserted(segnum, seg, is_swapped ? LASTPT : FIRSTPT))
466 /* insert v0 in the tree */
467 {
468 size_t tmp_d;
469
470 const size_t tu = locate_endpoint(&s.v0, &s.v1, s.root0, seg, qs);
471 const size_t tl = newtrap(tr); // tl is the new lower trapezoid
472 LIST_SET(tr, tl, LIST_GET(tr, tu));
473 LIST_AT(tr, tu)->lo = s.v0;
474 LIST_AT(tr, tl)->hi = s.v0;
475 LIST_AT(tr, tu)->d0 = tl;
476 LIST_AT(tr, tu)->d1 = 0;
477 LIST_AT(tr, tl)->u0 = tu;
478 LIST_AT(tr, tl)->u1 = 0;
479
480 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d0) && LIST_GET(tr, tmp_d).u0 == tu)
481 LIST_AT(tr, tmp_d)->u0 = tl;
482 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d0) && LIST_GET(tr, tmp_d).u1 == tu)
483 LIST_AT(tr, tmp_d)->u1 = tl;
484
485 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d1) && LIST_GET(tr, tmp_d).u0 == tu)
486 LIST_AT(tr, tmp_d)->u0 = tl;
487 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d1) && LIST_GET(tr, tmp_d).u1 == tu)
488 LIST_AT(tr, tmp_d)->u1 = tl;
489
490 /* Now update the query structure and obtain the sinks for the */
491 /* two trapezoids */
492
493 const size_t i1 = newnode(qs); // Upper trapezoid sink
494 const size_t i2 = newnode(qs); // Lower trapezoid sink
495 const size_t sk = LIST_GET(tr, tu).sink;
496
497 LIST_AT(qs, sk)->nodetype = T_Y;
498 LIST_AT(qs, sk)->yval = s.v0;
499 LIST_AT(qs, sk)->segnum = segnum; // not really required … maybe later
500 LIST_AT(qs, sk)->left = i2;
501 LIST_AT(qs, sk)->right = i1;
502
503 LIST_AT(qs, i1)->nodetype = T_SINK;
504 LIST_AT(qs, i1)->trnum = tu;
505 LIST_AT(qs, i1)->parent = sk;
506
507 LIST_AT(qs, i2)->nodetype = T_SINK;
508 LIST_AT(qs, i2)->trnum = tl;
509 LIST_AT(qs, i2)->parent = sk;
510
511 LIST_AT(tr, tu)->sink = i1;
512 LIST_AT(tr, tl)->sink = i2;
513 tfirst = tl;
514 }
515 else /* v0 already present */
516 { /* Get the topmost intersecting trapezoid */
517 tfirst = locate_endpoint(&s.v0, &s.v1, s.root0, seg, qs);
518 }
519
520
521 if (!inserted(segnum, seg, is_swapped ? FIRSTPT : LASTPT))
522 /* insert v1 in the tree */
523 {
524 size_t tmp_d;
525
526 const size_t tu = locate_endpoint(&s.v1, &s.v0, s.root1, seg, qs);
527
528 const size_t tl = newtrap(tr); // tl is the new lower trapezoid
529 LIST_SET(tr, tl, LIST_GET(tr, tu));
530 LIST_AT(tr, tu)->lo = s.v1;
531 LIST_AT(tr, tl)->hi = s.v1;
532 LIST_AT(tr, tu)->d0 = tl;
533 LIST_AT(tr, tu)->d1 = 0;
534 LIST_AT(tr, tl)->u0 = tu;
535 LIST_AT(tr, tl)->u1 = 0;
536
537 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d0) && LIST_GET(tr, tmp_d).u0 == tu)
538 LIST_AT(tr, tmp_d)->u0 = tl;
539 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d0) && LIST_GET(tr, tmp_d).u1 == tu)
540 LIST_AT(tr, tmp_d)->u1 = tl;
541
542 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d1) && LIST_GET(tr, tmp_d).u0 == tu)
543 LIST_AT(tr, tmp_d)->u0 = tl;
544 if (is_valid_trap(tmp_d = LIST_GET(tr, tl).d1) && LIST_GET(tr, tmp_d).u1 == tu)
545 LIST_AT(tr, tmp_d)->u1 = tl;
546
547 /* Now update the query structure and obtain the sinks for the */
548 /* two trapezoids */
549
550 const size_t i1 = newnode(qs); // Upper trapezoid sink
551 const size_t i2 = newnode(qs); // Lower trapezoid sink
552 const size_t sk = LIST_GET(tr, tu).sink;
553
554 LIST_AT(qs, sk)->nodetype = T_Y;
555 LIST_AT(qs, sk)->yval = s.v1;
556 LIST_AT(qs, sk)->segnum = segnum; // not really required … maybe later
557 LIST_AT(qs, sk)->left = i2;
558 LIST_AT(qs, sk)->right = i1;
559
560 LIST_AT(qs, i1)->nodetype = T_SINK;
561 LIST_AT(qs, i1)->trnum = tu;
562 LIST_AT(qs, i1)->parent = sk;
563
564 LIST_AT(qs, i2)->nodetype = T_SINK;
565 LIST_AT(qs, i2)->trnum = tl;
566 LIST_AT(qs, i2)->parent = sk;
567
568 LIST_AT(tr, tu)->sink = i1;
569 LIST_AT(tr, tl)->sink = i2;
570 tlast = tu;
571 }
572 else /* v1 already present */
573 { /* Get the lowermost intersecting trapezoid */
574 tlast = locate_endpoint(&s.v1, &s.v0, s.root1, seg, qs);
575 tribot = true;
576 }
577
578 /* Thread the segment into the query tree creating a new X-node */
579 /* First, split all the trapezoids which are intersected by s into */
580 /* two */
581
582 size_t t = tfirst; // topmost trapezoid
583
584 while (is_valid_trap(t) &&
585 greater_than_equal_to(LIST_GET(tr, t).lo, LIST_GET(tr, tlast).lo))
586 /* traverse from top to bot */
587 {
588 const size_t sk = LIST_GET(tr, t).sink;
589 const size_t i1 = newnode(qs); // left trapezoid sink
590 const size_t i2 = newnode(qs); // right trapezoid sink
591
592 LIST_AT(qs, sk)->nodetype = T_X;
593 LIST_AT(qs, sk)->segnum = segnum;
594 LIST_AT(qs, sk)->left = i1;
595 LIST_AT(qs, sk)->right = i2;
596
597 LIST_AT(qs, i1)->nodetype = T_SINK; // left trapezoid (use existing one)
598 LIST_AT(qs, i1)->trnum = t;
599 LIST_AT(qs, i1)->parent = sk;
600
601 LIST_AT(qs, i2)->nodetype = T_SINK; // right trapezoid (allocate new)
602 const size_t tn = newtrap(tr);
603 LIST_AT(qs, i2)->trnum = tn;
604 LIST_AT(tr, tn)->is_valid = true;
605 LIST_AT(qs, i2)->parent = sk;
606
607 if (t == tfirst)
608 tfirstr = tn;
609 if (equal_to(LIST_GET(tr, t).lo, LIST_GET(tr, tlast).lo))
610 tlastr = tn;
611
612 LIST_SET(tr, tn, LIST_GET(tr, t));
613 LIST_AT(tr, t)->sink = i1;
614 LIST_AT(tr, tn)->sink = i2;
615 const size_t t_sav = t;
616 const size_t tn_sav = tn;
617
618 if (!is_valid_trap(LIST_GET(tr, t).d0) &&
619 !is_valid_trap(LIST_GET(tr, t).d1)) { // error
620 fprintf(stderr, "trapezoid segment construction failed\n");
621 return -1;
622 }
623
624 /* only one trapezoid below. partition t into two and make the */
625 /* two resulting trapezoids t and tn as the upper neighbours of */
626 /* the sole lower trapezoid */
627
628 else if (is_valid_trap(LIST_GET(tr, t).d0) &&
629 !is_valid_trap(LIST_GET(tr, t).d1)) { // only one trapezoid below
630 update_trapezoid(&s, seg, tr, t, tn);
631
632 if (fp_equal(LIST_GET(tr, t).lo.y, LIST_GET(tr, tlast).lo.y) &&
633 fp_equal(LIST_GET(tr, t).lo.x, LIST_GET(tr, tlast).lo.x) && tribot)
634 { /* bottom forms a triangle */
635
636 if (is_swapped)
637 tmptriseg = seg[segnum].prev;
638 else
639 tmptriseg = seg[segnum].next;
640
641 if (tmptriseg > 0 && is_left_of(tmptriseg, seg, &s.v0))
642 {
643 /* L-R downward cusp */
644 LIST_AT(tr, LIST_GET(tr, t).d0)->u0 = t;
645 LIST_AT(tr, tn)->d0 = SIZE_MAX;
646 LIST_AT(tr, tn)->d1 = SIZE_MAX;
647 }
648 else
649 {
650 /* R-L downward cusp */
651 LIST_AT(tr, LIST_GET(tr, tn).d0)->u1 = tn;
652 LIST_AT(tr, t)->d0 = SIZE_MAX;
653 LIST_AT(tr, t)->d1 = SIZE_MAX;
654 }
655 }
656 else
657 {
658 if (is_valid_trap(LIST_GET(tr, LIST_GET(tr, t).d0).u0) &&
659 is_valid_trap(LIST_GET(tr, LIST_GET(tr, t).d0).u1)) {
660 if (LIST_GET(tr, LIST_GET(tr, t).d0).u0 == t) { // passes through LHS
661 LIST_AT(tr, LIST_GET(tr, t).d0)->usave = LIST_GET(tr, LIST_GET(tr, t).d0).u1;
662 LIST_AT(tr, LIST_GET(tr, t).d0)->uside = S_LEFT;
663 }
664 else
665 {
666 LIST_AT(tr, LIST_GET(tr, t).d0)->usave = LIST_GET(tr, LIST_GET(tr, t).d0).u0;
667 LIST_AT(tr, LIST_GET(tr, t).d0)->uside = S_RIGHT;
668 }
669 }
670 LIST_AT(tr, LIST_GET(tr, t).d0)->u0 = t;
671 LIST_AT(tr, LIST_GET(tr, t).d0)->u1 = tn;
672 }
673
674 t = LIST_GET(tr, t).d0;
675 }
676
677
678 else if (!is_valid_trap(LIST_GET(tr, t).d0) &&
679 is_valid_trap(LIST_GET(tr, t).d1)) { // only one trapezoid below
680 update_trapezoid(&s, seg, tr, t, tn);
681
682 if (fp_equal(LIST_GET(tr, t).lo.y, LIST_GET(tr, tlast).lo.y) &&
683 fp_equal(LIST_GET(tr, t).lo.x, LIST_GET(tr, tlast).lo.x) && tribot)
684 { /* bottom forms a triangle */
685
686 if (is_swapped)
687 tmptriseg = seg[segnum].prev;
688 else
689 tmptriseg = seg[segnum].next;
690
691 if (tmptriseg > 0 && is_left_of(tmptriseg, seg, &s.v0))
692 {
693 /* L-R downward cusp */
694 LIST_AT(tr, LIST_GET(tr, t).d1)->u0 = t;
695 LIST_AT(tr, tn)->d0 = SIZE_MAX;
696 LIST_AT(tr, tn)->d1 = SIZE_MAX;
697 }
698 else
699 {
700 /* R-L downward cusp */
701 LIST_AT(tr, LIST_GET(tr, tn).d1)->u1 = tn;
702 LIST_AT(tr, t)->d0 = SIZE_MAX;
703 LIST_AT(tr, t)->d1 = SIZE_MAX;
704 }
705 }
706 else
707 {
708 if (is_valid_trap(LIST_GET(tr, LIST_GET(tr, t).d1).u0) &&
709 is_valid_trap(LIST_GET(tr, LIST_GET(tr, t).d1).u1)) {
710 if (LIST_GET(tr, LIST_GET(tr, t).d1).u0 == t) { // passes through LHS
711 LIST_AT(tr, LIST_GET(tr, t).d1)->usave = LIST_GET(tr, LIST_GET(tr, t).d1).u1;
712 LIST_AT(tr, LIST_GET(tr, t).d1)->uside = S_LEFT;
713 }
714 else
715 {
716 LIST_AT(tr, LIST_GET(tr, t).d1)->usave = LIST_GET(tr, LIST_GET(tr, t).d1).u0;
717 LIST_AT(tr, LIST_GET(tr, t).d1)->uside = S_RIGHT;
718 }
719 }
720 LIST_AT(tr, LIST_GET(tr, t).d1)->u0 = t;
721 LIST_AT(tr, LIST_GET(tr, t).d1)->u1 = tn;
722 }
723
724 t = LIST_GET(tr, t).d1;
725 }
726
727 /* two trapezoids below. Find out which one is intersected by */
728 /* this segment and proceed down that one */
729
730 else
731 {
732 double y0, yt;
733 pointf tmppt;
734 size_t tnext;
735 bool i_d0 = false;
736 if (fp_equal(LIST_GET(tr, t).lo.y, s.v0.y)) {
737 if (LIST_GET(tr, t).lo.x > s.v0.x)
738 i_d0 = true;
739 }
740 else
741 {
742 tmppt.y = y0 = LIST_GET(tr, t).lo.y;
743 yt = (y0 - s.v0.y)/(s.v1.y - s.v0.y);
744 tmppt.x = s.v0.x + yt * (s.v1.x - s.v0.x);
745
746 if (less_than(tmppt, LIST_GET(tr, t).lo))
747 i_d0 = true;
748 }
749
750 /* check continuity from the top so that the lower-neighbour */
751 /* values are properly filled for the upper trapezoid */
752
753 update_trapezoid(&s, seg, tr, t, tn);
754
755 if (fp_equal(LIST_GET(tr, t).lo.y, LIST_GET(tr, tlast).lo.y) &&
756 fp_equal(LIST_GET(tr, t).lo.x, LIST_GET(tr, tlast).lo.x) && tribot)
757 {
758 /* this case arises only at the lowest trapezoid.. i.e.
759 tlast, if the lower endpoint of the segment is
760 already inserted in the structure */
761
762 LIST_AT(tr, LIST_GET(tr, t).d0)->u0 = t;
763 LIST_AT(tr, LIST_GET(tr, t).d0)->u1 = SIZE_MAX;
764 LIST_AT(tr, LIST_GET(tr, t).d1)->u0 = tn;
765 LIST_AT(tr, LIST_GET(tr, t).d1)->u1 = SIZE_MAX;
766
767 LIST_AT(tr, tn)->d0 = LIST_GET(tr, t).d1;
768 LIST_AT(tr, t)->d1 = SIZE_MAX;
769 LIST_AT(tr, tn)->d1 = SIZE_MAX;
770
771 tnext = LIST_GET(tr, t).d1;
772 }
773 else if (i_d0)
774 /* intersecting d0 */
775 {
776 LIST_AT(tr, LIST_GET(tr, t).d0)->u0 = t;
777 LIST_AT(tr, LIST_GET(tr, t).d0)->u1 = tn;
778 LIST_AT(tr, LIST_GET(tr, t).d1)->u0 = tn;
779 LIST_AT(tr, LIST_GET(tr, t).d1)->u1 = SIZE_MAX;
780
781 /* new code to determine the bottom neighbours of the */
782 /* newly partitioned trapezoid */
783
784 LIST_AT(tr, t)->d1 = SIZE_MAX;
785
786 tnext = LIST_GET(tr, t).d0;
787 }
788 else /* intersecting d1 */
789 {
790 LIST_AT(tr, LIST_GET(tr, t).d0)->u0 = t;
791 LIST_AT(tr, LIST_GET(tr, t).d0)->u1 = SIZE_MAX;
792 LIST_AT(tr, LIST_GET(tr, t).d1)->u0 = t;
793 LIST_AT(tr, LIST_GET(tr, t).d1)->u1 = tn;
794
795 /* new code to determine the bottom neighbours of the */
796 /* newly partitioned trapezoid */
797
798 LIST_AT(tr, tn)->d0 = LIST_GET(tr, t).d1;
799 LIST_AT(tr, tn)->d1 = SIZE_MAX;
800
801 tnext = LIST_GET(tr, t).d1;
802 }
803
804 t = tnext;
805 }
806
807 LIST_AT(tr, t_sav)->rseg = segnum;
808 LIST_AT(tr, tn_sav)->lseg = segnum;
809 } /* end-while */
810
811 /* Now combine those trapezoids which share common segments. We can */
812 /* use the pointers to the parent to connect these together. This */
813 /* works only because all these new trapezoids have been formed */
814 /* due to splitting by the segment, and hence have only one parent */
815
816 const size_t tfirstl = tfirst;
817 const size_t tlastl = tlast;
818 merge_trapezoids(segnum, tfirstl, tlastl, S_LEFT, tr, qs);
819 merge_trapezoids(segnum, tfirstr, tlastr, S_RIGHT, tr, qs);
820
821 seg[segnum].is_inserted = true;
822 return 0;
823}
824
825/* Update the roots stored for each of the endpoints of the segment.
826 * This is done to speed up the location-query for the endpoint when
827 * the segment is inserted into the trapezoidation subsequently
828 */
829static void find_new_roots(size_t segnum, segment_t *seg, traps_t *tr,
830 qnodes_t *qs) {
831 segment_t *s = &seg[segnum];
832
833 if (s->is_inserted) return;
834
835 s->root0 = (size_t)locate_endpoint(&s->v0, &s->v1, s->root0, seg, qs);
836 s->root0 = LIST_GET(tr, s->root0).sink;
837
838 s->root1 = (size_t)locate_endpoint(&s->v1, &s->v0, s->root1, seg, qs);
839 s->root1 = LIST_GET(tr, s->root1).sink;
840}
841
842/* Get log*n for given n */
843static size_t math_logstar_n(size_t n) {
844 assert(n >= 1);
845 size_t i = 0;
846 for (double v = (double)n; v >= 1; i++)
847 v = log2(v);
848
849 return i - 1;
850}
851
852static size_t math_N(size_t n, size_t h) {
853 double v = (double)n;
854 for (size_t i = 0; i < h; i++)
855 v = log2(v);
856
857 return (size_t)ceil((double)n / v);
858}
859
860/* Main routine to perform trapezoidation */
861traps_t construct_trapezoids(size_t nseg, segment_t *seg, size_t *permute) {
862 size_t segi = 0;
863
864 // we will append later nodes by expanding this on-demand
865 qnodes_t qs = {0};
866
867 // First trapezoid is reserved as a sentinel. We will append later
868 // trapezoids by expanding this on-demand.
869 traps_t tr = {0};
870 LIST_APPEND(&tr, (trap_t){0});
871
872 /* Add the first segment and get the query structure and trapezoid */
873 /* list initialised */
874
875 const size_t root = init_query_structure(permute[segi++], seg, &tr, &qs);
876
877 for (size_t i = 1; i <= nseg; i++)
878 seg[i].root0 = seg[i].root1 = root;
879
880 const size_t logstar = math_logstar_n(nseg);
881 for (size_t h = 1; h <= logstar; h++) {
882 for (size_t i = math_N(nseg, h -1) + 1; i <= math_N(nseg, h); i++) {
883 if (add_segment(permute[segi++], seg, &tr, &qs) != 0) {
884 LIST_FREE(&tr);
885 goto done;
886 }
887 }
888
889 /* Find a new root for each of the segment endpoints */
890 for (size_t i = 1; i <= nseg; i++)
891 find_new_roots(i, seg, &tr, &qs);
892 }
893
894 for (size_t i = math_N(nseg, logstar) + 1; i <= nseg; i++) {
895 if (add_segment(permute[segi++], seg, &tr, &qs) != 0) {
896 LIST_FREE(&tr);
897 goto done;
898 }
899 }
900
901done:
902 LIST_FREE(&qs);
903 return tr;
904}
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:133
type-generic dynamically expanding list
#define LIST_AT(list, index)
Definition list.h:210
#define LIST_APPEND(list,...)
Definition list.h:151
#define LIST(type)
Definition list.h:66
#define LIST_SIZE(list)
Definition list.h:92
#define LIST_FREE(list)
Definition list.h:413
#define LIST_SET(list, index, item)
Definition list.h:253
#define LIST_GET(list, index)
Definition list.h:197
double x
Definition geom.h:29
double y
Definition geom.h:29
size_t parent
doubly linked DAG
Definition trapezoid.c:59
size_t segnum
Definition trapezoid.c:56
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
size_t next
next logical segment
Definition trap.h:33
pointf v0
Definition trap.h:30
pointf v1
Definition trap.h:30
bool is_inserted
Definition trap.h:31
size_t prev
previous segment
Definition trap.h:34
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 size_t math_logstar_n(size_t n)
Definition trapezoid.c:843
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:267
#define S_LEFT
Definition trapezoid.c:47
static WUR int add_segment(size_t segnum, segment_t *seg, traps_t *tr, qnodes_t *qs)
Definition trapezoid.c:448
static pointf max_(pointf v0, pointf v1)
return the maximum of the two points
Definition trapezoid.c:79
traps_t construct_trapezoids(size_t nseg, segment_t *seg, size_t *permute)
Definition trapezoid.c:861
#define T_X
Definition trapezoid.c:40
static bool is_left_of(size_t segnum, segment_t *seg, pointf *v)
Definition trapezoid.c:213
static size_t math_N(size_t n, size_t h)
Definition trapezoid.c:852
static void update_trapezoid(segment_t *s, segment_t *seg, traps_t *tr, size_t t, size_t tn)
Definition trapezoid.c:374
#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 find_new_roots(size_t segnum, segment_t *seg, traps_t *tr, qnodes_t *qs)
Definition trapezoid.c:829
static bool inserted(size_t segnum, segment_t *seg, int whichpt)
Definition trapezoid.c:257
static void merge_trapezoids(size_t segnum, size_t tfirst, size_t tlast, int side, traps_t *tr, qnodes_t *qs)
Definition trapezoid.c:314
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(size_t segnum, segment_t *seg, traps_t *tr, qnodes_t *qs)
Definition trapezoid.c:123
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