From 3982acdddeea77db0933afb0d050bf49c62cae9a Mon Sep 17 00:00:00 2001 From: Mark Gillespie Date: Thu, 15 Feb 2024 16:37:08 -0500 Subject: [PATCH 1/5] Fix edge picks --- deps/glfw | 2 +- include/polyscope/pick.ipp | 6 +++--- include/polyscope/surface_mesh.h | 5 ++--- include/polyscope/surface_mesh.ipp | 6 ++++++ src/surface_mesh.cpp | 11 ++++++++--- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/deps/glfw b/deps/glfw index e2c926454..7d5a16ce7 160000 --- a/deps/glfw +++ b/deps/glfw @@ -1 +1 @@ -Subproject commit e2c92645460f680fd272fd2eed591efb2be7dc31 +Subproject commit 7d5a16ce714f0b5f4efa3262de22e4d948851525 diff --git a/include/polyscope/pick.ipp b/include/polyscope/pick.ipp index 9d4a34767..004a7a459 100644 --- a/include/polyscope/pick.ipp +++ b/include/polyscope/pick.ipp @@ -24,11 +24,11 @@ inline glm::vec3 indToVec(size_t globalInd) { globalInd = globalInd >> bitsForPickPacking; uint64_t high = globalInd; - return glm::vec3{static_cast(low) / factorF, static_cast(med) / factorF, - static_cast(high) / factorF}; + return 2500.f * glm::vec3{static_cast(low) / factorF, static_cast(med) / factorF, + static_cast(high) / factorF}; } inline uint64_t vecToInd(glm::vec3 vec) { - + vec /= 2500.f; uint64_t factor = 1 << bitsForPickPacking; double factorF = factor; diff --git a/include/polyscope/surface_mesh.h b/include/polyscope/surface_mesh.h index e3bb9b820..d465b2ac3 100644 --- a/include/polyscope/surface_mesh.h +++ b/include/polyscope/surface_mesh.h @@ -237,8 +237,8 @@ class SurfaceMesh : public QuantityStructure { size_t nEdges(); // NOTE causes population of nEdgesCount size_t nCornersCount = 0; // = nHalfedges = sum face degree - size_t nCorners() const { return nCornersCount; } - size_t nHalfedges() const { return nCornersCount; } + size_t nCorners() const { return cornerDataSize == INVALID_IND ? nCornersCount : cornerDataSize; } + size_t nHalfedges() const { return halfedgeDataSize == INVALID_IND ? nCornersCount : halfedgeDataSize; } // = Mesh helpers void nestedFacesToFlat(const std::vector>& nestedInds); @@ -357,7 +357,6 @@ class SurfaceMesh : public QuantityStructure { std::vector halfedgeEdgeCorrespondence; // ugly hack used to save a pick buffer attr, filled out lazily w/ edge indices - // Visualization settings PersistentValue surfaceColor; PersistentValue edgeColor; diff --git a/include/polyscope/surface_mesh.ipp b/include/polyscope/surface_mesh.ipp index 7d35e279d..d426eba95 100644 --- a/include/polyscope/surface_mesh.ipp +++ b/include/polyscope/surface_mesh.ipp @@ -136,6 +136,8 @@ void SurfaceMesh::setEdgePermutation(const T& perm, size_t expectedSize) { // now that we have edge indexing, enable edge-related stuff markEdgesAsUsed(); + + triangleAllEdgeInds.recomputeIfPopulated(); } template @@ -163,6 +165,8 @@ void SurfaceMesh::setHalfedgePermutation(const T& perm, size_t expectedSize) { } markHalfedgesAsUsed(); + triangleAllEdgeInds.recomputeIfPopulated(); + triangleAllHalfedgeInds.recomputeIfPopulated(); } template @@ -190,6 +194,8 @@ void SurfaceMesh::setCornerPermutation(const T& perm, size_t expectedSize) { } markCornersAsUsed(); + triangleAllEdgeInds.recomputeIfPopulated(); + triangleAllCornerInds.recomputeIfPopulated(); } diff --git a/src/surface_mesh.cpp b/src/surface_mesh.cpp index fe198f81f..39678c915 100644 --- a/src/surface_mesh.cpp +++ b/src/surface_mesh.cpp @@ -199,6 +199,8 @@ void SurfaceMesh::computeTriangleAllEdgeInds() { triangleAllEdgeInds.data.resize(3 * 3 * nFacesTriangulation()); halfedgeEdgeCorrespondence.resize(nHalfedges()); + bool haveCustomHalfedgeIndex = !halfedgePerm.empty(); + // used to loop over edges std::unordered_map, size_t, polyscope::hash_combine::hash>> seenEdgeInds; @@ -242,7 +244,10 @@ void SurfaceMesh::computeTriangleAllEdgeInds() { thisEdgeInd = seenEdgeInds[key]; } - halfedgeEdgeCorrespondence[start + j] = thisEdgeInd; + size_t he = start + j; + if (haveCustomHalfedgeIndex) he = halfedgePerm[he]; + + halfedgeEdgeCorrespondence[he] = thisEdgeInd; thisTriInds[j] = thisEdgeInd; } @@ -1243,7 +1248,7 @@ void SurfaceMesh::buildEdgeInfoGui(const SurfaceMeshPickResult& result) { size_t eInd = result.index; size_t displayInd = eInd; if (edgePerm.size() > 0) { - displayInd = edgePerm[eInd]; + // displayInd = edgePerm[eInd]; } ImGui::TextUnformatted(("Edge #" + std::to_string(displayInd)).c_str()); @@ -1267,7 +1272,7 @@ void SurfaceMesh::buildHalfedgeInfoGui(const SurfaceMeshPickResult& result) { size_t heInd = result.index; size_t displayInd = heInd; if (halfedgePerm.size() > 0) { - displayInd = halfedgePerm[heInd]; + // displayInd = halfedgePerm[heInd]; } ImGui::TextUnformatted(("Halfedge #" + std::to_string(displayInd)).c_str()); From 6351bbd8df5280b3bb755ee33df3e81ab7294446 Mon Sep 17 00:00:00 2001 From: Mark Gillespie Date: Fri, 16 Feb 2024 11:30:06 -0500 Subject: [PATCH 2/5] update glfw --- deps/glfw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/glfw b/deps/glfw index 7d5a16ce7..e2c926454 160000 --- a/deps/glfw +++ b/deps/glfw @@ -1 +1 @@ -Subproject commit 7d5a16ce714f0b5f4efa3262de22e4d948851525 +Subproject commit e2c92645460f680fd272fd2eed591efb2be7dc31 From 6ee2c394ee5e9b83f9d622d220d11a6a0b3d535c Mon Sep 17 00:00:00 2001 From: Mark Gillespie Date: Fri, 16 Feb 2024 11:31:15 -0500 Subject: [PATCH 3/5] Delete commented code --- src/surface_mesh.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/surface_mesh.cpp b/src/surface_mesh.cpp index 39678c915..136b9fa39 100644 --- a/src/surface_mesh.cpp +++ b/src/surface_mesh.cpp @@ -1247,9 +1247,6 @@ void SurfaceMesh::buildFaceInfoGui(const SurfaceMeshPickResult& result) { void SurfaceMesh::buildEdgeInfoGui(const SurfaceMeshPickResult& result) { size_t eInd = result.index; size_t displayInd = eInd; - if (edgePerm.size() > 0) { - // displayInd = edgePerm[eInd]; - } ImGui::TextUnformatted(("Edge #" + std::to_string(displayInd)).c_str()); ImGui::Spacing(); @@ -1271,9 +1268,6 @@ void SurfaceMesh::buildEdgeInfoGui(const SurfaceMeshPickResult& result) { void SurfaceMesh::buildHalfedgeInfoGui(const SurfaceMeshPickResult& result) { size_t heInd = result.index; size_t displayInd = heInd; - if (halfedgePerm.size() > 0) { - // displayInd = halfedgePerm[heInd]; - } ImGui::TextUnformatted(("Halfedge #" + std::to_string(displayInd)).c_str()); ImGui::Spacing(); From f27f6fb94334ee745bb550d6568960f53a5a439b Mon Sep 17 00:00:00 2001 From: Mark Gillespie Date: Fri, 16 Feb 2024 11:33:04 -0500 Subject: [PATCH 4/5] Remove more debug code --- include/polyscope/pick.ipp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/polyscope/pick.ipp b/include/polyscope/pick.ipp index 004a7a459..9d4a34767 100644 --- a/include/polyscope/pick.ipp +++ b/include/polyscope/pick.ipp @@ -24,11 +24,11 @@ inline glm::vec3 indToVec(size_t globalInd) { globalInd = globalInd >> bitsForPickPacking; uint64_t high = globalInd; - return 2500.f * glm::vec3{static_cast(low) / factorF, static_cast(med) / factorF, - static_cast(high) / factorF}; + return glm::vec3{static_cast(low) / factorF, static_cast(med) / factorF, + static_cast(high) / factorF}; } inline uint64_t vecToInd(glm::vec3 vec) { - vec /= 2500.f; + uint64_t factor = 1 << bitsForPickPacking; double factorF = factor; From e68bc721bfff6b2f8f8ae1c166dfabe8cae0e25d Mon Sep 17 00:00:00 2001 From: Mark Gillespie Date: Fri, 5 Sep 2025 15:12:42 +0200 Subject: [PATCH 5/5] Add pick callbacks --- include/polyscope/context.h | 1 + include/polyscope/pick.h | 4 ++ include/polyscope/polyscope.h | 3 ++ include/polyscope/structure.h | 4 ++ include/polyscope/surface_mesh.h | 15 ++++++ src/curve_network.cpp | 2 +- src/pick.cpp | 8 ++++ src/point_cloud.cpp | 2 +- src/polyscope.cpp | 3 ++ src/state.cpp | 1 + src/structure.cpp | 10 +++- src/surface_mesh.cpp | 79 +++++++++++++++++++++++++++++++- src/volume_grid.cpp | 4 +- src/volume_mesh.cpp | 2 +- 14 files changed, 130 insertions(+), 8 deletions(-) diff --git a/include/polyscope/context.h b/include/polyscope/context.h index 085be8311..7ff902b7b 100644 --- a/include/polyscope/context.h +++ b/include/polyscope/context.h @@ -108,6 +108,7 @@ struct Context { uint64_t nextPickBufferInd = 1; std::unordered_map> structureRanges; std::unordered_map> quantityRanges; + std::list> pickCallbacks; // ====================================================== // === Internal globals from internal.h diff --git a/include/polyscope/pick.h b/include/polyscope/pick.h index 72d4ff995..fd9a61218 100644 --- a/include/polyscope/pick.h +++ b/include/polyscope/pick.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include #include @@ -56,6 +57,9 @@ bool haveSelection(); void resetSelectionIfStructure(Structure* s); // If something from this structure is selected, clear the selection // (useful if a structure is being deleted) +std::list>::iterator registerPickCallback(const std::function& f); +void removePickCallback(std::list>::iterator callback); + namespace pick { // Old, deprecated picking API. Use the above functions instead. diff --git a/include/polyscope/polyscope.h b/include/polyscope/polyscope.h index 3d3ae7fee..5bf6e29a1 100644 --- a/include/polyscope/polyscope.h +++ b/include/polyscope/polyscope.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include #include @@ -108,6 +109,8 @@ extern bool& doDefaultMouseInteraction; // a callback function used to render a "user" gui extern std::function& userCallback; +extern std::list>& pickCallbacks; + // representative center for all registered structures glm::vec3 center(); diff --git a/include/polyscope/structure.h b/include/polyscope/structure.h index a153e96f3..9bbb806fa 100644 --- a/include/polyscope/structure.h +++ b/include/polyscope/structure.h @@ -116,6 +116,9 @@ class Structure : public render::ManagedBufferRegistry, public virtual WeakRefer Structure* setTransformGizmoEnabled(bool newVal); bool getTransformGizmoEnabled(); + Structure* setPickable(bool pickable); + bool getPickable(); + protected: // = State PersistentValue enabled; @@ -130,6 +133,7 @@ class Structure : public render::ManagedBufferRegistry, public virtual WeakRefer PersistentValue cullWholeElements; PersistentValue> ignoredSlicePlaneNames; + PersistentValue pickable; // Manage the bounding box & length scale // (this is defined _before_ the object transform is applied. To get the scale/bounding box after transforms, use the diff --git a/include/polyscope/surface_mesh.h b/include/polyscope/surface_mesh.h index d465b2ac3..89a886714 100644 --- a/include/polyscope/surface_mesh.h +++ b/include/polyscope/surface_mesh.h @@ -82,6 +82,12 @@ class SurfaceMesh : public QuantityStructure { virtual void buildCustomOptionsUI() override; virtual void buildPickUI(const PickResult&) override; + void registerVertexPickCallback(const std::function& f); + void registerEdgePickCallback(const std::function& f); + void registerHalfedgePickCallback(const std::function& f); + void registerFacePickCallback(const std::function& f); + void registerCornerPickCallback(const std::function& f); + // Render the the structure on screen virtual void draw() override; virtual void drawDelayed() override; @@ -398,6 +404,15 @@ class SurfaceMesh : public QuantityStructure { void buildHalfedgeInfoGui(const SurfaceMeshPickResult& result); void buildCornerInfoGui(const SurfaceMeshPickResult& result); + std::list> vertexPickCallbacks; + std::list> edgePickCallbacks; + std::list> halfedgePickCallbacks; + std::list> facePickCallbacks; + std::list> cornerPickCallbacks; + std::list>::iterator pickCallbackHandle; + bool hasPickCallback = false; + void handlePick(PickResult result); + // Manage per-element transparency // which (scalar) quantity to set point size from // TODO make these PersistentValue<>? diff --git a/src/curve_network.cpp b/src/curve_network.cpp index 9a7d35d1c..f29a898f5 100644 --- a/src/curve_network.cpp +++ b/src/curve_network.cpp @@ -179,7 +179,7 @@ void CurveNetwork::drawDelayed() { } void CurveNetwork::drawPick() { - if (!isEnabled()) { + if (!isEnabled() || !getPickable()) { return; } diff --git a/src/pick.cpp b/src/pick.cpp index db1cfe6ec..e497a5c75 100644 --- a/src/pick.cpp +++ b/src/pick.cpp @@ -87,6 +87,14 @@ void setSelection(PickResult newPick) { } } +std::list>::iterator registerPickCallback(const std::function& f) { + return state::globalContext.pickCallbacks.insert(state::globalContext.pickCallbacks.end(), f); +} + +void removePickCallback(std::list>::iterator callback) { + state::globalContext.pickCallbacks.erase(callback); +} + namespace pick { diff --git a/src/point_cloud.cpp b/src/point_cloud.cpp index 25e24a057..45dadd696 100644 --- a/src/point_cloud.cpp +++ b/src/point_cloud.cpp @@ -118,7 +118,7 @@ void PointCloud::drawDelayed() { } void PointCloud::drawPick() { - if (!isEnabled()) { + if (!isEnabled() || !getPickable()) { return; } diff --git a/src/polyscope.cpp b/src/polyscope.cpp index aa733d37d..bd8e62336 100644 --- a/src/polyscope.cpp +++ b/src/polyscope.cpp @@ -521,6 +521,9 @@ void processInputEvents() { glm::vec2 screenCoords{io.MousePos.x, io.MousePos.y}; PickResult pickResult = pickAtScreenCoords(screenCoords); setSelection(pickResult); + for (std::function& callback : state::globalContext.pickCallbacks) { + callback(pickResult); + } } } diff --git a/src/state.cpp b/src/state.cpp index d57c5ffa8..74a12f137 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -20,6 +20,7 @@ std::vector>& slicePlanes = globalContext.slicePlane std::vector>& widgets = globalContext.widgets; bool& doDefaultMouseInteraction = globalContext.doDefaultMouseInteraction; std::function& userCallback = globalContext.userCallback; +std::list>& pickCallbacks = globalContext.pickCallbacks; } // namespace state } // namespace polyscope diff --git a/src/structure.cpp b/src/structure.cpp index 264e0ff33..ebd9409bb 100644 --- a/src/structure.cpp +++ b/src/structure.cpp @@ -15,13 +15,14 @@ Structure::Structure(std::string name_, std::string subtypeName_) transformGizmo(subtypeName + "#" + name + "#transform_gizmo", objectTransform.get(), &objectTransform), cullWholeElements(subtypeName + "#" + name + "#cullWholeElements", false), ignoredSlicePlaneNames(subtypeName + "#" + name + "#ignored_slice_planes", {}), + pickable(subtypeName + "#" + name + "#pickable", true), objectSpaceBoundingBox( std::tuple{glm::vec3{-777, -777, -777}, glm::vec3{-777, -777, -777}}), objectSpaceLengthScale(-777) { validateName(name); } -Structure::~Structure() {}; +Structure::~Structure(){}; Structure* Structure::setEnabled(bool newEnabled) { if (newEnabled == isEnabled()) return this; @@ -318,6 +319,13 @@ Structure* Structure::setTransformGizmoEnabled(bool newVal) { } bool Structure::getTransformGizmoEnabled() { return transformGizmo.enabled.get(); } +Structure* Structure::setPickable(bool newPickable) { + pickable = newPickable; + requestRedraw(); + return this; +} +bool Structure::getPickable() { return pickable.get(); } + Structure* Structure::setIgnoreSlicePlane(std::string name, bool newValue) { if (getIgnoreSlicePlane(name) == newValue) { diff --git a/src/surface_mesh.cpp b/src/surface_mesh.cpp index 136b9fa39..fb8315d2e 100644 --- a/src/surface_mesh.cpp +++ b/src/surface_mesh.cpp @@ -784,7 +784,7 @@ void SurfaceMesh::drawDelayed() { } void SurfaceMesh::drawPick() { - if (!isEnabled()) { + if (!isEnabled() || !getPickable()) { return; } @@ -827,7 +827,7 @@ void SurfaceMesh::drawPick() { } void SurfaceMesh::drawPickDelayed() { - if (!isEnabled()) { + if (!isEnabled() || !getPickable()) { return; } @@ -1193,6 +1193,81 @@ glm::vec2 SurfaceMesh::projectToScreenSpace(glm::vec3 coord) { return glm::vec2{screenPoint.x, screenPoint.y} / screenPoint.w; } +void SurfaceMesh::registerVertexPickCallback(const std::function& f) { + if (!hasPickCallback) { + pickCallbackHandle = registerPickCallback([&](PickResult result) { handlePick(result); }); + hasPickCallback = true; + } + vertexPickCallbacks.insert(vertexPickCallbacks.end(), f); +} +void SurfaceMesh::registerEdgePickCallback(const std::function& f) { + if (!hasPickCallback) { + pickCallbackHandle = registerPickCallback([&](PickResult result) { handlePick(result); }); + hasPickCallback = true; + } + edgePickCallbacks.insert(edgePickCallbacks.end(), f); +} +void SurfaceMesh::registerHalfedgePickCallback(const std::function& f) { + if (!hasPickCallback) { + pickCallbackHandle = registerPickCallback([&](PickResult result) { handlePick(result); }); + hasPickCallback = true; + } + halfedgePickCallbacks.insert(halfedgePickCallbacks.end(), f); +} +void SurfaceMesh::registerFacePickCallback(const std::function& f) { + if (!hasPickCallback) { + pickCallbackHandle = registerPickCallback([&](PickResult result) { handlePick(result); }); + hasPickCallback = true; + } + facePickCallbacks.insert(facePickCallbacks.end(), f); +} +void SurfaceMesh::registerCornerPickCallback(const std::function& f) { + if (!hasPickCallback) { + pickCallbackHandle = registerPickCallback([&](PickResult result) { handlePick(result); }); + hasPickCallback = true; + } + cornerPickCallbacks.insert(cornerPickCallbacks.end(), f); +} +void SurfaceMesh::handlePick(PickResult rawResult) { + if (!rawResult.isHit || rawResult.structureName != name) return; + SurfaceMeshPickResult result = interpretPickResult(rawResult); + + switch (result.elementType) { + case MeshElement::VERTEX: { + for (const std::function& f : vertexPickCallbacks) f(result.index); + break; + } + case MeshElement::FACE: { + for (const std::function& f : facePickCallbacks) f(result.index); + break; + } + case MeshElement::EDGE: { + for (const std::function& f : edgePickCallbacks) f(result.index); + break; + } + case MeshElement::HALFEDGE: { + for (const std::function& f : halfedgePickCallbacks) f(result.index); + + // Also call edge pick callbacks while we're here + if (edgesHaveBeenUsed) { + // do the edge one too (see note in pick buffer filler) + uint32_t halfedgeInd = result.index; + if (halfedgeInd >= halfedgeEdgeCorrespondence.size()) { + exception("problem with halfedge edge indices"); + } + uint32_t edgeInd = halfedgeEdgeCorrespondence[halfedgeInd]; + for (const std::function& f : edgePickCallbacks) f(edgeInd); + } + + break; + } + case MeshElement::CORNER: { + for (const std::function& f : cornerPickCallbacks) f(result.index); + break; + } + }; +} + void SurfaceMesh::buildVertexInfoGui(const SurfaceMeshPickResult& result) { size_t vInd = result.index; size_t displayInd = vInd; diff --git a/src/volume_grid.cpp b/src/volume_grid.cpp index f108a7674..882b5c38a 100644 --- a/src/volume_grid.cpp +++ b/src/volume_grid.cpp @@ -148,7 +148,7 @@ void VolumeGrid::drawDelayed() { } void VolumeGrid::drawPick() { - if (!isEnabled()) { + if (!isEnabled() || !getPickable()) { return; } @@ -170,7 +170,7 @@ void VolumeGrid::drawPick() { // Draw the actual grid render::engine->setBackfaceCull(true); pickProgram->draw(); - + for (auto& x : quantities) { x.second->drawPick(); } diff --git a/src/volume_mesh.cpp b/src/volume_mesh.cpp index 78094bbcc..fd814a996 100644 --- a/src/volume_mesh.cpp +++ b/src/volume_mesh.cpp @@ -473,7 +473,7 @@ void VolumeMesh::drawDelayed() { } void VolumeMesh::drawPick() { - if (!isEnabled()) { + if (!isEnabled() || !getPickable()) { return; }