Support planar circle tangent snapping
This commit is contained in:
parent
5b4c1da881
commit
390d42a63a
4 changed files with 121 additions and 3 deletions
|
|
@ -25,8 +25,10 @@ fn to_render(circle: &Circle) -> RenderEntity {
|
||||||
// definition answers here, in the tessellation and in a trim.
|
// definition answers here, in the tessellation and in a trim.
|
||||||
let curve = crate::entities::curve::circle_curve(circle);
|
let curve = crate::entities::curve::circle_curve(circle);
|
||||||
let snap_pts = crate::entities::curve::snap_from(&curve).snap_pts;
|
let snap_pts = crate::entities::curve::snap_from(&curve).snap_pts;
|
||||||
let tangent = TangentGeom::Circle {
|
let tangent = TangentGeom::PlanarCircle {
|
||||||
center: [cwx as f32, cwy as f32, cwz as f32],
|
center: [cwx as f32, cwy as f32, cwz as f32],
|
||||||
|
axis_x: [ax.0 as f32, ax.1 as f32, ax.2 as f32],
|
||||||
|
axis_y: [ay.0 as f32, ay.1 as f32, ay.2 as f32],
|
||||||
radius: rf,
|
radius: rf,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
34
src/scene/cache/block_cache.rs
vendored
34
src/scene/cache/block_cache.rs
vendored
|
|
@ -1227,7 +1227,8 @@ fn translated_prototype_wire(
|
||||||
p2[axis] += delta_f32[axis];
|
p2[axis] += delta_f32[axis];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TangentGeom::Circle { center, .. } => {
|
TangentGeom::Circle { center, .. }
|
||||||
|
| TangentGeom::PlanarCircle { center, .. } => {
|
||||||
for axis in 0..3 {
|
for axis in 0..3 {
|
||||||
center[axis] += delta_f32[axis];
|
center[axis] += delta_f32[axis];
|
||||||
}
|
}
|
||||||
|
|
@ -2282,6 +2283,37 @@ fn transform_tangent(
|
||||||
radius: radius * s,
|
radius: radius * s,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TangentGeom::PlanarCircle {
|
||||||
|
center,
|
||||||
|
axis_x,
|
||||||
|
axis_y,
|
||||||
|
radius,
|
||||||
|
} => {
|
||||||
|
let c = t.apply(Vector3::new(
|
||||||
|
center[0] as f64,
|
||||||
|
center[1] as f64,
|
||||||
|
center[2] as f64,
|
||||||
|
));
|
||||||
|
let x = t.apply_rotation(Vector3::new(
|
||||||
|
axis_x[0] as f64,
|
||||||
|
axis_x[1] as f64,
|
||||||
|
axis_x[2] as f64,
|
||||||
|
));
|
||||||
|
let y = t.apply_rotation(Vector3::new(
|
||||||
|
axis_y[0] as f64,
|
||||||
|
axis_y[1] as f64,
|
||||||
|
axis_y[2] as f64,
|
||||||
|
));
|
||||||
|
let scale = (x.length() + y.length()) * 0.5;
|
||||||
|
let x = x.normalize();
|
||||||
|
let y = y.normalize();
|
||||||
|
TangentGeom::PlanarCircle {
|
||||||
|
center: [c.x as f32, c.y as f32, c.z as f32],
|
||||||
|
axis_x: [x.x as f32, x.y as f32, x.z as f32],
|
||||||
|
axis_y: [y.x as f32, y.y as f32, y.z as f32],
|
||||||
|
radius: radius * scale as f32,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,15 @@ pub enum SnapHint {
|
||||||
pub enum TangentGeom {
|
pub enum TangentGeom {
|
||||||
/// Infinite line through these two world-space points.
|
/// Infinite line through these two world-space points.
|
||||||
Line { p1: [f32; 3], p2: [f32; 3] },
|
Line { p1: [f32; 3], p2: [f32; 3] },
|
||||||
/// Circle/arc.
|
/// Round geometry in the world XY plane.
|
||||||
Circle { center: [f32; 3], radius: f32 },
|
Circle { center: [f32; 3], radius: f32 },
|
||||||
|
/// Complete circle in an arbitrary world-space plane.
|
||||||
|
PlanarCircle {
|
||||||
|
center: [f32; 3],
|
||||||
|
axis_x: [f32; 3],
|
||||||
|
axis_y: [f32; 3],
|
||||||
|
radius: f32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A 1-D entity (line, arc, polyline) represented as an ordered set of
|
/// A 1-D entity (line, arc, polyline) represented as an ordered set of
|
||||||
|
|
|
||||||
77
src/snap.rs
77
src/snap.rs
|
|
@ -1661,6 +1661,73 @@ impl Snapper {
|
||||||
});
|
});
|
||||||
(w, edge_d * edge_d)
|
(w, edge_d * edge_d)
|
||||||
}
|
}
|
||||||
|
TangentGeom::PlanarCircle {
|
||||||
|
center,
|
||||||
|
axis_x,
|
||||||
|
axis_y,
|
||||||
|
radius,
|
||||||
|
} => {
|
||||||
|
let cv = Vec3::from(*center);
|
||||||
|
let ux = Vec3::from(*axis_x).normalize_or_zero();
|
||||||
|
let uy = Vec3::from(*axis_y).normalize_or_zero();
|
||||||
|
let r = *radius;
|
||||||
|
let candidate = self.from_point.and_then(|from| {
|
||||||
|
let delta = from - cv;
|
||||||
|
let px = delta.dot(ux);
|
||||||
|
let py = delta.dot(uy);
|
||||||
|
let d2 = px * px + py * py;
|
||||||
|
if d2 <= r * r + 1.0e-6 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let base = r * r / d2;
|
||||||
|
let side = r * (d2 - r * r).sqrt() / d2;
|
||||||
|
[
|
||||||
|
cv + ux * (base * px - side * py)
|
||||||
|
+ uy * (base * py + side * px),
|
||||||
|
cv + ux * (base * px + side * py)
|
||||||
|
+ uy * (base * py - side * px),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.min_by(|a, b| {
|
||||||
|
let sa = world_to_screen(a.as_dvec3(), view_rot, eye, bounds);
|
||||||
|
let sb = world_to_screen(b.as_dvec3(), view_rot, eye, bounds);
|
||||||
|
dist2(sa, cursor_screen)
|
||||||
|
.total_cmp(&dist2(sb, cursor_screen))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let fallback = || {
|
||||||
|
(0..wire.points.len())
|
||||||
|
.map(|index| wp_f64(wire, index).as_vec3())
|
||||||
|
.filter(|point| point.is_finite())
|
||||||
|
.min_by(|a, b| {
|
||||||
|
let sa =
|
||||||
|
world_to_screen(a.as_dvec3(), view_rot, eye, bounds);
|
||||||
|
let sb =
|
||||||
|
world_to_screen(b.as_dvec3(), view_rot, eye, bounds);
|
||||||
|
dist2(sa, cursor_screen)
|
||||||
|
.total_cmp(&dist2(sb, cursor_screen))
|
||||||
|
})
|
||||||
|
.unwrap_or(cv)
|
||||||
|
};
|
||||||
|
let world = candidate.unwrap_or_else(fallback);
|
||||||
|
let edge_d2 = wire
|
||||||
|
.points
|
||||||
|
.windows(2)
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(index, _)| {
|
||||||
|
let a = wp_f64(wire, index);
|
||||||
|
let b = wp_f64(wire, index + 1);
|
||||||
|
(a.is_finite() && b.is_finite()).then(|| {
|
||||||
|
dist2_to_segment(
|
||||||
|
cursor_screen,
|
||||||
|
world_to_screen(a, view_rot, eye, bounds),
|
||||||
|
world_to_screen(b, view_rot, eye, bounds),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.fold(f32::INFINITY, f32::min);
|
||||||
|
(world, edge_d2)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let (tier, sub) = (
|
let (tier, sub) = (
|
||||||
snap_tier(SnapType::Tangent),
|
snap_tier(SnapType::Tangent),
|
||||||
|
|
@ -1687,6 +1754,16 @@ impl Snapper {
|
||||||
),
|
),
|
||||||
radius: *radius as f64,
|
radius: *radius as f64,
|
||||||
},
|
},
|
||||||
|
TangentGeom::PlanarCircle { center, radius, .. } => {
|
||||||
|
TangentObject::Circle {
|
||||||
|
center: glam::DVec3::new(
|
||||||
|
center[0] as f64,
|
||||||
|
center[1] as f64,
|
||||||
|
center[2] as f64,
|
||||||
|
),
|
||||||
|
radius: *radius as f64,
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
best = Some(SnapResult {
|
best = Some(SnapResult {
|
||||||
world: world_pt.as_dvec3(),
|
world: world_pt.as_dvec3(),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue