feat(spline): improve fit and CV editing

Keep planar conversions kernel-backed and leave unsupported 3D, CV-to-fit, and closed rebuilds unavailable instead of changing curve data.\n\nRefs #815
This commit is contained in:
Hakan Seven committed 2026-08-20 00:18:45 +03:00
commit 1c408e3a73
16 files changed
+929 -355

No files matched your search

+4
View File
@@ -83,6 +83,8 @@ pub enum GripShape {
Rectangle,
Triangle,
Circle,
/// Screen-offset menu selector.
Dropdown,
}
/// Describes one grip point for an entity.
@@ -163,6 +165,8 @@ pub enum GripMenuAction {
RemoveFitPoint,
Refit,
RefineVertices,
ShowFit,
ShowControlVertices,
MoveWithText,
StackText,
UnstackText,
+22 -5
View File
@@ -10,6 +10,17 @@ use crate::scene::model::object::{GripDef, GripShape};
pub const GRIP_THRESHOLD_PX: f32 = 8.0;
/// Half-size of the rendered grip square / diamond in pixels.
pub const GRIP_HALF_PX: f32 = 5.0;
/// Screen offset for dropdown selectors anchored to a vertex.
pub const GRIP_DROPDOWN_OFFSET_X_PX: f32 = 24.0;
pub const GRIP_DROPDOWN_OFFSET_Y_PX: f32 = 27.0;
fn marker_screen_position(mut point: Point, shape: GripShape) -> Point {
if shape == GripShape::Dropdown {
point.x += GRIP_DROPDOWN_OFFSET_X_PX;
point.y += GRIP_DROPDOWN_OFFSET_Y_PX;
}
point
}
// ── Active drag state ─────────────────────────────────────────────────────
@@ -106,6 +117,7 @@ pub fn grips_to_screen(
camera.project(g.world + dir, bounds)?,
))
});
let screen = marker_screen_position(screen, g.shape);
(g.id, screen, g.is_midpoint, g.shape, dir)
})
.collect()
@@ -131,7 +143,7 @@ pub fn grips_to_screen_paper(
.iter()
.map(|g| {
let projected = project(g.world);
let screen = Point::new(projected.x, projected.y);
let screen = marker_screen_position(Point::new(projected.x, projected.y), g.shape);
let dir = g.dir.map(|dir| {
[
dir.x as f32 / (2.0 * half_w) * bounds.width,
@@ -157,9 +169,12 @@ pub fn find_hit_grip_paper(
let mut best: Option<(usize, usize, bool, DVec3)> = None;
for (index, g) in grips.iter().enumerate() {
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,
let screen = marker_screen_position(
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.shape,
);
let dx = screen.x - cursor.x;
let dy = screen.y - cursor.y;
@@ -187,7 +202,7 @@ pub fn find_hit_grip(
let Some(screen) = camera.project(g.world, bounds) else {
continue;
};
let screen = Point::new(screen.x, screen.y);
let screen = marker_screen_position(Point::new(screen.x, screen.y), g.shape);
let dx = screen.x - cursor.x;
let dy = screen.y - cursor.y;
let d = (dx * dx + dy * dy).sqrt();
@@ -239,6 +254,7 @@ pub fn grips_to_screen_rte(
project_rte(g.world + dir, view_rot, eye, bounds)?,
))
});
let screen = marker_screen_position(screen, g.shape);
(g.id, screen, g.is_midpoint, g.shape, dir)
})
.collect()
@@ -260,6 +276,7 @@ pub fn find_hit_grip_rte(
let Some(screen) = project_rte(g.world, view_rot, eye, bounds) else {
continue;
};
let screen = marker_screen_position(Point::new(screen.x, screen.y), g.shape);
let dx = screen.x - cursor.x;
let dy = screen.y - cursor.y;
let d = (dx * dx + dy * dy).sqrt();