diff --git a/include/polyscope/curve_network.h b/include/polyscope/curve_network.h index 8ecc7c4d..e90366e0 100644 --- a/include/polyscope/curve_network.h +++ b/include/polyscope/curve_network.h @@ -147,10 +147,15 @@ class CurveNetwork : public QuantityStructure { // effect is multiplicative with pointRadius // negative values are always clamped to 0 // if autoScale==true, values are rescaled such that the largest has size pointRadius + void setNodeRadiusQuantity(CurveNetworkNodeScalarQuantity* quantity, bool autoScale = true); void setNodeRadiusQuantity(std::string name, bool autoScale = true); void clearNodeRadiusQuantity(); + void setEdgeRadiusQuantity(CurveNetworkEdgeScalarQuantity* quantity, bool autoScale = true); + void setEdgeRadiusQuantity(std::string name, bool autoScale = true); + void clearEdgeRadiusQuantity(); + // set the radius of the points CurveNetwork* setRadius(float newVal, bool isRelative = true); float getRadius(); @@ -189,7 +194,8 @@ class CurveNetwork : public QuantityStructure { void preparePick(); void recomputeGeometryIfPopulated(); - float computeRadiusMultiplierUniform(); + float computeNodeRadiusMultiplierUniform(); + float computeEdgeRadiusMultiplierUniform(); // Pick helpers void buildNodePickUI(const CurveNetworkPickResult& result); @@ -207,8 +213,11 @@ class CurveNetwork : public QuantityStructure { // Manage varying node, edge size std::string nodeRadiusQuantityName = ""; // empty string means none + std::string edgeRadiusQuantityName = ""; // empty string means none bool nodeRadiusQuantityAutoscale = true; + bool edgeRadiusQuantityAutoscale = true; CurveNetworkNodeScalarQuantity& resolveNodeRadiusQuantity(); // helper + CurveNetworkEdgeScalarQuantity& resolveEdgeRadiusQuantity(); // helper }; diff --git a/src/curve_network.cpp b/src/curve_network.cpp index 01430f7f..6cb34ea9 100644 --- a/src/curve_network.cpp +++ b/src/curve_network.cpp @@ -64,21 +64,48 @@ CurveNetwork::CurveNetwork(std::string name, std::vector nodes_, std: updateObjectSpaceBounds(); } -float CurveNetwork::computeRadiusMultiplierUniform() { - if (nodeRadiusQuantityName != "" && !nodeRadiusQuantityAutoscale) { - // special case: ignore radius uniform - return 1.; - } else { - // common case +float CurveNetwork::computeNodeRadiusMultiplierUniform() { + float scalarQScale = 1.; + if (nodeRadiusQuantityName != "") { + // node radius quantity only, or both - float scalarQScale = 1.; - if (nodeRadiusQuantityName != "") { - CurveNetworkNodeScalarQuantity& radQ = resolveNodeRadiusQuantity(); - scalarQScale = std::max(0., radQ.getDataRange().second); - } + if (!nodeRadiusQuantityAutoscale) return 1.; + + CurveNetworkNodeScalarQuantity& radQ = resolveNodeRadiusQuantity(); + scalarQScale = std::max(0., radQ.getDataRange().second); + + } else if (edgeRadiusQuantityName != "") { + // edge radius quantity + + if (!edgeRadiusQuantityAutoscale) return 1.; + + CurveNetworkEdgeScalarQuantity& radQ = resolveEdgeRadiusQuantity(); + scalarQScale = std::max(0., radQ.getDataRange().second); + } + + return getRadius() / scalarQScale; +} + +float CurveNetwork::computeEdgeRadiusMultiplierUniform() { + + float scalarQScale = 1.; + if (edgeRadiusQuantityName != "") { + // edge radius quantity only, or both + + if (!edgeRadiusQuantityAutoscale) return 1.; - return getRadius() / scalarQScale; + CurveNetworkEdgeScalarQuantity& radQ = resolveEdgeRadiusQuantity(); + scalarQScale = std::max(0., radQ.getDataRange().second); + } else if (nodeRadiusQuantityName != "") { + // node radius quantity only + + if (!nodeRadiusQuantityAutoscale) return 1.; + + CurveNetworkNodeScalarQuantity& radQ = resolveNodeRadiusQuantity(); + scalarQScale = std::max(0., radQ.getDataRange().second); } + + return getRadius() / scalarQScale; } // Helper to set uniforms @@ -87,7 +114,7 @@ void CurveNetwork::setCurveNetworkNodeUniforms(render::ShaderProgram& p) { glm::mat4 Pinv = glm::inverse(P); p.setUniform("u_invProjMatrix", glm::value_ptr(Pinv)); p.setUniform("u_viewport", render::engine->getCurrentViewport()); - p.setUniform("u_pointRadius", computeRadiusMultiplierUniform()); + p.setUniform("u_pointRadius", computeNodeRadiusMultiplierUniform()); } void CurveNetwork::setCurveNetworkEdgeUniforms(render::ShaderProgram& p) { @@ -95,7 +122,7 @@ void CurveNetwork::setCurveNetworkEdgeUniforms(render::ShaderProgram& p) { glm::mat4 Pinv = glm::inverse(P); p.setUniform("u_invProjMatrix", glm::value_ptr(Pinv)); p.setUniform("u_viewport", render::engine->getCurrentViewport()); - p.setUniform("u_radius", computeRadiusMultiplierUniform()); + p.setUniform("u_radius", computeNodeRadiusMultiplierUniform()); } void CurveNetwork::draw() { @@ -175,7 +202,7 @@ void CurveNetwork::drawPick() { std::vector CurveNetwork::addCurveNetworkNodeRules(std::vector initRules) { initRules = addStructureRules(initRules); - if (nodeRadiusQuantityName != "") { + if (nodeRadiusQuantityName != "" || edgeRadiusQuantityName != "") { initRules.push_back("SPHERE_VARIABLE_SIZE"); } if (wantsCullPosition()) { @@ -187,7 +214,7 @@ std::vector CurveNetwork::addCurveNetworkEdgeRules(std::vector(q.second.get()); + if (scalarQ != nullptr) { + if (ImGui::MenuItem(scalarQ->name.c_str(), nullptr, edgeRadiusQuantityName == scalarQ->name)) + setEdgeRadiusQuantity(scalarQ); + } + } + + ImGui::EndMenu(); + } if (render::buildMaterialOptionsGui(material.get())) { material.manuallyChanged(); @@ -521,6 +581,24 @@ void CurveNetwork::clearNodeRadiusQuantity() { refresh(); }; +void CurveNetwork::setEdgeRadiusQuantity(CurveNetworkEdgeScalarQuantity* quantity, bool autoScale) { + setEdgeRadiusQuantity(quantity->name, autoScale); +} + +void CurveNetwork::setEdgeRadiusQuantity(std::string name, bool autoScale) { + edgeRadiusQuantityName = name; + edgeRadiusQuantityAutoscale = autoScale; + + resolveEdgeRadiusQuantity(); // do it once, just so we fail fast if it doesn't exist + + refresh(); +} + +void CurveNetwork::clearEdgeRadiusQuantity() { + edgeRadiusQuantityName = ""; + refresh(); +}; + CurveNetwork* CurveNetwork::setRadius(float newVal, bool isRelative) { radius = ScaledValue(newVal, isRelative); polyscope::requestRedraw(); @@ -607,10 +685,27 @@ CurveNetworkNodeScalarQuantity& CurveNetwork::resolveNodeRadiusQuantity() { if (sizeQ != nullptr) { sizeScalarQ = dynamic_cast(sizeQ); if (sizeScalarQ == nullptr) { - exception("Cannot populate node size from quantity [" + name + "], it is not a scalar quantity"); + exception("Cannot populate node size from quantity [" + nodeRadiusQuantityName + + "], it is not a scalar quantity"); + } + } else { + exception("Cannot populate node size from quantity [" + nodeRadiusQuantityName + "], it does not exist"); + } + + return *sizeScalarQ; +} + +CurveNetworkEdgeScalarQuantity& CurveNetwork::resolveEdgeRadiusQuantity() { + CurveNetworkEdgeScalarQuantity* sizeScalarQ = nullptr; + CurveNetworkQuantity* sizeQ = getQuantity(edgeRadiusQuantityName); + if (sizeQ != nullptr) { + sizeScalarQ = dynamic_cast(sizeQ); + if (sizeScalarQ == nullptr) { + exception("Cannot populate edge size from quantity [" + edgeRadiusQuantityName + + "], it is not a scalar quantity"); } } else { - exception("Cannot populate node size from quantity [" + name + "], it does not exist"); + exception("Cannot populate edge size from quantity [" + edgeRadiusQuantityName + "], it does not exist"); } return *sizeScalarQ; diff --git a/src/curve_network_scalar_quantity.cpp b/src/curve_network_scalar_quantity.cpp index 620684bb..65fcf6c0 100644 --- a/src/curve_network_scalar_quantity.cpp +++ b/src/curve_network_scalar_quantity.cpp @@ -176,6 +176,9 @@ void CurveNetworkEdgeScalarQuantity::createProgram() { } void CurveNetworkEdgeScalarQuantity::updateNodeAverageValues() { + // NOTE: we don't have any caching or dirty-marking on this, so it is likely getting recomputed more often than + // necessary + parent.edgeTailInds.ensureHostBufferPopulated(); parent.edgeTipInds.ensureHostBufferPopulated(); values.ensureHostBufferPopulated(); diff --git a/test/src/curve_network_test.cpp b/test/src/curve_network_test.cpp index a16fb975..8b7fd794 100644 --- a/test/src/curve_network_test.cpp +++ b/test/src/curve_network_test.cpp @@ -93,7 +93,7 @@ TEST_F(PolyscopeTest, CurveNetworkScalarCategoricalEdge) { polyscope::removeAllStructures(); } -TEST_F(PolyscopeTest, CurveNetworkScalarRadius) { +TEST_F(PolyscopeTest, CurveNetworkNodeScalarRadius) { auto psCurve = registerCurveNetwork(); std::random_device rd; @@ -124,6 +124,72 @@ TEST_F(PolyscopeTest, CurveNetworkScalarRadius) { polyscope::removeAllStructures(); } +TEST_F(PolyscopeTest, CurveNetworkEdgeScalarRadius) { + auto psCurve = registerCurveNetwork(); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(1, 10); + + std::vector eScalar(psCurve->nEdges(), 0.1); + std::vector eScalar2(psCurve->nEdges(), 0.1); + std::generate(eScalar.begin(), eScalar.end(), [&]() { return dis(gen); }); + std::generate(eScalar2.begin(), eScalar2.end(), [&]() { return dis(gen); }); + + auto q1 = psCurve->addEdgeScalarQuantity("eScalar", eScalar); + auto q2 = psCurve->addEdgeScalarQuantity("eScalar2", eScalar2); + q1->setEnabled(true); + + psCurve->setEdgeRadiusQuantity(q1); + polyscope::show(3); + + psCurve->setEdgeRadiusQuantity("eScalar2"); + polyscope::show(3); + + psCurve->setEdgeRadiusQuantity("eScalar2", false); // no autoscaling + polyscope::show(3); + + psCurve->clearEdgeRadiusQuantity(); + polyscope::show(3); + + polyscope::removeAllStructures(); +} + +TEST_F(PolyscopeTest, CurveNetworkNodeAndEdgeScalarRadius) { + auto psCurve = registerCurveNetwork(); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(1, 10); + + std::vector vScalar(psCurve->nNodes(), 0.1); + std::generate(vScalar.begin(), vScalar.end(), [&]() { return dis(gen); }); + auto q1v = psCurve->addNodeScalarQuantity("vScalar", vScalar); + q1v->setEnabled(true); + + std::vector eScalar(psCurve->nEdges(), 0.1); + std::generate(eScalar.begin(), eScalar.end(), [&]() { return dis(gen); }); + auto q1e = psCurve->addEdgeScalarQuantity("eScalar", eScalar); + q1e->setEnabled(true); + + psCurve->setNodeRadiusQuantity(q1v); + psCurve->setEdgeRadiusQuantity(q1e); + polyscope::show(3); + + psCurve->clearNodeRadiusQuantity(); + polyscope::show(3); + + psCurve->setNodeRadiusQuantity(q1v, true); + psCurve->setEdgeRadiusQuantity(q1e, true); + polyscope::show(3); + + psCurve->setNodeRadiusQuantity(q1v); + psCurve->setEdgeRadiusQuantity(q1e, true); + polyscope::show(3); + + polyscope::removeAllStructures(); +} + TEST_F(PolyscopeTest, CurveNetworkVertexVector) { auto psCurve = registerCurveNetwork(); std::vector vals(psCurve->nNodes(), {1., 2., 3.});