Skip to content

Commit

Permalink
feat: surface plot with custom colormap range
Browse files Browse the repository at this point in the history
feat: surface plot demo with moving wave and range selection
closes: #12
  • Loading branch information
brenocq committed Dec 25, 2024
1 parent f0492cb commit 5fe6144
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 22 deletions.
3 changes: 2 additions & 1 deletion implot3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,8 @@ IMPLOT3D_TMP void PlotTriangle(const char* label_id, const T* xs, const T* ys, c

IMPLOT3D_TMP void PlotQuad(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DQuadFlags flags = 0, int offset = 0, int stride = sizeof(T));

IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, ImPlot3DSurfaceFlags flags = 0, int offset = 0, int stride = sizeof(T));
// Plot the surface defined by a grid of vertices. The grid is defined by the x and y arrays, and the z array contains the height of each vertex. A total of x_count * y_count vertices are expected for each array. Leave #scale_min and #scale_max both at 0 for automatic color scaling, or set them to a predefined range.
IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, double scale_min = 0.0, double scale_max = 0.0, ImPlot3DSurfaceFlags flags = 0, int offset = 0, int stride = sizeof(T));

IMPLOT3D_API void PlotMesh(const char* label_id, const ImPlot3DPoint* vtx, const unsigned int* idx, int vtx_count, int idx_count, ImPlot3DMeshFlags flags = 0);

Expand Down
74 changes: 64 additions & 10 deletions implot3d_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,37 +279,91 @@ void DemoQuadPlots() {
void DemoSurfacePlots() {
constexpr int N = 20;
static float xs[N * N], ys[N * N], zs[N * N];
static float t = 0.0f;
t += ImGui::GetIO().DeltaTime;

// Define the range for X and Y
constexpr float range_min = -5.0f;
constexpr float range_max = 5.0f;
constexpr float step = (range_max - range_min) / (N - 1);
constexpr float min_val = -1.0f;
constexpr float max_val = 1.0f;
constexpr float step = (max_val - min_val) / (N - 1);

// Populate the xs, ys, and zs arrays
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
int idx = i * N + j;
xs[idx] = range_min + j * step; // X values are constant along rows
ys[idx] = range_min + i * step; // Y values are constant along columns
zs[idx] = ImSin(ImSqrt((xs[idx] * xs[idx] + ys[idx] * ys[idx]))); // Z = sin(sqrt(X^2 + Y^2))
xs[idx] = min_val + j * step; // X values are constant along rows
ys[idx] = min_val + i * step; // Y values are constant along columns
zs[idx] = ImSin(2 * t + ImSqrt((xs[idx] * xs[idx] + ys[idx] * ys[idx]))); // z = sin(2t + sqrt(x^2 + y^2))
}
}

// Choose fill color
ImGui::Text("Fill color");
static int selected_fill = 1; // Colormap by default
static ImVec4 solid_color = ImVec4(0.8f, 0.8f, 0.2f, 0.6f);
const char* colormaps[] = {"Viridis", "Plasma", "Hot", "Cool", "Pink", "Jet",
"Twilight", "RdBu", "BrBG", "PiYG", "Spectral", "Greys"};
static int sel_colormap = 5; // Jet by default
{
ImGui::Indent();

// Choose solid color
ImGui::RadioButton("Solid", &selected_fill, 0);
if (selected_fill == 0) {
ImGui::SameLine();
ImGui::ColorEdit4("##SurfaceSolidColor", (float*)&solid_color);
}

// Choose colormap
ImGui::RadioButton("Colormap", &selected_fill, 1);
if (selected_fill == 1) {
ImGui::SameLine();
ImGui::Combo("##SurfaceColormap", &sel_colormap, colormaps, IM_ARRAYSIZE(colormaps));
}
ImGui::Unindent();
}

// Choose range
static bool custom_range = false;
static float range_min = -1.0f;
static float range_max = 1.0f;
ImGui::Checkbox("Custom range", &custom_range);
{
ImGui::Indent();

if (!custom_range)
ImGui::BeginDisabled();
ImGui::SliderFloat("Range min", &range_min, -1.0f, range_max - 0.01f);
ImGui::SliderFloat("Range max", &range_max, range_min + 0.01f, 1.0f);
if (!custom_range)
ImGui::EndDisabled();

ImGui::Unindent();
}

// Begin the plot
ImPlot3D::PushColormap("Hot");
if (ImPlot3D::BeginPlot("Surface Plots")) {
if (selected_fill == 1)
ImPlot3D::PushColormap(colormaps[sel_colormap]);
if (ImPlot3D::BeginPlot("Surface Plots", ImVec2(-1, 400), ImPlot3DFlags_NoClip)) {
// Set styles
ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -1.5, 1.5);
ImPlot3D::PushStyleVar(ImPlot3DStyleVar_FillAlpha, 0.8f);
if (selected_fill == 0)
ImPlot3D::SetNextFillStyle(solid_color);
ImPlot3D::SetNextLineStyle(ImPlot3D::GetColormapColor(1));

// Plot the surface
ImPlot3D::PlotSurface("Wave Surface", xs, ys, zs, N, N);
if (custom_range)
ImPlot3D::PlotSurface("Wave Surface", xs, ys, zs, N, N, (double)range_min, (double)range_max);
else
ImPlot3D::PlotSurface("Wave Surface", xs, ys, zs, N, N);

// End the plot
ImPlot3D::PopStyleVar();
ImPlot3D::EndPlot();
}
ImPlot3D::PopColormap();
if (selected_fill == 1)
ImPlot3D::PopColormap();
}

void DemoMeshPlots() {
Expand Down
33 changes: 22 additions & 11 deletions implot3d_items.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -700,11 +700,14 @@ struct RendererQuadFill : RendererBase {

template <class _Getter>
struct RendererSurfaceFill : RendererBase {
RendererSurfaceFill(const _Getter& getter, int x_count, int y_count, ImU32 col) : RendererBase((x_count - 1) * (y_count - 1), 6, 4),
Getter(getter),
XCount(x_count),
YCount(y_count),
Col(col) {}
RendererSurfaceFill(const _Getter& getter, int x_count, int y_count, ImU32 col, double scale_min, double scale_max) : RendererBase((x_count - 1) * (y_count - 1), 6, 4),
Getter(getter),
XCount(x_count),
YCount(y_count),
Col(col),
ScaleMin(scale_min),
ScaleMax(scale_max) {}

void Init(ImDrawList3D& draw_list_3d) const {
UV = draw_list_3d._SharedData->TexUvWhitePixel;

Expand Down Expand Up @@ -741,8 +744,14 @@ struct RendererSurfaceFill : RendererBase {
const ImPlot3DNextItemData& n = GetItemData();
if (n.IsAutoFill) {
float alpha = GImPlot3D->NextItemData.FillAlpha;
float min = Min;
float max = Max;
if (ScaleMin != 0.0 || ScaleMax != 0.0) {
min = ScaleMin;
max = ScaleMax;
}
for (int i = 0; i < 4; i++) {
ImVec4 col = SampleColormap(ImClamp(ImRemap01(p_plot[i].z, Min, Max), 0.0f, 1.0f));
ImVec4 col = SampleColormap(ImClamp(ImRemap01(p_plot[i].z, min, max), 0.0f, 1.0f));
col.w *= alpha;
cols[i] = ImGui::ColorConvertFloat4ToU32(col);
}
Expand Down Expand Up @@ -807,6 +816,8 @@ struct RendererSurfaceFill : RendererBase {
const int XCount;
const int YCount;
const ImU32 Col;
const double ScaleMin;
const double ScaleMax;
};

//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -1237,14 +1248,14 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
//-----------------------------------------------------------------------------

template <typename _Getter>
void PlotSurfaceEx(const char* label_id, const _Getter& getter, int x_count, int y_count, ImPlot3DSurfaceFlags flags) {
void PlotSurfaceEx(const char* label_id, const _Getter& getter, int x_count, int y_count, double scale_min, double scale_max, ImPlot3DSurfaceFlags flags) {
if (BeginItemEx(label_id, getter, flags, ImPlot3DCol_Fill)) {
const ImPlot3DNextItemData& n = GetItemData();

// Render fill
if (getter.Count >= 4 && n.RenderFill) {
const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Fill]);
RenderPrimitives<RendererSurfaceFill>(getter, x_count, y_count, col_fill);
RenderPrimitives<RendererSurfaceFill>(getter, x_count, y_count, col_fill, scale_min, scale_max);
}

// Render lines
Expand All @@ -1264,16 +1275,16 @@ void PlotSurfaceEx(const char* label_id, const _Getter& getter, int x_count, int
}
}

IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, ImPlot3DSurfaceFlags flags, int offset, int stride) {
IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, double scale_min, double scale_max, ImPlot3DSurfaceFlags flags, int offset, int stride) {
int count = x_count * y_count;
if (count < 4)
return;
GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride), IndexerIdx<T>(zs, count, offset, stride), count);
return PlotSurfaceEx(label_id, getter, x_count, y_count, flags);
return PlotSurfaceEx(label_id, getter, x_count, y_count, scale_min, scale_max, flags);
}

#define INSTANTIATE_MACRO(T) \
template IMPLOT3D_API void PlotSurface<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, ImPlot3DSurfaceFlags flags, int offset, int stride);
template IMPLOT3D_API void PlotSurface<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, double scale_min, double scale_max, ImPlot3DSurfaceFlags flags, int offset, int stride);
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
#undef INSTANTIATE_MACRO

Expand Down

0 comments on commit 5fe6144

Please sign in to comment.