mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-06 20:02:16 +00:00
Blends: an edge is smooth only where its faces are tangent all along it
The edge where a round runs into a cylinder's wall it is tangent to at one end (the bar's front top round meeting the wall at (40, 0, 8), where the round's contact with the front face meets the wall's tangent line with it) was refused as smooth, "nothing to round": the faces' normals were compared at the edge's start alone, which was that tangent end. They are now compared at five stations from end to end, and an edge is smooth only where its faces meet tangentially at every one: this edge is a crease everywhere but its end. The wall's seam with the front face, tangent all along, is still refused as smooth. The round of that edge with its tangent chain (the top's concave arc) now reaches SMLib, whose result fails its own check (internal poles, coincident control points, cusps where the round fades out): its rig case in fillets is listed as known failing for that SMLib defect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -493,6 +493,24 @@ fn rounds_on_a_bar_running_tangent_into_a_cylinder() {
|
||||
}
|
||||
}
|
||||
|
||||
/// After the peg's front top round, the edge where that round runs into the cylinder's wall: its
|
||||
/// faces are tangent at its lower end (40, 0, 8), where the round's contact with the front face
|
||||
/// meets the wall's tangent line with it, and meet at a crease everywhere else. It is no smooth
|
||||
/// edge (it was refused as one, judged at that end alone); the wall's seam with the front face,
|
||||
/// tangent all along, still is.
|
||||
#[test]
|
||||
fn an_edge_tangent_at_one_end_only_is_not_smooth() {
|
||||
let mut s = Session::default();
|
||||
peg(&mut s);
|
||||
run(&mut s, "solid.fillet", json!({"edges": [[20, 0, 10]], "radius": 2}));
|
||||
let fillet = |s: &mut Session, at: [f64; 3]| s.execute("solid.fillet", &json!({"edges": [at], "radius": 1, "tangent_chain": false}));
|
||||
if let Err(e) = fillet(&mut s, [37.2641, 0.4827, 9.3025]) {
|
||||
assert!(!e.to_string().contains("is smooth"), "{e}");
|
||||
}
|
||||
let seam = fillet(&mut s, [40.0, 0.0, 4.0]).unwrap_err().to_string();
|
||||
assert!(seam.contains("is smooth: its faces meet tangentially"), "{seam}");
|
||||
}
|
||||
|
||||
/// The top outline of two overlapping cylinders whose concave edges were rounded: the walls of
|
||||
/// the rounds meet the cylinders along the rolling-ball contact lines, straight but not lines.
|
||||
#[test]
|
||||
|
||||
@@ -682,17 +682,28 @@ fn normal_reach(rec: &Faces) -> f64 {
|
||||
rec.rec.tolerance.max(1e-7) * 10.0
|
||||
}
|
||||
|
||||
/// Whether edge `e`'s two faces meet tangentially (within [`TANGENT_DEGREES`]) at its start: a
|
||||
/// smooth edge, with no corner to blend.
|
||||
/// Stations along an edge, as fractions of its interval, where [`smooth_join`] compares its
|
||||
/// faces' normals: its ends, and between them.
|
||||
const SMOOTH_STATIONS: [f64; 5] = [0.0, 0.25, 0.5, 0.75, 1.0];
|
||||
|
||||
/// Whether edge `e`'s two faces meet tangentially (within [`TANGENT_DEGREES`]) all along it: a
|
||||
/// smooth edge, with no corner to blend. An edge whose faces are tangent at one end only (a
|
||||
/// round running into a wall it is tangent to where the wall meets its face) is a crease
|
||||
/// everywhere else, which is not smooth.
|
||||
fn smooth_join(rec: &Faces, e: usize) -> bool {
|
||||
let cos = TANGENT_DEGREES.to_radians().cos();
|
||||
let accept = normal_reach(rec);
|
||||
let faces: Vec<u32> = rec.faces_of(e).into_iter().collect();
|
||||
let (Some(&a), Some(&b), Some((p, _))) = (faces.first(), faces.get(1), rec.ends(e)) else { return false };
|
||||
match (rec.normal_at(a as usize, p, accept), rec.normal_at(b as usize, p, accept)) {
|
||||
(Some(na), Some(nb)) => faces.len() == 2 && na.dot(nb) > cos,
|
||||
_ => false,
|
||||
}
|
||||
let ([a, b], Some(edge)) = (faces.as_slice(), rec.rec.edges.get(e)) else { return false };
|
||||
let Ok(curve) = CurveEval::new(edge.parameters()) else { return false };
|
||||
let [t0, t1] = edge.interval;
|
||||
SMOOTH_STATIONS.iter().all(|s| {
|
||||
let Some(p) = curve.point(t0 + (t1 - t0) * s) else { return false };
|
||||
match (rec.normal_at(*a as usize, p, accept), rec.normal_at(*b as usize, p, accept)) {
|
||||
(Some(na), Some(nb)) => na.dot(nb) > cos,
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// `seeds` (edges in the record's order, each once), then every other edge that continues one
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"sources": [
|
||||
"tangency-labs/solidcraft#896",
|
||||
"tangency-labs/solidcraft#1207"
|
||||
],
|
||||
"what": "after the round of a bar's top edge running into a cylinder, an r2 round of the edge where that round meets the cylinder's wall (tangent at its lower end only, a crease elsewhere) with its tangent chain (the concave arc of the top): it was refused as a smooth edge",
|
||||
"source": "invariants",
|
||||
"method": "a bar 40 x 16 x 10 whose far end is a half disc R8 about (40, 8), a disc R8 about the same centre sketched on its top (the top's outline projected) and extruded 10 up, joined: the bar's sides run tangent into the cylinder, V0 = 6400 + 960 pi; then the front round (the r2 round of the front top edge (y = 0, z = 10) runs into the cylinder's wall: removed = integral over y in [0, 2] of (2 - sqrt(4 - (2 - y)^2)) (40 - sqrt(64 - (8 - y)^2)) dy = 32.3064821881 (Simpson)); the second round fades to nothing at the edge's tangent end, no closed form: one valid body, adding material (concave), so V > V0 - removed = 9383.622465 and below V0",
|
||||
"body_count": 1
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"commands": [
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": "XY",
|
||||
"name": "Peg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.line",
|
||||
"params": {
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
40,
|
||||
0
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.arc.center_point",
|
||||
"params": {
|
||||
"center": [
|
||||
40,
|
||||
8
|
||||
],
|
||||
"start": [
|
||||
40,
|
||||
0
|
||||
],
|
||||
"end": [
|
||||
40,
|
||||
16
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.line",
|
||||
"params": {
|
||||
"points": [
|
||||
[
|
||||
40,
|
||||
16
|
||||
],
|
||||
[
|
||||
0,
|
||||
16
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.line",
|
||||
"params": {
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
16
|
||||
],
|
||||
[
|
||||
0,
|
||||
0
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "solid.extrude",
|
||||
"params": {
|
||||
"sketch": "Peg",
|
||||
"distance": 10,
|
||||
"body_name": "Peg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
0,
|
||||
0,
|
||||
10
|
||||
],
|
||||
"x_dir": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"y_dir": [
|
||||
0,
|
||||
1,
|
||||
0
|
||||
]
|
||||
},
|
||||
"name": "Top"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.project",
|
||||
"params": {
|
||||
"refs": [
|
||||
{
|
||||
"face": [
|
||||
2,
|
||||
8,
|
||||
10
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.circle.center",
|
||||
"params": {
|
||||
"center": [
|
||||
40,
|
||||
8
|
||||
],
|
||||
"radius": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "solid.extrude",
|
||||
"params": {
|
||||
"sketch": "Top",
|
||||
"distance": 10,
|
||||
"operation": "join",
|
||||
"targets": [
|
||||
"Peg"
|
||||
],
|
||||
"profiles": [
|
||||
{
|
||||
"point": [
|
||||
40,
|
||||
8
|
||||
]
|
||||
}
|
||||
],
|
||||
"exact_regions": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.fillet",
|
||||
"params": {
|
||||
"edges": [
|
||||
[
|
||||
20,
|
||||
0,
|
||||
10
|
||||
]
|
||||
],
|
||||
"radius": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.fillet",
|
||||
"params": {
|
||||
"edges": [
|
||||
[
|
||||
37.2641,
|
||||
0.4827,
|
||||
9.3025
|
||||
]
|
||||
],
|
||||
"radius": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -15,13 +15,13 @@
|
||||
1080-fillet-sloped-basin-1-mm-rim-r4-4-edges not supported yet: a convex round past its face is not yet built by subtraction
|
||||
1113-chamfer-arc-top-edge-full-wall-height-refused builds the chamfer as tall as the wall where it should be refused as too large
|
||||
1207-fillet-bar-top-edge-after-boss-arc-round-r0-5 SMLib's check refuses the round: internal coincident control points in a B-spline curve (an internal pole)
|
||||
1207-fillet-corner-after-round-running-into-cylinder-solver-residues SMLib's check refuses the round: its corner's surface of revolution is close to singular at its boundary (SmSurface test 5)
|
||||
1668-fillet-outer-top-edge-of-thin-wall-r7-past-inside-corner a slit along the hollow, 0.507 mm^3 off: 38458.389 is V0 less the round's whole section along 104, so the section's part over the hollow (0.005068 mm^2 along 100), which is not material, is taken anyway
|
||||
190-fillet-disc-rim-past-its-2mm-wall SMLib status 1001: a convex round past its face is not yet built by subtraction
|
||||
2852-fillet-outer-round-r30-past-inner-round-refused refused for another reason: SMLib's result is not a closed solid, not too large naming 25.24
|
||||
2893-chamfer-distance-and-angle-past-the-wall-refused refused for another reason: SMLib SM_ERR_INVALID_INPUT, not a check saying the angle runs past the wall
|
||||
2893-chamfer-two-distances-elliptical-rim SMLib status 1001 on the elliptical rim's chamfer
|
||||
2893-chamfer-two-distances-past-the-bottom-refused refused for another reason: SMLib status 1001, not too large
|
||||
1207-fillet-corner-after-round-running-into-cylinder-solver-residues SMLib's check refuses the round: its corner's surface of revolution is close to singular at its boundary (SmSurface test 5)
|
||||
4782-fillet-bar-edge-wider-than-both-faces SMLib status 1001: a convex round past both its faces is not yet built by subtraction
|
||||
5158-fillet-round-just-past-a-10mm-face SMLib status 1001: a convex round 0.01 past its face is not yet built by subtraction (10.0001-10.04 open)
|
||||
5835-chamfer-sloped-basin-1-mm-rim-0-5-4-edges SMLib status 1001 on the chamfers meeting at the rim's re-entrant corners
|
||||
@@ -33,6 +33,7 @@
|
||||
851-chamfer-knife-edge-tip-2-box-less-cylinder SMLib SM_ERR_INVALID_INPUT on the knife edge (opposite normals on its two faces)
|
||||
851-chamfer-knife-edge-tip-2-sketched SMLib SM_ERR_INVALID_INPUT on the knife edge (opposite normals on its two faces)
|
||||
851-chamfer-knife-edge-tip-5 SMLib SM_ERR_INVALID_INPUT on the knife edge (opposite normals on its two faces)
|
||||
896-fillet-edge-tangent-at-one-end-into-cylinder SMLib's check refuses the round that fades out at the edge's tangent end: internal poles, coincident control points, cusps
|
||||
9-fillet-corner-curvature-with-reduce-fit not supported yet: solid.fillet builds corner g2 with the exact fit only
|
||||
9-fillet-corner-curvature-with-taper-fit not supported yet: solid.fillet builds corner g2 with the exact fit only
|
||||
926-chamfer-top-edge-ending-on-boss-wall-above-it not supported yet: the chamfer is refused as larger than its faces where it ends on the boss's wall
|
||||
|
||||
Reference in New Issue
Block a user