diff --git a/implot3d.h b/implot3d.h index d0e9f08..0cc502f 100644 --- a/implot3d.h +++ b/implot3d.h @@ -37,6 +37,8 @@ #include "imgui.h" #ifndef IMGUI_DISABLE +#include + //----------------------------------------------------------------------------- // [SECTION] Macros and Defines //----------------------------------------------------------------------------- @@ -50,6 +52,7 @@ #define IMPLOT3D_AUTO -1 // Deduce variable automatically #define IMPLOT3D_AUTO_COL ImVec4(0, 0, 0, -1) // Deduce color automatically #define IMPLOT3D_TMP template IMPLOT3D_API +#define IMPLOT3D_DEFAULT_MAJOR_STRIDE INT_MAX //----------------------------------------------------------------------------- // [SECTION] Forward declarations and basic types @@ -226,6 +229,13 @@ enum ImPlot3DSurfaceFlags_ { ImPlot3DSurfaceFlags_NoLines = 1 << 10, // No lines will be rendered ImPlot3DSurfaceFlags_NoFill = 1 << 11, // No fill will be rendered ImPlot3DSurfaceFlags_NoMarkers = 1 << 12, // No markers will be rendered + + // The plane to use for the surface plot + ImPlot3DSurfaceFlags_PlaneXY = 0, // Use the XY plane and Z values for surface. Default behavior. Use for None flag + ImPlot3DSurfaceFlags_PlaneXZ = 1 << 13, // Use the XZ plane and Y values for surface + ImPlot3DSurfaceFlags_PlaneYZ = 1 << 14, // Use the YZ plane and X values for surface + ImPlot3DSurfaceFlags_SwapAxes = 1 << 15, // Swap the minor and major axis. Only has an effect on the PlotSurface function where only the values are passed in + ImPlot3DSurfaceFlags_PlaneMask_ = ImPlot3DSurfaceFlags_PlaneXY | ImPlot3DSurfaceFlags_PlaneXZ | ImPlot3DSurfaceFlags_PlaneYZ, }; // Flags for PlotMesh @@ -451,8 +461,11 @@ IMPLOT3D_TMP void PlotQuad(const char* label_id, const T* xs, const T* ys, const // 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, +IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int minor_count, int major_count, double scale_min = 0.0, double scale_max = 0.0, ImPlot3DSurfaceFlags flags = 0, int offset = 0, int stride = sizeof(T)); +IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* values, int minor_count, int major_count, double scale_min = 0.0, double scale_max = 0.0, + ImPlot3DSurfaceFlags flags = 0, const ImVec2& minor_bounds = ImVec2(-1, 1), const ImVec2& major_bounds = ImVec2(-1, 1), + int offset = 0, int stride = sizeof(T), int major_offset = 0, int major_stride = IMPLOT3D_DEFAULT_MAJOR_STRIDE); IMPLOT3D_API void PlotMesh(const char* label_id, const ImPlot3DPoint* vtx, const unsigned int* idx, int vtx_count, int idx_count, ImPlot3DMeshFlags flags = 0); diff --git a/implot3d_demo.cpp b/implot3d_demo.cpp index 4e47fe3..e19bca4 100644 --- a/implot3d_demo.cpp +++ b/implot3d_demo.cpp @@ -17,6 +17,7 @@ // [SECTION] Plots // [SECTION] Axes // [SECTION] Custom +// [SECTION] Tools // [SECTION] Demo Window // [SECTION] Style Editor // [SECTION] User Namespace Implementation @@ -397,6 +398,61 @@ void DemoSurfacePlots() { ImPlot3D::PopColormap(); } +void DemoSimplifiedSurfacePlots() { + constexpr int N = 10; + constexpr int M = 20; + static float values[N * M]; + static float t = 0.0f; + t += ImGui::GetIO().DeltaTime; + + // Define the range for X and Y + constexpr float min_val = -1.0f; + constexpr float max_val = 1.0f; + constexpr float x_step = (max_val - min_val) / (M - 1); + constexpr float y_step = (max_val - min_val) / (N - 1); + + // Populate the values array + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + int idx = i * M + j; + float x = min_val + j * x_step; + float y = min_val + i * y_step; + values[idx] = ImSin(2 * t + ImSqrt((x * x + y * y))); // value = sin(2t + sqrt(x^2 + y^2)) + } + } + + // Select flags for the surface plot + static ImPlot3DSurfaceFlags flags = ImPlot3DSurfaceFlags_NoMarkers; + CHECKBOX_FLAG(flags, ImPlot3DSurfaceFlags_NoLines); + CHECKBOX_FLAG(flags, ImPlot3DSurfaceFlags_NoFill); + CHECKBOX_FLAG(flags, ImPlot3DSurfaceFlags_NoMarkers); + + // Begin the plot + ImPlot3D::PushColormap("Jet"); + if (ImPlot3D::BeginPlot("Surface Plots", ImVec2(-1, 400), ImPlot3DFlags_NoClip)) { + ImPlot3D::SetupAxes("x", "y", "z"); + ImPlot3D::SetupAxesLimits(-10, -5, 5, 10, -1.5, 1.5); + + // Set fill style + ImPlot3D::PushStyleVar(ImPlot3DStyleVar_FillAlpha, 0.8f); + + // Set line style + ImPlot3D::SetNextLineStyle(ImPlot3D::GetColormapColor(1)); + + // Set marker style + ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, IMPLOT3D_AUTO, ImPlot3D::GetColormapColor(2)); + + // Plot the surface + // Plot the X-Axis range from [-10, -5] and Y-Axis range from [5, 10] + ImPlot3D::PlotSurface("Wave Surface", values, M, N, 0.0, 0.0, flags, ImVec2(-10, -5), ImVec2(5, 10)); + + // End the plot + ImPlot3D::PopStyleVar(); + ImPlot3D::EndPlot(); + } + ImPlot3D::PopColormap(); +} + void DemoMeshPlots() { static int mesh_id = 0; ImGui::Combo("Mesh", &mesh_id, "Duck\0Sphere\0Cube\0\0"); @@ -737,6 +793,92 @@ void DemoAxisConstraints() { } } +//----------------------------------------------------------------------------- +// [SECTION] Tools +//----------------------------------------------------------------------------- + +void DemoSurfaceAxisOffsetStride() { + constexpr int N = 10; + constexpr int M = 20; + static float values[N * M]; + + // Define the range for X and Y + constexpr float min_val = -1.0f; + constexpr float max_val = 1.0f; + constexpr float x_step = (max_val - min_val) / (N - 1); + constexpr float y_step = (max_val - min_val) / (M - 1); + + // Populate the values array + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + int idx = i * M + j; + float x = min_val + j * x_step; + float y = min_val + i * y_step; + values[idx] = ImSin(ImSqrt((x * x + y * y))); // value = sin(sqrt(x^2 + y^2)) + } + } + + // Add offset and stride + static int offset = 0; + static int major_offset = 0; + static int stride = 1; + static int major_stride = 1; + static bool apply_major_stride = true; + if(ImGui::Checkbox("Apply Major stride", &apply_major_stride)) + major_stride = 1; + ImGui::SliderInt("Minor Offset", &offset, -200, 200); + ImGui::SliderInt("Major Offset", &major_offset, -20, 20); + ImGui::SliderInt("Minor Stride", &stride, -10, 10); + if (apply_major_stride) + ImGui::SliderInt("Major Stride", &major_stride, -10, 10); + + // Select flags for the surface plot + // Default to using ImPlot3DSurfaceFlags_PlaneXY flag. If neither the ImPlot3DSurfaceFlags_PlaneXZ and the ImPlot3DSurfaceFlags_PlaneYZ flag is + // set then use ImPlot3DSurfaceFlags_PlaneXY + static ImPlot3DSurfaceFlags flags = ImPlot3DSurfaceFlags_NoMarkers | ImPlot3DSurfaceFlags_PlaneXY; + CHECKBOX_FLAG(flags, ImPlot3DSurfaceFlags_NoLines); + CHECKBOX_FLAG(flags, ImPlot3DSurfaceFlags_NoFill); + CHECKBOX_FLAG(flags, ImPlot3DSurfaceFlags_NoMarkers); + if (ImGui::CheckboxFlags("ImPlot3DSurfaceFlags_PlaneXZ", &flags, ImPlot3DSurfaceFlags_PlaneXZ)) + flags &= ~(ImPlot3DSurfaceFlags_PlaneMask_ & ~ImPlot3DSurfaceFlags_PlaneXZ); + if (ImGui::CheckboxFlags("ImPlot3DSurfaceFlags_PlaneYZ", &flags, ImPlot3DSurfaceFlags_PlaneYZ)) + flags &= ~(ImPlot3DSurfaceFlags_PlaneMask_ & ~ImPlot3DSurfaceFlags_PlaneYZ); + CHECKBOX_FLAG(flags, ImPlot3DSurfaceFlags_SwapAxes); + + // Begin the plot + ImPlot3D::PushColormap("Jet"); + if (ImPlot3D::BeginPlot("Surface Plots", ImVec2(-1, 400), ImPlot3DFlags_NoClip)) { + ImPlot3D::SetupAxes("x", "y", "z"); + ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -1, 1); + + // Set fill style + ImPlot3D::PushStyleVar(ImPlot3DStyleVar_FillAlpha, 0.8f); + + // Set line style + ImPlot3D::SetNextLineStyle(ImPlot3D::GetColormapColor(1)); + + // Set marker style + ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, IMPLOT3D_AUTO, ImPlot3D::GetColormapColor(2)); + + // Update the number of minor and major items depending on the stride that was selected + const int updated_num_minor = stride == 0 ? M : (M / abs(stride) + (M % stride == 0 ? 0 : 1)); + const int updated_num_major = major_stride == 0 ? N : (N / abs(major_stride) + (N % major_stride == 0 ? 0 : 1)); + + const int array_offset = (major_stride < 0 ? (M * (N - 1)) : 0) + (stride < 0 ? M : 1) - 1; + + // Plot the surface + // The surface is flipped around the minor axis by specifying ImVec2(1, -1) for the minor bounds + ImPlot3D::PlotSurface("Wave Surface", &values[array_offset], updated_num_minor, updated_num_major, 0.0, 0.0, flags, ImVec2(1, -1), + ImVec2(-1, 1), offset, stride * sizeof(float), major_offset, + apply_major_stride ? (major_stride * M * sizeof(float)) : IMPLOT3D_DEFAULT_MAJOR_STRIDE); + + // End the plot + ImPlot3D::PopStyleVar(); + ImPlot3D::EndPlot(); + } + ImPlot3D::PopColormap(); +} + //----------------------------------------------------------------------------- // [SECTION] Custom //----------------------------------------------------------------------------- @@ -873,6 +1015,7 @@ void ShowAllDemos() { DemoHeader("Triangle Plots", DemoTrianglePlots); DemoHeader("Quad Plots", DemoQuadPlots); DemoHeader("Surface Plots", DemoSurfacePlots); + DemoHeader("Simplified Surface Plots", DemoSimplifiedSurfacePlots); DemoHeader("Mesh Plots", DemoMeshPlots); DemoHeader("Realtime Plots", DemoRealtimePlots); DemoHeader("Image Plots", DemoImagePlots); @@ -887,6 +1030,10 @@ void ShowAllDemos() { DemoHeader("Axis Constraints", DemoAxisConstraints); ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("Tools")) { + DemoHeader("Surface Axis, Offset and Stride Plot", DemoSurfaceAxisOffsetStride); + ImGui::EndTabItem(); + } if (ImGui::BeginTabItem("Custom")) { DemoHeader("Custom Styles", DemoCustomStyles); DemoHeader("Custom Rendering", DemoCustomRendering); diff --git a/implot3d_internal.h b/implot3d_internal.h index d1a22be..0ecd939 100644 --- a/implot3d_internal.h +++ b/implot3d_internal.h @@ -57,6 +57,8 @@ template static inline bool ImHasFlag(TSet set, // Flips a flag in a flagset template static inline void ImFlipFlag(TSet& set, TFlag flag) { ImHasFlag(set, flag) ? set &= ~flag : set |= flag; } template static inline T ImRemap01(T x, T x0, T x1) { return (x1 - x0) ? ((x - x0) / (x1 - x0)) : 0; } +// Returns always positive modulo (assumes r != 0) +static inline int ImPosMod(int l, int r) { return (l % r + r) % r; } // Returns true if val is NAN static inline bool ImNan(float val) { return isnan(val); } // Returns true if val is NAN or INFINITY diff --git a/implot3d_items.cpp b/implot3d_items.cpp index 703044a..9c7fb28 100644 --- a/implot3d_items.cpp +++ b/implot3d_items.cpp @@ -777,10 +777,24 @@ template struct RendererQuadImage : RendererBase { const ImU32 Col; }; +struct SurfacePlotPlaneGetter { + SurfacePlotPlaneGetter(ImPlane3D plane) : Plane(plane) {} + + IMPLOT3D_INLINE float operator()(const ImPlot3DPoint& point) const { + switch (Plane) { + case 0: return point.x; // X-Values + case 1: return point.y; // Y-Values + default: return point.z; // Z-Values(2) + } + } + const ImPlane3D Plane; +}; + template struct RendererSurfaceFill : RendererBase { - 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) {} + RendererSurfaceFill(const _Getter& getter, int minor_count, int major_count, ImU32 col, double scale_min, double scale_max, + const SurfacePlotPlaneGetter& plane_getter) + : RendererBase((minor_count - 1) * (major_count - 1), 6, 4), Getter(getter), MinorCount(minor_count), MajorCount(major_count), Col(col), + ScaleMin(scale_min), ScaleMax(scale_max), PlaneGetter(plane_getter) {} void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; @@ -791,22 +805,22 @@ template struct RendererSurfaceFill : RendererBase { Min = FLT_MAX; Max = -FLT_MAX; for (int i = 0; i < Getter.Count; i++) { - float z = Getter(i).z; - Min = ImMin(Min, z); - Max = ImMax(Max, z); + float val = PlaneGetter(Getter(i)); + Min = ImMin(Min, val); + Max = ImMax(Max, val); } } } IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const { - int x = prim % (XCount - 1); - int y = prim / (XCount - 1); + int minor = prim % (MinorCount - 1); + int major = prim / (MinorCount - 1); ImPlot3DPoint 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); + p_plot[0] = Getter(minor + major * MinorCount, minor, major); + p_plot[1] = Getter(minor + 1 + major * MinorCount, minor + 1, major); + p_plot[2] = Getter(minor + 1 + (major + 1) * MinorCount, minor + 1, (major + 1)); + p_plot[3] = Getter(minor + (major + 1) * MinorCount, minor, (major + 1)); // Check if the quad is outside the culling box if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1]) && !cull_box.Contains(p_plot[2]) && !cull_box.Contains(p_plot[3])) @@ -824,7 +838,7 @@ template struct RendererSurfaceFill : RendererBase { max = (float)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(PlaneGetter(p_plot[i]), min, max), 0.0f, 1.0f)); col.w *= alpha; cols[i] = ImGui::ColorConvertFloat4ToU32(col); } @@ -886,11 +900,12 @@ template struct RendererSurfaceFill : RendererBase { mutable ImVec2 UV; mutable float Min; // Minimum value for the colormap mutable float Max; // Minimum value for the colormap - const int XCount; - const int YCount; + const int MinorCount; + const int MajorCount; const ImU32 Col; const double ScaleMin; const double ScaleMax; + const SurfacePlotPlaneGetter& PlaneGetter; }; //----------------------------------------------------------------------------- @@ -917,6 +932,50 @@ template struct IndexerIdx { int Stride; }; +template struct IndexerIdxMajor { + IndexerIdxMajor(const T* data, int num_major, int num_minor, int major_offset, int offset, int major_stride, int stride) + : Data(data), + // If the stride does not have to be applied then use the major offset index as the number of major columns to shift by instead of applying + // the shift for the indexes It is a bit simpler to only have to append a constant to the index instead of having to add a constant to first + // determine the rows and then column offsets + MajorOffset((major_stride == int(sizeof(T)) * num_minor && stride > 0) ? ImPosMod(num_minor * major_offset, num_minor * num_major) + : ImPosMod(major_offset, num_major)), + Offset(ImPosMod(offset, num_minor * num_major)), MajorStride(major_stride), Stride(stride), + Type(((Offset == 0) << 0) | ((MajorOffset == 0) << 1) | ((Stride == int(sizeof(T)) && MajorStride > 0) << 2) | + ((MajorStride == int(sizeof(T)) * num_minor && Stride > 0) << 3)) {} + + template IMPLOT3D_INLINE double operator()(I idx, int count, int major, int minor, int num_major, int num_minor) const { + return (double)GetData(idx, count, major, minor, num_major, num_minor); + } + + template IMPLOT3D_INLINE T GetData(I idx, int count, int major, int minor, int num_major, int num_minor) const { + // clang-format off + // Get the data based based on the type + switch (Type) { + case 15: return Data[idx]; // No offset or stride + case 14: return Data[(Offset + idx) % count]; // Normal offset + case 13: return Data[(MajorOffset + idx) % count]; // Major offset + case 12: return Data[(MajorOffset + Offset + idx) % count]; // Major+normal offset + case 11: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((idx)) * Stride); // Normal stride + case 10: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((Offset + idx) % count) * Stride); // Normal stride and normal offset + case 9: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + idx) % count) * Stride); // Normal stride and major offset + case 8: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + Offset + idx) % count) * Stride); // Normal stride and major + normal offset + case 7: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + minor * sizeof(T)); // Major stride + case 6: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + (minor + Offset) / num_minor) % num_major) * MajorStride + ((minor + Offset) % num_minor) * sizeof(T)); + case 5: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + minor * sizeof(T)); // Major stride and major offset + case 4: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset + (minor + Offset) / num_minor) % num_major) * MajorStride + ((minor + Offset) % num_minor) * sizeof(T)); // Major stride and major+normal offset + case 3: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor)) * Stride); // Major+normal stride + case 2: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + (minor + Offset) / num_minor) % num_major) * MajorStride + (size_t)((minor + Offset) % num_minor) * Stride); // Major+normal stride and normal offset + case 1: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor)) * Stride); // Major+normal stride and major offset + case 0: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset + (minor + Offset) / num_minor) % num_major) * MajorStride + (size_t)((minor + Offset) % num_minor) * Stride); // Major+normal stride and major+normal offset + default: return T(0); + } + // clang-format on + } + const T* const Data; + const int MajorOffset, Offset, MajorStride, Stride, Type; +}; + //----------------------------------------------------------------------------- // [SECTION] Getters //----------------------------------------------------------------------------- @@ -926,12 +985,47 @@ template struct Get template IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const { return ImPlot3DPoint((float)IndexerX(idx), (float)IndexerY(idx), (float)IndexerZ(idx)); } + + template IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx, int minor, int major) const { return (*this)(idx); } + const _IndexerX IndexerX; const _IndexerY IndexerY; const _IndexerZ IndexerZ; const int Count; }; +template struct GetterMinorMajor { + GetterMinorMajor(const _Indexer& indexer, int num_major, int num_minor, int count, const ImVec2& major_bounds, const ImVec2& minor_bounds, + ImPlane3D plane, bool swap_axis) + : Indexer(indexer), NumMajor(num_major), NumMinor(num_minor), Count(count), + MajorValueRef((major_bounds.y - major_bounds.x) / (num_major - 1.0f)), MajorValueOffset(major_bounds.x), + MinorValueRef((minor_bounds.y - minor_bounds.x) / (num_minor - 1.0f)), MinorValueOffset(minor_bounds.x), Type(plane * 2 + swap_axis) {} + template IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const { + const int major = idx / NumMinor; + const int minor = idx % NumMinor; + return (*this)(idx, minor, major); + } + template IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx, int minor, int major) const { + const float major_value = major * MajorValueRef + MajorValueOffset; + const float minor_value = minor * MinorValueRef + MinorValueOffset; + const float value = (float)Indexer(idx, Count, major, minor, NumMajor, NumMinor); + switch (Type) { + case 5: return ImPlot3DPoint(major_value, minor_value, value); // X-Major + Y-Minor + Z-Values + case 4: return ImPlot3DPoint(minor_value, major_value, value); // X-Minor + Y-Major + Z-Values + case 3: return ImPlot3DPoint(major_value, value, minor_value); // X-Major + Y-Values + Z-Minor + case 2: return ImPlot3DPoint(minor_value, value, major_value); // X-Minor + Y-Values + Z-Major + case 1: return ImPlot3DPoint(value, major_value, minor_value); // X-Values + Y-Major + Z-Minor + case 0: return ImPlot3DPoint(value, minor_value, major_value); // X-Values + Y-Minor + Z-Major + default: return ImPlot3DPoint(0, 0, 0); + } + } + + const _Indexer& Indexer; + const int NumMajor, NumMinor, Count; + const float MajorValueRef, MajorValueOffset, MinorValueRef, MinorValueOffset; + const int Type; +}; + template struct GetterLoop { GetterLoop(_Getter getter) : Getter(getter), Count(getter.Count + 1) {} template IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const { @@ -963,9 +1057,9 @@ template struct GetterQuadLines { }; template struct GetterSurfaceLines { - GetterSurfaceLines(_Getter getter, int x_count, int y_count) : Getter(getter), XCount(x_count), YCount(y_count) { - int horizontal_segments = (XCount - 1) * YCount; - int vertical_segments = (YCount - 1) * XCount; + GetterSurfaceLines(_Getter getter, int minor_count, int major_count) : Getter(getter), MinorCount(minor_count), MajorCount(major_count) { + int horizontal_segments = (MinorCount - 1) * MajorCount; + int vertical_segments = (MajorCount - 1) * MinorCount; int segments = horizontal_segments + vertical_segments; Count = segments * 2; // Each segment has 2 endpoints } @@ -975,33 +1069,33 @@ template struct GetterSurfaceLines { int endpoint_i = (int)(idx % 2); int segment_i = (int)(idx / 2); - int horizontal_segments = (XCount - 1) * YCount; + int horizontal_segments = (MinorCount - 1) * MajorCount; int px, py; if (segment_i < horizontal_segments) { // Horizontal segment - int row = segment_i / (XCount - 1); - int col = segment_i % (XCount - 1); + int row = segment_i / (MinorCount - 1); + int col = segment_i % (MinorCount - 1); // Endpoint 0 is (col, row), endpoint 1 is (col+1, row) px = endpoint_i == 0 ? col : col + 1; py = row; } else { // Vertical segment int seg_v = segment_i - horizontal_segments; - int col = seg_v / (YCount - 1); - int row = seg_v % (YCount - 1); + int col = seg_v / (MajorCount - 1); + int row = seg_v % (MajorCount - 1); // Endpoint 0 is (col, row), endpoint 1 is (col, row+1) px = col; py = row + endpoint_i; } - return Getter(py * XCount + px); + return Getter(py * MinorCount + px, px, py); } const _Getter Getter; int Count; - const int XCount; - const int YCount; + const int MinorCount; + const int MajorCount; }; struct Getter3DPoints { @@ -1326,21 +1420,28 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES() // [SECTION] PlotSurface //----------------------------------------------------------------------------- -template void PlotSurfaceEx(const char* label_id, const _Getter& getter, int x_count, int y_count, double scale_min, +template void PlotSurfaceEx(const char* label_id, const _Getter& getter, int minor_count, int major_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 && !ImHasFlag(flags, ImPlot3DSurfaceFlags_NoFill)) { + ImPlane3D plane = ImPlane3D_XY; + if (ImHasFlag(flags, ImPlot3DSurfaceFlags_PlaneXZ)) + plane = ImPlane3D_XZ; + else if (ImHasFlag(flags, ImPlot3DSurfaceFlags_PlaneYZ)) + plane = ImPlane3D_YZ; + + SurfacePlotPlaneGetter plane_getter(plane); const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Fill]); - RenderPrimitives(getter, x_count, y_count, col_fill, scale_min, scale_max); + RenderPrimitives(getter, minor_count, major_count, col_fill, scale_min, scale_max, plane_getter); } // Render lines if (getter.Count >= 2 && n.RenderLine && !ImHasFlag(flags, ImPlot3DSurfaceFlags_NoLines)) { const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Line]); - RenderPrimitives(GetterSurfaceLines<_Getter>(getter, x_count, y_count), col_line, n.LineWeight); + RenderPrimitives(GetterSurfaceLines<_Getter>(getter, minor_count, major_count), col_line, n.LineWeight); } // Render markers @@ -1354,19 +1455,42 @@ template void PlotSurfaceEx(const char* label_id, const _Gett } } -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, +IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int minor_count, int major_count, double scale_min, double scale_max, ImPlot3DSurfaceFlags flags, int offset, int stride) { - int count = x_count * y_count; + int count = minor_count * major_count; if (count < 4) return; GetterXYZ, IndexerIdx, IndexerIdx> getter(IndexerIdx(xs, count, offset, stride), IndexerIdx(ys, count, offset, stride), IndexerIdx(zs, count, offset, stride), count); - return PlotSurfaceEx(label_id, getter, x_count, y_count, scale_min, scale_max, flags); + return PlotSurfaceEx(label_id, getter, minor_count, major_count, scale_min, scale_max, flags); +} + +IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* values, int minor_count, int major_count, double scale_min, double scale_max, + ImPlot3DSurfaceFlags flags, const ImVec2& minor_bounds, const ImVec2& major_bounds, int offset, int stride, + int major_offset, int major_stride) { + int count = major_count * minor_count; + if (count < 4) + return; + // Create the getter and the indexer that will be passed to PlotSurfaceEx. The getter and the indexer will produce the correct information based + // on the indexes passed in + IndexerIdxMajor indexer(values, major_count, minor_count, major_offset, offset, + (major_stride == IMPLOT3D_DEFAULT_MAJOR_STRIDE ? (sizeof(T) * minor_count) : major_stride), stride); + ImPlane3D plane = ImPlane3D_XY; + if (ImHasFlag(flags, ImPlot3DSurfaceFlags_PlaneXZ)) + plane = ImPlane3D_XZ; + else if (ImHasFlag(flags, ImPlot3DSurfaceFlags_PlaneYZ)) + plane = ImPlane3D_YZ; + const bool swap_axis = ImHasFlag(flags, ImPlot3DSurfaceFlags_SwapAxes); + GetterMinorMajor> getter(indexer, major_count, minor_count, count, major_bounds, minor_bounds, plane, swap_axis); + return PlotSurfaceEx(label_id, getter, minor_count, major_count, scale_min, scale_max, flags); } #define INSTANTIATE_MACRO(T) \ - template IMPLOT3D_API 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); + template IMPLOT3D_API void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int minor_count, int major_count, \ + double scale_min, double scale_max, ImPlot3DSurfaceFlags flags, int offset, int stride); \ + template IMPLOT3D_API void PlotSurface(const char* label_id, const T* values, int minor_count, int major_count, double scale_min, \ + double scale_max, ImPlot3DSurfaceFlags flags, const ImVec2& minor_bounds, const ImVec2& major_bounds, \ + int offset, int stride, int major_offset, int major_stride); CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO