From aae448a093b1df473f2fb6a210d22f3490a0aa60 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Mon, 22 Sep 2025 15:37:48 -0700 Subject: [PATCH 01/18] WIP on color bars --- .../polyscope/{histogram.h => color_bar.h} | 37 +++++----- .../polyscope/curve_network_scalar_quantity.h | 1 - .../polyscope/point_cloud_color_quantity.h | 1 - .../polyscope/point_cloud_scalar_quantity.h | 1 - include/polyscope/scalar_quantity.h | 4 +- .../polyscope/scalar_render_image_quantity.h | 1 - .../surface_parameterization_quantity.h | 1 - include/polyscope/surface_scalar_quantity.h | 1 - .../polyscope/volume_grid_scalar_quantity.h | 1 - .../polyscope/volume_mesh_scalar_quantity.h | 1 - src/CMakeLists.txt | 4 +- src/{histogram.cpp => color_bar.cpp} | 73 ++++++++++--------- 12 files changed, 61 insertions(+), 65 deletions(-) rename include/polyscope/{histogram.h => color_bar.h} (55%) rename src/{histogram.cpp => color_bar.cpp} (76%) diff --git a/include/polyscope/histogram.h b/include/polyscope/color_bar.h similarity index 55% rename from include/polyscope/histogram.h rename to include/polyscope/color_bar.h index bf1575603..0619de611 100644 --- a/include/polyscope/histogram.h +++ b/include/polyscope/color_bar.h @@ -13,12 +13,12 @@ namespace polyscope { // A histogram that shows up in ImGUI window // ONEDAY: we could definitely make a better histogram widget for categorical data... -class Histogram { +class ColorBar { public: - Histogram(); // must call buildHistogram() with data after - Histogram(std::vector& values, DataType datatype); // internally calls buildHistogram() + ColorBar(); // must call buildHistogram() with data after + ColorBar(std::vector& values, DataType datatype); // internally calls buildHistogram() - ~Histogram(); + ~ColorBar(); void buildHistogram(const std::vector& values, DataType datatype); void updateColormap(const std::string& newColormap); @@ -29,30 +29,33 @@ class Histogram { std::pair colormapRange; // in DATA values, not [0,1] private: - // = Helpers + // + DataType dataType = DataType::STANDARD; + std::pair dataRange; - // Manage the actual histogram - void fillBuffers(); - size_t rawHistBinCount = 51; + // == The inline horizontal histogram visualization in the structures bar - DataType dataType = DataType::STANDARD; + // Manage histogram counts for + void fillHistogramBuffers(); + size_t rawHistBinCount = 51; std::vector rawHistCurveY; std::vector> rawHistCurveX; - std::pair dataRange; - - // Render to texture - void renderToTexture(); - void prepare(); + // Render to a texture for the inline histogram visualization in the structures bar + void renderInlineHistogramToTexture(); + void prepareInlineHistogram(); unsigned int texDim = 600; - std::shared_ptr texture = nullptr; - std::shared_ptr framebuffer = nullptr; - std::shared_ptr program = nullptr; + std::shared_ptr inlineHistogramTexture = nullptr; + std::shared_ptr inlineHistogramFramebuffer = nullptr; + std::shared_ptr inlineHistogramProgram = nullptr; std::string colormap = "viridis"; // A few parameters which control appearance float bottomBarHeight = 0.35; float bottomBarGap = 0.1; + + // == The optional vertical colorbar which floats ont he main display + }; diff --git a/include/polyscope/curve_network_scalar_quantity.h b/include/polyscope/curve_network_scalar_quantity.h index 27c79bedf..8885a9cc4 100644 --- a/include/polyscope/curve_network_scalar_quantity.h +++ b/include/polyscope/curve_network_scalar_quantity.h @@ -4,7 +4,6 @@ #include "polyscope/affine_remapper.h" #include "polyscope/curve_network.h" -#include "polyscope/histogram.h" #include "polyscope/render/color_maps.h" #include "polyscope/scalar_quantity.h" diff --git a/include/polyscope/point_cloud_color_quantity.h b/include/polyscope/point_cloud_color_quantity.h index 8ebc346cb..8c924b5ab 100644 --- a/include/polyscope/point_cloud_color_quantity.h +++ b/include/polyscope/point_cloud_color_quantity.h @@ -4,7 +4,6 @@ #include "polyscope/affine_remapper.h" #include "polyscope/color_quantity.h" -#include "polyscope/histogram.h" #include "polyscope/point_cloud.h" #include "polyscope/point_cloud_quantity.h" diff --git a/include/polyscope/point_cloud_scalar_quantity.h b/include/polyscope/point_cloud_scalar_quantity.h index 903b9ed7a..644d60209 100644 --- a/include/polyscope/point_cloud_scalar_quantity.h +++ b/include/polyscope/point_cloud_scalar_quantity.h @@ -3,7 +3,6 @@ #pragma once #include "polyscope/affine_remapper.h" -#include "polyscope/histogram.h" #include "polyscope/point_cloud.h" #include "polyscope/render/color_maps.h" #include "polyscope/scalar_quantity.h" diff --git a/include/polyscope/scalar_quantity.h b/include/polyscope/scalar_quantity.h index 9d55dbb6b..aeb8d146a 100644 --- a/include/polyscope/scalar_quantity.h +++ b/include/polyscope/scalar_quantity.h @@ -3,7 +3,7 @@ #pragma once #include "polyscope/affine_remapper.h" -#include "polyscope/histogram.h" +#include "polyscope/color_bar.h" #include "polyscope/persistent_value.h" #include "polyscope/polyscope.h" #include "polyscope/render/engine.h" @@ -79,7 +79,7 @@ class ScalarQuantity { std::pair dataRange; PersistentValue vizRangeMin; PersistentValue vizRangeMax; - Histogram hist; + ColorBar hist; // Parameters PersistentValue cMap; diff --git a/include/polyscope/scalar_render_image_quantity.h b/include/polyscope/scalar_render_image_quantity.h index 9614a0c00..50851e7e6 100644 --- a/include/polyscope/scalar_render_image_quantity.h +++ b/include/polyscope/scalar_render_image_quantity.h @@ -3,7 +3,6 @@ #pragma once #include "polyscope/affine_remapper.h" -#include "polyscope/histogram.h" #include "polyscope/render/color_maps.h" #include "polyscope/render_image_quantity_base.h" #include "polyscope/scalar_quantity.h" diff --git a/include/polyscope/surface_parameterization_quantity.h b/include/polyscope/surface_parameterization_quantity.h index 957bac4a6..656c5a9d9 100644 --- a/include/polyscope/surface_parameterization_quantity.h +++ b/include/polyscope/surface_parameterization_quantity.h @@ -3,7 +3,6 @@ #pragma once #include "polyscope/affine_remapper.h" -#include "polyscope/histogram.h" #include "polyscope/parameterization_quantity.h" #include "polyscope/render/color_maps.h" #include "polyscope/render/engine.h" diff --git a/include/polyscope/surface_scalar_quantity.h b/include/polyscope/surface_scalar_quantity.h index 80260f114..1b2eab618 100644 --- a/include/polyscope/surface_scalar_quantity.h +++ b/include/polyscope/surface_scalar_quantity.h @@ -5,7 +5,6 @@ #include "polyscope/polyscope.h" #include "polyscope/affine_remapper.h" -#include "polyscope/histogram.h" #include "polyscope/render/color_maps.h" #include "polyscope/render/engine.h" #include "polyscope/scalar_quantity.h" diff --git a/include/polyscope/volume_grid_scalar_quantity.h b/include/polyscope/volume_grid_scalar_quantity.h index 503c046a6..5bc200e98 100644 --- a/include/polyscope/volume_grid_scalar_quantity.h +++ b/include/polyscope/volume_grid_scalar_quantity.h @@ -5,7 +5,6 @@ #include "polyscope/polyscope.h" #include "polyscope/affine_remapper.h" -#include "polyscope/histogram.h" #include "polyscope/render/color_maps.h" #include "polyscope/scalar_quantity.h" #include "polyscope/surface_mesh.h" diff --git a/include/polyscope/volume_mesh_scalar_quantity.h b/include/polyscope/volume_mesh_scalar_quantity.h index a31be7a02..795537add 100644 --- a/include/polyscope/volume_mesh_scalar_quantity.h +++ b/include/polyscope/volume_mesh_scalar_quantity.h @@ -5,7 +5,6 @@ #include "polyscope/polyscope.h" #include "polyscope/affine_remapper.h" -#include "polyscope/histogram.h" #include "polyscope/render/color_maps.h" #include "polyscope/render/engine.h" #include "polyscope/scalar_quantity.h" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e5a0679b4..4fab560c5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -183,7 +183,7 @@ SET(SRCS disjoint_sets.cpp file_helpers.cpp camera_parameters.cpp - histogram.cpp + color_bar.cpp persistent_value.cpp color_management.cpp transformation_gizmo.cpp @@ -271,6 +271,7 @@ SET(HEADERS ${INCLUDE_ROOT}/camera_view.h ${INCLUDE_ROOT}/camera_view.ipp ${INCLUDE_ROOT}/check_invalid_values.h + ${INCLUDE_ROOT}/color_bar.h ${INCLUDE_ROOT}/color_management.h ${INCLUDE_ROOT}/color_image_quantity.h ${INCLUDE_ROOT}/color_render_image_quantity.h @@ -293,7 +294,6 @@ SET(HEADERS ${INCLUDE_ROOT}/floating_quantity.h ${INCLUDE_ROOT}/floating_quantities.h ${INCLUDE_ROOT}/group.h - ${INCLUDE_ROOT}/histogram.h ${INCLUDE_ROOT}/image_quantity.h ${INCLUDE_ROOT}/imgui_config.h ${INCLUDE_ROOT}/implicit_helpers.h diff --git a/src/histogram.cpp b/src/color_bar.cpp similarity index 76% rename from src/histogram.cpp rename to src/color_bar.cpp index 12394e62b..5556f9dda 100644 --- a/src/histogram.cpp +++ b/src/color_bar.cpp @@ -1,6 +1,6 @@ // Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run -#include "polyscope/histogram.h" +#include "polyscope/color_bar.h" #include "polyscope/affine_remapper.h" #include "polyscope/polyscope.h" @@ -13,13 +13,13 @@ namespace polyscope { -Histogram::Histogram() {} +ColorBar::ColorBar() {} -Histogram::Histogram(std::vector& values, DataType dataType) { buildHistogram(values, dataType); } +ColorBar::ColorBar(std::vector& values, DataType dataType) { buildHistogram(values, dataType); } -Histogram::~Histogram() {} +ColorBar::~ColorBar() {} -void Histogram::buildHistogram(const std::vector& values, DataType dataType_) { +void ColorBar::buildHistogram(const std::vector& values, DataType dataType_) { dataType = dataType_; // Build arrays of values @@ -80,14 +80,14 @@ void Histogram::buildHistogram(const std::vector& values, DataType dataTy } -void Histogram::updateColormap(const std::string& newColormap) { +void ColorBar::updateColormap(const std::string& newColormap) { colormap = newColormap; - if (program) { - program.reset(); + if (inlineHistogramProgram) { + inlineHistogramProgram.reset(); } } -void Histogram::fillBuffers() { +void ColorBar::fillHistogramBuffers() { if (rawHistCurveY.size() == 0) { exception("histogram fillBuffers() called before buildHistogram"); @@ -129,65 +129,65 @@ void Histogram::fillBuffers() { coords.push_back(glm::vec2{0., bottomBarHeight}); - program->setAttribute("a_coord", coords); + inlineHistogramProgram->setAttribute("a_coord", coords); } -void Histogram::prepare() { +void ColorBar::prepareInlineHistogram() { - framebuffer = render::engine->generateFrameBuffer(texDim, texDim); - texture = render::engine->generateTextureBuffer(TextureFormat::RGBA8, texDim, texDim); - framebuffer->addColorBuffer(texture); + inlineHistogramFramebuffer = render::engine->generateFrameBuffer(texDim, texDim); + inlineHistogramTexture = render::engine->generateTextureBuffer(TextureFormat::RGBA8, texDim, texDim); + inlineHistogramFramebuffer->addColorBuffer(inlineHistogramTexture); - // Create the program + // Create the inlineHistogramProgram if (dataType == DataType::CATEGORICAL) { // for categorical data only - program = render::engine->requestShader("HISTOGRAM_CATEGORICAL", {"SHADE_CATEGORICAL_COLORMAP"}, - render::ShaderReplacementDefaults::Process); + inlineHistogramProgram = 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); + inlineHistogramProgram = render::engine->requestShader("HISTOGRAM", {"SHADE_COLORMAP_VALUE"}, + render::ShaderReplacementDefaults::Process); } - program->setTextureFromColormap("t_colormap", colormap, true); + inlineHistogramProgram->setTextureFromColormap("t_colormap", colormap, true); fillBuffers(); } -void Histogram::renderToTexture() { +void ColorBar::renderInlineHistogramToTexture() { - if (!program) { - prepare(); + if (!inlineHistogramProgram) { + prepareInlineHistogram(); } - framebuffer->clearColor = {0.0, 0.0, 0.0}; - framebuffer->clearAlpha = 0.2; - framebuffer->setViewport(0, 0, texDim, texDim); - framebuffer->bindForRendering(); - framebuffer->clear(); + inlineHistogramFramebuffer->clearColor = {0.0, 0.0, 0.0}; + inlineHistogramFramebuffer->clearAlpha = 0.2; + inlineHistogramFramebuffer->setViewport(0, 0, texDim, texDim); + inlineHistogramFramebuffer->bindForRendering(); + inlineHistogramFramebuffer->clear(); // = Set uniforms 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); + inlineHistogramProgram->setUniform("u_dataRangeLow", dataRange.first); + inlineHistogramProgram->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); + inlineHistogramProgram->setUniform("u_rangeLow", rangeLow); + inlineHistogramProgram->setUniform("u_rangeHigh", rangeHigh); } // Draw - program->draw(); + inlineHistogramProgram->draw(); } -void Histogram::buildUI(float width) { +void ColorBar::buildUI(float width) { // NOTE: I'm surprised this works, since we're drawing in the middle of imgui's processing. Possible source of bugs? renderToTexture(); @@ -201,8 +201,9 @@ void Histogram::buildUI(float width) { float h = w / aspect; // Render image - ImGui::Image((ImTextureID)(intptr_t)texture->getNativeHandle(), ImVec2(w, h), ImVec2(0, 1), ImVec2(1, 0)); - render::engine->preserveResourceUntilImguiFrameCompletes(texture); + ImGui::Image((ImTextureID)(intptr_t)inlineHistogramTexture->getNativeHandle(), ImVec2(w, h), ImVec2(0, 1), + ImVec2(1, 0)); + render::engine->preserveResourceUntilImguiFrameCompletes(inlineHistogramTexture); // Helpful info for drawing annotations below ImU32 annoColor = ImGui::ColorConvertFloat4ToU32(ImVec4(254 / 255., 221 / 255., 66 / 255., 1.0)); From 63bc26fcd0ee6c2d6b2263d230e915e9f565e206 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 5 Dec 2025 23:39:36 -0800 Subject: [PATCH 02/18] fix missed rename --- src/color_bar.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/color_bar.cpp b/src/color_bar.cpp index 5556f9dda..7ab3bcca3 100644 --- a/src/color_bar.cpp +++ b/src/color_bar.cpp @@ -151,7 +151,7 @@ void ColorBar::prepareInlineHistogram() { inlineHistogramProgram->setTextureFromColormap("t_colormap", colormap, true); - fillBuffers(); + fillHistogramBuffers(); } @@ -189,8 +189,7 @@ void ColorBar::renderInlineHistogramToTexture() { void ColorBar::buildUI(float width) { - // NOTE: I'm surprised this works, since we're drawing in the middle of imgui's processing. Possible source of bugs? - renderToTexture(); + renderInlineHistogramToTexture(); // Compute size for image float aspect = 4.0; From 47c82835d4416c4a6ee826b6bc4504d4b35b2780 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 5 Dec 2025 23:46:29 -0800 Subject: [PATCH 03/18] WIP more structure --- include/polyscope/color_bar.h | 35 ++++++++++++++++++------ include/polyscope/scalar_quantity.h | 3 ++- include/polyscope/scalar_quantity.ipp | 17 +++++++----- src/color_bar.cpp | 38 ++++++++++++++++++++++++--- src/surface_scalar_quantity.cpp | 12 ++++----- src/volume_mesh_scalar_quantity.cpp | 4 +-- 6 files changed, 81 insertions(+), 28 deletions(-) diff --git a/include/polyscope/color_bar.h b/include/polyscope/color_bar.h index 0619de611..b34fd1995 100644 --- a/include/polyscope/color_bar.h +++ b/include/polyscope/color_bar.h @@ -2,8 +2,10 @@ #pragma once +#include "polyscope/quantity.h" #include "polyscope/render/color_maps.h" #include "polyscope/render/engine.h" +#include "polyscope/widget.h" #include @@ -15,9 +17,8 @@ namespace polyscope { class ColorBar { public: - ColorBar(); // must call buildHistogram() with data after - ColorBar(std::vector& values, DataType datatype); // internally calls buildHistogram() - + ColorBar(Quantity& parent_); // must call buildHistogram() with data after + ColorBar(Quantity& parent_, std::vector& values, DataType datatype); // internally calls buildHistogram() ~ColorBar(); void buildHistogram(const std::vector& values, DataType datatype); @@ -26,16 +27,23 @@ class ColorBar { // Width = -1 means set automatically void buildUI(float width = -1.0); + Quantity& parent; std::pair colormapRange; // in DATA values, not [0,1] + + // Getters and setters + + void setOnscreenColorbarEnabled(bool newEnabled); + bool getOnscreenColorbarEnabled(); + private: - // + // Basic data defining the color map DataType dataType = DataType::STANDARD; std::pair dataRange; // == The inline horizontal histogram visualization in the structures bar - // Manage histogram counts for + // Manage histogram counts void fillHistogramBuffers(); size_t rawHistBinCount = 51; std::vector rawHistCurveY; @@ -53,10 +61,21 @@ class ColorBar { // A few parameters which control appearance float bottomBarHeight = 0.35; float bottomBarGap = 0.1; - + // == The optional vertical colorbar which floats ont he main display - + PersistentValue onscreenColorbarEnabled; + void prepareOnscreenColorBar(); + std::unique_ptr onscreenColorBarWidget = nullptr; +}; + +class OnscreenColorBarWidget : public Widget { +public: + OnscreenColorBarWidget(ColorBar& parent_); + virtual void draw() override; + +private: + ColorBar& parent; }; -}; // namespace polyscope +} // namespace polyscope diff --git a/include/polyscope/scalar_quantity.h b/include/polyscope/scalar_quantity.h index aeb8d146a..42f13dd6a 100644 --- a/include/polyscope/scalar_quantity.h +++ b/include/polyscope/scalar_quantity.h @@ -79,7 +79,8 @@ class ScalarQuantity { std::pair dataRange; PersistentValue vizRangeMin; PersistentValue vizRangeMax; - ColorBar hist; + + ColorBar colorBar; // Parameters PersistentValue cMap; diff --git a/include/polyscope/scalar_quantity.ipp b/include/polyscope/scalar_quantity.ipp index bff31d155..dde0a446c 100644 --- a/include/polyscope/scalar_quantity.ipp +++ b/include/polyscope/scalar_quantity.ipp @@ -12,7 +12,7 @@ ScalarQuantity::ScalarQuantity(QuantityT& quantity_, const std::vecto dataType(dataType_), dataRange(robustMinMax(values.data, 1e-5)), vizRangeMin(quantity.uniquePrefix() + "vizRangeMin", -777.), // set later, vizRangeMax(quantity.uniquePrefix() + "vizRangeMax", -777.), // including clearing cache - cMap(quantity.uniquePrefix() + "cmap", defaultColorMap(dataType)), + colorBar(quantity), cMap(quantity.uniquePrefix() + "cmap", defaultColorMap(dataType)), isolinesEnabled(quantity.uniquePrefix() + "isolinesEnabled", false), isolineStyle(quantity.uniquePrefix() + "isolinesStyle", IsolineStyle::Stripe), isolinePeriod(quantity.uniquePrefix() + "isolinePeriod", @@ -22,8 +22,8 @@ ScalarQuantity::ScalarQuantity(QuantityT& quantity_, const std::vecto { values.checkInvalidValues(); - hist.updateColormap(cMap.get()); - hist.buildHistogram(values.data, dataType); + colorBar.updateColormap(cMap.get()); + colorBar.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 @@ -38,7 +38,7 @@ void ScalarQuantity::buildScalarUI() { if (render::buildColormapSelector(cMap.get())) { quantity.refresh(); - hist.updateColormap(cMap.get()); + colorBar.updateColormap(cMap.get()); setColorMap(getColorMap()); } @@ -82,10 +82,10 @@ void ScalarQuantity::buildScalarUI() { // Draw the histogram of values - hist.colormapRange = std::pair(vizRangeMin.get(), vizRangeMax.get()); + colorBar.colormapRange = std::pair(vizRangeMin.get(), vizRangeMax.get()); float windowWidth = ImGui::GetWindowWidth(); float histWidth = 0.75 * windowWidth; - hist.buildUI(histWidth); + colorBar.buildUI(histWidth); // Data range // Note: %g specifiers are generally nicer than %e, but here we don't acutally have a choice. ImGui (for somewhat @@ -220,6 +220,9 @@ void ScalarQuantity::buildScalarOptionsUI() { if (dataType != DataType::CATEGORICAL) { if (ImGui::MenuItem("Enable isolines", NULL, isolinesEnabled.get())) setIsolinesEnabled(!isolinesEnabled.get()); } + if (ImGui::MenuItem("Onscreen Colormap", NULL, colorBar.getOnscreenColorbarEnabled())) { + colorBar.setOnscreenColorbarEnabled(!colorBar.getOnscreenColorbarEnabled()); + } } template @@ -306,7 +309,7 @@ void ScalarQuantity::updateData(const V& newValues) { template QuantityT* ScalarQuantity::setColorMap(std::string val) { cMap = val; - hist.updateColormap(cMap.get()); + colorBar.updateColormap(cMap.get()); quantity.refresh(); requestRedraw(); return &quantity; diff --git a/src/color_bar.cpp b/src/color_bar.cpp index 7ab3bcca3..355ac6935 100644 --- a/src/color_bar.cpp +++ b/src/color_bar.cpp @@ -13,9 +13,14 @@ namespace polyscope { -ColorBar::ColorBar() {} +ColorBar::ColorBar(Quantity& parent_) + : parent(parent_), onscreenColorbarEnabled(parent.uniquePrefix() + "onscreenColorbarEnabled", false) { + prepareOnscreenColorBar(); +} -ColorBar::ColorBar(std::vector& values, DataType dataType) { buildHistogram(values, dataType); } +ColorBar::ColorBar(Quantity& parent_, std::vector& values, DataType dataType) : ColorBar(parent_) { + buildHistogram(values, dataType); +} ColorBar::~ColorBar() {} @@ -180,10 +185,30 @@ void ColorBar::renderInlineHistogramToTexture() { inlineHistogramProgram->setUniform("u_rangeLow", rangeLow); inlineHistogramProgram->setUniform("u_rangeHigh", rangeHigh); } +} + +void ColorBar::setOnscreenColorbarEnabled(bool newEnabled) { + onscreenColorbarEnabled.set(newEnabled); + if (newEnabled) { + prepareOnscreenColorBar(); + } +} +bool ColorBar::getOnscreenColorbarEnabled() { return onscreenColorbarEnabled.get(); } + +void ColorBar::prepareOnscreenColorBar() { + if (onscreenColorBarWidget) { + // Already created, nothing to do + return; + } + onscreenColorBarWidget = std::unique_ptr(new OnscreenColorBarWidget(*this)); +} +OnscreenColorBarWidget::OnscreenColorBarWidget(ColorBar& parent_) : parent(parent_) {} - // Draw - inlineHistogramProgram->draw(); +void OnscreenColorBarWidget::draw() { + if (parent.getOnscreenColorbarEnabled()) { + std::cout << "drawing color bar" << std::endl; + } } @@ -274,6 +299,11 @@ void ColorBar::buildUI(float width) { } */ + + + // NOTE: the onscreen colorbar gets drawn in the draw() command, rather than with + // the rest of the UI stuff here, because we need it to happen even if this UI + // panel is collapsed. } diff --git a/src/surface_scalar_quantity.cpp b/src/surface_scalar_quantity.cpp index 35948f140..812bcae9b 100644 --- a/src/surface_scalar_quantity.cpp +++ b/src/surface_scalar_quantity.cpp @@ -66,7 +66,7 @@ SurfaceVertexScalarQuantity::SurfaceVertexScalarQuantity(std::string name, const { values.ensureHostBufferPopulated(); - hist.buildHistogram(values.data, dataType); + colorBar.buildHistogram(values.data, dataType); } void SurfaceVertexScalarQuantity::createProgram() { @@ -135,7 +135,7 @@ SurfaceFaceScalarQuantity::SurfaceFaceScalarQuantity(std::string name, const std { values.ensureHostBufferPopulated(); parent.faceAreas.ensureHostBufferPopulated(); - hist.buildHistogram(values.data, dataType); + colorBar.buildHistogram(values.data, dataType); } void SurfaceFaceScalarQuantity::createProgram() { @@ -184,7 +184,7 @@ SurfaceEdgeScalarQuantity::SurfaceEdgeScalarQuantity(std::string name, const std { values.ensureHostBufferPopulated(); - hist.buildHistogram(values.data, dataType); + colorBar.buildHistogram(values.data, dataType); } void SurfaceEdgeScalarQuantity::createProgram() { @@ -229,7 +229,7 @@ SurfaceHalfedgeScalarQuantity::SurfaceHalfedgeScalarQuantity(std::string name, c { values.ensureHostBufferPopulated(); - hist.buildHistogram(values.data, dataType); + colorBar.buildHistogram(values.data, dataType); } void SurfaceHalfedgeScalarQuantity::createProgram() { @@ -274,7 +274,7 @@ SurfaceCornerScalarQuantity::SurfaceCornerScalarQuantity(std::string name, const { values.ensureHostBufferPopulated(); - hist.buildHistogram(values.data, dataType); + colorBar.buildHistogram(values.data, dataType); } void SurfaceCornerScalarQuantity::createProgram() { @@ -343,7 +343,7 @@ SurfaceTextureScalarQuantity::SurfaceTextureScalarQuantity(std::string name, Sur TextureMapQuantity(*this, dimX_, dimY_, origin_), param(param_) { values.setTextureSize(dimX, dimY); values.ensureHostBufferPopulated(); - hist.buildHistogram(values.data, dataType); + colorBar.buildHistogram(values.data, dataType); if (dataType == DataType::CATEGORICAL) { // default to nearest filtering for categorical data, it's probably what the user wants diff --git a/src/volume_mesh_scalar_quantity.cpp b/src/volume_mesh_scalar_quantity.cpp index 3170b2f28..127d89015 100644 --- a/src/volume_mesh_scalar_quantity.cpp +++ b/src/volume_mesh_scalar_quantity.cpp @@ -217,8 +217,8 @@ void VolumeMeshVertexScalarQuantity::buildCustomUI() { VolumeMeshScalarQuantity::buildCustomUI(); if (isDrawingLevelSet) { - ImGui::DragFloat("##value", &levelSetValue, 0.01f, (float)hist.colormapRange.first, - (float)hist.colormapRange.second); + ImGui::DragFloat("##value", &levelSetValue, 0.01f, (float)colorBar.colormapRange.first, + (float)colorBar.colormapRange.second); if (ImGui::BeginMenu("Show Quantity")) { std::map>::iterator it; for (it = parent.quantities.begin(); it != parent.quantities.end(); it++) { From f0f660831bfca88d1dc706c6b2b4a36d29967221 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 5 Dec 2025 23:59:23 -0800 Subject: [PATCH 04/18] remove unused code, fix bug --- include/polyscope/color_bar.h | 1 - src/color_bar.cpp | 10 +++------- src/surface_scalar_quantity.cpp | 28 +++++----------------------- 3 files changed, 8 insertions(+), 31 deletions(-) diff --git a/include/polyscope/color_bar.h b/include/polyscope/color_bar.h index b34fd1995..a212a71ad 100644 --- a/include/polyscope/color_bar.h +++ b/include/polyscope/color_bar.h @@ -18,7 +18,6 @@ namespace polyscope { class ColorBar { public: ColorBar(Quantity& parent_); // must call buildHistogram() with data after - ColorBar(Quantity& parent_, std::vector& values, DataType datatype); // internally calls buildHistogram() ~ColorBar(); void buildHistogram(const std::vector& values, DataType datatype); diff --git a/src/color_bar.cpp b/src/color_bar.cpp index 355ac6935..b0945323f 100644 --- a/src/color_bar.cpp +++ b/src/color_bar.cpp @@ -14,13 +14,7 @@ namespace polyscope { ColorBar::ColorBar(Quantity& parent_) - : parent(parent_), onscreenColorbarEnabled(parent.uniquePrefix() + "onscreenColorbarEnabled", false) { - prepareOnscreenColorBar(); -} - -ColorBar::ColorBar(Quantity& parent_, std::vector& values, DataType dataType) : ColorBar(parent_) { - buildHistogram(values, dataType); -} + : parent(parent_), onscreenColorbarEnabled(parent.uniquePrefix() + "onscreenColorbarEnabled", false) {} ColorBar::~ColorBar() {} @@ -185,6 +179,8 @@ void ColorBar::renderInlineHistogramToTexture() { inlineHistogramProgram->setUniform("u_rangeLow", rangeLow); inlineHistogramProgram->setUniform("u_rangeHigh", rangeHigh); } + + inlineHistogramProgram->draw(); } void ColorBar::setOnscreenColorbarEnabled(bool newEnabled) { diff --git a/src/surface_scalar_quantity.cpp b/src/surface_scalar_quantity.cpp index 812bcae9b..894c5f57c 100644 --- a/src/surface_scalar_quantity.cpp +++ b/src/surface_scalar_quantity.cpp @@ -64,10 +64,7 @@ SurfaceVertexScalarQuantity::SurfaceVertexScalarQuantity(std::string name, const SurfaceMesh& mesh_, DataType dataType_) : SurfaceScalarQuantity(name, mesh_, "vertex", values_, dataType_) -{ - values.ensureHostBufferPopulated(); - colorBar.buildHistogram(values.data, dataType); -} +{} void SurfaceVertexScalarQuantity::createProgram() { // Create the program to draw this quantity @@ -132,11 +129,7 @@ SurfaceFaceScalarQuantity::SurfaceFaceScalarQuantity(std::string name, const std SurfaceMesh& mesh_, DataType dataType_) : SurfaceScalarQuantity(name, mesh_, "face", values_, dataType_) -{ - values.ensureHostBufferPopulated(); - parent.faceAreas.ensureHostBufferPopulated(); - colorBar.buildHistogram(values.data, dataType); -} +{} void SurfaceFaceScalarQuantity::createProgram() { // Create the program to draw this quantity @@ -182,10 +175,7 @@ SurfaceEdgeScalarQuantity::SurfaceEdgeScalarQuantity(std::string name, const std SurfaceMesh& mesh_, DataType dataType_) : SurfaceScalarQuantity(name, mesh_, "edge", values_, dataType_) -{ - values.ensureHostBufferPopulated(); - colorBar.buildHistogram(values.data, dataType); -} +{} void SurfaceEdgeScalarQuantity::createProgram() { // Create the program to draw this quantity @@ -227,10 +217,7 @@ SurfaceHalfedgeScalarQuantity::SurfaceHalfedgeScalarQuantity(std::string name, c SurfaceMesh& mesh_, DataType dataType_) : SurfaceScalarQuantity(name, mesh_, "halfedge", values_, dataType_) -{ - values.ensureHostBufferPopulated(); - colorBar.buildHistogram(values.data, dataType); -} +{} void SurfaceHalfedgeScalarQuantity::createProgram() { // Create the program to draw this quantity @@ -272,10 +259,7 @@ SurfaceCornerScalarQuantity::SurfaceCornerScalarQuantity(std::string name, const SurfaceMesh& mesh_, DataType dataType_) : SurfaceScalarQuantity(name, mesh_, "corner", values_, dataType_) -{ - values.ensureHostBufferPopulated(); - colorBar.buildHistogram(values.data, dataType); -} +{} void SurfaceCornerScalarQuantity::createProgram() { // Create the program to draw this quantity @@ -342,8 +326,6 @@ SurfaceTextureScalarQuantity::SurfaceTextureScalarQuantity(std::string name, Sur : SurfaceScalarQuantity(name, mesh_, "vertex", values_, dataType_), TextureMapQuantity(*this, dimX_, dimY_, origin_), param(param_) { values.setTextureSize(dimX, dimY); - values.ensureHostBufferPopulated(); - colorBar.buildHistogram(values.data, dataType); if (dataType == DataType::CATEGORICAL) { // default to nearest filtering for categorical data, it's probably what the user wants From 711cb60e22e60a66ca96b7e536ccd05d8db29a52 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 7 Dec 2025 00:35:44 -0800 Subject: [PATCH 05/18] add colormap texture getter --- include/polyscope/render/engine.h | 1 + src/render/engine.cpp | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/include/polyscope/render/engine.h b/include/polyscope/render/engine.h index 48a7493cc..5272b4f4d 100644 --- a/include/polyscope/render/engine.h +++ b/include/polyscope/render/engine.h @@ -614,6 +614,7 @@ class Engine { std::vector> colorMaps; const ValueColorMap& getColorMap(const std::string& name); void loadColorMap(std::string cmapName, std::string filename); + std::shared_ptr getColorMapTexture2d(const std::string& cmapName); // Helpers std::vector screenTrianglesCoords(); // two triangles which cover the screen diff --git a/src/render/engine.cpp b/src/render/engine.cpp index c959a2628..1c82db01e 100644 --- a/src/render/engine.cpp +++ b/src/render/engine.cpp @@ -356,7 +356,7 @@ void Engine::buildEngineGui() { options::ssaaFactor = ssaaFactor; requestRedraw(); } - + if (ImGui::InputFloat("UI Scale", &options::uiScale, 0.25f)) { options::uiScale = std::min(options::uiScale, 4.f); options::uiScale = std::max(options::uiScale, 0.25f); @@ -1160,6 +1160,25 @@ void Engine::loadDefaultColorMaps() { loadDefaultColorMap("hsv"); } +std::shared_ptr Engine::getColorMapTexture2d(const std::string& cmapName) { + const ValueColorMap& colormap = render::engine->getColorMap(cmapName); + + // Fill a buffer with the data + unsigned int dataLength = colormap.values.size() * 3; + std::vector colorBuffer(dataLength); + for (unsigned int i = 0; i < colormap.values.size(); i++) { + colorBuffer[3 * i + 0] = static_cast(colormap.values[i][0]); + colorBuffer[3 * i + 1] = static_cast(colormap.values[i][1]); + colorBuffer[3 * i + 2] = static_cast(colormap.values[i][2]); + } + + std::shared_ptr textureBuffer = + engine->generateTextureBuffer(TextureFormat::RGB32F, 1, colormap.values.size(), &(colorBuffer[0])); + + textureBuffer->setFilterMode(FilterMode::Linear); + + return textureBuffer; +} void Engine::showTextureInImGuiWindow(std::string windowName, TextureBuffer* buffer) { ImGui::Begin(windowName.c_str()); From 52e1ec38076d233885bc12b45c66199bb81f77ff Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 7 Dec 2025 00:36:12 -0800 Subject: [PATCH 06/18] more window pane placement logic --- src/internal.cpp | 8 ++++++ src/polyscope.cpp | 68 ++++++++++++++++++++++++++--------------------- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/internal.cpp b/src/internal.cpp index fb874d626..29e9c9f84 100644 --- a/src/internal.cpp +++ b/src/internal.cpp @@ -14,5 +14,13 @@ uint64_t getNextUniqueID() { return uniqueID++; } bool& pointCloudEfficiencyWarningReported = state::globalContext.pointCloudEfficiencyWarningReported; FloatingQuantityStructure*& globalFloatingQuantityStructure = state::globalContext.globalFloatingQuantityStructure; +float imguiStackMargin = 10; +float lastWindowHeightPolyscope = 200; +float lastWindowHeightUser = 200; +float lastRightSideFreeX = 10; +float lastRightSideFreeY = 10; +float leftWindowsWidth = -1.; +float rightWindowsWidth = -1.; + } // namespace internal } // namespace polyscope diff --git a/src/polyscope.cpp b/src/polyscope.cpp index 9d9b360e6..209dbfb24 100644 --- a/src/polyscope.cpp +++ b/src/polyscope.cpp @@ -45,13 +45,8 @@ bool redrawNextFrame = true; bool unshowRequested = false; // Some state about imgui windows to stack them -float imguiStackMargin = 10; -float lastWindowHeightPolyscope = 200; -float lastWindowHeightUser = 200; constexpr float INITIAL_LEFT_WINDOWS_WIDTH = 305; constexpr float INITIAL_RIGHT_WINDOWS_WIDTH = 500; -float leftWindowsWidth = -1.; -float rightWindowsWidth = -1.; auto prevMainLoopTime = std::chrono::steady_clock::now(); float rollingMainLoopDurationMicrosec = 0.; @@ -137,12 +132,12 @@ void writePrefsFile() { } void setInitialWindowWidths() { - leftWindowsWidth = INITIAL_LEFT_WINDOWS_WIDTH * options::uiScale; - rightWindowsWidth = INITIAL_RIGHT_WINDOWS_WIDTH * options::uiScale; + internal::leftWindowsWidth = INITIAL_LEFT_WINDOWS_WIDTH * options::uiScale; + internal::rightWindowsWidth = INITIAL_RIGHT_WINDOWS_WIDTH * options::uiScale; } void ensureWindowWidthsSet() { - if (leftWindowsWidth <= 0. || rightWindowsWidth <= 0.) { + if (internal::leftWindowsWidth <= 0. || internal::rightWindowsWidth <= 0.) { setInitialWindowWidths(); } } @@ -662,14 +657,15 @@ void userGuiBegin() { ImVec2 userGuiLoc; if (options::userGuiIsOnRightSide) { // right side - userGuiLoc = ImVec2(view::windowWidth - (rightWindowsWidth + imguiStackMargin), imguiStackMargin); - ImGui::SetNextWindowSize(ImVec2(rightWindowsWidth, 0.)); + userGuiLoc = ImVec2(view::windowWidth - (internal::rightWindowsWidth + internal::imguiStackMargin), + internal::imguiStackMargin); + ImGui::SetNextWindowSize(ImVec2(internal::rightWindowsWidth, 0.)); } else { // left side if (options::buildDefaultGuiPanels) { - userGuiLoc = ImVec2(leftWindowsWidth + 3 * imguiStackMargin, imguiStackMargin); + userGuiLoc = ImVec2(internal::leftWindowsWidth + 3 * internal::imguiStackMargin, internal::imguiStackMargin); } else { - userGuiLoc = ImVec2(imguiStackMargin, imguiStackMargin); + userGuiLoc = ImVec2(internal::imguiStackMargin, internal::imguiStackMargin); } } @@ -682,10 +678,15 @@ void userGuiBegin() { void userGuiEnd() { if (options::userGuiIsOnRightSide) { - rightWindowsWidth = INITIAL_RIGHT_WINDOWS_WIDTH * options::uiScale; - lastWindowHeightUser = imguiStackMargin + ImGui::GetWindowHeight(); + internal::rightWindowsWidth = INITIAL_RIGHT_WINDOWS_WIDTH * options::uiScale; + internal::lastWindowHeightUser = + internal::imguiStackMargin + ImGui::GetWindowHeight(); // TODO using deprecated function + internal::lastRightSideFreeX = view::windowWidth - internal::imguiStackMargin; + internal::lastRightSideFreeY = internal::lastWindowHeightUser + internal::imguiStackMargin; } else { - lastWindowHeightUser = 0; + internal::lastWindowHeightUser = 0; + internal::lastRightSideFreeX = view::windowWidth - internal::imguiStackMargin; + internal::lastRightSideFreeY = internal::imguiStackMargin; } ImGui::End(); ImGui::PopID(); @@ -698,8 +699,8 @@ void buildPolyscopeGui() { // Create window static bool showPolyscopeWindow = true; - ImGui::SetNextWindowPos(ImVec2(imguiStackMargin, imguiStackMargin)); - ImGui::SetNextWindowSize(ImVec2(leftWindowsWidth, 0.)); + ImGui::SetNextWindowPos(ImVec2(internal::imguiStackMargin, internal::imguiStackMargin)); + ImGui::SetNextWindowSize(ImVec2(internal::leftWindowsWidth, 0.)); ImGui::Begin("Polyscope", &showPolyscopeWindow); @@ -740,7 +741,8 @@ void buildPolyscopeGui() { } if (ImGui::IsItemHovered()) { - ImGui::SetNextWindowPos(ImVec2(2 * imguiStackMargin + leftWindowsWidth, imguiStackMargin)); + ImGui::SetNextWindowPos( + ImVec2(2 * internal::imguiStackMargin + internal::leftWindowsWidth, internal::imguiStackMargin)); ImGui::SetNextWindowSize(ImVec2(0., 0.)); // clang-format off @@ -848,8 +850,8 @@ void buildPolyscopeGui() { } - lastWindowHeightPolyscope = imguiStackMargin + ImGui::GetWindowHeight(); - leftWindowsWidth = ImGui::GetWindowWidth(); + internal::lastWindowHeightPolyscope = internal::imguiStackMargin + ImGui::GetWindowHeight(); + internal::leftWindowsWidth = ImGui::GetWindowWidth(); ImGui::End(); } @@ -860,9 +862,10 @@ void buildStructureGui() { // Create window static bool showStructureWindow = true; - ImGui::SetNextWindowPos(ImVec2(imguiStackMargin, lastWindowHeightPolyscope + 2 * imguiStackMargin)); - ImGui::SetNextWindowSize( - ImVec2(leftWindowsWidth, view::windowHeight - lastWindowHeightPolyscope - 3 * imguiStackMargin)); + ImGui::SetNextWindowPos( + ImVec2(internal::imguiStackMargin, internal::lastWindowHeightPolyscope + 2 * internal::imguiStackMargin)); + ImGui::SetNextWindowSize(ImVec2(internal::leftWindowsWidth, view::windowHeight - internal::lastWindowHeightPolyscope - + 3 * internal::imguiStackMargin)); ImGui::Begin("Structures", &showStructureWindow); // only show groups if there are any @@ -920,7 +923,7 @@ void buildStructureGui() { ImGui::PopID(); } - leftWindowsWidth = ImGui::GetWindowWidth(); + internal::leftWindowsWidth = ImGui::GetWindowWidth(); ImGui::End(); } @@ -930,9 +933,9 @@ void buildPickGui() { if (haveSelection()) { - ImGui::SetNextWindowPos(ImVec2(view::windowWidth - (rightWindowsWidth + imguiStackMargin), - 2 * imguiStackMargin + lastWindowHeightUser)); - ImGui::SetNextWindowSize(ImVec2(rightWindowsWidth, 0.)); + ImGui::SetNextWindowPos(ImVec2(view::windowWidth - (internal::rightWindowsWidth + internal::imguiStackMargin), + internal::lastRightSideFreeY + internal::imguiStackMargin)); + ImGui::SetNextWindowSize(ImVec2(internal::rightWindowsWidth, 0.)); ImGui::Begin("Selection", nullptr); PickResult selection = getSelection(); @@ -957,7 +960,8 @@ void buildPickGui() { ImGui::TextUnformatted("ERROR: INVALID STRUCTURE"); } - rightWindowsWidth = ImGui::GetWindowWidth(); + internal::rightWindowsWidth = ImGui::GetWindowWidth(); + internal::lastRightSideFreeY += 2 * internal::imguiStackMargin + ImGui::GetWindowHeight(); ImGui::End(); } } @@ -981,11 +985,15 @@ void buildUserGuiAndInvokeCallback() { if (beganUserGUI) { userGuiEnd(); } else { - lastWindowHeightUser = imguiStackMargin; + internal::lastWindowHeightUser = internal::imguiStackMargin; + internal::lastRightSideFreeX = view::windowWidth - internal::imguiStackMargin; + internal::lastRightSideFreeY = internal::imguiStackMargin; } } else { - lastWindowHeightUser = imguiStackMargin; + internal::lastWindowHeightUser = internal::imguiStackMargin; + internal::lastRightSideFreeX = view::windowWidth - internal::imguiStackMargin; + internal::lastRightSideFreeY = internal::imguiStackMargin; } } From 0161d29e4d0cbb9304565278727786b057046d41 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 7 Dec 2025 00:36:29 -0800 Subject: [PATCH 07/18] missed header from pane placement --- include/polyscope/internal.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/polyscope/internal.h b/include/polyscope/internal.h index 962183388..e57ffa821 100644 --- a/include/polyscope/internal.h +++ b/include/polyscope/internal.h @@ -26,5 +26,15 @@ extern bool& pointCloudEfficiencyWarningReported; // global members extern FloatingQuantityStructure*& globalFloatingQuantityStructure; + +// == UI and layout related +extern float imguiStackMargin; +extern float lastWindowHeightPolyscope; +extern float lastWindowHeightUser; +extern float lastRightSideFreeX; +extern float lastRightSideFreeY; +extern float leftWindowsWidth; +extern float rightWindowsWidth; + } // namespace internal } // namespace polyscope From 47b21381f4ad622168a63161117ba1990d7e2c2e Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 7 Dec 2025 00:36:49 -0800 Subject: [PATCH 08/18] working version of onscreen colorbar --- include/polyscope/color_bar.h | 4 +++ src/color_bar.cpp | 60 ++++++++++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/include/polyscope/color_bar.h b/include/polyscope/color_bar.h index a212a71ad..c66d3d4c6 100644 --- a/include/polyscope/color_bar.h +++ b/include/polyscope/color_bar.h @@ -15,8 +15,11 @@ namespace polyscope { // A histogram that shows up in ImGUI window // ONEDAY: we could definitely make a better histogram widget for categorical data... +class OncreenColorBarWidget; + class ColorBar { public: + friend class OnscreenColorBarWidget; ColorBar(Quantity& parent_); // must call buildHistogram() with data after ~ColorBar(); @@ -63,6 +66,7 @@ class ColorBar { // == The optional vertical colorbar which floats ont he main display PersistentValue onscreenColorbarEnabled; + std::shared_ptr cmapTexture; // this is just the plain colormap rgb void prepareOnscreenColorBar(); std::unique_ptr onscreenColorBarWidget = nullptr; }; diff --git a/src/color_bar.cpp b/src/color_bar.cpp index b0945323f..ff1157476 100644 --- a/src/color_bar.cpp +++ b/src/color_bar.cpp @@ -81,9 +81,8 @@ void ColorBar::buildHistogram(const std::vector& values, DataType dataTyp void ColorBar::updateColormap(const std::string& newColormap) { colormap = newColormap; - if (inlineHistogramProgram) { - inlineHistogramProgram.reset(); - } + inlineHistogramProgram.reset(); + cmapTexture.reset(); } void ColorBar::fillHistogramBuffers() { @@ -202,9 +201,60 @@ void ColorBar::prepareOnscreenColorBar() { OnscreenColorBarWidget::OnscreenColorBarWidget(ColorBar& parent_) : parent(parent_) {} void OnscreenColorBarWidget::draw() { - if (parent.getOnscreenColorbarEnabled()) { - std::cout << "drawing color bar" << std::endl; + if (!parent.parent.isEnabled()) return; + if (!parent.getOnscreenColorbarEnabled()) return; + + if (!parent.cmapTexture) { + parent.cmapTexture = render::engine->getColorMapTexture2d(parent.colormap); } + + // Draw the color bar + + // NOTE: right nwo the size of the colorbar is scaled by uiScale, but the positioning/margins are not. This is + // consistent with the other positioning/marching logic in the main gui panels. (Maybe they should all be scaled?) + float barRegionWidth = 30.0f * options::uiScale; + float tickRegionWidth = 80.0f * options::uiScale; + float marginWidth = 10.0f * options::uiScale; + float barRegionHeight = 300.0f * options::uiScale; + float borderWidth = 2.0f * options::uiScale; + ImVec2 barTopLeft(internal::lastRightSideFreeX - barRegionWidth - tickRegionWidth - marginWidth, + internal::lastRightSideFreeY + marginWidth); + + ImDrawList* dl = ImGui::GetBackgroundDrawList(); + + // Add a semi-transparent background + dl->AddRectFilled(ImVec2(barTopLeft.x - marginWidth, barTopLeft.y - marginWidth), + ImVec2(barTopLeft.x + barRegionWidth + tickRegionWidth + marginWidth, + barTopLeft.y + barRegionHeight + marginWidth), + IM_COL32(255, 255, 255, 200)); + + render::engine->preserveResourceUntilImguiFrameCompletes(parent.cmapTexture); + dl->AddImage((ImTextureID)(intptr_t)parent.cmapTexture->getNativeHandle(), ImVec2(barTopLeft.x, barTopLeft.y), + ImVec2(barTopLeft.x + barRegionWidth, barTopLeft.y + barRegionHeight), ImVec2(0, 1), ImVec2(1, 0)); + + dl->AddRect(barTopLeft, ImVec2(barTopLeft.x + barRegionWidth, barTopLeft.y + barRegionHeight), IM_COL32(0, 0, 0, 255), + 0.f, ImDrawFlags_None, borderWidth); + + // draw text ticks + int nTicks = 5; + for (int i = 0; i < nTicks; i++) { + float t = (float)i / (float)(nTicks - 1); + float yPos = barTopLeft.y + t * (barRegionHeight - 0.5 * borderWidth); + double val = parent.colormapRange.second - t * (parent.colormapRange.second - parent.colormapRange.first); + char buffer[64]; + snprintf(buffer, sizeof(buffer), "%.3g", val); + + // Make a little tick mark + dl->AddLine(ImVec2(barTopLeft.x + barRegionWidth - borderWidth, yPos), + ImVec2(barTopLeft.x + barRegionWidth + 5.0f, yPos), IM_COL32(0, 0, 0, 255), borderWidth); + + // Draw the actual text + ImVec2 textSize = ImGui::CalcTextSize(buffer); + dl->AddText(ImVec2(barTopLeft.x + barRegionWidth + 10.0f, yPos - textSize.y / 2), IM_COL32(0, 0, 0, 255), buffer); + } + + internal::lastRightSideFreeX -= + (barRegionWidth + tickRegionWidth + 2 * marginWidth + 10.0f) + 0.5 * barRegionWidth; // last bit is some padding } From 428c406a82a3049ab3e3c6c49b2bda0008579885 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 7 Dec 2025 00:37:03 -0800 Subject: [PATCH 09/18] expose setters for onscreen colorbar --- include/polyscope/scalar_quantity.h | 2 ++ include/polyscope/scalar_quantity.ipp | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/include/polyscope/scalar_quantity.h b/include/polyscope/scalar_quantity.h index 42f13dd6a..5da8c2ddb 100644 --- a/include/polyscope/scalar_quantity.h +++ b/include/polyscope/scalar_quantity.h @@ -45,6 +45,8 @@ class ScalarQuantity { // The color map QuantityT* setColorMap(std::string val); std::string getColorMap(); + QuantityT* setOnscreenColorbarEnabled(bool newEnabled); + bool getOnscreenColorbarEnabled(); // Data limits mapped in to colormap QuantityT* setMapRange(std::pair val); diff --git a/include/polyscope/scalar_quantity.ipp b/include/polyscope/scalar_quantity.ipp index dde0a446c..cdf64334f 100644 --- a/include/polyscope/scalar_quantity.ipp +++ b/include/polyscope/scalar_quantity.ipp @@ -319,6 +319,18 @@ std::string ScalarQuantity::getColorMap() { return cMap.get(); } + +template +QuantityT* ScalarQuantity::setOnscreenColorbarEnabled(bool newEnabled) { + colorBar.setOnscreenColorbarEnabled(newEnabled); + requestRedraw(); + return &quantity; +} +template +bool ScalarQuantity::getOnscreenColorbarEnabled() { + return colorBar.getOnscreenColorbarEnabled(); +} + template QuantityT* ScalarQuantity::setMapRange(std::pair val) { vizRangeMin = val.first; From 27567682f9ba4dc2475a0bb0d905da33ae0d67cd Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 7 Dec 2025 00:46:53 -0800 Subject: [PATCH 10/18] style tweaks --- src/color_bar.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/color_bar.cpp b/src/color_bar.cpp index ff1157476..ad1cf41ee 100644 --- a/src/color_bar.cpp +++ b/src/color_bar.cpp @@ -213,7 +213,7 @@ void OnscreenColorBarWidget::draw() { // NOTE: right nwo the size of the colorbar is scaled by uiScale, but the positioning/margins are not. This is // consistent with the other positioning/marching logic in the main gui panels. (Maybe they should all be scaled?) float barRegionWidth = 30.0f * options::uiScale; - float tickRegionWidth = 80.0f * options::uiScale; + float tickRegionWidth = 90.0f * options::uiScale; float marginWidth = 10.0f * options::uiScale; float barRegionHeight = 300.0f * options::uiScale; float borderWidth = 2.0f * options::uiScale; @@ -221,17 +221,20 @@ void OnscreenColorBarWidget::draw() { internal::lastRightSideFreeY + marginWidth); ImDrawList* dl = ImGui::GetBackgroundDrawList(); + ImU32 backgroundColor = IM_COL32(255, 255, 255, 180); - // Add a semi-transparent background + // dd a semi-transparent background dl->AddRectFilled(ImVec2(barTopLeft.x - marginWidth, barTopLeft.y - marginWidth), ImVec2(barTopLeft.x + barRegionWidth + tickRegionWidth + marginWidth, barTopLeft.y + barRegionHeight + marginWidth), - IM_COL32(255, 255, 255, 200)); + backgroundColor); + // draw the actual colormap gradient rectangle render::engine->preserveResourceUntilImguiFrameCompletes(parent.cmapTexture); dl->AddImage((ImTextureID)(intptr_t)parent.cmapTexture->getNativeHandle(), ImVec2(barTopLeft.x, barTopLeft.y), ImVec2(barTopLeft.x + barRegionWidth, barTopLeft.y + barRegionHeight), ImVec2(0, 1), ImVec2(1, 0)); + // draw the border around the map dl->AddRect(barTopLeft, ImVec2(barTopLeft.x + barRegionWidth, barTopLeft.y + barRegionHeight), IM_COL32(0, 0, 0, 255), 0.f, ImDrawFlags_None, borderWidth); @@ -242,15 +245,15 @@ void OnscreenColorBarWidget::draw() { float yPos = barTopLeft.y + t * (barRegionHeight - 0.5 * borderWidth); double val = parent.colormapRange.second - t * (parent.colormapRange.second - parent.colormapRange.first); char buffer[64]; - snprintf(buffer, sizeof(buffer), "%.3g", val); + snprintf(buffer, sizeof(buffer), "%.4g", val); // Make a little tick mark dl->AddLine(ImVec2(barTopLeft.x + barRegionWidth - borderWidth, yPos), - ImVec2(barTopLeft.x + barRegionWidth + 5.0f, yPos), IM_COL32(0, 0, 0, 255), borderWidth); + ImVec2(barTopLeft.x + barRegionWidth + 5.0f, yPos), IM_COL32_BLACK, borderWidth); // Draw the actual text ImVec2 textSize = ImGui::CalcTextSize(buffer); - dl->AddText(ImVec2(barTopLeft.x + barRegionWidth + 10.0f, yPos - textSize.y / 2), IM_COL32(0, 0, 0, 255), buffer); + dl->AddText(ImVec2(barTopLeft.x + barRegionWidth + 10.0f, yPos - textSize.y / 2), IM_COL32_BLACK, buffer); } internal::lastRightSideFreeX -= From 03b306e9a88c5434262b88af50c0403c1ad576ae Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 7 Dec 2025 00:55:51 -0800 Subject: [PATCH 11/18] unrelated spacing fixes --- src/polyscope.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/polyscope.cpp b/src/polyscope.cpp index 209dbfb24..171f8ee3a 100644 --- a/src/polyscope.cpp +++ b/src/polyscope.cpp @@ -682,11 +682,11 @@ void userGuiEnd() { internal::lastWindowHeightUser = internal::imguiStackMargin + ImGui::GetWindowHeight(); // TODO using deprecated function internal::lastRightSideFreeX = view::windowWidth - internal::imguiStackMargin; - internal::lastRightSideFreeY = internal::lastWindowHeightUser + internal::imguiStackMargin; + internal::lastRightSideFreeY = internal::lastWindowHeightUser; } else { internal::lastWindowHeightUser = 0; internal::lastRightSideFreeX = view::windowWidth - internal::imguiStackMargin; - internal::lastRightSideFreeY = internal::imguiStackMargin; + internal::lastRightSideFreeY = 0; } ImGui::End(); ImGui::PopID(); @@ -850,7 +850,7 @@ void buildPolyscopeGui() { } - internal::lastWindowHeightPolyscope = internal::imguiStackMargin + ImGui::GetWindowHeight(); + internal::lastWindowHeightPolyscope = ImGui::GetWindowHeight(); internal::leftWindowsWidth = ImGui::GetWindowWidth(); ImGui::End(); @@ -961,7 +961,7 @@ void buildPickGui() { } internal::rightWindowsWidth = ImGui::GetWindowWidth(); - internal::lastRightSideFreeY += 2 * internal::imguiStackMargin + ImGui::GetWindowHeight(); + internal::lastRightSideFreeY += internal::imguiStackMargin + ImGui::GetWindowHeight(); ImGui::End(); } } From 38c7ab385c419e46386de9416c78718ca2e00788 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 7 Dec 2025 00:55:58 -0800 Subject: [PATCH 12/18] more style tuning --- src/color_bar.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/color_bar.cpp b/src/color_bar.cpp index ad1cf41ee..179182869 100644 --- a/src/color_bar.cpp +++ b/src/color_bar.cpp @@ -217,8 +217,9 @@ void OnscreenColorBarWidget::draw() { float marginWidth = 10.0f * options::uiScale; float barRegionHeight = 300.0f * options::uiScale; float borderWidth = 2.0f * options::uiScale; + float tickWidth = 5.0f * options::uiScale; ImVec2 barTopLeft(internal::lastRightSideFreeX - barRegionWidth - tickRegionWidth - marginWidth, - internal::lastRightSideFreeY + marginWidth); + internal::lastRightSideFreeY + marginWidth + internal::imguiStackMargin); ImDrawList* dl = ImGui::GetBackgroundDrawList(); ImU32 backgroundColor = IM_COL32(255, 255, 255, 180); @@ -249,15 +250,15 @@ void OnscreenColorBarWidget::draw() { // Make a little tick mark dl->AddLine(ImVec2(barTopLeft.x + barRegionWidth - borderWidth, yPos), - ImVec2(barTopLeft.x + barRegionWidth + 5.0f, yPos), IM_COL32_BLACK, borderWidth); + ImVec2(barTopLeft.x + barRegionWidth + tickWidth, yPos), IM_COL32_BLACK, borderWidth); // Draw the actual text ImVec2 textSize = ImGui::CalcTextSize(buffer); - dl->AddText(ImVec2(barTopLeft.x + barRegionWidth + 10.0f, yPos - textSize.y / 2), IM_COL32_BLACK, buffer); + dl->AddText(ImVec2(barTopLeft.x + barRegionWidth + 2.f * tickWidth, yPos - textSize.y / 2), IM_COL32_BLACK, buffer); } internal::lastRightSideFreeX -= - (barRegionWidth + tickRegionWidth + 2 * marginWidth + 10.0f) + 0.5 * barRegionWidth; // last bit is some padding + (barRegionWidth + tickRegionWidth + marginWidth + 2.f * tickWidth) + internal::imguiStackMargin; } From 7589e6c4f79c348e2937dd7c7c565ce9a6da9adc Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 7 Dec 2025 01:27:18 -0800 Subject: [PATCH 13/18] add export function --- include/polyscope/color_bar.h | 1 + include/polyscope/render/color_maps.h | 7 ++- include/polyscope/scalar_quantity.h | 3 ++ include/polyscope/scalar_quantity.ipp | 11 +++++ src/color_bar.cpp | 66 +++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 2 deletions(-) diff --git a/include/polyscope/color_bar.h b/include/polyscope/color_bar.h index c66d3d4c6..d350190cd 100644 --- a/include/polyscope/color_bar.h +++ b/include/polyscope/color_bar.h @@ -32,6 +32,7 @@ class ColorBar { Quantity& parent; std::pair colormapRange; // in DATA values, not [0,1] + void exportColorbarToSVG(const std::string& filename); // Getters and setters diff --git a/include/polyscope/render/color_maps.h b/include/polyscope/render/color_maps.h index ebd59f279..660089e36 100644 --- a/include/polyscope/render/color_maps.h +++ b/include/polyscope/render/color_maps.h @@ -81,8 +81,11 @@ struct ValueColorMap { double scaledVal = val * (values.size() - 1); double lowerVal = std::floor(scaledVal); double upperBlendVal = scaledVal - lowerVal; - unsigned int lowerInd = static_cast(lowerVal); - unsigned int upperInd = lowerInd + 1; + int lowerInd = static_cast(lowerVal); + int upperInd = lowerInd + 1; + + lowerInd = std::min(std::max(0, lowerInd), (int)values.size() - 1); + upperInd = std::min(std::max(0, upperInd), (int)values.size() - 1); return (float)(1.0 - upperBlendVal) * values[lowerInd] + (float)upperBlendVal * values[upperInd]; } diff --git a/include/polyscope/scalar_quantity.h b/include/polyscope/scalar_quantity.h index 5da8c2ddb..aa2cda386 100644 --- a/include/polyscope/scalar_quantity.h +++ b/include/polyscope/scalar_quantity.h @@ -33,6 +33,9 @@ class ScalarQuantity { template void updateData(const V& newValues); + // Export the current colorbar as an SVG file + void exportColorbarToSVG(const std::string& filename); + // === Members QuantityT& quantity; diff --git a/include/polyscope/scalar_quantity.ipp b/include/polyscope/scalar_quantity.ipp index cdf64334f..a2f2e91c5 100644 --- a/include/polyscope/scalar_quantity.ipp +++ b/include/polyscope/scalar_quantity.ipp @@ -223,6 +223,11 @@ void ScalarQuantity::buildScalarOptionsUI() { if (ImGui::MenuItem("Onscreen Colormap", NULL, colorBar.getOnscreenColorbarEnabled())) { colorBar.setOnscreenColorbarEnabled(!colorBar.getOnscreenColorbarEnabled()); } + if (ImGui::MenuItem("Export Colormap", NULL, colorBar.getOnscreenColorbarEnabled())) { + std::string filename = quantity.parent.name + "_" + quantity.name + "_colorbar.svg"; + colorBar.exportColorbarToSVG(filename); + polyscope::info("Exported colormap to " + filename); + } } template @@ -297,6 +302,12 @@ QuantityT* ScalarQuantity::resetMapRange() { return &quantity; } + +template +void ScalarQuantity::exportColorbarToSVG(const std::string& filename) { + colorBar.exportColorbarToSVG(filename); +} + template template void ScalarQuantity::updateData(const V& newValues) { diff --git a/src/color_bar.cpp b/src/color_bar.cpp index 179182869..7a4303d65 100644 --- a/src/color_bar.cpp +++ b/src/color_bar.cpp @@ -8,6 +8,7 @@ #include "imgui.h" #include +#include #include #include @@ -261,6 +262,71 @@ void OnscreenColorBarWidget::draw() { (barRegionWidth + tickRegionWidth + marginWidth + 2.f * tickWidth) + internal::imguiStackMargin; } +void ColorBar::exportColorbarToSVG(const std::string& filename) { + std::ofstream svgFile(filename); + if (!svgFile.is_open()) { + polyscope::warning("Failed to open file for SVG export: " + filename); + return; + } + + const render::ValueColorMap& valueColormap = render::engine->getColorMap(colormap); + + // SVG dimensions + float svgWidth = 100.0f; + float svgHeight = 400.0f; + float barWidth = 30.0f; + float tickLength = 10.0f; + float textOffset = 15.0f; + int nTicks = 5; + + // SVG header + svgFile << "\n"; + svgFile << "\n"; + + // Gradient definition + svgFile << " \n"; + svgFile << " \n"; + + // Sample colormap at multiple points + int nSamples = 50; + for (int i = 0; i <= nSamples; i++) { + float t = (float)i / (float)nSamples; + glm::vec3 color = valueColormap.getValue(t); + int r = static_cast(color.x * 255); + int g = static_cast(color.y * 255); + int b = static_cast(color.z * 255); + svgFile << " \n"; + } + + svgFile << " \n"; + svgFile << " \n"; + + // Draw the colorbar rectangle + svgFile << " \n"; + + // Ticks and labels + for (int i = 0; i < nTicks; i++) { + float t = (float)i / (float)(nTicks - 1); + float yPos = 10 + t * (svgHeight - 20); + double val = colormapRange.second - t * (colormapRange.second - colormapRange.first); + + // tick mark + svgFile << " \n"; + + // text label + char buffer[64]; + snprintf(buffer, sizeof(buffer), "%.4g", val); + svgFile << " " << buffer << "\n"; + } + + svgFile << "\n"; + svgFile.close(); +} void ColorBar::buildUI(float width) { From 19a58de3ede4d2e4be446cfbdc3270589dd08ade Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 7 Dec 2025 16:07:26 -0800 Subject: [PATCH 14/18] minor fix to colorbar options menu --- include/polyscope/scalar_quantity.ipp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/polyscope/scalar_quantity.ipp b/include/polyscope/scalar_quantity.ipp index a2f2e91c5..3a873927b 100644 --- a/include/polyscope/scalar_quantity.ipp +++ b/include/polyscope/scalar_quantity.ipp @@ -220,13 +220,13 @@ void ScalarQuantity::buildScalarOptionsUI() { if (dataType != DataType::CATEGORICAL) { if (ImGui::MenuItem("Enable isolines", NULL, isolinesEnabled.get())) setIsolinesEnabled(!isolinesEnabled.get()); } - if (ImGui::MenuItem("Onscreen Colormap", NULL, colorBar.getOnscreenColorbarEnabled())) { + if (ImGui::MenuItem("Onscreen Colorbar", NULL, colorBar.getOnscreenColorbarEnabled())) { colorBar.setOnscreenColorbarEnabled(!colorBar.getOnscreenColorbarEnabled()); } - if (ImGui::MenuItem("Export Colormap", NULL, colorBar.getOnscreenColorbarEnabled())) { + if (ImGui::MenuItem("Export Colorbar")) { std::string filename = quantity.parent.name + "_" + quantity.name + "_colorbar.svg"; colorBar.exportColorbarToSVG(filename); - polyscope::info("Exported colormap to " + filename); + polyscope::info("Exported colorbar to " + filename); } } From 70979a7a9d7711d5cb79a2b67f7051798380a12f Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 7 Dec 2025 23:23:38 -0800 Subject: [PATCH 15/18] add vec2 persistent value --- include/polyscope/persistent_value.h | 2 ++ src/persistent_value.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/include/polyscope/persistent_value.h b/include/polyscope/persistent_value.h index 517a88b33..efc69ae47 100644 --- a/include/polyscope/persistent_value.h +++ b/include/polyscope/persistent_value.h @@ -134,6 +134,7 @@ extern PersistentCache persistentCache_double; extern PersistentCache persistentCache_float; extern PersistentCache persistentCache_bool; extern PersistentCache persistentCache_string; +extern PersistentCache persistentCache_glmvec2; extern PersistentCache persistentCache_glmvec3; extern PersistentCache persistentCache_glmmat4; extern PersistentCache> persistentCache_scaleddouble; @@ -150,6 +151,7 @@ template<> inline PersistentCache& getPersistentCacheR template<> inline PersistentCache& getPersistentCacheRef() { return persistentCache_float; } template<> inline PersistentCache& getPersistentCacheRef() { return persistentCache_bool; } template<> inline PersistentCache& getPersistentCacheRef() { return persistentCache_string; } +template<> inline PersistentCache& getPersistentCacheRef() { return persistentCache_glmvec2; } template<> inline PersistentCache& getPersistentCacheRef() { return persistentCache_glmvec3; } template<> inline PersistentCache& getPersistentCacheRef() { return persistentCache_glmmat4; } template<> inline PersistentCache>& getPersistentCacheRef>() { return persistentCache_scaleddouble; } diff --git a/src/persistent_value.cpp b/src/persistent_value.cpp index 6476bb360..85e62f768 100644 --- a/src/persistent_value.cpp +++ b/src/persistent_value.cpp @@ -12,6 +12,7 @@ PersistentCache persistentCache_double; PersistentCache persistentCache_float; PersistentCache persistentCache_bool; PersistentCache persistentCache_string; +PersistentCache persistentCache_glmvec2; PersistentCache persistentCache_glmvec3; PersistentCache persistentCache_glmmat4; PersistentCache> persistentCache_scaleddouble; From bbc06a9d99c20f7512550c0f12a6ed7d698d0005 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 7 Dec 2025 23:23:53 -0800 Subject: [PATCH 16/18] add set location option --- include/polyscope/color_bar.h | 5 +++++ include/polyscope/scalar_quantity.h | 11 +++++++++-- include/polyscope/scalar_quantity.ipp | 11 +++++++++++ src/color_bar.cpp | 26 +++++++++++++++++++++----- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/include/polyscope/color_bar.h b/include/polyscope/color_bar.h index d350190cd..afca0996b 100644 --- a/include/polyscope/color_bar.h +++ b/include/polyscope/color_bar.h @@ -38,6 +38,10 @@ class ColorBar { void setOnscreenColorbarEnabled(bool newEnabled); bool getOnscreenColorbarEnabled(); + + // Location in screen coords. (-1,-1), means "place automatically" (default) + void setOnscreenColorbarLocation(glm::vec2 newScreenCoords); + glm::vec2 getOnscreenColorbarLocation(); private: // Basic data defining the color map @@ -67,6 +71,7 @@ class ColorBar { // == The optional vertical colorbar which floats ont he main display PersistentValue onscreenColorbarEnabled; + PersistentValue onscreenColorbarLocation; std::shared_ptr cmapTexture; // this is just the plain colormap rgb void prepareOnscreenColorBar(); std::unique_ptr onscreenColorBarWidget = nullptr; diff --git a/include/polyscope/scalar_quantity.h b/include/polyscope/scalar_quantity.h index aa2cda386..9644b7121 100644 --- a/include/polyscope/scalar_quantity.h +++ b/include/polyscope/scalar_quantity.h @@ -48,14 +48,21 @@ class ScalarQuantity { // The color map QuantityT* setColorMap(std::string val); std::string getColorMap(); - QuantityT* setOnscreenColorbarEnabled(bool newEnabled); - bool getOnscreenColorbarEnabled(); // Data limits mapped in to colormap QuantityT* setMapRange(std::pair val); std::pair getMapRange(); QuantityT* resetMapRange(); // reset to full range std::pair getDataRange(); + + // Color bar options (it is always displayed inline in the structures panel) + QuantityT* setOnscreenColorbarEnabled(bool newEnabled); + bool getOnscreenColorbarEnabled(); + + // Location in screen coords. (-1,-1), means "place automatically" (default) + QuantityT* setOnscreenColorbarLocation(glm::vec2 newScreenCoords); + glm::vec2 getOnscreenColorbarLocation(); + // Isolines // NOTE there's a name typo, errant `s` in isolinesEnabled (leaving to avoid breaking change) diff --git a/include/polyscope/scalar_quantity.ipp b/include/polyscope/scalar_quantity.ipp index 3a873927b..31cb0f216 100644 --- a/include/polyscope/scalar_quantity.ipp +++ b/include/polyscope/scalar_quantity.ipp @@ -342,6 +342,17 @@ bool ScalarQuantity::getOnscreenColorbarEnabled() { return colorBar.getOnscreenColorbarEnabled(); } +template +QuantityT* ScalarQuantity::setOnscreenColorbarLocation(glm::vec2 newScreenCoords) { + colorBar.setOnscreenColorbarLocation(newScreenCoords); + requestRedraw(); + return &quantity; +} +template +glm::vec2 ScalarQuantity::getOnscreenColorbarLocation() { + return colorBar.getOnscreenColorbarLocation(); +} + template QuantityT* ScalarQuantity::setMapRange(std::pair val) { vizRangeMin = val.first; diff --git a/src/color_bar.cpp b/src/color_bar.cpp index 7a4303d65..20e68ab27 100644 --- a/src/color_bar.cpp +++ b/src/color_bar.cpp @@ -15,7 +15,8 @@ namespace polyscope { ColorBar::ColorBar(Quantity& parent_) - : parent(parent_), onscreenColorbarEnabled(parent.uniquePrefix() + "onscreenColorbarEnabled", false) {} + : parent(parent_), onscreenColorbarEnabled(parent.uniquePrefix() + "onscreenColorbarEnabled", false), + onscreenColorbarLocation(parent.uniquePrefix() + "onscreenColorbarLocation", glm::vec2(-1., -1.)) {} ColorBar::~ColorBar() {} @@ -191,6 +192,9 @@ void ColorBar::setOnscreenColorbarEnabled(bool newEnabled) { } bool ColorBar::getOnscreenColorbarEnabled() { return onscreenColorbarEnabled.get(); } +void ColorBar::setOnscreenColorbarLocation(glm::vec2 newScreenCoords) { onscreenColorbarLocation.set(newScreenCoords); } +glm::vec2 ColorBar::getOnscreenColorbarLocation() { return onscreenColorbarLocation.get(); } + void ColorBar::prepareOnscreenColorBar() { if (onscreenColorBarWidget) { // Already created, nothing to do @@ -209,6 +213,9 @@ void OnscreenColorBarWidget::draw() { parent.cmapTexture = render::engine->getColorMapTexture2d(parent.colormap); } + bool placeAutomatically = + (parent.onscreenColorbarLocation.get().x == -1.f && parent.onscreenColorbarLocation.get().y == -1.f); + // Draw the color bar // NOTE: right nwo the size of the colorbar is scaled by uiScale, but the positioning/margins are not. This is @@ -219,8 +226,15 @@ void OnscreenColorBarWidget::draw() { float barRegionHeight = 300.0f * options::uiScale; float borderWidth = 2.0f * options::uiScale; float tickWidth = 5.0f * options::uiScale; - ImVec2 barTopLeft(internal::lastRightSideFreeX - barRegionWidth - tickRegionWidth - marginWidth, - internal::lastRightSideFreeY + marginWidth + internal::imguiStackMargin); + + + ImVec2 barTopLeft; + if (placeAutomatically) { + barTopLeft = ImVec2(internal::lastRightSideFreeX - barRegionWidth - tickRegionWidth - marginWidth, + internal::lastRightSideFreeY + marginWidth + internal::imguiStackMargin); + } else { + barTopLeft = ImVec2(parent.onscreenColorbarLocation.get().x, parent.onscreenColorbarLocation.get().y); + } ImDrawList* dl = ImGui::GetBackgroundDrawList(); ImU32 backgroundColor = IM_COL32(255, 255, 255, 180); @@ -258,8 +272,10 @@ void OnscreenColorBarWidget::draw() { dl->AddText(ImVec2(barTopLeft.x + barRegionWidth + 2.f * tickWidth, yPos - textSize.y / 2), IM_COL32_BLACK, buffer); } - internal::lastRightSideFreeX -= - (barRegionWidth + tickRegionWidth + marginWidth + 2.f * tickWidth) + internal::imguiStackMargin; + if (placeAutomatically) { + internal::lastRightSideFreeX -= + (barRegionWidth + tickRegionWidth + marginWidth + 2.f * tickWidth) + internal::imguiStackMargin; + } } void ColorBar::exportColorbarToSVG(const std::string& filename) { From 1c43c34f2596e2edcc10be12df1b79407d021839 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 7 Dec 2025 23:27:46 -0800 Subject: [PATCH 17/18] add tests --- test/src/misc_test.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/src/misc_test.cpp b/test/src/misc_test.cpp index 6862cd3cc..e796ddcc9 100644 --- a/test/src/misc_test.cpp +++ b/test/src/misc_test.cpp @@ -21,7 +21,32 @@ TEST_F(PolyscopeTest, TestScalarQuantity) { q1->setMapRange(newRange); EXPECT_EQ(newRange, q1->getMapRange()); + polyscope::show(3); + + polyscope::removeAllStructures(); +} + +TEST_F(PolyscopeTest, TestScalarColormapQuantity) { + auto psPoints = registerPointCloud(); + + std::vector vScalar(psPoints->nPoints(), 7.); + auto q1 = psPoints->addScalarQuantity("vScalar", vScalar); + q1->setEnabled(true); + polyscope::show(3); + // set colormap by name + q1->setColorMap("plasma"); + EXPECT_EQ("plasma", q1->getColorMap()); + polyscope::show(3); + + // enable the onscreen colormap + q1->setOnscreenColorbarEnabled(true); + EXPECT_TRUE(q1->getOnscreenColorbarEnabled()); + polyscope::show(3); + + // set its location manually + q1->setOnscreenColorbarLocation(glm::vec2(500.f, 500.f)); + EXPECT_EQ(glm::vec2(500.f, 500.f), q1->getOnscreenColorbarLocation()); polyscope::show(3); polyscope::removeAllStructures(); From 77efcc563d5f75a7dc785f1bab7fece8ff12d23d Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 7 Dec 2025 23:28:24 -0800 Subject: [PATCH 18/18] add onscreen to demo --- examples/demo-app/demo_app.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/demo-app/demo_app.cpp b/examples/demo-app/demo_app.cpp index f1eaee4b3..c358f0f43 100644 --- a/examples/demo-app/demo_app.cpp +++ b/examples/demo-app/demo_app.cpp @@ -143,7 +143,8 @@ void processFileOBJ(std::string filename) { randColor[iV] = {{polyscope::randomUnit(), polyscope::randomUnit(), polyscope::randomUnit()}}; } polyscope::getSurfaceMesh(niceName)->addVertexScalarQuantity("cX_really_really_stupid_long_name_how_dumb", valX); - polyscope::getSurfaceMesh(niceName)->addVertexScalarQuantity("cY", valY); + auto q = polyscope::getSurfaceMesh(niceName)->addVertexScalarQuantity("cY", valY); + q->setOnscreenColorbarEnabled(true); // set the onscreen colormap for this one polyscope::getSurfaceMesh(niceName)->addVertexScalarQuantity("cZ", valZ); polyscope::getSurfaceMesh(niceName)->addVertexColorQuantity("vColor", randColor); polyscope::getSurfaceMesh(niceName)->addVertexScalarQuantity("cY_sym", valY, polyscope::DataType::SYMMETRIC);