Blends and chamfers
Everything here is in ogeom::fillet.
Rounding one edge
let block = ogeom::algo::make_box(&mut model, Frame::WORLD, (40.0, 30.0, 12.0), T)
.unwrap()
.shape;
// Pick the top edge along y = 0 and round it at radius 2.
let edge = edge_near(&model, &block, Point::new(20.0, 0.0, 12.0));
let rounded = ogeom::fillet::fillet_edge(&mut model, &block, &edge, 2.0, T)
.unwrap()
.shape;
// A fillet removes the square corner and leaves the quarter cylinder:
// ΔV = (1 − π/4)·r²·length, exactly.
let volume = ogeom::algo::volume_properties(&model, &rounded, Deflection::default(), T)
.unwrap()
.mass;
let exact = 40.0 * 30.0 * 12.0 - (1.0 - core::f64::consts::FRAC_PI_4) * 4.0 * 40.0;
assert!((volume - exact).abs() / exact < 0.01);
| Function | Result |
|---|---|
fillet_edge | Constant-radius round. |
fillet_edge_variable | Round with a radius law along the edge. |
chamfer_edge | Symmetric flat bevel. |
chamfer_edge_distances | Asymmetric bevel (two distances). |
chamfer_edge_angle | Bevel from a distance and an angle. |
Fillets are not limited to planes. Between cylinders, cones, spheres, tori and fitted patches, the rolling ball is marched numerically instead of solved in closed form. The blend surface is fitted through its cross-section arcs and merged into the solid the same way.
Several edges at once
Edges filleted or chamfered in separate calls each stop flush against whatever they end on. Passed in one call, they join:
let block = ogeom::algo::make_box(&mut model, Frame::WORLD, (20.0, 20.0, 10.0), T)
.unwrap()
.shape;
let top = [
Point::new(10.0, 0.0, 10.0),
Point::new(20.0, 10.0, 10.0),
Point::new(10.0, 20.0, 10.0),
Point::new(0.0, 10.0, 10.0),
]
.map(|at| edge_near(&model, &block, at));
// Four bevels as one operation mitre where they meet: each corner loses
// the overlap of two prisms, a third of the cube of the distance.
let bevelled = ogeom::fillet::chamfer_edges(&mut model, &block, &top, 1.0, T)
.unwrap()
.shape;
let volume = ogeom::algo::volume_properties(&model, &bevelled, Deflection::default(), T)
.unwrap()
.mass;
assert!((volume - (4000.0 - 4.0 * 10.0 + 4.0 / 3.0)).abs() < 1e-2);
// Three fillets meeting at a corner close it with the ball's own patch,
// an octant of a sphere, instead of leaving the bands' caps standing.
let corner = [
Point::new(10.0, 20.0, 10.0),
Point::new(20.0, 10.0, 10.0),
Point::new(20.0, 20.0, 5.0),
]
.map(|at| edge_near(&model, &block, at));
let rounded = ogeom::fillet::fillet_edges(&mut model, &block, &corner, 2.0, T)
.unwrap()
.shape;
let spheres = ogeom::topo::explore_unique(&model, &rounded, ogeom::topo::ShapeType::Face)
.unwrap()
.iter()
.filter(|face| {
let data = model.node(face).unwrap().data().as_face().unwrap();
matches!(
model.geometry().surface(data.surface),
Some(ogeom::geom::SurfaceGeometry::Sphere(_))
)
})
.count();
assert_eq!(spheres, 1);
fillet_edgestrims neighbouring bands against each other, joins tangent chains without a seam, and fills every vertex where three or more filleted edges meet with a rolling-ball corner patch.round_vertexis that corner tool on its own:- where one ball can touch all the faces, it produces a sphere patch;
- where no single ball can (for example the apex of a rectangular pyramid), it produces the exact envelope of the rolling ball: spheres joined by cylinders.
chamfer_edgesandchamfer_edges_withbevel a set of edges in one operation. Every wedge is built on the solid as it was before the call, so the bevels mitre where they meet.
Blends between faces without a shared edge
blend_facesrolls a constant-radius ball between two faces of one solid that need not share an edge. Planes, and curved faces whose surfaces share a direction or an axis, get an exact cylinder or torus. Curved faces that meet along edges of the solid are blended along those edges, marched where the seat has no closed form. Curved faces that share no edge and no direction or axis (a B-spline face, a cylinder at a slant to a plane) are marched round where their surfaces cross. Where the ball leaves one face, the round ends in a flat cap in the ball’s section.march_blendis the underlying marcher. It traces the contact circle and reports why it stopped (BlendStop). Use it to blend up to an obstruction on purpose.
Tangency checks
A blend must end tangent to the faces it joins. analyse_blend measures
the achieved contact and returns it as BlendContact. Fillets report their
own tangency deviation, and a blend that cannot reach tangency within
tolerance is refused.
Each BlendContact carries the gap (G0), the angle between the normals
(G1) and the step in normal curvature square to the edge (G2), all sampled
at stations along the shared edge. face_curvature_samples gives the
principal curvatures on a grid over one face, for curvature maps and zebra
stripes.