Skip to content

Commit 2012f51

Browse files
authored
add variable curve network radius on edges (#351)
1 parent 6a8eb34 commit 2012f51

File tree

4 files changed

+196
-23
lines changed

4 files changed

+196
-23
lines changed

include/polyscope/curve_network.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,15 @@ class CurveNetwork : public QuantityStructure<CurveNetwork> {
147147
// effect is multiplicative with pointRadius
148148
// negative values are always clamped to 0
149149
// if autoScale==true, values are rescaled such that the largest has size pointRadius
150+
150151
void setNodeRadiusQuantity(CurveNetworkNodeScalarQuantity* quantity, bool autoScale = true);
151152
void setNodeRadiusQuantity(std::string name, bool autoScale = true);
152153
void clearNodeRadiusQuantity();
153154

155+
void setEdgeRadiusQuantity(CurveNetworkEdgeScalarQuantity* quantity, bool autoScale = true);
156+
void setEdgeRadiusQuantity(std::string name, bool autoScale = true);
157+
void clearEdgeRadiusQuantity();
158+
154159
// set the radius of the points
155160
CurveNetwork* setRadius(float newVal, bool isRelative = true);
156161
float getRadius();
@@ -189,7 +194,8 @@ class CurveNetwork : public QuantityStructure<CurveNetwork> {
189194
void preparePick();
190195

191196
void recomputeGeometryIfPopulated();
192-
float computeRadiusMultiplierUniform();
197+
float computeNodeRadiusMultiplierUniform();
198+
float computeEdgeRadiusMultiplierUniform();
193199

194200
// Pick helpers
195201
void buildNodePickUI(const CurveNetworkPickResult& result);
@@ -207,8 +213,11 @@ class CurveNetwork : public QuantityStructure<CurveNetwork> {
207213

208214
// Manage varying node, edge size
209215
std::string nodeRadiusQuantityName = ""; // empty string means none
216+
std::string edgeRadiusQuantityName = ""; // empty string means none
210217
bool nodeRadiusQuantityAutoscale = true;
218+
bool edgeRadiusQuantityAutoscale = true;
211219
CurveNetworkNodeScalarQuantity& resolveNodeRadiusQuantity(); // helper
220+
CurveNetworkEdgeScalarQuantity& resolveEdgeRadiusQuantity(); // helper
212221
};
213222

214223

src/curve_network.cpp

Lines changed: 116 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -64,21 +64,48 @@ CurveNetwork::CurveNetwork(std::string name, std::vector<glm::vec3> nodes_, std:
6464
updateObjectSpaceBounds();
6565
}
6666
67-
float CurveNetwork::computeRadiusMultiplierUniform() {
68-
if (nodeRadiusQuantityName != "" && !nodeRadiusQuantityAutoscale) {
69-
// special case: ignore radius uniform
70-
return 1.;
71-
} else {
72-
// common case
67+
float CurveNetwork::computeNodeRadiusMultiplierUniform() {
68+
float scalarQScale = 1.;
69+
if (nodeRadiusQuantityName != "") {
70+
// node radius quantity only, or both
7371
74-
float scalarQScale = 1.;
75-
if (nodeRadiusQuantityName != "") {
76-
CurveNetworkNodeScalarQuantity& radQ = resolveNodeRadiusQuantity();
77-
scalarQScale = std::max(0., radQ.getDataRange().second);
78-
}
72+
if (!nodeRadiusQuantityAutoscale) return 1.;
73+
74+
CurveNetworkNodeScalarQuantity& radQ = resolveNodeRadiusQuantity();
75+
scalarQScale = std::max(0., radQ.getDataRange().second);
76+
77+
} else if (edgeRadiusQuantityName != "") {
78+
// edge radius quantity
79+
80+
if (!edgeRadiusQuantityAutoscale) return 1.;
81+
82+
CurveNetworkEdgeScalarQuantity& radQ = resolveEdgeRadiusQuantity();
83+
scalarQScale = std::max(0., radQ.getDataRange().second);
84+
}
85+
86+
return getRadius() / scalarQScale;
87+
}
88+
89+
float CurveNetwork::computeEdgeRadiusMultiplierUniform() {
90+
91+
float scalarQScale = 1.;
92+
if (edgeRadiusQuantityName != "") {
93+
// edge radius quantity only, or both
94+
95+
if (!edgeRadiusQuantityAutoscale) return 1.;
7996
80-
return getRadius() / scalarQScale;
97+
CurveNetworkEdgeScalarQuantity& radQ = resolveEdgeRadiusQuantity();
98+
scalarQScale = std::max(0., radQ.getDataRange().second);
99+
} else if (nodeRadiusQuantityName != "") {
100+
// node radius quantity only
101+
102+
if (!nodeRadiusQuantityAutoscale) return 1.;
103+
104+
CurveNetworkNodeScalarQuantity& radQ = resolveNodeRadiusQuantity();
105+
scalarQScale = std::max(0., radQ.getDataRange().second);
81106
}
107+
108+
return getRadius() / scalarQScale;
82109
}
83110
84111
// Helper to set uniforms
@@ -87,15 +114,15 @@ void CurveNetwork::setCurveNetworkNodeUniforms(render::ShaderProgram& p) {
87114
glm::mat4 Pinv = glm::inverse(P);
88115
p.setUniform("u_invProjMatrix", glm::value_ptr(Pinv));
89116
p.setUniform("u_viewport", render::engine->getCurrentViewport());
90-
p.setUniform("u_pointRadius", computeRadiusMultiplierUniform());
117+
p.setUniform("u_pointRadius", computeNodeRadiusMultiplierUniform());
91118
}
92119
93120
void CurveNetwork::setCurveNetworkEdgeUniforms(render::ShaderProgram& p) {
94121
glm::mat4 P = view::getCameraPerspectiveMatrix();
95122
glm::mat4 Pinv = glm::inverse(P);
96123
p.setUniform("u_invProjMatrix", glm::value_ptr(Pinv));
97124
p.setUniform("u_viewport", render::engine->getCurrentViewport());
98-
p.setUniform("u_radius", computeRadiusMultiplierUniform());
125+
p.setUniform("u_radius", computeNodeRadiusMultiplierUniform());
99126
}
100127
101128
void CurveNetwork::draw() {
@@ -175,7 +202,7 @@ void CurveNetwork::drawPick() {
175202
std::vector<std::string> CurveNetwork::addCurveNetworkNodeRules(std::vector<std::string> initRules) {
176203
initRules = addStructureRules(initRules);
177204
178-
if (nodeRadiusQuantityName != "") {
205+
if (nodeRadiusQuantityName != "" || edgeRadiusQuantityName != "") {
179206
initRules.push_back("SPHERE_VARIABLE_SIZE");
180207
}
181208
if (wantsCullPosition()) {
@@ -187,7 +214,7 @@ std::vector<std::string> CurveNetwork::addCurveNetworkEdgeRules(std::vector<std:
187214
initRules = addStructureRules(initRules);
188215
189216
// use node radius to blend cylinder radius
190-
if (nodeRadiusQuantityName != "") {
217+
if (nodeRadiusQuantityName != "" || edgeRadiusQuantityName != "") {
191218
initRules.push_back("CYLINDER_VARIABLE_SIZE");
192219
}
193220
@@ -296,17 +323,35 @@ void CurveNetwork::preparePick() {
296323
void CurveNetwork::fillNodeGeometryBuffers(render::ShaderProgram& program) {
297324
program.setAttribute("a_position", nodePositions.getRenderAttributeBuffer());
298325
299-
if (nodeRadiusQuantityName != "") {
326+
bool haveNodeRadiusQuantity = (nodeRadiusQuantityName != "");
327+
bool haveEdgeRadiusQuantity = (edgeRadiusQuantityName != "");
328+
329+
if (haveNodeRadiusQuantity) {
330+
// have just node, or have both
300331
CurveNetworkNodeScalarQuantity& nodeRadQ = resolveNodeRadiusQuantity();
301332
program.setAttribute("a_pointRadius", nodeRadQ.values.getRenderAttributeBuffer());
333+
} else if (haveEdgeRadiusQuantity) {
334+
// have just edge
335+
CurveNetworkEdgeScalarQuantity& edgeRadQ = resolveEdgeRadiusQuantity();
336+
edgeRadQ.updateNodeAverageValues();
337+
program.setAttribute("a_pointRadius", edgeRadQ.nodeAverageValues.getRenderAttributeBuffer());
302338
}
303339
}
304340
305341
void CurveNetwork::fillEdgeGeometryBuffers(render::ShaderProgram& program) {
306342
program.setAttribute("a_position_tail", nodePositions.getIndexedRenderAttributeBuffer(edgeTailInds));
307343
program.setAttribute("a_position_tip", nodePositions.getIndexedRenderAttributeBuffer(edgeTipInds));
308344
309-
if (nodeRadiusQuantityName != "") {
345+
bool haveNodeRadiusQuantity = (nodeRadiusQuantityName != "");
346+
bool haveEdgeRadiusQuantity = (edgeRadiusQuantityName != "");
347+
348+
if (haveEdgeRadiusQuantity) {
349+
// have just edge or have both
350+
CurveNetworkEdgeScalarQuantity& edgeRadQ = resolveEdgeRadiusQuantity();
351+
program.setAttribute("a_tailRadius", edgeRadQ.values.getRenderAttributeBuffer());
352+
program.setAttribute("a_tipRadius", edgeRadQ.values.getRenderAttributeBuffer());
353+
} else if (haveNodeRadiusQuantity) {
354+
// have just node
310355
CurveNetworkNodeScalarQuantity& nodeRadQ = resolveNodeRadiusQuantity();
311356
program.setAttribute("a_tailRadius", nodeRadQ.values.getIndexedRenderAttributeBuffer(edgeTailInds));
312357
program.setAttribute("a_tipRadius", nodeRadQ.values.getIndexedRenderAttributeBuffer(edgeTipInds));
@@ -422,7 +467,7 @@ void CurveNetwork::buildCustomUI() {
422467
423468
void CurveNetwork::buildCustomOptionsUI() {
424469
425-
if (ImGui::BeginMenu("Variable Radius")) {
470+
if (ImGui::BeginMenu("Node Variable Radius")) {
426471
427472
if (ImGui::MenuItem("none", nullptr, nodeRadiusQuantityName == "")) clearNodeRadiusQuantity();
428473
ImGui::Separator();
@@ -438,6 +483,21 @@ void CurveNetwork::buildCustomOptionsUI() {
438483
ImGui::EndMenu();
439484
}
440485
486+
if (ImGui::BeginMenu("Edge Variable Radius")) {
487+
488+
if (ImGui::MenuItem("none", nullptr, edgeRadiusQuantityName == "")) clearEdgeRadiusQuantity();
489+
ImGui::Separator();
490+
491+
for (auto& q : quantities) {
492+
CurveNetworkEdgeScalarQuantity* scalarQ = dynamic_cast<CurveNetworkEdgeScalarQuantity*>(q.second.get());
493+
if (scalarQ != nullptr) {
494+
if (ImGui::MenuItem(scalarQ->name.c_str(), nullptr, edgeRadiusQuantityName == scalarQ->name))
495+
setEdgeRadiusQuantity(scalarQ);
496+
}
497+
}
498+
499+
ImGui::EndMenu();
500+
}
441501
442502
if (render::buildMaterialOptionsGui(material.get())) {
443503
material.manuallyChanged();
@@ -521,6 +581,24 @@ void CurveNetwork::clearNodeRadiusQuantity() {
521581
refresh();
522582
};
523583

584+
void CurveNetwork::setEdgeRadiusQuantity(CurveNetworkEdgeScalarQuantity* quantity, bool autoScale) {
585+
setEdgeRadiusQuantity(quantity->name, autoScale);
586+
}
587+
588+
void CurveNetwork::setEdgeRadiusQuantity(std::string name, bool autoScale) {
589+
edgeRadiusQuantityName = name;
590+
edgeRadiusQuantityAutoscale = autoScale;
591+
592+
resolveEdgeRadiusQuantity(); // do it once, just so we fail fast if it doesn't exist
593+
594+
refresh();
595+
}
596+
597+
void CurveNetwork::clearEdgeRadiusQuantity() {
598+
edgeRadiusQuantityName = "";
599+
refresh();
600+
};
601+
524602
CurveNetwork* CurveNetwork::setRadius(float newVal, bool isRelative) {
525603
radius = ScaledValue<float>(newVal, isRelative);
526604
polyscope::requestRedraw();
@@ -607,10 +685,27 @@ CurveNetworkNodeScalarQuantity& CurveNetwork::resolveNodeRadiusQuantity() {
607685
if (sizeQ != nullptr) {
608686
sizeScalarQ = dynamic_cast<CurveNetworkNodeScalarQuantity*>(sizeQ);
609687
if (sizeScalarQ == nullptr) {
610-
exception("Cannot populate node size from quantity [" + name + "], it is not a scalar quantity");
688+
exception("Cannot populate node size from quantity [" + nodeRadiusQuantityName +
689+
"], it is not a scalar quantity");
690+
}
691+
} else {
692+
exception("Cannot populate node size from quantity [" + nodeRadiusQuantityName + "], it does not exist");
693+
}
694+
695+
return *sizeScalarQ;
696+
}
697+
698+
CurveNetworkEdgeScalarQuantity& CurveNetwork::resolveEdgeRadiusQuantity() {
699+
CurveNetworkEdgeScalarQuantity* sizeScalarQ = nullptr;
700+
CurveNetworkQuantity* sizeQ = getQuantity(edgeRadiusQuantityName);
701+
if (sizeQ != nullptr) {
702+
sizeScalarQ = dynamic_cast<CurveNetworkEdgeScalarQuantity*>(sizeQ);
703+
if (sizeScalarQ == nullptr) {
704+
exception("Cannot populate edge size from quantity [" + edgeRadiusQuantityName +
705+
"], it is not a scalar quantity");
611706
}
612707
} else {
613-
exception("Cannot populate node size from quantity [" + name + "], it does not exist");
708+
exception("Cannot populate edge size from quantity [" + edgeRadiusQuantityName + "], it does not exist");
614709
}
615710

616711
return *sizeScalarQ;

src/curve_network_scalar_quantity.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ void CurveNetworkEdgeScalarQuantity::createProgram() {
176176
}
177177
178178
void CurveNetworkEdgeScalarQuantity::updateNodeAverageValues() {
179+
// NOTE: we don't have any caching or dirty-marking on this, so it is likely getting recomputed more often than
180+
// necessary
181+
179182
parent.edgeTailInds.ensureHostBufferPopulated();
180183
parent.edgeTipInds.ensureHostBufferPopulated();
181184
values.ensureHostBufferPopulated();

test/src/curve_network_test.cpp

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ TEST_F(PolyscopeTest, CurveNetworkScalarCategoricalEdge) {
9393
polyscope::removeAllStructures();
9494
}
9595

96-
TEST_F(PolyscopeTest, CurveNetworkScalarRadius) {
96+
TEST_F(PolyscopeTest, CurveNetworkNodeScalarRadius) {
9797
auto psCurve = registerCurveNetwork();
9898

9999
std::random_device rd;
@@ -124,6 +124,72 @@ TEST_F(PolyscopeTest, CurveNetworkScalarRadius) {
124124
polyscope::removeAllStructures();
125125
}
126126

127+
TEST_F(PolyscopeTest, CurveNetworkEdgeScalarRadius) {
128+
auto psCurve = registerCurveNetwork();
129+
130+
std::random_device rd;
131+
std::mt19937 gen(rd());
132+
std::uniform_int_distribution<> dis(1, 10);
133+
134+
std::vector<double> eScalar(psCurve->nEdges(), 0.1);
135+
std::vector<double> eScalar2(psCurve->nEdges(), 0.1);
136+
std::generate(eScalar.begin(), eScalar.end(), [&]() { return dis(gen); });
137+
std::generate(eScalar2.begin(), eScalar2.end(), [&]() { return dis(gen); });
138+
139+
auto q1 = psCurve->addEdgeScalarQuantity("eScalar", eScalar);
140+
auto q2 = psCurve->addEdgeScalarQuantity("eScalar2", eScalar2);
141+
q1->setEnabled(true);
142+
143+
psCurve->setEdgeRadiusQuantity(q1);
144+
polyscope::show(3);
145+
146+
psCurve->setEdgeRadiusQuantity("eScalar2");
147+
polyscope::show(3);
148+
149+
psCurve->setEdgeRadiusQuantity("eScalar2", false); // no autoscaling
150+
polyscope::show(3);
151+
152+
psCurve->clearEdgeRadiusQuantity();
153+
polyscope::show(3);
154+
155+
polyscope::removeAllStructures();
156+
}
157+
158+
TEST_F(PolyscopeTest, CurveNetworkNodeAndEdgeScalarRadius) {
159+
auto psCurve = registerCurveNetwork();
160+
161+
std::random_device rd;
162+
std::mt19937 gen(rd());
163+
std::uniform_int_distribution<> dis(1, 10);
164+
165+
std::vector<double> vScalar(psCurve->nNodes(), 0.1);
166+
std::generate(vScalar.begin(), vScalar.end(), [&]() { return dis(gen); });
167+
auto q1v = psCurve->addNodeScalarQuantity("vScalar", vScalar);
168+
q1v->setEnabled(true);
169+
170+
std::vector<double> eScalar(psCurve->nEdges(), 0.1);
171+
std::generate(eScalar.begin(), eScalar.end(), [&]() { return dis(gen); });
172+
auto q1e = psCurve->addEdgeScalarQuantity("eScalar", eScalar);
173+
q1e->setEnabled(true);
174+
175+
psCurve->setNodeRadiusQuantity(q1v);
176+
psCurve->setEdgeRadiusQuantity(q1e);
177+
polyscope::show(3);
178+
179+
psCurve->clearNodeRadiusQuantity();
180+
polyscope::show(3);
181+
182+
psCurve->setNodeRadiusQuantity(q1v, true);
183+
psCurve->setEdgeRadiusQuantity(q1e, true);
184+
polyscope::show(3);
185+
186+
psCurve->setNodeRadiusQuantity(q1v);
187+
psCurve->setEdgeRadiusQuantity(q1e, true);
188+
polyscope::show(3);
189+
190+
polyscope::removeAllStructures();
191+
}
192+
127193
TEST_F(PolyscopeTest, CurveNetworkVertexVector) {
128194
auto psCurve = registerCurveNetwork();
129195
std::vector<glm::vec3> vals(psCurve->nNodes(), {1., 2., 3.});

0 commit comments

Comments
 (0)