ezEngine Release 25.08
Loading...
Searching...
No Matches
jc_voronoi.h
1// Copyright (c) 2015-2023 Mathias Westerdahl
2// For LICENSE (MIT), USAGE or HISTORY, see bottom of file
3
4#ifndef JC_VORONOI_H
5# define JC_VORONOI_H
6
7# include <float.h>
8# include <math.h>
9# include <stddef.h>
10# include <stdint.h>
11# include <stdlib.h>
12
13# include <assert.h>
14
15# ifdef __cplusplus
16extern "C"
17{
18# endif
19
20# ifndef JCV_REAL_TYPE
21# define JCV_REAL_TYPE float
22# endif
23
24# ifndef JCV_REAL_TYPE_EPSILON
25# define JCV_REAL_TYPE_EPSILON FLT_EPSILON
26# endif
27
28# ifndef JCV_ATAN2
29# define JCV_ATAN2(_Y_, _X_) atan2f(_Y_, _X_)
30# endif
31
32# ifndef JCV_SQRT
33# define JCV_SQRT(_X_) sqrtf(_X_)
34# endif
35
36# ifndef JCV_PI
37# define JCV_PI 3.14159265358979323846264338327950288f
38# endif
39
40# ifndef JCV_FLT_MAX
41# define JCV_FLT_MAX 3.402823466e+38F
42# endif
43
44# ifndef JCV_EDGE_INTERSECT_THRESHOLD
45// Fix for Issue #40
46# define JCV_EDGE_INTERSECT_THRESHOLD 1.0e-10F
47# endif
48
49 // Also see: JCV_DISABLE_STRUCT_PACKING
50
51 typedef JCV_REAL_TYPE jcv_real;
52
53 typedef struct jcv_point_ jcv_point;
54 typedef struct jcv_rect_ jcv_rect;
55 typedef struct jcv_site_ jcv_site;
56 typedef struct jcv_edge_ jcv_edge;
57 typedef struct jcv_graphedge_ jcv_graphedge;
60 typedef struct jcv_diagram_ jcv_diagram;
61 typedef struct jcv_clipper_ jcv_clipper;
62 typedef struct jcv_context_internal_ jcv_context_internal;
63
65 typedef int (*jcv_clip_test_point_fn)(const jcv_clipper* clipper, const jcv_point p);
69 typedef int (*jcv_clip_edge_fn)(const jcv_clipper* clipper, jcv_edge* e);
74 typedef void (*jcv_clip_fillgap_fn)(const jcv_clipper* clipper, jcv_context_internal* allocator, jcv_site* s);
75
76
77
84 extern void jcv_diagram_generate(int num_points, const jcv_point* points, const jcv_rect* rect, const jcv_clipper* clipper, jcv_diagram* diagram);
85
86 typedef void* (*FJCVAllocFn)(void* userctx, size_t size);
87 typedef void (*FJCVFreeFn)(void* userctx, void* p);
88
89 // Same as above, but allows the client to use a custom allocator
90 extern void jcv_diagram_generate_useralloc(int num_points, const jcv_point* points, const jcv_rect* rect, const jcv_clipper* clipper, void* userallocctx, FJCVAllocFn allocfn, FJCVFreeFn freefn, jcv_diagram* diagram);
91
92 // Uses free (or the registered custom free function)
93 extern void jcv_diagram_free(jcv_diagram* diagram);
94
95 // Returns an array of sites, where each index is the same as the original input point array.
96 extern const jcv_site* jcv_diagram_get_sites(const jcv_diagram* diagram);
97
98 // Returns a linked list of all the voronoi edges
99 // excluding the ones that lie on the borders of the bounding box.
100 // For a full list of edges, you need to iterate over the sites, and their graph edges.
101 extern const jcv_edge* jcv_diagram_get_edges(const jcv_diagram* diagram);
102
103 // Iterates over a list of edges, skipping invalid edges (where p0==p1)
104 extern const jcv_edge* jcv_diagram_get_next_edge(const jcv_edge* edge);
105
106 // Creates an iterator over the delauney edges of a voronoi diagram
107 void jcv_delauney_begin(const jcv_diagram* diagram, jcv_delauney_iter* iter);
108
109 // Steps the iterator and returns the next edge
110 // Returns 0 when there are no more edges
111 int jcv_delauney_next(jcv_delauney_iter* iter, jcv_delauney_edge* next);
112
113 // For the default clipper
114 extern int jcv_boxshape_test(const jcv_clipper* clipper, const jcv_point p);
115 extern int jcv_boxshape_clip(const jcv_clipper* clipper, jcv_edge* e);
116 extern void jcv_boxshape_fillgaps(const jcv_clipper* clipper, jcv_context_internal* allocator, jcv_site* s);
117
118
119# ifndef JCV_DISABLE_STRUCT_PACKING
120# pragma pack(push, 1)
121# endif
122
124 {
125 jcv_real x;
126 jcv_real y;
127 };
128
130 {
131 struct jcv_graphedge_* next;
132 struct jcv_edge_* edge;
133 struct jcv_site_* neighbor;
134 jcv_point pos[2];
135 jcv_real angle;
136 };
137
139 {
140 jcv_point p;
141 int index; // Index into the original list of points
142 jcv_graphedge* edges; // The half edges owned by the cell
143 };
144
145 // The coefficients a, b and c are from the general line equation: ax * by + c = 0
147 {
148 struct jcv_edge_* next;
149 jcv_site* sites[2];
150 jcv_point pos[2];
151 jcv_real a;
152 jcv_real b;
153 jcv_real c;
154 };
155
157 {
158 const jcv_edge* sentinel;
159 const jcv_edge* current;
160 };
161
163 {
164 const jcv_edge* edge; // The voronoi edge separating the two sites
165 const jcv_site* sites[2];
166 jcv_point pos[2]; // the positions of the two sites
167 };
168
170 {
171 jcv_point min;
172 jcv_point max;
173 };
174
176 {
177 jcv_clip_test_point_fn test_fn;
178 jcv_clip_edge_fn clip_fn;
179 jcv_clip_fillgap_fn fill_fn;
180 jcv_point min; // The bounding rect min
181 jcv_point max; // The bounding rect max
182 void* ctx; // User defined context
183 };
184
186 {
187 jcv_context_internal* internal;
188 int numsites;
189 jcv_point min;
190 jcv_point max;
191 };
192
193# ifndef JCV_DISABLE_STRUCT_PACKING
194# pragma pack(pop)
195# endif
196
197# ifdef __cplusplus
198}
199# endif
200
201#endif // JC_VORONOI_H
202
203#ifdef JC_VORONOI_IMPLEMENTATION
204# undef JC_VORONOI_IMPLEMENTATION
205
206# include <memory.h>
207
208// INTERNAL FUNCTIONS
209
210# if defined(_MSC_VER) && !defined(__cplusplus)
211# define inline __inline
212# endif
213
214static const int JCV_DIRECTION_LEFT = 0;
215static const int JCV_DIRECTION_RIGHT = 1;
216static const jcv_real JCV_INVALID_VALUE = (jcv_real)-JCV_FLT_MAX;
217
218// jcv_real
219
220static inline jcv_real jcv_abs(jcv_real v)
221{
222 return (v < 0) ? -v : v;
223}
224
225static inline int jcv_real_eq(jcv_real a, jcv_real b)
226{
227 return jcv_abs(a - b) < JCV_REAL_TYPE_EPSILON;
228}
229
230static inline jcv_real jcv_real_to_int(jcv_real v)
231{
232 return (sizeof(jcv_real) == 4) ? (jcv_real)(int)v : (jcv_real)(long long)v;
233}
234
235// Only used for calculating the initial bounding box
236static inline jcv_real jcv_floor(jcv_real v)
237{
238 jcv_real i = jcv_real_to_int(v);
239 return (v < i) ? i - 1 : i;
240}
241
242// Only used for calculating the initial bounding box
243static inline jcv_real jcv_ceil(jcv_real v)
244{
245 jcv_real i = jcv_real_to_int(v);
246 return (v > i) ? i + 1 : i;
247}
248
249static inline jcv_real jcv_min(jcv_real a, jcv_real b)
250{
251 return a < b ? a : b;
252}
253
254static inline jcv_real jcv_max(jcv_real a, jcv_real b)
255{
256 return a > b ? a : b;
257}
258
259// jcv_point
260
261static inline int jcv_point_cmp(const void* p1, const void* p2)
262{
263 const jcv_point* s1 = (const jcv_point*)p1;
264 const jcv_point* s2 = (const jcv_point*)p2;
265 return (s1->y != s2->y) ? (s1->y < s2->y ? -1 : 1) : (s1->x < s2->x ? -1 : 1);
266}
267
268static inline int jcv_point_less(const jcv_point* pt1, const jcv_point* pt2)
269{
270 return (pt1->y == pt2->y) ? (pt1->x < pt2->x) : pt1->y < pt2->y;
271}
272
273static inline int jcv_point_eq(const jcv_point* pt1, const jcv_point* pt2)
274{
275 return jcv_real_eq(pt1->y, pt2->y) && jcv_real_eq(pt1->x, pt2->x);
276}
277
278static inline int jcv_point_on_box_edge(const jcv_point* pt, const jcv_point* min, const jcv_point* max)
279{
280 return pt->x == min->x || pt->y == min->y || pt->x == max->x || pt->y == max->y;
281}
282
283// corners
284
285static const int JCV_EDGE_LEFT = 1;
286static const int JCV_EDGE_RIGHT = 2;
287static const int JCV_EDGE_BOTTOM = 4;
288static const int JCV_EDGE_TOP = 8;
289
290static const int JCV_CORNER_NONE = 0;
291static const int JCV_CORNER_TOP_LEFT = 1;
292static const int JCV_CORNER_BOTTOM_LEFT = 2;
293static const int JCV_CORNER_BOTTOM_RIGHT = 3;
294static const int JCV_CORNER_TOP_RIGHT = 4;
295
296static inline int jcv_get_edge_flags(const jcv_point* pt, const jcv_point* min, const jcv_point* max)
297{
298 int flags = 0;
299 if (pt->x == min->x)
300 flags |= JCV_EDGE_LEFT;
301 else if (pt->x == max->x)
302 flags |= JCV_EDGE_RIGHT;
303 if (pt->y == min->y)
304 flags |= JCV_EDGE_BOTTOM;
305 else if (pt->y == max->y)
306 flags |= JCV_EDGE_TOP;
307 return flags;
308}
309
310static inline int jcv_edge_flags_to_corner(int edge_flags)
311{
312# define TEST_FLAGS(_FLAGS, _RETVAL) \
313 if ((_FLAGS) == edge_flags) \
314 return _RETVAL
315 TEST_FLAGS(JCV_EDGE_TOP | JCV_EDGE_LEFT, JCV_CORNER_TOP_LEFT);
316 TEST_FLAGS(JCV_EDGE_TOP | JCV_EDGE_RIGHT, JCV_CORNER_TOP_RIGHT);
317 TEST_FLAGS(JCV_EDGE_BOTTOM | JCV_EDGE_LEFT, JCV_CORNER_BOTTOM_LEFT);
318 TEST_FLAGS(JCV_EDGE_BOTTOM | JCV_EDGE_RIGHT, JCV_CORNER_BOTTOM_RIGHT);
319# undef TEST_FLAGS
320 return 0;
321}
322
323static inline int jcv_is_corner(int corner)
324{
325 return corner != 0;
326}
327
328static inline int jcv_corner_rotate_90(int corner)
329{
330 corner--;
331 corner = (corner + 1) % 4;
332 return corner + 1;
333}
334static inline jcv_point jcv_corner_to_point(int corner, const jcv_point* min, const jcv_point* max)
335{
336 jcv_point p;
337 if (corner == JCV_CORNER_TOP_LEFT)
338 {
339 p.x = min->x;
340 p.y = max->y;
341 }
342 else if (corner == JCV_CORNER_TOP_RIGHT)
343 {
344 p.x = max->x;
345 p.y = max->y;
346 }
347 else if (corner == JCV_CORNER_BOTTOM_LEFT)
348 {
349 p.x = min->x;
350 p.y = min->y;
351 }
352 else if (corner == JCV_CORNER_BOTTOM_RIGHT)
353 {
354 p.x = max->x;
355 p.y = min->y;
356 }
357 else
358 {
359 p.x = JCV_INVALID_VALUE;
360 p.y = JCV_INVALID_VALUE;
361 }
362 return p;
363}
364
365static inline jcv_real jcv_point_dist_sq(const jcv_point* pt1, const jcv_point* pt2)
366{
367 jcv_real diffx = pt1->x - pt2->x;
368 jcv_real diffy = pt1->y - pt2->y;
369 return diffx * diffx + diffy * diffy;
370}
371
372static inline jcv_real jcv_point_dist(const jcv_point* pt1, const jcv_point* pt2)
373{
374 return (jcv_real)(JCV_SQRT(jcv_point_dist_sq(pt1, pt2)));
375}
376
377// Structs
378
379# ifndef JCV_DISABLE_STRUCT_PACKING
380# pragma pack(push, 1)
381# endif
382
383typedef struct jcv_halfedge_
384{
385 jcv_edge* edge;
386 struct jcv_halfedge_* left;
387 struct jcv_halfedge_* right;
388 jcv_point vertex;
389 jcv_real y;
390 int direction; // 0=left, 1=right
391 int pqpos;
392} jcv_halfedge;
393
394typedef struct jcv_memoryblock_
395{
396 size_t sizefree;
397 struct jcv_memoryblock_* next;
398 char* memory;
399} jcv_memoryblock;
400
401
402typedef int (*FJCVPriorityQueuePrint)(const void* node, int pos);
403
404typedef struct jcv_priorityqueue_
405{
406 // Implements a binary heap
407 int maxnumitems;
408 int numitems;
409 void** items;
410} jcv_priorityqueue;
411
412
413struct jcv_context_internal_
414{
415 void* mem;
416 jcv_edge* edges;
417 jcv_halfedge* beachline_start;
418 jcv_halfedge* beachline_end;
419 jcv_halfedge* last_inserted;
420 jcv_priorityqueue* eventqueue;
421
422 jcv_site* sites;
423 jcv_site* bottomsite;
424 int numsites;
425 int currentsite;
426 int _padding;
427
428 jcv_memoryblock* memblocks;
429 jcv_edge* edgepool;
430 jcv_halfedge* halfedgepool;
431 void** eventmem;
432 jcv_clipper clipper;
433
434 void* memctx; // Given by the user
435 FJCVAllocFn alloc;
436 FJCVFreeFn free;
437
438 jcv_rect rect;
439};
440
441# ifndef JCV_DISABLE_STRUCT_PACKING
442# pragma pack(pop)
443# endif
444
445void jcv_diagram_free(jcv_diagram* d)
446{
447 jcv_context_internal* internal = d->internal;
448 void* memctx = internal->memctx;
449 FJCVFreeFn freefn = internal->free;
450 while (internal->memblocks)
451 {
452 jcv_memoryblock* p = internal->memblocks;
453 internal->memblocks = internal->memblocks->next;
454 freefn(memctx, p);
455 }
456
457 freefn(memctx, internal->mem);
458}
459
460const jcv_site* jcv_diagram_get_sites(const jcv_diagram* diagram)
461{
462 return diagram->internal->sites;
463}
464
465const jcv_edge* jcv_diagram_get_edges(const jcv_diagram* diagram)
466{
467 jcv_edge e;
468 e.next = diagram->internal->edges;
469 return jcv_diagram_get_next_edge(&e);
470}
471
472const jcv_edge* jcv_diagram_get_next_edge(const jcv_edge* edge)
473{
474 const jcv_edge* e = edge->next;
475 while (e != 0 && jcv_point_eq(&e->pos[0], &e->pos[1]))
476 {
477 e = e->next;
478 }
479 return e;
480}
481
482void jcv_delauney_begin(const jcv_diagram* diagram, jcv_delauney_iter* iter)
483{
484 iter->current = 0;
485 iter->sentinel = jcv_diagram_get_edges(diagram);
486}
487
488int jcv_delauney_next(jcv_delauney_iter* iter, jcv_delauney_edge* next)
489{
490 if (iter->sentinel)
491 {
492 iter->current = iter->sentinel;
493 iter->sentinel = 0;
494 }
495 else
496 {
497 // Note: If we use the raw edges, we still get a proper delauney triangulation
498 // However, the result looks less relevant to the cells contained within the bounding box
499 // E.g. some cells that look isolated from each other, suddenly still are connected,
500 // because they share an edge outside of the bounding box
501 iter->current = jcv_diagram_get_next_edge(iter->current);
502 }
503
504 while (iter->current && (iter->current->sites[0] == 0 || iter->current->sites[1] == 0))
505 {
506 iter->current = jcv_diagram_get_next_edge(iter->current);
507 }
508
509 if (!iter->current)
510 return 0;
511
512 next->edge = iter->current;
513 next->sites[0] = next->edge->sites[0];
514 next->sites[1] = next->edge->sites[1];
515 next->pos[0] = next->sites[0]->p;
516 next->pos[1] = next->sites[1]->p;
517 return 1;
518}
519
520static inline void* jcv_align(void* value, size_t alignment)
521{
522 return (void*)(((uintptr_t)value + (alignment - 1)) & ~(alignment - 1));
523}
524
525static void* jcv_alloc(jcv_context_internal* internal, size_t size)
526{
527 if (!internal->memblocks || internal->memblocks->sizefree < (size + sizeof(void*)))
528 {
529 size_t blocksize = 16 * 1024;
530 jcv_memoryblock* block = (jcv_memoryblock*)internal->alloc(internal->memctx, blocksize);
531 size_t offset = sizeof(jcv_memoryblock);
532 block->sizefree = blocksize - offset;
533 block->next = internal->memblocks;
534 block->memory = ((char*)block) + offset;
535 internal->memblocks = block;
536 }
537 void* p_raw = internal->memblocks->memory;
538 void* p_aligned = jcv_align(p_raw, sizeof(void*));
539 size += (uintptr_t)p_aligned - (uintptr_t)p_raw;
540 internal->memblocks->memory += size;
541 internal->memblocks->sizefree -= size;
542 return p_aligned;
543}
544
545static jcv_edge* jcv_alloc_edge(jcv_context_internal* internal)
546{
547 return (jcv_edge*)jcv_alloc(internal, sizeof(jcv_edge));
548}
549
550static jcv_halfedge* jcv_alloc_halfedge(jcv_context_internal* internal)
551{
552 if (internal->halfedgepool)
553 {
554 jcv_halfedge* edge = internal->halfedgepool;
555 internal->halfedgepool = internal->halfedgepool->right;
556 return edge;
557 }
558
559 return (jcv_halfedge*)jcv_alloc(internal, sizeof(jcv_halfedge));
560}
561
562static jcv_graphedge* jcv_alloc_graphedge(jcv_context_internal* internal)
563{
564 return (jcv_graphedge*)jcv_alloc(internal, sizeof(jcv_graphedge));
565}
566
567static void* jcv_alloc_fn(void* memctx, size_t size)
568{
569 (void)memctx;
570 return malloc(size);
571}
572
573static void jcv_free_fn(void* memctx, void* p)
574{
575 (void)memctx;
576 free(p);
577}
578
579// jcv_edge
580
581static inline int jcv_is_valid(const jcv_point* p)
582{
583 return (p->x != JCV_INVALID_VALUE || p->y != JCV_INVALID_VALUE) ? 1 : 0;
584}
585
586static void jcv_edge_create(jcv_edge* e, jcv_site* s1, jcv_site* s2)
587{
588 e->next = 0;
589 e->sites[0] = s1;
590 e->sites[1] = s2;
591 e->pos[0].x = JCV_INVALID_VALUE;
592 e->pos[0].y = JCV_INVALID_VALUE;
593 e->pos[1].x = JCV_INVALID_VALUE;
594 e->pos[1].y = JCV_INVALID_VALUE;
595
596 // Create line equation between S1 and S2:
597 // jcv_real a = -1 * (s2->p.y - s1->p.y);
598 // jcv_real b = s2->p.x - s1->p.x;
599 // //jcv_real c = -1 * (s2->p.x - s1->p.x) * s1->p.y + (s2->p.y - s1->p.y) * s1->p.x;
600 //
601 // // create perpendicular line
602 // jcv_real pa = b;
603 // jcv_real pb = -a;
604 // //jcv_real pc = pa * s1->p.x + pb * s1->p.y;
605 //
606 // // Move to the mid point
607 // jcv_real mx = s1->p.x + dx * jcv_real(0.5);
608 // jcv_real my = s1->p.y + dy * jcv_real(0.5);
609 // jcv_real pc = ( pa * mx + pb * my );
610
611 jcv_real dx = s2->p.x - s1->p.x;
612 jcv_real dy = s2->p.y - s1->p.y;
613 int dx_is_larger = (dx * dx) > (dy * dy); // instead of fabs
614
615 // Simplify it, using dx and dy
616 e->c = dx * (s1->p.x + dx * (jcv_real)0.5) + dy * (s1->p.y + dy * (jcv_real)0.5);
617
618 if (dx_is_larger)
619 {
620 e->a = (jcv_real)1;
621 e->b = dy / dx;
622 e->c /= dx;
623 }
624 else
625 {
626 e->a = dx / dy;
627 e->b = (jcv_real)1;
628 e->c /= dy;
629 }
630}
631
632// CLIPPING
633int jcv_boxshape_test(const jcv_clipper* clipper, const jcv_point p)
634{
635 return p.x >= clipper->min.x && p.x <= clipper->max.x &&
636 p.y >= clipper->min.y && p.y <= clipper->max.y;
637}
638
639// The line equation: ax + by + c = 0
640// see jcv_edge_create
641int jcv_boxshape_clip(const jcv_clipper* clipper, jcv_edge* e)
642{
643 jcv_real pxmin = clipper->min.x;
644 jcv_real pxmax = clipper->max.x;
645 jcv_real pymin = clipper->min.y;
646 jcv_real pymax = clipper->max.y;
647
648 jcv_real x1, y1, x2, y2;
649 jcv_point* s1;
650 jcv_point* s2;
651 if (e->a == (jcv_real)1 && e->b >= (jcv_real)0)
652 {
653 s1 = jcv_is_valid(&e->pos[1]) ? &e->pos[1] : 0;
654 s2 = jcv_is_valid(&e->pos[0]) ? &e->pos[0] : 0;
655 }
656 else
657 {
658 s1 = jcv_is_valid(&e->pos[0]) ? &e->pos[0] : 0;
659 s2 = jcv_is_valid(&e->pos[1]) ? &e->pos[1] : 0;
660 }
661
662 if (e->a == (jcv_real)1) // delta x is larger
663 {
664 y1 = pymin;
665 if (s1 != 0 && s1->y > pymin)
666 {
667 y1 = s1->y;
668 }
669 if (y1 > pymax)
670 {
671 y1 = pymax;
672 }
673 x1 = e->c - e->b * y1;
674 y2 = pymax;
675 if (s2 != 0 && s2->y < pymax)
676 y2 = s2->y;
677
678 if (y2 < pymin)
679 {
680 y2 = pymin;
681 }
682 x2 = (e->c) - (e->b) * y2;
683 // Never occurs according to lcov
684 // if( ((x1 > pxmax) & (x2 > pxmax)) | ((x1 < pxmin) & (x2 < pxmin)) )
685 // {
686 // return 0;
687 // }
688 if (x1 > pxmax)
689 {
690 x1 = pxmax;
691 y1 = (e->c - x1) / e->b;
692 }
693 else if (x1 < pxmin)
694 {
695 x1 = pxmin;
696 y1 = (e->c - x1) / e->b;
697 }
698 if (x2 > pxmax)
699 {
700 x2 = pxmax;
701 y2 = (e->c - x2) / e->b;
702 }
703 else if (x2 < pxmin)
704 {
705 x2 = pxmin;
706 y2 = (e->c - x2) / e->b;
707 }
708 }
709 else // delta y is larger
710 {
711 x1 = pxmin;
712 if (s1 != 0 && s1->x > pxmin)
713 x1 = s1->x;
714 if (x1 > pxmax)
715 {
716 x1 = pxmax;
717 }
718 y1 = e->c - e->a * x1;
719 x2 = pxmax;
720 if (s2 != 0 && s2->x < pxmax)
721 x2 = s2->x;
722 if (x2 < pxmin)
723 {
724 x2 = pxmin;
725 }
726 y2 = e->c - e->a * x2;
727 // Never occurs according to lcov
728 // if( ((y1 > pymax) & (y2 > pymax)) | ((y1 < pymin) & (y2 < pymin)) )
729 // {
730 // return 0;
731 // }
732 if (y1 > pymax)
733 {
734 y1 = pymax;
735 x1 = (e->c - y1) / e->a;
736 }
737 else if (y1 < pymin)
738 {
739 y1 = pymin;
740 x1 = (e->c - y1) / e->a;
741 }
742 if (y2 > pymax)
743 {
744 y2 = pymax;
745 x2 = (e->c - y2) / e->a;
746 }
747 else if (y2 < pymin)
748 {
749 y2 = pymin;
750 x2 = (e->c - y2) / e->a;
751 }
752 }
753
754 e->pos[0].x = x1;
755 e->pos[0].y = y1;
756 e->pos[1].x = x2;
757 e->pos[1].y = y2;
758
759 // If the two points are equal, the result is invalid
760 return (x1 == x2 && y1 == y2) ? 0 : 1;
761}
762
763// The line equation: ax + by + c = 0
764// see jcv_edge_create
765static int jcv_edge_clipline(jcv_context_internal* internal, jcv_edge* e)
766{
767 return internal->clipper.clip_fn(&internal->clipper, e);
768}
769
770static jcv_edge* jcv_edge_new(jcv_context_internal* internal, jcv_site* s1, jcv_site* s2)
771{
772 jcv_edge* e = jcv_alloc_edge(internal);
773 jcv_edge_create(e, s1, s2);
774 return e;
775}
776
777
778// jcv_halfedge
779
780static void jcv_halfedge_link(jcv_halfedge* edge, jcv_halfedge* newedge)
781{
782 newedge->left = edge;
783 newedge->right = edge->right;
784 edge->right->left = newedge;
785 edge->right = newedge;
786}
787
788static inline void jcv_halfedge_unlink(jcv_halfedge* he)
789{
790 he->left->right = he->right;
791 he->right->left = he->left;
792 he->left = 0;
793 he->right = 0;
794}
795
796static inline jcv_halfedge* jcv_halfedge_new(jcv_context_internal* internal, jcv_edge* e, int direction)
797{
798 jcv_halfedge* he = jcv_alloc_halfedge(internal);
799 he->edge = e;
800 he->left = 0;
801 he->right = 0;
802 he->direction = direction;
803 he->pqpos = 0;
804 // These are set outside
805 // he->y
806 // he->vertex
807 return he;
808}
809
810static void jcv_halfedge_delete(jcv_context_internal* internal, jcv_halfedge* he)
811{
812 he->right = internal->halfedgepool;
813 internal->halfedgepool = he;
814}
815
816static inline jcv_site* jcv_halfedge_leftsite(const jcv_halfedge* he)
817{
818 return he->edge->sites[he->direction];
819}
820
821static inline jcv_site* jcv_halfedge_rightsite(const jcv_halfedge* he)
822{
823 return he->edge ? he->edge->sites[1 - he->direction] : 0;
824}
825
826static int jcv_halfedge_rightof(const jcv_halfedge* he, const jcv_point* p)
827{
828 const jcv_edge* e = he->edge;
829 const jcv_site* topsite = e->sites[1];
830
831 int right_of_site = (p->x > topsite->p.x) ? 1 : 0;
832 if (right_of_site && he->direction == JCV_DIRECTION_LEFT)
833 return 1;
834 if (!right_of_site && he->direction == JCV_DIRECTION_RIGHT)
835 return 0;
836
837 jcv_real dxp, dyp, dxs, t1, t2, t3, yl;
838
839 int above;
840 if (e->a == (jcv_real)1)
841 {
842 dyp = p->y - topsite->p.y;
843 dxp = p->x - topsite->p.x;
844 int fast = 0;
845 if ((!right_of_site & (e->b < (jcv_real)0)) | (right_of_site & (e->b >= (jcv_real)0)))
846 {
847 above = dyp >= e->b * dxp;
848 fast = above;
849 }
850 else
851 {
852 above = (p->x + p->y * e->b) > e->c;
853 if (e->b < (jcv_real)0)
854 above = !above;
855 if (!above)
856 fast = 1;
857 }
858 if (!fast)
859 {
860 dxs = topsite->p.x - e->sites[0]->p.x;
861 above = e->b * (dxp * dxp - dyp * dyp) < dxs * dyp * ((jcv_real)1 + (jcv_real)2 * dxp / dxs + e->b * e->b);
862 if (e->b < (jcv_real)0)
863 above = !above;
864 }
865 }
866 else // e->b == 1
867 {
868 yl = e->c - e->a * p->x;
869 t1 = p->y - yl;
870 t2 = p->x - topsite->p.x;
871 t3 = yl - topsite->p.y;
872 above = t1 * t1 > (t2 * t2 + t3 * t3);
873 }
874 return (he->direction == JCV_DIRECTION_LEFT ? above : !above);
875}
876
877// Keeps the priority queue sorted with events sorted in ascending order
878// Return 1 if the edges needs to be swapped
879static inline int jcv_halfedge_compare(const jcv_halfedge* he1, const jcv_halfedge* he2)
880{
881 return (he1->y == he2->y) ? he1->vertex.x > he2->vertex.x : he1->y > he2->y;
882}
883
884static int jcv_halfedge_intersect(const jcv_halfedge* he1, const jcv_halfedge* he2, jcv_point* out)
885{
886 const jcv_edge* e1 = he1->edge;
887 const jcv_edge* e2 = he2->edge;
888
889 jcv_real d = e1->a * e2->b - e1->b * e2->a;
890 if (((jcv_real)-JCV_EDGE_INTERSECT_THRESHOLD < d && d < (jcv_real)JCV_EDGE_INTERSECT_THRESHOLD))
891 {
892 return 0;
893 }
894 out->x = (e1->c * e2->b - e1->b * e2->c) / d;
895 out->y = (e1->a * e2->c - e1->c * e2->a) / d;
896
897 const jcv_edge* e;
898 const jcv_halfedge* he;
899 if (jcv_point_less(&e1->sites[1]->p, &e2->sites[1]->p))
900 {
901 he = he1;
902 e = e1;
903 }
904 else
905 {
906 he = he2;
907 e = e2;
908 }
909
910 int right_of_site = out->x >= e->sites[1]->p.x;
911 if ((right_of_site && he->direction == JCV_DIRECTION_LEFT) || (!right_of_site && he->direction == JCV_DIRECTION_RIGHT))
912 {
913 return 0;
914 }
915
916 return 1;
917}
918
919
920// Priority queue
921
922static int jcv_pq_moveup(jcv_priorityqueue* pq, int pos)
923{
924 jcv_halfedge** items = (jcv_halfedge**)pq->items;
925 jcv_halfedge* node = items[pos];
926
927 for (int parent = (pos >> 1);
928 pos > 1 && jcv_halfedge_compare(items[parent], node);
929 pos = parent, parent = parent >> 1)
930 {
931 items[pos] = items[parent];
932 items[pos]->pqpos = pos;
933 }
934
935 node->pqpos = pos;
936 items[pos] = node;
937 return pos;
938}
939
940static int jcv_pq_maxchild(jcv_priorityqueue* pq, int pos)
941{
942 int child = pos << 1;
943 if (child >= pq->numitems)
944 return 0;
945 jcv_halfedge** items = (jcv_halfedge**)pq->items;
946 if ((child + 1) < pq->numitems && jcv_halfedge_compare(items[child], items[child + 1]))
947 return child + 1;
948 return child;
949}
950
951static int jcv_pq_movedown(jcv_priorityqueue* pq, int pos)
952{
953 jcv_halfedge** items = (jcv_halfedge**)pq->items;
954 jcv_halfedge* node = items[pos];
955
956 int child = jcv_pq_maxchild(pq, pos);
957 while (child && jcv_halfedge_compare(node, items[child]))
958 {
959 items[pos] = items[child];
960 items[pos]->pqpos = pos;
961 pos = child;
962 child = jcv_pq_maxchild(pq, pos);
963 }
964
965 items[pos] = node;
966 items[pos]->pqpos = pos;
967 return pos;
968}
969
970static void jcv_pq_create(jcv_priorityqueue* pq, int capacity, void** buffer)
971{
972 pq->maxnumitems = capacity;
973 pq->numitems = 1;
974 pq->items = buffer;
975}
976
977static int jcv_pq_empty(jcv_priorityqueue* pq)
978{
979 return pq->numitems == 1 ? 1 : 0;
980}
981
982static int jcv_pq_push(jcv_priorityqueue* pq, void* node)
983{
984 assert(pq->numitems < pq->maxnumitems);
985 int n = pq->numitems++;
986 pq->items[n] = node;
987 return jcv_pq_moveup(pq, n);
988}
989
990static void* jcv_pq_pop(jcv_priorityqueue* pq)
991{
992 void* node = pq->items[1];
993 pq->items[1] = pq->items[--pq->numitems];
994 jcv_pq_movedown(pq, 1);
995 return node;
996}
997
998static void* jcv_pq_top(jcv_priorityqueue* pq)
999{
1000 return pq->items[1];
1001}
1002
1003static void jcv_pq_remove(jcv_priorityqueue* pq, jcv_halfedge* node)
1004{
1005 if (pq->numitems == 1)
1006 return;
1007 int pos = node->pqpos;
1008 if (pos == 0)
1009 return;
1010
1011 jcv_halfedge** items = (jcv_halfedge**)pq->items;
1012
1013 items[pos] = items[--pq->numitems];
1014 if (jcv_halfedge_compare(node, items[pos]))
1015 jcv_pq_moveup(pq, pos);
1016 else
1017 jcv_pq_movedown(pq, pos);
1018 node->pqpos = pos;
1019}
1020
1021// internal functions
1022
1023static inline jcv_site* jcv_nextsite(jcv_context_internal* internal)
1024{
1025 return (internal->currentsite < internal->numsites) ? &internal->sites[internal->currentsite++] : 0;
1026}
1027
1028static jcv_halfedge* jcv_get_edge_above_x(jcv_context_internal* internal, const jcv_point* p)
1029{
1030 // Gets the arc on the beach line at the x coordinate (i.e. right above the new site event)
1031
1032 // A good guess it's close by (Can be optimized)
1033 jcv_halfedge* he = internal->last_inserted;
1034 if (!he)
1035 {
1036 if (p->x < (internal->rect.max.x - internal->rect.min.x) / 2)
1037 he = internal->beachline_start;
1038 else
1039 he = internal->beachline_end;
1040 }
1041
1042 //
1043 if (he == internal->beachline_start || (he != internal->beachline_end && jcv_halfedge_rightof(he, p)))
1044 {
1045 do
1046 {
1047 he = he->right;
1048 } while (he != internal->beachline_end && jcv_halfedge_rightof(he, p));
1049
1050 he = he->left;
1051 }
1052 else
1053 {
1054 do
1055 {
1056 he = he->left;
1057 } while (he != internal->beachline_start && !jcv_halfedge_rightof(he, p));
1058 }
1059
1060 return he;
1061}
1062
1063static int jcv_check_circle_event(const jcv_halfedge* he1, const jcv_halfedge* he2, jcv_point* vertex)
1064{
1065 jcv_edge* e1 = he1->edge;
1066 jcv_edge* e2 = he2->edge;
1067 if (e1 == 0 || e2 == 0 || e1->sites[1] == e2->sites[1])
1068 {
1069 return 0;
1070 }
1071
1072 return jcv_halfedge_intersect(he1, he2, vertex);
1073}
1074
1075static void jcv_site_event(jcv_context_internal* internal, jcv_site* site)
1076{
1077 jcv_halfedge* left = jcv_get_edge_above_x(internal, &site->p);
1078 jcv_halfedge* right = left->right;
1079 jcv_site* bottom = jcv_halfedge_rightsite(left);
1080 if (!bottom)
1081 bottom = internal->bottomsite;
1082
1083 jcv_edge* edge = jcv_edge_new(internal, bottom, site);
1084 edge->next = internal->edges;
1085 internal->edges = edge;
1086
1087 jcv_halfedge* edge1 = jcv_halfedge_new(internal, edge, JCV_DIRECTION_LEFT);
1088 jcv_halfedge* edge2 = jcv_halfedge_new(internal, edge, JCV_DIRECTION_RIGHT);
1089
1090 jcv_halfedge_link(left, edge1);
1091 jcv_halfedge_link(edge1, edge2);
1092
1093 internal->last_inserted = right;
1094
1095 jcv_point p;
1096 if (jcv_check_circle_event(left, edge1, &p))
1097 {
1098 jcv_pq_remove(internal->eventqueue, left);
1099 left->vertex = p;
1100 left->y = p.y + jcv_point_dist(&site->p, &p);
1101 jcv_pq_push(internal->eventqueue, left);
1102 }
1103 if (jcv_check_circle_event(edge2, right, &p))
1104 {
1105 edge2->vertex = p;
1106 edge2->y = p.y + jcv_point_dist(&site->p, &p);
1107 jcv_pq_push(internal->eventqueue, edge2);
1108 }
1109}
1110
1111// https://cp-algorithms.com/geometry/oriented-triangle-area.html
1112static inline jcv_real jcv_determinant(const jcv_point* a, const jcv_point* b, const jcv_point* c)
1113{
1114 return (b->x - a->x) * (c->y - a->y) - (b->y - a->y) * (c->x - a->x);
1115}
1116
1117static inline jcv_real jcv_calc_sort_metric(const jcv_site* site, const jcv_graphedge* edge)
1118{
1119 // We take the average of the two points, since we can better distinguish between very small edges
1120 jcv_real half = 1 / (jcv_real)2;
1121 jcv_real x = (edge->pos[0].x + edge->pos[1].x) * half;
1122 jcv_real y = (edge->pos[0].y + edge->pos[1].y) * half;
1123 jcv_real diffy = y - site->p.y;
1124 jcv_real angle = JCV_ATAN2(diffy, x - site->p.x);
1125 if (diffy < 0)
1126 angle = angle + 2 * JCV_PI;
1127 return (jcv_real)angle;
1128}
1129
1130static inline int jcv_graphedge_eq(jcv_graphedge* a, jcv_graphedge* b)
1131{
1132 return jcv_real_eq(a->angle, b->angle) && jcv_point_eq(&a->pos[0], &b->pos[0]) && jcv_point_eq(&a->pos[1], &b->pos[1]);
1133}
1134
1135static void jcv_sortedges_insert(jcv_site* site, jcv_graphedge* edge)
1136{
1137 // Special case for the head end
1138 jcv_graphedge* prev = 0;
1139 if (site->edges == 0 || site->edges->angle >= edge->angle)
1140 {
1141 edge->next = site->edges;
1142 site->edges = edge;
1143 }
1144 else
1145 {
1146 // Locate the node before the point of insertion
1147 jcv_graphedge* current = site->edges;
1148 while (current->next != 0 && current->next->angle < edge->angle)
1149 {
1150 current = current->next;
1151 }
1152 prev = current;
1153 edge->next = current->next;
1154 current->next = edge;
1155 }
1156
1157 // check to avoid duplicates
1158 if (prev && jcv_graphedge_eq(prev, edge))
1159 {
1160 prev->next = edge->next;
1161 }
1162 else if (edge->next && jcv_graphedge_eq(edge, edge->next))
1163 {
1164 edge->next = edge->next->next;
1165 }
1166}
1167
1168static void jcv_finishline(jcv_context_internal* internal, jcv_edge* e)
1169{
1170 if (!jcv_edge_clipline(internal, e))
1171 {
1172 return;
1173 }
1174
1175 // Make sure the graph edges are CCW
1176 int flip = jcv_determinant(&e->sites[0]->p, &e->pos[0], &e->pos[1]) > (jcv_real)0 ? 0 : 1;
1177
1178 for (int i = 0; i < 2; ++i)
1179 {
1180 jcv_graphedge* ge = jcv_alloc_graphedge(internal);
1181
1182 ge->edge = e;
1183 ge->next = 0;
1184 ge->neighbor = e->sites[1 - i];
1185 ge->pos[flip] = e->pos[i];
1186 ge->pos[1 - flip] = e->pos[1 - i];
1187 ge->angle = jcv_calc_sort_metric(e->sites[i], ge);
1188
1189 jcv_sortedges_insert(e->sites[i], ge);
1190 }
1191}
1192
1193
1194static void jcv_endpos(jcv_context_internal* internal, jcv_edge* e, const jcv_point* p, int direction)
1195{
1196 e->pos[direction] = *p;
1197
1198 if (!jcv_is_valid(&e->pos[1 - direction]))
1199 return;
1200
1201 jcv_finishline(internal, e);
1202}
1203
1204static inline void jcv_create_corner_edge(jcv_context_internal* internal, const jcv_site* site, jcv_graphedge* current, jcv_graphedge* gap)
1205{
1206 gap->neighbor = 0;
1207 gap->pos[0] = current->pos[1];
1208
1209 if (current->pos[1].x < internal->rect.max.x && current->pos[1].y == internal->rect.min.y)
1210 {
1211 gap->pos[1].x = internal->rect.max.x;
1212 gap->pos[1].y = internal->rect.min.y;
1213 }
1214 else if (current->pos[1].x > internal->rect.min.x && current->pos[1].y == internal->rect.max.y)
1215 {
1216 gap->pos[1].x = internal->rect.min.x;
1217 gap->pos[1].y = internal->rect.max.y;
1218 }
1219 else if (current->pos[1].y > internal->rect.min.y && current->pos[1].x == internal->rect.min.x)
1220 {
1221 gap->pos[1].x = internal->rect.min.x;
1222 gap->pos[1].y = internal->rect.min.y;
1223 }
1224 else if (current->pos[1].y < internal->rect.max.y && current->pos[1].x == internal->rect.max.x)
1225 {
1226 gap->pos[1].x = internal->rect.max.x;
1227 gap->pos[1].y = internal->rect.max.y;
1228 }
1229
1230 gap->angle = jcv_calc_sort_metric(site, gap);
1231}
1232
1233static jcv_edge* jcv_create_gap_edge(jcv_context_internal* internal, jcv_site* site, jcv_graphedge* ge)
1234{
1235 jcv_edge* edge = jcv_alloc_edge(internal);
1236 edge->pos[0] = ge->pos[0];
1237 edge->pos[1] = ge->pos[1];
1238 edge->sites[0] = site;
1239 edge->sites[1] = 0;
1240 edge->a = edge->b = edge->c = 0;
1241 edge->next = internal->edges;
1242 internal->edges = edge;
1243 return edge;
1244}
1245
1246void jcv_boxshape_fillgaps(const jcv_clipper* clipper, jcv_context_internal* allocator, jcv_site* site)
1247{
1248 // They're sorted CCW, so if the current->pos[1] != next->pos[0], then we have a gap
1249 jcv_graphedge* current = site->edges;
1250 if (!current)
1251 {
1252 // No edges, then it should be a single cell
1253 assert(allocator->numsites == 1);
1254
1255 jcv_graphedge* gap = jcv_alloc_graphedge(allocator);
1256 gap->neighbor = 0;
1257 gap->pos[0] = clipper->min;
1258 gap->pos[1].x = clipper->max.x;
1259 gap->pos[1].y = clipper->min.y;
1260 gap->angle = jcv_calc_sort_metric(site, gap);
1261 gap->next = 0;
1262 gap->edge = jcv_create_gap_edge(allocator, site, gap);
1263
1264 current = gap;
1265 site->edges = gap;
1266 }
1267
1268 jcv_graphedge* next = current->next;
1269 if (!next)
1270 {
1271 jcv_graphedge* gap = jcv_alloc_graphedge(allocator);
1272 jcv_create_corner_edge(allocator, site, current, gap);
1273 gap->edge = jcv_create_gap_edge(allocator, site, gap);
1274
1275 gap->next = current->next;
1276 current->next = gap;
1277 current = gap;
1278 next = site->edges;
1279 }
1280
1281 while (current && next)
1282 {
1283 int current_edge_flags = jcv_get_edge_flags(&current->pos[1], &clipper->min, &clipper->max);
1284 if (current_edge_flags && !jcv_point_eq(&current->pos[1], &next->pos[0]))
1285 {
1286 // Cases:
1287 // Current and Next on the same border
1288 // Current on one border, and Next on another border
1289 // Current on the corner, Next on the border
1290 // Current on the corner, Next on another border (another corner in between)
1291
1292 int next_edge_flags = jcv_get_edge_flags(&next->pos[0], &clipper->min, &clipper->max);
1293 if (current_edge_flags & next_edge_flags)
1294 {
1295 // Current and Next on the same border
1296 jcv_graphedge* gap = jcv_alloc_graphedge(allocator);
1297 gap->neighbor = 0;
1298 gap->pos[0] = current->pos[1];
1299 gap->pos[1] = next->pos[0];
1300 gap->angle = jcv_calc_sort_metric(site, gap);
1301 gap->edge = jcv_create_gap_edge(allocator, site, gap);
1302
1303 gap->next = current->next;
1304 current->next = gap;
1305 }
1306 else
1307 {
1308 // Current and Next on different borders
1309 int corner_flag = jcv_edge_flags_to_corner(current_edge_flags);
1310 if (corner_flag)
1311 {
1312 // we are already at one corner, so we need to find the next one
1313 corner_flag = jcv_corner_rotate_90(corner_flag);
1314 }
1315 else
1316 {
1317 // we are on the middle of a border
1318 // we need to find the adjacent corner, following the borders CCW
1319 if (current_edge_flags == JCV_EDGE_TOP)
1320 {
1321 corner_flag = JCV_CORNER_TOP_LEFT;
1322 }
1323 else if (current_edge_flags == JCV_EDGE_LEFT)
1324 {
1325 corner_flag = JCV_CORNER_BOTTOM_LEFT;
1326 }
1327 else if (current_edge_flags == JCV_EDGE_BOTTOM)
1328 {
1329 corner_flag = JCV_CORNER_BOTTOM_RIGHT;
1330 }
1331 else if (current_edge_flags == JCV_EDGE_RIGHT)
1332 {
1333 corner_flag = JCV_CORNER_TOP_RIGHT;
1334 }
1335 }
1336 jcv_point corner = jcv_corner_to_point(corner_flag, &clipper->min, &clipper->max);
1337
1338 jcv_graphedge* gap = jcv_alloc_graphedge(allocator);
1339 gap->neighbor = 0;
1340 gap->pos[0] = current->pos[1];
1341 gap->pos[1] = corner;
1342 gap->angle = jcv_calc_sort_metric(site, gap);
1343 gap->edge = jcv_create_gap_edge(allocator, site, gap);
1344
1345 gap->next = current->next;
1346 current->next = gap;
1347 }
1348 }
1349
1350 current = current->next;
1351 if (current)
1352 {
1353 next = current->next;
1354 if (!next)
1355 next = site->edges;
1356 }
1357 }
1358}
1359
1360
1361// Since the algorithm leaves gaps at the borders/corner, we want to fill them
1362static void jcv_fillgaps(jcv_diagram* diagram)
1363{
1364 jcv_context_internal* internal = diagram->internal;
1365 if (!internal->clipper.fill_fn)
1366 return;
1367
1368 for (int i = 0; i < internal->numsites; ++i)
1369 {
1370 jcv_site* site = &internal->sites[i];
1371 internal->clipper.fill_fn(&internal->clipper, internal, site);
1372 }
1373}
1374
1375
1376static void jcv_circle_event(jcv_context_internal* internal)
1377{
1378 jcv_halfedge* left = (jcv_halfedge*)jcv_pq_pop(internal->eventqueue);
1379
1380 jcv_halfedge* leftleft = left->left;
1381 jcv_halfedge* right = left->right;
1382 jcv_halfedge* rightright = right->right;
1383 jcv_site* bottom = jcv_halfedge_leftsite(left);
1384 jcv_site* top = jcv_halfedge_rightsite(right);
1385
1386 jcv_point vertex = left->vertex;
1387 jcv_endpos(internal, left->edge, &vertex, left->direction);
1388 jcv_endpos(internal, right->edge, &vertex, right->direction);
1389
1390 internal->last_inserted = rightright;
1391
1392 jcv_pq_remove(internal->eventqueue, right);
1393 jcv_halfedge_unlink(left);
1394 jcv_halfedge_unlink(right);
1395 jcv_halfedge_delete(internal, left);
1396 jcv_halfedge_delete(internal, right);
1397
1398 int direction = JCV_DIRECTION_LEFT;
1399 if (bottom->p.y > top->p.y)
1400 {
1401 jcv_site* temp = bottom;
1402 bottom = top;
1403 top = temp;
1404 direction = JCV_DIRECTION_RIGHT;
1405 }
1406
1407 jcv_edge* edge = jcv_edge_new(internal, bottom, top);
1408 edge->next = internal->edges;
1409 internal->edges = edge;
1410
1411 jcv_halfedge* he = jcv_halfedge_new(internal, edge, direction);
1412 jcv_halfedge_link(leftleft, he);
1413 jcv_endpos(internal, edge, &vertex, JCV_DIRECTION_RIGHT - direction);
1414
1415 jcv_point p;
1416 if (jcv_check_circle_event(leftleft, he, &p))
1417 {
1418 jcv_pq_remove(internal->eventqueue, leftleft);
1419 leftleft->vertex = p;
1420 leftleft->y = p.y + jcv_point_dist(&bottom->p, &p);
1421 jcv_pq_push(internal->eventqueue, leftleft);
1422 }
1423 if (jcv_check_circle_event(he, rightright, &p))
1424 {
1425 he->vertex = p;
1426 he->y = p.y + jcv_point_dist(&bottom->p, &p);
1427 jcv_pq_push(internal->eventqueue, he);
1428 }
1429}
1430
1431void jcv_diagram_generate(int num_points, const jcv_point* points, const jcv_rect* rect, const jcv_clipper* clipper, jcv_diagram* d)
1432{
1433 jcv_diagram_generate_useralloc(num_points, points, rect, clipper, 0, jcv_alloc_fn, jcv_free_fn, d);
1434}
1435
1436typedef union jcv_cast_align_struct_
1437{
1438 char* charp;
1439 void** voidpp;
1440} jcv_cast_align_struct;
1441
1442static inline void jcv_rect_union(jcv_rect* rect, const jcv_point* p)
1443{
1444 rect->min.x = jcv_min(rect->min.x, p->x);
1445 rect->min.y = jcv_min(rect->min.y, p->y);
1446 rect->max.x = jcv_max(rect->max.x, p->x);
1447 rect->max.y = jcv_max(rect->max.y, p->y);
1448}
1449
1450static inline void jcv_rect_round(jcv_rect* rect)
1451{
1452 rect->min.x = jcv_floor(rect->min.x);
1453 rect->min.y = jcv_floor(rect->min.y);
1454 rect->max.x = jcv_ceil(rect->max.x);
1455 rect->max.y = jcv_ceil(rect->max.y);
1456}
1457
1458static inline void jcv_rect_inflate(jcv_rect* rect, jcv_real amount)
1459{
1460 rect->min.x -= amount;
1461 rect->min.y -= amount;
1462 rect->max.x += amount;
1463 rect->max.y += amount;
1464}
1465
1466static int jcv_prune_duplicates(jcv_context_internal* internal, jcv_rect* rect)
1467{
1468 int num_sites = internal->numsites;
1469 jcv_site* sites = internal->sites;
1470
1471 jcv_rect r;
1472 r.min.x = r.min.y = JCV_FLT_MAX;
1473 r.max.x = r.max.y = -JCV_FLT_MAX;
1474
1475 int offset = 0;
1476 // Prune duplicates first
1477 for (int i = 0; i < num_sites; i++)
1478 {
1479 const jcv_site* s = &sites[i];
1480 // Remove duplicates, to avoid anomalies
1481 if (i > 0 && jcv_point_eq(&s->p, &sites[i - 1].p))
1482 {
1483 offset++;
1484 continue;
1485 }
1486
1487 sites[i - offset] = sites[i];
1488
1489 jcv_rect_union(&r, &s->p);
1490 }
1491 internal->numsites -= offset;
1492 if (rect)
1493 {
1494 *rect = r;
1495 }
1496 return offset;
1497}
1498
1499static int jcv_prune_not_in_shape(jcv_context_internal* internal, jcv_rect* rect)
1500{
1501 int num_sites = internal->numsites;
1502 jcv_site* sites = internal->sites;
1503
1504 jcv_rect r;
1505 r.min.x = r.min.y = JCV_FLT_MAX;
1506 r.max.x = r.max.y = -JCV_FLT_MAX;
1507
1508 int offset = 0;
1509 for (int i = 0; i < num_sites; i++)
1510 {
1511 const jcv_site* s = &sites[i];
1512
1513 if (!internal->clipper.test_fn(&internal->clipper, s->p))
1514 {
1515 offset++;
1516 continue;
1517 }
1518
1519 sites[i - offset] = sites[i];
1520
1521 jcv_rect_union(&r, &s->p);
1522 }
1523 internal->numsites -= offset;
1524 if (rect)
1525 {
1526 *rect = r;
1527 }
1528 return offset;
1529}
1530
1531static jcv_context_internal* jcv_alloc_internal(int num_points, void* userallocctx, FJCVAllocFn allocfn, FJCVFreeFn freefn)
1532{
1533 // Interesting limits from Euler's equation
1534 // Slide 81: https://courses.cs.washington.edu/courses/csep521/01au/lectures/lecture10slides.pdf
1535 // Page 3: https://sites.cs.ucsb.edu/~suri/cs235/Voronoi.pdf
1536 size_t eventssize = (size_t)(num_points * 2) * sizeof(void*); // beachline can have max 2*n-5 parabolas
1537 size_t sitessize = (size_t)num_points * sizeof(jcv_site);
1538 size_t memsize = sizeof(jcv_priorityqueue) + eventssize + sitessize + sizeof(jcv_context_internal) + 16u; // 16 bytes padding for alignment
1539
1540 char* originalmem = (char*)allocfn(userallocctx, memsize);
1541 memset(originalmem, 0, memsize);
1542
1543 // align memory
1544 char* mem = (char*)jcv_align(originalmem, sizeof(void*));
1545
1546 jcv_context_internal* internal = (jcv_context_internal*)mem;
1547 mem += sizeof(jcv_context_internal);
1548 internal->mem = originalmem;
1549 internal->memctx = userallocctx;
1550 internal->alloc = allocfn;
1551 internal->free = freefn;
1552
1553 mem = (char*)jcv_align(mem, sizeof(void*));
1554 internal->sites = (jcv_site*)mem;
1555 mem += sitessize;
1556
1557 mem = (char*)jcv_align(mem, sizeof(void*));
1558 internal->eventqueue = (jcv_priorityqueue*)mem;
1559 mem += sizeof(jcv_priorityqueue);
1560 assert(((uintptr_t)mem & (sizeof(void*) - 1)) == 0);
1561
1562 jcv_cast_align_struct tmp;
1563 tmp.charp = mem;
1564 internal->eventmem = tmp.voidpp;
1565
1566 assert((mem + eventssize) <= (originalmem + memsize));
1567
1568 return internal;
1569}
1570
1571void jcv_diagram_generate_useralloc(int num_points, const jcv_point* points, const jcv_rect* rect, const jcv_clipper* clipper, void* userallocctx, FJCVAllocFn allocfn, FJCVFreeFn freefn, jcv_diagram* d)
1572{
1573 if (d->internal)
1574 jcv_diagram_free(d);
1575
1576 jcv_context_internal* internal = jcv_alloc_internal(num_points, userallocctx, allocfn, freefn);
1577
1578 internal->beachline_start = jcv_halfedge_new(internal, 0, 0);
1579 internal->beachline_end = jcv_halfedge_new(internal, 0, 0);
1580
1581 internal->beachline_start->left = 0;
1582 internal->beachline_start->right = internal->beachline_end;
1583 internal->beachline_end->left = internal->beachline_start;
1584 internal->beachline_end->right = 0;
1585
1586 internal->last_inserted = 0;
1587
1588 int max_num_events = num_points * 2; // beachline can have max 2*n-5 parabolas
1589 jcv_pq_create(internal->eventqueue, max_num_events, (void**)internal->eventmem);
1590
1591 internal->numsites = num_points;
1592 jcv_site* sites = internal->sites;
1593
1594 for (int i = 0; i < num_points; ++i)
1595 {
1596 sites[i].p = points[i];
1597 sites[i].edges = 0;
1598 sites[i].index = i;
1599 }
1600
1601 qsort(sites, (size_t)num_points, sizeof(jcv_site), jcv_point_cmp);
1602
1603 jcv_clipper box_clipper;
1604 if (clipper == 0)
1605 {
1606 box_clipper.test_fn = jcv_boxshape_test;
1607 box_clipper.clip_fn = jcv_boxshape_clip;
1608 box_clipper.fill_fn = jcv_boxshape_fillgaps;
1609 clipper = &box_clipper;
1610 }
1611 internal->clipper = *clipper;
1612
1613 jcv_rect tmp_rect;
1614 tmp_rect.min.x = tmp_rect.min.y = JCV_FLT_MAX;
1615 tmp_rect.max.x = tmp_rect.max.y = -JCV_FLT_MAX;
1616 jcv_prune_duplicates(internal, &tmp_rect);
1617
1618 // Prune using the test second
1619 if (internal->clipper.test_fn)
1620 {
1621 // e.g. used by the box clipper in the test_fn
1622 internal->clipper.min = rect ? rect->min : tmp_rect.min;
1623 internal->clipper.max = rect ? rect->max : tmp_rect.max;
1624
1625 jcv_prune_not_in_shape(internal, &tmp_rect);
1626
1627 // The pruning might have made the bounding box smaller
1628 if (!rect)
1629 {
1630 // In the case of all sites being all on a horizontal or vertical line, the
1631 // rect area will be zero, and the diagram generation will most likely fail
1632 jcv_rect_round(&tmp_rect);
1633 jcv_rect_inflate(&tmp_rect, 10);
1634
1635 internal->clipper.min = tmp_rect.min;
1636 internal->clipper.max = tmp_rect.max;
1637 }
1638 }
1639
1640 internal->rect = rect ? *rect : tmp_rect;
1641
1642 d->min = internal->rect.min;
1643 d->max = internal->rect.max;
1644 d->numsites = internal->numsites;
1645 d->internal = internal;
1646
1647 internal->bottomsite = jcv_nextsite(internal);
1648
1649 jcv_priorityqueue* pq = internal->eventqueue;
1650 jcv_site* site = jcv_nextsite(internal);
1651
1652 int finished = 0;
1653 while (!finished)
1654 {
1655 jcv_point lowest_pq_point;
1656 if (!jcv_pq_empty(pq))
1657 {
1658 jcv_halfedge* he = (jcv_halfedge*)jcv_pq_top(pq);
1659 lowest_pq_point.x = he->vertex.x;
1660 lowest_pq_point.y = he->y;
1661 }
1662
1663 if (site != 0 && (jcv_pq_empty(pq) || jcv_point_less(&site->p, &lowest_pq_point)))
1664 {
1665 jcv_site_event(internal, site);
1666 site = jcv_nextsite(internal);
1667 }
1668 else if (!jcv_pq_empty(pq))
1669 {
1670 jcv_circle_event(internal);
1671 }
1672 else
1673 {
1674 finished = 1;
1675 }
1676 }
1677
1678 for (jcv_halfedge* he = internal->beachline_start->right; he != internal->beachline_end; he = he->right)
1679 {
1680 jcv_finishline(internal, he->edge);
1681 }
1682
1683 jcv_fillgaps(d);
1684}
1685
1686#endif // JC_VORONOI_IMPLEMENTATION
1687
1688/*
1689
1690ABOUT:
1691
1692 A fast single file 2D voronoi diagram generator
1693
1694HISTORY:
1695 0.9 2023-01-22 - Modified the Delauney iterator creation api
1696 0.8 2022-12-20 - Added fix for missing border edges
1697 More robust removal of duplicate graph edges
1698 Added iterator for Delauney edges
1699 0.7 2019-10-25 - Added support for clipping against convex polygons
1700 - Added JCV_EDGE_INTERSECT_THRESHOLD for edge intersections
1701 - Fixed issue where the bounds calculation wasn’t considering all points
1702 0.6 2018-10-21 - Removed JCV_CEIL/JCV_FLOOR/JCV_FABS
1703 - Optimizations: Fewer indirections, better beach head approximation
1704 0.5 2018-10-14 - Fixed issue where the graph edge had the wrong edge assigned (issue #28)
1705 - Fixed issue where a point was falsely passing the jcv_is_valid() test (issue #22)
1706 - Fixed jcv_diagram_get_edges() so it now returns _all_ edges (issue #28)
1707 - Added jcv_diagram_get_next_edge() to skip zero length edges (issue #10)
1708 - Added defines JCV_CEIL/JCV_FLOOR/JCV_FLT_MAX for easier configuration
1709 0.4 2017-06-03 - Increased the max number of events that are preallocated
1710 0.3 2017-04-16 - Added clipping box as input argument (Automatically calculated if needed)
1711 - Input points are pruned based on bounding box
1712 0.2 2016-12-30 - Fixed issue of edges not being closed properly
1713 - Fixed issue when having many events
1714 - Fixed edge sorting
1715 - Code cleanup
1716 0.1 Initial version
1717
1718LICENSE:
1719
1720 The MIT License (MIT)
1721
1722 Copyright (c) 2015-2019 Mathias Westerdahl
1723
1724 Permission is hereby granted, free of charge, to any person obtaining a copy
1725 of this software and associated documentation files (the "Software"), to deal
1726 in the Software without restriction, including without limitation the rights
1727 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1728 copies of the Software, and to permit persons to whom the Software is
1729 furnished to do so, subject to the following conditions:
1730
1731 The above copyright notice and this permission notice shall be included in all
1732 copies or substantial portions of the Software.
1733
1734 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1735 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1736 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1737 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1738 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1739 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1740 SOFTWARE.
1741
1742
1743DISCLAIMER:
1744
1745 This software is supplied "AS IS" without any warranties and support
1746
1747USAGE:
1748
1749 The input points are pruned if
1750
1751 * There are duplicates points
1752 * The input points are outside of the bounding box (i.e. fail the clipping test function)
1753 * The input points are rejected by the clipper's test function
1754
1755 The input bounding box is optional (calculated automatically)
1756
1757 The input domain is (-FLT_MAX, FLT_MAX] (for floats)
1758
1759 The api consists of these functions:
1760
1761 void jcv_diagram_generate( int num_points, const jcv_point* points, const jcv_rect* rect, const jcv_clipper* clipper, jcv_diagram* diagram );
1762 void jcv_diagram_generate_useralloc( int num_points, const jcv_point* points, const jcv_rect* rect, const jcv_clipper* clipper, const jcv_clipper* clipper, void* userallocctx, FJCVAllocFn allocfn, FJCVFreeFn freefn, jcv_diagram* diagram );
1763 void jcv_diagram_free( jcv_diagram* diagram );
1764
1765 const jcv_site* jcv_diagram_get_sites( const jcv_diagram* diagram );
1766 const jcv_edge* jcv_diagram_get_edges( const jcv_diagram* diagram );
1767 const jcv_edge* jcv_diagram_get_next_edge( const jcv_edge* edge );
1768
1769 An example usage:
1770
1771 #define JC_VORONOI_IMPLEMENTATION
1772 // If you wish to use doubles
1773 //#define JCV_REAL_TYPE double
1774 //#define JCV_ATAN2 atan2
1775 //#define JCV_FLT_MAX 1.7976931348623157E+308
1776 #include "jc_voronoi.h"
1777
1778 void draw_edges(const jcv_diagram* diagram);
1779 void draw_cells(const jcv_diagram* diagram);
1780
1781 void generate_and_draw(int numpoints, const jcv_point* points)
1782 {
1783 jcv_diagram diagram;
1784 memset(&diagram, 0, sizeof(jcv_diagram));
1785 jcv_diagram_generate(count, points, 0, 0, &diagram);
1786
1787 draw_edges(diagram);
1788 draw_cells(diagram);
1789
1790 jcv_diagram_free( &diagram );
1791 }
1792
1793 void draw_edges(const jcv_diagram* diagram)
1794 {
1795 // If all you need are the edges
1796 const jcv_edge* edge = jcv_diagram_get_edges( diagram );
1797 while( edge )
1798 {
1799 draw_line(edge->pos[0], edge->pos[1]);
1800 edge = jcv_diagram_get_next_edge(edge);
1801 }
1802 }
1803
1804 void draw_cells(const jcv_diagram* diagram)
1805 {
1806 // If you want to draw triangles, or relax the diagram,
1807 // you can iterate over the sites and get all edges easily
1808 const jcv_site* sites = jcv_diagram_get_sites( diagram );
1809 for( int i = 0; i < diagram->numsites; ++i )
1810 {
1811 const jcv_site* site = &sites[i];
1812
1813 const jcv_graphedge* e = site->edges;
1814 while( e )
1815 {
1816 draw_triangle( site->p, e->pos[0], e->pos[1]);
1817 e = e->next;
1818 }
1819 }
1820 }
1821
1822 // Here is a simple example of how to do the relaxations of the cells
1823 void relax_points(const jcv_diagram* diagram, jcv_point* points)
1824 {
1825 const jcv_site* sites = jcv_diagram_get_sites(diagram);
1826 for( int i = 0; i < diagram->numsites; ++i )
1827 {
1828 const jcv_site* site = &sites[i];
1829 jcv_point sum = site->p;
1830 int count = 1;
1831
1832 const jcv_graphedge* edge = site->edges;
1833
1834 while( edge )
1835 {
1836 sum.x += edge->pos[0].x;
1837 sum.y += edge->pos[0].y;
1838 ++count;
1839 edge = edge->next;
1840 }
1841
1842 points[site->index].x = sum.x / count;
1843 points[site->index].y = sum.y / count;
1844 }
1845 }
1846
1847 */
constexpr TYPE e()
Returns the natural constant e.
Definition jc_voronoi.h:176
Definition jc_voronoi.h:163
Definition jc_voronoi.h:157
Definition jc_voronoi.h:186
Definition jc_voronoi.h:147
Definition jc_voronoi.h:130
Definition jc_voronoi.h:124
Definition jc_voronoi.h:170
Definition jc_voronoi.h:139