Another project
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 9.9 kB View raw
1use std::collections::HashSet; 2 3use bone_types::{EdgeLabel, FaceLabel, SketchEntityId, SolidKey, VertexLabel}; 4use ron::extensions::Extensions; 5use serde::{Deserialize, Serialize}; 6use truck_modeling::{EdgeID, Face, FaceID, Shell, Solid, VertexID}; 7 8use super::build::{SolidLabeling, assemble}; 9use super::edges::EdgeCurve3; 10use super::{BrepError, BrepSolid, LabelKind}; 11 12const GRID_DECIMALS: usize = 6; 13 14fn blob_options() -> ron::Options { 15 ron::Options::default().with_default_extension(Extensions::UNWRAP_NEWTYPES) 16} 17 18#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 19#[serde(deny_unknown_fields)] 20pub struct EdgeReattach { 21 pub label: EdgeLabel, 22 pub curve: Option<EdgeCurve3>, 23} 24 25#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 26#[serde(deny_unknown_fields)] 27pub struct BrepReattach { 28 faces: Vec<FaceLabel>, 29 edges: Vec<EdgeReattach>, 30 vertices: Vec<VertexLabel>, 31 closed_curves: Vec<SketchEntityId>, 32 order: SolidKey, 33} 34 35impl BrepReattach { 36 #[must_use] 37 pub fn faces(&self) -> &[FaceLabel] { 38 &self.faces 39 } 40 41 #[must_use] 42 pub fn edges(&self) -> &[EdgeReattach] { 43 &self.edges 44 } 45 46 #[must_use] 47 pub fn vertices(&self) -> &[VertexLabel] { 48 &self.vertices 49 } 50} 51 52pub(super) fn ordered_faces(solid: &Solid) -> Vec<FaceID> { 53 solid 54 .boundaries() 55 .iter() 56 .flat_map(Shell::face_iter) 57 .map(Face::id) 58 .collect() 59} 60 61pub(super) fn ordered_edges(solid: &Solid) -> Vec<EdgeID> { 62 let mut seen = HashSet::new(); 63 solid 64 .edge_iter() 65 .map(|edge| edge.id()) 66 .filter(|id| seen.insert(*id)) 67 .collect() 68} 69 70pub(super) fn ordered_vertices(solid: &Solid) -> Vec<VertexID> { 71 let mut seen = HashSet::new(); 72 solid 73 .vertex_iter() 74 .map(|vertex| vertex.id()) 75 .filter(|id| seen.insert(*id)) 76 .collect() 77} 78 79pub(crate) fn capture(solid: &Solid, labeling: &SolidLabeling) -> Result<BrepReattach, BrepError> { 80 let faces = ordered_faces(solid) 81 .into_iter() 82 .map(|id| { 83 labeling 84 .faces 85 .get(&id) 86 .copied() 87 .ok_or(BrepError::MissingLabel { 88 kind: LabelKind::Face, 89 }) 90 }) 91 .collect::<Result<Vec<_>, _>>()?; 92 let edges = ordered_edges(solid) 93 .into_iter() 94 .map(|id| { 95 let label = labeling 96 .edges 97 .get(&id) 98 .copied() 99 .ok_or(BrepError::MissingLabel { 100 kind: LabelKind::Edge, 101 })?; 102 Ok(EdgeReattach { 103 label, 104 curve: labeling.edge_curves.get(&id).cloned(), 105 }) 106 }) 107 .collect::<Result<Vec<_>, BrepError>>()?; 108 let vertices = ordered_vertices(solid) 109 .into_iter() 110 .map(|id| { 111 labeling 112 .vertices 113 .get(&id) 114 .copied() 115 .ok_or(BrepError::MissingLabel { 116 kind: LabelKind::Vertex, 117 }) 118 }) 119 .collect::<Result<Vec<_>, _>>()?; 120 let mut closed_curves: Vec<SketchEntityId> = labeling.closed_curves.iter().copied().collect(); 121 closed_curves.sort_unstable(); 122 Ok(BrepReattach { 123 faces, 124 edges, 125 vertices, 126 closed_curves, 127 order: order_key(solid), 128 }) 129} 130 131pub(super) fn to_labeling( 132 solid: &Solid, 133 reattach: &BrepReattach, 134) -> Result<SolidLabeling, BrepError> { 135 if order_key(solid) != reattach.order { 136 return Err(BrepError::ReattachOrder); 137 } 138 let face_ids = ordered_faces(solid); 139 let edge_ids = ordered_edges(solid); 140 let vertex_ids = ordered_vertices(solid); 141 expect_count(LabelKind::Face, face_ids.len(), reattach.faces.len())?; 142 expect_count(LabelKind::Edge, edge_ids.len(), reattach.edges.len())?; 143 expect_count(LabelKind::Vertex, vertex_ids.len(), reattach.vertices.len())?; 144 145 let faces = face_ids 146 .iter() 147 .zip(&reattach.faces) 148 .map(|(id, label)| (*id, *label)) 149 .collect(); 150 let edges = edge_ids 151 .iter() 152 .zip(&reattach.edges) 153 .map(|(id, edge)| (*id, edge.label)) 154 .collect(); 155 let edge_curves = edge_ids 156 .iter() 157 .zip(&reattach.edges) 158 .filter_map(|(id, edge)| edge.curve.clone().map(|curve| (*id, curve))) 159 .collect(); 160 let vertices = vertex_ids 161 .iter() 162 .zip(&reattach.vertices) 163 .map(|(id, label)| (*id, *label)) 164 .collect(); 165 let closed_curves = reattach.closed_curves.iter().copied().collect(); 166 Ok(SolidLabeling { 167 faces, 168 edges, 169 vertices, 170 closed_curves, 171 edge_curves, 172 }) 173} 174 175fn expect_count(kind: LabelKind, found: usize, expected: usize) -> Result<(), BrepError> { 176 if found == expected { 177 Ok(()) 178 } else { 179 Err(BrepError::ReattachMismatch { 180 kind, 181 found, 182 expected, 183 }) 184 } 185} 186 187fn quantize(value: f64) -> String { 188 let prec = GRID_DECIMALS; 189 let text = format!("{value:.prec$}"); 190 if text.bytes().all(|byte| matches!(byte, b'-' | b'0' | b'.')) { 191 format!("{:.prec$}", 0.0) 192 } else { 193 text 194 } 195} 196 197fn point_token(point: truck_modeling::Point3) -> String { 198 format!( 199 "{},{},{}", 200 quantize(point.x), 201 quantize(point.y), 202 quantize(point.z) 203 ) 204} 205 206fn edge_token(front: truck_modeling::Point3, back: truck_modeling::Point3) -> String { 207 let mut ends = [point_token(front), point_token(back)]; 208 ends.sort_unstable(); 209 ends.join("|") 210} 211 212fn digest_to_key(canonical: &str) -> SolidKey { 213 let digest = blake3::hash(canonical.as_bytes()); 214 let mut key = [0u8; 16]; 215 key.copy_from_slice(&digest.as_bytes()[..16]); 216 SolidKey::from_bytes(key) 217} 218 219pub(super) fn face_centroid(face: &Face) -> truck_modeling::Point3 { 220 let (x, y, z, count) = face 221 .boundaries() 222 .iter() 223 .flat_map(|wire| wire.vertex_iter().map(|vertex| vertex.point())) 224 .fold((0.0, 0.0, 0.0, 0.0_f64), |(x, y, z, count), point| { 225 (x + point.x, y + point.y, z + point.z, count + 1.0) 226 }); 227 if count > 0.0 { 228 truck_modeling::Point3::new(x / count, y / count, z / count) 229 } else { 230 truck_modeling::Point3::new(0.0, 0.0, 0.0) 231 } 232} 233 234fn face_token(face: &Face) -> String { 235 let mut edges: Vec<String> = face 236 .boundaries() 237 .iter() 238 .flat_map(|wire| wire.edge_iter()) 239 .map(|edge| edge_token(edge.front().point(), edge.back().point())) 240 .collect(); 241 edges.sort_unstable(); 242 edges.dedup(); 243 format!("{}|{}", point_token(face_centroid(face)), edges.join(",")) 244} 245 246pub(super) fn content_key(solid: &Solid) -> SolidKey { 247 let mut vertices: Vec<String> = solid 248 .vertex_iter() 249 .map(|vertex| point_token(vertex.point())) 250 .collect(); 251 vertices.sort_unstable(); 252 vertices.dedup(); 253 254 let mut edges: Vec<String> = solid 255 .edge_iter() 256 .map(|edge| edge_token(edge.front().point(), edge.back().point())) 257 .collect(); 258 edges.sort_unstable(); 259 edges.dedup(); 260 261 digest_to_key(&format!( 262 "t:{}/{}/{}\nv:{}\n{}\ne:{}\n{}\n", 263 ordered_faces(solid).len(), 264 ordered_edges(solid).len(), 265 ordered_vertices(solid).len(), 266 vertices.len(), 267 vertices.join("\n"), 268 edges.len(), 269 edges.join("\n") 270 )) 271} 272 273pub(super) fn order_key(solid: &Solid) -> SolidKey { 274 let mut seen_vertices = HashSet::new(); 275 let vertices: Vec<String> = solid 276 .vertex_iter() 277 .filter(|vertex| seen_vertices.insert(vertex.id())) 278 .map(|vertex| point_token(vertex.point())) 279 .collect(); 280 let mut seen_edges = HashSet::new(); 281 let edges: Vec<String> = solid 282 .edge_iter() 283 .filter(|edge| seen_edges.insert(edge.id())) 284 .map(|edge| edge_token(edge.front().point(), edge.back().point())) 285 .collect(); 286 let faces: Vec<String> = solid 287 .boundaries() 288 .iter() 289 .flat_map(Shell::face_iter) 290 .map(face_token) 291 .collect(); 292 digest_to_key(&format!( 293 "ov:{}\noe:{}\nof:{}\n", 294 vertices.join("|"), 295 edges.join("|"), 296 faces.join("|") 297 )) 298} 299 300impl BrepSolid { 301 pub fn to_blob(&self) -> Result<Vec<u8>, BrepError> { 302 blob_options() 303 .to_string(self.arena.solid()) 304 .map(String::into_bytes) 305 .map_err(|_| BrepError::BlobSerialize) 306 } 307 308 #[must_use] 309 pub fn reattach_data(&self) -> &BrepReattach { 310 &self.reattach 311 } 312 313 #[must_use] 314 pub fn content_key(&self) -> SolidKey { 315 content_key(self.arena.solid()) 316 } 317 318 pub fn from_blob(bytes: &[u8], reattach: &BrepReattach) -> Result<BrepSolid, BrepError> { 319 let text = core::str::from_utf8(bytes).map_err(|_| BrepError::BlobParse)?; 320 let solid: Solid = blob_options() 321 .from_str(text) 322 .map_err(|_| BrepError::BlobParse)?; 323 let labeling = to_labeling(&solid, reattach)?; 324 assemble(solid, &labeling) 325 } 326} 327 328#[cfg(test)] 329mod tests { 330 use super::quantize; 331 332 #[test] 333 fn quantize_collapses_sub_grid_magnitudes_to_unsigned_zero() { 334 assert_eq!(quantize(0.0), "0.000000"); 335 assert_eq!(quantize(-0.0), "0.000000"); 336 assert_eq!(quantize(4.0e-7), "0.000000"); 337 assert_eq!(quantize(-4.0e-7), "0.000000"); 338 assert_eq!(quantize(-1.0e-12), "0.000000"); 339 assert_eq!(quantize(4.0e-7), quantize(-4.0e-7)); 340 } 341 342 #[test] 343 fn quantize_keeps_signed_values_above_the_grid() { 344 assert_eq!(quantize(-1.0e-6), "-0.000001"); 345 assert_eq!(quantize(1.0e-6), "0.000001"); 346 assert_eq!(quantize(-2.5), "-2.500000"); 347 } 348}