Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion include/polyscope/curve_network.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,15 @@ class CurveNetwork : public QuantityStructure<CurveNetwork> {
// 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();
Expand Down Expand Up @@ -189,7 +194,8 @@ class CurveNetwork : public QuantityStructure<CurveNetwork> {
void preparePick();

void recomputeGeometryIfPopulated();
float computeRadiusMultiplierUniform();
float computeNodeRadiusMultiplierUniform();
float computeEdgeRadiusMultiplierUniform();

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

// 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
};


Expand Down
137 changes: 116 additions & 21 deletions src/curve_network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,21 +64,48 @@ CurveNetwork::CurveNetwork(std::string name, std::vector<glm::vec3> 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
Expand All @@ -87,15 +114,15 @@ 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) {
glm::mat4 P = view::getCameraPerspectiveMatrix();
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() {
Expand Down Expand Up @@ -175,7 +202,7 @@ void CurveNetwork::drawPick() {
std::vector<std::string> CurveNetwork::addCurveNetworkNodeRules(std::vector<std::string> initRules) {
initRules = addStructureRules(initRules);

if (nodeRadiusQuantityName != "") {
if (nodeRadiusQuantityName != "" || edgeRadiusQuantityName != "") {
initRules.push_back("SPHERE_VARIABLE_SIZE");
}
if (wantsCullPosition()) {
Expand All @@ -187,7 +214,7 @@ std::vector<std::string> CurveNetwork::addCurveNetworkEdgeRules(std::vector<std:
initRules = addStructureRules(initRules);

// use node radius to blend cylinder radius
if (nodeRadiusQuantityName != "") {
if (nodeRadiusQuantityName != "" || edgeRadiusQuantityName != "") {
initRules.push_back("CYLINDER_VARIABLE_SIZE");
}

Expand Down Expand Up @@ -296,17 +323,35 @@ void CurveNetwork::preparePick() {
void CurveNetwork::fillNodeGeometryBuffers(render::ShaderProgram& program) {
program.setAttribute("a_position", nodePositions.getRenderAttributeBuffer());

if (nodeRadiusQuantityName != "") {
bool haveNodeRadiusQuantity = (nodeRadiusQuantityName != "");
bool haveEdgeRadiusQuantity = (edgeRadiusQuantityName != "");

if (haveNodeRadiusQuantity) {
// have just node, or have both
CurveNetworkNodeScalarQuantity& nodeRadQ = resolveNodeRadiusQuantity();
program.setAttribute("a_pointRadius", nodeRadQ.values.getRenderAttributeBuffer());
} else if (haveEdgeRadiusQuantity) {
// have just edge
CurveNetworkEdgeScalarQuantity& edgeRadQ = resolveEdgeRadiusQuantity();
edgeRadQ.updateNodeAverageValues();
program.setAttribute("a_pointRadius", edgeRadQ.nodeAverageValues.getRenderAttributeBuffer());
}
}

void CurveNetwork::fillEdgeGeometryBuffers(render::ShaderProgram& program) {
program.setAttribute("a_position_tail", nodePositions.getIndexedRenderAttributeBuffer(edgeTailInds));
program.setAttribute("a_position_tip", nodePositions.getIndexedRenderAttributeBuffer(edgeTipInds));

if (nodeRadiusQuantityName != "") {
bool haveNodeRadiusQuantity = (nodeRadiusQuantityName != "");
bool haveEdgeRadiusQuantity = (edgeRadiusQuantityName != "");

if (haveEdgeRadiusQuantity) {
// have just edge or have both
CurveNetworkEdgeScalarQuantity& edgeRadQ = resolveEdgeRadiusQuantity();
program.setAttribute("a_tailRadius", edgeRadQ.values.getRenderAttributeBuffer());
program.setAttribute("a_tipRadius", edgeRadQ.values.getRenderAttributeBuffer());
} else if (haveNodeRadiusQuantity) {
// have just node
CurveNetworkNodeScalarQuantity& nodeRadQ = resolveNodeRadiusQuantity();
program.setAttribute("a_tailRadius", nodeRadQ.values.getIndexedRenderAttributeBuffer(edgeTailInds));
program.setAttribute("a_tipRadius", nodeRadQ.values.getIndexedRenderAttributeBuffer(edgeTipInds));
Expand Down Expand Up @@ -422,7 +467,7 @@ void CurveNetwork::buildCustomUI() {

void CurveNetwork::buildCustomOptionsUI() {

if (ImGui::BeginMenu("Variable Radius")) {
if (ImGui::BeginMenu("Node Variable Radius")) {

if (ImGui::MenuItem("none", nullptr, nodeRadiusQuantityName == "")) clearNodeRadiusQuantity();
ImGui::Separator();
Expand All @@ -438,6 +483,21 @@ void CurveNetwork::buildCustomOptionsUI() {
ImGui::EndMenu();
}

if (ImGui::BeginMenu("Edge Variable Radius")) {

if (ImGui::MenuItem("none", nullptr, edgeRadiusQuantityName == "")) clearEdgeRadiusQuantity();
ImGui::Separator();

for (auto& q : quantities) {
CurveNetworkEdgeScalarQuantity* scalarQ = dynamic_cast<CurveNetworkEdgeScalarQuantity*>(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();
Expand Down Expand Up @@ -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<float>(newVal, isRelative);
polyscope::requestRedraw();
Expand Down Expand Up @@ -607,10 +685,27 @@ CurveNetworkNodeScalarQuantity& CurveNetwork::resolveNodeRadiusQuantity() {
if (sizeQ != nullptr) {
sizeScalarQ = dynamic_cast<CurveNetworkNodeScalarQuantity*>(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<CurveNetworkEdgeScalarQuantity*>(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;
Expand Down
3 changes: 3 additions & 0 deletions src/curve_network_scalar_quantity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
68 changes: 67 additions & 1 deletion test/src/curve_network_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<double> eScalar(psCurve->nEdges(), 0.1);
std::vector<double> 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<double> 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<double> 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<glm::vec3> vals(psCurve->nNodes(), {1., 2., 3.});
Expand Down
Loading