brep_kernel/meshing/watertight_tessellation/
stride_encode.rs1use super::*;
2
3pub fn tessellate_brep_watertight_face_stride(
23 solid: &BrepSolid,
24 chord_tolerance: f64,
25 stride: usize,
26 offset: usize,
27) -> Result<Mesh, String> {
28 if !(chord_tolerance > 0.0) || !chord_tolerance.is_finite() {
29 return Err("tessellate_brep_watertight: chord tolerance must be positive".into());
30 }
31 let samples = sample_all_edges(solid, chord_tolerance)?;
32 tessellate_faces_stride(solid, &samples, chord_tolerance, stride, offset)
33}
34
35pub fn tessellate_brep_watertight_face_stride_with_samples(
42 solid: &BrepSolid,
43 chord_tolerance: f64,
44 stride: usize,
45 offset: usize,
46 encoded_samples: &[f64],
47) -> Result<Mesh, String> {
48 if !(chord_tolerance > 0.0) || !chord_tolerance.is_finite() {
49 return Err("tessellate_brep_watertight: chord tolerance must be positive".into());
50 }
51 let samples = decode_edge_samples(encoded_samples)?;
52 tessellate_faces_stride(solid, &samples, chord_tolerance, stride, offset)
53}
54
55#[cfg(feature = "parallel")]
67pub(super) struct SyncFace<'a>(u32, &'a FaceRecord);
68#[cfg(feature = "parallel")]
69unsafe impl Sync for SyncFace<'_> {}
70
71pub(super) fn tessellate_faces_stride(
81 solid: &BrepSolid,
82 samples: &HashMap<u64, EdgeSamples>,
83 chord_tolerance: f64,
84 stride: usize,
85 offset: usize,
86) -> Result<Mesh, String> {
87 let stride = stride.max(1);
88 let offset = offset % stride;
89 let mut work: Vec<(u32, &FaceRecord)> = Vec::new();
90 let mut sequential_face_id = 0u32;
91 for shell in &solid.shells {
92 for face in &shell.faces {
93 if (sequential_face_id as usize) % stride == offset {
94 work.push((sequential_face_id, face));
95 }
96 sequential_face_id += 1;
97 }
98 }
99 let mesh_one = |face_id: u32, face: &FaceRecord| -> Result<Mesh, String> {
100 let mut fragment = Mesh::default();
101 tessellate_face_watertight(face, samples, chord_tolerance, face_id, &mut fragment)?;
102 Ok(fragment)
103 };
104 #[cfg(feature = "parallel")]
105 let fragments = {
106 use rayon::prelude::*;
107 let sync_work: Vec<SyncFace> = work.iter().map(|&(id, face)| SyncFace(id, face)).collect();
108 sync_work
109 .par_iter()
110 .map(|face| mesh_one(face.0, face.1))
111 .collect::<Result<Vec<Mesh>, String>>()?
112 };
113 #[cfg(not(feature = "parallel"))]
114 let fragments = work
115 .iter()
116 .map(|&(id, face)| mesh_one(id, face))
117 .collect::<Result<Vec<Mesh>, String>>()?;
118 Ok(concatenate_meshes(fragments))
119}
120
121pub(super) fn concatenate_meshes(fragments: Vec<Mesh>) -> Mesh {
124 let mut mesh = Mesh::default();
125 for fragment in fragments {
126 let base = (mesh.positions.len() / 3) as u32;
127 mesh.positions.extend_from_slice(&fragment.positions);
128 mesh.normals.extend_from_slice(&fragment.normals);
129 mesh.indices
130 .extend(fragment.indices.iter().map(|index| index + base));
131 mesh.face_ids.extend_from_slice(&fragment.face_ids);
132 }
133 mesh
134}
135
136pub fn sample_edge_polylines(
142 solid: &BrepSolid,
143 chord_tolerance: f64,
144) -> Result<Vec<(u64, Vec<Vec3>)>, String> {
145 if !(chord_tolerance > 0.0) || !chord_tolerance.is_finite() {
146 return Err("tessellate_brep_watertight: chord tolerance must be positive".into());
147 }
148 let samples = sample_all_edges(solid, chord_tolerance)?;
149 let degenerate: std::collections::HashSet<u64> = solid
150 .edges
151 .iter()
152 .filter(|edge| edge.degenerate)
153 .map(|edge| edge.id)
154 .collect();
155 let mut out: Vec<(u64, Vec<Vec3>)> = samples
156 .into_iter()
157 .filter(|(id, samples)| !degenerate.contains(id) && samples.positions.len() >= 2)
158 .map(|(id, samples)| (id, samples.positions))
159 .collect();
160 out.sort_by_key(|(id, _)| *id);
161 Ok(out)
162}
163
164pub fn sample_edges_encoded(solid: &BrepSolid, chord_tolerance: f64) -> Result<Vec<f64>, String> {
170 if !(chord_tolerance > 0.0) || !chord_tolerance.is_finite() {
171 return Err("tessellate_brep_watertight: chord tolerance must be positive".into());
172 }
173 let samples = sample_all_edges(solid, chord_tolerance)?;
174 Ok(encode_edge_samples(&samples))
175}
176
177pub(super) fn encode_edge_samples(samples: &HashMap<u64, EdgeSamples>) -> Vec<f64> {
178 let mut out = Vec::new();
179 out.push(samples.len() as f64);
180 for (edge_id, edge) in samples {
181 out.push(*edge_id as f64);
182 out.push(edge.fractions.len() as f64);
183 out.push(edge.worst_sag);
184 out.extend_from_slice(&edge.fractions);
185 for position in &edge.positions {
186 out.push(position.x);
187 out.push(position.y);
188 out.push(position.z);
189 }
190 }
191 out
192}
193
194pub(super) fn decode_edge_samples(data: &[f64]) -> Result<HashMap<u64, EdgeSamples>, String> {
195 let mut cursor = 0usize;
196 let mut take = |count: usize| -> Result<&[f64], String> {
197 let end = cursor
198 .checked_add(count)
199 .filter(|end| *end <= data.len())
200 .ok_or("watertight tessellation: truncated edge-sample buffer")?;
201 let slice = &data[cursor..end];
202 cursor = end;
203 Ok(slice)
204 };
205 let edge_count = take(1)?[0] as usize;
206 let mut samples = HashMap::with_capacity(edge_count);
207 for _ in 0..edge_count {
208 let header = take(3)?;
209 let edge_id = header[0] as u64;
210 let sample_count = header[1] as usize;
211 let worst_sag = header[2];
212 let fractions = take(sample_count)?.to_vec();
213 let position_values = take(sample_count * 3)?;
214 let positions = position_values
215 .chunks_exact(3)
216 .map(|chunk| Vec3::new(chunk[0], chunk[1], chunk[2]))
217 .collect();
218 samples.insert(
219 edge_id,
220 EdgeSamples {
221 fractions,
222 positions,
223 worst_sag,
224 },
225 );
226 }
227 Ok(samples)
228}