diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e049ebe7..a3f30007 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -360,6 +360,9 @@ if(NOT APPLE) target_link_libraries(sphere OpenGL::GL glfw Freetype::Freetype) endif() +add_executable(ellipsoid ellipsoid.cpp) +target_link_libraries(ellipsoid OpenGL::GL glfw Freetype::Freetype) + # constexpr code won't work on Apple Clang if(NOT APPLE) add_executable(icosahedron icosahedron.cpp) @@ -377,6 +380,9 @@ if(NOT APPLE) target_link_libraries(geodesic_ce OpenGL::GL glfw Freetype::Freetype) endif() +add_executable(model_crawler model_crawler.cpp) +target_link_libraries(model_crawler OpenGL::GL glfw Freetype::Freetype) + add_executable(tri tri.cpp) target_link_libraries(tri OpenGL::GL glfw Freetype::Freetype) @@ -386,6 +392,9 @@ target_link_libraries(draw_triangles OpenGL::GL glfw Freetype::Freetype) add_executable(draw_triangles_intersections draw_triangles_intersections.cpp) target_link_libraries(draw_triangles_intersections OpenGL::GL glfw Freetype::Freetype) +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) @@ -425,11 +434,14 @@ target_link_libraries(rhombo_scene2 OpenGL::GL glfw Freetype::Freetype) add_executable(cube cube.cpp) target_link_libraries(cube OpenGL::GL glfw Freetype::Freetype) -if(Eigen3_FOUND) - add_executable(cubetrans cubetrans.cpp) - target_include_directories (cubetrans BEFORE PRIVATE ${EIGEN3_INCLUDE_DIR}) - target_link_libraries(cubetrans OpenGL::GL glfw Freetype::Freetype) -endif(Eigen3_FOUND) +# Eigen finding is not reliable on Github Mac OS runners +if(NOT APPLE) + if(Eigen3_FOUND) + add_executable(cubetrans cubetrans.cpp) + target_include_directories (cubetrans BEFORE PRIVATE ${EIGEN3_INCLUDE_DIR}) + target_link_libraries(cubetrans OpenGL::GL glfw Freetype::Freetype) + endif(Eigen3_FOUND) +endif() add_executable(cubetrans2 cubetrans2.cpp) target_link_libraries(cubetrans2 OpenGL::GL glfw Freetype::Freetype) @@ -529,3 +541,7 @@ 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) diff --git a/examples/cray_eye.cpp b/examples/cray_eye.cpp new file mode 100644 index 00000000..a949f580 --- /dev/null +++ b/examples/cray_eye.cpp @@ -0,0 +1,76 @@ +/* + * A compound ray eye viewer + * + * Demonstrating use of mplot::compoundray::EyeVisual + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +int main (int argc, char** argv) +{ + std::string eyefile = ""; + if (argc < 2) { + std::cout << "Usage: " << argv[0] << " path/to/eyefile.eye [projection sphere radius]\n"; + return -1; + } else { + eyefile = std::string (argv[1]); + } + + float psrad = 0.5f; + if (argc > 2) { psrad = std::atof (argv[2]); } + + auto v = mplot::Visual<>(1024, 768, "mplot::compoundray::EyeVisual"); + + // We read the information from the eye file into a vector of Ommatidium objects. Ommatidium is + // defined in "cameras/CompoundEyeDataTypes.h" in compound ray, mplot::Ommatidium is a + // mplot/Seb's maths style equivalent. It contains 2 3D float vectors and two scalar floating point + // values. + auto ommatidia = std::make_unique>(); + std::vector> ommatidiaColours; + + // Read the eye file into ommatidia data structure. compound ray does this internally when we're + // using it, but for this example we instead make use of mplot::compoundray::readEye + if (mplot::compoundray::readEye (ommatidia.get(), eyefile) == nullptr) { std::cout << "Failed to read eye\n"; return -1; } + + // Make some dummy data to demo the eye + sm::vvec ommatidiaData; + ommatidiaData.linspace (0, 1, ommatidia->size()); + // Colour it with a colour map + mplot::ColourMap cm (mplot::ColourMapType::Plasma); + ommatidiaColours.resize (ommatidia->size()); + for (size_t i = 0; i < ommatidia->size(); ++i) { + ommatidiaColours[i] = cm.convert (ommatidiaData[i]); + } + + auto eyevm = std::make_unique> (sm::vec<>{}, &ommatidiaColours, ommatidia.get()); + v.bindmodel (eyevm); + eyevm->name = "CompoundRay Eye"; + eyevm->show_cones = true; + + auto ptype = mplot::compoundray::EyeVisual<>::projection_type::equirectangular; // mercator, equirectangular or cassini + sm::vec<> centre = { 0, 0, 0 }; + sm::mat44 twod_tr; + twod_tr.translate (sm::vec<>{0,0,-0.1}); + eyevm->add_spherical_projection (ptype, twod_tr, centre, psrad); + eyevm->pre_set_cone_length (4e-6f); + eyevm->show_sphere = true; + eyevm->show_rays = false; + eyevm->finalize(); + + [[maybe_unused]] auto ep = v.addVisualModel (eyevm); + // ep->reinitColours(); + ep->scaleViewMatrix (1000.0f); + + v.keepOpen(); +} diff --git a/examples/draw_triangles_intersections.cpp b/examples/draw_triangles_intersections.cpp index fae569be..e5e19755 100644 --- a/examples/draw_triangles_intersections.cpp +++ b/examples/draw_triangles_intersections.cpp @@ -268,6 +268,9 @@ int main() sm::vec<> start = { 2, 0, 5 }; sm::vec<> dirn = { 0, 0, -10 }; + sm::vec<> start_fr2 = { 1, .9, 5 }; + sm::vec<> dirn_fr2 = { 0.5, -0.7, -10 }; + sm::vec<> start_bh = { 0, 0, -5 }; sm::vec<> dirn_bh = { 1.5, 1.5, 10 }; @@ -281,6 +284,11 @@ int main() sv->finalize(); v.addVisualModel (sv); + sv = std::make_unique>(start_fr2, 0.1, mplot::colour::goldenrod3); + v.bindmodel (sv); + sv->finalize(); + v.addVisualModel (sv); + auto vvm = std::make_unique>(start); v.bindmodel (vvm); vvm->thevec = dirn; @@ -305,14 +313,26 @@ int main() vvm->finalize(); [[maybe_unused]] auto vvmp2 = v.addVisualModel (vvm); - tvp->vertex_postprocess(); + vvm = std::make_unique>(start_fr2); + v.bindmodel (vvm); + vvm->thevec = dirn_fr2; + vvm->vgoes = VectorGoes::FromOrigin; + vvm->thickness = 0.02f; + vvm->arrowhead_prop = 0.1f; + vvm->fixed_colour = true; + vvm->single_colour = mplot::colour::crimson; + vvm->addLabel ("Ray from front through middle", {-0.8, -0.5, 0}, mplot::TextFeatures(0.1f)); + vvm->finalize(); + [[maybe_unused]] auto vvmp3 = v.addVisualModel (vvm); + + tvp->make_navmesh(); auto vm = tvp->getViewMatrix(); auto vmi = vm.inverse(); auto start_wr = (vmi * start).less_one_dim(); // wr to tvp std::cout << "start_wr = " << start_wr << std::endl; - auto [hit, ti, tn] = tvp->find_triangle_crossing (start_wr, dirn); + auto [hit, ti, tn] = tvp->navmesh->find_triangle_crossing (start_wr, dirn); if (ti[0] == std::numeric_limits::max()) { std::cout << "NO HIT\n"; } else { @@ -325,9 +345,23 @@ int main() v.addVisualModel (sv); } + auto start_wr_fr2 = (vmi * start_fr2).less_one_dim(); // wr to tvp + auto [hit_fr2, ti_fr2, tn_fr2] = tvp->navmesh->find_triangle_crossing (start_wr_fr2, dirn_fr2); + if (ti[0] == std::numeric_limits::max()) { + std::cout << "NO HIT\n"; + } else { + std::cout << "Indices: " << ti_fr2[0] << "," << ti_fr2[1] << "," << ti_fr2[2] << std::endl; + std::cout << "Contains hit " << hit_fr2 << std::endl; + + sv = std::make_unique>(hit_fr2, 0.07, mplot::colour::springgreen2); + v.bindmodel (sv); + sv->finalize(); + v.addVisualModel (sv); + } + auto start_wr_bh = (vmi * start_bh).less_one_dim(); // wr to tvp std::cout << "start_wr = " << start_wr << std::endl; - auto [hit_bh, ti_bh, tn_bh] = tvp->find_triangle_crossing (start_wr_bh, dirn_bh); + auto [hit_bh, ti_bh, tn_bh] = tvp->navmesh->find_triangle_crossing (start_wr_bh, dirn_bh); if (ti_bh[0] == std::numeric_limits::max()) { std::cout << "NO HIT\n"; } else { @@ -339,17 +373,7 @@ int main() sv->finalize(); v.addVisualModel (sv); } -#if 0 - auto dtv = std::make_unique>(sm::vec{3.0f,0.0f,0.0f}); - v.bindmodel (dtv); - dtv->finalize(); - v.addVisualModel (dtv); - - auto tctv = std::make_unique>(sm::vec{6.0f,0.0f,0.0f}); - v.bindmodel (tctv); - tctv->finalize(); - v.addVisualModel (tctv); -#endif + v.keepOpen(); return 0; } diff --git a/examples/ellipsoid.cpp b/examples/ellipsoid.cpp new file mode 100644 index 00000000..b90b2bfd --- /dev/null +++ b/examples/ellipsoid.cpp @@ -0,0 +1,55 @@ +/* + * Visualize an ellipsoid + */ +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +// Quick visual that simply draws ellipsoid +template +class PrimitiveVisual : public mplot::VisualModel +{ +public: + PrimitiveVisual (const sm::vec _offset) { this->viewmatrix.translate (_offset); } + + void initializeVertices() + { + sm::mat44 tr; + tr.rotate (sm::vec<>::uz(), sm::mathconst::pi_over_4); + this->computeEllipsoid (sm::vec{0}, + mplot::colour::royalblue, + mplot::colour::maroon3, + sm::vec{1,2,3}, + 40, 40, tr); + } +}; + +int main() +{ + mplot::Visual v(1024, 768, "Ellipsoid primitive"); + v.lightingEffects (true); + + auto pvm = std::make_unique> (sm::vec<>{}); + v.bindmodel (pvm); + pvm->finalize(); + auto pvmp = v.addVisualModel (pvm); + + constexpr bool show_normals = true; + if constexpr (show_normals) { + // Create an associate normals model + auto nrm = std::make_unique> (pvmp); + v.bindmodel (nrm); + nrm->finalize(); + v.addVisualModel (nrm); + } + + v.keepOpen(); +} diff --git a/examples/geodesic_with_normals.cpp b/examples/geodesic_with_normals.cpp index 3bca7161..a832dcef 100644 --- a/examples/geodesic_with_normals.cpp +++ b/examples/geodesic_with_normals.cpp @@ -52,8 +52,6 @@ int main() gv1p->data.linspace (0.0f, 1+i * imax_mult, sz1); gv1p->reinitColours(); - gv1p->vertex_postprocess(); // creates the triangles and normals required for NormalsVisual - // Create an associate normals model auto nrm = std::make_unique> (gv1p); v.bindmodel (nrm); diff --git a/examples/healpix.cpp b/examples/healpix.cpp index f380b271..ca17eef8 100644 --- a/examples/healpix.cpp +++ b/examples/healpix.cpp @@ -1,5 +1,8 @@ /* * Make a healpix visual, showing the NEST index in a colour map + * + * This program also demonstrates 2D spherical projections that are available in sm::geometry and + * the Visual class mplot::SphereProjectionVisual */ #include #include @@ -7,6 +10,7 @@ #include #include #include +#include int main (int argc, char** argv) { @@ -25,7 +29,17 @@ int main (int argc, char** argv) // scheme. If we fill it with sequential values, then the colour map will show the // hierarchical nature of the HEALPix. for (int64_t p = 0; p < hpv->n_pixels(); ++p) { - hpv->pixeldata[p] = static_cast(p) / hpv->n_pixels(); + hpv->pixeldata[p] = 0.5f * static_cast(p) / hpv->n_pixels(); + // Mark circles for the x, y and z axes to check they line up + hp::t_vec pv = hp::nest2vec (hpv->get_nside(), p); + auto v = sm::vec({pv.x, pv.y, pv.z}); + if (v.angle (sm::vec::ux()) < 0.05) { + hpv->pixeldata[p] = 0.6; + } else if (v.angle (sm::vec::uy()) < 0.05) { + hpv->pixeldata[p] = 0.8; + } else if (v.angle (sm::vec::uz()) < 0.05) { + hpv->pixeldata[p] = 1.0; + } } std::stringstream ss; @@ -37,7 +51,112 @@ int main (int argc, char** argv) // Finalize and add hpv->finalize(); - v.addVisualModel (hpv); + auto hpvp = v.addVisualModel (hpv); + + // Show some 2D projections, as well + sm::vvec> hpvcolours (hpvp->pixeldata.size(), mplot::colour::crimson); + sm::vvec> latlong (hpvp->pixeldata.size()); + for (uint32_t i = 0; i < hpvp->pixeldata.size(); ++i) { + + // ang.theta is co-latitude (and needs re-casting to be longitude), ang.phi is a longitude + hp::t_ang ang = hp::nest2ang (hpvp->get_nside(), i); + + // ang.theta is pi for the South pole and 0 for the North pole + // latitude should be in range -pi/2 (S) < lat <= pi/2 (N) + float _lat = (sm::mathconst::pi - ang.theta) - sm::mathconst::pi_over_2; + + latlong[i] = { _lat, static_cast(ang.phi) }; + hpvcolours[i] = hpvp->cm.convert (hpvp->pixeldata[i]); + } + + // Add two-dimensional projections + auto spv = std::make_unique> (sm::vec{5,0,0}); + v.bindmodel (spv); + spv->twodimensional (true); + spv->proj_type = sm::geometry::spherical_projection::type::mercator; + spv->latlong = latlong; + spv->lambda0 = 0.0f; + spv->colour = hpvcolours; + spv->finalize(); + auto spvp = v.addVisualModel (spv); + auto ext = spvp->extents(); // Use VisualModel::extents() to help place the label + spvp->addLabel ("Mercator projection", sm::vec<>{ ext[0].min, ext[1].min - 0.16f, 0.0f }, mplot::TextFeatures(0.08f)); + + spv = std::make_unique> (sm::vec{13,0,0}); + v.bindmodel (spv); + spv->twodimensional (true); + spv->proj_type = sm::geometry::spherical_projection::type::mercator; + spv->latlong = latlong; + spv->lambda0 = sm::mathconst::pi_over_4; + spv->colour = hpvcolours; + spv->finalize(); + spvp = v.addVisualModel (spv); + ext = spvp->extents(); // Use VisualModel::extents() to help place the label + std::stringstream ss1; + ss1 << "Mercator projection with " + << mplot::unicode::toUtf8(mplot::unicode::lambda) + << mplot::unicode::toUtf8(mplot::unicode::subs0) + << " = " << mplot::unicode::toUtf8(mplot::unicode::pi) << "/4"; + spvp->addLabel (ss1.str(), + sm::vec<>{ ext[0].min, ext[1].min - 0.16f, 0.0f }, mplot::TextFeatures(0.08f)); + + spv = std::make_unique> (sm::vec{-5,-4,0}); + v.bindmodel (spv); + spv->twodimensional (true); + spv->proj_type = sm::geometry::spherical_projection::type::equirectangular; + spv->latlong = latlong; + spv->colour = hpvcolours; + spv->finalize(); + spvp = v.addVisualModel (spv); + ext = spvp->extents(); + spvp->addLabel ("Equirectangular projection", sm::vec<>{ ext[0].min, ext[1].min - 0.16f, 0.0f }, mplot::TextFeatures(0.08f)); + + spv = std::make_unique> (sm::vec{-5,-8,0}); + v.bindmodel (spv); + spv->twodimensional (true); + spv->lambda0 = sm::mathconst::pi_over_4; + //spv->phi0 = sm::mathconst::pi_over_4; // Latitude offset + //spv->phi1 = sm::mathconst::pi_over_4; // Longitude scaling + spv->proj_type = sm::geometry::spherical_projection::type::equirectangular; + spv->latlong = latlong; + spv->colour = hpvcolours; + spv->finalize(); + spvp = v.addVisualModel (spv); + ext = spvp->extents(); + std::stringstream ss2; + ss2 << "Equirectangular projection with " + << mplot::unicode::toUtf8(mplot::unicode::lambda) + << mplot::unicode::toUtf8(mplot::unicode::subs0) + << " = " << mplot::unicode::toUtf8(mplot::unicode::pi) << "/4"; + spvp->addLabel (ss2.str(), sm::vec<>{ ext[0].min, ext[1].min - 0.16f, 0.0f }, mplot::TextFeatures(0.08f)); + + spv = std::make_unique> (sm::vec{-9,3,0}); + v.bindmodel (spv); + spv->twodimensional (true); + spv->proj_type = sm::geometry::spherical_projection::type::cassini; + spv->latlong = latlong; + spv->colour = hpvcolours; + spv->finalize(); + spvp = v.addVisualModel (spv); + ext = spvp->extents(); + spvp->addLabel ("Cassini projection", sm::vec<>{ ext[0].min, ext[1].min - 0.16f, 0.0f }, mplot::TextFeatures(0.08f)); + + spv = std::make_unique> (sm::vec{-4,3,0}); + v.bindmodel (spv); + spv->twodimensional (true); + spv->lambda0 = sm::mathconst::pi_over_4; + spv->proj_type = sm::geometry::spherical_projection::type::cassini; + spv->latlong = latlong; + spv->colour = hpvcolours; + spv->finalize(); + spvp = v.addVisualModel (spv); + ext = spvp->extents(); + std::stringstream ss3; + ss3 << "Cassini projection with " + << mplot::unicode::toUtf8(mplot::unicode::lambda) + << mplot::unicode::toUtf8(mplot::unicode::subs0) + << " = " << mplot::unicode::toUtf8(mplot::unicode::pi) << "/4"; + spvp->addLabel (ss3.str(), sm::vec<>{ ext[0].min, ext[1].min - 0.16f, 0.0f }, mplot::TextFeatures(0.08f)); v.keepOpen(); return 0; diff --git a/examples/hexy_eyes_dhex.eye b/examples/hexy_eyes_dhex.eye new file mode 100644 index 00000000..243ac413 --- /dev/null +++ b/examples/hexy_eyes_dhex.eye @@ -0,0 +1,1560 @@ +1.38564062 0 0.800000012 0.866025388 0 0.5 0.059501 1 +1.35484648 -0.0333333351 0.848887682 0.847220838 -0.0208442044 0.530831635 0.0586682 1 +1.35484648 0.0333333351 0.848887682 0.847220838 0.0208442044 0.530831635 0.0588084 1 +1.38371396 0.0666666701 0.79888767 0.865272462 0.0416884087 0.499565303 0.059784 1 +1.41258144 0.0333333351 0.748887658 0.883324146 0.0208442062 0.468299031 0.0604536 1 +1.41258144 -0.0333333351 0.748887658 0.883324146 -0.0208442062 0.468299031 0.0603094 1 +1.38371396 -0.0666666701 0.79888767 0.865272462 -0.0416884087 0.499565303 0.059501 1 +1.32017303 -0.0666666701 0.895535648 0.826840222 -0.0417541377 0.560884714 0.0579523 1 +1.3221128 0 0.89665556 0.827618957 0 0.561290324 0.0579523 1 +1.32017303 0.0666666701 0.895535648 0.826840222 0.0417541377 0.560884714 0.0582305 1 +1.3509804 0.100000009 0.846655548 0.845689476 0.0625982136 0.52999115 0.0592297 1 +1.37790811 0.13333334 0.795535624 0.863000393 0.0835082754 0.498253524 0.0603628 1 +1.40871537 0.100000009 0.746655524 0.881830633 0.0625982136 0.467393011 0.0608891 1 +1.4356432 0.0666666701 0.6955356 0.899160564 0.0417541377 0.435622305 0.061537 1 +1.43758297 0 0.696655512 0.899901152 0 0.436093837 0.0612397 1 +1.4356432 -0.0666666701 0.6955356 0.899160564 -0.0417541377 0.435622305 0.0612397 1 +1.40871537 -0.100000009 0.746655524 0.881830633 -0.0625982136 0.467393011 0.0604536 1 +1.37790811 -0.13333334 0.795535624 0.863000393 -0.0835082754 0.498253524 0.059784 1 +1.3509804 -0.100000009 0.846655548 0.845689476 -0.0625982136 0.52999115 0.0588084 1 +1.28154075 -0.100000001 0.939897954 0.804782927 -0.0627980754 0.590237856 0.0573419 1 +1.28546011 -0.0333333313 0.942160785 0.806381166 -0.0209103115 0.591026306 0.0572024 1 +1.28546011 0.0333333388 0.942160785 0.806381166 0.0209103171 0.591026306 0.0573419 1 +1.28154075 0.100000009 0.939897954 0.804782927 0.0627980828 0.590237856 0.0577647 1 +1.3143276 0.13333334 0.892160773 0.82449013 0.0836412609 0.559660852 0.0587982 1 +1.3431952 0.166666672 0.842160761 0.842598975 0.104551569 0.528295338 0.0599553 1 +1.36814332 0.200000018 0.789897919 0.859167695 0.125596166 0.496040702 0.0612573 1 +1.40093017 0.166666672 0.742160738 0.878816724 0.104551576 0.46556443 0.0616358 1 +1.42979777 0.13333334 0.692160726 0.896925569 0.0836412534 0.434198916 0.0621446 1 +1.45474589 0.100000001 0.639897883 0.913552463 0.0627980754 0.401843578 0.0627711 1 +1.45866525 0.0333333313 0.642160714 0.915034413 0.0209103115 0.402833432 0.0623085 1 +1.45866525 -0.0333333388 0.642160714 0.915034413 -0.0209103171 0.402833432 0.0621533 1 +1.45474589 -0.100000009 0.639897883 0.913552463 -0.0627980828 0.401843578 0.0623085 1 +1.42979777 -0.13333334 0.692160726 0.896925569 -0.0836412534 0.434198916 0.061537 1 +1.40093017 -0.166666672 0.742160738 0.878816724 -0.104551576 0.46556443 0.0608891 1 +1.36814332 -0.200000018 0.789897919 0.859167695 -0.125596166 0.496040702 0.0603628 1 +1.3431952 -0.166666672 0.842160761 0.842598975 -0.104551569 0.528295338 0.0592297 1 +1.3143276 -0.13333334 0.892160773 0.82449013 -0.0836412609 0.559660852 0.0582305 1 +1.23881078 -0.13333334 0.981894433 0.780912697 -0.084049724 0.618959606 0.0568416 1 +1.24477983 -0.0666666701 0.985340655 0.783389151 -0.0419559702 0.62011379 0.0565618 1 +1.24676013 0 0.986483991 0.784209371 0 0.620496273 0.0565618 1 +1.24477983 0.0666666701 0.985340655 0.783389151 0.0419559702 0.62011379 0.0568416 1 +1.23881078 0.13333334 0.981894433 0.780912697 0.084049724 0.618959606 0.0574188 1 +1.27364731 0.166666687 0.935340703 0.801556587 0.104889929 0.588646889 0.058487 1 +1.3044951 0.200000018 0.886484027 0.820524573 0.125799567 0.557596505 0.0596811 1 +1.33138239 0.233333349 0.835340679 0.837891579 0.146845892 0.525712907 0.0610134 1 +1.35428083 0.266666681 0.781894445 0.85370189 0.168099448 0.492885053 0.062514 1 +1.38911736 0.233333349 0.735340655 0.874226451 0.146845892 0.462778926 0.0627254 1 +1.41996527 0.200000018 0.686483979 0.893154919 0.125799567 0.431796879 0.063081 1 +1.44685245 0.166666672 0.635340631 0.910561502 0.104889929 0.399845004 0.0635633 1 +1.46975088 0.13333334 0.581894457 0.926491082 0.084049724 0.366810501 0.0641787 1 +1.47571993 0.0666666701 0.585340679 0.928728878 0.0419559702 0.368378013 0.0635267 1 +1.47770023 0 0.586484015 0.929470181 0 0.36889717 0.0632066 1 +1.47571993 -0.0666666701 0.585340679 0.928728878 -0.0419559702 0.368378013 0.0632066 1 +1.46975088 -0.13333334 0.581894457 0.926491082 -0.084049724 0.366810501 0.0635267 1 +1.44685245 -0.166666687 0.635340631 0.910561502 -0.104889944 0.399845004 0.0627711 1 +1.41996527 -0.200000018 0.686483979 0.893154919 -0.125799567 0.431796879 0.0621446 1 +1.38911736 -0.233333349 0.735340655 0.874226451 -0.146845892 0.462778926 0.0616358 1 +1.35428083 -0.266666681 0.781894445 0.85370189 -0.168099448 0.492885053 0.0612573 1 +1.33138239 -0.233333349 0.835340679 0.837891579 -0.146845892 0.525712907 0.0599553 1 +1.3044951 -0.200000018 0.886484027 0.820524573 -0.125799567 0.557596505 0.0587982 1 +1.27364731 -0.166666672 0.935340703 0.801556587 -0.104889922 0.588646889 0.0577647 1 +1.19177425 -0.166666687 1.0214045 0.755048394 -0.105591655 0.647110701 0.0564605 1 +1.19989884 -0.100000009 1.02609527 0.758484006 -0.0632123351 0.648618698 0.056029 1 +1.20393121 -0.0333333388 1.02842331 0.760184824 -0.0210472979 0.649365842 0.0558851 1 +1.20393121 0.0333333313 1.02842331 0.760184824 0.0210472923 0.649365842 0.056029 1 +1.19989884 0.100000001 1.02609527 0.758484006 0.0632123277 0.648618698 0.0564605 1 +1.19177425 0.166666672 1.0214045 0.755048394 0.105591647 0.647110701 0.0572076 1 +1.22876632 0.200000018 0.976095259 0.776731789 0.12642467 0.61701256 0.0583155 1 +1.26166618 0.233333349 0.928423345 0.79663986 0.147331089 0.58622402 0.0595471 1 +1.29053366 0.266666681 0.878423333 0.814867318 0.168378383 0.554653108 0.0609175 1 +1.31536889 0.300000012 0.826095283 0.831475317 0.18963699 0.522194088 0.0624548 1 +1.33611178 0.333333343 0.771404505 0.846493423 0.211183295 0.488723218 0.0641917 1 +1.37310386 0.300000012 0.726095259 0.867970943 0.18963699 0.458981723 0.0642131 1 +1.40600383 0.266666681 0.678423285 0.887777269 0.168378368 0.428369224 0.0643967 1 +1.43487132 0.233333349 0.628423274 0.906004786 0.147331074 0.396798283 0.0647256 1 +1.45970643 0.200000018 0.576095283 0.922714412 0.12642467 0.36416325 0.0651911 1 +1.48044932 0.166666687 0.521404505 0.937938452 0.105591655 0.330335766 0.0657968 1 +1.48857391 0.100000009 0.526095271 0.940962255 0.0632123351 0.332557082 0.0649234 1 +1.49260628 0.0333333388 0.528423309 0.942459822 0.0210472997 0.33365646 0.0644152 1 +1.49260628 -0.0333333313 0.528423309 0.942459822 -0.0210472941 0.33365646 0.0642456 1 +1.48857391 -0.100000001 0.526095271 0.940962255 -0.0632123277 0.332557082 0.0644152 1 +1.48044932 -0.166666672 0.521404505 0.937938452 -0.105591647 0.330335766 0.0649234 1 +1.45970643 -0.200000018 0.576095283 0.922714412 -0.12642467 0.36416325 0.0641787 1 +1.43487132 -0.233333349 0.628423274 0.906004786 -0.147331074 0.396798283 0.0635633 1 +1.40600383 -0.266666681 0.678423285 0.887777269 -0.168378368 0.428369224 0.063081 1 +1.37310386 -0.300000012 0.726095259 0.867970943 -0.18963699 0.458981723 0.0627254 1 +1.33611178 -0.333333343 0.771404505 0.846493423 -0.211183295 0.488723218 0.062514 1 +1.31536889 -0.300000012 0.826095283 0.831475317 -0.18963699 0.522194088 0.0610134 1 +1.29053366 -0.266666681 0.878423333 0.814867318 -0.168378383 0.554653108 0.0596811 1 +1.26166618 -0.233333349 0.928423345 0.79663986 -0.147331089 0.58622402 0.058487 1 +1.22876632 -0.200000018 0.976095259 0.776731789 -0.12642467 0.61701256 0.0574188 1 +1.14013553 -0.200000003 1.05825758 0.726950765 -0.127520069 0.674745381 0.0562107 1 +1.15056598 -0.133333325 1.06427968 0.731455445 -0.0847647041 0.676600218 0.0556069 1 +1.15675986 -0.0666666627 1.06785572 0.734121025 -0.0423090383 0.677699268 0.0553102 1 +1.15881395 7.4505806e-09 1.06904161 0.735003412 4.72569583e-09 0.678063333 0.0553102 1 +1.15675986 0.0666666776 1.06785572 0.734121025 0.0423090495 0.677699268 0.0556069 1 +1.15056598 0.133333355 1.06427968 0.731455445 0.0847647265 0.676600218 0.0562107 1 +1.14013553 0.200000018 1.05825758 0.726950765 0.127520069 0.674745381 0.0571555 1 +1.17943358 0.233333349 1.0142796 0.749807596 0.148338258 0.644813359 0.0583052 1 +1.21449494 0.266666681 0.967855632 0.770761788 0.169236183 0.61423564 0.0595761 1 +1.24541652 0.300000012 0.919041634 0.789933026 0.190281659 0.582922518 0.0609889 1 +1.27222991 0.333333343 0.867855668 0.807402432 0.211545214 0.55077213 0.0625722 1 +1.29490364 0.366666675 0.814279616 0.823215902 0.233102933 0.51766628 0.0643559 1 +1.31334066 0.400000036 0.758257568 0.83738631 0.255040109 0.483465225 0.0663852 1 +1.3526386 0.366666675 0.714279592 0.859920144 0.233102962 0.454092771 0.0661755 1 +1.38770008 0.333333343 0.66785562 0.880683899 0.211545214 0.423844963 0.0661584 1 +1.41862166 0.300000012 0.619041562 0.899792194 0.190281659 0.392640829 0.0663115 1 +1.44543505 0.266666681 0.567855656 0.917324603 0.169236183 0.360381424 0.0666184 1 +1.46810865 0.233333349 0.514279604 0.933328569 0.148338258 0.326945722 0.0670762 1 +1.4865458 0.200000003 0.458257556 0.947821975 0.127520055 0.292185128 0.0676711 1 +1.49697626 0.133333325 0.464279592 0.95168072 0.0847647041 0.295158952 0.066536 1 +1.50317013 0.0666666627 0.467855632 0.953965247 0.0423090346 0.296917826 0.0658058 1 +1.50522423 -7.4505806e-09 0.469041586 0.954721808 -4.72569583e-09 0.297500014 0.0654487 1 +1.50317013 -0.0666666776 0.467855632 0.953965247 -0.0423090458 0.296917826 0.0654487 1 +1.49697626 -0.133333355 0.464279592 0.95168072 -0.0847647265 0.295158952 0.0658058 1 +1.4865458 -0.200000018 0.458257556 0.947821975 -0.127520055 0.292185128 0.0665342 1 +1.46810865 -0.233333349 0.514279604 0.933328569 -0.148338258 0.326945722 0.0657968 1 +1.44543505 -0.266666681 0.567855656 0.917324603 -0.169236183 0.360381424 0.0651911 1 +1.41862166 -0.300000012 0.619041562 0.899792194 -0.190281659 0.392640829 0.0647256 1 +1.38770008 -0.333333343 0.66785562 0.880683899 -0.211545214 0.423844963 0.0643967 1 +1.3526386 -0.366666675 0.714279592 0.859920144 -0.233102962 0.454092771 0.0642131 1 +1.31334066 -0.400000036 0.758257568 0.83738631 -0.255040109 0.483465225 0.0641917 1 +1.29490364 -0.366666675 0.814279616 0.823215902 -0.233102933 0.51766628 0.0624548 1 +1.27222991 -0.333333343 0.867855668 0.807402432 -0.211545214 0.55077213 0.0609175 1 +1.24541652 -0.300000012 0.919041634 0.789933026 -0.190281659 0.582922518 0.0595471 1 +1.21449494 -0.266666681 0.967855632 0.770761788 -0.169236183 0.61423564 0.0583155 1 +1.17943358 -0.233333349 1.0142796 0.749807596 -0.148338258 0.644813359 0.0572076 1 +1.08348429 -0.233333349 1.09221661 0.696300268 -0.149951488 0.701912105 0.0561183 1 +1.09643066 -0.166666672 1.09969127 0.70202446 -0.106713615 0.704112172 0.0553155 1 +1.10494339 -0.100000001 1.10460603 0.705770612 -0.0638739169 0.705555141 0.0548511 1 +1.10916543 -0.0333333313 1.10704362 0.707623422 -0.0212659407 0.706269741 0.0546954 1 +1.10916543 0.0333333388 1.10704362 0.707623422 0.0212659445 0.706269741 0.0548511 1 +1.10494339 0.100000009 1.10460603 0.705770612 0.0638739243 0.705555141 0.0553155 1 +1.09643066 0.166666687 1.09969127 0.70202446 0.106713623 0.704112172 0.0561183 1 +1.08348429 0.233333349 1.09221661 0.696300268 0.149951488 0.701912105 0.0572993 1 +1.12529814 0.266666681 1.04969132 0.720507801 0.170741796 0.6720981 0.058488 1 +1.16267848 0.300000012 1.00460613 0.742648125 0.191621721 0.641681135 0.0598069 1 +1.195768 0.333333343 0.957043648 0.762873888 0.212659404 0.610572994 0.061267 1 +1.22463548 0.366666675 0.907043695 0.781290829 0.233925372 0.578674138 0.0628992 1 +1.24928093 0.400000036 0.854606092 0.797964573 0.255495667 0.545870304 0.0647403 1 +1.2696358 0.433333367 0.79969126 0.812924445 0.277455389 0.512027621 0.0668312 1 +1.28555691 0.466666698 0.742216647 0.82616204 0.299902946 0.476984859 0.0692405 1 +1.32737076 0.433333367 0.699691236 0.849891186 0.277455419 0.447999477 0.0687269 1 +1.3647511 0.400000036 0.654606044 0.871719897 0.255495667 0.41812247 0.0684625 1 +1.39784062 0.366666675 0.607043624 0.89179194 0.233925372 0.387280643 0.0684058 1 +1.4267081 0.333333343 0.557043672 0.910208642 0.212659404 0.355381727 0.068533 1 +1.45135355 0.300000012 0.504606068 0.927036345 0.191621765 0.32231167 0.0688275 1 +1.47170842 0.266666681 0.449691236 0.94230783 0.170741767 0.287929028 0.0692793 1 +1.48762953 0.233333349 0.392216623 0.956023812 0.149951473 0.252057672 0.0698854 1 +1.5005759 0.166666672 0.399691224 0.96079123 0.106713615 0.255914956 0.0684146 1 +1.50908864 0.100000001 0.404606044 0.963913918 0.0638739094 0.258437693 0.0674159 1 +1.51331067 0.0333333313 0.407043636 0.965459287 0.0212659407 0.25968498 0.0668365 1 +1.51331067 -0.0333333388 0.407043636 0.965459287 -0.0212659445 0.25968498 0.0666471 1 +1.50908864 -0.100000009 0.404606044 0.963913918 -0.0638739169 0.258437693 0.0668365 1 +1.5005759 -0.166666687 0.399691224 0.96079123 -0.106713623 0.255914956 0.0674159 1 +1.48762953 -0.233333349 0.392216623 0.956023812 -0.149951473 0.252057672 0.0684146 1 +1.47170842 -0.266666681 0.449691236 0.94230783 -0.170741767 0.287929028 0.0676711 1 +1.45135355 -0.300000012 0.504606068 0.927036345 -0.191621765 0.32231167 0.0670762 1 +1.4267081 -0.333333343 0.557043672 0.910208642 -0.212659404 0.355381727 0.0666184 1 +1.39784062 -0.366666675 0.607043624 0.89179194 -0.233925372 0.387280643 0.0663115 1 +1.3647511 -0.400000036 0.654606044 0.871719897 -0.255495667 0.41812247 0.0661584 1 +1.32737076 -0.433333367 0.699691236 0.849891186 -0.277455419 0.447999477 0.0661755 1 +1.28555691 -0.466666698 0.742216647 0.82616204 -0.299902946 0.476984859 0.0663852 1 +1.2696358 -0.433333367 0.79969126 0.812924445 -0.277455389 0.512027621 0.0643559 1 +1.24928093 -0.400000036 0.854606092 0.797964573 -0.255495667 0.545870304 0.0625722 1 +1.22463548 -0.366666675 0.907043695 0.781290829 -0.233925372 0.578674138 0.0609889 1 +1.195768 -0.333333343 0.957043648 0.762873888 -0.212659404 0.610572994 0.0595761 1 +1.16267848 -0.300000012 1.00460613 0.742648125 -0.191621721 0.641681135 0.0583052 1 +1.12529814 -0.266666681 1.04969132 0.720507801 -0.170741796 0.6720981 0.0571555 1 +1.02125049 -0.266666681 1.12295258 0.662662804 -0.173033059 0.728654623 0.056216 1 +1.03700662 -0.200000018 1.13204944 0.669815063 -0.129182428 0.731204391 0.0551763 1 +1.04806089 -0.13333334 1.13843155 0.674802423 -0.0858477429 0.732988298 0.0545119 1 +1.05461681 -0.0666666701 1.14221668 0.677748561 -0.0428432748 0.734044552 0.0541883 1 +1.05678964 0 1.14347112 0.678723156 0 0.734394312 0.0541883 1 +1.05461681 0.0666666701 1.14221668 0.677748561 0.0428432748 0.734044552 0.0545119 1 +1.04806089 0.13333334 1.13843155 0.674802423 0.0858477429 0.732988298 0.0551763 1 +1.03700662 0.200000018 1.13204944 0.669815063 0.129182428 0.731204391 0.056216 1 +1.02125049 0.266666681 1.12295258 0.662662804 0.173033059 0.728654623 0.0576955 1 +1.0658741 0.300000012 1.08204937 0.688460946 0.193773627 0.698908746 0.0589289 1 +1.10579586 0.333333373 1.03843164 0.711975515 0.214619368 0.668602526 0.0602896 1 +1.14121938 0.366666675 0.992216647 0.733403623 0.235638008 0.637647152 0.061803 1 +1.17225969 0.400000036 0.943471134 0.752883732 0.256900012 0.605944276 0.0634929 1 +1.19895434 0.433333367 0.892216682 0.770506918 0.278481305 0.573382258 0.0653958 1 +1.22126591 0.466666698 0.838431597 0.786321819 0.300467104 0.539830923 0.0675626 1 +1.23907924 0.5 0.782049417 0.800336182 0.322956026 0.505135119 0.0700576 1 +1.25219059 0.533333361 0.722952604 0.812513828 0.346066117 0.469105095 0.0729754 1 +1.2968142 0.5 0.682049394 0.837627947 0.322956026 0.44054392 0.0720524 1 +1.33673608 0.466666698 0.638431549 0.860668242 0.300467104 0.41105926 0.0714655 1 +1.37215948 0.433333337 0.592216611 0.881817102 0.278481275 0.380587488 0.0711408 1 +1.40319979 0.400000036 0.543471158 0.901205063 0.256900012 0.349044353 0.0710502 1 +1.42989445 0.366666675 0.492216617 0.918920457 0.235638037 0.316322595 0.0711601 1 +1.45220613 0.333333343 0.438431561 0.935014546 0.214619339 0.282287687 0.0714521 1 +1.47001934 0.300000012 0.382049382 0.949503183 0.193773627 0.246770293 0.071914 1 +1.48313069 0.266666681 0.322952598 0.962364852 0.173033059 0.209555522 0.0725475 1 +1.49888682 0.200000018 0.3320494 0.968149066 0.129182428 0.214474708 0.0706452 1 +1.5099411 0.13333334 0.338431567 0.972187698 0.0858477429 0.217901871 0.0693085 1 +1.51649702 0.0666666701 0.342216641 0.974575579 0.0428432822 0.21992524 0.0684564 1 +1.51866984 0 0.34347114 0.975365698 0 0.220594347 0.0680421 1 +1.51649702 -0.0666666701 0.342216641 0.974575579 -0.0428432822 0.21992524 0.0680421 1 +1.5099411 -0.13333334 0.338431567 0.972187698 -0.0858477429 0.217901871 0.0684564 1 +1.49888682 -0.200000018 0.3320494 0.968149066 -0.129182428 0.214474708 0.0693085 1 +1.48313069 -0.266666681 0.322952598 0.962364852 -0.173033059 0.209555522 0.0706452 1 +1.47001934 -0.300000012 0.382049382 0.949503183 -0.193773627 0.246770293 0.0698854 1 +1.45220613 -0.333333373 0.438431561 0.935014546 -0.214619368 0.282287687 0.0692793 1 +1.42989445 -0.366666675 0.492216617 0.918920457 -0.235638037 0.316322595 0.0688275 1 +1.40319979 -0.400000036 0.543471158 0.901205063 -0.256900012 0.349044353 0.068533 1 +1.37215948 -0.433333367 0.592216611 0.881817102 -0.278481305 0.380587488 0.0684058 1 +1.33673608 -0.466666698 0.638431549 0.860668242 -0.300467104 0.41105926 0.0684625 1 +1.2968142 -0.5 0.682049394 0.837627947 -0.322956026 0.44054392 0.0687269 1 +1.25219059 -0.533333361 0.722952604 0.812513828 -0.346066117 0.469105095 0.0692405 1 +1.23907924 -0.5 0.782049417 0.800336182 -0.322956026 0.505135119 0.0668312 1 +1.22126591 -0.466666698 0.838431597 0.786321819 -0.300467104 0.539830923 0.0647403 1 +1.19895434 -0.433333337 0.892216682 0.770506918 -0.278481275 0.573382258 0.0628992 1 +1.17225969 -0.400000036 0.943471134 0.752883732 -0.256900012 0.605944276 0.061267 1 +1.14121938 -0.366666675 0.992216647 0.733403623 -0.235638008 0.637647152 0.0598069 1 +1.10579586 -0.333333343 1.03843164 0.711975515 -0.214619339 0.668602526 0.0584891 1 +1.0658741 -0.300000012 1.08204937 0.688460946 -0.193773627 0.698908746 0.0572993 1 +0.952627957 -0.300000012 1.1500001 0.625430822 -0.196959645 0.755011976 0.0565734 1 +0.971612751 -0.233333349 1.16096091 0.634308934 -0.152329654 0.757923245 0.0552292 1 +0.985525548 -0.166666687 1.16899347 0.640764892 -0.108362645 0.76005125 0.0543267 1 +0.994654477 -0.100000009 1.17426407 0.644978166 -0.0648444444 0.761444986 0.053805 1 +0.999176681 -0.0333333388 1.176875 0.647058666 -0.0215863977 0.762134612 0.053633 1 +0.999176681 0.0333333313 1.176875 0.647058666 0.0215863939 0.762134612 0.053805 1 +0.994654477 0.100000001 1.17426407 0.644978166 0.0648444444 0.761444986 0.0543267 1 +0.985525548 0.166666672 1.16899347 0.640764892 0.108362637 0.76005125 0.0552292 1 +0.971612751 0.233333349 1.16096091 0.634308934 0.152329654 0.757923245 0.0565734 1 +0.952627957 0.300000012 1.1500001 0.625430822 0.196959645 0.755011976 0.0584452 1 +1.00048029 0.333333343 1.11096096 0.65315491 0.217613786 0.725281239 0.0597111 1 +1.04326069 0.366666675 1.06899357 0.678302765 0.238397762 0.69503361 0.0611159 1 +1.08125699 0.400000036 1.0242641 0.701135039 0.259377778 0.664178312 0.0626769 1 +1.11464679 0.433333337 0.976874948 0.72183615 0.280623138 0.632616222 0.0644291 1 +1.14351428 0.466666698 0.926874936 0.740530431 0.302209526 0.600236595 0.0664077 1 +1.16785955 0.5 0.874264121 0.757292032 0.324222207 0.566911697 0.0686635 1 +1.18759823 0.533333361 0.818993568 0.772147596 0.346760422 0.532489777 0.0712682 1 +1.20255291 0.566666722 0.760960937 0.785076261 0.36994347 0.496786743 0.0743177 1 +1.2124356 0.600000024 0.700000048 0.796002924 0.393919289 0.459572494 0.0779568 1 +1.26028788 0.566666722 0.660960913 0.822768033 0.36994347 0.43150261 0.0764467 1 +1.3030684 0.533333361 0.618993521 0.847223401 0.346760392 0.402454525 0.0754048 1 +1.34106469 0.5 0.57426405 0.869605899 0.324222207 0.37237832 0.0747276 1 +1.37445438 0.466666698 0.526874959 0.890085399 0.302209526 0.341199905 0.0743506 1 +1.40332186 0.433333367 0.476874918 0.9087798 0.280623138 0.308820307 0.0742253 1 +1.42766726 0.400000006 0.424264073 0.925762773 0.259377748 0.275111645 0.074329 1 +1.44740593 0.366666675 0.368993521 0.941068172 0.238397762 0.239910617 0.0746373 1 +1.4623605 0.333333343 0.310960919 0.954689384 0.217613786 0.203008145 0.075144 1 +1.47224331 0.300000012 0.25 0.966575027 0.196959645 0.164133027 0.0758458 1 +1.4912281 0.233333349 0.260960877 0.973535359 0.152329654 0.170366049 0.0733538 1 +1.50514078 0.166666687 0.268993437 0.978606105 0.108362645 0.174893022 0.0715697 1 +1.51426983 0.100000009 0.274263978 0.981919706 0.0648444444 0.177844927 0.0703677 1 +1.51879191 0.0333333388 0.2768749 0.983557224 0.0215863977 0.179301918 0.0696725 1 +1.51879191 -0.0333333313 0.2768749 0.983557224 -0.0215863939 0.179301918 0.0694444 1 +1.51426983 -0.100000001 0.274263978 0.981919706 -0.0648444369 0.177844927 0.0696725 1 +1.50514078 -0.166666672 0.268993437 0.978606105 -0.108362637 0.174893022 0.0703677 1 +1.4912281 -0.233333349 0.260960877 0.973535359 -0.152329654 0.170366049 0.0715697 1 +1.47224331 -0.300000012 0.25 0.966575027 -0.196959645 0.164133027 0.0733538 1 +1.4623605 -0.333333343 0.310960919 0.954689384 -0.217613786 0.203008145 0.0725475 1 +1.44740593 -0.366666675 0.368993521 0.941068172 -0.238397762 0.239910617 0.071914 1 +1.42766726 -0.400000036 0.424264073 0.925762773 -0.259377778 0.275111645 0.0714521 1 +1.40332186 -0.433333337 0.476874918 0.9087798 -0.280623138 0.308820307 0.0711601 1 +1.37445438 -0.466666698 0.526874959 0.890085399 -0.302209526 0.341199905 0.0710502 1 +1.34106469 -0.5 0.57426405 0.869605899 -0.324222207 0.37237832 0.0711408 1 +1.3030684 -0.533333361 0.618993521 0.847223401 -0.346760392 0.402454525 0.0714655 1 +1.26028788 -0.566666722 0.660960913 0.822768033 -0.36994347 0.43150261 0.0720524 1 +1.2124356 -0.600000024 0.700000048 0.796002924 -0.393919289 0.459572494 0.0729754 1 +1.20255291 -0.566666722 0.760960937 0.785076261 -0.36994347 0.496786743 0.0700576 1 +1.18759823 -0.533333361 0.818993568 0.772147596 -0.346760422 0.532489777 0.0675626 1 +1.16785955 -0.5 0.874264121 0.757292032 -0.324222207 0.566911697 0.0653976 1 +1.14351428 -0.466666698 0.926874936 0.740530431 -0.302209526 0.600236595 0.0634929 1 +1.11464679 -0.433333367 0.976874948 0.72183615 -0.280623138 0.632616222 0.061803 1 +1.08125699 -0.400000006 1.0242641 0.701135039 -0.259377778 0.664178312 0.0602896 1 +1.04326069 -0.366666675 1.06899357 0.678302765 -0.238397762 0.69503361 0.0589289 1 +1.00048029 -0.333333343 1.11096096 0.65315491 -0.217613786 0.725281239 0.0576955 1 +0.876437306 -0.333333373 1.17267799 0.583717525 -0.222003937 0.781017303 0.0573024 1 +0.89927125 -0.266666681 1.18586123 0.5947662 -0.176369831 0.784313023 0.0555501 1 +0.916505456 -0.200000018 1.19581139 0.603023231 -0.131591856 0.786795139 0.0543311 1 +0.928555012 -0.133333355 1.20276821 0.608755231 -0.0874125734 0.788527846 0.0535606 1 +0.935685873 -0.0666666776 1.20688522 0.612131715 -0.0436137691 0.789552093 0.0531897 1 +0.938046813 -7.4505806e-09 1.20824826 0.613247216 -4.87080998e-09 0.789891124 0.0531897 1 +0.935685873 0.0666666627 1.20688522 0.612131715 0.0436137617 0.789552093 0.0535618 1 +0.928555012 0.133333325 1.20276821 0.608755231 0.087412551 0.788527846 0.0543311 1 +0.916505456 0.200000003 1.19581139 0.603023231 0.131591842 0.786795139 0.0555501 1 +0.89927125 0.266666681 1.18586123 0.5947662 0.176369831 0.784313023 0.0573024 1 +0.876437306 0.333333343 1.17267799 0.583717525 0.222003922 0.781017303 0.0597391 1 +0.928138793 0.366666675 1.13586128 0.613858759 0.242508501 0.751243651 0.0610007 1 +0.974240541 0.400000036 1.09581137 0.641010523 0.263183713 0.720999181 0.0624262 1 +1.01515758 0.433333367 1.05276823 0.665531397 0.284090817 0.690188706 0.0640298 1 +1.05115592 0.466666698 1.00688529 0.687672973 0.305296361 0.658710837 0.0658394 1 +1.08238447 0.5 0.958248258 0.707607746 0.326874495 0.626453817 0.0679008 1 +1.10889101 0.533333361 0.906885207 0.725443661 0.348910123 0.59329015 0.0702625 1 +1.13062763 0.566666722 0.852768242 0.741232812 0.371503353 0.559069812 0.0730008 1 +1.14744556 0.600000024 0.795811415 0.754972398 0.39477554 0.523611486 0.0762271 1 +1.15907896 0.633333385 0.735861242 0.766599536 0.418878347 0.486688912 0.0800906 1 +1.1651125 0.666666687 0.672677994 0.775978625 0.444007844 0.448011458 0.0848333 1 +1.21681392 0.633333385 0.635861218 0.804784715 0.418878376 0.420550257 0.0824019 1 +1.26291573 0.600000024 0.595811367 0.830947042 0.39477554 0.39201957 0.08068 1 +1.30383277 0.566666722 0.552768171 0.854785085 0.371503353 0.362391561 0.0794901 1 +1.33983111 0.533333361 0.506885231 0.876526177 0.348910123 0.33160758 0.0787147 1 +1.37105954 0.5 0.458248228 0.896328866 0.326874524 0.299579352 0.0782822 1 +1.3975662 0.466666698 0.406885207 0.914296806 0.305296361 0.266186923 0.0781418 1 +1.41930282 0.433333337 0.352768183 0.93048656 0.284090787 0.231272742 0.0782662 1 +1.43612075 0.400000036 0.295811385 0.944908917 0.263183713 0.19463183 0.0786373 1 +1.44775414 0.366666675 0.235861197 0.957525492 0.242508501 0.155995473 0.079248 1 +1.45378768 0.333333373 0.172677964 0.968239605 0.222003922 0.115005545 0.0801062 1 +1.47662163 0.266666681 0.1858612 0.976618052 0.176369816 0.122926146 0.0767544 1 +1.49385583 0.200000018 0.195811361 0.982896209 0.131591856 0.128835887 0.074341 1 +1.50590539 0.133333355 0.202768177 0.987262726 0.0874125659 0.132933617 0.0726666 1 +1.51303625 0.0666666776 0.206885189 0.989838064 0.0436137691 0.135345623 0.0716081 1 +1.51539719 7.4505806e-09 0.208248228 0.990689278 4.87080909e-09 0.13614206 0.0710955 1 +1.51303625 -0.0666666627 0.206885189 0.989838064 -0.0436137617 0.135345623 0.0710955 1 +1.50590539 -0.133333325 0.202768177 0.987262726 -0.0874125436 0.132933617 0.0716081 1 +1.49385583 -0.200000003 0.195811361 0.982896209 -0.131591842 0.128835887 0.0726674 1 +1.47662163 -0.266666681 0.1858612 0.976618052 -0.176369816 0.122926146 0.074341 1 +1.45378768 -0.333333343 0.172677964 0.968239605 -0.222003907 0.115005545 0.0767544 1 +1.44775414 -0.366666675 0.235861197 0.957525492 -0.242508501 0.155995473 0.0758458 1 +1.43612075 -0.400000036 0.295811385 0.944908917 -0.263183713 0.19463183 0.075144 1 +1.41930282 -0.433333367 0.352768183 0.93048656 -0.284090817 0.231272742 0.0746373 1 +1.3975662 -0.466666698 0.406885207 0.914296806 -0.305296361 0.266186923 0.074329 1 +1.37105954 -0.5 0.458248228 0.896328866 -0.326874524 0.299579352 0.0742253 1 +1.33983111 -0.533333361 0.506885231 0.876526177 -0.348910123 0.33160758 0.0743506 1 +1.30383277 -0.566666722 0.552768171 0.854785085 -0.371503353 0.362391561 0.0747276 1 +1.26291573 -0.600000024 0.595811367 0.830947042 -0.39477554 0.39201957 0.0754048 1 +1.21681392 -0.633333385 0.635861218 0.804784715 -0.418878376 0.420550257 0.0764467 1 +1.1651125 -0.666666687 0.672677994 0.775978625 -0.444007844 0.448011458 0.0779568 1 +1.15907896 -0.633333385 0.735861242 0.766599536 -0.418878347 0.486688912 0.0743177 1 +1.14744556 -0.600000024 0.795811415 0.754972398 -0.39477554 0.523611486 0.0712682 1 +1.13062763 -0.566666722 0.852768242 0.741232812 -0.371503353 0.559069812 0.0686635 1 +1.10889101 -0.533333361 0.906885207 0.725443661 -0.348910123 0.59329015 0.0664077 1 +1.08238447 -0.5 0.958248258 0.707607746 -0.326874495 0.626453817 0.0644291 1 +1.05115592 -0.466666698 1.00688529 0.687672973 -0.305296361 0.658710837 0.0626769 1 +1.01515758 -0.433333337 1.05276823 0.665531397 -0.284090817 0.690188706 0.0611159 1 +0.974240541 -0.400000036 1.09581137 0.641010523 -0.263183713 0.720999181 0.0597111 1 +0.928138793 -0.366666675 1.13586128 0.613858759 -0.242508501 0.751243651 0.0584452 1 +0.7908566 -0.366666704 1.18993473 0.536147118 -0.248575151 0.806694984 0.0586215 1 +0.818513989 -0.300000012 1.2059027 0.550073862 -0.201611891 0.810414374 0.0562679 1 +0.839776754 -0.233333349 1.21817875 0.560645461 -0.155776262 0.813271344 0.0546147 1 +0.85526979 -0.166666687 1.22712362 0.568276584 -0.110740229 0.815351605 0.0535183 1 +0.865397453 -0.100000016 1.23297083 0.573232889 -0.066239275 0.81671077 0.0528895 1 +0.870403767 -0.0333333462 1.2358613 0.57567358 -0.0220462345 0.817382336 0.0526839 1 +0.870403767 0.0333333239 1.2358613 0.57567358 0.0220462196 0.817382336 0.0528895 1 +0.865397453 0.099999994 1.23297083 0.573232889 0.0662392601 0.81671077 0.0535183 1 +0.85526979 0.166666657 1.22712362 0.568276584 0.110740207 0.815351605 0.0546147 1 +0.839776754 0.233333334 1.21817875 0.560645461 0.155776247 0.813271344 0.0562679 1 +0.818513989 0.300000012 1.2059027 0.550073862 0.201611891 0.810414374 0.0586215 1 +0.7908566 0.366666675 1.18993473 0.536147118 0.248575121 0.806694984 0.0619322 1 +0.847381473 0.400000006 1.15590262 0.569473922 0.268815845 0.776812375 0.0631018 1 +0.89751178 0.433333337 1.11817873 0.599190116 0.289298773 0.746510148 0.0644856 1 +0.941872358 0.466666698 1.07712364 0.625818908 0.310072631 0.715685427 0.0660971 1 +0.980867624 0.5 1.03297091 0.649719417 0.331196278 0.684232235 0.0679518 1 +1.0147413 0.533333361 0.985861242 0.671136558 0.352739662 0.652035654 0.0700899 1 +1.0436089 0.566666722 0.93586123 0.690229118 0.37478587 0.618966281 0.0725746 1 +1.06747019 0.600000024 0.88297087 0.707084298 0.397435546 0.584873319 0.0754823 1 +1.08620989 0.633333385 0.827123642 0.721722782 0.420812875 0.54957515 0.0789327 1 +1.09958434 0.666666687 0.768178701 0.734096289 0.445075035 0.512845755 0.0831049 1 +1.10718918 0.700000048 0.705902636 0.744075 0.470427781 0.47439453 0.0882712 1 +1.10839927 0.733333349 0.639934659 0.751419485 0.497150242 0.433832288 0.0948841 1 +1.16492414 0.700000048 0.605902612 0.78287518 0.470427781 0.407190561 0.0908404 1 +1.21505451 0.666666687 0.568178654 0.811185598 0.445075005 0.379323155 0.087999 1 +1.25941503 0.633333385 0.52712357 0.83680737 0.420812845 0.350242674 0.085998 1 +1.2984103 0.600000024 0.482970864 0.860057294 0.397435546 0.319916308 0.0846262 1 +1.33228397 0.566666722 0.4358612 0.881155074 0.37478593 0.288272858 0.0837458 1 +1.36115158 0.533333361 0.385861218 0.900247693 0.352739632 0.255203515 0.0832685 1 +1.38501287 0.5 0.332970858 0.917422235 0.331196278 0.220557421 0.0831523 1 +1.40375257 0.466666698 0.2771236 0.932711244 0.31007266 0.184132382 0.0833609 1 +1.41712713 0.433333367 0.218178645 0.946091771 0.289298773 0.145658791 0.0838854 1 +1.42473173 0.400000036 0.155902535 0.957476199 0.268815875 0.10477268 0.0847397 1 +1.42594194 0.366666704 0.0899346247 0.966691852 0.248575151 0.0609695725 0.085957 1 +1.45359933 0.300000012 0.10590259 0.976876378 0.201611891 0.0711707398 0.0812364 1 +1.4748621 0.233333349 0.118178643 0.984636426 0.155776277 0.0788975433 0.0778803 1 +1.49035513 0.166666687 0.127123579 0.990253508 0.110740229 0.0844661519 0.0755257 1 +1.5004828 0.100000016 0.132970795 0.993908703 0.066239275 0.0880788714 0.0739637 1 +1.50548911 0.0333333462 0.135861203 0.995710671 0.0220462363 0.0898568109 0.0730694 1 +1.50548911 -0.0333333239 0.135861203 0.995710671 -0.0220462214 0.0898568109 0.0727782 1 +1.5004828 -0.099999994 0.132970795 0.993908703 -0.0662392601 0.0880788714 0.0730694 1 +1.49035513 -0.166666657 0.127123579 0.990253508 -0.110740207 0.0844661519 0.0739637 1 +1.4748621 -0.233333334 0.118178643 0.984636426 -0.155776262 0.0788975433 0.0755257 1 +1.45359933 -0.300000012 0.10590259 0.976876378 -0.201611891 0.0711707398 0.0778803 1 +1.42594194 -0.366666675 0.0899346247 0.966691852 -0.248575121 0.0609695725 0.0812364 1 +1.42473185 -0.400000006 0.155902594 0.957476258 -0.268815845 0.104772724 0.080107 1 +1.41712713 -0.433333337 0.218178645 0.946091771 -0.289298743 0.145658791 0.0792473 1 +1.40375257 -0.466666698 0.2771236 0.932711244 -0.31007266 0.184132382 0.0786373 1 +1.38501287 -0.5 0.332970858 0.917422235 -0.331196278 0.220557421 0.0782662 1 +1.36115158 -0.533333361 0.385861218 0.900247693 -0.352739632 0.255203515 0.0781418 1 +1.33228397 -0.566666722 0.4358612 0.881155074 -0.37478593 0.288272858 0.0782822 1 +1.2984103 -0.600000024 0.482970864 0.860057294 -0.397435546 0.319916308 0.0787147 1 +1.25941503 -0.633333385 0.52712357 0.83680737 -0.420812845 0.350242674 0.0794901 1 +1.21505451 -0.666666687 0.568178654 0.811185598 -0.445075005 0.379323155 0.08068 1 +1.16492414 -0.700000048 0.605902612 0.78287518 -0.470427781 0.407190561 0.0824019 1 +1.10839927 -0.733333349 0.639934659 0.751419485 -0.497150242 0.433832288 0.0848333 1 +1.10718918 -0.700000048 0.705902636 0.744075 -0.470427781 0.47439453 0.0800906 1 +1.09958434 -0.666666687 0.768178701 0.734096289 -0.445075035 0.512845755 0.0762271 1 +1.08620989 -0.633333385 0.827123642 0.721722782 -0.420812875 0.54957515 0.0730008 1 +1.06747019 -0.600000024 0.88297087 0.707084298 -0.397435546 0.584873319 0.0702625 1 +1.0436089 -0.566666722 0.93586123 0.690229118 -0.37478587 0.618966281 0.0679008 1 +1.0147413 -0.533333361 0.985861242 0.671136558 -0.352739662 0.652035654 0.0658394 1 +0.980867624 -0.5 1.03297091 0.649719417 -0.331196278 0.684232235 0.0640298 1 +0.941872358 -0.466666698 1.07712364 0.625818908 -0.310072631 0.715685427 0.0624262 1 +0.89751178 -0.433333367 1.11817873 0.599190056 -0.289298773 0.746510088 0.0610007 1 +0.847381353 -0.400000036 1.15590262 0.569473863 -0.268815875 0.776812375 0.0597391 1 +0.692820191 -0.400000006 1.20000005 0.480384409 -0.277350128 0.832050383 0.0609919 1 +0.72698009 -0.333333343 1.21972215 0.498430669 -0.228539348 0.836263537 0.05765 1 +0.753434896 -0.266666651 1.23499584 0.512174666 -0.181276321 0.83953321 0.0553296 1 +0.773205042 -0.199999988 1.24641025 0.522318482 -0.135104761 0.841979861 0.0537607 1 +0.78693676 -0.133333325 1.25433826 0.529301643 -0.0896813497 0.84368068 0.0527846 1 +0.795030355 -0.0666666552 1.25901103 0.533394098 -0.0447273478 0.844683528 0.0523136 1 +0.797704816 1.49011612e-08 1.26055515 0.534742594 9.98901495e-09 0.845014989 0.0523136 1 +0.795030355 0.066666685 1.25901103 0.533394098 0.0447273701 0.844683528 0.0527834 1 +0.78693676 0.133333355 1.25433826 0.529301643 0.089681372 0.84368068 0.0537607 1 +0.773204923 0.200000018 1.24641013 0.522318482 0.135104805 0.84197998 0.0553296 1 +0.753434896 0.26666671 1.23499584 0.512174666 0.181276366 0.83953321 0.0576479 1 +0.72698009 0.333333373 1.21972215 0.498430669 0.228539377 0.836263537 0.0609919 1 +0.692820191 0.400000036 1.20000005 0.480384409 0.277350128 0.832050383 0.0658711 1 +0.755847573 0.433333337 1.1697222 0.518222749 0.29710114 0.801982641 0.0666551 1 +0.811169863 0.466666698 1.13499582 0.551422119 0.317233622 0.771554589 0.0678261 1 +0.859807551 0.5 1.09641016 0.58082056 0.337761968 0.740651309 0.0693318 1 +0.902406812 0.533333361 1.05433822 0.606967986 0.358725429 0.709158599 0.071171 1 +0.93936789 0.566666722 1.00901103 0.630231619 0.380182564 0.676955938 0.0733684 1 +0.970909894 0.600000024 0.960555136 0.650850832 0.402210891 0.643909514 0.075977 1 +0.997102916 0.633333385 0.909011006 0.668966651 0.42490992 0.609864891 0.0790898 1 +1.01787686 0.666666687 0.854338169 0.684634328 0.448406786 0.574636519 0.0828432 1 +1.03301251 0.700000048 0.796410143 0.697824717 0.472866833 0.537994146 0.0874472 1 +1.04210997 0.733333349 0.734995902 0.708412051 0.498509943 0.499640107 0.093238 1 +1.04452276 0.76666671 0.66972214 0.716143668 0.525640547 0.45917359 0.100777 1 +1.03923035 0.800000072 0.599999964 0.720576644 0.554700255 0.416025162 0.111114 1 +1.10225773 0.76666671 0.569722116 0.755727768 0.525640547 0.390611768 0.103741 1 +1.15758014 0.733333349 0.534995854 0.786907017 0.498509884 0.363682806 0.0987829 1 +1.20621765 0.700000048 0.496410102 0.814828992 0.472866833 0.335336924 0.0953467 1 +1.24881697 0.666666687 0.454338163 0.839967012 0.448406786 0.305592477 0.0929648 1 +1.28577805 0.633333385 0.409010977 0.862641811 0.42490992 0.274409711 0.0913613 1 +1.31732011 0.600000024 0.360555112 0.883067429 0.402210891 0.241698638 0.0903733 1 +1.34351313 0.566666722 0.309010983 0.901376903 0.380182564 0.207318664 0.0899039 1 +1.36428702 0.533333361 0.254338175 0.917633295 0.358725429 0.171070442 0.0898966 1 +1.3794229 0.5 0.196410134 0.931833088 0.337761939 0.132679731 0.0903383 1 +1.38852024 0.466666698 0.134995788 0.943897009 0.317233622 0.0917682797 0.0912482 1 +1.39093292 0.433333367 0.0697220936 0.953648686 0.29710117 0.0478027239 0.0926967 1 +1.3856405 0.400000006 -6.58440484e-08 0.960768878 0.277350128 -4.56546374e-08 0.0948363 1 +1.4198004 0.333333343 0.0197220985 0.973440766 0.228539348 0.0135218268 0.0876204 1 +1.44625521 0.266666651 0.0349957868 0.983144403 0.181276321 0.0237896554 0.082678 1 +1.46602535 0.199999988 0.0464101359 0.990335286 0.13510479 0.0313511603 0.0792511 1 +1.47975707 0.133333325 0.0543381497 0.995299637 0.0896813497 0.036548391 0.0769337 1 +1.48785067 0.0666666552 0.0590109751 0.998214424 0.0447273478 0.039591074 0.0754902 1 +1.49052513 -1.49011612e-08 0.060555093 0.999175787 -9.98901584e-09 0.0405931994 0.0747978 1 +1.48785067 -0.066666685 0.0590109751 0.998214424 -0.0447273701 0.039591074 0.0747978 1 +1.47975707 -0.133333355 0.0543381497 0.995299637 -0.089681372 0.036548391 0.0754902 1 +1.46602523 -0.200000018 0.0464100763 0.990335226 -0.135104805 0.0313511193 0.076933 1 +1.44625521 -0.26666671 0.0349957868 0.983144403 -0.181276366 0.0237896554 0.0792518 1 +1.4198004 -0.333333373 0.0197220985 0.973440766 -0.228539377 0.0135218268 0.082678 1 +1.3856405 -0.400000036 -6.58440484e-08 0.960768878 -0.277350128 -4.56546374e-08 0.0876198 1 +1.39093292 -0.433333337 0.0697220936 0.953648686 -0.29710114 0.0478027239 0.0859584 1 +1.38852024 -0.466666698 0.134995788 0.943897009 -0.317233622 0.0917682797 0.0847383 1 +1.3794229 -0.5 0.196410134 0.931833088 -0.337761939 0.132679731 0.0838847 1 +1.36428702 -0.533333361 0.254338175 0.917633295 -0.358725429 0.171070442 0.0833609 1 +1.34351313 -0.566666722 0.309010983 0.901376903 -0.380182564 0.207318664 0.0831523 1 +1.31732011 -0.600000024 0.360555112 0.883067429 -0.402210891 0.241698638 0.0832685 1 +1.28577805 -0.633333385 0.409010977 0.862641811 -0.42490992 0.274409711 0.0837458 1 +1.24881697 -0.666666687 0.454338163 0.839967012 -0.448406786 0.305592477 0.0846262 1 +1.20621765 -0.700000048 0.496410102 0.814828992 -0.472866833 0.335336924 0.085998 1 +1.15758014 -0.733333349 0.534995854 0.786907017 -0.498509884 0.363682806 0.087999 1 +1.10225773 -0.76666671 0.569722116 0.755727768 -0.525640547 0.390611768 0.0908404 1 +1.03923035 -0.800000072 0.599999964 0.720576644 -0.554700255 0.416025162 0.0948841 1 +1.04452276 -0.76666671 0.66972214 0.716143668 -0.525640547 0.45917359 0.0882712 1 +1.04210997 -0.733333349 0.734995902 0.708412051 -0.498509943 0.499640107 0.0831049 1 +1.03301251 -0.700000048 0.796410143 0.697824717 -0.472866833 0.537994146 0.0789327 1 +1.01787686 -0.666666687 0.854338169 0.684634328 -0.448406786 0.574636519 0.0754823 1 +0.997102916 -0.633333385 0.909011006 0.668966651 -0.42490992 0.609864891 0.0725746 1 +0.970909894 -0.600000024 0.960555136 0.650850832 -0.402210891 0.643909514 0.0700899 1 +0.93936789 -0.566666722 1.00901103 0.630231619 -0.380182564 0.676955938 0.0679518 1 +0.902406812 -0.533333361 1.05433822 0.606967986 -0.358725429 0.709158599 0.0660971 1 +0.859807551 -0.5 1.09641016 0.58082056 -0.337761968 0.740651309 0.0644856 1 +0.811169863 -0.466666698 1.13499582 0.551422119 -0.317233622 0.771554589 0.0631018 1 +0.755847573 -0.433333367 1.1697222 0.518222749 -0.29710117 0.801982641 0.0619322 1 +0.576386809 -0.433333337 1.19944382 0.411845148 -0.309629291 0.857037485 0.0656216 1 +0.620432734 -0.366666675 1.22487366 0.436567724 -0.258005142 0.861882806 0.0603361 1 +0.654239476 -0.300000012 1.24439204 0.455108255 -0.208688855 0.865635812 0.0568374 1 +0.679750144 -0.233333334 1.25912058 0.468861312 -0.160942927 0.868485212 0.0544944 1 +0.698112547 -0.166666657 1.2697221 0.47863856 -0.114269666 0.870544374 0.0529842 1 +0.710022867 -0.099999994 1.27659857 0.484927177 -0.0682974011 0.87188369 0.0521366 1 +0.715885103 -0.0333333239 1.27998316 0.488007247 -0.0227227863 0.872543752 0.0518614 1 +0.715885103 0.0333333462 1.27998316 0.488007247 0.0227228012 0.872543752 0.0521366 1 +0.710022867 0.100000016 1.27659857 0.484927177 0.068297416 0.87188369 0.0529842 1 +0.698112547 0.166666687 1.2697221 0.47863856 0.114269689 0.870544374 0.0544944 1 +0.679750144 0.233333349 1.25912058 0.468861312 0.160942927 0.868485212 0.0568374 1 +0.654239476 0.300000012 1.24439204 0.455108255 0.208688855 0.865635812 0.0603361 1 +0.620432734 0.366666704 1.22487366 0.436567724 0.258005142 0.861882806 0.0656207 1 +0.576386809 0.433333367 1.19944382 0.411845148 0.309629291 0.857037485 0.0740749 1 +0.649300218 0.466666698 1.17487371 0.456880331 0.328370184 0.82670033 0.0733351 1 +0.711974502 0.5 1.14439213 0.495270461 0.347814739 0.7960729 0.0736865 1 +0.766352654 0.533333361 1.10912061 0.528595865 0.367869556 0.76502192 0.0747428 1 +0.81358248 0.566666722 1.06972206 0.557806849 0.388516963 0.733420789 0.0763616 1 +0.854360461 0.600000024 1.02659857 0.583505988 0.409784406 0.701140106 0.078509 1 +0.88909018 0.633333385 0.979983151 0.606078327 0.431733102 0.668038607 0.0812173 1 +0.917957842 0.666666687 0.929983199 0.625756979 0.454455853 0.633954465 0.084574 1 +0.94096297 0.700000048 0.876598597 0.642653227 0.478081822 0.598694026 0.0887412 1 +0.957920194 0.733333349 0.819722116 0.656767368 0.502786577 0.562016249 0.0939859 1 +0.968425274 0.76666671 0.759120643 0.667976499 0.528812468 0.523607492 0.100753 1 +0.971782148 0.800000072 0.694392025 0.676000297 0.556503654 0.483039558 0.109837 1 +0.96684289 0.833333373 0.624873698 0.680319369 0.586375296 0.439692587 0.1228 1 +0.951664507 0.866666734 0.549443781 0.679991961 0.619258583 0.392593563 0.143336 1 +1.02457798 0.833333373 0.524873674 0.720944583 0.586375296 0.369327515 0.126463 1 +1.08725226 0.800000072 0.494392008 0.756324708 0.556503654 0.343913645 0.116452 1 +1.14163041 0.76666671 0.459120601 0.787445664 0.528812468 0.316680878 0.109959 1 +1.1888603 0.733333349 0.41972211 0.815104067 0.502786577 0.287769049 0.105592 1 +1.2296381 0.700000048 0.376598567 0.839810908 0.478081852 0.257207036 0.10265 1 +1.26436806 0.666666687 0.329983175 0.861899137 0.454455823 0.224944159 0.100755 1 +1.29323542 0.633333385 0.279983103 0.881577611 0.431733102 0.190859944 0.0996925 1 +1.31624067 0.600000024 0.226598591 0.898958147 0.409784406 0.154760942 0.0993554 1 +1.33319783 0.566666722 0.169722036 0.914064467 0.388516933 0.116364487 0.0997069 1 +1.34370303 0.533333361 0.10912057 0.926826298 0.367869526 0.0752664953 0.100786 1 +1.34705985 0.50000006 0.0443919711 0.937054634 0.347814798 0.0308803655 0.102738 1 +1.34212065 0.466666669 -0.0251263436 0.944383562 0.328370154 -0.0176801588 0.105888 1 +1.32694221 0.433333337 -0.100556247 0.948138833 0.309629291 -0.0718503669 0.110988 1 +1.37098813 0.366666675 -0.0751263425 0.964696229 0.258005112 -0.052862674 0.0979935 1 +1.40479493 0.300000012 -0.0556080267 0.97721678 0.208688855 -0.0386825837 0.08988 1 +1.43030548 0.233333334 -0.0408794209 0.986560881 0.160942927 -0.0281968005 0.0845126 1 +1.448668 0.166666657 -0.0302779004 0.993232906 0.114269666 -0.020759074 0.0809192 1 +1.4605782 0.099999994 -0.0234014317 0.997537017 0.0682974011 -0.0159825701 0.0786055 1 +1.46644044 0.0333333239 -0.0200169012 0.999648631 0.0227227863 -0.0136451973 0.0773043 1 +1.46644044 -0.0333333462 -0.0200169012 0.999648631 -0.0227228012 -0.0136451973 0.0768841 1 +1.4605782 -0.100000016 -0.0234014317 0.997537017 -0.068297416 -0.0159825701 0.0773043 1 +1.448668 -0.166666687 -0.0302779004 0.993232906 -0.114269689 -0.020759074 0.0786055 1 +1.43030548 -0.233333349 -0.0408794209 0.986560881 -0.160942927 -0.0281968005 0.0809192 1 +1.40479493 -0.300000012 -0.0556080267 0.97721678 -0.208688855 -0.0386825837 0.0845126 1 +1.37098813 -0.366666704 -0.0751263425 0.964696229 -0.258005142 -0.052862674 0.08988 1 +1.32694221 -0.433333367 -0.100556247 0.948138833 -0.309629291 -0.0718503669 0.0979929 1 +1.34212065 -0.466666698 -0.0251263436 0.944383562 -0.328370154 -0.0176801588 0.0948363 1 +1.34705985 -0.5 0.0443920307 0.937054515 -0.347814739 0.0308804046 0.092696 1 +1.34370303 -0.533333361 0.10912057 0.926826298 -0.367869526 0.0752664953 0.0912482 1 +1.33319783 -0.566666722 0.169722036 0.914064467 -0.388516933 0.116364487 0.0903383 1 +1.31624067 -0.600000024 0.226598591 0.898958147 -0.409784406 0.154760942 0.0898966 1 +1.29323542 -0.633333385 0.279983103 0.881577611 -0.431733102 0.190859944 0.0899039 1 +1.26436806 -0.666666687 0.329983175 0.861899137 -0.454455823 0.224944159 0.0903733 1 +1.2296381 -0.700000048 0.376598567 0.839810908 -0.478081852 0.257207036 0.0913613 1 +1.1888603 -0.733333349 0.41972211 0.815104067 -0.502786577 0.287769049 0.0929648 1 +1.14163041 -0.76666671 0.459120601 0.787445664 -0.528812468 0.316680878 0.0953467 1 +1.08725226 -0.800000072 0.494392008 0.756324708 -0.556503654 0.343913645 0.0987829 1 +1.02457798 -0.833333373 0.524873674 0.720944583 -0.586375296 0.369327515 0.103741 1 +0.951664507 -0.866666734 0.549443781 0.679991961 -0.619258583 0.392593563 0.111114 1 +0.96684289 -0.833333373 0.624873698 0.680319369 -0.586375296 0.439692587 0.100777 1 +0.971782148 -0.800000072 0.694392025 0.676000297 -0.556503654 0.483039558 0.093238 1 +0.968425274 -0.76666671 0.759120643 0.667976499 -0.528812468 0.523607492 0.0874472 1 +0.957920194 -0.733333349 0.819722116 0.656767368 -0.502786577 0.562016249 0.0828432 1 +0.94096297 -0.700000048 0.876598597 0.642653227 -0.478081822 0.598694026 0.0790898 1 +0.917957842 -0.666666687 0.929983199 0.625756979 -0.454455853 0.633954465 0.075977 1 +0.88909018 -0.633333385 0.979983151 0.606078327 -0.431733102 0.668038607 0.0733684 1 +0.854360461 -0.600000024 1.02659857 0.583505988 -0.409784406 0.701140106 0.071171 1 +0.81358248 -0.566666722 1.06972206 0.557806849 -0.388516963 0.733420789 0.0693318 1 +0.766352654 -0.533333361 1.10912061 0.528595865 -0.367869556 0.76502192 0.0678261 1 +0.711974502 -0.50000006 1.14439201 0.495270461 -0.347814769 0.796072841 0.0666551 1 +0.649300218 -0.466666669 1.17487371 0.456880331 -0.328370154 0.82670033 0.0658711 1 +0.426382512 -0.466666698 1.17950547 0.31862089 -0.348723888 0.881403625 0.0770384 1 +0.489635676 -0.400000006 1.21602464 0.357249945 -0.291849613 0.887240827 0.0663025 1 +0.535787344 -0.333333343 1.2426703 0.384434313 -0.239170954 0.891631901 0.0601133 1 +0.570076108 -0.266666681 1.26246691 0.40412429 -0.189038768 0.894956887 0.0562319 1 +0.595053196 -0.200000003 1.27688754 0.418209255 -0.140561983 0.897409141 0.0537629 1 +0.612125516 -0.133333325 1.28674424 0.427716255 -0.0931652561 0.899098992 0.0522828 1 +0.622092724 -0.0666666627 1.29249883 0.433222592 -0.0464263633 0.900090396 0.0515882 1 +0.625371933 7.4505806e-09 1.29439199 0.435027182 5.18284393e-09 0.900417328 0.0515882 1 +0.622092724 0.0666666776 1.29249883 0.433222592 0.0464263745 0.900090396 0.0522828 1 +0.612125516 0.133333355 1.28674424 0.427716255 0.0931652784 0.899098992 0.0537629 1 +0.595053196 0.200000018 1.27688754 0.418209255 0.140561983 0.897409141 0.0562319 1 +0.570076108 0.266666681 1.26246691 0.40412429 0.189038768 0.894956887 0.0601133 1 +0.535787344 0.333333373 1.2426703 0.384434313 0.239170983 0.891631901 0.0663007 1 +0.489635676 0.400000036 1.21602464 0.357249945 0.291849643 0.887240827 0.0770384 1 +0.426382512 0.466666698 1.17950547 0.31862089 0.348723888 0.881403625 0.100344 1 +0.518503189 0.50000006 1.16602468 0.378312349 0.364812016 0.850759566 0.0895587 1 +0.59352231 0.533333361 1.14267039 0.425859869 0.382673532 0.819880664 0.085982 1 +0.656678617 0.566666663 1.11246693 0.465516388 0.401707351 0.788622618 0.0851058 1 +0.710523248 0.600000024 1.07688749 0.499362797 0.421685964 0.756847203 0.0857206 1 +0.756462991 0.633333385 1.03674412 0.528570592 0.442535073 0.72441411 0.087396 1 +0.795297861 0.666666687 0.992498815 0.553841889 0.464263678 0.691171706 0.0900107 1 +0.827444553 0.700000048 0.944392025 0.575594842 0.486940712 0.656947017 0.0936092 1 +0.853032887 0.733333349 0.892498791 0.594048262 0.510690033 0.621532142 0.0983699 1 +0.871933043 0.76666671 0.83674413 0.609254062 0.535700321 0.584666193 0.104667 1 +0.883728266 0.800000072 0.776887417 0.621092975 0.562247992 0.546004176 0.113183 1 +0.887618601 0.833333373 0.712466896 0.629228711 0.590746164 0.505064487 0.12526 1 +0.882197499 0.866666734 0.642670333 0.632988036 0.62184453 0.461124212 0.143925 1 +0.864913344 0.900000036 0.566024661 0.631061554 0.65666163 0.412985206 0.178323 1 +0.830527723 0.933333397 0.47950542 0.620624602 0.697447777 0.358317822 0.118789 1 +0.92264843 0.900000036 0.466024637 0.673186421 0.65666163 0.340022743 0.183761 1 +0.997667551 0.866666734 0.442670316 0.715839267 0.62184453 0.317621648 0.152746 1 +1.06082368 0.833333373 0.412466854 0.752013028 0.590746164 0.29239586 0.136883 1 +1.11466837 0.800000072 0.376887411 0.783400059 0.562248051 0.26488024 0.127357 1 +1.16060817 0.76666671 0.3367441 0.810962796 0.535700321 0.235296413 0.121288 1 +1.1994431 0.733333349 0.292498767 0.835286796 0.510690033 0.203694835 0.117425 1 +1.23158979 0.700000048 0.244391993 0.856730163 0.486940682 0.170006275 0.115158 1 +1.25717807 0.666666687 0.192498773 0.875493169 0.464263678 0.134055287 0.11419 1 +1.27607822 0.633333385 0.136744097 0.891646326 0.442535073 0.0955485031 0.114422 1 +1.28787363 0.600000024 0.0768874511 0.905130267 0.421685934 0.054037258 0.115938 1 +1.29176378 0.566666722 0.0124668283 0.91572541 0.401707411 0.00883767754 0.119067 1 +1.28634274 0.533333361 -0.0573297217 0.922967434 0.382673532 -0.0411348119 0.124635 1 +1.26905859 0.5 -0.133975372 0.925935626 0.364812016 -0.0977516547 0.13482 1 +1.2346729 0.466666698 -0.220494613 0.922628343 0.348723888 -0.164767995 0.157429 1 +1.29792607 0.400000006 -0.183975384 0.94699806 0.291849613 -0.134232864 0.120117 1 +1.34407771 0.333333343 -0.157329723 0.96439302 0.239170954 -0.112886101 0.102962 1 +1.37836647 0.266666681 -0.137533113 0.977117538 0.189038768 -0.0974965841 0.0930835 1 +1.40334368 0.200000003 -0.123112537 0.986283779 0.140561983 -0.0865247026 0.0868948 1 +1.420416 0.133333325 -0.113255836 0.992500663 0.0931652486 -0.0791363195 0.0829626 1 +1.43038321 0.0666666627 -0.107501246 0.996112466 0.0464263633 -0.0748633891 0.080606 1 +1.43366241 -7.4505806e-09 -0.105608024 0.997297823 -5.18284349e-09 -0.0734640509 0.0794984 1 +1.43038321 -0.0666666776 -0.107501246 0.996112466 -0.0464263745 -0.0748633891 0.0794984 1 +1.420416 -0.133333355 -0.113255836 0.992500663 -0.093165271 -0.0791363195 0.080606 1 +1.40334368 -0.200000018 -0.123112537 0.986283779 -0.140561983 -0.0865247026 0.0829619 1 +1.37836647 -0.266666681 -0.137533113 0.977117538 -0.189038768 -0.0974965841 0.0868948 1 +1.34407771 -0.333333373 -0.157329723 0.96439302 -0.239170983 -0.112886101 0.0930835 1 +1.29792607 -0.400000036 -0.183975384 0.94699806 -0.291849643 -0.134232864 0.102961 1 +1.2346729 -0.466666698 -0.220494613 0.922628343 -0.348723888 -0.164767995 0.120117 1 +1.26905859 -0.50000006 -0.133975372 0.925935626 -0.364812046 -0.0977516547 0.110987 1 +1.28634274 -0.533333361 -0.0573297217 0.922967434 -0.382673532 -0.0411348119 0.105888 1 +1.29176402 -0.566666663 0.0124668879 0.91572541 -0.401707321 0.00883771759 0.102739 1 +1.28787363 -0.600000024 0.0768874511 0.905130267 -0.421685934 0.054037258 0.100786 1 +1.27607822 -0.633333385 0.136744097 0.891646326 -0.442535073 0.0955485031 0.0997069 1 +1.25717807 -0.666666687 0.192498773 0.875493169 -0.464263678 0.134055287 0.0993554 1 +1.23158979 -0.700000048 0.244391993 0.856730163 -0.486940682 0.170006275 0.0996925 1 +1.1994431 -0.733333349 0.292498767 0.835286796 -0.510690033 0.203694835 0.100755 1 +1.16060817 -0.76666671 0.3367441 0.810962796 -0.535700321 0.235296413 0.10265 1 +1.11466837 -0.800000072 0.376887411 0.783400059 -0.562248051 0.26488024 0.105592 1 +1.06082368 -0.833333373 0.412466854 0.752013028 -0.590746164 0.29239586 0.109959 1 +0.997667551 -0.866666734 0.442670316 0.715839267 -0.62184453 0.317621648 0.116452 1 +0.92264843 -0.900000036 0.466024637 0.673186421 -0.65666163 0.340022743 0.126463 1 +0.830527723 -0.933333397 0.47950542 0.620624602 -0.697447777 0.358317822 0.143336 1 +0.864913344 -0.900000036 0.566024661 0.631061554 -0.65666163 0.412985206 0.1228 1 +0.882197499 -0.866666734 0.642670333 0.632988036 -0.62184453 0.461124212 0.109837 1 +0.887618601 -0.833333373 0.712466896 0.629228711 -0.590746164 0.505064487 0.100753 1 +0.883728266 -0.800000072 0.776887417 0.621092975 -0.562247992 0.546004176 0.0939859 1 +0.871933043 -0.76666671 0.83674413 0.609254062 -0.535700321 0.584666193 0.0887412 1 +0.853032887 -0.733333349 0.892498791 0.594048262 -0.510690033 0.621532142 0.084574 1 +0.827444553 -0.700000048 0.944392025 0.575594842 -0.486940712 0.656947017 0.0812173 1 +0.795297861 -0.666666687 0.992498815 0.553841889 -0.464263678 0.691171706 0.078509 1 +0.756462991 -0.633333385 1.03674412 0.528570592 -0.442535073 0.72441411 0.0763616 1 +0.710523248 -0.600000024 1.07688749 0.499362797 -0.421685964 0.756847203 0.0747428 1 +0.656678498 -0.566666722 1.11246693 0.465516359 -0.401707411 0.788622677 0.0736865 1 +0.59352231 -0.533333361 1.14267039 0.425859869 -0.382673532 0.819880664 0.0733359 1 +0.518503189 -0.5 1.16602468 0.378312379 -0.364812016 0.850759625 0.0740765 1 +0.302626967 -0.433333397 1.17472184 0.234931335 -0.336399615 0.911944449 0.0870656 1 +0.380994439 -0.366666704 1.21996725 0.286541253 -0.275765538 0.91752243 0.0692784 1 +0.433012545 -0.300000042 1.25 0.319221079 -0.221162975 0.921512246 0.0607881 1 +0.469573259 -0.233333379 1.27110827 0.341504693 -0.169695452 0.924433947 0.05598 1 +0.494850755 -0.166666701 1.28570223 0.356600195 -0.120103642 0.92650491 0.0531639 1 +0.510866523 -0.100000031 1.29494894 0.366038412 -0.07165052 0.927837372 0.0516656 1 +0.518651783 -0.0333333611 1.29944384 0.370591789 -0.0238176566 0.92849046 0.0511937 1 +0.518651783 0.033333309 1.29944384 0.370591789 0.0238176193 0.92849046 0.0516656 1 +0.510866523 0.0999999791 1.29494894 0.366038412 0.0716504827 0.927837372 0.0531662 1 +0.494850755 0.166666657 1.28570223 0.356600195 0.120103613 0.92650491 0.055981 1 +0.469573259 0.233333319 1.27110827 0.341504693 0.169695407 0.924433947 0.0607881 1 +0.433012545 0.299999982 1.25 0.319221079 0.22116293 0.921512246 0.0692784 1 +0.380994439 0.366666675 1.21996725 0.286541253 0.275765508 0.91752243 0.0870642 1 +0.302627087 0.433333337 1.17472184 0.234931409 0.336399555 0.91194433 0.0870642 1 +0.3314946 0.533333361 1.12472188 0.257341444 0.414030224 0.87312907 0.100344 1 +0.438729405 0.566666722 1.11996722 0.329963058 0.426183134 0.842313707 0.131075 1 +0.519615054 0.600000024 1.10000002 0.383065313 0.44232589 0.810930789 0.11316 1 +0.585043311 0.633333325 1.07110834 0.425482094 0.460601777 0.778980613 0.107453 1 +0.63918829 0.666666687 1.03570223 0.460612953 0.48041448 0.746349514 0.10624 1 +0.6840716 0.700000048 0.994948983 0.490140676 0.501553476 0.712885857 0.10764 1 +0.720724523 0.733333349 0.949443877 0.514978588 0.523987949 0.678405225 0.111132 1 +0.749591887 0.76666671 0.899443805 0.535605192 0.547805667 0.642678797 0.116796 1 +0.770674169 0.800000072 0.844948947 0.552191854 0.573203981 0.605410099 0.125227 1 +0.783525884 0.833333373 0.785702229 0.56462574 0.600518107 0.566194057 0.137869 1 +0.787115932 0.866666734 0.721108258 0.57244271 0.630297303 0.524437547 0.158262 1 +0.7794227 0.900000036 0.649999976 0.574598074 0.663488865 0.479186356 0.125665 1 +0.756272137 0.933333397 0.56996727 0.568782985 0.701948583 0.42866537 0.0785295 1 +0.706772327 0.966666698 0.474721909 0.54867202 0.750429749 0.368529767 0.100757 1 +0.764507413 0.966666698 0.374721885 0.59349215 0.75042969 0.290899068 0.118599 1 +0.871742189 0.933333397 0.369967252 0.655626655 0.701948643 0.278247833 0.107222 1 +0.952627838 0.900000036 0.349999934 0.702286601 0.663488865 0.258023381 0.0860174 1 +1.01805604 0.866666734 0.321108252 0.740397513 0.630297184 0.233531103 0.178503 1 +1.07220101 0.833333373 0.285702199 0.772651315 0.600518107 0.205883205 0.160172 1 +1.11708438 0.800000012 0.244948924 0.800396562 0.573203981 0.175507128 0.149904 1 +1.15373707 0.76666671 0.199443772 0.824378729 0.547805667 0.142508373 0.144125 1 +1.18260479 0.733333349 0.149443835 0.845005453 0.523987949 0.10678196 0.14146 1 +1.20368695 0.700000048 0.0949489027 0.862447619 0.501553476 0.0680313557 0.141455 1 +1.21653867 0.666666687 0.035702195 0.876664162 0.48041448 0.0257277768 0.144298 1 +1.22012866 0.633333385 -0.0288917627 0.887358189 0.460601896 -0.0210119989 0.151062 1 +1.21243548 0.600000024 -0.100000091 0.893819332 0.44232589 -0.0737210438 0.164999 1 +1.1892848 0.566666663 -0.180032775 0.894446611 0.426183105 -0.135400459 0.10106 1 +1.13978505 0.533333361 -0.275278181 0.884822667 0.414030224 -0.21370028 0.131894 1 +1.16865242 0.433333397 -0.325278252 0.907232642 0.336399585 -0.252515674 0.154905 1 +1.24701989 0.366666704 -0.280032784 0.937868416 0.275765538 -0.210609227 0.141906 1 +1.29903793 0.300000042 -0.250000089 0.957663596 0.22116299 -0.184302524 0.112296 1 +1.33559871 0.233333379 -0.22889176 0.971335649 0.169695452 -0.166465208 0.098187 1 +1.3608762 0.166666701 -0.214297801 0.980676949 0.120103642 -0.154427648 0.0902054 1 +1.37689197 0.100000031 -0.205051094 0.986549973 0.07165052 -0.14692013 0.0855387 1 +1.38467717 0.0333333611 -0.200556248 0.989392221 0.0238176584 -0.14330329 0.0830518 1 +1.38467717 -0.033333309 -0.200556248 0.989392221 -0.0238176212 -0.14330329 0.0822671 1 +1.37689197 -0.0999999791 -0.205051094 0.986549973 -0.0716504827 -0.14692013 0.0830518 1 +1.3608762 -0.166666657 -0.214297801 0.980676949 -0.120103613 -0.154427648 0.0855387 1 +1.33559871 -0.233333319 -0.22889176 0.971335649 -0.169695407 -0.166465208 0.0902061 1 +1.29903793 -0.299999982 -0.250000089 0.957663596 -0.221162945 -0.184302524 0.0981876 1 +1.24701989 -0.366666675 -0.280032784 0.937868416 -0.275765508 -0.210609227 0.112297 1 +1.16865253 -0.433333337 -0.325278193 0.907232702 -0.336399555 -0.252515614 0.141907 1 +1.13978505 -0.533333361 -0.275278181 0.884822667 -0.414030224 -0.21370028 0.157429 1 +1.1892848 -0.566666722 -0.180032805 0.894446552 -0.426183105 -0.135400474 0.13482 1 +1.21243548 -0.600000024 -0.100000091 0.893819332 -0.44232589 -0.0737210438 0.124635 1 +1.22012866 -0.633333325 -0.0288917627 0.887358189 -0.460601836 -0.0210119989 0.119068 1 +1.21653867 -0.666666687 0.035702195 0.876664162 -0.48041448 0.0257277768 0.115938 1 +1.20368695 -0.700000048 0.0949489027 0.862447619 -0.501553476 0.0680313557 0.114422 1 +1.18260479 -0.733333349 0.149443835 0.845005453 -0.523987949 0.10678196 0.11419 1 +1.15373707 -0.76666671 0.199443772 0.824378729 -0.547805667 0.142508373 0.115158 1 +1.11708438 -0.800000072 0.244948924 0.800396442 -0.573203981 0.175507113 0.117426 1 +1.07220101 -0.833333373 0.285702199 0.772651315 -0.600518107 0.205883205 0.121288 1 +1.01805604 -0.866666734 0.321108252 0.740397513 -0.630297184 0.233531103 0.127357 1 +0.952627838 -0.900000036 0.349999934 0.702286601 -0.663488865 0.258023381 0.136883 1 +0.871742189 -0.933333397 0.369967252 0.655626655 -0.701948643 0.278247833 0.152746 1 +0.764507413 -0.966666698 0.374721885 0.59349215 -0.75042969 0.290899068 0.183761 1 +0.706772327 -0.966666698 0.474721909 0.54867202 -0.750429749 0.368529767 0.178323 1 +0.756272137 -0.933333397 0.56996727 0.568782985 -0.701948583 0.42866537 0.143925 1 +0.7794227 -0.900000036 0.649999976 0.574598074 -0.663488865 0.479186356 0.12526 1 +0.787115932 -0.866666734 0.721108258 0.57244271 -0.630297303 0.524437547 0.113183 1 +0.783525884 -0.833333373 0.785702229 0.56462574 -0.600518107 0.566194057 0.104667 1 +0.770674169 -0.800000012 0.844948947 0.552191913 -0.573203981 0.605410159 0.0983705 1 +0.749591887 -0.76666671 0.899443805 0.535605192 -0.547805667 0.642678797 0.0936092 1 +0.720724523 -0.733333349 0.949443877 0.514978588 -0.523987949 0.678405225 0.0900107 1 +0.6840716 -0.700000048 0.994948983 0.490140676 -0.501553476 0.712885857 0.087396 1 +0.63918829 -0.666666687 1.03570223 0.460612953 -0.48041448 0.746349514 0.0857206 1 +0.585043311 -0.633333385 1.07110834 0.425482094 -0.460601807 0.778980613 0.0851079 1 +0.519615054 -0.600000024 1.10000002 0.383065313 -0.44232589 0.810930789 0.085982 1 +0.438729465 -0.566666663 1.11996722 0.329963088 -0.426183105 0.842313707 0.0895593 1 +0.3314946 -0.533333361 1.12472188 0.257341444 -0.414030224 0.87312907 0.100344 1 +0.221034139 -0.333333343 1.19428086 0.175497234 -0.264660835 0.948238015 0.0838683 1 +0.295782417 -0.266666681 1.23743677 0.227535158 -0.205137432 0.951917231 0.0646989 1 +0.340577573 -0.200000018 1.26329923 0.257311463 -0.15110302 0.954441547 0.0567439 1 +0.368647486 -0.13333334 1.27950549 0.275477499 -0.0996353924 0.956130207 0.0528297 1 +0.384333581 -0.0666666701 1.28856182 0.285472006 -0.0495180972 0.957106948 0.0511541 1 +0.389397413 0 1.29148543 0.28867507 0 0.957427144 0.0511541 1 +0.384333581 0.0666666701 1.28856182 0.285472006 0.0495180972 0.957106948 0.0528297 1 +0.368647486 0.13333334 1.27950549 0.275477499 0.0996353924 0.956130207 0.0567439 1 +0.340577573 0.200000018 1.26329923 0.257311463 0.15110302 0.954441547 0.0646989 1 +0.295782417 0.266666681 1.23743677 0.227535158 0.205137432 0.951917231 0.0646989 1 +0.307636559 0.633333385 1.04428077 0.244258046 0.502855599 0.829140663 0.131075 1 +0.411252409 0.666666746 1.03743672 0.316362232 0.512843609 0.798064113 0.0869064 1 +0.484915137 0.700000048 1.01329923 0.366360664 0.528860569 0.765562832 0.0674 1 +0.541852713 0.733333349 0.979505539 0.404907763 0.547994614 0.731950521 0.0594388 1 +0.586406112 0.76666671 0.938561797 0.43556571 0.569458127 0.697136879 0.0556884 1 +0.620337486 0.800000072 0.891485393 0.459879726 0.593070388 0.66089195 0.0543191 1 +0.644141138 0.833333373 0.838561773 0.478449672 0.618976295 0.622859776 0.0548032 1 +0.657322645 0.866666734 0.779505432 0.491194457 0.647630036 0.582497418 0.0572066 1 +0.658120215 0.900000036 0.713299274 0.497219712 0.679963589 0.538908303 0.0622539 1 +0.642192602 0.933333397 0.637436748 0.494016469 0.717980981 0.490357965 0.0721393 1 +0.873132706 0.933333397 0.237436756 0.671670675 0.717980981 0.182651863 0.137311 1 +0.946795404 0.900000036 0.21329923 0.715318143 0.679963589 0.161150783 0.0838292 1 +1.00373292 0.866666675 0.179505467 0.750054836 0.647629976 0.134138212 0.0740717 1 +1.04828632 0.833333373 0.138561741 0.77863723 0.618976295 0.102919713 0.0695783 1 +1.08221769 0.800000072 0.0914853439 0.802289069 0.593070388 0.0678215548 0.0681096 1 +1.1060214 0.76666671 0.038561713 0.821521163 0.569458187 0.0286425408 0.0690429 1 +1.11920297 0.733333349 -0.0204945542 0.836341679 0.547994614 -0.0153148714 0.0725376 1 +1.12000048 0.700000048 -0.0867007896 0.84617722 0.528860569 -0.0655037537 0.0796273 1 +1.10407281 0.666666687 -0.162563264 0.849324942 0.512843609 -0.125054285 0.0933819 1 +1.21954286 0.266666681 -0.362563252 0.938152075 0.205137432 -0.278907359 0.175696 1 +1.26433802 0.200000018 -0.336700767 0.955226362 0.15110302 -0.254382491 0.109631 1 +1.29240799 0.13333334 -0.320494592 0.965771794 0.0996353924 -0.239494517 0.0958525 1 +1.30809402 0.0666666701 -0.311438233 0.971614897 0.0495180972 -0.231327429 0.0890802 1 +1.31315792 0 -0.308514625 0.973493814 0 -0.228713602 0.0861824 1 +1.30809402 -0.0666666701 -0.311438233 0.971614897 -0.0495180972 -0.231327429 0.0861824 1 +1.29240799 -0.13333334 -0.320494592 0.965771794 -0.0996353924 -0.239494517 0.0890802 1 +1.26433802 -0.200000018 -0.336700767 0.955226362 -0.15110302 -0.254382491 0.0958525 1 +1.21954286 -0.266666681 -0.362563252 0.938152075 -0.205137432 -0.278907359 0.109631 1 +1.14479458 -0.333333343 -0.405719191 0.908946812 -0.264660835 -0.322133929 0.142895 1 +1.05819201 -0.633333385 -0.255719304 0.84018594 -0.502855599 -0.203036651 0.198882 1 +1.10407281 -0.666666746 -0.162563324 0.849324942 -0.512843609 -0.12505433 0.164999 1 +1.12000048 -0.700000048 -0.0867007896 0.84617722 -0.528860569 -0.0655037537 0.151062 1 +1.11920297 -0.733333349 -0.0204945542 0.836341679 -0.547994614 -0.0153148714 0.144298 1 +1.1060214 -0.76666671 0.038561713 0.821521163 -0.569458187 0.0286425408 0.141455 1 +1.08221769 -0.800000072 0.0914853439 0.802289069 -0.593070388 0.0678215548 0.14146 1 +1.04828632 -0.833333373 0.138561741 0.77863723 -0.618976295 0.102919713 0.144125 1 +1.0037328 -0.866666734 0.179505408 0.750054777 -0.647630036 0.134138182 0.149905 1 +0.946795404 -0.900000036 0.21329923 0.715318143 -0.679963589 0.161150783 0.160172 1 +0.873132706 -0.933333397 0.237436756 0.671670675 -0.717980981 0.182651863 0.178503 1 +0.769516945 -0.966666698 0.244280815 0.610982955 -0.767516375 0.193954676 0.217237 1 +0.596311808 -0.966666698 0.544280827 0.473461121 -0.767516375 0.43214941 0.198343 1 +0.642192602 -0.933333397 0.637436748 0.494016469 -0.717980981 0.490357965 0.158262 1 +0.658120215 -0.900000036 0.713299274 0.497219712 -0.679963589 0.538908303 0.137869 1 +0.657322764 -0.866666675 0.779505491 0.491194516 -0.647629976 0.582497418 0.125227 1 +0.644141138 -0.833333373 0.838561773 0.478449672 -0.618976295 0.622859776 0.116796 1 +0.620337486 -0.800000072 0.891485393 0.459879726 -0.593070388 0.66089195 0.111132 1 +0.586406112 -0.76666671 0.938561797 0.43556571 -0.569458127 0.697136879 0.10764 1 +0.541852713 -0.733333349 0.979505539 0.404907763 -0.547994614 0.731950521 0.10624 1 +0.484915137 -0.700000048 1.01329923 0.366360664 -0.528860569 0.765562832 0.107453 1 +0.411252469 -0.666666687 1.03743684 0.316362262 -0.512843549 0.798064172 0.11316 1 +0.307636678 -0.633333325 1.04428089 0.244258121 -0.502855539 0.829140663 0.131075 1 +0.942721903 -0.833333373 -0.0557193048 0.748504698 -0.661652148 -0.0442401543 0.219203 1 +0.913854361 -0.866666734 -0.00571927987 0.725584388 -0.688118219 -0.00454100827 0.220669 1 +0.451974154 -0.866666734 0.794280767 0.358859569 -0.688118219 0.630645037 0.172366 1 +0.423106611 -0.833333373 0.844280779 0.335939258 -0.661652148 0.670344174 0.165746 1 +-1.38564062 0 0.800000012 -0.866025388 0 0.5 0.059501 1 +-1.35484648 -0.0333333351 0.848887682 -0.847220838 -0.0208442044 0.530831635 0.0586682 1 +-1.35484648 0.0333333351 0.848887682 -0.847220838 0.0208442044 0.530831635 0.0588084 1 +-1.38371396 0.0666666701 0.79888767 -0.865272462 0.0416884087 0.499565303 0.059784 1 +-1.41258144 0.0333333351 0.748887658 -0.883324146 0.0208442062 0.468299031 0.0604536 1 +-1.41258144 -0.0333333351 0.748887658 -0.883324146 -0.0208442062 0.468299031 0.0603094 1 +-1.38371396 -0.0666666701 0.79888767 -0.865272462 -0.0416884087 0.499565303 0.059501 1 +-1.32017303 -0.0666666701 0.895535648 -0.826840222 -0.0417541377 0.560884714 0.0579523 1 +-1.3221128 0 0.89665556 -0.827618957 0 0.561290324 0.0579523 1 +-1.32017303 0.0666666701 0.895535648 -0.826840222 0.0417541377 0.560884714 0.0582305 1 +-1.3509804 0.100000009 0.846655548 -0.845689476 0.0625982136 0.52999115 0.0592297 1 +-1.37790811 0.13333334 0.795535624 -0.863000393 0.0835082754 0.498253524 0.0603628 1 +-1.40871537 0.100000009 0.746655524 -0.881830633 0.0625982136 0.467393011 0.0608891 1 +-1.4356432 0.0666666701 0.6955356 -0.899160564 0.0417541377 0.435622305 0.061537 1 +-1.43758297 0 0.696655512 -0.899901152 0 0.436093837 0.0612397 1 +-1.4356432 -0.0666666701 0.6955356 -0.899160564 -0.0417541377 0.435622305 0.0612397 1 +-1.40871537 -0.100000009 0.746655524 -0.881830633 -0.0625982136 0.467393011 0.0604536 1 +-1.37790811 -0.13333334 0.795535624 -0.863000393 -0.0835082754 0.498253524 0.059784 1 +-1.3509804 -0.100000009 0.846655548 -0.845689476 -0.0625982136 0.52999115 0.0588084 1 +-1.28154075 -0.100000001 0.939897954 -0.804782927 -0.0627980754 0.590237856 0.0573419 1 +-1.28546011 -0.0333333313 0.942160785 -0.806381166 -0.0209103115 0.591026306 0.0572024 1 +-1.28546011 0.0333333388 0.942160785 -0.806381166 0.0209103171 0.591026306 0.0573419 1 +-1.28154075 0.100000009 0.939897954 -0.804782927 0.0627980828 0.590237856 0.0577647 1 +-1.3143276 0.13333334 0.892160773 -0.82449013 0.0836412609 0.559660852 0.0587982 1 +-1.3431952 0.166666672 0.842160761 -0.842598975 0.104551569 0.528295338 0.0599553 1 +-1.36814332 0.200000018 0.789897919 -0.859167695 0.125596166 0.496040702 0.0612573 1 +-1.40093017 0.166666672 0.742160738 -0.878816724 0.104551576 0.46556443 0.0616358 1 +-1.42979777 0.13333334 0.692160726 -0.896925569 0.0836412534 0.434198916 0.0621446 1 +-1.45474589 0.100000001 0.639897883 -0.913552463 0.0627980754 0.401843578 0.0627711 1 +-1.45866525 0.0333333313 0.642160714 -0.915034413 0.0209103115 0.402833432 0.0623085 1 +-1.45866525 -0.0333333388 0.642160714 -0.915034413 -0.0209103171 0.402833432 0.0621533 1 +-1.45474589 -0.100000009 0.639897883 -0.913552463 -0.0627980828 0.401843578 0.0623085 1 +-1.42979777 -0.13333334 0.692160726 -0.896925569 -0.0836412534 0.434198916 0.061537 1 +-1.40093017 -0.166666672 0.742160738 -0.878816724 -0.104551576 0.46556443 0.0608891 1 +-1.36814332 -0.200000018 0.789897919 -0.859167695 -0.125596166 0.496040702 0.0603628 1 +-1.3431952 -0.166666672 0.842160761 -0.842598975 -0.104551569 0.528295338 0.0592297 1 +-1.3143276 -0.13333334 0.892160773 -0.82449013 -0.0836412609 0.559660852 0.0582305 1 +-1.23881078 -0.13333334 0.981894433 -0.780912697 -0.084049724 0.618959606 0.0568416 1 +-1.24477983 -0.0666666701 0.985340655 -0.783389151 -0.0419559702 0.62011379 0.0565618 1 +-1.24676013 0 0.986483991 -0.784209371 0 0.620496273 0.0565618 1 +-1.24477983 0.0666666701 0.985340655 -0.783389151 0.0419559702 0.62011379 0.0568416 1 +-1.23881078 0.13333334 0.981894433 -0.780912697 0.084049724 0.618959606 0.0574188 1 +-1.27364731 0.166666687 0.935340703 -0.801556587 0.104889929 0.588646889 0.058487 1 +-1.3044951 0.200000018 0.886484027 -0.820524573 0.125799567 0.557596505 0.0596811 1 +-1.33138239 0.233333349 0.835340679 -0.837891579 0.146845892 0.525712907 0.0610134 1 +-1.35428083 0.266666681 0.781894445 -0.85370189 0.168099448 0.492885053 0.062514 1 +-1.38911736 0.233333349 0.735340655 -0.874226451 0.146845892 0.462778926 0.0627254 1 +-1.41996527 0.200000018 0.686483979 -0.893154919 0.125799567 0.431796879 0.063081 1 +-1.44685245 0.166666672 0.635340631 -0.910561502 0.104889929 0.399845004 0.0635633 1 +-1.46975088 0.13333334 0.581894457 -0.926491082 0.084049724 0.366810501 0.0641787 1 +-1.47571993 0.0666666701 0.585340679 -0.928728878 0.0419559702 0.368378013 0.0635267 1 +-1.47770023 0 0.586484015 -0.929470181 0 0.36889717 0.0632066 1 +-1.47571993 -0.0666666701 0.585340679 -0.928728878 -0.0419559702 0.368378013 0.0632066 1 +-1.46975088 -0.13333334 0.581894457 -0.926491082 -0.084049724 0.366810501 0.0635267 1 +-1.44685245 -0.166666687 0.635340631 -0.910561502 -0.104889944 0.399845004 0.0627711 1 +-1.41996527 -0.200000018 0.686483979 -0.893154919 -0.125799567 0.431796879 0.0621446 1 +-1.38911736 -0.233333349 0.735340655 -0.874226451 -0.146845892 0.462778926 0.0616358 1 +-1.35428083 -0.266666681 0.781894445 -0.85370189 -0.168099448 0.492885053 0.0612573 1 +-1.33138239 -0.233333349 0.835340679 -0.837891579 -0.146845892 0.525712907 0.0599553 1 +-1.3044951 -0.200000018 0.886484027 -0.820524573 -0.125799567 0.557596505 0.0587982 1 +-1.27364731 -0.166666672 0.935340703 -0.801556587 -0.104889922 0.588646889 0.0577647 1 +-1.19177425 -0.166666687 1.0214045 -0.755048394 -0.105591655 0.647110701 0.0564605 1 +-1.19989884 -0.100000009 1.02609527 -0.758484006 -0.0632123351 0.648618698 0.056029 1 +-1.20393121 -0.0333333388 1.02842331 -0.760184824 -0.0210472979 0.649365842 0.0558851 1 +-1.20393121 0.0333333313 1.02842331 -0.760184824 0.0210472923 0.649365842 0.056029 1 +-1.19989884 0.100000001 1.02609527 -0.758484006 0.0632123277 0.648618698 0.0564605 1 +-1.19177425 0.166666672 1.0214045 -0.755048394 0.105591647 0.647110701 0.0572076 1 +-1.22876632 0.200000018 0.976095259 -0.776731789 0.12642467 0.61701256 0.0583155 1 +-1.26166618 0.233333349 0.928423345 -0.79663986 0.147331089 0.58622402 0.0595471 1 +-1.29053366 0.266666681 0.878423333 -0.814867318 0.168378383 0.554653108 0.0609175 1 +-1.31536889 0.300000012 0.826095283 -0.831475317 0.18963699 0.522194088 0.0624548 1 +-1.33611178 0.333333343 0.771404505 -0.846493423 0.211183295 0.488723218 0.0641917 1 +-1.37310386 0.300000012 0.726095259 -0.867970943 0.18963699 0.458981723 0.0642131 1 +-1.40600383 0.266666681 0.678423285 -0.887777269 0.168378368 0.428369224 0.0643967 1 +-1.43487132 0.233333349 0.628423274 -0.906004786 0.147331074 0.396798283 0.0647256 1 +-1.45970643 0.200000018 0.576095283 -0.922714412 0.12642467 0.36416325 0.0651911 1 +-1.48044932 0.166666687 0.521404505 -0.937938452 0.105591655 0.330335766 0.0657968 1 +-1.48857391 0.100000009 0.526095271 -0.940962255 0.0632123351 0.332557082 0.0649234 1 +-1.49260628 0.0333333388 0.528423309 -0.942459822 0.0210472997 0.33365646 0.0644152 1 +-1.49260628 -0.0333333313 0.528423309 -0.942459822 -0.0210472941 0.33365646 0.0642456 1 +-1.48857391 -0.100000001 0.526095271 -0.940962255 -0.0632123277 0.332557082 0.0644152 1 +-1.48044932 -0.166666672 0.521404505 -0.937938452 -0.105591647 0.330335766 0.0649234 1 +-1.45970643 -0.200000018 0.576095283 -0.922714412 -0.12642467 0.36416325 0.0641787 1 +-1.43487132 -0.233333349 0.628423274 -0.906004786 -0.147331074 0.396798283 0.0635633 1 +-1.40600383 -0.266666681 0.678423285 -0.887777269 -0.168378368 0.428369224 0.063081 1 +-1.37310386 -0.300000012 0.726095259 -0.867970943 -0.18963699 0.458981723 0.0627254 1 +-1.33611178 -0.333333343 0.771404505 -0.846493423 -0.211183295 0.488723218 0.062514 1 +-1.31536889 -0.300000012 0.826095283 -0.831475317 -0.18963699 0.522194088 0.0610134 1 +-1.29053366 -0.266666681 0.878423333 -0.814867318 -0.168378383 0.554653108 0.0596811 1 +-1.26166618 -0.233333349 0.928423345 -0.79663986 -0.147331089 0.58622402 0.058487 1 +-1.22876632 -0.200000018 0.976095259 -0.776731789 -0.12642467 0.61701256 0.0574188 1 +-1.14013553 -0.200000003 1.05825758 -0.726950765 -0.127520069 0.674745381 0.0562107 1 +-1.15056598 -0.133333325 1.06427968 -0.731455445 -0.0847647041 0.676600218 0.0556069 1 +-1.15675986 -0.0666666627 1.06785572 -0.734121025 -0.0423090383 0.677699268 0.0553102 1 +-1.15881395 7.4505806e-09 1.06904161 -0.735003412 4.72569583e-09 0.678063333 0.0553102 1 +-1.15675986 0.0666666776 1.06785572 -0.734121025 0.0423090495 0.677699268 0.0556069 1 +-1.15056598 0.133333355 1.06427968 -0.731455445 0.0847647265 0.676600218 0.0562107 1 +-1.14013553 0.200000018 1.05825758 -0.726950765 0.127520069 0.674745381 0.0571555 1 +-1.17943358 0.233333349 1.0142796 -0.749807596 0.148338258 0.644813359 0.0583052 1 +-1.21449494 0.266666681 0.967855632 -0.770761788 0.169236183 0.61423564 0.0595761 1 +-1.24541652 0.300000012 0.919041634 -0.789933026 0.190281659 0.582922518 0.0609889 1 +-1.27222991 0.333333343 0.867855668 -0.807402432 0.211545214 0.55077213 0.0625722 1 +-1.29490364 0.366666675 0.814279616 -0.823215902 0.233102933 0.51766628 0.0643559 1 +-1.31334066 0.400000036 0.758257568 -0.83738631 0.255040109 0.483465225 0.0663852 1 +-1.3526386 0.366666675 0.714279592 -0.859920144 0.233102962 0.454092771 0.0661755 1 +-1.38770008 0.333333343 0.66785562 -0.880683899 0.211545214 0.423844963 0.0661584 1 +-1.41862166 0.300000012 0.619041562 -0.899792194 0.190281659 0.392640829 0.0663115 1 +-1.44543505 0.266666681 0.567855656 -0.917324603 0.169236183 0.360381424 0.0666184 1 +-1.46810865 0.233333349 0.514279604 -0.933328569 0.148338258 0.326945722 0.0670762 1 +-1.4865458 0.200000003 0.458257556 -0.947821975 0.127520055 0.292185128 0.0676711 1 +-1.49697626 0.133333325 0.464279592 -0.95168072 0.0847647041 0.295158952 0.066536 1 +-1.50317013 0.0666666627 0.467855632 -0.953965247 0.0423090346 0.296917826 0.0658058 1 +-1.50522423 -7.4505806e-09 0.469041586 -0.954721808 -4.72569583e-09 0.297500014 0.0654487 1 +-1.50317013 -0.0666666776 0.467855632 -0.953965247 -0.0423090458 0.296917826 0.0654487 1 +-1.49697626 -0.133333355 0.464279592 -0.95168072 -0.0847647265 0.295158952 0.0658058 1 +-1.4865458 -0.200000018 0.458257556 -0.947821975 -0.127520055 0.292185128 0.0665342 1 +-1.46810865 -0.233333349 0.514279604 -0.933328569 -0.148338258 0.326945722 0.0657968 1 +-1.44543505 -0.266666681 0.567855656 -0.917324603 -0.169236183 0.360381424 0.0651911 1 +-1.41862166 -0.300000012 0.619041562 -0.899792194 -0.190281659 0.392640829 0.0647256 1 +-1.38770008 -0.333333343 0.66785562 -0.880683899 -0.211545214 0.423844963 0.0643967 1 +-1.3526386 -0.366666675 0.714279592 -0.859920144 -0.233102962 0.454092771 0.0642131 1 +-1.31334066 -0.400000036 0.758257568 -0.83738631 -0.255040109 0.483465225 0.0641917 1 +-1.29490364 -0.366666675 0.814279616 -0.823215902 -0.233102933 0.51766628 0.0624548 1 +-1.27222991 -0.333333343 0.867855668 -0.807402432 -0.211545214 0.55077213 0.0609175 1 +-1.24541652 -0.300000012 0.919041634 -0.789933026 -0.190281659 0.582922518 0.0595471 1 +-1.21449494 -0.266666681 0.967855632 -0.770761788 -0.169236183 0.61423564 0.0583155 1 +-1.17943358 -0.233333349 1.0142796 -0.749807596 -0.148338258 0.644813359 0.0572076 1 +-1.08348429 -0.233333349 1.09221661 -0.696300268 -0.149951488 0.701912105 0.0561183 1 +-1.09643066 -0.166666672 1.09969127 -0.70202446 -0.106713615 0.704112172 0.0553155 1 +-1.10494339 -0.100000001 1.10460603 -0.705770612 -0.0638739169 0.705555141 0.0548511 1 +-1.10916543 -0.0333333313 1.10704362 -0.707623422 -0.0212659407 0.706269741 0.0546954 1 +-1.10916543 0.0333333388 1.10704362 -0.707623422 0.0212659445 0.706269741 0.0548511 1 +-1.10494339 0.100000009 1.10460603 -0.705770612 0.0638739243 0.705555141 0.0553155 1 +-1.09643066 0.166666687 1.09969127 -0.70202446 0.106713623 0.704112172 0.0561183 1 +-1.08348429 0.233333349 1.09221661 -0.696300268 0.149951488 0.701912105 0.0572993 1 +-1.12529814 0.266666681 1.04969132 -0.720507801 0.170741796 0.6720981 0.058488 1 +-1.16267848 0.300000012 1.00460613 -0.742648125 0.191621721 0.641681135 0.0598069 1 +-1.195768 0.333333343 0.957043648 -0.762873888 0.212659404 0.610572994 0.061267 1 +-1.22463548 0.366666675 0.907043695 -0.781290829 0.233925372 0.578674138 0.0628992 1 +-1.24928093 0.400000036 0.854606092 -0.797964573 0.255495667 0.545870304 0.0647403 1 +-1.2696358 0.433333367 0.79969126 -0.812924445 0.277455389 0.512027621 0.0668312 1 +-1.28555691 0.466666698 0.742216647 -0.82616204 0.299902946 0.476984859 0.0692405 1 +-1.32737076 0.433333367 0.699691236 -0.849891186 0.277455419 0.447999477 0.0687269 1 +-1.3647511 0.400000036 0.654606044 -0.871719897 0.255495667 0.41812247 0.0684625 1 +-1.39784062 0.366666675 0.607043624 -0.89179194 0.233925372 0.387280643 0.0684058 1 +-1.4267081 0.333333343 0.557043672 -0.910208642 0.212659404 0.355381727 0.068533 1 +-1.45135355 0.300000012 0.504606068 -0.927036345 0.191621765 0.32231167 0.0688275 1 +-1.47170842 0.266666681 0.449691236 -0.94230783 0.170741767 0.287929028 0.0692793 1 +-1.48762953 0.233333349 0.392216623 -0.956023812 0.149951473 0.252057672 0.0698854 1 +-1.5005759 0.166666672 0.399691224 -0.96079123 0.106713615 0.255914956 0.0684146 1 +-1.50908864 0.100000001 0.404606044 -0.963913918 0.0638739094 0.258437693 0.0674159 1 +-1.51331067 0.0333333313 0.407043636 -0.965459287 0.0212659407 0.25968498 0.0668365 1 +-1.51331067 -0.0333333388 0.407043636 -0.965459287 -0.0212659445 0.25968498 0.0666471 1 +-1.50908864 -0.100000009 0.404606044 -0.963913918 -0.0638739169 0.258437693 0.0668365 1 +-1.5005759 -0.166666687 0.399691224 -0.96079123 -0.106713623 0.255914956 0.0674159 1 +-1.48762953 -0.233333349 0.392216623 -0.956023812 -0.149951473 0.252057672 0.0684146 1 +-1.47170842 -0.266666681 0.449691236 -0.94230783 -0.170741767 0.287929028 0.0676711 1 +-1.45135355 -0.300000012 0.504606068 -0.927036345 -0.191621765 0.32231167 0.0670762 1 +-1.4267081 -0.333333343 0.557043672 -0.910208642 -0.212659404 0.355381727 0.0666184 1 +-1.39784062 -0.366666675 0.607043624 -0.89179194 -0.233925372 0.387280643 0.0663115 1 +-1.3647511 -0.400000036 0.654606044 -0.871719897 -0.255495667 0.41812247 0.0661584 1 +-1.32737076 -0.433333367 0.699691236 -0.849891186 -0.277455419 0.447999477 0.0661755 1 +-1.28555691 -0.466666698 0.742216647 -0.82616204 -0.299902946 0.476984859 0.0663852 1 +-1.2696358 -0.433333367 0.79969126 -0.812924445 -0.277455389 0.512027621 0.0643559 1 +-1.24928093 -0.400000036 0.854606092 -0.797964573 -0.255495667 0.545870304 0.0625722 1 +-1.22463548 -0.366666675 0.907043695 -0.781290829 -0.233925372 0.578674138 0.0609889 1 +-1.195768 -0.333333343 0.957043648 -0.762873888 -0.212659404 0.610572994 0.0595761 1 +-1.16267848 -0.300000012 1.00460613 -0.742648125 -0.191621721 0.641681135 0.0583052 1 +-1.12529814 -0.266666681 1.04969132 -0.720507801 -0.170741796 0.6720981 0.0571555 1 +-1.02125049 -0.266666681 1.12295258 -0.662662804 -0.173033059 0.728654623 0.056216 1 +-1.03700662 -0.200000018 1.13204944 -0.669815063 -0.129182428 0.731204391 0.0551763 1 +-1.04806089 -0.13333334 1.13843155 -0.674802423 -0.0858477429 0.732988298 0.0545119 1 +-1.05461681 -0.0666666701 1.14221668 -0.677748561 -0.0428432748 0.734044552 0.0541883 1 +-1.05678964 0 1.14347112 -0.678723156 0 0.734394312 0.0541883 1 +-1.05461681 0.0666666701 1.14221668 -0.677748561 0.0428432748 0.734044552 0.0545119 1 +-1.04806089 0.13333334 1.13843155 -0.674802423 0.0858477429 0.732988298 0.0551763 1 +-1.03700662 0.200000018 1.13204944 -0.669815063 0.129182428 0.731204391 0.056216 1 +-1.02125049 0.266666681 1.12295258 -0.662662804 0.173033059 0.728654623 0.0576955 1 +-1.0658741 0.300000012 1.08204937 -0.688460946 0.193773627 0.698908746 0.0589289 1 +-1.10579586 0.333333373 1.03843164 -0.711975515 0.214619368 0.668602526 0.0602896 1 +-1.14121938 0.366666675 0.992216647 -0.733403623 0.235638008 0.637647152 0.061803 1 +-1.17225969 0.400000036 0.943471134 -0.752883732 0.256900012 0.605944276 0.0634929 1 +-1.19895434 0.433333367 0.892216682 -0.770506918 0.278481305 0.573382258 0.0653958 1 +-1.22126591 0.466666698 0.838431597 -0.786321819 0.300467104 0.539830923 0.0675626 1 +-1.23907924 0.5 0.782049417 -0.800336182 0.322956026 0.505135119 0.0700576 1 +-1.25219059 0.533333361 0.722952604 -0.812513828 0.346066117 0.469105095 0.0729754 1 +-1.2968142 0.5 0.682049394 -0.837627947 0.322956026 0.44054392 0.0720524 1 +-1.33673608 0.466666698 0.638431549 -0.860668242 0.300467104 0.41105926 0.0714655 1 +-1.37215948 0.433333337 0.592216611 -0.881817102 0.278481275 0.380587488 0.0711408 1 +-1.40319979 0.400000036 0.543471158 -0.901205063 0.256900012 0.349044353 0.0710502 1 +-1.42989445 0.366666675 0.492216617 -0.918920457 0.235638037 0.316322595 0.0711601 1 +-1.45220613 0.333333343 0.438431561 -0.935014546 0.214619339 0.282287687 0.0714521 1 +-1.47001934 0.300000012 0.382049382 -0.949503183 0.193773627 0.246770293 0.071914 1 +-1.48313069 0.266666681 0.322952598 -0.962364852 0.173033059 0.209555522 0.0725475 1 +-1.49888682 0.200000018 0.3320494 -0.968149066 0.129182428 0.214474708 0.0706452 1 +-1.5099411 0.13333334 0.338431567 -0.972187698 0.0858477429 0.217901871 0.0693085 1 +-1.51649702 0.0666666701 0.342216641 -0.974575579 0.0428432822 0.21992524 0.0684564 1 +-1.51866984 0 0.34347114 -0.975365698 0 0.220594347 0.0680421 1 +-1.51649702 -0.0666666701 0.342216641 -0.974575579 -0.0428432822 0.21992524 0.0680421 1 +-1.5099411 -0.13333334 0.338431567 -0.972187698 -0.0858477429 0.217901871 0.0684564 1 +-1.49888682 -0.200000018 0.3320494 -0.968149066 -0.129182428 0.214474708 0.0693085 1 +-1.48313069 -0.266666681 0.322952598 -0.962364852 -0.173033059 0.209555522 0.0706452 1 +-1.47001934 -0.300000012 0.382049382 -0.949503183 -0.193773627 0.246770293 0.0698854 1 +-1.45220613 -0.333333373 0.438431561 -0.935014546 -0.214619368 0.282287687 0.0692793 1 +-1.42989445 -0.366666675 0.492216617 -0.918920457 -0.235638037 0.316322595 0.0688275 1 +-1.40319979 -0.400000036 0.543471158 -0.901205063 -0.256900012 0.349044353 0.068533 1 +-1.37215948 -0.433333367 0.592216611 -0.881817102 -0.278481305 0.380587488 0.0684058 1 +-1.33673608 -0.466666698 0.638431549 -0.860668242 -0.300467104 0.41105926 0.0684625 1 +-1.2968142 -0.5 0.682049394 -0.837627947 -0.322956026 0.44054392 0.0687269 1 +-1.25219059 -0.533333361 0.722952604 -0.812513828 -0.346066117 0.469105095 0.0692405 1 +-1.23907924 -0.5 0.782049417 -0.800336182 -0.322956026 0.505135119 0.0668312 1 +-1.22126591 -0.466666698 0.838431597 -0.786321819 -0.300467104 0.539830923 0.0647403 1 +-1.19895434 -0.433333337 0.892216682 -0.770506918 -0.278481275 0.573382258 0.0628992 1 +-1.17225969 -0.400000036 0.943471134 -0.752883732 -0.256900012 0.605944276 0.061267 1 +-1.14121938 -0.366666675 0.992216647 -0.733403623 -0.235638008 0.637647152 0.0598069 1 +-1.10579586 -0.333333343 1.03843164 -0.711975515 -0.214619339 0.668602526 0.0584891 1 +-1.0658741 -0.300000012 1.08204937 -0.688460946 -0.193773627 0.698908746 0.0572993 1 +-0.952627957 -0.300000012 1.1500001 -0.625430822 -0.196959645 0.755011976 0.0565734 1 +-0.971612751 -0.233333349 1.16096091 -0.634308934 -0.152329654 0.757923245 0.0552292 1 +-0.985525548 -0.166666687 1.16899347 -0.640764892 -0.108362645 0.76005125 0.0543267 1 +-0.994654477 -0.100000009 1.17426407 -0.644978166 -0.0648444444 0.761444986 0.053805 1 +-0.999176681 -0.0333333388 1.176875 -0.647058666 -0.0215863977 0.762134612 0.053633 1 +-0.999176681 0.0333333313 1.176875 -0.647058666 0.0215863939 0.762134612 0.053805 1 +-0.994654477 0.100000001 1.17426407 -0.644978166 0.0648444444 0.761444986 0.0543267 1 +-0.985525548 0.166666672 1.16899347 -0.640764892 0.108362637 0.76005125 0.0552292 1 +-0.971612751 0.233333349 1.16096091 -0.634308934 0.152329654 0.757923245 0.0565734 1 +-0.952627957 0.300000012 1.1500001 -0.625430822 0.196959645 0.755011976 0.0584452 1 +-1.00048029 0.333333343 1.11096096 -0.65315491 0.217613786 0.725281239 0.0597111 1 +-1.04326069 0.366666675 1.06899357 -0.678302765 0.238397762 0.69503361 0.0611159 1 +-1.08125699 0.400000036 1.0242641 -0.701135039 0.259377778 0.664178312 0.0626769 1 +-1.11464679 0.433333337 0.976874948 -0.72183615 0.280623138 0.632616222 0.0644291 1 +-1.14351428 0.466666698 0.926874936 -0.740530431 0.302209526 0.600236595 0.0664077 1 +-1.16785955 0.5 0.874264121 -0.757292032 0.324222207 0.566911697 0.0686635 1 +-1.18759823 0.533333361 0.818993568 -0.772147596 0.346760422 0.532489777 0.0712682 1 +-1.20255291 0.566666722 0.760960937 -0.785076261 0.36994347 0.496786743 0.0743177 1 +-1.2124356 0.600000024 0.700000048 -0.796002924 0.393919289 0.459572494 0.0779568 1 +-1.26028788 0.566666722 0.660960913 -0.822768033 0.36994347 0.43150261 0.0764467 1 +-1.3030684 0.533333361 0.618993521 -0.847223401 0.346760392 0.402454525 0.0754048 1 +-1.34106469 0.5 0.57426405 -0.869605899 0.324222207 0.37237832 0.0747276 1 +-1.37445438 0.466666698 0.526874959 -0.890085399 0.302209526 0.341199905 0.0743506 1 +-1.40332186 0.433333367 0.476874918 -0.9087798 0.280623138 0.308820307 0.0742253 1 +-1.42766726 0.400000006 0.424264073 -0.925762773 0.259377748 0.275111645 0.074329 1 +-1.44740593 0.366666675 0.368993521 -0.941068172 0.238397762 0.239910617 0.0746373 1 +-1.4623605 0.333333343 0.310960919 -0.954689384 0.217613786 0.203008145 0.075144 1 +-1.47224331 0.300000012 0.25 -0.966575027 0.196959645 0.164133027 0.0758458 1 +-1.4912281 0.233333349 0.260960877 -0.973535359 0.152329654 0.170366049 0.0733538 1 +-1.50514078 0.166666687 0.268993437 -0.978606105 0.108362645 0.174893022 0.0715697 1 +-1.51426983 0.100000009 0.274263978 -0.981919706 0.0648444444 0.177844927 0.0703677 1 +-1.51879191 0.0333333388 0.2768749 -0.983557224 0.0215863977 0.179301918 0.0696725 1 +-1.51879191 -0.0333333313 0.2768749 -0.983557224 -0.0215863939 0.179301918 0.0694444 1 +-1.51426983 -0.100000001 0.274263978 -0.981919706 -0.0648444369 0.177844927 0.0696725 1 +-1.50514078 -0.166666672 0.268993437 -0.978606105 -0.108362637 0.174893022 0.0703677 1 +-1.4912281 -0.233333349 0.260960877 -0.973535359 -0.152329654 0.170366049 0.0715697 1 +-1.47224331 -0.300000012 0.25 -0.966575027 -0.196959645 0.164133027 0.0733538 1 +-1.4623605 -0.333333343 0.310960919 -0.954689384 -0.217613786 0.203008145 0.0725475 1 +-1.44740593 -0.366666675 0.368993521 -0.941068172 -0.238397762 0.239910617 0.071914 1 +-1.42766726 -0.400000036 0.424264073 -0.925762773 -0.259377778 0.275111645 0.0714521 1 +-1.40332186 -0.433333337 0.476874918 -0.9087798 -0.280623138 0.308820307 0.0711601 1 +-1.37445438 -0.466666698 0.526874959 -0.890085399 -0.302209526 0.341199905 0.0710502 1 +-1.34106469 -0.5 0.57426405 -0.869605899 -0.324222207 0.37237832 0.0711408 1 +-1.3030684 -0.533333361 0.618993521 -0.847223401 -0.346760392 0.402454525 0.0714655 1 +-1.26028788 -0.566666722 0.660960913 -0.822768033 -0.36994347 0.43150261 0.0720524 1 +-1.2124356 -0.600000024 0.700000048 -0.796002924 -0.393919289 0.459572494 0.0729754 1 +-1.20255291 -0.566666722 0.760960937 -0.785076261 -0.36994347 0.496786743 0.0700576 1 +-1.18759823 -0.533333361 0.818993568 -0.772147596 -0.346760422 0.532489777 0.0675626 1 +-1.16785955 -0.5 0.874264121 -0.757292032 -0.324222207 0.566911697 0.0653976 1 +-1.14351428 -0.466666698 0.926874936 -0.740530431 -0.302209526 0.600236595 0.0634929 1 +-1.11464679 -0.433333367 0.976874948 -0.72183615 -0.280623138 0.632616222 0.061803 1 +-1.08125699 -0.400000006 1.0242641 -0.701135039 -0.259377778 0.664178312 0.0602896 1 +-1.04326069 -0.366666675 1.06899357 -0.678302765 -0.238397762 0.69503361 0.0589289 1 +-1.00048029 -0.333333343 1.11096096 -0.65315491 -0.217613786 0.725281239 0.0576955 1 +-0.876437306 -0.333333373 1.17267799 -0.583717525 -0.222003937 0.781017303 0.0573024 1 +-0.89927125 -0.266666681 1.18586123 -0.5947662 -0.176369831 0.784313023 0.0555501 1 +-0.916505456 -0.200000018 1.19581139 -0.603023231 -0.131591856 0.786795139 0.0543311 1 +-0.928555012 -0.133333355 1.20276821 -0.608755231 -0.0874125734 0.788527846 0.0535606 1 +-0.935685873 -0.0666666776 1.20688522 -0.612131715 -0.0436137691 0.789552093 0.0531897 1 +-0.938046813 -7.4505806e-09 1.20824826 -0.613247216 -4.87080998e-09 0.789891124 0.0531897 1 +-0.935685873 0.0666666627 1.20688522 -0.612131715 0.0436137617 0.789552093 0.0535618 1 +-0.928555012 0.133333325 1.20276821 -0.608755231 0.087412551 0.788527846 0.0543311 1 +-0.916505456 0.200000003 1.19581139 -0.603023231 0.131591842 0.786795139 0.0555501 1 +-0.89927125 0.266666681 1.18586123 -0.5947662 0.176369831 0.784313023 0.0573024 1 +-0.876437306 0.333333343 1.17267799 -0.583717525 0.222003922 0.781017303 0.0597391 1 +-0.928138793 0.366666675 1.13586128 -0.613858759 0.242508501 0.751243651 0.0610007 1 +-0.974240541 0.400000036 1.09581137 -0.641010523 0.263183713 0.720999181 0.0624262 1 +-1.01515758 0.433333367 1.05276823 -0.665531397 0.284090817 0.690188706 0.0640298 1 +-1.05115592 0.466666698 1.00688529 -0.687672973 0.305296361 0.658710837 0.0658394 1 +-1.08238447 0.5 0.958248258 -0.707607746 0.326874495 0.626453817 0.0679008 1 +-1.10889101 0.533333361 0.906885207 -0.725443661 0.348910123 0.59329015 0.0702625 1 +-1.13062763 0.566666722 0.852768242 -0.741232812 0.371503353 0.559069812 0.0730008 1 +-1.14744556 0.600000024 0.795811415 -0.754972398 0.39477554 0.523611486 0.0762271 1 +-1.15907896 0.633333385 0.735861242 -0.766599536 0.418878347 0.486688912 0.0800906 1 +-1.1651125 0.666666687 0.672677994 -0.775978625 0.444007844 0.448011458 0.0848333 1 +-1.21681392 0.633333385 0.635861218 -0.804784715 0.418878376 0.420550257 0.0824019 1 +-1.26291573 0.600000024 0.595811367 -0.830947042 0.39477554 0.39201957 0.08068 1 +-1.30383277 0.566666722 0.552768171 -0.854785085 0.371503353 0.362391561 0.0794901 1 +-1.33983111 0.533333361 0.506885231 -0.876526177 0.348910123 0.33160758 0.0787147 1 +-1.37105954 0.5 0.458248228 -0.896328866 0.326874524 0.299579352 0.0782822 1 +-1.3975662 0.466666698 0.406885207 -0.914296806 0.305296361 0.266186923 0.0781418 1 +-1.41930282 0.433333337 0.352768183 -0.93048656 0.284090787 0.231272742 0.0782662 1 +-1.43612075 0.400000036 0.295811385 -0.944908917 0.263183713 0.19463183 0.0786373 1 +-1.44775414 0.366666675 0.235861197 -0.957525492 0.242508501 0.155995473 0.079248 1 +-1.45378768 0.333333373 0.172677964 -0.968239605 0.222003922 0.115005545 0.0801062 1 +-1.47662163 0.266666681 0.1858612 -0.976618052 0.176369816 0.122926146 0.0767544 1 +-1.49385583 0.200000018 0.195811361 -0.982896209 0.131591856 0.128835887 0.074341 1 +-1.50590539 0.133333355 0.202768177 -0.987262726 0.0874125659 0.132933617 0.0726666 1 +-1.51303625 0.0666666776 0.206885189 -0.989838064 0.0436137691 0.135345623 0.0716081 1 +-1.51539719 7.4505806e-09 0.208248228 -0.990689278 4.87080909e-09 0.13614206 0.0710955 1 +-1.51303625 -0.0666666627 0.206885189 -0.989838064 -0.0436137617 0.135345623 0.0710955 1 +-1.50590539 -0.133333325 0.202768177 -0.987262726 -0.0874125436 0.132933617 0.0716081 1 +-1.49385583 -0.200000003 0.195811361 -0.982896209 -0.131591842 0.128835887 0.0726674 1 +-1.47662163 -0.266666681 0.1858612 -0.976618052 -0.176369816 0.122926146 0.074341 1 +-1.45378768 -0.333333343 0.172677964 -0.968239605 -0.222003907 0.115005545 0.0767544 1 +-1.44775414 -0.366666675 0.235861197 -0.957525492 -0.242508501 0.155995473 0.0758458 1 +-1.43612075 -0.400000036 0.295811385 -0.944908917 -0.263183713 0.19463183 0.075144 1 +-1.41930282 -0.433333367 0.352768183 -0.93048656 -0.284090817 0.231272742 0.0746373 1 +-1.3975662 -0.466666698 0.406885207 -0.914296806 -0.305296361 0.266186923 0.074329 1 +-1.37105954 -0.5 0.458248228 -0.896328866 -0.326874524 0.299579352 0.0742253 1 +-1.33983111 -0.533333361 0.506885231 -0.876526177 -0.348910123 0.33160758 0.0743506 1 +-1.30383277 -0.566666722 0.552768171 -0.854785085 -0.371503353 0.362391561 0.0747276 1 +-1.26291573 -0.600000024 0.595811367 -0.830947042 -0.39477554 0.39201957 0.0754048 1 +-1.21681392 -0.633333385 0.635861218 -0.804784715 -0.418878376 0.420550257 0.0764467 1 +-1.1651125 -0.666666687 0.672677994 -0.775978625 -0.444007844 0.448011458 0.0779568 1 +-1.15907896 -0.633333385 0.735861242 -0.766599536 -0.418878347 0.486688912 0.0743177 1 +-1.14744556 -0.600000024 0.795811415 -0.754972398 -0.39477554 0.523611486 0.0712682 1 +-1.13062763 -0.566666722 0.852768242 -0.741232812 -0.371503353 0.559069812 0.0686635 1 +-1.10889101 -0.533333361 0.906885207 -0.725443661 -0.348910123 0.59329015 0.0664077 1 +-1.08238447 -0.5 0.958248258 -0.707607746 -0.326874495 0.626453817 0.0644291 1 +-1.05115592 -0.466666698 1.00688529 -0.687672973 -0.305296361 0.658710837 0.0626769 1 +-1.01515758 -0.433333337 1.05276823 -0.665531397 -0.284090817 0.690188706 0.0611159 1 +-0.974240541 -0.400000036 1.09581137 -0.641010523 -0.263183713 0.720999181 0.0597111 1 +-0.928138793 -0.366666675 1.13586128 -0.613858759 -0.242508501 0.751243651 0.0584452 1 +-0.7908566 -0.366666704 1.18993473 -0.536147118 -0.248575151 0.806694984 0.0586215 1 +-0.818513989 -0.300000012 1.2059027 -0.550073862 -0.201611891 0.810414374 0.0562679 1 +-0.839776754 -0.233333349 1.21817875 -0.560645461 -0.155776262 0.813271344 0.0546147 1 +-0.85526979 -0.166666687 1.22712362 -0.568276584 -0.110740229 0.815351605 0.0535183 1 +-0.865397453 -0.100000016 1.23297083 -0.573232889 -0.066239275 0.81671077 0.0528895 1 +-0.870403767 -0.0333333462 1.2358613 -0.57567358 -0.0220462345 0.817382336 0.0526839 1 +-0.870403767 0.0333333239 1.2358613 -0.57567358 0.0220462196 0.817382336 0.0528895 1 +-0.865397453 0.099999994 1.23297083 -0.573232889 0.0662392601 0.81671077 0.0535183 1 +-0.85526979 0.166666657 1.22712362 -0.568276584 0.110740207 0.815351605 0.0546147 1 +-0.839776754 0.233333334 1.21817875 -0.560645461 0.155776247 0.813271344 0.0562679 1 +-0.818513989 0.300000012 1.2059027 -0.550073862 0.201611891 0.810414374 0.0586215 1 +-0.7908566 0.366666675 1.18993473 -0.536147118 0.248575121 0.806694984 0.0619322 1 +-0.847381473 0.400000006 1.15590262 -0.569473922 0.268815845 0.776812375 0.0631018 1 +-0.89751178 0.433333337 1.11817873 -0.599190116 0.289298773 0.746510148 0.0644856 1 +-0.941872358 0.466666698 1.07712364 -0.625818908 0.310072631 0.715685427 0.0660971 1 +-0.980867624 0.5 1.03297091 -0.649719417 0.331196278 0.684232235 0.0679518 1 +-1.0147413 0.533333361 0.985861242 -0.671136558 0.352739662 0.652035654 0.0700899 1 +-1.0436089 0.566666722 0.93586123 -0.690229118 0.37478587 0.618966281 0.0725746 1 +-1.06747019 0.600000024 0.88297087 -0.707084298 0.397435546 0.584873319 0.0754823 1 +-1.08620989 0.633333385 0.827123642 -0.721722782 0.420812875 0.54957515 0.0789327 1 +-1.09958434 0.666666687 0.768178701 -0.734096289 0.445075035 0.512845755 0.0831049 1 +-1.10718918 0.700000048 0.705902636 -0.744075 0.470427781 0.47439453 0.0882712 1 +-1.10839927 0.733333349 0.639934659 -0.751419485 0.497150242 0.433832288 0.0948841 1 +-1.16492414 0.700000048 0.605902612 -0.78287518 0.470427781 0.407190561 0.0908404 1 +-1.21505451 0.666666687 0.568178654 -0.811185598 0.445075005 0.379323155 0.087999 1 +-1.25941503 0.633333385 0.52712357 -0.83680737 0.420812845 0.350242674 0.085998 1 +-1.2984103 0.600000024 0.482970864 -0.860057294 0.397435546 0.319916308 0.0846262 1 +-1.33228397 0.566666722 0.4358612 -0.881155074 0.37478593 0.288272858 0.0837458 1 +-1.36115158 0.533333361 0.385861218 -0.900247693 0.352739632 0.255203515 0.0832685 1 +-1.38501287 0.5 0.332970858 -0.917422235 0.331196278 0.220557421 0.0831523 1 +-1.40375257 0.466666698 0.2771236 -0.932711244 0.31007266 0.184132382 0.0833609 1 +-1.41712713 0.433333367 0.218178645 -0.946091771 0.289298773 0.145658791 0.0838854 1 +-1.42473173 0.400000036 0.155902535 -0.957476199 0.268815875 0.10477268 0.0847397 1 +-1.42594194 0.366666704 0.0899346247 -0.966691852 0.248575151 0.0609695725 0.085957 1 +-1.45359933 0.300000012 0.10590259 -0.976876378 0.201611891 0.0711707398 0.0812364 1 +-1.4748621 0.233333349 0.118178643 -0.984636426 0.155776277 0.0788975433 0.0778803 1 +-1.49035513 0.166666687 0.127123579 -0.990253508 0.110740229 0.0844661519 0.0755257 1 +-1.5004828 0.100000016 0.132970795 -0.993908703 0.066239275 0.0880788714 0.0739637 1 +-1.50548911 0.0333333462 0.135861203 -0.995710671 0.0220462363 0.0898568109 0.0730694 1 +-1.50548911 -0.0333333239 0.135861203 -0.995710671 -0.0220462214 0.0898568109 0.0727782 1 +-1.5004828 -0.099999994 0.132970795 -0.993908703 -0.0662392601 0.0880788714 0.0730694 1 +-1.49035513 -0.166666657 0.127123579 -0.990253508 -0.110740207 0.0844661519 0.0739637 1 +-1.4748621 -0.233333334 0.118178643 -0.984636426 -0.155776262 0.0788975433 0.0755257 1 +-1.45359933 -0.300000012 0.10590259 -0.976876378 -0.201611891 0.0711707398 0.0778803 1 +-1.42594194 -0.366666675 0.0899346247 -0.966691852 -0.248575121 0.0609695725 0.0812364 1 +-1.42473185 -0.400000006 0.155902594 -0.957476258 -0.268815845 0.104772724 0.080107 1 +-1.41712713 -0.433333337 0.218178645 -0.946091771 -0.289298743 0.145658791 0.0792473 1 +-1.40375257 -0.466666698 0.2771236 -0.932711244 -0.31007266 0.184132382 0.0786373 1 +-1.38501287 -0.5 0.332970858 -0.917422235 -0.331196278 0.220557421 0.0782662 1 +-1.36115158 -0.533333361 0.385861218 -0.900247693 -0.352739632 0.255203515 0.0781418 1 +-1.33228397 -0.566666722 0.4358612 -0.881155074 -0.37478593 0.288272858 0.0782822 1 +-1.2984103 -0.600000024 0.482970864 -0.860057294 -0.397435546 0.319916308 0.0787147 1 +-1.25941503 -0.633333385 0.52712357 -0.83680737 -0.420812845 0.350242674 0.0794901 1 +-1.21505451 -0.666666687 0.568178654 -0.811185598 -0.445075005 0.379323155 0.08068 1 +-1.16492414 -0.700000048 0.605902612 -0.78287518 -0.470427781 0.407190561 0.0824019 1 +-1.10839927 -0.733333349 0.639934659 -0.751419485 -0.497150242 0.433832288 0.0848333 1 +-1.10718918 -0.700000048 0.705902636 -0.744075 -0.470427781 0.47439453 0.0800906 1 +-1.09958434 -0.666666687 0.768178701 -0.734096289 -0.445075035 0.512845755 0.0762271 1 +-1.08620989 -0.633333385 0.827123642 -0.721722782 -0.420812875 0.54957515 0.0730008 1 +-1.06747019 -0.600000024 0.88297087 -0.707084298 -0.397435546 0.584873319 0.0702625 1 +-1.0436089 -0.566666722 0.93586123 -0.690229118 -0.37478587 0.618966281 0.0679008 1 +-1.0147413 -0.533333361 0.985861242 -0.671136558 -0.352739662 0.652035654 0.0658394 1 +-0.980867624 -0.5 1.03297091 -0.649719417 -0.331196278 0.684232235 0.0640298 1 +-0.941872358 -0.466666698 1.07712364 -0.625818908 -0.310072631 0.715685427 0.0624262 1 +-0.89751178 -0.433333367 1.11817873 -0.599190056 -0.289298773 0.746510088 0.0610007 1 +-0.847381353 -0.400000036 1.15590262 -0.569473863 -0.268815875 0.776812375 0.0597391 1 +-0.692820191 -0.400000006 1.20000005 -0.480384409 -0.277350128 0.832050383 0.0609919 1 +-0.72698009 -0.333333343 1.21972215 -0.498430669 -0.228539348 0.836263537 0.05765 1 +-0.753434896 -0.266666651 1.23499584 -0.512174666 -0.181276321 0.83953321 0.0553296 1 +-0.773205042 -0.199999988 1.24641025 -0.522318482 -0.135104761 0.841979861 0.0537607 1 +-0.78693676 -0.133333325 1.25433826 -0.529301643 -0.0896813497 0.84368068 0.0527846 1 +-0.795030355 -0.0666666552 1.25901103 -0.533394098 -0.0447273478 0.844683528 0.0523136 1 +-0.797704816 1.49011612e-08 1.26055515 -0.534742594 9.98901495e-09 0.845014989 0.0523136 1 +-0.795030355 0.066666685 1.25901103 -0.533394098 0.0447273701 0.844683528 0.0527834 1 +-0.78693676 0.133333355 1.25433826 -0.529301643 0.089681372 0.84368068 0.0537607 1 +-0.773204923 0.200000018 1.24641013 -0.522318482 0.135104805 0.84197998 0.0553296 1 +-0.753434896 0.26666671 1.23499584 -0.512174666 0.181276366 0.83953321 0.0576479 1 +-0.72698009 0.333333373 1.21972215 -0.498430669 0.228539377 0.836263537 0.0609919 1 +-0.692820191 0.400000036 1.20000005 -0.480384409 0.277350128 0.832050383 0.0658711 1 +-0.755847573 0.433333337 1.1697222 -0.518222749 0.29710114 0.801982641 0.0666551 1 +-0.811169863 0.466666698 1.13499582 -0.551422119 0.317233622 0.771554589 0.0678261 1 +-0.859807551 0.5 1.09641016 -0.58082056 0.337761968 0.740651309 0.0693318 1 +-0.902406812 0.533333361 1.05433822 -0.606967986 0.358725429 0.709158599 0.071171 1 +-0.93936789 0.566666722 1.00901103 -0.630231619 0.380182564 0.676955938 0.0733684 1 +-0.970909894 0.600000024 0.960555136 -0.650850832 0.402210891 0.643909514 0.075977 1 +-0.997102916 0.633333385 0.909011006 -0.668966651 0.42490992 0.609864891 0.0790898 1 +-1.01787686 0.666666687 0.854338169 -0.684634328 0.448406786 0.574636519 0.0828432 1 +-1.03301251 0.700000048 0.796410143 -0.697824717 0.472866833 0.537994146 0.0874472 1 +-1.04210997 0.733333349 0.734995902 -0.708412051 0.498509943 0.499640107 0.093238 1 +-1.04452276 0.76666671 0.66972214 -0.716143668 0.525640547 0.45917359 0.100777 1 +-1.03923035 0.800000072 0.599999964 -0.720576644 0.554700255 0.416025162 0.111114 1 +-1.10225773 0.76666671 0.569722116 -0.755727768 0.525640547 0.390611768 0.103741 1 +-1.15758014 0.733333349 0.534995854 -0.786907017 0.498509884 0.363682806 0.0987829 1 +-1.20621765 0.700000048 0.496410102 -0.814828992 0.472866833 0.335336924 0.0953467 1 +-1.24881697 0.666666687 0.454338163 -0.839967012 0.448406786 0.305592477 0.0929648 1 +-1.28577805 0.633333385 0.409010977 -0.862641811 0.42490992 0.274409711 0.0913613 1 +-1.31732011 0.600000024 0.360555112 -0.883067429 0.402210891 0.241698638 0.0903733 1 +-1.34351313 0.566666722 0.309010983 -0.901376903 0.380182564 0.207318664 0.0899039 1 +-1.36428702 0.533333361 0.254338175 -0.917633295 0.358725429 0.171070442 0.0898966 1 +-1.3794229 0.5 0.196410134 -0.931833088 0.337761939 0.132679731 0.0903383 1 +-1.38852024 0.466666698 0.134995788 -0.943897009 0.317233622 0.0917682797 0.0912482 1 +-1.39093292 0.433333367 0.0697220936 -0.953648686 0.29710117 0.0478027239 0.0926967 1 +-1.3856405 0.400000006 -6.58440484e-08 -0.960768878 0.277350128 -4.56546374e-08 0.0948363 1 +-1.4198004 0.333333343 0.0197220985 -0.973440766 0.228539348 0.0135218268 0.0876204 1 +-1.44625521 0.266666651 0.0349957868 -0.983144403 0.181276321 0.0237896554 0.082678 1 +-1.46602535 0.199999988 0.0464101359 -0.990335286 0.13510479 0.0313511603 0.0792511 1 +-1.47975707 0.133333325 0.0543381497 -0.995299637 0.0896813497 0.036548391 0.0769337 1 +-1.48785067 0.0666666552 0.0590109751 -0.998214424 0.0447273478 0.039591074 0.0754902 1 +-1.49052513 -1.49011612e-08 0.060555093 -0.999175787 -9.98901584e-09 0.0405931994 0.0747978 1 +-1.48785067 -0.066666685 0.0590109751 -0.998214424 -0.0447273701 0.039591074 0.0747978 1 +-1.47975707 -0.133333355 0.0543381497 -0.995299637 -0.089681372 0.036548391 0.0754902 1 +-1.46602523 -0.200000018 0.0464100763 -0.990335226 -0.135104805 0.0313511193 0.076933 1 +-1.44625521 -0.26666671 0.0349957868 -0.983144403 -0.181276366 0.0237896554 0.0792518 1 +-1.4198004 -0.333333373 0.0197220985 -0.973440766 -0.228539377 0.0135218268 0.082678 1 +-1.3856405 -0.400000036 -6.58440484e-08 -0.960768878 -0.277350128 -4.56546374e-08 0.0876198 1 +-1.39093292 -0.433333337 0.0697220936 -0.953648686 -0.29710114 0.0478027239 0.0859584 1 +-1.38852024 -0.466666698 0.134995788 -0.943897009 -0.317233622 0.0917682797 0.0847383 1 +-1.3794229 -0.5 0.196410134 -0.931833088 -0.337761939 0.132679731 0.0838847 1 +-1.36428702 -0.533333361 0.254338175 -0.917633295 -0.358725429 0.171070442 0.0833609 1 +-1.34351313 -0.566666722 0.309010983 -0.901376903 -0.380182564 0.207318664 0.0831523 1 +-1.31732011 -0.600000024 0.360555112 -0.883067429 -0.402210891 0.241698638 0.0832685 1 +-1.28577805 -0.633333385 0.409010977 -0.862641811 -0.42490992 0.274409711 0.0837458 1 +-1.24881697 -0.666666687 0.454338163 -0.839967012 -0.448406786 0.305592477 0.0846262 1 +-1.20621765 -0.700000048 0.496410102 -0.814828992 -0.472866833 0.335336924 0.085998 1 +-1.15758014 -0.733333349 0.534995854 -0.786907017 -0.498509884 0.363682806 0.087999 1 +-1.10225773 -0.76666671 0.569722116 -0.755727768 -0.525640547 0.390611768 0.0908404 1 +-1.03923035 -0.800000072 0.599999964 -0.720576644 -0.554700255 0.416025162 0.0948841 1 +-1.04452276 -0.76666671 0.66972214 -0.716143668 -0.525640547 0.45917359 0.0882712 1 +-1.04210997 -0.733333349 0.734995902 -0.708412051 -0.498509943 0.499640107 0.0831049 1 +-1.03301251 -0.700000048 0.796410143 -0.697824717 -0.472866833 0.537994146 0.0789327 1 +-1.01787686 -0.666666687 0.854338169 -0.684634328 -0.448406786 0.574636519 0.0754823 1 +-0.997102916 -0.633333385 0.909011006 -0.668966651 -0.42490992 0.609864891 0.0725746 1 +-0.970909894 -0.600000024 0.960555136 -0.650850832 -0.402210891 0.643909514 0.0700899 1 +-0.93936789 -0.566666722 1.00901103 -0.630231619 -0.380182564 0.676955938 0.0679518 1 +-0.902406812 -0.533333361 1.05433822 -0.606967986 -0.358725429 0.709158599 0.0660971 1 +-0.859807551 -0.5 1.09641016 -0.58082056 -0.337761968 0.740651309 0.0644856 1 +-0.811169863 -0.466666698 1.13499582 -0.551422119 -0.317233622 0.771554589 0.0631018 1 +-0.755847573 -0.433333367 1.1697222 -0.518222749 -0.29710117 0.801982641 0.0619322 1 +-0.576386809 -0.433333337 1.19944382 -0.411845148 -0.309629291 0.857037485 0.0656216 1 +-0.620432734 -0.366666675 1.22487366 -0.436567724 -0.258005142 0.861882806 0.0603361 1 +-0.654239476 -0.300000012 1.24439204 -0.455108255 -0.208688855 0.865635812 0.0568374 1 +-0.679750144 -0.233333334 1.25912058 -0.468861312 -0.160942927 0.868485212 0.0544944 1 +-0.698112547 -0.166666657 1.2697221 -0.47863856 -0.114269666 0.870544374 0.0529842 1 +-0.710022867 -0.099999994 1.27659857 -0.484927177 -0.0682974011 0.87188369 0.0521366 1 +-0.715885103 -0.0333333239 1.27998316 -0.488007247 -0.0227227863 0.872543752 0.0518614 1 +-0.715885103 0.0333333462 1.27998316 -0.488007247 0.0227228012 0.872543752 0.0521366 1 +-0.710022867 0.100000016 1.27659857 -0.484927177 0.068297416 0.87188369 0.0529842 1 +-0.698112547 0.166666687 1.2697221 -0.47863856 0.114269689 0.870544374 0.0544944 1 +-0.679750144 0.233333349 1.25912058 -0.468861312 0.160942927 0.868485212 0.0568374 1 +-0.654239476 0.300000012 1.24439204 -0.455108255 0.208688855 0.865635812 0.0603361 1 +-0.620432734 0.366666704 1.22487366 -0.436567724 0.258005142 0.861882806 0.0656207 1 +-0.576386809 0.433333367 1.19944382 -0.411845148 0.309629291 0.857037485 0.0740749 1 +-0.649300218 0.466666698 1.17487371 -0.456880331 0.328370184 0.82670033 0.0733351 1 +-0.711974502 0.5 1.14439213 -0.495270461 0.347814739 0.7960729 0.0736865 1 +-0.766352654 0.533333361 1.10912061 -0.528595865 0.367869556 0.76502192 0.0747428 1 +-0.81358248 0.566666722 1.06972206 -0.557806849 0.388516963 0.733420789 0.0763616 1 +-0.854360461 0.600000024 1.02659857 -0.583505988 0.409784406 0.701140106 0.078509 1 +-0.88909018 0.633333385 0.979983151 -0.606078327 0.431733102 0.668038607 0.0812173 1 +-0.917957842 0.666666687 0.929983199 -0.625756979 0.454455853 0.633954465 0.084574 1 +-0.94096297 0.700000048 0.876598597 -0.642653227 0.478081822 0.598694026 0.0887412 1 +-0.957920194 0.733333349 0.819722116 -0.656767368 0.502786577 0.562016249 0.0939859 1 +-0.968425274 0.76666671 0.759120643 -0.667976499 0.528812468 0.523607492 0.100753 1 +-0.971782148 0.800000072 0.694392025 -0.676000297 0.556503654 0.483039558 0.109837 1 +-0.96684289 0.833333373 0.624873698 -0.680319369 0.586375296 0.439692587 0.1228 1 +-0.951664507 0.866666734 0.549443781 -0.679991961 0.619258583 0.392593563 0.143336 1 +-1.02457798 0.833333373 0.524873674 -0.720944583 0.586375296 0.369327515 0.126463 1 +-1.08725226 0.800000072 0.494392008 -0.756324708 0.556503654 0.343913645 0.116452 1 +-1.14163041 0.76666671 0.459120601 -0.787445664 0.528812468 0.316680878 0.109959 1 +-1.1888603 0.733333349 0.41972211 -0.815104067 0.502786577 0.287769049 0.105592 1 +-1.2296381 0.700000048 0.376598567 -0.839810908 0.478081852 0.257207036 0.10265 1 +-1.26436806 0.666666687 0.329983175 -0.861899137 0.454455823 0.224944159 0.100755 1 +-1.29323542 0.633333385 0.279983103 -0.881577611 0.431733102 0.190859944 0.0996925 1 +-1.31624067 0.600000024 0.226598591 -0.898958147 0.409784406 0.154760942 0.0993554 1 +-1.33319783 0.566666722 0.169722036 -0.914064467 0.388516933 0.116364487 0.0997069 1 +-1.34370303 0.533333361 0.10912057 -0.926826298 0.367869526 0.0752664953 0.100786 1 +-1.34705985 0.50000006 0.0443919711 -0.937054634 0.347814798 0.0308803655 0.102738 1 +-1.34212065 0.466666669 -0.0251263436 -0.944383562 0.328370154 -0.0176801588 0.105888 1 +-1.32694221 0.433333337 -0.100556247 -0.948138833 0.309629291 -0.0718503669 0.110988 1 +-1.37098813 0.366666675 -0.0751263425 -0.964696229 0.258005112 -0.052862674 0.0979935 1 +-1.40479493 0.300000012 -0.0556080267 -0.97721678 0.208688855 -0.0386825837 0.08988 1 +-1.43030548 0.233333334 -0.0408794209 -0.986560881 0.160942927 -0.0281968005 0.0845126 1 +-1.448668 0.166666657 -0.0302779004 -0.993232906 0.114269666 -0.020759074 0.0809192 1 +-1.4605782 0.099999994 -0.0234014317 -0.997537017 0.0682974011 -0.0159825701 0.0786055 1 +-1.46644044 0.0333333239 -0.0200169012 -0.999648631 0.0227227863 -0.0136451973 0.0773043 1 +-1.46644044 -0.0333333462 -0.0200169012 -0.999648631 -0.0227228012 -0.0136451973 0.0768841 1 +-1.4605782 -0.100000016 -0.0234014317 -0.997537017 -0.068297416 -0.0159825701 0.0773043 1 +-1.448668 -0.166666687 -0.0302779004 -0.993232906 -0.114269689 -0.020759074 0.0786055 1 +-1.43030548 -0.233333349 -0.0408794209 -0.986560881 -0.160942927 -0.0281968005 0.0809192 1 +-1.40479493 -0.300000012 -0.0556080267 -0.97721678 -0.208688855 -0.0386825837 0.0845126 1 +-1.37098813 -0.366666704 -0.0751263425 -0.964696229 -0.258005142 -0.052862674 0.08988 1 +-1.32694221 -0.433333367 -0.100556247 -0.948138833 -0.309629291 -0.0718503669 0.0979929 1 +-1.34212065 -0.466666698 -0.0251263436 -0.944383562 -0.328370154 -0.0176801588 0.0948363 1 +-1.34705985 -0.5 0.0443920307 -0.937054515 -0.347814739 0.0308804046 0.092696 1 +-1.34370303 -0.533333361 0.10912057 -0.926826298 -0.367869526 0.0752664953 0.0912482 1 +-1.33319783 -0.566666722 0.169722036 -0.914064467 -0.388516933 0.116364487 0.0903383 1 +-1.31624067 -0.600000024 0.226598591 -0.898958147 -0.409784406 0.154760942 0.0898966 1 +-1.29323542 -0.633333385 0.279983103 -0.881577611 -0.431733102 0.190859944 0.0899039 1 +-1.26436806 -0.666666687 0.329983175 -0.861899137 -0.454455823 0.224944159 0.0903733 1 +-1.2296381 -0.700000048 0.376598567 -0.839810908 -0.478081852 0.257207036 0.0913613 1 +-1.1888603 -0.733333349 0.41972211 -0.815104067 -0.502786577 0.287769049 0.0929648 1 +-1.14163041 -0.76666671 0.459120601 -0.787445664 -0.528812468 0.316680878 0.0953467 1 +-1.08725226 -0.800000072 0.494392008 -0.756324708 -0.556503654 0.343913645 0.0987829 1 +-1.02457798 -0.833333373 0.524873674 -0.720944583 -0.586375296 0.369327515 0.103741 1 +-0.951664507 -0.866666734 0.549443781 -0.679991961 -0.619258583 0.392593563 0.111114 1 +-0.96684289 -0.833333373 0.624873698 -0.680319369 -0.586375296 0.439692587 0.100777 1 +-0.971782148 -0.800000072 0.694392025 -0.676000297 -0.556503654 0.483039558 0.093238 1 +-0.968425274 -0.76666671 0.759120643 -0.667976499 -0.528812468 0.523607492 0.0874472 1 +-0.957920194 -0.733333349 0.819722116 -0.656767368 -0.502786577 0.562016249 0.0828432 1 +-0.94096297 -0.700000048 0.876598597 -0.642653227 -0.478081822 0.598694026 0.0790898 1 +-0.917957842 -0.666666687 0.929983199 -0.625756979 -0.454455853 0.633954465 0.075977 1 +-0.88909018 -0.633333385 0.979983151 -0.606078327 -0.431733102 0.668038607 0.0733684 1 +-0.854360461 -0.600000024 1.02659857 -0.583505988 -0.409784406 0.701140106 0.071171 1 +-0.81358248 -0.566666722 1.06972206 -0.557806849 -0.388516963 0.733420789 0.0693318 1 +-0.766352654 -0.533333361 1.10912061 -0.528595865 -0.367869556 0.76502192 0.0678261 1 +-0.711974502 -0.50000006 1.14439201 -0.495270461 -0.347814769 0.796072841 0.0666551 1 +-0.649300218 -0.466666669 1.17487371 -0.456880331 -0.328370154 0.82670033 0.0658711 1 +-0.426382512 -0.466666698 1.17950547 -0.31862089 -0.348723888 0.881403625 0.0770384 1 +-0.489635676 -0.400000006 1.21602464 -0.357249945 -0.291849613 0.887240827 0.0663025 1 +-0.535787344 -0.333333343 1.2426703 -0.384434313 -0.239170954 0.891631901 0.0601133 1 +-0.570076108 -0.266666681 1.26246691 -0.40412429 -0.189038768 0.894956887 0.0562319 1 +-0.595053196 -0.200000003 1.27688754 -0.418209255 -0.140561983 0.897409141 0.0537629 1 +-0.612125516 -0.133333325 1.28674424 -0.427716255 -0.0931652561 0.899098992 0.0522828 1 +-0.622092724 -0.0666666627 1.29249883 -0.433222592 -0.0464263633 0.900090396 0.0515882 1 +-0.625371933 7.4505806e-09 1.29439199 -0.435027182 5.18284393e-09 0.900417328 0.0515882 1 +-0.622092724 0.0666666776 1.29249883 -0.433222592 0.0464263745 0.900090396 0.0522828 1 +-0.612125516 0.133333355 1.28674424 -0.427716255 0.0931652784 0.899098992 0.0537629 1 +-0.595053196 0.200000018 1.27688754 -0.418209255 0.140561983 0.897409141 0.0562319 1 +-0.570076108 0.266666681 1.26246691 -0.40412429 0.189038768 0.894956887 0.0601133 1 +-0.535787344 0.333333373 1.2426703 -0.384434313 0.239170983 0.891631901 0.0663007 1 +-0.489635676 0.400000036 1.21602464 -0.357249945 0.291849643 0.887240827 0.0770384 1 +-0.426382512 0.466666698 1.17950547 -0.31862089 0.348723888 0.881403625 0.100344 1 +-0.518503189 0.50000006 1.16602468 -0.378312349 0.364812016 0.850759566 0.0895587 1 +-0.59352231 0.533333361 1.14267039 -0.425859869 0.382673532 0.819880664 0.085982 1 +-0.656678617 0.566666663 1.11246693 -0.465516388 0.401707351 0.788622618 0.0851058 1 +-0.710523248 0.600000024 1.07688749 -0.499362797 0.421685964 0.756847203 0.0857206 1 +-0.756462991 0.633333385 1.03674412 -0.528570592 0.442535073 0.72441411 0.087396 1 +-0.795297861 0.666666687 0.992498815 -0.553841889 0.464263678 0.691171706 0.0900107 1 +-0.827444553 0.700000048 0.944392025 -0.575594842 0.486940712 0.656947017 0.0936092 1 +-0.853032887 0.733333349 0.892498791 -0.594048262 0.510690033 0.621532142 0.0983699 1 +-0.871933043 0.76666671 0.83674413 -0.609254062 0.535700321 0.584666193 0.104667 1 +-0.883728266 0.800000072 0.776887417 -0.621092975 0.562247992 0.546004176 0.113183 1 +-0.887618601 0.833333373 0.712466896 -0.629228711 0.590746164 0.505064487 0.12526 1 +-0.882197499 0.866666734 0.642670333 -0.632988036 0.62184453 0.461124212 0.143925 1 +-0.864913344 0.900000036 0.566024661 -0.631061554 0.65666163 0.412985206 0.178323 1 +-0.830527723 0.933333397 0.47950542 -0.620624602 0.697447777 0.358317822 0.118789 1 +-0.92264843 0.900000036 0.466024637 -0.673186421 0.65666163 0.340022743 0.183761 1 +-0.997667551 0.866666734 0.442670316 -0.715839267 0.62184453 0.317621648 0.152746 1 +-1.06082368 0.833333373 0.412466854 -0.752013028 0.590746164 0.29239586 0.136883 1 +-1.11466837 0.800000072 0.376887411 -0.783400059 0.562248051 0.26488024 0.127357 1 +-1.16060817 0.76666671 0.3367441 -0.810962796 0.535700321 0.235296413 0.121288 1 +-1.1994431 0.733333349 0.292498767 -0.835286796 0.510690033 0.203694835 0.117425 1 +-1.23158979 0.700000048 0.244391993 -0.856730163 0.486940682 0.170006275 0.115158 1 +-1.25717807 0.666666687 0.192498773 -0.875493169 0.464263678 0.134055287 0.11419 1 +-1.27607822 0.633333385 0.136744097 -0.891646326 0.442535073 0.0955485031 0.114422 1 +-1.28787363 0.600000024 0.0768874511 -0.905130267 0.421685934 0.054037258 0.115938 1 +-1.29176378 0.566666722 0.0124668283 -0.91572541 0.401707411 0.00883767754 0.119067 1 +-1.28634274 0.533333361 -0.0573297217 -0.922967434 0.382673532 -0.0411348119 0.124635 1 +-1.26905859 0.5 -0.133975372 -0.925935626 0.364812016 -0.0977516547 0.13482 1 +-1.2346729 0.466666698 -0.220494613 -0.922628343 0.348723888 -0.164767995 0.157429 1 +-1.29792607 0.400000006 -0.183975384 -0.94699806 0.291849613 -0.134232864 0.120117 1 +-1.34407771 0.333333343 -0.157329723 -0.96439302 0.239170954 -0.112886101 0.102962 1 +-1.37836647 0.266666681 -0.137533113 -0.977117538 0.189038768 -0.0974965841 0.0930835 1 +-1.40334368 0.200000003 -0.123112537 -0.986283779 0.140561983 -0.0865247026 0.0868948 1 +-1.420416 0.133333325 -0.113255836 -0.992500663 0.0931652486 -0.0791363195 0.0829626 1 +-1.43038321 0.0666666627 -0.107501246 -0.996112466 0.0464263633 -0.0748633891 0.080606 1 +-1.43366241 -7.4505806e-09 -0.105608024 -0.997297823 -5.18284349e-09 -0.0734640509 0.0794984 1 +-1.43038321 -0.0666666776 -0.107501246 -0.996112466 -0.0464263745 -0.0748633891 0.0794984 1 +-1.420416 -0.133333355 -0.113255836 -0.992500663 -0.093165271 -0.0791363195 0.080606 1 +-1.40334368 -0.200000018 -0.123112537 -0.986283779 -0.140561983 -0.0865247026 0.0829619 1 +-1.37836647 -0.266666681 -0.137533113 -0.977117538 -0.189038768 -0.0974965841 0.0868948 1 +-1.34407771 -0.333333373 -0.157329723 -0.96439302 -0.239170983 -0.112886101 0.0930835 1 +-1.29792607 -0.400000036 -0.183975384 -0.94699806 -0.291849643 -0.134232864 0.102961 1 +-1.2346729 -0.466666698 -0.220494613 -0.922628343 -0.348723888 -0.164767995 0.120117 1 +-1.26905859 -0.50000006 -0.133975372 -0.925935626 -0.364812046 -0.0977516547 0.110987 1 +-1.28634274 -0.533333361 -0.0573297217 -0.922967434 -0.382673532 -0.0411348119 0.105888 1 +-1.29176402 -0.566666663 0.0124668879 -0.91572541 -0.401707321 0.00883771759 0.102739 1 +-1.28787363 -0.600000024 0.0768874511 -0.905130267 -0.421685934 0.054037258 0.100786 1 +-1.27607822 -0.633333385 0.136744097 -0.891646326 -0.442535073 0.0955485031 0.0997069 1 +-1.25717807 -0.666666687 0.192498773 -0.875493169 -0.464263678 0.134055287 0.0993554 1 +-1.23158979 -0.700000048 0.244391993 -0.856730163 -0.486940682 0.170006275 0.0996925 1 +-1.1994431 -0.733333349 0.292498767 -0.835286796 -0.510690033 0.203694835 0.100755 1 +-1.16060817 -0.76666671 0.3367441 -0.810962796 -0.535700321 0.235296413 0.10265 1 +-1.11466837 -0.800000072 0.376887411 -0.783400059 -0.562248051 0.26488024 0.105592 1 +-1.06082368 -0.833333373 0.412466854 -0.752013028 -0.590746164 0.29239586 0.109959 1 +-0.997667551 -0.866666734 0.442670316 -0.715839267 -0.62184453 0.317621648 0.116452 1 +-0.92264843 -0.900000036 0.466024637 -0.673186421 -0.65666163 0.340022743 0.126463 1 +-0.830527723 -0.933333397 0.47950542 -0.620624602 -0.697447777 0.358317822 0.143336 1 +-0.864913344 -0.900000036 0.566024661 -0.631061554 -0.65666163 0.412985206 0.1228 1 +-0.882197499 -0.866666734 0.642670333 -0.632988036 -0.62184453 0.461124212 0.109837 1 +-0.887618601 -0.833333373 0.712466896 -0.629228711 -0.590746164 0.505064487 0.100753 1 +-0.883728266 -0.800000072 0.776887417 -0.621092975 -0.562247992 0.546004176 0.0939859 1 +-0.871933043 -0.76666671 0.83674413 -0.609254062 -0.535700321 0.584666193 0.0887412 1 +-0.853032887 -0.733333349 0.892498791 -0.594048262 -0.510690033 0.621532142 0.084574 1 +-0.827444553 -0.700000048 0.944392025 -0.575594842 -0.486940712 0.656947017 0.0812173 1 +-0.795297861 -0.666666687 0.992498815 -0.553841889 -0.464263678 0.691171706 0.078509 1 +-0.756462991 -0.633333385 1.03674412 -0.528570592 -0.442535073 0.72441411 0.0763616 1 +-0.710523248 -0.600000024 1.07688749 -0.499362797 -0.421685964 0.756847203 0.0747428 1 +-0.656678498 -0.566666722 1.11246693 -0.465516359 -0.401707411 0.788622677 0.0736865 1 +-0.59352231 -0.533333361 1.14267039 -0.425859869 -0.382673532 0.819880664 0.0733359 1 +-0.518503189 -0.5 1.16602468 -0.378312379 -0.364812016 0.850759625 0.0740765 1 +-0.302626967 -0.433333397 1.17472184 -0.234931335 -0.336399615 0.911944449 0.0870656 1 +-0.380994439 -0.366666704 1.21996725 -0.286541253 -0.275765538 0.91752243 0.0692784 1 +-0.433012545 -0.300000042 1.25 -0.319221079 -0.221162975 0.921512246 0.0607881 1 +-0.469573259 -0.233333379 1.27110827 -0.341504693 -0.169695452 0.924433947 0.05598 1 +-0.494850755 -0.166666701 1.28570223 -0.356600195 -0.120103642 0.92650491 0.0531639 1 +-0.510866523 -0.100000031 1.29494894 -0.366038412 -0.07165052 0.927837372 0.0516656 1 +-0.518651783 -0.0333333611 1.29944384 -0.370591789 -0.0238176566 0.92849046 0.0511937 1 +-0.518651783 0.033333309 1.29944384 -0.370591789 0.0238176193 0.92849046 0.0516656 1 +-0.510866523 0.0999999791 1.29494894 -0.366038412 0.0716504827 0.927837372 0.0531662 1 +-0.494850755 0.166666657 1.28570223 -0.356600195 0.120103613 0.92650491 0.055981 1 +-0.469573259 0.233333319 1.27110827 -0.341504693 0.169695407 0.924433947 0.0607881 1 +-0.433012545 0.299999982 1.25 -0.319221079 0.22116293 0.921512246 0.0692784 1 +-0.380994439 0.366666675 1.21996725 -0.286541253 0.275765508 0.91752243 0.0870642 1 +-0.302627087 0.433333337 1.17472184 -0.234931409 0.336399555 0.91194433 0.0870642 1 +-0.3314946 0.533333361 1.12472188 -0.257341444 0.414030224 0.87312907 0.100344 1 +-0.438729405 0.566666722 1.11996722 -0.329963058 0.426183134 0.842313707 0.131075 1 +-0.519615054 0.600000024 1.10000002 -0.383065313 0.44232589 0.810930789 0.11316 1 +-0.585043311 0.633333325 1.07110834 -0.425482094 0.460601777 0.778980613 0.107453 1 +-0.63918829 0.666666687 1.03570223 -0.460612953 0.48041448 0.746349514 0.10624 1 +-0.6840716 0.700000048 0.994948983 -0.490140676 0.501553476 0.712885857 0.10764 1 +-0.720724523 0.733333349 0.949443877 -0.514978588 0.523987949 0.678405225 0.111132 1 +-0.749591887 0.76666671 0.899443805 -0.535605192 0.547805667 0.642678797 0.116796 1 +-0.770674169 0.800000072 0.844948947 -0.552191854 0.573203981 0.605410099 0.125227 1 +-0.783525884 0.833333373 0.785702229 -0.56462574 0.600518107 0.566194057 0.137869 1 +-0.787115932 0.866666734 0.721108258 -0.57244271 0.630297303 0.524437547 0.158262 1 +-0.7794227 0.900000036 0.649999976 -0.574598074 0.663488865 0.479186356 0.125665 1 +-0.756272137 0.933333397 0.56996727 -0.568782985 0.701948583 0.42866537 0.0785295 1 +-0.706772327 0.966666698 0.474721909 -0.54867202 0.750429749 0.368529767 0.100757 1 +-0.764507413 0.966666698 0.374721885 -0.59349215 0.75042969 0.290899068 0.118599 1 +-0.871742189 0.933333397 0.369967252 -0.655626655 0.701948643 0.278247833 0.107222 1 +-0.952627838 0.900000036 0.349999934 -0.702286601 0.663488865 0.258023381 0.0860174 1 +-1.01805604 0.866666734 0.321108252 -0.740397513 0.630297184 0.233531103 0.178503 1 +-1.07220101 0.833333373 0.285702199 -0.772651315 0.600518107 0.205883205 0.160172 1 +-1.11708438 0.800000012 0.244948924 -0.800396562 0.573203981 0.175507128 0.149904 1 +-1.15373707 0.76666671 0.199443772 -0.824378729 0.547805667 0.142508373 0.144125 1 +-1.18260479 0.733333349 0.149443835 -0.845005453 0.523987949 0.10678196 0.14146 1 +-1.20368695 0.700000048 0.0949489027 -0.862447619 0.501553476 0.0680313557 0.141455 1 +-1.21653867 0.666666687 0.035702195 -0.876664162 0.48041448 0.0257277768 0.144298 1 +-1.22012866 0.633333385 -0.0288917627 -0.887358189 0.460601896 -0.0210119989 0.151062 1 +-1.21243548 0.600000024 -0.100000091 -0.893819332 0.44232589 -0.0737210438 0.164999 1 +-1.1892848 0.566666663 -0.180032775 -0.894446611 0.426183105 -0.135400459 0.10106 1 +-1.13978505 0.533333361 -0.275278181 -0.884822667 0.414030224 -0.21370028 0.131894 1 +-1.16865242 0.433333397 -0.325278252 -0.907232642 0.336399585 -0.252515674 0.154905 1 +-1.24701989 0.366666704 -0.280032784 -0.937868416 0.275765538 -0.210609227 0.141906 1 +-1.29903793 0.300000042 -0.250000089 -0.957663596 0.22116299 -0.184302524 0.112296 1 +-1.33559871 0.233333379 -0.22889176 -0.971335649 0.169695452 -0.166465208 0.098187 1 +-1.3608762 0.166666701 -0.214297801 -0.980676949 0.120103642 -0.154427648 0.0902054 1 +-1.37689197 0.100000031 -0.205051094 -0.986549973 0.07165052 -0.14692013 0.0855387 1 +-1.38467717 0.0333333611 -0.200556248 -0.989392221 0.0238176584 -0.14330329 0.0830518 1 +-1.38467717 -0.033333309 -0.200556248 -0.989392221 -0.0238176212 -0.14330329 0.0822671 1 +-1.37689197 -0.0999999791 -0.205051094 -0.986549973 -0.0716504827 -0.14692013 0.0830518 1 +-1.3608762 -0.166666657 -0.214297801 -0.980676949 -0.120103613 -0.154427648 0.0855387 1 +-1.33559871 -0.233333319 -0.22889176 -0.971335649 -0.169695407 -0.166465208 0.0902061 1 +-1.29903793 -0.299999982 -0.250000089 -0.957663596 -0.221162945 -0.184302524 0.0981876 1 +-1.24701989 -0.366666675 -0.280032784 -0.937868416 -0.275765508 -0.210609227 0.112297 1 +-1.16865253 -0.433333337 -0.325278193 -0.907232702 -0.336399555 -0.252515614 0.141907 1 +-1.13978505 -0.533333361 -0.275278181 -0.884822667 -0.414030224 -0.21370028 0.157429 1 +-1.1892848 -0.566666722 -0.180032805 -0.894446552 -0.426183105 -0.135400474 0.13482 1 +-1.21243548 -0.600000024 -0.100000091 -0.893819332 -0.44232589 -0.0737210438 0.124635 1 +-1.22012866 -0.633333325 -0.0288917627 -0.887358189 -0.460601836 -0.0210119989 0.119068 1 +-1.21653867 -0.666666687 0.035702195 -0.876664162 -0.48041448 0.0257277768 0.115938 1 +-1.20368695 -0.700000048 0.0949489027 -0.862447619 -0.501553476 0.0680313557 0.114422 1 +-1.18260479 -0.733333349 0.149443835 -0.845005453 -0.523987949 0.10678196 0.11419 1 +-1.15373707 -0.76666671 0.199443772 -0.824378729 -0.547805667 0.142508373 0.115158 1 +-1.11708438 -0.800000072 0.244948924 -0.800396442 -0.573203981 0.175507113 0.117426 1 +-1.07220101 -0.833333373 0.285702199 -0.772651315 -0.600518107 0.205883205 0.121288 1 +-1.01805604 -0.866666734 0.321108252 -0.740397513 -0.630297184 0.233531103 0.127357 1 +-0.952627838 -0.900000036 0.349999934 -0.702286601 -0.663488865 0.258023381 0.136883 1 +-0.871742189 -0.933333397 0.369967252 -0.655626655 -0.701948643 0.278247833 0.152746 1 +-0.764507413 -0.966666698 0.374721885 -0.59349215 -0.75042969 0.290899068 0.183761 1 +-0.706772327 -0.966666698 0.474721909 -0.54867202 -0.750429749 0.368529767 0.178323 1 +-0.756272137 -0.933333397 0.56996727 -0.568782985 -0.701948583 0.42866537 0.143925 1 +-0.7794227 -0.900000036 0.649999976 -0.574598074 -0.663488865 0.479186356 0.12526 1 +-0.787115932 -0.866666734 0.721108258 -0.57244271 -0.630297303 0.524437547 0.113183 1 +-0.783525884 -0.833333373 0.785702229 -0.56462574 -0.600518107 0.566194057 0.104667 1 +-0.770674169 -0.800000012 0.844948947 -0.552191913 -0.573203981 0.605410159 0.0983705 1 +-0.749591887 -0.76666671 0.899443805 -0.535605192 -0.547805667 0.642678797 0.0936092 1 +-0.720724523 -0.733333349 0.949443877 -0.514978588 -0.523987949 0.678405225 0.0900107 1 +-0.6840716 -0.700000048 0.994948983 -0.490140676 -0.501553476 0.712885857 0.087396 1 +-0.63918829 -0.666666687 1.03570223 -0.460612953 -0.48041448 0.746349514 0.0857206 1 +-0.585043311 -0.633333385 1.07110834 -0.425482094 -0.460601807 0.778980613 0.0851079 1 +-0.519615054 -0.600000024 1.10000002 -0.383065313 -0.44232589 0.810930789 0.085982 1 +-0.438729465 -0.566666663 1.11996722 -0.329963088 -0.426183105 0.842313707 0.0895593 1 +-0.3314946 -0.533333361 1.12472188 -0.257341444 -0.414030224 0.87312907 0.100344 1 +-0.221034139 -0.333333343 1.19428086 -0.175497234 -0.264660835 0.948238015 0.0838683 1 +-0.295782417 -0.266666681 1.23743677 -0.227535158 -0.205137432 0.951917231 0.0646989 1 +-0.340577573 -0.200000018 1.26329923 -0.257311463 -0.15110302 0.954441547 0.0567439 1 +-0.368647486 -0.13333334 1.27950549 -0.275477499 -0.0996353924 0.956130207 0.0528297 1 +-0.384333581 -0.0666666701 1.28856182 -0.285472006 -0.0495180972 0.957106948 0.0511541 1 +-0.389397413 0 1.29148543 -0.28867507 0 0.957427144 0.0511541 1 +-0.384333581 0.0666666701 1.28856182 -0.285472006 0.0495180972 0.957106948 0.0528297 1 +-0.368647486 0.13333334 1.27950549 -0.275477499 0.0996353924 0.956130207 0.0567439 1 +-0.340577573 0.200000018 1.26329923 -0.257311463 0.15110302 0.954441547 0.0646989 1 +-0.295782417 0.266666681 1.23743677 -0.227535158 0.205137432 0.951917231 0.0646989 1 +-0.307636559 0.633333385 1.04428077 -0.244258046 0.502855599 0.829140663 0.131075 1 +-0.411252409 0.666666746 1.03743672 -0.316362232 0.512843609 0.798064113 0.0869064 1 +-0.484915137 0.700000048 1.01329923 -0.366360664 0.528860569 0.765562832 0.0674 1 +-0.541852713 0.733333349 0.979505539 -0.404907763 0.547994614 0.731950521 0.0594388 1 +-0.586406112 0.76666671 0.938561797 -0.43556571 0.569458127 0.697136879 0.0556884 1 +-0.620337486 0.800000072 0.891485393 -0.459879726 0.593070388 0.66089195 0.0543191 1 +-0.644141138 0.833333373 0.838561773 -0.478449672 0.618976295 0.622859776 0.0548032 1 +-0.657322645 0.866666734 0.779505432 -0.491194457 0.647630036 0.582497418 0.0572066 1 +-0.658120215 0.900000036 0.713299274 -0.497219712 0.679963589 0.538908303 0.0622539 1 +-0.642192602 0.933333397 0.637436748 -0.494016469 0.717980981 0.490357965 0.0721393 1 +-0.873132706 0.933333397 0.237436756 -0.671670675 0.717980981 0.182651863 0.137311 1 +-0.946795404 0.900000036 0.21329923 -0.715318143 0.679963589 0.161150783 0.0838292 1 +-1.00373292 0.866666675 0.179505467 -0.750054836 0.647629976 0.134138212 0.0740717 1 +-1.04828632 0.833333373 0.138561741 -0.77863723 0.618976295 0.102919713 0.0695783 1 +-1.08221769 0.800000072 0.0914853439 -0.802289069 0.593070388 0.0678215548 0.0681096 1 +-1.1060214 0.76666671 0.038561713 -0.821521163 0.569458187 0.0286425408 0.0690429 1 +-1.11920297 0.733333349 -0.0204945542 -0.836341679 0.547994614 -0.0153148714 0.0725376 1 +-1.12000048 0.700000048 -0.0867007896 -0.84617722 0.528860569 -0.0655037537 0.0796273 1 +-1.10407281 0.666666687 -0.162563264 -0.849324942 0.512843609 -0.125054285 0.0933819 1 +-1.21954286 0.266666681 -0.362563252 -0.938152075 0.205137432 -0.278907359 0.175696 1 +-1.26433802 0.200000018 -0.336700767 -0.955226362 0.15110302 -0.254382491 0.109631 1 +-1.29240799 0.13333334 -0.320494592 -0.965771794 0.0996353924 -0.239494517 0.0958525 1 +-1.30809402 0.0666666701 -0.311438233 -0.971614897 0.0495180972 -0.231327429 0.0890802 1 +-1.31315792 0 -0.308514625 -0.973493814 0 -0.228713602 0.0861824 1 +-1.30809402 -0.0666666701 -0.311438233 -0.971614897 -0.0495180972 -0.231327429 0.0861824 1 +-1.29240799 -0.13333334 -0.320494592 -0.965771794 -0.0996353924 -0.239494517 0.0890802 1 +-1.26433802 -0.200000018 -0.336700767 -0.955226362 -0.15110302 -0.254382491 0.0958525 1 +-1.21954286 -0.266666681 -0.362563252 -0.938152075 -0.205137432 -0.278907359 0.109631 1 +-1.14479458 -0.333333343 -0.405719191 -0.908946812 -0.264660835 -0.322133929 0.142895 1 +-1.05819201 -0.633333385 -0.255719304 -0.84018594 -0.502855599 -0.203036651 0.198882 1 +-1.10407281 -0.666666746 -0.162563324 -0.849324942 -0.512843609 -0.12505433 0.164999 1 +-1.12000048 -0.700000048 -0.0867007896 -0.84617722 -0.528860569 -0.0655037537 0.151062 1 +-1.11920297 -0.733333349 -0.0204945542 -0.836341679 -0.547994614 -0.0153148714 0.144298 1 +-1.1060214 -0.76666671 0.038561713 -0.821521163 -0.569458187 0.0286425408 0.141455 1 +-1.08221769 -0.800000072 0.0914853439 -0.802289069 -0.593070388 0.0678215548 0.14146 1 +-1.04828632 -0.833333373 0.138561741 -0.77863723 -0.618976295 0.102919713 0.144125 1 +-1.0037328 -0.866666734 0.179505408 -0.750054777 -0.647630036 0.134138182 0.149905 1 +-0.946795404 -0.900000036 0.21329923 -0.715318143 -0.679963589 0.161150783 0.160172 1 +-0.873132706 -0.933333397 0.237436756 -0.671670675 -0.717980981 0.182651863 0.178503 1 +-0.769516945 -0.966666698 0.244280815 -0.610982955 -0.767516375 0.193954676 0.217237 1 +-0.596311808 -0.966666698 0.544280827 -0.473461121 -0.767516375 0.43214941 0.198343 1 +-0.642192602 -0.933333397 0.637436748 -0.494016469 -0.717980981 0.490357965 0.158262 1 +-0.658120215 -0.900000036 0.713299274 -0.497219712 -0.679963589 0.538908303 0.137869 1 +-0.657322764 -0.866666675 0.779505491 -0.491194516 -0.647629976 0.582497418 0.125227 1 +-0.644141138 -0.833333373 0.838561773 -0.478449672 -0.618976295 0.622859776 0.116796 1 +-0.620337486 -0.800000072 0.891485393 -0.459879726 -0.593070388 0.66089195 0.111132 1 +-0.586406112 -0.76666671 0.938561797 -0.43556571 -0.569458127 0.697136879 0.10764 1 +-0.541852713 -0.733333349 0.979505539 -0.404907763 -0.547994614 0.731950521 0.10624 1 +-0.484915137 -0.700000048 1.01329923 -0.366360664 -0.528860569 0.765562832 0.107453 1 +-0.411252469 -0.666666687 1.03743684 -0.316362262 -0.512843549 0.798064172 0.11316 1 +-0.307636678 -0.633333325 1.04428089 -0.244258121 -0.502855539 0.829140663 0.131075 1 +-0.942721903 -0.833333373 -0.0557193048 -0.748504698 -0.661652148 -0.0442401543 0.219203 1 +-0.913854361 -0.866666734 -0.00571927987 -0.725584388 -0.688118219 -0.00454100827 0.220669 1 +-0.451974154 -0.866666734 0.794280767 -0.358859569 -0.688118219 0.630645037 0.172366 1 +-0.423106611 -0.833333373 0.844280779 -0.335939258 -0.661652148 0.670344174 0.165746 1 diff --git a/examples/model_crawler.cpp b/examples/model_crawler.cpp new file mode 100644 index 00000000..3f282591 --- /dev/null +++ b/examples/model_crawler.cpp @@ -0,0 +1,134 @@ +/* + * Move one model (mplot::CoordArrows) around another (mplot::GeodesicVisual), as if it were + * crawling over it. Demonstrates mplot::NavMesh, which allows you to move over a triangular + * landscape model, following the exact contour defined by the landscape's mesh. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +int main (int argc, char** argv) +{ + int rtn = -1; + + mplot::Visual v(1024, 768, "Crawling a surface with NavMesh features"); + v.rotateAboutNearest (true); + + // How big to make the sphere? + constexpr float radius = 2.0f; + // How many iterations for the geodesic? Try 2, 3 and 4 + int geo_itrns = 4; + if (argc > 1) { + geo_itrns = std::atoi (argv[1]); + if (geo_itrns > 6) { + std::cout << "Warning: GeodesicVisual takes a long time to build for iterations > 6!" << std::endl; + } + } + // How high to hover the arrows + constexpr float hoverheight = 0.05f; + // Model locations within the scene + //sm::vec arrows_loc = { 0.01f, radius + 1.5f * hoverheight, 0.2f }; + sm::vec arrows_loc = { 0.0f, radius + 1.5f * hoverheight, 0.0f }; + sm::vec sphere_loc = {}; + + // A CoordArrows is our "crawling" agent + auto ca = std::make_unique> (arrows_loc); + v.bindmodel (ca); + ca->finalize(); + [[maybe_unused]] auto cap = v.addVisualModel (ca); + + // A ScatterVisual will show the agent's trail + sm::vvec> sv_points; + sm::vvec sv_data; + auto sv = std::make_unique> (sphere_loc); + v.bindmodel (sv); + sv->setDataCoords (&sv_points); + sv->setScalarData (&sv_data); + sv->radiusFixed = 0.015f; + sv->cm.setType (mplot::ColourMapType::Plasma); + sv->colourScale.compute_scaling (0.0f, 1.0f); + sv->finalize(); + [[maybe_unused]] auto svp = v.addVisualModel (sv); // use svp->add (coord, value) + + // A sphere, approximated by an icosahedral geodesic, is our landscape + mplot::ColourMap cm (mplot::ColourMapType::Jet); + auto cl = cm.convert (0.5f); + auto gv = std::make_unique> (sphere_loc, radius); + v.bindmodel (gv); + gv->iterations = geo_itrns; + std::string lbl = "GeodesicVisual with computed NavMesh"; + gv->addLabel (lbl, {0, -(radius + 0.1f), 0}, mplot::TextFeatures (0.06f)); + gv->cm.setType (mplot::ColourMapType::Jet); + gv->colour_bb = cl; + gv->finalize(); + auto gvp = v.addVisualModel (gv); + // re-colour sphere with sequential colouring after construction + gvp->data.linspace (0.0f, 1.0f, gvp->data.size()); + gvp->reinitColours(); + // Make the navmesh for the geodesic, this doesn't occur automatically and has to come after finalize() + gvp->make_navmesh(); + + // We're going to move the coordinate arrows forwards (along its z-axis), so that it 'orbits' + float move_step = 0.1f; // 0.075 <= move_step and iterations 6 to fail + sm::vec mv_ca = sm::vec::uz() * move_step; + + // The viewmatrices have to be passed to mplot::NavMesh::compute_mesh_movement + sm::mat44 ca_view = cap->getViewMatrix(); + sm::mat44 sph_view = gvp->getViewMatrix(); + + // Find the triangle that we're initially located above with + // mplot::NavMesh::find_triangle_hit. This updates internal state in NavMesh. It could be + // executed automatically in compute_mesh_movement + auto[hp_scene, tn0, ti0] = gvp->navmesh->find_triangle_hit (ca_view, sph_view); + std::cout << "Find hit finds hit point " << hp_scene << std::endl; + + int move_counter = 0; + constexpr int move_max = 1000; + while (!v.readyToFinish()) { + + // Wait .018 s and also poll for mouse/keyboard events + v.waitevents (0.018); + + // Compute a new movement over the landscape mesh (the sphere) + try { + ca_view = gvp->navmesh->compute_mesh_movement (mv_ca, ca_view, sph_view, hoverheight); + } catch (mplot::NavException& e) { + if (e.m_type == mplot::NavException::type::off_edge) { + std::cout << "You can handle movements that go off the edge of a flat model\n"; + } + std::cout << "Exception navigating mesh at movement count " << move_counter << ": " << e.what() << std::endl; + throw e; + } + + // Update the viewmatrix of the coord arrows, setting its position within the scene + cap->setViewMatrix (ca_view); + + // Compute the new location + arrows_loc = (ca_view * sm::vec{}).less_one_dim(); + + // We're adding and rebuilding the not-very-optimized ScatterVisual, so if move_max is too + // high, the program will slow down (too many tiny spheres!) + if (move_counter++ < move_max) { + svp->add (arrows_loc, static_cast(move_counter) / move_max); + } + + // Re-render the scene + v.render(); + } + + v.keepOpen(); + + return rtn; +} diff --git a/examples/rod_with_normals.cpp b/examples/rod_with_normals.cpp index aff1d57b..b52bda30 100644 --- a/examples/rod_with_normals.cpp +++ b/examples/rod_with_normals.cpp @@ -38,7 +38,6 @@ int main() rvm->use_oriented_tube = false; rvm->finalize(); auto rvmp1 = v.addVisualModel (rvm); - rvmp1->vertex_postprocess(); auto nrm = std::make_unique> (rvmp1); v.bindmodel (nrm); @@ -53,7 +52,6 @@ int main() v.bindmodel (rvm); rvm->finalize(); auto rvmp2 = v.addVisualModel (rvm); - rvmp2->vertex_postprocess(); // Create an associate normals model nrm = std::make_unique> (rvmp2); diff --git a/examples/testce.cpp b/examples/testce.cpp index 6d2fb40a..d52d9883 100644 --- a/examples/testce.cpp +++ b/examples/testce.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include int main() diff --git a/examples/triangle_intersect.cpp b/examples/triangle_intersect.cpp new file mode 100644 index 00000000..f99165c5 --- /dev/null +++ b/examples/triangle_intersect.cpp @@ -0,0 +1,89 @@ +// Utility to debug some triangle intersection problems + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main (int argc, char** argv) +{ + if (argc < 2) { + std::cout << "Usage: " << argv[0] << " '{ \"json\" : \"string\" }'\n\n" + << "An example:\n " << argv[0] << " '{ \"t0\" : [-0.76,0.99,-0.87], \"t1\" : [-0.04,0.64,-1.4], \"t2\" : [-0.94,0.55,-1], \"l0\" : [-0.43,0.62,-1.24], \"l\" : [0.43,-0.43,0.8] }'\n\n" + << "where t1, t2 and t3 define triangle vertices and l0, l define ray start and direction\n\n" + << "This program can be used to visually debug sm::algo::ray_tri_intersection\n"; + + return -1; + } + + std::string jstr = std::string (argv[1]); + std::cout << "json arg: " << jstr << std::endl; + + // {"t0" : [-0.760357976,0.990107834,-0.865470469],"t1" : [-0.0382558256,0.637388527,-1.44368529],"t2" : [-0.941664934,0.551642597,-1.00695968],"l0" : [-0.428883553,0.622524023,-1.24276352],"l" : [0.425372064,-0.432439089,0.795018792]} + + sm::config conf; + conf.parse (jstr); + std::cout << "Got t0: " << conf.getvec ("t0") << std::endl; + + mplot::Visual v(1024, 768, "Ray-triangle intersection"); + v.lightingEffects(); + + // Triangle vertices + sm::vec<> v1 = conf.getvec ("t0"); + sm::vec<> v2 = conf.getvec ("t1"); + sm::vec<> v3 = conf.getvec ("t2"); + // Ray start and direction + sm::vec<> start = conf.getvec ("l0"); + sm::vec<> dirn = conf.getvec ("l"); + + auto tv = std::make_unique> (sm::vec<>{}, v1, v2, v3, mplot::colour::blue); + v.bindmodel (tv); + tv->finalize(); + [[maybe_unused]] auto tvp = v.addVisualModel (tv); + + float start_sphr = dirn.length() / 20.0f; + auto sv = std::make_unique>(start, start_sphr, mplot::colour::goldenrod3); + v.bindmodel (sv); + sv->finalize(); + v.addVisualModel (sv); + + auto vvm = std::make_unique>(start); + v.bindmodel (vvm); + vvm->thevec = dirn; + vvm->vgoes = mplot::VectorGoes::FromOrigin; + vvm->thickness = 0.02f; + vvm->arrowhead_prop = 0.1f; + vvm->fixed_colour = true; + vvm->single_colour = mplot::colour::crimson; + vvm->finalize(); + [[maybe_unused]] auto vvmp = v.addVisualModel (vvm); + + tvp->make_navmesh(); + + auto vm = tvp->getViewMatrix(); + auto vmi = vm.inverse(); + + auto start_wr = (vmi * start).less_one_dim(); // wr to tvp + std::cout << "start_wr = " << start_wr << std::endl; + auto [hit, ti, tn] = tvp->navmesh->find_triangle_crossing (start_wr, dirn); + if (ti[0] == std::numeric_limits::max()) { + std::cout << "NO HIT\n"; + } else { + std::cout << "Indices: " << ti[0] << "," << ti[1] << "," << ti[2] << std::endl; + std::cout << "Contains hit " << hit << std::endl; + + sv = std::make_unique>(hit, start_sphr * 1.1f, mplot::colour::springgreen2); + v.bindmodel (sv); + sv->finalize(); + v.addVisualModel (sv); + } + + v.keepOpen(); + return 0; +} diff --git a/maths b/maths index 01c1a19b..7d3174d9 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 01c1a19bec5f580cd86099584a12ec7a4032b71b +Subproject commit 7d3174d91fe1b93f76dc284d3bba9e29b312bde6 diff --git a/mplot/CMakeLists.txt b/mplot/CMakeLists.txt index c5388ab1..3c149f64 100644 --- a/mplot/CMakeLists.txt +++ b/mplot/CMakeLists.txt @@ -23,6 +23,7 @@ install( DatasetStyle.h graphing.h graphstyles.h + NavMesh.h TextFeatures.h TextGeometry.h @@ -90,6 +91,7 @@ install( RodVisual.h ScatterVisual.h SphereVisual.h + SphericalProjectionVisual.h TriangleVisual.h TriaxesVisual.h TriFrameVisual.h diff --git a/mplot/GratingVisual.h b/mplot/GratingVisual.h index c28a4526..46704287 100644 --- a/mplot/GratingVisual.h +++ b/mplot/GratingVisual.h @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include @@ -162,16 +162,16 @@ namespace mplot { } if (_bi.test(0)) { // bottom - fp = sm::algo::crossing_point (_p, _q, bot_p, bot_q); + fp = sm::geometry::crossing_point (_p, _q, bot_p, bot_q); fp_id = border_id::bottom; if (_ti.test(0)) { // bottom and top edges - fq = sm::algo::crossing_point (_p, _q, top_p, top_q); + fq = sm::geometry::crossing_point (_p, _q, top_p, top_q); fq_id = border_id::top; } else if (_li.test(0)) { // bottom and left edges - fq = sm::algo::crossing_point (_p, _q, left_p, left_q); + fq = sm::geometry::crossing_point (_p, _q, left_p, left_q); fq_id = border_id::left; // What if it ALSO intersects with the right edge? And the bottom/left @@ -180,14 +180,14 @@ namespace mplot { // There's a third intersection. Was B==L? if ((fp-fq).length() < thresh) { // If so, replace L with T: - fq = sm::algo::crossing_point (_p, _q, top_p, top_q); + fq = sm::geometry::crossing_point (_p, _q, top_p, top_q); fq_id = border_id::top; } } else if (_ri.test(0)) { // There's a third intersection. Was B==L? if ((fp-fq).length() < thresh) { // If so, replace L with R: - fq = sm::algo::crossing_point (_p, _q, right_p, right_q); + fq = sm::geometry::crossing_point (_p, _q, right_p, right_q); fq_id = border_id::right; } } @@ -195,14 +195,14 @@ namespace mplot { } else if (_ri.test(0)) { // bottom and right edges - fq = sm::algo::crossing_point (_p, _q, right_p, right_q); + fq = sm::geometry::crossing_point (_p, _q, right_p, right_q); fq_id = border_id::right; if (_ti.test(0)) { // There's a third intersection. Was B==R? if ((fp-fq).length() < thresh) { // If so, replace R with T: - fq = sm::algo::crossing_point (_p, _q, top_p, top_q); + fq = sm::geometry::crossing_point (_p, _q, top_p, top_q); fq_id = border_id::top; } } @@ -220,12 +220,12 @@ namespace mplot { } else if (_ti.test(0)) { - fp = sm::algo::crossing_point (_p, _q, top_p, top_q); + fp = sm::geometry::crossing_point (_p, _q, top_p, top_q); fp_id = border_id::top; if (_li.test(0)) { // top and left - fq = sm::algo::crossing_point (_p, _q, left_p, left_q); + fq = sm::geometry::crossing_point (_p, _q, left_p, left_q); fq_id = border_id::left; // Third intersection tests @@ -233,35 +233,35 @@ namespace mplot { // There's a third intersection. Was T==L? if ((fp-fq).length() < thresh) { // If so, replace L with B: - fq = sm::algo::crossing_point (_p, _q, bot_p, bot_q); + fq = sm::geometry::crossing_point (_p, _q, bot_p, bot_q); fq_id = border_id::bottom; } } else if (_ri.test(0)) { // There's a third intersection. Was T==L? if ((fp-fq).length() < thresh) { // If so, replace L with R: - fq = sm::algo::crossing_point (_p, _q, right_p, right_q); + fq = sm::geometry::crossing_point (_p, _q, right_p, right_q); fq_id = border_id::right; } } } else if (_ri.test(0)) { // top and right - fq = sm::algo::crossing_point (_p, _q, right_p, right_q); + fq = sm::geometry::crossing_point (_p, _q, right_p, right_q); fq_id = border_id::right; if (_bi.test(0)) { // There's a third intersection. Was T==R? if ((fp-fq).length() < thresh) { // If so, replace R with B: - fq = sm::algo::crossing_point (_p, _q, bot_p, bot_q); + fq = sm::geometry::crossing_point (_p, _q, bot_p, bot_q); fq_id = border_id::bottom; } } else if (_li.test(0)) { // There's a third intersection. Was T==R? if ((fp-fq).length() < thresh) { // If so, replace R with L: - fq = sm::algo::crossing_point (_p, _q, left_p, left_q); + fq = sm::geometry::crossing_point (_p, _q, left_p, left_q); fq_id = border_id::left; } } @@ -276,12 +276,12 @@ namespace mplot { } } } else if (_li.test(0)) { - fp = sm::algo::crossing_point (_p, _q, left_p, left_q); + fp = sm::geometry::crossing_point (_p, _q, left_p, left_q); fp_id = border_id::left; if (_ri.test(0)) { // left and right - fq = sm::algo::crossing_point (_p, _q, right_p, right_q); + fq = sm::geometry::crossing_point (_p, _q, right_p, right_q); fq_id = border_id::right; } else { fq = fp; @@ -294,7 +294,7 @@ namespace mplot { } } else if (_ri.test(0)) { // Maybe ri alone - fp = sm::algo::crossing_point (_p, _q, right_p, right_q); + fp = sm::geometry::crossing_point (_p, _q, right_p, right_q); fp_id = border_id::right; fq = fp; // Is fq bottom or top? @@ -518,10 +518,10 @@ namespace mplot { q1 = p - half_wave; // Compute intersections for p1, q1 - std::bitset<2> bi = sm::algo::segments_intersect (p1, q1, bot_p, bot_q); - std::bitset<2> ti = sm::algo::segments_intersect (p1, q1, top_p, top_q); - std::bitset<2> li = sm::algo::segments_intersect (p1, q1, left_p, left_q); - std::bitset<2> ri = sm::algo::segments_intersect (p1, q1, right_p, right_q); + std::bitset<2> bi = sm::geometry::segments_intersect (p1, q1, bot_p, bot_q); + std::bitset<2> ti = sm::geometry::segments_intersect (p1, q1, top_p, top_q); + std::bitset<2> li = sm::geometry::segments_intersect (p1, q1, left_p, left_q); + std::bitset<2> ri = sm::geometry::segments_intersect (p1, q1, right_p, right_q); if constexpr (debug_text) { std::cout << "p1/q1 intersections bi: " << bi << " ti: " << ti << " li: " << li << " ri: " << ri << std::endl; } @@ -568,10 +568,10 @@ namespace mplot { } // repeat computation of intersections for p2, q2. - bi = sm::algo::segments_intersect (p2, q2, bot_p, bot_q); - ti = sm::algo::segments_intersect (p2, q2, top_p, top_q); - li = sm::algo::segments_intersect (p2, q2, left_p, left_q); - ri = sm::algo::segments_intersect (p2, q2, right_p, right_q); + bi = sm::geometry::segments_intersect (p2, q2, bot_p, bot_q); + ti = sm::geometry::segments_intersect (p2, q2, top_p, top_q); + li = sm::geometry::segments_intersect (p2, q2, left_p, left_q); + ri = sm::geometry::segments_intersect (p2, q2, right_p, right_q); if constexpr (debug_text) { std::cout << "p2/q2 intersections bi: " << bi << " ti: " << ti << " li: " << li << " ri: " << ri << std::endl; } @@ -636,7 +636,7 @@ namespace mplot { if (!first_off && !second_off) { // both bands are ON, will draw a band. // Does fp1-fp2 intersect with fq1-fq2? (if so triangles for the band will draw badly so swap a pair) - std::bitset<2> fpi = sm::algo::segments_intersect (fp1, fp2, fq1, fq2); + std::bitset<2> fpi = sm::geometry::segments_intersect (fp1, fp2, fq1, fq2); if (fpi.test(0)) { this->swap_pair (fp2, fq2, fp2_id, fq2_id); } if constexpr (debug_text) { std::cout << "** DRAW " << (i%2==0?"G":"B") << " band\n"; } this->draw_band (fp1, fq1, fp2, fq2, col); diff --git a/mplot/LengthscaleVisual.h b/mplot/LengthscaleVisual.h index 64beff5d..02d86162 100644 --- a/mplot/LengthscaleVisual.h +++ b/mplot/LengthscaleVisual.h @@ -52,10 +52,10 @@ namespace mplot sm::quaternion text_rotn; // no rotation by default - if (this->axis == this->ux) { + if (this->axis == sm::vec::ux()) { lblpos = bar_centre + sm::vec{ -geom.half_width(), -this->width * 0.5f - 3.0f * geom.half_height(), 0.0f }; - } else if (this->axis == this->uy) { + } else if (this->axis == sm::vec::uy()) { // Rotated label lblpos = bar_centre + sm::vec{ this->width * 0.5f + 3.0f * geom.half_height(), -geom.half_width(), 0.0f }; // Set rotation @@ -83,9 +83,9 @@ namespace mplot //! Font size, etc mplot::TextFeatures text_features; // A unit vector defining the axis along which this length scale will be drawn - sm::vec axis = this->ux; + sm::vec axis = sm::vec::ux(); //! A unit vector defining what direction we want to look at the text from - sm::vec upaxis = this->uz; + sm::vec upaxis = sm::vec::uz(); }; } // namespace mplot diff --git a/mplot/NavMesh.h b/mplot/NavMesh.h new file mode 100644 index 00000000..19449ada --- /dev/null +++ b/mplot/NavMesh.h @@ -0,0 +1,1413 @@ +/*! + * \file + * + * A navigation mesh class. An instance of this navigation mesh may be owned by a VisualModel to aid + * navigation across and around the model. + * + * \author Seb James + * \date October 2025 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace mplot +{ + // Exception that returns triangles that were near the location of the error + struct NavException : public std::exception + { + enum class type : uint32_t { generic, no_intersection, zero_mv, mv_to_vertex, undetected_crossing, nan_mv, off_edge }; + + NavException (const type _type) : m_type(_type) {} + NavException (const type _type, const std::vector>& t) : m_type(_type) { this->tris = t; } + + using std::exception::what; + const char* what() + { + switch (m_type) { + case type::no_intersection: + return "No intersection (at start) with triangle or neighbours"; + break; + case type::zero_mv: + return "Zero length mv_inplane so stop/freeze/crash"; + break; + case type::mv_to_vertex: + return "We've moved to a vertex, should have captured this case"; + break; + case type::undetected_crossing: + return "Should have detected crossing just now"; + break; + case type::nan_mv: + return "mv_inplane contained NaN"; + break; + case type::off_edge: + return "The movement went off the edge of the model"; + break; + case type::generic: + default: + break; + } + return "Generic"; + } + // Error type determines message generated + type m_type = type::generic; + // Triangles of interest (as indices into NavMesh::vertex) + std::vector> tris; + }; + + /*! + * Navigation mesh of triangles. + * + * This is built from an OpenGL vertex/indices set by VisualModel::make_navmesh() + */ + struct NavMesh + { + /*! + * Minimum set of vertices to generate a topological mesh. populated by + * VisualModel::make_navmesh() + */ + std::vector> vertex; + + /*! + * The edges that make up the same triangles as are shown with the parent VisualModel's + * indices & vertexPositions, but in terms of this->vertex. Each edge must be two indices + * in *ascending numerical order*. populated by VisualModel::make_navmesh() + */ + std::set> edges; + + /*! + * Triangles too. Might be more useful than edges. Triangle given as indices into + * this->vertex. populated by VisualModel::make_navmesh() + */ + sm::vvec, sm::vec, sm::vec, sm::vec>> triangles; + + /*! + * Maps index in vertex to the original parent->indices index. populated by + * VisualModel::make_navmesh() + */ + sm::vvec> vertexidx_to_indices; + + //! Holds a copy of the bb of the parent model + sm::range> bb; + + //! When navigating, this is the 'current triangle' that you're located over/near + std::array ti0 = {}; + + /*! + * The normal of ti0. This is the current triangle normal (in our mesh's frame of + * reference) that our agent/camera is 'next to' + */ + sm::vec tn0 = {}; + + /*! + * Return index of this->vertex that is closest to scene_coord. Can use vertexidx_to_indices + * to find the indices into vertexPositions and vertexNormals that this index in the + * topographic mesh relates to. + * + * \param scene_coord Supplied coordinate in scene frame of referencea + * \param viewmatrix The viewmatrix of the model which converts model frame coordinates to the scene frame + */ + uint32_t find_vertex_nearest (const sm::vec& scene_coord, const sm::mat44& viewmatrix) const + { + uint32_t i = std::numeric_limits::max(); + // Brute force it. (But we have a mesh; can this guarantee a faster search? I don't think so) + float min_d = std::numeric_limits::max(); + for (uint32_t j = 0; j < this->vertex.size(); ++j) { + sm::vec vcoord = (viewmatrix * this->vertex[j]).less_one_dim(); + float d = (scene_coord - vcoord).length(); + if (d < min_d) { + min_d = d; + i = j; + } + } + return i; + } + + // Return the three vertices for the triangle specified as three indices into NavMesh::vertex + sm::vec, 3> triangle_vertices (const std::array& tri_indices) const + { + sm::vec, 3> trivert; + if (tri_indices[0] < this->vertex.size()) { trivert[0] = this->vertex[tri_indices[0]]; } + if (tri_indices[1] < this->vertex.size()) { trivert[1] = this->vertex[tri_indices[1]]; } + if (tri_indices[2] < this->vertex.size()) { trivert[2] = this->vertex[tri_indices[2]]; } + return trivert; + } + + // Return the three vertices for the triangle specified as three indices into NavMesh::vertex transformed by transform + sm::vec, 3> triangle_vertices (const std::array& tri_indices, const sm::mat44& transform) const + { + sm::vec, 3> trivert; + if (tri_indices[0] < this->vertex.size()) { trivert[0] = (transform * this->vertex[tri_indices[0]]).less_one_dim(); } + if (tri_indices[1] < this->vertex.size()) { trivert[1] = (transform * this->vertex[tri_indices[1]]).less_one_dim(); } + if (tri_indices[2] < this->vertex.size()) { trivert[2] = (transform * this->vertex[tri_indices[2]]).less_one_dim(); } + return trivert; + } + + // Compute the triangle normal for the ordered triplet of triangle vertices, tverts + sm::vec triangle_normal (const sm::vec, 3>& tverts) + { + sm::vec n = (tverts[1] - tverts[0]).cross (tverts[2] - tverts[0]); + n.renormalize(); + return n; + } + + sm::vvec neighbours (const uint32_t _idx) const + { + sm::vvec rtn; + // Search edges to find those that include _idx and then pack up the other ends in a return object + for (auto e : this->edges) { + // we have e[0] and e[1] + if (e[0] == _idx) { + // neighb is e[1] + rtn.push_back (e[1]); + } else if (e[1] == _idx) { + // neighb is e[0] + rtn.push_back (e[0]); + } + } + return rtn; + } + + // Determine if ti0 is on the edge of the model (with < 3 edge neighbours), If so, place 1 + // in its final element. Also mark as on edge any nighbours sharing one of its vertices + uint32_t mark_if_on_edge (std::array& _ti0) + { + constexpr bool debug_met = false; + uint32_t n2 = 0; // Neighbours sharing 2 vertices (up to 3) + + std::vector*> neighb_edge_tris; + + for (auto& t: this->triangles) { + auto [ti, tn, tnc, tnd] = t; + auto a0 = _ti0[0]; + auto b0 = _ti0[1]; + auto c0 = _ti0[2]; + auto a = ti[0]; + auto b = ti[1]; + auto c = ti[2]; + if (( a == a0 && ((b == b0 && c != c0) || (c == b0 && b != c0))) + || (b == a0 && ((a == b0 && c != c0) || (c == b0 && a != c0))) + || (c == a0 && ((a == b0 && b != c0) || (b == b0 && a != c0)))) { + ++n2; + neighb_edge_tris.push_back (&ti); + } + else if (( a == b0 && ((b == c0 && c != a0) || (c == c0 && b != a0))) + || (b == b0 && ((a == c0 && c != a0) || (c == c0 && a != a0))) + || (c == b0 && ((a == c0 && b != a0) || (b == c0 && a != a0)))) { + ++n2; + neighb_edge_tris.push_back (&ti); + } + else if (( a == c0 && ((b == a0 && c != b0) || (c == a0 && b != b0))) + || (b == c0 && ((a == a0 && c != b0) || (c == a0 && a != b0))) + || (c == c0 && ((a == a0 && b != b0) || (b == a0 && a != b0)))) { + ++n2; + neighb_edge_tris.push_back (&ti); + } + } + + if (n2 < 3) { + if constexpr (debug_met) { + std::cout << _ti0[0] << "-" << _ti0[1] << "-" << _ti0[2] << " is on the edge"; + } + _ti0[3] = 1; + for (auto& net : neighb_edge_tris) { + if constexpr (debug_met) { std::cout << " mark vtx neighbour "; } + (*net)[3] = 1; + } + if constexpr (debug_met) { std::cout << std::endl; } + + return neighb_edge_tris.size() + 1; + } // Meaning that the triangle is 'on the edge' of the model + return 0; + } + + // Go through all triangles, marking if they're an 'edge' triangle. A triangle is ALSO on + // the edge if on of its neighbours has < 3 edge neighbours. + void mark_edge_triangles() + { + constexpr bool debug_met = false; + uint32_t ec = 0; + for (auto& t: this->triangles) { + auto& [ti, tn, tnc, tnd] = t; + ec += mark_if_on_edge (ti); // ALSO loops through triangles + } + if constexpr (debug_met) { + std::cout << ec << " / " << this->triangles.size() << " triangles are on edge\n"; + } + } + + // Count 2-vertex (i.e. edge) neighbours and also 1-vertex neighbours for triangle _ti0 + std::tuple count_neighbour_triangles (const std::array& _ti0) const + { + // Count neighbour triangles + uint32_t n1 = 0; // Neighbour sharing 1 vertex (any number) + uint32_t n2 = 0; // Neighbours sharing 2 vertices (up to 3) + for (auto t: this->triangles) { + auto [ti, tn, tnc, tnd] = t; + auto a0 = _ti0[0]; + auto b0 = _ti0[1]; + auto c0 = _ti0[2]; + auto a = ti[0]; + auto b = ti[1]; + auto c = ti[2]; + + if (( a == a0 && ((b == b0 && c != c0) || (c == b0 && b != c0))) + || (b == a0 && ((a == b0 && c != c0) || (c == b0 && a != c0))) + || (c == a0 && ((a == b0 && b != c0) || (b == b0 && a != c0)))) { ++n2; } + + else if (( a == b0 && ((b == c0 && c != a0) || (c == c0 && b != a0))) + || (b == b0 && ((a == c0 && c != a0) || (c == c0 && a != a0))) + || (c == b0 && ((a == c0 && b != a0) || (b == c0 && a != a0)))) { ++n2; } + + else if (( a == c0 && ((b == a0 && c != b0) || (c == a0 && b != b0))) + || (b == c0 && ((a == a0 && c != b0) || (c == a0 && a != b0))) + || (c == c0 && ((a == a0 && b != b0) || (b == a0 && a != b0)))) { ++n2; } + + else if (( a == a0 && b != b0 && b != c0 && c != b0 && c != c0) + || (b == a0 && c != b0 && c != c0 && a != b0 && a != c0) + || (c == a0 && a != b0 && a != c0 && b != b0 && b != c0)) { ++n1; } + } + + return {n2, n1}; + } + + sm::vvec> neighbour_triangles (const uint32_t _idx) const + { + sm::vvec> rtn; + for (auto t: this->triangles) { + auto [ti, tn, tnc, tnd] = t; + // If it includes _idx, add it to rtn + if (ti[0] == _idx || ti[1] == _idx || ti[2] == _idx) { + rtn.push_back (ti); + } + } + return rtn; + } + + std::tuple, sm::vec> + first_triangle_containing (uint32_t _idx) const + { + for (auto t: this->triangles) { + auto [ti, tn, tnc, tnd] = t; + if (ti[0] == _idx || ti[1] == _idx || ti[2] == _idx) { + return {ti, tn}; + } + } + return {}; + } + + /* + * Find the location, and the triangle indices at which a ray starting from coord (scene + * frame) with direction vdir - the 'penetration point' intersects with this NavMesh + * model. The length of vdir is used to avoid finding the intersection at the 'back' of the + * model. + * + * \return a tuple containing crossing location, triangle identity (three indices) and triangle normal vector + */ + std::tuple, std::array, sm::vec> + find_triangle_crossing (const sm::vec& coord_mf, const sm::vec& vdir) const + { + constexpr auto umax = std::numeric_limits::max(); + constexpr auto fmax = std::numeric_limits::max(); + sm::vec vstart = coord_mf - (vdir / 2.0f); + + // Return objects + sm::vec isect_p = { fmax, fmax, fmax }; + std::array isect_ti = { umax, umax, umax, 0 }; + sm::vec isect_tn = { fmax, fmax, fmax }; + + auto isect_d = std::numeric_limits::max(); // distance to intersect + + for (auto tri : this->triangles) { + auto [ti, tn, tnc, tnd] = tri; + auto [isect, p] = sm::geometry::ray_tri_intersection (this->vertex[ti[0]], this->vertex[ti[1]], this->vertex[ti[2]], vstart, vdir); + // What if the triangle is one on the *other side of the model*?? Have to use + // vdir.sos() to exclude those that are too far and the distance^2 to find the + // closest one that isn't. + if (isect) { + float d = (p - vstart).sos(); + if (d < isect_d && d < vdir.sos()) { + isect_p = p; + isect_ti = ti; + isect_tn = tn; + isect_d = d; + } + } + } + + if (isect_p[0] == fmax) { + // Found no triangle intersection; check vertices, in case vdir points perfectly at a vertex + for (uint32_t ti = 0; ti < this->vertex.size(); ++ti) { + sm::vec vertex_n = find_vertex_normal (ti); // also loops + vertex_n.renormalize(); + vstart = coord_mf + (vertex_n / 2.0f); + if (sm::geometry::ray_point_intersection (this->vertex[ti], vstart, -vertex_n)) { + float d = (this->vertex[ti] - vstart).sos(); + if (d < isect_d && d < vdir.sos()) { + std::cout << "Register vertex triangle_crossing\n"; + isect_p = this->vertex[ti]; + auto [_ti, _tn] = first_triangle_containing (ti); + isect_ti = _ti; + isect_tn = _tn; + isect_d = d; + } + } + } + } + + return { isect_p, isect_ti, isect_tn }; + } + + // Find the location, and the triangle indices at which a ray between coord (in model frame) + // and the model centroid cross - the 'penetration point'. + std::tuple, std::array, sm::vec> + find_triangle_crossing (const sm::vec& coord_mf) const + { + sm::vec vdir = this->bb.mid() - coord_mf; + vdir.renormalize(); + return this->find_triangle_crossing (coord_mf, vdir); + } + + // Find a triangle containing indices a and b that isn't 'not_this' and return, along with its normal. + std::tuple, sm::vec> + find_other_triangle_containing (const uint32_t a, const uint32_t b, const std::array& not_this) const + { + constexpr uint32_t umax = std::numeric_limits::max(); + std::array other = {umax, umax, umax, 0}; + constexpr float fmax = std::numeric_limits::max(); + sm::vec other_n = {fmax, fmax, fmax}; + for (auto tri : triangles) { + auto [ti, tn, tnc, tnd] = tri; + if (ti[0] == not_this[0] && ti[1] == not_this[1] && ti[2] == not_this[2]) { + continue; + } + if ((ti[0] == a && (ti[1] == b || ti[2] == b)) + || (ti[1] == a && (ti[0] == b || ti[2] == b)) + || (ti[2] == a && (ti[0] == b || ti[1] == b))) { + other = ti; + other_n = tn; + break; + } + } + return {other, other_n}; + } + + // Find all the one-neighbours of 'of_this' + std::vector, sm::vec>> + find_one_neighbours (const std::array& of_this) const + { + std::vector, sm::vec>> rtn = {}; + auto a = of_this[0]; + auto b = of_this[1]; + auto c = of_this[2]; + for (auto tri : triangles) { + auto [ti, tn, tnc, tnd] = tri; + if ((ti[0] == a && ti[1] != b && ti[1] != c && ti[2] != b && ti[2] != c) + || (ti[1] == a && ti[2] != b && ti[2] != c && ti[0] != b && ti[0] != c) + || (ti[2] == a && ti[0] != b && ti[0] != c && ti[1] != b && ti[1] != c) + || + (ti[0] == b && ti[1] != c && ti[1] != a && ti[2] != c && ti[2] != a) + || (ti[1] == b && ti[2] != c && ti[2] != a && ti[0] != c && ti[0] != a) + || (ti[2] == b && ti[0] != c && ti[0] != a && ti[1] != c && ti[1] != a) + || + (ti[0] == c && ti[1] != a && ti[1] != b && ti[2] != a && ti[2] != b) + || (ti[1] == c && ti[2] != a && ti[2] != b && ti[0] != a && ti[0] != b) + || (ti[2] == c && ti[0] != a && ti[0] != b && ti[1] != a && ti[1] != b)) { + + rtn.push_back ({ti, tn}); + } + } + return rtn; + } + + // Find all the neighbours of triangle vertex index a + std::vector, sm::vec>> + find_neighbours (const uint32_t a) const + { + std::vector, sm::vec>> rtn = {}; + for (auto tri : triangles) { + auto [ti, tn, tnc, tnd] = tri; + if (ti[0] == a || ti[1] == a || ti[2] == a) { rtn.push_back({ti, tn}); } + } + return rtn; + } + + sm::vec find_vertex_normal (const uint32_t ti) const + { + auto neighbs = this->find_neighbours (ti); + sm::vec vn = {}; + if (neighbs.size() == 0) { return vn; } + for (auto nb : neighbs) { + auto [ti, tn] = nb; + vn += tn; + } + return (vn / neighbs.size()); + } + + // Find the common vertex (ignoring a/b[3]) between a and b + uint32_t common_vertex (const std::array& a, const std::array& b) + { + uint32_t cv = std::numeric_limits::max(); + if (a[0] == b[0] || a[1] == b[0] || a[2] == b[0]) { + cv = b[0]; + } else if (a[0] == b[1] || a[1] == b[1] || a[2] == b[1]) { + cv = b[1]; + } else if (a[0] == b[2] || a[1] == b[2] || a[2] == b[2]) { + cv = b[2]; + } + return cv; + } + + // Flags class + enum class pm_fl : uint32_t + { + no_cross_point, // Means 'there was no crossing' + colinear // Means the movement was colinear with an edge + }; + /* + * The partial movement that takes us to the crossing point, specified as movement + endpoint + * (rather than startpoint + movement) + */ + struct partial_movement + { + // The movement vector + sm::vec mv = {}; + // The end coordinate of the movement + sm::vec end = {}; + // boolean state + sm::flags flags; + }; + + /* + * Find the part of mv_inplane that gets us to the triangle boundary defined by edge_s and + * edge_e + * + * IS IS ASSUMED that mv_s is in the triangle plane and that a movement of mv_inplane would cross + * the edge if it were long enough. + * + * All vectors and coordinates here are in the same coordinate frame as the triangle + * vertices. That could be either the model frame OR the scene frame (but always one or the + * other). + * + * \param edge_s Starting coordinate of the edge + * \param edge_e End coordinate of the edge + * \param t_norm The triangle normal vector + * \param mv_s The movement starting point + * \param mv_inplane The planned movement, starting from hovlocn + * + * \return a struct containing the partial movement vector and the end of the movement as a + * coordinate. If mv_inplane does not cross the edge, then the return object contains the vector + * mv_inplane itself, and the coordinate that this movement ends at. + */ + partial_movement find_edge_crossing (const sm::vec& edge_s, + const sm::vec& edge_e, + const sm::vec& t_norm, + const sm::vec& mv_s, + const sm::vec& mv_inplane) + { + constexpr bool debug = false; + partial_movement pm; + sm::vec edge = edge_e - edge_s; + + sm::vec u_y = edge; + u_y.renormalize(); + sm::vec u_z = t_norm; + u_z.renormalize(); + sm::vec u_x = u_y.cross (u_z); + if constexpr (debug) { + std::cout << "fec: mv_inplane = " << mv_inplane << std::endl; + std::cout << "fec: edge = " << edge << std::endl; + std::cout << "fec: Basis: " << u_x << " " << u_y << " " << u_z << std::endl; + } + + // Create a matrix to convert from mdl frame movements to the triangle frame of ref. + sm::mat44 from_triangle_frame = sm::mat44::frombasis (u_x, u_y, u_z); + sm::mat44 to_triangle_frame = from_triangle_frame.inverse(); + + // Use Edge as our 'y' and the orthogonal as our 'x', then express mv_inplane in terms + // of these two unit vectors. We also have our 'z' which is the triangle normal. + sm::vec mv_inplane4d = to_triangle_frame * mv_inplane; + sm::vec mv_inplane2d = { mv_inplane4d[0], mv_inplane4d[1] }; + sm::vec h_4d = to_triangle_frame * mv_s; + sm::vec h_2d = { h_4d[0], h_4d[1] }; + sm::vec edge_4d = to_triangle_frame * edge; + sm::vec edge_2d = { edge_4d[0], edge_4d[1] }; + sm::vec edge_s_4d = to_triangle_frame * edge_s; + sm::vec edge_s_2d = { edge_s_4d[0], edge_s_4d[1] }; + + // Can now apply algo to find crossing point + if constexpr (debug) { + std::cout << "fec: intersection test for lines: " << edge_s_2d << " --> " << (edge_2d + edge_s_2d) + << " and " << h_2d << " --> " << (h_2d + mv_inplane2d) << "\n"; + } + + std::bitset<2> si = sm::geometry::segments_intersect (edge_s_2d, edge_s_2d + edge_2d, h_2d, h_2d + mv_inplane2d); + if (si.test(1)) { + if constexpr (debug) { std::cout << "fec: Colinear with triangle edge!\n"; } + pm.flags.set (pm_fl::colinear, true); + // Identify the vertex that we're moving towards. edge_4d is the triangle edge. + // so: mv_inplane4d.dot (edge_4d) should be positive if edge_e is the vertex + sm::vec mv_inplane3d = mv_inplane4d.less_one_dim(); + sm::vec edge_e_3d = (to_triangle_frame * edge_e).less_one_dim(); + sm::vec edge_s_3d = edge_s_4d.less_one_dim(); + + if constexpr (debug) { + std::cout << "mv_inplane: " << mv_inplane3d << ", edge_e: " << edge_e_3d << ", edge_s: " << edge_s_3d << std::endl; + std::cout << "mv_inplane.dot (edge_e): " << mv_inplane3d.dot (edge_e_3d) << std::endl; + std::cout << "mv_inplane.dot (edge_s): " << mv_inplane3d.dot (edge_s_3d) << std::endl; + } + sm::vec to_v = {}; + if (mv_inplane3d.dot (edge_e_3d) > mv_inplane3d.dot (edge_s_3d)) { + to_v = edge_e_3d - (h_4d).less_one_dim(); + } else { + to_v = edge_s_3d - (h_4d).less_one_dim(); + } + + if (to_v.length() <= mv_inplane3d.length()) { + if constexpr (debug) { std::cout << "fec: partial colinear move to vertex\n"; } + pm.flags.set (pm_fl::no_cross_point, false); + pm.mv = (from_triangle_frame * to_v).less_one_dim(); // need to know if we were to go over a vertex + pm.end = (from_triangle_frame * edge_e_3d).less_one_dim(); + } else { + if constexpr (debug) { std::cout << "fec: partial colinear along/within edge\n"; } + pm.flags.set (pm_fl::no_cross_point, true); + // Compute end from mv_inplane4d + pm.mv = (from_triangle_frame * mv_inplane4d).less_one_dim(); + pm.end = (from_triangle_frame * (h_4d + mv_inplane4d)).less_one_dim(); + } + + } else { + if (si.test(0)) { + // Intersects as expected + sm::vec cp2d = sm::geometry::crossing_point (edge_s_2d, edge_s_2d + edge_2d, h_2d, h_2d + mv_inplane2d); + // Now go from cross point 2d to a point in model coordinates? + pm.end = (from_triangle_frame * cp2d.plus_one_dim(edge_s_4d[2])).less_one_dim(); + if constexpr (debug) { std::cout << "fec: Cross point in mdl frame: " << pm.end << std::endl; } + pm.mv = pm.end - mv_s; + + } else { + // 'No intersection' can occur when: the movement goes over/close to the end of the edge. + // Or when: the move starts ON the edge of a triangle and then moves *away* from the tri. + if constexpr (debug) { + std::cout << "fec: No intersection across edge for: " + << (edge_s_2d) << " -- " << (edge_2d + edge_s_2d) << " and " + << h_2d << " -- " << (h_2d + mv_inplane2d) << std::endl; + } + // Mark that there was no intersection + pm.flags.set (pm_fl::no_cross_point, true); + pm.mv = sm::vec{}; + pm.end = mv_s; + } + } + + return pm; + } + + /* + * After testing up to all three edges of a triangle, we return information about the crossing + * location and the indices of the triangle that form the crossed edge in this structure. + */ + struct crossing_data + { + // edge_idx_a/b are the indices of the triangle vertices on the crossed edge + uint32_t edge_idx_a = 0; + uint32_t edge_idx_b = 0; + // The crossed edge as a vector + sm::vec tri_edge = {}; + // The partial movement. pm.mv is the movement, pm.end is the crossing point + partial_movement pm = {}; + }; + + /* + * Find the location at which a movement from mv_s in the direction mv_inplane crosses one of + * the edges of the triangle specified by the three vertices in t_verts/t_indices. + * + * IT IS ASSUMED that the triangle normal passing through mv_s WILL intersect the + * triangle (this may include an edge or vertex intersection). (Test beforehand with sm::geometry::ray_tri_intersection) + * + * All coordinates are in the frame of the model that contains this triangle. + * + * \param t_verts *Ordered* vertices of the triangle. Vertices should be in anti-clockwise order + * when viewed with the triangle normal vector coming 'out of the page' + * + * \param t_indices The *Ordered* indices of the vertices in t_verts. Used to return the crossed + * edge specified as two common indices. See t_verts for correct order of triangle vertices. + * + * \param mv_s The start of the planned movement + * + * \param mv_inplane The planned movement + * + * \param t_norm The triangle normal. While this could be computed from t_verts, it has already + * been computed by the program and so I'm passing it in here. + */ + crossing_data compute_crossing_location (const sm::vec, 3>& t_verts, + const std::array& t_indices, + const sm::vec& mv_s, + const sm::vec& mv_inplane, + const sm::vec& t_norm) + { + constexpr bool debug = false; + crossing_data cd; + cd.pm.flags.set (pm_fl::no_cross_point, true); + + const sm::vec& t0 = t_verts[0]; + const sm::vec& t1 = t_verts[1]; + const sm::vec& t2 = t_verts[2]; + + sm::vec p = mv_s + mv_inplane; + sm::vec edge = t1 - t0; + sm::vec ptoe = p - t0; + bool inside01 = (t_norm.dot (edge.cross (ptoe)) >= 0); + if (!inside01) { + partial_movement pm = find_edge_crossing (t0, t1, t_norm, mv_s, mv_inplane); + if constexpr (debug) { + if (pm.flags.test (pm_fl::colinear)) { + std::cout << "ccl: fec returned pm.colinear true for t0t1\n"; + } + } + if (pm.flags.test (pm_fl::no_cross_point) + && pm.flags.test (pm_fl::colinear) == false) { + inside01 = true; + if constexpr (debug) { + std::cout << "ccl: No intersection for edge t0t1 " << t0 << " -- " << t1 + << " and move " << mv_s << " -- " << (mv_s + mv_inplane) << std::endl; + } + } else { + if constexpr (debug) { + if (pm.flags.test (pm_fl::colinear)) { std::cout << "ccl: colinear t0t1\n"; } + std::cout << "ccl: Intersection for edge t0t1 " << t0 << " -- " << t1 + << " and move " << mv_s << " -- " << (mv_s + mv_inplane) << std::endl; + } + cd.pm = pm; + cd.tri_edge = edge; + cd.edge_idx_a = t_indices[0]; + cd.edge_idx_b = t_indices[1]; + } + } + + edge = t2 - t1; ptoe = p - t1; + bool inside21 = (t_norm.dot (edge.cross (ptoe)) >= 0); + if (!inside21) { + partial_movement pm = find_edge_crossing (t1, t2, t_norm, mv_s, mv_inplane); + if constexpr (debug) { + if (pm.flags.test (pm_fl::colinear)) { + std::cout << "ccl: fec returned pm.colinear true for t1t2\n"; + } + } + if (pm.flags.test (pm_fl::no_cross_point) + && pm.flags.test (pm_fl::colinear) == false) { + inside21 = true; + if constexpr (debug) { + std::cout << "ccl: No intersection for edge t1t2 " << t1 << " -- " << t2 + << " and move " << mv_s << " -- " << (mv_s + mv_inplane) << std::endl; + } + } else { + if constexpr (debug) { + if (pm.flags.test (pm_fl::colinear)) { std::cout << "ccl: colinear t1t2\n"; } + std::cout << "ccl: Intersection for edge t1t2 " << t1 << " -- " << t2 + << " and move " << mv_s << " -- " << (mv_s + mv_inplane) << std::endl; + } + cd.pm = pm; + cd.tri_edge = edge; + cd.edge_idx_a = t_indices[2]; + cd.edge_idx_b = t_indices[1]; + } + } + + edge = t0 - t2; ptoe = p - t2; + bool inside02 = (t_norm.dot (edge.cross (ptoe)) >= 0); + if (!inside02) { + partial_movement pm = find_edge_crossing (t2, t0, t_norm, mv_s, mv_inplane); + if constexpr (debug) { + if (pm.flags.test (pm_fl::colinear)) { + std::cout << "ccl: fec returned pm.colinear true for t2t0\n"; + } + } + if (pm.flags.test (pm_fl::no_cross_point) + && pm.flags.test (pm_fl::colinear) == false) { + inside02 = true; + if constexpr (debug) { + std::cout << "ccl: No intersection for edge t2t0 " << t2 << " -- " << t0 + << " and move " << mv_s << " -- " << (mv_s + mv_inplane) << std::endl; + } + } else { + if constexpr (debug) { + if (pm.flags.test (pm_fl::colinear)) { std::cout << "ccl: colinear t2t0\n"; } + std::cout << "ccl: Intersection for edge t2t0 " << t2 << " -- " << t0 + << " and move " << mv_s << " -- " << (mv_s + mv_inplane) << std::endl; + } + cd.pm = pm; + cd.tri_edge = edge; + cd.edge_idx_a = t_indices[0]; + cd.edge_idx_b = t_indices[2]; + } + } + + // We've now tested edge crossing for three edges in the triangle. + // + if constexpr (debug) { + if (cd.pm.flags.test (pm_fl::no_cross_point) == false) { + std::cout << "ccl: Crossed over" << (inside01 ? " " : " 0-1") + << (inside21 ? " " : " 2-1") << (inside02 ? " " : " 0-2") << std::endl; + // could test pairs of inside01 etc to detect crossing a vertex + } else if (cd.pm.flags.test (pm_fl::colinear) == true) { + // Movement was colinear. Set Crossed vertex? + std::cout << "ccl: movement was colinear!\n"; + if (cd.pm.flags.test (pm_fl::no_cross_point)) { + std::cout << "ccl: Colinear along edge" << std::endl; + } else { + std::cout << "ccl: Colinear to vertex" << std::endl; + } + // cd.pm.no_cross_point will tell if there's a cross point or not + } else { + // We have NO crossing, which can occur for a variety of reasons + std::cout << "ccl: No crossings " << (inside01 ? " " : "!!0-1") + << (inside21 ? " " : "!!2-1") << (inside02 ? " " : "!!0-2") << std::endl; + } + } + + return cd; + } + + /*! + * Find the model location, starting from the location of a camera specified in + * camspace. Cast a ray towards the centroid of this navmesh and figure out which triangle + * in the navmesh the ray passes through. + * + * \param camspace The camera transformation matrix that converts camera coordinates into + * the scene frame. This gives us the start location for the ray. + * + * \param model_to_scene The model to scene transformation for the parent of the navmesh + * + * \return tuple containing: the hit point in scene coordinates; the triangle normal of the + * triangle we hit; and the indices of the triangle we hit. + */ + std::tuple, sm::vec, std::array> + find_triangle_hit (const sm::mat44& camspace, const sm::mat44& model_to_scene) + { + sm::mat44 scene_to_model = model_to_scene.inverse(); + // use camera location in gltf to start from, then find model surface. + sm::vec camloc_mf = (scene_to_model * camspace * sm::vec{}).less_one_dim(); + this->ti0 = {}; + this->tn0 = {}; + sm::vec hit = {}; + sm::vec vdir = this->bb.mid() - camloc_mf; + std::tie (hit, this->ti0, this->tn0) = this->find_triangle_crossing (camloc_mf , vdir); + + if (this->ti0[0] == std::numeric_limits::max()) { std::cout << __func__ << ": No hit\n"; } + + sm::vec hp_scene = (model_to_scene * hit).less_one_dim(); + + constexpr bool debug = false; + if constexpr (debug) { + std::cout << "found hit at " << hit << " (model); " << hp_scene << " (scene)\n"; + // Check we'll get a hit when we compute_mesh_movement: + sm::vec, 3> tv_mf = this->triangle_vertices (this->ti0); + std::cout << "tn0: " << this->tn0 << ", length " << this->tn0.length() << std::endl; + std::cout << "TEST ray_tri_intersection (hit,-tn0): " << (hit + (this->tn0 / 2.0f)) << "," << -this->tn0 << std::endl; + auto [isect, hov_mf] = sm::geometry::ray_tri_intersection (tv_mf[0], tv_mf[1], tv_mf[2], hit + (this->tn0 / 2.0f), -this->tn0); + if (isect) { + std::cout << "ray_tri_intersection confirms we would hit at " << hov_mf << "\n"; + } else { + std::cout << "ray_tri_intersection DOES NOT get a hit\n"; + //throw std::runtime_error ("ray_tri_intersection DOES NOT get a hit!"); + } + } + + return { hp_scene, this->tn0, this->ti0 }; + } + + /*! + * Using data about the model location for the camera found with find_triangle_hit, return a + * camera position matrix (scene frame) + * + * \return a transform matrix that places a camera frame of reference at hp_scene, oriented + * with its y-axis in line with the normal of the triangle at the hit point, and with its x + * and z axes randomly oriented. The frame is set to hover hoverheight 'above' the triangle + */ + sm::mat44 position_camera (const sm::vec& hp_scene, const sm::mat44& model_to_scene, + const float hoverheight) + { + // Let's 'draw' the camera towards the model and then arrange its normal upwards wrt to the normal of the model. + if (this->tn0[0] == std::numeric_limits::max()) { + std::cout << __func__ << ": No hit/triangle normal\n"; + return sm::mat44{}; + } + + // Place the camera on the model, and orient it randomly in the 'model plane' + // The camera frame always has y up. Choose a random vector in the plane for 'x' + // and then set z from this random x and the triangle norm (y). + sm::vec rand_vec; + rand_vec.randomize(); + sm::vec _x = rand_vec.cross (this->tn0); + _x.renormalize(); + sm::vec _z = _x.cross (this->tn0); + + // I think this positions correctly now (which is all it has to do). It ignores scaling + // in model_to_scene. Can be reduced to use fewer mat44s. + sm::mat44 cam_mv_y; + cam_mv_y.translate (sm::vec{0, hoverheight, 0}); + // The basis _x, tn0, _z, where these are vectors in the model frame that define a camera frame + sm::mat44 cam_to_model_rotn = sm::mat44::frombasis (_x, this->tn0, _z); + // Get the rotation from scene frame to model + sm::mat44 m_to_sc_rotn = model_to_scene.rotation_mat44(); + sm::mat44 hp_m; + hp_m.translate (hp_scene); + sm::mat44 coord_rotn = hp_m * m_to_sc_rotn * cam_to_model_rotn * cam_mv_y; + + return coord_rotn; + } + + /*! + * Compute a movement over this navigation mesh. + * + * We convert the triangle vertices from the model frame to the scene frame before computing + * reorientations, so that non-uniform scalings in the model do not fox us. + * + * \param mv_camframe A movement vector in the camera's own frame of reference (an ego-motion) + * \param cam_to_scene The transformation matrix to bring the camera coordinates to the scene frame + * \param model_to_scene The transformation matrix to convert model coordinates to the scene frame + * \param hoverheight + * + * \return The re-positioned camera transform matrix + */ + sm::mat44 compute_mesh_movement (const sm::vec& mv_camframe, + const sm::mat44& cam_to_scene, + const sm::mat44& model_to_scene, + const float hoverheight) + { + constexpr bool debug_move = false; + constexpr bool debug_move2 = true; + + // A data-containing exception to throw + mplot::NavException ne (mplot::NavException::type::generic); + ne.tris.push_back (this->ti0); + + // Boolean state flags used in this function + enum class cmm_fl : uint32_t { done, detected_crossing, single_movement, vertex_crossing }; + sm::flags flags; + + // Camera location, scene frame + sm::vec camloc_sf = cam_to_scene.translation(); + // Convert indices to vertices for triangle ti0, converting to the scene frame + sm::vec, 3> tv_sf = this->triangle_vertices (this->ti0, model_to_scene); + // Compute the triangle normal in the scene frame + this->tn0 = this->triangle_normal (tv_sf); + + if constexpr (debug_move) { + std::cout << "\n# compute_mesh_movement: ti0 " << this->ti0[0] << "," << this->ti0[1] << "," << this->ti0[2] + << " has vertices (sf) at " << tv_sf << " and normal " << this->tn0 + << ". upcoming movement (camframe) is " << mv_camframe << std::endl; + std::cout << "Initial camera location (camloc_sf): " << camloc_sf << std::endl; + } + + // Does camloc_sf in dirn tn0 intersect the tv_sf triangle? This + // returns true if camloc_sf is on the edge of the triangle or on a + // vertex. Assumes we're above the model and within the length of tn0 of the + // surface. + // + // IF we're on an edge, then this intersection algo may disagree with + // compute_crossing_location, which currently looks for crossing each of the three + // boundaries and so requires that the start point is *within* the boundary. + // + if constexpr (debug_move) { + std::cout << "First ray_tri_intersection (raystart,-tn0): " << (camloc_sf + (this->tn0 / 2.0f)) << "," << -this->tn0 << std::endl; + } + bool isect = false; + sm::vec hov_sf = {}; + std::tie (isect, hov_sf) = sm::geometry::ray_tri_intersection (tv_sf[0], tv_sf[1], tv_sf[2], camloc_sf + (this->tn0 / 2.0f), -this->tn0); + + // Use the detected location, hov_sf to compute the surface location of the camera - its 'hover location' + sm::mat44 cam_to_surface = cam_to_scene; + cam_to_surface.pretranslate (hov_sf - camloc_sf); // This is now our init pose; the camera is now at the surface + + // Try double precision + if (isect == false) { + std::tie (isect, hov_sf) = sm::geometry::ray_tri_intersection (tv_sf[0], tv_sf[1], tv_sf[2], camloc_sf + (this->tn0 / 2.0f), -this->tn0); + if constexpr (debug_move) { + if (isect == false) { + std::cout << "No isect at start with " << this->ti0[0] << "," << this->ti0[1] << "," << this->ti0[2] + << " using float OR double internally" << std::endl; + } else { + std::cout << "Intersection at start with " << this->ti0[0] << "," << this->ti0[1] << "," << this->ti0[2] + << " using *double* internally" << std::endl; + } + } + } + + // If that didn't work, try the triangle *vertices* + uint32_t int_vertex = std::numeric_limits::max(); // intersection vertex + if (isect == false) { + if constexpr (debug_move) { std::cout << "Try the triangle vertices...\n"; } + for (uint32_t i = 0u; i < 3u; i++) { + + // We need to use the *vertex* normal for this test - the average of all the adjacent triangle normals! + sm::vec vertex_n = this->find_vertex_normal (this->ti0[i]); + vertex_n.renormalize(); + if constexpr (debug_move) { + std::cout << "Vertex normal for triangle index " << ti0[i] << " is " << vertex_n << std::endl; + } + + if (sm::geometry::ray_point_intersection (tv_sf[i], camloc_sf + (vertex_n / 2.0f), -vertex_n)) { + if constexpr (debug_move) { + std::cout << "A VERTEX intersection is the start at " << tv_sf[i] << ", compare this with hov_sf = " << hov_sf << "\n"; + // if start is vertex, need to check movement across all the triangle-neighbours of this vertex (see later use of int_vertex) + } + hov_sf = tv_sf[i]; + int_vertex = i; + isect = true; + } + } + } + + std::vector> trisearched; // the other triangles we search. To place in exception + if (isect == false) { + + if constexpr (debug_move2) { + std::cout << "No intersection (at start) with triangle " + << this->ti0[0] << "," << this->ti0[1] << "," << this->ti0[2] + << ", so correct ti0 and tn0 (if we can)" << std::endl; + } + + // When very close to the boundary, ray_tri_intersection may fail. This triggers a + // search for a neighbouring triangle which the camera may instead be hovering over + // (this can occur when moving along an edge) + for (uint32_t i = 0u; i < 3u; i++) { + uint32_t i1 = i; + uint32_t i2 = (i + 1) % 3u; + auto [_ti, _tn] = this->find_other_triangle_containing (this->ti0[i1], this->ti0[i2], this->ti0); + if (_ti[0] != std::numeric_limits::max()) { + trisearched.push_back (_ti); + // Test to see if start location was inside a neighbour + sm::vec, 3> tv_lf = this->triangle_vertices (_ti, model_to_scene); + // _tn was returned in model frame coordinates, so recompute in scene frame + _tn = this->triangle_normal (tv_lf); + + auto [is, h] = sm::geometry::ray_tri_intersection (tv_lf[0], tv_lf[1], tv_lf[2], camloc_sf + (_tn / 2.0f), -_tn); + if constexpr (debug_move) { + std::cout << "Start of move " << (is ? "IS" : "is NOT") << " in " << _ti[0] << "," << _ti[1] << "," << _ti[2] << std::endl; + } + if (is) { + if constexpr (debug_move) { std::cout << "*** Correcting!\n"; } + // We're in this neighbour, so update ti0/tn0 and mark isect true + this->ti0 = _ti; + tv_sf = tv_lf; + this->tn0 = _tn; + isect = true; + // This requires a number of matrix recomputations: + hov_sf = h; + cam_to_surface = cam_to_scene; + cam_to_surface.pretranslate (hov_sf - camloc_sf); // This is our init pose, placed on the surface + break; + } + } // else missing neighbour. Could see if it would land in a neighbour that's just off the edge? + } + + if (isect == false) { + if constexpr (debug_move2) { + std::cout << "No intersection (at start) with triangle " + << this->ti0[0] << "," << this->ti0[1] << "," << this->ti0[2] << " OR neighbours" << std::endl; + } + + // Final test to see if we're on boundary? + float closest_edge_d = sm::geometry::dist_to_tri_edge (tv_sf[0], tv_sf[1], tv_sf[2], camloc_sf - (this->tn0 * hoverheight)); + if constexpr (debug_move2) { + std::cout << "Closest distance from " << (camloc_sf - (this->tn0 * hoverheight)) + << " to ti0 edge: " << closest_edge_d << std::endl; + } + constexpr float ced_thresh = std::numeric_limits::epsilon() * 50; + if (closest_edge_d < ced_thresh) { + // make tiny adjustment to camloc_sf so we ARE in the triangle? OR... + isect = true; // SAY we are, and proceed? <-- this if it works. + } else { + ne.m_type = NavException::type::no_intersection; + ne.tris.insert (ne.tris.end(), trisearched.begin(), trisearched.end()); + throw ne; + } + } else { + if constexpr (debug_move2) { + std::cout << "Found intersection (at start) with (2-)neighbour triangle " + << this->ti0[0] << "," << this->ti0[1] << "," << this->ti0[2] << std::endl; + } + } + + } else { + if constexpr (debug_move) { + std::cout << "First ray_tri_intersected. Start of move is IN triangle " + << this->ti0[0] << "," << this->ti0[1] << "," << this->ti0[2] + << " from coord " << camloc_sf << " and dirn " << -this->tn0 << std::endl; + } + } + + // rest of function assumes isect was true (exception otherwise) + + // Find component of movement that is in the current triangle plane (in the scene frame of reference) + sm::vec mv_sf = (cam_to_scene * mv_camframe).less_one_dim() - camloc_sf; + sm::vec mv_orthog = this->tn0 * (mv_sf.dot (this->tn0) / (this->tn0.dot (this->tn0))); + sm::vec mv_inplane = mv_sf - mv_orthog; // scene frame, a relative movement + + if (mv_inplane.length() == 0.0f) { + if constexpr (debug_move) { std::cout << "No movement, so return unchanged camera viewmatrix\n"; } + return cam_to_scene; + } + + // New section to handle the case that we started right on a vertex + if (isect == true && int_vertex != std::numeric_limits::max()) { + // We HAVE a vertex intersection. Check if we either cross, or land in one of this vertex's neighbours to correct our starting triangle and normal. + auto onens = this->find_neighbours (this->ti0[int_vertex]); + for (auto onen : onens) { + auto [_ti, _tn] = onen; + sm::vec _mv_orthog = _tn * (mv_sf.dot (_tn) / (_tn.dot (_tn))); + sm::vec _mv_inplane = mv_sf - _mv_orthog; // scene frame, a relative movement + sm::vec, 3> tv_nb = this->triangle_vertices (_ti, model_to_scene); + // _tn = this->triangle_normal (tv_nb); // shouldn't need to recompute + crossing_data cd = this->compute_crossing_location (tv_nb, _ti, hov_sf, _mv_inplane, _tn); + if (cd.pm.flags.test (pm_fl::no_cross_point) == false) { + this->ti0 = _ti; + this->tn0 = _tn; + tv_sf = tv_nb; + mv_orthog = _mv_orthog; + mv_inplane = _mv_inplane; + if constexpr (debug_move) { + std::cout << "Break on cross point with triangle (" << _ti[0] << "," << _ti[1] << "," << _ti[2] << ")\n"; + } + break; + } else { + // No crossing, did we land in the triangle? + auto [is, h] = sm::geometry::ray_tri_intersection (tv_nb[0], tv_nb[1], tv_nb[2], hov_sf + _mv_inplane + (_tn / 2.0f), -_tn); + if (is) { // then we DID land in this neighbour tri + this->ti0 = _ti; + this->tn0 = _tn; + tv_sf = tv_nb; + mv_orthog = _mv_orthog; + mv_inplane = _mv_inplane; + if constexpr (debug_move) { + std::cout << "Break as we landed in triangle (" << _ti[0] << "," << _ti[1] << "," << _ti[2] << ")\n"; + } + break; + } + } + } + } // Now carry on with corrected mv_inplane, tn0 and ti0 + + // A 'detected crossing' is one where we had to use a secondary method (comparing the + // triangle containing the start and the triangle containing the end) to determine that + // a triangle edge had been crossed, because the original method + // (compute_crossing_location, which uses a faster, but numerically fallible approach) + // failed. + sm::vec detected_edge = {}; + sm::vec detected_edgevec = {}; + std::array detected_newtri = {}; // new triangle detected as part of a vertex crossing + + // Now loop while our path may traverse one or more triangles + while (!flags.test (cmm_fl::done)) { + + if constexpr (debug_move) { + std::cout << "\n* loopstart: Processing mv_inplane: " << hov_sf << "," << mv_inplane << " ti0 = (" + << this->ti0[0] << "," << this->ti0[1] << "," << this->ti0[2] << ") with tn0 = " << this->tn0 << std::endl; + } + + if (mv_inplane.length() == 0) { + ne.m_type = NavException::type::zero_mv; + throw ne; + } + if (mv_inplane.has_nan()) { + ne.m_type = NavException::type::nan_mv; + throw ne; + } + + // Apply the edge crossing algorithm + crossing_data cd = this->compute_crossing_location (tv_sf, this->ti0, hov_sf, mv_inplane, this->tn0); + + if (cd.pm.flags.test (pm_fl::no_cross_point) == false || flags.test (cmm_fl::detected_crossing) || flags.test (cmm_fl::vertex_crossing)) { + // Then an edge (or vertex)crossing WAS detected (by compute_crossing_location or a prev. 'detected crossing') + + if (flags.test (cmm_fl::detected_crossing)) { + if constexpr (debug_move) { + std::cout << "This is a detected crossing; changing edge_idx_a/b to " << detected_edge << std::endl; + } + // We have to update our crossing data, as we detected a crossing over + // an edge (probably while moving along that edge) + cd.edge_idx_a = detected_edge[0]; + cd.edge_idx_b = detected_edge[1]; + cd.tri_edge = detected_edgevec; + cd.pm.mv = mv_inplane; + cd.pm.end = hov_sf + mv_inplane; + } else { + if constexpr (debug_move) { + std::cout << "This IS a crossing (compute_crossing_location found it) " << std::endl; + } + } + + // _ti, _tn are the new triangle + sm::vec _tn = {}; + std::array _ti = {}; + if (flags.test (cmm_fl::vertex_crossing)) { + if constexpr (debug_move) { + std::cout << "Setting _ti to over-the-vertex tri " + << detected_newtri[0] << "-" << detected_newtri[0] << "-" << detected_newtri[0] << std::endl; + } + _ti = detected_newtri; + } else { + // Can work out new triangle here across the crossed edge + if constexpr (debug_move) { + std::cout << "find triangle across edge: find_other_triangle_containing (" + << cd.edge_idx_a << ", " << cd.edge_idx_b + << ", [" << this->ti0[0] << "," << this->ti0[1] << "," << this->ti0[2] << "])" << std::endl; + } + std::tie (_ti, _tn) = this->find_other_triangle_containing (cd.edge_idx_a, cd.edge_idx_b, this->ti0); + } + + if (_ti[0] != std::numeric_limits::max()) { + + // Re-orient onto the new triangle + sm::vec, 3> newtv_sf = this->triangle_vertices (_ti, model_to_scene); + _tn = this->triangle_normal (newtv_sf); + + if constexpr (debug_move) { + std::cout << "Re-orient to new triangle " << _ti[0] << "," << _ti[1] << "," << _ti[2] + << "[ " << newtv_sf << " ] with normal " << _tn << "\n"; + } + + // If a vertex crossing, we have to make an edge that is the cross product of the two triangle normals + if (flags.test (cmm_fl::vertex_crossing)) { cd.tri_edge = this->tn0.cross (_tn); } + + // Compute the reorientation due to the requested movement. + // Rotate by the angle between the normals. I think this is constrained to be <= pi + float rotn_angle = this->tn0.angle (_tn, cd.tri_edge); + // If tn0 and _tn are identical, then rotn_angle will be NaN, but in that case we want no rotation + if (std::isnan (rotn_angle)) { rotn_angle = 0.0f; } + sm::mat44 reorient_model; // reorientation transformation in sf + reorient_model.rotate (cd.tri_edge, rotn_angle); + sm::vec mv_rest = (reorient_model * (mv_inplane - cd.pm.mv)).less_one_dim(); + reorient_model.pretranslate (hov_sf + cd.pm.mv + mv_rest); + reorient_model.translate (-hov_sf); // r_t_to + r_t1 = -(hov_sf + cd.pm.mv) + cd.pm.mv = -hov_sf + + if (mv_rest.length() == 0) { + // The first movement to edge completed the movement. We actually landed ON the edge. + cam_to_surface = reorient_model * cam_to_surface; + flags.set (cmm_fl::done, true); + } else { + // There's additional movement to complete. + if constexpr (debug_move) { std::cout << "mv_rest length is " << mv_rest.length() << std::endl; } + + // At this point, can test to see if the end point of the movement + // lands in the adjacent triangle. If so, we're done, if not, time + // for another loop. + sm::vec endmv = (reorient_model * cam_to_surface * sm::vec{}).less_one_dim(); + // Is endmv in newtv_sf/_ti? + auto [isect2, isectpoint2] = sm::geometry::ray_tri_intersection (newtv_sf[0], newtv_sf[1], newtv_sf[2], + endmv + (_tn / 2.0f), -_tn); + if constexpr (debug_move) { + std::cout << "endmv = " << endmv << " DOES" << (isect2 ? "" : " NOT") << " land in new triangle\n"; + } + if (isect2) { + // We DID land in the neighbouring triangle. We are done. + cam_to_surface = reorient_model * cam_to_surface; + flags.set (cmm_fl::done, true); + } else { + if constexpr (debug_move) { std::cout << "did we sail past or land on the boundary or land in a 1-neighbour?\n"; } + // Incomplete; We've sailed past newtv_sf. Or perhaps landed on the boundary??? + // We need to + // set an end-point that is on newtv_sf, update hov_sf, + // then recurse. also recompute the movement encoded in + // reorient_model + reorient_model.pretranslate (-mv_rest); + cam_to_surface = reorient_model * cam_to_surface; + hov_sf = cd.pm.end; // crossing data planned movement end + // Also update planned move, which is now shorter and in a new direction + tv_sf = newtv_sf; + mv_inplane = mv_rest; + } + } + + this->ti0 = _ti; + ne.tris.push_back (this->ti0); + this->tn0 = _tn; + + } else { + // other triangle not found?! We probably went off the edge of our navigation model mesh + ne.m_type = NavException::type::off_edge; + throw ne; + continue; + } + + } else { // NO triangle edge crossing was detected with compute_crossing_location + + // We had intersection in ti0, but no apparent crossing over its edges. + // We may have moved entirely within the starting triangle or colinear with an edge. Test for these cases. + + // Check if it was a colinear movement + if (cd.pm.flags.test (pm_fl::colinear)) { + if (cd.pm.flags.test (pm_fl::no_cross_point) == true) { + flags.set (cmm_fl::single_movement, true); + } else { // We've moved to a vertex, should have captured this case + ne.m_type = NavException::type::mv_to_vertex; + throw ne; + } + } else { + // Test if it was movement-within; the simplest case + if constexpr (debug_move) { + std::cout << "No cross point and not colinear.\n Testing if " + << (hov_sf + mv_inplane + (this->tn0 / 2.0f)) << "," << -this->tn0 + << " intersects tv_sf (" << tv_sf << "\n"; + } + auto [single_mv, he] = sm::geometry::ray_tri_intersection (tv_sf[0], tv_sf[1], tv_sf[2], hov_sf + mv_inplane + (this->tn0 / 2.0f), -this->tn0); + flags.set (cmm_fl::single_movement, single_mv); + } + + if (flags.test (cmm_fl::single_movement)) { + if constexpr (debug_move) { std::cout << "End of movement is *still* in ti0, so move mv_inplane/mv_camframe\n"; } + // Perform simplest movement, which is just to translate by mv_inplane + cam_to_surface.pretranslate (mv_inplane); + flags.set (cmm_fl::done, true); + + } else { + if constexpr (debug_move) { + std::cout << "End of movement is NOT in ti0 " << this->ti0[0] << "," << this->ti0[1] << "," << this->ti0[2] << ". Look for start neighbours\n"; + } + + // Test 3 neighbours across the edges to find any for which the start location is also within-boundary + flags.set (cmm_fl::detected_crossing, false); + flags.set (cmm_fl::vertex_crossing, false); + std::array _ti_2n = { std::numeric_limits::max() }; + sm::vec_tn_2n = {}; + for (uint32_t i = 0u; i < 3u; i++) { + uint32_t i1 = i; + uint32_t i2 = (i + 1) % 3u; + auto [_ti, _tn] = this->find_other_triangle_containing (this->ti0[i1], this->ti0[i2], this->ti0); + if (_ti[0] != std::numeric_limits::max()) { + // Test to see if start location was inside a neighbour + sm::vec, 3> tv_nb = this->triangle_vertices (_ti, model_to_scene); + _tn = this->triangle_normal (tv_nb); + + auto [is, h] = sm::geometry::ray_tri_intersection (tv_nb[0], tv_nb[1], tv_nb[2], hov_sf + (_tn / 2.0f), -_tn); + sm::vec mv_orthog_nb = _tn * (mv_inplane.dot (_tn) / (_tn.dot(_tn))); + sm::vec mv_inplane_nb = mv_inplane - mv_orthog_nb; + if constexpr (debug_move) { + std::cout << "endis? ray_tri_intersection with " << (hov_sf + mv_inplane_nb + (_tn / 2.0f)) << "," << -_tn << std::endl; + } + auto [endis, endh] = sm::geometry::ray_tri_intersection (tv_nb[0], tv_nb[1], tv_nb[2], hov_sf + mv_inplane_nb + (_tn / 2.0f), -_tn); + if constexpr (debug_move) { + std::cout << "Start of move " << (is ? "IS" : "is NOT") + << " in " << _ti[0] << "," << _ti[1] << "," << _ti[2] << " / " << tv_nb << std::endl; + std::cout << "End of move " << (endis ? "IS" : "is NOT") + << " in that triangle" << std::endl; + } + + // Here, start is in original, end may not be in original. This + // is an 'intersection detected crossing' of a triangle edge + // which wasn't picked up with compute_crossing_location + if (endis) { + // End is in neighbour so this is a detected crossing + if constexpr (debug_move) { std::cout << "DETECTED crossing! Pass on to next loop!\n"; } + flags.set (cmm_fl::detected_crossing, true); + detected_edge = { this->ti0[i1], this->ti0[i2] }; + detected_edgevec = tv_nb[i2] - tv_nb[i1]; + break; // out of for + } else { // end not in neighbour + if (is) { // start is in neighbour tri (will re-orient to this and re-loop) + _ti_2n = _ti; + _tn_2n = _tn; + break; // out of for + } // else end is not in neighbour, and neither is start. This + // occurs if the end is ON the boundary, but precision errors + // mean this location isn't 'in' either start or neighbour + // (according to ray_tri_intersection) + } + } + } + + // Test one-neighbours here if necessary (that is, if the two neighbour test above failed) + if (flags.test (cmm_fl::detected_crossing) == false && + _ti_2n[0] == std::numeric_limits::max()) { + auto onens = this->find_one_neighbours (this->ti0); + for (auto onen : onens) { + // Are we in this one? + auto [_ti, _tn] = onen; + sm::vec, 3> tv_nb = this->triangle_vertices (_ti, model_to_scene); + _tn = this->triangle_normal (tv_nb); + auto [is, h] = sm::geometry::ray_tri_intersection (tv_nb[0], tv_nb[1], tv_nb[2], hov_sf + (_tn / 2.0f), -_tn); + sm::vec mv_orthog_nb = _tn * (mv_inplane.dot (_tn) / (_tn.dot(_tn))); + sm::vec mv_inplane_nb = mv_inplane - mv_orthog_nb; + if constexpr (debug_move) { + std::cout << "endis ONE-n? ray_tri_intersection with " << (hov_sf + mv_inplane_nb + (_tn / 2.0f)) << "," << -_tn << std::endl; + } + auto [endis, endh] = sm::geometry::ray_tri_intersection (tv_nb[0], tv_nb[1], tv_nb[2], hov_sf + mv_inplane_nb + (_tn / 2.0f), -_tn); + if constexpr (debug_move) { + std::cout << "Start of move " << (is ? "IS" : "is NOT") + << " in ONE-neighbour " << _ti[0] << "," << _ti[1] << "," << _ti[2] << " / " << tv_nb << std::endl; + std::cout << "And End of move " << (endis ? "IS" : "is NOT") + << " in that ONE-neighbour " << std::endl; + } + + if (endis) { + // End is in one-neighbour so this is a detected crossing + if constexpr (debug_move) { std::cout << "DETECTED crossing over ONE-neighbour! Pass on to next loop!\n"; } + flags.set (cmm_fl::vertex_crossing, true); + detected_edge = { this->common_vertex (this->ti0, _ti), std::numeric_limits::max() }; + detected_edgevec = {}; // to be the cross product of the last-triangle normal and the newtri normal. + detected_newtri = _ti; + break; // out of for + } else { // end not in one-neighbour + if (is) { // start is in one-neighbour tri (will re-orient to this and re-loop) + _ti_2n = _ti; + _tn_2n = _tn; + break; // out of for + } // else end is not in one-neighbour, and neither is start. + } + } + } + + if (_ti_2n[0] != std::numeric_limits::max()) { + // Now we know an alternative start triangle for the movement. Re-orient to this and re-loop + this->ti0 = _ti_2n; + ne.tris.push_back (this->ti0); + this->tn0 = _tn_2n; + // recompute mv_inplane for this neighbour triangle + mv_orthog = this->tn0 * (mv_sf.dot (this->tn0) / (this->tn0.dot (this->tn0))); + mv_inplane = mv_sf - mv_orthog; // sf + } else if (flags.test (cmm_fl::detected_crossing)) { + // We didn't find an alternative start triangle, but we did detect an edge crossing by intersection, so continue. + } else if (flags.test (cmm_fl::vertex_crossing)) { + // We didn't find an alternative start triangle, but we did detect a vertex crossing, so continue. + } else { + // End of move not evidently in self or neighbours, so assume it's bang on the boundary + if constexpr (debug_move2) { std::cout << "Movement complete on boundary ASSUMPTION\n"; } + cam_to_surface.pretranslate (mv_inplane); + flags.set (cmm_fl::done, true); + } + + } // single movement if/else + + } // compute_crossing_location if/else + + } // triangle traversing while loop + + // Raise cam_to_surface up by hoverheight and then return + cam_to_surface.pretranslate (hoverheight * this->tn0); + if constexpr (debug_move) { + std::cout << "looping mv_inplanes completed. Final camloc_sf: " << cam_to_surface.translation() << std::endl; + } + return cam_to_surface; + + } // compute_mesh_movement + + }; // struct NavMesh + +} // namespace diff --git a/mplot/NormalsVisual.h b/mplot/NormalsVisual.h index cdac03e2..4a92e8cb 100644 --- a/mplot/NormalsVisual.h +++ b/mplot/NormalsVisual.h @@ -20,6 +20,8 @@ namespace mplot { { this->mymodel = _mymodel; this->viewmatrix = _mymodel->getViewMatrix(); + // We create the model's navmesh, in case it wasn't already done + this->mymodel->make_navmesh(); } void initializeVertices() @@ -49,24 +51,29 @@ namespace mplot { _clr, tube_r, this->arrowhead_prop, cone_r, this->shapesides); } - std::array ti = {}; - sm::vec nv = {}; - sm::vec nvc = {}; - sm::vec nvd = {}; - sm::vec pos = {}; - // We also have vp1 (public) and triangles (also public) - for (auto t : mymodel->triangles) { - std::tie(ti, nv, nvc, nvd) = t; - // Plot tn at mean location of ti - pos = (mymodel->vp1[ti[0]] + mymodel->vp1[ti[1]] + mymodel->vp1[ti[2]]) / 3.0f; - // Mesh triangle normals - this->computeArrow (pos, (pos + nv * this->scale_factor), - clr, tube_r, this->arrowhead_prop, cone_r, this->shapesides); - // Computed triangle normals - this->computeArrow (pos, (pos + nvc * this->scale_factor), - clrnc, tube_r, this->arrowhead_prop, cone_r, this->shapesides); - this->computeArrow (pos, (pos + nvd * scale_factor), - clrnd, tube_r, this->arrowhead_prop, cone_r, this->shapesides); + + // If we also have the navmesh, then use its triangles to show face normals + if (mymodel->navmesh) { + + std::array ti = {}; + sm::vec nv = {}; + sm::vec nvc = {}; + sm::vec nvd = {}; + sm::vec pos = {}; + + for (auto t : mymodel->navmesh->triangles) { + std::tie(ti, nv, nvc, nvd) = t; + // Plot tn at mean location of ti + pos = (mymodel->navmesh->vertex[ti[0]] + mymodel->navmesh->vertex[ti[1]] + mymodel->navmesh->vertex[ti[2]]) / 3.0f; + // Mesh triangle normals + this->computeArrow (pos, (pos + nv * this->scale_factor), + clr, tube_r, this->arrowhead_prop, cone_r, this->shapesides); + // Computed triangle normals + this->computeArrow (pos, (pos + nvc * this->scale_factor), + clrnc, tube_r, this->arrowhead_prop, cone_r, this->shapesides); + this->computeArrow (pos, (pos + nvd * scale_factor), + clrnd, tube_r, this->arrowhead_prop, cone_r, this->shapesides); + } } }; diff --git a/mplot/SphericalProjectionVisual.h b/mplot/SphericalProjectionVisual.h new file mode 100644 index 00000000..95f12003 --- /dev/null +++ b/mplot/SphericalProjectionVisual.h @@ -0,0 +1,138 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mplot +{ + //! This class creates a flat projection of spherical data provided as vvecs of + //! latitude-longitude pairs and scalar or vector values. Use VisualDataModel? + template requires std::is_floating_point_v + struct SphericalProjectionVisual : public mplot::VisualModel + { + SphericalProjectionVisual() {} + SphericalProjectionVisual (const sm::vec _offset) : mplot::VisualModel(_offset) {} + + sm::geometry::spherical_projection::type proj_type = sm::geometry::spherical_projection::type::mercator; + + sm::vec project (const sm::vec& ll, const T radius) + { + 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) { + return sm::geometry::spherical_projection::cassini (ll, radius, this->lambda0); + } else { + return sm::geometry::spherical_projection::mercator (ll, radius, this->lambda0); + } + } + + void initializeVertices() + { + this->vertexPositions.clear(); + this->vertexNormals.clear(); + this->vertexColors.clear(); + this->indices.clear(); + this->xy.resize (this->latlong.size()); + for (uint32_t i = 0; i < this->latlong.size(); ++i) { + this->xy[i] = this->project (this->latlong[i], this->radius).plus_one_dim().template as(); + } + this->voronoi2d(); + } + + //! Compute a triangle from 3 arbitrary corners + void computeTriangle (sm::vec c1, sm::vec c2, sm::vec c3, const std::array& colr) + { + // v is the face normal + sm::vec u1 = c1-c2; + sm::vec u2 = c2-c3; + sm::vec v = u1.cross(u2); + v.renormalize(); + // Push corner vertices + this->vertex_push (c1.as_float(), this->vertexPositions); + this->vertex_push (c2.as_float(), this->vertexPositions); + this->vertex_push (c3.as_float(), this->vertexPositions); + // Colours/normals + for (uint32_t i = 0; i < 3U; ++i) { + this->vertex_push (colr, this->vertexColors); + this->vertex_push (v.as_float(), this->vertexNormals); + } + this->indices.push_back (this->idx++); + this->indices.push_back (this->idx++); + this->indices.push_back (this->idx++); + } + + void voronoi2d() + { + // Use mplot::range to find the extents of dataCoords. From these create a + // rectangle to pass to diagram_generate. + int ncoords = static_cast(this->xy.size()); + + jcv::manager vorman; // we need double precision for projections, float may run into trouble + vorman.border_width = this->border_width; + vorman.diagram_generate (this->xy); + + int diag_nsites = vorman.diagram_numsites(); + if (diag_nsites != ncoords) { + std::cout << "WARNING: diagram's ncoords (" << diag_nsites << ") != ncoords (" << ncoords << ")?!?!\n"; + } + + // We obtain access to the Voronoi cell sites: + const jcv::site* sites = vorman.diagram_get_sites(); + + for (int i = 0; i < vorman.diagram_numsites() && i < ncoords; ++i) { + const jcv::site* site = &sites[i]; + jcv::graphedge* e = site->edges; // The very first edge + while (e) { + // Set z. Should be done in jcvoronoi, but haven't found out how + e->pos[0][2] = this->xy[i][2]; + e->pos[1][2] = e->pos[0][2]; + e = e->next; + } + } + + // To draw triangles iterate over the 'sites' and draw triangles + for (int i = 0; i < vorman.diagram_numsites() && i < ncoords; ++i) { + const jcv::site* site = &sites[i]; + const jcv::graphedge* e = site->edges; + std::array c = mplot::colour::black; + if (static_cast(site->index) < this->colour.size()) { c = this->colour[site->index]; } + while (e) { + this->computeTriangle (site->p, e->pos[0], e->pos[1], c); + if constexpr (show_centres) { + auto sphc = (site->p + e->pos[0] + e->pos[1]) / T{3}; + this->computeSphere (sphc, this->centre_col, this->centre_rad, 4, 4); + } + e = e->next; + } + } + } + + // latlong, supplied by user + sm::vvec> latlong; + // Colour, supplied by user + sm::vvec> colour; + // xy, result of projection, but in 3D. double precision always + sm::vvec> xy; + // The radius of our sphere + T radius = T{1}; + // The longitudinal offset + T lambda0 = T{0}; + // Params for equirectangular projection + T phi0 = T{0}; + T phi1 = T{0}; + // A border width for the Voronoi cells + T border_width = T{0.001}; + + // To debug the centres of the Voronoi cells, set show_centres true + static constexpr bool show_centres = false; + static constexpr std::array centre_col = mplot::colour::black; + static constexpr T centre_rad = T{0.005}; + }; +} diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index a0379830..25940c46 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -296,6 +296,8 @@ namespace mplot { if (found_model == true) { this->vm.erase (this->vm.begin() + modelId); } } + void clear () { this->vm.clear(); } + void set_cursorpos (double _x, double _y) { this->cursorpos = {static_cast(_x), static_cast(_y)}; } //! A callback function @@ -513,6 +515,7 @@ namespace mplot { this->sceneview.translate (this->scenetrans_default); this->sceneview_tr.setToIdentity(); this->sceneview_tr.translate (this->scenetrans_default); + this->d_to_rotation_centre = -this->scenetrans_default[2]; } //! Set the scene's x and y values at the same time. @@ -891,7 +894,7 @@ namespace mplot { sm::vec rotation_centre = {}; // Distance to the 'rotation centre'. Used to scale the effect of the scroll wheel - float d_to_rotation_centre = 5.0f; + float d_to_rotation_centre = -zDefault; //! The projection matrix is a member of this class. Value is set during setPerspective() or setOrthographic() sm::mat44 projection; @@ -1119,6 +1122,7 @@ namespace mplot { this->sceneview_tr.translate (this->scenetrans_default); this->scenetrans_delta.zero(); this->rotation_delta.reset(); + this->d_to_rotation_centre = -this->scenetrans_default[2]; needs_render = true; } @@ -1387,8 +1391,7 @@ namespace mplot { // Add the depth at which the object lies. Use forward projection to determine the // correct z coordinate for the inverse projection. This assumes only one object. - sm::vec st = this->savedSceneview.translation(); - sm::vec point = { 0.0f, 0.0f, st.z(), 1.0f }; + sm::vec point = { 0.0f, 0.0f, -this->d_to_rotation_centre, 1.0f }; sm::vec pp = this->projection * point; float coord_z = pp[2] / pp[3]; // divide by pp[3] is divide by/normalise by 'w'. diff --git a/mplot/VisualModelBase.h b/mplot/VisualModelBase.h index 14e41a97..0f872e0e 100644 --- a/mplot/VisualModelBase.h +++ b/mplot/VisualModelBase.h @@ -35,7 +35,7 @@ #include -#include +#include #include #include #include @@ -47,6 +47,7 @@ #include #include +#include namespace mplot { @@ -216,79 +217,6 @@ namespace mplot { this->indices.reserve (6u * n_vertices); } - /** - * Neighbour vertex mesh code - */ - - // Minimum set of vertices to generate a topological mesh - std::vector> vp1; - // Maps index in vp1 to the original this->indices index - sm::vvec> vp1_to_indices; - // The edges that make up the same triangles as are shown with this->indices, but in terms of vp1. - // Each edge must be two indices in *ascending numerical order* - std::set> edges; - // Triangles too. Might be more useful than edges. Triangle given as indices into vp1 - sm::vvec, sm::vec, sm::vec, sm::vec>> triangles; - - // Return index of vp1 that is closest to scene_coord. Can use vp1_to_indices to find the indices - // into vertexPositions and vertexNormals that this index in the topographic mesh relates to. - uint32_t find_vp1_nearest (const sm::vec& scene_coord) const - { - uint32_t i = std::numeric_limits::max(); - // Brute force it. (But we have a mesh; can this guarantee a faster search? I don't think so) - float min_d = std::numeric_limits::max(); - for (uint32_t j = 0; j < this->vp1.size(); ++j) { - sm::vec<> vcoord = (this->viewmatrix * vp1[j]).less_one_dim(); - //std::cout << "vcoord: " << vcoord; - float d = (scene_coord - vcoord).length(); - //std::cout << ", distance " << d << " from " << scene_coord << std::endl; - if (d < min_d) { - min_d = d; - i = j; - } - } - return i; - } - - sm::vec, 3> triangle_vertices (const std::array& tri_indices) const - { - sm::vec, 3> trivert; - if (tri_indices[0] < this->vp1.size()) { trivert[0] = this->vp1[tri_indices[0]]; } - if (tri_indices[1] < this->vp1.size()) { trivert[1] = this->vp1[tri_indices[1]]; } - if (tri_indices[2] < this->vp1.size()) { trivert[2] = this->vp1[tri_indices[2]]; } - return trivert; - } - - sm::vvec neighbours (const uint32_t _idx) const - { - sm::vvec rtn; - // Search edges to find those that include _idx and then pack up the other ends in a return object - for (auto e : this->edges) { - // we have e[0] and e[1] - if (e[0] == _idx) { - // neighb is e[1] - rtn.push_back (e[1]); - } else if (e[1] == _idx) { - // neighb is e[0] - rtn.push_back (e[0]); - } - } - return rtn; - } - - sm::vvec> neighbour_triangles (const uint32_t _idx) const - { - sm::vvec> rtn; - for (auto t: this->triangles) { - auto [ti, tn, tnc, tnd] = t; - // If it includes _idx, add it to rtn - if (ti[0] == _idx || ti[1] == _idx || ti[2] == _idx) { - rtn.push_back (ti); - } - } - return rtn; - } - // Get a single position from vertexPositions, using the index into the vector // interpretation of vertexPositions sm::vec get_position (const uint32_t vec_idx) const @@ -305,160 +233,120 @@ namespace mplot { return (*vn)[vec_idx]; } - // Return a tuple containing crossing location, triangle identity (three indices) and triangle normal vector - std::tuple, std::array, sm::vec> - find_triangle_crossing (const sm::vec& coord, const sm::vec& vdir) const - { - for (auto tri : triangles) { - auto [ti, tn, tnc, tnd] = tri; - auto [isect, p] = sm::algo::ray_tri_intersection (this->vp1[ti[0]], this->vp1[ti[1]], this->vp1[ti[2]], coord - (vdir / 2.0f), vdir); - if (isect) { return {p, ti, tn}; } - } - - // Failed to find, return container full of maxes - sm::vec p = {}; - p.set_from (std::numeric_limits::max()); - constexpr uint32_t umax = std::numeric_limits::max(); - return {p , std::array{umax, umax, umax}, p}; - - } - - // Find a triangle containing indices a and b that isn't 'not_this' and return, along with its normal. - std::tuple, sm::vec> - find_other_triangle_containing (const uint32_t a, const uint32_t b, const std::array& not_this) const - { - constexpr bool debug_normals = false; - - constexpr uint32_t umax = std::numeric_limits::max(); - std::array other = {umax, umax, umax}; - constexpr float fmax = std::numeric_limits::max(); - sm::vec other_n = {fmax, fmax, fmax}; - sm::vec my_n = {fmax, fmax, fmax}; // debug - for (auto tri : triangles) { - auto [ti, tn, tnc, tnd] = tri; - if (ti == not_this) { - if constexpr (debug_normals) { my_n = tn; } - continue; - } - if ((ti[0] == a && (ti[1] == b || ti[2] == b)) - || (ti[1] == a && (ti[0] == b || ti[2] == b)) - || (ti[2] == a && (ti[0] == b || ti[1] == b))) { - other = ti; - other_n = tn; - if constexpr (!debug_normals) { break; } - } - } - if constexpr (debug_normals) { - std::cout << "my_n: " << my_n << " and other_n: " << other_n << std::endl; - } - return {other, other_n}; - } + /** + * Neighbour vertex mesh code. + */ - // Find the location, and the triangle indices at which a ray between coord and the model - // centroid cross - the 'penetration point'. This is essentially ray casting and if it gets - // used extensively, should go into a compute shader. - std::tuple, std::array, sm::vec> - find_triangle_crossing (const sm::vec& coord) const - { - sm::vec vdir = this->bb.mid() - coord; - vdir.renormalize(); - return this->find_triangle_crossing (coord, vdir); - } + // Our navigation mesh data struct + std::unique_ptr navmesh; /*! * Post-process vertices to generate a neighbour relationship mesh. The usual vertices and - * indices may not be useful to help a ground-based agent to navigate the surface defined by - * the mesh. This is because vertices may be duplicated at any location, so that adjacent - * faces can have different normals and colours. + * indices may not be useful to help an agent to navigate the surface defined by the + * mesh. This is because vertices may be duplicated at any location, so that adjacent faces + * can have different normals and colours. * * To help guide movement across a mesh, it would be useful to have a mesh that always gives * neighbour relationships. */ - void vertex_postprocess() // make_neighbour_mesh() ? + void make_navmesh() { - constexpr bool debug = false; - constexpr bool debug_reorder = false; + constexpr bool debug_mn = false; + if constexpr (debug_mn) { std::cout << "make_navmesh: Called" << std::endl; } - if constexpr (debug) { std::cout << __func__ << " called\n"; } - // For each vertex, search for other vertices that have the same or almost the same location + if (this->navmesh) { return; } // already made it - // Treat vertexPositions as a vector of vec: - auto vp = reinterpret_cast>*>(&this->vertexPositions); - uint32_t vps = vp->size(); + if (this->flags.test (vm_bools::compute_bb) == false) { + throw std::runtime_error ("make_navmesh requires compute_bb flag to be true"); + } + this->update_bb(); - constexpr float vlen_thresh = 0.0f; + // Create a new navmesh + this->navmesh = std::make_unique(); - // For each entry in vp1, list the entries in vertexPositions that are in the same locn - std::map> equiv; + // Copy the bounding box + navmesh->bb = this->bb; - // Populate equiv - for (uint32_t i = 0; i < vps; ++i) { - for (uint32_t j = 0; j < vps; ++j) { - if (((*vp)[i] - (*vp)[j]).length() <= vlen_thresh) { equiv[i].push_back (j); } + // Treat vertexPositions as a vector of vec: + auto vp = reinterpret_cast>*>(&this->vertexPositions); + + uint32_t vps = vp->size(); + std::unordered_map, std::set, sm::vec::hash> equiv_v; + uint32_t i = 0; + for (auto p : *vp) { equiv_v[p].insert (i++); } + std::map> equiv; + for (auto e : equiv_v) { equiv[*e.second.begin()] = e.second; } + if constexpr (debug_mn) { + for (auto e : equiv) { + std::cout << "make_navmesh: equiv[" << e.first << "] = "; + for (auto idx : e.second) { std::cout << idx << ","; } + std::cout << std::endl; } + std::cout << "make_navmesh: Populated equiv which has " + << equiv.size() << " vvecs" << std::endl; } - // Prune duplicates - std::erase_if (equiv, [](const auto& eq) { const auto& [k, v] = eq; return v.find_first_of (k) > 0; }); - // Make inverse of equiv to translate from original (indices, vertexPositions) index to new topographic mesh index - sm::vvec equiv_top (vps, 0); - this->vp1_to_indices.resize (equiv.size()); - uint32_t i = 0; - for (auto eq : equiv) { - if constexpr (debug) { std::cout << "equiv[" << eq.first << "] = " << eq.second << std::endl; } - this->vp1_to_indices[i] = eq.second; - for (auto ev : eq.second) { - equiv_top[ev] = i; + // Make inverse of equiv to translate from original (indices, vertexPositions) index to + // new topographic mesh index + sm::vvec navmesh_idx (vps, 0); + navmesh->vertexidx_to_indices.resize (equiv.size()); + + uint32_t vcount = 0; + i = 0; + for (auto eqs : equiv) { + vcount += eqs.second.size(); + navmesh->vertexidx_to_indices[i].resize (eqs.second.size()); + std::copy (eqs.second.begin(), eqs.second.end(), navmesh->vertexidx_to_indices[i].begin()); + for (auto ev : eqs.second) { + if constexpr (debug_mn) { std::cout << "make_navmesh: set navmesh_idx[" << ev << "] = " << i << std::endl; } + navmesh_idx[ev] = i; } ++i; } - if constexpr (debug) { - uint32_t cntr = 0; - for (auto eqi : equiv_top) { - std::cout << "equiv_top[" << cntr++ << "] = " << eqi << std::endl; - } + if constexpr (debug_mn) { + std::cout << "make_navmesh: Created equiv inverse" << std::endl; } - // Can now populate vp1, a vector of coordinates, if required, or simply access (*vp) as needed using equiv.first - vp1.resize (equiv.size(), {0}); - i = 0; - for (auto eq : equiv) { vp1[i++] = (*vp)[eq.first]; } - if constexpr (debug) { - for (i = 0; i < vp1.size(); ++i) { - std::cout << "vp1[" << i << "] = " << vp1[i] << std::endl; - } + if (vcount != vps) { + std::cout << "make_navmesh: WARNING: Vertex count from equiv is " << vcount + << " which should (but does not) equal " << vps << std::endl; } - // Lastly, generate edges. For which we require use of indices, which is expressed in - // terms of the old indices. That lookup is equiv_top. + // Can now populate vertex, a vector of coordinates, if required, or simply access (*vp) + // as needed using equiv.first + navmesh->vertex.resize (equiv.size(), {0}); + i = 0; + for (auto eq : equiv) { navmesh->vertex[i++] = (*vp)[eq.first]; } // FIXME? + // Lastly, generate edges. For which we require use of indices, which is expressed in + // terms of the old indices. That lookup is navmesh_idx. for (uint32_t i = 0; i < this->indices.size(); i += 3) { // Each three entries in indices is a triangle containing 3 edges. NB: Edges must be listed in ascending order! - std::array e = { equiv_top[indices[i]], equiv_top[indices[i+1]] }; + std::array e = { navmesh_idx[indices[i]], navmesh_idx[indices[i+1]] }; if (e[0] > e[1]) { uint32_t t = e[0]; e[0] = e[1]; e[1] = t; } - this->edges.insert (e); + navmesh->edges.insert (e); - e = { equiv_top[indices[i]], equiv_top[indices[i+2]] }; + e = { navmesh_idx[indices[i]], navmesh_idx[indices[i+2]] }; if (e[0] > e[1]) { uint32_t t = e[0]; e[0] = e[1]; e[1] = t; } - this->edges.insert (e); + navmesh->edges.insert (e); - e = { equiv_top[indices[i+1]], equiv_top[indices[i+2]] }; + e = { navmesh_idx[indices[i+1]], navmesh_idx[indices[i+2]] }; if (e[0] > e[1]) { uint32_t t = e[0]; e[0] = e[1]; e[1] = t; } - this->edges.insert (e); + navmesh->edges.insert (e); - // Direct population of triangles - std::array t = { equiv_top[indices[i]], equiv_top[indices[i+1]], equiv_top[indices[i+2]] }; + // Direct population of triangles. Three indices and a 4th number to hold flags (with bit0 meaning edge-triangle) + std::array t = { navmesh_idx[indices[i]], navmesh_idx[indices[i+1]], navmesh_idx[indices[i+2]], 0 }; // The normal vector for this triangle could be obtained from the mesh normals, but // we can't trust them (though they're easy to get, as we're dealing with indices @@ -469,37 +357,29 @@ namespace mplot { // Compute trinorm as well and compare with the one from the mesh - perhaps it's // different? We really want the right normal. - const sm::vec& tv0 = vp1[t[0]]; - const sm::vec& tv1 = vp1[t[1]]; - const sm::vec& tv2 = vp1[t[2]]; - sm::vec nx = (tv1 - tv0); - sm::vec ny = (tv2 - tv0); - sm::vec n = nx.cross (ny); + const sm::vec& tv0 = navmesh->vertex[t[0]]; + const sm::vec& tv1 = navmesh->vertex[t[1]]; + const sm::vec& tv2 = navmesh->vertex[t[2]]; + sm::vec nx = (tv1 - tv0); + sm::vec ny = (tv2 - tv0); + sm::vec n = nx.cross (ny); n.renormalize(); // Check rotational sense of triangles? if (n.dot (trinorm) < 0.0f) { // need to swap order in t: - if constexpr (debug_reorder) { std::cout << "Triangle reordered (corners 1 and 2 switched)\n"; } uint32_t ti = t[2]; t[2] = t[1]; t[1] = ti; n = -n; // Also reverse n } - this->triangles.push_back ({t, n, nx, ny}); // n is computed normal + navmesh->triangles.push_back ({t, n, nx, ny}); // n is computed normal } + if constexpr (debug_mn) { std::cout << "make_navmesh: Created triangles" << std::endl; } - if constexpr (debug) { - for (auto e : edges) { - std::cout << "Edge: " << e[0] << "," << e[1] << std::endl; - } - for (auto t : this->triangles) { - auto [ti, tn, tnc, tnd] = t; - std::cout << "Tri: " << ti[0] << "," << ti[1] << "," << ti[2] << ", norm " << tn << std::endl; - } - std::cout << this->edges.size() << " edges and " << this->triangles.size() << " triangles in model '" << this->name << "'\n"; - } + //navmesh->mark_edge_triangles(); + //if constexpr (debug_mn) { std::cout << "make_navmesh: Marked edge triangles and done." << std::endl; } } /** @@ -533,6 +413,8 @@ namespace mplot { void postmultViewMatrix (const sm::mat44& m) { this->viewmatrix = this->viewmatrix * m; } void premultViewMatrix (const sm::mat44& m) { this->viewmatrix = m * this->viewmatrix; } + void scaleViewMatrix (const float by) { this->viewmatrix.scale (by); } + virtual void setSceneMatrixTexts (const sm::mat44& sv) = 0; //! When setting the scene matrix, also have to set the text's scene matrices. @@ -2102,6 +1984,165 @@ namespace mplot { } } + /*! + * Compute an ellipsoid surface of the form x*x/a*a + y*y/b*b + z*z/c*c = 1 + * + * so is the offset position for the ellipsoid + * + * sc is the colour at one end of the z axis + * + * sc2 is the colour at the other end + * + * abc are the three ellipsoid parameters + * + * rings is the number of rings along the z axis + * + * segments is the number of segments in each ring along the z axis + * + * tr transform matrix to apply to each point in the ellipse (applied before so is applied + * as a transform) + */ + void computeEllipsoid (sm::vec so, + std::array sc, + std::array sc2, + sm::vec abc, + int rings = 10, int segments = 12, + sm::mat44 tr = sm::mat44{}) + { + // We have two angular parameters t and t2. t in range 0-2pi and t2 in range 0-pi. t + // gives the 'xy' ellipse; t2 gives the change in size of the xy ellipse as the z axis + // is traversed. + float t = 0.0f; + float t2 = 0.0f; + // used computing normals + sm::vec two_over_abcsq = 2.0f / abc.sq(); + // Holding the coordinates of each point on the ellipsoid as we compute it + sm::vec p = {}; + // The normal vector + sm::vec n = {}; + + // sm::vec versions of the colours + sm::vec _sc = {}; + _sc.set_from (sc); + sm::vec _sc2 = {}; + _sc2.set_from (sc2); + + // Push the central point + p = { 0, 0, abc[2] }; + this->vertex_push (so + (tr * p).less_one_dim(), this->vertexPositions); + n = { 0, 0, 1 }; + this->vertex_push (n, this->vertexNormals); + this->vertex_push (_sc, this->vertexColors); + + GLuint capMiddle = this->idx++; + GLuint ringStartIdx = this->idx; + GLuint lastRingStartIdx = this->idx; + + t2 = sm::mathconst::pi / rings; + p[2] = abc[2] * std::cos(t2); + + bool firstseg = true; + for (int j = 0; j < segments; j++) { + + t = sm::mathconst::two_pi * static_cast(j) / segments; + p[0] = abc[0] * std::cos(t) * std::sin(t2); + p[1] = abc[1] * std::sin(t) * std::sin(t2); + + sm::vec<> pp = (tr * p).less_one_dim(); + this->vertex_push (so + pp, this->vertexPositions); + + n = (tr * (p * two_over_abcsq)).less_one_dim(); + n.renormalize(); + this->vertex_push (n, this->vertexNormals); + + sm::vec sc_ring = _sc * (1.0f - 1.0f / rings) + _sc2 * 1.0f / rings; + this->vertex_push (sc_ring, this->vertexColors); + + if (!firstseg) { + this->indices.push_back (capMiddle); + this->indices.push_back (this->idx-1); + this->indices.push_back (this->idx++); + } else { + this->idx++; + firstseg = false; + } + } + this->indices.push_back (capMiddle); + this->indices.push_back (this->idx-1); + this->indices.push_back (capMiddle+1); + + // Now add the triangles around the rings + for (int i = 2; i < rings; i++) { + + t2 = sm::mathconst::pi * (static_cast(i) / rings); + p[2] = abc[2] * std::cos(t2); + + sm::vec sc_ring = _sc * (1.0f - static_cast(i) / rings) + _sc2 * static_cast(i) / rings; + + for (int j = 0; j < segments; j++) { + + // "current" segment + t = sm::mathconst::two_pi * static_cast(j) / segments; + p[0] = abc[0] * std::cos(t) * std::sin(t2); + p[1] = abc[1] * std::sin(t) * std::sin(t2); + + // NB: Only add ONE vertex per segment. ALREADY have the first ring! + sm::vec<> pp = (tr * p).less_one_dim(); + this->vertex_push (so + pp, this->vertexPositions); + // The vertex normal of a vertex that makes up a sphere is + // just a normal vector in the direction of the vertex. + n = (tr * (p * two_over_abcsq)).less_one_dim(); + n.renormalize(); + this->vertex_push (n, this->vertexNormals); + + this->vertex_push (sc_ring, this->vertexColors); + + if (j == segments - 1) { + // Last vertex is back to the start + this->indices.push_back (ringStartIdx++); + this->indices.push_back (this->idx); + this->indices.push_back (lastRingStartIdx); + this->indices.push_back (lastRingStartIdx); + this->indices.push_back (this->idx++); + this->indices.push_back (lastRingStartIdx+segments); + } else { + this->indices.push_back (ringStartIdx++); + this->indices.push_back (this->idx); + this->indices.push_back (ringStartIdx); + this->indices.push_back (ringStartIdx); + this->indices.push_back (this->idx++); + this->indices.push_back (this->idx); + } + } + lastRingStartIdx += segments; + } + + // bottom cap + + // Push the central point of the bottom cap + p = { 0, 0, -abc[2] }; + this->vertex_push (so + (tr * p).less_one_dim(), this->vertexPositions); + n = { 0, 0, -1 }; + this->vertex_push (n, this->vertexNormals); + this->vertex_push (_sc2, this->vertexColors); + capMiddle = this->idx++; + firstseg = true; + // No more vertices to push, just do the indices for the bottom cap + ringStartIdx = lastRingStartIdx; + for (int j = 0; j < segments; j++) { + if (j != segments - 1) { + this->indices.push_back (capMiddle); + this->indices.push_back (ringStartIdx++); + this->indices.push_back (ringStartIdx); + } else { + // Last segment + this->indices.push_back (capMiddle); + this->indices.push_back (ringStartIdx); + this->indices.push_back (lastRingStartIdx); + } + } + } + /*! * Compute vertices for an icosahedron. */ @@ -2726,17 +2767,17 @@ namespace mplot { sm::vec l_n_1 = e_p.less_one_dim() + (n_ortho * hw) - n_vec; sm::vec l_n_2 = n_p.less_one_dim() + (n_ortho * hw) + n_vec; - std::bitset<2> isect = sm::algo::segments_intersect (l_p_1, l_p_2, l_c_1, l_c_2); + std::bitset<2> isect = sm::geometry::segments_intersect (l_p_1, l_p_2, l_c_1, l_c_2); if (isect.test(0) == true && isect.test(1) == false) { // test for intersection but not colinear - c1_p = sm::algo::crossing_point (l_p_1, l_p_2, l_c_1, l_c_2); + c1_p = sm::geometry::crossing_point (l_p_1, l_p_2, l_c_1, l_c_2); } else if (isect.test(0) == true && isect.test(1) == true) { c1_p = /*s_p.less_one_dim() +*/ (c_ortho * hw); } else { // no intersection. prev could have been start c1_p = /*s_p.less_one_dim() +*/ (c_ortho * hw); } - isect = sm::algo::segments_intersect (l_c_1, l_c_2, l_n_1, l_n_2); + isect = sm::geometry::segments_intersect (l_c_1, l_c_2, l_n_1, l_n_2); if (isect.test(0) == true && isect.test(1) == false) { - c4_p = sm::algo::crossing_point (l_c_1, l_c_2, l_n_1, l_n_2); + c4_p = sm::geometry::crossing_point (l_c_1, l_c_2, l_n_1, l_n_2); } else if (isect.test(0) == true && isect.test(1) == true) { c4_p = e_p.less_one_dim() + (c_ortho * hw); } else { // no intersection, prev could have been end @@ -2751,18 +2792,18 @@ namespace mplot { sm::vec o_l_n_1 = e_p.less_one_dim() - (n_ortho * hw) - n_vec; sm::vec o_l_n_2 = n_p.less_one_dim() - (n_ortho * hw) + n_vec; - isect = sm::algo::segments_intersect (o_l_p_1, o_l_p_2, o_l_c_1, o_l_c_2); + isect = sm::geometry::segments_intersect (o_l_p_1, o_l_p_2, o_l_c_1, o_l_c_2); if (isect.test(0) == true && isect.test(1) == false) { // test for intersection but not colinear - c2_p = sm::algo::crossing_point (o_l_p_1, o_l_p_2, o_l_c_1, o_l_c_2); + c2_p = sm::geometry::crossing_point (o_l_p_1, o_l_p_2, o_l_c_1, o_l_c_2); } else if (isect.test(0) == true && isect.test(1) == true) { c2_p = /*s_p.less_one_dim()*/ - (c_ortho * hw); } else { // no intersection. prev could have been start c2_p = /*s_p.less_one_dim()*/ - (c_ortho * hw); } - isect = sm::algo::segments_intersect (o_l_c_1, o_l_c_2, o_l_n_1, o_l_n_2); + isect = sm::geometry::segments_intersect (o_l_c_1, o_l_c_2, o_l_n_1, o_l_n_2); if (isect.test(0) == true && isect.test(1) == false) { - c3_p = sm::algo::crossing_point (o_l_c_1, o_l_c_2, o_l_n_1, o_l_n_2); + c3_p = sm::geometry::crossing_point (o_l_c_1, o_l_c_2, o_l_n_1, o_l_n_2); } else if (isect.test(0) == true && isect.test(1) == true) { c3_p = e_p.less_one_dim() - (c_ortho * hw); } else { // no intersection. next could have been end diff --git a/mplot/VoronoiVisual.h b/mplot/VoronoiVisual.h index e07eec51..8da3c7a6 100644 --- a/mplot/VoronoiVisual.h +++ b/mplot/VoronoiVisual.h @@ -25,7 +25,6 @@ #include #include -#define JC_VORONOI_IMPLEMENTATION #include namespace mplot { @@ -57,7 +56,7 @@ namespace mplot { //! Compute 2.5D Voronoi diagram using code adapted from // https://github.com/JCash/voronoi. The adaptation is to add a third dimension - // to jcv_point. + // to point. void initializeVertices() { unsigned int ncoords = this->dataCoords == nullptr ? 0 : this->dataCoords->size(); @@ -91,7 +90,7 @@ namespace mplot { } // Use mplot::range to find the extents of dataCoords. From these create a - // rectangle to pass to jcv_diagram_generate. + // rectangle to pass to diagram_generate. sm::range rx, ry; rx.search_init(); ry.search_init(); @@ -101,17 +100,12 @@ namespace mplot { } // Generate the 2D Voronoi diagram - jcv_diagram diagram; - std::memset (&diagram, 0, sizeof(jcv_diagram)); + jcv::manager vorman; + vorman.border_width = this->border_width; + vorman.diagram_generate (*(dcoords_ptr)); - jcv_rect domain = { - jcv_point{rx.min - this->border_width, ry.min - this->border_width, 0.0f}, - jcv_point{rx.max + this->border_width, ry.max + this->border_width, 0.0f} - }; - jcv_diagram_generate (ncoords, this->dcoords_ptr->data(), &domain, 0, &diagram); - - // We obtain access the the Voronoi cell sites: - const jcv_site* sites = jcv_diagram_get_sites (&diagram); + // We obtain access to the Voronoi cell sites: + const jcv::site* sites = vorman.diagram_get_sites(); // Now scan through the Voronoi cell 'sites' and 'edges' to re-assign z // values in the edges. This is not going to be particularly efficient, but @@ -130,21 +124,21 @@ namespace mplot { // Mapping same edge end-point locations to the averate z value of the adjacent cell centres std::map, float, veccmp> edge_end_zsums; - for (int i = 0; i < diagram.numsites; ++i) { + for (int i = 0; i < vorman.diagram_numsites(); ++i) { // We have the current edge_1, the next edge_2 and the previous edge_0 - const jcv_site* site = &sites[i]; - jcv_graphedge* edge_first = site->edges; // The very first edge - jcv_graphedge* edge_1 = edge_first; - jcv_graphedge* edge_2 = edge_first; - jcv_graphedge* edge_0 = edge_first; + const jcv::site* site = &sites[i]; + jcv::graphedge* edge_first = site->edges; // The very first edge + jcv::graphedge* edge_1 = edge_first; + jcv::graphedge* edge_2 = edge_first; + jcv::graphedge* edge_0 = edge_first; while (edge_0->next) { edge_0 = edge_0->next; } while (edge_1) { // Set z to 0. Should be done in jcvoronoi, but haven't found out how - edge_1->pos[0][2] = jcv_real{0}; - edge_1->pos[1][2] = jcv_real{0}; + edge_1->pos[0][2] = 0.0f; + edge_1->pos[1][2] = 0.0f; edge_2 = edge_1->next ? edge_1->next : edge_first; // edge_0 already set @@ -155,21 +149,21 @@ namespace mplot { // solution would be to modify the jcvoronoi algorithm to populate // both ends of all edges with additional logic. for (unsigned int j = 0; j < 2; ++j) { - if (edge_1->edge->sites[j]) { + if (edge_1->edge_->sites[j]) { //std::cout << "insert edge_1 " << edge_1->edge->sites[j]->p << " into edge_pos_centres[" << edge_1->pos[1] << "]\n"; - edge_pos_centres[edge_1->pos[1]].insert (edge_1->edge->sites[j]->p); + edge_pos_centres[edge_1->pos[1]].insert (edge_1->edge_->sites[j]->p); //std::cout << "insert edge_1 " << edge_1->edge->sites[j]->p << " into edge_pos_centres[" << edge_1->pos[0] << "]\n"; - edge_pos_centres[edge_1->pos[0]].insert (edge_1->edge->sites[j]->p); + edge_pos_centres[edge_1->pos[0]].insert (edge_1->edge_->sites[j]->p); } // By definition, cellcentres_1 also gets edge_2 sites... - if (edge_2->edge->sites[j]) { + if (edge_2->edge_->sites[j]) { //std::cout << "insert edge_2 " << edge_2->edge->sites[j]->p << " into edge_pos_centres[" << edge_1->pos[1] << "]\n"; - edge_pos_centres[edge_1->pos[1]].insert (edge_2->edge->sites[j]->p); + edge_pos_centres[edge_1->pos[1]].insert (edge_2->edge_->sites[j]->p); } // and cellcentres_0 gets edge_0 sites - if (edge_0->edge->sites[j]) { + if (edge_0->edge_->sites[j]) { //std::cout << "insert edge_0 " << edge_0->edge->sites[j]->p << " into edge_pos_centres[" << edge_1->pos[0] << "]\n"; - edge_pos_centres[edge_1->pos[0]].insert (edge_0->edge->sites[j]->p); + edge_pos_centres[edge_1->pos[0]].insert (edge_0->edge_->sites[j]->p); } } @@ -192,9 +186,9 @@ namespace mplot { } // Now go through edge_end_zsums and edges and update z values - for (int i = 0; i < diagram.numsites; ++i) { - const jcv_site* site = &sites[i]; - jcv_graphedge* edge_1 = site->edges; // The very first edge + for (int i = 0; i < vorman.diagram_numsites(); ++i) { + const jcv::site* site = &sites[i]; + jcv::graphedge* edge_1 = site->edges; // The very first edge while (edge_1) { // For each edge, set z from the map float zsum0 = 0.0f; @@ -228,9 +222,9 @@ namespace mplot { sm::vec t1 = {0.0f}; sm::vec t2 = {0.0f}; sm::quaternion rqinv = rq.invert(); - for (int i = 0; i < diagram.numsites; ++i) { - const jcv_site* site = &sites[i]; - const jcv_graphedge* e = site->edges; + for (int i = 0; i < vorman.diagram_numsites(); ++i) { + const jcv::site* site = &sites[i]; + const jcv::graphedge* e = site->edges; unsigned int site_triangles = 0; while (e) { // NB: There are 3 each of pos/col/norm vertices (and 3 indices) per @@ -249,9 +243,9 @@ namespace mplot { } } else { // No need to inverse rotate - for (int i = 0; i < diagram.numsites; ++i) { - const jcv_site* site = &sites[i]; - const jcv_graphedge* e = site->edges; + for (int i = 0; i < vorman.diagram_numsites(); ++i) { + const jcv::site* site = &sites[i]; + const jcv::graphedge* e = site->edges; unsigned int site_triangles = 0; while (e) { this->computeTriangle (site->p, e->pos[0], e->pos[1], this->setColour(site->index)); @@ -263,7 +257,7 @@ namespace mplot { this->triangle_count_sum += site_triangles; } } - if (static_cast(diagram.numsites) != ncoords) { + if (static_cast(vorman.diagram_numsites()) != ncoords) { std::cout << "WARNING: numsites != ncoords ?!?!\n"; } @@ -275,9 +269,9 @@ namespace mplot { sm::vec t0 = {0.0f}; sm::vec t1 = {0.0f}; sm::quaternion rqinv = rq.invert(); - for (int i = 0; i < diagram.numsites; ++i) { - const jcv_site* site = &sites[i]; - const jcv_graphedge* e = site->edges; + for (int i = 0; i < vorman.diagram_numsites(); ++i) { + const jcv::site* site = &sites[i]; + const jcv::graphedge* e = site->edges; while (e) { t0 = rqinv * (e->pos[0] * this->zoom); t1 = rqinv * (e->pos[1] * this->zoom); @@ -288,9 +282,9 @@ namespace mplot { } else { // No rotations required - for (int i = 0; i < diagram.numsites; ++i) { - const jcv_site* site = &sites[i]; - const jcv_graphedge* e = site->edges; + for (int i = 0; i < vorman.diagram_numsites(); ++i) { + const jcv::site* site = &sites[i]; + const jcv::graphedge* e = site->edges; while (e) { this->computeTube (e->pos[0] * this->zoom, e->pos[1] * this->zoom, mplot::colour::royalblue, mplot::colour::goldenrod2, this->voronoi_grid_thickness, 12); @@ -307,9 +301,9 @@ namespace mplot { sm::vec t1 = {0.0f}; sm::quaternion rqinv = rq.invert(); - for (int i = 0; i < diagram.numsites; ++i) { - const jcv_site* site = &sites[i]; - const jcv_graphedge* e = site->edges; + for (int i = 0; i < vorman.diagram_numsites(); ++i) { + const jcv::site* site = &sites[i]; + const jcv::graphedge* e = site->edges; while (e) { t0 = rqinv * sm::vec{ e->pos[0].x() * this->zoom, e->pos[0].y() * this->zoom, 0.0f }; t1 = rqinv * sm::vec{ e->pos[1].x() * this->zoom, e->pos[1].y() * this->zoom, 0.0f }; @@ -320,9 +314,9 @@ namespace mplot { } else { // Show the 2D Voronoi diagram's edges at z=0 - for (int i = 0; i < diagram.numsites; ++i) { - const jcv_site* site = &sites[i]; - const jcv_graphedge* e = site->edges; + for (int i = 0; i < vorman.diagram_numsites(); ++i) { + const jcv::site* site = &sites[i]; + const jcv::graphedge* e = site->edges; while (e) { 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 }, @@ -339,9 +333,6 @@ namespace mplot { this->computeSphere ((*this->dataCoords)[i] * this->zoom, mplot::colour::black, this->dataCoord_sphere_size); } } - - // At end free the Voronoi diagram memory - jcv_diagram_free (&diagram); } void reinitColoursScalar() diff --git a/mplot/compoundray/EyeVisual.h b/mplot/compoundray/EyeVisual.h index eb5adeda..c7dfdddd 100644 --- a/mplot/compoundray/EyeVisual.h +++ b/mplot/compoundray/EyeVisual.h @@ -8,13 +8,64 @@ #include #include #include +#include +#include #include #include -#include "cameras/CompoundEyeDataTypes.h" +#include namespace mplot::compoundray { + // This is a binary-compatible equivalent to struct Ommatidium from cameras/CompoundEyeDataTypes.h in compound-ray. + // Use reinterpret_cast*>(ommatidia) if your ommatidia originate inside compound ray. + struct Ommatidium + { + sm::vec relativePosition = {}; + sm::vec relativeDirection = {}; + float acceptanceAngleRadians = 0.0f; + float focalPointOffset = 0.0f; + }; + + // Helper function. Read the compound-ray csv eye file into ommatidia. ommatidia should be a pointer to an allocate vector. + [[maybe_unused]] static std::vector* + readEye (std::vector* ommatidia, const std::string& path) + { + if (ommatidia == nullptr) { return ommatidia; } + + std::cout << "Path: " << path << std::endl; + + ommatidia->clear(); + + std::ifstream eyeDataFile (path, std::ifstream::in); + if(!eyeDataFile.is_open()) { + std::cout << "Failed to open eye data file " << path << "\n"; + return ommatidia; + } + + std::string line; + size_t ommCount = 0; + while (std::getline (eyeDataFile, line)) { + std::vector splitData = mplot::tools::stringToVector (line, " "); + if (splitData.size() < 8) { + std::cout << "Malformed line, continue...\n"; + continue; + } + mplot::compoundray::Ommatidium o = { + sm::vec{ std::stof(splitData[0]), std::stof(splitData[1]), std::stof(splitData[2]) }, + sm::vec{ std::stof(splitData[3]), std::stof(splitData[4]), std::stof(splitData[5]) }, + std::stof(splitData[6]), + std::stof(splitData[7]) + }; + std::cout << "o: " << o.relativePosition << "; " << o.relativeDirection << "; " << o.acceptanceAngleRadians << "; " << o.focalPointOffset << std::endl; + ommatidia->push_back (o); + ommCount++; + } + std::cout << " Loaded " << ommCount << " ommatidia." << std::endl; + + return ommatidia; + } + //! This class creates a visualization of a compound-ray format compound eye model template class EyeVisual : public mplot::VisualModel @@ -25,7 +76,7 @@ namespace mplot::compoundray //! Initialise with offset, start and end coordinates, radius and a single colour. EyeVisual (const sm::vec _offset, std::vector>* _ommData, - std::vector* _ommatidia) + std::vector* _ommatidia) { this->init (_offset, _ommData, _ommatidia); } @@ -34,7 +85,7 @@ namespace mplot::compoundray void init (const sm::vec _offset, std::vector>* _ommData, - std::vector* _ommatidia) + std::vector* _ommatidia) { this->viewmatrix.translate (_offset); this->ommData = _ommData; @@ -47,36 +98,154 @@ namespace mplot::compoundray if (ommData->empty()) { return; } size_t n_verts = this->vertexColors.size(); // should be tube_vertices * n_omm if (n_verts == 0u) { return; } // model doesn't exist yet - - this->vertexColors.clear(); // Could re-write not clear/push size_t n_omm = ommData->size(); - int num_vertices = disc_vertices; - if (this->show_cones == true) { - num_vertices = cone_vertices + disc_vertices; - } // else num_vertices = disc_vertices; + std::size_t i_3d = 0; + if (show_3d) { + // Replace colours for the 3D part of the model + int num_vertices = disc_vertices; + if (this->show_cones == true) { + num_vertices = cone_vertices + disc_vertices; + } // else num_vertices = disc_vertices; - // 3 colours, n_omm tubes, cone_vertices vertices per cone. - if (n_verts != 3u * n_omm * static_cast(num_vertices)) { - throw std::runtime_error ("EyeVisual: n_verts/n_omm sizes mismatch!"); + // Re-colour cones built from a focal point offset and acceptance angle + for (size_t i = 0u; i < n_omm; ++i) { + // Update the 3 RGB values in vertexColors tube_vertices times + int j = 0; + for (; j < num_vertices; ++j) { + this->vertexColors[i * num_vertices * 3 + j * 3] = (*ommData)[i][0]; + this->vertexColors[i * num_vertices * 3 + j * 3 + 1] = (*ommData)[i][1]; + this->vertexColors[i * num_vertices * 3 + j * 3 + 2] = (*ommData)[i][2]; + } + } + // i_3d is the index offset for the 3D part + i_3d = n_omm * num_vertices * 3; } - // Could test on: if (this->focal_point_sum > 0.0f) { // if the number of vertices were - // different, but I chose cones in both cases - - // Re-colour cones built from a focal point offset and acceptance angle - for (size_t i = 0u; i < n_omm; ++i) { - // Update the 3 RGB values in vertexColors tube_vertices times - int j = 0; - for (; j < num_vertices; ++j) { - this->vertex_push ((*ommData)[i], this->vertexColors); + // Replace colours in the 2D part of the model + for (uint32_t pri = 0; pri < this->projections.size(); ++pri) { + // Replace elements of vertexColors + std::size_t tcounts = 0; + std::size_t d_2d = 0; + for (std::size_t i = 0u; i < this->projections[pri].triangle_counts.size(); ++i) { + auto c = (*ommData)[this->projections[pri].site_indices[i] + this->projections[pri].start_i]; + std::size_t d_idx = i_3d + tcounts * 9; // 3 floats per vtx, 3 vtxs per tri + for (std::size_t j = 0; j < 3 * this->projections[pri].triangle_counts[i]; ++j) { + // This is ONE colour vertex. Need 3 per triangle. + this->vertexColors[d_idx + 3 * j] = c[0]; + this->vertexColors[d_idx + 3 * j + 1] = c[1]; + this->vertexColors[d_idx + 3 * j + 2] = c[2]; + d_2d += 3; + } + tcounts += this->projections[pri].triangle_counts[i]; } + i_3d += d_2d; } // Lastly, this call copies vertexColors (etc) into the OpenGL memory space this->reinit_colour_buffer(); } + // Available projections + enum class projection_type : uint8_t + { + mercator, + cassini, + equirectangular, + cylindrical + }; + + // Project latitude/longitude ll with projection type t and given radius + sm::vec spherical_projection (const sm::vec& ll, projection_type t, const float radius) + { + sm::vec xy = {}; + if (t == projection_type::equirectangular) { + //sm::mathconst::pi_over_2 + xy = sm::geometry::spherical_projection::equirectangular (ll, radius); + } else if (t == projection_type::cassini) { + xy = sm::geometry::spherical_projection::cassini (ll, radius); + } else if (t == projection_type::cylindrical) { + throw std::runtime_error ("Not a spherical projection"); + } else { + xy = sm::geometry::spherical_projection::mercator (ll, radius); + } + return xy; + } + + // 2D positions for the ommatidia centres encoded in 3D vecs. Gets re-used for each projection + sm::vvec> omm2d; + + /* + * Possibly each of these need replication for each of multiple 2d projections + */ + struct projection_data + { + // Use this to position the 2D map wrt the three D model. You can translate, scale and rotate + sm::mat44 twod_transform; + // The user-provided radius of the projection sphere. Will need to match the size of the compound ray eye + float proj_radius = 0.0f; + // The centre of the user-provided projection sphere or cylinder + sm::vec proj_centre = {}; + // The height of a projection cylinder + sm::vec proj_height = {}; + // 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 + sm::vvec triangle_counts; + // Record the data index for each Voronoi cell index. For reinitColours + sm::vvec site_indices; + // Sum of triangles used for reinitColours + uint32_t triangle_count_sum = 0; + // Starting ommatidium index for projection + uint32_t start_i = 0; + // End index + uint32_t end_i = std::numeric_limits::max(); + }; + + // A compound eye visualization may require several projections to 2D + std::vector projections; + + void add_spherical_projection (projection_type t, const sm::mat44& _twod_transform, + const sm::vec& centre, const float radius, + const uint32_t _start_i = 0, + const uint32_t _end_i = std::numeric_limits::max()) + { + projection_data d; + d.proj_type = t; + d.twod_transform = _twod_transform; + d.proj_centre = centre; + d.proj_radius = radius; + d.start_i = _start_i; + d.end_i = _end_i; + this->projections.push_back (d); + } + + void add_spherical_projection (projection_type t, const sm::vec& _twod_offset, + const sm::vec& centre, const float radius, + 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); + } + + void add_cylindrical_projection (const sm::mat44& _twod_transform, const sm::vec& centre, + const float radius, const float height, + const uint32_t _start_i = 0, + const uint32_t _end_i = std::numeric_limits::max()) + { + projection_data d; + d.proj_type = projection_data::cylindrical; + d.twod_transform = _twod_transform; + d.proj_centre = centre; + d.proj_radius = radius; + d.proj_height = height; + d.start_i = _start_i; + d.end_i = _end_i; + this->projections.push_back (d); + } + //! Initialize vertex buffer objects and vertex array object. void initializeVertices() { @@ -96,79 +265,224 @@ namespace mplot::compoundray // Draw ommatidia size_t n_omm = ommData->size(); + // Determine eye dimensions + sm::range> ommrng = sm::range>::search_initialized(); + for (size_t i = 0u; i < n_omm; ++i) { ommrng.update ((*ommatidia)[i].relativePosition); } + float ray_radius = ommrng.span().max() / 500.0f; + + // Find mean minimum ommatidial distance + sm::vvec dist_to_other (n_omm, 0.0f); + sm::vvec min_dist_to_other (n_omm, 0.0f); + for (size_t i = 0u; i < n_omm; ++i) { + for (size_t j = 0u; j < n_omm; ++j) { + if (i == j) { + dist_to_other[j] = 10000.0f; + } else { + dist_to_other[j] = ((*ommatidia)[i].relativePosition - (*ommatidia)[j].relativePosition).length(); + } + } + min_dist_to_other[i] = dist_to_other.min(); + } + std::cerr << "Mean ommatidial distance: " << min_dist_to_other.mean() << std::endl; + // First find out if all focal points are 0 this->focal_point_sum = 0.0f; for (size_t i = 0u; i < n_omm; ++i) { - this->focal_point_sum += (*ommatidia)[i].focalPointOffset; + this->focal_point_sum += std::abs((*ommatidia)[i].focalPointOffset); } - if (this->focal_point_sum > 0.0f) { + if (show_3d && this->focal_point_sum > 0.0f) { // We have focal points, so draw with the relativePosition representing the centre // of the ommatidial lens - the base of a cone - which then extends back to the cone // tip, which can be thought of as the location of the ommatidial 'sensor' for (size_t i = 0u; i < n_omm; ++i) { // Ommatidia colour, position/shape std::array colour = (*ommData)[i]; - float3 rpos = (*ommatidia)[i].relativePosition; - float3 rdir = (*ommatidia)[i].relativeDirection; float angle = (*ommatidia)[i].acceptanceAngleRadians; - float focal_point = (*ommatidia)[i].focalPointOffset; - sm::vec pos = { rpos.x, rpos.y, rpos.z }; - sm::vec dir = { rdir.x, rdir.y, rdir.z }; + float focal_point = std::abs((*ommatidia)[i].focalPointOffset); + sm::vec pos = (*ommatidia)[i].relativePosition; + sm::vec dir = (*ommatidia)[i].relativeDirection; dir.renormalize(); // Tip of cone is 'behind' the position of the ommatidial face/lens sm::vec ommatidial_detector_point = pos - dir * focal_point; - // work out radius from acceptance angle and focal_point - float radius = focal_point * std::tan (angle / 2.0f); - // The disc - this->computeTube (pos, pos + (0.1f * radius * dir), colour, colour, radius, tube_faces); + // work out a radius from acceptance angle and focal_point + // The discs are based on the inter-ommatidial distances in space, which have to have been computed + float dw = min_dist_to_other[i]; + this->computeTube (pos, pos + (0.05f * dw * dir), colour, colour, dw * 0.5f, tube_faces); + + // This visualizes the optical cones if (this->show_cones == true) { + float optical_radius = focal_point * std::tan (angle / 2.0f); // Colour comes from ommData. ringoffset is 1.0f - this->computeCone (pos, ommatidial_detector_point, 0.0f, colour, radius, tube_faces); + this->computeCone (pos, ommatidial_detector_point, 0.0f, colour, optical_radius, tube_faces); } } - } else { + } else if (show_3d && this->focal_point_sum <= 0.0f) { // All our focal_points are 0. Don't have focal point offset to help define our // cones, only acceptance angle. Use manually specified tube_length (or computed // radius) to figure out the size of a cone, whose tip is the location of the // ommatidial sensor AND the centre of the ommatidial lens for (size_t i = 0u; i < n_omm; ++i) { std::array colour = (*ommData)[i]; - float3 rpos = (*ommatidia)[i].relativePosition; - float3 rdir = (*ommatidia)[i].relativeDirection; float angle = (*ommatidia)[i].acceptanceAngleRadians; // pos will be the tip of the cone in this case, and the centre of the disc - sm::vec pos = { rpos.x, rpos.y, rpos.z }; - sm::vec dir = { rdir.x, rdir.y, rdir.z }; + sm::vec pos = (*ommatidia)[i].relativePosition; + sm::vec dir = (*ommatidia)[i].relativeDirection; dir.renormalize(); - // do a cone - sm::vec ommatidial_cone_pos = pos + dir * this->cone_length; - float ringoffset = 0.0f; - // work out radius from acceptance angle and focal_point - float radius = this->disc_width / 2.0f; // will be negative if not set - if (radius < 0.0f) { // fall back to using cone_length - radius = this->cone_length * std::tan (angle / 2.0f); - } - // Show a disc. Use disc_width, or if it is -ve, cone_length and computed radius - this->computeTube (pos, pos - (0.1f * radius * dir), colour, colour, radius, tube_faces); - // And optionally a cone + + float dw = min_dist_to_other[i]; + this->computeTube (pos, pos + (0.05f * dw * dir), colour, colour, dw * 0.5f, tube_faces); + // We don't have a focal length to show cones, but we can still show the acceptance angle if (this->show_cones == true) { - this->computeCone (ommatidial_cone_pos, pos, ringoffset, colour, radius, tube_faces); + // do a cone of angle 'acceptanceAngle' using user-supplied cone_length + sm::vec ommatidial_cone_pos = pos + dir * this->cone_length; + float radius = this->cone_length * std::tan (angle / 2.0f); + this->computeCone (ommatidial_cone_pos, pos, 0.0f, colour, radius, tube_faces); + } + } + } + + for (uint32_t pri = 0; pri < this->projections.size(); ++pri) { + this->omm2d.clear(); + if (this->projections[pri].proj_type == projection_type::cylindrical) { + std::cout << "Cylindrical projections are currently unimplemented\n"; + } else { + // Compute intersections between ommatidia direction vectors and our projection sphere. + + // Rotate coordinates as the compound eye looks forwards along z, whereas 2D + // projections look forwards along x by convention. + sm::mat44 coord_rotn; + coord_rotn.rotate (sm::vec<>::uz(), sm::mathconst::pi_over_2); + coord_rotn.rotate (sm::vec<>::ux(), sm::mathconst::pi_over_2); + + for (size_t i = this->projections[pri].start_i; i < this->ommatidia->size() && i < this->projections[pri].end_i; ++i) { + sm::vec, 2> sph_coord = sm::geometry::ray_sphere_intersection (this->projections[pri].proj_centre, + this->projections[pri].proj_radius, + (*ommatidia)[i].relativePosition, + -(*ommatidia)[i].relativeDirection); + if (sph_coord[0][0] != std::numeric_limits::max()) { + // 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); + 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()); + } } + // Make 2D Voronoi of omm2d. + this->voronoi2d (pri); } } + for (uint32_t pri = 0; pri < this->projections.size(); ++pri) { + + if (this->show_sphere) { + this->computeSphere (this->projections[pri].proj_centre, mplot::colour::grey50, + this->projections[pri].proj_radius, 18, 18); + } + + if (this->show_rays) { + for (size_t i = 0; i < this->ommatidia->size(); ++i) { + // Can now find intersections on our sphere + sm::vec<> l0 = (*ommatidia)[i].relativePosition; + sm::vec<> l = -(*ommatidia)[i].relativeDirection; + // Make rays a sensible length based on projections.proj_radius + l.renormalize(); + l *= this->projections[pri].proj_radius * 3.0f; + // Show direction vector from ommatidium position + this->computeArrow (l0, l0 + l, mplot::colour::grey80, ray_radius); + // Recompute intersections + sm::vec, 2> intersections = sm::geometry::ray_sphere_intersection (this->projections[pri].proj_centre, + this->projections[pri].proj_radius, l0, l); + if (intersections[0][0] != std::numeric_limits::max()) { + // intersections[0] is the coordinate for the ommatidia pixel on the sphere + this->computeSphere (intersections[0], mplot::colour::crimson, 0.006f * this->projections[pri].proj_radius); + } + } + } + } } + void voronoi2d (uint32_t pri) + { + // Use mplot::range to find the extents of dataCoords. From these create a + // rectangle to pass to diagram_generate. + int ncoords = static_cast(this->omm2d.size()); + + 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); + + int diag_nsites = vorman.diagram_numsites(); + if (diag_nsites != ncoords) { + std::cout << "WARNING: diagram's ncoords (" << diag_nsites << ") != ncoords (" << ncoords << ")?!?!\n"; + } + + // We obtain access to the Voronoi cell sites: + const jcv::site* sites = vorman.diagram_get_sites(); + + for (int i = 0; i < diag_nsites && i < ncoords; ++i) { + const jcv::site* site = &sites[i]; + jcv::graphedge* e = site->edges; // The very first edge + while (e) { + // Set z. Should be done in jcvoronoi, but haven't found out how + e->pos[0][2] = this->omm2d[i][2]; + e->pos[1][2] = e->pos[0][2]; + e = e->next; + } + } + + // To draw triangles iterate over the 'sites' and get the edges + this->projections[pri].triangle_counts.resize (ncoords, 0); + this->projections[pri].site_indices.resize (ncoords, 0); + this->projections[pri].triangle_count_sum = 0; + + sm::vvec> flat_triangles; // contains a sequence of triplets of vecs + sm::vvec> flat_colours; // fewer elements than flat_triangles + // To draw triangles iterate over the 'sites' and draw triangles + for (int i = 0; i < diag_nsites && i < ncoords; ++i) { + const jcv::site* site = &sites[i]; + const jcv::graphedge* e = site->edges; + this->projections[pri].site_indices[i] = site->index; + std::array colour = mplot::colour::black; + if (site->index + this->projections[pri].start_i < ommData->size()) { + colour = (*ommData)[site->index + this->projections[pri].start_i]; + } else { + std::cout << "Uh oh, can't access colour [" << site->index << " + " << this->projections[pri].start_i << "]\n"; + } + uint32_t site_triangles = 0; + while (e) { + flat_triangles.push_back (site->p.as()); + flat_triangles.push_back (e->pos[0].as()); + flat_triangles.push_back (e->pos[1].as()); + flat_colours.push_back (colour); + ++site_triangles; + e = e->next; + } + this->projections[pri].triangle_counts[i] = site_triangles; + this->projections[pri].triangle_count_sum += site_triangles; + } + + // Can now computeTriangles + for (uint32_t i = 0; i < flat_triangles.size(); i += 3) { + sm::vec t1 = (this->projections[pri].twod_transform * flat_triangles[i]).less_one_dim(); + sm::vec t2 = (this->projections[pri].twod_transform * flat_triangles[i + 1]).less_one_dim(); + sm::vec t3 = (this->projections[pri].twod_transform * flat_triangles[i + 2]).less_one_dim(); + this->computeTriangle (t1, t2, t3, flat_colours[i/3]); + } + } + + // If false, hide 3D representation (the ommatidial cones and discs) + bool show_3d = true; // Visualize in two modes "disc" mode, showing just a 2D disc for each ommatidium and - // disc+cone mode, where the acceptance angle is displayed too. Runtime switchable. + // disc+cone mode, where the acceptance angle/optical cone is displayed too. Runtime switchable. bool show_cones = false; // The colours detected by each ommatidium std::vector>* ommData = nullptr; - // The position and orientation of each oimmatidium - std::vector* ommatidia = nullptr; + // The position and orientation of each ommatidium + std::vector* ommatidia = nullptr; // If sum is 0, then we have a special case for rendering the eye as we have no focal point - // offsets specified for this eye (and hence the radius of the ommatidium is not known) + // offsets specified for this eye (and hence the optical radius of the ommatidium is not known) float focal_point_sum = 0.0f; // Hard-coded number of faces making up an ommatidial element (the higher this is, the more round it will look) static constexpr int tube_faces = 18; @@ -176,16 +490,49 @@ namespace mplot::compoundray static constexpr int cone_vertices = tube_faces * 3 + 2; static constexpr int disc_vertices = tube_faces * 4 + 2; // Setter for cone_length must reinit vertices - void set_cone_length (float _cone_length) { this->cone_length = _cone_length; this->reinit(); } + void set_cone_length (float _cone_length) + { + if (this->focal_point_sum > 0.0f) { + std::cout << "WARNING: manual cone length will be ignored because " + << "compound-ray eye file specifies focal offsets\n"; + } + this->cone_length = _cone_length; this->reinit(); + } + void pre_set_cone_length (float _cone_length) { this->cone_length = _cone_length; } float get_cone_length() { return this->cone_length; } - // Setter for the disc width. To replace cone length? Or operate as alternative? - void set_disc_width (float _disc_width) { this->disc_width = _disc_width; this->reinit(); } - float get_disc_width() { return this->disc_width; } + + // Should projection spheres be shown visually (maybe by external code? + bool show_sphere = false; + // Should we show arrows/intersection locations with the projection sphere(s)? + bool show_rays = false; + // Width of borders around 2D map(s) + float border_width = std::numeric_limits::epsilon(); + private: // User-modifiable ommatidial cone length which is used if there's no focal point offset float cone_length = 0.1f; - // User-modifiable ommatidial disc width. If negative ignored? - float disc_width = -1.0f; + + //! Compute a triangle from 3 arbitrary corners + void computeTriangle (sm::vec c1, sm::vec c2, sm::vec c3, const std::array& colr) + { + // v is the face normal + sm::vec u1 = c1-c2; + sm::vec u2 = c2-c3; + sm::vec v = u1.cross(u2); + v.renormalize(); + // Push corner vertices + this->vertex_push (c1, this->vertexPositions); + this->vertex_push (c2, this->vertexPositions); + this->vertex_push (c3, this->vertexPositions); + // Colours/normals + for (uint32_t i = 0; i < 3U; ++i) { + this->vertex_push (colr, this->vertexColors); + this->vertex_push (v, this->vertexNormals); + } + this->indices.push_back (this->idx++); + this->indices.push_back (this->idx++); + this->indices.push_back (this->idx++); + } }; } // namespace comray diff --git a/mplot/compoundray/interop.h b/mplot/compoundray/interop.h index 2e11b300..2b74f60e 100644 --- a/mplot/compoundray/interop.h +++ b/mplot/compoundray/interop.h @@ -51,7 +51,7 @@ namespace mplot::compoundray * This function finds the meshes in compound-ray's MulticamScene and creates corresponding * VisualModels in the mplot::Visual */ - void scene_to_visualmodels (MulticamScene* thescene, mplot::Visual<>* thevisual) + void scene_to_visualmodels (MulticamScene* thescene, mplot::Visual<>* thevisual, bool make_navmeshes = false) { static constexpr bool debug_meshload = false; std::vector> mymeshes = thescene->getMeshes(); @@ -141,7 +141,7 @@ namespace mplot::compoundray auto vertvm = std::make_unique> (tfm, ind, posn, norm, colr); thevisual->bindmodel (vertvm); vertvm->name = mymeshes[mi]->name; - vertvm->vertex_postprocess(); + if (make_navmeshes == true) { vertvm->make_navmesh(); } vertvm->finalize(); thevisual->addVisualModel (vertvm); } diff --git a/mplot/healpix/healpix_bare.hpp b/mplot/healpix/healpix_bare.hpp index 9065d3f9..281dddfb 100644 --- a/mplot/healpix/healpix_bare.hpp +++ b/mplot/healpix/healpix_bare.hpp @@ -46,9 +46,14 @@ namespace hp { */ /*! - * A structure describing a location on the sphere. \a Theta is the co-latitude - * in radians (0 at the North Pole, increasing to pi at the South Pole. - * \a Phi is the azimuth in radians. + * A structure describing a location on the sphere. \a theta is the co-latitude in radians (0 at + * the North Pole, increasing to pi at the South Pole. \a phi is the azimuth in radians. It's + * also correct to describe it as the longitude. With theta on the equator (theta = pi/2), phi = + * 0 gives the x unit vector (1,0,0) and phi = pi/2 gives the y unit vector (0,1,0). This + * matches longitude in the Earth-centered Earth-fixed (ECEF) scheme. Test by passing these + * values to + * + * hp::loc2vec (hp::ang2loc (_t_ang)) */ typedef struct { double theta, phi; } t_ang; /*! diff --git a/mplot/jcvoronoi/jc_voronoi.h b/mplot/jcvoronoi/jc_voronoi.h index 76c3f262..ec7e85d4 100644 --- a/mplot/jcvoronoi/jc_voronoi.h +++ b/mplot/jcvoronoi/jc_voronoi.h @@ -5,1627 +5,1436 @@ // Copyright (c) 2015-2023 Mathias Westerdahl // For LICENSE (MIT), USAGE or HISTORY, see bottom of file -#ifndef JC_VORONOI_H -#define JC_VORONOI_H +// This version has been re-written by Seb into a templated C++ style, to allow easy choice between +// the use of single or double precision floating point values (the type T). It has also been +// namespaced (jcv) so that type names look like jcv::site instead of jcv_site; jcv::edge rather +// than jcv_edge, and so on. -#include -#include -#include -#include -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef JCV_REAL_TYPE - #define JCV_REAL_TYPE float -#endif - -#ifndef JCV_REAL_TYPE_EPSILON - #define JCV_REAL_TYPE_EPSILON FLT_EPSILON -#endif +#pragma once -#ifndef JCV_ATAN2 -#define JCV_ATAN2(_Y_, _X_) std::atan2(_Y_, _X_) -#endif - -#ifndef JCV_SQRT -#define JCV_SQRT(_X_) std::sqrt(_X_) -#endif - -#ifndef JCV_PI - #define JCV_PI 3.14159265358979323846264338327950288f -#endif - -#ifndef JCV_FLT_MAX - #define JCV_FLT_MAX 3.402823466e+38F -#endif +#include // qsort(), malloc() & free(). Seb hasn't altered the C-style memory allocation + // used when diagrams are generated +#include // one sanity message +#include +#include +#include // assert() +#include // uintptr_t etc +#include +#include +#include #ifndef JCV_EDGE_INTERSECT_THRESHOLD // Fix for Issue #40 #define JCV_EDGE_INTERSECT_THRESHOLD 1.0e-10F #endif -typedef JCV_REAL_TYPE jcv_real; - -#include - -typedef sm::vec jcv_point; -typedef struct jcv_rect_ jcv_rect; -typedef struct jcv_site_ jcv_site; -typedef struct jcv_edge_ jcv_edge; -typedef struct jcv_graphedge_ jcv_graphedge; -typedef struct jcv_delauney_edge_ jcv_delauney_edge; -typedef struct jcv_delauney_iter_ jcv_delauney_iter; -typedef struct jcv_diagram_ jcv_diagram; -typedef struct jcv_clipper_ jcv_clipper; -typedef struct jcv_context_internal_ jcv_context_internal; - -/// Tests if a point is inside the final shape -typedef int (*jcv_clip_test_point_fn)(const jcv_clipper* clipper, const jcv_point p); -/** Given an edge, and the clipper, calculates the e->pos[0] and e->pos[1] - * Returns 0 if not successful - */ -typedef int (*jcv_clip_edge_fn)(const jcv_clipper* clipper, jcv_edge* e); -/** 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. - */ -typedef void (*jcv_clip_fillgap_fn)(const jcv_clipper* clipper, jcv_context_internal* allocator, jcv_site* s); - - - -/** - * Uses malloc - * If a clipper is not supplied, a default box clipper will be used - * 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. - */ -extern void jcv_diagram_generate( int num_points, const jcv_point* points, const jcv_rect* rect, const jcv_clipper* clipper, jcv_diagram* diagram ); - -typedef void* (*FJCVAllocFn)(void* userctx, size_t size); -typedef void (*FJCVFreeFn)(void* userctx, void* p); - -// Same as above, but allows the client to use a custom allocator -extern void jcv_diagram_generate_useralloc( int num_points, const jcv_point* points, const jcv_rect* rect, const jcv_clipper* clipper, void* userallocctx, FJCVAllocFn allocfn, FJCVFreeFn freefn, jcv_diagram* diagram ); - -// Uses free (or the registered custom free function) -extern void jcv_diagram_free( jcv_diagram* diagram ); - -// Returns an array of sites, where each index is the same as the original input point array. -extern const jcv_site* jcv_diagram_get_sites( const jcv_diagram* diagram ); - -// Returns a linked list of all the voronoi edges -// excluding the ones that lie on the borders of the bounding box. -// For a full list of edges, you need to iterate over the sites, and their graph edges. -extern const jcv_edge* jcv_diagram_get_edges( const jcv_diagram* diagram ); - -// Iterates over a list of edges, skipping invalid edges (where p0==p1) -extern const jcv_edge* jcv_diagram_get_next_edge( const jcv_edge* edge ); - -// Creates an iterator over the delauney edges of a voronoi diagram -void jcv_delauney_begin( const jcv_diagram* diagram, jcv_delauney_iter* iter ); - -// Steps the iterator and returns the next edge -// Returns 0 when there are no more edges -int jcv_delauney_next( jcv_delauney_iter* iter, jcv_delauney_edge* next ); - -// For the default clipper -extern int jcv_boxshape_test(const jcv_clipper* clipper, const jcv_point p); -extern int jcv_boxshape_clip(const jcv_clipper* clipper, jcv_edge* e); -extern void jcv_boxshape_fillgaps(const jcv_clipper* clipper, jcv_context_internal* allocator, jcv_site* s); - - -#pragma pack(push, 1) - -struct jcv_graphedge_ -{ - struct jcv_graphedge_* next; - struct jcv_edge_* edge; - struct jcv_site_* neighbor; - jcv_point pos[2]; - jcv_real angle; -}; - -struct jcv_site_ -{ - jcv_point p; - int index; // Index into the original list of points - jcv_graphedge* edges; // The half edges owned by the cell -}; - -// The coefficients a, b and c are from the general line equation: ax * by + c = 0 -struct jcv_edge_ -{ - struct jcv_edge_* next; - jcv_site* sites[2]; - jcv_point pos[2]; - jcv_real a; - jcv_real b; - jcv_real c; -}; - -struct jcv_delauney_iter_ -{ - const jcv_edge* sentinel; - const jcv_edge* current; -}; - -struct jcv_delauney_edge_ -{ - const jcv_edge* edge; // The voronoi edge separating the two sites - const jcv_site* sites[2]; - jcv_point pos[2]; // the positions of the two sites -}; - -struct jcv_rect_ -{ - jcv_point min; - jcv_point max; -}; - -struct jcv_clipper_ -{ - jcv_clip_test_point_fn test_fn; - jcv_clip_edge_fn clip_fn; - jcv_clip_fillgap_fn fill_fn; - jcv_point min; // The bounding rect min - jcv_point max; // The bounding rect max - void* ctx; // User defined context -}; - -struct jcv_diagram_ -{ - jcv_context_internal* internal; - int numsites; - jcv_point min; - jcv_point max; -}; - -#pragma pack(pop) - -#ifdef __cplusplus -} -#endif - -#endif // JC_VORONOI_H - -#ifdef JC_VORONOI_IMPLEMENTATION -#undef JC_VORONOI_IMPLEMENTATION - -#include - -// INTERNAL FUNCTIONS - -#if defined(_MSC_VER) && !defined(__cplusplus) - #define inline __inline -#endif - -static const int JCV_DIRECTION_LEFT = 0; -static const int JCV_DIRECTION_RIGHT = 1; -static const jcv_real JCV_INVALID_VALUE = (jcv_real)-JCV_FLT_MAX; - -// jcv_real - -static inline jcv_real jcv_abs(jcv_real v) { - return (v < 0) ? -v : v; -} - -static inline int jcv_real_eq(jcv_real a, jcv_real b) -{ - return jcv_abs(a - b) < JCV_REAL_TYPE_EPSILON; -} - -static inline jcv_real jcv_real_to_int(jcv_real v) { - return (sizeof(jcv_real) == 4) ? (jcv_real)(int)v : (jcv_real)(long long)v; -} - -// Only used for calculating the initial bounding box -static inline jcv_real jcv_floor(jcv_real v) { - jcv_real i = jcv_real_to_int(v); - return (v < i) ? i - 1 : i; -} - -// Only used for calculating the initial bounding box -static inline jcv_real jcv_ceil(jcv_real v) { - jcv_real i = jcv_real_to_int(v); - return (v > i) ? i + 1 : i; -} - -static inline jcv_real jcv_min(jcv_real a, jcv_real b) { - return a < b ? a : b; -} - -static inline jcv_real jcv_max(jcv_real a, jcv_real b) { - return a > b ? a : b; -} - -// jcv_point - -static inline int jcv_point_cmp(const void* p1, const void* p2) -{ - const jcv_point* s1 = (const jcv_point*) p1; - const jcv_point* s2 = (const jcv_point*) p2; - return (s1->y() != s2->y()) ? (s1->y() < s2->y() ? -1 : 1) : (s1->x() < s2->x() ? -1 : 1); -} - -static inline int jcv_point_less( const jcv_point* pt1, const jcv_point* pt2 ) -{ - return (pt1->y() == pt2->y()) ? (pt1->x() < pt2->x()) : pt1->y() < pt2->y(); -} - -static inline int jcv_point_eq( const jcv_point* pt1, const jcv_point* pt2 ) -{ - return jcv_real_eq(pt1->y(), pt2->y()) && jcv_real_eq(pt1->x(), pt2->x()); -} - -static inline int jcv_point_on_box_edge( const jcv_point* pt, const jcv_point* min, const jcv_point* max ) -{ - return pt->x() == min->x() || pt->y() == min->y() || pt->x() == max->x() || pt->y() == max->y(); -} - -// corners - -static const int JCV_EDGE_LEFT = 1; -static const int JCV_EDGE_RIGHT = 2; -static const int JCV_EDGE_BOTTOM = 4; -static const int JCV_EDGE_TOP = 8; - -static const int JCV_CORNER_NONE = 0; -static const int JCV_CORNER_TOP_LEFT = 1; -static const int JCV_CORNER_BOTTOM_LEFT = 2; -static const int JCV_CORNER_BOTTOM_RIGHT = 3; -static const int JCV_CORNER_TOP_RIGHT = 4; - -static inline int jcv_get_edge_flags( const jcv_point* pt, const jcv_point* min, const jcv_point* max ) -{ - int flags = 0; - if (pt->x() == min->x()) flags |= JCV_EDGE_LEFT; - else if (pt->x() == max->x()) flags |= JCV_EDGE_RIGHT; - if (pt->y() == min->y()) flags |= JCV_EDGE_BOTTOM; - else if (pt->y() == max->y()) flags |= JCV_EDGE_TOP; - return flags; -} - -static inline int jcv_edge_flags_to_corner(int edge_flags) -{ - #define TEST_FLAGS(_FLAGS, _RETVAL) if ( (_FLAGS) == edge_flags ) return _RETVAL - TEST_FLAGS(JCV_EDGE_TOP|JCV_EDGE_LEFT, JCV_CORNER_TOP_LEFT); - TEST_FLAGS(JCV_EDGE_TOP|JCV_EDGE_RIGHT, JCV_CORNER_TOP_RIGHT); - TEST_FLAGS(JCV_EDGE_BOTTOM|JCV_EDGE_LEFT, JCV_CORNER_BOTTOM_LEFT); - TEST_FLAGS(JCV_EDGE_BOTTOM|JCV_EDGE_RIGHT, JCV_CORNER_BOTTOM_RIGHT); - #undef TEST_FLAGS - return 0; -} - -static inline int jcv_is_corner(int corner) -{ - return corner != 0; -} - -static inline int jcv_corner_rotate_90(int corner) -{ - corner--; - corner = (corner+1)%4; - return corner + 1; -} -static inline jcv_point jcv_corner_to_point(int corner, const jcv_point* min, const jcv_point* max ) +namespace jcv { - jcv_point p; - if (corner == JCV_CORNER_TOP_LEFT) { p[0] = min->x(); p[1] = max->y(); } - else if (corner == JCV_CORNER_TOP_RIGHT) { p[0] = max->x(); p[1] = max->y(); } - else if (corner == JCV_CORNER_BOTTOM_LEFT) { p[0] = min->x(); p[1] = min->y(); } - else if (corner == JCV_CORNER_BOTTOM_RIGHT) { p[0] = max->x(); p[1] = min->y(); } - else { p[0] = JCV_INVALID_VALUE; p[1] = JCV_INVALID_VALUE; } - return p; -} - -static inline jcv_real jcv_point_dist_sq( const jcv_point* pt1, const jcv_point* pt2) -{ - jcv_real diffx = pt1->x() - pt2->x(); - jcv_real diffy = pt1->y() - pt2->y(); - return diffx * diffx + diffy * diffy; -} - -static inline jcv_real jcv_point_dist( const jcv_point* pt1, const jcv_point* pt2 ) -{ - return (jcv_real)(JCV_SQRT(jcv_point_dist_sq(pt1, pt2))); -} - -// Structs - #pragma pack(push, 1) -typedef struct jcv_halfedge_ -{ - jcv_edge* edge; - struct jcv_halfedge_* left; - struct jcv_halfedge_* right; - jcv_point vertex; - jcv_real y; - int direction; // 0=left, 1=right - int pqpos; -} jcv_halfedge; - -typedef struct jcv_memoryblock_ -{ - size_t sizefree; - struct jcv_memoryblock_* next; - char* memory; -} jcv_memoryblock; - + // Seb has replaced the original point struct with use of his fixed-size mathematical vector + // class, sm::vec + template + using point = sm::vec; -typedef int (*FJCVPriorityQueuePrint)(const void* node, int pos); - -typedef struct jcv_priorityqueue_ -{ - // Implements a binary heap - int maxnumitems; - int numitems; - void** items; -} jcv_priorityqueue; - - -struct jcv_context_internal_ -{ - void* mem; - jcv_edge* edges; - jcv_halfedge* beachline_start; - jcv_halfedge* beachline_end; - jcv_halfedge* last_inserted; - jcv_priorityqueue* eventqueue; - - jcv_site* sites; - jcv_site* bottomsite; - int numsites; - int currentsite; - int _padding; - - jcv_memoryblock* memblocks; - jcv_edge* edgepool; - jcv_halfedge* halfedgepool; - void** eventmem; - jcv_clipper clipper; - - void* memctx; // Given by the user - FJCVAllocFn alloc; - FJCVFreeFn free; - - jcv_rect rect; -}; - -#pragma pack(pop) + // forward-declare graphedge + template struct graphedge; -void jcv_diagram_free( jcv_diagram* d ) -{ - jcv_context_internal* internal = d->internal; - void* memctx = internal->memctx; - FJCVFreeFn freefn = internal->free; - while(internal->memblocks) + template + struct site { - jcv_memoryblock* p = internal->memblocks; - internal->memblocks = internal->memblocks->next; - freefn( memctx, p ); - } - - freefn( memctx, internal->mem ); -} - -const jcv_site* jcv_diagram_get_sites( const jcv_diagram* diagram ) -{ - return diagram->internal->sites; -} - -const jcv_edge* jcv_diagram_get_edges( const jcv_diagram* diagram ) -{ - jcv_edge e; - e.next = diagram->internal->edges; - return jcv_diagram_get_next_edge(&e); -} - -const jcv_edge* jcv_diagram_get_next_edge( const jcv_edge* edge ) -{ - const jcv_edge* e = edge->next; - while (e != 0 && jcv_point_eq(&e->pos[0], &e->pos[1])) { - e = e->next; - } - return e; -} - -void jcv_delauney_begin( const jcv_diagram* diagram, jcv_delauney_iter* iter ) -{ - iter->current = 0; - iter->sentinel = jcv_diagram_get_edges(diagram); -} - -int jcv_delauney_next( jcv_delauney_iter* iter, jcv_delauney_edge* next ) -{ - if (iter->sentinel) + point p; + int index; // Index into the original list of points + graphedge* edges; // The half edges owned by the cell + }; + + // The coefficients a, b and c are from the general line equation: ax * by + c = 0 + template + struct edge { - iter->current = iter->sentinel; - iter->sentinel = 0; - } - else { - // Note: If we use the raw edges, we still get a proper delauney triangulation - // However, the result looks less relevant to the cells contained within the bounding box - // E.g. some cells that look isolated from each other, suddenly still are connected, - // because they share an edge outside of the bounding box - iter->current = jcv_diagram_get_next_edge(iter->current); - } - - while (iter->current && (iter->current->sites[0] == 0 || iter->current->sites[1] == 0)) + struct edge* next; + site* sites[2]; + point pos[2]; + T a; + T b; + T c; + }; + + template + struct graphedge { - iter->current = jcv_diagram_get_next_edge(iter->current); - } + struct graphedge* next; + struct edge* edge_; + struct site* neighbor; + point pos[2]; + T angle; + }; + + template + struct delauney_iter + { + const edge* sentinel; + const edge* current; + }; - if (!iter->current) - return 0; + template + struct delauney_edge + { + const edge* edge_; // The voronoi edge separating the two sites + const site* sites[2]; + point pos[2]; // the positions of the two sites + }; - next->edge = iter->current; - next->sites[0] = next->edge->sites[0]; - next->sites[1] = next->edge->sites[1]; - next->pos[0] = next->sites[0]->p; - next->pos[1] = next->sites[1]->p; - return 1; -} + template + struct rect + { + point min; + point max; + }; -static inline void* jcv_align(void* value, size_t alignment) -{ - return (void*) (((uintptr_t) value + (alignment-1)) & ~(alignment-1)); -} + // Forward declare context_internal for the std::function + template struct context_internal; -static void* jcv_alloc(jcv_context_internal* internal, size_t size) -{ - if( !internal->memblocks || internal->memblocks->sizefree < (size+sizeof(void*)) ) + template + struct clipper { - size_t blocksize = 16 * 1024; - jcv_memoryblock* block = (jcv_memoryblock*)internal->alloc( internal->memctx, blocksize ); - size_t offset = sizeof(jcv_memoryblock); - block->sizefree = blocksize - offset; - block->next = internal->memblocks; - block->memory = ((char*)block) + offset; - internal->memblocks = block; - } - void* p_raw = internal->memblocks->memory; - void* p_aligned = jcv_align(p_raw, sizeof(void*)); - size += (uintptr_t)p_aligned - (uintptr_t)p_raw; - internal->memblocks->memory += size; - internal->memblocks->sizefree -= size; - return p_aligned; -} - -static jcv_edge* jcv_alloc_edge(jcv_context_internal* internal) -{ - return (jcv_edge*)jcv_alloc(internal, sizeof(jcv_edge)); -} + // Tests if a point is inside the final shape + 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; + // 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, + context_internal* allocator, site* s)> fill_fn; + + point min; // The bounding rect min + point max; // The bounding rect max + void* ctx; // User defined context + }; + + // Second batch of structs + template + struct halfedge + { + edge* edge_; + struct halfedge* left; + struct halfedge* right; + point vertex; + T y; + int direction; // 0=left, 1=right + int pqpos; + }; + + struct memoryblock + { + size_t sizefree; + struct memoryblock* next; + char* memory; + }; -static jcv_halfedge* jcv_alloc_halfedge(jcv_context_internal* internal) -{ - if( internal->halfedgepool ) + struct priorityqueue { - jcv_halfedge* edge = internal->halfedgepool; - internal->halfedgepool = internal->halfedgepool->right; - return edge; - } + // Implements a binary heap + int maxnumitems; + int numitems; + void** items; + }; - return (jcv_halfedge*)jcv_alloc(internal, sizeof(jcv_halfedge)); -} + using FJCVAllocFn = void*(void* userctx, size_t size); + using FJCVFreeFn = void(void* userctx, void* p); -static jcv_graphedge* jcv_alloc_graphedge(jcv_context_internal* internal) -{ - return (jcv_graphedge*)jcv_alloc(internal, sizeof(jcv_graphedge)); -} + template + struct context_internal + { + void* mem; + edge* edges; + halfedge* beachline_start; + halfedge* beachline_end; + halfedge* last_inserted; + priorityqueue* eventqueue; + + site* sites; + site* bottomsite; + int numsites; + int currentsite; + int _padding; + + memoryblock* memblocks; + edge* edgepool; + halfedge* halfedgepool; + void** eventmem; + clipper clipper_; + + void* memctx; // Given by the user + std::function alloc; + std::function free; + + rect rect_; + }; + + template + struct diagram + { + context_internal* internal; + int numsites; + point min; + point max; + }; -static void* jcv_alloc_fn(void* memctx, size_t size) -{ - (void)memctx; - return malloc(size); -} +#pragma pack(pop) -static void jcv_free_fn(void* memctx, void* p) -{ - (void)memctx; - free(p); -} + // The mananger class. Type T is what is called real in the original code + template requires std::is_floating_point_v + struct manager + { + manager(){} + ~manager() { jcv::manager::diagram_free (&this->diagram); } -// jcv_edge + static constexpr T edge_intersect_threshold = T{JCV_EDGE_INTERSECT_THRESHOLD}; -static inline int jcv_is_valid(const jcv_point* p) -{ - return (p->x() != JCV_INVALID_VALUE || p->y() != JCV_INVALID_VALUE) ? 1 : 0; -} + // INTERNAL FUNCTIONS -static void jcv_edge_create(jcv_edge* e, jcv_site* s1, jcv_site* s2) -{ - e->next = 0; - e->sites[0] = s1; - e->sites[1] = s2; - e->pos[0][0] = JCV_INVALID_VALUE; - e->pos[0][1] = JCV_INVALID_VALUE; - e->pos[1][0] = JCV_INVALID_VALUE; - e->pos[1][1] = JCV_INVALID_VALUE; - - // Create line equation between S1 and S2: - // jcv_real a = -1 * (s2->p[1] - s1->p[1]); - // jcv_real b = s2->p[0] - s1->p[0]; - // //jcv_real c = -1 * (s2->p[0] - s1->p[0]) * s1->p[1] + (s2->p[1] - s1->p[1]) * s1->p[0]; - // - // // create perpendicular line - // jcv_real pa = b; - // jcv_real pb = -a; - // //jcv_real pc = pa * s1->p[0] + pb * s1->p[1]; - // - // // Move to the mid point - // jcv_real mx = s1->p[0] + dx * jcv_real(0.5); - // jcv_real my = s1->p[1] + dy * jcv_real(0.5); - // jcv_real pc = ( pa * mx + pb * my ); - - jcv_real dx = s2->p[0] - s1->p[0]; - jcv_real dy = s2->p[1] - s1->p[1]; - int dx_is_larger = (dx*dx) > (dy*dy); // instead of fabs - - // Simplify it, using dx and dy - e->c = dx * (s1->p[0] + dx * (jcv_real)0.5) + dy * (s1->p[1] + dy * (jcv_real)0.5); - - if( dx_is_larger ) - { - e->a = (jcv_real)1; - e->b = dy / dx; - e->c /= dx; - } - else - { - e->a = dx / dy; - e->b = (jcv_real)1; - e->c /= dy; - } -} + static const int DIRECTION_LEFT = 0; + static const int DIRECTION_RIGHT = 1; -// CLIPPING -int jcv_boxshape_test(const jcv_clipper* clipper, const jcv_point p) -{ - return p[0] >= clipper->min[0] && p[0] <= clipper->max[0] && - p[1] >= clipper->min[1] && p[1] <= clipper->max[1]; -} + static constexpr T invalid_value = std::numeric_limits::lowest(); -// The line equation: ax + by + c = 0 -// see jcv_edge_create -int jcv_boxshape_clip(const jcv_clipper* clipper, jcv_edge* e) -{ - jcv_real pxmin = clipper->min[0]; - jcv_real pxmax = clipper->max[0]; - jcv_real pymin = clipper->min[1]; - jcv_real pymax = clipper->max[1]; - - jcv_real x1, y1, x2, y2; - jcv_point* s1; - jcv_point* s2; - if (e->a == (jcv_real)1 && e->b >= (jcv_real)0) - { - s1 = jcv_is_valid(&e->pos[1]) ? &e->pos[1] : 0; - s2 = jcv_is_valid(&e->pos[0]) ? &e->pos[0] : 0; - } - else - { - s1 = jcv_is_valid(&e->pos[0]) ? &e->pos[0] : 0; - s2 = jcv_is_valid(&e->pos[1]) ? &e->pos[1] : 0; - } + // App specific equality of scalars + static int equal (T a, T b) { return std::abs(a - b) < std::numeric_limits::epsilon(); } - if (e->a == (jcv_real)1) // delta x is larger - { - y1 = pymin; - if (s1 != 0 && s1->y() > pymin) - { - y1 = s1->y(); - } - if( y1 > pymax ) + // Vector equality + static int equal (const point* pt1, const point* pt2) { - y1 = pymax; + return equal (pt1->y(), pt2->y()) && equal (pt1->x(), pt2->x()); } - x1 = e->c - e->b * y1; - y2 = pymax; - if (s2 != 0 && s2->y() < pymax) - y2 = s2->y(); - if( y2 < pymin ) + // point (i.e. vector) comparison, accepting void pointers + static int point_cmp (const void* p1, const void* p2) { - y2 = pymin; + const point* s1 = static_cast*>(p1); + const point* s2 = static_cast*>(p2); + return (s1->y() != s2->y()) ? (s1->y() < s2->y() ? -1 : 1) : (s1->x() < s2->x() ? -1 : 1); } - 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) + + // Return app-specific pt1 < pt2 operation between vectors + static int lessthan (const point* pt1, const point* pt2) { - x1 = pxmax; - y1 = (e->c - x1) / e->b; + return (pt1->y() == pt2->y()) ? (pt1->x() < pt2->x()) : pt1->y() < pt2->y(); } - else if (x1 < pxmin) + + [[maybe_unused]] + static int point_on_box_edge (const point* pt, const point* min, const point* max) { - x1 = pxmin; - y1 = (e->c - x1) / e->b; + return pt->x() == min->x() || pt->y() == min->y() || pt->x() == max->x() || pt->y() == max->y(); } - if (x2 > pxmax) + + // edges and corners + static const int EDGE_LEFT = 1; + static const int EDGE_RIGHT = 2; + static const int EDGE_BOTTOM = 4; + static const int EDGE_TOP = 8; + + static const int CORNER_NONE = 0; + static const int CORNER_TOP_LEFT = 1; + static const int CORNER_BOTTOM_LEFT = 2; + static const int CORNER_BOTTOM_RIGHT = 3; + static const int CORNER_TOP_RIGHT = 4; + + static int get_edge_flags (const point* pt, const point* min, const point* max) { - x2 = pxmax; - y2 = (e->c - x2) / e->b; + int flags = 0; + if (pt->x() == min->x()) { + flags |= EDGE_LEFT; + } else if (pt->x() == max->x()) { + flags |= EDGE_RIGHT; + } + if (pt->y() == min->y()) { + flags |= EDGE_BOTTOM; + } else if (pt->y() == max->y()) { + flags |= EDGE_TOP; + } + return flags; } - else if (x2 < pxmin) + + static int edge_flags_to_corner (int edge_flags) { - x2 = pxmin; - y2 = (e->c - x2) / e->b; +#define TEST_FLAGS(_FLAGS, _RETVAL) if ( (_FLAGS) == edge_flags) return _RETVAL + TEST_FLAGS (EDGE_TOP|EDGE_LEFT, CORNER_TOP_LEFT); + TEST_FLAGS (EDGE_TOP|EDGE_RIGHT, CORNER_TOP_RIGHT); + TEST_FLAGS (EDGE_BOTTOM|EDGE_LEFT, CORNER_BOTTOM_LEFT); + TEST_FLAGS (EDGE_BOTTOM|EDGE_RIGHT, CORNER_BOTTOM_RIGHT); +#undef TEST_FLAGS + return 0; } - } - else // delta y is larger - { - x1 = pxmin; - if( s1 != 0 && s1->x() > pxmin ) - x1 = s1->x(); - if( x1 > pxmax ) + + [[maybe_unused]] static int is_corner(int corner) { return corner != 0; } + + static int corner_rotate_90 (int corner) { - x1 = pxmax; + corner--; + corner = (corner+1)%4; + return corner + 1; } - y1 = e->c - e->a * x1; - x2 = pxmax; - if( s2 != 0 && s2->x() < pxmax ) - x2 = s2->x(); - if( x2 < pxmin ) + + static point corner_to_point (int corner, const point* min, const point* max) { - x2 = pxmin; + point p; + if (corner == CORNER_TOP_LEFT) { p[0] = min->x(); p[1] = max->y(); } + else if (corner == CORNER_TOP_RIGHT) { p[0] = max->x(); p[1] = max->y(); } + else if (corner == CORNER_BOTTOM_LEFT) { p[0] = min->x(); p[1] = min->y(); } + else if (corner == CORNER_BOTTOM_RIGHT) { p[0] = max->x(); p[1] = min->y(); } + else { p[0] = invalid_value; p[1] = invalid_value; } + return p; } - 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 ) + + static T point_dist_sq (const point* pt1, const point* pt2) { - y1 = pymax; - x1 = (e->c - y1) / e->a; + T diffx = pt1->x() - pt2->x(); + T diffy = pt1->y() - pt2->y(); + return diffx * diffx + diffy * diffy; } - else if( y1 < pymin ) + + static T point_dist (const point* pt1, const point* pt2) { - y1 = pymin; - x1 = (e->c - y1) / e->a; + return std::sqrt (point_dist_sq (pt1, pt2)); } - if( y2 > pymax ) + + // Uses free (or the registered custom free function) + static void diagram_free (diagram* d) { - y2 = pymax; - x2 = (e->c - y2) / e->a; + context_internal* internal = d->internal; + void* memctx = internal->memctx; + while (internal->memblocks) { + memoryblock* p = internal->memblocks; + internal->memblocks = internal->memblocks->next; + internal->free (memctx, p); + } + + internal->free (memctx, internal->mem); } - else if( y2 < pymin ) + + // Returns an array of sites, where each index is the same as the original input point array. + static const site* diagram_get_sites (const diagram* diagram) { - y2 = pymin; - x2 = (e->c - y2) / e->a; + return diagram->internal->sites; } - } - e->pos[0][0] = x1; - e->pos[0][1] = y1; - e->pos[1][0] = x2; - e->pos[1][1] = y2; + // User API + const site* diagram_get_sites() { return diagram_get_sites (&this->diagram); } - // If the two points are equal, the result is invalid - return (x1 == x2 && y1 == y2) ? 0 : 1; -} + // Iterates over a list of edges, skipping invalid edges (where p0==p1) + const edge* diagram_get_next_edge (const edge* _edge) + { + const edge* e = _edge->next; + while (e != 0 && equal (&e->pos[0], &e->pos[1])) { + e = e->next; + } + return e; + } -// The line equation: ax + by + c = 0 -// see jcv_edge_create -static int jcv_edge_clipline(jcv_context_internal* internal, jcv_edge* e) -{ - return internal->clipper.clip_fn(&internal->clipper, e); -} + // Returns a linked list of all the voronoi edges excluding the ones that lie on the borders of + // the bounding box. For a full list of edges, you need to iterate over the sites, and their + // graph edges. + const edge* diagram_get_edges (const diagram* diagram) + { + edge e; + e.next = diagram->internal->edges; + return diagram_get_next_edge (&e); + } -static jcv_edge* jcv_edge_new(jcv_context_internal* internal, jcv_site* s1, jcv_site* s2) -{ - jcv_edge* e = jcv_alloc_edge(internal); - jcv_edge_create(e, s1, s2); - return e; -} + // Creates an iterator over the delauney edges of a voronoi diagram + void delauney_begin (const diagram* diagram, delauney_iter* iter) + { + iter->current = 0; + iter->sentinel = diagram_get_edges (diagram); + } + // Steps the iterator and returns the next edge Returns 0 when there are no more edges + int delauney_next (delauney_iter* iter, delauney_edge* next) + { + if (iter->sentinel) { + iter->current = iter->sentinel; + iter->sentinel = 0; + } else { + // Note: If we use the raw edges, we still get a proper delauney triangulation + // However, the result looks less relevant to the cells contained within the bounding box + // E.g. some cells that look isolated from each other, suddenly still are connected, + // because they share an edge outside of the bounding box + iter->current = diagram_get_next_edge (iter->current); + } -// jcv_halfedge + while (iter->current && (iter->current->sites[0] == 0 || iter->current->sites[1] == 0)) { + iter->current = diagram_get_next_edge(iter->current); + } -static void jcv_halfedge_link(jcv_halfedge* edge, jcv_halfedge* newedge) -{ - newedge->left = edge; - newedge->right = edge->right; - edge->right->left = newedge; - edge->right = newedge; -} + if (!iter->current) { return 0; } -static inline void jcv_halfedge_unlink(jcv_halfedge* he) -{ - he->left->right = he->right; - he->right->left = he->left; - he->left = 0; - he->right = 0; -} + next->edge_ = iter->current; + next->sites[0] = next->edge_->sites[0]; + next->sites[1] = next->edge_->sites[1]; + next->pos[0] = next->sites[0]->p; + next->pos[1] = next->sites[1]->p; + return 1; + } -static inline jcv_halfedge* jcv_halfedge_new(jcv_context_internal* internal, jcv_edge* e, int direction) -{ - jcv_halfedge* he = jcv_alloc_halfedge(internal); - he->edge = e; - he->left = 0; - he->right = 0; - he->direction = direction; - he->pqpos = 0; - // These are set outside - //he->y() - //he->vertex - return he; -} - -static void jcv_halfedge_delete(jcv_context_internal* internal, jcv_halfedge* he) -{ - he->right = internal->halfedgepool; - internal->halfedgepool = he; -} + static void* align (void* value, size_t alignment) + { + return (void*) (((uintptr_t) value + (alignment-1)) & ~(alignment-1)); + } -static inline jcv_site* jcv_halfedge_leftsite(const jcv_halfedge* he) -{ - return he->edge->sites[he->direction]; -} + static void* alloc (context_internal* internal, size_t size) + { + if (!internal->memblocks || internal->memblocks->sizefree < (size + sizeof(void*))) { + size_t blocksize = 16 * 1024; + memoryblock* block = (memoryblock*)internal->alloc (internal->memctx, blocksize); + size_t offset = sizeof(memoryblock); + block->sizefree = blocksize - offset; + block->next = internal->memblocks; + block->memory = ((char*)block) + offset; + internal->memblocks = block; + } + void* p_raw = internal->memblocks->memory; + void* p_aligned = align (p_raw, sizeof(void*)); + size += (uintptr_t)p_aligned - (uintptr_t)p_raw; + internal->memblocks->memory += size; + internal->memblocks->sizefree -= size; + return p_aligned; + } -static inline jcv_site* jcv_halfedge_rightsite(const jcv_halfedge* he) -{ - return he->edge ? he->edge->sites[1 - he->direction] : 0; -} + static edge* alloc_edge (context_internal* internal) + { + return (edge*) alloc (internal, sizeof(edge)); + } -static int jcv_halfedge_rightof(const jcv_halfedge* he, const jcv_point* p) -{ - const jcv_edge* e = he->edge; - const jcv_site* topsite = e->sites[1]; + static halfedge* alloc_halfedge (context_internal* internal) + { + if (internal->halfedgepool) { + halfedge* edge = internal->halfedgepool; + internal->halfedgepool = internal->halfedgepool->right; + return edge; + } + return (halfedge*) alloc (internal, sizeof(halfedge)); + } - int right_of_site = (p->x() > topsite->p[0]) ? 1 : 0; - if (right_of_site && he->direction == JCV_DIRECTION_LEFT) - return 1; - if (!right_of_site && he->direction == JCV_DIRECTION_RIGHT) - return 0; + static graphedge* alloc_graphedge (context_internal* internal) + { + return (graphedge*) alloc (internal, sizeof(graphedge)); + } - jcv_real dxp, dyp, dxs, t1, t2, t3, yl; + static void* alloc_fn (void* memctx, size_t size) + { + (void)memctx; + return malloc (size); + } - int above; - if (e->a == (jcv_real)1) - { - dyp = p->y() - topsite->p[1]; - dxp = p->x() - topsite->p[0]; - int fast = 0; - if( (!right_of_site & (e->b < (jcv_real)0)) | (right_of_site & (e->b >= (jcv_real)0)) ) + static void free_fn (void* memctx, void* p) { - above = dyp >= e->b * dxp; - fast = above; + (void)memctx; + free (p); } - else + + // edge methods + static int is_valid (const point* p) { - above = (p->x() + p->y() * e->b) > e->c; - if (e->b < (jcv_real)0) - above = !above; - if (!above) - fast = 1; + return (p->x() != invalid_value || p->y() != invalid_value) ? 1 : 0; } - if (!fast) + + static void edge_create (edge* e, site* s1, site* s2) { - dxs = topsite->p[0] - e->sites[0]->p[0]; - above = e->b * (dxp * dxp - dyp * dyp) - < dxs * dyp * ((jcv_real)1 + (jcv_real)2 * dxp / dxs + e->b * e->b); - if (e->b < (jcv_real)0) - above = !above; + e->next = 0; + e->sites[0] = s1; + e->sites[1] = s2; + e->pos[0][0] = invalid_value; + e->pos[0][1] = invalid_value; + e->pos[1][0] = invalid_value; + e->pos[1][1] = invalid_value; + + // Create line equation between S1 and S2: + // T a = -1 * (s2->p[1] - s1->p[1]); + // T b = s2->p[0] - s1->p[0]; + // //T c = -1 * (s2->p[0] - s1->p[0]) * s1->p[1] + (s2->p[1] - s1->p[1]) * s1->p[0]; + // + // // create perpendicular line + // T pa = b; + // T pb = -a; + // //T pc = pa * s1->p[0] + pb * s1->p[1]; + // + // // Move to the mid point + // T mx = s1->p[0] + dx * T(0.5); + // T my = s1->p[1] + dy * T(0.5); + // T pc = (pa * mx + pb * my); + + T dx = s2->p[0] - s1->p[0]; + T dy = s2->p[1] - s1->p[1]; + int dx_is_larger = (dx*dx) > (dy*dy); // instead of fabs + + // Simplify it, using dx and dy + e->c = dx * (s1->p[0] + dx * T{0.5}) + dy * (s1->p[1] + dy * T{0.5}); + + if (dx_is_larger) { + e->a = T{1}; + e->b = dy / dx; + e->c /= dx; + } else { + e->a = dx / dy; + e->b = T{1}; + e->c /= dy; + } } - } - else // e->b == 1 - { - yl = e->c - e->a * p->x(); - t1 = p->y() - yl; - t2 = p->x() - topsite->p[0]; - t3 = yl - topsite->p[1]; - above = t1 * t1 > (t2 * t2 + t3 * t3); - } - return (he->direction == JCV_DIRECTION_LEFT ? above : !above); -} -// Keeps the priority queue sorted with events sorted in ascending order -// Return 1 if the edges needs to be swapped -static inline int jcv_halfedge_compare( const jcv_halfedge* he1, const jcv_halfedge* he2 ) -{ - return (he1->y == he2->y) ? he1->vertex[0] > he2->vertex[0] : he1->y > he2->y; -} + // CLIPPING + 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]; + } -static int jcv_halfedge_intersect(const jcv_halfedge* he1, const jcv_halfedge* he2, jcv_point* out) -{ - const jcv_edge* e1 = he1->edge; - const jcv_edge* e2 = he2->edge; + // The line equation: ax + by + c = 0 + // see edge_create + static int boxshape_clip (const clipper* clipper, edge* e) + { + T pxmin = clipper->min[0]; + T pxmax = clipper->max[0]; + T pymin = clipper->min[1]; + T pymax = clipper->max[1]; + + T x1, y1, x2, y2; + point* s1; + point* s2; + if (e->a == T{1} && e->b >= T{0}) { + s1 = is_valid(&e->pos[1]) ? &e->pos[1] : 0; + s2 = is_valid(&e->pos[0]) ? &e->pos[0] : 0; + } else { + s1 = is_valid(&e->pos[0]) ? &e->pos[0] : 0; + s2 = is_valid(&e->pos[1]) ? &e->pos[1] : 0; + } - jcv_real d = e1->a * e2->b - e1->b * e2->a; - if( ((jcv_real)-JCV_EDGE_INTERSECT_THRESHOLD < d && d < (jcv_real)JCV_EDGE_INTERSECT_THRESHOLD) ) - { - return 0; - } - (*out)[0] = (e1->c * e2->b - e1->b * e2->c) / d; - (*out)[1] = (e1->a * e2->c - e1->c * e2->a) / d; - // I considered trying to determine the correct z here, but we don't have all the - // information required. So just set out->z to a default value meaning 'unset' (Seb) - (*out)[2] = 0.0f; // NB: this does not set z for all edges - - const jcv_edge* e; - const jcv_halfedge* he; - if( jcv_point_less( &e1->sites[1]->p, &e2->sites[1]->p) ) - { - he = he1; - e = e1; - } - else - { - he = he2; - e = e2; - } + if (e->a == T{1}) { // delta x is larger + y1 = pymin; + if (s1 != 0 && s1->y() > pymin) { y1 = s1->y(); } + if (y1 > pymax) { y1 = pymax; } + x1 = e->c - e->b * y1; + y2 = pymax; + if (s2 != 0 && s2->y() < pymax) { y2 = s2->y(); } + + 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) { + x1 = pxmax; + y1 = (e->c - x1) / e->b; + } else if (x1 < pxmin) { + x1 = pxmin; + y1 = (e->c - x1) / e->b; + } + if (x2 > pxmax) { + x2 = pxmax; + y2 = (e->c - x2) / e->b; + } else if (x2 < pxmin) { + x2 = pxmin; + y2 = (e->c - x2) / e->b; + } - int right_of_site = out->x() >= e->sites[1]->p[0]; - if ((right_of_site && he->direction == JCV_DIRECTION_LEFT) || (!right_of_site && he->direction == JCV_DIRECTION_RIGHT)) - { - return 0; - } + } else { // delta y is larger + + x1 = pxmin; + if (s1 != 0 && s1->x() > pxmin) { x1 = s1->x(); } + if (x1 > pxmax) { x1 = pxmax; } + y1 = e->c - e->a * x1; + x2 = pxmax; + if (s2 != 0 && s2->x() < pxmax) { x2 = s2->x(); } + 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) { + y1 = pymax; + x1 = (e->c - y1) / e->a; + } else if (y1 < pymin) { + y1 = pymin; + x1 = (e->c - y1) / e->a; + } + if (y2 > pymax) { + y2 = pymax; + x2 = (e->c - y2) / e->a; + } else if (y2 < pymin) { + y2 = pymin; + x2 = (e->c - y2) / e->a; + } + } - return 1; -} + e->pos[0][0] = x1; + e->pos[0][1] = y1; + e->pos[1][0] = x2; + e->pos[1][1] = y2; + // If the two points are equal, the result is invalid + return (x1 == x2 && y1 == y2) ? 0 : 1; + } -// Priority queue + // The line equation: ax + by + c = 0 + // see edge_create + static int edge_clipline (context_internal* internal, edge* e) + { + return internal->clipper_.clip_fn (&internal->clipper_, e); + } -static int jcv_pq_moveup(jcv_priorityqueue* pq, int pos) -{ - jcv_halfedge** items = (jcv_halfedge**)pq->items; - jcv_halfedge* node = items[pos]; + static edge* edge_new (context_internal* internal, site* s1, site* s2) + { + edge* e = alloc_edge (internal); + edge_create (e, s1, s2); + return e; + } - for( int parent = (pos >> 1); - pos > 1 && jcv_halfedge_compare(items[parent], node); - pos = parent, parent = parent >> 1) - { - items[pos] = items[parent]; - items[pos]->pqpos = pos; - } - node->pqpos = pos; - items[pos] = node; - return pos; -} + // halfedge -static int jcv_pq_maxchild(jcv_priorityqueue* pq, int pos) -{ - int child = pos << 1; - if( child >= pq->numitems ) - return 0; - jcv_halfedge** items = (jcv_halfedge**)pq->items; - if( (child + 1) < pq->numitems && jcv_halfedge_compare(items[child], items[child+1]) ) - return child+1; - return child; -} - -static int jcv_pq_movedown(jcv_priorityqueue* pq, int pos) -{ - jcv_halfedge** items = (jcv_halfedge**)pq->items; - jcv_halfedge* node = items[pos]; + static void halfedge_link (halfedge* edge, halfedge* newedge) + { + newedge->left = edge; + newedge->right = edge->right; + edge->right->left = newedge; + edge->right = newedge; + } - int child = jcv_pq_maxchild(pq, pos); - while( child && jcv_halfedge_compare(node, items[child]) ) - { - items[pos] = items[child]; - items[pos]->pqpos = pos; - pos = child; - child = jcv_pq_maxchild(pq, pos); - } + static void halfedge_unlink (halfedge* he) + { + he->left->right = he->right; + he->right->left = he->left; + he->left = 0; + he->right = 0; + } - items[pos] = node; - items[pos]->pqpos = pos; - return pos; -} + static halfedge* halfedge_new (context_internal* internal, edge* e, int direction) + { + halfedge* he = alloc_halfedge (internal); + he->edge_ = e; + he->left = 0; + he->right = 0; + he->direction = direction; + he->pqpos = 0; + // These are set outside + //he->y() + //he->vertex + return he; + } -static void jcv_pq_create(jcv_priorityqueue* pq, int capacity, void** buffer) -{ - pq->maxnumitems = capacity; - pq->numitems = 1; - pq->items = buffer; -} + static void halfedge_delete (context_internal* internal, halfedge* he) + { + he->right = internal->halfedgepool; + internal->halfedgepool = he; + } -static int jcv_pq_empty(jcv_priorityqueue* pq) -{ - return pq->numitems == 1 ? 1 : 0; -} + static site* halfedge_leftsite (const halfedge* he) + { + return he->edge_->sites[he->direction]; + } -static int jcv_pq_push(jcv_priorityqueue* pq, void* node) -{ - assert(pq->numitems < pq->maxnumitems); - int n = pq->numitems++; - pq->items[n] = node; - return jcv_pq_moveup(pq, n); -} + static site* halfedge_rightsite (const halfedge* he) + { + return he->edge_ ? he->edge_->sites[1 - he->direction] : 0; + } -static void* jcv_pq_pop(jcv_priorityqueue* pq) -{ - void* node = pq->items[1]; - pq->items[1] = pq->items[--pq->numitems]; - jcv_pq_movedown(pq, 1); - return node; -} + static int halfedge_rightof (const halfedge* he, const point* p) + { + const edge* e = he->edge_; + const site* topsite = e->sites[1]; + + int right_of_site = (p->x() > topsite->p[0]) ? 1 : 0; + if (right_of_site && he->direction == DIRECTION_LEFT) { return 1; } + if (!right_of_site && he->direction == DIRECTION_RIGHT) { return 0; } + + T dxp, dyp, dxs, t1, t2, t3, yl; + + int above; + if (e->a == T{1}) { + dyp = p->y() - topsite->p[1]; + dxp = p->x() - topsite->p[0]; + int fast = 0; + if ((!right_of_site & (e->b < T{0})) | (right_of_site & (e->b >= T{0}))) { + above = dyp >= e->b * dxp; + fast = above; + } else { + above = (p->x() + p->y() * e->b) > e->c; + if (e->b < T{0}) { above = !above; } + if (!above) { fast = 1; } + } + if (!fast) { + dxs = topsite->p[0] - e->sites[0]->p[0]; + above = e->b * (dxp * dxp - dyp * dyp) + < dxs * dyp * (T{1} + T{2} * dxp / dxs + e->b * e->b); + if (e->b < T{0}) { above = !above; } + } + } else { // e->b == 1 + yl = e->c - e->a * p->x(); + t1 = p->y() - yl; + t2 = p->x() - topsite->p[0]; + t3 = yl - topsite->p[1]; + above = t1 * t1 > (t2 * t2 + t3 * t3); + } + return (he->direction == DIRECTION_LEFT ? above : !above); + } -static void* jcv_pq_top(jcv_priorityqueue* pq) -{ - return pq->items[1]; -} + // Keeps the priority queue sorted with events sorted in ascending order + // Return 1 if the edges needs to be swapped + static int halfedge_compare (const halfedge* he1, const halfedge* he2) + { + return (he1->y == he2->y) ? he1->vertex[0] > he2->vertex[0] : he1->y > he2->y; + } -static void jcv_pq_remove(jcv_priorityqueue* pq, jcv_halfedge* node) -{ - if( pq->numitems == 1 ) - return; - int pos = node->pqpos; - if( pos == 0 ) - return; + static int halfedge_intersect (const halfedge* he1, const halfedge* he2, point* out) + { + const edge* e1 = he1->edge_; + const edge* e2 = he2->edge_; + + T d = e1->a * e2->b - e1->b * e2->a; + if (-edge_intersect_threshold < d && d < edge_intersect_threshold) { return 0; } + (*out)[0] = (e1->c * e2->b - e1->b * e2->c) / d; + (*out)[1] = (e1->a * e2->c - e1->c * e2->a) / d; + // I considered trying to determine the correct z here, but we don't have all the + // information required. So just set out->z to a default value meaning 'unset' (Seb) + (*out)[2] = 0.0f; // NB: this does not set z for all edges + + const edge* e; + const halfedge* he; + if (lessthan (&e1->sites[1]->p, &e2->sites[1]->p)) { + he = he1; + e = e1; + } else { + he = he2; + e = e2; + } - jcv_halfedge** items = (jcv_halfedge**)pq->items; + int right_of_site = out->x() >= e->sites[1]->p[0]; + if ((right_of_site && he->direction == DIRECTION_LEFT) + || (!right_of_site && he->direction == DIRECTION_RIGHT)) { + return 0; + } - items[pos] = items[--pq->numitems]; - if( jcv_halfedge_compare( node, items[pos] ) ) - jcv_pq_moveup( pq, pos ); - else - jcv_pq_movedown( pq, pos ); - node->pqpos = pos; -} + return 1; + } -// internal functions -static inline jcv_site* jcv_nextsite(jcv_context_internal* internal) -{ - return (internal->currentsite < internal->numsites) ? &internal->sites[internal->currentsite++] : 0; -} + // Priority queue -static jcv_halfedge* jcv_get_edge_above_x(jcv_context_internal* internal, const jcv_point* p) -{ - // Gets the arc on the beach line at the x coordinate (i.e. right above the new site event) + static int pq_moveup (priorityqueue* pq, int pos) + { + halfedge** items = (halfedge**)pq->items; + halfedge* node = items[pos]; - // A good guess it's close by (Can be optimized) - jcv_halfedge* he = internal->last_inserted; - if( !he ) - { - if( p->x() < (internal->rect.max[0] - internal->rect.min[0]) / 2 ) - he = internal->beachline_start; - else - he = internal->beachline_end; - } + for (int parent = (pos >> 1); pos > 1 && halfedge_compare(items[parent], node); pos = parent, parent = parent >> 1) { + items[pos] = items[parent]; + items[pos]->pqpos = pos; + } - // - if( he == internal->beachline_start || (he != internal->beachline_end && jcv_halfedge_rightof(he, p)) ) - { - do { - he = he->right; + node->pqpos = pos; + items[pos] = node; + return pos; } - while( he != internal->beachline_end && jcv_halfedge_rightof(he, p) ); - he = he->left; - } - else - { - do { - he = he->left; + static int pq_maxchild (priorityqueue* pq, int pos) + { + int child = pos << 1; + if (child >= pq->numitems) { return 0; } + halfedge** items = (halfedge**)pq->items; + if ((child + 1) < pq->numitems && halfedge_compare (items[child], items[child+1])) { return child + 1; } + return child; } - while( he != internal->beachline_start && !jcv_halfedge_rightof(he, p) ); - } - return he; -} + static int pq_movedown (priorityqueue* pq, int pos) + { + halfedge** items = (halfedge**)pq->items; + halfedge* node = items[pos]; + + int child = pq_maxchild (pq, pos); + while (child && halfedge_compare (node, items[child])) { + items[pos] = items[child]; + items[pos]->pqpos = pos; + pos = child; + child = pq_maxchild(pq, pos); + } -static int jcv_check_circle_event(const jcv_halfedge* he1, const jcv_halfedge* he2, jcv_point* vertex) -{ - jcv_edge* e1 = he1->edge; - jcv_edge* e2 = he2->edge; - if( e1 == 0 || e2 == 0 || e1->sites[1] == e2->sites[1] ) - { - return 0; - } + items[pos] = node; + items[pos]->pqpos = pos; + return pos; + } - return jcv_halfedge_intersect(he1, he2, vertex); -} + static void pq_create (priorityqueue* pq, int capacity, void** buffer) + { + pq->maxnumitems = capacity; + pq->numitems = 1; + pq->items = buffer; + } -static void jcv_site_event(jcv_context_internal* internal, jcv_site* site) -{ - jcv_halfedge* left = jcv_get_edge_above_x(internal, &site->p); - jcv_halfedge* right = left->right; - jcv_site* bottom = jcv_halfedge_rightsite(left); - if( !bottom ) - bottom = internal->bottomsite; + static int pq_empty (priorityqueue* pq) { return pq->numitems == 1 ? 1 : 0; } - jcv_edge* edge = jcv_edge_new(internal, bottom, site); - edge->next = internal->edges; - internal->edges = edge; + static int pq_push (priorityqueue* pq, void* node) + { + assert (pq->numitems < pq->maxnumitems); + int n = pq->numitems++; + pq->items[n] = node; + return pq_moveup(pq, n); + } - jcv_halfedge* edge1 = jcv_halfedge_new(internal, edge, JCV_DIRECTION_LEFT); - jcv_halfedge* edge2 = jcv_halfedge_new(internal, edge, JCV_DIRECTION_RIGHT); + static void* pq_pop (priorityqueue* pq) + { + void* node = pq->items[1]; + pq->items[1] = pq->items[--pq->numitems]; + pq_movedown(pq, 1); + return node; + } - jcv_halfedge_link(left, edge1); - jcv_halfedge_link(edge1, edge2); + static void* pq_top (priorityqueue* pq) { return pq->items[1]; } - internal->last_inserted = right; + static void pq_remove (priorityqueue* pq, halfedge* node) + { + if (pq->numitems == 1) { return; } + int pos = node->pqpos; + if (pos == 0) { return; } - jcv_point p; - if( jcv_check_circle_event( left, edge1, &p ) ) - { - jcv_pq_remove(internal->eventqueue, left); - left->vertex = p; - left->y = p[1] + jcv_point_dist(&site->p, &p); - jcv_pq_push(internal->eventqueue, left); - } - if( jcv_check_circle_event( edge2, right, &p ) ) - { - edge2->vertex = p; - edge2->y = p[1] + jcv_point_dist(&site->p, &p); - jcv_pq_push(internal->eventqueue, edge2); - } -} + halfedge** items = (halfedge**)pq->items; -// https://cp-algorithms.com/geometry/oriented-triangle-area.html -static inline jcv_real jcv_determinant(const jcv_point* a, const jcv_point* b, const jcv_point* c) -{ - return (b->x() - a->x())*(c->y() - a->y()) - (b->y() - a->y())*(c->x() - a->x()); -} + items[pos] = items[--pq->numitems]; + if (halfedge_compare (node, items[pos])) { + pq_moveup (pq, pos); + } else { + pq_movedown (pq, pos); + } + node->pqpos = pos; + } -static inline jcv_real jcv_calc_sort_metric(const jcv_site* site, const jcv_graphedge* edge) -{ - // We take the average of the two points, since we can better distinguish between very small edges - jcv_real half = 1/(jcv_real)2; - jcv_real x = (edge->pos[0][0] + edge->pos[1][0]) * half; - jcv_real y = (edge->pos[0][1] + edge->pos[1][1]) * half; - jcv_real diffy = y - site->p[1]; - jcv_real angle = JCV_ATAN2( diffy, x - site->p[0] ); - if( diffy < 0 ) - angle = angle + 2 * JCV_PI; - return (jcv_real)angle; -} - -static inline int jcv_graphedge_eq(jcv_graphedge* a, jcv_graphedge* b) -{ - return jcv_real_eq(a->angle, b->angle) && jcv_point_eq( &a->pos[0], &b->pos[0] ) && jcv_point_eq( &a->pos[1], &b->pos[1] ); -} + // internal functions -static void jcv_sortedges_insert(jcv_site* site, jcv_graphedge* edge) -{ - // Special case for the head end - jcv_graphedge* prev = 0; - if (site->edges == 0 || site->edges->angle >= edge->angle) - { - edge->next = site->edges; - site->edges = edge; - } - else - { - // Locate the node before the point of insertion - jcv_graphedge* current = site->edges; - while(current->next != 0 && current->next->angle < edge->angle) + static site* nextsite (context_internal* internal) { - current = current->next; + return (internal->currentsite < internal->numsites) ? &internal->sites[internal->currentsite++] : 0; } - prev = current; - edge->next = current->next; - current->next = edge; - } - // check to avoid duplicates - if (prev && jcv_graphedge_eq(prev, edge)) - { - prev->next = edge->next; - } - else if (edge->next && jcv_graphedge_eq(edge, edge->next)) - { - edge->next = edge->next->next; - } -} + static halfedge* get_edge_above_x (context_internal* internal, const point* p) + { + // Gets the arc on the beach line at the x coordinate (i.e. right above the new site event) -static void jcv_finishline(jcv_context_internal* internal, jcv_edge* e) -{ - if( !jcv_edge_clipline(internal, e) ) { - return; - } + // A good guess it's close by (Can be optimized) + halfedge* he = internal->last_inserted; + if (!he) { + if (p->x() < (internal->rect_.max[0] - internal->rect_.min[0]) / 2) { he = internal->beachline_start; } + else { he = internal->beachline_end; } + } - // Make sure the graph edges are CCW - int flip = jcv_determinant(&e->sites[0]->p, &e->pos[0], &e->pos[1]) > (jcv_real)0 ? 0 : 1; + if (he == internal->beachline_start || (he != internal->beachline_end && halfedge_rightof(he, p))) { + do { he = he->right; } + while (he != internal->beachline_end && halfedge_rightof(he, p)); - for( int i = 0; i < 2; ++i ) - { - jcv_graphedge* ge = jcv_alloc_graphedge(internal); + he = he->left; + } else { + do { he = he->left; } + while (he != internal->beachline_start && !halfedge_rightof(he, p)); + } - ge->edge = e; - ge->next = 0; - ge->neighbor = e->sites[1-i]; - ge->pos[flip] = e->pos[i]; - ge->pos[1-flip] = e->pos[1-i]; - ge->angle = jcv_calc_sort_metric(e->sites[i], ge); + return he; + } - jcv_sortedges_insert( e->sites[i], ge ); - } -} + static int check_circle_event (const halfedge* he1, const halfedge* he2, point* vertex) + { + edge* e1 = he1->edge_; + edge* e2 = he2->edge_; + if (e1 == 0 || e2 == 0 || e1->sites[1] == e2->sites[1]) { return 0; } + return halfedge_intersect (he1, he2, vertex); + } -static void jcv_endpos(jcv_context_internal* internal, jcv_edge* e, const jcv_point* p, int direction) -{ - e->pos[direction] = *p; + static void site_event (context_internal* internal, site* _site) + { + halfedge* left = get_edge_above_x(internal, &_site->p); + halfedge* right = left->right; + site* bottom = halfedge_rightsite(left); + if (!bottom) { bottom = internal->bottomsite; } - if( !jcv_is_valid(&e->pos[1 - direction]) ) - return; + edge* _edge = edge_new (internal, bottom, _site); + _edge->next = internal->edges; + internal->edges = _edge; - jcv_finishline(internal, e); -} + halfedge* edge1 = halfedge_new (internal, _edge, DIRECTION_LEFT); + halfedge* edge2 = halfedge_new (internal, _edge, DIRECTION_RIGHT); -static inline void jcv_create_corner_edge(jcv_context_internal* internal, const jcv_site* site, jcv_graphedge* current, jcv_graphedge* gap) -{ - gap->neighbor = 0; - gap->pos[0] = current->pos[1]; + halfedge_link (left, edge1); + halfedge_link (edge1, edge2); - if( current->pos[1][0] < internal->rect.max[0] && current->pos[1][1] == internal->rect.min[1] ) - { - gap->pos[1][0] = internal->rect.max[0]; - gap->pos[1][1] = internal->rect.min[1]; - } - else if( current->pos[1][0] > internal->rect.min[0] && current->pos[1][1] == internal->rect.max[1] ) - { - gap->pos[1][0] = internal->rect.min[0]; - gap->pos[1][1] = internal->rect.max[1]; - } - else if( current->pos[1][1] > internal->rect.min[1] && current->pos[1][0] == internal->rect.min[0] ) - { - gap->pos[1][0] = internal->rect.min[0]; - gap->pos[1][1] = internal->rect.min[1]; - } - else if( current->pos[1][1] < internal->rect.max[1] && current->pos[1][0] == internal->rect.max[0] ) - { - gap->pos[1][0] = internal->rect.max[0]; - gap->pos[1][1] = internal->rect.max[1]; - } - - gap->angle = jcv_calc_sort_metric(site, gap); -} + internal->last_inserted = right; -static jcv_edge* jcv_create_gap_edge(jcv_context_internal* internal, jcv_site* site, jcv_graphedge* ge) -{ - jcv_edge* edge = jcv_alloc_edge(internal); - edge->pos[0] = ge->pos[0]; - edge->pos[1] = ge->pos[1]; - edge->sites[0] = site; - edge->sites[1] = 0; - edge->a = edge->b = edge->c = 0; - edge->next = internal->edges; - internal->edges = edge; - return edge; -} - -void jcv_boxshape_fillgaps(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_graphedge* current = site->edges; - if( !current ) - { - // No edges, then it should be a single cell - assert( allocator->numsites == 1 ); - - jcv_graphedge* gap = jcv_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->angle = jcv_calc_sort_metric(site, gap); - gap->next = 0; - gap->edge = jcv_create_gap_edge(allocator, site, gap); - - current = gap; - site->edges = gap; - } + point p; + if (check_circle_event (left, edge1, &p)) { + pq_remove (internal->eventqueue, left); + left->vertex = p; + left->y = p[1] + point_dist (&_site->p, &p); + pq_push(internal->eventqueue, left); + } + if (check_circle_event (edge2, right, &p)) { + edge2->vertex = p; + edge2->y = p[1] + point_dist (&_site->p, &p); + pq_push (internal->eventqueue, edge2); + } + } - jcv_graphedge* next = current->next; - if( !next ) - { - jcv_graphedge* gap = jcv_alloc_graphedge(allocator); - jcv_create_corner_edge(allocator, site, current, gap); - gap->edge = jcv_create_gap_edge(allocator, site, gap); - - gap->next = current->next; - current->next = gap; - current = gap; - next = site->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()); + } - while( current && next ) - { - int current_edge_flags = jcv_get_edge_flags(¤t->pos[1], &clipper->min, &clipper->max); - if( current_edge_flags && !jcv_point_eq(¤t->pos[1], &next->pos[0])) + static T calc_sort_metric (const site* _site, const graphedge* _edge) { - // Cases: - // Current and Next on the same border - // Current on one border, and Next on another border - // Current on the corner, Next on the border - // Current on the corner, Next on another border (another corner in between) + // We take the average of the two points, since we can better distinguish between very small edges + T x = (_edge->pos[0][0] + _edge->pos[1][0]) * T{0.5}; + T y = (_edge->pos[0][1] + _edge->pos[1][1]) * T{0.5}; + T diffy = y - _site->p[1]; + T angle = std::atan2 (diffy, x - _site->p[0]); + if (diffy < 0) { angle = angle + sm::mathconst::two_pi; } + return angle; + } - int next_edge_flags = jcv_get_edge_flags(&next->pos[0], &clipper->min, &clipper->max); - if (current_edge_flags & next_edge_flags) - { - // Current and Next on the same border - jcv_graphedge* gap = jcv_alloc_graphedge(allocator); - gap->neighbor = 0; - gap->pos[0] = current->pos[1]; - gap->pos[1] = next->pos[0]; - gap->angle = jcv_calc_sort_metric(site, gap); - gap->edge = jcv_create_gap_edge(allocator, site, gap); + static int graphedge_eq (graphedge* a, graphedge* b) + { + return equal (a->angle, b->angle) && equal (&a->pos[0], &b->pos[0]) && equal (&a->pos[1], &b->pos[1]); + } - gap->next = current->next; - current->next = gap; + static void sortedges_insert (site* _site, graphedge* _edge) + { + // Special case for the head end + graphedge* prev = 0; + if (_site->edges == 0 || _site->edges->angle >= _edge->angle) { + _edge->next = _site->edges; + _site->edges = _edge; + } else { + // Locate the node before the point of insertion + graphedge* current = _site->edges; + while (current->next != 0 && current->next->angle < _edge->angle) { current = current->next; } + prev = current; + _edge->next = current->next; + current->next = _edge; } - else { - // Current and Next on different borders - int corner_flag = jcv_edge_flags_to_corner(current_edge_flags); - if (corner_flag) - { - // we are already at one corner, so we need to find the next one - corner_flag = jcv_corner_rotate_90(corner_flag); - } - else - { - // we are on the middle of a border - // we need to find the adjacent corner, following the borders CCW - if (current_edge_flags == JCV_EDGE_TOP) { corner_flag = JCV_CORNER_TOP_LEFT; } - else if (current_edge_flags == JCV_EDGE_LEFT) { corner_flag = JCV_CORNER_BOTTOM_LEFT; } - else if (current_edge_flags == JCV_EDGE_BOTTOM) { corner_flag = JCV_CORNER_BOTTOM_RIGHT; } - else if (current_edge_flags == JCV_EDGE_RIGHT) { corner_flag = JCV_CORNER_TOP_RIGHT; } - - } - jcv_point corner = jcv_corner_to_point(corner_flag, &clipper->min, &clipper->max); - jcv_graphedge* gap = jcv_alloc_graphedge(allocator); - gap->neighbor = 0; - gap->pos[0] = current->pos[1]; - gap->pos[1] = corner; - gap->angle = jcv_calc_sort_metric(site, gap); - gap->edge = jcv_create_gap_edge(allocator, site, gap); - - gap->next = current->next; - current->next = gap; + // check to avoid duplicates + if (prev && graphedge_eq(prev, _edge)) { + prev->next = _edge->next; + } else if (_edge->next && graphedge_eq(_edge, _edge->next)) { + _edge->next = _edge->next->next; } } - current = current->next; - if( current ) + static void finishline (context_internal* internal, edge* e) { - next = current->next; - if( !next ) - next = site->edges; + 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; + + for (int i = 0; i < 2; ++i) { + graphedge* ge = alloc_graphedge (internal); + ge->edge_ = e; + ge->next = 0; + ge->neighbor = e->sites[1-i]; + 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); + } } - } -} - -// Since the algorithm leaves gaps at the borders/corner, we want to fill them -static void jcv_fillgaps(jcv_diagram* diagram) -{ - jcv_context_internal* internal = diagram->internal; - if (!internal->clipper.fill_fn) - return; - for( int i = 0; i < internal->numsites; ++i ) - { - jcv_site* site = &internal->sites[i]; - internal->clipper.fill_fn(&internal->clipper, internal, site); - } -} + static void endpos (context_internal* internal, edge* e, const point* p, int direction) + { + e->pos[direction] = *p; + if (!is_valid(&e->pos[1 - direction])) { return; } + finishline (internal, e); + } + static void create_corner_edge (context_internal* internal, const site* site, graphedge* current, graphedge* gap) + { + gap->neighbor = 0; + gap->pos[0] = current->pos[1]; + + if (current->pos[1][0] < internal->rect_.max[0] && current->pos[1][1] == internal->rect_.min[1]) { + gap->pos[1][0] = internal->rect_.max[0]; + gap->pos[1][1] = internal->rect_.min[1]; + } else if (current->pos[1][0] > internal->rect_.min[0] && current->pos[1][1] == internal->rect_.max[1]) { + gap->pos[1][0] = internal->rect_.min[0]; + gap->pos[1][1] = internal->rect_.max[1]; + } else if (current->pos[1][1] > internal->rect_.min[1] && current->pos[1][0] == internal->rect_.min[0]) { + gap->pos[1][0] = internal->rect_.min[0]; + gap->pos[1][1] = internal->rect_.min[1]; + } else if (current->pos[1][1] < internal->rect_.max[1] && current->pos[1][0] == internal->rect_.max[0]) { + gap->pos[1][0] = internal->rect_.max[0]; + gap->pos[1][1] = internal->rect_.max[1]; + } -static void jcv_circle_event(jcv_context_internal* internal) -{ - jcv_halfedge* left = (jcv_halfedge*)jcv_pq_pop(internal->eventqueue); + gap->angle = calc_sort_metric(site, gap); + } - jcv_halfedge* leftleft = left->left; - jcv_halfedge* right = left->right; - jcv_halfedge* rightright= right->right; - jcv_site* bottom = jcv_halfedge_leftsite(left); - jcv_site* top = jcv_halfedge_rightsite(right); + static edge* create_gap_edge (context_internal* internal, site* site, graphedge* ge) + { + edge* edge = alloc_edge (internal); + edge->pos[0] = ge->pos[0]; + edge->pos[1] = ge->pos[1]; + edge->sites[0] = site; + edge->sites[1] = 0; + edge->a = edge->b = edge->c = 0; + edge->next = internal->edges; + internal->edges = edge; + return edge; + } - jcv_point vertex = left->vertex; - jcv_endpos(internal, left->edge, &vertex, left->direction); - jcv_endpos(internal, right->edge, &vertex, right->direction); + static void boxshape_fillgaps (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) { + // No edges, then it should be a single cell + assert (allocator->numsites == 1); - internal->last_inserted = rightright; + 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->angle = calc_sort_metric(site, gap); + gap->next = 0; + gap->edge_ = create_gap_edge (allocator, site, gap); + + current = gap; + site->edges = gap; + } - jcv_pq_remove(internal->eventqueue, right); - jcv_halfedge_unlink(left); - jcv_halfedge_unlink(right); - jcv_halfedge_delete(internal, left); - jcv_halfedge_delete(internal, right); + graphedge* next = current->next; + if (!next) { + graphedge* gap = alloc_graphedge (allocator); + create_corner_edge(allocator, site, current, gap); + gap->edge_ = create_gap_edge (allocator, site, gap); - int direction = JCV_DIRECTION_LEFT; - if( bottom->p[1] > top->p[1] ) - { - jcv_site* temp = bottom; - bottom = top; - top = temp; - direction = JCV_DIRECTION_RIGHT; - } + gap->next = current->next; + current->next = gap; + current = gap; + next = site->edges; + } - jcv_edge* edge = jcv_edge_new(internal, bottom, top); - edge->next = internal->edges; - internal->edges = edge; + // Need a sanity check, as this while loop can go infinite if earlier code fails to + // generate the Voronoi diagram correctly. We usually go 5 or 6 times around the while, + // so if we get to 1024 we're probably in an interminable loop. + constexpr int loopcount_thresh = 1024; + int loopcount = 0; + + while (current && next && loopcount < loopcount_thresh) { + + 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])) { + // Cases: + // Current and Next on the same border + // Current on one border, and Next on another border + // Current on the corner, Next on the border + // Current on the corner, Next on another border (another corner in between) + + int next_edge_flags = get_edge_flags(&next->pos[0], &clipper->min, &clipper->max); + if (current_edge_flags & next_edge_flags) { + // Current and Next on the same border + graphedge* gap = alloc_graphedge (allocator); + gap->neighbor = 0; + gap->pos[0] = current->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; + } else { + // Current and Next on different borders + int corner_flag = edge_flags_to_corner (current_edge_flags); + if (corner_flag) { + // we are already at one corner, so we need to find the next one + corner_flag = corner_rotate_90 (corner_flag); + } else { + // we are on the middle of a border + // we need to find the adjacent corner, following the borders CCW + if (current_edge_flags == EDGE_TOP) { corner_flag = CORNER_TOP_LEFT; } + else if (current_edge_flags == EDGE_LEFT) { corner_flag = CORNER_BOTTOM_LEFT; } + else if (current_edge_flags == EDGE_BOTTOM) { corner_flag = CORNER_BOTTOM_RIGHT; } + else if (current_edge_flags == EDGE_RIGHT) { corner_flag = CORNER_TOP_RIGHT; } + + } + point corner = corner_to_point (corner_flag, &clipper->min, &clipper->max); + + graphedge* gap = alloc_graphedge (allocator); + gap->neighbor = 0; + gap->pos[0] = current->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; + } + } - jcv_halfedge* he = jcv_halfedge_new(internal, edge, direction); - jcv_halfedge_link(leftleft, he); - jcv_endpos(internal, edge, &vertex, JCV_DIRECTION_RIGHT - direction); + current = current->next; + if (current) { + next = current->next; + 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"; + } + } + } - jcv_point p; - if( jcv_check_circle_event( leftleft, he, &p ) ) - { - jcv_pq_remove(internal->eventqueue, leftleft); - leftleft->vertex = p; - leftleft->y = p[1] + jcv_point_dist(&bottom->p, &p); - jcv_pq_push(internal->eventqueue, leftleft); - } - if( jcv_check_circle_event( he, rightright, &p ) ) - { - he->vertex = p; - he->y = p[1] + jcv_point_dist(&bottom->p, &p); - jcv_pq_push(internal->eventqueue, he); - } -} -void jcv_diagram_generate( int num_points, const jcv_point* points, const jcv_rect* rect, const jcv_clipper* clipper, jcv_diagram* d ) -{ - jcv_diagram_generate_useralloc(num_points, points, rect, clipper, 0, jcv_alloc_fn, jcv_free_fn, d); -} + // Since the algorithm leaves gaps at the borders/corner, we want to fill them + 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); + } + } -typedef union jcv_cast_align_struct_ -{ - char* charp; - void** voidpp; -} jcv_cast_align_struct; -static inline void jcv_rect_union(jcv_rect* rect, const jcv_point* p) -{ - rect->min[0] = jcv_min(rect->min[0], p->x()); - rect->min[1] = jcv_min(rect->min[1], p->y()); - rect->max[0] = jcv_max(rect->max[0], p->x()); - rect->max[1] = jcv_max(rect->max[1], p->y()); -} + static void circle_event (context_internal* internal) + { + halfedge* left = (halfedge*)pq_pop (internal->eventqueue); + halfedge* leftleft = left->left; + halfedge* right = left->right; + halfedge* rightright= right->right; + site* bottom = halfedge_leftsite(left); + site* top = halfedge_rightsite(right); + + point vertex = left->vertex; + endpos (internal, left->edge_, &vertex, left->direction); + endpos (internal, right->edge_, &vertex, right->direction); + + internal->last_inserted = rightright; + + pq_remove (internal->eventqueue, right); + halfedge_unlink (left); + halfedge_unlink (right); + halfedge_delete (internal, left); + halfedge_delete (internal, right); + + int direction = DIRECTION_LEFT; + if (bottom->p[1] > top->p[1]) { + site* temp = bottom; + bottom = top; + top = temp; + direction = DIRECTION_RIGHT; + } -static inline void jcv_rect_round(jcv_rect* rect) -{ - rect->min[0] = jcv_floor(rect->min[0]); - rect->min[1] = jcv_floor(rect->min[1]); - rect->max[0] = jcv_ceil(rect->max[0]); - rect->max[1] = jcv_ceil(rect->max[1]); -} + edge* edge = edge_new (internal, bottom, top); + edge->next = internal->edges; + internal->edges = edge; -static inline void jcv_rect_inflate(jcv_rect* rect, jcv_real amount) -{ - rect->min[0] -= amount; - rect->min[1] -= amount; - rect->max[0] += amount; - rect->max[1] += amount; -} + halfedge* he = halfedge_new (internal, edge, direction); + halfedge_link (leftleft, he); + endpos (internal, edge, &vertex, DIRECTION_RIGHT - direction); -static int jcv_prune_duplicates(jcv_context_internal* internal, jcv_rect* rect) -{ - int num_sites = internal->numsites; - jcv_site* sites = internal->sites; + point p; + if (check_circle_event (leftleft, he, &p)) { + pq_remove (internal->eventqueue, leftleft); + leftleft->vertex = p; + leftleft->y = p[1] + point_dist (&bottom->p, &p); + pq_push (internal->eventqueue, leftleft); + } + if (check_circle_event (he, rightright, &p)) { + he->vertex = p; + he->y = p[1] + point_dist (&bottom->p, &p); + pq_push (internal->eventqueue, he); + } + } - jcv_rect r; - r.min[0] = r.min[1] = JCV_FLT_MAX; - r.max[0] = r.max[1] = -JCV_FLT_MAX; + typedef union cast_align_struct_ + { + char* charp; + void** voidpp; + } cast_align_struct; - int offset = 0; - // Prune duplicates first - for (int i = 0; i < num_sites; i++) - { - const jcv_site* s = &sites[i]; - // Remove duplicates, to avoid anomalies - if( i > 0 && jcv_point_eq(&s->p, &sites[i - 1].p) ) + static void rect_union (rect* rect, const point* p) { - offset++; - continue; + rect->min[0] = std::min (rect->min[0], p->x()); + rect->min[1] = std::min (rect->min[1], p->y()); + rect->max[0] = std::max (rect->max[0], p->x()); + rect->max[1] = std::max (rect->max[1], p->y()); } - sites[i - offset] = sites[i]; - - jcv_rect_union(&r, &s->p); - } - internal->numsites -= offset; - if (rect) { - *rect = r; - } - return offset; -} - -static int jcv_prune_not_in_shape(jcv_context_internal* internal, jcv_rect* rect) -{ - int num_sites = internal->numsites; - jcv_site* sites = internal->sites; - - jcv_rect r; - r.min[0] = r.min[1] = JCV_FLT_MAX; - r.max[0] = r.max[1] = -JCV_FLT_MAX; - - int offset = 0; - for (int i = 0; i < num_sites; i++) - { - const jcv_site* s = &sites[i]; - - if (!internal->clipper.test_fn(&internal->clipper, s->p)) + static void rect_round (rect* rect) { - offset++; - continue; + rect->min[0] = std::floor (rect->min[0]); + rect->min[1] = std::floor (rect->min[1]); + rect->max[0] = std::ceil (rect->max[0]); + rect->max[1] = std::ceil (rect->max[1]); } - sites[i - offset] = sites[i]; - - jcv_rect_union(&r, &s->p); - } - internal->numsites -= offset; - if (rect) { - *rect = r; - } - return offset; -} + static void rect_inflate (rect* rect, T amount) + { + rect->min[0] -= amount; + rect->min[1] -= amount; + rect->max[0] += amount; + rect->max[1] += amount; + } -static jcv_context_internal* jcv_alloc_internal(int num_points, void* userallocctx, FJCVAllocFn allocfn, FJCVFreeFn freefn) -{ - // Interesting limits from Euler's equation - // Slide 81: https://courses.cs.washington.edu/courses/csep521/01au/lectures/lecture10slides.pdf - // Page 3: https://sites.cs.ucsb.edu/~suri/cs235/Voronoi.pdf - size_t eventssize = (size_t)(num_points*2) * sizeof(void*); // beachline can have max 2*n-5 parabolas - size_t sitessize = (size_t)num_points * sizeof(jcv_site); - size_t memsize = sizeof(jcv_priorityqueue) + eventssize + sitessize + sizeof(jcv_context_internal) + 16u; // 16 bytes padding for alignment + static int prune_duplicates (context_internal* internal, rect* _rect) + { + int num_sites = internal->numsites; + site* sites = internal->sites; + + rect r; + r.min[0] = r.min[1] = std::numeric_limits::max(); + r.max[0] = r.max[1] = std::numeric_limits::lowest(); + + int offset = 0; + // Prune duplicates first + for (int i = 0; i < num_sites; i++) { + const site* s = &sites[i]; + // Remove duplicates, to avoid anomalies + if (i > 0 && equal (&s->p, &sites[i - 1].p)) { + offset++; + continue; + } - char* originalmem = (char*)allocfn(userallocctx, memsize); - memset(originalmem, 0, memsize); + sites[i - offset] = sites[i]; - // align memory - char* mem = (char*)jcv_align(originalmem, sizeof(void*)); + rect_union (&r, &s->p); + } + internal->numsites -= offset; + if (_rect) { *_rect = r; } + return offset; + } - jcv_context_internal* internal = (jcv_context_internal*)mem; - mem += sizeof(jcv_context_internal); - internal->mem = originalmem; - internal->memctx = userallocctx; - internal->alloc = allocfn; - internal->free = freefn; + static int prune_not_in_shape (context_internal* internal, rect* _rect) + { + int num_sites = internal->numsites; + site* sites = internal->sites; - mem = (char*)jcv_align(mem, sizeof(void*)); - internal->sites = (jcv_site*) mem; - mem += sitessize; + rect r; + r.min[0] = r.min[1] = std::numeric_limits::max(); + r.max[0] = r.max[1] = std::numeric_limits::lowest(); - mem = (char*)jcv_align(mem, sizeof(void*)); - internal->eventqueue = (jcv_priorityqueue*)mem; - mem += sizeof(jcv_priorityqueue); - assert( ((uintptr_t)mem & (sizeof(void*)-1)) == 0 ); + int offset = 0; + for (int i = 0; i < num_sites; i++) { + const site* s = &sites[i]; - jcv_cast_align_struct tmp; - tmp.charp = mem; - internal->eventmem = tmp.voidpp; + if (!internal->clipper_.test_fn (&internal->clipper_, s->p)) { + offset++; + continue; + } - assert((mem+eventssize) <= (originalmem+memsize)); + sites[i - offset] = sites[i]; - return internal; -} + rect_union (&r, &s->p); + } + internal->numsites -= offset; + if (_rect) { *_rect = r; } + return offset; + } -void jcv_diagram_generate_useralloc(int num_points, const jcv_point* points, const jcv_rect* rect, const jcv_clipper* clipper, void* userallocctx, FJCVAllocFn allocfn, FJCVFreeFn freefn, jcv_diagram* d) -{ - if( d->internal ) - jcv_diagram_free( d ); + static context_internal* alloc_internal (int num_points, void* userallocctx, FJCVAllocFn allocfn, FJCVFreeFn freefn) + { + // Interesting limits from Euler's equation + // Slide 81: https://courses.cs.washington.edu/courses/csep521/01au/lectures/lecture10slides.pdf + // Page 3: https://sites.cs.ucsb.edu/~suri/cs235/Voronoi.pdf + size_t eventssize = (size_t)(num_points*2) * sizeof(void*); // beachline can have max 2*n-5 parabolas + size_t sitessize = (size_t)num_points * sizeof(site); + size_t memsize = sizeof(priorityqueue) + eventssize + sitessize + sizeof(context_internal) + 16u; // 16 bytes padding for alignment + + char* originalmem = (char*)allocfn (userallocctx, memsize); + memset (originalmem, 0, memsize); + + // align memory + char* mem = (char*)align (originalmem, sizeof(void*)); + + context_internal* internal = (context_internal*)mem; + mem += sizeof(context_internal); + internal->mem = originalmem; + internal->memctx = userallocctx; + internal->alloc = allocfn; + internal->free = freefn; + + mem = (char*)align (mem, sizeof(void*)); + internal->sites = (site*) mem; + mem += sitessize; + + mem = (char*)align (mem, sizeof(void*)); + internal->eventqueue = (priorityqueue*)mem; + mem += sizeof(priorityqueue); + assert (((uintptr_t)mem & (sizeof(void*)-1)) == 0); + + cast_align_struct tmp; + tmp.charp = mem; + internal->eventmem = tmp.voidpp; + + assert ((mem+eventssize) <= (originalmem+memsize)); + + return internal; + } - jcv_context_internal* internal = jcv_alloc_internal(num_points, userallocctx, allocfn, freefn); + // 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, + void* userallocctx, FJCVAllocFn allocfn, FJCVFreeFn freefn, diagram* d) + { + if (d->internal) { diagram_free (d); } - internal->beachline_start = jcv_halfedge_new(internal, 0, 0); - internal->beachline_end = jcv_halfedge_new(internal, 0, 0); + context_internal* internal = alloc_internal (num_points, userallocctx, allocfn, freefn); - internal->beachline_start->left = 0; - internal->beachline_start->right = internal->beachline_end; - internal->beachline_end->left = internal->beachline_start; - internal->beachline_end->right = 0; + internal->beachline_start = halfedge_new (internal, 0, 0); + internal->beachline_end = halfedge_new (internal, 0, 0); - internal->last_inserted = 0; + internal->beachline_start->left = 0; + internal->beachline_start->right = internal->beachline_end; + internal->beachline_end->left = internal->beachline_start; + internal->beachline_end->right = 0; - int max_num_events = num_points*2; // beachline can have max 2*n-5 parabolas - jcv_pq_create(internal->eventqueue, max_num_events, (void**)internal->eventmem); + internal->last_inserted = 0; - internal->numsites = num_points; - jcv_site* sites = internal->sites; + int max_num_events = num_points * 2; // beachline can have max 2*n-5 parabolas + pq_create (internal->eventqueue, max_num_events, (void**)internal->eventmem); - for( int i = 0; i < num_points; ++i ) - { - sites[i].p = points[i]; - sites[i].edges = 0; - sites[i].index = i; - } + internal->numsites = num_points; + site* sites = internal->sites; - qsort(sites, (size_t)num_points, sizeof(jcv_site), jcv_point_cmp); + for (int i = 0; i < num_points; ++i) { + sites[i].p = points[i]; + sites[i].edges = 0; + sites[i].index = i; + } - jcv_clipper box_clipper; - if (clipper == 0) { - box_clipper.test_fn = jcv_boxshape_test; - box_clipper.clip_fn = jcv_boxshape_clip; - box_clipper.fill_fn = jcv_boxshape_fillgaps; - clipper = &box_clipper; - } - internal->clipper = *clipper; + qsort (sites, (size_t)num_points, sizeof(site), point_cmp); - jcv_rect tmp_rect; - tmp_rect.min[0] = tmp_rect.min[1] = JCV_FLT_MAX; - tmp_rect.max[0] = tmp_rect.max[1] = -JCV_FLT_MAX; - jcv_prune_duplicates(internal, &tmp_rect); + clipper box_clipper; + if (_clipper == 0) { + // 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; + _clipper = &box_clipper; + } + internal->clipper_ = *_clipper; + + rect tmp_rect; + tmp_rect.min[0] = tmp_rect.min[1] = std::numeric_limits::max(); + tmp_rect.max[0] = tmp_rect.max[1] = std::numeric_limits::lowest(); + prune_duplicates (internal, &tmp_rect); + + // Prune using the test second + if (internal->clipper_.test_fn) { + // e.g. used by the box clipper in the test_fn + internal->clipper_.min = _rect ? _rect->min : tmp_rect.min; + internal->clipper_.max = _rect ? _rect->max : tmp_rect.max; + + prune_not_in_shape (internal, &tmp_rect); + + // The pruning might have made the bounding box smaller + if (!_rect) { + // 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, 10); + + internal->clipper_.min = tmp_rect.min; + internal->clipper_.max = tmp_rect.max; + } + } - // Prune using the test second - if (internal->clipper.test_fn) - { - // e.g. used by the box clipper in the test_fn - internal->clipper.min = rect ? rect->min : tmp_rect.min; - internal->clipper.max = rect ? rect->max : tmp_rect.max; + internal->rect_ = _rect ? *_rect : tmp_rect; - jcv_prune_not_in_shape(internal, &tmp_rect); + d->min = internal->rect_.min; + d->max = internal->rect_.max; + d->numsites = internal->numsites; + d->internal = internal; - // The pruning might have made the bounding box smaller - if (!rect) { - // 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 - jcv_rect_round(&tmp_rect); - jcv_rect_inflate(&tmp_rect, 10); + internal->bottomsite = nextsite (internal); - internal->clipper.min = tmp_rect.min; - internal->clipper.max = tmp_rect.max; - } - } + priorityqueue* pq = internal->eventqueue; + site* site = nextsite (internal); - internal->rect = rect ? *rect : tmp_rect; + int finished = 0; + while (!finished) { - d->min = internal->rect.min; - d->max = internal->rect.max; - d->numsites = internal->numsites; - d->internal = internal; + point lowest_pq_point; + if (!pq_empty (pq)) { + halfedge* he = (halfedge*)pq_top (pq); + lowest_pq_point[0] = he->vertex[0]; + lowest_pq_point[1] = he->y; + } - internal->bottomsite = jcv_nextsite(internal); + if (site != 0 && (pq_empty(pq) || lessthan (&site->p, &lowest_pq_point))) { + site_event (internal, site); + site = nextsite (internal); + } else if (!pq_empty (pq)) { + circle_event (internal); + } else { + finished = 1; + } + } - jcv_priorityqueue* pq = internal->eventqueue; - jcv_site* site = jcv_nextsite(internal); + for (halfedge* he = internal->beachline_start->right; he != internal->beachline_end; he = he->right) { + finishline (internal, he->edge_); + } - int finished = 0; - while( !finished ) - { - jcv_point lowest_pq_point; - if( !jcv_pq_empty(pq) ) - { - jcv_halfedge* he = (jcv_halfedge*)jcv_pq_top(pq); - lowest_pq_point[0] = he->vertex[0]; - lowest_pq_point[1] = he->y; + fillgaps (d); } - if( site != 0 && (jcv_pq_empty(pq) || jcv_point_less(&site->p, &lowest_pq_point) ) ) + /** + * Uses malloc + * If a clipper is not supplied, a default box clipper will be used + * 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) { - jcv_site_event(internal, site); - site = jcv_nextsite(internal); + diagram_generate_useralloc(num_points, points, rect, clipper, 0, alloc_fn, free_fn, d); } - else if( !jcv_pq_empty(pq) ) - { - jcv_circle_event(internal); - } - else + + // User API + void diagram_generate (const std::vector>& centres) { - finished = 1; + int ncoords = static_cast(centres.size()); + sm::range rx, ry; + rx.search_init(); + ry.search_init(); + for (int i = 0; i < ncoords ; ++i) { + rx.update (centres[i][0]); + ry.update (centres[i][1]); + } + std::memset (&this->diagram, 0, sizeof(jcv::diagram)); + this->domain = { + jcv::point{rx.min - this->border_width, ry.min - this->border_width, 0.0f}, + jcv::point{rx.max + this->border_width, ry.max + this->border_width, 0.0f} + }; + jcv::manager::diagram_generate (ncoords, centres.data(), &this->domain, 0, &this->diagram); } - } - for( jcv_halfedge* he = internal->beachline_start->right; he != internal->beachline_end; he = he->right ) - { - jcv_finishline(internal, he->edge); - } + int diagram_numsites() const { return this->diagram.numsites; } + + // User-configurable border width + T border_width = std::numeric_limits::epsilon(); - jcv_fillgaps(d); -} + private: + // Our diagram + jcv::diagram diagram; + // A domain for the diagram. + jcv::rect domain = {}; + }; // end struct jcv -#endif // JC_VORONOI_IMPLEMENTATION +} // namespace + +/* + * The Original, pre-mathplot about message follows: + */ /* diff --git a/mplot/jcvoronoi/jc_voronoi_clip.h b/mplot/jcvoronoi/jc_voronoi_clip.h index 7f0a6087..1a2a9fa4 100644 --- a/mplot/jcvoronoi/jc_voronoi_clip.h +++ b/mplot/jcvoronoi/jc_voronoi_clip.h @@ -1,3 +1,6 @@ +// 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 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 49ffff57..2a0db1bf 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -87,3 +87,6 @@ add_test(testmaketicks testmaketicks) add_executable(testmakeformatticks testmakeformatticks.cpp) add_test(testmakeformatticks testmakeformatticks) + +add_executable(unordered_map_vec_set unordered_map_vec_set.cpp) +add_test(unordered_map_vec_set unordered_map_vec_set) diff --git a/tests/unordered_map_vec_set.cpp b/tests/unordered_map_vec_set.cpp new file mode 100644 index 00000000..3a43080e --- /dev/null +++ b/tests/unordered_map_vec_set.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include + +int main() +{ + std::unordered_map, std::set, sm::vec::hash> equiv_v; + + std::set st = { 1, 2, 3 }; + std::set st2 = { 4, 5, 6 }; + + sm::vec vc = { 0.1f, 0.2f, 0.3f }; + + equiv_v[vc] = st; + sm::vec vc2 = vc; + vc2[0] += 0.2f; + equiv_v[vc2] = st2; + + std::cout << "(orig) unordered_map[" << vc << "]:\n"; + for (auto el : equiv_v[vc]) { + std::cout << "set element: " << el << std::endl; + } + + sm::vec vc1 = vc; + vc1[0] += 0.2f; + std::cout << "+= 0.2 unordered_map[" << vc1 << "]:\n"; + for (auto el : equiv_v[vc1]) { + std::cout << "set element: " << el << std::endl; + } + + vc1[0] -= 0.2f; + std::cout << "+= 0.2 -= unordered_map[" << vc1 << "]:\n"; + for (auto el : equiv_v[vc1]) { + std::cout << "set element: " << el << std::endl; + } + if (vc1 != vc) { std::cout << "They're different anyway!\n"; } + + std::cout << "vc2 unordered_map[" << vc2 << "]:\n"; + for (auto el : equiv_v[vc2]) { + std::cout << "set element: " << el << std::endl; + } + + return 0; +}