feat(ellipse): add complete grip editing

This commit is contained in:
Hakan Seven committed 2026-08-19 23:48:13 +03:00
commit 0f250bb485
5 files changed
+193 -92

No files matched your search

+2 -4
View File
@@ -101,10 +101,8 @@ pub struct GripDef {
pub is_midpoint: bool,
/// Visual marker shape for the grip.
pub shape: GripShape,
/// World-XY direction vector used to orient a `Rectangle` grip
/// along its segment. `None` for shapes that don't need rotation
/// (Square, Triangle in non-directional contexts).
pub dir: Option<[f32; 2]>,
/// World-space marker direction, projected with the grip position.
pub dir: Option<glam::DVec3>,
/// World-space axis that constrains this grip's drag.
pub axis: Option<glam::DVec3>,
}
+37 -9
View File
@@ -78,6 +78,10 @@ impl GripEdit {
// ── Screen-space helpers ───────────────────────────────────────────────────
fn marker_direction(from: Vec2, to: Vec2) -> [f32; 2] {
[to.x - from.x, from.y - to.y]
}
/// Project a slice of `GripDef`s to screen space.
/// Returns `(grip_id, screen_pos, is_midpoint, shape)` for each grip.
pub fn grips_to_screen(
@@ -91,11 +95,18 @@ pub fn grips_to_screen(
// Project from the f64 grip position via the relative-to-eye path so
// the grip stays glued to the wire at UTM-scale coordinates (an
// `as_vec3` cast first would quantize it ~0.5 m off at high zoom).
let screen = match camera.project(g.world, bounds) {
let projected = camera.project(g.world, bounds);
let screen = match projected {
Some(p) => Point::new(bounds.x + p.x, bounds.y + p.y),
None => Point::new(f32::NAN, f32::NAN),
};
(g.id, screen, g.is_midpoint, g.shape, g.dir)
let dir = g.dir.and_then(|dir| {
Some(marker_direction(
projected?,
camera.project(g.world + dir, bounds)?,
))
});
(g.id, screen, g.is_midpoint, g.shape, dir)
})
.collect()
}
@@ -110,14 +121,24 @@ pub fn grips_to_screen_paper(
half_h: f32,
bounds: Rectangle,
) -> Vec<(usize, Point, bool, GripShape, Option<[f32; 2]>)> {
let project = |world: DVec3| {
Vec2::new(
(world.x as f32 - tx + half_w) / (2.0 * half_w) * bounds.width,
(ty + half_h - world.y as f32) / (2.0 * half_h) * bounds.height,
)
};
grips
.iter()
.map(|g| {
let screen = Point::new(
(g.world.x as f32 - tx + half_w) / (2.0 * half_w) * bounds.width,
(ty + half_h - g.world.y as f32) / (2.0 * half_h) * bounds.height,
);
(g.id, screen, g.is_midpoint, g.shape, g.dir)
let projected = project(g.world);
let screen = Point::new(projected.x, projected.y);
let dir = g.dir.map(|dir| {
[
dir.x as f32 / (2.0 * half_w) * bounds.width,
dir.y as f32 / (2.0 * half_h) * bounds.height,
]
});
(g.id, screen, g.is_midpoint, g.shape, dir)
})
.collect()
}
@@ -207,11 +228,18 @@ pub fn grips_to_screen_rte(
grips
.iter()
.map(|g| {
let screen = match project_rte(g.world, view_rot, eye, bounds) {
let projected = project_rte(g.world, view_rot, eye, bounds);
let screen = match projected {
Some(p) => Point::new(bounds.x + p.x, bounds.y + p.y),
None => Point::new(f32::NAN, f32::NAN),
};
(g.id, screen, g.is_midpoint, g.shape, g.dir)
let dir = g.dir.and_then(|dir| {
Some(marker_direction(
projected?,
project_rte(g.world + dir, view_rot, eye, bounds)?,
))
});
(g.id, screen, g.is_midpoint, g.shape, dir)
})
.collect()
}