diff --git a/examples/demo-app/demo_app.cpp b/examples/demo-app/demo_app.cpp index 27b826a2..6a483ece 100644 --- a/examples/demo-app/demo_app.cpp +++ b/examples/demo-app/demo_app.cpp @@ -52,23 +52,28 @@ void constructDemoCurveNetwork(std::string curveName, std::vector nod { // Add some node values std::vector valX(nNodes); + std::vector valNodeCat(nNodes); std::vector valXabs(nNodes); std::vector> randColor(nNodes); std::vector 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 edgeLen(nEdges); + std::vector valEdgeCat(nEdges); std::vector> randColor(nEdges); std::vector randVec(nEdges); for (size_t iE = 0; iE < nEdges; iE++) { @@ -77,10 +82,13 @@ void constructDemoCurveNetwork(std::string curveName, std::vector 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); } @@ -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(); @@ -114,12 +123,14 @@ void processFileOBJ(std::string filename) { std::vector valY(nVertices); std::vector valZ(nVertices); std::vector valMag(nVertices); + std::vector valCat(nVertices); std::vector> 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()}}; } @@ -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); @@ -137,6 +150,7 @@ void processFileOBJ(std::string filename) { // Add some face scalars std::vector fArea(nFaces); std::vector zero(nFaces); + std::vector fCat(nFaces); std::vector> fColor(nFaces); for (size_t iF = 0; iF < nFaces; iF++) { std::vector& face = faceIndices[iF]; @@ -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(); @@ -165,15 +182,19 @@ void processFileOBJ(std::string filename) { std::vector eLen; std::vector heLen; std::vector cAngle; + std::vector cID; + std::vector eCat; + std::vector heCat; + std::vector cCat; std::unordered_set, polyscope::hash_combine::hash>> seenEdges; std::vector edgeOrdering; for (size_t iF = 0; iF < nFaces; iF++) { std::vector& 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]; @@ -188,11 +209,15 @@ 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(); @@ -200,6 +225,13 @@ void processFileOBJ(std::string filename) { 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 @@ -673,13 +705,16 @@ void addDataToPointCloud(std::string pointCloudName, const std::vector xC(points.size()); std::vector> randColor(points.size()); + std::vector 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 @@ -844,7 +879,7 @@ int main(int argc, char** argv) { // polyscope::view::windowWidth = 600; // polyscope::view::windowHeight = 800; // polyscope::options::maxFPS = -1; - polyscope::options::verbosity = 100; + polyscope::options::verbosity = 5000; polyscope::options::enableRenderErrorChecks = true; polyscope::options::allowHeadlessBackends = true; diff --git a/include/polyscope/affine_remapper.ipp b/include/polyscope/affine_remapper.ipp index 53677ac4..bf929fb1 100644 --- a/include/polyscope/affine_remapper.ipp +++ b/include/polyscope/affine_remapper.ipp @@ -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"; diff --git a/include/polyscope/histogram.h b/include/polyscope/histogram.h index aa33592e..bf157560 100644 --- a/include/polyscope/histogram.h +++ b/include/polyscope/histogram.h @@ -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& values); // internally calls buildHistogram() + Histogram(); // must call buildHistogram() with data after + Histogram(std::vector& values, DataType datatype); // internally calls buildHistogram() ~Histogram(); - void buildHistogram(const std::vector& values); + void buildHistogram(const std::vector& values, DataType datatype); void updateColormap(const std::string& newColormap); // Width = -1 means set automatically @@ -33,6 +35,7 @@ class Histogram { void fillBuffers(); size_t rawHistBinCount = 51; + DataType dataType = DataType::STANDARD; std::vector rawHistCurveY; std::vector> rawHistCurveX; std::pair dataRange; diff --git a/include/polyscope/render/color_maps.h b/include/polyscope/render/color_maps.h index 6eefe2b1..131f53ce 100644 --- a/include/polyscope/render/color_maps.h +++ b/include/polyscope/render/color_maps.h @@ -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) diff --git a/include/polyscope/render/colormap_defs.h b/include/polyscope/render/colormap_defs.h index 2a56533a..77e494c5 100644 --- a/include/polyscope/render/colormap_defs.h +++ b/include/polyscope/render/colormap_defs.h @@ -21,6 +21,7 @@ extern const std::vector CM_JET; extern const std::vector CM_TURBO; extern const std::vector CM_REDS; extern const std::vector CM_PHASE; +extern const std::vector CM_HSV; } // namespace render diff --git a/include/polyscope/render/opengl/shaders/cylinder_shaders.h b/include/polyscope/render/opengl/shaders/cylinder_shaders.h index ce63d638..95acf544 100644 --- a/include/polyscope/render/opengl/shaders/cylinder_shaders.h +++ b/include/polyscope/render/opengl/shaders/cylinder_shaders.h @@ -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; diff --git a/include/polyscope/render/opengl/shaders/histogram_shaders.h b/include/polyscope/render/opengl/shaders/histogram_shaders.h index 9f5403e5..a2397571 100644 --- a/include/polyscope/render/opengl/shaders/histogram_shaders.h +++ b/include/polyscope/render/opengl/shaders/histogram_shaders.h @@ -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; diff --git a/include/polyscope/render/opengl/shaders/rules.h b/include/polyscope/render/opengl/shaders/rules.h index 1e24766d..ac8ec018 100644 --- a/include/polyscope/render/opengl/shaders/rules.h +++ b/include/polyscope/render/opengl/shaders/rules.h @@ -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 diff --git a/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h b/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h index 81c61ac4..d89d3433 100644 --- a/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h +++ b/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h @@ -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; diff --git a/include/polyscope/scalar_quantity.ipp b/include/polyscope/scalar_quantity.ipp index 374e7064..a7eea09b 100644 --- a/include/polyscope/scalar_quantity.ipp +++ b/include/polyscope/scalar_quantity.ipp @@ -21,7 +21,8 @@ ScalarQuantity::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 @@ -59,16 +60,23 @@ void ScalarQuantity::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 @@ -82,7 +90,7 @@ void ScalarQuantity::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); @@ -92,11 +100,11 @@ void ScalarQuantity::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: { @@ -116,10 +124,13 @@ void ScalarQuantity::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) { @@ -168,12 +179,19 @@ void ScalarQuantity::buildScalarUI() { template void ScalarQuantity::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 std::vector ScalarQuantity::addScalarRules(std::vector 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"); } @@ -183,8 +201,10 @@ std::vector ScalarQuantity::addScalarRules(std::vector void ScalarQuantity::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()); @@ -196,6 +216,7 @@ template QuantityT* ScalarQuantity::resetMapRange() { switch (dataType) { case DataType::STANDARD: + case DataType::CATEGORICAL: vizRangeMin = dataRange.first; vizRangeMax = dataRange.second; break; @@ -285,6 +306,9 @@ double ScalarQuantity::getIsolineDarkness() { template QuantityT* ScalarQuantity::setIsolinesEnabled(bool newEnabled) { + if (dataType == DataType::CATEGORICAL) { + newEnabled = false; // no isolines allowed for categorical + } isolinesEnabled = newEnabled; quantity.refresh(); requestRedraw(); diff --git a/include/polyscope/surface_mesh.h b/include/polyscope/surface_mesh.h index 0d752913..fbb19ab7 100644 --- a/include/polyscope/surface_mesh.h +++ b/include/polyscope/surface_mesh.h @@ -102,6 +102,7 @@ class SurfaceMesh : public QuantityStructure { render::ManagedBuffer triangleFaceInds; // on triangulated mesh [3 * nTriFace] render::ManagedBuffer triangleCornerInds; // on triangulated mesh [3 * nTriFace] // these next 3 use the ***perm if it has been set + render::ManagedBuffer triangleAllVertexInds; // on triangulated mesh, all 3 [3 * 3 * nTriFace] render::ManagedBuffer triangleAllEdgeInds; // on triangulated mesh, all 3 [3 * 3 * nTriFace] render::ManagedBuffer triangleAllHalfedgeInds; // on triangulated mesh, all 3 [3 * 3 * nTriFace] render::ManagedBuffer triangleAllCornerInds; // on triangulated mesh, all 3 [3 * 3 * nTriFace] @@ -315,6 +316,7 @@ class SurfaceMesh : public QuantityStructure { std::vector triangleVertexIndsData; // index of the corresponding vertex std::vector triangleFaceIndsData; // index of the corresponding original face std::vector triangleCornerIndsData; // index of the corresponding original corner + std::vector triangleAllVertexIndsData; // index of the corresponding original vertex std::vector triangleAllEdgeIndsData; // index of the corresponding original edge std::vector triangleAllHalfedgeIndsData; // index of the corresponding original halfedge std::vector triangleAllCornerIndsData; // index of the corresponding original corner @@ -360,6 +362,7 @@ class SurfaceMesh : public QuantityStructure { /// == Compute indices & geometry data void computeTriangleCornerInds(); + void computeTriangleAllVertexInds(); void computeTriangleAllEdgeInds(); void computeTriangleAllHalfedgeInds(); void computeTriangleAllCornerInds(); diff --git a/include/polyscope/types.h b/include/polyscope/types.h index 124a5afb..13717d93 100644 --- a/include/polyscope/types.h +++ b/include/polyscope/types.h @@ -57,7 +57,8 @@ enum class ManagedBufferType { // STANDARD: [-inf, inf], zero does not mean anything special (ie, position) // SYMMETRIC: [-inf, inf], zero is special (ie, net profit/loss) // MAGNITUDE: [0, inf], zero is special (ie, length of a vector) -enum class DataType { STANDARD = 0, SYMMETRIC, MAGNITUDE }; +// CATEGORICAL: data is integers corresponding to labels, etc +enum class DataType { STANDARD = 0, SYMMETRIC, MAGNITUDE, CATEGORICAL }; }; // namespace polyscope diff --git a/src/curve_network_scalar_quantity.cpp b/src/curve_network_scalar_quantity.cpp index e346debf..e26aa2eb 100644 --- a/src/curve_network_scalar_quantity.cpp +++ b/src/curve_network_scalar_quantity.cpp @@ -91,7 +91,7 @@ void CurveNetworkNodeScalarQuantity::createProgram() { render::engine->addMaterialRules(parent.getMaterial(), addScalarRules( parent.addCurveNetworkEdgeRules( - {"CYLINDER_PROPAGATE_BLEND_VALUE"} + {dataType == DataType::CATEGORICAL ? "CYLINDER_PROPAGATE_NEAREST_VALUE" : "CYLINDER_PROPAGATE_BLEND_VALUE"} ) ) ) @@ -184,18 +184,57 @@ void CurveNetworkEdgeScalarQuantity::updateNodeAverageValues() { values.ensureHostBufferPopulated(); nodeAverageValues.data.resize(parent.nNodes()); - for (size_t iE = 0; iE < parent.nEdges(); iE++) { - size_t eTail = parent.edgeTailInds.data[iE]; - size_t eTip = parent.edgeTipInds.data[iE]; + if (dataType == DataType::CATEGORICAL) { + // uncommon case: take the mode of adjacent values + + // count how many times each value occurs incident on the node + std::vector> valueCounts(parent.nNodes()); + auto incrementValueCount = [&](size_t ind, float val) { + std::unordered_map& map = valueCounts[ind]; + if (map.find(val) == map.end()) { + map[val] = 1; + } else { + map[val] += 1; + } + }; + + for (size_t iE = 0; iE < parent.nEdges(); iE++) { + size_t eTail = parent.edgeTailInds.data[iE]; + size_t eTip = parent.edgeTipInds.data[iE]; + + incrementValueCount(eTail, values.data[iE]); + incrementValueCount(eTip, values.data[iE]); + } - nodeAverageValues.data[eTail] += values.data[iE]; - nodeAverageValues.data[eTip] += values.data[iE]; - } + for (size_t iN = 0; iN < parent.nNodes(); iN++) { + // find the value which occured most often in the counts + int32_t maxCount = 0; + float maxVal = 0.; + for (const auto& entry : valueCounts[iN]) { + if (entry.second > maxCount) { + maxCount = entry.second; + maxVal = entry.first; + } + } + nodeAverageValues.data[iN] = maxVal; + } + } else { + // common case: take the mean of adjacent values + + // mean reduction + for (size_t iE = 0; iE < parent.nEdges(); iE++) { + size_t eTail = parent.edgeTailInds.data[iE]; + size_t eTip = parent.edgeTipInds.data[iE]; + + nodeAverageValues.data[eTail] += values.data[iE]; + nodeAverageValues.data[eTip] += values.data[iE]; + } - for (size_t iN = 0; iN < parent.nNodes(); iN++) { - nodeAverageValues.data[iN] /= parent.nodeDegrees[iN]; - if (parent.nodeDegrees[iN] == 0) { - nodeAverageValues.data[iN] = 0.; + for (size_t iN = 0; iN < parent.nNodes(); iN++) { + nodeAverageValues.data[iN] /= parent.nodeDegrees[iN]; + if (parent.nodeDegrees[iN] == 0) { + nodeAverageValues.data[iN] = 0.; + } } } diff --git a/src/histogram.cpp b/src/histogram.cpp index d3870773..b8d898ef 100644 --- a/src/histogram.cpp +++ b/src/histogram.cpp @@ -15,11 +15,12 @@ namespace polyscope { Histogram::Histogram() {} -Histogram::Histogram(std::vector& values) { buildHistogram(values); } +Histogram::Histogram(std::vector& values, DataType dataType) { buildHistogram(values, dataType); } Histogram::~Histogram() {} -void Histogram::buildHistogram(const std::vector& values) { +void Histogram::buildHistogram(const std::vector& values, DataType dataType_) { + dataType = dataType_; // Build arrays of values size_t N = values.size(); @@ -138,7 +139,15 @@ void Histogram::prepare() { framebuffer->addColorBuffer(texture); // Create the program - program = render::engine->requestShader("HISTOGRAM", {}, render::ShaderReplacementDefaults::Process); + if (dataType == DataType::CATEGORICAL) { + // for categorical data only + program = render::engine->requestShader("HISTOGRAM_CATEGORICAL", {"SHADE_CATEGORICAL_COLORMAP"}, + render::ShaderReplacementDefaults::Process); + } else { + // common case + program = render::engine->requestShader("HISTOGRAM", {"SHADE_COLORMAP_VALUE"}, + render::ShaderReplacementDefaults::Process); + } program->setTextureFromColormap("t_colormap", colormap, true); @@ -160,10 +169,18 @@ void Histogram::renderToTexture() { // = Set uniforms - // Colormap range (remapped to the 0-1 coords we use) - program->setUniform("u_cmapRangeMin", (colormapRange.first - dataRange.first) / (dataRange.second - dataRange.first)); - program->setUniform("u_cmapRangeMax", - (colormapRange.second - dataRange.first) / (dataRange.second - dataRange.first)); + if (dataType == DataType::CATEGORICAL) { + // Used to restore [0,1] tvals to the orininal data range for the categorical int remapping + program->setUniform("u_dataRangeLow", dataRange.first); + program->setUniform("u_dataRangeHigh", dataRange.second); + } else { + // Colormap range (remapped to the 0-1 coords we use) + float rangeLow = (colormapRange.first - dataRange.first) / (dataRange.second - dataRange.first); + float rangeHigh = (colormapRange.second - dataRange.first) / (dataRange.second - dataRange.first); + program->setUniform("u_rangeLow", rangeLow); + program->setUniform("u_rangeHigh", rangeHigh); + } + // Draw program->draw(); diff --git a/src/render/color_maps.cpp b/src/render/color_maps.cpp index e49da344..5e8f21c8 100644 --- a/src/render/color_maps.cpp +++ b/src/render/color_maps.cpp @@ -71,6 +71,9 @@ const std::vector CM_REDS = const std::vector CM_PHASE = {{0.658308392892,0.469939169032,0.0494128820399},{0.658308392892,0.469939169032,0.0494128820399},{0.664337418937,0.466201900857,0.057664734504},{0.664337418937,0.466201900857,0.057664734504},{0.670208692505,0.462480138122,0.0653456030954},{0.670208692505,0.462480138122,0.0653456030954},{0.676042990533,0.458698375996,0.0727317432221},{0.676042990533,0.458698375996,0.0727317432221},{0.681752284628,0.454914065184,0.0797926195668},{0.681752284628,0.454914065184,0.0797926195668},{0.687402804728,0.451084166991,0.0866710295087},{0.687402804728,0.451084166991,0.0866710295087},{0.692950498095,0.447238929651,0.0933586896015},{0.692950498095,0.447238929651,0.0933586896015},{0.698426191209,0.443357678593,0.0999283926833},{0.698426191209,0.443357678593,0.0999283926833},{0.703812298104,0.439453276235,0.106387104564},{0.703812298104,0.439453276235,0.106387104564},{0.709120692319,0.435517652511,0.112771744925},{0.709120692319,0.435517652511,0.112771744925},{0.714345244901,0.431555756203,0.119093478945},{0.714345244901,0.431555756203,0.119093478945},{0.719492886167,0.427562717169,0.125376059096},{0.719492886167,0.427562717169,0.125376059096},{0.724556192748,0.423544697127,0.13162325163},{0.724556192748,0.423544697127,0.13162325163},{0.72954894829,0.419490984923,0.137863048349},{0.72954894829,0.419490984923,0.137863048349},{0.734451724225,0.41541774051,0.144080393372},{0.734451724225,0.41541774051,0.144080393372},{0.739294955064,0.411299732702,0.150322173697},{0.739294955064,0.411299732702,0.150322173697},{0.744038335124,0.407171577287,0.156543346185},{0.744038335124,0.407171577287,0.156543346185},{0.748736952369,0.402985190782,0.16282281613},{0.748736952369,0.402985190782,0.16282281613},{0.753323193752,0.39880106902,0.169075663806},{0.753323193752,0.39880106902,0.169075663806},{0.757880829458,0.394542451134,0.175421792407},{0.762332602216,0.39028095672,0.181759153853},{0.762332602216,0.39028095672,0.181759153853},{0.7667320479,0.385965494354,0.188168187504},{0.7667320479,0.385965494354,0.188168187504},{0.771052472182,0.381621414822,0.194615319358},{0.771052472182,0.381621414822,0.194615319358},{0.775295277846,0.377247324652,0.201106520895},{0.775295277846,0.377247324652,0.201106520895},{0.779486659378,0.37281508501,0.207687295365},{0.779486659378,0.37281508501,0.207687295365},{0.783585336262,0.368367724773,0.214297364199},{0.783585336262,0.368367724773,0.214297364199},{0.787637631373,0.363853995029,0.221016475748},{0.787637631373,0.363853995029,0.221016475748},{0.79161133858,0.359308040423,0.22779740121},{0.79161133858,0.359308040423,0.22779740121},{0.795506055251,0.354729897918,0.234643526025},{0.795506055251,0.354729897918,0.234643526025},{0.799353983813,0.350079592948,0.241618319348},{0.799353983813,0.350079592948,0.241618319348},{0.803116708478,0.345401516392,0.248658916218},{0.803116708478,0.345401516392,0.248658916218},{0.806810326186,0.34067452251,0.25580075146},{0.806810326186,0.34067452251,0.25580075146},{0.8104452043,0.335882484131,0.263072216807},{0.8104452043,0.335882484131,0.263072216807},{0.813996801063,0.331055383196,0.270431826071},{0.813996801063,0.331055383196,0.270431826071},{0.817476894782,0.326175263767,0.277910961902},{0.817476894782,0.326175263767,0.277910961902},{0.820894148194,0.321226288958,0.285538460449},{0.820894148194,0.321226288958,0.285538460449},{0.824227126161,0.316236198585,0.293276167778},{0.824227126161,0.316236198585,0.293276167778},{0.827476613752,0.311201543345,0.301133878848},{0.827476613752,0.311201543345,0.301133878848},{0.830663985673,0.30608459405,0.309175785183},{0.830663985673,0.30608459405,0.309175785183},{0.833763065889,0.300922435954,0.317349208622},{0.836772858757,0.295713461259,0.325661993753},{0.836772858757,0.295713461259,0.325661993753},{0.839696930167,0.290447232868,0.334136649771},{0.839696930167,0.290447232868,0.334136649771},{0.842538733432,0.285111507699,0.342799619335},{0.842538733432,0.285111507699,0.342799619335},{0.845282969306,0.279729165834,0.351620780974},{0.845282969306,0.279729165834,0.351620780974},{0.847927041458,0.274300451817,0.360606806181},{0.847927041458,0.274300451817,0.360606806181},{0.850467925711,0.268826235069,0.369763947699},{0.850467925711,0.268826235069,0.369763947699},{0.852910557451,0.263288587404,0.379131161153},{0.852910557451,0.263288587404,0.379131161153},{0.855242004405,0.257708883964,0.388682169609},{0.855242004405,0.257708883964,0.388682169609},{0.857456726321,0.252093665886,0.398416010867},{0.857456726321,0.252093665886,0.398416010867},{0.859550232072,0.246447370855,0.408336253344},{0.859550232072,0.246447370855,0.408336253344},{0.861517669567,0.240775634951,0.418445571241},{0.861517669567,0.240775634951,0.418445571241},{0.863353924562,0.235085213863,0.428746057324},{0.863353924562,0.235085213863,0.428746057324},{0.865056845219,0.229372876811,0.43926007811},{0.865056845219,0.229372876811,0.43926007811},{0.866616063146,0.223663075912,0.449961266039},{0.866616063146,0.223663075912,0.449961266039},{0.86802577872,0.217967852481,0.46084758006},{0.86802577872,0.217967852481,0.46084758006},{0.86928002814,0.212301322732,0.471915544486},{0.86928002814,0.212301322732,0.471915544486},{0.870372741448,0.206679880751,0.483160154069},{0.870372741448,0.206679880751,0.483160154069},{0.871297806808,0.201122398412,0.49457479396},{0.871297806808,0.201122398412,0.49457479396},{0.872049140265,0.195650412874,0.506151179086},{0.872049140265,0.195650412874,0.506151179086},{0.87262075977,0.190288288901,0.517879317254},{0.873006861953,0.185063339211,0.529747499865},{0.873006861953,0.185063339211,0.529747499865},{0.873201899807,0.180005881391,0.541742323374},{0.873201899807,0.180005881391,0.541742323374},{0.87320065922,0.175149204974,0.553848743653},{0.87320065922,0.175149204974,0.553848743653},{0.872998332157,0.170529417732,0.566050164184},{0.872998332157,0.170529417732,0.566050164184},{0.872590584303,0.166185137096,0.578328557682},{0.872590584303,0.166185137096,0.578328557682},{0.871973612653,0.16215697962,0.590664660687},{0.871973612653,0.16215697962,0.590664660687},{0.87114414307,0.158486668076,0.603038808554},{0.87114414307,0.158486668076,0.603038808554},{0.870099660691,0.155216866248,0.615428437863},{0.870099660691,0.155216866248,0.615428437863},{0.868838231517,0.152388922857,0.627811754858},{0.868838231517,0.152388922857,0.627811754858},{0.867358580349,0.150041987043,0.640166505942},{0.867358580349,0.150041987043,0.640166505942},{0.865660095361,0.148211493519,0.65247021711},{0.865660095361,0.148211493519,0.65247021711},{0.863742820371,0.146927620733,0.664700433402},{0.863742820371,0.146927620733,0.664700433402},{0.861607435585,0.14621385657,0.676834951829},{0.861607435585,0.14621385657,0.676834951829},{0.85925522796,0.146085818174,0.688852041748},{0.85925522796,0.146085818174,0.688852041748},{0.856688052659,0.146550460138,0.700730647495},{0.856688052659,0.146550460138,0.700730647495},{0.853908294081,0.147605763482,0.712450541666},{0.853908294081,0.147605763482,0.712450541666},{0.850918813243,0.149240943921,0.723992497409},{0.850918813243,0.149240943921,0.723992497409},{0.847722873216,0.151437167202,0.735338489692},{0.847722873216,0.151437167202,0.735338489692},{0.844324093567,0.154168650668,0.746471739322},{0.840726393963,0.157404031455,0.757376782904},{0.840726393963,0.157404031455,0.757376782904},{0.836933938672,0.161107855552,0.76803951842},{0.836933938672,0.161107855552,0.76803951842},{0.832951083227,0.165242052784,0.778447227341},{0.832951083227,0.165242052784,0.778447227341},{0.828782324029,0.169767291005,0.788588575589},{0.828782324029,0.169767291005,0.788588575589},{0.824432251451,0.174644139132,0.798453595936},{0.824432251451,0.174644139132,0.798453595936},{0.819905506793,0.179834004693,0.808033654553},{0.819905506793,0.179834004693,0.808033654553},{0.81520674324,0.185299841441,0.817321404386},{0.81520674324,0.185299841441,0.817321404386},{0.810340590837,0.191006643757,0.826310727942},{0.810340590837,0.191006643757,0.826310727942},{0.805311757905,0.196921601061,0.834996450489},{0.805311757905,0.196921601061,0.834996450489},{0.800124669532,0.20301465242,0.843374862107},{0.800124669532,0.20301465242,0.843374862107},{0.794783673009,0.209258261464,0.851443195646},{0.794783673009,0.209258261464,0.851443195646},{0.789293018039,0.215627373716,0.859199565535},{0.789293018039,0.215627373716,0.859199565535},{0.783656806972,0.222099355044,0.866642944436},{0.783656806972,0.222099355044,0.866642944436},{0.777878979612,0.228653855109,0.87377308288},{0.777878979612,0.228653855109,0.87377308288},{0.771963300247,0.235272649647,0.880590429922},{0.771963300247,0.235272649647,0.880590429922},{0.765913346604,0.241939473532,0.887096055527},{0.765913346604,0.241939473532,0.887096055527},{0.759732500465,0.248639853039,0.893291575193},{0.759732500465,0.248639853039,0.893291575193},{0.753423939683,0.255360942964,0.899179077196},{0.753423939683,0.255360942964,0.899179077196},{0.74699063142,0.262091372137,0.904761052687},{0.74699063142,0.262091372137,0.904761052687},{0.740435326435,0.268821099342,0.910040328779},{0.733760554319,0.275541280537,0.915020004719},{0.733760554319,0.275541280537,0.915020004719},{0.726968619585,0.282244147513,0.919703391141},{0.726968619585,0.282244147513,0.919703391141},{0.720061598583,0.288922897644,0.924093952388},{0.720061598583,0.288922897644,0.924093952388},{0.713041337231,0.295571594063,0.92819525188},{0.713041337231,0.295571594063,0.92819525188},{0.705909449591,0.302185075438,0.932010900454},{0.705909449591,0.302185075438,0.932010900454},{0.698667317349,0.308758874406,0.935544507658},{0.698667317349,0.308758874406,0.935544507658},{0.691316090279,0.315289143767,0.938799635947},{0.691316090279,0.315289143767,0.938799635947},{0.683856687822,0.321772589498,0.941779757756},{0.683856687822,0.321772589498,0.941779757756},{0.676289801898,0.328206409748,0.944488215474},{0.676289801898,0.328206409748,0.944488215474},{0.66861590113,0.334588239008,0.946928184318},{0.66861590113,0.334588239008,0.946928184318},{0.660835236665,0.340916096734,0.949102638181},{0.660835236665,0.340916096734,0.949102638181},{0.652947849787,0.347188339785,0.951014318531},{0.652947849787,0.347188339785,0.951014318531},{0.644953581573,0.35340361808,0.952665706495},{0.644953581573,0.35340361808,0.952665706495},{0.636852084815,0.359560832988,0.954058998276},{0.636852084815,0.359560832988,0.954058998276},{0.628642838505,0.365659098003,0.955196084109},{0.628642838505,0.365659098003,0.955196084109},{0.620325165144,0.371697701338,0.956078530991},{0.620325165144,0.371697701338,0.956078530991},{0.611898251184,0.377676070126,0.956707569474},{0.611898251184,0.377676070126,0.956707569474},{0.603361170918,0.38359373599,0.957084084833},{0.603361170918,0.38359373599,0.957084084833},{0.594712914127,0.389450301774,0.957208612975},{0.594712914127,0.389450301774,0.957208612975},{0.585952417808,0.395245409322,0.957081341492},{0.577078602298,0.400978708233,0.956702116283},{0.577078602298,0.400978708233,0.956702116283},{0.568090412076,0.406649825569,0.956070454204},{0.568090412076,0.406649825569,0.956070454204},{0.55898686152,0.412258336579,0.955185562223},{0.55898686152,0.412258336579,0.955185562223},{0.549767085846,0.417803736546,0.954046363531},{0.549767085846,0.417803736546,0.954046363531},{0.540430397369,0.42328541394,0.952651531091},{0.540430397369,0.42328541394,0.952651531091},{0.530976347181,0.428702625127,0.950999529007},{0.530976347181,0.428702625127,0.950999529007},{0.521404792215,0.43405447093,0.94908866207},{0.521404792215,0.43405447093,0.94908866207},{0.511715967538,0.439339875449,0.94691713371},{0.511715967538,0.439339875449,0.94691713371},{0.501910563544,0.444557567548,0.944483112445},{0.501910563544,0.444557567548,0.944483112445},{0.491989807571,0.449706065529,0.941784806747},{0.491989807571,0.449706065529,0.941784806747},{0.481955549211,0.454783665516,0.938820547987},{0.481955549211,0.454783665516,0.938820547987},{0.471810348375,0.45978843412,0.93558888087},{0.471810348375,0.45978843412,0.93558888087},{0.461557564917,0.464718205967,0.932088660443},{0.461557564917,0.464718205967,0.932088660443},{0.451198008225,0.469572177684,0.92831786369},{0.451198008225,0.469572177684,0.92831786369},{0.440738524632,0.47434688193,0.924276690668},{0.440738524632,0.47434688193,0.924276690668},{0.430187219196,0.479038640683,0.919966197002},{0.430187219196,0.479038640683,0.919966197002},{0.419551659042,0.483644396275,0.91538759064},{0.419551659042,0.483644396275,0.91538759064},{0.408840633101,0.488160938611,0.910542931565},{0.408840633101,0.488160938611,0.910542931565},{0.398064208302,0.492584942395,0.905435227216},{0.387233771755,0.496913010871,0.900068517037},{0.387233771755,0.496913010871,0.900068517037},{0.376362056499,0.501141725475,0.894447942754},{0.376362056499,0.501141725475,0.894447942754},{0.365461266376,0.505268403433,0.888578767472},{0.365461266376,0.505268403433,0.888578767472},{0.354546543979,0.509289796888,0.882468192304},{0.354546543979,0.509289796888,0.882468192304},{0.343637793185,0.513201579589,0.876126636093},{0.343637793185,0.513201579589,0.876126636093},{0.332753093914,0.51700081982,0.869564086482},{0.332753093914,0.51700081982,0.869564086482},{0.321911658393,0.520684873358,0.862791658562},{0.321911658393,0.520684873358,0.862791658562},{0.311133719862,0.524251441443,0.855821520667},{0.311133719862,0.524251441443,0.855821520667},{0.300440398609,0.527698623816,0.848666794877},{0.300440398609,0.527698623816,0.848666794877},{0.289853256631,0.531025054992,0.841341230552},{0.289853256631,0.531025054992,0.841341230552},{0.279396159424,0.534229314818,0.833860509465},{0.279396159424,0.534229314818,0.833860509465},{0.269091811976,0.537310987157,0.82623984187},{0.269091811976,0.537310987157,0.82623984187},{0.258963000993,0.540270201043,0.81849474753},{0.258963000993,0.540270201043,0.81849474753},{0.249032393162,0.543107629026,0.810640895215},{0.249032393162,0.543107629026,0.810640895215},{0.239322289958,0.54582448394,0.80269392237},{0.239322289958,0.54582448394,0.80269392237},{0.229856635182,0.548421889,0.794671215898},{0.229856635182,0.548421889,0.794671215898},{0.220655100294,0.550902414196,0.786587062692},{0.220655100294,0.550902414196,0.786587062692},{0.211736409442,0.553269012911,0.778455329698},{0.211736409442,0.553269012911,0.778455329698},{0.203118430649,0.555524891192,0.770289734711},{0.203118430649,0.555524891192,0.770289734711},{0.194817201504,0.557673646703,0.76210317763},{0.186846596629,0.559719222965,0.753907633934},{0.186846596629,0.559719222965,0.753907633934},{0.179217988478,0.561665861309,0.745714066205},{0.179217988478,0.561665861309,0.745714066205},{0.171942195902,0.56351747024,0.737534984317},{0.171942195902,0.56351747024,0.737534984317},{0.165022951267,0.565279153364,0.729377543987},{0.165022951267,0.565279153364,0.729377543987},{0.158461160281,0.56695600177,0.721248188626},{0.158461160281,0.56695600177,0.721248188626},{0.152254991821,0.568552967252,0.713153205188},{0.152254991821,0.568552967252,0.713153205188},{0.146398761868,0.570075056921,0.705097686656},{0.146398761868,0.570075056921,0.705097686656},{0.140882840591,0.571527291452,0.697085535335},{0.140882840591,0.571527291452,0.697085535335},{0.135693662537,0.572914665847,0.689119475827},{0.135693662537,0.572914665847,0.689119475827},{0.130813852542,0.574242112738,0.681201076313},{0.130813852542,0.574242112738,0.681201076313},{0.126222473429,0.575514468206,0.673330776933},{0.126222473429,0.575514468206,0.673330776933},{0.121895393804,0.576736439983,0.665507924233},{0.121895393804,0.576736439983,0.665507924233},{0.117806543972,0.577912352304,0.657732326014},{0.117806543972,0.577912352304,0.657732326014},{0.113926129801,0.57904679578,0.649999836645},{0.113926129801,0.57904679578,0.649999836645},{0.110223478239,0.580143980837,0.642306368114},{0.110223478239,0.580143980837,0.642306368114},{0.106667323518,0.581207821548,0.634647331489},{0.106667323518,0.581207821548,0.634647331489},{0.103226306764,0.582241975777,0.627017287995},{0.103226306764,0.582241975777,0.627017287995},{0.0998697011666,0.583249824863,0.619410007498},{0.0998697011666,0.583249824863,0.619410007498},{0.0965681326987,0.584234454901,0.611818530585},{0.0965681326987,0.584234454901,0.611818530585},{0.0932942920612,0.585198639665,0.604235234177},{0.0900236427693,0.586144825264,0.596651900546},{0.0900236427693,0.586144825264,0.596651900546},{0.0867351401365,0.587075116665,0.589059789486},{0.0867351401365,0.587075116665,0.589059789486},{0.0834119859822,0.587991266222,0.581449713302},{0.0834119859822,0.587991266222,0.581449713302},{0.0800424540035,0.588894664373,0.573812114106},{0.0800424540035,0.588894664373,0.573812114106},{0.0766208306059,0.589786332627,0.566137142784},{0.0766208306059,0.589786332627,0.566137142784},{0.073148524854,0.590666918973,0.558414738841},{0.073148524854,0.590666918973,0.558414738841},{0.0696354071465,0.591536695753,0.55063471023},{0.0696354071465,0.591536695753,0.55063471023},{0.0661014350115,0.592395560023,0.542786812151},{0.0661014350115,0.592395560023,0.542786812151},{0.0625786076009,0.593243036315,0.534860823801},{0.0625786076009,0.593243036315,0.534860823801},{0.0591130375998,0.594078331469,0.526846137602},{0.0591130375998,0.594078331469,0.526846137602},{0.0557676501029,0.5949002975,0.518732197987},{0.0557676501029,0.5949002975,0.518732197987},{0.0526251056542,0.595707320083,0.510509780826},{0.0526251056542,0.595707320083,0.510509780826},{0.0497888068094,0.596497503811,0.502169359532},{0.0497888068094,0.596497503811,0.502169359532},{0.0473831926939,0.597268621556,0.493701736171},{0.0473831926939,0.597268621556,0.493701736171},{0.0455506684766,0.598018125295,0.485098086772},{0.0455506684766,0.598018125295,0.485098086772},{0.044443961893,0.59874315667,0.476350000404},{0.044443961893,0.59874315667,0.476350000404},{0.044213229489,0.599440556688,0.46744951244},{0.044213229489,0.599440556688,0.46744951244},{0.0449891786771,0.600106874013,0.458389132783},{0.0449891786771,0.600106874013,0.458389132783},{0.0468660448507,0.600738371276,0.449161870185},{0.0498897902424,0.601331028898,0.439761254173},{0.0498897902424,0.601331028898,0.439761254173},{0.0540557300631,0.601880554394,0.430181201574},{0.0540557300631,0.601880554394,0.430181201574},{0.0593220854021,0.602382887631,0.420405432562},{0.0593220854021,0.602382887631,0.420405432562},{0.0656077388042,0.602832583137,0.410437715603},{0.0656077388042,0.602832583137,0.410437715603},{0.0728196236389,0.603224415597,0.400273626235},{0.0728196236389,0.603224415597,0.400273626235},{0.0808617678135,0.603552833505,0.389909414935},{0.0808617678135,0.603552833505,0.389909414935},{0.0896436576776,0.603811940421,0.379342077786},{0.0896436576776,0.603811940421,0.379342077786},{0.0990895248698,0.603995543502,0.368564118402},{0.0990895248698,0.603995543502,0.368564118402},{0.109146168639,0.604096946088,0.357557987688},{0.109146168639,0.604096946088,0.357557987688},{0.119741186824,0.604108584409,0.34634096055},{0.119741186824,0.604108584409,0.34634096055},{0.130827463571,0.604022803635,0.334914160517},{0.130827463571,0.604022803635,0.334914160517},{0.14238003476,0.60383119145,0.323266999746},{0.14238003476,0.60383119145,0.323266999746},{0.154384700863,0.603524253284,0.311388229549},{0.154384700863,0.603524253284,0.311388229549},{0.166790927844,0.603093006307,0.299310287856},{0.166790927844,0.603093006307,0.299310287856},{0.179597574193,0.602526680057,0.287023704448},{0.179597574193,0.602526680057,0.287023704448},{0.192799657016,0.601813638169,0.274529642485},{0.192799657016,0.601813638169,0.274529642485},{0.206344646186,0.600944661405,0.261879400204},{0.206344646186,0.600944661405,0.261879400204},{0.220272872973,0.599904299374,0.249042513665},{0.220272872973,0.599904299374,0.249042513665},{0.234498332925,0.598685911475,0.236110215698},{0.234498332925,0.598685911475,0.236110215698},{0.249044157731,0.597274598402,0.223077804617},{0.263820058885,0.595666560172,0.210046733237},{0.263820058885,0.595666560172,0.210046733237},{0.278810397244,0.593852096035,0.197054842687},{0.278810397244,0.593852096035,0.197054842687},{0.293914943602,0.591833478387,0.184216205574},{0.293914943602,0.591833478387,0.184216205574},{0.309063395803,0.589613019789,0.171619421326},{0.309063395803,0.589613019789,0.171619421326},{0.324155770115,0.587201319418,0.159377534847},{0.324155770115,0.587201319418,0.159377534847},{0.339105898793,0.584611641776,0.147590123966},{0.339105898793,0.584611641776,0.147590123966},{0.353796240305,0.581867930492,0.136377340519},{0.353796240305,0.581867930492,0.136377340519},{0.368179053993,0.57898610151,0.125800541591},{0.368179053993,0.57898610151,0.125800541591},{0.382159657379,0.575995119173,0.115950397799},{0.382159657379,0.575995119173,0.115950397799},{0.395728244523,0.572909280944,0.106850382067},{0.395728244523,0.572909280944,0.106850382067},{0.408819264891,0.569757269791,0.0985552120261},{0.408819264891,0.569757269791,0.0985552120261},{0.421481064904,0.566541586272,0.0910400224657},{0.421481064904,0.566541586272,0.0910400224657},{0.433649533456,0.56329295616,0.0843411623911},{0.433649533456,0.56329295616,0.0843411623911},{0.445389078352,0.560008590638,0.0784130544391},{0.445389078352,0.560008590638,0.0784130544391},{0.456724213085,0.556694295148,0.0732291301251},{0.456724213085,0.556694295148,0.0732291301251},{0.467650170768,0.553363726905,0.0687676213476},{0.467650170768,0.553363726905,0.0687676213476},{0.478191378182,0.550021300819,0.0649843572199},{0.478191378182,0.550021300819,0.0649843572199},{0.488396861238,0.546661950698,0.0618216283741},{0.488396861238,0.546661950698,0.0618216283741},{0.498289239879,0.54328739536,0.0592272577501},{0.498289239879,0.54328739536,0.0592272577501},{0.507891137442,0.539898266893,0.0571446599845},{0.517224745646,0.53649428722,0.055514762882},{0.517224745646,0.53649428722,0.055514762882},{0.526311502589,0.533074430075,0.0542779255664},{0.526311502589,0.533074430075,0.0542779255664},{0.535171864525,0.529637065882,0.0533756685344},{0.535171864525,0.529637065882,0.0533756685344},{0.543825153637,0.526180088704,0.0527520754738},{0.543825153637,0.526180088704,0.0527520754738},{0.552289466379,0.522701025565,0.0523547907545},{0.552289466379,0.522701025565,0.0523547907545},{0.560581629478,0.519197129018,0.0521355994447},{0.560581629478,0.519197129018,0.0521355994447},{0.568717193317,0.515665454088,0.0520506243945},{0.568717193317,0.515665454088,0.0520506243945},{0.576710454772,0.512102920631,0.0520602020436},{0.576710454772,0.512102920631,0.0520602020436},{0.58457450375,0.508506361973,0.052128506748},{0.58457450375,0.508506361973,0.052128506748},{0.592321289445,0.504872560293,0.052222987749},{0.592321289445,0.504872560293,0.052222987749},{0.599961703895,0.501198268844,0.0523136690356},{0.599961703895,0.501198268844,0.0523136690356},{0.607505681632,0.497480220579,0.0523723445572},{0.607505681632,0.497480220579,0.0523723445572},{0.614962315274,0.493715122292,0.0523716818241},{0.614962315274,0.493715122292,0.0523716818241},{0.622339987733,0.489899632822,0.0522842261313},{0.622339987733,0.489899632822,0.0522842261313},{0.629646522519,0.486030323314,0.0520812738141},{0.629646522519,0.486030323314,0.0520812738141},{0.636889354233,0.482103616943,0.0517315523255},{0.636889354233,0.482103616943,0.0517315523255},{0.644075722043,0.478115704908,0.0511996007682},{0.644075722043,0.478115704908,0.0511996007682},{0.651212889353,0.474062435013,0.0504436747876},{0.651212889353,0.474062435013,0.0504436747876},{0.658308392892,0.469939169032,0.049412882041},{0.658308392892,0.469939169032,0.049412882041},}; +const std::vector CM_HSV = +{{0.85,0.0,0.0},{0.85,0.0,0.0},{0.85,0.019687519687519674,0.0},{0.85,0.019687519687519674,0.0},{0.85,0.03937503937503935,0.0},{0.85,0.03937503937503935,0.0},{0.85,0.059062559062559014,0.0},{0.85,0.059062559062559014,0.0},{0.85,0.07875007875007878,0.0},{0.85,0.07875007875007878,0.0},{0.85,0.09843759843759844,0.0},{0.85,0.09843759843759844,0.0},{0.85,0.11812511812511813,0.0},{0.85,0.11812511812511813,0.0},{0.85,0.1378126378126378,0.0},{0.85,0.1378126378126378,0.0},{0.85,0.15750015750015747,0.0},{0.85,0.15750015750015747,0.0},{0.85,0.17718767718767714,0.0},{0.85,0.17718767718767714,0.0},{0.85,0.1968751968751969,0.0},{0.85,0.1968751968751969,0.0},{0.85,0.21656271656271658,0.0},{0.85,0.21656271656271658,0.0},{0.85,0.23625023625023625,0.0},{0.85,0.23625023625023625,0.0},{0.85,0.2559377559377559,0.0},{0.85,0.2559377559377559,0.0},{0.85,0.2756252756252756,0.0},{0.85,0.2756252756252756,0.0},{0.85,0.29531279531279525,0.0},{0.85,0.29531279531279525,0.0},{0.85,0.31500031500031495,0.0},{0.85,0.31500031500031495,0.0},{0.85,0.3346878346878346,0.0},{0.85,0.3346878346878346,0.0},{0.85,0.3543753543753543,0.0},{0.85,0.3543753543753543,0.0},{0.85,0.37406287406287403,0.0},{0.85,0.3937503937503937,0.0},{0.85,0.3937503937503937,0.0},{0.85,0.41343791343791336,0.0},{0.85,0.41343791343791336,0.0},{0.85,0.43312543312543306,0.0},{0.85,0.43312543312543306,0.0},{0.85,0.45281295281295275,0.0},{0.85,0.45281295281295275,0.0},{0.85,0.47250047250047245,0.0},{0.85,0.47250047250047245,0.0},{0.85,0.49218799218799203,0.0},{0.85,0.49218799218799203,0.0},{0.85,0.5118755118755118,0.0},{0.85,0.5118755118755118,0.0},{0.85,0.5315630315630314,0.0},{0.85,0.5315630315630314,0.0},{0.85,0.5512505512505512,0.0},{0.85,0.5512505512505512,0.0},{0.85,0.5709380709380709,0.0},{0.85,0.5709380709380709,0.0},{0.85,0.5906255906255905,0.0},{0.85,0.5906255906255905,0.0},{0.85,0.6103131103131103,0.0},{0.85,0.6103131103131103,0.0},{0.85,0.63000063000063,0.0},{0.85,0.63000063000063,0.0},{0.85,0.6496881496881495,0.0},{0.85,0.6496881496881495,0.0},{0.85,0.6693756693756693,0.0},{0.85,0.6693756693756693,0.0},{0.85,0.6890631890631891,0.0},{0.85,0.6890631890631891,0.0},{0.85,0.7087507087507086,0.0},{0.85,0.7087507087507086,0.0},{0.85,0.7284382284382283,0.0},{0.85,0.7284382284382283,0.0},{0.85,0.7481257481257481,0.0},{0.85,0.7481257481257481,0.0},{0.85,0.7678132678132678,0.0},{0.85,0.7875007875007874,0.0},{0.85,0.7875007875007874,0.0},{0.846562230937231,0.8037505381255381,0.0},{0.846562230937231,0.8037505381255381,0.0},{0.8399997243747244,0.8168755512505512,0.0},{0.8399997243747244,0.8168755512505512,0.0},{0.8334372178122178,0.8300005643755644,0.0},{0.8334372178122178,0.8300005643755644,0.0},{0.8268747112497115,0.8431255775005774,0.0},{0.8268747112497115,0.8431255775005774,0.0},{0.8140616140616144,0.85,0.0},{0.8140616140616144,0.85,0.0},{0.7943740943740945,0.85,0.0},{0.7943740943740945,0.85,0.0},{0.7746865746865746,0.85,0.0},{0.7746865746865746,0.85,0.0},{0.7549990549990548,0.85,0.0},{0.7549990549990548,0.85,0.0},{0.7353115353115355,0.85,0.0},{0.7353115353115355,0.85,0.0},{0.7156240156240157,0.85,0.0},{0.7156240156240157,0.85,0.0},{0.6959364959364958,0.85,0.0},{0.6959364959364958,0.85,0.0},{0.6762489762489764,0.85,0.0},{0.6762489762489764,0.85,0.0},{0.6565614565614571,0.85,0.0},{0.6565614565614571,0.85,0.0},{0.6368739368739367,0.85,0.0},{0.6368739368739367,0.85,0.0},{0.6171864171864174,0.85,0.0},{0.6171864171864174,0.85,0.0},{0.5974988974988975,0.85,0.0},{0.5974988974988975,0.85,0.0},{0.5778113778113777,0.85,0.0},{0.5778113778113777,0.85,0.0},{0.5581238581238585,0.85,0.0},{0.5581238581238585,0.85,0.0},{0.5384363384363385,0.85,0.0},{0.5187488187488187,0.85,0.0},{0.5187488187488187,0.85,0.0},{0.4990612990612994,0.85,0.0},{0.4990612990612994,0.85,0.0},{0.47937377937377956,0.85,0.0},{0.47937377937377956,0.85,0.0},{0.4596862596862597,0.85,0.0},{0.4596862596862597,0.85,0.0},{0.4399987399987404,0.85,0.0},{0.4399987399987404,0.85,0.0},{0.42031122031122,0.85,0.0},{0.42031122031122,0.85,0.0},{0.4006237006237007,0.85,0.0},{0.4006237006237007,0.85,0.0},{0.38093618093618087,0.85,0.0},{0.38093618093618087,0.85,0.0},{0.36124866124866095,0.85,0.0},{0.36124866124866095,0.85,0.0},{0.34156114156114165,0.85,0.0},{0.34156114156114165,0.85,0.0},{0.3218736218736218,0.85,0.0},{0.3218736218736218,0.85,0.0},{0.3021861021861019,0.85,0.0},{0.3021861021861019,0.85,0.0},{0.2824985824985827,0.85,0.0},{0.2824985824985827,0.85,0.0},{0.2628110628110628,0.85,0.0},{0.2628110628110628,0.85,0.0},{0.24312354312354348,0.85,0.0},{0.24312354312354348,0.85,0.0},{0.22343602343602362,0.85,0.0},{0.22343602343602362,0.85,0.0},{0.20374850374850376,0.85,0.0},{0.20374850374850376,0.85,0.0},{0.18406098406098392,0.85,0.0},{0.18406098406098392,0.85,0.0},{0.16437346437346462,0.85,0.0},{0.14468594468594476,0.85,0.0},{0.14468594468594476,0.85,0.0},{0.1249984249984249,0.85,0.0},{0.1249984249984249,0.85,0.0},{0.10531090531090559,0.85,0.0},{0.10531090531090559,0.85,0.0},{0.08562338562338631,0.85,0.0},{0.08562338562338631,0.85,0.0},{0.06593586593586587,0.85,0.0},{0.06593586593586587,0.85,0.0},{0.04624834624834602,0.85,0.0},{0.04624834624834602,0.85,0.0},{0.02656089628315742,0.8500011156261156,0.0},{0.02656089628315742,0.8500011156261156,0.0},{0.007088918601823065,0.8631261287511287,0.0},{0.007088918601823065,0.8631261287511287,0.0},{0.0,0.8634369290619291,0.013225858210837826},{0.0,0.8634369290619291,0.013225858210837826},{0.0,0.8568744224994225,0.033031738107403674},{0.0,0.8568744224994225,0.033031738107403674},{0.0,0.850311915936916,0.05222756897819825},{0.0,0.850311915936916,0.05222756897819825},{0.0,0.85,0.07187665374123467},{0.0,0.85,0.07187665374123467},{0.0,0.85,0.09156404939803806},{0.0,0.85,0.09156404939803806},{0.0,0.85,0.11125144505484116},{0.0,0.85,0.11125144505484116},{0.0,0.85,0.1309388407116443},{0.0,0.85,0.1309388407116443},{0.0,0.85,0.15062623636844713},{0.0,0.85,0.15062623636844713},{0.0,0.85,0.17031363202525024},{0.0,0.85,0.17031363202525024},{0.0,0.85,0.19000102768205335},{0.0,0.85,0.19000102768205335},{0.0,0.85,0.20968842333885618},{0.0,0.85,0.20968842333885618},{0.0,0.85,0.229375818995659},{0.0,0.85,0.2490632146524624},{0.0,0.85,0.2490632146524624},{0.0,0.85,0.2687506103092652},{0.0,0.85,0.2687506103092652},{0.0,0.85,0.28843800596606833},{0.0,0.85,0.28843800596606833},{0.0,0.85,0.30812540162287144},{0.0,0.85,0.30812540162287144},{0.0,0.85,0.3278127972796743},{0.0,0.85,0.3278127972796743},{0.0,0.85,0.3475001929364774},{0.0,0.85,0.3475001929364774},{0.0,0.85,0.36718758859328055},{0.0,0.85,0.36718758859328055},{0.0,0.85,0.38687498425008304},{0.0,0.85,0.38687498425008304},{0.0,0.85,0.4065623799068865},{0.0,0.85,0.4065623799068865},{0.0,0.85,0.4262497755636896},{0.0,0.85,0.4262497755636896},{0.0,0.85,0.44593717122049237},{0.0,0.85,0.44593717122049237},{0.0,0.85,0.4656245668772958},{0.0,0.85,0.4656245668772958},{0.0,0.85,0.48531196253409864},{0.0,0.85,0.48531196253409864},{0.0,0.85,0.5049993581909015},{0.0,0.85,0.5049993581909015},{0.0,0.85,0.5246867538477046},{0.0,0.85,0.5246867538477046},{0.0,0.85,0.5443741495045071},{0.0,0.85,0.5443741495045071},{0.0,0.85,0.564061545161311},{0.0,0.85,0.564061545161311},{0.0,0.85,0.5837489408181139},{0.0,0.85,0.5837489408181139},{0.0,0.85,0.6034363364749167},{0.0,0.85,0.6034363364749167},{0.0,0.85,0.6231237321317196},{0.0,0.85,0.6428111277885227},{0.0,0.85,0.6428111277885227},{0.0,0.85,0.6624985234453261},{0.0,0.85,0.6624985234453261},{0.0,0.85,0.6821859191021292},{0.0,0.85,0.6821859191021292},{0.0,0.85,0.7018733147589318},{0.0,0.85,0.7018733147589318},{0.0,0.85,0.7215607104157351},{0.0,0.85,0.7215607104157351},{0.0,0.85,0.7412481060725382},{0.0,0.85,0.7412481060725382},{0.0,0.85,0.7609355017293411},{0.0,0.85,0.7609355017293411},{0.0,0.85,0.7806228973861442},{0.0,0.85,0.7806228973861442},{0.0,0.85,0.8003102930429473},{0.0,0.85,0.8003102930429473},{0.0,0.85,0.8199976886997501},{0.0,0.85,0.8199976886997501},{0.0,0.85,0.8396850843565533},{0.0,0.85,0.8396850843565533},{0.0,0.8406274609399609,0.85},{0.0,0.8406274609399609,0.85},{0.0,0.8209399412524405,0.85},{0.0,0.8209399412524405,0.85},{0.0,0.8012524215649223,0.85},{0.0,0.8012524215649223,0.85},{0.0,0.7815649018774012,0.85},{0.0,0.7815649018774012,0.85},{0.0,0.7618773821898821,0.85},{0.0,0.7618773821898821,0.85},{0.0,0.7421898625023622,0.85},{0.0,0.7421898625023622,0.85},{0.0,0.7225023428148429,0.85},{0.0,0.7225023428148429,0.85},{0.0,0.7028148231273225,0.85},{0.0,0.6831273034398032,0.85},{0.0,0.6831273034398032,0.85},{0.0,0.6634397837522833,0.85},{0.0,0.6634397837522833,0.85},{0.0,0.6437522640647639,0.85},{0.0,0.6437522640647639,0.85},{0.0,0.6240647443772441,0.85},{0.0,0.6240647443772441,0.85},{0.0,0.6043772246897248,0.85},{0.0,0.6043772246897248,0.85},{0.0,0.5846897050022044,0.85},{0.0,0.5846897050022044,0.85},{0.0,0.5650021853146852,0.85},{0.0,0.5650021853146852,0.85},{0.0,0.5453146656271652,0.85},{0.0,0.5453146656271652,0.85},{0.0,0.525627145939646,0.85},{0.0,0.525627145939646,0.85},{0.0,0.5059396262521261,0.85},{0.0,0.5059396262521261,0.85},{0.0,0.4862521065646068,0.85},{0.0,0.4862521065646068,0.85},{0.0,0.46656458687708635,0.85},{0.0,0.46656458687708635,0.85},{0.0,0.4468770671895671,0.85},{0.0,0.4468770671895671,0.85},{0.0,0.42718954750204724,0.85},{0.0,0.42718954750204724,0.85},{0.0,0.40750202781452793,0.85},{0.0,0.40750202781452793,0.85},{0.0,0.38781450812700813,0.85},{0.0,0.38781450812700813,0.85},{0.0,0.36812698843948877,0.85},{0.0,0.36812698843948877,0.85},{0.0,0.34843946875196835,0.85},{0.0,0.34843946875196835,0.85},{0.0,0.32875194906444904,0.85},{0.0,0.32875194906444904,0.85},{0.0,0.3090644293769292,0.85},{0.0,0.28937690968940993,0.85},{0.0,0.28937690968940993,0.85},{0.0,0.2696893900018901,0.85},{0.0,0.2696893900018901,0.85},{0.0,0.2500018703143707,0.85},{0.0,0.2500018703143707,0.85},{0.0,0.23031435062685035,0.85},{0.0,0.23031435062685035,0.85},{0.0,0.2106268309393305,0.85},{0.0,0.2106268309393305,0.85},{0.0,0.19093931125181116,0.85},{0.0,0.19093931125181116,0.85},{0.0,0.17125179156429188,0.85},{0.0,0.17125179156429188,0.85},{0.0,0.15156427187677202,0.85},{0.0,0.15156427187677202,0.85},{0.0,0.1318767521892527,0.85},{0.0,0.1318767521892527,0.85},{0.0,0.1121892325017323,0.85},{0.0,0.1121892325017323,0.85},{0.0,0.09250171281421299,0.85},{0.0,0.09250171281421299,0.85},{0.0,0.07281419312669313,0.85},{0.0,0.07281419312669313,0.85},{0.0,0.05312667343917327,0.85},{0.0,0.05312667343917327,0.85},{0.0,0.03395946704038469,0.8565619487494486},{0.0,0.03395946704038469,0.8565619487494486},{0.0,0.014182959146786335,0.8631244553119553},{0.0,0.014182959146786335,0.8631244553119553},{0.006131102175554736,0.0,0.8637510762510765},{0.006131102175554736,0.0,0.8637510762510765},{0.02566117870736248,0.0,0.8506260631260634},{0.02566117870736248,0.0,0.8506260631260634},{0.04531092499842433,0.0,0.85},{0.04531092499842433,0.0,0.85},{0.06499844468594362,0.0,0.85},{0.06499844468594362,0.0,0.85},{0.08468596437346404,0.0,0.85},{0.10437348406098447,0.0,0.85},{0.10437348406098447,0.0,0.85},{0.12406100374850376,0.0,0.85},{0.12406100374850376,0.0,0.85},{0.14374852343602307,0.0,0.85},{0.14374852343602307,0.0,0.85},{0.16343604312354237,0.0,0.85},{0.16343604312354237,0.0,0.85},{0.1831235628110628,0.0,0.85},{0.1831235628110628,0.0,0.85},{0.20281108249858207,0.0,0.85},{0.20281108249858207,0.0,0.85},{0.2224986021861025,0.0,0.85},{0.2224986021861025,0.0,0.85},{0.2421861218736218,0.0,0.85},{0.2421861218736218,0.0,0.85},{0.2618736415611411,0.0,0.85},{0.2618736415611411,0.0,0.85},{0.2815611612486604,0.0,0.85},{0.2815611612486604,0.0,0.85},{0.30124868093618085,0.0,0.85},{0.30124868093618085,0.0,0.85},{0.3209362006237012,0.0,0.85},{0.3209362006237012,0.0,0.85},{0.34062372031122057,0.0,0.85},{0.34062372031122057,0.0,0.85},{0.3603112399987398,0.0,0.85},{0.3603112399987398,0.0,0.85},{0.3799987596862592,0.0,0.85},{0.3799987596862592,0.0,0.85},{0.39968627937377954,0.0,0.85},{0.39968627937377954,0.0,0.85},{0.4193737990612989,0.0,0.85},{0.4193737990612989,0.0,0.85},{0.43906131874881926,0.0,0.85},{0.43906131874881926,0.0,0.85},{0.4587488384363386,0.0,0.85},{0.4784363581238579,0.0,0.85},{0.4784363581238579,0.0,0.85},{0.4981238778113771,0.0,0.85},{0.4981238778113771,0.0,0.85},{0.5178113974988976,0.0,0.85},{0.5178113974988976,0.0,0.85},{0.5374989171864181,0.0,0.85},{0.5374989171864181,0.0,0.85},{0.5571864368739373,0.0,0.85},{0.5571864368739373,0.0,0.85},{0.5768739565614566,0.0,0.85},{0.5768739565614566,0.0,0.85},{0.5965614762489759,0.0,0.85},{0.5965614762489759,0.0,0.85},{0.6162489959364963,0.0,0.85},{0.6162489959364963,0.0,0.85},{0.6359365156240155,0.0,0.85},{0.6359365156240155,0.0,0.85},{0.655624035311536,0.0,0.85},{0.655624035311536,0.0,0.85},{0.6753115549990554,0.0,0.85},{0.6753115549990554,0.0,0.85},{0.6949990746865746,0.0,0.85},{0.6949990746865746,0.0,0.85},{0.714686594374094,0.0,0.85},{0.714686594374094,0.0,0.85},{0.7343741140616143,0.0,0.85},{0.7343741140616143,0.0,0.85},{0.7540616337491348,0.0,0.85},{0.7540616337491348,0.0,0.85},{0.773749153436653,0.0,0.85},{0.773749153436653,0.0,0.85},{0.7934366731241733,0.0,0.85},{0.7934366731241733,0.0,0.85},{0.8131241928116927,0.0,0.85},{0.8131241928116927,0.0,0.85},{0.8265622374997377,0.0,0.8437505250005248},{0.8265622374997377,0.0,0.8437505250005248},{0.8331247440622441,0.0,0.8306255118755116},{0.8396872506247507,0.0,0.8175004987504988},{0.8396872506247507,0.0,0.8175004987504988},{0.8462497571872573,0.0,0.8043754856254854},{0.8462497571872573,0.0,0.8043754856254854},{0.85,0.0,0.7884382087507088},{0.85,0.0,0.7884382087507088},{0.85,0.0,0.7687506890631883},{0.85,0.0,0.7687506890631883},{0.85,0.0,0.749063169375669},{0.85,0.0,0.749063169375669},{0.85,0.0,0.7293756496881486},{0.85,0.0,0.7293756496881486},{0.85,0.0,0.7096881300006292},{0.85,0.0,0.7096881300006292},{0.85,0.0,0.69000061031311},{0.85,0.0,0.69000061031311},{0.85,0.0,0.6703130906255895},{0.85,0.0,0.6703130906255895},{0.85,0.0,0.6506255709380703},{0.85,0.0,0.6506255709380703},{0.85,0.0,0.630938051250551},{0.85,0.0,0.630938051250551},{0.85,0.0,0.6112505315630317},{0.85,0.0,0.6112505315630317},{0.85,0.0,0.5915630118755113},{0.85,0.0,0.5915630118755113},{0.85,0.0,0.571875492187992},{0.85,0.0,0.571875492187992},{0.85,0.0,0.5521879725004716},{0.85,0.0,0.5521879725004716},{0.85,0.0,0.5325004528129522},{0.85,0.0,0.5325004528129522},{0.85,0.0,0.512812933125433},{0.85,0.0,0.512812933125433},{0.85,0.0,0.49312541343791255},{0.85,0.0,0.49312541343791255},{0.85,0.0,0.47343789375039325},{0.85,0.0,0.47343789375039325},{0.85,0.0,0.4537503740628739},{0.85,0.0,0.4340628543753535},{0.85,0.0,0.4340628543753535},{0.85,0.0,0.41437533468783416},{0.85,0.0,0.41437533468783416},{0.85,0.0,0.3946878150003149},{0.85,0.0,0.3946878150003149},{0.85,0.0,0.37500029531279444},{0.85,0.0,0.37500029531279444},{0.85,0.0,0.35531277562527525},{0.85,0.0,0.35531277562527525},{0.85,0.0,0.33562525593775594},{0.85,0.0,0.33562525593775594},{0.85,0.0,0.3159377362502355},{0.85,0.0,0.3159377362502355},{0.85,0.0,0.2962502165627162},{0.85,0.0,0.2962502165627162},{0.85,0.0,0.2765626968751969},{0.85,0.0,0.2765626968751969},{0.85,0.0,0.2568751771876765},{0.85,0.0,0.2568751771876765},{0.85,0.0,0.2371876575001572},{0.85,0.0,0.2371876575001572},{0.85,0.0,0.21750013781263788},{0.85,0.0,0.21750013781263788},{0.85,0.0,0.19781261812511747},{0.85,0.0,0.19781261812511747},{0.85,0.0,0.17812509843759816},{0.85,0.0,0.17812509843759816},{0.85,0.0,0.15843757875007886},{0.85,0.0,0.15843757875007886},{0.85,0.0,0.13875005906255844},{0.85,0.0,0.13875005906255844},{0.85,0.0,0.11906253937503915},{0.85,0.0,0.11906253937503915},{0.85,0.0,0.09937501968751986},{0.85,0.0,0.09937501968751986},{0.85,0.0,0.07968749999999944},{0.85,0.0,0.07968749999999944},}; + // clang-format on diff --git a/src/render/engine.cpp b/src/render/engine.cpp index 1fd11b5d..517af781 100644 --- a/src/render/engine.cpp +++ b/src/render/engine.cpp @@ -1128,6 +1128,8 @@ void Engine::loadDefaultColorMap(std::string name) { buff = &CM_JET; } else if (name == "turbo") { buff = &CM_TURBO; + } else if (name == "hsv") { + buff = &CM_HSV; } else { exception("unrecognized default colormap " + name); } @@ -1149,6 +1151,7 @@ void Engine::loadDefaultColorMaps() { loadDefaultColorMap("rainbow"); loadDefaultColorMap("jet"); loadDefaultColorMap("turbo"); + loadDefaultColorMap("hsv"); } diff --git a/src/render/mock_opengl/mock_gl_engine.cpp b/src/render/mock_opengl/mock_gl_engine.cpp index ca6b1226..3d0d69ae 100644 --- a/src/render/mock_opengl/mock_gl_engine.cpp +++ b/src/render/mock_opengl/mock_gl_engine.cpp @@ -1902,6 +1902,7 @@ void MockGLEngine::populateDefaultShadersAndRules() { registerShaderProgram("RAYCAST_TANGENT_VECTOR", {FLEX_TANGENT_VECTOR_VERT_SHADER, FLEX_VECTOR_GEOM_SHADER, FLEX_VECTOR_FRAG_SHADER}, DrawMode::Points); registerShaderProgram("RAYCAST_CYLINDER", {FLEX_CYLINDER_VERT_SHADER, FLEX_CYLINDER_GEOM_SHADER, FLEX_CYLINDER_FRAG_SHADER}, DrawMode::Points); registerShaderProgram("HISTOGRAM", {HISTOGRAM_VERT_SHADER, HISTOGRAM_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("HISTOGRAM_CATEGORICAL", {HISTOGRAM_VERT_SHADER, HISTOGRAM_CATEGORICAL_FRAG_SHADER}, DrawMode::Triangles); registerShaderProgram("GROUND_PLANE_TILE", {GROUND_PLANE_VERT_SHADER, GROUND_PLANE_TILE_FRAG_SHADER}, DrawMode::Triangles); registerShaderProgram("GROUND_PLANE_TILE_REFLECT", {GROUND_PLANE_VERT_SHADER, GROUND_PLANE_TILE_REFLECT_FRAG_SHADER}, DrawMode::Triangles); registerShaderProgram("GROUND_PLANE_SHADOW", {GROUND_PLANE_VERT_SHADER, GROUND_PLANE_SHADOW_FRAG_SHADER}, DrawMode::Triangles); @@ -1948,6 +1949,7 @@ void MockGLEngine::populateDefaultShadersAndRules() { registerShaderRule("LIGHT_PASSTHRU", LIGHT_PASSTHRU); registerShaderRule("SHADE_BASECOLOR", SHADE_BASECOLOR); registerShaderRule("SHADE_COLOR", SHADE_COLOR); + registerShaderRule("SHADE_CATEGORICAL_COLORMAP", SHADE_CATEGORICAL_COLORMAP); registerShaderRule("SHADECOLOR_FROM_UNIFORM", SHADECOLOR_FROM_UNIFORM); registerShaderRule("SHADE_COLORMAP_VALUE", SHADE_COLORMAP_VALUE); registerShaderRule("SHADE_COLORMAP_ANGULAR2", SHADE_COLORMAP_ANGULAR2); @@ -1983,6 +1985,7 @@ void MockGLEngine::populateDefaultShadersAndRules() { registerShaderRule("MESH_PROPAGATE_VALUE", MESH_PROPAGATE_VALUE); registerShaderRule("MESH_PROPAGATE_VALUEALPHA", MESH_PROPAGATE_VALUEALPHA); registerShaderRule("MESH_PROPAGATE_FLAT_VALUE", MESH_PROPAGATE_FLAT_VALUE); + registerShaderRule("MESH_PROPAGATE_VALUE_CORNER_NEAREST", MESH_PROPAGATE_VALUE_CORNER_NEAREST); registerShaderRule("MESH_PROPAGATE_VALUE2", MESH_PROPAGATE_VALUE2); registerShaderRule("MESH_PROPAGATE_TCOORD", MESH_PROPAGATE_TCOORD); registerShaderRule("MESH_PROPAGATE_COLOR", MESH_PROPAGATE_COLOR); @@ -2016,6 +2019,7 @@ void MockGLEngine::populateDefaultShadersAndRules() { // cylinder things registerShaderRule("CYLINDER_PROPAGATE_VALUE", CYLINDER_PROPAGATE_VALUE); registerShaderRule("CYLINDER_PROPAGATE_BLEND_VALUE", CYLINDER_PROPAGATE_BLEND_VALUE); + registerShaderRule("CYLINDER_PROPAGATE_NEAREST_VALUE", CYLINDER_PROPAGATE_NEAREST_VALUE); registerShaderRule("CYLINDER_PROPAGATE_COLOR", CYLINDER_PROPAGATE_COLOR); registerShaderRule("CYLINDER_PROPAGATE_BLEND_COLOR", CYLINDER_PROPAGATE_BLEND_COLOR); registerShaderRule("CYLINDER_PROPAGATE_PICK", CYLINDER_PROPAGATE_PICK); diff --git a/src/render/opengl/gl_engine.cpp b/src/render/opengl/gl_engine.cpp index f028686c..65e68a83 100644 --- a/src/render/opengl/gl_engine.cpp +++ b/src/render/opengl/gl_engine.cpp @@ -2459,6 +2459,7 @@ void GLEngine::populateDefaultShadersAndRules() { registerShaderProgram("RAYCAST_TANGENT_VECTOR", {FLEX_TANGENT_VECTOR_VERT_SHADER, FLEX_VECTOR_GEOM_SHADER, FLEX_VECTOR_FRAG_SHADER}, DrawMode::Points); registerShaderProgram("RAYCAST_CYLINDER", {FLEX_CYLINDER_VERT_SHADER, FLEX_CYLINDER_GEOM_SHADER, FLEX_CYLINDER_FRAG_SHADER}, DrawMode::Points); registerShaderProgram("HISTOGRAM", {HISTOGRAM_VERT_SHADER, HISTOGRAM_FRAG_SHADER}, DrawMode::Triangles); + registerShaderProgram("HISTOGRAM_CATEGORICAL", {HISTOGRAM_VERT_SHADER, HISTOGRAM_CATEGORICAL_FRAG_SHADER}, DrawMode::Triangles); registerShaderProgram("GROUND_PLANE_TILE", {GROUND_PLANE_VERT_SHADER, GROUND_PLANE_TILE_FRAG_SHADER}, DrawMode::Triangles); registerShaderProgram("GROUND_PLANE_TILE_REFLECT", {GROUND_PLANE_VERT_SHADER, GROUND_PLANE_TILE_REFLECT_FRAG_SHADER}, DrawMode::Triangles); registerShaderProgram("GROUND_PLANE_SHADOW", {GROUND_PLANE_VERT_SHADER, GROUND_PLANE_SHADOW_FRAG_SHADER}, DrawMode::Triangles); @@ -2505,6 +2506,7 @@ void GLEngine::populateDefaultShadersAndRules() { registerShaderRule("LIGHT_PASSTHRU", LIGHT_PASSTHRU); registerShaderRule("SHADE_BASECOLOR", SHADE_BASECOLOR); registerShaderRule("SHADE_COLOR", SHADE_COLOR); + registerShaderRule("SHADE_CATEGORICAL_COLORMAP", SHADE_CATEGORICAL_COLORMAP); registerShaderRule("SHADECOLOR_FROM_UNIFORM", SHADECOLOR_FROM_UNIFORM); registerShaderRule("SHADE_COLORMAP_VALUE", SHADE_COLORMAP_VALUE); registerShaderRule("SHADE_COLORMAP_ANGULAR2", SHADE_COLORMAP_ANGULAR2); @@ -2540,6 +2542,7 @@ void GLEngine::populateDefaultShadersAndRules() { registerShaderRule("MESH_PROPAGATE_VALUE", MESH_PROPAGATE_VALUE); registerShaderRule("MESH_PROPAGATE_VALUEALPHA", MESH_PROPAGATE_VALUEALPHA); registerShaderRule("MESH_PROPAGATE_FLAT_VALUE", MESH_PROPAGATE_FLAT_VALUE); + registerShaderRule("MESH_PROPAGATE_VALUE_CORNER_NEAREST", MESH_PROPAGATE_VALUE_CORNER_NEAREST); registerShaderRule("MESH_PROPAGATE_VALUE2", MESH_PROPAGATE_VALUE2); registerShaderRule("MESH_PROPAGATE_TCOORD", MESH_PROPAGATE_TCOORD); registerShaderRule("MESH_PROPAGATE_COLOR", MESH_PROPAGATE_COLOR); @@ -2573,6 +2576,7 @@ void GLEngine::populateDefaultShadersAndRules() { // cylinder things registerShaderRule("CYLINDER_PROPAGATE_VALUE", CYLINDER_PROPAGATE_VALUE); registerShaderRule("CYLINDER_PROPAGATE_BLEND_VALUE", CYLINDER_PROPAGATE_BLEND_VALUE); + registerShaderRule("CYLINDER_PROPAGATE_NEAREST_VALUE", CYLINDER_PROPAGATE_NEAREST_VALUE); registerShaderRule("CYLINDER_PROPAGATE_COLOR", CYLINDER_PROPAGATE_COLOR); registerShaderRule("CYLINDER_PROPAGATE_BLEND_COLOR", CYLINDER_PROPAGATE_BLEND_COLOR); registerShaderRule("CYLINDER_PROPAGATE_PICK", CYLINDER_PROPAGATE_PICK); diff --git a/src/render/opengl/shaders/common.cpp b/src/render/opengl/shaders/common.cpp index 45f37fd6..fd33d2f8 100644 --- a/src/render/opengl/shaders/common.cpp +++ b/src/render/opengl/shaders/common.cpp @@ -113,6 +113,27 @@ vec2 sphericalTexCoords(vec3 v) { return uv; } +// Take the component of values corresponding to the largest component of keys +// If there is a tie for the max, you get an average +float selectMax(vec3 keys, vec3 values) { + float maxVal = max(max(keys.x, keys.y), keys.z); + float outSum = 0; + float outCount = 0; + if(keys.x == maxVal) { + outSum += values.x; + outCount += 1.; + } + if(keys.y == maxVal) { + outSum += values.y; + outCount += 1.; + } + if(keys.z == maxVal) { + outSum += values.z; + outCount += 1.; + } + return outSum / outCount; +} + // Used to sample colors. Samples a series of most-distant values from a range [0,1] // offset from a starting value 'start' and wrapped around. index=0 returns start. // We only actually output distinct floats for the first 10 bits, then the pattern repeats. diff --git a/src/render/opengl/shaders/cylinder_shaders.cpp b/src/render/opengl/shaders/cylinder_shaders.cpp index 6b579760..601c7937 100644 --- a/src/render/opengl/shaders/cylinder_shaders.cpp +++ b/src/render/opengl/shaders/cylinder_shaders.cpp @@ -290,6 +290,48 @@ const ShaderReplacementRule CYLINDER_PROPAGATE_BLEND_VALUE ( /* textures */ {} ); +// like propagate value, but takes two values at tip and tail and nearest-endpoint interpolates +const ShaderReplacementRule CYLINDER_PROPAGATE_NEAREST_VALUE ( + /* rule name */ "CYLINDER_PROPAGATE_NEAREST_VALUE", + { /* replacement sources */ + {"VERT_DECLARATIONS", R"( + in float a_value_tail; + in float a_value_tip; + out float a_valueTailToGeom; + out float a_valueTipToGeom; + )"}, + {"VERT_ASSIGNMENTS", R"( + a_valueTailToGeom = a_value_tail; + a_valueTipToGeom = a_value_tip; + )"}, + {"GEOM_DECLARATIONS", R"( + in float a_valueTailToGeom[]; + in float a_valueTipToGeom[]; + out float a_valueTailToFrag; + out float a_valueTipToFrag; + )"}, + {"GEOM_PER_EMIT", R"( + a_valueTailToFrag = a_valueTailToGeom[0]; + a_valueTipToFrag = a_valueTipToGeom[0]; + )"}, + {"FRAG_DECLARATIONS", R"( + in float a_valueTailToFrag; + in float a_valueTipToFrag; + float length2(vec3 x); + )"}, + {"GENERATE_SHADE_VALUE", R"( + float tEdge = dot(pHit - tailView, tipView - tailView) / length2(tipView - tailView); + float shadeValue = tEdge < 0.5 ? a_valueTailToFrag : a_valueTipToFrag; + )"}, + }, + /* uniforms */ {}, + /* attributes */ { + {"a_value_tail", RenderDataType::Float}, + {"a_value_tip", RenderDataType::Float}, + }, + /* textures */ {} +); + const ShaderReplacementRule CYLINDER_PROPAGATE_COLOR ( /* rule name */ "CYLINDER_PROPAGATE_COLOR", { /* replacement sources */ diff --git a/src/render/opengl/shaders/histogram_shaders.cpp b/src/render/opengl/shaders/histogram_shaders.cpp index eb6d2b34..c7e647dd 100644 --- a/src/render/opengl/shaders/histogram_shaders.cpp +++ b/src/render/opengl/shaders/histogram_shaders.cpp @@ -27,11 +27,11 @@ R"( ${ GLSL_VERSION }$ in vec2 a_coord; - out float t; + out float shadeValueRaw; void main() { - t = a_coord.x; + shadeValueRaw = a_coord.x; vec2 scaledCoord = vec2(a_coord.x, a_coord.y * .85); gl_Position = vec4(2.*scaledCoord - vec2(1.0, 1.0),0.,1.); } @@ -44,8 +44,6 @@ const ShaderStageSpecification HISTOGRAM_FRAG_SHADER = { // uniforms { - {"u_cmapRangeMin", RenderDataType::Float}, - {"u_cmapRangeMax", RenderDataType::Float} }, // attributes @@ -54,33 +52,75 @@ const ShaderStageSpecification HISTOGRAM_FRAG_SHADER = { // textures { - {"t_colormap", 1} }, // source R"( ${ GLSL_VERSION }$ - in float t; + in float shadeValueRaw; - uniform sampler1D t_colormap; - uniform float u_cmapRangeMin; - uniform float u_cmapRangeMax; + ${ FRAG_DECLARATIONS }$ layout(location = 0) out vec4 outputF; void main() { - float mapT = (t - u_cmapRangeMin) / (u_cmapRangeMax - u_cmapRangeMin); - float clampMapT = clamp(mapT, 0.f, 1.f); + + float shadeValue = shadeValueRaw; + + ${ GENERATE_SHADE_COLOR }$ // Darken when outside range float darkFactor = 1.0; - if(clampMapT != mapT) { + if(shadeValue < u_rangeLow || shadeValue > u_rangeHigh) { darkFactor = 0.6; } - outputF = vec4(darkFactor*texture(t_colormap, clampMapT).rgb, 1.0); + outputF = vec4(darkFactor*albedoColor.rgb, 1.0); + } +)" +}; + +const ShaderStageSpecification HISTOGRAM_CATEGORICAL_FRAG_SHADER = { + + ShaderStageType::Fragment, + + // uniforms + { + {"u_dataRangeLow", RenderDataType::Float}, + {"u_dataRangeHigh", RenderDataType::Float} + }, + + // attributes + { + }, + + // textures + { + }, + + // source +R"( + ${ GLSL_VERSION }$ + + in float shadeValueRaw; + uniform float u_dataRangeLow; + uniform float u_dataRangeHigh; + + ${ FRAG_DECLARATIONS }$ + + layout(location = 0) out vec4 outputF; + + void main() + { + + // Used to restore [0,1] tvals to the orininal data range for the categorical int remapping + float shadeValue = mix(u_dataRangeLow, u_dataRangeHigh, shadeValueRaw); + + ${ GENERATE_SHADE_COLOR }$ + + outputF = vec4(albedoColor.rgb, 1.0); } )" }; diff --git a/src/render/opengl/shaders/rules.cpp b/src/render/opengl/shaders/rules.cpp index da510e4c..df420ef5 100644 --- a/src/render/opengl/shaders/rules.cpp +++ b/src/render/opengl/shaders/rules.cpp @@ -139,6 +139,33 @@ const ShaderReplacementRule SHADE_COLORMAP_VALUE( } ); +const ShaderReplacementRule SHADE_CATEGORICAL_COLORMAP( + /* rule name */ "SHADE_CATEGORICAL_COLORMAP", + { /* replacement sources */ + {"FRAG_DECLARATIONS", R"( + uniform sampler1D t_colormap; + float intToDistinctReal(float start, int index); + )"}, + {"GENERATE_SHADE_COLOR", R"( + // sample the categorical color + int shadeInt = int(round(shadeValue)); + float startOffset = 0.; + if(shadeInt < 0) { // do something sane for negative values + shadeInt = -shadeInt; + startOffset = 1./3.; // arbitrary value to shift the negative ones a bit + } + float catVal = intToDistinctReal(startOffset, shadeInt); + vec3 albedoColor = texture(t_colormap, catVal).rgb; + )"} + }, + /* uniforms */ { + }, + /* attributes */ {}, + /* textures */ { + {"t_colormap", 1} + } +); + // input: attribute vec2 shadeValue2 // output: vec3 albedoColor const ShaderReplacementRule SHADE_COLORMAP_ANGULAR2( diff --git a/src/render/opengl/shaders/surface_mesh_shaders.cpp b/src/render/opengl/shaders/surface_mesh_shaders.cpp index 2b1b3ee5..4f3a3068 100644 --- a/src/render/opengl/shaders/surface_mesh_shaders.cpp +++ b/src/render/opengl/shaders/surface_mesh_shaders.cpp @@ -304,6 +304,65 @@ const ShaderReplacementRule MESH_PROPAGATE_FLAT_VALUE ( /* textures */ {} ); +const ShaderReplacementRule MESH_PROPAGATE_VALUE_CORNER_NEAREST ( + /* rule name */ "MESH_PROPAGATE_VALUE_CORNER_NEAREST", + // REQUIRES: barycentric coords + { /* replacement sources */ + {"VERT_DECLARATIONS", R"( + in vec3 a_value3; + flat out vec3 a_value3ToFrag; + out vec3 a_boostedBarycoordsToFrag; + )"}, + {"VERT_ASSIGNMENTS", R"( + a_value3ToFrag = a_value3; + + // We want to slightly change the interpolation beyond nearest-vertex: if two + // vertices in a triangle have the same value, we want to shade with a staight-line between + // them, as if it's nearest to a shared linear function + // This is a trick to get that behavior, by adjusting the value of the barycoords before + // interpolation (can be shown to work by considering adding the post-interpolated coordinates + // if they match, then doing some algebra to pull it out before interpolation) + vec3 boostedBarycoords = a_barycoord; + if(a_value3.x == a_value3.y) { + boostedBarycoords.x += a_barycoord.y; + boostedBarycoords.y += a_barycoord.x; + } + if(a_value3.y == a_value3.z) { + boostedBarycoords.y += a_barycoord.z; + boostedBarycoords.z += a_barycoord.y; + } + if(a_value3.z == a_value3.x) { + boostedBarycoords.z += a_barycoord.x; + boostedBarycoords.x += a_barycoord.z; + } + + // boostedBarycoords.y += a_barycoord.x * float(a_value3.x == a_value3.y); + // boostedBarycoords.z += a_barycoord.x * float(a_value3.x == a_value3.z); + // boostedBarycoords.x += a_barycoord.y * float(a_value3.y == a_value3.x); + // boostedBarycoords.z += a_barycoord.y * float(a_value3.y == a_value3.z); + // boostedBarycoords.x += a_barycoord.z * float(a_value3.z == a_value3.x); + // boostedBarycoords.y += a_barycoord.z * float(a_value3.z == a_value3.y); + + a_boostedBarycoordsToFrag = boostedBarycoords; + )"}, + {"FRAG_DECLARATIONS", R"( + flat in vec3 a_value3ToFrag; + in vec3 a_boostedBarycoordsToFrag; + float selectMax(vec3 keys, vec3 values); + )"}, + {"GENERATE_SHADE_VALUE", R"( + // set the value equal to the entry of the vector corresponding to the largest component + // of the barycoords + float shadeValue = selectMax(a_boostedBarycoordsToFrag, a_value3ToFrag); + )"}, + }, + /* uniforms */ {}, + /* attributes */ { + {"a_value3", RenderDataType::Vector3Float}, + }, + /* textures */ {} +); + const ShaderReplacementRule MESH_PROPAGATE_HALFEDGE_VALUE ( /* rule name */ "MESH_PROPAGATE_HALFEDGE_VALUE", { /* replacement sources */ diff --git a/src/scalar_render_image_quantity.cpp b/src/scalar_render_image_quantity.cpp index 83c812d9..27bb3d34 100644 --- a/src/scalar_render_image_quantity.cpp +++ b/src/scalar_render_image_quantity.cpp @@ -87,7 +87,6 @@ void ScalarRenderImageQuantity::prepare() { getImageOriginRule(imageOrigin), hasNormals ? "SHADE_NORMAL_FROM_TEXTURE" : "SHADE_NORMAL_FROM_VIEWPOS_VAR", "TEXTURE_PROPAGATE_VALUE", - "SHADE_COLORMAP_VALUE" } ) ), diff --git a/src/surface_mesh.cpp b/src/surface_mesh.cpp index 559c31f5..6eaca0c5 100644 --- a/src/surface_mesh.cpp +++ b/src/surface_mesh.cpp @@ -35,6 +35,7 @@ vertexPositions( this, uniquePrefix() + "vertexPositions", vertexP triangleVertexInds( this, uniquePrefix() + "triangleVertexInds", triangleVertexIndsData), triangleFaceInds( this, uniquePrefix() + "triangleFaceInds", triangleFaceIndsData), triangleCornerInds( this, uniquePrefix() + "triangleCornerInds", triangleCornerIndsData, std::bind(&SurfaceMesh::computeTriangleCornerInds, this)), +triangleAllVertexInds( this, uniquePrefix() + "triangleAllVertexInds", triangleAllVertexIndsData, std::bind(&SurfaceMesh::computeTriangleAllVertexInds, this)), triangleAllEdgeInds( this, uniquePrefix() + "triangleAllEdgeInds", triangleAllEdgeIndsData, std::bind(&SurfaceMesh::computeTriangleAllEdgeInds, this)), triangleAllHalfedgeInds( this, uniquePrefix() + "triangleHalfedgeInds", triangleAllHalfedgeIndsData, std::bind(&SurfaceMesh::computeTriangleAllHalfedgeInds, this)), triangleAllCornerInds( this, uniquePrefix() + "triangleAllCornerInds", triangleAllCornerIndsData, std::bind(&SurfaceMesh::computeTriangleAllCornerInds, this)), @@ -324,6 +325,35 @@ void SurfaceMesh::computeTriangleCornerInds() { triangleCornerInds.markHostBufferUpdated(); } +void SurfaceMesh::computeTriangleAllVertexInds() { + + triangleAllVertexInds.data.clear(); + triangleAllVertexInds.data.reserve(3 * 3 * nFacesTriangulation()); + + size_t iTriFace = 0; + for (size_t iF = 0; iF < nFaces(); iF++) { + size_t iStart = faceIndsStart[iF]; + size_t D = faceIndsStart[iF + 1] - iStart; + uint32_t vRoot = faceIndsEntries[iStart]; + + // implicitly triangulate from root + for (size_t j = 1; (j + 1) < D; j++) { + uint32_t vB = faceIndsEntries[iStart + j]; + uint32_t vC = faceIndsEntries[iStart + ((j + 1) % D)]; + + // triangle vertex indices, all three values-each + for (size_t k = 0; k < 3; k++) { + triangleAllVertexInds.data.push_back(vRoot); + triangleAllVertexInds.data.push_back(vB); + triangleAllVertexInds.data.push_back(vC); + } + iTriFace++; + } + } + + triangleAllVertexInds.markHostBufferUpdated(); +} + void SurfaceMesh::computeTriangleAllHalfedgeInds() { triangleAllHalfedgeInds.data.clear(); diff --git a/src/surface_scalar_quantity.cpp b/src/surface_scalar_quantity.cpp index cad35b79..35948f14 100644 --- a/src/surface_scalar_quantity.cpp +++ b/src/surface_scalar_quantity.cpp @@ -66,25 +66,48 @@ SurfaceVertexScalarQuantity::SurfaceVertexScalarQuantity(std::string name, const { values.ensureHostBufferPopulated(); - hist.buildHistogram(values.data); + hist.buildHistogram(values.data, dataType); } void SurfaceVertexScalarQuantity::createProgram() { // Create the program to draw this quantity - // clang-format off - program = render::engine->requestShader("MESH", - render::engine->addMaterialRules(parent.getMaterial(), - parent.addSurfaceMeshRules( - addScalarRules( - {"MESH_PROPAGATE_VALUE"} + if (dataType == DataType::CATEGORICAL) { + // special case: nearst-vertex interpolation for categorical data + // (plus some special-case handling for cases where two vertices have the same value) + + // clang-format off + program = render::engine->requestShader("MESH", + render::engine->addMaterialRules(parent.getMaterial(), + parent.addSurfaceMeshRules( + addScalarRules( + {"MESH_PROPAGATE_VALUE_CORNER_NEAREST"} + ) ) ) - ) - ); - // clang-format on + ); + // clang-format on + + program->setAttribute("a_value3", values.getIndexedRenderAttributeBuffer(parent.triangleAllVertexInds)); + + } else { + // common case: linear interpolation within each triangle + + // clang-format off + program = render::engine->requestShader("MESH", + render::engine->addMaterialRules(parent.getMaterial(), + parent.addSurfaceMeshRules( + addScalarRules( + {"MESH_PROPAGATE_VALUE"} + ) + ) + ) + ); + // clang-format on + + program->setAttribute("a_value", values.getIndexedRenderAttributeBuffer(parent.triangleVertexInds)); + } - program->setAttribute("a_value", values.getIndexedRenderAttributeBuffer(parent.triangleVertexInds)); parent.setMeshGeometryAttributes(*program); render::engine->setMaterial(*program, parent.getMaterial()); program->setTextureFromColormap("t_colormap", cMap.get()); @@ -112,7 +135,7 @@ SurfaceFaceScalarQuantity::SurfaceFaceScalarQuantity(std::string name, const std { values.ensureHostBufferPopulated(); parent.faceAreas.ensureHostBufferPopulated(); - hist.buildHistogram(values.data); + hist.buildHistogram(values.data, dataType); } void SurfaceFaceScalarQuantity::createProgram() { @@ -161,7 +184,7 @@ SurfaceEdgeScalarQuantity::SurfaceEdgeScalarQuantity(std::string name, const std { values.ensureHostBufferPopulated(); - hist.buildHistogram(values.data); + hist.buildHistogram(values.data, dataType); } void SurfaceEdgeScalarQuantity::createProgram() { @@ -206,7 +229,7 @@ SurfaceHalfedgeScalarQuantity::SurfaceHalfedgeScalarQuantity(std::string name, c { values.ensureHostBufferPopulated(); - hist.buildHistogram(values.data); + hist.buildHistogram(values.data, dataType); } void SurfaceHalfedgeScalarQuantity::createProgram() { @@ -251,13 +274,33 @@ SurfaceCornerScalarQuantity::SurfaceCornerScalarQuantity(std::string name, const { values.ensureHostBufferPopulated(); - hist.buildHistogram(values.data); + hist.buildHistogram(values.data, dataType); } void SurfaceCornerScalarQuantity::createProgram() { // Create the program to draw this quantity - // clang-format off + if (dataType == DataType::CATEGORICAL) { + // special case: nearst-vertex interpolation for categorical data + // (plus some special-case handling for cases where two vertices have the same value) + + // clang-format off + program = render::engine->requestShader("MESH", + render::engine->addMaterialRules(parent.getMaterial(), + parent.addSurfaceMeshRules( + addScalarRules( + {"MESH_PROPAGATE_VALUE_CORNER_NEAREST"} + ) + ) + ) + ); + // clang-format on + + program->setAttribute("a_value3", values.getIndexedRenderAttributeBuffer(parent.triangleAllCornerInds)); + + } else { + + // clang-format off program = render::engine->requestShader("MESH", render::engine->addMaterialRules(parent.getMaterial(), parent.addSurfaceMeshRules( @@ -267,9 +310,11 @@ void SurfaceCornerScalarQuantity::createProgram() { ) ) ); - // clang-format on + // clang-format on + + program->setAttribute("a_value", values.getIndexedRenderAttributeBuffer(parent.triangleCornerInds)); + } - program->setAttribute("a_value", values.getIndexedRenderAttributeBuffer(parent.triangleCornerInds)); parent.setMeshGeometryAttributes(*program); render::engine->setMaterial(*program, parent.getMaterial()); program->setTextureFromColormap("t_colormap", cMap.get()); @@ -298,7 +343,12 @@ SurfaceTextureScalarQuantity::SurfaceTextureScalarQuantity(std::string name, Sur TextureMapQuantity(*this, dimX_, dimY_, origin_), param(param_) { values.setTextureSize(dimX, dimY); values.ensureHostBufferPopulated(); - hist.buildHistogram(values.data); + hist.buildHistogram(values.data, dataType); + + if (dataType == DataType::CATEGORICAL) { + // default to nearest filtering for categorical data, it's probably what the user wants + filterMode.setPassive(FilterMode::Nearest); + } } void SurfaceTextureScalarQuantity::createProgram() { diff --git a/test/src/curve_network_test.cpp b/test/src/curve_network_test.cpp index 2d5c24b1..3b5ebeb9 100644 --- a/test/src/curve_network_test.cpp +++ b/test/src/curve_network_test.cpp @@ -66,6 +66,15 @@ TEST_F(PolyscopeTest, CurveNetworkScalarNode) { polyscope::removeAllStructures(); } +TEST_F(PolyscopeTest, CurveNetworkScalarCategoricalNode) { + auto psCurve = registerCurveNetwork(); + std::vector vScalar(psCurve->nNodes(), 7.); + auto q1 = psCurve->addNodeScalarQuantity("vScalar", vScalar, polyscope::DataType::CATEGORICAL); + q1->setEnabled(true); + polyscope::show(3); + polyscope::removeAllStructures(); +} + TEST_F(PolyscopeTest, CurveNetworkScalarEdge) { auto psCurve = registerCurveNetwork(); std::vector eScalar(psCurve->nEdges(), 9.); @@ -75,6 +84,15 @@ TEST_F(PolyscopeTest, CurveNetworkScalarEdge) { polyscope::removeAllStructures(); } +TEST_F(PolyscopeTest, CurveNetworkScalarCategoricalEdge) { + auto psCurve = registerCurveNetwork(); + std::vector eScalar(psCurve->nEdges(), 9.); + auto q3 = psCurve->addEdgeScalarQuantity("eScalar", eScalar, polyscope::DataType::CATEGORICAL); + q3->setEnabled(true); + polyscope::show(3); + polyscope::removeAllStructures(); +} + TEST_F(PolyscopeTest, CurveNetworkScalarRadius) { auto psCurve = registerCurveNetwork(); diff --git a/test/src/floating_test.cpp b/test/src/floating_test.cpp index 8af2e9d2..6d6960ac 100644 --- a/test/src/floating_test.cpp +++ b/test/src/floating_test.cpp @@ -23,6 +23,13 @@ TEST_F(PolyscopeTest, FloatingImageTest) { polyscope::show(3); im->setShowFullscreen(true); polyscope::show(3); + + // categorical + polyscope::ScalarImageQuantity* im2 = polyscope::addScalarImageQuantity( + "im scalar cat", dimX, dimY, vals, polyscope::ImageOrigin::UpperLeft, polyscope::DataType::CATEGORICAL); + polyscope::show(3); + im2->setShowFullscreen(true); + polyscope::show(3); } { // ColorImageQuantity @@ -128,6 +135,14 @@ TEST_F(PolyscopeTest, FloatingRenderImageTest) { im->setEnabled(true); polyscope::show(3); } + { // categorical + polyscope::ScalarRenderImageQuantity* im = + polyscope::addScalarRenderImageQuantity("render im scalar", dimX, dimY, depthVals, normalVals, scalarVals, + polyscope::ImageOrigin::UpperLeft, polyscope::DataType::CATEGORICAL); + im->updateBuffers(depthVals, normalVals, scalarVals); + im->setEnabled(true); + polyscope::show(3); + } { // RawColorImageQuantity polyscope::RawColorRenderImageQuantity* im = polyscope::addRawColorRenderImageQuantity( diff --git a/test/src/point_cloud_test.cpp b/test/src/point_cloud_test.cpp index c85edfcd..a07a5dff 100644 --- a/test/src/point_cloud_test.cpp +++ b/test/src/point_cloud_test.cpp @@ -112,6 +112,25 @@ TEST_F(PolyscopeTest, PointCloudScalar) { polyscope::removeAllStructures(); } +TEST_F(PolyscopeTest, PointCloudScalarCategorical) { + auto psPoints = registerPointCloud(); + + std::vector vScalar(psPoints->nPoints(), 7.); + auto q1 = psPoints->addScalarQuantity("vScalar", vScalar, polyscope::DataType::CATEGORICAL); + q1->setEnabled(true); + polyscope::show(3); + + psPoints->setPointRenderMode(polyscope::PointRenderMode::Quad); + polyscope::show(3); + + q1->updateData(vScalar); + polyscope::show(3); + + polyscope::show(3); + + polyscope::removeAllStructures(); +} + TEST_F(PolyscopeTest, PointCloudVector) { auto psPoints = registerPointCloud(); diff --git a/test/src/surface_mesh_test.cpp b/test/src/surface_mesh_test.cpp index d141642a..266c6c03 100644 --- a/test/src/surface_mesh_test.cpp +++ b/test/src/surface_mesh_test.cpp @@ -225,6 +225,15 @@ TEST_F(PolyscopeTest, SurfaceMeshScalarVertex) { polyscope::removeAllStructures(); } +TEST_F(PolyscopeTest, SurfaceMeshScalarCategoricalVertex) { + auto psMesh = registerTriangleMesh(); + std::vector vScalar(psMesh->nVertices(), 7.); + auto q1 = psMesh->addVertexScalarQuantity("vScalar", vScalar, polyscope::DataType::CATEGORICAL); + q1->setEnabled(true); + polyscope::show(3); + polyscope::removeAllStructures(); +} + TEST_F(PolyscopeTest, SurfaceMeshScalarFace) { auto psMesh = registerTriangleMesh(); std::vector fScalar(psMesh->nFaces(), 8.); @@ -234,6 +243,15 @@ TEST_F(PolyscopeTest, SurfaceMeshScalarFace) { polyscope::removeAllStructures(); } +TEST_F(PolyscopeTest, SurfaceMeshScalarCategoricalFace) { + auto psMesh = registerTriangleMesh(); + std::vector fScalar(psMesh->nFaces(), 8.); + auto q2 = psMesh->addFaceScalarQuantity("fScalar", fScalar, polyscope::DataType::CATEGORICAL); + q2->setEnabled(true); + polyscope::show(3); + polyscope::removeAllStructures(); +} + TEST_F(PolyscopeTest, SurfaceMeshScalarEdge) { auto psMesh = registerTriangleMesh(); size_t nEdges = 6; @@ -246,6 +264,18 @@ TEST_F(PolyscopeTest, SurfaceMeshScalarEdge) { polyscope::removeAllStructures(); } +TEST_F(PolyscopeTest, SurfaceMeshScalarCategoricalEdge) { + auto psMesh = registerTriangleMesh(); + size_t nEdges = 6; + std::vector eScalar(nEdges, 9.); + std::vector ePerm = {5, 3, 1, 2, 4, 0}; + psMesh->setEdgePermutation(ePerm); + auto q3 = psMesh->addEdgeScalarQuantity("eScalar", eScalar, polyscope::DataType::CATEGORICAL); + q3->setEnabled(true); + polyscope::show(3); + polyscope::removeAllStructures(); +} + TEST_F(PolyscopeTest, SurfaceMeshScalarHalfedge) { auto psMesh = registerTriangleMesh(); std::vector heScalar(psMesh->nHalfedges(), 10.); @@ -255,6 +285,15 @@ TEST_F(PolyscopeTest, SurfaceMeshScalarHalfedge) { polyscope::removeAllStructures(); } +TEST_F(PolyscopeTest, SurfaceMeshScalarCategoricalHalfedge) { + auto psMesh = registerTriangleMesh(); + std::vector heScalar(psMesh->nHalfedges(), 10.); + auto q4 = psMesh->addHalfedgeScalarQuantity("heScalar", heScalar, polyscope::DataType::CATEGORICAL); + q4->setEnabled(true); + polyscope::show(3); + polyscope::removeAllStructures(); +} + TEST_F(PolyscopeTest, SurfaceMeshScalarHalfedgePerm) { auto psMesh = registerTriangleMesh(); std::vector heScalar(5 + psMesh->nHalfedges(), 10.); @@ -278,6 +317,15 @@ TEST_F(PolyscopeTest, SurfaceMeshScalarCorner) { polyscope::removeAllStructures(); } +TEST_F(PolyscopeTest, SurfaceMeshScalarCategoricalCorner) { + auto psMesh = registerTriangleMesh(); + std::vector cornerScalar(psMesh->nCorners(), 10.); + auto q4 = psMesh->addCornerScalarQuantity("cornerScalar", cornerScalar, polyscope::DataType::CATEGORICAL); + q4->setEnabled(true); + polyscope::show(3); + polyscope::removeAllStructures(); +} + TEST_F(PolyscopeTest, SurfaceMeshScalarCornerPerm) { auto psMesh = registerTriangleMesh(); std::vector cornerScalar(5 + psMesh->nCorners(), 10.); @@ -318,6 +366,23 @@ TEST_F(PolyscopeTest, SurfaceMeshScalarTexture) { polyscope::removeAllStructures(); } +TEST_F(PolyscopeTest, SurfaceMeshScalarCategoricalTexture) { + auto psMesh = registerTriangleMesh(); + + std::vector vals(psMesh->nCorners(), {1., 2.}); + auto qParam = psMesh->addParameterizationQuantity("param", vals); + + size_t dimX = 10; + size_t dimY = 15; + std::vector valuesTex(dimX * dimY, 0.77); + polyscope::SurfaceTextureScalarQuantity* qScalar = psMesh->addTextureScalarQuantity( + "tScalar", *qParam, dimX, dimY, valuesTex, polyscope::ImageOrigin::UpperLeft, polyscope::DataType::CATEGORICAL); + qScalar->setEnabled(true); + polyscope::show(3); + + polyscope::removeAllStructures(); +} + TEST_F(PolyscopeTest, SurfaceMeshScalarTransparency) { auto psMesh = registerTriangleMesh(); diff --git a/test/src/volume_mesh_test.cpp b/test/src/volume_mesh_test.cpp index cf0f7507..c357e4fd 100644 --- a/test/src/volume_mesh_test.cpp +++ b/test/src/volume_mesh_test.cpp @@ -169,6 +169,19 @@ TEST_F(PolyscopeTest, VolumeMeshScalarVertex) { polyscope::removeAllStructures(); } +TEST_F(PolyscopeTest, VolumeMeshScalarCategoricalVertex) { + std::vector verts; + std::vector> cells; + std::tie(verts, cells) = getVolumeMeshData(); + polyscope::VolumeMesh* psVol = polyscope::registerVolumeMesh("vol", verts, cells); + + std::vector vals(verts.size(), 0.44); + auto q1 = psVol->addVertexScalarQuantity("vals", vals, polyscope::DataType::CATEGORICAL); + q1->setEnabled(true); + polyscope::show(3); + polyscope::removeAllStructures(); +} + TEST_F(PolyscopeTest, VolumeMeshScalarCell) { std::vector verts; std::vector> cells; @@ -182,6 +195,19 @@ TEST_F(PolyscopeTest, VolumeMeshScalarCell) { polyscope::removeAllStructures(); } +TEST_F(PolyscopeTest, VolumeMeshScalarCategoricalCell) { + std::vector verts; + std::vector> cells; + std::tie(verts, cells) = getVolumeMeshData(); + polyscope::VolumeMesh* psVol = polyscope::registerVolumeMesh("vol", verts, cells); + + std::vector vals(cells.size(), 0.44); + auto q1 = psVol->addCellScalarQuantity("vals", vals, polyscope::DataType::CATEGORICAL); + q1->setEnabled(true); + polyscope::show(3); + polyscope::removeAllStructures(); +} + TEST_F(PolyscopeTest, VolumeMeshVertexVector) { std::vector verts; std::vector> cells; @@ -224,6 +250,12 @@ TEST_F(PolyscopeTest, VolumeMeshInspect) { auto q1 = psVol->addVertexScalarQuantity("vals", vals); q1->setEnabled(true); polyscope::show(3); + + // with a categorical quantity + auto q1Cat = psVol->addVertexScalarQuantity("vals", vals, polyscope::DataType::CATEGORICAL); + q1Cat->setEnabled(true); + polyscope::show(3); + polyscope::removeAllStructures(); polyscope::removeLastSceneSlicePlane();