refactor(fillet): take the corner arc from cadkernel

`compute_fillet` keeps the entity work — resolving which side of each line
the pick kept, and trimming the lines back — and hands the geometry out.
The kernel takes the corner as an apex plus the two keep directions, which
is what the side resolution here already produces, and returns the tangent
points, centre and the endpoints in fill-the-corner order.

`line_circle_ts` and `circle_circle_pts` were further copies of maths the
kernel already had; the second now calls `circle_circle_points`, added for
callers that want the crossings as points rather than as angles to cut at.

`arc_span` and `clamp_angle_to_arc` stay put on purpose. This file's
`arc_span` treats any span under 1e-6 as a full turn, where the kernel's
only does so at zero or below. That difference is real for a degenerate
arc, and picking a winner is a separate question from moving the code.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 5 committed 2026-08-08 16:20:34 +03:00
1 parent 397fb38292
commit 95e61f3e8d
2 files changed
+12 -84

No files matched your search

Generated
+1 -1
View File
@@ -945,7 +945,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]]
name = "cadkernel"
version = "0.1.0"
source = "git+https://github.com/HakanSeven12/cadkernel.git#9c7b2b75ab76806d086f2e7eca17750771150dba"
source = "git+https://github.com/HakanSeven12/cadkernel.git#feed535a0f72f9479d618a4999dba9bafcea250f"
dependencies = [
"cavalier_contours",
]
+11 -83
View File
@@ -14,6 +14,9 @@ use acadrust::entities::{Arc as ArcEnt, Line as LineEnt, LwPolyline};
// Shared plane geometry, from cadkernel via the local adapters.
use super::geom;
use super::geom::{arc_points as arc_pts, line_line as ll, normalize_angle as norm_angle};
use acadrust::kernel::geom2d::{
circle_circle_points as circle_circle_pts, fillet_between_rays, line_circle,
};
use acadrust::types::Vector3;
use acadrust::{EntityType, Handle};
use glam::DVec3;
@@ -121,7 +124,6 @@ fn compute_fillet(
return None;
}
let half = angle / 2.0;
let z = l1.start.z;
if radius < 1e-9 {
@@ -135,47 +137,19 @@ fn compute_fillet(
return Some((EntityType::Line(new_l1), EntityType::Line(new_l2), None));
}
// Distance from P to tangent points
let d = radius / half.tan();
let rounded = fillet_between_rays([px, py], dir1, dir2, radius)?;
// Tangent points
let t1 = [px + d * dir1[0], py + d * dir1[1]];
let t2 = [px + d * dir2[0], py + d * dir2[1]];
// Trim each line back to where the arc meets it.
let new_l1 = trim_to_xy(l1, rounded.tangent1, dir1, u1)?;
let new_l2 = trim_to_xy(l2, rounded.tangent2, dir2, u2)?;
// Arc center: along bisector of dir1+dir2, distance = r / sin(half)
let bx = dir1[0] + dir2[0];
let by = dir1[1] + dir2[1];
let blen = (bx * bx + by * by).sqrt();
if blen < 1e-10 {
return None;
}
let arc_dist = radius / half.sin();
let arc_cx = px + arc_dist * bx / blen;
let arc_cy = py + arc_dist * by / blen;
let a_start = (t1[1] - arc_cy).atan2(t1[0] - arc_cx);
let a_end = (t2[1] - arc_cy).atan2(t2[0] - arc_cx);
// Pick CCW direction that fills the concave corner
let cross = dir1[0] * dir2[1] - dir1[1] * dir2[0];
let (start_angle, end_angle) = if cross <= 0.0 {
(a_start, a_end)
} else {
(a_end, a_start)
};
// Trim l1 to T1 and l2 to T2
let new_l1 = trim_to_xy(l1, t1, dir1, u1)?;
let new_l2 = trim_to_xy(l2, t2, dir2, u2)?;
// Build arc entity
let mut arc = ArcEnt::new();
arc.common = l1.common.clone();
arc.common.handle = Handle::NULL;
arc.center = Vector3::new(arc_cx, arc_cy, z);
arc.center = Vector3::new(rounded.centre[0], rounded.centre[1], z);
arc.radius = radius;
arc.start_angle = norm_angle(start_angle);
arc.end_angle = norm_angle(end_angle);
arc.start_angle = rounded.start_angle;
arc.end_angle = rounded.end_angle;
Some((
EntityType::Line(new_l1),
@@ -286,52 +260,6 @@ fn trim_arc(orig: &ArcEnt, new_start: f64, new_end: f64) -> ArcEnt {
a
}
/// Intersect a line (point p + direction d) with a circle (center c, radius r).
/// Returns up to 2 parameter values t on the line.
fn line_circle_ts(px: f64, py: f64, dx: f64, dy: f64, cx: f64, cy: f64, r: f64) -> Vec<f64> {
let fx = px - cx;
let fy = py - cy;
let a = dx * dx + dy * dy;
let b = 2.0 * (fx * dx + fy * dy);
let c = fx * fx + fy * fy - r * r;
let disc = b * b - 4.0 * a * c;
if disc < 0.0 {
return vec![];
}
let sq = disc.sqrt();
if disc < 1e-14 {
vec![(-b) / (2.0 * a)]
} else {
vec![(-b - sq) / (2.0 * a), (-b + sq) / (2.0 * a)]
}
}
/// Intersect two circles. Returns intersection points.
fn circle_circle_pts(c1: [f64; 2], r1: f64, c2: [f64; 2], r2: f64) -> Vec<[f64; 2]> {
let dx = c2[0] - c1[0];
let dy = c2[1] - c1[1];
let d = (dx * dx + dy * dy).sqrt();
if d < 1e-12 || d > r1 + r2 + 1e-9 || d < (r1 - r2).abs() - 1e-9 {
return vec![];
}
let a = (r1 * r1 - r2 * r2 + d * d) / (2.0 * d);
let h2 = r1 * r1 - a * a;
if h2 < 0.0 {
return vec![];
}
let h = h2.sqrt();
let mx = c1[0] + a * dx / d;
let my = c1[1] + a * dy / d;
if h < 1e-9 {
vec![[mx, my]]
} else {
vec![
[mx + h * dy / d, my - h * dx / d],
[mx - h * dy / d, my + h * dx / d],
]
}
}
// ── LwPolyline helpers ────────────────────────────────────────────────────
/// Find the index of the LwPolyline segment nearest to `click` (DXF XY).
@@ -761,7 +689,7 @@ fn fillet_line_arc(
let (ac, ar, a_start, a_end, _) = arc_geom(arc);
// Intersection of infinite line with the arc's circle
let ts = line_circle_ts(p1[0], p1[1], u[0], u[1], ac[0], ac[1], ar);
let ts = line_circle(p1, u, ac, ar);
if radius < 1e-9 {
// r=0: trim to intersection (nearest to each click)