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 that need not share an edge.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.