Another project
0

Configure Feed

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

at main 6.4 kB View raw
1use std::collections::BTreeSet; 2 3use bone_kernel::{ 4 BrepFace, ExtrudeDirection, ExtrudeEndCondition, ExtrudeFeature, ExtrudeSense, MergeResult, 5}; 6use bone_types::{ 7 ExtrudeId, FaceFingerprint, FaceLabel, FaceRef, FaceRole, FeatureId, Length, Plane3, Point2, 8 Point3, PositiveLength, Resolution, RollbackMarker, SketchDimensionId, SketchEntityId, 9 SketchId, SketchPlaneBasis, Tolerance, UnitVec3, millimeter, 10}; 11 12use crate::Sketch; 13use crate::document::Document; 14use crate::matcher::resolve_face; 15use crate::recompute::{EvaluatedModel, RecomputeScope}; 16use crate::sketch::{DimensionKind, EditOutcome, SketchDimension, SketchEdit, SketchEntity}; 17 18pub(crate) fn xy_basis() -> SketchPlaneBasis { 19 let Ok(basis) = SketchPlaneBasis::new( 20 Point3::origin(), 21 UnitVec3::x_axis(), 22 UnitVec3::y_axis(), 23 Tolerance::new(1e-9), 24 ) else { 25 panic!("xy plane basis is orthogonal"); 26 }; 27 basis 28} 29 30pub(crate) fn add_point(sketch: Sketch, x: f64, y: f64) -> (Sketch, SketchEntityId) { 31 let Ok((next, EditOutcome::Entity(id))) = sketch.apply(SketchEdit::AddEntity( 32 SketchEntity::point(Point2::from_mm(x, y)), 33 )) else { 34 panic!("add point"); 35 }; 36 (next, id) 37} 38 39fn circle_entities(radius_mm: f64) -> (Sketch, SketchEntityId) { 40 let (sketch, center) = add_point(Sketch::new(xy_basis()), 0.0, 0.0); 41 let Ok((next, EditOutcome::Entity(circle))) = sketch.apply(SketchEdit::AddEntity( 42 SketchEntity::circle(center, Length::new::<millimeter>(radius_mm), false), 43 )) else { 44 panic!("add circle"); 45 }; 46 (next, circle) 47} 48 49pub(crate) fn circle_sketch(radius_mm: f64) -> Sketch { 50 circle_entities(radius_mm).0 51} 52 53pub(crate) fn circle_with_radius_dim( 54 radius_mm: f64, 55) -> (Sketch, SketchEntityId, SketchDimensionId) { 56 let (sketch, circle) = circle_entities(radius_mm); 57 let Ok((next, EditOutcome::Dimension(dim))) = 58 sketch.apply(SketchEdit::AddDimension(SketchDimension::Radius { 59 target: circle, 60 value: Length::new::<millimeter>(radius_mm), 61 kind: DimensionKind::Driving, 62 })) 63 else { 64 panic!("add radius dimension"); 65 }; 66 (next, circle, dim) 67} 68 69pub(crate) fn open_chain_sketch() -> Sketch { 70 let add_line = |sketch: Sketch, from: SketchEntityId, to: SketchEntityId| { 71 let Ok((next, _)) = 72 sketch.apply(SketchEdit::AddEntity(SketchEntity::line(from, to, false))) 73 else { 74 panic!("add line"); 75 }; 76 next 77 }; 78 let (sketch, a) = add_point(Sketch::new(xy_basis()), 0.0, 0.0); 79 let (sketch, b) = add_point(sketch, 10.0, 0.0); 80 let (sketch, c) = add_point(sketch, 10.0, 5.0); 81 add_line(add_line(sketch, a, b), b, c) 82} 83 84pub(crate) fn blind_extrude(sketch: SketchId, depth_mm: f64) -> ExtrudeFeature { 85 let Ok(depth) = PositiveLength::new(Length::new::<millimeter>(depth_mm)) else { 86 panic!("{depth_mm} mm is a positive depth"); 87 }; 88 ExtrudeFeature { 89 sketch, 90 direction: ExtrudeDirection::Normal { 91 sense: ExtrudeSense::Forward, 92 }, 93 end_condition: ExtrudeEndCondition::Blind { depth }, 94 draft: None, 95 thin_wall: None, 96 merge_result: MergeResult::Separate, 97 } 98} 99 100pub(crate) struct Chain { 101 pub(crate) sketch: FeatureId, 102 pub(crate) extrude: FeatureId, 103 pub(crate) sketch_id: SketchId, 104 pub(crate) extrude_id: ExtrudeId, 105} 106 107pub(crate) fn chain_handles( 108 document: &Document, 109 sketch_id: SketchId, 110 extrude_id: ExtrudeId, 111) -> Chain { 112 let tree = document.feature_tree(); 113 let (Some(sketch), Some(extrude)) = ( 114 tree.feature_of_sketch(sketch_id), 115 tree.feature_of_extrude(extrude_id), 116 ) else { 117 panic!("inserted chain resolves to feature ids"); 118 }; 119 Chain { 120 sketch, 121 extrude, 122 sketch_id, 123 extrude_id, 124 } 125} 126 127pub(crate) fn push_chain( 128 document: &mut Document, 129 label: &str, 130 radius_mm: f64, 131 depth_mm: f64, 132) -> Chain { 133 let sketch_id = document.allocate_sketch(); 134 document.insert_sketch(sketch_id, label.to_owned(), circle_sketch(radius_mm)); 135 let extrude_id = document.commit_extrude(blind_extrude(sketch_id, depth_mm)); 136 chain_handles(document, sketch_id, extrude_id) 137} 138 139pub(crate) fn placeholder_fingerprint() -> FaceFingerprint { 140 FaceFingerprint { 141 plane: Plane3::new_unchecked(Point3::origin(), UnitVec3::x_axis(), UnitVec3::y_axis()), 142 centroid: Point3::origin(), 143 } 144} 145 146fn face_label_of( 147 model: &EvaluatedModel, 148 body: FeatureId, 149 matches_role: impl Fn(FaceRole) -> bool, 150) -> FaceLabel { 151 let Some(solid) = model.body(body) else { 152 panic!("the body is built"); 153 }; 154 let Some(label) = solid 155 .iter_faces() 156 .map(BrepFace::label) 157 .find(|label| matches_role(label.role)) 158 else { 159 panic!("the body carries the requested face role"); 160 }; 161 label 162} 163 164pub(crate) fn end_cap_ref(model: &EvaluatedModel, body: FeatureId) -> FaceRef { 165 let label = face_label_of(model, body, |role| matches!(role, FaceRole::EndCap)); 166 let Resolution::Resolved(resolved) = resolve_face(model, label, placeholder_fingerprint()) 167 else { 168 panic!("the end cap resolves on the model that built it"); 169 }; 170 let Some(face_ref) = model.face_ref(resolved) else { 171 panic!("a planar cap yields a face reference"); 172 }; 173 face_ref 174} 175 176pub(crate) fn side_face_label(model: &EvaluatedModel, body: FeatureId) -> FaceLabel { 177 face_label_of(model, body, |role| matches!(role, FaceRole::Side { .. })) 178} 179 180pub(crate) fn push_face_bound_chain( 181 document: &mut Document, 182 label: &str, 183 face: FaceRef, 184 radius_mm: f64, 185 depth_mm: f64, 186) -> Chain { 187 let sketch_id = document.allocate_sketch(); 188 document.insert_sketch(sketch_id, label.to_owned(), circle_sketch(radius_mm)); 189 let Ok(()) = document.bind_sketch_to_face(sketch_id, face) else { 190 panic!("the face binding is acyclic"); 191 }; 192 let extrude_id = document.commit_extrude(blind_extrude(sketch_id, depth_mm)); 193 chain_handles(document, sketch_id, extrude_id) 194} 195 196pub(crate) fn full_model(document: &Document) -> EvaluatedModel { 197 let mut model = EvaluatedModel::new(); 198 model.recompute( 199 document, 200 &BTreeSet::new(), 201 RollbackMarker::AtEnd, 202 RecomputeScope::Full, 203 ); 204 model 205}