fix: change md files to md folder
This commit is contained in:
+121
@@ -0,0 +1,121 @@
|
||||
Here is the translation of the document into English Markdown format:
|
||||
|
||||
# Geometric Analysis: Progressive Center Drift During Asymmetrical Zoom & Pre-Roll Gutter Solution
|
||||
|
||||
This document analyzes the mathematical root cause of center drift during zoom operations at asymmetric timeline markers (e.g., zooming at $1\text{ s}$ drifts drastically compared to $5\text{ s}$ on a $10\text{ s}$ total track length). It also provides a structural solution using boundary margins (**Pre-roll/Post-roll Gutter**) to lock the absolute anchor point in all interaction scenarios.
|
||||
|
||||
---
|
||||
|
||||
## 1. Mathematical Proof: Why Zooming at $1\text{ s}$ Drifts Further Than $5\text{ s}$
|
||||
|
||||
This visual discrepancy is not caused by random calculation precision errors, but is the mathematical result of boundary clamping (**Scroll Left Clamping**).
|
||||
|
||||
### 1.1. Conservation Equation for Mouse/Playhead Anchor Points
|
||||
|
||||
To preserve the visual location of time marker $t$ at pixel coordinate $X_{\text{viewport}}$ relative to the display before and after changing the zoom scale factor ($Z_{\text{current}} \rightarrow Z_{\text{new}}$), the required horizontal scroll offset $S_{\text{new}}$ (`scrollLeft`) must satisfy:
|
||||
|
||||
$$S_{\text{new}} = (t \times Z_{\text{new}}) - X_{\text{viewport}}$$
|
||||
|
||||
### 1.2. Scenario Analysis: Zooming Out at $X_{\text{viewport}} = 300\text{ px}$ (Cursor at Screen Center)
|
||||
|
||||
Assume the timeline is zoomed out significantly, reducing the zoom ratio down to $Z_{\text{new}} = 100\text{ px/second}$.
|
||||
|
||||
#### Scenario A: Operator zooms at the central symmetrical coordinate $t = 5.0\text{ s}$
|
||||
|
||||
Applying the target scroll position calculation:
|
||||
|
||||
$$S_{\text{new}} = (5.0 \times 100) - 300 = 500 - 300 = +200\text{ px}$$
|
||||
|
||||
* **Result:** Because $+200\text{ px} \ge 0$, the scroll position resides safely within physical boundary limits. The browser sets `scrollLeft = 200` smoothly. The $5.0\text{ s}$ point remains locked at position $300\text{ px}$ on the screen with a spatial drift of $0\text{ px}$.
|
||||
|
||||
#### Scenario B: Operator zooms at an asymmetrical coordinate near the left edge $t = 1.0\text{ s}$
|
||||
|
||||
Applying the target scroll position calculation:
|
||||
|
||||
$$S_{\text{new}} = (1.0 \times 100) - 300 = 100 - 300 = -200\text{ px}$$
|
||||
|
||||
* **Critical Issue:** Browsers and operating hardware cannot execute negative scroll values ($scrollLeft < 0$), instantly **clamping the horizontal scroll position at the minimum boundary $S_{\text{clamped}} = 0\text{ px}$**.
|
||||
* Due to this clamping, the actual on-screen rendering coordinate of the $1.0\text{ s}$ milestone drifts to:
|
||||
|
||||
$$X_{\text{viewport\_actual}} = (1.0 \times 100) - 0 = 100\text{ px}$$
|
||||
|
||||
* **Visual Discrepancy:** The $1.0\text{ s}$ marker, which should remain stationary at coordinate $300\text{ px}$, is **pulled to the left to coordinate $100\text{ px}$** (resulting in a spatial shift of $200\text{ px}$).
|
||||
|
||||
> **Geometric Principle:** The smaller the zoom anchor timestamp $t$ (the closer it sits to the left boundary), the more likely the required scroll position $S_{\text{new}}$ drops below zero to be clamped at $0$, increasing visual waveform displacement during zoom-out operations.
|
||||
|
||||
---
|
||||
|
||||
## 2. Professional DAW Solution: Pre-Roll & Post-Roll Gutters
|
||||
|
||||
To permanently eliminate this behavior and give SonicForge Studio a professional zoom experience similar to Reaper or Adobe Audition, apply a **Pre-roll & Post-roll Gutter (Boundary Margins)**.
|
||||
|
||||
```text
|
||||
|<─────────────────── Actual Timeline Scroll Width ───────────────────>|
|
||||
+──────────────────────────┬───────────────────────────────────────────+
|
||||
| [ Pre-roll Gutter ] │ 0:00.000 (Actual music start time) |
|
||||
| (Width: W_viewport) │ |
|
||||
| (scrollLeft can run here)│ [ Waveform and track grid start here... ]|
|
||||
+──────────────────────────┴───────────────────────────────────────────+
|
||||
▲
|
||||
│ [ 1.0s anchor point remains 100% stationary here ]
|
||||
│ Because the scrollbar is allowed to retreat negatively into the gutter!
|
||||
|
||||
```
|
||||
|
||||
1. **Enabling Visual Negative Scrolling:** Instead of starting the timeline canvas at pixel coordinate $0\text{ px}$ (corresponding to $0.0\text{ s}$), prepend an empty padding region (**Gutter**) equal to the full viewport width $W_{\text{viewport}}$ (e.g., $1200\text{ px}$) before the $0.0\text{ s}$ mark.
|
||||
2. **Updated Coordinate Mapping Formula:**
|
||||
The physical pixel coordinate $X$ of timestamp $t$ on the Canvas includes the offset padding:
|
||||
|
||||
$$X_t = (t \times Z) + W_{\text{pre\_roll}}$$
|
||||
|
||||
3. **Unclamped Scroll Conservation Equation:**
|
||||
When zooming at any asymmetrical timestamp (including $0.1\text{ s}$ or $0.0\text{ s}$):
|
||||
|
||||
$$S_{\text{new}} = (t \times Z_{\text{new}}) + W_{\text{pre\_roll}} - X_{\text{viewport}}$$
|
||||
|
||||
* Because $W_{\text{pre\_roll}}$ is added, $S_{\text{new}}$ remains greater than $0$ during standard zoom-out actions, eliminating the clamp at $0$. Your $1.0\text{ s}$ timestamp or playhead stays stationary, the waveform graphics scale symmetrically, and the $0.0\text{ s}$ mark smoothly recedes toward the center of the viewport, exposing a subtle, professional dark gray pre-roll gutter area in front of the track.
|
||||
|
||||
---
|
||||
|
||||
## 3. Implementing the Boundary Lock Algorithm in Source Code
|
||||
|
||||
Below is the upgraded mouse wheel zoom event handler for `index.html`, incorporating pre-roll margin compensation:
|
||||
|
||||
```javascript
|
||||
const handleTimelineZoomWithGutter = (e) => {
|
||||
if (!e.ctrlKey) return;
|
||||
e.preventDefault();
|
||||
|
||||
const timelineWrapper = timelineWrapperRef.current;
|
||||
if (!timelineWrapper) return;
|
||||
|
||||
const rect = timelineWrapper.getBoundingClientRect();
|
||||
const mouseXInViewport = e.clientX - rect.left;
|
||||
|
||||
// Pre-roll gutter padding equal to half the viewport width to allow scrolling past 0s
|
||||
const preRollPadding = rect.width / 2;
|
||||
|
||||
const scrollLeftCurrent = timelineWrapper.scrollLeft;
|
||||
const zoomCurrent = zoom;
|
||||
const anchorTime = (scrollLeftCurrent + mouseXInViewport - preRollPadding) / zoomCurrent;
|
||||
|
||||
const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
|
||||
let zoomNew = zoomCurrent * zoomFactor;
|
||||
|
||||
// Apply zoom constraints
|
||||
zoomNew = Math.max(minZoom, Math.min(2000, zoomNew));
|
||||
|
||||
// Calculate new scroll offset preserving the anchor point under the cursor
|
||||
const scrollLeftNew = (anchorTime * zoomNew) + preRollPadding - mouseXInViewport;
|
||||
|
||||
// Update state
|
||||
setZoom(zoomNew);
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
timelineWrapper.scrollLeft = scrollLeftNew;
|
||||
});
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
This upgrade enables SonicForge Studio to achieve zero-latency, sample-accurate zooming with studio-grade anchor locking!
|
||||
Reference in New Issue
Block a user