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
33 changes: 33 additions & 0 deletions implot3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,9 @@ struct ImPlot3DSpec {
// Optionally use #user_data for context. Return the number of characters written (excluding null terminator)
typedef int (*ImPlot3DFormatter)(double value, char* buff, int size, void* user_data);

// Callback signature for data getter.
typedef ImPlot3DPoint (*ImPlot3DGetter)(int idx, const void* user_data);

// Callback signature for axis transform
typedef double (*ImPlot3DTransform)(double value, void* user_data);

Expand Down Expand Up @@ -630,20 +633,50 @@ IMPLOT3D_API void SetupLegend(ImPlot3DLocation location, ImPlot3DLegendFlags fla
// 2. If your data is in separate arrays or requires computation, you can copy/transform
// it into temporary float or double arrays before plotting.
//
// 3. Write a custom getter C function or C++ non-capturing lambda and pass it and optionally
// your data to an ImPlot3D function post-fixed with a G (e.g. PlotScatterG). This has a slight
// performance cost, but probably not enough to worry about unless your data is very large.
// Examples:
//
// ImPlot3DPoint MyDataGetter(int idx, const void* data) {
// const MyData* my_data = (const MyData*)data;
// return ImPlot3DPoint(
// my_data->GetX(idx),
// my_data->GetY(idx),
// my_data->GetZ(idx)
// );
// }
// ...
// auto lambda = [](int idx, const void* data) -> ImPlot3DPoint {
// // ...
// return {x, y, z};
// };
// ...
// if (ImPlot3D::BeginPlot("MyPlot")) {
// MyData my_data;
// ImPlot3D::PlotScatterG("scatter", MyDataGetter, &my_data, my_data.Size());
// ImPlot3D::PlotLineG("line", lambda, &my_data, 1000);
// ImPlot3D::EndPlot();
// }
//
// NB: All types are converted to double before plotting. You may lose information
// if you try plotting extremely large 64-bit integral types. Proceed with caution!

// Plots a scatter plot in 3D. Points are rendered as markers at the specified coordinates
IMPLOT3D_TMP void PlotScatter(const char* label_id, const T* xs, const T* ys, const T* zs, int count, const ImPlot3DSpec& spec = ImPlot3DSpec());
IMPLOT3D_API void PlotScatterG(const char* label_id, ImPlot3DGetter getter, const void* data, int count, const ImPlot3DSpec& spec = ImPlot3DSpec());

// Plots a line in 3D. Consecutive points are connected with line segments
IMPLOT3D_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, const T* zs, int count, const ImPlot3DSpec& spec = ImPlot3DSpec());
IMPLOT3D_API void PlotLineG(const char* label_id, ImPlot3DGetter getter, const void* data, int count, const ImPlot3DSpec& spec = ImPlot3DSpec());

// Plots triangles in 3D. Every 3 consecutive points define a triangle
IMPLOT3D_TMP void PlotTriangle(const char* label_id, const T* xs, const T* ys, const T* zs, int count, const ImPlot3DSpec& spec = ImPlot3DSpec());
IMPLOT3D_API void PlotTriangleG(const char* label_id, ImPlot3DGetter getter, const void* data, int count, const ImPlot3DSpec& spec = ImPlot3DSpec());

// Plots quads in 3D. Every 4 consecutive points define a quadrilateral
IMPLOT3D_TMP void PlotQuad(const char* label_id, const T* xs, const T* ys, const T* zs, int count, const ImPlot3DSpec& spec = ImPlot3DSpec());
IMPLOT3D_API void PlotQuadG(const char* label_id, ImPlot3DGetter getter, const void* data, int count, const ImPlot3DSpec& spec = ImPlot3DSpec());

// 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
Expand Down
109 changes: 109 additions & 0 deletions implot3d_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,31 @@ namespace MyImPlot3D {
// Example for Custom Styles section
void StyleSeaborn();

// Example for Custom Data and Getters section.
struct Vector3f {
Vector3f(const float x, const float y, const float z) : x(x), y(y), z(z) {}

float x;
float y;
float z;
};

// Example for Custom Data and Getters section.
struct WaveData {
WaveData(const double x, const double z, const double amp, const double freq, const double offset)
: X(x), Z(z), Amp(amp), Freq(freq), Offset(offset) {}

double X;
double Z;
double Amp;
double Freq;
double Offset;
};

ImPlot3DPoint SineWave(int idx, const void* wave_data);
ImPlot3DPoint SawWave(int idx, const void* wave_data);
ImPlot3DPoint Spiral(int idx, const void* wave_data);

} // namespace MyImPlot3D

namespace ImPlot3D {
Expand Down Expand Up @@ -1743,6 +1768,47 @@ void DemoCustomPerPointStyle() {
}
}

void DemoCustomDataAndGetters() {
IMGUI_DEMO_MARKER("Custom/Custom Data and Getters");
ImGui::BulletText("You can plot custom structs using the stride feature.");
ImGui::BulletText("Most plotters can also be passed a function pointer for getting data.");
ImGui::Indent();
ImGui::BulletText("You can optionally pass user data to be given to your getter function.");
ImGui::BulletText("Non-capturing C++ lambdas can be passed as function pointers as well!");
ImGui::Unindent();

if (ImPlot3D::BeginPlot("##Custom Data")) {
// custom structs using stride example:
const MyImPlot3D::Vector3f vec3_left_data[2] = {MyImPlot3D::Vector3f(0, 0, 0), MyImPlot3D::Vector3f(-1, -1, 1)};
const MyImPlot3D::Vector3f vec3_right_data[2] = {MyImPlot3D::Vector3f(0, 0, 0), MyImPlot3D::Vector3f(1, 1, 1)};

ImPlot3D::PlotLine("Vector3f", &vec3_left_data[0].x, &vec3_left_data[0].y, &vec3_left_data[0].z, 2,
{ImPlot3DProp_Stride, sizeof(MyImPlot3D::Vector3f)});
ImPlot3D::PlotLine("Vector3f", &vec3_right_data[0].x, &vec3_right_data[0].y, &vec3_right_data[0].z, 2,
{ImPlot3DProp_Stride, sizeof(MyImPlot3D::Vector3f)});

// custom getter example 1:
ImPlot3D::PlotLineG("Spiral", MyImPlot3D::Spiral, nullptr, 1000);

// custom getter example 2:
static MyImPlot3D::WaveData sine_data(0.001, 0.18, 0.08, 3.0, 0.25);
static MyImPlot3D::WaveData saw_data(0.001, 0.18, 0.08, 3.0, 0.75);
ImPlot3D::PlotLineG("Waves", MyImPlot3D::SineWave, &sine_data, 1000);
ImPlot3D::PlotLineG("Waves", MyImPlot3D::SawWave, &saw_data, 1000);

// you can also pass C++ lambdas:
//
// auto lambda = [](int idx, const void* data) -> ImPlot3DPoint {
// ...
// return {x, y, z};
// };
//
// ImPlot3D::PlotLineG("My Lambda", lambda, data, 1000);

ImPlot3D::EndPlot();
}
}

//-----------------------------------------------------------------------------
// [SECTION] Config
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -1904,6 +1970,7 @@ void ShowAllDemos() {
DemoHeader("Custom Rendering", DemoCustomRendering);
DemoHeader("Custom Overlay", DemoCustomOverlay);
DemoHeader("Custom Per-Point Style", DemoCustomPerPointStyle);
DemoHeader("Custom Data and Getters", DemoCustomDataAndGetters);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Config")) {
Expand Down Expand Up @@ -2388,4 +2455,46 @@ void StyleSeaborn() {
style.PlotMinSize = ImVec2(300, 225);
}

ImPlot3DPoint SineWave(const int idx, const void* const wave_data) {
const auto* const wd = static_cast<const MyImPlot3D::WaveData*>(wave_data);

const double x = idx * wd->X;
const double y = wd->Z;
const double z = wd->Offset + wd->Amp * ImSin(2.0 * 3.14f * wd->Freq * x);

return {x, y, z};
}

ImPlot3DPoint SawWave(const int idx, const void* const wave_data) {
const auto* const wd = static_cast<const MyImPlot3D::WaveData*>(wave_data);

const double x = idx * wd->X;
const double y = wd->Z;

const double phase = 3.14f * wd->Freq * x;
const double z = wd->Offset + wd->Amp * (-2.0 / 3.14f * ImAtan2(ImCos(phase), ImSin(phase)));

return {x, y, z};
}

ImPlot3DPoint Spiral(const int idx, const void*) {
constexpr float outer_radius = 0.9f;
constexpr float inner_radius = 0.0f;
constexpr float increment_per_rev = 0.05f;
constexpr int point_count = 1000;

constexpr float revolutions = (outer_radius - inner_radius) / increment_per_rev;
constexpr float max_theta = 2.0f * revolutions * 3.14f;
const float t = static_cast<float>(idx) / static_cast<float>(point_count - 1);
const float theta = max_theta * t;

const float radius = inner_radius + increment_per_rev * theta / (2.0f * 3.14f);

const float x = radius * ImCos(theta);
const float y = radius * ImSin(theta);
const float z = t;

return {x, y, z};
}

} // namespace MyImPlot3D
43 changes: 43 additions & 0 deletions implot3d_items.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,20 @@ template <typename TGX, typename TGY, typename TGZ> struct GetterMeshTriangles {
int Count;
};

/// Interprets a user's function pointer as ImPlot3DPoints
struct GetterFuncPtr {
GetterFuncPtr(const ImPlot3DGetter getter, const void* const data, const int count) : Getter(getter), Data(data), Count(count) {}

template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator[](I idx) const { return Getter(idx, Data); }

template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const { return Getter(idx, Data); }

ImPlot3DGetter Getter;
const void* const Data;
const int Count;
typedef ImPlot3DPoint value_type;
};

//-----------------------------------------------------------------------------
// [SECTION] Color and Size Getters
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -1273,6 +1287,13 @@ template <typename T> void PlotScatter(const char* label_id, const T* xs, const
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
#undef INSTANTIATE_MACRO

IMPLOT3D_API void PlotScatterG(const char* const label_id, const ImPlot3DGetter getter, const void* data, const int count, const ImPlot3DSpec& spec) {
if (count < 1)
return;

return PlotScatterEx(label_id, GetterFuncPtr(getter, data, count), spec);
}

//-----------------------------------------------------------------------------
// [SECTION] PlotLine
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -1341,6 +1362,13 @@ IMPLOT3D_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, const
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
#undef INSTANTIATE_MACRO

IMPLOT3D_API void PlotLineG(const char* const label_id, const ImPlot3DGetter getter, const void* data, const int count, const ImPlot3DSpec& spec) {
if (count < 2)
return;

return PlotLineEx(label_id, GetterFuncPtr(getter, data, count), spec);
}

//-----------------------------------------------------------------------------
// [SECTION] PlotTriangle
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -1391,6 +1419,14 @@ IMPLOT3D_TMP void PlotTriangle(const char* label_id, const T* xs, const T* ys, c
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
#undef INSTANTIATE_MACRO

IMPLOT3D_API void PlotTriangleG(const char* const label_id, const ImPlot3DGetter getter, const void* data, const int count,
const ImPlot3DSpec& spec) {
if (count < 3)
return;

return PlotTriangleEx(label_id, GetterFuncPtr(getter, data, count), spec);
}

//-----------------------------------------------------------------------------
// [SECTION] PlotQuad
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -1440,6 +1476,13 @@ IMPLOT3D_TMP void PlotQuad(const char* label_id, const T* xs, const T* ys, const
CALL_INSTANTIATE_FOR_NUMERIC_TYPES()
#undef INSTANTIATE_MACRO

IMPLOT3D_API void PlotQuadG(const char* const label_id, const ImPlot3DGetter getter, const void* data, const int count, const ImPlot3DSpec& spec) {
if (count < 3)
return;

return PlotQuadEx(label_id, GetterFuncPtr(getter, data, count), spec);
}

//-----------------------------------------------------------------------------
// [SECTION] PlotSurface
//-----------------------------------------------------------------------------
Expand Down