From 75b5a2510763722425226e2195d7d8625e26993d Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 16 Oct 2026 16:42:34 +0100 Subject: [PATCH 01/11] First draft of boundary showing --- examples/CMakeLists.txt | 3 +++ examples/hexgrid_boundary.cpp | 50 +++++++++++++++++++++++++++++++++++ maths | 2 +- mplot/HexGridVisual.cppm | 25 ++++++++++++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 examples/hexgrid_boundary.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 46fb1621..360f2059 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -103,6 +103,9 @@ target_link_libraries(hexgrid mathplot_examples) add_executable(hexgrid_transforms hexgrid_transforms.cpp) target_link_libraries(hexgrid_transforms mathplot_examples) +add_executable(hexgrid_boundary hexgrid_boundary.cpp) +target_link_libraries(hexgrid_boundary mathplot_examples) + add_executable(hexgrid_fft hexgrid_fft.cpp) target_link_libraries(hexgrid_fft mathplot_examples) diff --git a/examples/hexgrid_boundary.cpp b/examples/hexgrid_boundary.cpp new file mode 100644 index 00000000..e943edb0 --- /dev/null +++ b/examples/hexgrid_boundary.cpp @@ -0,0 +1,50 @@ +/* + * An example mplot::Visual scene, containing hexgrid with boundary shown + */ + +#include +#include +#include +#include + +import sm.vec; +import sm.hexgrid; + +import mplot.visual; +import mplot.visualdatamodel; +import mplot.hexgridvisual; + +int main() +{ + // Contructor args are width, height, title + mplot::Visual v(1600, 1000, "mplot::HexGridVisual"); + + sm::hexgrid hg1(0.01f, 0.205f, 0.0f); + //hg1.set_circular_boundary (0.1f); + hg1.set_boundary_on_outer_edge(); + + // Make some dummy data (a sine wave) to make an interesting surface + std::vector data1(hg1.num(), 0.0f); + for (unsigned int ri = 0; ri < hg1.num(); ++ri) { + data1[ri] = 0.05f + 0.05f * std::sin (20.0f * hg1.d_x[ri]) * std::sin (10.0f * hg1.d_y[ri]) ; // Range 0->1 + } + + // sm::HexVisMode::HexInterp to see the hexagons or sm::HexVisMode::Triangles for a smoother surface plot + const mplot::HexVisMode visMode = mplot::HexVisMode::HexInterp; + + sm::vec offset = { 0.0f, -0.05f, 0.0f }; + auto hgv1 = std::make_unique>(&hg1, offset); + hgv1->set_parent (v.get_id()); + hgv1->cm.setType (mplot::ColourMapType::Ice); + hgv1->showboundary = true; + hgv1->setScalarData (&data1); + hgv1->hexVisMode = visMode; + hgv1->zScale.null_scaling(); + hgv1->addLabel ("hexalign::point_up", sm::vec<>{ -hg1.width()/2.1f, -hg1.width()/1.6f }, mplot::TextFeatures(0.02f)); + hgv1->finalize(); + v.addVisualModel (hgv1); + + v.keepOpen(); + + return 0; +} diff --git a/maths b/maths index 7a523af5..54bba80f 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 7a523af556636b16033a2831fa8e4d51dbb246ac +Subproject commit 54bba80f0f04bd645182caba53932cf34e54e159 diff --git a/mplot/HexGridVisual.cppm b/mplot/HexGridVisual.cppm index 866b6126..fb425ebd 100644 --- a/mplot/HexGridVisual.cppm +++ b/mplot/HexGridVisual.cppm @@ -321,6 +321,10 @@ export namespace mplot if (this->showhexes == true) { this->computeHexes(); } + // Boundary + if (this->showboundary == true) { + this->computeHexgridBoundary(); + } // Optionally show a Flat surface for the zero plane if (this->zerogrid == true) { this->computeZerogridIndices(); @@ -328,6 +332,25 @@ export namespace mplot // End trial grid } + // Draw carefully around the boundary with tubes or lines or quads + void computeHexgridBoundary() + { + std::array blkclr = {}; + sm::mat lt (this->hg->tfm.template as()); + for (auto hptr : this->hg->bhexen) { + std::uint32_t hi = hptr->vi; + for (std::uint32_t i = 0; i < 6; ++i) { + if (this->hg->has_neighbour (hi, i) == false) { + // Draw in line from vtx 5 to 0, 0 to 1, 1 to 2, etc + std::uint32_t i0 = (i + 5u) % 6u; + sm::vec c0 = lt * hptr->get_vertex_coord (i0).template as().plus_one_dim (0.0f); + sm::vec c = lt * hptr->get_vertex_coord (i).template as().plus_one_dim (0.0f); + this->computeTube (c0, c, blkclr, blkclr, this->hg->d / 12.0f); + } + } + } + } + // Compute vertices for the patchwork quilt of hexes void computeHexes() { @@ -407,9 +430,11 @@ export namespace mplot // Use a single colour for each hex, even though hex z positions are // interpolated. Do the _colour_ scaling: std::array clr = this->setColour (hi); +#if 0 // Now going to draw carefully around the boundary if (this->showboundary && (this->hg->vhexen[hi])->boundary_hex() == true) { this->markHex (hi); } +#endif if (this->showcentre && _x == 0.0f && _y == 0.0f) { this->markHex (hi); } From 87d55e5e157f752757ee65a5dde02b8e70b6b144 Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 16 Oct 2026 18:29:40 +0100 Subject: [PATCH 02/11] A lovely boundary --- examples/hexgrid_boundary.cpp | 6 ++++-- maths | 2 +- mplot/HexGridVisual.cppm | 40 ++++++++++++++++++++++++++++++++--- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/examples/hexgrid_boundary.cpp b/examples/hexgrid_boundary.cpp index e943edb0..702c1057 100644 --- a/examples/hexgrid_boundary.cpp +++ b/examples/hexgrid_boundary.cpp @@ -18,10 +18,12 @@ int main() { // Contructor args are width, height, title mplot::Visual v(1600, 1000, "mplot::HexGridVisual"); + v.fov = 15; + //v.rotateAboutNearest (true); sm::hexgrid hg1(0.01f, 0.205f, 0.0f); - //hg1.set_circular_boundary (0.1f); - hg1.set_boundary_on_outer_edge(); + hg1.set_circular_boundary (0.1f); + //hg1.set_boundary_on_outer_edge(); // Make some dummy data (a sine wave) to make an interesting surface std::vector data1(hg1.num(), 0.0f); diff --git a/maths b/maths index 54bba80f..70b7718e 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 54bba80f0f04bd645182caba53932cf34e54e159 +Subproject commit 70b7718e14024b98e737be660c18b583f7f1c69a diff --git a/mplot/HexGridVisual.cppm b/mplot/HexGridVisual.cppm index fb425ebd..3efc8764 100644 --- a/mplot/HexGridVisual.cppm +++ b/mplot/HexGridVisual.cppm @@ -335,17 +335,51 @@ export namespace mplot // Draw carefully around the boundary with tubes or lines or quads void computeHexgridBoundary() { - std::array blkclr = {}; + constexpr std::array clr = {}; + constexpr std::array clr0 = {.1f,.1f,.1f}; sm::mat lt (this->hg->tfm.template as()); + //std::cout << "lt:\n" << lt << std::endl; + for (auto hptr : this->hg->bhexen) { std::uint32_t hi = hptr->vi; + // Look at each possible neighbour direction and see if we need a tube for (std::uint32_t i = 0; i < 6; ++i) { if (this->hg->has_neighbour (hi, i) == false) { - // Draw in line from vtx 5 to 0, 0 to 1, 1 to 2, etc + // There's no neighbour, so we need a tube. The logic is made complex by the + // need to angle the ends of the tubes correctly depending on the shape of + // the boundary std::uint32_t i0 = (i + 5u) % 6u; sm::vec c0 = lt * hptr->get_vertex_coord (i0).template as().plus_one_dim (0.0f); sm::vec c = lt * hptr->get_vertex_coord (i).template as().plus_one_dim (0.0f); - this->computeTube (c0, c, blkclr, blkclr, this->hg->d / 12.0f); + + sm::vec u0 = {}; // tube end normal for c0 + sm::vec u = {}; // tube end normal for c + sm::vec v = {}; + + std::uint32_t di = 0u; // direction to corner neighbour + std::uint32_t vi = 0u; // vector to cross with uz depends on if corner neighbour exists or not + + di = (i + 5u) % 6u; + if (this->hg->has_neighbour (hi, di) == false) { + vi = (i + 5u) % 6u; + } else { + vi = i; + } + v = hptr->get_vertex (vi).plus_one_dim(); + v.renormalize(); + u0 = lt * v.cross (sm::vec::uz()); + + di = (i + 1u) % 6u; + if (this->hg->has_neighbour (hi, di) == false) { + vi = i; + } else { + vi = (i + 5u) % 6u; + } + v = hptr->get_vertex (vi).plus_one_dim(); + v.renormalize(); + u = lt * v.cross (-sm::vec::uz()); + + this->computeOpenFlaredTube (c0, c, u0, u, clr0, clr, this->hg->d / 12.0f, this->hg->d / 12.0f); } } } From f3849a4ea77835721536113fb46a1f347cec64a2 Mon Sep 17 00:00:00 2001 From: Seb James Date: Sat, 17 Oct 2026 12:53:01 +0100 Subject: [PATCH 03/11] get_vertex() calls need to be converted to float --- mplot/HexGridVisual.cppm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mplot/HexGridVisual.cppm b/mplot/HexGridVisual.cppm index 3efc8764..90702d26 100644 --- a/mplot/HexGridVisual.cppm +++ b/mplot/HexGridVisual.cppm @@ -365,7 +365,7 @@ export namespace mplot } else { vi = i; } - v = hptr->get_vertex (vi).plus_one_dim(); + v = hptr->get_vertex (vi).template as().plus_one_dim(); v.renormalize(); u0 = lt * v.cross (sm::vec::uz()); @@ -375,7 +375,7 @@ export namespace mplot } else { vi = (i + 5u) % 6u; } - v = hptr->get_vertex (vi).plus_one_dim(); + v = hptr->get_vertex (vi).template as().plus_one_dim(); v.renormalize(); u = lt * v.cross (-sm::vec::uz()); From 1d8441a446f1d145ce95efb110bf2b7fbd3ea859 Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 19 Oct 2026 10:30:05 +0100 Subject: [PATCH 04/11] Changes in maths to fix boundaries on rotated hexgrids plus additional example in hexgrid_boundary --- examples/hexgrid_boundary.cpp | 22 +++++++++++++++++++++- maths | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/examples/hexgrid_boundary.cpp b/examples/hexgrid_boundary.cpp index 702c1057..3b8c8b42 100644 --- a/examples/hexgrid_boundary.cpp +++ b/examples/hexgrid_boundary.cpp @@ -42,10 +42,30 @@ int main() hgv1->setScalarData (&data1); hgv1->hexVisMode = visMode; hgv1->zScale.null_scaling(); - hgv1->addLabel ("hexalign::point_up", sm::vec<>{ -hg1.width()/2.1f, -hg1.width()/1.6f }, mplot::TextFeatures(0.02f)); + hgv1->addLabel ("Boundary of hexalign::point_up", sm::vec<>{ -hg1.width()/2.1f, -hg1.width()/1.6f }, mplot::TextFeatures(0.02f)); hgv1->finalize(); v.addVisualModel (hgv1); + sm::hexgrid hg2(0.01f, 0.205f, 0.0f); + hg2.set_circular_boundary (0.1f); + + sm::mat rotn; + rotn.rotate (sm::mathconst::pi_over_4); + + hg2.transform (rotn); + + offset[0] += 0.3f; + auto hgv2 = std::make_unique>(&hg2, offset); + hgv2->set_parent (v.get_id()); + hgv2->cm.setType (mplot::ColourMapType::Plasma); + hgv2->showboundary = true; + hgv2->setScalarData (&data1); + hgv2->hexVisMode = visMode; + hgv2->zScale.null_scaling(); + hgv2->addLabel ("Boundary of transformed hexgrid", sm::vec<>{ -hg2.width()/2.1f, -hg2.width()/1.6f }, mplot::TextFeatures(0.02f)); + hgv2->finalize(); + v.addVisualModel (hgv2); + v.keepOpen(); return 0; diff --git a/maths b/maths index 70b7718e..2c26e0fa 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 70b7718e14024b98e737be660c18b583f7f1c69a +Subproject commit 2c26e0fab53943f0191545a210e3813241b81c16 From 37e91975bd84e13b4f2fe959612f4ee22667c5ba Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 19 Oct 2026 13:57:04 +0100 Subject: [PATCH 05/11] Add one additional, forgotten transform --- mplot/HexGridVisual.cppm | 1 + 1 file changed, 1 insertion(+) diff --git a/mplot/HexGridVisual.cppm b/mplot/HexGridVisual.cppm index 90702d26..73429d4a 100644 --- a/mplot/HexGridVisual.cppm +++ b/mplot/HexGridVisual.cppm @@ -339,6 +339,7 @@ export namespace mplot constexpr std::array clr0 = {.1f,.1f,.1f}; sm::mat lt (this->hg->tfm.template as()); //std::cout << "lt:\n" << lt << std::endl; + lt = this->tfm * lt; // Also apply our own tfm for (auto hptr : this->hg->bhexen) { std::uint32_t hi = hptr->vi; From 09e4d00f7575c4b9309ed723b99ad723cb0ec0fd Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 19 Oct 2026 13:59:45 +0100 Subject: [PATCH 06/11] Updates maths --- maths | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths b/maths index 2c26e0fa..19f84ce9 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 2c26e0fab53943f0191545a210e3813241b81c16 +Subproject commit 19f84ce937541ffd58266100079e88077cdc10cf From 5bdd6edfde807c1ddd2418c6d46e59ed25655014 Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 19 Oct 2026 14:34:32 +0100 Subject: [PATCH 07/11] Enable boundary for HexGridVisual in Triangles hexVisMode --- mplot/HexGridVisual.cppm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mplot/HexGridVisual.cppm b/mplot/HexGridVisual.cppm index 73429d4a..bba95891 100644 --- a/mplot/HexGridVisual.cppm +++ b/mplot/HexGridVisual.cppm @@ -310,6 +310,9 @@ export namespace mplot } } this->idx = nhex; + + // Boundary can be drawn on non-update + if (this->showboundary == true) { this->computeHexgridBoundary(); } } } From bc882c3a08688e0661f4b9005f4d2ecc4e7161e3 Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 19 Oct 2026 15:09:51 +0100 Subject: [PATCH 08/11] Update to maths main --- maths | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths b/maths index 19f84ce9..46b2a4d5 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 19f84ce937541ffd58266100079e88077cdc10cf +Subproject commit 46b2a4d5c5007c3928b6916d5b3baef4b5281da9 From 462f7ad383803e6cf668ad5b628742a775f444d6 Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 19 Oct 2026 15:12:34 +0100 Subject: [PATCH 09/11] Removes old boundary marking scheme --- mplot/HexGridVisual.cppm | 5 ----- 1 file changed, 5 deletions(-) diff --git a/mplot/HexGridVisual.cppm b/mplot/HexGridVisual.cppm index bba95891..8dd35507 100644 --- a/mplot/HexGridVisual.cppm +++ b/mplot/HexGridVisual.cppm @@ -468,11 +468,6 @@ export namespace mplot // Use a single colour for each hex, even though hex z positions are // interpolated. Do the _colour_ scaling: std::array clr = this->setColour (hi); -#if 0 // Now going to draw carefully around the boundary - if (this->showboundary && (this->hg->vhexen[hi])->boundary_hex() == true) { - this->markHex (hi); - } -#endif if (this->showcentre && _x == 0.0f && _y == 0.0f) { this->markHex (hi); } From 6dfacc85ca6c52ede2b62c2574df955da23afcfa Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 19 Oct 2026 15:57:54 +0100 Subject: [PATCH 10/11] Two examples that were missing a #include --- examples/hexgrid_image_fft.cpp | 1 + examples/obb_collisions.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/examples/hexgrid_image_fft.cpp b/examples/hexgrid_image_fft.cpp index 8777b10a..12e986ab 100644 --- a/examples/hexgrid_image_fft.cpp +++ b/examples/hexgrid_image_fft.cpp @@ -9,6 +9,7 @@ #include #include #include +#include import sm.vec; import sm.vvec; diff --git a/examples/obb_collisions.cpp b/examples/obb_collisions.cpp index b8cca995..18277b39 100644 --- a/examples/obb_collisions.cpp +++ b/examples/obb_collisions.cpp @@ -6,6 +6,7 @@ #include #include #include +#include import sm.vec; import sm.quaternion; From fe493d4197aaf6e27ba543eaaa043a01b7da7249 Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 20 Oct 2026 10:39:36 +0100 Subject: [PATCH 11/11] Do zoom/reflection with the viewmatrix - the proper way --- mplot/HexGridVisual.cppm | 32 +++++++++----------------------- mplot/VisualModel.cppm | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/mplot/HexGridVisual.cppm b/mplot/HexGridVisual.cppm index 8dd35507..31d01dc7 100644 --- a/mplot/HexGridVisual.cppm +++ b/mplot/HexGridVisual.cppm @@ -52,19 +52,6 @@ export namespace mplot //! something, so that it is visible. void markHex (std::uint32_t hi) { this->markedHexes.insert(hi); } - void set_zoom (const float factor) - { - this->tfm = (sm::mat::identity() * factor) * this->tfm; - } - - void reflect_lr() - { - this->tfm = sm::mat::reflection(sm::vec::ux()) * this->tfm; - } - - //! scalar 'zoom' has been replaced with a transform matrix to allow zoom, but also flip or rotate - sm::mat tfm; - //! Show a set of hexes at the zero? bool zerogrid = false; @@ -257,7 +244,7 @@ export namespace mplot // mapping of the 2D hexgrid onto a 3D manifold. if (this->dataCoords == nullptr) { sm::vec crd = { this->hg->d_x[hi], this->hg->d_y[hi], this->dcopy[hi] }; - sm::vec p = this->tfm * crd.template as(); + sm::vec p = crd.template as(); if (update == false) { this->vertexPositions[hi * 3] = p[0]; this->vertexPositions[hi * 3 + 1] = p[1]; @@ -265,7 +252,7 @@ export namespace mplot this->vertexPositions[hi * 3 + 2] = p[2]; } else { // Otherwise use the positions directly in the hexgrid: - sm::vec p = this->tfm * (*this->dataCoords)[hi]; // sm::vec{ (*this->dataCoords)[hi][0], (*this->dataCoords)[hi][1], (*this->dataCoords)[hi][2] }; + const sm::vec p = (*this->dataCoords)[hi]; if (update == false) { this->vertexPositions[hi * 3] = p[0]; this->vertexPositions[hi * 3 + 1] = p[1]; @@ -342,7 +329,6 @@ export namespace mplot constexpr std::array clr0 = {.1f,.1f,.1f}; sm::mat lt (this->hg->tfm.template as()); //std::cout << "lt:\n" << lt << std::endl; - lt = this->tfm * lt; // Also apply our own tfm for (auto hptr : this->hg->bhexen) { std::uint32_t hi = hptr->vi; @@ -477,7 +463,7 @@ export namespace mplot // Use the centre position as the first location for finding the normal vector vtx_0 = this->dataCoords == nullptr ? sm::vec{ _x, _y, datumC } : coordC; - this->vertex_push ((this->tfm * vtx_0), this->vertexPositions); + this->vertex_push (vtx_0, this->vertexPositions); // A hexagon corner/vertex position sm::vec crnr = {}; @@ -518,7 +504,7 @@ export namespace mplot vtx_1 = coordC; } } - this->vertex_push (this->tfm * vtx_1, this->vertexPositions); + this->vertex_push (vtx_1, this->vertexPositions); // SE vertex @@ -553,7 +539,7 @@ export namespace mplot vtx_2 = coordC; } } - this->vertex_push (this->tfm * vtx_2, this->vertexPositions); + this->vertex_push (vtx_2, this->vertexPositions); // S @@ -588,7 +574,7 @@ export namespace mplot vtx_tmp = coordC; } } - this->vertex_push (this->tfm * vtx_tmp, this->vertexPositions); + this->vertex_push (vtx_tmp, this->vertexPositions); // SW if (this->dataCoords == nullptr) { @@ -622,7 +608,7 @@ export namespace mplot vtx_tmp = coordC; } } - this->vertex_push (this->tfm * vtx_tmp, this->vertexPositions); + this->vertex_push (vtx_tmp, this->vertexPositions); // NW if (this->dataCoords == nullptr) { @@ -656,7 +642,7 @@ export namespace mplot vtx_tmp = coordC; } } - this->vertex_push (this->tfm * vtx_tmp, this->vertexPositions); + this->vertex_push (vtx_tmp, this->vertexPositions); // N if (this->dataCoords == nullptr) { @@ -690,7 +676,7 @@ export namespace mplot vtx_tmp = coordC; } } - this->vertex_push (this->tfm * vtx_tmp, this->vertexPositions); + this->vertex_push (vtx_tmp, this->vertexPositions); // From vtx_0,1,2 compute normal. This sets the correct normal, but note // that there is only one 'layer' of vertices; the back of the diff --git a/mplot/VisualModel.cppm b/mplot/VisualModel.cppm index dbdd084e..9209bf7e 100644 --- a/mplot/VisualModel.cppm +++ b/mplot/VisualModel.cppm @@ -836,8 +836,29 @@ export namespace mplot void postmultViewMatrix (const sm::mat& m) { this->viewmatrix = this->viewmatrix * m; } void premultViewMatrix (const sm::mat& m) { this->viewmatrix = m * this->viewmatrix; } + //! Scale the viewmatrix (this allows you to 'zoom' your model) void scaleViewMatrix (const float by) { this->viewmatrix.scale (by); } + //! An alias for scaleViewMatrix + void set_zoom (const float factor) { this->viewmatrix.scale (factor); } + + //! Reflect your model left to right (across the y-z plane through the origin) + void reflect_x() + { + this->postmultViewMatrix (sm::mat::reflection (sm::vec{}, sm::vec::ux())); + } + + //! Reflect your model up/down (across the x-z plane through the origin) + void reflect_y() + { + this->postmultViewMatrix (sm::mat::reflection (sm::vec{}, sm::vec::uy())); + } + + void reflect_z() + { + this->postmultViewMatrix (sm::mat::reflection (sm::vec{}, sm::vec::uz())); + } + //! When setting the scene matrix, also have to set the text's scene matrices. void setSceneMatrix (const sm::mat& sv) {