Skip to content

Commit b593be9

Browse files
authored
Merge pull request #13
Feat: Implement graph panning
2 parents dfd7990 + 97d5b86 commit b593be9

File tree

2 files changed

+66
-22
lines changed

2 files changed

+66
-22
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ profile.json
1717
CMakeUserPresets.json
1818
*.sublime-workspace
1919
.idea/
20+
21+
.vs/

src/core/Core/Application.cpp

Lines changed: 64 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,40 @@ ExitStatus App::Application::run() {
136136
const ImVec2 canvas_p0 = ImGui::GetCursorScreenPos();
137137
const ImVec2 canvas_sz = ImGui::GetContentRegionAvail();
138138
const auto canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y);
139-
const ImVec2 origin(canvas_p0.x + canvas_sz.x * 0.5f, canvas_p0.y + canvas_sz.y * 0.5f);
139+
// --- PANNING IMPLEMENTATION BEGIN ---
140+
static bool isPanning = false;
141+
static ImVec2 lastMousePos = {0.0f, 0.0f};
142+
static ImVec2 originOffset = {0.0f, 0.0f};
143+
144+
// Get mouse position
145+
ImVec2 mousePos = ImGui::GetMousePos();
146+
147+
// Detect click start only when cursor is inside the graphing area
148+
if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
149+
isPanning = true;
150+
lastMousePos = mousePos;
151+
}
152+
153+
// Stop panning when mouse released
154+
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
155+
isPanning = false;
156+
}
157+
158+
// While dragging, update origin offset
159+
if (isPanning && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
160+
ImVec2 delta = ImVec2(mousePos.x - lastMousePos.x, mousePos.y - lastMousePos.y);
161+
originOffset.x += delta.x;
162+
originOffset.y += delta.y;
163+
lastMousePos = mousePos;
164+
}
165+
166+
// Apply offset to origin
167+
const ImVec2 origin(
168+
canvas_p0.x + canvas_sz.x * 0.5f + originOffset.x,
169+
canvas_p0.y + canvas_sz.y * 0.5f + originOffset.y
170+
);
171+
// --- PANNING IMPLEMENTATION END ---
172+
140173
float lineThickness = 6.0f;
141174

142175
// --- Thin integer gridlines with adaptive spacing ---
@@ -228,12 +261,13 @@ ExitStatus App::Application::run() {
228261
const double t_step = 0.02;
229262

230263
for (t = t_min; t <= t_max; t += t_step) {
231-
const double vx = expr_fx.value();
232-
const double vy = expr_gx.value();
233-
234-
235-
ImVec2 screen_pos(origin.x + static_cast<float>(vx * zoom),
236-
origin.y - static_cast<float>(vy * zoom));
264+
const double vx = expr_fx.value();
265+
const double vy = expr_gx.value();
266+
// The 'origin' variable already includes the pan offset.
267+
ImVec2 screen_pos(
268+
origin.x + static_cast<float>(vx * zoom),
269+
origin.y - static_cast<float>(vy * zoom)
270+
);
237271
points.push_back(screen_pos);
238272
}
239273

@@ -264,11 +298,11 @@ ExitStatus App::Application::run() {
264298

265299
if (parser.compile(func_str, expression)) {
266300
// grid parameters
267-
const double x_min = -canvas_sz.x / (2 * zoom);
268-
const double x_max = canvas_sz.x / (2 * zoom);
269-
const double y_min = -canvas_sz.y / (2 * zoom);
270-
const double y_max = canvas_sz.y / (2 * zoom);
271-
301+
const double x_min = (-canvas_sz.x * 0.5f - originOffset.x) / zoom;
302+
const double x_max = ( canvas_sz.x * 0.5f - originOffset.x) / zoom;
303+
const double y_min = (-canvas_sz.y * 0.5f + originOffset.y) / zoom;
304+
const double y_max = ( canvas_sz.y * 0.5f + originOffset.y) / zoom;
305+
272306
// adaptive step size with performance limit
273307
const double step = std::max(0.025, 1.5 / zoom);
274308
const ImU32 inequality_color = IM_COL32(100, 150, 255, 180);
@@ -346,10 +380,10 @@ ExitStatus App::Application::run() {
346380

347381
if (compile_ok) {
348382
// grid parameters
349-
const double x_min = -canvas_sz.x / (2 * zoom);
350-
const double x_max = canvas_sz.x / (2 * zoom);
351-
const double y_min = -canvas_sz.y / (2 * zoom);
352-
const double y_max = canvas_sz.y / (2 * zoom);
383+
const double x_min = (-canvas_sz.x * 0.5f - originOffset.x) / zoom;
384+
const double x_max = ( canvas_sz.x * 0.5f - originOffset.x) / zoom;
385+
const double y_min = (-canvas_sz.y * 0.5f + originOffset.y) / zoom;
386+
const double y_max = ( canvas_sz.y * 0.5f + originOffset.y) / zoom;
353387
const double step = std::max(0.008, 1.0 / zoom); //dynamic step based on zoom level
354388

355389
const ImU32 implicit_color = IM_COL32(64, 199, 128, 255);
@@ -473,12 +507,20 @@ ExitStatus App::Application::run() {
473507
exprtk::parser<double> parser;
474508
parser.compile(function, expression);
475509

476-
for (x = -canvas_sz.x / (2 * zoom); x < canvas_sz.x / (2 * zoom); x += 0.05) {
477-
const double y = expression.value();
478-
479-
ImVec2 screen_pos(origin.x + x * zoom, origin.y - y * zoom);
480-
points.push_back(screen_pos);
481-
}
510+
// Calculate the visible x-range in world-space, accounting for the pan
511+
const float world_x_min = (-canvas_sz.x * 0.5f - originOffset.x) / zoom;
512+
const float world_x_max = ( canvas_sz.x * 0.5f - originOffset.x) / zoom;
513+
514+
// Evaluate the function across the correct visible world-range
515+
for (x = world_x_min; x <= world_x_max; x += 0.05) {
516+
const double y = expression.value();
517+
// The 'origin' variable already includes the pan offset.
518+
ImVec2 screen_pos(
519+
origin.x + static_cast<float>(x * zoom),
520+
origin.y - static_cast<float>(y * zoom)
521+
);
522+
points.push_back(screen_pos);
523+
}
482524

483525
draw_list->AddPolyline(points.data(),
484526
points.size(),

0 commit comments

Comments
 (0)