fix(trim): keep retained geometry continuous
Use kernel trim spans so unrelated boundary crossings do not split survivors.
This commit is contained in:
parent
afd4c96a78
commit
f9c3546551
3 changed files with 49 additions and 44 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -878,7 +878,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cadkernel"
|
name = "cadkernel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=b8ec104#b8ec104e86060fe7d5c698870915b5388569649b"
|
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=ff950ce#ff950ce4146c9fb94a5478e6704ada01264dbf9f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"acadrust",
|
"acadrust",
|
||||||
"cavalier_contours",
|
"cavalier_contours",
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ rfd = "0.17"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "931c4ab", features = ["serde"] }
|
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "931c4ab", features = ["serde"] }
|
||||||
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "b8ec104", features = ["acis", "offset"] }
|
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "ff950ce", features = ["acis", "offset"] }
|
||||||
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
|
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
|
||||||
flate2 = "1"
|
flate2 = "1"
|
||||||
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
|
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ use acadrust::{EntityType, Handle};
|
||||||
use glam::DVec3;
|
use glam::DVec3;
|
||||||
use cadkernel::geom2d::nurbs::clamped_uniform_knots;
|
use cadkernel::geom2d::nurbs::clamped_uniform_knots;
|
||||||
use cadkernel::geom2d::{
|
use cadkernel::geom2d::{
|
||||||
intersect as kernel_intersect, Arc as KernelArc, Circle as KernelCircle, Curve,
|
intersect as kernel_intersect, trim_spans as kernel_trim_spans, Arc as KernelArc,
|
||||||
Extent as KernelExtent,
|
Circle as KernelCircle, Curve, Extent as KernelExtent,
|
||||||
Ellipse as KernelEllipse, EllipseArc as KernelEllipseArc, Line as KernelLine,
|
Ellipse as KernelEllipse, EllipseArc as KernelEllipseArc, Line as KernelLine,
|
||||||
NurbsCurve, Ray as KernelRay, Tolerance as KernelTolerance, XLine as KernelXLine,
|
NurbsCurve, Ray as KernelRay, Tolerance as KernelTolerance, XLine as KernelXLine,
|
||||||
};
|
};
|
||||||
|
|
@ -485,7 +485,24 @@ fn trim_ellipse(orig: &EllipseEnt, ts: &[f64], t_click: f64) -> Vec<EntityType>
|
||||||
return vec![EntityType::Ellipse(e)];
|
return vec![EntityType::Ellipse(e)];
|
||||||
}
|
}
|
||||||
|
|
||||||
trim_intervals(ts, t_click)
|
let major_radius = orig.major_axis.x.hypot(orig.major_axis.y);
|
||||||
|
if major_radius < 1e-9 {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
let curve = Curve::Ellipse(KernelEllipseArc {
|
||||||
|
ellipse: KernelEllipse {
|
||||||
|
centre: [orig.center.x, orig.center.y],
|
||||||
|
major_radius,
|
||||||
|
minor_radius: major_radius * orig.minor_axis_ratio,
|
||||||
|
major_axis: [
|
||||||
|
orig.major_axis.x / major_radius,
|
||||||
|
orig.major_axis.y / major_radius,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
start_parameter: t0,
|
||||||
|
end_parameter: t1,
|
||||||
|
});
|
||||||
|
trim_intervals(&curve, ts, t_click)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(ta, tb)| {
|
.filter_map(|(ta, tb)| {
|
||||||
if (tb - ta).abs() < 1e-6 {
|
if (tb - ta).abs() < 1e-6 {
|
||||||
|
|
@ -545,11 +562,13 @@ fn extend_ellipse(orig: &EllipseEnt, t_click: f64, geos: &[Geo]) -> Option<Entit
|
||||||
|
|
||||||
/// Trim a Spline entity. Returns surviving spline pieces (one or two).
|
/// Trim a Spline entity. Returns surviving spline pieces (one or two).
|
||||||
fn trim_spline(spl: &SplineEnt, ts: &[f64], t_click: f64) -> Vec<EntityType> {
|
fn trim_spline(spl: &SplineEnt, ts: &[f64], t_click: f64) -> Vec<EntityType> {
|
||||||
let Some((t0, t1)) = spline_range(spl) else {
|
let Some(curve) = spline_to_nurbs(spl) else {
|
||||||
return vec![];
|
return vec![];
|
||||||
};
|
};
|
||||||
|
let (t0, t1) = curve.domain();
|
||||||
|
let curve = Curve::Nurbs(curve);
|
||||||
|
|
||||||
trim_intervals(ts, t_click)
|
trim_intervals(&curve, ts, t_click)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(ta, tb)| {
|
.filter_map(|(ta, tb)| {
|
||||||
let t_lo = t0 + ta * (t1 - t0);
|
let t_lo = t0 + ta * (t1 - t0);
|
||||||
|
|
@ -642,38 +661,15 @@ fn extend_spline(spl: &SplineEnt, t_click: f64, geos: &[Geo]) -> Option<EntityTy
|
||||||
|
|
||||||
// ── Trim helpers ──────────────────────────────────────────────────────────
|
// ── Trim helpers ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Remove the t-interval containing `t_click` from sorted ts. Returns surviving pieces.
|
fn trim_intervals(curve: &Curve, ts: &[f64], t_click: f64) -> Vec<(f64, f64)> {
|
||||||
fn trim_intervals(ts: &[f64], t_click: f64) -> Vec<(f64, f64)> {
|
kernel_trim_spans(
|
||||||
cut_intervals(ts, t_click, KernelExtent::Bounded)
|
curve,
|
||||||
}
|
ts,
|
||||||
|
t_click,
|
||||||
/// The stretches a target keeps after the one holding the click is removed.
|
KernelTolerance::new(CUT_TOLERANCE),
|
||||||
///
|
)
|
||||||
/// The outer bounds come from how far the target's parameter runs, so a ray
|
.into_iter()
|
||||||
/// keeps an interval that reaches infinity rather than one that stops at an
|
.map(|span| (span[0], span[1]))
|
||||||
/// invented far end. Those infinities are what tell the caller a surviving
|
|
||||||
/// piece is still unbounded.
|
|
||||||
fn cut_intervals(ts: &[f64], t_click: f64, extent: KernelExtent) -> Vec<(f64, f64)> {
|
|
||||||
let (first, last) = match extent {
|
|
||||||
KernelExtent::Bounded => (0.0, 1.0),
|
|
||||||
KernelExtent::Forward => (0.0, f64::INFINITY),
|
|
||||||
KernelExtent::Infinite => (f64::NEG_INFINITY, f64::INFINITY),
|
|
||||||
};
|
|
||||||
let mut bounds = vec![first];
|
|
||||||
bounds.extend_from_slice(ts);
|
|
||||||
bounds.push(last);
|
|
||||||
bounds.dedup_by(|a, b| (*a - *b).abs() < 1e-6);
|
|
||||||
|
|
||||||
let remove = bounds
|
|
||||||
.windows(2)
|
|
||||||
.position(|w| t_click >= w[0] - 1e-6 && t_click <= w[1] + 1e-6);
|
|
||||||
|
|
||||||
bounds
|
|
||||||
.windows(2)
|
|
||||||
.enumerate()
|
|
||||||
.filter(|(idx, _)| Some(*idx) != remove)
|
|
||||||
.filter(|(_, w)| (w[1] - w[0]) > 1e-6)
|
|
||||||
.map(|(_, w)| (w[0], w[1]))
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -682,7 +678,8 @@ fn trim_line(orig: &LineEnt, ts: &[f64], t_click: f64) -> Vec<EntityType> {
|
||||||
let p1 = [orig.start.x, orig.start.y];
|
let p1 = [orig.start.x, orig.start.y];
|
||||||
let p2 = [orig.end.x, orig.end.y];
|
let p2 = [orig.end.x, orig.end.y];
|
||||||
let z = orig.start.z;
|
let z = orig.start.z;
|
||||||
trim_intervals(ts, t_click)
|
let curve = Curve::Line(KernelLine { start: p1, end: p2 });
|
||||||
|
trim_intervals(&curve, ts, t_click)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(ta, tb)| {
|
.filter_map(|(ta, tb)| {
|
||||||
let a = lerp2(p1, p2, ta);
|
let a = lerp2(p1, p2, ta);
|
||||||
|
|
@ -768,8 +765,14 @@ fn trim_arc(orig: &ArcEnt, ts: &[f64], t_click: f64) -> Vec<EntityType> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let angle_at = |t: f64| norm(a0) + span * t;
|
let angle_at = |t: f64| norm(a0) + span * t;
|
||||||
|
let curve = Curve::Arc(KernelArc {
|
||||||
|
centre: [orig.center.x, orig.center.y],
|
||||||
|
radius: orig.radius,
|
||||||
|
start_angle: a0,
|
||||||
|
end_angle: a1,
|
||||||
|
});
|
||||||
|
|
||||||
trim_intervals(ts, t_click)
|
trim_intervals(&curve, ts, t_click)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(ta, tb)| {
|
.filter_map(|(ta, tb)| {
|
||||||
if (tb - ta).abs() < 1e-6 {
|
if (tb - ta).abs() < 1e-6 {
|
||||||
|
|
@ -1113,7 +1116,8 @@ fn trim_ray(orig: &RayEnt, ts: &[f64], t_click: f64) -> Vec<EntityType> {
|
||||||
let dz = orig.direction.z;
|
let dz = orig.direction.z;
|
||||||
let pt = |t: f64| [bx + t * dx, by + t * dy, bz + t * dz];
|
let pt = |t: f64| [bx + t * dx, by + t * dy, bz + t * dz];
|
||||||
|
|
||||||
cut_intervals(ts, t_click, KernelExtent::Forward)
|
let curve = ray_curve(orig);
|
||||||
|
trim_intervals(&curve, ts, t_click)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(ta, tb)| {
|
.filter_map(|(ta, tb)| {
|
||||||
let pa = pt(ta);
|
let pa = pt(ta);
|
||||||
|
|
@ -1159,7 +1163,8 @@ fn trim_xline(orig: &XLineEnt, ts: &[f64], t_click: f64) -> Vec<EntityType> {
|
||||||
let dz = orig.direction.z;
|
let dz = orig.direction.z;
|
||||||
let pt = |t: f64| [bx + t * dx, by + t * dy, bz + t * dz];
|
let pt = |t: f64| [bx + t * dx, by + t * dy, bz + t * dz];
|
||||||
|
|
||||||
cut_intervals(ts, t_click, KernelExtent::Infinite)
|
let curve = xline_curve(orig);
|
||||||
|
trim_intervals(&curve, ts, t_click)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(ta, tb)| {
|
.filter_map(|(ta, tb)| {
|
||||||
let pa = pt(ta);
|
let pa = pt(ta);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue