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