Skip to content

Commit fa9ebd5

Browse files
authored
Merge pull request #7 from ankitspandey36/issue1
feat(graph): add parametric plotting support (f(t), g(t))
2 parents b14b753 + 8646d9f commit fa9ebd5

File tree

2 files changed

+109
-24
lines changed

2 files changed

+109
-24
lines changed

src/core/Core/Application.cpp

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

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

176-
exprtk::expression<double> expression;
177-
expression.register_symbol_table(symbolTable);
244+
exprtk::symbol_table<double> symbolTable;
245+
symbolTable.add_constants();
246+
addConstants(symbolTable);
247+
symbolTable.add_variable("x", x);
178248

179-
exprtk::parser<double> parser;
180-
parser.compile(function, expression);
249+
exprtk::expression<double> expression;
250+
expression.register_symbol_table(symbolTable);
181251

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();
252+
exprtk::parser<double> parser;
253+
parser.compile(function, expression);
186254

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-
}
255+
for (x = -canvas_sz.x / (2 * zoom); x < canvas_sz.x / (2 * zoom); x += 0.05) {
256+
const double y = expression.value();
191257

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);
258+
259+
ImVec2 screen_pos(origin.x + x * zoom, origin.y - y * zoom);
260+
points.push_back(screen_pos);
261+
}
262+
263+
draw_list->AddPolyline(points.data(),
264+
points.size(),
265+
IM_COL32(199, 68, 64, 255),
266+
ImDrawFlags_None,
267+
lineThickness);
268+
}
195269

196270
ImGui::End();
197271
ImGui::PopStyleColor();

src/core/Core/funcs.hpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
#ifndef IMGRAPH_FUNCS_HPP
66
#define IMGRAPH_FUNCS_HPP
77
#include <numbers>
8-
8+
#include <string>
9+
#include <cctype>
910
#include "exprtk.hpp"
1011

1112
inline void addConstants(exprtk::symbol_table<double> &symbolTable) {
@@ -18,4 +19,14 @@ inline void addConstants(exprtk::symbol_table<double> &symbolTable) {
1819
symbolTable.add_constant("γ", std::numbers::egamma);
1920
}
2021

22+
inline std::string trim(const std::string& s) {
23+
const char* ws = " \t\n\r";
24+
size_t start = s.find_first_not_of(ws);
25+
size_t end = s.find_last_not_of(ws);
26+
if (start == std::string::npos)
27+
return std::string();
28+
return s.substr(start, end - start + 1);
29+
}
30+
2131
#endif // IMGRAPH_FUNCS_HPP
32+

0 commit comments

Comments
 (0)