From 6b55aa208635ea3ea73e132a1ec598ab762fbed4 Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 9 Jan 2026 12:13:48 +0000 Subject: [PATCH 01/53] Incorporates jc_voronoi_clip.h into jcv::manager --- mplot/jcvoronoi/jc_voronoi.h | 235 ++++++++++++++++++++++++++++++++++- 1 file changed, 234 insertions(+), 1 deletion(-) diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 8a6e27a6..01503b3e 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -183,6 +183,14 @@ namespace jcv point max; }; + // Used for boundary clipping + template + struct clipping_polygon + { + jcv::point* points; + int num_points; + }; + #pragma pack(pop) // The mananger class. Type T is what is called real in the original code @@ -1420,6 +1428,231 @@ namespace jcv int diagram_numsites() const { return this->diagram.numsites; } + /** + * Boundary clipping code (was in jc_voronoi_clip.h) + */ + + static point mix (point a, point b, T t) + { + point r; + r.x = a.x + (b.x - a.x) * t; + r.y = a.y + (b.y - a.y) * t; + return r; + } + + // if it returns [0.0, 1.0] it's on the line segment + static T point_to_line_segment_t (point p, point p0, point p1) + { + point vpoint = p - p0; + point vsegment = p1 - p0; + return vsegment.dot (vpoint) / vsegment.dot (vsegment); + } + + int clip_polygon_test_point (const clipper* clipper, const point p) + { + clipping_polygon* polygon = (clipping_polygon*)clipper->ctx; // reinterpret? why? + int num_points = polygon->num_points; + + // convex polygon + // winding CCW + // all polygon normals point outward + // if the point is in front of the plane, it is outside + + int result = 1; + for (int i = 0; i < num_points; ++i) { + point p0 = polygon->points[i]; + point p1 = polygon->points[(i + 1) % num_points]; + point n = {}; + n.x = p1.y() - p0.y(); + n.y = p0.x() - p1.x(); + point diff = p - p0; + + if (n.dot (diff) > 0) { + result = 0; + break; + } + } + return result; + } + + static int ray_intersect_polygon (const clipper* clipper, + point p0, point p1, + T* out_t0, T* out_t1) + { + clipping_polygon* polygon = (clipping_polygon*)clipper->ctx; + int num_points = polygon->num_points; + + T t0 = T{0}; + T t1 = T{1}; + point dir = p1 - p0; + + for (int i = 0; i < num_points; ++i) { + point v0 = polygon->points[i]; + point v1 = polygon->points[(i+1)%num_points]; + point n; + n.x = v1.y - v0.y; + n.y = -(v1.x - v0.x); + + point v0p0 = p0 - v0; + + T N = -v0p0.dot (n); + T D = dir.dot (n); + if (std::abs(D) < T{0.0001}) { // parallel to the line + if (N < T{0}) { return 0; } + continue; + } + + T t = N / D; + if (D < T{0}) { // -> entering + t0 = t > t0 ? t : t0; + if (t0 > t1) { return 0; } + } else { // D > 0 -> exiting + t1 = t < t1 ? t : t1; + if (t1 < t0) { return 0; } + } + } + + *out_t0 = t0; + *out_t1 = t1; + return 1; + } + + int clip_polygon_clip_edge (const clipper* clipper, edge* e) + { + // Using the box clipper to get a finite line segment + int result = manager::boxshape_clip(clipper, e); + if (!result) { return 0; } + + point p0 = e->pos[0]; + point p1 = e->pos[1]; + + T t0; + T t1; + result = ray_intersect_polygon (clipper, p0, p1, &t0, &t1); + + if (!result) { + e->pos[0] = e->pos[1]; + return 0; + } + + e->pos[0] = mix (p0, p1, t0); + e->pos[1] = mix (p0, p1, t1); + return 1; + } + + // Find the edge which the point sits on + static int find_polygon_edge (const clipper* clipper, point p) + { + clipping_polygon* polygon = (clipping_polygon*)clipper->ctx; + + int min_edge = -1; + T min_dist = std::numeric_limits::max(); + int num_points = polygon->num_points; + for (int i = 0; i < num_points; ++i) + { + point p0 = polygon->points[i]; + if (p == p0) { return i; } + + point p1 = polygon->points[(i+1)%num_points]; + point vsegment = p1 - p0; + point vpoint = p - p0; + + T t = vsegment.dot (vpoint) / vsegment.dot (vsegment); + + if (t < T{0} || t > T{1}) { continue; } + + point projected = p0 + vsegment * t; + T distsq = (p - projected).length_sq(); + + if (distsq < min_dist) { + min_dist = distsq; + min_edge = i; + } + } + assert (min_edge >= 0); + return min_edge; + } + + void clip_polygon_fill_gaps (const clipper* clipper, + context_internal* allocator, site* site) + { + // They're sorted CCW, so if the current->pos[1] != next->pos[0], then we have a gap + clipping_polygon* polygon = (clipping_polygon*)clipper->ctx; + int num_points = polygon->num_points; + + graphedge* current = site->edges; + if (!current) { + graphedge* gap = alloc_graphedge (allocator); + gap->neighbor = 0; + // Pick the first edge of the polygon (which is also CCW) + gap->pos[0] = polygon->points[0]; + gap->pos[1] = polygon->points[1]; + gap->angle = calc_sort_metric(site, gap); + gap->next = 0; + gap->edge = create_gap_edge (allocator, site, gap); + + current = gap; + site->edges = gap; + } + + graphedge* next = current->next; + if (!next) { + graphedge* gap = alloc_graphedge (allocator); + + int polygon_edge = find_polygon_edge (clipper, current->pos[1]); + if (!(current->pos[1] == polygon->points[(polygon_edge+1)%num_points])) { + gap->pos[0] = current->pos[1]; + gap->pos[1] = polygon->points[(polygon_edge+1)%num_points]; + } else { + gap->pos[0] = polygon->points[(polygon_edge+1)%num_points]; + gap->pos[1] = polygon->points[(polygon_edge+2)%num_points]; + } + + gap->neighbor = 0; + gap->angle = calc_sort_metric(site, gap); + gap->next = 0; + gap->edge = create_gap_edge(allocator, site, gap); + + gap->next = current->next; + current->next = gap; + current = gap; + next = site->edges; + } + + while (current && next) { + + if (!(current->pos[1] == next->pos[0])) { + int polygon_edge1 = find_polygon_edge (clipper, current->pos[1]); + int polygon_edge2 = find_polygon_edge (clipper, next->pos[0]); + + graphedge* gap = alloc_graphedge (allocator); + gap->pos[0] = current->pos[1]; + + if (polygon_edge1 != polygon_edge2) { + gap->pos[1] = polygon->points[(polygon_edge1 + 1) % num_points]; + } else { + gap->pos[1] = next->pos[0]; + } + + gap->neighbor = 0; + gap->angle = calc_sort_metric (site, gap); + gap->edge = create_gap_edge (allocator, site, gap); + gap->next = current->next; + current->next = gap; + } + + current = current->next; + if (current) { + next = current->next; + if (!next) { next = site->edges; } + } + } + } + + /** + * End of boundary clipping code + */ + // User-configurable border width T border_width = std::numeric_limits::epsilon(); @@ -1428,7 +1661,7 @@ namespace jcv jcv::diagram diagram; // A domain for the diagram. jcv::rect domain = {}; - }; // end struct jcv + }; // end struct jcv::manager } // namespace From 19b1034a8f4874f01b8227cf59993f5db1f778b4 Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 9 Jan 2026 12:14:10 +0000 Subject: [PATCH 02/53] Adds a skeleton app to be the test case for boundaried voronois --- examples/CMakeLists.txt | 3 ++ examples/voronoi_boundary.cpp | 55 +++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 examples/voronoi_boundary.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2e147b34..702c95af 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -411,6 +411,9 @@ target_link_libraries(triangle_intersect OpenGL::GL glfw Freetype::Freetype) add_executable(voronoi_random voronoi_random.cpp) target_link_libraries(voronoi_random OpenGL::GL glfw Freetype::Freetype) +add_executable(voronoi_boundary voronoi_boundary.cpp) +target_link_libraries(voronoi_boundary OpenGL::GL glfw Freetype::Freetype) + add_executable(voronoi_rectangular voronoi_rectangular.cpp) target_link_libraries(voronoi_rectangular OpenGL::GL glfw Freetype::Freetype) diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp new file mode 100644 index 00000000..21a4d054 --- /dev/null +++ b/examples/voronoi_boundary.cpp @@ -0,0 +1,55 @@ +/* + * This example generates a number (n_points) of random (but bounded) coordinates and + * uses the VoronoiVisual to display the coordinates as a map, with the order of + * random-choice being used to colourize the Voronoi cells. + * + * Test harness for drawing an arbitrary boundary around the Voronoi region + * + * Author Seb James + * Date 2026 + */ +#include +#include +#include +#include +#include + +static constexpr int n_points = 60; + +int main() +{ + int rtn = -1; + + mplot::Visual v(1024, 768, "VoronoiVisual"); + + sm::rand_uniform rngxy(-0.8f, 0.8f, n_points); + sm::rand_uniform rngz(0.8f, 1.0f, n_points); + + // make n_points random coordinates + std::vector> points(n_points); + std::vector data(n_points); + + for (unsigned int i = 0; i < n_points; ++i) { + points[i] = { rngxy.get(), rngxy.get(), rngz.get() }; + data[i] = static_cast(i) / n_points; + } + + mplot::ColourMapType cmap_t = mplot::ColourMapType::Plasma; + + sm::vec offset = { 0.0f }; + auto vorv = std::make_unique> (offset); + v.bindmodel (vorv); + vorv->show_voronoi2d = true; // true to show the 2D voronoi edges + vorv->debug_dataCoords = true; // true to show coordinate spheres + float length_scale = 8.0f / std::sqrt (n_points); + vorv->border_width = length_scale; + vorv->cm.setType (cmap_t); + vorv->setDataCoords (&points); + vorv->setScalarData (&data); + vorv->finalize(); + v.addVisualModel (vorv); + + v.keepOpen(); + + return rtn; +} From 79987c7de9fc0886bf8fed1fd81fc4f59f044fc2 Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 9 Jan 2026 13:47:28 +0000 Subject: [PATCH 03/53] Changes to make the boundary setting compile (doesn't work yet) --- mplot/VoronoiVisual.h | 31 ++++++--- mplot/jcvoronoi/jc_voronoi.h | 127 ++++++++++++++++++++++------------- 2 files changed, 101 insertions(+), 57 deletions(-) diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 556ed1b2..547f7ce3 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -89,8 +89,12 @@ namespace mplot this->dcoords_ptr = this->dataCoords; } - // Use mplot::range to find the extents of dataCoords. From these create a - // rectangle to pass to diagram_generate. + // Generate the 2D Voronoi diagram + jcv::manager vorman; + vorman.border_width = this->border_width; + + // Use mplot::range to find the extents of dataCoords. Helps to define boundary for test + // code. sm::range rx, ry; rx.search_init(); ry.search_init(); @@ -98,11 +102,18 @@ namespace mplot rx.update ((*this->dcoords_ptr)[i][0]); ry.update ((*this->dcoords_ptr)[i][1]); } - - // Generate the 2D Voronoi diagram - jcv::manager vorman; - vorman.border_width = this->border_width; - vorman.diagram_generate (*(dcoords_ptr)); + // Test triangle boundary + this->boundary.resize (3); + this->boundary[0] = { 2 * rx.min, 2 * ry.min }; + this->boundary[1] = { rx.max - rx.min, 2 * ry.max }; + this->boundary[2] = { 2 * rx.max, 2 * ry.min }; + + if (this->boundary.size() > 0) { + vorman.diagram_generate (*(dcoords_ptr), this->boundary); + } else { + // default rectangular box boundary + vorman.diagram_generate (*(dcoords_ptr)); + } // We obtain access to the Voronoi cell sites: const jcv::site* sites = vorman.diagram_get_sites(); @@ -258,7 +269,8 @@ namespace mplot } } if (static_cast(vorman.diagram_numsites()) != ncoords) { - std::cout << "WARNING: numsites != ncoords ?!?!\n"; + std::cout << "WARNING: numsites (" << vorman.diagram_numsites() + << ") != ncoords (" << ncoords << ")?!?!\n"; } // Draw optional objects @@ -473,7 +485,8 @@ namespace mplot //! Record the data index for each Voronoi cell index sm::vvec site_indices; unsigned int triangle_count_sum = 0; - + // Polygon domain coordinates + std::vector> boundary; //! Internally owned version of dataCoords after rotation std::vector> dcoords; //! A pointer either to dcoords or this->dataCoords diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 01503b3e..fbe42f8e 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -111,7 +111,7 @@ namespace jcv point min; // The bounding rect min point max; // The bounding rect max - void* ctx; // User defined context + void* ctx; // User defined context function }; // Second batch of structs @@ -187,8 +187,7 @@ namespace jcv template struct clipping_polygon { - jcv::point* points; - int num_points; + std::vector> points; }; #pragma pack(pop) @@ -1323,7 +1322,7 @@ namespace jcv qsort (sites, (size_t)num_points, sizeof(site), point_cmp); clipper box_clipper; - if (_clipper == 0) { + if (_clipper == nullptr) { // model->get_shaderprogs = &mplot::VisualBase::get_shaderprogs; box_clipper.test_fn = &jcv::manager::boxshape_test; box_clipper.clip_fn = &jcv::manager::boxshape_clip; @@ -1404,7 +1403,12 @@ namespace jcv */ static void diagram_generate (int num_points, const point* points, const rect* rect, const clipper* clipper, diagram* d) { - diagram_generate_useralloc(num_points, points, rect, clipper, 0, alloc_fn, free_fn, d); + diagram_generate_useralloc (num_points, points, rect, clipper, 0, alloc_fn, free_fn, d); + } + + static void diagram_generate (int num_points, const point* points, const clipper* clipper, diagram* d) + { + diagram_generate_useralloc (num_points, points, 0, clipper, 0, alloc_fn, free_fn, d); } // User API @@ -1426,17 +1430,45 @@ namespace jcv jcv::manager::diagram_generate (ncoords, centres.data(), &this->domain, 0, &this->diagram); } + // User API to generate with a polygon boundary + void diagram_generate (const std::vector>& centres, std::vector>& polygon) + { + int ncoords = static_cast(centres.size()); + + std::memset (&this->diagram, 0, sizeof(jcv::diagram)); + + jcv::clipper polygonclipper; + polygonclipper.test_fn = &jcv::manager::clip_polygon_test_point; + polygonclipper.clip_fn = &jcv::manager::clip_polygon_clip_edge; + polygonclipper.fill_fn = &jcv::manager::clip_polygon_fill_gaps; + polygonclipper.ctx = &polygon; + + jcv::manager::diagram_generate (ncoords, centres.data(), &polygonclipper, &this->diagram); + } + int diagram_numsites() const { return this->diagram.numsites; } /** * Boundary clipping code (was in jc_voronoi_clip.h) + * + * Usage: + * + * std::vector> polygon; + * // Triangle + * polygon.resize(3); + * polygon.points[0] = { width/2, height/5 }; + * polygon.points[1] = { width - width/5, height - height/5 }; + * polygon.points[2] = { width/5, height - height/5 }; + * + * jcv::manager vorman; + * vorman.diagram_generate (*(dcoords_ptr), polygon); */ static point mix (point a, point b, T t) { - point r; - r.x = a.x + (b.x - a.x) * t; - r.y = a.y + (b.y - a.y) * t; + point r = {}; + r[0] = a[0] + (b[0] - a[0]) * t; + r[1] = a[1] + (b[1] - a[1]) * t; return r; } @@ -1448,10 +1480,10 @@ namespace jcv return vsegment.dot (vpoint) / vsegment.dot (vsegment); } - int clip_polygon_test_point (const clipper* clipper, const point p) + static int clip_polygon_test_point (const clipper* clipper, const point p) { - clipping_polygon* polygon = (clipping_polygon*)clipper->ctx; // reinterpret? why? - int num_points = polygon->num_points; + auto polygon = reinterpret_cast>*>(clipper->ctx); + int num_points = polygon->size(); // convex polygon // winding CCW @@ -1460,11 +1492,11 @@ namespace jcv int result = 1; for (int i = 0; i < num_points; ++i) { - point p0 = polygon->points[i]; - point p1 = polygon->points[(i + 1) % num_points]; + point p0 = (*polygon)[i]; + point p1 = (*polygon)[(i + 1) % num_points]; point n = {}; - n.x = p1.y() - p0.y(); - n.y = p0.x() - p1.x(); + n[0] = p1.y() - p0.y(); + n[1] = p0.x() - p1.x(); point diff = p - p0; if (n.dot (diff) > 0) { @@ -1479,19 +1511,19 @@ namespace jcv point p0, point p1, T* out_t0, T* out_t1) { - clipping_polygon* polygon = (clipping_polygon*)clipper->ctx; - int num_points = polygon->num_points; + auto polygon = reinterpret_cast>*>(clipper->ctx); + int num_points = polygon->size(); T t0 = T{0}; T t1 = T{1}; point dir = p1 - p0; for (int i = 0; i < num_points; ++i) { - point v0 = polygon->points[i]; - point v1 = polygon->points[(i+1)%num_points]; - point n; - n.x = v1.y - v0.y; - n.y = -(v1.x - v0.x); + point v0 = (*polygon)[i]; + point v1 = (*polygon)[(i+1)%num_points]; + point n = {}; + n[0] = v1.y() - v0.y(); + n[1] = -(v1.x() - v0.x()); point v0p0 = p0 - v0; @@ -1517,10 +1549,10 @@ namespace jcv return 1; } - int clip_polygon_clip_edge (const clipper* clipper, edge* e) + static int clip_polygon_clip_edge (const clipper* clipper, edge* e) { // Using the box clipper to get a finite line segment - int result = manager::boxshape_clip(clipper, e); + int result = manager::boxshape_clip (clipper, e); if (!result) { return 0; } point p0 = e->pos[0]; @@ -1535,25 +1567,24 @@ namespace jcv return 0; } - e->pos[0] = mix (p0, p1, t0); - e->pos[1] = mix (p0, p1, t1); + e->pos[0] = mix (p0, p1, t0); + e->pos[1] = mix (p0, p1, t1); return 1; } // Find the edge which the point sits on static int find_polygon_edge (const clipper* clipper, point p) { - clipping_polygon* polygon = (clipping_polygon*)clipper->ctx; - + auto polygon = reinterpret_cast>*>(clipper->ctx); int min_edge = -1; T min_dist = std::numeric_limits::max(); - int num_points = polygon->num_points; + int num_points = polygon->size(); for (int i = 0; i < num_points; ++i) { - point p0 = polygon->points[i]; + point p0 = (*polygon)[i]; if (p == p0) { return i; } - point p1 = polygon->points[(i+1)%num_points]; + point p1 = (*polygon)[(i+1)%num_points]; point vsegment = p1 - p0; point vpoint = p - p0; @@ -1573,23 +1604,23 @@ namespace jcv return min_edge; } - void clip_polygon_fill_gaps (const clipper* clipper, - context_internal* allocator, site* site) + static void clip_polygon_fill_gaps (const clipper* clipper, + context_internal* allocator, site* site) { // They're sorted CCW, so if the current->pos[1] != next->pos[0], then we have a gap - clipping_polygon* polygon = (clipping_polygon*)clipper->ctx; - int num_points = polygon->num_points; + auto polygon = reinterpret_cast>*>(clipper->ctx); + int num_points = polygon->size(); graphedge* current = site->edges; if (!current) { graphedge* gap = alloc_graphedge (allocator); gap->neighbor = 0; // Pick the first edge of the polygon (which is also CCW) - gap->pos[0] = polygon->points[0]; - gap->pos[1] = polygon->points[1]; - gap->angle = calc_sort_metric(site, gap); + gap->pos[0] = (*polygon)[0]; + gap->pos[1] = (*polygon)[1]; + gap->angle = calc_sort_metric (site, gap); gap->next = 0; - gap->edge = create_gap_edge (allocator, site, gap); + gap->edge_ = create_gap_edge (allocator, site, gap); current = gap; site->edges = gap; @@ -1599,19 +1630,19 @@ namespace jcv if (!next) { graphedge* gap = alloc_graphedge (allocator); - int polygon_edge = find_polygon_edge (clipper, current->pos[1]); - if (!(current->pos[1] == polygon->points[(polygon_edge+1)%num_points])) { + int polygon_edge = find_polygon_edge (clipper, current->pos[1]); + if (!(current->pos[1] == (*polygon)[(polygon_edge+1)%num_points])) { gap->pos[0] = current->pos[1]; - gap->pos[1] = polygon->points[(polygon_edge+1)%num_points]; + gap->pos[1] = (*polygon)[(polygon_edge+1)%num_points]; } else { - gap->pos[0] = polygon->points[(polygon_edge+1)%num_points]; - gap->pos[1] = polygon->points[(polygon_edge+2)%num_points]; + gap->pos[0] = (*polygon)[(polygon_edge+1)%num_points]; + gap->pos[1] = (*polygon)[(polygon_edge+2)%num_points]; } gap->neighbor = 0; - gap->angle = calc_sort_metric(site, gap); + gap->angle = calc_sort_metric (site, gap); gap->next = 0; - gap->edge = create_gap_edge(allocator, site, gap); + gap->edge_ = create_gap_edge(allocator, site, gap); gap->next = current->next; current->next = gap; @@ -1629,14 +1660,14 @@ namespace jcv gap->pos[0] = current->pos[1]; if (polygon_edge1 != polygon_edge2) { - gap->pos[1] = polygon->points[(polygon_edge1 + 1) % num_points]; + gap->pos[1] = (*polygon)[(polygon_edge1 + 1) % num_points]; } else { gap->pos[1] = next->pos[0]; } gap->neighbor = 0; gap->angle = calc_sort_metric (site, gap); - gap->edge = create_gap_edge (allocator, site, gap); + gap->edge_ = create_gap_edge (allocator, site, gap); gap->next = current->next; current->next = gap; } From e1abd04f553b24333d5e1a8e57bd0c5382dd2f50 Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 9 Jan 2026 15:46:10 +0000 Subject: [PATCH 04/53] About to make the polygon boundary nicer --- mplot/VoronoiVisual.h | 45 ++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 547f7ce3..977e887f 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -93,20 +93,27 @@ namespace mplot jcv::manager vorman; vorman.border_width = this->border_width; - // Use mplot::range to find the extents of dataCoords. Helps to define boundary for test - // code. - sm::range rx, ry; - rx.search_init(); - ry.search_init(); - for (unsigned int i = 0; i < ncoords; ++i) { - rx.update ((*this->dcoords_ptr)[i][0]); - ry.update ((*this->dcoords_ptr)[i][1]); + if (this->rectangular_domain == false) { + // Use mplot::range to find the extents of dataCoords. Helps to define boundary for + // test code. + sm::range rx, ry; + rx.search_init(); + ry.search_init(); + for (unsigned int i = 0; i < ncoords; ++i) { + rx.update ((*this->dcoords_ptr)[i][0]); + ry.update ((*this->dcoords_ptr)[i][1]); + } + // Test triangle boundary + this->boundary.resize (3); + sm::vec<> ofs = { 0.0f, 0.4f }; + // Points MUST be in anti-clockwise order!!!!! + std::cout << "rx: " << rx << " and ry: " << ry << std::endl; + this->boundary[0] = { 2 * rx.max, 2 * ry.min }; + this->boundary[1] = { (rx.min + rx.max) / 2.0f, 2 * ry.max }; + this->boundary[2] = { 2 * rx.min, 2 * ry.min }; + for (auto& b : boundary) { b += ofs; } + std::cout << "Boundary: " << boundary[0] << "; " << boundary[1] << "; " << boundary[2] << "\n"; } - // Test triangle boundary - this->boundary.resize (3); - this->boundary[0] = { 2 * rx.min, 2 * ry.min }; - this->boundary[1] = { rx.max - rx.min, 2 * ry.max }; - this->boundary[2] = { 2 * rx.max, 2 * ry.min }; if (this->boundary.size() > 0) { vorman.diagram_generate (*(dcoords_ptr), this->boundary); @@ -115,6 +122,14 @@ namespace mplot vorman.diagram_generate (*(dcoords_ptr)); } + if (static_cast(vorman.diagram_numsites()) == 0) { + if (this->boundary.empty()) { + throw std::runtime_error ("numsites == 0."); + } else { + throw std::runtime_error ("numsites == 0. Make sure your boundary points appear in anti-clockwise order"); + } + } + // We obtain access to the Voronoi cell sites: const jcv::site* sites = vorman.diagram_get_sites(); @@ -485,6 +500,10 @@ namespace mplot //! Record the data index for each Voronoi cell index sm::vvec site_indices; unsigned int triangle_count_sum = 0; + // Create a rectangular domain or use a smoother boundary? + bool rectangular_domain = true; + // When making the boundary, how many points? Or use some f (dcoords.size())? + unsigned int num_boundary_points = 100; // Polygon domain coordinates std::vector> boundary; //! Internally owned version of dataCoords after rotation From 2c9d24bec1319d400246c955df50c7b4b63aeb9f Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 9 Jan 2026 16:46:01 +0000 Subject: [PATCH 05/53] Progress, but have an odd behaviour here --- examples/voronoi_boundary.cpp | 3 +- mplot/VoronoiVisual.h | 42 +++++++++++++----------- mplot/jcvoronoi/jc_voronoi.h | 61 ++++++++++++++--------------------- 3 files changed, 49 insertions(+), 57 deletions(-) diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp index 21a4d054..ac190240 100644 --- a/examples/voronoi_boundary.cpp +++ b/examples/voronoi_boundary.cpp @@ -42,7 +42,8 @@ int main() vorv->show_voronoi2d = true; // true to show the 2D voronoi edges vorv->debug_dataCoords = true; // true to show coordinate spheres float length_scale = 8.0f / std::sqrt (n_points); - vorv->border_width = length_scale; + std::cout << "Setting border_width to length scale " << length_scale << std::endl; + vorv->border_width = length_scale; vorv->cm.setType (cmap_t); vorv->setDataCoords (&points); vorv->setScalarData (&data); diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 977e887f..d9cc24ed 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -94,25 +94,29 @@ namespace mplot vorman.border_width = this->border_width; if (this->rectangular_domain == false) { - // Use mplot::range to find the extents of dataCoords. Helps to define boundary for - // test code. - sm::range rx, ry; - rx.search_init(); - ry.search_init(); - for (unsigned int i = 0; i < ncoords; ++i) { - rx.update ((*this->dcoords_ptr)[i][0]); - ry.update ((*this->dcoords_ptr)[i][1]); + // Then we make up a perimeter to be our boundary + sm::vvec max_lengths; + max_lengths.resize (this->num_boundary_points, 0.0f); + for (unsigned int i = 0; i < this->dcoords_ptr->size(); ++i) { + sm::vec c = (*this->dcoords_ptr)[i].less_one_dim(); + float a = c.angle(); + sm::algo::zero_to_twopi (a); + a *= (this->num_boundary_points / sm::mathconst::two_pi); + int ai = std::floor (a); + if (static_cast(ai) == this->num_boundary_points) { ai = 0; } + max_lengths[ai] = c.length() > max_lengths[ai] ? c.length() : max_lengths[ai]; } - // Test triangle boundary - this->boundary.resize (3); - sm::vec<> ofs = { 0.0f, 0.4f }; + // Points MUST be in anti-clockwise order!!!!! - std::cout << "rx: " << rx << " and ry: " << ry << std::endl; - this->boundary[0] = { 2 * rx.max, 2 * ry.min }; - this->boundary[1] = { (rx.min + rx.max) / 2.0f, 2 * ry.max }; - this->boundary[2] = { 2 * rx.min, 2 * ry.min }; - for (auto& b : boundary) { b += ofs; } - std::cout << "Boundary: " << boundary[0] << "; " << boundary[1] << "; " << boundary[2] << "\n"; + this->boundary.resize (this->num_boundary_points); + for (unsigned int i = 0; i < this->num_boundary_points; ++i) { + float l = max_lengths[i] + this->border_width; + float ang = i * sm::mathconst::two_pi / this->num_boundary_points; + this->boundary[i] = { l * std::cos (ang), l * std::sin (ang) }; + } + } + for (auto b : this->boundary) { + std::cout << "Boundary point: " << b << std::endl; } if (this->boundary.size() > 0) { @@ -501,9 +505,9 @@ namespace mplot sm::vvec site_indices; unsigned int triangle_count_sum = 0; // Create a rectangular domain or use a smoother boundary? - bool rectangular_domain = true; + bool rectangular_domain = false; // When making the boundary, how many points? Or use some f (dcoords.size())? - unsigned int num_boundary_points = 100; + unsigned int num_boundary_points = 12; // Polygon domain coordinates std::vector> boundary; //! Internally owned version of dataCoords after rotation diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index fbe42f8e..24f50441 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -237,6 +237,23 @@ namespace jcv return pt->x() == min->x() || pt->y() == min->y() || pt->x() == max->x() || pt->y() == max->y(); } + static point mix (point a, point b, T t) + { + point r = {}; + r[0] = a[0] + (b[0] - a[0]) * t; + r[1] = a[1] + (b[1] - a[1]) * t; + return r; + } + + // if it returns [0.0, 1.0] it's on the line segment + static T point_to_line_segment_t (point p, point p0, point p1) + { + point vpoint = p - p0; + point vsegment = p1 - p0; + return vsegment.dot (vpoint) / vsegment.dot (vsegment); + } + + // edges and corners static const int EDGE_LEFT = 1; static const int EDGE_RIGHT = 2; @@ -1448,38 +1465,6 @@ namespace jcv int diagram_numsites() const { return this->diagram.numsites; } - /** - * Boundary clipping code (was in jc_voronoi_clip.h) - * - * Usage: - * - * std::vector> polygon; - * // Triangle - * polygon.resize(3); - * polygon.points[0] = { width/2, height/5 }; - * polygon.points[1] = { width - width/5, height - height/5 }; - * polygon.points[2] = { width/5, height - height/5 }; - * - * jcv::manager vorman; - * vorman.diagram_generate (*(dcoords_ptr), polygon); - */ - - static point mix (point a, point b, T t) - { - point r = {}; - r[0] = a[0] + (b[0] - a[0]) * t; - r[1] = a[1] + (b[1] - a[1]) * t; - return r; - } - - // if it returns [0.0, 1.0] it's on the line segment - static T point_to_line_segment_t (point p, point p0, point p1) - { - point vpoint = p - p0; - point vsegment = p1 - p0; - return vsegment.dot (vpoint) / vsegment.dot (vsegment); - } - static int clip_polygon_test_point (const clipper* clipper, const point p) { auto polygon = reinterpret_cast>*>(clipper->ctx); @@ -1558,8 +1543,8 @@ namespace jcv point p0 = e->pos[0]; point p1 = e->pos[1]; - T t0; - T t1; + T t0 = T{0}; + T t1 = T{0}; result = ray_intersect_polygon (clipper, p0, p1, &t0, &t1); if (!result) { @@ -1579,8 +1564,7 @@ namespace jcv int min_edge = -1; T min_dist = std::numeric_limits::max(); int num_points = polygon->size(); - for (int i = 0; i < num_points; ++i) - { + for (int i = 0; i < num_points; ++i) { point p0 = (*polygon)[i]; if (p == p0) { return i; } @@ -1600,6 +1584,7 @@ namespace jcv min_edge = i; } } + assert (min_edge >= 0); return min_edge; } @@ -1642,7 +1627,7 @@ namespace jcv gap->neighbor = 0; gap->angle = calc_sort_metric (site, gap); gap->next = 0; - gap->edge_ = create_gap_edge(allocator, site, gap); + gap->edge_ = create_gap_edge (allocator, site, gap); gap->next = current->next; current->next = gap; @@ -1653,7 +1638,9 @@ namespace jcv while (current && next) { if (!(current->pos[1] == next->pos[0])) { + int polygon_edge1 = find_polygon_edge (clipper, current->pos[1]); + //std::cout << "3 fpe for p = next->pos[0] = " << next->pos[0] << std::endl; int polygon_edge2 = find_polygon_edge (clipper, next->pos[0]); graphedge* gap = alloc_graphedge (allocator); From 0376492fc159900d92aa5e3d198b50c874b4db92 Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 9 Jan 2026 17:01:07 +0000 Subject: [PATCH 06/53] Refactors some function names --- examples/voronoi_boundary.cpp | 2 +- mplot/VoronoiVisual.h | 2 +- mplot/jcvoronoi/jc_voronoi.h | 24 ++++++++++++------------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp index ac190240..66eed901 100644 --- a/examples/voronoi_boundary.cpp +++ b/examples/voronoi_boundary.cpp @@ -41,7 +41,7 @@ int main() v.bindmodel (vorv); vorv->show_voronoi2d = true; // true to show the 2D voronoi edges vorv->debug_dataCoords = true; // true to show coordinate spheres - float length_scale = 8.0f / std::sqrt (n_points); + float length_scale = 4.0f / std::sqrt (n_points); std::cout << "Setting border_width to length scale " << length_scale << std::endl; vorv->border_width = length_scale; vorv->cm.setType (cmap_t); diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index d9cc24ed..11e74d95 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -507,7 +507,7 @@ namespace mplot // Create a rectangular domain or use a smoother boundary? bool rectangular_domain = false; // When making the boundary, how many points? Or use some f (dcoords.size())? - unsigned int num_boundary_points = 12; + unsigned int num_boundary_points = 11; // Polygon domain coordinates std::vector> boundary; //! Internally owned version of dataCoords after rotation diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 24f50441..2cd96095 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -1014,7 +1014,7 @@ namespace jcv return edge; } - static void boxshape_fillgaps (const clipper* clipper, context_internal* allocator, site* site) + static void boxshape_fill (const clipper* clipper, context_internal* allocator, site* site) { // They're sorted CCW, so if the current->pos[1] != next->pos[0], then we have a gap graphedge* current = site->edges; @@ -1025,11 +1025,11 @@ namespace jcv graphedge* gap = alloc_graphedge (allocator); gap->neighbor = 0; gap->pos[0] = clipper->min; - gap->pos[1][0] = clipper->max[0]; - gap->pos[1][1] = clipper->min[1]; + gap->pos[1][0] = clipper->max[0]; + gap->pos[1][1] = clipper->min[1]; gap->angle = calc_sort_metric(site, gap); gap->next = 0; - gap->edge_ = create_gap_edge (allocator, site, gap); + gap->edge_ = create_gap_edge (allocator, site, gap); current = gap; site->edges = gap; @@ -1343,7 +1343,7 @@ namespace jcv // model->get_shaderprogs = &mplot::VisualBase::get_shaderprogs; box_clipper.test_fn = &jcv::manager::boxshape_test; box_clipper.clip_fn = &jcv::manager::boxshape_clip; - box_clipper.fill_fn = &jcv::manager::boxshape_fillgaps; + box_clipper.fill_fn = &jcv::manager::boxshape_fill; _clipper = &box_clipper; } internal->clipper_ = *_clipper; @@ -1455,9 +1455,9 @@ namespace jcv std::memset (&this->diagram, 0, sizeof(jcv::diagram)); jcv::clipper polygonclipper; - polygonclipper.test_fn = &jcv::manager::clip_polygon_test_point; - polygonclipper.clip_fn = &jcv::manager::clip_polygon_clip_edge; - polygonclipper.fill_fn = &jcv::manager::clip_polygon_fill_gaps; + polygonclipper.test_fn = &jcv::manager::polygon_test; + polygonclipper.clip_fn = &jcv::manager::polygon_clip; + polygonclipper.fill_fn = &jcv::manager::polygon_fill; polygonclipper.ctx = &polygon; jcv::manager::diagram_generate (ncoords, centres.data(), &polygonclipper, &this->diagram); @@ -1465,7 +1465,7 @@ namespace jcv int diagram_numsites() const { return this->diagram.numsites; } - static int clip_polygon_test_point (const clipper* clipper, const point p) + static int polygon_test (const clipper* clipper, const point p) { auto polygon = reinterpret_cast>*>(clipper->ctx); int num_points = polygon->size(); @@ -1534,7 +1534,7 @@ namespace jcv return 1; } - static int clip_polygon_clip_edge (const clipper* clipper, edge* e) + static int polygon_clip (const clipper* clipper, edge* e) { // Using the box clipper to get a finite line segment int result = manager::boxshape_clip (clipper, e); @@ -1589,8 +1589,8 @@ namespace jcv return min_edge; } - static void clip_polygon_fill_gaps (const clipper* clipper, - context_internal* allocator, site* site) + static void polygon_fill (const clipper* clipper, + context_internal* allocator, site* site) { // They're sorted CCW, so if the current->pos[1] != next->pos[0], then we have a gap auto polygon = reinterpret_cast>*>(clipper->ctx); From d9bfdeaffebb61680e6f26a92bb015c2ca826860 Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 9 Jan 2026 17:22:54 +0000 Subject: [PATCH 07/53] Found the location of the issue, just need to handle it nicely now --- mplot/jcvoronoi/jc_voronoi.h | 47 +++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 2cd96095..5578be5a 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -546,10 +546,11 @@ namespace jcv if (y2 < pymin) { y2 = pymin; } x2 = (e->c) - (e->b) * y2; // Never occurs according to lcov - // if (((x1 > pxmax) & (x2 > pxmax)) | ((x1 < pxmin) & (x2 < pxmin))) - // { - // return 0; - // } + if (((x1 > pxmax) & (x2 > pxmax)) | ((x1 < pxmin) & (x2 < pxmin))) + { + std::cout << "Yes it fucking does" << std::endl; + return 0; + } if (x1 > pxmax) { x1 = pxmax; y1 = (e->c - x1) / e->b; @@ -576,7 +577,10 @@ namespace jcv if (x2 < pxmin) { x2 = pxmin; } y2 = e->c - e->a * x2; // Never occurs according to lcov - // if (((y1 > pymax) & (y2 > pymax)) | ((y1 < pymin) & (y2 < pymin))) { return 0; } + if (((y1 > pymax) & (y2 > pymax)) | ((y1 < pymin) & (y2 < pymin))) { + std::cout << "Yes it fucking does" << std::endl; + return 0; + } if (y1 > pymax) { y1 = pymax; x1 = (e->c - y1) / e->a; @@ -606,6 +610,7 @@ namespace jcv // see edge_create static int edge_clipline (context_internal* internal, edge* e) { + std::cout << "edge_clipline calling clip_fn...\n"; return internal->clipper_.clip_fn (&internal->clipper_, e); } @@ -954,6 +959,12 @@ namespace jcv static void finishline (context_internal* internal, edge* e) { +#if 1 // Here's something that needs to be dealt with: + if (e->pos[1][0] == std::numeric_limits::lowest()) { + std::cout << "finishline Returning now, special case\n"; + return; + } +#endif if (!edge_clipline (internal, e)) { return; } // Make sure the graph edges are CCW @@ -1493,7 +1504,7 @@ namespace jcv } static int ray_intersect_polygon (const clipper* clipper, - point p0, point p1, + const point p0, const point p1, T* out_t0, T* out_t1) { auto polygon = reinterpret_cast>*>(clipper->ctx); @@ -1536,6 +1547,8 @@ namespace jcv static int polygon_clip (const clipper* clipper, edge* e) { + std::cout << __func__ << " called for edge e from " << e->pos[0] << " to " << e->pos[1] << std::endl; + // Using the box clipper to get a finite line segment int result = manager::boxshape_clip (clipper, e); if (!result) { return 0; } @@ -1554,10 +1567,14 @@ namespace jcv e->pos[0] = mix (p0, p1, t0); e->pos[1] = mix (p0, p1, t1); + if (e->pos[1][1] == 11) { + std::cout << "Yikes e->pos[1] = " << e->pos[1] << "\n"; + std::cout << "mix (" << p0 << ", " << p1 << "," << t1 << ") was the culprit\n"; + } return 1; } - // Find the edge which the point sits on + // Find the edge which the point p sits on static int find_polygon_edge (const clipper* clipper, point p) { auto polygon = reinterpret_cast>*>(clipper->ctx); @@ -1601,8 +1618,9 @@ namespace jcv graphedge* gap = alloc_graphedge (allocator); gap->neighbor = 0; // Pick the first edge of the polygon (which is also CCW) - gap->pos[0] = (*polygon)[0]; + gap->pos[0] = (*polygon)[0]; // ???? gap->pos[1] = (*polygon)[1]; + if (gap->pos[1][1] == 11) { std::cout << " bleurgh 0\n"; } gap->angle = calc_sort_metric (site, gap); gap->next = 0; gap->edge_ = create_gap_edge (allocator, site, gap); @@ -1616,12 +1634,14 @@ namespace jcv graphedge* gap = alloc_graphedge (allocator); int polygon_edge = find_polygon_edge (clipper, current->pos[1]); - if (!(current->pos[1] == (*polygon)[(polygon_edge+1)%num_points])) { + if (!(current->pos[1] == (*polygon)[(polygon_edge + 1) % num_points])) { gap->pos[0] = current->pos[1]; - gap->pos[1] = (*polygon)[(polygon_edge+1)%num_points]; + gap->pos[1] = (*polygon)[(polygon_edge + 1) % num_points]; + if (gap->pos[1][1] == 11) { std::cout << " bleurgh 1\n"; } } else { - gap->pos[0] = (*polygon)[(polygon_edge+1)%num_points]; - gap->pos[1] = (*polygon)[(polygon_edge+2)%num_points]; + gap->pos[0] = (*polygon)[(polygon_edge + 1) % num_points]; + gap->pos[1] = (*polygon)[(polygon_edge + 2) % num_points]; + if (gap->pos[1][1] == 11) { std::cout << " bleurgh 2\n"; } } gap->neighbor = 0; @@ -1645,11 +1665,14 @@ namespace jcv graphedge* gap = alloc_graphedge (allocator); gap->pos[0] = current->pos[1]; + if (gap->pos[1][1] == 11) { std::cout << " bleurgh 3\n"; } if (polygon_edge1 != polygon_edge2) { gap->pos[1] = (*polygon)[(polygon_edge1 + 1) % num_points]; + if (gap->pos[1][1] == 11) { std::cout << " bleurgh 4\n"; } } else { gap->pos[1] = next->pos[0]; + if (gap->pos[1][1] == 11) { std::cout << " bleurgh 5\n"; } } gap->neighbor = 0; From d999fa5e3c4a37ef4f7bf17f99a27b9d491207f3 Mon Sep 17 00:00:00 2001 From: Seb James Date: Sat, 10 Jan 2026 10:37:14 +0000 Subject: [PATCH 08/53] Tiny changes --- examples/voronoi_boundary.cpp | 3 ++- mplot/VoronoiVisual.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp index 66eed901..ef55edb3 100644 --- a/examples/voronoi_boundary.cpp +++ b/examples/voronoi_boundary.cpp @@ -41,7 +41,8 @@ int main() v.bindmodel (vorv); vorv->show_voronoi2d = true; // true to show the 2D voronoi edges vorv->debug_dataCoords = true; // true to show coordinate spheres - float length_scale = 4.0f / std::sqrt (n_points); + //float length_scale = 4.0f / std::sqrt (n_points); + float length_scale = 0.2f; std::cout << "Setting border_width to length scale " << length_scale << std::endl; vorv->border_width = length_scale; vorv->cm.setType (cmap_t); diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 11e74d95..d9cc24ed 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -507,7 +507,7 @@ namespace mplot // Create a rectangular domain or use a smoother boundary? bool rectangular_domain = false; // When making the boundary, how many points? Or use some f (dcoords.size())? - unsigned int num_boundary_points = 11; + unsigned int num_boundary_points = 12; // Polygon domain coordinates std::vector> boundary; //! Internally owned version of dataCoords after rotation From 571b0dfafdddd7abc37ac3753c8adbd403766cc5 Mon Sep 17 00:00:00 2001 From: Seb James Date: Sat, 10 Jan 2026 10:51:48 +0000 Subject: [PATCH 09/53] Get a nice looking boundary at this commit --- mplot/jcvoronoi/jc_voronoi.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 5578be5a..fb3d9ea9 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -960,12 +960,19 @@ namespace jcv static void finishline (context_internal* internal, edge* e) { #if 1 // Here's something that needs to be dealt with: - if (e->pos[1][0] == std::numeric_limits::lowest()) { - std::cout << "finishline Returning now, special case\n"; - return; + // These just mean 'edge goes to infinity' + if (e->pos[1][0] == std::numeric_limits::lowest() +// || e->pos[1][1] == std::numeric_limits::lowest() +// || e->pos[0][0] == std::numeric_limits::lowest() +// || e->pos[0][1] == std::numeric_limits::lowest() + ) { + std::cout << "edge to infinity case\n"; } #endif - if (!edge_clipline (internal, e)) { return; } + if (!edge_clipline (internal, e)) { + std::cout << "edge_clipline returned false. Returning now\n"; + return; + } // Make sure the graph edges are CCW int flip = determinant (&e->sites[0]->p, &e->pos[0], &e->pos[1]) > T{0} ? 0 : 1; From d19af8f8b0e76960409de5c7b98ce2a196694961 Mon Sep 17 00:00:00 2001 From: Seb James Date: Sat, 10 Jan 2026 10:53:45 +0000 Subject: [PATCH 10/53] With just this change, I get the min_edge >= 0 assertion fail --- examples/voronoi_boundary.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp index ef55edb3..3ff12a59 100644 --- a/examples/voronoi_boundary.cpp +++ b/examples/voronoi_boundary.cpp @@ -42,7 +42,7 @@ int main() vorv->show_voronoi2d = true; // true to show the 2D voronoi edges vorv->debug_dataCoords = true; // true to show coordinate spheres //float length_scale = 4.0f / std::sqrt (n_points); - float length_scale = 0.2f; + float length_scale = 0.1f; std::cout << "Setting border_width to length scale " << length_scale << std::endl; vorv->border_width = length_scale; vorv->cm.setType (cmap_t); From ef9f62354669cc4735ce5ab3851ef8d3feb64de9 Mon Sep 17 00:00:00 2001 From: Seb James Date: Sat, 10 Jan 2026 11:54:06 +0000 Subject: [PATCH 11/53] additional debugging/hacking --- mplot/jcvoronoi/jc_voronoi.h | 73 +++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index fb3d9ea9..7dca396a 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -46,6 +46,17 @@ namespace jcv point p; int index; // Index into the original list of points graphedge* edges; // The half edges owned by the cell +#if 1 + void cout_edges() + { + std::cout << "site " << index << " @ " << p << " has edges:\n"; + graphedge* e = edges; + while (e) { + std::cout << " " << e->str() << std::endl; + e = e->next; + } + } +#endif }; // The coefficients a, b and c are from the general line equation: ax * by + c = 0 @@ -68,6 +79,14 @@ namespace jcv struct site* neighbor; point pos[2]; T angle; +#if 1 + std::string str() + { + std::stringstream ss; + ss << "graphedge with edge: " << edge_->pos[0] << "--" << edge_->pos[1]; + return ss.str(); + } +#endif }; template @@ -909,12 +928,14 @@ namespace jcv edge2->y = p[1] + point_dist (&_site->p, &p); pq_push (internal->eventqueue, edge2); } + + _site->cout_edges(); } // https://cp-algorithms.com/geometry/oriented-triangle-area.html static T determinant (const point* a, const point* b, const point* c) { - return (b->x() - a->x())*(c->y() - a->y()) - (b->y() - a->y())*(c->x() - a->x()); + return (b->x() - a->x()) * (c->y() - a->y()) - (b->y() - a->y())*(c->x() - a->x()); } static T calc_sort_metric (const site* _site, const graphedge* _edge) @@ -1384,6 +1405,7 @@ namespace jcv // In the case of all sites being all on a horizontal or vertical line, the // rect area will be zero, and the diagram generation will most likely fail rect_round(&tmp_rect); + //throw std::runtime_error ("rect_inflate creates the 11 (10+1)"); rect_inflate(&tmp_rect, 10); internal->clipper_.min = tmp_rect.min; @@ -1412,6 +1434,7 @@ namespace jcv lowest_pq_point[0] = he->vertex[0]; lowest_pq_point[1] = he->y; } + std::cout << "lowest_pq_point = " << lowest_pq_point << std::endl; if (site != 0 && (pq_empty(pq) || lessthan (&site->p, &lowest_pq_point))) { site_event (internal, site); @@ -1598,6 +1621,9 @@ namespace jcv T t = vsegment.dot (vpoint) / vsegment.dot (vsegment); + // Comment this line out to see that it's not so much the fill function that's at + // fault, but the construction of the edges (with one edge going out to the + // rectangle boundary limit) if (t < T{0} || t > T{1}) { continue; } point projected = p0 + vsegment * t; @@ -1609,7 +1635,11 @@ namespace jcv } } +#if 1 + if (min_edge == -1) { min_edge = 0; } // hack +#else assert (min_edge >= 0); +#endif return min_edge; } @@ -1620,29 +1650,30 @@ namespace jcv auto polygon = reinterpret_cast>*>(clipper->ctx); int num_points = polygon->size(); - graphedge* current = site->edges; - if (!current) { + graphedge* curr_graphedge = site->edges; + if (!curr_graphedge) { + std::cout << "Entered if (!curr_graphedge) block\n"; graphedge* gap = alloc_graphedge (allocator); gap->neighbor = 0; // Pick the first edge of the polygon (which is also CCW) - gap->pos[0] = (*polygon)[0]; // ???? + gap->pos[0] = (*polygon)[0]; gap->pos[1] = (*polygon)[1]; if (gap->pos[1][1] == 11) { std::cout << " bleurgh 0\n"; } gap->angle = calc_sort_metric (site, gap); gap->next = 0; gap->edge_ = create_gap_edge (allocator, site, gap); - current = gap; + curr_graphedge = gap; site->edges = gap; } - graphedge* next = current->next; + graphedge* next = curr_graphedge->next; if (!next) { graphedge* gap = alloc_graphedge (allocator); - int polygon_edge = find_polygon_edge (clipper, current->pos[1]); - if (!(current->pos[1] == (*polygon)[(polygon_edge + 1) % num_points])) { - gap->pos[0] = current->pos[1]; + int polygon_edge = find_polygon_edge (clipper, curr_graphedge->pos[1]); + if (!(curr_graphedge->pos[1] == (*polygon)[(polygon_edge + 1) % num_points])) { + gap->pos[0] = curr_graphedge->pos[1]; gap->pos[1] = (*polygon)[(polygon_edge + 1) % num_points]; if (gap->pos[1][1] == 11) { std::cout << " bleurgh 1\n"; } } else { @@ -1656,22 +1687,22 @@ namespace jcv gap->next = 0; gap->edge_ = create_gap_edge (allocator, site, gap); - gap->next = current->next; - current->next = gap; - current = gap; + gap->next = curr_graphedge->next; + curr_graphedge->next = gap; + curr_graphedge = gap; next = site->edges; } - while (current && next) { + while (curr_graphedge && next) { - if (!(current->pos[1] == next->pos[0])) { + if (!(curr_graphedge->pos[1] == next->pos[0])) { - int polygon_edge1 = find_polygon_edge (clipper, current->pos[1]); + int polygon_edge1 = find_polygon_edge (clipper, curr_graphedge->pos[1]); //std::cout << "3 fpe for p = next->pos[0] = " << next->pos[0] << std::endl; int polygon_edge2 = find_polygon_edge (clipper, next->pos[0]); graphedge* gap = alloc_graphedge (allocator); - gap->pos[0] = current->pos[1]; + gap->pos[0] = curr_graphedge->pos[1]; if (gap->pos[1][1] == 11) { std::cout << " bleurgh 3\n"; } if (polygon_edge1 != polygon_edge2) { @@ -1685,13 +1716,13 @@ namespace jcv gap->neighbor = 0; gap->angle = calc_sort_metric (site, gap); gap->edge_ = create_gap_edge (allocator, site, gap); - gap->next = current->next; - current->next = gap; + gap->next = curr_graphedge->next; + curr_graphedge->next = gap; } - current = current->next; - if (current) { - next = current->next; + curr_graphedge = curr_graphedge->next; + if (curr_graphedge) { + next = curr_graphedge->next; if (!next) { next = site->edges; } } } From 5a3754959cb72acb6e27ec764bf12c79c1686725 Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 12 Jan 2026 07:36:36 +0000 Subject: [PATCH 12/53] Work in progress --- examples/voronoi_boundary.cpp | 13 ++++- mplot/VisualDataModel.h | 3 ++ mplot/VoronoiVisual.h | 40 ++++++++++++--- mplot/jcvoronoi/jc_voronoi.h | 91 +++++++++++++++++++++++++---------- 4 files changed, 114 insertions(+), 33 deletions(-) diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp index 3ff12a59..5e191973 100644 --- a/examples/voronoi_boundary.cpp +++ b/examples/voronoi_boundary.cpp @@ -14,7 +14,7 @@ #include #include -static constexpr int n_points = 60; +static constexpr int n_points = 13; int main() { @@ -42,9 +42,18 @@ int main() vorv->show_voronoi2d = true; // true to show the 2D voronoi edges vorv->debug_dataCoords = true; // true to show coordinate spheres //float length_scale = 4.0f / std::sqrt (n_points); - float length_scale = 0.1f; +#if 0 + // rectangular + float length_scale = 12.0f; + vorv->rectangular_domain = true; +#else + // polygonal + float length_scale = 1.0f; + vorv->rectangular_domain = false; +#endif std::cout << "Setting border_width to length scale " << length_scale << std::endl; vorv->border_width = length_scale; + vorv->cm.setType (cmap_t); vorv->setDataCoords (&points); vorv->setScalarData (&data); diff --git a/mplot/VisualDataModel.h b/mplot/VisualDataModel.h index 6bc2c025..72ceadd0 100644 --- a/mplot/VisualDataModel.h +++ b/mplot/VisualDataModel.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -141,6 +142,8 @@ namespace mplot this->reinit(); } + sm::vec coordsCentroid() const { return sm::algo::centroid (*this->dataCoords); } + //! An overridable function to set the colour of rect ri std::array setColour (uint64_t ri) { diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index d9cc24ed..5b108a7c 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -73,6 +73,8 @@ namespace mplot this->setupScaling(); + std::cout << "Data coords centroid is " << this->coordsCentroid() << std::endl;; + sm::quaternion rq; if (this->data_z_direction != sm::vec<>::uz()) { // Find the rotation between data_z_direction and uz @@ -94,6 +96,23 @@ namespace mplot vorman.border_width = this->border_width; if (this->rectangular_domain == false) { +#if 1 + sm::vec<> cent = this->coordsCentroid(); + sm::vec cent2 = {cent[0], cent[1]}; + sm::vvec lengths (this->dcoords_ptr->size(), 0.0f); + for (unsigned int i = 0; i < this->dcoords_ptr->size(); ++i) { + sm::vec c = (*this->dcoords_ptr)[i].less_one_dim() - cent2; + lengths[i] = c.length(); + } + float max_len = lengths.max(); + // Points MUST be in anti-clockwise order!!!!! + this->boundary.resize (this->num_boundary_points, cent2.plus_one_dim()); + float l = max_len + this->border_width; + for (unsigned int i = 0; i < this->num_boundary_points; ++i) { + float ang = i * sm::mathconst::two_pi / this->num_boundary_points; + this->boundary[i] += { l * std::cos (ang), l * std::sin (ang) }; + } +#else // Then we make up a perimeter to be our boundary sm::vvec max_lengths; max_lengths.resize (this->num_boundary_points, 0.0f); @@ -114,6 +133,7 @@ namespace mplot float ang = i * sm::mathconst::two_pi / this->num_boundary_points; this->boundary[i] = { l * std::cos (ang), l * std::sin (ang) }; } +#endif } for (auto b : this->boundary) { std::cout << "Boundary point: " << b << std::endl; @@ -363,6 +383,16 @@ namespace mplot for (unsigned int i = 0; i < ncoords; ++i) { this->computeSphere ((*this->dataCoords)[i] * this->zoom, mplot::colour::black, this->dataCoord_sphere_size); } + bool first = true; + for (auto b : this->boundary) { + std::cout << "Debug boundary point at " << b << std::endl; + if (first) { + this->computeSphere (b * this->zoom, mplot::colour::crimson, 4.0f * this->dataCoord_sphere_size); + first = false; + } else { + this->computeSphere (b * this->zoom, mplot::colour::dodgerblue2, 3.0f * this->dataCoord_sphere_size); + } + } } } @@ -457,8 +487,6 @@ namespace mplot bool debug_dataCoords = false; //! The size of the black spheres are dataCoord locations float dataCoord_sphere_size = 0.008f; - - //! What direction should be considered 'z' when converting the data into a voronoi diagram? //! The data values will be rotated before the Voronoi pass, then rotated back. sm::vec data_z_direction = sm::vec<>::uz(); @@ -467,6 +495,10 @@ namespace mplot //! datacoordinate ranges. This defaults to epsilon to give the best possible //! surface with a rectangular grid. float border_width = std::numeric_limits::epsilon(); + // Create a rectangular domain or use a smoother boundary? + bool rectangular_domain = false; + // When making the boundary, how many points? Or use some f (dcoords.size())? + unsigned int num_boundary_points = 12; // Do we add index labels? bool labelIndices = false; @@ -504,10 +536,6 @@ namespace mplot //! Record the data index for each Voronoi cell index sm::vvec site_indices; unsigned int triangle_count_sum = 0; - // Create a rectangular domain or use a smoother boundary? - bool rectangular_domain = false; - // When making the boundary, how many points? Or use some f (dcoords.size())? - unsigned int num_boundary_points = 12; // Polygon domain coordinates std::vector> boundary; //! Internally owned version of dataCoords after rotation diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 7dca396a..1770fd50 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -22,6 +22,7 @@ #include #include #include +#include #ifndef JCV_EDGE_INTERSECT_THRESHOLD // Fix for Issue #40 @@ -538,6 +539,7 @@ namespace jcv // see edge_create static int boxshape_clip (const clipper* clipper, edge* e) { + std::cout << "boxshape clip edge " << e->pos[0] << "--" << e->pos[1] << "..." << std::endl; T pxmin = clipper->min[0]; T pxmax = clipper->max[0]; T pymin = clipper->min[1]; @@ -565,11 +567,11 @@ namespace jcv if (y2 < pymin) { y2 = pymin; } x2 = (e->c) - (e->b) * y2; // Never occurs according to lcov - if (((x1 > pxmax) & (x2 > pxmax)) | ((x1 < pxmin) & (x2 < pxmin))) - { - std::cout << "Yes it fucking does" << std::endl; - return 0; - } + //if (((x1 > pxmax) & (x2 > pxmax)) | ((x1 < pxmin) & (x2 < pxmin))) + //{ + // std::cout << "Yes it does" << std::endl; + // return 0; + //} if (x1 > pxmax) { x1 = pxmax; y1 = (e->c - x1) / e->b; @@ -596,10 +598,10 @@ namespace jcv if (x2 < pxmin) { x2 = pxmin; } y2 = e->c - e->a * x2; // Never occurs according to lcov - if (((y1 > pymax) & (y2 > pymax)) | ((y1 < pymin) & (y2 < pymin))) { - std::cout << "Yes it fucking does" << std::endl; - return 0; - } + //if (((y1 > pymax) & (y2 > pymax)) | ((y1 < pymin) & (y2 < pymin))) { + // std::cout << "Yes it does" << std::endl; + // return 0; + //} if (y1 > pymax) { y1 = pymax; x1 = (e->c - y1) / e->a; @@ -621,6 +623,8 @@ namespace jcv e->pos[1][0] = x2; e->pos[1][1] = y2; + std::cout << "After boxshape_clip, e is " << e->pos[0] << "--" << e->pos[1] << std::endl; + // If the two points are equal, the result is invalid return (x1 == x2 && y1 == y2) ? 0 : 1; } @@ -629,7 +633,6 @@ namespace jcv // see edge_create static int edge_clipline (context_internal* internal, edge* e) { - std::cout << "edge_clipline calling clip_fn...\n"; return internal->clipper_.clip_fn (&internal->clipper_, e); } @@ -929,7 +932,7 @@ namespace jcv pq_push (internal->eventqueue, edge2); } - _site->cout_edges(); + // _site->cout_edges(); // seems to output no edges? } // https://cp-algorithms.com/geometry/oriented-triangle-area.html @@ -980,20 +983,20 @@ namespace jcv static void finishline (context_internal* internal, edge* e) { -#if 1 // Here's something that needs to be dealt with: +#if 0 // Here's something that needs to be dealt with: + std::cout << "finishline for edge " << e->pos[0] << "--" << e->pos[1] << std::endl; + bool etoi = false; // These just mean 'edge goes to infinity' if (e->pos[1][0] == std::numeric_limits::lowest() -// || e->pos[1][1] == std::numeric_limits::lowest() -// || e->pos[0][0] == std::numeric_limits::lowest() -// || e->pos[0][1] == std::numeric_limits::lowest() + || e->pos[1][1] == std::numeric_limits::lowest() + || e->pos[0][0] == std::numeric_limits::lowest() + || e->pos[0][1] == std::numeric_limits::lowest() ) { std::cout << "edge to infinity case\n"; + etoi = true; } #endif - if (!edge_clipline (internal, e)) { - std::cout << "edge_clipline returned false. Returning now\n"; - return; - } + if (!edge_clipline (internal, e)) { return; } // Make sure the graph edges are CCW int flip = determinant (&e->sites[0]->p, &e->pos[0], &e->pos[1]) > T{0} ? 0 : 1; @@ -1006,6 +1009,7 @@ namespace jcv ge->pos[flip] = e->pos[i]; ge->pos[1-flip] = e->pos[1-i]; ge->angle = calc_sort_metric(e->sites[i], ge); + sortedges_insert (e->sites[i], ge); } } @@ -1535,7 +1539,12 @@ namespace jcv static int ray_intersect_polygon (const clipper* clipper, const point p0, const point p1, - T* out_t0, T* out_t1) +#if 0 + T* out_t0, T* out_t1 +#else + point* out +#endif + ) { auto polygon = reinterpret_cast>*>(clipper->ctx); int num_points = polygon->size(); @@ -1544,9 +1553,32 @@ namespace jcv T t1 = T{1}; point dir = p1 - p0; +#if 1 + // First wind to find out if p0 or p1 is inside clipper's boundary? + for (int i = 0; i < num_points; ++i) { point v0 = (*polygon)[i]; - point v1 = (*polygon)[(i+1)%num_points]; + point v1 = (*polygon)[(i + 1) % num_points]; + + // find crossing point of v0,v1 and p0,p1 + std::bitset<2> isect = sm::geometry::segments_intersect (v0, v1, p0, p1); + if (isect.test(0) == true) { + // lines intersect. Find intersection point + sm::vec cp = sm::geometry::crossing_point (v0, v1, p0, p1); + *out = { cp[0], cp[1], T{0} }; + } else { + if (isect.test(1) == true) { + // lines co-linear + } + } + } +#else + // Need to re-write this loop, I reckon. + for (int i = 0; i < num_points; ++i) { + point v0 = (*polygon)[i]; + point v1 = (*polygon)[(i + 1) % num_points]; + //old code + std::cout << " v = [" << v0.str_comma_separated() << "; " << v1.str_comma_separated() << "]; plot (v(:,1),v(:,2), 'o-r');" << std::endl; point n = {}; n[0] = v1.y() - v0.y(); n[1] = -(v1.x() - v0.x()); @@ -1570,36 +1602,45 @@ namespace jcv } } + std::cout << "returning t0 = " << t0 << ", t1 = " << t1 << std::endl; *out_t0 = t0; *out_t1 = t1; +#endif return 1; } static int polygon_clip (const clipper* clipper, edge* e) { - std::cout << __func__ << " called for edge e from " << e->pos[0] << " to " << e->pos[1] << std::endl; + std::cout << std::endl << __func__ << " called for edge e from " << e->pos[0] << " to " << e->pos[1] << std::endl; // Using the box clipper to get a finite line segment int result = manager::boxshape_clip (clipper, e); if (!result) { return 0; } + // The rest of this function isn't doing the additional clipping necessary point p0 = e->pos[0]; point p1 = e->pos[1]; T t0 = T{0}; T t1 = T{0}; - result = ray_intersect_polygon (clipper, p0, p1, &t0, &t1); + point out = {}; + std::cout << __func__ << ": ray_intersect_polygon for " << p0 << " to " << p1 << std::endl; + result = ray_intersect_polygon (clipper, p0, p1, &out); // &t0, &t1); + std::cout << __func__ << ": ray_intersect_polygon returned " << result << std::endl; if (!result) { e->pos[0] = e->pos[1]; return 0; } + std::cout << "set e->pos[0] to mix with t0 = " << t0 << std::endl; e->pos[0] = mix (p0, p1, t0); + std::cout << "set e->pos[1] to mix with t1 = " << t1 << std::endl; e->pos[1] = mix (p0, p1, t1); + std::cout << "e->pos[0] = " << e->pos[0] << ", " << "e->pos[1] = " << e->pos[1] << "\n"; if (e->pos[1][1] == 11) { std::cout << "Yikes e->pos[1] = " << e->pos[1] << "\n"; - std::cout << "mix (" << p0 << ", " << p1 << "," << t1 << ") was the culprit\n"; + //std::cout << "mix (" << p0 << ", " << p1 << "," << t1 << ") was the culprit\n"; } return 1; } @@ -1636,7 +1677,7 @@ namespace jcv } #if 1 - if (min_edge == -1) { min_edge = 0; } // hack + if (min_edge == -1) { min_edge = 0; } // hack, to avoid crash in polygon_fill #else assert (min_edge >= 0); #endif From bdb348e23c3f3edb16f7ff82c27a815f05dde1aa Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 12 Jan 2026 14:59:47 +0000 Subject: [PATCH 13/53] More work in progress. Going round in circles? --- examples/voronoi_boundary.cpp | 2 +- maths | 2 +- mplot/VoronoiVisual.h | 49 +++------ mplot/jcvoronoi/jc_voronoi.h | 202 ++++++++++++++++++++++++++-------- 4 files changed, 172 insertions(+), 83 deletions(-) diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp index 5e191973..2de52706 100644 --- a/examples/voronoi_boundary.cpp +++ b/examples/voronoi_boundary.cpp @@ -48,7 +48,7 @@ int main() vorv->rectangular_domain = true; #else // polygonal - float length_scale = 1.0f; + float length_scale = 0.5f; vorv->rectangular_domain = false; #endif std::cout << "Setting border_width to length scale " << length_scale << std::endl; diff --git a/maths b/maths index db69cc49..fb3e8abf 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit db69cc490c54c267c99f1220819b485566e03f14 +Subproject commit fb3e8abf94b3d17b64033d217ef94b13ba60d211 diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 5b108a7c..10fc58ab 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -75,29 +75,32 @@ namespace mplot std::cout << "Data coords centroid is " << this->coordsCentroid() << std::endl;; - sm::quaternion rq; + sm::quaternion rq; if (this->data_z_direction != sm::vec<>::uz()) { // Find the rotation between data_z_direction and uz this->dcoords.resize (ncoords); - sm::vec r_axis = this->data_z_direction.cross (sm::vec<>::uz()); + sm::vec r_axis = this->data_z_direction.cross (sm::vec<>::uz()).template as(); r_axis.renormalize(); - float r_angle = this->data_z_direction.angle (sm::vec<>::uz(), r_axis); + F r_angle = static_cast(this->data_z_direction.angle (sm::vec<>::uz(), r_axis)); rq.rotate(r_axis, r_angle); for (size_t i = 0; i < ncoords; ++i) { - this->dcoords[i] = rq * (*this->dataCoords)[i]; + this->dcoords[i] = rq * (*this->dataCoords)[i].template as(); } this->dcoords_ptr = &this->dcoords; } else { - this->dcoords_ptr = this->dataCoords; + this->dcoords.resize (ncoords); + for (size_t i = 0; i < ncoords; ++i) { + this->dcoords[i] = (*this->dataCoords)[i].template as(); + } + this->dcoords_ptr = &this->dcoords; } // Generate the 2D Voronoi diagram - jcv::manager vorman; + jcv::manager vorman; vorman.border_width = this->border_width; if (this->rectangular_domain == false) { -#if 1 - sm::vec<> cent = this->coordsCentroid(); + sm::vec cent = this->coordsCentroid(); sm::vec cent2 = {cent[0], cent[1]}; sm::vvec lengths (this->dcoords_ptr->size(), 0.0f); for (unsigned int i = 0; i < this->dcoords_ptr->size(); ++i) { @@ -112,28 +115,6 @@ namespace mplot float ang = i * sm::mathconst::two_pi / this->num_boundary_points; this->boundary[i] += { l * std::cos (ang), l * std::sin (ang) }; } -#else - // Then we make up a perimeter to be our boundary - sm::vvec max_lengths; - max_lengths.resize (this->num_boundary_points, 0.0f); - for (unsigned int i = 0; i < this->dcoords_ptr->size(); ++i) { - sm::vec c = (*this->dcoords_ptr)[i].less_one_dim(); - float a = c.angle(); - sm::algo::zero_to_twopi (a); - a *= (this->num_boundary_points / sm::mathconst::two_pi); - int ai = std::floor (a); - if (static_cast(ai) == this->num_boundary_points) { ai = 0; } - max_lengths[ai] = c.length() > max_lengths[ai] ? c.length() : max_lengths[ai]; - } - - // Points MUST be in anti-clockwise order!!!!! - this->boundary.resize (this->num_boundary_points); - for (unsigned int i = 0; i < this->num_boundary_points; ++i) { - float l = max_lengths[i] + this->border_width; - float ang = i * sm::mathconst::two_pi / this->num_boundary_points; - this->boundary[i] = { l * std::cos (ang), l * std::sin (ang) }; - } -#endif } for (auto b : this->boundary) { std::cout << "Boundary point: " << b << std::endl; @@ -538,10 +519,10 @@ namespace mplot unsigned int triangle_count_sum = 0; // Polygon domain coordinates std::vector> boundary; - //! Internally owned version of dataCoords after rotation - std::vector> dcoords; - //! A pointer either to dcoords or this->dataCoords - const std::vector>* dcoords_ptr; + //! Internally owned version of dataCoords after rotation and in F precision + std::vector> dcoords; + //! A pointer to dcoords + const std::vector>* dcoords_ptr; }; } // namespace mplot diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 1770fd50..76eb0367 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -23,6 +23,7 @@ #include #include #include +#include #ifndef JCV_EDGE_INTERSECT_THRESHOLD // Fix for Issue #40 @@ -539,7 +540,8 @@ namespace jcv // see edge_create static int boxshape_clip (const clipper* clipper, edge* e) { - std::cout << "boxshape clip edge " << e->pos[0] << "--" << e->pos[1] << "..." << std::endl; + std::cout << "boxshape clip edge " << e->pos[0] << "--" << e->pos[1] + << " f = " << e->a << "x + " << e->b << "y + " << e->c << "..." << std::endl; T pxmin = clipper->min[0]; T pxmax = clipper->max[0]; T pymin = clipper->min[1]; @@ -981,22 +983,17 @@ namespace jcv } } - static void finishline (context_internal* internal, edge* e) + static int finishline (context_internal* internal, edge* e) { -#if 0 // Here's something that needs to be dealt with: - std::cout << "finishline for edge " << e->pos[0] << "--" << e->pos[1] << std::endl; - bool etoi = false; - // These just mean 'edge goes to infinity' - if (e->pos[1][0] == std::numeric_limits::lowest() - || e->pos[1][1] == std::numeric_limits::lowest() - || e->pos[0][0] == std::numeric_limits::lowest() - || e->pos[0][1] == std::numeric_limits::lowest() - ) { - std::cout << "edge to infinity case\n"; - etoi = true; + int er = 0; + if (!(er = edge_clipline (internal, e))) { + return 0; + } else if (er == 2) { + // remove e + std::cout << "finishline: Remove that edge\n"; + return 1; // 1 means remove } -#endif - if (!edge_clipline (internal, e)) { return; } + // Make sure the graph edges are CCW int flip = determinant (&e->sites[0]->p, &e->pos[0], &e->pos[1]) > T{0} ? 0 : 1; @@ -1012,6 +1009,8 @@ namespace jcv sortedges_insert (e->sites[i], ge); } + + return 0; } @@ -1019,7 +1018,9 @@ namespace jcv { e->pos[direction] = *p; if (!is_valid(&e->pos[1 - direction])) { return; } - finishline (internal, e); + if (finishline (internal, e)) { + std::cout << __func__ << ": finishline returned 1; remove e?" << std::endl; + } } static void create_corner_edge (context_internal* internal, const site* site, graphedge* current, graphedge* gap) @@ -1164,7 +1165,9 @@ namespace jcv static void fillgaps (diagram* diagram) { context_internal* internal = diagram->internal; + if (!internal->clipper_.fill_fn) { return; } + for (int i = 0; i < internal->numsites; ++i) { site* site = &internal->sites[i]; internal->clipper_.fill_fn (&internal->clipper_, internal, site); @@ -1451,9 +1454,17 @@ namespace jcv } for (halfedge* he = internal->beachline_start->right; he != internal->beachline_end; he = he->right) { - finishline (internal, he->edge_); + if (finishline (internal, he->edge_) == 1) { + std::cout << __func__ << ": finishline returned 1; remove he" << std::endl; +#if 0 + he->left->right = he->right; + he->right->left = he->left; + he = he->left; // to go right again +#endif + } } + std::cout << "\n\n\n\nFILLGAPS\n\n"; fillgaps (d); } @@ -1537,42 +1548,90 @@ namespace jcv return result; } +#define NEWRAY 1 static int ray_intersect_polygon (const clipper* clipper, +#ifdef NEWRAY + point& p0, point& p1 +#else const point p0, const point p1, -#if 0 T* out_t0, T* out_t1 -#else - point* out #endif ) { auto polygon = reinterpret_cast>*>(clipper->ctx); int num_points = polygon->size(); - T t0 = T{0}; - T t1 = T{1}; - point dir = p1 - p0; - -#if 1 +#ifdef NEWRAY // First wind to find out if p0 or p1 is inside clipper's boundary? + std::vector> poly2d (num_points); + for (int i = 0; i < num_points; ++i) { + poly2d[i][0] = (*polygon)[i][0]; + poly2d[i][1] = (*polygon)[i][1]; + //std::cout << "Boundary point " << poly2d[i] << std::endl; + } + sm::winder w (poly2d); + int w_p0 = w.wind (p0.less_one_dim()); + int w_p1 = w.wind (p1.less_one_dim()); + std::cout << "p0 winding number is " << w_p0 << std::endl; + std::cout << "p1 winding number is " << w_p1 << std::endl; + + if (w_p0 == 0 && w_p1 == 0) { + // Both outside means remove this edge. + //std::cout << "Both outside, so probably want to remove this edge?" << std::endl; + return 2; + + } else if (w_p0 != 0 && w_p1 != 0) { + // Both inside + //std::cout << "Both edge points are inside boundary, no changes needed" << std::endl; + return 1; + } + [[maybe_unused]] bool changed_p = false; for (int i = 0; i < num_points; ++i) { - point v0 = (*polygon)[i]; - point v1 = (*polygon)[(i + 1) % num_points]; + sm::vec v0 = (*polygon)[i].less_one_dim(); + sm::vec v1 = (*polygon)[(i + 1) % num_points].less_one_dim(); // find crossing point of v0,v1 and p0,p1 - std::bitset<2> isect = sm::geometry::segments_intersect (v0, v1, p0, p1); + std::bitset<2> isect = sm::geometry::segments_intersect (v0, v1, p0.less_one_dim(), p1.less_one_dim()); if (isect.test(0) == true) { + if (isect.test(1) == true) { + // lines co-linear. This is always an error? + std::cout << "Return 0 as isect.test(0)==isect.test(1) == true\n"; + return 0; + } // lines intersect. Find intersection point - sm::vec cp = sm::geometry::crossing_point (v0, v1, p0, p1); - *out = { cp[0], cp[1], T{0} }; + sm::vec cp = sm::geometry::crossing_point (v0, v1, p0.less_one_dim(), p1.less_one_dim()); + + if (w_p0 != 0) { // p0 inside, p1 outside + std::cout << "Updating p1 from " << p1 << " "; + p1[0] = cp[0]; + p1[1] = cp[1]; + std::cout << " to " << p1 << std::endl; + changed_p = true; + } else if (w_p1 != 0) { + std::cout << "Updating p0 from " << p0 << " "; + p0[0] = cp[0]; + p0[1] = cp[1]; + std::cout << " to " << p0 << std::endl; + changed_p = true; + } else { + std::cout << "Neither p0 nor p1 were inside the polygon?\n"; + return 0; + } } else { if (isect.test(1) == true) { - // lines co-linear - } + // lines co-linear. This is always an error? + std::cout << "Return 0 as isect.test(1) == true\n"; + return 0; + } // else no crossing point with this section. } } + // return changed_p == true ? 1 : 0; + return 1; #else + T t0 = T{0}; + T t1 = T{1}; + point dir = p1 - p0; // Need to re-write this loop, I reckon. for (int i = 0; i < num_points; ++i) { point v0 = (*polygon)[i]; @@ -1605,8 +1664,8 @@ namespace jcv std::cout << "returning t0 = " << t0 << ", t1 = " << t1 << std::endl; *out_t0 = t0; *out_t1 = t1; -#endif return 1; +#endif } static int polygon_clip (const clipper* clipper, edge* e) @@ -1615,43 +1674,82 @@ namespace jcv // Using the box clipper to get a finite line segment int result = manager::boxshape_clip (clipper, e); - if (!result) { return 0; } + if (!result) { + std::cout << "Return 0 as boxshape_clip returned 0\n"; // doesn't happen in my code + return 0; + } + + // Return here for sanity check + //return 1; + + if (result == 2) { + std::cout << "Remove edge " << e->pos[0] << "--" << e->pos[1] << std::endl; + return result; + } // The rest of this function isn't doing the additional clipping necessary point p0 = e->pos[0]; point p1 = e->pos[1]; - T t0 = T{0}; - T t1 = T{0}; - point out = {}; + [[maybe_unused]] T t0 = T{0}; + [[maybe_unused]] T t1 = T{0}; std::cout << __func__ << ": ray_intersect_polygon for " << p0 << " to " << p1 << std::endl; - result = ray_intersect_polygon (clipper, p0, p1, &out); // &t0, &t1); +#ifdef NEWRAY + result = ray_intersect_polygon (clipper, p0, p1); // or p0, p1 could be the output? +#else + result = ray_intersect_polygon (clipper, p0, p1, &t0, &t1); +#endif std::cout << __func__ << ": ray_intersect_polygon returned " << result << std::endl; +#ifdef NEWRAY + if (result == 2) { + // Remove edge e? + return result; + } +#endif if (!result) { e->pos[0] = e->pos[1]; return 0; } +#ifdef NEWRAY + e->pos[0] = p0; + e->pos[1] = p1; +#else std::cout << "set e->pos[0] to mix with t0 = " << t0 << std::endl; e->pos[0] = mix (p0, p1, t0); std::cout << "set e->pos[1] to mix with t1 = " << t1 << std::endl; e->pos[1] = mix (p0, p1, t1); +#endif + std::cout << "e->pos[0] = " << e->pos[0] << ", " << "e->pos[1] = " << e->pos[1] << "\n"; - if (e->pos[1][1] == 11) { - std::cout << "Yikes e->pos[1] = " << e->pos[1] << "\n"; - //std::cout << "mix (" << p0 << ", " << p1 << "," << t1 << ") was the culprit\n"; - } return 1; } // Find the edge which the point p sits on static int find_polygon_edge (const clipper* clipper, point p) { + std::cout << __func__ << ": called for point " << p << std::endl; + if (std::isnan(p[2])) { + // Replace nan. Whence nan? + p[2] = T{0}; + } + auto polygon = reinterpret_cast>*>(clipper->ctx); int min_edge = -1; T min_dist = std::numeric_limits::max(); int num_points = polygon->size(); + + // First wind to find out if p0 or p1 is inside clipper's boundary? + std::vector> poly2d (num_points); + for (int i = 0; i < num_points; ++i) { + poly2d[i][0] = (*polygon)[i][0]; + poly2d[i][1] = (*polygon)[i][1]; + } + sm::winder w (poly2d); + int w_p = w.wind (p.less_one_dim()); + std::cout << "p winding number is " << w_p << std::endl; + for (int i = 0; i < num_points; ++i) { point p0 = (*polygon)[i]; if (p == p0) { return i; } @@ -1676,17 +1774,20 @@ namespace jcv } } -#if 1 +#if 0 if (min_edge == -1) { min_edge = 0; } // hack, to avoid crash in polygon_fill #else assert (min_edge >= 0); #endif + std::cout << "Edge " << min_edge << std::endl; return min_edge; } static void polygon_fill (const clipper* clipper, context_internal* allocator, site* site) { + std::cout << __func__ << " called for site " << site->index << "@" << site->p << std::endl; + // They're sorted CCW, so if the current->pos[1] != next->pos[0], then we have a gap auto polygon = reinterpret_cast>*>(clipper->ctx); int num_points = polygon->size(); @@ -1734,6 +1835,8 @@ namespace jcv next = site->edges; } + constexpr int loopcount_thresh = 20; + int loopcount = 0; while (curr_graphedge && next) { if (!(curr_graphedge->pos[1] == next->pos[0])) { @@ -1744,14 +1847,11 @@ namespace jcv graphedge* gap = alloc_graphedge (allocator); gap->pos[0] = curr_graphedge->pos[1]; - if (gap->pos[1][1] == 11) { std::cout << " bleurgh 3\n"; } if (polygon_edge1 != polygon_edge2) { gap->pos[1] = (*polygon)[(polygon_edge1 + 1) % num_points]; - if (gap->pos[1][1] == 11) { std::cout << " bleurgh 4\n"; } } else { gap->pos[1] = next->pos[0]; - if (gap->pos[1][1] == 11) { std::cout << " bleurgh 5\n"; } } gap->neighbor = 0; @@ -1764,8 +1864,16 @@ namespace jcv curr_graphedge = curr_graphedge->next; if (curr_graphedge) { next = curr_graphedge->next; - if (!next) { next = site->edges; } + if (!next) { + next = site->edges; + } + } + ++loopcount; + if (loopcount >= loopcount_thresh) { + std::cout << "Too many loops. This can be caused by numerical precision errors when using T==float on some sets of points\n"; + throw std::runtime_error ("Kaboom"); } + } } From ed0d6b87ccbe6241b375d8a12966f1f6f495f6be Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 12 Jan 2026 15:20:21 +0000 Subject: [PATCH 14/53] Debug reduction --- mplot/VoronoiVisual.h | 4 -- mplot/jcvoronoi/jc_voronoi.h | 132 +++++++---------------------------- 2 files changed, 27 insertions(+), 109 deletions(-) diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 10fc58ab..0c9d15c9 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -116,9 +116,6 @@ namespace mplot this->boundary[i] += { l * std::cos (ang), l * std::sin (ang) }; } } - for (auto b : this->boundary) { - std::cout << "Boundary point: " << b << std::endl; - } if (this->boundary.size() > 0) { vorman.diagram_generate (*(dcoords_ptr), this->boundary); @@ -366,7 +363,6 @@ namespace mplot } bool first = true; for (auto b : this->boundary) { - std::cout << "Debug boundary point at " << b << std::endl; if (first) { this->computeSphere (b * this->zoom, mplot::colour::crimson, 4.0f * this->dataCoord_sphere_size); first = false; diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 76eb0367..7fbce317 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -540,8 +540,8 @@ namespace jcv // see edge_create static int boxshape_clip (const clipper* clipper, edge* e) { - std::cout << "boxshape clip edge " << e->pos[0] << "--" << e->pos[1] - << " f = " << e->a << "x + " << e->b << "y + " << e->c << "..." << std::endl; + //std::cout << "boxshape clip edge " << e->pos[0] << "--" << e->pos[1] + // << " f = " << e->a << "x + " << e->b << "y + " << e->c << "..." << std::endl; T pxmin = clipper->min[0]; T pxmax = clipper->max[0]; T pymin = clipper->min[1]; @@ -625,7 +625,7 @@ namespace jcv e->pos[1][0] = x2; e->pos[1][1] = y2; - std::cout << "After boxshape_clip, e is " << e->pos[0] << "--" << e->pos[1] << std::endl; + //std::cout << "After boxshape_clip, e is " << e->pos[0] << "--" << e->pos[1] << std::endl; // If the two points are equal, the result is invalid return (x1 == x2 && y1 == y2) ? 0 : 1; @@ -991,7 +991,7 @@ namespace jcv } else if (er == 2) { // remove e std::cout << "finishline: Remove that edge\n"; - return 1; // 1 means remove + return 2; // 2 means remove } @@ -1018,8 +1018,8 @@ namespace jcv { e->pos[direction] = *p; if (!is_valid(&e->pos[1 - direction])) { return; } - if (finishline (internal, e)) { - std::cout << __func__ << ": finishline returned 1; remove e?" << std::endl; + if (finishline (internal, e) == 2) { + std::cout << __func__ << ": finishline returned 2; remove e?" << std::endl; } } @@ -1441,7 +1441,6 @@ namespace jcv lowest_pq_point[0] = he->vertex[0]; lowest_pq_point[1] = he->y; } - std::cout << "lowest_pq_point = " << lowest_pq_point << std::endl; if (site != 0 && (pq_empty(pq) || lessthan (&site->p, &lowest_pq_point))) { site_event (internal, site); @@ -1454,18 +1453,18 @@ namespace jcv } for (halfedge* he = internal->beachline_start->right; he != internal->beachline_end; he = he->right) { - if (finishline (internal, he->edge_) == 1) { - std::cout << __func__ << ": finishline returned 1; remove he" << std::endl; + if (finishline (internal, he->edge_) == 2) { + std::cout << __func__ << ": finishline returned 2; remove he?" << std::endl; #if 0 he->left->right = he->right; he->right->left = he->left; he = he->left; // to go right again #endif - } + } // else, presumably it worked } - std::cout << "\n\n\n\nFILLGAPS\n\n"; - fillgaps (d); + //std::cout << "\n\n\n\nFILLGAPS\n\n"; + //fillgaps (d); } /** @@ -1548,20 +1547,11 @@ namespace jcv return result; } -#define NEWRAY 1 - static int ray_intersect_polygon (const clipper* clipper, -#ifdef NEWRAY - point& p0, point& p1 -#else - const point p0, const point p1, - T* out_t0, T* out_t1 -#endif - ) + static int ray_intersect_polygon (const clipper* clipper, point& p0, point& p1) { auto polygon = reinterpret_cast>*>(clipper->ctx); int num_points = polygon->size(); -#ifdef NEWRAY // First wind to find out if p0 or p1 is inside clipper's boundary? std::vector> poly2d (num_points); for (int i = 0; i < num_points; ++i) { @@ -1572,8 +1562,8 @@ namespace jcv sm::winder w (poly2d); int w_p0 = w.wind (p0.less_one_dim()); int w_p1 = w.wind (p1.less_one_dim()); - std::cout << "p0 winding number is " << w_p0 << std::endl; - std::cout << "p1 winding number is " << w_p1 << std::endl; + //std::cout << "p0 winding number is " << w_p0 << std::endl; + //std::cout << "p1 winding number is " << w_p1 << std::endl; if (w_p0 == 0 && w_p1 == 0) { // Both outside means remove this edge. @@ -1603,19 +1593,19 @@ namespace jcv sm::vec cp = sm::geometry::crossing_point (v0, v1, p0.less_one_dim(), p1.less_one_dim()); if (w_p0 != 0) { // p0 inside, p1 outside - std::cout << "Updating p1 from " << p1 << " "; + //std::cout << "Updating p1 from " << p1 << " "; p1[0] = cp[0]; p1[1] = cp[1]; - std::cout << " to " << p1 << std::endl; + //std::cout << " to " << p1 << std::endl; changed_p = true; } else if (w_p1 != 0) { - std::cout << "Updating p0 from " << p0 << " "; + //std::cout << "Updating p0 from " << p0 << " "; p0[0] = cp[0]; p0[1] = cp[1]; - std::cout << " to " << p0 << std::endl; + //std::cout << " to " << p0 << std::endl; changed_p = true; } else { - std::cout << "Neither p0 nor p1 were inside the polygon?\n"; + //std::cout << "Neither p0 nor p1 were inside the polygon?\n"; return 0; } } else { @@ -1628,49 +1618,11 @@ namespace jcv } // return changed_p == true ? 1 : 0; return 1; -#else - T t0 = T{0}; - T t1 = T{1}; - point dir = p1 - p0; - // Need to re-write this loop, I reckon. - for (int i = 0; i < num_points; ++i) { - point v0 = (*polygon)[i]; - point v1 = (*polygon)[(i + 1) % num_points]; - //old code - std::cout << " v = [" << v0.str_comma_separated() << "; " << v1.str_comma_separated() << "]; plot (v(:,1),v(:,2), 'o-r');" << std::endl; - point n = {}; - n[0] = v1.y() - v0.y(); - n[1] = -(v1.x() - v0.x()); - - point v0p0 = p0 - v0; - - T N = -v0p0.dot (n); - T D = dir.dot (n); - if (std::abs(D) < T{0.0001}) { // parallel to the line - if (N < T{0}) { return 0; } - continue; - } - - T t = N / D; - if (D < T{0}) { // -> entering - t0 = t > t0 ? t : t0; - if (t0 > t1) { return 0; } - } else { // D > 0 -> exiting - t1 = t < t1 ? t : t1; - if (t1 < t0) { return 0; } - } - } - - std::cout << "returning t0 = " << t0 << ", t1 = " << t1 << std::endl; - *out_t0 = t0; - *out_t1 = t1; - return 1; -#endif } static int polygon_clip (const clipper* clipper, edge* e) { - std::cout << std::endl << __func__ << " called for edge e from " << e->pos[0] << " to " << e->pos[1] << std::endl; + // std::cout << std::endl << __func__ << " called for edge e from " << e->pos[0] << " to " << e->pos[1] << std::endl; // Using the box clipper to get a finite line segment int result = manager::boxshape_clip (clipper, e); @@ -1679,8 +1631,8 @@ namespace jcv return 0; } - // Return here for sanity check - //return 1; + // Return here for sanity check on the polygon clipping + // return 1; if (result == 2) { std::cout << "Remove edge " << e->pos[0] << "--" << e->pos[1] << std::endl; @@ -1693,36 +1645,23 @@ namespace jcv [[maybe_unused]] T t0 = T{0}; [[maybe_unused]] T t1 = T{0}; - std::cout << __func__ << ": ray_intersect_polygon for " << p0 << " to " << p1 << std::endl; -#ifdef NEWRAY - result = ray_intersect_polygon (clipper, p0, p1); // or p0, p1 could be the output? -#else - result = ray_intersect_polygon (clipper, p0, p1, &t0, &t1); -#endif - std::cout << __func__ << ": ray_intersect_polygon returned " << result << std::endl; + //std::cout << __func__ << ": ray_intersect_polygon for " << p0 << " to " << p1 << std::endl; + result = ray_intersect_polygon (clipper, p0, p1); -#ifdef NEWRAY if (result == 2) { // Remove edge e? return result; } -#endif + if (!result) { e->pos[0] = e->pos[1]; return 0; } -#ifdef NEWRAY e->pos[0] = p0; e->pos[1] = p1; -#else - std::cout << "set e->pos[0] to mix with t0 = " << t0 << std::endl; - e->pos[0] = mix (p0, p1, t0); - std::cout << "set e->pos[1] to mix with t1 = " << t1 << std::endl; - e->pos[1] = mix (p0, p1, t1); -#endif - std::cout << "e->pos[0] = " << e->pos[0] << ", " << "e->pos[1] = " << e->pos[1] << "\n"; + //std::cout << "e->pos[0] = " << e->pos[0] << ", " << "e->pos[1] = " << e->pos[1] << "\n"; return 1; } @@ -1732,6 +1671,7 @@ namespace jcv std::cout << __func__ << ": called for point " << p << std::endl; if (std::isnan(p[2])) { // Replace nan. Whence nan? + std::cout << "Replacing a nan which can cause this function to fail\n"; p[2] = T{0}; } @@ -1740,16 +1680,6 @@ namespace jcv T min_dist = std::numeric_limits::max(); int num_points = polygon->size(); - // First wind to find out if p0 or p1 is inside clipper's boundary? - std::vector> poly2d (num_points); - for (int i = 0; i < num_points; ++i) { - poly2d[i][0] = (*polygon)[i][0]; - poly2d[i][1] = (*polygon)[i][1]; - } - sm::winder w (poly2d); - int w_p = w.wind (p.less_one_dim()); - std::cout << "p winding number is " << w_p << std::endl; - for (int i = 0; i < num_points; ++i) { point p0 = (*polygon)[i]; if (p == p0) { return i; } @@ -1760,9 +1690,6 @@ namespace jcv T t = vsegment.dot (vpoint) / vsegment.dot (vsegment); - // Comment this line out to see that it's not so much the fill function that's at - // fault, but the construction of the edges (with one edge going out to the - // rectangle boundary limit) if (t < T{0} || t > T{1}) { continue; } point projected = p0 + vsegment * t; @@ -1774,12 +1701,7 @@ namespace jcv } } -#if 0 - if (min_edge == -1) { min_edge = 0; } // hack, to avoid crash in polygon_fill -#else assert (min_edge >= 0); -#endif - std::cout << "Edge " << min_edge << std::endl; return min_edge; } From 831d169a3ffcafda40dc32b0fdd9fa077dfd8385 Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 12 Jan 2026 15:30:21 +0000 Subject: [PATCH 15/53] Avoids crashing in fillgaps --- mplot/jcvoronoi/jc_voronoi.h | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 7fbce317..cdde69d5 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -1463,8 +1463,8 @@ namespace jcv } // else, presumably it worked } - //std::cout << "\n\n\n\nFILLGAPS\n\n"; - //fillgaps (d); + std::cout << "\n\n\nFILLGAPS\n\n"; + fillgaps (d); } /** @@ -1761,10 +1761,16 @@ namespace jcv int loopcount = 0; while (curr_graphedge && next) { - if (!(curr_graphedge->pos[1] == next->pos[0])) { + if (!( + curr_graphedge->pos[1][0] == next->pos[0][0]) + && curr_graphedge->pos[1][1] == next->pos[0][1] + ) { + std::cout << curr_graphedge->pos[1] << " != " << next->pos[0] << ", apparently\n"; + + std::cout << "1 find_polygon_edge for p = curr_graphedge->pos[1] = " << curr_graphedge->pos[1] << std::endl; int polygon_edge1 = find_polygon_edge (clipper, curr_graphedge->pos[1]); - //std::cout << "3 fpe for p = next->pos[0] = " << next->pos[0] << std::endl; + std::cout << "2 find_polygon_edge for p = next->pos[0] = " << next->pos[0] << std::endl; int polygon_edge2 = find_polygon_edge (clipper, next->pos[0]); graphedge* gap = alloc_graphedge (allocator); @@ -1790,11 +1796,7 @@ namespace jcv next = site->edges; } } - ++loopcount; - if (loopcount >= loopcount_thresh) { - std::cout << "Too many loops. This can be caused by numerical precision errors when using T==float on some sets of points\n"; - throw std::runtime_error ("Kaboom"); - } + if (++loopcount >= loopcount_thresh) { throw std::runtime_error ("Kaboom (too many loops)"); } } } From 19a1a82f91d732553a022b6c1613e5114067ff71 Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 12 Jan 2026 15:36:35 +0000 Subject: [PATCH 16/53] Last bit of WIP before a change of scene --- mplot/jcvoronoi/jc_voronoi.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index cdde69d5..1f926c28 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -1716,13 +1716,11 @@ namespace jcv graphedge* curr_graphedge = site->edges; if (!curr_graphedge) { - std::cout << "Entered if (!curr_graphedge) block\n"; graphedge* gap = alloc_graphedge (allocator); gap->neighbor = 0; // Pick the first edge of the polygon (which is also CCW) gap->pos[0] = (*polygon)[0]; gap->pos[1] = (*polygon)[1]; - if (gap->pos[1][1] == 11) { std::cout << " bleurgh 0\n"; } gap->angle = calc_sort_metric (site, gap); gap->next = 0; gap->edge_ = create_gap_edge (allocator, site, gap); @@ -1733,17 +1731,16 @@ namespace jcv graphedge* next = curr_graphedge->next; if (!next) { + std::cout << "ENTERED\n"; graphedge* gap = alloc_graphedge (allocator); int polygon_edge = find_polygon_edge (clipper, curr_graphedge->pos[1]); if (!(curr_graphedge->pos[1] == (*polygon)[(polygon_edge + 1) % num_points])) { gap->pos[0] = curr_graphedge->pos[1]; gap->pos[1] = (*polygon)[(polygon_edge + 1) % num_points]; - if (gap->pos[1][1] == 11) { std::cout << " bleurgh 1\n"; } } else { gap->pos[0] = (*polygon)[(polygon_edge + 1) % num_points]; gap->pos[1] = (*polygon)[(polygon_edge + 2) % num_points]; - if (gap->pos[1][1] == 11) { std::cout << " bleurgh 2\n"; } } gap->neighbor = 0; @@ -1766,19 +1763,21 @@ namespace jcv && curr_graphedge->pos[1][1] == next->pos[0][1] ) { - std::cout << curr_graphedge->pos[1] << " != " << next->pos[0] << ", apparently\n"; + //std::cout << curr_graphedge->pos[1] << " != " << next->pos[0] << ", apparently\n"; - std::cout << "1 find_polygon_edge for p = curr_graphedge->pos[1] = " << curr_graphedge->pos[1] << std::endl; + //std::cout << "1 find_polygon_edge for p = curr_graphedge->pos[1] = " << curr_graphedge->pos[1] << std::endl; int polygon_edge1 = find_polygon_edge (clipper, curr_graphedge->pos[1]); - std::cout << "2 find_polygon_edge for p = next->pos[0] = " << next->pos[0] << std::endl; + //std::cout << "2 find_polygon_edge for p = next->pos[0] = " << next->pos[0] << std::endl; int polygon_edge2 = find_polygon_edge (clipper, next->pos[0]); graphedge* gap = alloc_graphedge (allocator); gap->pos[0] = curr_graphedge->pos[1]; if (polygon_edge1 != polygon_edge2) { + std::cout << "gap->pos[1] is from the polygon (polygon_edge1 != polygon_edge2)\n"; gap->pos[1] = (*polygon)[(polygon_edge1 + 1) % num_points]; } else { + std::cout << "gap->pos[1] is next->pos[0]\n"; gap->pos[1] = next->pos[0]; } @@ -1786,6 +1785,7 @@ namespace jcv gap->angle = calc_sort_metric (site, gap); gap->edge_ = create_gap_edge (allocator, site, gap); gap->next = curr_graphedge->next; + std::cout << "Added a gap\n"; curr_graphedge->next = gap; } From aa1cbb441fe6ce10202102eada0c437b2d0fca16 Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 12 Jan 2026 17:21:16 +0000 Subject: [PATCH 17/53] Debugging progress --- mplot/VoronoiVisual.h | 7 ++++ mplot/jcvoronoi/jc_voronoi.h | 64 +++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 0c9d15c9..42d9a66c 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -370,6 +370,13 @@ namespace mplot this->computeSphere (b * this->zoom, mplot::colour::dodgerblue2, 3.0f * this->dataCoord_sphere_size); } } + for (auto dngl : vorman.danglers) { + this->computeSphere (dngl * this->zoom, mplot::colour::springgreen2, 4.0f * this->dataCoord_sphere_size); + // label? + // Draw an index label... + this->addLabel (dngl.str(), dngl * this->zoom + labelOffset, mplot::TextFeatures(labelSize) ); + + } } } diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 1f926c28..6dcab83e 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -120,19 +121,21 @@ namespace jcv struct clipper { // Tests if a point is inside the final shape - std::function* _clipper, const point p)> test_fn; + std::function* _clipper, const point p)> test_fn; // Given an edge, and the clipper, calculates the e->pos[0] and e->pos[1] // Returns 0 if not successful - std::function* _clipper, edge* e)> clip_fn; + std::function* _clipper, edge* e)> clip_fn; // Given the clipper, the site and the last edge, // closes any gaps in the polygon by adding new edges that follow the bounding shape // The internal context is use when allocating new edges. - std::function* _clipper, + std::function* _clipper, context_internal* allocator, site* s)> fill_fn; point min; // The bounding rect min point max; // The bounding rect max void* ctx; // User defined context function + + sm::vvec>* danglers = nullptr; }; // Second batch of structs @@ -530,7 +533,7 @@ namespace jcv } // CLIPPING - static int boxshape_test (const clipper* clipper, const point p) + static int boxshape_test (/*const*/ clipper* clipper, const point p) { return p[0] >= clipper->min[0] && p[0] <= clipper->max[0] && p[1] >= clipper->min[1] && p[1] <= clipper->max[1]; @@ -538,7 +541,7 @@ namespace jcv // The line equation: ax + by + c = 0 // see edge_create - static int boxshape_clip (const clipper* clipper, edge* e) + static int boxshape_clip (/*const*/ clipper* clipper, edge* e) { //std::cout << "boxshape clip edge " << e->pos[0] << "--" << e->pos[1] // << " f = " << e->a << "x + " << e->b << "y + " << e->c << "..." << std::endl; @@ -1058,7 +1061,7 @@ namespace jcv return edge; } - static void boxshape_fill (const clipper* clipper, context_internal* allocator, site* site) + static void boxshape_fill (/*const*/ clipper* clipper, context_internal* allocator, site* site) { // They're sorted CCW, so if the current->pos[1] != next->pos[0], then we have a gap graphedge* current = site->edges; @@ -1353,7 +1356,7 @@ namespace jcv // This version of diagram_generate allows the client to use a custom allocator static void diagram_generate_useralloc (int num_points, const point* points, - const rect* _rect, const clipper* _clipper, + const rect* _rect, /*const*/ clipper* _clipper, void* userallocctx, FJCVAllocFn allocfn, FJCVFreeFn freefn, diagram* d) { if (d->internal) { diagram_free (d); } @@ -1473,12 +1476,12 @@ namespace jcv * If rect is null, an automatic bounding box is calculated, with an extra padding of 10 units * All points will be culled against the bounding rect, and all edges will be clipped against it. */ - static void diagram_generate (int num_points, const point* points, const rect* rect, const clipper* clipper, diagram* d) + static void diagram_generate (int num_points, const point* points, const rect* rect, /*const*/ clipper* clipper, diagram* d) { diagram_generate_useralloc (num_points, points, rect, clipper, 0, alloc_fn, free_fn, d); } - static void diagram_generate (int num_points, const point* points, const clipper* clipper, diagram* d) + static void diagram_generate (int num_points, const point* points, /*const*/ clipper* clipper, diagram* d) { diagram_generate_useralloc (num_points, points, 0, clipper, 0, alloc_fn, free_fn, d); } @@ -1514,13 +1517,17 @@ namespace jcv polygonclipper.clip_fn = &jcv::manager::polygon_clip; polygonclipper.fill_fn = &jcv::manager::polygon_fill; polygonclipper.ctx = &polygon; + polygonclipper.danglers = &this->danglers; jcv::manager::diagram_generate (ncoords, centres.data(), &polygonclipper, &this->diagram); + std::cout << "After diagram_generate, polygon danglers; " << polygonclipper.danglers->size() << std::endl; + //this->danglers = polygonclipper.danglers; } + sm::vvec> danglers = {}; // debug int diagram_numsites() const { return this->diagram.numsites; } - static int polygon_test (const clipper* clipper, const point p) + static int polygon_test (/*const*/ clipper* clipper, const point p) { auto polygon = reinterpret_cast>*>(clipper->ctx); int num_points = polygon->size(); @@ -1547,7 +1554,7 @@ namespace jcv return result; } - static int ray_intersect_polygon (const clipper* clipper, point& p0, point& p1) + static int ray_intersect_polygon (/*const*/ clipper* clipper, point& p0, point& p1) { auto polygon = reinterpret_cast>*>(clipper->ctx); int num_points = polygon->size(); @@ -1620,7 +1627,7 @@ namespace jcv return 1; } - static int polygon_clip (const clipper* clipper, edge* e) + static int polygon_clip (/*const*/ clipper* clipper, edge* e) { // std::cout << std::endl << __func__ << " called for edge e from " << e->pos[0] << " to " << e->pos[1] << std::endl; @@ -1666,12 +1673,12 @@ namespace jcv } // Find the edge which the point p sits on - static int find_polygon_edge (const clipper* clipper, point p) + static int find_polygon_edge (/*const*/ clipper* clipper, point p) { - std::cout << __func__ << ": called for point " << p << std::endl; + // std::cout << __func__ << ": called for point " << p << std::endl; if (std::isnan(p[2])) { // Replace nan. Whence nan? - std::cout << "Replacing a nan which can cause this function to fail\n"; + // std::cout << "Replacing a nan which can cause this function to fail\n"; p[2] = T{0}; } @@ -1684,7 +1691,7 @@ namespace jcv point p0 = (*polygon)[i]; if (p == p0) { return i; } - point p1 = (*polygon)[(i+1)%num_points]; + point p1 = (*polygon)[(i + 1) % num_points]; point vsegment = p1 - p0; point vpoint = p - p0; @@ -1705,10 +1712,10 @@ namespace jcv return min_edge; } - static void polygon_fill (const clipper* clipper, + static void polygon_fill (/*const*/ clipper* clipper, context_internal* allocator, site* site) { - std::cout << __func__ << " called for site " << site->index << "@" << site->p << std::endl; + //std::cout << __func__ << " called for site " << site->index << "@" << site->p << std::endl; // They're sorted CCW, so if the current->pos[1] != next->pos[0], then we have a gap auto polygon = reinterpret_cast>*>(clipper->ctx); @@ -1758,18 +1765,25 @@ namespace jcv int loopcount = 0; while (curr_graphedge && next) { + clipper->danglers->push_back (curr_graphedge->pos[0]); if (!( curr_graphedge->pos[1][0] == next->pos[0][0]) && curr_graphedge->pos[1][1] == next->pos[0][1] ) { - //std::cout << curr_graphedge->pos[1] << " != " << next->pos[0] << ", apparently\n"; + std::cout << curr_graphedge->pos[0].str_comma_separated() << ";" << std::endl; + // Add to memory so that I can debug! + //clipper->danglers->push_back (curr_graphedge->pos[0]); - //std::cout << "1 find_polygon_edge for p = curr_graphedge->pos[1] = " << curr_graphedge->pos[1] << std::endl; int polygon_edge1 = find_polygon_edge (clipper, curr_graphedge->pos[1]); - //std::cout << "2 find_polygon_edge for p = next->pos[0] = " << next->pos[0] << std::endl; int polygon_edge2 = find_polygon_edge (clipper, next->pos[0]); + if (polygon_edge1 != polygon_edge2) { + std::cout << "1 find_polygon_edge for p = curr_graphedge->pos[1] = " << curr_graphedge->pos[1] << " got edge " << polygon_edge1 << std::endl; + std::cout << "2 find_polygon_edge for p = next->pos[0] = " << next->pos[0] << " got edge " << polygon_edge2 << std::endl; + } + + // Creating a new edge... graphedge* gap = alloc_graphedge (allocator); gap->pos[0] = curr_graphedge->pos[1]; @@ -1785,18 +1799,16 @@ namespace jcv gap->angle = calc_sort_metric (site, gap); gap->edge_ = create_gap_edge (allocator, site, gap); gap->next = curr_graphedge->next; - std::cout << "Added a gap\n"; curr_graphedge->next = gap; } curr_graphedge = curr_graphedge->next; if (curr_graphedge) { next = curr_graphedge->next; - if (!next) { - next = site->edges; - } + if (!next) { next = site->edges; } } - if (++loopcount >= loopcount_thresh) { throw std::runtime_error ("Kaboom (too many loops)"); } + ++loopcount; + if (loopcount >= loopcount_thresh) { throw std::runtime_error ("Kaboom (too many loops)"); } } } From da7d3a636634a72321918d9423d0ff7199aa97bd Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 12 Jan 2026 23:25:55 +0000 Subject: [PATCH 18/53] WIP --- mplot/jcvoronoi/jc_voronoi.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 6dcab83e..a8b9f053 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -1778,10 +1778,10 @@ namespace jcv int polygon_edge1 = find_polygon_edge (clipper, curr_graphedge->pos[1]); int polygon_edge2 = find_polygon_edge (clipper, next->pos[0]); - if (polygon_edge1 != polygon_edge2) { - std::cout << "1 find_polygon_edge for p = curr_graphedge->pos[1] = " << curr_graphedge->pos[1] << " got edge " << polygon_edge1 << std::endl; - std::cout << "2 find_polygon_edge for p = next->pos[0] = " << next->pos[0] << " got edge " << polygon_edge2 << std::endl; - } + //if (polygon_edge1 != polygon_edge2) { + std::cout << "1 find_polygon_edge for p = curr_graphedge->pos[1] = " << curr_graphedge->pos[1] << " got edge " << polygon_edge1 << std::endl; + std::cout << "2 find_polygon_edge for p = next->pos[0] = " << next->pos[0] << " got edge " << polygon_edge2 << std::endl; + //} // Creating a new edge... graphedge* gap = alloc_graphedge (allocator); @@ -1792,9 +1792,11 @@ namespace jcv gap->pos[1] = (*polygon)[(polygon_edge1 + 1) % num_points]; } else { std::cout << "gap->pos[1] is next->pos[0]\n"; - gap->pos[1] = next->pos[0]; + gap->pos[1] = next->pos[0]; // always this } + std::cout << "New gap from " << gap->pos[0] << "--" << gap->pos[1] << std::endl; + gap->neighbor = 0; gap->angle = calc_sort_metric (site, gap); gap->edge_ = create_gap_edge (allocator, site, gap); From 5fd1542d830b85e8de20d78ef402205e30947951 Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 13 Jan 2026 12:05:03 +0000 Subject: [PATCH 19/53] Sorted --- examples/voronoi_boundary.cpp | 2 +- mplot/jcvoronoi/jc_voronoi.h | 216 +++++++++++++++++++++++----------- 2 files changed, 147 insertions(+), 71 deletions(-) diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp index 2de52706..c72132dd 100644 --- a/examples/voronoi_boundary.cpp +++ b/examples/voronoi_boundary.cpp @@ -44,7 +44,7 @@ int main() //float length_scale = 4.0f / std::sqrt (n_points); #if 0 // rectangular - float length_scale = 12.0f; + float length_scale = 5.0f; vorv->rectangular_domain = true; #else // polygonal diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index a8b9f053..fe788ed5 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -1010,6 +1010,7 @@ namespace jcv ge->pos[1-flip] = e->pos[1-i]; ge->angle = calc_sort_metric(e->sites[i], ge); + // std::cout << "finishline: Insert graphedge " << ge->pos[0] << "--" << ge->pos[1] << std::endl; sortedges_insert (e->sites[i], ge); } @@ -1064,8 +1065,8 @@ namespace jcv static void boxshape_fill (/*const*/ clipper* clipper, context_internal* allocator, site* site) { // They're sorted CCW, so if the current->pos[1] != next->pos[0], then we have a gap - graphedge* current = site->edges; - if (!current) { + graphedge* curr_graphedge = site->edges; + if (!curr_graphedge) { // No edges, then it should be a single cell assert (allocator->numsites == 1); @@ -1078,19 +1079,19 @@ namespace jcv gap->next = 0; gap->edge_ = create_gap_edge (allocator, site, gap); - current = gap; + curr_graphedge = gap; site->edges = gap; } - graphedge* next = current->next; + graphedge* next = curr_graphedge->next; if (!next) { graphedge* gap = alloc_graphedge (allocator); - create_corner_edge(allocator, site, current, gap); + create_corner_edge(allocator, site, curr_graphedge, gap); gap->edge_ = create_gap_edge (allocator, site, gap); - gap->next = current->next; - current->next = gap; - current = gap; + gap->next = curr_graphedge->next; + curr_graphedge->next = gap; + curr_graphedge = gap; next = site->edges; } @@ -1100,10 +1101,11 @@ namespace jcv constexpr int loopcount_thresh = 1024; int loopcount = 0; - while (current && next && loopcount < loopcount_thresh) { + while (curr_graphedge && next && loopcount < loopcount_thresh) { + + int current_edge_flags = get_edge_flags(&curr_graphedge->pos[1], &clipper->min, &clipper->max); - int current_edge_flags = get_edge_flags(¤t->pos[1], &clipper->min, &clipper->max); - if (current_edge_flags && !equal(¤t->pos[1], &next->pos[0])) { + if (current_edge_flags && !equal(&curr_graphedge->pos[1], &next->pos[0])) { // Cases: // Current and Next on the same border // Current on one border, and Next on another border @@ -1115,13 +1117,13 @@ namespace jcv // Current and Next on the same border graphedge* gap = alloc_graphedge (allocator); gap->neighbor = 0; - gap->pos[0] = current->pos[1]; + gap->pos[0] = curr_graphedge->pos[1]; gap->pos[1] = next->pos[0]; gap->angle = calc_sort_metric (site, gap); gap->edge_ = create_gap_edge (allocator, site, gap); - gap->next = current->next; - current->next = gap; + gap->next = curr_graphedge->next; + curr_graphedge->next = gap; } else { // Current and Next on different borders int corner_flag = edge_flags_to_corner (current_edge_flags); @@ -1141,19 +1143,19 @@ namespace jcv graphedge* gap = alloc_graphedge (allocator); gap->neighbor = 0; - gap->pos[0] = current->pos[1]; + gap->pos[0] = curr_graphedge->pos[1]; gap->pos[1] = corner; gap->angle = calc_sort_metric(site, gap); gap->edge_ = create_gap_edge (allocator, site, gap); - gap->next = current->next; - current->next = gap; + gap->next = curr_graphedge->next; + curr_graphedge->next = gap; } - } + } // else inside border - current = current->next; - if (current) { - next = current->next; + curr_graphedge = curr_graphedge->next; + if (curr_graphedge) { + next = curr_graphedge->next; if (!next) { next = site->edges; } } ++loopcount; @@ -1173,6 +1175,7 @@ namespace jcv for (int i = 0; i < internal->numsites; ++i) { site* site = &internal->sites[i]; + // Call fill fn for each site internal->clipper_.fill_fn (&internal->clipper_, internal, site); } } @@ -1672,44 +1675,61 @@ namespace jcv return 1; } - // Find the edge which the point p sits on - static int find_polygon_edge (/*const*/ clipper* clipper, point p) + static point get_polygon_vertex (/*const*/ clipper* clipper, int vtx_idx) { - // std::cout << __func__ << ": called for point " << p << std::endl; - if (std::isnan(p[2])) { - // Replace nan. Whence nan? - // std::cout << "Replacing a nan which can cause this function to fail\n"; - p[2] = T{0}; - } + point p = {}; + auto polygon = reinterpret_cast>*>(clipper->ctx); + int num_points = polygon->size(); + if (vtx_idx < num_points) { p = (*polygon)[vtx_idx]; } + return p; + } + + // In field 0, return: 0: p NOT on polygon; 1: p on polygon edge section; 2: p on polygon vertex + // In field 1, return the index of the edge or vertex referred to in field 0. + static sm::vec find_polygon_edge (/*const*/ clipper* clipper, const point& _p) + { + point p = _p; + sm::vec info = {}; + if (std::isnan(p[2])) { p[2] = T{0}; } auto polygon = reinterpret_cast>*>(clipper->ctx); - int min_edge = -1; T min_dist = std::numeric_limits::max(); int num_points = polygon->size(); for (int i = 0; i < num_points; ++i) { + point p0 = (*polygon)[i]; - if (p == p0) { return i; } + if (p == p0) { + // ON vertex p0 + info[0] = 2; + info[1] = i; + break; + } point p1 = (*polygon)[(i + 1) % num_points]; + if (p == p1) { + // ON vertex p1 + info[0] = 2; + info[1] = (i + 1) % num_points; + break; + } + + // Now is p on the edge? point vsegment = p1 - p0; point vpoint = p - p0; - T t = vsegment.dot (vpoint) / vsegment.dot (vsegment); - if (t < T{0} || t > T{1}) { continue; } - - point projected = p0 + vsegment * t; + point projected = p0 + vsegment * t; // projection of vpoint onto vsegment T distsq = (p - projected).length_sq(); if (distsq < min_dist) { min_dist = distsq; - min_edge = i; + info[0] = 1; + info[1] = i; } } - assert (min_edge >= 0); - return min_edge; + return info; } static void polygon_fill (/*const*/ clipper* clipper, @@ -1738,10 +1758,11 @@ namespace jcv graphedge* next = curr_graphedge->next; if (!next) { - std::cout << "ENTERED\n"; + std::cout << "Entered !next block\n"; graphedge* gap = alloc_graphedge (allocator); - int polygon_edge = find_polygon_edge (clipper, curr_graphedge->pos[1]); + sm::vec polygon_info = find_polygon_edge (clipper, curr_graphedge->pos[1]); + int polygon_edge = polygon_info[1]; if (!(curr_graphedge->pos[1] == (*polygon)[(polygon_edge + 1) % num_points])) { gap->pos[0] = curr_graphedge->pos[1]; gap->pos[1] = (*polygon)[(polygon_edge + 1) % num_points]; @@ -1766,43 +1787,98 @@ namespace jcv while (curr_graphedge && next) { clipper->danglers->push_back (curr_graphedge->pos[0]); - if (!( - curr_graphedge->pos[1][0] == next->pos[0][0]) - && curr_graphedge->pos[1][1] == next->pos[0][1] - ) { - std::cout << curr_graphedge->pos[0].str_comma_separated() << ";" << std::endl; - // Add to memory so that I can debug! - //clipper->danglers->push_back (curr_graphedge->pos[0]); + // Which edge of the polygon, if any, are we on? current_edge[0] indicates whether + // the point is "not edge/vertex" (value 0); "on an edge" (value 1) or "is a vertex" + // (value 2). current_edge[1] indicates the index of the edge. + sm::vec current_edge = find_polygon_edge (clipper, curr_graphedge->pos[1]); - int polygon_edge1 = find_polygon_edge (clipper, curr_graphedge->pos[1]); - int polygon_edge2 = find_polygon_edge (clipper, next->pos[0]); + if (current_edge[0] > 0 && !equal (&curr_graphedge->pos[1], &next->pos[0])) { - //if (polygon_edge1 != polygon_edge2) { - std::cout << "1 find_polygon_edge for p = curr_graphedge->pos[1] = " << curr_graphedge->pos[1] << " got edge " << polygon_edge1 << std::endl; - std::cout << "2 find_polygon_edge for p = next->pos[0] = " << next->pos[0] << " got edge " << polygon_edge2 << std::endl; - //} + sm::vec next_edge = find_polygon_edge (clipper, next->pos[0]); - // Creating a new edge... - graphedge* gap = alloc_graphedge (allocator); - gap->pos[0] = curr_graphedge->pos[1]; + if (current_edge[0] == 1 && next_edge[0] == 1 && current_edge[1] == next_edge[1]) { - if (polygon_edge1 != polygon_edge2) { - std::cout << "gap->pos[1] is from the polygon (polygon_edge1 != polygon_edge2)\n"; - gap->pos[1] = (*polygon)[(polygon_edge1 + 1) % num_points]; - } else { - std::cout << "gap->pos[1] is next->pos[0]\n"; - gap->pos[1] = next->pos[0]; // always this - } + // Case: Current and Next on the same border - std::cout << "New gap from " << gap->pos[0] << "--" << gap->pos[1] << std::endl; + graphedge* gap = alloc_graphedge (allocator); + gap->neighbor = 0; + gap->pos[0] = curr_graphedge->pos[1]; + gap->pos[1] = next->pos[0]; + gap->angle = calc_sort_metric (site, gap); + gap->edge_ = create_gap_edge (allocator, site, gap); - gap->neighbor = 0; - gap->angle = calc_sort_metric (site, gap); - gap->edge_ = create_gap_edge (allocator, site, gap); - gap->next = curr_graphedge->next; - curr_graphedge->next = gap; - } + gap->next = curr_graphedge->next; + curr_graphedge->next = gap; + + } else if (current_edge[0] == 1 && next_edge[0] == 1 && next_edge[1] != current_edge[1]) { + + // Case: Current and Next on different borders, so we need to find the + // adjacent vertex, following the borders CCW + + int next_vertex = (current_edge[1] + 1) % num_points; + point vtx = get_polygon_vertex (clipper, next_vertex); + + graphedge* gap = alloc_graphedge (allocator); + gap->neighbor = 0; + gap->pos[0] = curr_graphedge->pos[1]; + gap->pos[1] = vtx; + gap->angle = calc_sort_metric (site, gap); + gap->edge_ = create_gap_edge (allocator, site, gap); + + gap->next = curr_graphedge->next; + curr_graphedge->next = gap; + + } else if (current_edge[0] == 1 && next_edge[0] == 2) { + + // Case: Current on border, next on a vertex + point vtx = get_polygon_vertex (clipper, next_edge[1]); + + graphedge* gap = alloc_graphedge (allocator); + gap->neighbor = 0; + gap->pos[0] = curr_graphedge->pos[1]; + gap->pos[1] = vtx; + gap->angle = calc_sort_metric (site, gap); + gap->edge_ = create_gap_edge (allocator, site, gap); + + gap->next = curr_graphedge->next; + curr_graphedge->next = gap; + + } else if (current_edge[0] == 2 && next_edge[0] == 1 && next_edge[1] == current_edge[1]) { + + // Case: Current on vertex, next on *same* border + graphedge* gap = alloc_graphedge (allocator); + gap->neighbor = 0; + gap->pos[0] = curr_graphedge->pos[1]; + gap->pos[1] = next->pos[0]; + gap->angle = calc_sort_metric (site, gap); + gap->edge_ = create_gap_edge (allocator, site, gap); + + gap->next = curr_graphedge->next; + curr_graphedge->next = gap; + + } else if (current_edge[0] == 2 && next_edge[0] == 1 && next_edge[1] != current_edge[1]) { + + // Case: Current on vertex, next on another border, so we need to find the adjacent + // vertex, following the borders CCW + int next_vertex = (current_edge[1] + 1) % num_points; + point vtx = get_polygon_vertex (clipper, next_vertex); + + graphedge* gap = alloc_graphedge (allocator); + gap->neighbor = 0; + gap->pos[0] = curr_graphedge->pos[1]; + gap->pos[1] = vtx; + gap->angle = calc_sort_metric (site, gap); + gap->edge_ = create_gap_edge (allocator, site, gap); + + gap->next = curr_graphedge->next; + curr_graphedge->next = gap; + + } else { + // next not on polygon? + std::cout << "Whoop whoop, unhandled case\n"; + } + } // else current_edge->pos[1] is not on the polygonal boundary curr_graphedge = curr_graphedge->next; if (curr_graphedge) { From f8d85d15607f765d41a3ccec31a88df43f303bf7 Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 13 Jan 2026 12:26:40 +0000 Subject: [PATCH 20/53] Small changes --- mplot/jcvoronoi/jc_voronoi.h | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index fe788ed5..d34572b2 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -1459,17 +1459,9 @@ namespace jcv } for (halfedge* he = internal->beachline_start->right; he != internal->beachline_end; he = he->right) { - if (finishline (internal, he->edge_) == 2) { - std::cout << __func__ << ": finishline returned 2; remove he?" << std::endl; -#if 0 - he->left->right = he->right; - he->right->left = he->left; - he = he->left; // to go right again -#endif - } // else, presumably it worked + finishline (internal, he->edge_); } - std::cout << "\n\n\nFILLGAPS\n\n"; fillgaps (d); } From 82183556534e728edfead3061c1ea7efde78d1ab Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 13 Jan 2026 12:28:09 +0000 Subject: [PATCH 21/53] Removes some visual debug code --- mplot/VoronoiVisual.h | 7 ------- mplot/jcvoronoi/jc_voronoi.h | 38 ++++++++++++++---------------------- 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 42d9a66c..0c9d15c9 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -370,13 +370,6 @@ namespace mplot this->computeSphere (b * this->zoom, mplot::colour::dodgerblue2, 3.0f * this->dataCoord_sphere_size); } } - for (auto dngl : vorman.danglers) { - this->computeSphere (dngl * this->zoom, mplot::colour::springgreen2, 4.0f * this->dataCoord_sphere_size); - // label? - // Draw an index label... - this->addLabel (dngl.str(), dngl * this->zoom + labelOffset, mplot::TextFeatures(labelSize) ); - - } } } diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index d34572b2..0766a034 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -121,21 +121,19 @@ namespace jcv struct clipper { // Tests if a point is inside the final shape - std::function* _clipper, const point p)> test_fn; + std::function* _clipper, const point p)> test_fn; // Given an edge, and the clipper, calculates the e->pos[0] and e->pos[1] // Returns 0 if not successful - std::function* _clipper, edge* e)> clip_fn; + std::function* _clipper, edge* e)> clip_fn; // Given the clipper, the site and the last edge, // closes any gaps in the polygon by adding new edges that follow the bounding shape // The internal context is use when allocating new edges. - std::function* _clipper, + std::function* _clipper, context_internal* allocator, site* s)> fill_fn; point min; // The bounding rect min point max; // The bounding rect max void* ctx; // User defined context function - - sm::vvec>* danglers = nullptr; }; // Second batch of structs @@ -533,7 +531,7 @@ namespace jcv } // CLIPPING - static int boxshape_test (/*const*/ clipper* clipper, const point p) + static int boxshape_test (const clipper* clipper, const point p) { return p[0] >= clipper->min[0] && p[0] <= clipper->max[0] && p[1] >= clipper->min[1] && p[1] <= clipper->max[1]; @@ -541,7 +539,7 @@ namespace jcv // The line equation: ax + by + c = 0 // see edge_create - static int boxshape_clip (/*const*/ clipper* clipper, edge* e) + static int boxshape_clip (const clipper* clipper, edge* e) { //std::cout << "boxshape clip edge " << e->pos[0] << "--" << e->pos[1] // << " f = " << e->a << "x + " << e->b << "y + " << e->c << "..." << std::endl; @@ -1062,7 +1060,7 @@ namespace jcv return edge; } - static void boxshape_fill (/*const*/ clipper* clipper, context_internal* allocator, site* site) + static void boxshape_fill (const clipper* clipper, context_internal* allocator, site* site) { // They're sorted CCW, so if the current->pos[1] != next->pos[0], then we have a gap graphedge* curr_graphedge = site->edges; @@ -1359,7 +1357,7 @@ namespace jcv // This version of diagram_generate allows the client to use a custom allocator static void diagram_generate_useralloc (int num_points, const point* points, - const rect* _rect, /*const*/ clipper* _clipper, + const rect* _rect, const clipper* _clipper, void* userallocctx, FJCVAllocFn allocfn, FJCVFreeFn freefn, diagram* d) { if (d->internal) { diagram_free (d); } @@ -1471,12 +1469,12 @@ namespace jcv * If rect is null, an automatic bounding box is calculated, with an extra padding of 10 units * All points will be culled against the bounding rect, and all edges will be clipped against it. */ - static void diagram_generate (int num_points, const point* points, const rect* rect, /*const*/ clipper* clipper, diagram* d) + static void diagram_generate (int num_points, const point* points, const rect* rect, const clipper* clipper, diagram* d) { diagram_generate_useralloc (num_points, points, rect, clipper, 0, alloc_fn, free_fn, d); } - static void diagram_generate (int num_points, const point* points, /*const*/ clipper* clipper, diagram* d) + static void diagram_generate (int num_points, const point* points, const clipper* clipper, diagram* d) { diagram_generate_useralloc (num_points, points, 0, clipper, 0, alloc_fn, free_fn, d); } @@ -1512,17 +1510,13 @@ namespace jcv polygonclipper.clip_fn = &jcv::manager::polygon_clip; polygonclipper.fill_fn = &jcv::manager::polygon_fill; polygonclipper.ctx = &polygon; - polygonclipper.danglers = &this->danglers; jcv::manager::diagram_generate (ncoords, centres.data(), &polygonclipper, &this->diagram); - std::cout << "After diagram_generate, polygon danglers; " << polygonclipper.danglers->size() << std::endl; - //this->danglers = polygonclipper.danglers; } - sm::vvec> danglers = {}; // debug int diagram_numsites() const { return this->diagram.numsites; } - static int polygon_test (/*const*/ clipper* clipper, const point p) + static int polygon_test (const clipper* clipper, const point p) { auto polygon = reinterpret_cast>*>(clipper->ctx); int num_points = polygon->size(); @@ -1549,7 +1543,7 @@ namespace jcv return result; } - static int ray_intersect_polygon (/*const*/ clipper* clipper, point& p0, point& p1) + static int ray_intersect_polygon (const clipper* clipper, point& p0, point& p1) { auto polygon = reinterpret_cast>*>(clipper->ctx); int num_points = polygon->size(); @@ -1622,7 +1616,7 @@ namespace jcv return 1; } - static int polygon_clip (/*const*/ clipper* clipper, edge* e) + static int polygon_clip (const clipper* clipper, edge* e) { // std::cout << std::endl << __func__ << " called for edge e from " << e->pos[0] << " to " << e->pos[1] << std::endl; @@ -1667,7 +1661,7 @@ namespace jcv return 1; } - static point get_polygon_vertex (/*const*/ clipper* clipper, int vtx_idx) + static point get_polygon_vertex (const clipper* clipper, int vtx_idx) { point p = {}; auto polygon = reinterpret_cast>*>(clipper->ctx); @@ -1678,7 +1672,7 @@ namespace jcv // In field 0, return: 0: p NOT on polygon; 1: p on polygon edge section; 2: p on polygon vertex // In field 1, return the index of the edge or vertex referred to in field 0. - static sm::vec find_polygon_edge (/*const*/ clipper* clipper, const point& _p) + static sm::vec find_polygon_edge (const clipper* clipper, const point& _p) { point p = _p; sm::vec info = {}; @@ -1724,7 +1718,7 @@ namespace jcv return info; } - static void polygon_fill (/*const*/ clipper* clipper, + static void polygon_fill (const clipper* clipper, context_internal* allocator, site* site) { //std::cout << __func__ << " called for site " << site->index << "@" << site->p << std::endl; @@ -1778,8 +1772,6 @@ namespace jcv int loopcount = 0; while (curr_graphedge && next) { - clipper->danglers->push_back (curr_graphedge->pos[0]); - // Which edge of the polygon, if any, are we on? current_edge[0] indicates whether // the point is "not edge/vertex" (value 0); "on an edge" (value 1) or "is a vertex" // (value 2). current_edge[1] indicates the index of the edge. From 90916d790396fc380104bc9fbd5f9d8919708c2f Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 13 Jan 2026 12:36:31 +0000 Subject: [PATCH 22/53] Tidy up... --- mplot/jcvoronoi/jc_voronoi.h | 49 ++++++++---------------------------- 1 file changed, 11 insertions(+), 38 deletions(-) diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 0766a034..dc544369 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -984,18 +984,16 @@ namespace jcv } } - static int finishline (context_internal* internal, edge* e) + static void finishline (context_internal* internal, edge* e) { int er = 0; if (!(er = edge_clipline (internal, e))) { - return 0; + return; } else if (er == 2) { - // remove e - std::cout << "finishline: Remove that edge\n"; - return 2; // 2 means remove + // 2 means the edge 'was removed/not added' + return; } - // Make sure the graph edges are CCW int flip = determinant (&e->sites[0]->p, &e->pos[0], &e->pos[1]) > T{0} ? 0 : 1; @@ -1011,8 +1009,6 @@ namespace jcv // std::cout << "finishline: Insert graphedge " << ge->pos[0] << "--" << ge->pos[1] << std::endl; sortedges_insert (e->sites[i], ge); } - - return 0; } @@ -1020,9 +1016,7 @@ namespace jcv { e->pos[direction] = *p; if (!is_valid(&e->pos[1 - direction])) { return; } - if (finishline (internal, e) == 2) { - std::cout << __func__ << ": finishline returned 2; remove e?" << std::endl; - } + finishline (internal, e); } static void create_corner_edge (context_internal* internal, const site* site, graphedge* current, graphedge* gap) @@ -1555,21 +1549,14 @@ namespace jcv poly2d[i][1] = (*polygon)[i][1]; //std::cout << "Boundary point " << poly2d[i] << std::endl; } - sm::winder w (poly2d); + sm::winder w (poly2d); // May well work with 3d points int w_p0 = w.wind (p0.less_one_dim()); int w_p1 = w.wind (p1.less_one_dim()); - //std::cout << "p0 winding number is " << w_p0 << std::endl; - //std::cout << "p1 winding number is " << w_p1 << std::endl; if (w_p0 == 0 && w_p1 == 0) { - // Both outside means remove this edge. - //std::cout << "Both outside, so probably want to remove this edge?" << std::endl; - return 2; - + return 2; // Both outside means remove this edge. } else if (w_p0 != 0 && w_p1 != 0) { - // Both inside - //std::cout << "Both edge points are inside boundary, no changes needed" << std::endl; - return 1; + return 1; // Both inside } [[maybe_unused]] bool changed_p = false; @@ -1589,30 +1576,25 @@ namespace jcv sm::vec cp = sm::geometry::crossing_point (v0, v1, p0.less_one_dim(), p1.less_one_dim()); if (w_p0 != 0) { // p0 inside, p1 outside - //std::cout << "Updating p1 from " << p1 << " "; p1[0] = cp[0]; p1[1] = cp[1]; - //std::cout << " to " << p1 << std::endl; changed_p = true; } else if (w_p1 != 0) { - //std::cout << "Updating p0 from " << p0 << " "; p0[0] = cp[0]; p0[1] = cp[1]; - //std::cout << " to " << p0 << std::endl; changed_p = true; } else { - //std::cout << "Neither p0 nor p1 were inside the polygon?\n"; + // Neither p0 nor p1 were inside the polygon return 0; } + } else { if (isect.test(1) == true) { // lines co-linear. This is always an error? - std::cout << "Return 0 as isect.test(1) == true\n"; return 0; } // else no crossing point with this section. } } - // return changed_p == true ? 1 : 0; return 1; } @@ -1630,22 +1612,14 @@ namespace jcv // Return here for sanity check on the polygon clipping // return 1; - if (result == 2) { - std::cout << "Remove edge " << e->pos[0] << "--" << e->pos[1] << std::endl; - return result; - } - // The rest of this function isn't doing the additional clipping necessary point p0 = e->pos[0]; point p1 = e->pos[1]; - [[maybe_unused]] T t0 = T{0}; - [[maybe_unused]] T t1 = T{0}; - //std::cout << __func__ << ": ray_intersect_polygon for " << p0 << " to " << p1 << std::endl; result = ray_intersect_polygon (clipper, p0, p1); if (result == 2) { - // Remove edge e? + // Here, we omit to set e->pos[] from p0, p1 and just return 2. return result; } @@ -1657,7 +1631,6 @@ namespace jcv e->pos[0] = p0; e->pos[1] = p1; - //std::cout << "e->pos[0] = " << e->pos[0] << ", " << "e->pos[1] = " << e->pos[1] << "\n"; return 1; } From 8b6c728afae05af0291cc7e4855190ba1ca6adb5 Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 13 Jan 2026 12:44:31 +0000 Subject: [PATCH 23/53] More tidyup --- mplot/VoronoiVisual.h | 2 -- mplot/jcvoronoi/jc_voronoi.h | 36 ++++++++++-------------------------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 0c9d15c9..229c2a51 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -73,8 +73,6 @@ namespace mplot this->setupScaling(); - std::cout << "Data coords centroid is " << this->coordsCentroid() << std::endl;; - sm::quaternion rq; if (this->data_z_direction != sm::vec<>::uz()) { // Find the rotation between data_z_direction and uz diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index dc544369..4a748486 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -1542,16 +1542,10 @@ namespace jcv auto polygon = reinterpret_cast>*>(clipper->ctx); int num_points = polygon->size(); - // First wind to find out if p0 or p1 is inside clipper's boundary? - std::vector> poly2d (num_points); - for (int i = 0; i < num_points; ++i) { - poly2d[i][0] = (*polygon)[i][0]; - poly2d[i][1] = (*polygon)[i][1]; - //std::cout << "Boundary point " << poly2d[i] << std::endl; - } - sm::winder w (poly2d); // May well work with 3d points - int w_p0 = w.wind (p0.less_one_dim()); - int w_p1 = w.wind (p1.less_one_dim()); + // First wind to find out if p0 or p1 is inside clipper's boundary + sm::winder w (*polygon); // winder ignores z + int w_p0 = w.wind (p0); + int w_p1 = w.wind (p1); if (w_p0 == 0 && w_p1 == 0) { return 2; // Both outside means remove this edge. @@ -1559,7 +1553,6 @@ namespace jcv return 1; // Both inside } - [[maybe_unused]] bool changed_p = false; for (int i = 0; i < num_points; ++i) { sm::vec v0 = (*polygon)[i].less_one_dim(); sm::vec v1 = (*polygon)[(i + 1) % num_points].less_one_dim(); @@ -1569,7 +1562,6 @@ namespace jcv if (isect.test(0) == true) { if (isect.test(1) == true) { // lines co-linear. This is always an error? - std::cout << "Return 0 as isect.test(0)==isect.test(1) == true\n"; return 0; } // lines intersect. Find intersection point @@ -1578,11 +1570,9 @@ namespace jcv if (w_p0 != 0) { // p0 inside, p1 outside p1[0] = cp[0]; p1[1] = cp[1]; - changed_p = true; } else if (w_p1 != 0) { p0[0] = cp[0]; p0[1] = cp[1]; - changed_p = true; } else { // Neither p0 nor p1 were inside the polygon return 0; @@ -1600,19 +1590,13 @@ namespace jcv static int polygon_clip (const clipper* clipper, edge* e) { - // std::cout << std::endl << __func__ << " called for edge e from " << e->pos[0] << " to " << e->pos[1] << std::endl; - // Using the box clipper to get a finite line segment int result = manager::boxshape_clip (clipper, e); - if (!result) { - std::cout << "Return 0 as boxshape_clip returned 0\n"; // doesn't happen in my code - return 0; - } + if (!result) { return 0; } - // Return here for sanity check on the polygon clipping + // Return here for a sanity check on the polygon clipping // return 1; - // The rest of this function isn't doing the additional clipping necessary point p0 = e->pos[0]; point p1 = e->pos[1]; @@ -1634,6 +1618,7 @@ namespace jcv return 1; } + // Get the polygon vertex with index vtx_idx from the clipper static point get_polygon_vertex (const clipper* clipper, int vtx_idx) { point p = {}; @@ -1643,6 +1628,8 @@ namespace jcv return p; } + // Find which edge (or vertex) of the polygon in the clipper a point _p lies on + // // In field 0, return: 0: p NOT on polygon; 1: p on polygon edge section; 2: p on polygon vertex // In field 1, return the index of the edge or vertex referred to in field 0. static sm::vec find_polygon_edge (const clipper* clipper, const point& _p) @@ -1694,8 +1681,6 @@ namespace jcv static void polygon_fill (const clipper* clipper, context_internal* allocator, site* site) { - //std::cout << __func__ << " called for site " << site->index << "@" << site->p << std::endl; - // They're sorted CCW, so if the current->pos[1] != next->pos[0], then we have a gap auto polygon = reinterpret_cast>*>(clipper->ctx); int num_points = polygon->size(); @@ -1717,7 +1702,6 @@ namespace jcv graphedge* next = curr_graphedge->next; if (!next) { - std::cout << "Entered !next block\n"; graphedge* gap = alloc_graphedge (allocator); sm::vec polygon_info = find_polygon_edge (clipper, curr_graphedge->pos[1]); @@ -1741,7 +1725,7 @@ namespace jcv next = site->edges; } - constexpr int loopcount_thresh = 20; + constexpr int loopcount_thresh = 1024; int loopcount = 0; while (curr_graphedge && next) { From fce6b991baed04f275fddf22aa9b699b6b1b0684 Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 13 Jan 2026 12:46:17 +0000 Subject: [PATCH 24/53] Use latest maths on main --- maths | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths b/maths index fb3e8abf..75663fc1 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit fb3e8abf94b3d17b64033d217ef94b13ba60d211 +Subproject commit 75663fc11e97fb21c72bc57706b94497b867c8cf From cbf496a2ec3efd445a803f52608717432619035a Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 13 Jan 2026 12:51:00 +0000 Subject: [PATCH 25/53] Tidy up, restore default of rectangular domain in VoronoiVisual for now --- mplot/VoronoiVisual.h | 2 +- mplot/jcvoronoi/jc_voronoi.h | 15 +-------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 229c2a51..799c185b 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -471,7 +471,7 @@ namespace mplot //! surface with a rectangular grid. float border_width = std::numeric_limits::epsilon(); // Create a rectangular domain or use a smoother boundary? - bool rectangular_domain = false; + bool rectangular_domain = true; // When making the boundary, how many points? Or use some f (dcoords.size())? unsigned int num_boundary_points = 12; diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 4a748486..378701e2 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -49,17 +49,6 @@ namespace jcv point p; int index; // Index into the original list of points graphedge* edges; // The half edges owned by the cell -#if 1 - void cout_edges() - { - std::cout << "site " << index << " @ " << p << " has edges:\n"; - graphedge* e = edges; - while (e) { - std::cout << " " << e->str() << std::endl; - e = e->next; - } - } -#endif }; // The coefficients a, b and c are from the general line equation: ax * by + c = 0 @@ -82,7 +71,7 @@ namespace jcv struct site* neighbor; point pos[2]; T angle; -#if 1 +#if 0 std::string str() { std::stringstream ss; @@ -934,8 +923,6 @@ namespace jcv edge2->y = p[1] + point_dist (&_site->p, &p); pq_push (internal->eventqueue, edge2); } - - // _site->cout_edges(); // seems to output no edges? } // https://cp-algorithms.com/geometry/oriented-triangle-area.html From 7dbee4c985be294876c72eb1a5d1de73397986fb Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 13 Jan 2026 14:21:45 +0000 Subject: [PATCH 26/53] Tidying, set up a scheme for different domain shapes on Voronoi diagram --- examples/voronoi_boundary.cpp | 16 ++++------ mplot/VoronoiVisual.h | 56 +++++++++++++++++++---------------- mplot/jcvoronoi/jc_voronoi.h | 20 +++++++++++-- 3 files changed, 53 insertions(+), 39 deletions(-) diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp index c72132dd..0ad04cf2 100644 --- a/examples/voronoi_boundary.cpp +++ b/examples/voronoi_boundary.cpp @@ -14,7 +14,7 @@ #include #include -static constexpr int n_points = 13; +static constexpr int n_points = 80; int main() { @@ -41,19 +41,15 @@ int main() v.bindmodel (vorv); vorv->show_voronoi2d = true; // true to show the 2D voronoi edges vorv->debug_dataCoords = true; // true to show coordinate spheres - //float length_scale = 4.0f / std::sqrt (n_points); + vorv->border_width = 0.5f; #if 0 - // rectangular - float length_scale = 5.0f; - vorv->rectangular_domain = true; + // Use a rectangular domain boundary + // vorv->rectangular_domain = true; // this is default #else // polygonal - float length_scale = 0.5f; - vorv->rectangular_domain = false; + vorv->dom_shape = mplot::VoronoiVisual::domain_shape::circular; + vorv->num_boundary_points = 30; // default 30 #endif - std::cout << "Setting border_width to length scale " << length_scale << std::endl; - vorv->border_width = length_scale; - vorv->cm.setType (cmap_t); vorv->setDataCoords (&points); vorv->setScalarData (&data); diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 799c185b..9b7484a2 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -45,6 +45,15 @@ namespace mplot }; public: + + // The shape of the domain to draw around the points in the Voronoi diagram + enum class domain_shape : uint32_t + { + rectangular, + ellipsoid, + circular + }; + VoronoiVisual (const sm::vec _offset) { this->viewmatrix.translate (_offset); @@ -73,31 +82,30 @@ namespace mplot this->setupScaling(); - sm::quaternion rq; + sm::quaternion rq; if (this->data_z_direction != sm::vec<>::uz()) { // Find the rotation between data_z_direction and uz this->dcoords.resize (ncoords); - sm::vec r_axis = this->data_z_direction.cross (sm::vec<>::uz()).template as(); + sm::vec r_axis = this->data_z_direction.cross (sm::vec<>::uz()); r_axis.renormalize(); - F r_angle = static_cast(this->data_z_direction.angle (sm::vec<>::uz(), r_axis)); + float r_angle = this->data_z_direction.angle (sm::vec<>::uz(), r_axis); rq.rotate(r_axis, r_angle); for (size_t i = 0; i < ncoords; ++i) { - this->dcoords[i] = rq * (*this->dataCoords)[i].template as(); + this->dcoords[i] = rq * (*this->dataCoords)[i]; } this->dcoords_ptr = &this->dcoords; } else { - this->dcoords.resize (ncoords); - for (size_t i = 0; i < ncoords; ++i) { - this->dcoords[i] = (*this->dataCoords)[i].template as(); - } - this->dcoords_ptr = &this->dcoords; + this->dcoords_ptr = this->dataCoords; } // Generate the 2D Voronoi diagram - jcv::manager vorman; + jcv::manager vorman; + if (this->border_width == -1) { this->border_width = 4.0f / std::sqrt (ncoords); } vorman.border_width = this->border_width; - if (this->rectangular_domain == false) { + if (this->dom_shape == domain_shape::ellipsoid) { + throw std::runtime_error ("ellipsoid shape unimplemented"); + } else if (this->dom_shape == domain_shape::circular) { sm::vec cent = this->coordsCentroid(); sm::vec cent2 = {cent[0], cent[1]}; sm::vvec lengths (this->dcoords_ptr->size(), 0.0f); @@ -107,13 +115,14 @@ namespace mplot } float max_len = lengths.max(); // Points MUST be in anti-clockwise order!!!!! + this->boundary.clear(); this->boundary.resize (this->num_boundary_points, cent2.plus_one_dim()); float l = max_len + this->border_width; for (unsigned int i = 0; i < this->num_boundary_points; ++i) { float ang = i * sm::mathconst::two_pi / this->num_boundary_points; this->boundary[i] += { l * std::cos (ang), l * std::sin (ang) }; } - } + } // else rectangular default does not populate this->boundary if (this->boundary.size() > 0) { vorman.diagram_generate (*(dcoords_ptr), this->boundary); @@ -359,14 +368,9 @@ namespace mplot for (unsigned int i = 0; i < ncoords; ++i) { this->computeSphere ((*this->dataCoords)[i] * this->zoom, mplot::colour::black, this->dataCoord_sphere_size); } - bool first = true; + // Polygonal boundary (if used) for (auto b : this->boundary) { - if (first) { - this->computeSphere (b * this->zoom, mplot::colour::crimson, 4.0f * this->dataCoord_sphere_size); - first = false; - } else { - this->computeSphere (b * this->zoom, mplot::colour::dodgerblue2, 3.0f * this->dataCoord_sphere_size); - } + this->computeSphere (b * this->zoom, mplot::colour::dodgerblue2, 3.0f * this->dataCoord_sphere_size); } } } @@ -469,11 +473,11 @@ namespace mplot //! You can add a little extra to the rectangle that is auto-detected from the //! datacoordinate ranges. This defaults to epsilon to give the best possible //! surface with a rectangular grid. - float border_width = std::numeric_limits::epsilon(); - // Create a rectangular domain or use a smoother boundary? - bool rectangular_domain = true; + float border_width = -1; + // Create a rectangular domain or use a smoother (circular) boundary? + domain_shape dom_shape = domain_shape::rectangular; // When making the boundary, how many points? Or use some f (dcoords.size())? - unsigned int num_boundary_points = 12; + unsigned int num_boundary_points = 30; // Do we add index labels? bool labelIndices = false; @@ -513,10 +517,10 @@ namespace mplot unsigned int triangle_count_sum = 0; // Polygon domain coordinates std::vector> boundary; - //! Internally owned version of dataCoords after rotation and in F precision - std::vector> dcoords; + //! Internally owned version of dataCoords after rotation + std::vector> dcoords; //! A pointer to dcoords - const std::vector>* dcoords_ptr; + const std::vector>* dcoords_ptr; }; } // namespace mplot diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 378701e2..bb9d617e 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -1397,8 +1397,7 @@ namespace jcv // In the case of all sites being all on a horizontal or vertical line, the // rect area will be zero, and the diagram generation will most likely fail rect_round(&tmp_rect); - //throw std::runtime_error ("rect_inflate creates the 11 (10+1)"); - rect_inflate(&tmp_rect, 10); + rect_inflate(&tmp_rect, 100); internal->clipper_.min = tmp_rect.min; internal->clipper_.max = tmp_rect.max; @@ -1802,9 +1801,24 @@ namespace jcv gap->next = curr_graphedge->next; curr_graphedge->next = gap; + } else if (next_edge[0] == 0) { + + // Case: Current on vertex or border, but next not on polygon boundary + std::cout << "Current on vertex or border, but next not on polygon boundary\n"; + graphedge* gap = alloc_graphedge (allocator); + gap->neighbor = 0; + gap->pos[0] = curr_graphedge->pos[1]; + gap->pos[1] = next->pos[0]; + gap->angle = calc_sort_metric (site, gap); + gap->edge_ = create_gap_edge (allocator, site, gap); + + gap->next = curr_graphedge->next; + curr_graphedge->next = gap; + } else { // next not on polygon? - std::cout << "Whoop whoop, unhandled case\n"; + std::cout << "Whoop whoop, unhandled case: current_edge = " + << current_edge << " and next_edge = " << next_edge << "\n"; } } // else current_edge->pos[1] is not on the polygonal boundary From 0fca139ec8c90626de82566961962e698b9d0072 Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 13 Jan 2026 16:21:42 +0000 Subject: [PATCH 27/53] Preparing for ellipsoid boundary --- mplot/VoronoiVisual.h | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 9b7484a2..dfba4b8f 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -18,6 +18,8 @@ #include #include +#include + #include #include #include @@ -46,7 +48,9 @@ namespace mplot public: - // The shape of the domain to draw around the points in the Voronoi diagram + // The shape of the domain to draw around the points in the Voronoi diagram Each kind of + // boundary shape is auto-fit to the data points, although the user can set an additional + // border_width. enum class domain_shape : uint32_t { rectangular, @@ -103,9 +107,8 @@ namespace mplot if (this->border_width == -1) { this->border_width = 4.0f / std::sqrt (ncoords); } vorman.border_width = this->border_width; - if (this->dom_shape == domain_shape::ellipsoid) { - throw std::runtime_error ("ellipsoid shape unimplemented"); - } else if (this->dom_shape == domain_shape::circular) { + if (this->dom_shape == domain_shape::ellipsoid || this->dom_shape == domain_shape::circular) { + sm::vec cent = this->coordsCentroid(); sm::vec cent2 = {cent[0], cent[1]}; sm::vvec lengths (this->dcoords_ptr->size(), 0.0f); @@ -117,10 +120,27 @@ namespace mplot // Points MUST be in anti-clockwise order!!!!! this->boundary.clear(); this->boundary.resize (this->num_boundary_points, cent2.plus_one_dim()); - float l = max_len + this->border_width; - for (unsigned int i = 0; i < this->num_boundary_points; ++i) { - float ang = i * sm::mathconst::two_pi / this->num_boundary_points; - this->boundary[i] += { l * std::cos (ang), l * std::sin (ang) }; + + if (this->dom_shape == domain_shape::ellipsoid) { + throw std::runtime_error ("implement me"); +#if 0 + // Extra setup + arma::mat x (dcoords_ptr->size(), 2); + for (unsigned int i = 0; i < dcoords_ptr->size(); ++i) { + x(i, 0) = (*this->dcoords_ptr)[i][0]; + x(i, 1) = (*this->dcoords_ptr)[i][1]; + } + std::cout << "x: " << x << std::endl; + arma::mat coeff = arma::princomp (x); + std::cout << "princ coeff: " << coeff << std::endl; + // From PCA determine ellipsoid axes +#endif + } else { + float l = max_len + this->border_width; + for (unsigned int i = 0; i < this->num_boundary_points; ++i) { + float ang = i * sm::mathconst::two_pi / this->num_boundary_points; + this->boundary[i] += { l * std::cos (ang), l * std::sin (ang) }; + } } } // else rectangular default does not populate this->boundary From 46470df7932fb2e9b82e590fa469b0baba8b10c9 Mon Sep 17 00:00:00 2001 From: Seb James Date: Wed, 14 Jan 2026 12:07:27 +0000 Subject: [PATCH 28/53] Adds elliptic boundary finding --- examples/CMakeLists.txt | 62 ++++++++++++++++------------ examples/ellipse_pca.cpp | 78 +++++++++++++++++++++++++++++++++++ examples/voronoi_boundary.cpp | 6 ++- mplot/VoronoiVisual.h | 45 +++++++++++++------- 4 files changed, 148 insertions(+), 43 deletions(-) create mode 100644 examples/ellipse_pca.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 702c95af..85793b6b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -408,33 +408,6 @@ target_link_libraries(draw_triangles_intersections OpenGL::GL glfw Freetype::Fre add_executable(triangle_intersect triangle_intersect.cpp) target_link_libraries(triangle_intersect OpenGL::GL glfw Freetype::Freetype) -add_executable(voronoi_random voronoi_random.cpp) -target_link_libraries(voronoi_random OpenGL::GL glfw Freetype::Freetype) - -add_executable(voronoi_boundary voronoi_boundary.cpp) -target_link_libraries(voronoi_boundary OpenGL::GL glfw Freetype::Freetype) - -add_executable(voronoi_rectangular voronoi_rectangular.cpp) -target_link_libraries(voronoi_rectangular OpenGL::GL glfw Freetype::Freetype) - -add_executable(voronoi_vectordata voronoi_vectordata.cpp) -target_link_libraries(voronoi_vectordata OpenGL::GL glfw Freetype::Freetype) - -add_executable(voronoi_function voronoi_function.cpp) -target_link_libraries(voronoi_function OpenGL::GL glfw Freetype::Freetype) - -add_executable(voronoi_function_flat voronoi_function_flat.cpp) -target_link_libraries(voronoi_function_flat OpenGL::GL glfw Freetype::Freetype) - -add_executable(voronoi_fixed voronoi_fixed.cpp) -target_link_libraries(voronoi_fixed OpenGL::GL glfw Freetype::Freetype) - -add_executable(voronoi_fixed_xz voronoi_fixed_xz.cpp) -target_link_libraries(voronoi_fixed_xz OpenGL::GL glfw Freetype::Freetype) - -add_executable(voronoi_fixed_nearlyz voronoi_fixed_nearlyz.cpp) -target_link_libraries(voronoi_fixed_nearlyz OpenGL::GL glfw Freetype::Freetype) - add_executable(rectangle rectangle.cpp) target_link_libraries(rectangle OpenGL::GL glfw Freetype::Freetype) @@ -561,3 +534,38 @@ target_link_libraries(show_boundingboxes OpenGL::GL glfw Freetype::Freetype) # if have compound-ray header add_executable(cray_eye cray_eye.cpp) target_link_libraries(cray_eye OpenGL::GL glfw Freetype::Freetype) + +if(ARMADILLO_FOUND) + # use of principle component analysis from arma in this example + add_executable(ellipse_pca ellipse_pca.cpp) + target_link_libraries(ellipse_pca ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) + + # Voronoi code now also uses arma + add_executable(voronoi_random voronoi_random.cpp) + target_link_libraries(voronoi_random ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) + + add_executable(voronoi_boundary voronoi_boundary.cpp) + target_link_libraries(voronoi_boundary ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) + + add_executable(voronoi_rectangular voronoi_rectangular.cpp) + target_link_libraries(voronoi_rectangular ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) + + add_executable(voronoi_vectordata voronoi_vectordata.cpp) + target_link_libraries(voronoi_vectordata ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) + + add_executable(voronoi_function voronoi_function.cpp) + target_link_libraries(voronoi_function ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) + + add_executable(voronoi_function_flat voronoi_function_flat.cpp) + target_link_libraries(voronoi_function_flat ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) + + add_executable(voronoi_fixed voronoi_fixed.cpp) + target_link_libraries(voronoi_fixed ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) + + add_executable(voronoi_fixed_xz voronoi_fixed_xz.cpp) + target_link_libraries(voronoi_fixed_xz ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) + + add_executable(voronoi_fixed_nearlyz voronoi_fixed_nearlyz.cpp) + target_link_libraries(voronoi_fixed_nearlyz ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) + +endif(ARMADILLO_FOUND) diff --git a/examples/ellipse_pca.cpp b/examples/ellipse_pca.cpp new file mode 100644 index 00000000..86bfc904 --- /dev/null +++ b/examples/ellipse_pca.cpp @@ -0,0 +1,78 @@ +/* + * Applying principle component analysis (using arma) to some random data + */ +#include + +#include +#include +#include +#include + +#include +#include + +int main() +{ + constexpr unsigned int n_samp = 100; + + // Create data: Draw some samples from 2D Gaussian and rotate + sm::rand_normal rn1 (0.0f, 2.0f); + sm::rand_normal rn2 (0.0f, 0.5f); + sm::vvec> _x (n_samp, {0}); + sm::mat22 rotn; + rotn.rotate (sm::mathconst::pi_over_4); + for (unsigned int i = 0; i < n_samp; ++i) { + _x[i] = rotn * sm::vec{ rn1.get(), rn2.get() }; + } + + // Graph the data + mplot::Visual v(1024, 768, "Principle component analysis"); + // Create a GraphVisual object (obtaining a unique_ptr to the object) with a spatial offset within the scene of 0,0,0 + auto gv = std::make_unique> (sm::vec{-0.5f,-0.5f,0.0f}); + mplot::DatasetStyle ds (mplot::stylepolicy::markers); + ds.datalabel = std::string("data"); + v.bindmodel (gv); + gv->setlimits (-5, 5, -5, 5); + gv->setdata (_x, ds); + + // Place data in arma::Mat + arma::Mat x(_x.size(), 2); + for (unsigned int i = 0; i < _x.size(); ++i) { + x(i, 0) = _x[i][0]; + x(i, 1) = _x[i][1]; + } + + // Apply arma::princomp + arma::Mat co, sc; + arma::Col lat, tsq; + arma::princomp (co, sc, lat, tsq, x); + + std::cout << "coeff: " << co << std::endl; + std::cout << "latent: " << lat << std::endl; + + // Mat access is (r, c) + sm::vec pc1vec = { co(0, 0), co(1, 0) }; + float angle1 = pc1vec.angle(); + std::cout << "Angle of first component " << pc1vec << " is " << angle1 * sm::mathconst::rad2deg + << " and length is " << lat(0) << std::endl; + sm::vec pc2vec = { co(0, 1), co(1, 1) }; + float angle2 = pc2vec.angle(); + std::cout << "Angle of 2nd component " << pc2vec << " is " << angle2 * sm::mathconst::rad2deg + << " and length is " << lat(1) << std::endl; + + mplot::DatasetStyle ds2 (mplot::stylepolicy::lines); + // Show 3 sigma axes + ds2.setcolour (mplot::colour::crimson); + ds2.datalabel = std::string("PC1 3") + mplot::unicode::toUtf8 (mplot::unicode::sigma); + sm::vvec> pc1vv = { {0,0}, pc1vec * 3.0f * std::sqrt(lat(0))}; + gv->setdata (pc1vv, ds2); + + ds2.setcolour (mplot::colour::orange); + ds2.datalabel = std::string("PC2 3") + mplot::unicode::toUtf8 (mplot::unicode::sigma); + sm::vvec> pc2vv = { {0,0}, pc2vec * 3.0f * std::sqrt(lat(1)) }; + gv->setdata (pc2vv, ds2); + + gv->finalize(); + v.addVisualModel (gv); + v.keepOpen(); +} diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp index 0ad04cf2..8b5a1b43 100644 --- a/examples/voronoi_boundary.cpp +++ b/examples/voronoi_boundary.cpp @@ -41,14 +41,16 @@ int main() v.bindmodel (vorv); vorv->show_voronoi2d = true; // true to show the 2D voronoi edges vorv->debug_dataCoords = true; // true to show coordinate spheres - vorv->border_width = 0.5f; #if 0 // Use a rectangular domain boundary // vorv->rectangular_domain = true; // this is default + // vorv->border_width = 0.5f; #else // polygonal - vorv->dom_shape = mplot::VoronoiVisual::domain_shape::circular; + //vorv->dom_shape = mplot::VoronoiVisual::domain_shape::circular; + vorv->dom_shape = mplot::VoronoiVisual::domain_shape::ellipsoid; vorv->num_boundary_points = 30; // default 30 + vorv->border_width = 0.01f; #endif vorv->cm.setType (cmap_t); vorv->setDataCoords (&points); diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index dfba4b8f..87a4bb81 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -23,6 +23,10 @@ #include #include #include +#include +#ifdef ADAPTIVE_SIGMA +# include +#endif #include #include @@ -122,24 +126,35 @@ namespace mplot this->boundary.resize (this->num_boundary_points, cent2.plus_one_dim()); if (this->dom_shape == domain_shape::ellipsoid) { - throw std::runtime_error ("implement me"); -#if 0 - // Extra setup - arma::mat x (dcoords_ptr->size(), 2); + // Find ellipse parameters for the data. First place data in an arma::mat, offsetting by the centroid + arma::Mat x (dcoords_ptr->size(), 2); for (unsigned int i = 0; i < dcoords_ptr->size(); ++i) { - x(i, 0) = (*this->dcoords_ptr)[i][0]; - x(i, 1) = (*this->dcoords_ptr)[i][1]; + x(i, 0) = (*this->dcoords_ptr)[i][0] - cent2[0]; + x(i, 1) = (*this->dcoords_ptr)[i][1] - cent2[1]; } - std::cout << "x: " << x << std::endl; - arma::mat coeff = arma::princomp (x); - std::cout << "princ coeff: " << coeff << std::endl; - // From PCA determine ellipsoid axes -#endif - } else { + // From PCA determine ellipsoid axes. Angle of coefficient vector and length of + // eigen values gives the ellipse parameters + arma::Mat co, sc; + arma::Col lat, tsq; + arma::princomp (co, sc, lat, tsq, x); + // Mat access is (r, c) + sm::vec pc1vec = { co(0, 0), co(1, 0) }; + sm::mat22 el_rotn; + el_rotn.rotate (pc1vec.angle()); + float a = this->n_sigma * std::sqrt (lat(0)); + float b = this->n_sigma * std::sqrt (lat(1)); + // Create the elliptic boundary + for (unsigned int i = 0; i < this->num_boundary_points; ++i) { + float phi = i * sm::mathconst::two_pi / this->num_boundary_points; + sm::vec bp = el_rotn * sm::vec{ a * std::cos (phi), b * std::sin (phi) } + cent2; + this->boundary[i] = bp.plus_one_dim(); + } + + } else { // circular boundary float l = max_len + this->border_width; for (unsigned int i = 0; i < this->num_boundary_points; ++i) { - float ang = i * sm::mathconst::two_pi / this->num_boundary_points; - this->boundary[i] += { l * std::cos (ang), l * std::sin (ang) }; + float phi = i * sm::mathconst::two_pi / this->num_boundary_points; + this->boundary[i] += { l * std::cos (phi), l * std::sin (phi) }; } } } // else rectangular default does not populate this->boundary @@ -498,6 +513,8 @@ namespace mplot domain_shape dom_shape = domain_shape::rectangular; // When making the boundary, how many points? Or use some f (dcoords.size())? unsigned int num_boundary_points = 30; + // When finding ellipsoid shape from PCA, how many sigmas to multiply the principle axes length by? + float n_sigma = 3.0f; // Do we add index labels? bool labelIndices = false; From ca548a6d87d18cbd063bb166e4b85923b98a9e14 Mon Sep 17 00:00:00 2001 From: Seb James Date: Wed, 14 Jan 2026 12:08:03 +0000 Subject: [PATCH 29/53] Remove cruft from an idea/attempt --- mplot/VoronoiVisual.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 87a4bb81..f21ef887 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -24,9 +24,6 @@ #include #include #include -#ifdef ADAPTIVE_SIGMA -# include -#endif #include #include From 64c0b7dd09dacb3578a5c62da6f0f6e4de5b1698 Mon Sep 17 00:00:00 2001 From: Seb James Date: Wed, 14 Jan 2026 17:15:23 +0000 Subject: [PATCH 30/53] Attempting to trace round the points --- examples/CMakeLists.txt | 3 + examples/trace_boundary.cpp | 135 ++++++++++++++++++++++++++++++++++ examples/voronoi_boundary.cpp | 3 +- mplot/VoronoiVisual.h | 39 +++++++++- mplot/compoundray/EyeVisual.h | 40 +++++++++- 5 files changed, 216 insertions(+), 4 deletions(-) create mode 100644 examples/trace_boundary.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 85793b6b..5c034eb5 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -569,3 +569,6 @@ if(ARMADILLO_FOUND) target_link_libraries(voronoi_fixed_nearlyz ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) endif(ARMADILLO_FOUND) + +add_executable(trace_boundary trace_boundary.cpp) +target_link_libraries(trace_boundary OpenGL::GL glfw Freetype::Freetype) diff --git a/examples/trace_boundary.cpp b/examples/trace_boundary.cpp new file mode 100644 index 00000000..f2982b0f --- /dev/null +++ b/examples/trace_boundary.cpp @@ -0,0 +1,135 @@ +/* + * Trace around a boundary of points in 2D + */ +#include + +#include +#include +#include +#include +#include + +#include +#include + + +int main (int argc, char** argv) +{ + int nbp = 5; + if (argc > 1) { + nbp = std::stoi (argv[1]); + } + // Graph the data + mplot::Visual v(1024, 768, "Boundary tracing"); + mplot::DatasetStyle ds (mplot::stylepolicy::markers); + // Create a GraphVisual object (obtaining a unique_ptr to the object) with a spatial offset within the scene of 0,0,0 + auto gv = std::make_unique> (sm::vec{-0.5f,-0.5f,0.0f}); + + constexpr unsigned int n_points = 80; + + // Create data + sm::rand_uniform rngxy(-0.8f, 0.8f, n_points); + sm::vvec> points(n_points); + for (unsigned int i = 0; i < n_points; ++i) { + points[i] = { rngxy.get(), rngxy.get() }; + } + + // Trace algorithm + sm::vec cent2 = sm::algo::centroid (points); + sm::vvec> boundary (nbp, sm::vec{}); + sm::vvec> centres (nbp, sm::vec{}); + + // For tracing, make an angle-sorted set of the coordinates,, based on the angle + // from the first two dimensions, sorted between angle 0 and 2pi + sm::vvec> dcoords_srt (points.size()); + // Offset by centroid + for (unsigned int i = 0; i < points.size(); ++i) { + dcoords_srt[i] = points[i] - cent2; + } + // Sort by angle + std::sort (dcoords_srt.begin(), dcoords_srt.end(), [](const sm::vec& a, const sm::vec& b){ + float aa = a.angle(); + sm::algo::zero_to_twopi (aa); + float ab = b.angle(); + sm::algo::zero_to_twopi (ab); + return aa < ab; + }); + + int dssz = dcoords_srt.size(); + // Can now iterate through dcoords_srt, slice by slice + int step = dcoords_srt.size() / (nbp - 1); + const float theta = sm::mathconst::pi / nbp; + for (int i = 0; i < dssz; i += step) { + // This is a pie slice. + int idx0 = (i / step) % nbp; // current index + float phi0 = idx0 * sm::mathconst::two_pi / nbp; + + std::cout << "Pie slice starting at i = " << i << std::endl; + float l = 0.0f; + for (int j = i - step / 2; j < (i + step / 2); ++j) { + int idx = (dssz + (j % dssz)) % dssz; // (w + (i % w)) % w; + float ll = dcoords_srt[idx].length(); + float aa = dcoords_srt[idx].angle(); + sm::algo::zero_to_twopi (aa); + float th = std::abs(phi0 - aa); + ll /= std::cos (th); + l = ll > l ? ll : l; + // FIXME: project onto phi0 (trying above) + } + std::cout << "max length is " << l << std::endl; + + int idx1 = (nbp + ((i / step - 1) % nbp)) % nbp; // 'previous' (or more clockwise) + + centres[idx0] = cent2 + sm::vec{ l * std::cos (phi0), l * std::sin (phi0) }; + std::cout << "centres[idx0] = " << centres[idx0] << std::endl; + + // Start and end of slice + float idx0f = static_cast(idx0); + float phi1 = (idx0f - 0.5f) * sm::mathconst::two_pi / nbp; + float phi2 = (idx0f + 0.5f) * sm::mathconst::two_pi / nbp; + + std::cout << "Slice centre idx " << idx0 << " angle " << phi0 << " with bnd index " + << idx1 << " to " << idx0 << " from angle " << phi1 << " to " << phi2 << std::endl; + + float b1len = boundary[idx1].length(); + if (b1len) { + b1len = (boundary[idx1] - cent2).length(); + } + // Length at phi1/phi2 is longer than at phi0 + float hyp = l / std::cos (theta); + std::cout << " for l = " << l << " and theta = " << theta * sm::mathconst::rad2deg << " hyp = " << hyp << std::endl; + sm::vec cand_vec = { hyp * std::cos (phi1), hyp * std::sin (phi1) }; + float cvlen = cand_vec.length(); + + boundary[idx1] = cvlen > b1len ? (cent2 + cand_vec) : boundary[idx1]; // prev + boundary[idx0] = cent2 + sm::vec{ hyp * std::cos (phi2), hyp * std::sin (phi2) }; // curr + } + + v.bindmodel (gv); + gv->setlimits (-1.5, 1.5, -1.5, 1.5); + + gv->setdata (points, ds); + + sm::vvec> vv_cent2 (1); + ds.markercolour = mplot::colour::springgreen2; + ds.markersize *= 2; + ds.markerstyle = mplot::markerstyle::hexagon; + vv_cent2[0] = cent2; + gv->setdata (vv_cent2, ds); + ds.markersize /= 2; + + ds.linecolour = mplot::colour::crimson; + ds.markercolour = mplot::colour::black; + ds.markerstyle = mplot::markerstyle::circle; + ds.showlines = true; + gv->setdata (boundary, ds); + + ds.markercolour = mplot::colour::maroon; + ds.markersize /= 3; + ds.showlines = false; + gv->setdata (centres, ds); + + gv->finalize(); + v.addVisualModel (gv); + v.keepOpen(); +} diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp index 8b5a1b43..22379e18 100644 --- a/examples/voronoi_boundary.cpp +++ b/examples/voronoi_boundary.cpp @@ -48,7 +48,8 @@ int main() #else // polygonal //vorv->dom_shape = mplot::VoronoiVisual::domain_shape::circular; - vorv->dom_shape = mplot::VoronoiVisual::domain_shape::ellipsoid; + //vorv->dom_shape = mplot::VoronoiVisual::domain_shape::ellipsoid; + vorv->dom_shape = mplot::VoronoiVisual::domain_shape::traced; vorv->num_boundary_points = 30; // default 30 vorv->border_width = 0.01f; #endif diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index f21ef887..6274594d 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -56,7 +56,8 @@ namespace mplot { rectangular, ellipsoid, - circular + circular, + traced // trace around the points - arbitrary polygon }; VoronoiVisual (const sm::vec _offset) @@ -154,6 +155,42 @@ namespace mplot this->boundary[i] += { l * std::cos (phi), l * std::sin (phi) }; } } + } else if (this->dom_shape == domain_shape::traced) { + std::cout << "Trace round the points\n"; + sm::vec cent = this->coordsCentroid(); + sm::vec cent2 = {cent[0], cent[1]}; + this->boundary.resize (this->num_boundary_points, cent2.plus_one_dim()); + + // For tracing, make an angle-sorted set of the coordinates,, based on the angle + // from the first two dimensions, sorted between angle 0 and 2pi + std::vector> dcoords_srt (*this->dcoords_ptr); + std::cout << "dcoords_srt.size() " << dcoords_srt.size() << std::endl; + // Offset by centroid + for (auto& dc : dcoords_srt) { dc -= cent; } + // Sort by angle + std::sort (dcoords_srt.begin(), dcoords_srt.end(), [](sm::vec a, sm::vec b){ + float aa = a.less_one_dim().angle(); + sm::algo::zero_to_twopi (aa); + float ab = b.less_one_dim().angle(); + sm::algo::zero_to_twopi (ab); + return aa < ab; + }); + std::cout << "dcoords_srt.size() " << dcoords_srt.size() << std::endl; + // Can now iterate through dcoords_srt, slice by slice + unsigned int step = dcoords_srt.size() / this->num_boundary_points; + for (unsigned int i = 0; i < dcoords_srt.size(); i += step) { + // This is a pie slice. + std::cout << "Pie slice starting at i = " << i << std::endl; + float l = 0.0f; + for (unsigned int j = i; j < dcoords_srt.size() && j < (i + step); ++j) { + float ll = dcoords_srt[j].less_one_dim().length(); + l = ll > l ? ll : l; + } + std::cout << "for boundary[" << (i / step) << "], max length is " << l << std::endl; + float phi = (i / step) * sm::mathconst::two_pi / this->num_boundary_points; + this->boundary[i / step] += sm::vec{ l * std::cos (phi), l * std::sin (phi), 1.0f }; // 1 is hack + } + } // else rectangular default does not populate this->boundary if (this->boundary.size() > 0) { diff --git a/mplot/compoundray/EyeVisual.h b/mplot/compoundray/EyeVisual.h index 8893a42e..9264cdb1 100644 --- a/mplot/compoundray/EyeVisual.h +++ b/mplot/compoundray/EyeVisual.h @@ -6,13 +6,18 @@ #include #include + +#include + #include #include #include #include +#include +#include + #include #include - #include namespace mplot::compoundray @@ -435,7 +440,38 @@ namespace mplot::compoundray jcv::manager vorman; // we need double precision for projections, float may run into trouble vorman.border_width = this->border_width; - vorman.diagram_generate (this->omm2d); + + sm::vec cent = sm::algo::centroid (omm2d); + sm::vec cent2 = cent.less_one_dim(); + constexpr unsigned int num_boundary_points = 30; + std::vector> boundary (num_boundary_points, cent); + + // Find ellipse parameters for the data. First place data in an arma::mat, offsetting by the centroid + arma::Mat x (omm2d.size(), 2); + for (unsigned int i = 0; i < omm2d.size(); ++i) { + x(i, 0) = omm2d[i][0] - cent2[0]; + x(i, 1) = omm2d[i][1] - cent2[1]; + } + // From PCA determine ellipsoid axes. Angle of coefficient vector and length of + // eigen values gives the ellipse parameters + arma::Mat co, sc; + arma::Col lat, tsq; + arma::princomp (co, sc, lat, tsq, x); + // Mat access is (r, c) + sm::vec pc1vec = { co(0, 0), co(1, 0) }; + sm::mat22 el_rotn; + el_rotn.rotate (pc1vec.angle()); + constexpr double n_sigma = 3.0; + double a = n_sigma * std::sqrt (lat(0)); + double b = n_sigma * std::sqrt (lat(1)); + // Create the elliptic boundary + for (unsigned int i = 0; i < num_boundary_points; ++i) { + double phi = i * sm::mathconst::two_pi / num_boundary_points; + sm::vec bp = el_rotn * sm::vec{ a * std::cos (phi), b * std::sin (phi) } + cent2; + boundary[i] = bp.plus_one_dim(); + } + + vorman.diagram_generate (this->omm2d, boundary); int diag_nsites = vorman.diagram_numsites(); if (diag_nsites != ncoords) { From c8a0c0eaab9ea9a61bc118afddaf6cee2f7a04ac Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 15 Jan 2026 13:12:05 +0000 Subject: [PATCH 31/53] Use Graham's scan! --- examples/trace_boundary.cpp | 105 ++++-------------------------------- maths | 2 +- 2 files changed, 10 insertions(+), 97 deletions(-) diff --git a/examples/trace_boundary.cpp b/examples/trace_boundary.cpp index f2982b0f..5625b82f 100644 --- a/examples/trace_boundary.cpp +++ b/examples/trace_boundary.cpp @@ -7,128 +7,41 @@ #include #include #include -#include +#include #include #include -int main (int argc, char** argv) +int main() { - int nbp = 5; - if (argc > 1) { - nbp = std::stoi (argv[1]); - } - // Graph the data + // Get ready to graph the data mplot::Visual v(1024, 768, "Boundary tracing"); mplot::DatasetStyle ds (mplot::stylepolicy::markers); - // Create a GraphVisual object (obtaining a unique_ptr to the object) with a spatial offset within the scene of 0,0,0 auto gv = std::make_unique> (sm::vec{-0.5f,-0.5f,0.0f}); + v.bindmodel (gv); - constexpr unsigned int n_points = 80; + constexpr unsigned int n_points = 200; // Create data sm::rand_uniform rngxy(-0.8f, 0.8f, n_points); sm::vvec> points(n_points); - for (unsigned int i = 0; i < n_points; ++i) { - points[i] = { rngxy.get(), rngxy.get() }; - } + for (unsigned int i = 0; i < n_points; ++i) { points[i] = { rngxy.get(), rngxy.get() }; } // Trace algorithm - sm::vec cent2 = sm::algo::centroid (points); - sm::vvec> boundary (nbp, sm::vec{}); - sm::vvec> centres (nbp, sm::vec{}); - - // For tracing, make an angle-sorted set of the coordinates,, based on the angle - // from the first two dimensions, sorted between angle 0 and 2pi - sm::vvec> dcoords_srt (points.size()); - // Offset by centroid - for (unsigned int i = 0; i < points.size(); ++i) { - dcoords_srt[i] = points[i] - cent2; - } - // Sort by angle - std::sort (dcoords_srt.begin(), dcoords_srt.end(), [](const sm::vec& a, const sm::vec& b){ - float aa = a.angle(); - sm::algo::zero_to_twopi (aa); - float ab = b.angle(); - sm::algo::zero_to_twopi (ab); - return aa < ab; - }); - - int dssz = dcoords_srt.size(); - // Can now iterate through dcoords_srt, slice by slice - int step = dcoords_srt.size() / (nbp - 1); - const float theta = sm::mathconst::pi / nbp; - for (int i = 0; i < dssz; i += step) { - // This is a pie slice. - int idx0 = (i / step) % nbp; // current index - float phi0 = idx0 * sm::mathconst::two_pi / nbp; - - std::cout << "Pie slice starting at i = " << i << std::endl; - float l = 0.0f; - for (int j = i - step / 2; j < (i + step / 2); ++j) { - int idx = (dssz + (j % dssz)) % dssz; // (w + (i % w)) % w; - float ll = dcoords_srt[idx].length(); - float aa = dcoords_srt[idx].angle(); - sm::algo::zero_to_twopi (aa); - float th = std::abs(phi0 - aa); - ll /= std::cos (th); - l = ll > l ? ll : l; - // FIXME: project onto phi0 (trying above) - } - std::cout << "max length is " << l << std::endl; - - int idx1 = (nbp + ((i / step - 1) % nbp)) % nbp; // 'previous' (or more clockwise) - - centres[idx0] = cent2 + sm::vec{ l * std::cos (phi0), l * std::sin (phi0) }; - std::cout << "centres[idx0] = " << centres[idx0] << std::endl; - - // Start and end of slice - float idx0f = static_cast(idx0); - float phi1 = (idx0f - 0.5f) * sm::mathconst::two_pi / nbp; - float phi2 = (idx0f + 0.5f) * sm::mathconst::two_pi / nbp; - - std::cout << "Slice centre idx " << idx0 << " angle " << phi0 << " with bnd index " - << idx1 << " to " << idx0 << " from angle " << phi1 << " to " << phi2 << std::endl; + sm::vvec> boundary = sm::geometry::graham_scan (points); - float b1len = boundary[idx1].length(); - if (b1len) { - b1len = (boundary[idx1] - cent2).length(); - } - // Length at phi1/phi2 is longer than at phi0 - float hyp = l / std::cos (theta); - std::cout << " for l = " << l << " and theta = " << theta * sm::mathconst::rad2deg << " hyp = " << hyp << std::endl; - sm::vec cand_vec = { hyp * std::cos (phi1), hyp * std::sin (phi1) }; - float cvlen = cand_vec.length(); - - boundary[idx1] = cvlen > b1len ? (cent2 + cand_vec) : boundary[idx1]; // prev - boundary[idx0] = cent2 + sm::vec{ hyp * std::cos (phi2), hyp * std::sin (phi2) }; // curr - } - - v.bindmodel (gv); gv->setlimits (-1.5, 1.5, -1.5, 1.5); gv->setdata (points, ds); - sm::vvec> vv_cent2 (1); - ds.markercolour = mplot::colour::springgreen2; - ds.markersize *= 2; - ds.markerstyle = mplot::markerstyle::hexagon; - vv_cent2[0] = cent2; - gv->setdata (vv_cent2, ds); - ds.markersize /= 2; - + ds.showlines = true; ds.linecolour = mplot::colour::crimson; ds.markercolour = mplot::colour::black; ds.markerstyle = mplot::markerstyle::circle; - ds.showlines = true; + ds.markersize *= 1.2f; gv->setdata (boundary, ds); - ds.markercolour = mplot::colour::maroon; - ds.markersize /= 3; - ds.showlines = false; - gv->setdata (centres, ds); - gv->finalize(); v.addVisualModel (gv); v.keepOpen(); diff --git a/maths b/maths index 75663fc1..730c554e 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 75663fc11e97fb21c72bc57706b94497b867c8cf +Subproject commit 730c554eae1df260a051cf0c8c9db17592ae9df9 From d460c619a2cfd84bb48c6bb36c437f728016aead Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 15 Jan 2026 13:40:46 +0000 Subject: [PATCH 32/53] Adds traced boundary code. Seems good. Just need to resolve bugs in the Voronoi polygon code to finish --- examples/voronoi_boundary.cpp | 4 +-- mplot/VoronoiVisual.h | 53 +++++++++++++---------------------- 2 files changed, 21 insertions(+), 36 deletions(-) diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp index 22379e18..06c0437b 100644 --- a/examples/voronoi_boundary.cpp +++ b/examples/voronoi_boundary.cpp @@ -50,8 +50,8 @@ int main() //vorv->dom_shape = mplot::VoronoiVisual::domain_shape::circular; //vorv->dom_shape = mplot::VoronoiVisual::domain_shape::ellipsoid; vorv->dom_shape = mplot::VoronoiVisual::domain_shape::traced; - vorv->num_boundary_points = 30; // default 30 - vorv->border_width = 0.01f; + vorv->num_boundary_points = 30; // default 30, ignored for traced + vorv->border_width = 0.2f; #endif vorv->cm.setType (cmap_t); vorv->setDataCoords (&points); diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 6274594d..a4500f0a 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include @@ -156,41 +158,24 @@ namespace mplot } } } else if (this->dom_shape == domain_shape::traced) { - std::cout << "Trace round the points\n"; - sm::vec cent = this->coordsCentroid(); - sm::vec cent2 = {cent[0], cent[1]}; - this->boundary.resize (this->num_boundary_points, cent2.plus_one_dim()); - - // For tracing, make an angle-sorted set of the coordinates,, based on the angle - // from the first two dimensions, sorted between angle 0 and 2pi - std::vector> dcoords_srt (*this->dcoords_ptr); - std::cout << "dcoords_srt.size() " << dcoords_srt.size() << std::endl; - // Offset by centroid - for (auto& dc : dcoords_srt) { dc -= cent; } - // Sort by angle - std::sort (dcoords_srt.begin(), dcoords_srt.end(), [](sm::vec a, sm::vec b){ - float aa = a.less_one_dim().angle(); - sm::algo::zero_to_twopi (aa); - float ab = b.less_one_dim().angle(); - sm::algo::zero_to_twopi (ab); - return aa < ab; - }); - std::cout << "dcoords_srt.size() " << dcoords_srt.size() << std::endl; - // Can now iterate through dcoords_srt, slice by slice - unsigned int step = dcoords_srt.size() / this->num_boundary_points; - for (unsigned int i = 0; i < dcoords_srt.size(); i += step) { - // This is a pie slice. - std::cout << "Pie slice starting at i = " << i << std::endl; - float l = 0.0f; - for (unsigned int j = i; j < dcoords_srt.size() && j < (i + step); ++j) { - float ll = dcoords_srt[j].less_one_dim().length(); - l = ll > l ? ll : l; - } - std::cout << "for boundary[" << (i / step) << "], max length is " << l << std::endl; - float phi = (i / step) * sm::mathconst::two_pi / this->num_boundary_points; - this->boundary[i / step] += sm::vec{ l * std::cos (phi), l * std::sin (phi), 1.0f }; // 1 is hack + // Copy 3D points to 2D + sm::vvec> coords2 (dcoords_ptr->size()); + for (unsigned int i = 0; i < dcoords_ptr->size(); ++i) { + coords2[i] = (*dcoords_ptr)[i].less_one_dim(); + } + auto bnd2centr = sm::algo::centroid (coords2); + // Find convex hull + sm::vvec> bnd2 = sm::geometry::graham_scan (coords2); + this->boundary.resize (bnd2.size()); + // Copy 2D to 3D boundary + for (unsigned int i = 0; i < bnd2.size(); ++i) { + this->boundary[i] = bnd2[i].plus_one_dim(); + // Add border + sm::vec brd = bnd2[i] - bnd2centr; // border vector from centroid to point + brd.renormalize(); + brd *= this->border_width; + this->boundary[i] += brd.plus_one_dim(); } - } // else rectangular default does not populate this->boundary if (this->boundary.size() > 0) { From d9a18e95cb5be5dac9ebdd3ded3f0678e3fae833 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 15 Jan 2026 14:05:12 +0000 Subject: [PATCH 33/53] Makes boundary shape settable with cmd arg (0, 1, 2 or 3) --- examples/voronoi_boundary.cpp | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp index 06c0437b..b0edca2d 100644 --- a/examples/voronoi_boundary.cpp +++ b/examples/voronoi_boundary.cpp @@ -16,10 +16,13 @@ static constexpr int n_points = 80; -int main() +int main (int argc, char** argv) { int rtn = -1; + int domshape = 0; // 0 for rectangular, 1 for circ, 2 for ellipse, 3 for traced + if (argc > 1) { domshape = std::stoi (argv[1]); } + mplot::Visual v(1024, 768, "VoronoiVisual"); sm::rand_uniform rngxy(-0.8f, 0.8f, n_points); @@ -41,18 +44,22 @@ int main() v.bindmodel (vorv); vorv->show_voronoi2d = true; // true to show the 2D voronoi edges vorv->debug_dataCoords = true; // true to show coordinate spheres -#if 0 - // Use a rectangular domain boundary - // vorv->rectangular_domain = true; // this is default - // vorv->border_width = 0.5f; -#else - // polygonal - //vorv->dom_shape = mplot::VoronoiVisual::domain_shape::circular; - //vorv->dom_shape = mplot::VoronoiVisual::domain_shape::ellipsoid; - vorv->dom_shape = mplot::VoronoiVisual::domain_shape::traced; - vorv->num_boundary_points = 30; // default 30, ignored for traced + + // traced, circular, ellipsoid, rectangular: + if (domshape == 1) { + vorv->dom_shape = mplot::VoronoiVisual::domain_shape::circular; + } else if (domshape == 2) { + vorv->dom_shape = mplot::VoronoiVisual::domain_shape::ellipsoid; + } else if (domshape == 3) { + vorv->dom_shape = mplot::VoronoiVisual::domain_shape::traced; + } else { + vorv->dom_shape = mplot::VoronoiVisual::domain_shape::rectangular; + } + // A border to add to our domain boundary vorv->border_width = 0.2f; -#endif + // Some domain shapes need to know a number of points: + vorv->num_boundary_points = 30; + vorv->cm.setType (cmap_t); vorv->setDataCoords (&points); vorv->setScalarData (&data); From 4db542a055863f8e934821071f6d702f8d764a6a Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 15 Jan 2026 14:27:13 +0000 Subject: [PATCH 34/53] More runtime options --- examples/voronoi_boundary.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp index b0edca2d..5e7c49fa 100644 --- a/examples/voronoi_boundary.cpp +++ b/examples/voronoi_boundary.cpp @@ -14,14 +14,17 @@ #include #include -static constexpr int n_points = 80; - int main (int argc, char** argv) { int rtn = -1; + float border_width = 0.2f; + unsigned int n_points = 80; int domshape = 0; // 0 for rectangular, 1 for circ, 2 for ellipse, 3 for traced + if (argc > 1) { domshape = std::stoi (argv[1]); } + if (argc > 2) { border_width = std::stof (argv[2]); } + if (argc > 3) { n_points = std::stoi (argv[3]); } mplot::Visual v(1024, 768, "VoronoiVisual"); @@ -56,7 +59,7 @@ int main (int argc, char** argv) vorv->dom_shape = mplot::VoronoiVisual::domain_shape::rectangular; } // A border to add to our domain boundary - vorv->border_width = 0.2f; + vorv->border_width = border_width; // Some domain shapes need to know a number of points: vorv->num_boundary_points = 30; From 2f3d84356de43f197e42aacee8dffb9c628d20bc Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 15 Jan 2026 15:18:20 +0000 Subject: [PATCH 35/53] cray is dep. on armadillo --- examples/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 5c034eb5..0540341c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -531,11 +531,11 @@ endif() add_executable(show_boundingboxes show_boundingboxes.cpp) target_link_libraries(show_boundingboxes OpenGL::GL glfw Freetype::Freetype) -# if have compound-ray header -add_executable(cray_eye cray_eye.cpp) -target_link_libraries(cray_eye OpenGL::GL glfw Freetype::Freetype) - if(ARMADILLO_FOUND) + # if have compound-ray header + add_executable(cray_eye cray_eye.cpp) + target_link_libraries(cray_eye OpenGL::GL glfw Freetype::Freetype) + # use of principle component analysis from arma in this example add_executable(ellipse_pca ellipse_pca.cpp) target_link_libraries(ellipse_pca ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) From 7a81d860b06e1aefc4d1cdb7456d06fb42bd6f09 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 15 Jan 2026 17:12:16 +0000 Subject: [PATCH 36/53] Debugging more problems with clipping and traversing the boundary --- examples/voronoi_boundary.cpp | 2 +- maths | 2 +- mplot/VoronoiVisual.h | 3 +++ mplot/compoundray/EyeVisual.h | 41 ++++++++++++----------------- mplot/jcvoronoi/jc_voronoi.h | 49 +++++++++++++++++++++++------------ 5 files changed, 53 insertions(+), 44 deletions(-) diff --git a/examples/voronoi_boundary.cpp b/examples/voronoi_boundary.cpp index 5e7c49fa..259001c8 100644 --- a/examples/voronoi_boundary.cpp +++ b/examples/voronoi_boundary.cpp @@ -61,7 +61,7 @@ int main (int argc, char** argv) // A border to add to our domain boundary vorv->border_width = border_width; // Some domain shapes need to know a number of points: - vorv->num_boundary_points = 30; + vorv->num_boundary_points = 12; vorv->cm.setType (cmap_t); vorv->setDataCoords (&points); diff --git a/maths b/maths index 730c554e..4a9448c7 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 730c554eae1df260a051cf0c8c9db17592ae9df9 +Subproject commit 4a9448c7ec7348b73f720b52c023703553dafbf6 diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index a4500f0a..aa6d7842 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -411,6 +411,7 @@ namespace mplot this->computeTube ({ e->pos[0].x() * this->zoom, e->pos[0].y() * this->zoom, 0.0f }, { e->pos[1].x() * this->zoom, e->pos[1].y() * this->zoom, 0.0f }, mplot::colour::black, mplot::colour::black, this->voronoi_grid_thickness, 6); + this->addLabel (e->pos[0].str(), e->pos[0].less_one_dim().plus_one_dim() * this->zoom + labelOffset, mplot::TextFeatures(labelSize) ); e = e->next; } } @@ -425,6 +426,8 @@ namespace mplot // Polygonal boundary (if used) for (auto b : this->boundary) { this->computeSphere (b * this->zoom, mplot::colour::dodgerblue2, 3.0f * this->dataCoord_sphere_size); + // addlabel + this->addLabel (b.str(), b * this->zoom + labelOffset, mplot::TextFeatures(labelSize) ); } } } diff --git a/mplot/compoundray/EyeVisual.h b/mplot/compoundray/EyeVisual.h index 9264cdb1..05bee567 100644 --- a/mplot/compoundray/EyeVisual.h +++ b/mplot/compoundray/EyeVisual.h @@ -441,34 +441,25 @@ namespace mplot::compoundray jcv::manager vorman; // we need double precision for projections, float may run into trouble vorman.border_width = this->border_width; - sm::vec cent = sm::algo::centroid (omm2d); - sm::vec cent2 = cent.less_one_dim(); - constexpr unsigned int num_boundary_points = 30; - std::vector> boundary (num_boundary_points, cent); + std::vector> boundary; - // Find ellipse parameters for the data. First place data in an arma::mat, offsetting by the centroid - arma::Mat x (omm2d.size(), 2); + // Copy 3D points to 2D + sm::vvec> coords2 (omm2d.size()); for (unsigned int i = 0; i < omm2d.size(); ++i) { - x(i, 0) = omm2d[i][0] - cent2[0]; - x(i, 1) = omm2d[i][1] - cent2[1]; + coords2[i] = omm2d[i].less_one_dim(); } - // From PCA determine ellipsoid axes. Angle of coefficient vector and length of - // eigen values gives the ellipse parameters - arma::Mat co, sc; - arma::Col lat, tsq; - arma::princomp (co, sc, lat, tsq, x); - // Mat access is (r, c) - sm::vec pc1vec = { co(0, 0), co(1, 0) }; - sm::mat22 el_rotn; - el_rotn.rotate (pc1vec.angle()); - constexpr double n_sigma = 3.0; - double a = n_sigma * std::sqrt (lat(0)); - double b = n_sigma * std::sqrt (lat(1)); - // Create the elliptic boundary - for (unsigned int i = 0; i < num_boundary_points; ++i) { - double phi = i * sm::mathconst::two_pi / num_boundary_points; - sm::vec bp = el_rotn * sm::vec{ a * std::cos (phi), b * std::sin (phi) } + cent2; - boundary[i] = bp.plus_one_dim(); + auto bnd2centr = sm::algo::centroid (coords2); + // Find convex hull + sm::vvec> bnd2 = sm::geometry::graham_scan (coords2); + boundary.resize (bnd2.size()); + // Copy 2D to 3D boundary + for (unsigned int i = 0; i < bnd2.size(); ++i) { + boundary[i] = bnd2[i].plus_one_dim(); + // Add border + sm::vec brd = bnd2[i] - bnd2centr; // border vector from centroid to point + brd.renormalize(); + brd *= this->border_width; + boundary[i] += brd.plus_one_dim(); } vorman.diagram_generate (this->omm2d, boundary); diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index bb9d617e..2456db0b 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -71,14 +71,6 @@ namespace jcv struct site* neighbor; point pos[2]; T angle; -#if 0 - std::string str() - { - std::stringstream ss; - ss << "graphedge with edge: " << edge_->pos[0] << "--" << edge_->pos[1]; - return ss.str(); - } -#endif }; template @@ -1397,7 +1389,7 @@ namespace jcv // In the case of all sites being all on a horizontal or vertical line, the // rect area will be zero, and the diagram generation will most likely fail rect_round(&tmp_rect); - rect_inflate(&tmp_rect, 100); + rect_inflate(&tmp_rect, 10); internal->clipper_.min = tmp_rect.min; internal->clipper_.max = tmp_rect.max; @@ -1440,7 +1432,7 @@ namespace jcv finishline (internal, he->edge_); } - fillgaps (d); + //fillgaps (d); } /** @@ -1534,17 +1526,27 @@ namespace jcv int w_p1 = w.wind (p1); if (w_p0 == 0 && w_p1 == 0) { + std::cout << "Both points outside boundary\n"; return 2; // Both outside means remove this edge. } else if (w_p0 != 0 && w_p1 != 0) { + std::cout << "Both points INSIDE boundary, return 1\n"; return 1; // Both inside } + std::cout << "p0 is " << (w_p0 == 0 ? "outside" : "inside") << " and p1 is " << (w_p1 == 0 ? "outside" : "inside") << std::endl; + + std::cout << "p0 = " << p0.str_mat() << std::endl; + std::cout << "bnd = ["; for (int i = 0; i < num_points; ++i) { sm::vec v0 = (*polygon)[i].less_one_dim(); sm::vec v1 = (*polygon)[(i + 1) % num_points].less_one_dim(); + std::cout << v0.str_comma_separated() << ";\n"; + //std::cout << "Test segments intersect for v0/v1: " << v0 << ", " << v1 << ", p0/p1: " + // << p0.less_one_dim() << ", " << p1.less_one_dim() << std::endl; // find crossing point of v0,v1 and p0,p1 std::bitset<2> isect = sm::geometry::segments_intersect (v0, v1, p0.less_one_dim(), p1.less_one_dim()); + //std::cout << "Intersect? " << (isect.test(0) ? "yes" : "no") << " colinear? " << (isect.test(1) ? "yes" : "no") << std::endl; if (isect.test(0) == true) { if (isect.test(1) == true) { // lines co-linear. This is always an error? @@ -1571,6 +1573,7 @@ namespace jcv } // else no crossing point with this section. } } + std::cout << "]\n"; return 1; } @@ -1581,26 +1584,28 @@ namespace jcv if (!result) { return 0; } // Return here for a sanity check on the polygon clipping - // return 1; + //return 1; point p0 = e->pos[0]; point p1 = e->pos[1]; + std::cout << "**Clip for: " << p0 << " to " << p1 << std::endl; + result = ray_intersect_polygon (clipper, p0, p1); if (result == 2) { - // Here, we omit to set e->pos[] from p0, p1 and just return 2. + // Both p0 and p1 were outside boundary. return result; - } - - if (!result) { + } else if (result == 0) { e->pos[0] = e->pos[1]; - return 0; - } + return result; + } // else result should be 1, which is ok e->pos[0] = p0; e->pos[1] = p1; + std::cout << "Clipped to: " << p0 << " to " << p1 << std::endl; + return 1; } @@ -1727,6 +1732,7 @@ namespace jcv if (current_edge[0] == 1 && next_edge[0] == 1 && current_edge[1] == next_edge[1]) { // Case: Current and Next on the same border + std::cout << "Current and next on same border\n"; graphedge* gap = alloc_graphedge (allocator); gap->neighbor = 0; @@ -1742,9 +1748,11 @@ namespace jcv // Case: Current and Next on different borders, so we need to find the // adjacent vertex, following the borders CCW + std::cout << "Current and next on different borders\n"; int next_vertex = (current_edge[1] + 1) % num_points; point vtx = get_polygon_vertex (clipper, next_vertex); + std::cout << "vtx = " << vtx << std::endl; graphedge* gap = alloc_graphedge (allocator); gap->neighbor = 0; @@ -1759,6 +1767,7 @@ namespace jcv } else if (current_edge[0] == 1 && next_edge[0] == 2) { // Case: Current on border, next on a vertex + std::cout << "Current on border next on vertex\n"; point vtx = get_polygon_vertex (clipper, next_edge[1]); graphedge* gap = alloc_graphedge (allocator); @@ -1773,6 +1782,7 @@ namespace jcv } else if (current_edge[0] == 2 && next_edge[0] == 1 && next_edge[1] == current_edge[1]) { + std::cout << "Current on vertex next on same border\n"; // Case: Current on vertex, next on *same* border graphedge* gap = alloc_graphedge (allocator); gap->neighbor = 0; @@ -1786,6 +1796,7 @@ namespace jcv } else if (current_edge[0] == 2 && next_edge[0] == 1 && next_edge[1] != current_edge[1]) { + std::cout << "Current on vertex next on another border\n"; // Case: Current on vertex, next on another border, so we need to find the adjacent // vertex, following the borders CCW int next_vertex = (current_edge[1] + 1) % num_points; @@ -1822,6 +1833,10 @@ namespace jcv } } // else current_edge->pos[1] is not on the polygonal boundary + if ((curr_graphedge->pos[0] - curr_graphedge->pos[1]).length_sq() > 25) { + std::cout << "added a long edge from " << curr_graphedge->pos[0] << " to " << curr_graphedge->pos[1] << std::endl; + } + curr_graphedge = curr_graphedge->next; if (curr_graphedge) { next = curr_graphedge->next; From 27c3d08ee9184c4422abcacbc1766a685474a454 Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 16 Jan 2026 08:52:37 +0000 Subject: [PATCH 37/53] Brings in updated winder code and adds debugging to jc_voronoi --- examples/voronoi_function.cpp | 5 ++- examples/voronoi_function_flat.cpp | 7 +++- maths | 2 +- mplot/VoronoiVisual.h | 8 ++-- mplot/jcvoronoi/jc_voronoi.h | 62 +++++++++++++++++++----------- 5 files changed, 54 insertions(+), 30 deletions(-) diff --git a/examples/voronoi_function.cpp b/examples/voronoi_function.cpp index dbc2fed4..6ff9f7b8 100644 --- a/examples/voronoi_function.cpp +++ b/examples/voronoi_function.cpp @@ -44,7 +44,10 @@ int main() vorv->show_voronoi2d = false; // true to show the 2D voronoi edges vorv->debug_dataCoords = false; // true to show coordinate spheres float length_scale = 4.0f / std::sqrt (n_points); - vorv->border_width = length_scale; + vorv->border_width = length_scale; + //vorv->dom_shape = mplot::VoronoiVisual::domain_shape::traced; + //vorv->dom_shape = mplot::VoronoiVisual::domain_shape::circular; + //vorv->dom_shape = mplot::VoronoiVisual::domain_shape::rectangular; // default vorv->cm.setType (cmap_t); vorv->setDataCoords (&points); vorv->setScalarData (&data); diff --git a/examples/voronoi_function_flat.cpp b/examples/voronoi_function_flat.cpp index 6ae4e4ca..a65d9714 100644 --- a/examples/voronoi_function_flat.cpp +++ b/examples/voronoi_function_flat.cpp @@ -43,7 +43,10 @@ int main() vorv->show_voronoi2d = true; // true to show the 2D voronoi edges vorv->debug_dataCoords = false; // true to show coordinate spheres float length_scale = 4.0f / std::sqrt (n_points); - vorv->border_width = length_scale; + vorv->border_width = length_scale; + //vorv->dom_shape = mplot::VoronoiVisual::domain_shape::traced; + //vorv->dom_shape = mplot::VoronoiVisual::domain_shape::circular; + //vorv->dom_shape = mplot::VoronoiVisual::domain_shape::rectangular; // default vorv->cm.setType (cmap_t); vorv->setDataCoords (&points); vorv->setScalarData (&data); @@ -61,7 +64,7 @@ int main() if (fcount++% 600 == 0) { vorvp->cm.setType (++cmap_t); } - vorvp->reinitColours(); // Not quite working when I change the colourmap + vorvp->reinitColours(); v.waitevents(0.018); v.render(); k += 0.01f; diff --git a/maths b/maths index 4a9448c7..3246efed 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 4a9448c7ec7348b73f720b52c023703553dafbf6 +Subproject commit 3246efed38718f5319346b54f0fec9a76c7538fd diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index aa6d7842..560fca52 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -411,7 +411,9 @@ namespace mplot this->computeTube ({ e->pos[0].x() * this->zoom, e->pos[0].y() * this->zoom, 0.0f }, { e->pos[1].x() * this->zoom, e->pos[1].y() * this->zoom, 0.0f }, mplot::colour::black, mplot::colour::black, this->voronoi_grid_thickness, 6); - this->addLabel (e->pos[0].str(), e->pos[0].less_one_dim().plus_one_dim() * this->zoom + labelOffset, mplot::TextFeatures(labelSize) ); + //this->addLabel (e->pos[0].str(), + // e->pos[0].less_one_dim().plus_one_dim() * this->zoom + labelOffset, + // mplot::TextFeatures(labelSize) ); e = e->next; } } @@ -426,8 +428,8 @@ namespace mplot // Polygonal boundary (if used) for (auto b : this->boundary) { this->computeSphere (b * this->zoom, mplot::colour::dodgerblue2, 3.0f * this->dataCoord_sphere_size); - // addlabel - this->addLabel (b.str(), b * this->zoom + labelOffset, mplot::TextFeatures(labelSize) ); + // Show text of boundary vertex position like this + // this->addLabel (b.str(), b * this->zoom + labelOffset, mplot::TextFeatures(labelSize) ); } } } diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 2456db0b..ef8a0397 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -1432,7 +1432,7 @@ namespace jcv finishline (internal, he->edge_); } - //fillgaps (d); + fillgaps (d); } /** @@ -1517,6 +1517,7 @@ namespace jcv static int ray_intersect_polygon (const clipper* clipper, point& p0, point& p1) { + constexpr bool debug_ray_intersect = false; auto polygon = reinterpret_cast>*>(clipper->ctx); int num_points = polygon->size(); @@ -1526,27 +1527,32 @@ namespace jcv int w_p1 = w.wind (p1); if (w_p0 == 0 && w_p1 == 0) { - std::cout << "Both points outside boundary\n"; + if constexpr (debug_ray_intersect) { std::cout << "Both points outside boundary\n"; } return 2; // Both outside means remove this edge. } else if (w_p0 != 0 && w_p1 != 0) { - std::cout << "Both points INSIDE boundary, return 1\n"; + if constexpr (debug_ray_intersect) { std::cout << "Both points INSIDE boundary, return 1\n"; } return 1; // Both inside } - std::cout << "p0 is " << (w_p0 == 0 ? "outside" : "inside") << " and p1 is " << (w_p1 == 0 ? "outside" : "inside") << std::endl; - - std::cout << "p0 = " << p0.str_mat() << std::endl; - std::cout << "bnd = ["; + if constexpr (debug_ray_intersect) { + std::cout << "p0 is " << (w_p0 == 0 ? "outside" : "inside") << " and p1 is " + << (w_p1 == 0 ? "outside" : "inside") << std::endl; + } for (int i = 0; i < num_points; ++i) { sm::vec v0 = (*polygon)[i].less_one_dim(); sm::vec v1 = (*polygon)[(i + 1) % num_points].less_one_dim(); - std::cout << v0.str_comma_separated() << ";\n"; - //std::cout << "Test segments intersect for v0/v1: " << v0 << ", " << v1 << ", p0/p1: " - // << p0.less_one_dim() << ", " << p1.less_one_dim() << std::endl; + + if constexpr (debug_ray_intersect) { + std::cout << "Test segments intersect for v0/v1: " << v0 << ", " << v1 << ", p0/p1: " + << p0.less_one_dim() << ", " << p1.less_one_dim() << std::endl; + } // find crossing point of v0,v1 and p0,p1 std::bitset<2> isect = sm::geometry::segments_intersect (v0, v1, p0.less_one_dim(), p1.less_one_dim()); - //std::cout << "Intersect? " << (isect.test(0) ? "yes" : "no") << " colinear? " << (isect.test(1) ? "yes" : "no") << std::endl; + if constexpr (debug_ray_intersect) { + std::cout << "Intersect? " << (isect.test(0) ? "yes" : "no") + << " colinear? " << (isect.test(1) ? "yes" : "no") << std::endl; + } if (isect.test(0) == true) { if (isect.test(1) == true) { // lines co-linear. This is always an error? @@ -1573,12 +1579,14 @@ namespace jcv } // else no crossing point with this section. } } - std::cout << "]\n"; + return 1; } static int polygon_clip (const clipper* clipper, edge* e) { + constexpr bool debug_polyclip = false; + // Using the box clipper to get a finite line segment int result = manager::boxshape_clip (clipper, e); if (!result) { return 0; } @@ -1589,7 +1597,9 @@ namespace jcv point p0 = e->pos[0]; point p1 = e->pos[1]; - std::cout << "**Clip for: " << p0 << " to " << p1 << std::endl; + if constexpr (debug_polyclip) { + std::cout << "**Clip for: " << p0 << " to " << p1 << std::endl; + } result = ray_intersect_polygon (clipper, p0, p1); @@ -1604,7 +1614,9 @@ namespace jcv e->pos[0] = p0; e->pos[1] = p1; - std::cout << "Clipped to: " << p0 << " to " << p1 << std::endl; + if constexpr (debug_polyclip) { + std::cout << "Clipped to: " << p0 << " to " << p1 << std::endl; + } return 1; } @@ -1672,6 +1684,8 @@ namespace jcv static void polygon_fill (const clipper* clipper, context_internal* allocator, site* site) { + constexpr bool debug_polyfill = false; + // They're sorted CCW, so if the current->pos[1] != next->pos[0], then we have a gap auto polygon = reinterpret_cast>*>(clipper->ctx); int num_points = polygon->size(); @@ -1732,7 +1746,7 @@ namespace jcv if (current_edge[0] == 1 && next_edge[0] == 1 && current_edge[1] == next_edge[1]) { // Case: Current and Next on the same border - std::cout << "Current and next on same border\n"; + if constexpr (debug_polyfill) { std::cout << "Current and next on same border\n"; } graphedge* gap = alloc_graphedge (allocator); gap->neighbor = 0; @@ -1748,11 +1762,11 @@ namespace jcv // Case: Current and Next on different borders, so we need to find the // adjacent vertex, following the borders CCW - std::cout << "Current and next on different borders\n"; + if constexpr (debug_polyfill) { std::cout << "Current and next on different borders\n"; } int next_vertex = (current_edge[1] + 1) % num_points; point vtx = get_polygon_vertex (clipper, next_vertex); - std::cout << "vtx = " << vtx << std::endl; + if constexpr (debug_polyfill) { std::cout << "vtx = " << vtx << std::endl; } graphedge* gap = alloc_graphedge (allocator); gap->neighbor = 0; @@ -1767,7 +1781,7 @@ namespace jcv } else if (current_edge[0] == 1 && next_edge[0] == 2) { // Case: Current on border, next on a vertex - std::cout << "Current on border next on vertex\n"; + if constexpr (debug_polyfill) { std::cout << "Current on border next on vertex\n"; } point vtx = get_polygon_vertex (clipper, next_edge[1]); graphedge* gap = alloc_graphedge (allocator); @@ -1782,7 +1796,7 @@ namespace jcv } else if (current_edge[0] == 2 && next_edge[0] == 1 && next_edge[1] == current_edge[1]) { - std::cout << "Current on vertex next on same border\n"; + if constexpr (debug_polyfill) { std::cout << "Current on vertex next on same border\n"; } // Case: Current on vertex, next on *same* border graphedge* gap = alloc_graphedge (allocator); gap->neighbor = 0; @@ -1796,7 +1810,7 @@ namespace jcv } else if (current_edge[0] == 2 && next_edge[0] == 1 && next_edge[1] != current_edge[1]) { - std::cout << "Current on vertex next on another border\n"; + if constexpr (debug_polyfill) { std::cout << "Current on vertex next on another border\n"; } // Case: Current on vertex, next on another border, so we need to find the adjacent // vertex, following the borders CCW int next_vertex = (current_edge[1] + 1) % num_points; @@ -1815,7 +1829,7 @@ namespace jcv } else if (next_edge[0] == 0) { // Case: Current on vertex or border, but next not on polygon boundary - std::cout << "Current on vertex or border, but next not on polygon boundary\n"; + if constexpr (debug_polyfill) { std::cout << "Current on vertex or border, but next not on polygon boundary\n"; } graphedge* gap = alloc_graphedge (allocator); gap->neighbor = 0; gap->pos[0] = curr_graphedge->pos[1]; @@ -1833,8 +1847,10 @@ namespace jcv } } // else current_edge->pos[1] is not on the polygonal boundary - if ((curr_graphedge->pos[0] - curr_graphedge->pos[1]).length_sq() > 25) { - std::cout << "added a long edge from " << curr_graphedge->pos[0] << " to " << curr_graphedge->pos[1] << std::endl; + if constexpr (debug_polyfill) { + if ((curr_graphedge->pos[0] - curr_graphedge->pos[1]).length_sq() > 25) { + std::cout << "added a long edge from " << curr_graphedge->pos[0] << " to " << curr_graphedge->pos[1] << std::endl; + } } curr_graphedge = curr_graphedge->next; From f947dfdbce2c4d3f8fb48eb773e979d5f4e4c8e0 Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 16 Jan 2026 17:00:40 +0000 Subject: [PATCH 38/53] Updates maths, bringing in polysolve and winder-fix-in-main --- maths | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths b/maths index 3246efed..a17fcbb9 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 3246efed38718f5319346b54f0fec9a76c7538fd +Subproject commit a17fcbb99f6f73b0da63ee17e70dd95e3f2d32fd From ed07121f2841e4e55437da569af4babecece6ff2 Mon Sep 17 00:00:00 2001 From: Seb James Date: Sat, 17 Jan 2026 15:45:05 +0000 Subject: [PATCH 39/53] Current WIP on PCA --- examples/ellipse_pca.cpp | 10 +++++++++- maths | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/examples/ellipse_pca.cpp b/examples/ellipse_pca.cpp index 86bfc904..d304d331 100644 --- a/examples/ellipse_pca.cpp +++ b/examples/ellipse_pca.cpp @@ -6,6 +6,8 @@ #include #include #include +#include + #include #include @@ -20,7 +22,7 @@ int main() sm::rand_normal rn2 (0.0f, 0.5f); sm::vvec> _x (n_samp, {0}); sm::mat22 rotn; - rotn.rotate (sm::mathconst::pi_over_4); + rotn.rotate (sm::mathconst::pi_over_8); for (unsigned int i = 0; i < n_samp; ++i) { _x[i] = rotn * sm::vec{ rn1.get(), rn2.get() }; } @@ -35,6 +37,11 @@ int main() gv->setlimits (-5, 5, -5, 5); gv->setdata (_x, ds); + // sm::pca + std::cout << "\nsm::pca gives:\n"; + [[maybe_unused]] sm::pca::result pca_res = sm::pca::compute (_x); + + std::cout << "\narma gives:\n"; // Place data in arma::Mat arma::Mat x(_x.size(), 2); for (unsigned int i = 0; i < _x.size(); ++i) { @@ -48,6 +55,7 @@ int main() arma::princomp (co, sc, lat, tsq, x); std::cout << "coeff: " << co << std::endl; + //std::cout << "scores: " << sc << std::endl; std::cout << "latent: " << lat << std::endl; // Mat access is (r, c) diff --git a/maths b/maths index a17fcbb9..073e3697 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit a17fcbb99f6f73b0da63ee17e70dd95e3f2d32fd +Subproject commit 073e3697a80a06ac1d07f71d48a5899820c9a858 From 0194cbac0983843802cda45d3889656624b5d44e Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 19 Jan 2026 14:48:02 +0000 Subject: [PATCH 40/53] Update maths lib --- maths | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths b/maths index 073e3697..f0bdcec2 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 073e3697a80a06ac1d07f71d48a5899820c9a858 +Subproject commit f0bdcec2afa6901baef48db4c4f97292562f23bc From c073d6a3861e5e38c778d725be6640ab501c4e17 Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 19 Jan 2026 15:12:55 +0000 Subject: [PATCH 41/53] Update maths, and we won't be using sm::pca until it's ready --- examples/ellipse_pca.cpp | 5 ----- maths | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/examples/ellipse_pca.cpp b/examples/ellipse_pca.cpp index d304d331..42822bdc 100644 --- a/examples/ellipse_pca.cpp +++ b/examples/ellipse_pca.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include @@ -37,10 +36,6 @@ int main() gv->setlimits (-5, 5, -5, 5); gv->setdata (_x, ds); - // sm::pca - std::cout << "\nsm::pca gives:\n"; - [[maybe_unused]] sm::pca::result pca_res = sm::pca::compute (_x); - std::cout << "\narma gives:\n"; // Place data in arma::Mat arma::Mat x(_x.size(), 2); diff --git a/maths b/maths index f0bdcec2..0acb70ea 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit f0bdcec2afa6901baef48db4c4f97292562f23bc +Subproject commit 0acb70ea154f031751f9921bc4581e65242f4261 From 73badf71ae39480caced2e23683a656d2c8324fd Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 20 Jan 2026 10:36:18 +0000 Subject: [PATCH 42/53] Remove ellipse domain option from VoronoiVisual, removing the arma requirement --- examples/CMakeLists.txt | 68 +++++++++++++++++------------------ mplot/VoronoiVisual.h | 11 ++++-- mplot/compoundray/EyeVisual.h | 2 -- 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 0540341c..8168f544 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -408,6 +408,33 @@ target_link_libraries(draw_triangles_intersections OpenGL::GL glfw Freetype::Fre add_executable(triangle_intersect triangle_intersect.cpp) target_link_libraries(triangle_intersect OpenGL::GL glfw Freetype::Freetype) +add_executable(voronoi_random voronoi_random.cpp) +target_link_libraries(voronoi_random OpenGL::GL glfw Freetype::Freetype) + +add_executable(voronoi_boundary voronoi_boundary.cpp) +target_link_libraries(voronoi_boundary OpenGL::GL glfw Freetype::Freetype) + +add_executable(voronoi_rectangular voronoi_rectangular.cpp) +target_link_libraries(voronoi_rectangular OpenGL::GL glfw Freetype::Freetype) + +add_executable(voronoi_vectordata voronoi_vectordata.cpp) +target_link_libraries(voronoi_vectordata OpenGL::GL glfw Freetype::Freetype) + +add_executable(voronoi_function voronoi_function.cpp) +target_link_libraries(voronoi_function OpenGL::GL glfw Freetype::Freetype) + +add_executable(voronoi_function_flat voronoi_function_flat.cpp) +target_link_libraries(voronoi_function_flat OpenGL::GL glfw Freetype::Freetype) + +add_executable(voronoi_fixed voronoi_fixed.cpp) +target_link_libraries(voronoi_fixed OpenGL::GL glfw Freetype::Freetype) + +add_executable(voronoi_fixed_xz voronoi_fixed_xz.cpp) +target_link_libraries(voronoi_fixed_xz OpenGL::GL glfw Freetype::Freetype) + +add_executable(voronoi_fixed_nearlyz voronoi_fixed_nearlyz.cpp) +target_link_libraries(voronoi_fixed_nearlyz OpenGL::GL glfw Freetype::Freetype) + add_executable(rectangle rectangle.cpp) target_link_libraries(rectangle OpenGL::GL glfw Freetype::Freetype) @@ -531,44 +558,15 @@ endif() add_executable(show_boundingboxes show_boundingboxes.cpp) target_link_libraries(show_boundingboxes OpenGL::GL glfw Freetype::Freetype) -if(ARMADILLO_FOUND) - # if have compound-ray header - add_executable(cray_eye cray_eye.cpp) - target_link_libraries(cray_eye OpenGL::GL glfw Freetype::Freetype) +# if have compound-ray header +add_executable(cray_eye cray_eye.cpp) +target_link_libraries(cray_eye OpenGL::GL glfw Freetype::Freetype) - # use of principle component analysis from arma in this example +if(ARMADILLO_FOUND) + # Make use of principle component analysis from arma in this example add_executable(ellipse_pca ellipse_pca.cpp) target_link_libraries(ellipse_pca ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) - - # Voronoi code now also uses arma - add_executable(voronoi_random voronoi_random.cpp) - target_link_libraries(voronoi_random ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) - - add_executable(voronoi_boundary voronoi_boundary.cpp) - target_link_libraries(voronoi_boundary ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) - - add_executable(voronoi_rectangular voronoi_rectangular.cpp) - target_link_libraries(voronoi_rectangular ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) - - add_executable(voronoi_vectordata voronoi_vectordata.cpp) - target_link_libraries(voronoi_vectordata ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) - - add_executable(voronoi_function voronoi_function.cpp) - target_link_libraries(voronoi_function ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) - - add_executable(voronoi_function_flat voronoi_function_flat.cpp) - target_link_libraries(voronoi_function_flat ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) - - add_executable(voronoi_fixed voronoi_fixed.cpp) - target_link_libraries(voronoi_fixed ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) - - add_executable(voronoi_fixed_xz voronoi_fixed_xz.cpp) - target_link_libraries(voronoi_fixed_xz ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) - - add_executable(voronoi_fixed_nearlyz voronoi_fixed_nearlyz.cpp) - target_link_libraries(voronoi_fixed_nearlyz ${ARMADILLO_LIBRARY} ${ARMADILLO_LIBRARIES} OpenGL::GL glfw Freetype::Freetype) - -endif(ARMADILLO_FOUND) +endif() add_executable(trace_boundary trace_boundary.cpp) target_link_libraries(trace_boundary OpenGL::GL glfw Freetype::Freetype) diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 560fca52..335a5eb2 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -18,8 +18,6 @@ #include #include -#include - #include #include #include @@ -126,6 +124,13 @@ namespace mplot this->boundary.resize (this->num_boundary_points, cent2.plus_one_dim()); if (this->dom_shape == domain_shape::ellipsoid) { + throw std::runtime_error ("Elliptic domain shapes not supported"); + /* + * Here is an approach using arma::princomp, but I don't want the arma + * dependency, as elliptic boundaries are not as useful to me as traced + * boundaries. This code awaits sm::pca::compute(). + */ +#if 0 // Find ellipse parameters for the data. First place data in an arma::mat, offsetting by the centroid arma::Mat x (dcoords_ptr->size(), 2); for (unsigned int i = 0; i < dcoords_ptr->size(); ++i) { @@ -149,7 +154,7 @@ namespace mplot sm::vec bp = el_rotn * sm::vec{ a * std::cos (phi), b * std::sin (phi) } + cent2; this->boundary[i] = bp.plus_one_dim(); } - +#endif } else { // circular boundary float l = max_len + this->border_width; for (unsigned int i = 0; i < this->num_boundary_points; ++i) { diff --git a/mplot/compoundray/EyeVisual.h b/mplot/compoundray/EyeVisual.h index 05bee567..8b891852 100644 --- a/mplot/compoundray/EyeVisual.h +++ b/mplot/compoundray/EyeVisual.h @@ -7,8 +7,6 @@ #include #include -#include - #include #include #include From 3f71d3c14bd45600e62948b30bf92feb7b2b388d Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 20 Jan 2026 10:44:27 +0000 Subject: [PATCH 43/53] Minimise differences in jc_voronoi --- mplot/compoundray/EyeVisual.h | 3 --- mplot/jcvoronoi/jc_voronoi.h | 25 +++++++------------------ 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/mplot/compoundray/EyeVisual.h b/mplot/compoundray/EyeVisual.h index 8b891852..63f29889 100644 --- a/mplot/compoundray/EyeVisual.h +++ b/mplot/compoundray/EyeVisual.h @@ -6,14 +6,11 @@ #include #include - #include #include #include #include -#include #include - #include #include #include diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index ef8a0397..293ab892 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -522,8 +521,6 @@ namespace jcv // see edge_create static int boxshape_clip (const clipper* clipper, edge* e) { - //std::cout << "boxshape clip edge " << e->pos[0] << "--" << e->pos[1] - // << " f = " << e->a << "x + " << e->b << "y + " << e->c << "..." << std::endl; T pxmin = clipper->min[0]; T pxmax = clipper->max[0]; T pymin = clipper->min[1]; @@ -551,11 +548,10 @@ namespace jcv if (y2 < pymin) { y2 = pymin; } x2 = (e->c) - (e->b) * y2; // Never occurs according to lcov - //if (((x1 > pxmax) & (x2 > pxmax)) | ((x1 < pxmin) & (x2 < pxmin))) - //{ - // std::cout << "Yes it does" << std::endl; - // return 0; - //} + // if (((x1 > pxmax) & (x2 > pxmax)) | ((x1 < pxmin) & (x2 < pxmin))) + // { + // return 0; + // } if (x1 > pxmax) { x1 = pxmax; y1 = (e->c - x1) / e->b; @@ -582,10 +578,9 @@ namespace jcv if (x2 < pxmin) { x2 = pxmin; } y2 = e->c - e->a * x2; // Never occurs according to lcov - //if (((y1 > pymax) & (y2 > pymax)) | ((y1 < pymin) & (y2 < pymin))) { - // std::cout << "Yes it does" << std::endl; - // return 0; - //} + // if (((y1 > pymax) & (y2 > pymax)) | ((y1 < pymin) & (y2 < pymin))) { + // return 0; + // } if (y1 > pymax) { y1 = pymax; x1 = (e->c - y1) / e->a; @@ -607,8 +602,6 @@ namespace jcv e->pos[1][0] = x2; e->pos[1][1] = y2; - //std::cout << "After boxshape_clip, e is " << e->pos[0] << "--" << e->pos[1] << std::endl; - // If the two points are equal, the result is invalid return (x1 == x2 && y1 == y2) ? 0 : 1; } @@ -984,8 +977,6 @@ namespace jcv ge->pos[flip] = e->pos[i]; ge->pos[1-flip] = e->pos[1-i]; ge->angle = calc_sort_metric(e->sites[i], ge); - - // std::cout << "finishline: Insert graphedge " << ge->pos[0] << "--" << ge->pos[1] << std::endl; sortedges_insert (e->sites[i], ge); } } @@ -1141,9 +1132,7 @@ namespace jcv static void fillgaps (diagram* diagram) { context_internal* internal = diagram->internal; - if (!internal->clipper_.fill_fn) { return; } - for (int i = 0; i < internal->numsites; ++i) { site* site = &internal->sites[i]; // Call fill fn for each site From 982b110d2b378af4f7442149ce7dff7669bb9e37 Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 20 Jan 2026 10:44:57 +0000 Subject: [PATCH 44/53] polygon clipping is now in jc_voronoi.h --- mplot/jcvoronoi/jc_voronoi_clip.h | 379 ------------------------------ 1 file changed, 379 deletions(-) delete mode 100644 mplot/jcvoronoi/jc_voronoi_clip.h diff --git a/mplot/jcvoronoi/jc_voronoi_clip.h b/mplot/jcvoronoi/jc_voronoi_clip.h deleted file mode 100644 index 1a2a9fa4..00000000 --- a/mplot/jcvoronoi/jc_voronoi_clip.h +++ /dev/null @@ -1,379 +0,0 @@ -// Note: This will need to be brought in-line with changes in jc_voronoi.h made as part of Seb's -// mathplot work. - -// Copyright (c) 2019 Mathias Westerdahl -// For full LICENSE (MIT) or USAGE, see bottom of file - -#ifndef JC_VORONOI_CLIP_H -#define JC_VORONOI_CLIP_H - -#include "jc_voronoi.h" - -#pragma pack(push, 1) - -typedef struct jcv_clipping_polygon_ -{ - jcv_point* points; - int num_points; -} jcv_clipping_polygon; - -#pragma pack(pop) - - -// Convex polygon clip functions -int jcv_clip_polygon_test_point(const jcv_clipper* clipper, const jcv_point p); -int jcv_clip_polygon_clip_edge(const jcv_clipper* clipper, jcv_edge* e); -void jcv_clip_polygon_fill_gaps(const jcv_clipper* clipper, jcv_context_internal* allocator, jcv_site* site); - - -#endif // JC_VORONOI_CLIP_H - -#ifdef JC_VORONOI_CLIP_IMPLEMENTATION -#undef JC_VORONOI_CLIP_IMPLEMENTATION - -// These helpers will probably end up in the main library - -static inline jcv_real jcv_cross(const jcv_point a, const jcv_point b) { - return a.x * b.y - a.y * b.x; -} - -static inline jcv_point jcv_add(jcv_point a, jcv_point b) { - jcv_point r; - r.x = a.x + b.x; - r.y = a.y + b.y; - return r; -} - -static inline jcv_point jcv_sub(jcv_point a, jcv_point b) { - jcv_point r; - r.x = a.x - b.x; - r.y = a.y - b.y; - return r; -} - -static inline jcv_point jcv_mul(jcv_point v, jcv_real s) { - jcv_point r; - r.x = v.x * s; - r.y = v.y * s; - return r; -} - -static inline jcv_point jcv_mix(jcv_point a, jcv_point b, jcv_real t) { - jcv_point r; - r.x = a.x + (b.x - a.x) * t; - r.y = a.y + (b.y - a.y) * t; - return r; -} - -static inline jcv_real jcv_dot(jcv_point a, jcv_point b) { - return a.x * b.x + a.y * b.y; -} - - -static inline jcv_real jcv_length(jcv_point v) { - return JCV_SQRT(v.x*v.x + v.y*v.y); -} - -static inline jcv_real jcv_length_sq(jcv_point v) { - return v.x*v.x + v.y*v.y; -} - -static inline jcv_real jcv_fabs(jcv_real a) { - return a < 0 ? -a : a; -} - -// if it returns [0.0, 1.0] it's on the line segment -static inline jcv_real jcv_point_to_line_segment_t(jcv_point p, jcv_point p0, jcv_point p1) { - jcv_point vpoint = jcv_sub(p, p0); - jcv_point vsegment = jcv_sub(p1, p0); - return jcv_dot(vsegment, vpoint) / jcv_dot(vsegment, vsegment); -} - -int jcv_clip_polygon_test_point(const jcv_clipper* clipper, const jcv_point p) -{ - jcv_clipping_polygon* polygon = (jcv_clipping_polygon*)clipper->ctx; - int num_points = polygon->num_points; - - // convex polygon - // winding CCW - // all polygon normals point outward - // if the point is in front of the plane, it is outside - - int result = 1; - for (int i = 0; i < num_points; ++i) - { - jcv_point p0 = polygon->points[i]; - jcv_point p1 = polygon->points[(i+1)%num_points]; - jcv_point n; - n.x = p1.y - p0.y; - n.y = p0.x - p1.x; - jcv_point diff; - diff.x = p.x - p0.x; - diff.y = p.y - p0.y; - - if (jcv_dot(n, diff) > 0) { - result = 0; - break; - } - } - return result; -} - -static int jcv_ray_intersect_polygon(const jcv_clipper* clipper, jcv_point p0, jcv_point p1, jcv_real* out_t0, jcv_real* out_t1) -{ - jcv_clipping_polygon* polygon = (jcv_clipping_polygon*)clipper->ctx; - int num_points = polygon->num_points; - - jcv_real t0 = (jcv_real)0; - jcv_real t1 = (jcv_real)1; - jcv_point dir = jcv_sub(p1, p0); - - for (int i = 0; i < num_points; ++i) - { - jcv_point v0 = polygon->points[i]; - jcv_point v1 = polygon->points[(i+1)%num_points]; - jcv_point n; - n.x = v1.y - v0.y; - n.y = -(v1.x - v0.x); - - jcv_point v0p0 = jcv_sub(p0, v0); - - jcv_real N = -jcv_dot(v0p0, n); - jcv_real D = jcv_dot(dir, n); - if (jcv_fabs(D) < 0.0001f) // parallel to the line - { - if (N < 0) - return 0; - continue; - } - - jcv_real t = N / D; - if (D < 0) // -> entering - { - t0 = t > t0 ? t : t0; - if (t0 > t1) - return 0; - } - else // D > 0 -> exiting - { - t1 = t < t1 ? t : t1; - if (t1 < t0) - return 0; - } - } - - *out_t0 = t0; - *out_t1 = t1; - return 1; -} - -int jcv_clip_polygon_clip_edge(const jcv_clipper* clipper, jcv_edge* e) -{ - // Using the box clipper to get a finite line segment - int result = jcv_boxshape_clip(clipper, e); - if (!result) - return 0; - - jcv_point p0 = e->pos[0]; - jcv_point p1 = e->pos[1]; - - jcv_real t0; - jcv_real t1; - result = jcv_ray_intersect_polygon(clipper, p0, p1, &t0, &t1); - - if (!result) { - e->pos[0] = e->pos[1]; - return 0; - } - - e->pos[0] = jcv_mix(p0, p1, t0); - e->pos[1] = jcv_mix(p0, p1, t1); - return 1; -} - -// Find the edge which the point sits on -static int jcv_find_polygon_edge(const jcv_clipper* clipper, jcv_point p) -{ - jcv_clipping_polygon* polygon = (jcv_clipping_polygon*)clipper->ctx; - - int min_edge = -1; - jcv_real min_dist = JCV_FLT_MAX; - int num_points = polygon->num_points; - for (int i = 0; i < num_points; ++i) - { - jcv_point p0 = polygon->points[i]; - if (jcv_point_eq(&p, &p0)) - return i; - - jcv_point p1 = polygon->points[(i+1)%num_points]; - jcv_point vsegment = jcv_sub(p1, p0); - jcv_point vpoint = jcv_sub(p, p0); - - jcv_real t = jcv_dot(vsegment, vpoint) / jcv_dot(vsegment,vsegment); - - if (t < (jcv_real)0.0f || t > (jcv_real)1.0f) - continue; - - jcv_point projected = jcv_add(p0, jcv_mul(vsegment, t)); - jcv_real distsq = jcv_length_sq(jcv_sub(p, projected)); - - if (distsq < min_dist) { - min_dist = distsq; - min_edge = i; - } - } - assert(min_edge >= 0); - return min_edge; -} - -void jcv_clip_polygon_fill_gaps(const jcv_clipper* clipper, jcv_context_internal* allocator, jcv_site* site) -{ - // They're sorted CCW, so if the current->pos[1] != next->pos[0], then we have a gap - jcv_clipping_polygon* polygon = (jcv_clipping_polygon*)clipper->ctx; - int num_points = polygon->num_points; - - jcv_graphedge* current = site->edges; - if( !current ) - { - jcv_graphedge* gap = jcv_alloc_graphedge(allocator); - gap->neighbor = 0; - // Pick the first edge of the polygon (which is also CCW) - gap->pos[0] = polygon->points[0]; - gap->pos[1] = polygon->points[1]; - gap->angle = jcv_calc_sort_metric(site, gap); - gap->next = 0; - gap->edge = jcv_create_gap_edge(allocator, site, gap); - - current = gap; - site->edges = gap; - } - - jcv_graphedge* next = current->next; - if( !next ) - { - jcv_graphedge* gap = jcv_alloc_graphedge(allocator); - - int polygon_edge = jcv_find_polygon_edge(clipper, current->pos[1]); - if (!jcv_point_eq(¤t->pos[1], &polygon->points[(polygon_edge+1)%num_points])) { - gap->pos[0] = current->pos[1]; - gap->pos[1] = polygon->points[(polygon_edge+1)%num_points]; - } else { - gap->pos[0] = polygon->points[(polygon_edge+1)%num_points]; - gap->pos[1] = polygon->points[(polygon_edge+2)%num_points]; - } - - gap->neighbor = 0; - gap->angle = jcv_calc_sort_metric(site, gap); - gap->next = 0; - gap->edge = jcv_create_gap_edge(allocator, site, gap); - - gap->next = current->next; - current->next = gap; - current = gap; - next = site->edges; - } - - while (current && next) - { - if (!jcv_point_eq(¤t->pos[1], &next->pos[0])) - { - int polygon_edge1 = jcv_find_polygon_edge(clipper, current->pos[1]); - int polygon_edge2 = jcv_find_polygon_edge(clipper, next->pos[0]); - - jcv_graphedge* gap = jcv_alloc_graphedge(allocator); - gap->pos[0] = current->pos[1]; - - if (polygon_edge1 != polygon_edge2) { - gap->pos[1] = polygon->points[(polygon_edge1+1)%num_points]; - } else { - gap->pos[1] = next->pos[0]; - } - - gap->neighbor = 0; - gap->angle = jcv_calc_sort_metric(site, gap); - gap->edge = jcv_create_gap_edge(allocator, site, gap); - gap->next = current->next; - current->next = gap; - } - - current = current->next; - if( current ) - { - next = current->next; - if( !next ) { - next = site->edges; - } - } - } -} - -#endif // JC_VORONOI_CLIP_IMPLEMENTATION - - -/* - -ABOUT: - - Helper functions for clipping a vosonoi diagram against a convex polygon - - -LICENSE: - - The MIT License (MIT) - - Copyright (c) 2019 Mathias Westerdahl - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - - -DISCLAIMER: - - This software is supplied "AS IS" without any warranties and support - -USAGE: - -USAGE: - - The function `jcv_clipper` struct allows for supplying a set of custom clipper functions to interact with the generating of the resulting diagram. - - #define JC_VORONOI_CLIP_IMPLEMENTATION - #include "jc_voronoi_clip.h" - - jcv_clipping_polygon polygon; - // Triangle - polygon.num_points = 3; - polygon.points = (jcv_point*)malloc(sizeof(jcv_point)*(size_t)polygon.num_points); - - polygon.points[0].x = width/2; - polygon.points[1].x = width - width/5; - polygon.points[2].x = width/5; - polygon.points[0].y = height/5; - polygon.points[1].y = height - height/5; - polygon.points[2].y = height - height/5; - - jcv_clipper polygonclipper; - polygonclipper.test_fn = jcv_clip_polygon_test_point; - polygonclipper.clip_fn = jcv_clip_polygon_clip_edge; - polygonclipper.fill_fn = jcv_clip_polygon_fill_gaps; - polygonclipper.ctx = &polygon; - - jcv_diagram diagram; - memset(&diagram, 0, sizeof(jcv_diagram)); - jcv_diagram_generate(count, (const jcv_point*)points, 0, clipper, &diagram); -*/ From 17844462775f75acce1342521f850ce970169296 Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 20 Jan 2026 10:45:19 +0000 Subject: [PATCH 45/53] There's no file to add --- mplot/jcvoronoi/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mplot/jcvoronoi/CMakeLists.txt b/mplot/jcvoronoi/CMakeLists.txt index cd5f3abf..91e79358 100644 --- a/mplot/jcvoronoi/CMakeLists.txt +++ b/mplot/jcvoronoi/CMakeLists.txt @@ -1 +1 @@ -install(FILES jc_voronoi_clip.h jc_voronoi.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/mplot/jcvoronoi) +install(FILES jc_voronoi.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/mplot/jcvoronoi) From 4a4645241a3f2b145c9b0393623772b6b2ae4c9a Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 20 Jan 2026 10:47:47 +0000 Subject: [PATCH 46/53] These functions are unused --- mplot/jcvoronoi/jc_voronoi.h | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 293ab892..d35dd939 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -233,29 +233,6 @@ namespace jcv return (pt1->y() == pt2->y()) ? (pt1->x() < pt2->x()) : pt1->y() < pt2->y(); } - [[maybe_unused]] - static int point_on_box_edge (const point* pt, const point* min, const point* max) - { - return pt->x() == min->x() || pt->y() == min->y() || pt->x() == max->x() || pt->y() == max->y(); - } - - static point mix (point a, point b, T t) - { - point r = {}; - r[0] = a[0] + (b[0] - a[0]) * t; - r[1] = a[1] + (b[1] - a[1]) * t; - return r; - } - - // if it returns [0.0, 1.0] it's on the line segment - static T point_to_line_segment_t (point p, point p0, point p1) - { - point vpoint = p - p0; - point vsegment = p1 - p0; - return vsegment.dot (vpoint) / vsegment.dot (vsegment); - } - - // edges and corners static const int EDGE_LEFT = 1; static const int EDGE_RIGHT = 2; From 22350e5a8d0af84a6d061a79515335ca33e9097a Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 20 Jan 2026 10:49:05 +0000 Subject: [PATCH 47/53] Minimise diffs --- mplot/jcvoronoi/jc_voronoi.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index d35dd939..ba317437 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -555,9 +555,7 @@ namespace jcv if (x2 < pxmin) { x2 = pxmin; } y2 = e->c - e->a * x2; // Never occurs according to lcov - // if (((y1 > pymax) & (y2 > pymax)) | ((y1 < pymin) & (y2 < pymin))) { - // return 0; - // } + // if (((y1 > pymax) & (y2 > pymax)) | ((y1 < pymin) & (y2 < pymin))) { return 0; } if (y1 > pymax) { y1 = pymax; x1 = (e->c - y1) / e->a; From f4a6178c190d09c36a8744497419e3918264e049 Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 20 Jan 2026 10:54:26 +0000 Subject: [PATCH 48/53] No need for mat22 --- mplot/VoronoiVisual.h | 1 - 1 file changed, 1 deletion(-) diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index 335a5eb2..72965e28 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -21,7 +21,6 @@ #include #include #include -#include #include #include From 4ad2c643711d99f0b0139fa35f05f533274bd150 Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 20 Jan 2026 11:16:36 +0000 Subject: [PATCH 49/53] Updates to trace boundary --- examples/screenshots/trace_boundary.png | Bin 0 -> 103287 bytes examples/trace_boundary.cpp | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 examples/screenshots/trace_boundary.png diff --git a/examples/screenshots/trace_boundary.png b/examples/screenshots/trace_boundary.png new file mode 100644 index 0000000000000000000000000000000000000000..bd761e6f72a3ed4daf9d283fc0ae1fcf6e06ec6c GIT binary patch literal 103287 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

+Iyfwl7r_3}q0a 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 literal 0 HcmV?d00001 diff --git a/examples/trace_boundary.cpp b/examples/trace_boundary.cpp index 5625b82f..c767388c 100644 --- a/examples/trace_boundary.cpp +++ b/examples/trace_boundary.cpp @@ -1,5 +1,6 @@ /* - * Trace around a boundary of points in 2D + * Trace around a boundary of points in 2D. This code uses Graham's scan to compute the convex hull + * of a set of randomly generated points. */ #include @@ -16,7 +17,7 @@ int main() { // Get ready to graph the data - mplot::Visual v(1024, 768, "Boundary tracing"); + mplot::Visual v(1024, 768, "Graham's scan computes the convex hull"); mplot::DatasetStyle ds (mplot::stylepolicy::markers); auto gv = std::make_unique> (sm::vec{-0.5f,-0.5f,0.0f}); v.bindmodel (gv); @@ -24,23 +25,22 @@ int main() constexpr unsigned int n_points = 200; // Create data - sm::rand_uniform rngxy(-0.8f, 0.8f, n_points); + sm::rand_uniform rngxy(-0.8f, 0.8f); sm::vvec> points(n_points); for (unsigned int i = 0; i < n_points; ++i) { points[i] = { rngxy.get(), rngxy.get() }; } // Trace algorithm sm::vvec> boundary = sm::geometry::graham_scan (points); + // Close the boundary + boundary.push_back (boundary.front()); - gv->setlimits (-1.5, 1.5, -1.5, 1.5); + gv->setlimits (-1, 1, -1, 1); gv->setdata (points, ds); - ds.showlines = true; - ds.linecolour = mplot::colour::crimson; - ds.markercolour = mplot::colour::black; - ds.markerstyle = mplot::markerstyle::circle; - ds.markersize *= 1.2f; - gv->setdata (boundary, ds); + mplot::DatasetStyle ds2 (mplot::stylepolicy::lines); + ds2.linecolour = mplot::colour::crimson; + gv->setdata (boundary, ds2); gv->finalize(); v.addVisualModel (gv); From 6b722b6a53bbbe2369f97e2342766d933d522c47 Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 20 Jan 2026 11:22:33 +0000 Subject: [PATCH 50/53] Updates to ellipse_pca --- examples/ellipse_pca.cpp | 4 ++-- examples/ellipse_pca.png | Bin 0 -> 56088 bytes examples/trace_boundary.cpp | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) create mode 100644 examples/ellipse_pca.png diff --git a/examples/ellipse_pca.cpp b/examples/ellipse_pca.cpp index 42822bdc..ba87d025 100644 --- a/examples/ellipse_pca.cpp +++ b/examples/ellipse_pca.cpp @@ -27,13 +27,13 @@ int main() } // Graph the data - mplot::Visual v(1024, 768, "Principle component analysis"); + mplot::Visual v(1024, 768, "Principle component analysis with armadillo"); // Create a GraphVisual object (obtaining a unique_ptr to the object) with a spatial offset within the scene of 0,0,0 auto gv = std::make_unique> (sm::vec{-0.5f,-0.5f,0.0f}); mplot::DatasetStyle ds (mplot::stylepolicy::markers); ds.datalabel = std::string("data"); v.bindmodel (gv); - gv->setlimits (-5, 5, -5, 5); + gv->setlimits (-8, 8, -8, 8); gv->setdata (_x, ds); std::cout << "\narma gives:\n"; diff --git a/examples/ellipse_pca.png b/examples/ellipse_pca.png new file mode 100644 index 0000000000000000000000000000000000000000..852294719c2f6fd3bda734d8d5ec5de8a479ceef GIT binary patch literal 56088 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 X)|~O%q4cSqoSxCuGSs}QZgb*)t^#*1 literal 0 HcmV?d00001 diff --git a/examples/trace_boundary.cpp b/examples/trace_boundary.cpp index c767388c..6177c7f8 100644 --- a/examples/trace_boundary.cpp +++ b/examples/trace_boundary.cpp @@ -13,7 +13,6 @@ #include #include - int main() { // Get ready to graph the data From 77201680d31ea01046eb531e3882c1756ee2b90e Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 20 Jan 2026 11:23:51 +0000 Subject: [PATCH 51/53] A voronoi boundary screenshot --- examples/screenshots/voronoi_boundary.png | Bin 0 -> 123268 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/screenshots/voronoi_boundary.png diff --git a/examples/screenshots/voronoi_boundary.png b/examples/screenshots/voronoi_boundary.png new file mode 100644 index 0000000000000000000000000000000000000000..b5265250a5e29e2ae0fa453a913bd221288f7ee9 GIT binary patch literal 123268 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 t3qwD1${{;}qn^OP) literal 0 HcmV?d00001 From 260160ddae9c814f90cdf9c177c9143f7f15a16f Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 20 Jan 2026 11:31:29 +0000 Subject: [PATCH 52/53] Moves an png and adds a missing png --- .../draw_triangles_intersections.png | Bin 0 -> 123818 bytes examples/{ => screenshots}/ellipse_pca.png | Bin 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/screenshots/draw_triangles_intersections.png rename examples/{ => screenshots}/ellipse_pca.png (100%) diff --git a/examples/screenshots/draw_triangles_intersections.png b/examples/screenshots/draw_triangles_intersections.png new file mode 100644 index 0000000000000000000000000000000000000000..d5dff0191d7c612f6ece1bb3178cb54b066da9e0 GIT binary patch literal 123818 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%0D5@xz`(|n`B@)%SQ@t&0hrvRG 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 C33&Se literal 0 HcmV?d00001 diff --git a/examples/ellipse_pca.png b/examples/screenshots/ellipse_pca.png similarity index 100% rename from examples/ellipse_pca.png rename to examples/screenshots/ellipse_pca.png From 8e5854449191a06382b3cbb5cb0482b16bf44c92 Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 20 Jan 2026 11:37:25 +0000 Subject: [PATCH 53/53] Adds new screenshots for added examples --- docs/index.md | 84 ++++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/docs/index.md b/docs/index.md index 97ec5304..e22c456a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -18,44 +18,46 @@ Jump over to the [**mathplot reference section**](/mathplot/ref) These are screenshots from the many [mathplot examples](https://github.com/sebsjames/mathplot/tree/main/examples) -| ![visual](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/visual.png?raw=true) [visual](https://github.com/sebsjames/mathplot/blob/main/examples/visual.cpp)| ![vvec_rgb_gaussians](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/vvec_rgb_gaussians.png?raw=true) [vvec_rgb_gaussians](https://github.com/sebsjames/mathplot/blob/main/examples/vvec_rgb_gaussians.cpp)| ![graph_logist2](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_logist2.png?raw=true) [graph_logist2](https://github.com/sebsjames/mathplot/blob/main/examples/graph_logist2.cpp)| -| ![rectangle](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rectangle.png?raw=true) [rectangle](https://github.com/sebsjames/mathplot/blob/main/examples/rectangle.cpp)| ![graph_bar](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_bar.png?raw=true) [graph_bar](https://github.com/sebsjames/mathplot/blob/main/examples/graph_bar.cpp)| ![voronoi_fixed_xz](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_fixed_xz.png?raw=true) [voronoi_fixed_xz](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_fixed_xz.cpp)| -| ![graph_rightaxis](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_rightaxis.png?raw=true) [graph_rightaxis](https://github.com/sebsjames/mathplot/blob/main/examples/graph_rightaxis.cpp)| ![icosahedron](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/icosahedron.png?raw=true) [icosahedron](https://github.com/sebsjames/mathplot/blob/main/examples/icosahedron.cpp)| ![hexgrid_image_rect](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/hexgrid_image_rect.png?raw=true) [hexgrid_image_rect](https://github.com/sebsjames/mathplot/blob/main/examples/hexgrid_image_rect.cpp)| -| ![voronoi_function_flat](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_function_flat.png?raw=true) [voronoi_function_flat](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_function_flat.cpp)| ![scatter_hex_mercator](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/scatter_hex_mercator.png?raw=true) [scatter_hex_mercator](https://github.com/sebsjames/mathplot/blob/main/examples/scatter_hex_mercator.cpp)| ![cray_eye](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/cray_eye.png?raw=true) [cray_eye](https://github.com/sebsjames/mathplot/blob/main/examples/cray_eye.cpp)| -| ![hexgrid_image_onsphere](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/hexgrid_image_onsphere.png?raw=true) [hexgrid_image_onsphere](https://github.com/sebsjames/mathplot/blob/main/examples/hexgrid_image_onsphere.cpp)| ![quiver](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/quiver.png?raw=true) [quiver](https://github.com/sebsjames/mathplot/blob/main/examples/quiver.cpp)| ![vvec_convolve](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/vvec_convolve.png?raw=true) [vvec_convolve](https://github.com/sebsjames/mathplot/blob/main/examples/vvec_convolve.cpp)| -| ![geodesic](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/geodesic.png?raw=true) [geodesic](https://github.com/sebsjames/mathplot/blob/main/examples/geodesic.cpp)| ![bootstrap](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/bootstrap.png?raw=true) [bootstrap](https://github.com/sebsjames/mathplot/blob/main/examples/bootstrap.cpp)| ![cyclic_colour](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/cyclic_colour.png?raw=true) [cyclic_colour](https://github.com/sebsjames/mathplot/blob/main/examples/cyclic_colour.cpp)| -| ![voronoi_fixed_nearlyz](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_fixed_nearlyz.png?raw=true) [voronoi_fixed_nearlyz](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_fixed_nearlyz.cpp)| ![randvec](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/randvec.png?raw=true) [randvec](https://github.com/sebsjames/mathplot/blob/main/examples/randvec.cpp)| ![linregr](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/linregr.png?raw=true) [linregr](https://github.com/sebsjames/mathplot/blob/main/examples/linregr.cpp)| -| ![graph_logist](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_logist.png?raw=true) [graph_logist](https://github.com/sebsjames/mathplot/blob/main/examples/graph_logist.cpp)| ![lighting_test](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/lighting_test.png?raw=true) [lighting_test](https://github.com/sebsjames/mathplot/blob/main/examples/lighting_test.cpp)| ![colourbar](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourbar.png?raw=true) [colourbar](https://github.com/sebsjames/mathplot/blob/main/examples/colourbar.cpp)| -| ![draw_triangles](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/draw_triangles.png?raw=true) [draw_triangles](https://github.com/sebsjames/mathplot/blob/main/examples/draw_triangles.cpp)| ![showcase](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/showcase.png?raw=true) [showcase](https://github.com/sebsjames/mathplot/blob/main/examples/showcase.cpp)| ![graph_incoming_data_rescale](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_incoming_data_rescale.png?raw=true) [graph_incoming_data_rescale](https://github.com/sebsjames/mathplot/blob/main/examples/graph_incoming_data_rescale.cpp)| -| ![izhikevich](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/izhikevich.png?raw=true) [izhikevich](https://github.com/sebsjames/mathplot/blob/main/examples/izhikevich.cpp)| ![lines](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/lines.png?raw=true) [lines](https://github.com/sebsjames/mathplot/blob/main/examples/lines.cpp)| ![graph_fouraxes](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_fouraxes.png?raw=true) [graph_fouraxes](https://github.com/sebsjames/mathplot/blob/main/examples/graph_fouraxes.cpp)| -| ![helloworld](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/helloworld.png?raw=true) [helloworld](https://github.com/sebsjames/mathplot/blob/main/examples/helloworld.cpp)| ![graph_histo](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_histo.png?raw=true) [graph_histo](https://github.com/sebsjames/mathplot/blob/main/examples/graph_histo.cpp)| ![grid_border](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grid_border.png?raw=true) [grid_border](https://github.com/sebsjames/mathplot/blob/main/examples/grid_border.cpp)| -| ![hsvwheel](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/hsvwheel.png?raw=true) [hsvwheel](https://github.com/sebsjames/mathplot/blob/main/examples/hsvwheel.cpp)| ![scatter_ico](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/scatter_ico.png?raw=true) [scatter_ico](https://github.com/sebsjames/mathplot/blob/main/examples/scatter_ico.cpp)| ![cubetrans2](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/cubetrans2.png?raw=true) [cubetrans2](https://github.com/sebsjames/mathplot/blob/main/examples/cubetrans2.cpp)| -| ![vectorvis](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/vectorvis.png?raw=true) [vectorvis](https://github.com/sebsjames/mathplot/blob/main/examples/vectorvis.cpp)| ![colourmaps_cet](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_cet.png?raw=true) [colourmaps_cet](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_cet.cpp)| ![voronoi_random](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_random.png?raw=true) [voronoi_random](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_random.cpp)| -| ![logisticmap](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/logisticmap.png?raw=true) [logisticmap](https://github.com/sebsjames/mathplot/blob/main/examples/logisticmap.cpp)| ![polar](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/polar.png?raw=true) [polar](https://github.com/sebsjames/mathplot/blob/main/examples/polar.cpp)| ![graph_incoming_data](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_incoming_data.png?raw=true) [graph_incoming_data](https://github.com/sebsjames/mathplot/blob/main/examples/graph_incoming_data.cpp)| -| ![scatter_instanced](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/scatter_instanced.png?raw=true) [scatter_instanced](https://github.com/sebsjames/mathplot/blob/main/examples/scatter_instanced.cpp)| ![graph_line_xcross](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_line_xcross.png?raw=true) [graph_line_xcross](https://github.com/sebsjames/mathplot/blob/main/examples/graph_line_xcross.cpp)| ![graph_dynamic_x2](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_dynamic_x2.png?raw=true) [graph_dynamic_x2](https://github.com/sebsjames/mathplot/blob/main/examples/graph_dynamic_x2.cpp)| -| ![duochrome](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/duochrome.png?raw=true) [duochrome](https://github.com/sebsjames/mathplot/blob/main/examples/duochrome.cpp)| ![fps](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/fps.png?raw=true) [fps](https://github.com/sebsjames/mathplot/blob/main/examples/fps.cpp)| ![anneal_asa](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/anneal_asa.png?raw=true) [anneal_asa](https://github.com/sebsjames/mathplot/blob/main/examples/anneal_asa.cpp)| -| ![curvytelly_pipe](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/curvytelly_pipe.png?raw=true) [curvytelly_pipe](https://github.com/sebsjames/mathplot/blob/main/examples/curvytelly_pipe.cpp)| ![grid_simple](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grid_simple.png?raw=true) [grid_simple](https://github.com/sebsjames/mathplot/blob/main/examples/grid_simple.cpp)| ![grating](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grating.png?raw=true) [grating](https://github.com/sebsjames/mathplot/blob/main/examples/grating.cpp)| -| ![curvytelly](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/curvytelly.png?raw=true) [curvytelly](https://github.com/sebsjames/mathplot/blob/main/examples/curvytelly.cpp)| ![coordarrows](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/coordarrows.png?raw=true) [coordarrows](https://github.com/sebsjames/mathplot/blob/main/examples/coordarrows.cpp)| ![rhombo_scene](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rhombo_scene.png?raw=true) [rhombo_scene](https://github.com/sebsjames/mathplot/blob/main/examples/rhombo_scene.cpp)| -| ![zernike_radial](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/zernike_radial.png?raw=true) [zernike_radial](https://github.com/sebsjames/mathplot/blob/main/examples/zernike_radial.cpp)| ![line](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/line.png?raw=true) [line](https://github.com/sebsjames/mathplot/blob/main/examples/line.cpp)| ![colourmap_test](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmap_test.png?raw=true) [colourmap_test](https://github.com/sebsjames/mathplot/blob/main/examples/colourmap_test.cpp)| -| ![colourmaps_crameri](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_crameri.png?raw=true) [colourmaps_crameri](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_crameri.cpp)| ![cartgrid](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/cartgrid.png?raw=true) [cartgrid](https://github.com/sebsjames/mathplot/blob/main/examples/cartgrid.cpp)| ![graph_coords](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_coords.png?raw=true) [graph_coords](https://github.com/sebsjames/mathplot/blob/main/examples/graph_coords.cpp)| -| ![hexgrid_image](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/hexgrid_image.png?raw=true) [hexgrid_image](https://github.com/sebsjames/mathplot/blob/main/examples/hexgrid_image.cpp)| ![cube](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/cube.png?raw=true) [cube](https://github.com/sebsjames/mathplot/blob/main/examples/cube.cpp)| ![rhombo](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rhombo.png?raw=true) [rhombo](https://github.com/sebsjames/mathplot/blob/main/examples/rhombo.cpp)| -| ![colourmaps_other](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_other.png?raw=true) [colourmaps_other](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_other.cpp)| ![vvec_gauss](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/vvec_gauss.png?raw=true) [vvec_gauss](https://github.com/sebsjames/mathplot/blob/main/examples/vvec_gauss.cpp)| ![rosenbrock_asa](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rosenbrock_asa.png?raw=true) [rosenbrock_asa](https://github.com/sebsjames/mathplot/blob/main/examples/rosenbrock_asa.cpp)| -| ![model_crawler](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/model_crawler.png?raw=true) [model_crawler](https://github.com/sebsjames/mathplot/blob/main/examples/model_crawler.cpp)| ![graph_twinax](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_twinax.png?raw=true) [graph_twinax](https://github.com/sebsjames/mathplot/blob/main/examples/graph_twinax.cpp)| ![colourmaps_desaturating](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_desaturating.png?raw=true) [colourmaps_desaturating](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_desaturating.cpp)| -| ![grid_flat_dynamic](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grid_flat_dynamic.png?raw=true) [grid_flat_dynamic](https://github.com/sebsjames/mathplot/blob/main/examples/grid_flat_dynamic.cpp)| ![helloversion](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/helloversion.png?raw=true) [helloversion](https://github.com/sebsjames/mathplot/blob/main/examples/helloversion.cpp)| ![rod](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rod.png?raw=true) [rod](https://github.com/sebsjames/mathplot/blob/main/examples/rod.cpp)| -| ![rhombo_scene2](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rhombo_scene2.png?raw=true) [rhombo_scene2](https://github.com/sebsjames/mathplot/blob/main/examples/rhombo_scene2.cpp)| ![rotating_models](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rotating_models.png?raw=true) [rotating_models](https://github.com/sebsjames/mathplot/blob/main/examples/rotating_models.cpp)| ![zernike](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/zernike.png?raw=true) [zernike](https://github.com/sebsjames/mathplot/blob/main/examples/zernike.cpp)| -| ![colourmap_browser](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmap_browser.png?raw=true) [colourmap_browser](https://github.com/sebsjames/mathplot/blob/main/examples/colourmap_browser.cpp)| ![scatter_geodesic](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/scatter_geodesic.png?raw=true) [scatter_geodesic](https://github.com/sebsjames/mathplot/blob/main/examples/scatter_geodesic.cpp)| ![izhikevich_alt](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/izhikevich_alt.png?raw=true) [izhikevich_alt](https://github.com/sebsjames/mathplot/blob/main/examples/izhikevich_alt.cpp)| -| ![voronoi_vectordata](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_vectordata.png?raw=true) [voronoi_vectordata](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_vectordata.cpp)| ![tri](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/tri.png?raw=true) [tri](https://github.com/sebsjames/mathplot/blob/main/examples/tri.cpp)| ![pointrows](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/pointrows.png?raw=true) [pointrows](https://github.com/sebsjames/mathplot/blob/main/examples/pointrows.cpp)| -| ![quads](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/quads.png?raw=true) [quads](https://github.com/sebsjames/mathplot/blob/main/examples/quads.cpp)| ![voronoi_rectangular](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_rectangular.png?raw=true) [voronoi_rectangular](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_rectangular.cpp)| ![grid_border2](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grid_border2.png?raw=true) [grid_border2](https://github.com/sebsjames/mathplot/blob/main/examples/grid_border2.cpp)| -| ![graph1](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph1.png?raw=true) [graph1](https://github.com/sebsjames/mathplot/blob/main/examples/graph1.cpp)| ![vonmises](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/vonmises.png?raw=true) [vonmises](https://github.com/sebsjames/mathplot/blob/main/examples/vonmises.cpp)| ![breadcrumbs](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/breadcrumbs.png?raw=true) [breadcrumbs](https://github.com/sebsjames/mathplot/blob/main/examples/breadcrumbs.cpp)| -| ![grid_image](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grid_image.png?raw=true) [grid_image](https://github.com/sebsjames/mathplot/blob/main/examples/grid_image.cpp)| ![sphere](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/sphere.png?raw=true) [sphere](https://github.com/sebsjames/mathplot/blob/main/examples/sphere.cpp)| ![convolve_rect](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/convolve_rect.png?raw=true) [convolve_rect](https://github.com/sebsjames/mathplot/blob/main/examples/convolve_rect.cpp)| -| ![hexgrid](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/hexgrid.png?raw=true) [hexgrid](https://github.com/sebsjames/mathplot/blob/main/examples/hexgrid.cpp)| ![twowindows](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/twowindows.png?raw=true) [twowindows](https://github.com/sebsjames/mathplot/blob/main/examples/twowindows.cpp)| ![txt](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/txt.png?raw=true) [txt](https://github.com/sebsjames/mathplot/blob/main/examples/txt.cpp)| -| ![ellipsoid](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/ellipsoid.png?raw=true) [ellipsoid](https://github.com/sebsjames/mathplot/blob/main/examples/ellipsoid.cpp)| ![vvec_smoothgauss](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/vvec_smoothgauss.png?raw=true) [vvec_smoothgauss](https://github.com/sebsjames/mathplot/blob/main/examples/vvec_smoothgauss.cpp)| ![grid_simple_rand](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grid_simple_rand.png?raw=true) [grid_simple_rand](https://github.com/sebsjames/mathplot/blob/main/examples/grid_simple_rand.cpp)| -| ![scatter](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/scatter.png?raw=true) [scatter](https://github.com/sebsjames/mathplot/blob/main/examples/scatter.cpp)| ![ring](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/ring.png?raw=true) [ring](https://github.com/sebsjames/mathplot/blob/main/examples/ring.cpp)| ![sph_to_cart](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/sph_to_cart.png?raw=true) [sph_to_cart](https://github.com/sebsjames/mathplot/blob/main/examples/sph_to_cart.cpp)| -| ![rod_pan](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rod_pan.png?raw=true) [rod_pan](https://github.com/sebsjames/mathplot/blob/main/examples/rod_pan.cpp)| ![colourbar_interactive](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourbar_interactive.png?raw=true) [colourbar_interactive](https://github.com/sebsjames/mathplot/blob/main/examples/colourbar_interactive.cpp)| ![curvytelly_chequered_pipe](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/curvytelly_chequered_pipe.png?raw=true) [curvytelly_chequered_pipe](https://github.com/sebsjames/mathplot/blob/main/examples/curvytelly_chequered_pipe.cpp)| -| ![graph_distributions](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_distributions.png?raw=true) [graph_distributions](https://github.com/sebsjames/mathplot/blob/main/examples/graph_distributions.cpp)| ![rosenbrock](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rosenbrock.png?raw=true) [rosenbrock](https://github.com/sebsjames/mathplot/blob/main/examples/rosenbrock.cpp)| ![cubetrans](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/cubetrans.png?raw=true) [cubetrans](https://github.com/sebsjames/mathplot/blob/main/examples/cubetrans.cpp)| -| ![geodesic_with_normals](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/geodesic_with_normals.png?raw=true) [geodesic_with_normals](https://github.com/sebsjames/mathplot/blob/main/examples/geodesic_with_normals.cpp)| ![voronoi_function](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_function.png?raw=true) [voronoi_function](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_function.cpp)| ![geodesic_ce](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/geodesic_ce.png?raw=true) [geodesic_ce](https://github.com/sebsjames/mathplot/blob/main/examples/geodesic_ce.cpp)| -| ![healpix](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/healpix.png?raw=true) [healpix](https://github.com/sebsjames/mathplot/blob/main/examples/healpix.cpp)| ![convolve](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/convolve.png?raw=true) [convolve](https://github.com/sebsjames/mathplot/blob/main/examples/convolve.cpp)| ![graph_dynamic_sine_rescale](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_dynamic_sine_rescale.png?raw=true) [graph_dynamic_sine_rescale](https://github.com/sebsjames/mathplot/blob/main/examples/graph_dynamic_sine_rescale.cpp)| -| ![rod_with_normals](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rod_with_normals.png?raw=true) [rod_with_normals](https://github.com/sebsjames/mathplot/blob/main/examples/rod_with_normals.cpp)| ![colourmaps_hsv1d](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_hsv1d.png?raw=true) [colourmaps_hsv1d](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_hsv1d.cpp)| ![colourmaps_lenthe](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_lenthe.png?raw=true) [colourmaps_lenthe](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_lenthe.cpp)| -| ![scatter_dynamic](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/scatter_dynamic.png?raw=true) [scatter_dynamic](https://github.com/sebsjames/mathplot/blob/main/examples/scatter_dynamic.cpp)| ![unicode_coordaxes](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/unicode_coordaxes.png?raw=true) [unicode_coordaxes](https://github.com/sebsjames/mathplot/blob/main/examples/unicode_coordaxes.cpp)| ![graph_line](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_line.png?raw=true) [graph_line](https://github.com/sebsjames/mathplot/blob/main/examples/graph_line.cpp)| -| ![colourmaps_mono](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_mono.png?raw=true) [colourmaps_mono](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_mono.cpp)| ![voronoi_fixed](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_fixed.png?raw=true) [voronoi_fixed](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_fixed.cpp)| ![graph_change_xaxis](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_change_xaxis.png?raw=true) [graph_change_xaxis](https://github.com/sebsjames/mathplot/blob/main/examples/graph_change_xaxis.cpp)| -| ![grid_resampled_image](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grid_resampled_image.png?raw=true) [grid_resampled_image](https://github.com/sebsjames/mathplot/blob/main/examples/grid_resampled_image.cpp)| ![cone](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/cone.png?raw=true) [cone](https://github.com/sebsjames/mathplot/blob/main/examples/cone.cpp)| ![colourmaps_matplotlib](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_matplotlib.png?raw=true) [colourmaps_matplotlib](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_matplotlib.cpp)| +| ![icosahedron](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/icosahedron.png?raw=true) [icosahedron](https://github.com/sebsjames/mathplot/blob/main/examples/icosahedron.cpp)| ![convolve_rect](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/convolve_rect.png?raw=true) [convolve_rect](https://github.com/sebsjames/mathplot/blob/main/examples/convolve_rect.cpp)| ![graph_twinax](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_twinax.png?raw=true) [graph_twinax](https://github.com/sebsjames/mathplot/blob/main/examples/graph_twinax.cpp)| +| ![izhikevich](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/izhikevich.png?raw=true) [izhikevich](https://github.com/sebsjames/mathplot/blob/main/examples/izhikevich.cpp)| ![bootstrap](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/bootstrap.png?raw=true) [bootstrap](https://github.com/sebsjames/mathplot/blob/main/examples/bootstrap.cpp)| ![curvytelly](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/curvytelly.png?raw=true) [curvytelly](https://github.com/sebsjames/mathplot/blob/main/examples/curvytelly.cpp)| +| ![hexgrid_image_onsphere](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/hexgrid_image_onsphere.png?raw=true) [hexgrid_image_onsphere](https://github.com/sebsjames/mathplot/blob/main/examples/hexgrid_image_onsphere.cpp)| ![grid_border](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grid_border.png?raw=true) [grid_border](https://github.com/sebsjames/mathplot/blob/main/examples/grid_border.cpp)| ![voronoi_random](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_random.png?raw=true) [voronoi_random](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_random.cpp)| +| ![grid_resampled_image](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grid_resampled_image.png?raw=true) [grid_resampled_image](https://github.com/sebsjames/mathplot/blob/main/examples/grid_resampled_image.cpp)| ![colourmaps_other](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_other.png?raw=true) [colourmaps_other](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_other.cpp)| ![rectangle](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rectangle.png?raw=true) [rectangle](https://github.com/sebsjames/mathplot/blob/main/examples/rectangle.cpp)| +| ![scatter_geodesic](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/scatter_geodesic.png?raw=true) [scatter_geodesic](https://github.com/sebsjames/mathplot/blob/main/examples/scatter_geodesic.cpp)| ![scatter](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/scatter.png?raw=true) [scatter](https://github.com/sebsjames/mathplot/blob/main/examples/scatter.cpp)| ![voronoi_fixed_nearlyz](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_fixed_nearlyz.png?raw=true) [voronoi_fixed_nearlyz](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_fixed_nearlyz.cpp)| +| ![logisticmap](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/logisticmap.png?raw=true) [logisticmap](https://github.com/sebsjames/mathplot/blob/main/examples/logisticmap.cpp)| ![graph_incoming_data](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_incoming_data.png?raw=true) [graph_incoming_data](https://github.com/sebsjames/mathplot/blob/main/examples/graph_incoming_data.cpp)| ![geodesic_ce](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/geodesic_ce.png?raw=true) [geodesic_ce](https://github.com/sebsjames/mathplot/blob/main/examples/geodesic_ce.cpp)| +| ![hexgrid_image_rect](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/hexgrid_image_rect.png?raw=true) [hexgrid_image_rect](https://github.com/sebsjames/mathplot/blob/main/examples/hexgrid_image_rect.cpp)| ![helloworld](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/helloworld.png?raw=true) [helloworld](https://github.com/sebsjames/mathplot/blob/main/examples/helloworld.cpp)| ![graph_bar](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_bar.png?raw=true) [graph_bar](https://github.com/sebsjames/mathplot/blob/main/examples/graph_bar.cpp)| +| ![colourmaps_matplotlib](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_matplotlib.png?raw=true) [colourmaps_matplotlib](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_matplotlib.cpp)| ![grating](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grating.png?raw=true) [grating](https://github.com/sebsjames/mathplot/blob/main/examples/grating.cpp)| ![graph1](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph1.png?raw=true) [graph1](https://github.com/sebsjames/mathplot/blob/main/examples/graph1.cpp)| +| ![voronoi_rectangular](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_rectangular.png?raw=true) [voronoi_rectangular](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_rectangular.cpp)| ![grid_simple_rand](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grid_simple_rand.png?raw=true) [grid_simple_rand](https://github.com/sebsjames/mathplot/blob/main/examples/grid_simple_rand.cpp)| ![quiver](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/quiver.png?raw=true) [quiver](https://github.com/sebsjames/mathplot/blob/main/examples/quiver.cpp)| +| ![scatter_ico](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/scatter_ico.png?raw=true) [scatter_ico](https://github.com/sebsjames/mathplot/blob/main/examples/scatter_ico.cpp)| ![healpix](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/healpix.png?raw=true) [healpix](https://github.com/sebsjames/mathplot/blob/main/examples/healpix.cpp)| ![colourmaps_mono](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_mono.png?raw=true) [colourmaps_mono](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_mono.cpp)| +| ![izhikevich_alt](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/izhikevich_alt.png?raw=true) [izhikevich_alt](https://github.com/sebsjames/mathplot/blob/main/examples/izhikevich_alt.cpp)| ![duochrome](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/duochrome.png?raw=true) [duochrome](https://github.com/sebsjames/mathplot/blob/main/examples/duochrome.cpp)| ![rhombo_scene2](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rhombo_scene2.png?raw=true) [rhombo_scene2](https://github.com/sebsjames/mathplot/blob/main/examples/rhombo_scene2.cpp)| +| ![rotating_models](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rotating_models.png?raw=true) [rotating_models](https://github.com/sebsjames/mathplot/blob/main/examples/rotating_models.cpp)| ![graph_histo](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_histo.png?raw=true) [graph_histo](https://github.com/sebsjames/mathplot/blob/main/examples/graph_histo.cpp)| ![helloversion](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/helloversion.png?raw=true) [helloversion](https://github.com/sebsjames/mathplot/blob/main/examples/helloversion.cpp)| +| ![graph_rightaxis](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_rightaxis.png?raw=true) [graph_rightaxis](https://github.com/sebsjames/mathplot/blob/main/examples/graph_rightaxis.cpp)| ![rod](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rod.png?raw=true) [rod](https://github.com/sebsjames/mathplot/blob/main/examples/rod.cpp)| ![draw_triangles_intersections](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/draw_triangles_intersections.png?raw=true) [draw_triangles_intersections](https://github.com/sebsjames/mathplot/blob/main/examples/draw_triangles_intersections.cpp)| +| ![cubetrans](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/cubetrans.png?raw=true) [cubetrans](https://github.com/sebsjames/mathplot/blob/main/examples/cubetrans.cpp)| ![scatter_dynamic](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/scatter_dynamic.png?raw=true) [scatter_dynamic](https://github.com/sebsjames/mathplot/blob/main/examples/scatter_dynamic.cpp)| ![sph_to_cart](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/sph_to_cart.png?raw=true) [sph_to_cart](https://github.com/sebsjames/mathplot/blob/main/examples/sph_to_cart.cpp)| +| ![rod_pan](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rod_pan.png?raw=true) [rod_pan](https://github.com/sebsjames/mathplot/blob/main/examples/rod_pan.cpp)| ![colourmaps_desaturating](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_desaturating.png?raw=true) [colourmaps_desaturating](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_desaturating.cpp)| ![cubetrans2](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/cubetrans2.png?raw=true) [cubetrans2](https://github.com/sebsjames/mathplot/blob/main/examples/cubetrans2.cpp)| +| ![graph_dynamic_x2](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_dynamic_x2.png?raw=true) [graph_dynamic_x2](https://github.com/sebsjames/mathplot/blob/main/examples/graph_dynamic_x2.cpp)| ![scatter_instanced](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/scatter_instanced.png?raw=true) [scatter_instanced](https://github.com/sebsjames/mathplot/blob/main/examples/scatter_instanced.cpp)| ![rhombo_scene](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rhombo_scene.png?raw=true) [rhombo_scene](https://github.com/sebsjames/mathplot/blob/main/examples/rhombo_scene.cpp)| +| ![tri](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/tri.png?raw=true) [tri](https://github.com/sebsjames/mathplot/blob/main/examples/tri.cpp)| ![graph_fouraxes](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_fouraxes.png?raw=true) [graph_fouraxes](https://github.com/sebsjames/mathplot/blob/main/examples/graph_fouraxes.cpp)| ![cone](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/cone.png?raw=true) [cone](https://github.com/sebsjames/mathplot/blob/main/examples/cone.cpp)| +| ![colourmaps_cet](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_cet.png?raw=true) [colourmaps_cet](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_cet.cpp)| ![grid_image](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grid_image.png?raw=true) [grid_image](https://github.com/sebsjames/mathplot/blob/main/examples/grid_image.cpp)| ![curvytelly_chequered_pipe](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/curvytelly_chequered_pipe.png?raw=true) [curvytelly_chequered_pipe](https://github.com/sebsjames/mathplot/blob/main/examples/curvytelly_chequered_pipe.cpp)| +| ![anneal_asa](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/anneal_asa.png?raw=true) [anneal_asa](https://github.com/sebsjames/mathplot/blob/main/examples/anneal_asa.cpp)| ![graph_incoming_data_rescale](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_incoming_data_rescale.png?raw=true) [graph_incoming_data_rescale](https://github.com/sebsjames/mathplot/blob/main/examples/graph_incoming_data_rescale.cpp)| ![geodesic](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/geodesic.png?raw=true) [geodesic](https://github.com/sebsjames/mathplot/blob/main/examples/geodesic.cpp)| +| ![colourmap_browser](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmap_browser.png?raw=true) [colourmap_browser](https://github.com/sebsjames/mathplot/blob/main/examples/colourmap_browser.cpp)| ![scatter_hex_mercator](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/scatter_hex_mercator.png?raw=true) [scatter_hex_mercator](https://github.com/sebsjames/mathplot/blob/main/examples/scatter_hex_mercator.cpp)| ![unicode_coordaxes](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/unicode_coordaxes.png?raw=true) [unicode_coordaxes](https://github.com/sebsjames/mathplot/blob/main/examples/unicode_coordaxes.cpp)| +| ![vectorvis](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/vectorvis.png?raw=true) [vectorvis](https://github.com/sebsjames/mathplot/blob/main/examples/vectorvis.cpp)| ![colourmaps_hsv1d](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_hsv1d.png?raw=true) [colourmaps_hsv1d](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_hsv1d.cpp)| ![colourmap_test](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmap_test.png?raw=true) [colourmap_test](https://github.com/sebsjames/mathplot/blob/main/examples/colourmap_test.cpp)| +| ![grid_simple](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grid_simple.png?raw=true) [grid_simple](https://github.com/sebsjames/mathplot/blob/main/examples/grid_simple.cpp)| ![cube](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/cube.png?raw=true) [cube](https://github.com/sebsjames/mathplot/blob/main/examples/cube.cpp)| ![vonmises](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/vonmises.png?raw=true) [vonmises](https://github.com/sebsjames/mathplot/blob/main/examples/vonmises.cpp)| +| ![graph_dynamic_sine_rescale](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_dynamic_sine_rescale.png?raw=true) [graph_dynamic_sine_rescale](https://github.com/sebsjames/mathplot/blob/main/examples/graph_dynamic_sine_rescale.cpp)| ![colourmaps_crameri](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_crameri.png?raw=true) [colourmaps_crameri](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_crameri.cpp)| ![vvec_gauss](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/vvec_gauss.png?raw=true) [vvec_gauss](https://github.com/sebsjames/mathplot/blob/main/examples/vvec_gauss.cpp)| +| ![txt](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/txt.png?raw=true) [txt](https://github.com/sebsjames/mathplot/blob/main/examples/txt.cpp)| ![ring](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/ring.png?raw=true) [ring](https://github.com/sebsjames/mathplot/blob/main/examples/ring.cpp)| ![lines](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/lines.png?raw=true) [lines](https://github.com/sebsjames/mathplot/blob/main/examples/lines.cpp)| +| ![vvec_convolve](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/vvec_convolve.png?raw=true) [vvec_convolve](https://github.com/sebsjames/mathplot/blob/main/examples/vvec_convolve.cpp)| ![hexgrid](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/hexgrid.png?raw=true) [hexgrid](https://github.com/sebsjames/mathplot/blob/main/examples/hexgrid.cpp)| ![ellipse_pca](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/ellipse_pca.png?raw=true) [ellipse_pca](https://github.com/sebsjames/mathplot/blob/main/examples/ellipse_pca.cpp)| +| ![graph_logist2](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_logist2.png?raw=true) [graph_logist2](https://github.com/sebsjames/mathplot/blob/main/examples/graph_logist2.cpp)| ![cyclic_colour](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/cyclic_colour.png?raw=true) [cyclic_colour](https://github.com/sebsjames/mathplot/blob/main/examples/cyclic_colour.cpp)| ![graph_line](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_line.png?raw=true) [graph_line](https://github.com/sebsjames/mathplot/blob/main/examples/graph_line.cpp)| +| ![voronoi_vectordata](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_vectordata.png?raw=true) [voronoi_vectordata](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_vectordata.cpp)| ![rod_with_normals](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rod_with_normals.png?raw=true) [rod_with_normals](https://github.com/sebsjames/mathplot/blob/main/examples/rod_with_normals.cpp)| ![colourmaps_lenthe](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourmaps_lenthe.png?raw=true) [colourmaps_lenthe](https://github.com/sebsjames/mathplot/blob/main/examples/colourmaps_lenthe.cpp)| +| ![curvytelly_pipe](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/curvytelly_pipe.png?raw=true) [curvytelly_pipe](https://github.com/sebsjames/mathplot/blob/main/examples/curvytelly_pipe.cpp)| ![visual](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/visual.png?raw=true) [visual](https://github.com/sebsjames/mathplot/blob/main/examples/visual.cpp)| ![graph_coords](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_coords.png?raw=true) [graph_coords](https://github.com/sebsjames/mathplot/blob/main/examples/graph_coords.cpp)| +| ![graph_line_xcross](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_line_xcross.png?raw=true) [graph_line_xcross](https://github.com/sebsjames/mathplot/blob/main/examples/graph_line_xcross.cpp)| ![hsvwheel](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/hsvwheel.png?raw=true) [hsvwheel](https://github.com/sebsjames/mathplot/blob/main/examples/hsvwheel.cpp)| ![colourbar_interactive](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourbar_interactive.png?raw=true) [colourbar_interactive](https://github.com/sebsjames/mathplot/blob/main/examples/colourbar_interactive.cpp)| +| ![hexgrid_image](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/hexgrid_image.png?raw=true) [hexgrid_image](https://github.com/sebsjames/mathplot/blob/main/examples/hexgrid_image.cpp)| ![grid_border2](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grid_border2.png?raw=true) [grid_border2](https://github.com/sebsjames/mathplot/blob/main/examples/grid_border2.cpp)| ![zernike_radial](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/zernike_radial.png?raw=true) [zernike_radial](https://github.com/sebsjames/mathplot/blob/main/examples/zernike_radial.cpp)| +| ![voronoi_function_flat](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_function_flat.png?raw=true) [voronoi_function_flat](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_function_flat.cpp)| ![showcase](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/showcase.png?raw=true) [showcase](https://github.com/sebsjames/mathplot/blob/main/examples/showcase.cpp)| ![zernike](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/zernike.png?raw=true) [zernike](https://github.com/sebsjames/mathplot/blob/main/examples/zernike.cpp)| +| ![graph_change_xaxis](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_change_xaxis.png?raw=true) [graph_change_xaxis](https://github.com/sebsjames/mathplot/blob/main/examples/graph_change_xaxis.cpp)| ![graph_distributions](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_distributions.png?raw=true) [graph_distributions](https://github.com/sebsjames/mathplot/blob/main/examples/graph_distributions.cpp)| ![geodesic_with_normals](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/geodesic_with_normals.png?raw=true) [geodesic_with_normals](https://github.com/sebsjames/mathplot/blob/main/examples/geodesic_with_normals.cpp)| +| ![rosenbrock](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rosenbrock.png?raw=true) [rosenbrock](https://github.com/sebsjames/mathplot/blob/main/examples/rosenbrock.cpp)| ![breadcrumbs](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/breadcrumbs.png?raw=true) [breadcrumbs](https://github.com/sebsjames/mathplot/blob/main/examples/breadcrumbs.cpp)| ![quads](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/quads.png?raw=true) [quads](https://github.com/sebsjames/mathplot/blob/main/examples/quads.cpp)| +| ![cray_eye](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/cray_eye.png?raw=true) [cray_eye](https://github.com/sebsjames/mathplot/blob/main/examples/cray_eye.cpp)| ![graph_logist](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/graph_logist.png?raw=true) [graph_logist](https://github.com/sebsjames/mathplot/blob/main/examples/graph_logist.cpp)| ![cartgrid](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/cartgrid.png?raw=true) [cartgrid](https://github.com/sebsjames/mathplot/blob/main/examples/cartgrid.cpp)| +| ![twowindows](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/twowindows.png?raw=true) [twowindows](https://github.com/sebsjames/mathplot/blob/main/examples/twowindows.cpp)| ![voronoi_fixed_xz](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_fixed_xz.png?raw=true) [voronoi_fixed_xz](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_fixed_xz.cpp)| ![vvec_smoothgauss](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/vvec_smoothgauss.png?raw=true) [vvec_smoothgauss](https://github.com/sebsjames/mathplot/blob/main/examples/vvec_smoothgauss.cpp)| +| ![trace_boundary](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/trace_boundary.png?raw=true) [trace_boundary](https://github.com/sebsjames/mathplot/blob/main/examples/trace_boundary.cpp)| ![polar](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/polar.png?raw=true) [polar](https://github.com/sebsjames/mathplot/blob/main/examples/polar.cpp)| ![lighting_test](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/lighting_test.png?raw=true) [lighting_test](https://github.com/sebsjames/mathplot/blob/main/examples/lighting_test.cpp)| +| ![linregr](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/linregr.png?raw=true) [linregr](https://github.com/sebsjames/mathplot/blob/main/examples/linregr.cpp)| ![voronoi_fixed](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_fixed.png?raw=true) [voronoi_fixed](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_fixed.cpp)| ![rosenbrock_asa](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rosenbrock_asa.png?raw=true) [rosenbrock_asa](https://github.com/sebsjames/mathplot/blob/main/examples/rosenbrock_asa.cpp)| +| ![coordarrows](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/coordarrows.png?raw=true) [coordarrows](https://github.com/sebsjames/mathplot/blob/main/examples/coordarrows.cpp)| ![voronoi_function](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_function.png?raw=true) [voronoi_function](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_function.cpp)| ![ellipsoid](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/ellipsoid.png?raw=true) [ellipsoid](https://github.com/sebsjames/mathplot/blob/main/examples/ellipsoid.cpp)| +| ![colourbar](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/colourbar.png?raw=true) [colourbar](https://github.com/sebsjames/mathplot/blob/main/examples/colourbar.cpp)| ![vvec_rgb_gaussians](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/vvec_rgb_gaussians.png?raw=true) [vvec_rgb_gaussians](https://github.com/sebsjames/mathplot/blob/main/examples/vvec_rgb_gaussians.cpp)| ![line](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/line.png?raw=true) [line](https://github.com/sebsjames/mathplot/blob/main/examples/line.cpp)| +| ![randvec](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/randvec.png?raw=true) [randvec](https://github.com/sebsjames/mathplot/blob/main/examples/randvec.cpp)| ![convolve](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/convolve.png?raw=true) [convolve](https://github.com/sebsjames/mathplot/blob/main/examples/convolve.cpp)| ![fps](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/fps.png?raw=true) [fps](https://github.com/sebsjames/mathplot/blob/main/examples/fps.cpp)| +| ![voronoi_boundary](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/voronoi_boundary.png?raw=true) [voronoi_boundary](https://github.com/sebsjames/mathplot/blob/main/examples/voronoi_boundary.cpp)| ![sphere](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/sphere.png?raw=true) [sphere](https://github.com/sebsjames/mathplot/blob/main/examples/sphere.cpp)| ![rhombo](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/rhombo.png?raw=true) [rhombo](https://github.com/sebsjames/mathplot/blob/main/examples/rhombo.cpp)| +| ![grid_flat_dynamic](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/grid_flat_dynamic.png?raw=true) [grid_flat_dynamic](https://github.com/sebsjames/mathplot/blob/main/examples/grid_flat_dynamic.cpp)| ![pointrows](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/pointrows.png?raw=true) [pointrows](https://github.com/sebsjames/mathplot/blob/main/examples/pointrows.cpp)| ![model_crawler](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/model_crawler.png?raw=true) [model_crawler](https://github.com/sebsjames/mathplot/blob/main/examples/model_crawler.cpp)| +| ![draw_triangles](https://github.com/sebsjames/mathplot/blob/main/examples/screenshots/draw_triangles.png?raw=true) [draw_triangles](https://github.com/sebsjames/mathplot/blob/main/examples/draw_triangles.cpp)|