Skip to content

Commit 0bb670c

Browse files
authored
Merge pull request #14 from atulya-srivastava/implicit-relations
Implicit relations
2 parents fe08571 + 8af0541 commit 0bb670c

File tree

2 files changed

+119
-0
lines changed

2 files changed

+119
-0
lines changed

src/core/Core/Application.cpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,97 @@ ExitStatus App::Application::run() {
212212
}
213213
}
214214

215+
// check for implicit form: f(x,y) = g(x,y)
216+
if (!plotted) {
217+
size_t equals_pos = findTopLevelEquals(func_str);
218+
219+
if (equals_pos != std::string::npos) {
220+
// split into LHS and RHS
221+
std::string lhs = trim(func_str.substr(0, equals_pos));
222+
std::string rhs = trim(func_str.substr(equals_pos + 1));
223+
224+
// create expression: LHS - RHS
225+
std::string implicit_expr = "(" + lhs + ") - (" + rhs + ")";
226+
227+
// setup exprtk with x and y variables
228+
double x = 0.0, y = 0.0;
229+
exprtk::symbol_table<double> symbolTable;
230+
symbolTable.add_constants();
231+
addConstants(symbolTable);
232+
symbolTable.add_variable("x", x);
233+
symbolTable.add_variable("y", y);
234+
235+
exprtk::expression<double> expression;
236+
expression.register_symbol_table(symbolTable);
237+
238+
exprtk::parser<double> parser;
239+
bool compile_ok = parser.compile(implicit_expr, expression);
240+
241+
if (compile_ok) {
242+
// grid parameters
243+
const double x_min = -canvas_sz.x / (2 * zoom);
244+
const double x_max = canvas_sz.x / (2 * zoom);
245+
const double y_min = -canvas_sz.y / (2 * zoom);
246+
const double y_max = canvas_sz.y / (2 * zoom);
247+
const double step = std::max(0.008, 1.0 / zoom); //dynamic step based on zoom level
248+
249+
const ImU32 implicit_color = IM_COL32(64, 199, 128, 255);
250+
const float dot_radius = 2.5f;
251+
252+
// scan horizontally for sign changes
253+
for (y = y_min; y <= y_max; y += step) {
254+
double prev_val = 0.0;
255+
bool first = true;
256+
257+
for (x = x_min; x <= x_max; x += step) {
258+
double curr_val = expression.value();
259+
260+
if (!first && prev_val * curr_val < 0) {
261+
// sign change detected
262+
double t = prev_val / (prev_val - curr_val);
263+
double x_zero = (x - step) + t * step;
264+
double y_zero = y;
265+
266+
// transform to screen coordinates and draw immediately
267+
ImVec2 screen_pos(origin.x + static_cast<float>(x_zero * zoom),
268+
origin.y - static_cast<float>(y_zero * zoom));
269+
draw_list->AddCircleFilled(screen_pos, dot_radius, implicit_color);
270+
}
271+
272+
prev_val = curr_val;
273+
first = false;
274+
}
275+
}
276+
277+
// vertical scan
278+
for (x = x_min; x <= x_max; x += step) {
279+
double prev_val = 0.0;
280+
bool first = true;
281+
282+
for (y = y_min; y <= y_max; y += step) {
283+
double curr_val = expression.value();
284+
285+
if (!first && prev_val * curr_val < 0) {
286+
// sign change detected
287+
double t = prev_val / (prev_val - curr_val);
288+
double x_zero = x;
289+
double y_zero = (y - step) + t * step;
290+
291+
ImVec2 screen_pos(origin.x + static_cast<float>(x_zero * zoom),
292+
origin.y - static_cast<float>(y_zero * zoom));
293+
draw_list->AddCircleFilled(screen_pos, dot_radius, implicit_color);
294+
}
295+
296+
prev_val = curr_val;
297+
first = false;
298+
}
299+
}
300+
301+
plotted = true;
302+
}
303+
}
304+
}
305+
215306
if (!plotted) {
216307
std::string func_str(function);
217308
bool is_polar = func_str.find("r=") != std::string::npos || func_str.find("r =") != std::string::npos;

src/core/Core/funcs.hpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,33 @@ inline std::string trim(const std::string& s) {
2828
return s.substr(start, end - start + 1);
2929
}
3030

31+
// function to find top-level '=' that's not part of ==, <=, >=, !=
32+
static size_t findTopLevelEquals(const std::string& str) {
33+
int depth = 0;
34+
35+
for (size_t i = 0; i < str.size(); ++i) {
36+
char c = str[i];
37+
if (c == '(') {
38+
++depth;
39+
} else if (c == ')') {
40+
--depth;
41+
} else if (c == '=' && depth == 0) {
42+
// check it's not part of ==, <=, >=, !=
43+
bool isOperator = false;
44+
if (i > 0 && (str[i-1] == '=' || str[i-1] == '<' || str[i-1] == '>' || str[i-1] == '!')) {
45+
isOperator = true;
46+
}
47+
if (i + 1 < str.size() && str[i+1] == '=') {
48+
isOperator = true;
49+
}
50+
51+
if (!isOperator) {
52+
return i;
53+
}
54+
}
55+
}
56+
return std::string::npos;
57+
}
58+
3159
#endif // IMGRAPH_FUNCS_HPP
3260

0 commit comments

Comments
 (0)