Skip to content

resolved issue #876: Pinch Zoom Y-Axis Calculation Error#978

Open
MaheepTulsian wants to merge 1 commit intoCircuitVerse:mainfrom
MaheepTulsian:maheep
Open

resolved issue #876: Pinch Zoom Y-Axis Calculation Error#978
MaheepTulsian wants to merge 1 commit intoCircuitVerse:mainfrom
MaheepTulsian:maheep

Conversation

@MaheepTulsian
Copy link

@MaheepTulsian MaheepTulsian commented Feb 23, 2026

Fixes #876

Describe the changes you have made in this PR -

Pinch zoom on mobile was calculating Y-axis position wrongly at listeners.js line 118 because it was using globalScope.ox (X offset) instead of globalScope.oy (Y offset). I've corrected it back to globalScope.oy (Y offset).

Screenshots of the UI changes (If any) -

Screen.Recording.2026-02-23.at.11.37.25.PM.mov

Code Understanding and AI Usage

Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code?

  • No, I wrote all the code myself
  • Yes, I used AI assistance (continue below)

If you used AI assistance:

  • I have reviewed every single line of the AI-generated code
  • I can explain the purpose and logic of each function/component I added
  • I have tested edge cases and understand how the code handles them
  • I have modified the AI output to follow this project's coding standards and conventions

Explain your implementation approach:

Pinch zoom on mobile was calculating Y-axis position wrongly because it was using globalScope.ox (X offset) instead of globalScope.oy (Y offset). It was a clear typo error.


Checklist before requesting a review

  • I have added proper PR title and linked to the issue
  • I have performed a self-review of my code
  • I can explain the purpose of every function, class, and logic block I added
  • I understand why my changes work and have tested them thoroughly
  • I have considered potential edge cases and how my code handles them
  • If it is a core feature, I have added thorough tests
  • My code follows the project's style guidelines and conventions

Note: Please check Allow edits from maintainers if you would like us to assist in the PR.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed pinch-zoom panning behavior to correctly handle vertical axis positioning during zoom operations.

@netlify
Copy link

netlify bot commented Feb 23, 2026

Deploy Preview for circuitverse ready!

Name Link
🔨 Latest commit 0eb03e2
🔍 Latest deploy log https://app.netlify.com/projects/circuitverse/deploys/699c9b295f0ef40008041f67
😎 Deploy Preview https://deploy-preview-978--circuitverse.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 49 (🟢 up 4 from production)
Accessibility: 66 (no change from production)
Best Practices: 92 (no change from production)
SEO: 82 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 23, 2026

Walkthrough

A calculation error in the pinch-zoom functionality within src/simulator/src/listeners.js was corrected. The Yf variable calculation in the pinchZoom function was using globalScope.ox (X-axis origin) when it should have been using globalScope.oy (Y-axis origin). This single-line correction ensures the Y-axis origin is properly referenced during pinch-zoom center calculations, affecting how the center Y position and subsequent panning behavior are computed.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main change: fixing the Y-axis calculation error in pinch zoom functionality, directly matching the bug fix implemented in the code.
Linked Issues check ✅ Passed The code change directly addresses issue #876 by correcting the Y-axis offset calculation in pinch-zoom (globalScope.ox to globalScope.oy), enabling proper canvas centering during touch gestures.
Out of Scope Changes check ✅ Passed The change is narrowly focused on the pinch-zoom Y-axis calculation bug identified in issue #876, with no unrelated modifications present.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
src/simulator/src/listeners.js (4)

153-154: ⚠️ Potential issue | 🟡 Minor

Canvas pan resets to the origin when zoom is clamped.

globalScope.ox/globalScope.oy are overwritten with currCentreX * (scale - oldScale). When pinchZ hits its floor (0.5) or ceiling (2), globalScope.scale === oldScale, making the product 0 and snapping the canvas to the origin mid-gesture. These should accumulate into the existing offset, not replace it.

🐛 Proposed fix
- globalScope.ox = Math.round(currCentreX * (globalScope.scale - oldScale));
- globalScope.oy = Math.round(currCentreY * (globalScope.scale - oldScale));
+ globalScope.ox += Math.round(currCentreX * (globalScope.scale - oldScale));
+ globalScope.oy += Math.round(currCentreY * (globalScope.scale - oldScale));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/simulator/src/listeners.js` around lines 153 - 154, The pan offsets are
being overwritten causing the canvas to snap to origin when pinchZ is clamped;
change the assignment so the computed delta (currCentreX * (globalScope.scale -
oldScale) and currCentreY * (globalScope.scale - oldScale)) is added to the
existing globalScope.ox/globalScope.oy instead of replacing them (i.e.,
accumulate the delta into globalScope.ox and globalScope.oy), ensuring the code
that updates offsets (referencing globalScope.ox, globalScope.oy, currCentreX,
currCentreY, globalScope.scale and oldScale/pinchZ) uses addition rather than
assignment.

153-154: ⚠️ Potential issue | 🟡 Minor

Canvas pan position resets to origin when zoom is clamped.

globalScope.ox and globalScope.oy are overwritten rather than accumulated. When pinchZ reaches its boundary (0.5 or 2), globalScope.scale equals oldScale, making (globalScope.scale - oldScale) === 0. Both offsets are zeroed out, snapping the canvas to the origin mid-gesture.

🐛 Proposed fix (accumulate delta rather than overwrite)
- globalScope.ox = Math.round(currCentreX * (globalScope.scale - oldScale));
- globalScope.oy = Math.round(currCentreY * (globalScope.scale - oldScale));
+ globalScope.ox += Math.round(currCentreX * (globalScope.scale - oldScale));
+ globalScope.oy += Math.round(currCentreY * (globalScope.scale - oldScale));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/simulator/src/listeners.js` around lines 153 - 154, The pan offsets are
being overwritten which zeros them when zoom is clamped; change the assignment
to accumulate the delta instead: compute the delta as Math.round(currCentreX *
(globalScope.scale - oldScale)) and Math.round(currCentreY * (globalScope.scale
- oldScale)) and add those deltas to globalScope.ox and globalScope.oy (instead
of setting them), so in the pinch/zoom handler (symbols: globalScope.ox,
globalScope.oy, globalScope.scale, oldScale, currCentreX, currCentreY, pinchZ)
the offsets are incremented by the zoom-induced delta and the canvas won't snap
to origin when scale is clamped.

126-126: ⚠️ Potential issue | 🟠 Major

Math.sqrt silently drops the Y component — pinch distance is horizontal-only.

Math.sqrt() returns the square root of a number and accepts only one parameter. The second argument (e.touches[1].clientY - e.touches[0].clientY) ** 2 is silently ignored by the JS runtime, so distance resolves to |Δx| — purely horizontal. A mostly-vertical or diagonal pinch gesture produces near-zero distance, making pinchZ barely change and rendering the zoom unresponsive for those orientations.

The idiomatic fix is Math.hypot(), which returns the square root of the sum of squares of its arguments:

🐛 Proposed fix
- distance = Math.sqrt((e.touches[1].clientX - e.touches[0].clientX) ** 2, (e.touches[1].clientY - e.touches[0].clientY) ** 2);
+ distance = Math.hypot(e.touches[1].clientX - e.touches[0].clientX, e.touches[1].clientY - e.touches[0].clientY);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/simulator/src/listeners.js` at line 126, The computed pinch distance
incorrectly passes two arguments to Math.sqrt making the Y delta ignored (so
distance effectively equals |Δx|); update the calculation for distance in the
touch handler (the line that assigns to distance using
e.touches[0]/e.touches[1]) to compute the true Euclidean distance either by
using Math.hypot(e.touches[1].clientX - e.touches[0].clientX,
e.touches[1].clientY - e.touches[0].clientY) or by summing squares and taking
Math.sqrt((dx*dx)+(dy*dy)); this fixes pinchZ/zoom responsiveness for
vertical/diagonal gestures.

126-126: ⚠️ Potential issue | 🟠 Major

Math.sqrt silently ignores the second argument — distance is horizontal-only.

Math.sqrt accepts exactly one argument per the ECMAScript specification; the Y-component (e.touches[1].clientY - e.touches[0].clientY) ** 2 is silently discarded. The computed distance equals |Δx|, not true Euclidean distance. A primarily vertical pinch gesture produces near-zero distance (e.g., vertical-only pinch with Δx=0, Δy=5 yields Math.sqrt(0, 25) = 0), meaning pinchZ barely changes and zoom is unresponsive for vertical axis pinches.

🐛 Proposed fix
- distance = Math.sqrt((e.touches[1].clientX - e.touches[0].clientX) ** 2, (e.touches[1].clientY - e.touches[0].clientY) ** 2);
+ distance = Math.sqrt((e.touches[1].clientX - e.touches[0].clientX) ** 2 + (e.touches[1].clientY - e.touches[0].clientY) ** 2);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/simulator/src/listeners.js` at line 126, The computed `distance` in
listeners.js is wrong because Math.sqrt is called with two arguments, discarding
the Y component; fix the calculation of `distance` (used to compute pinch/zoom,
e.g., `distance` and `pinchZ`) by summing the squared Δx and Δy and then taking
Math.sqrt of that single sum (i.e., compute dx = e.touches[1].clientX -
e.touches[0].clientX, dy = e.touches[1].clientY - e.touches[0].clientY, then
distance = Math.sqrt(dx*dx + dy*dy)) so vertical-only pinches change `distance`
correctly and zoom/pinch logic behaves as expected.
🧹 Nitpick comments (2)
src/simulator/src/listeners.js (2)

143-154: Lingering "not working as expected" comment — zoom-to-pinch-center is still inaccurate.

The comment on Line 143 was already present and still applies: the centre-based pan update doesn't follow the standard zoom-to-point formula. The correct derivation for preserving the world point under the pinch centre after a scale change is new_ox = RawX - Xf_pixels * new_scale (and equivalently for Y), where Xf_pixels = (RawX - old_ox). The current grid-snapped approximation (currCentreX * Δscale) will drift from the actual pinch midpoint.

Would you like me to open a follow-up issue to track a full zoom-to-pinch-center implementation, or generate a corrected formula as a starting point?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/simulator/src/listeners.js` around lines 143 - 154, The
zoom-to-pinch-centre math is incorrect: replace the grid-snapped approximation
that sets globalScope.ox/globalScope.oy using currCentreX * (globalScope.scale -
oldScale) with the proper preserve-point formula; compute new offsets as newOx =
RawX - ((RawX - globalScope.ox) / oldScale) * globalScope.scale and newOy = RawY
- ((RawY - globalScope.oy) / oldScale) * globalScope.scale (use RawX/RawY,
globalScope.ox/oy, oldScale and the updated globalScope.scale) and assign them
to globalScope.ox/globalScope.oy, removing the currCentreX/currCentreY
rounding-based approach.

143-154: Lingering "not working as expected" comment indicates the zoom-to-center algorithm is still incomplete.

After the oy fix, the midpoint coordinates (centreX/centreY) are correctly fed into both axes, but the comment on line 143 still applies: the resulting ox/oy update doesn't implement standard zoom-to-point semantics (which would require new_ox = RawX - Xf_pixels * new_scale). The current formula computes a grid-snapped approximation that drifts from the actual pinch center.

Would you like me to open a new issue to track the full zoom-to-pinch-center implementation, or generate a corrected formula draft?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/simulator/src/listeners.js` around lines 143 - 154, The current
pinch-center math updates globalScope.ox/oy using a grid-snapped delta which
drifts from the true pinch point; replace that with the standard zoom-to-point
formula: compute new_ox = RawX - ((RawX - globalScope.ox) / oldScale) *
globalScope.scale and new_oy = RawY - ((RawY - globalScope.oy) / oldScale) *
globalScope.scale (using RawX/RawY, oldScale and globalScope.scale from the
diff), and if you still need grid snapping apply rounding to the final world
coordinates after this calculation rather than using the snapped Xf/Yf to derive
ox/oy.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/simulator/src/listeners.js`:
- Around line 153-154: The pan offsets are being overwritten causing the canvas
to snap to origin when pinchZ is clamped; change the assignment so the computed
delta (currCentreX * (globalScope.scale - oldScale) and currCentreY *
(globalScope.scale - oldScale)) is added to the existing
globalScope.ox/globalScope.oy instead of replacing them (i.e., accumulate the
delta into globalScope.ox and globalScope.oy), ensuring the code that updates
offsets (referencing globalScope.ox, globalScope.oy, currCentreX, currCentreY,
globalScope.scale and oldScale/pinchZ) uses addition rather than assignment.
- Around line 153-154: The pan offsets are being overwritten which zeros them
when zoom is clamped; change the assignment to accumulate the delta instead:
compute the delta as Math.round(currCentreX * (globalScope.scale - oldScale))
and Math.round(currCentreY * (globalScope.scale - oldScale)) and add those
deltas to globalScope.ox and globalScope.oy (instead of setting them), so in the
pinch/zoom handler (symbols: globalScope.ox, globalScope.oy, globalScope.scale,
oldScale, currCentreX, currCentreY, pinchZ) the offsets are incremented by the
zoom-induced delta and the canvas won't snap to origin when scale is clamped.
- Line 126: The computed pinch distance incorrectly passes two arguments to
Math.sqrt making the Y delta ignored (so distance effectively equals |Δx|);
update the calculation for distance in the touch handler (the line that assigns
to distance using e.touches[0]/e.touches[1]) to compute the true Euclidean
distance either by using Math.hypot(e.touches[1].clientX - e.touches[0].clientX,
e.touches[1].clientY - e.touches[0].clientY) or by summing squares and taking
Math.sqrt((dx*dx)+(dy*dy)); this fixes pinchZ/zoom responsiveness for
vertical/diagonal gestures.
- Line 126: The computed `distance` in listeners.js is wrong because Math.sqrt
is called with two arguments, discarding the Y component; fix the calculation of
`distance` (used to compute pinch/zoom, e.g., `distance` and `pinchZ`) by
summing the squared Δx and Δy and then taking Math.sqrt of that single sum
(i.e., compute dx = e.touches[1].clientX - e.touches[0].clientX, dy =
e.touches[1].clientY - e.touches[0].clientY, then distance = Math.sqrt(dx*dx +
dy*dy)) so vertical-only pinches change `distance` correctly and zoom/pinch
logic behaves as expected.

---

Nitpick comments:
In `@src/simulator/src/listeners.js`:
- Around line 143-154: The zoom-to-pinch-centre math is incorrect: replace the
grid-snapped approximation that sets globalScope.ox/globalScope.oy using
currCentreX * (globalScope.scale - oldScale) with the proper preserve-point
formula; compute new offsets as newOx = RawX - ((RawX - globalScope.ox) /
oldScale) * globalScope.scale and newOy = RawY - ((RawY - globalScope.oy) /
oldScale) * globalScope.scale (use RawX/RawY, globalScope.ox/oy, oldScale and
the updated globalScope.scale) and assign them to globalScope.ox/globalScope.oy,
removing the currCentreX/currCentreY rounding-based approach.
- Around line 143-154: The current pinch-center math updates globalScope.ox/oy
using a grid-snapped delta which drifts from the true pinch point; replace that
with the standard zoom-to-point formula: compute new_ox = RawX - ((RawX -
globalScope.ox) / oldScale) * globalScope.scale and new_oy = RawY - ((RawY -
globalScope.oy) / oldScale) * globalScope.scale (using RawX/RawY, oldScale and
globalScope.scale from the diff), and if you still need grid snapping apply
rounding to the final world coordinates after this calculation rather than using
the snapped Xf/Yf to derive ox/oy.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed7e693 and 0eb03e2.

📒 Files selected for processing (1)
  • src/simulator/src/listeners.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐞 Bug: Pinch Zoom Y-Axis Calculation Error

1 participant