-
Notifications
You must be signed in to change notification settings - Fork 54
Feat: Simplify PlotSurface API #135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
ae10a40
103ec10
9e93356
3903969
15f563e
dc073f1
71a400a
35f4047
dff4d4f
cb3b07c
62d4bcd
d4b4b25
77ac908
c4faa99
39fab8d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,6 +37,8 @@ | |
#include "imgui.h" | ||
#ifndef IMGUI_DISABLE | ||
|
||
#include <limits.h> | ||
|
||
//----------------------------------------------------------------------------- | ||
// [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 <typename T> IMPLOT3D_API | ||
#define IMPLOT3D_DEFAULT_MAJOR_STRIDE INT_MAX | ||
|
||
//----------------------------------------------------------------------------- | ||
// [SECTION] Forward declarations and basic types | ||
|
@@ -453,6 +456,10 @@ IMPLOT3D_TMP void PlotQuad(const char* label_id, const T* xs, const T* ys, const | |
// 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_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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could maybe simplify this. Having all these parameters allows for a lot of control. |
||
const ImVec2& minor_bounds = ImVec2(-1, 1), const ImVec2& major_bounds = ImVec2(-1, 1), ImPlot3DSurfaceFlags flags = 0, | ||
ImAxis3D values_axis = ImAxis3D_Z, ImAxis3D major_axis = ImAxis3D_Y, int minor_offset = 0, int major_offset = 0, | ||
int minor_stride = sizeof(T), 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); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -397,6 +397,95 @@ void DemoSurfacePlots() { | |
ImPlot3D::PopColormap(); | ||
} | ||
|
||
void DemoSimplifiedSurfacePlotsOffsetStride() { | ||
constexpr int N = 10; | ||
constexpr int M = 20; | ||
static float values[N * M]; | ||
static float t = 0.0f; | ||
// t += ImGui::GetIO().DeltaTime; | ||
ACvanWyk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// 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(2 * t + ImSqrt((x * x + y * y))); // value = sin(2t + sqrt(x^2 + y^2)) | ||
} | ||
} | ||
|
||
// Determine the | ||
static ImAxis3D values_axis = ImAxis3D_Z; | ||
static ImAxis3D major_axis = ImAxis3D_Y; | ||
if (ImGui::Combo("Values Axis", &values_axis, "X-Axis\0Y-Axis\0Z-Axis\0")) { | ||
// The major and value axis cannot be the same | ||
if (major_axis == values_axis) { | ||
major_axis = (major_axis + 1) % ImAxis3D_COUNT; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Values-Axis and the Major-Axis cannot have the same value. Change them if they are the same. |
||
} | ||
} | ||
if (ImGui::Combo("Major Axis", &major_axis, "X-Axis\0Y-Axis\0Z-Axis\0")) { | ||
if (major_axis == values_axis) { | ||
values_axis = (values_axis + 1) % ImAxis3D_COUNT; | ||
} | ||
} | ||
// Add offset and stride | ||
static int minor_offset = 0; | ||
static int major_offset = 0; | ||
static int minor_stride = 1; | ||
static int major_stride = 1; | ||
|
||
ImGui::SliderInt("Minor Offset", &minor_offset, -20, 20); | ||
ImGui::SliderInt("Major Offset", &major_offset, -20, 20); | ||
ImGui::SliderInt("Minor Stride", &minor_stride, -7, 7); | ||
ImGui::SliderInt("Major Stride", &major_stride, -7, 7); | ||
|
||
// Select flags | ||
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(-1, 1, -1, 1, -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)); | ||
|
||
// Update the number of minor and major items depending on the stride that was selected | ||
const int updated_num_minor = minor_stride == 0 ? M : (M / abs(minor_stride) + (M % minor_stride == 0 ? 0 : 1)); | ||
const int updated_num_major = major_stride == 0 ? N : (N / abs(major_stride) + (N % major_stride == 0 ? 0 : 1)); | ||
|
||
// Add an offset to the array if either the major or the minor stride is negative | ||
const int array_offset = (major_stride < 0 ? (M * (N - 1)) : 0) + (minor_stride < 0 ? M : 1) - 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to add an offset to the array since we are now striding backwards. I added support for negative strides if needed. I don't know if it necessary? |
||
|
||
// 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, ImVec2(1, -1), ImVec2(-1, 1), | ||
flags, values_axis, major_axis, minor_offset, major_offset, minor_stride * sizeof(float), | ||
major_stride * M * sizeof(float)); | ||
|
||
// 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"); | ||
|
@@ -873,6 +962,7 @@ void ShowAllDemos() { | |
DemoHeader("Triangle Plots", DemoTrianglePlots); | ||
DemoHeader("Quad Plots", DemoQuadPlots); | ||
DemoHeader("Surface Plots", DemoSurfacePlots); | ||
DemoHeader("Simplified Surface Plots with Offset and Stride", DemoSimplifiedSurfacePlotsOffsetStride); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what to call this or where to put this. There is also nothing really simple about it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I split this into two separate demos:
Did not know where to put the axis, offset and stride demo. I followed the example of |
||
DemoHeader("Mesh Plots", DemoMeshPlots); | ||
DemoHeader("Realtime Plots", DemoRealtimePlots); | ||
DemoHeader("Image Plots", DemoImagePlots); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -917,6 +917,50 @@ template <typename T> struct IndexerIdx { | |
int Stride; | ||
}; | ||
|
||
template <typename T> struct IndexerIdxMajorMinor { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what to call the |
||
IndexerIdxMajorMinor(const T* data, int num_major, int num_minor, int major_offset, int minor_offset, int major_stride, int minor_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 && minor_stride > 0) ? ImPosMod(num_minor * major_offset, num_minor * num_major) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a little optimization here which makes it a bit more difficult to follow what is going on but I think worth it. I am hoping it is clear enough? |
||
: ImPosMod(major_offset, num_major)), | ||
MinorOffset(ImPosMod(minor_offset, num_minor)), MajorStride(major_stride), MinorStride(minor_stride), | ||
Type(((MinorOffset == 0) << 0) | ((MajorOffset == 0) << 1) | ((MinorStride == int(sizeof(T)) && MajorStride > 0) << 2) | | ||
((MajorStride == int(sizeof(T)) * num_minor && MinorStride > 0) << 3)) {} | ||
|
||
template <typename I> 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 <typename I> 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[(((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]; // Minor offset | ||
case 13: return Data[(MajorOffset + idx) % count]; // Major offset | ||
case 12: return Data[(MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]; // Major+minor offset | ||
case 11: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((idx)) * MinorStride); // Minor stride | ||
case 10: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride); // Minor stride and minor offset | ||
case 9: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + idx) % count) * MinorStride); // Minor stride and major offset | ||
case 8: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride); // Minor stride and major + minor 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)) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T)); // Major stride and minor offset | ||
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) % num_major) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T)); // Major stride and major+minor offset | ||
case 3: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor)) * MinorStride); // Major+minor stride | ||
case 2: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride); // Major+minor stride and minor offset | ||
case 1: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor)) * MinorStride); // Major+minor stride and major offset | ||
case 0: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride); // Major+minor stride and major+minor offset | ||
default: return T(0); | ||
} | ||
// clang-format on | ||
} | ||
const T* const Data; | ||
const int MajorOffset, MinorOffset, MajorStride, MinorStride, Type; | ||
}; | ||
|
||
//----------------------------------------------------------------------------- | ||
// [SECTION] Getters | ||
//----------------------------------------------------------------------------- | ||
|
@@ -932,6 +976,36 @@ template <typename _IndexerX, typename _IndexerY, typename _IndexerZ> struct Get | |
const int Count; | ||
}; | ||
|
||
template <typename _Indexer> struct GetterMinorMajor { | ||
GetterMinorMajor(const _Indexer& indexer, int num_major, int num_minor, int count, const ImVec2& major_bounds, const ImVec2& minor_bounds, | ||
ImAxis3D values_axis, ImAxis3D major_axis) | ||
: Indexer(indexer), NumMajor(num_major), NumMinor(num_minor), Count(count), MajorRef(major_bounds.y - major_bounds.x), | ||
MajorOffset(major_bounds.x), MinorRef(minor_bounds.y - minor_bounds.x), MinorOffset(minor_bounds.x), Type(values_axis * 3 + major_axis) {} | ||
template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const { | ||
const int major = idx / NumMinor; | ||
const int minor = idx % NumMinor; | ||
const float major_value = (major / float(NumMajor - 1.0f)) * MajorRef + MajorOffset; | ||
const float minor_value = (minor / float(NumMinor - 1.0f)) * MinorRef + MinorOffset; | ||
const float value = (float)Indexer(idx, Count, major, minor, NumMajor, NumMinor); | ||
switch (Type) { | ||
case 7: return ImPlot3DPoint(minor_value, major_value, value); // Z-Values + Y-Major | ||
case 6: return ImPlot3DPoint(major_value, minor_value, value); // Z-Values + X-Major | ||
case 5: return ImPlot3DPoint(minor_value, value, major_value); // Y-Values + Z-Major | ||
case 3: return ImPlot3DPoint(major_value, value, minor_value); // Y-Values + X-Major | ||
case 2: return ImPlot3DPoint(value, minor_value, major_value); // X-Values + Z-Major | ||
case 1: return ImPlot3DPoint(value, major_value, minor_value); // X-Values + Y-Major | ||
case 8: // Z-Values + Z-Major. Not valid. Maybe assert here? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should not support the value axis and major axis being the same type. There is a |
||
case 4: // Y-Values + Y-Major. Not valid. Maybe assert here? | ||
case 0: // X-Values + X-Major. Not Valid. Maybe assert here? | ||
default: return ImPlot3DPoint(0, 0, 0); | ||
} | ||
} | ||
const _Indexer& Indexer; | ||
const int NumMajor, NumMinor, Count; | ||
const float MajorRef, MajorOffset, MinorRef, MinorOffset; | ||
const int Type; | ||
}; | ||
|
||
template <typename _Getter> struct GetterLoop { | ||
GetterLoop(_Getter getter) : Getter(getter), Count(getter.Count + 1) {} | ||
template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const { | ||
|
@@ -1364,9 +1438,32 @@ IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, co | |
return PlotSurfaceEx(label_id, getter, x_count, y_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, | ||
const ImVec2& minor_bounds, const ImVec2& major_bounds, ImPlot3DSurfaceFlags flags, ImAxis3D values_axis, | ||
ImAxis3D major_axis, int minor_offset, int major_offset, int minor_stride, int major_stride) { | ||
IM_ASSERT_USER_ERROR(values_axis != major_axis, "The values axis and major axis needs to be two different values"); | ||
// IM_ASSERT_USER_ERROR(values_axis == ImAxis3D_Z, "Only support Z-Axis at the moment. Need to change PlotSurfaceEx for this to work with anything | ||
ACvanWyk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// other than z-axis"); | ||
int count = major_count * minor_count; | ||
if (count < 4) | ||
return; | ||
GetterMinorMajor<IndexerIdxMajorMinor<T>> getter( | ||
IndexerIdxMajorMinor<T>(values, major_count, minor_count, major_offset, minor_offset, | ||
(major_stride == IMPLOT3D_DEFAULT_MAJOR_STRIDE ? (sizeof(T) * minor_count) : major_stride), minor_stride), | ||
major_count, minor_count, count, major_bounds, minor_bounds, values_axis, major_axis); | ||
|
||
// TODO: I am pretty sure that the way that PlotSurfaceEx works is that it takes in the minor and the major count and thus it first iterates over | ||
// the major then minor values. I need to confirm this first though | ||
return PlotSurfaceEx(label_id, getter, minor_count, major_count, scale_min, scale_max, flags); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @brenocq: As far as I can tell There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did some further investigation and other than the |
||
} | ||
|
||
#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, \ | ||
double scale_min, double scale_max, ImPlot3DSurfaceFlags flags, int offset, int stride); | ||
double scale_min, double scale_max, ImPlot3DSurfaceFlags flags, int offset, int stride); \ | ||
template IMPLOT3D_API void PlotSurface<T>(const char* label_id, const T* values, int minor_count, int major_count, double scale_min, \ | ||
double scale_max, const ImVec2& minor_bounds, const ImVec2& major_bounds, ImPlot3DSurfaceFlags flags, \ | ||
ImAxis3D values_axis, ImAxis3D major_axis, int minor_offset, int major_offset, int minor_stride, \ | ||
int major_stride); | ||
CALL_INSTANTIATE_FOR_NUMERIC_TYPES() | ||
#undef INSTANTIATE_MACRO | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need this for
INT_MAX
used inIMPLOT3D_DEFAULT_MAJOR_STRIDE
. Cannot usesizeof(T)
,-1(i/uint8)
or0
which are all potentially valid values.I cannot think of a good value to make
IMPLOT3D_DEFAULT_MAJOR_STRIDE
other thanINT_MAX
. Maybe somebody has a better suggestion?