forked from 1b-agent/opencadstudio-upstream
fix(dyn): clamp guide length to the viewport so a huge distance can't OOM
The dynamic-input guides are dotted, and a dash pattern is measured in pixels: the tessellator emits a quad every few pixels, so a guide costs in proportion to its SCREEN length, not to what it represents. Entering a large distance (5000000 in the value box) projects the cursor millions of pixels from the anchor, so the guide asks for millions of dash quads — gigabytes — and the process fills RAM and dies while building a frame whose content is off-screen anyway (#406). Clamp the drawn length to just past the viewport diagonal. `dx`/`dy` still come from the true cursor, and the angle and read-outs are computed from the raw length, so direction and every displayed value are unchanged — only the tail past the viewport edge, which nobody can see, is dropped. Clamping `cursor` bounds every guide below it: they all derive from it, including the polar arc and the perpendicular endpoint. The reporter's own bisect points here: with DYN off the crash does not happen, and drawing the same line by command (which builds no interactive guide) never reproduced it. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
f720e40ba1
commit
48a43aae11
1 file changed
+23
-4
+23
-4
@@ -1757,10 +1757,29 @@ impl DynInputCanvas {
|
||||
/// Guided layout: draw the guide geometry anchored at `base`, then place
|
||||
/// each box according to its role.
|
||||
fn draw_guided(&self, frame: &mut canvas::Frame, bounds: iced::Rectangle, base: Point) {
|
||||
let cursor = self.cursor_screen;
|
||||
let (vx, vy) = (cursor.x - base.x, cursor.y - base.y);
|
||||
let len = (vx * vx + vy * vy).sqrt().max(1.0);
|
||||
let (dx, dy) = (vx / len, vy / len);
|
||||
let cursor_raw = self.cursor_screen;
|
||||
let (vx, vy) = (cursor_raw.x - base.x, cursor_raw.y - base.y);
|
||||
let raw_len = (vx * vx + vy * vy).sqrt().max(1.0);
|
||||
let (dx, dy) = (vx / raw_len, vy / raw_len);
|
||||
// Clamp the drawn length to just past the viewport.
|
||||
//
|
||||
// These guides are dotted, and a dash pattern is measured in PIXELS: the
|
||||
// tessellator emits a quad every few pixels, so the cost scales with a
|
||||
// guide's screen length, not with what it represents. Typing a large
|
||||
// distance (5000000 into the value box) projects the cursor millions of
|
||||
// pixels away — millions of quads, gigabytes of them — and the process
|
||||
// dies building a frame whose content is off-screen anyway (#406).
|
||||
//
|
||||
// Everything past the viewport edge is invisible, so dropping it costs
|
||||
// nothing: `dx`/`dy` keep the true direction, the angle and every
|
||||
// read-out are computed from `raw_len` above, and only the tail nobody
|
||||
// can see is cut. Clamping `cursor` here bounds every guide below —
|
||||
// they all derive from it.
|
||||
let len = raw_len.min(bounds.width.hypot(bounds.height) * 1.5);
|
||||
let cursor = Point {
|
||||
x: base.x + dx * len,
|
||||
y: base.y + dy * len,
|
||||
};
|
||||
// Perpendicular pointing to the lower half so labels sit under the line.
|
||||
let (mut nx, mut ny) = (-dy, dx);
|
||||
if ny < 0.0 {
|
||||
|
||||
Reference in new issue
Block a user