Skip to content

Commit

Permalink
contour line scalar shading, add more perceptual colormaps (#317)
Browse files Browse the repository at this point in the history
* Add contour shader

* Add darkness parameter to contour shader

* Add grey and perceptually uniform colormaps

* Add gray and perceptually uniform color maps

* revert unintended changes

* restore recently-added changes

* unrelated small warning fix

* re-restore new cmap names

* missing shader register

* merge contour lines to be a variant style for existing isolines option

* rename isoline width to period

* remove old note

* add a few tests

* change colormap order

---------

Co-authored-by: keenancrane <kmcrane@cs.cmu.edu>
Co-authored-by: Nicholas Sharp <nmwsharp@gmail.com>
  • Loading branch information
3 people authored Jan 3, 2025
1 parent 9d5c3a4 commit 9584cf3
Show file tree
Hide file tree
Showing 17 changed files with 208 additions and 22 deletions.
2 changes: 2 additions & 0 deletions include/polyscope/persistent_value.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ extern PersistentCache<ParamVizStyle> persistentCache_paramVizStyle;
extern PersistentCache<BackFacePolicy> persistentCache_BackFacePolicy;
extern PersistentCache<MeshShadeStyle> persistentCache_MeshNormalType;
extern PersistentCache<FilterMode> persistentCache_FilterMode;
extern PersistentCache<IsolineStyle> persistentCache_IsolineStyle;

template<> inline PersistentCache<double>& getPersistentCacheRef<double>() { return persistentCache_double; }
template<> inline PersistentCache<float>& getPersistentCacheRef<float>() { return persistentCache_float; }
Expand All @@ -157,6 +158,7 @@ template<> inline PersistentCache<ParamVizStyle>& getPersistentCacheR
template<> inline PersistentCache<BackFacePolicy>& getPersistentCacheRef<BackFacePolicy>() { return persistentCache_BackFacePolicy; }
template<> inline PersistentCache<MeshShadeStyle>& getPersistentCacheRef<MeshShadeStyle>() { return persistentCache_MeshNormalType; }
template<> inline PersistentCache<FilterMode>& getPersistentCacheRef<FilterMode>() { return persistentCache_FilterMode; }
template<> inline PersistentCache<IsolineStyle>& getPersistentCacheRef<IsolineStyle>() { return persistentCache_IsolineStyle; }
}
// clang-format on

Expand Down
9 changes: 7 additions & 2 deletions include/polyscope/render/color_maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@ bool buildColormapSelector(std::string& cm, std::string fieldname = "##colormap_


// ColorMaps currently available below
// Sequential:
// Sequential & perceptually uniform:
// - viridis (CM_VIRIDIS)
// - magma (CM_MAGMA)
// - inferno (CM_INFERNO)
// - plasma (CM_PLASMA)
// - gray (CM_GRAY)
//
// Sequential:
// - blues (CM_BLUES)
// - reds (CM_REDS)
//
Expand All @@ -32,7 +38,6 @@ bool buildColormapSelector(std::string& cm, std::string fieldname = "##colormap_
// - rainbow (CM_RAINBOW)
// - jet (CM_JET)
// - turbo (CM_TURBO)
// - hsv (CM_HSV)
//
// Cyclic:
// - phase (CM_PHASE)
Expand Down
4 changes: 4 additions & 0 deletions include/polyscope/render/colormap_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ namespace render {
// === the default colormaps themselves
// (stored in color_maps.cpp)

extern const std::vector<glm::vec3> CM_GRAY;
extern const std::vector<glm::vec3> CM_VIRIDIS;
extern const std::vector<glm::vec3> CM_PLASMA;
extern const std::vector<glm::vec3> CM_INFERNO;
extern const std::vector<glm::vec3> CM_MAGMA;
extern const std::vector<glm::vec3> CM_COOLWARM;
extern const std::vector<glm::vec3> CM_BLUES;
extern const std::vector<glm::vec3> CM_PIYG;
Expand Down
1 change: 1 addition & 0 deletions include/polyscope/render/opengl/shaders/rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ extern const ShaderReplacementRule SHADE_CHECKER_VALUE2; // generate a tw
extern const ShaderReplacementRule SHADE_CHECKER_CATEGORY; // generate a checker with colors from a categorical int
extern const ShaderReplacementRule SHADEVALUE_MAG_VALUE2; // generate a shadeValue from the magnitude of shadeValue2
extern const ShaderReplacementRule ISOLINE_STRIPE_VALUECOLOR; // modulate albedoColor based on shadeValue
extern const ShaderReplacementRule CONTOUR_VALUECOLOR; // modulate albedoColor based on shadeValue
extern const ShaderReplacementRule CHECKER_VALUE2COLOR; // modulate albedoColor based on shadeValue2
extern const ShaderReplacementRule SHADE_BASECOLOR; // constant from u_baseColor
extern const ShaderReplacementRule PREMULTIPLY_COLOR;
Expand Down
17 changes: 14 additions & 3 deletions include/polyscope/scalar_quantity.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,21 @@ class ScalarQuantity {
std::pair<double, double> getDataRange();

// Isolines
// NOTE there's a name typo, errant `s` in isolinesEnabled (leaving to avoid breaking change)
QuantityT* setIsolinesEnabled(bool newEnabled);
bool getIsolinesEnabled();
QuantityT* setIsolineWidth(double size, bool isRelative);
double getIsolineWidth();
QuantityT* setIsolineStyle(IsolineStyle val);
IsolineStyle getIsolineStyle();
QuantityT* setIsolinePeriod(double size, bool isRelative);
double getIsolinePeriod();
QuantityT* setIsolineDarkness(double val);
double getIsolineDarkness();
QuantityT* setIsolineContourThickness(double val);
double getIsolineContourThickness();

// Old / depracted methods kept for compatability
QuantityT* setIsolineWidth(double size, bool isRelative);
double getIsolineWidth();

protected:
std::vector<float> valuesData;
Expand All @@ -75,8 +84,10 @@ class ScalarQuantity {
// Parameters
PersistentValue<std::string> cMap;
PersistentValue<bool> isolinesEnabled;
PersistentValue<ScaledValue<float>> isolineWidth;
PersistentValue<IsolineStyle> isolineStyle;
PersistentValue<ScaledValue<float>> isolinePeriod;
PersistentValue<float> isolineDarkness;
PersistentValue<float> isolineContourThickness;
};

} // namespace polyscope
Expand Down
122 changes: 107 additions & 15 deletions include/polyscope/scalar_quantity.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ ScalarQuantity<QuantityT>::ScalarQuantity(QuantityT& quantity_, const std::vecto
vizRangeMax(quantity.uniquePrefix() + "vizRangeMax", -777.), // including clearing cache
cMap(quantity.uniquePrefix() + "cmap", defaultColorMap(dataType)),
isolinesEnabled(quantity.uniquePrefix() + "isolinesEnabled", false),
isolineWidth(quantity.uniquePrefix() + "isolineWidth",
absoluteValue((dataRange.second - dataRange.first) * 0.02)),
isolineDarkness(quantity.uniquePrefix() + "isolineDarkness", 0.7)
isolineStyle(quantity.uniquePrefix() + "isolinesStyle", IsolineStyle::Stripe),
isolinePeriod(quantity.uniquePrefix() + "isolinePeriod",
absoluteValue((dataRange.second - dataRange.first) * 0.02)),
isolineDarkness(quantity.uniquePrefix() + "isolineDarkness", 0.7),
isolineContourThickness(quantity.uniquePrefix() + "isolineContourThickness", 0.3)

{
values.checkInvalidValues();
Expand Down Expand Up @@ -144,22 +146,46 @@ void ScalarQuantity<QuantityT>::buildScalarUI() {

// Isolines
if (isolinesEnabled.get()) {

ImGui::PushItemWidth(100);


auto styleName = [](const IsolineStyle& m) -> std::string {
switch (m) {
case IsolineStyle::Stripe:
return "Stripe";
case IsolineStyle::Contour:
return "Contour";
}
return "";
};

ImGui::TextUnformatted("Isoline style");
ImGui::SameLine();
if (ImGui::BeginCombo("##IsolineStyle", styleName(getIsolineStyle()).c_str())) {
for (IsolineStyle s : {IsolineStyle::Stripe, IsolineStyle::Contour}) {
std::string sName = styleName(s);
if (ImGui::Selectable(sName.c_str(), getIsolineStyle() == s)) {
setIsolineStyle(s);
}
}
ImGui::EndCombo();
}

// Isoline width
ImGui::TextUnformatted("Isoline width");
ImGui::TextUnformatted("Isoline period");
ImGui::SameLine();
if (isolineWidth.get().isRelative()) {
if (ImGui::DragFloat("##Isoline width relative", isolineWidth.get().getValuePtr(), .001, 0.0001, 1.0, "%.4f",
if (isolinePeriod.get().isRelative()) {
if (ImGui::DragFloat("##Isoline period relative", isolinePeriod.get().getValuePtr(), .001, 0.0001, 1.0, "%.4f",
ImGuiSliderFlags_Logarithmic | ImGuiSliderFlags_NoRoundToFormat)) {
isolineWidth.manuallyChanged();
isolinePeriod.manuallyChanged();
requestRedraw();
}
} else {
float scaleWidth = dataRange.second - dataRange.first;
if (ImGui::DragFloat("##Isoline width absolute", isolineWidth.get().getValuePtr(), scaleWidth / 1000, 0.,
if (ImGui::DragFloat("##Isoline period absolute", isolinePeriod.get().getValuePtr(), scaleWidth / 1000, 0.,
scaleWidth, "%.4f", ImGuiSliderFlags_Logarithmic | ImGuiSliderFlags_NoRoundToFormat)) {
isolineWidth.manuallyChanged();
isolinePeriod.manuallyChanged();
requestRedraw();
}
}
Expand All @@ -172,6 +198,18 @@ void ScalarQuantity<QuantityT>::buildScalarUI() {
requestRedraw();
}


// Isoline Contour Thickness
if (isolineStyle.get() == IsolineStyle::Contour) {
ImGui::TextUnformatted("Contour thickness");
ImGui::SameLine();
if (ImGui::DragFloat("##Contour thickness", &isolineContourThickness.get(), .001, 0.0001, 1.0, "%.4f",
ImGuiSliderFlags_Logarithmic | ImGuiSliderFlags_NoRoundToFormat)) {
isolineContourThickness.manuallyChanged();
requestRedraw();
}
}

ImGui::PopItemWidth();
}
}
Expand All @@ -192,9 +230,18 @@ std::vector<std::string> ScalarQuantity<QuantityT>::addScalarRules(std::vector<s
// common case
rules.push_back("SHADE_COLORMAP_VALUE");
}

if (isolinesEnabled.get()) {
rules.push_back("ISOLINE_STRIPE_VALUECOLOR");
switch (isolineStyle.get()) {
case IsolineStyle::Stripe:
rules.push_back("ISOLINE_STRIPE_VALUECOLOR");
break;
case IsolineStyle::Contour:
rules.push_back("CONTOUR_VALUECOLOR");
break;
}
}

return rules;
}

Expand All @@ -207,8 +254,17 @@ void ScalarQuantity<QuantityT>::setScalarUniforms(render::ShaderProgram& p) {
}

if (isolinesEnabled.get()) {
p.setUniform("u_modLen", getIsolineWidth());
p.setUniform("u_modDarkness", getIsolineDarkness());
switch (isolineStyle.get()) {
case IsolineStyle::Stripe:
p.setUniform("u_modLen", getIsolinePeriod());
p.setUniform("u_modDarkness", getIsolineDarkness());
break;
case IsolineStyle::Contour:
p.setUniform("u_modLen", getIsolinePeriod());
p.setUniform("u_modThickness", getIsolineContourThickness());
p.setUniform("u_modDarkness", getIsolineDarkness());
break;
}
}
}

Expand Down Expand Up @@ -277,17 +333,26 @@ std::pair<double, double> ScalarQuantity<QuantityT>::getDataRange() {
}

template <typename QuantityT>
QuantityT* ScalarQuantity<QuantityT>::setIsolineWidth(double size, bool isRelative) {
isolineWidth = ScaledValue<float>(size, isRelative);
QuantityT* ScalarQuantity<QuantityT>::setIsolinePeriod(double size, bool isRelative) {
isolinePeriod = ScaledValue<float>(size, isRelative);
if (!isolinesEnabled.get()) {
setIsolinesEnabled(true);
}
requestRedraw();
return &quantity;
}
template <typename QuantityT>
double ScalarQuantity<QuantityT>::getIsolinePeriod() {
return isolinePeriod.get().asAbsolute();
}

template <typename QuantityT>
QuantityT* ScalarQuantity<QuantityT>::setIsolineWidth(double size, bool isRelative) {
return setIsolinePeriod(size, isRelative);
}
template <typename QuantityT>
double ScalarQuantity<QuantityT>::getIsolineWidth() {
return isolineWidth.get().asAbsolute();
return getIsolinePeriod();
}

template <typename QuantityT>
Expand Down Expand Up @@ -319,4 +384,31 @@ bool ScalarQuantity<QuantityT>::getIsolinesEnabled() {
return isolinesEnabled.get();
}

template <typename QuantityT>
QuantityT* ScalarQuantity<QuantityT>::setIsolineStyle(IsolineStyle val) {
isolineStyle = val;
quantity.refresh();
requestRedraw();
return &quantity;
}
template <typename QuantityT>
IsolineStyle ScalarQuantity<QuantityT>::getIsolineStyle() {
return isolineStyle.get();
}


template <typename QuantityT>
QuantityT* ScalarQuantity<QuantityT>::setIsolineContourThickness(double val) {
isolineContourThickness = val;
if (!isolinesEnabled.get()) {
setIsolinesEnabled(true);
}
requestRedraw();
return &quantity;
}
template <typename QuantityT>
double ScalarQuantity<QuantityT>::getIsolineContourThickness() {
return isolineContourThickness.get();
}

} // namespace polyscope
1 change: 1 addition & 0 deletions include/polyscope/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ enum class MeshElement { VERTEX = 0, FACE, EDGE, HALFEDGE, CORNER };
enum class MeshShadeStyle { Smooth = 0, Flat, TriFlat };
enum class VolumeMeshElement { VERTEX = 0, EDGE, FACE, CELL };
enum class VolumeCellType { TET = 0, HEX };
enum class IsolineStyle { Stripe = 0, Contour };

enum class ImplicitRenderMode { SphereMarch, FixedStep };
enum class ImageOrigin { LowerLeft, UpperLeft };
Expand Down
1 change: 1 addition & 0 deletions src/persistent_value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ PersistentCache<ParamVizStyle> persistentCache_paramVizStyle;
PersistentCache<BackFacePolicy> persistentCache_BackFacePolicy;
PersistentCache<MeshShadeStyle> persistentCache_MeshNormalType;
PersistentCache<FilterMode> persistentCache_FilterMode;
PersistentCache<IsolineStyle> persistentCache_IsolineStyle;
// clang-format on
} // namespace detail
} // namespace polyscope
12 changes: 12 additions & 0 deletions src/render/color_maps.cpp

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions src/render/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,14 @@ void Engine::loadDefaultColorMap(std::string name) {
const std::vector<glm::vec3>* buff = nullptr;
if (name == "viridis") {
buff = &CM_VIRIDIS;
} else if (name == "magma") {
buff = &CM_MAGMA;
} else if (name == "inferno") {
buff = &CM_INFERNO;
} else if (name == "plasma") {
buff = &CM_PLASMA;
} else if (name == "gray") {
buff = &CM_GRAY;
} else if (name == "coolwarm") {
buff = &CM_COOLWARM;
} else if (name == "blues") {
Expand Down Expand Up @@ -1142,6 +1150,10 @@ void Engine::loadDefaultColorMap(std::string name) {

void Engine::loadDefaultColorMaps() {
loadDefaultColorMap("viridis");
loadDefaultColorMap("plasma");
loadDefaultColorMap("inferno");
loadDefaultColorMap("magma");
loadDefaultColorMap("gray");
loadDefaultColorMap("coolwarm");
loadDefaultColorMap("blues");
loadDefaultColorMap("reds");
Expand Down
1 change: 1 addition & 0 deletions src/render/mock_opengl/mock_gl_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1959,6 +1959,7 @@ void MockGLEngine::populateDefaultShadersAndRules() {
registerShaderRule("SHADEVALUE_MAG_VALUE2", SHADEVALUE_MAG_VALUE2);
registerShaderRule("ISOLINE_STRIPE_VALUECOLOR", ISOLINE_STRIPE_VALUECOLOR);
registerShaderRule("CHECKER_VALUE2COLOR", CHECKER_VALUE2COLOR);
registerShaderRule("CONTOUR_VALUECOLOR", CONTOUR_VALUECOLOR);
registerShaderRule("INVERSE_TONEMAP", INVERSE_TONEMAP);

// Texture and image things
Expand Down
1 change: 1 addition & 0 deletions src/render/opengl/gl_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2515,6 +2515,7 @@ void GLEngine::populateDefaultShadersAndRules() {
registerShaderRule("SHADE_CHECKER_CATEGORY", SHADE_CHECKER_CATEGORY);
registerShaderRule("SHADEVALUE_MAG_VALUE2", SHADEVALUE_MAG_VALUE2);
registerShaderRule("ISOLINE_STRIPE_VALUECOLOR", ISOLINE_STRIPE_VALUECOLOR);
registerShaderRule("CONTOUR_VALUECOLOR", CONTOUR_VALUECOLOR);
registerShaderRule("CHECKER_VALUE2COLOR", CHECKER_VALUE2COLOR);
registerShaderRule("INVERSE_TONEMAP", INVERSE_TONEMAP);

Expand Down
26 changes: 26 additions & 0 deletions src/render/opengl/shaders/rules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,32 @@ const ShaderReplacementRule ISOLINE_STRIPE_VALUECOLOR (
/* textures */ {}
);


const ShaderReplacementRule CONTOUR_VALUECOLOR (
/* rule name */ "CONTOUR_VALUECOLOR",
{ /* replacement sources */
{"FRAG_DECLARATIONS", R"(
uniform float u_modLen;
uniform float u_modThickness;
uniform float u_modDarkness;
)"},
{"GENERATE_SHADE_COLOR", R"(
/* TODO: get rid of arbitrary constants */
vec2 gradF = vec2( dFdx(shadeValue), dFdy(shadeValue) );
float w = 1./( 10. / u_modLen * u_modThickness * length(gradF) );
float s = u_modDarkness * exp( -pow( w*(fract(abs(shadeValue/u_modLen))-0.5), 8.0 ));
albedoColor *= 1.-s;
)"}
},
/* uniforms */ {
{"u_modLen", RenderDataType::Float},
{"u_modThickness", RenderDataType::Float},
{"u_modDarkness", RenderDataType::Float},
},
/* attributes */ {},
/* textures */ {}
);

const ShaderReplacementRule CHECKER_VALUE2COLOR (
/* rule name */ "CHECKER_VALUE2COLOR",
{ /* replacement sources */
Expand Down
2 changes: 0 additions & 2 deletions src/surface_mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,6 @@ 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;
Expand All @@ -347,7 +346,6 @@ void SurfaceMesh::computeTriangleAllVertexInds() {
triangleAllVertexInds.data.push_back(vB);
triangleAllVertexInds.data.push_back(vC);
}
iTriFace++;
}
}

Expand Down
Loading

0 comments on commit 9584cf3

Please sign in to comment.