Skip to content

Commit

Permalink
Demo immapp: Add Haiku Butterfly (using ImPlot3d)
Browse files Browse the repository at this point in the history
  • Loading branch information
pthom committed Jan 4, 2025
1 parent bdef831 commit d109ab2
Show file tree
Hide file tree
Showing 4 changed files with 244 additions and 9 deletions.
9 changes: 5 additions & 4 deletions bindings/imgui_bundle/demos_cpp/demo_immapp_launcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,13 @@ std::function<void()> makeGui()
DemoApp{
"demo_powersave", "How to have smooth animations, and how spare the CPU when idling"
},
DemoApp{
"demo_font_common_glyph_range",
"How to load fonts with specific glyph ranges (e.g., Chinese, Japanese, Korean)",
},
DemoApp{"demo_testengine", "How to use ImGui Test Engine to test and automate your application"},
DemoApp{"demo_python_context_manager",
"How to use a python context manager for `imgui.begin()`, `imgui.end()`, etc."},
DemoApp{"demo_command_palette", "a Sublime Text or VSCode style command palette in ImGui"},
DemoApp{"demo_parametric_curve","Illustration of the Immediate GUI paradigm, with a simple parametric curve"},
DemoApp{"haiku_implot_heart", "Share some love for ImGui and ImPlot"},
DemoApp{"haiku_butterfly", "Haiku: Butterfly effect using ImPlot3D"},
DemoApp{"demo_drag_and_drop", "Drag and drop demo"},
DemoApp{"demo_implot_markdown", "How to quickly run an app that uses implot and/or markdown with ImmApp"},
DemoApp{
Expand All @@ -49,6 +46,10 @@ std::function<void()> makeGui()
"demo_pydantic",
"Python: How to use ImVec2 and ImVec4 with Pydantic",
},
DemoApp{
"demo_font_common_glyph_range",
"How to load fonts with specific glyph ranges (e.g., Chinese, Japanese, Korean)",
},
DemoApp{
"imgui_example_glfw_opengl3",
"Python: translation of the [GLFW+OpenGL3 example](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_opengl3/main.cpp) from Dear ImGui. "
Expand Down
128 changes: 128 additions & 0 deletions bindings/imgui_bundle/demos_cpp/demos_immapp/haiku_butterfly.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Lorenz Attractor & Butterfly Effect
// This example demonstrates the Lorenz Attractor and the butterfly effect,
// showing how tiny changes in initial conditions lead to diverging trajectories.

#include "imgui.h"
#include "implot3d/implot3d.h"
#include "immapp/runner.h"
#include "imgui_md_wrapper/imgui_md_wrapper.h"
#include <vector>

struct LorenzParams {
float sigma = 10.0f;
float rho = 28.0f;
float beta = 8.0f / 3.0f;
float dt = 0.01f;
int max_size = 2000;
} PARAMS;


class AnimatedLorenzTrajectory {
public:
AnimatedLorenzTrajectory(float x, float y, float z) : xs({x}), ys({y}), zs({z}) {}

void step() {
float x = xs.back(), y = ys.back(), z = zs.back();
float dx = PARAMS.sigma * (y - x);
float dy = x * (PARAMS.rho - z) - y;
float dz = x * y - PARAMS.beta * z;
x += dx * PARAMS.dt;
y += dy * PARAMS.dt;
z += dz * PARAMS.dt;

xs.push_back(x);
ys.push_back(y);
zs.push_back(z);

if (xs.size() > static_cast<size_t>(PARAMS.max_size)) {
xs.erase(xs.begin());
ys.erase(ys.begin());
zs.erase(zs.begin());
}
}
std::vector<float> xs, ys, zs;
};

class CompareLorenzTrajectories
{
public:
float initial_delta = 0.1f;

CompareLorenzTrajectories() { init_trajectories(); }

void init_trajectories() {
traj1 = std::make_unique<AnimatedLorenzTrajectory>(0.0f, 1.0f, 1.05f);
traj2 = std::make_unique<AnimatedLorenzTrajectory>(0.0f + initial_delta, 1.0f, 1.05f);
}

void gui_params() {
ImGui::SliderFloat("Sigma", &PARAMS.sigma, 0.0f, 100.0f);
ImGui::SetItemTooltip("Controls the rate of divergence between nearby points (chaos level).");

ImGui::SliderFloat("Rho", &PARAMS.rho, 0.0f, 100.0f);
ImGui::SetItemTooltip("Determines the size and shape of the attractor.");

ImGui::SliderFloat("Beta", &PARAMS.beta, 0.0f, 10.0f);
ImGui::SetItemTooltip("A damping parameter affecting vertical movement.");

ImGui::SliderFloat("dt", &PARAMS.dt, 0.0f, 0.05f);
ImGui::SetItemTooltip("Time step size for numerical integration (smaller is smoother).");

ImGui::SliderFloat("Initial Delta", &initial_delta, 0.0f, 0.2f);
ImGui::SetItemTooltip("Initial difference between trajectories to demonstrate divergence.");

if (ImGui::Button("Reset")) {
init_trajectories();
}
}

void gui_plot() {
if (ImPlot3D::BeginPlot("Lorenz Attractor", HelloImGui::EmToVec2(40, 40))) {
ImPlot3D::SetupAxes("X", "Y", "Z",
ImPlot3DAxisFlags_AutoFit,
ImPlot3DAxisFlags_AutoFit,
ImPlot3DAxisFlags_AutoFit);
ImPlot3D::PlotLine(
"Trajectory", traj1->xs.data(), traj1->ys.data(), traj1->zs.data(), traj1->xs.size());
ImPlot3D::PlotLine("Trajectory2", traj2->xs.data(), traj2->ys.data(), traj2->zs.data(), traj2->xs.size());
ImPlot3D::EndPlot();
}
traj1->step();
traj2->step();
}

void gui() {
ImGuiMd::RenderUnindented(R"(
# Lorenz Attractor & Butterfly Effect
This is a simple example of the Lorenz Attractor. It shows two trajectories that diverge
because of a small initial difference, illustrating chaos theory in action.
The term **butterfly effect** in popular media may stem from the real-world implications
of the Lorenz attractor, namely that tiny changes in initial conditions evolve to
completely different trajectories.)");
ImGui::SeparatorText("Parameters");
gui_params();
ImGui::SeparatorText("Plot");
gui_plot();
}

private:
std::unique_ptr<AnimatedLorenzTrajectory> traj1, traj2;
};

int main() {
CompareLorenzTrajectories lorenz_comparer;
ImmApp::AddOnsParams addOnsParams;
addOnsParams.withImplot3d = true;
addOnsParams.withMarkdown = true;

HelloImGui::RunnerParams runnerParams;
runnerParams.fpsIdling.enableIdling = false;
runnerParams.appWindowParams.windowGeometry.sizeAuto = true;
runnerParams.appWindowParams.windowTitle = "Butterfly Effect";
runnerParams.callbacks.ShowGui = [&]() { lorenz_comparer.gui(); };

ImmApp::Run(runnerParams, addOnsParams);

return 0;
}
11 changes: 6 additions & 5 deletions bindings/imgui_bundle/demos_python/demo_immapp_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ def make_gui() -> GuiFunction:
"demo_powersave",
"How to have smooth animations, and how spare the CPU when idling",
),
DemoApp(
"demo_font_common_glyph_range",
"How to load fonts with specific glyph ranges (e.g., Chinese, Japanese, Korean)",
),
DemoApp(
"demo_testengine",
"How to use ImGui Test Engine to test and automate your application",
Expand All @@ -55,7 +51,8 @@ def make_gui() -> GuiFunction:
"demo_parametric_curve",
"Illustration of the Immediate GUI paradigm, with a simple parametric curve",
),
DemoApp("haiku_implot_heart", "Share some love for ImGui and ImPlot"),
DemoApp("haiku_implot_heart", "Haiku: share some love for ImGui and ImPlot"),
DemoApp("haiku_butterfly", "Haiku: Butterfly effect using ImPlot3D"),
DemoApp("demo_drag_and_drop", "Drag and drop demo"),
DemoApp(
"demo_implot_markdown",
Expand All @@ -69,6 +66,10 @@ def make_gui() -> GuiFunction:
"demo_pydantic",
"Python: How to use ImVec2 and ImVec4 with Pydantic",
),
DemoApp(
"demo_font_common_glyph_range",
"How to load fonts with specific glyph ranges (e.g., Chinese, Japanese, Korean)",
),
DemoApp(
"imgui_example_glfw_opengl3",
"Python: translation of the [GLFW+OpenGL3 example](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_opengl3/main.cpp) from Dear ImGui. "
Expand Down
105 changes: 105 additions & 0 deletions bindings/imgui_bundle/demos_python/demos_immapp/haiku_butterfly.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""# Lorenz Attractor & Butterfly Effect
This is a simple example of the Lorenz Attractor. It shows two trajectories that diverge
because of a small initial difference, illustrating chaos theory in action.
The term **butterfly effect** in popular media may stem from the real-world implications
of the Lorenz attractor, namely that tiny changes in initial conditions evolve to
completely different trajectories.
"""
# TODO: HANDLE NEWLINES IN MARKDOWN AS SPACE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

import numpy as np
from imgui_bundle import implot3d, immapp, imgui, imgui_md, hello_imgui
from dataclasses import dataclass


@dataclass
class LorenzParams:
sigma: float = 10.0
rho: float = 28.0
beta: float = 8.0 / 3.0
dt: float = 0.01
max_size: int = 2000

PARAMS = LorenzParams()


class AnimatedLorenzTrajectory:
def __init__(self, x, y, z):
self.xs = np.array([x])
self.ys = np.array([y])
self.zs = np.array([z])

def step(self):
x, y, z = self.xs[-1], self.ys[-1], self.zs[-1]
dx = PARAMS.sigma * (y - x)
dy = x * (PARAMS.rho - z) - y
dz = x * y - PARAMS.beta * z
x += dx * PARAMS.dt
y += dy * PARAMS.dt
z += dz * PARAMS.dt

self.xs = np.concatenate([self.xs, [x]])
self.ys = np.concatenate([self.ys, [y]])
self.zs = np.concatenate([self.zs, [z]])
if len(self.xs) > PARAMS.max_size:
self.xs = self.xs[-PARAMS.max_size:]
self.ys = self.ys[-PARAMS.max_size:]
self.zs = self.zs[-PARAMS.max_size:]


class CompareLorenzTrajectories:
initial_delta = 0.1
def __init__(self):
self.init_trajectories()

def init_trajectories(self):
x, y, z = 0.0, 1.0, 1.05
self.traj1 = AnimatedLorenzTrajectory(x, y, z)
self.traj2 = AnimatedLorenzTrajectory(x + self.initial_delta, y, z)

def gui_params(self):
_, PARAMS.sigma = imgui.slider_float("Sigma", PARAMS.sigma, 0.0, 100.0)
imgui.set_item_tooltip("Controls the rate of divergence between nearby points (chaos level).")

_, PARAMS.rho = imgui.slider_float("Rho", PARAMS.rho, 0.0, 100.0)
imgui.set_item_tooltip("Determines the size and shape of the attractor.")

_, PARAMS.beta = imgui.slider_float("Beta", PARAMS.beta, 0.0, 10.0)
imgui.set_item_tooltip("A damping parameter affecting vertical movement.")

_, PARAMS.dt = imgui.slider_float("dt", PARAMS.dt, 0.0, 0.05)
imgui.set_item_tooltip("Time step size for numerical integration (smaller is smoother).")

_, self.initial_delta = imgui.slider_float("Initial Delta", self.initial_delta, 0.0, 0.2)
imgui.set_item_tooltip("Initial difference between trajectories to demonstrate divergence.")

if imgui.button("Reset"):
self.init_trajectories()

def gui_plot(self):
if implot3d.begin_plot("Lorenz Attractor", hello_imgui.em_to_vec2(40, 40)):
implot3d.setup_axes("X", "Y", "Z",
implot3d.AxisFlags_.auto_fit.value, implot3d.AxisFlags_.auto_fit.value, implot3d.AxisFlags_.auto_fit.value)
implot3d.plot_line("Trajectory", self.traj1.xs, self.traj1.ys, self.traj1.zs)
implot3d.plot_line("Trajectory2", self.traj2.xs, self.traj2.ys, self.traj2.zs)
implot3d.end_plot()
self.traj1.step()
self.traj2.step()

def gui(self):
imgui_md.render_unindented(__doc__)
imgui.separator_text("Parameters")
self.gui_params()
imgui.separator_text("Plot")
self.gui_plot()


lorenz_comparer = CompareLorenzTrajectories()

immapp.run(lambda: lorenz_comparer.gui(),
with_implot3d=True,
with_markdown=True,
window_size_auto=True,
window_title="Butterfly Effect",
fps_idle=0)

0 comments on commit d109ab2

Please sign in to comment.