Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 64 additions & 8 deletions main/opengeometry/src/brep/builder/boundary.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use super::brep_builder::{Builder, Use};
use crate::brep::error::GeometryError;
use crate::brep::frame::Frame3;
use crate::brep::geometry::CurveGeometry;
use crate::brep::frame::UV;
use crate::brep::geometry::{Curve, CurveGeometry, Surface, SurfaceGeometry};
use crate::brep::pcurve::PcurveGeometry;
use crate::brep::topology::{EdgeGeometry, Orientation};
use crate::math::{dot, scale, sub, Point3};
Expand All @@ -15,7 +16,7 @@ pub(crate) fn dimensions(values: &[f64]) -> Result<(), GeometryError> {
Ok(())
}

pub(crate) fn plane_boundary(
pub fn plane_boundary(
builder: &Builder,
frame: Frame3,
edge: u32,
Expand Down Expand Up @@ -50,16 +51,71 @@ pub(crate) fn plane_boundary(
Ok(boundary(edge, from, to, sense, pcurve))
}

pub(crate) fn uv_line(origin: [f64; 2], direction: [f64; 2]) -> PcurveGeometry {
PcurveGeometry::Line2 { origin, direction }
}
pub(crate) fn boundary(
const PROJECTION_SAMPLES: usize = 64;

pub fn projected_boundary(
builder: &Builder,
surface: &SurfaceGeometry,
edge: u32,
from: u32,
to: u32,
sense: Orientation,
pcurve: PcurveGeometry,
) -> Use {
hint: Option<UV>,
) -> Result<Use, GeometryError> {
let geometry = &builder
.brep
.topology
.edges
.get(edge as usize)
.ok_or_else(|| GeometryError::InvalidTopology("builder references missing edge".into()))?
.geometry;
let EdgeGeometry::Curve { curve, range } = *geometry else {
return Err(GeometryError::InvalidGeometry(
"collapsed projected boundary".into(),
));
};
let evaluator = builder.brep.geometry.curve(curve)?;
let at = |i: usize| range.lo + range.width() * i as f64 / PROJECTION_SAMPLES as f64;
let mut uv: Vec<UV> = Vec::with_capacity(PROJECTION_SAMPLES + 1);
for i in 0..=PROJECTION_SAMPLES {
let previous = uv.last().copied().or(hint);
uv.push(surface.project(evaluator.point_at(at(i))?, previous)?);
}
let (start, end) = (uv[0], uv[PROJECTION_SAMPLES]);
let width = range.width();
let rate: UV = std::array::from_fn(|axis| {
if width > 0.0 {
(end[axis] - start[axis]) / width
} else {
0.0
}
});
let periods = surface.charts()[0].periods;
for (i, sample) in uv.iter().enumerate() {
for axis in 0..2 {
let guess = start[axis] + rate[axis] * (at(i) - range.lo);
if periods[axis].is_some_and(|period| (guess - sample[axis]).abs() >= period / 4.0) {
return Err(GeometryError::UnsupportedGeometry(
"projected boundary winds unevenly around a periodic surface".into(),
));
}
}
}
let pcurve = PcurveGeometry::ProjectedCurve {
curve,
surface: builder.brep.geometry.surfaces.len() as u32,
chart: 0,
uv_hint: start,
uv_rate: rate,
parameter_origin: range.lo,
};
Ok(boundary(edge, from, to, sense, pcurve))
}

pub fn uv_line(origin: [f64; 2], direction: [f64; 2]) -> PcurveGeometry {
PcurveGeometry::Line2 { origin, direction }
}
pub fn boundary(edge: u32, from: u32, to: u32, sense: Orientation, pcurve: PcurveGeometry) -> Use {
Use {
edge,
from,
Expand Down
45 changes: 33 additions & 12 deletions main/opengeometry/src/brep/builder/brep_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,27 @@ use crate::brep::topology::{
};
use crate::math::{Interval, Point3};

pub(crate) struct Use {
pub struct Use {
pub(super) edge: u32,
pub(super) from: u32,
pub(super) to: u32,
pub(super) sense: Orientation,
pub(super) pcurve: PcurveGeometry,
}

pub(crate) struct Builder {
pub struct Builder {
pub(crate) brep: BrepEnvelope,
}
impl Builder {
pub(crate) fn new(id: String, accuracy: Accuracy) -> Result<Self, GeometryError> {
pub fn new(id: String, accuracy: Accuracy) -> Result<Self, GeometryError> {
Ok(Self {
brep: BrepEnvelope::new(id, accuracy)?,
})
}
pub(crate) fn vertex(&mut self, p: Point3) -> u32 {
pub fn brep(&self) -> &BrepEnvelope {
&self.brep
}
pub fn vertex(&mut self, p: Point3) -> u32 {
let id = self.brep.topology.vertices.len() as u32;
self.brep.topology.vertices.push(Vertex {
id,
Expand All @@ -35,7 +38,7 @@ impl Builder {
});
id
}
pub(crate) fn edge(&mut self, curve: CurveGeometry, range: Interval, seam: bool) -> u32 {
pub fn edge(&mut self, curve: CurveGeometry, range: Interval, seam: bool) -> u32 {
let curve_id = self.brep.geometry.curves.len() as u32;
self.brep.geometry.curves.push(curve);
self.edge_geometry(
Expand Down Expand Up @@ -129,24 +132,29 @@ impl Builder {
});
Ok(loop_id)
}
pub(crate) fn face(
pub fn face(
&mut self,
key: &str,
surface: SurfaceGeometry,
bounds: [[f64; 2]; 2],
uses: Vec<Use>,
) -> Result<(), GeometryError> {
) -> Result<u32, GeometryError> {
self.face_with_holes(key, surface, bounds, uses, Vec::new())
}
pub(crate) fn face_with_holes(
pub fn face_with_holes(
&mut self,
key: &str,
surface: SurfaceGeometry,
bounds: [[f64; 2]; 2],
outer_uses: Vec<Use>,
hole_uses: Vec<Vec<Use>>,
) -> Result<(), GeometryError> {
mut outer_uses: Vec<Use>,
mut hole_uses: Vec<Vec<Use>>,
) -> Result<u32, GeometryError> {
let surface_id = self.brep.geometry.surfaces.len() as u32;
for u in outer_uses.iter_mut().chain(hole_uses.iter_mut().flatten()) {
if let PcurveGeometry::ProjectedCurve { surface, .. } = &mut u.pcurve {
*surface = surface_id;
}
}
self.brep.geometry.surfaces.push(surface);
let face = self.brep.topology.faces.len() as u32;
let outer = self.face_loop(face, false, outer_uses)?;
Expand Down Expand Up @@ -180,9 +188,21 @@ impl Builder {
reversed: false,
},
});
Ok(face)
}
pub fn set_face_sense(&mut self, face: u32, sense: Orientation) -> Result<(), GeometryError> {
self.brep
.topology
.faces
.get_mut(face as usize)
.ok_or(GeometryError::MissingReference {
kind: "face".into(),
index: face,
})?
.sense = sense;
Ok(())
}
pub(crate) fn finish_solid(mut self) -> Result<BrepEnvelope, GeometryError> {
pub fn finish_solid(mut self) -> Result<BrepEnvelope, GeometryError> {
let faces = (0..self.brep.topology.faces.len() as u32).collect();
self.brep.topology.shells.push(Shell {
id: 0,
Expand All @@ -193,6 +213,7 @@ impl Builder {
outer_shell: 0,
cavity_shells: Vec::new(),
});
self.brep.orient_loops()?;
self.brep.validate()?;
Ok(self.brep)
}
Expand Down
Loading