Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions implot.h
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,9 @@ IMPLOT_API void PlotImage(const char* label_id, ImTextureID tex_ref, const ImPlo
// Plots a centered text label at point x,y with an optional pixel offset. Text color can be changed with ImPlot::PushStyleColor(ImPlotCol_InlayText, ...).
IMPLOT_API void PlotText(const char* text, double x, double y, const ImVec2& pix_offset=ImVec2(0,0), ImPlotTextFlags flags=0);

// Plots a 2D filled contour without lines, given a regular/irregular grid of data.
IMPLOT_TMP void PlotContourFill(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, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max);

// Plots a dummy item (i.e. adds a legend entry colored by ImPlotCol_Line)
IMPLOT_API void PlotDummy(const char* label_id, ImPlotDummyFlags flags=0);

Expand Down
43 changes: 43 additions & 0 deletions implot_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,48 @@ void Demo_NaNValues() {
ImPlot::EndPlot();
}
}
//-----------------------------------------------------------------------------

void Demo_ContourFill() {
constexpr int M = 240;
constexpr int N = 160;
static float delta = 0.025f;
static float xs[M * N];
static float ys[M * N];
static float zs[M * N];

static float scale_min = FLT_MAX;
static float scale_max = -FLT_MAX;
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
int idx = j * M + i;
xs[idx] = -3.f + i * delta;
ys[idx] = -2.f + j * delta;
zs[idx] = (1.f - 0.5f * xs[idx] + powf(xs[idx], 5) + powf(ys[idx], 3)) * expf(-xs[idx] * xs[idx] - ys[idx] * ys[idx]);
scale_min = (scale_min < zs[idx]) ? scale_min : zs[idx];
scale_max = (scale_max > zs[idx]) ? scale_max : zs[idx];
}
}

// Choose colormap
const char* colormaps[] = { "Viridis", "Plasma", "Hot", "Cool", "Pink", "Jet", "Twilight", "RdBu", "BrBG", "PiYG", "Spectral", "Greys" };
static int sel_colormap = 5; // Jet by default
ImGui::SetNextItemWidth(360);
ImGui::Combo("##ContourColormap", &sel_colormap, colormaps, IM_ARRAYSIZE(colormaps));
ImGui::SameLine();
ImGui::Text("Choose colormap");
ImGui::SetNextItemWidth(360);
ImGui::DragFloatRange2("Min / Max", &scale_min, &scale_max, 0.01f, -3.f, 3.f);

ImPlot::PushColormap(colormaps[sel_colormap]);
if (ImPlot::BeginPlot("##Contour", ImVec2(M*2, N*2))) {
ImPlot::PlotContourFill("Filled Contour", xs, ys, zs, M, N, scale_min, scale_max, ImPlotPoint(-3.f, -2.f), ImPlotPoint(-3.f + M * delta, -2.f + N * delta));
ImPlot::EndPlot();
}
ImGui::SameLine();
ImPlot::ColormapScale("##Z-Scale", scale_min, scale_max, ImVec2(60, N*2));
ImPlot::PopColormap();
}

//-----------------------------------------------------------------------------

Expand Down Expand Up @@ -2254,6 +2296,7 @@ void ShowDemoWindow(bool* p_open) {
DemoHeader("Images", Demo_Images);
DemoHeader("Markers and Text", Demo_MarkersAndText);
DemoHeader("NaN Values", Demo_NaNValues);
DemoHeader("Filled Contour", Demo_ContourFill);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Subplots")) {
Expand Down
137 changes: 137 additions & 0 deletions implot_items.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,18 @@ struct GetterXY {
const int Count;
};

template <typename _IndexerX, typename _IndexerY, typename _IndexerZ>
struct GetterXYZ {
GetterXYZ(_IndexerX x, _IndexerY y, _IndexerZ z, int count) : IndexerX(x), IndexerY(y), IndexerZ(z), Count(count) { }
template <typename I> IMPLOT_INLINE ImVec4 operator()(I idx) const {
return ImVec4((float)IndexerX(idx), (float)IndexerY(idx), (float)IndexerZ(idx), 0.f);
}
const _IndexerX IndexerX;
const _IndexerY IndexerY;
const _IndexerZ IndexerZ;
const int Count;
};

/// Interprets a user's function pointer as ImPlotPoints
struct GetterFuncPtr {
GetterFuncPtr(ImPlotGetter getter, void* data, int count) :
Expand Down Expand Up @@ -1355,6 +1367,105 @@ struct RendererRectC : RendererBase {
mutable ImVec2 UV;
};

template <typename _Getter>
struct RendererContourFill : RendererBase {
RendererContourFill(const _Getter& getter, int x_count, int y_count, ImU32 col, float scale_min, float 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(ImDrawList& draw_list) const {
UV = draw_list._Data->TexUvWhitePixel;
}

IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {
int x = prim % (XCount - 1);
int y = prim / (XCount - 1);

ImVec4 p_plot[4];
p_plot[0] = Getter(x + y * XCount);
p_plot[1] = Getter(x + 1 + y * XCount);
p_plot[2] = Getter(x + 1 + (y + 1) * XCount);
p_plot[3] = Getter(x + (y + 1) * XCount);

// Check if the coordinates of vertices and their values are valid numbers
if (isnan(p_plot[0].x) || isnan(p_plot[0].y) || isnan(p_plot[0].z) || isnan(p_plot[1].x) || isnan(p_plot[1].y) || isnan(p_plot[1].z)
|| isnan(p_plot[2].x) || isnan(p_plot[2].y) || isnan(p_plot[2].z) || isnan(p_plot[3].x) || isnan(p_plot[3].y) || isnan(p_plot[3].z))
return false;

// Compute colors
ImU32 cols[4] = {Col, Col, Col, Col};
float alpha = GImPlot->NextItemData.FillAlpha;
for (int i = 0; i < 4; i++) {
ImVec4 col = SampleColormap(ImClamp(ImRemap01(p_plot[i].z, ScaleMin, ScaleMax), 0.0f, 1.0f));
col.w *= alpha;
cols[i] = ImGui::ColorConvertFloat4ToU32(col);
}

// Project the quad vertices to screen space
ImVec2 p[4];
p[0] = PlotToPixels(ImVec2(p_plot[0].x, p_plot[0].y));
p[1] = PlotToPixels(ImVec2(p_plot[1].x, p_plot[1].y));
p[2] = PlotToPixels(ImVec2(p_plot[2].x, p_plot[2].y));
p[3] = PlotToPixels(ImVec2(p_plot[3].x, p_plot[3].y));

// Check if the quad is outside the culling box
if (!cull_rect.Contains(p[0]) && !cull_rect.Contains(p[1]) &&
!cull_rect.Contains(p[2]) && !cull_rect.Contains(p[3]))
return false;

// Add vertices for two triangles
draw_list._VtxWritePtr[0].pos.x = p[0].x;
draw_list._VtxWritePtr[0].pos.y = p[0].y;
draw_list._VtxWritePtr[0].uv = UV;
draw_list._VtxWritePtr[0].col = cols[0];

draw_list._VtxWritePtr[1].pos.x = p[1].x;
draw_list._VtxWritePtr[1].pos.y = p[1].y;
draw_list._VtxWritePtr[1].uv = UV;
draw_list._VtxWritePtr[1].col = cols[1];

draw_list._VtxWritePtr[2].pos.x = p[2].x;
draw_list._VtxWritePtr[2].pos.y = p[2].y;
draw_list._VtxWritePtr[2].uv = UV;
draw_list._VtxWritePtr[2].col = cols[2];

draw_list._VtxWritePtr[3].pos.x = p[3].x;
draw_list._VtxWritePtr[3].pos.y = p[3].y;
draw_list._VtxWritePtr[3].uv = UV;
draw_list._VtxWritePtr[3].col = cols[3];

draw_list._VtxWritePtr += 4;

// Add indices for two triangles
draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx);
draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1);
draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2);

draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx);
draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2);
draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3);

draw_list._IdxWritePtr += 6;

// Update vertex count
draw_list._VtxCurrentIdx += 4;

return true;
}

const _Getter& Getter;
mutable ImVec2 UV;
const int XCount;
const int YCount;
const ImU32 Col;
const float ScaleMin;
const float ScaleMax;
};

//-----------------------------------------------------------------------------
// [SECTION] RenderPrimitives
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -2838,6 +2949,32 @@ void PlotText(const char* text, double x, double y, const ImVec2& pixel_offset,
PopPlotClipRect();
}

//-----------------------------------------------------------------------------
// [SECTION] PlotContourFill
//-----------------------------------------------------------------------------

template <typename T>
void PlotContourFill(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, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) {
int count = x_count * y_count;
if (count < 4)
return;
GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count), IndexerIdx<T>(ys, count), IndexerIdx<T>(zs, count), count);
if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max), 0, ImPlotCol_Fill)) {
const ImPlotNextItemData& n = GetItemData();

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

#define INSTANTIATE_MACRO(T) template IMPLOT_API void PlotContourFill<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, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max);
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
#undef INSTANTIATE_MACRO

//-----------------------------------------------------------------------------
// [SECTION] PlotDummy
//-----------------------------------------------------------------------------
Expand Down