diff --git a/maths b/maths index 1eb98e81..db69cc49 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 1eb98e81f068b819cda7440e9b4ef458ca297f7b +Subproject commit db69cc490c54c267c99f1220819b485566e03f14 diff --git a/mplot/SphericalProjectionVisual.h b/mplot/SphericalProjectionVisual.h index 95f12003..dd3ffe43 100644 --- a/mplot/SphericalProjectionVisual.h +++ b/mplot/SphericalProjectionVisual.h @@ -22,8 +22,12 @@ namespace mplot sm::geometry::spherical_projection::type proj_type = sm::geometry::spherical_projection::type::mercator; - sm::vec project (const sm::vec& ll, const T radius) + sm::vec project (sm::vec ll, const T radius) { + // Apply this->rotation to lat-long coordinates, ll + sm::vec llv = sm::geometry::spherical_projection::latlong_to_xyz (ll, T{1}); + ll = sm::geometry::spherical_projection::xyz_to_latlong (this->rotation * llv); + if (ll.has_nan()) { throw std::runtime_error ("nan in ll"); } if (this->proj_type == sm::geometry::spherical_projection::type::equirectangular) { return sm::geometry::spherical_projection::equirectangular (ll, radius, this->lambda0, this->phi0, this->phi1); } else if (this->proj_type == sm::geometry::spherical_projection::type::cassini) { @@ -114,7 +118,7 @@ namespace mplot } } - // latlong, supplied by user + // latlong, supplied by user in radians sm::vvec> latlong; // Colour, supplied by user sm::vvec> colour; @@ -134,5 +138,16 @@ namespace mplot static constexpr bool show_centres = false; static constexpr std::array centre_col = mplot::colour::black; static constexpr T centre_rad = T{0.005}; + + void set_rotation (const sm::quaternion& r) + { + this->rotation = r; + this->rotation.renormalize(); + } + sm::quaternion get_rotation () const { return this->rotation; } + + private: + // A rotation to apply to each latitude-longitude before feeding it to the projection. + sm::quaternion rotation; }; } diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 5a73e54d..c2caa54a 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -65,7 +65,11 @@ namespace mplot //! True means that at least one of our VisualModels is an instanced rendering model haveInstanced, //! When true, the instanced data SSBO needs to be copied to the GPU - instancedNeedsUpdate + instancedNeedsUpdate, + //! Left mouse button is down + mouseButtonLeftPressed, + //! Right mouse button is down + mouseButtonRightPressed }; //! Boolean options - similar to state, but more likely to be modified by client code @@ -613,6 +617,11 @@ namespace mplot this->sceneview.rotate (_rotn); } + // What is the scene view's current rotation quaternion? + sm::quaternion getSceneRotation() const { return this->sceneview.rotation(); } + // What is the scene view's current translation? + sm::vec getSceneTranslation() const { return this->sceneview.translation(); } + void lightingEffects (const bool effects_on = true) { this->ambient_intensity = effects_on ? 0.4f : 1.0f; @@ -1525,10 +1534,20 @@ namespace mplot this->find_rotation_centre(); if (button == mplot::mousebutton::left) { // Primary button means rotate + if (action == keyaction::press) { + this->state.set (visual_state::mouseButtonLeftPressed); + } else if (action == keyaction::release) { + this->state.set (visual_state::mouseButtonLeftPressed, false); + } this->state.set (visual_state::rotateModMode, ((mods & keymod::control) ? true : false)); this->state.set (visual_state::rotateMode, (action == keyaction::press)); this->state.set (visual_state::translateMode, false); } else if (button == mplot::mousebutton::right) { // Secondary button means translate + if (action == keyaction::press) { + this->state.set (visual_state::mouseButtonRightPressed); + } else if (action == keyaction::release) { + this->state.set (visual_state::mouseButtonRightPressed, false); + } this->state.set (visual_state::rotateMode, false); this->state.set (visual_state::translateMode, (action == keyaction::press)); } diff --git a/mplot/compoundray/EyeVisual.h b/mplot/compoundray/EyeVisual.h index 558b266f..8893a42e 100644 --- a/mplot/compoundray/EyeVisual.h +++ b/mplot/compoundray/EyeVisual.h @@ -191,6 +191,8 @@ namespace mplot::compoundray sm::vec proj_centre = {}; // The height of a projection cylinder sm::vec proj_height = {}; + // A rotation to apply before projecting + sm::quaternion proj_rotation; // Which spherical to 2D projection to use? projection_type proj_type = projection_type::mercator; // Have to record the number of triangles in each cell in the 2D map in order to update the colours @@ -210,6 +212,7 @@ namespace mplot::compoundray void add_spherical_projection (projection_type t, const sm::mat44& _twod_transform, const sm::vec& centre, const float radius, + const sm::quaternion rotn = sm::quaternion(), const uint32_t _start_i = 0, const uint32_t _end_i = std::numeric_limits::max()) { @@ -218,6 +221,8 @@ namespace mplot::compoundray d.twod_transform = _twod_transform; d.proj_centre = centre; d.proj_radius = radius; + d.proj_rotation = rotn; + d.proj_rotation.renormalize(); d.start_i = _start_i; d.end_i = _end_i; this->projections.push_back (d); @@ -225,12 +230,13 @@ namespace mplot::compoundray void add_spherical_projection (projection_type t, const sm::vec& _twod_offset, const sm::vec& centre, const float radius, + const sm::quaternion rotn = sm::quaternion(), const uint32_t _start_i = 0, const uint32_t _end_i = std::numeric_limits::max()) { sm::mat44 tr; tr.translate (_twod_offset); - this->add_spherical_projection (t, tr, centre, radius, _start_i, _end_i); + this->add_spherical_projection (t, tr, centre, radius, rotn, _start_i, _end_i); } void add_cylindrical_projection (const sm::mat44& _twod_transform, const sm::vec& centre, @@ -370,9 +376,14 @@ namespace mplot::compoundray (*ommatidia)[i].relativePosition, -(*ommatidia)[i].relativeDirection); if (sph_coord[0][0] != std::numeric_limits::max()) { + sph_coord[0] -= this->projections[pri].proj_centre; // offset by centre before rotation // sph_coord[0] is the coordinate for the ommatidia pixel on the sphere sm::vec rot_coord = (coord_rotn * sph_coord[0]).less_one_dim(); - sm::vec ll = sm::geometry::spherical_projection::xyz_to_latlong (rot_coord, this->projections[pri].proj_radius); + // Now apply our projection rotation quaternion + rot_coord = this->projections[pri].proj_rotation * rot_coord; + if (rot_coord.has_nan()) { throw std::runtime_error ("rot_coord has NaN"); } + sm::vec ll = sm::geometry::spherical_projection::xyz_to_latlong (rot_coord); + if (ll.has_nan()) { throw std::runtime_error ("latlong has NaN"); } sm::vec xy = this->spherical_projection (ll, this->projections[pri].proj_type, this->projections[pri].proj_radius); // Add xy as one of the points that we'll make a Voronoi diagram from. this->omm2d.push_back (xy.plus_one_dim().as()); diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index ec7e85d4..8a6e27a6 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -1086,8 +1086,8 @@ namespace jcv if (!next) { next = site->edges; } } ++loopcount; - if (loopcount > 1000) { - std::cout << "Too many loops. Thi can be caused by numerical precision errors when using T==float on some sets of points\n"; + 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"; } } }