fix(fillet): honor selected trim side
This commit is contained in:
parent
456ed24d71
commit
c17e31c1ed
1 changed files with 31 additions and 63 deletions
|
|
@ -91,19 +91,31 @@ fn compute_fillet(
|
||||||
click2: [f64; 2],
|
click2: [f64; 2],
|
||||||
radius: f64,
|
radius: f64,
|
||||||
) -> Option<(EntityType, EntityType, Option<EntityType>)> {
|
) -> Option<(EntityType, EntityType, Option<EntityType>)> {
|
||||||
let (p1, _p2, u1, _len1) = line_geom(l1);
|
let (p1, p2, u1, _len1) = line_geom(l1);
|
||||||
let (p3, _p4, u2, _len2) = line_geom(l2);
|
let (p3, p4, u2, _len2) = line_geom(l2);
|
||||||
|
|
||||||
// Intersection of infinite lines
|
// Intersection of infinite lines
|
||||||
let (t_p, u_p) = ll(p1[0], p1[1], u1[0], u1[1], p3[0], p3[1], u2[0], u2[1])?;
|
let (t_p, _u_p) = ll(p1[0], p1[1], u1[0], u1[1], p3[0], p3[1], u2[0], u2[1])?;
|
||||||
|
|
||||||
// Intersection point
|
// Intersection point
|
||||||
let px = p1[0] + t_p * u1[0];
|
let px = p1[0] + t_p * u1[0];
|
||||||
let py = p1[1] + t_p * u1[1];
|
let py = p1[1] + t_p * u1[1];
|
||||||
|
|
||||||
// Direction from P toward each click (the "keep" side)
|
// Direction from P toward each click (the "keep" side)
|
||||||
let s1 = project_click(click1, [px, py], u1); // positive = along u1
|
let picked_side = |click: [f64; 2], a: [f64; 2], b: [f64; 2], unit: [f64; 2]| {
|
||||||
let s2 = project_click(click2, [px, py], u2);
|
let picked = project_click(click, [px, py], unit);
|
||||||
|
if picked.abs() > 1e-9 {
|
||||||
|
picked
|
||||||
|
} else {
|
||||||
|
// Clicking exactly on the intersection is common when the two
|
||||||
|
// entities already meet. Fall back to the existing segment's
|
||||||
|
// midpoint so entity start/end storage order cannot flip the side
|
||||||
|
// FILLET keeps (#616).
|
||||||
|
project_click([(a[0] + b[0]) * 0.5, (a[1] + b[1]) * 0.5], [px, py], unit)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let s1 = picked_side(click1, p1, p2, u1); // positive = along u1
|
||||||
|
let s2 = picked_side(click2, p3, p4, u2);
|
||||||
let dir1 = if s1 >= 0.0 {
|
let dir1 = if s1 >= 0.0 {
|
||||||
[u1[0], u1[1]]
|
[u1[0], u1[1]]
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -128,8 +140,13 @@ fn compute_fillet(
|
||||||
let z = l1.start.z;
|
let z = l1.start.z;
|
||||||
|
|
||||||
if radius < 1e-9 {
|
if radius < 1e-9 {
|
||||||
// r = 0: just extend/trim both lines to the intersection
|
// r = 0: extend/trim the endpoint opposite each clicked keep-side.
|
||||||
let (new_l1, new_l2) = trim_to_point(l1, t_p, p1, u1, l2, u_p, p3, u2)?;
|
// The old path chose by the sign of t_p/u_p, so reversing an entity's
|
||||||
|
// stored direction changed the result and an intersection at its start
|
||||||
|
// could collapse the whole line (#616).
|
||||||
|
let intersection = [px, py];
|
||||||
|
let new_l1 = trim_to_xy(l1, intersection, dir1, u1)?;
|
||||||
|
let new_l2 = trim_to_xy(l2, intersection, dir2, u2)?;
|
||||||
return Some((EntityType::Line(new_l1), EntityType::Line(new_l2), None));
|
return Some((EntityType::Line(new_l1), EntityType::Line(new_l2), None));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,8 +180,8 @@ fn compute_fillet(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Trim l1 to T1 and l2 to T2
|
// Trim l1 to T1 and l2 to T2
|
||||||
let new_l1 = trim_to_xy(l1, t_p, t1, dir1, p1, u1)?;
|
let new_l1 = trim_to_xy(l1, t1, dir1, u1)?;
|
||||||
let new_l2 = trim_to_xy(l2, u_p, t2, dir2, p3, u2)?;
|
let new_l2 = trim_to_xy(l2, t2, dir2, u2)?;
|
||||||
|
|
||||||
// Build arc entity
|
// Build arc entity
|
||||||
let mut arc = ArcEnt::new();
|
let mut arc = ArcEnt::new();
|
||||||
|
|
@ -182,30 +199,20 @@ fn compute_fillet(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trim a line's parameter to an intersection t on the same side as dir (keep side).
|
/// Move the endpoint opposite the selected keep-side to the tangent point.
|
||||||
fn trim_to_xy(
|
fn trim_to_xy(
|
||||||
orig: &LineEnt,
|
orig: &LineEnt,
|
||||||
t_isect: f64,
|
|
||||||
tangent: [f64; 2],
|
tangent: [f64; 2],
|
||||||
dir: [f64; 2],
|
dir: [f64; 2],
|
||||||
p1: [f64; 2],
|
|
||||||
unit: [f64; 2],
|
unit: [f64; 2],
|
||||||
) -> Option<LineEnt> {
|
) -> Option<LineEnt> {
|
||||||
let z = orig.start.z;
|
let z = orig.start.z;
|
||||||
let mut l = orig.clone();
|
let mut l = orig.clone();
|
||||||
l.common.handle = Handle::NULL;
|
l.common.handle = Handle::NULL;
|
||||||
|
|
||||||
// t_tangent: parameter of the tangent point along the line from start
|
// dir is positive along unit → keep the portion beyond the tangent in that direction
|
||||||
let t_tan = (tangent[0] - p1[0]) * unit[0] + (tangent[1] - p1[1]) * unit[1];
|
|
||||||
|
|
||||||
// dir is positive along unit → we keep the portion BEYOND t_tan in that direction
|
|
||||||
// dir positive: keep from t_tan to +∞ (i.e. set start to tangent point)
|
// dir positive: keep from t_tan to +∞ (i.e. set start to tangent point)
|
||||||
// dir negative: keep from -∞ to t_tan (i.e. set end to tangent point)
|
// dir negative: keep from -∞ to t_tan (i.e. set end to tangent point)
|
||||||
let len = {
|
|
||||||
let dx = orig.end.x - orig.start.x;
|
|
||||||
let dy = orig.end.y - orig.start.y;
|
|
||||||
(dx * dx + dy * dy).sqrt().max(1e-12)
|
|
||||||
};
|
|
||||||
let dot = dir[0] * unit[0] + dir[1] * unit[1]; // +1 or -1
|
let dot = dir[0] * unit[0] + dir[1] * unit[1]; // +1 or -1
|
||||||
|
|
||||||
if dot > 0.0 {
|
if dot > 0.0 {
|
||||||
|
|
@ -215,48 +222,9 @@ fn trim_to_xy(
|
||||||
// keep from start to tangent → move end to tangent point
|
// keep from start to tangent → move end to tangent point
|
||||||
l.end = Vector3::new(tangent[0], tangent[1], z);
|
l.end = Vector3::new(tangent[0], tangent[1], z);
|
||||||
}
|
}
|
||||||
let _ = (t_isect, len, t_tan); // used implicitly via `dot`
|
|
||||||
Some(l)
|
Some(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trim both lines exactly to their intersection point (r=0 case).
|
|
||||||
fn trim_to_point(
|
|
||||||
l1: &LineEnt,
|
|
||||||
t_p: f64,
|
|
||||||
p1: [f64; 2],
|
|
||||||
u1: [f64; 2],
|
|
||||||
l2: &LineEnt,
|
|
||||||
u_p: f64,
|
|
||||||
_p3: [f64; 2],
|
|
||||||
_u2: [f64; 2],
|
|
||||||
) -> Option<(LineEnt, LineEnt)> {
|
|
||||||
let px = p1[0] + t_p * u1[0];
|
|
||||||
let py = p1[1] + t_p * u1[1];
|
|
||||||
let z1 = l1.start.z;
|
|
||||||
let z2 = l2.start.z;
|
|
||||||
|
|
||||||
// For l1: if t_p is past the midpoint, keep start…P; else keep P…end
|
|
||||||
// We use the same "which end is P closer to" logic
|
|
||||||
let mut ll1 = l1.clone();
|
|
||||||
ll1.common.handle = Handle::NULL;
|
|
||||||
let mut ll2 = l2.clone();
|
|
||||||
ll2.common.handle = Handle::NULL;
|
|
||||||
|
|
||||||
if t_p >= 0.0 {
|
|
||||||
ll1.end = Vector3::new(px, py, z1);
|
|
||||||
} else {
|
|
||||||
ll1.start = Vector3::new(px, py, z1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if u_p >= 0.0 {
|
|
||||||
ll2.end = Vector3::new(px, py, z2);
|
|
||||||
} else {
|
|
||||||
ll2.start = Vector3::new(px, py, z2);
|
|
||||||
}
|
|
||||||
|
|
||||||
Some((ll1, ll2))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Point-generation helpers ──────────────────────────────────────────────
|
// ── Point-generation helpers ──────────────────────────────────────────────
|
||||||
|
|
||||||
fn line_pts(l: &LineEnt) -> Vec<[f32; 3]> {
|
fn line_pts(l: &LineEnt) -> Vec<[f32; 3]> {
|
||||||
|
|
@ -1148,7 +1116,7 @@ fn compute_chamfer(
|
||||||
let (p1, _, u1, _) = line_geom(l1);
|
let (p1, _, u1, _) = line_geom(l1);
|
||||||
let (p3, _, u2, _) = line_geom(l2);
|
let (p3, _, u2, _) = line_geom(l2);
|
||||||
|
|
||||||
let (t_p, u_p) = ll(p1[0], p1[1], u1[0], u1[1], p3[0], p3[1], u2[0], u2[1])?;
|
let (t_p, _u_p) = ll(p1[0], p1[1], u1[0], u1[1], p3[0], p3[1], u2[0], u2[1])?;
|
||||||
|
|
||||||
let px = p1[0] + t_p * u1[0];
|
let px = p1[0] + t_p * u1[0];
|
||||||
let py = p1[1] + t_p * u1[1];
|
let py = p1[1] + t_p * u1[1];
|
||||||
|
|
@ -1172,8 +1140,8 @@ fn compute_chamfer(
|
||||||
let c2 = [px + dist2 * dir2[0], py + dist2 * dir2[1]];
|
let c2 = [px + dist2 * dir2[0], py + dist2 * dir2[1]];
|
||||||
|
|
||||||
// Trim l1 to c1 and l2 to c2
|
// Trim l1 to c1 and l2 to c2
|
||||||
let new_l1 = trim_to_xy(l1, t_p, c1, dir1, p1, u1)?;
|
let new_l1 = trim_to_xy(l1, c1, dir1, u1)?;
|
||||||
let new_l2 = trim_to_xy(l2, u_p, c2, dir2, p3, u2)?;
|
let new_l2 = trim_to_xy(l2, c2, dir2, u2)?;
|
||||||
|
|
||||||
// Chamfer line
|
// Chamfer line
|
||||||
let mut cline = l1.clone();
|
let mut cline = l1.clone();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue