@@ -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;
0 commit comments