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..3b8c8b42 --- /dev/null +++ b/examples/hexgrid_boundary.cpp @@ -0,0 +1,72 @@ +/* + * 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"); + 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(); + + // 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 ("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/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; diff --git a/maths b/maths index 7a523af5..46b2a4d5 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 7a523af556636b16033a2831fa8e4d51dbb246ac +Subproject commit 46b2a4d5c5007c3928b6916d5b3baef4b5281da9 diff --git a/mplot/HexGridVisual.cppm b/mplot/HexGridVisual.cppm index 866b6126..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]; @@ -310,6 +297,9 @@ export namespace mplot } } this->idx = nhex; + + // Boundary can be drawn on non-update + if (this->showboundary == true) { this->computeHexgridBoundary(); } } } @@ -321,6 +311,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 +322,59 @@ export namespace mplot // End trial grid } + // Draw carefully around the boundary with tubes or lines or quads + void computeHexgridBoundary() + { + 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) { + // 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); + + 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).template as().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).template as().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); + } + } + } + } + // Compute vertices for the patchwork quilt of hexes void computeHexes() { @@ -407,9 +454,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 (this->showboundary && (this->hg->vhexen[hi])->boundary_hex() == true) { - this->markHex (hi); - } if (this->showcentre && _x == 0.0f && _y == 0.0f) { this->markHex (hi); } @@ -419,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 = {}; @@ -460,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 @@ -495,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 @@ -530,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) { @@ -564,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) { @@ -598,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) { @@ -632,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) {