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
43 changes: 39 additions & 4 deletions examples/demo-app/demo_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,23 +52,28 @@ void constructDemoCurveNetwork(std::string curveName, std::vector<glm::vec3> nod

{ // Add some node values
std::vector<double> valX(nNodes);
std::vector<double> valNodeCat(nNodes);
std::vector<double> valXabs(nNodes);
std::vector<std::array<double, 3>> randColor(nNodes);
std::vector<glm::vec3> randVec(nNodes);
for (size_t iN = 0; iN < nNodes; iN++) {
valX[iN] = nodes[iN].x;
valNodeCat[iN] = iN * 5 / nNodes;
valXabs[iN] = std::fabs(nodes[iN].x);
randColor[iN] = {{polyscope::randomUnit(), polyscope::randomUnit(), polyscope::randomUnit()}};
randVec[iN] = glm::vec3{polyscope::randomUnit() - .5, polyscope::randomUnit() - .5, polyscope::randomUnit() - .5};
}
polyscope::getCurveNetwork(curveName)->addNodeScalarQuantity("nX", valX);
polyscope::getCurveNetwork(curveName)->addNodeScalarQuantity("nXabs", valXabs);
polyscope::getCurveNetwork(curveName)->addNodeScalarQuantity("node categorical", valNodeCat,
polyscope::DataType::CATEGORICAL);
polyscope::getCurveNetwork(curveName)->addNodeColorQuantity("nColor", randColor);
polyscope::getCurveNetwork(curveName)->addNodeVectorQuantity("randVecN", randVec);
}

{ // Add some edge values
std::vector<double> edgeLen(nEdges);
std::vector<double> valEdgeCat(nEdges);
std::vector<std::array<double, 3>> randColor(nEdges);
std::vector<glm::vec3> randVec(nEdges);
for (size_t iE = 0; iE < nEdges; iE++) {
Expand All @@ -77,10 +82,13 @@ void constructDemoCurveNetwork(std::string curveName, std::vector<glm::vec3> nod
size_t nB = std::get<1>(edge);

edgeLen[iE] = glm::length(nodes[nA] - nodes[nB]);
valEdgeCat[iE] = iE * 5 / nEdges;
randColor[iE] = {{polyscope::randomUnit(), polyscope::randomUnit(), polyscope::randomUnit()}};
randVec[iE] = glm::vec3{polyscope::randomUnit() - .5, polyscope::randomUnit() - .5, polyscope::randomUnit() - .5};
}
polyscope::getCurveNetwork(curveName)->addEdgeScalarQuantity("edge len", edgeLen, polyscope::DataType::MAGNITUDE);
polyscope::getCurveNetwork(curveName)->addEdgeScalarQuantity("edge categorical", valEdgeCat,
polyscope::DataType::CATEGORICAL);
polyscope::getCurveNetwork(curveName)->addEdgeColorQuantity("eColor", randColor);
polyscope::getCurveNetwork(curveName)->addEdgeVectorQuantity("randVecE", randVec);
}
Expand All @@ -104,6 +112,7 @@ void processFileOBJ(std::string filename) {
auto psMesh = polyscope::registerSurfaceMesh(niceName, vertexPositionsGLM, faceIndices);

auto psSimpleMesh = polyscope::registerSimpleTriangleMesh(niceName, vertexPositionsGLM, faceIndices);
psSimpleMesh->setEnabled(false);

// Useful data
size_t nVertices = psMesh->nVertices();
Expand All @@ -114,12 +123,14 @@ void processFileOBJ(std::string filename) {
std::vector<double> valY(nVertices);
std::vector<double> valZ(nVertices);
std::vector<double> valMag(nVertices);
std::vector<double> valCat(nVertices);
std::vector<std::array<double, 3>> randColor(nVertices);
for (size_t iV = 0; iV < nVertices; iV++) {
valX[iV] = vertexPositionsGLM[iV].x / 10000;
valY[iV] = vertexPositionsGLM[iV].y;
valZ[iV] = vertexPositionsGLM[iV].z;
valMag[iV] = glm::length(vertexPositionsGLM[iV]);
valCat[iV] = (int32_t)(iV * 7 / nVertices) - 2;

randColor[iV] = {{polyscope::randomUnit(), polyscope::randomUnit(), polyscope::randomUnit()}};
}
Expand All @@ -129,6 +140,8 @@ void processFileOBJ(std::string filename) {
polyscope::getSurfaceMesh(niceName)->addVertexColorQuantity("vColor", randColor);
polyscope::getSurfaceMesh(niceName)->addVertexScalarQuantity("cY_sym", valY, polyscope::DataType::SYMMETRIC);
polyscope::getSurfaceMesh(niceName)->addVertexScalarQuantity("cNorm", valMag, polyscope::DataType::MAGNITUDE);
polyscope::getSurfaceMesh(niceName)->addVertexScalarQuantity("categorical vert", valCat,
polyscope::DataType::CATEGORICAL);

polyscope::getSurfaceMesh(niceName)->addVertexDistanceQuantity("cY_dist", valY);
polyscope::getSurfaceMesh(niceName)->addVertexSignedDistanceQuantity("cY_signeddist", valY);
Expand All @@ -137,6 +150,7 @@ void processFileOBJ(std::string filename) {
// Add some face scalars
std::vector<double> fArea(nFaces);
std::vector<double> zero(nFaces);
std::vector<double> fCat(nFaces);
std::vector<std::array<double, 3>> fColor(nFaces);
for (size_t iF = 0; iF < nFaces; iF++) {
std::vector<size_t>& face = faceIndices[iF];
Expand All @@ -153,10 +167,13 @@ void processFileOBJ(std::string filename) {

zero[iF] = 0;
fColor[iF] = {{polyscope::randomUnit(), polyscope::randomUnit(), polyscope::randomUnit()}};
fCat[iF] = (int32_t)(iF * 25 / nFaces) - 12;
}
polyscope::getSurfaceMesh(niceName)->addFaceScalarQuantity("face area", fArea, polyscope::DataType::MAGNITUDE);
polyscope::getSurfaceMesh(niceName)->addFaceScalarQuantity("zero", zero);
polyscope::getSurfaceMesh(niceName)->addFaceColorQuantity("fColor", fColor);
polyscope::getSurfaceMesh(niceName)->addFaceScalarQuantity("categorical face", fCat,
polyscope::DataType::CATEGORICAL);


// size_t nEdges = psMesh->nEdges();
Expand All @@ -165,15 +182,19 @@ void processFileOBJ(std::string filename) {
std::vector<double> eLen;
std::vector<double> heLen;
std::vector<double> cAngle;
std::vector<double> cID;
std::vector<double> eCat;
std::vector<double> heCat;
std::vector<double> cCat;
std::unordered_set<std::pair<size_t, size_t>, polyscope::hash_combine::hash<std::pair<size_t, size_t>>> seenEdges;
std::vector<uint32_t> edgeOrdering;
for (size_t iF = 0; iF < nFaces; iF++) {
std::vector<size_t>& face = faceIndices[iF];

for (size_t iV = 0; iV < face.size(); iV++) {
size_t i0 = face[iV];
size_t i1 = face[(iV + 1) % face.size()];
size_t im1 = face[(iV + face.size() - 1) % face.size()];
for (size_t iC = 0; iC < face.size(); iC++) {
size_t i0 = face[iC];
size_t i1 = face[(iC + 1) % face.size()];
size_t im1 = face[(iC + face.size() - 1) % face.size()];
glm::vec3 p0 = vertexPositionsGLM[i0];
glm::vec3 p1 = vertexPositionsGLM[i1];
glm::vec3 pm1 = vertexPositionsGLM[im1];
Expand All @@ -188,18 +209,29 @@ void processFileOBJ(std::string filename) {
auto p = std::make_pair(iMin, iMax);
if (seenEdges.find(p) == seenEdges.end()) {
eLen.push_back(len);
eCat.push_back((iF + iC) % 5);
edgeOrdering.push_back(edgeOrdering.size()); // totally coincidentally, this is the trivial ordering
seenEdges.insert(p);
}
heLen.push_back(len);
cAngle.push_back(angle);
heCat.push_back((iF + iC) % 7);
cCat.push_back(i0 % 12);
cID.push_back(iC);
}
}
size_t nEdges = edgeOrdering.size();
polyscope::getSurfaceMesh(niceName)->setEdgePermutation(edgeOrdering);
polyscope::getSurfaceMesh(niceName)->addEdgeScalarQuantity("edge length", eLen);
polyscope::getSurfaceMesh(niceName)->addHalfedgeScalarQuantity("halfedge length", heLen);
polyscope::getSurfaceMesh(niceName)->addCornerScalarQuantity("corner angle", cAngle);
polyscope::getSurfaceMesh(niceName)->addCornerScalarQuantity("corner ID", cID);
polyscope::getSurfaceMesh(niceName)->addEdgeScalarQuantity("categorical edge", eCat,
polyscope::DataType::CATEGORICAL);
polyscope::getSurfaceMesh(niceName)->addHalfedgeScalarQuantity("categorical halfedge", heCat,
polyscope::DataType::CATEGORICAL);
polyscope::getSurfaceMesh(niceName)->addCornerScalarQuantity("categorical corner", cCat,
polyscope::DataType::CATEGORICAL);


// Test error
Expand Down Expand Up @@ -673,13 +705,16 @@ void addDataToPointCloud(std::string pointCloudName, const std::vector<glm::vec3
// Add some scalar quantities
std::vector<double> xC(points.size());
std::vector<std::array<double, 3>> randColor(points.size());
std::vector<double> cat(points.size());
for (size_t i = 0; i < points.size(); i++) {
xC[i] = points[i].x;
randColor[i] = {{polyscope::randomUnit(), polyscope::randomUnit(), polyscope::randomUnit()}};
cat[i] = i * 12 / points.size();
}
polyscope::getPointCloud(pointCloudName)->addScalarQuantity("xC", xC);
polyscope::getPointCloud(pointCloudName)->addColorQuantity("random color", randColor);
polyscope::getPointCloud(pointCloudName)->addColorQuantity("random color2", randColor);
polyscope::getPointCloud(pointCloudName)->addScalarQuantity("categorical", cat, polyscope::DataType::CATEGORICAL);


// Add some vector quantities
Expand Down
2 changes: 2 additions & 0 deletions include/polyscope/affine_remapper.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ inline std::string defaultColorMap(DataType type) {
return "coolwarm";
case DataType::MAGNITUDE:
return "blues";
case DataType::CATEGORICAL:
return "hsv";
break;
}
return "viridis";
Expand Down
11 changes: 7 additions & 4 deletions include/polyscope/histogram.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@

namespace polyscope {

// A histogram that shows up in ImGUI
// A histogram that shows up in ImGUI window
// ONEDAY: we could definitely make a better histogram widget for categorical data...

class Histogram {
public:
Histogram(); // must call buildHistogram() with data after
Histogram(std::vector<float>& values); // internally calls buildHistogram()
Histogram(); // must call buildHistogram() with data after
Histogram(std::vector<float>& values, DataType datatype); // internally calls buildHistogram()

~Histogram();

void buildHistogram(const std::vector<float>& values);
void buildHistogram(const std::vector<float>& values, DataType datatype);
void updateColormap(const std::string& newColormap);

// Width = -1 means set automatically
Expand All @@ -33,6 +35,7 @@ class Histogram {
void fillBuffers();
size_t rawHistBinCount = 51;

DataType dataType = DataType::STANDARD;
std::vector<float> rawHistCurveY;
std::vector<std::array<float, 2>> rawHistCurveX;
std::pair<double, double> dataRange;
Expand Down
1 change: 1 addition & 0 deletions include/polyscope/render/color_maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ bool buildColormapSelector(std::string& cm, std::string fieldname = "##colormap_
// - rainbow (CM_RAINBOW)
// - jet (CM_JET)
// - turbo (CM_TURBO)
// - hsv (CM_HSV)
//
// Cyclic:
// - phase (CM_PHASE)
Expand Down
1 change: 1 addition & 0 deletions include/polyscope/render/colormap_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ extern const std::vector<glm::vec3> CM_JET;
extern const std::vector<glm::vec3> CM_TURBO;
extern const std::vector<glm::vec3> CM_REDS;
extern const std::vector<glm::vec3> CM_PHASE;
extern const std::vector<glm::vec3> CM_HSV;


} // namespace render
Expand Down
1 change: 1 addition & 0 deletions include/polyscope/render/opengl/shaders/cylinder_shaders.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extern const ShaderStageSpecification FLEX_CYLINDER_FRAG_SHADER;
// Rules specific to cylinders
extern const ShaderReplacementRule CYLINDER_PROPAGATE_VALUE;
extern const ShaderReplacementRule CYLINDER_PROPAGATE_BLEND_VALUE;
extern const ShaderReplacementRule CYLINDER_PROPAGATE_NEAREST_VALUE;
extern const ShaderReplacementRule CYLINDER_PROPAGATE_COLOR;
extern const ShaderReplacementRule CYLINDER_PROPAGATE_BLEND_COLOR;
extern const ShaderReplacementRule CYLINDER_PROPAGATE_PICK;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace backend_openGL3 {
// High level pipeline
extern const ShaderStageSpecification HISTOGRAM_VERT_SHADER;
extern const ShaderStageSpecification HISTOGRAM_FRAG_SHADER;
extern const ShaderStageSpecification HISTOGRAM_CATEGORICAL_FRAG_SHADER;

// Rules
// extern const ShaderReplacementRule RULE_NAME;
Expand Down
1 change: 1 addition & 0 deletions include/polyscope/render/opengl/shaders/rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ extern const ShaderReplacementRule SHADE_BASECOLOR; // constant from
extern const ShaderReplacementRule SHADE_COLOR; // from shadeColor
extern const ShaderReplacementRule SHADECOLOR_FROM_UNIFORM;
extern const ShaderReplacementRule SHADE_COLORMAP_VALUE; // colormapped from shadeValue
extern const ShaderReplacementRule SHADE_CATEGORICAL_COLORMAP; // use ints to sample distinct values from colormap
extern const ShaderReplacementRule SHADE_COLORMAP_ANGULAR2; // colormapped from angle of shadeValue2
extern const ShaderReplacementRule SHADE_GRID_VALUE2; // generate a two-color grid with lines from shadeValue2
extern const ShaderReplacementRule SHADE_CHECKER_VALUE2; // generate a two-color checker from shadeValue2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ extern const ShaderReplacementRule MESH_BACKFACE_DARKEN;
extern const ShaderReplacementRule MESH_PROPAGATE_VALUE;
extern const ShaderReplacementRule MESH_PROPAGATE_VALUEALPHA;
extern const ShaderReplacementRule MESH_PROPAGATE_FLAT_VALUE;
extern const ShaderReplacementRule MESH_PROPAGATE_VALUE_CORNER_NEAREST;
extern const ShaderReplacementRule MESH_PROPAGATE_INT;
extern const ShaderReplacementRule MESH_PROPAGATE_VALUE2;
extern const ShaderReplacementRule MESH_PROPAGATE_TCOORD;
Expand Down
64 changes: 44 additions & 20 deletions include/polyscope/scalar_quantity.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ ScalarQuantity<QuantityT>::ScalarQuantity(QuantityT& quantity_, const std::vecto
{
values.checkInvalidValues();
hist.updateColormap(cMap.get());
hist.buildHistogram(values.data);
hist.buildHistogram(values.data, dataType);
// TODO: I think we might be building the histogram ^^^ twice for many quantities

if (vizRangeMin.holdsDefaultValue()) { // min and max should always have same cache state
// dynamically compute a viz range from the data min/max
Expand Down Expand Up @@ -59,16 +60,23 @@ void ScalarQuantity<QuantityT>::buildScalarUI() {
extraText = "This quantity was added as **magnitude** scalar quantity, so only a "
"single symmetric range control can be adjusted, and it must be positive.";
} break;
case DataType::CATEGORICAL: {
extraText = "This quantity was added as **categorical** scalar quantity, it is "
"interpreted as integer labels, each shaded with a distinct color. "
"Range controls are not used, vminmax are used only to set histogram limits, "
"if provided.";
} break;
}
std::string mainText = "The window below shows the colormap used to visualize this scalar, "
"and a histogram of the the data values. The text boxes below show the "
"range limits for the color map.\n\n";
if (dataType != DataType::CATEGORICAL) {
mainText += "To adjust the limit range for the color map, click-and-drag on the text "
"box. Control-click to type a value, even one outside the visible range.";
}
mainText += extraText;
ImGui::SameLine();
ImGuiHelperMarker(("The window below shows the colormap used to visualize this scalar, "
"and a histogram of the the data values. The text boxes below show the "
"range limits for the color map."
"\n\n"
"To adjust the limit range for the color map, click-and-drag on the text "
"box. Control-click to type a value, even one outside the visible range." +
extraText)
.c_str());
ImGuiHelperMarker(mainText.c_str());


// Draw the histogram of values
Expand All @@ -82,7 +90,7 @@ void ScalarQuantity<QuantityT>::buildScalarUI() {
// valid reasons) links the resolution of the slider to the decimal width of the formatted number. When %g formats a
// number with few decimal places, sliders can break. There is no way to set a minimum number of decimal places with
// %g, unfortunately.
{
if (dataType != DataType::CATEGORICAL) {

float imPad = ImGui::GetStyle().ItemSpacing.x;
ImGui::PushItemWidth((histWidth - imPad) / 2);
Expand All @@ -92,11 +100,11 @@ void ScalarQuantity<QuantityT>::buildScalarUI() {
switch (dataType) {
case DataType::STANDARD: {

changed = changed || ImGui::DragFloat("##min", &vizRangeMin.get(), speed, dataRange.first, vizRangeMax.get(),
"%.5g", ImGuiSliderFlags_NoRoundToFormat);
changed = changed | ImGui::DragFloat("##min", &vizRangeMin.get(), speed, dataRange.first, vizRangeMax.get(),
"%.5g", ImGuiSliderFlags_NoRoundToFormat);
ImGui::SameLine();
changed = changed || ImGui::DragFloat("##max", &vizRangeMax.get(), speed, vizRangeMin.get(), dataRange.second,
"%.5g", ImGuiSliderFlags_NoRoundToFormat);
changed = changed | ImGui::DragFloat("##max", &vizRangeMax.get(), speed, vizRangeMin.get(), dataRange.second,
"%.5g", ImGuiSliderFlags_NoRoundToFormat);

} break;
case DataType::SYMMETRIC: {
Expand All @@ -116,10 +124,13 @@ void ScalarQuantity<QuantityT>::buildScalarUI() {

} break;
case DataType::MAGNITUDE: {
changed = changed || ImGui::DragFloat("##max", &vizRangeMax.get(), speed, 0.f, dataRange.second, "%.5g",
ImGuiSliderFlags_NoRoundToFormat);
changed = changed | ImGui::DragFloat("##max", &vizRangeMax.get(), speed, 0.f, dataRange.second, "%.5g",
ImGuiSliderFlags_NoRoundToFormat);

} break;
case DataType::CATEGORICAL: {
// unused
} break;
}

if (changed) {
Expand Down Expand Up @@ -168,12 +179,19 @@ void ScalarQuantity<QuantityT>::buildScalarUI() {
template <typename QuantityT>
void ScalarQuantity<QuantityT>::buildScalarOptionsUI() {
if (ImGui::MenuItem("Reset colormap range")) resetMapRange();
if (ImGui::MenuItem("Enable isolines", NULL, isolinesEnabled.get())) setIsolinesEnabled(!isolinesEnabled.get());
if (dataType != DataType::CATEGORICAL) {
if (ImGui::MenuItem("Enable isolines", NULL, isolinesEnabled.get())) setIsolinesEnabled(!isolinesEnabled.get());
}
}

template <typename QuantityT>
std::vector<std::string> ScalarQuantity<QuantityT>::addScalarRules(std::vector<std::string> rules) {
rules.push_back("SHADE_COLORMAP_VALUE");
if (dataType == DataType::CATEGORICAL) {
rules.push_back("SHADE_CATEGORICAL_COLORMAP");
} else {
// common case
rules.push_back("SHADE_COLORMAP_VALUE");
}
if (isolinesEnabled.get()) {
rules.push_back("ISOLINE_STRIPE_VALUECOLOR");
}
Expand All @@ -183,8 +201,10 @@ std::vector<std::string> ScalarQuantity<QuantityT>::addScalarRules(std::vector<s

template <typename QuantityT>
void ScalarQuantity<QuantityT>::setScalarUniforms(render::ShaderProgram& p) {
p.setUniform("u_rangeLow", vizRangeMin.get());
p.setUniform("u_rangeHigh", vizRangeMax.get());
if (dataType != DataType::CATEGORICAL) {
p.setUniform("u_rangeLow", vizRangeMin.get());
p.setUniform("u_rangeHigh", vizRangeMax.get());
}

if (isolinesEnabled.get()) {
p.setUniform("u_modLen", getIsolineWidth());
Expand All @@ -196,6 +216,7 @@ template <typename QuantityT>
QuantityT* ScalarQuantity<QuantityT>::resetMapRange() {
switch (dataType) {
case DataType::STANDARD:
case DataType::CATEGORICAL:
vizRangeMin = dataRange.first;
vizRangeMax = dataRange.second;
break;
Expand Down Expand Up @@ -285,6 +306,9 @@ double ScalarQuantity<QuantityT>::getIsolineDarkness() {

template <typename QuantityT>
QuantityT* ScalarQuantity<QuantityT>::setIsolinesEnabled(bool newEnabled) {
if (dataType == DataType::CATEGORICAL) {
newEnabled = false; // no isolines allowed for categorical
}
isolinesEnabled = newEnabled;
quantity.refresh();
requestRedraw();
Expand Down
Loading
Loading