Skip to content

Commit 8255a76

Browse files
feat(graph): add parametric plotting support (f(t), g(t))
1 parent b14b753 commit 8255a76

File tree

1 file changed

+104
-23
lines changed

1 file changed

+104
-23
lines changed

src/core/Core/Application.cpp

Lines changed: 104 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,10 @@ ExitStatus App::Application::run() {
123123
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse |
124124
ImGuiWindowFlags_NoTitleBar);
125125

126-
ImGui::InputTextMultiline(
127-
"##search", function, sizeof(function), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 4));
126+
ImGui::InputTextMultiline("##search",
127+
function,
128+
sizeof(function),
129+
ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 4));
128130

129131
ImGui::End();
130132
}
@@ -162,36 +164,115 @@ ExitStatus App::Application::run() {
162164
IM_COL32(0, 0, 0, 255),
163165
lineThickness); // Y-axis
164166

165-
// 2. Plot a function (e.g., y = sin(x))
166167
const float zoom = 100.0f; // Pixels per unit
167168
std::vector<ImVec2> points;
168169

169-
double x;
170+
// (f(t), g(t))
171+
std::string func_str(function);
172+
auto trim = [](std::string s) {
173+
const char* ws = " \t\n\r";
174+
size_t start = s.find_first_not_of(ws);
175+
size_t end = s.find_last_not_of(ws);
176+
if (start == std::string::npos)
177+
return std::string();
178+
return s.substr(start, end - start + 1);
179+
};
180+
181+
bool plotted = false;
182+
183+
if (!func_str.empty() && func_str.front() == '(' && func_str.back() == ')') {
184+
const std::string inner = func_str.substr(1, func_str.size() - 2);
185+
// top-level comma separating f and g
186+
int depth = 0;
187+
size_t split_pos = std::string::npos;
188+
for (size_t i = 0; i < inner.size(); ++i) {
189+
char c = inner[i];
190+
if (c == '(')
191+
++depth;
192+
else if (c == ')')
193+
--depth;
194+
else if (c == ',' && depth == 0) {
195+
split_pos = i;
196+
break;
197+
}
198+
}
199+
200+
if (split_pos != std::string::npos) {
201+
std::string fx = trim(inner.substr(0, split_pos));
202+
std::string gx = trim(inner.substr(split_pos + 1));
203+
204+
// Prepare exprtk
205+
double t = 0.0;
206+
exprtk::symbol_table<double> sym_t;
207+
sym_t.add_constants();
208+
addConstants(sym_t);
209+
sym_t.add_variable("t", t);
210+
211+
exprtk::expression<double> expr_fx;
212+
expr_fx.register_symbol_table(sym_t);
213+
exprtk::expression<double> expr_gx;
214+
expr_gx.register_symbol_table(sym_t);
215+
216+
exprtk::parser<double> parser;
217+
bool ok_fx = parser.compile(fx, expr_fx);
218+
bool ok_gx = parser.compile(gx, expr_gx);
219+
220+
if (ok_fx && ok_gx) {
221+
// iterate t
222+
const double t_min = -10.0;
223+
const double t_max = 10.0;
224+
const double t_step = 0.02;
225+
226+
for (t = t_min; t <= t_max; t += t_step) {
227+
const double vx = expr_fx.value();
228+
const double vy = expr_gx.value();
229+
230+
231+
ImVec2 screen_pos(origin.x + static_cast<float>(vx * zoom),
232+
origin.y - static_cast<float>(vy * zoom));
233+
points.push_back(screen_pos);
234+
}
235+
236+
// Draw curve
237+
draw_list->AddPolyline(points.data(),
238+
points.size(),
239+
IM_COL32(64, 128, 199, 255),
240+
ImDrawFlags_None,
241+
lineThickness);
242+
plotted = true;
243+
}
244+
}
245+
}
170246

171-
exprtk::symbol_table<double> symbolTable;
172-
symbolTable.add_constants();
173-
addConstants(symbolTable);
174-
symbolTable.add_variable("x", x);
247+
if (!plotted) {
248+
// Fallback to y = f(x) plotting using variable x
249+
double x;
175250

176-
exprtk::expression<double> expression;
177-
expression.register_symbol_table(symbolTable);
251+
exprtk::symbol_table<double> symbolTable;
252+
symbolTable.add_constants();
253+
addConstants(symbolTable);
254+
symbolTable.add_variable("x", x);
178255

179-
exprtk::parser<double> parser;
180-
parser.compile(function, expression);
256+
exprtk::expression<double> expression;
257+
expression.register_symbol_table(symbolTable);
181258

182-
for (x = -canvas_sz.x / (2 * zoom); x < canvas_sz.x / (2 * zoom); x += 0.05) {
183-
// This loop uses the *mathematical* values of x. This is later converted to the pixel
184-
// values below
185-
const double y = expression.value();
259+
exprtk::parser<double> parser;
260+
parser.compile(function, expression);
186261

187-
// Convert graph coordinates to screen coordinates
188-
ImVec2 screen_pos(origin.x + x * zoom, origin.y - y * zoom);
189-
points.push_back(screen_pos);
190-
}
262+
for (x = -canvas_sz.x / (2 * zoom); x < canvas_sz.x / (2 * zoom); x += 0.05) {
263+
const double y = expression.value();
191264

192-
// Draw the function as a polyline
193-
draw_list->AddPolyline(
194-
points.data(), points.size(), IM_COL32(199, 68, 64, 255), ImDrawFlags_None, lineThickness);
265+
266+
ImVec2 screen_pos(origin.x + x * zoom, origin.y - y * zoom);
267+
points.push_back(screen_pos);
268+
}
269+
270+
draw_list->AddPolyline(points.data(),
271+
points.size(),
272+
IM_COL32(199, 68, 64, 255),
273+
ImDrawFlags_None,
274+
lineThickness);
275+
}
195276

196277
ImGui::End();
197278
ImGui::PopStyleColor();

0 commit comments

Comments
 (0)