1#![allow(clippy::must_use_candidate)]
15
16use crate::generated::model as m;
17use crate::scene::geometry::{Curve, Edge, Face, Point, Solid};
18use crate::scene::{Ctx, Scene};
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum DimensionKind {
23 Size,
25 Location,
27 AngularSize,
29 AngularLocation,
31}
32
33#[derive(Clone, Copy)]
34enum DimImpl {
35 Size(m::DimensionalSizeId),
36 Location(m::DimensionalLocationId),
37 AngularSize(m::AngularSizeId),
38 AngularLocation(m::AngularLocationId),
39}
40
41#[derive(Clone, Copy)]
44pub struct Dimension<'m> {
45 cx: Ctx<'m>,
46 which: DimImpl,
47}
48
49impl Scene<'_> {
50 pub fn dimensions(&self) -> impl Iterator<Item = Dimension<'_>> + '_ {
54 all_dimensions(self.ctx()).into_iter()
55 }
56}
57
58fn all_dimensions(cx: Ctx<'_>) -> Vec<Dimension<'_>> {
61 let mut out = Vec::new();
62 for i in 0..cx.model.dimensional_size_arena.items.len() {
63 out.push(Dimension {
64 cx,
65 which: DimImpl::Size(m::DimensionalSizeId(i)),
66 });
67 }
68 for i in 0..cx.model.dimensional_location_arena.items.len() {
69 out.push(Dimension {
70 cx,
71 which: DimImpl::Location(m::DimensionalLocationId(i)),
72 });
73 }
74 for i in 0..cx.model.angular_size_arena.items.len() {
75 out.push(Dimension {
76 cx,
77 which: DimImpl::AngularSize(m::AngularSizeId(i)),
78 });
79 }
80 for i in 0..cx.model.angular_location_arena.items.len() {
81 out.push(Dimension {
82 cx,
83 which: DimImpl::AngularLocation(m::AngularLocationId(i)),
84 });
85 }
86 out
87}
88
89impl<'m> Dimension<'m> {
90 pub fn kind(&self) -> DimensionKind {
92 match self.which {
93 DimImpl::Size(_) => DimensionKind::Size,
94 DimImpl::Location(_) => DimensionKind::Location,
95 DimImpl::AngularSize(_) => DimensionKind::AngularSize,
96 DimImpl::AngularLocation(_) => DimensionKind::AngularLocation,
97 }
98 }
99
100 pub fn name(&self) -> &'m str {
102 match self.which {
103 DimImpl::Size(i) => &self.cx.model.dimensional_size_arena.get(i.0).name,
104 DimImpl::Location(i) => &self.cx.model.dimensional_location_arena.get(i.0).name,
105 DimImpl::AngularSize(i) => &self.cx.model.angular_size_arena.get(i.0).name,
106 DimImpl::AngularLocation(i) => &self.cx.model.angular_location_arena.get(i.0).name,
107 }
108 }
109
110 pub fn key(&self) -> m::EntityKey {
112 match self.which {
113 DimImpl::Size(i) => m::EntityKey::DimensionalSize(i),
114 DimImpl::Location(i) => m::EntityKey::DimensionalLocation(i),
115 DimImpl::AngularSize(i) => m::EntityKey::AngularSize(i),
116 DimImpl::AngularLocation(i) => m::EntityKey::AngularLocation(i),
117 }
118 }
119
120 pub fn value(&self) -> Option<f64> {
131 let cx = self.cx;
132 let rg = cx.ref_graph();
133 for r in rg.referrers(self.key()) {
134 let m::EntityKey::DimensionalCharacteristicRepresentation(dcr_id) = r else {
135 continue;
136 };
137 let dcr = cx
138 .model
139 .dimensional_characteristic_representation_arena
140 .get(dcr_id.0);
141 let sdr_id = match &dcr.representation {
142 m::ShapeDimensionRepresentationRef::ShapeDimensionRepresentation(id) => id,
143 m::ShapeDimensionRepresentationRef::Complex(_) => {
144 cx.warn("dimension value representation is a complex instance".to_owned());
145 continue;
146 }
147 };
148 let sdr = cx.model.shape_dimension_representation_arena.get(sdr_id.0);
149 for item in &sdr.items {
150 if let m::RepresentationItemRef::MeasureRepresentationItem(mid) = item {
151 return Some(
152 cx.model
153 .measure_representation_item_arena
154 .get(mid.0)
155 .value_component
156 .value
157 .as_f64(),
158 );
159 }
160 }
161 }
162 None
163 }
164}
165
166#[derive(Clone, Copy, Debug, PartialEq, Eq)]
168pub enum ToleranceKind {
169 Position,
171 Flatness,
173 Straightness,
175 Roundness,
177 Cylindricity,
179 LineProfile,
181 SurfaceProfile,
183 Angularity,
185 Parallelism,
187 Perpendicularity,
189 Concentricity,
191 Coaxiality,
193 Symmetry,
195 CircularRunout,
197 TotalRunout,
199 Other,
202}
203
204#[derive(Clone, Copy)]
208pub struct Tolerance<'m> {
209 cx: Ctx<'m>,
210 which: TolImpl,
211}
212
213macro_rules! leaf_tolerances {
219 ($( $ent:ident, $id:ident, $arena:ident, $kind:ident );+ $(;)?) => {
220 #[derive(Clone, Copy)]
221 enum TolImpl {
222 $( $ent(m::$id), )+
223 Complex(m::ComplexUnitId),
224 }
225
226 fn push_dedicated_tolerances<'m>(cx: Ctx<'m>, out: &mut Vec<Tolerance<'m>>) {
227 $(
228 for i in 0..cx.model.$arena.items.len() {
229 out.push(Tolerance { cx, which: TolImpl::$ent(m::$id(i)) });
230 }
231 )+
232 }
233
234 impl<'m> Tolerance<'m> {
235 pub fn kind(&self) -> ToleranceKind {
237 match self.which {
238 $( TolImpl::$ent(_) => ToleranceKind::$kind, )+
239 TolImpl::Complex(cuid) => self.complex_kind(cuid),
240 }
241 }
242
243 pub fn key(&self) -> m::EntityKey {
245 match self.which {
246 $( TolImpl::$ent(i) => m::EntityKey::$ent(i), )+
247 TolImpl::Complex(cuid) => m::EntityKey::ComplexUnit(cuid),
248 }
249 }
250
251 fn dedicated_fields(&self) -> Option<(&'m str, Option<&'m m::LengthMeasureWithUnitRef>)> {
253 match self.which {
254 $( TolImpl::$ent(i) => {
255 let e = self.cx.model.$arena.get(i.0);
256 Some((&e.name, e.magnitude.as_ref()))
257 } )+
258 TolImpl::Complex(_) => None,
259 }
260 }
261
262 fn dedicated_target(&self) -> Option<&'m m::GeometricToleranceTargetRef> {
264 match self.which {
265 $( TolImpl::$ent(i) => {
266 Some(&self.cx.model.$arena.get(i.0).toleranced_shape_aspect)
267 } )+
268 TolImpl::Complex(_) => None,
269 }
270 }
271 }
272 };
273}
274
275leaf_tolerances! {
276 PositionTolerance, PositionToleranceId, position_tolerance_arena, Position;
277 FlatnessTolerance, FlatnessToleranceId, flatness_tolerance_arena, Flatness;
278 StraightnessTolerance, StraightnessToleranceId, straightness_tolerance_arena, Straightness;
279 RoundnessTolerance, RoundnessToleranceId, roundness_tolerance_arena, Roundness;
280 CylindricityTolerance, CylindricityToleranceId, cylindricity_tolerance_arena, Cylindricity;
281 LineProfileTolerance, LineProfileToleranceId, line_profile_tolerance_arena, LineProfile;
282 SurfaceProfileTolerance, SurfaceProfileToleranceId, surface_profile_tolerance_arena, SurfaceProfile;
283 AngularityTolerance, AngularityToleranceId, angularity_tolerance_arena, Angularity;
284 ParallelismTolerance, ParallelismToleranceId, parallelism_tolerance_arena, Parallelism;
285 PerpendicularityTolerance,PerpendicularityToleranceId,perpendicularity_tolerance_arena, Perpendicularity;
286 ConcentricityTolerance, ConcentricityToleranceId, concentricity_tolerance_arena, Concentricity;
287 CoaxialityTolerance, CoaxialityToleranceId, coaxiality_tolerance_arena, Coaxiality;
288 SymmetryTolerance, SymmetryToleranceId, symmetry_tolerance_arena, Symmetry;
289 CircularRunoutTolerance, CircularRunoutToleranceId, circular_runout_tolerance_arena, CircularRunout;
290 TotalRunoutTolerance, TotalRunoutToleranceId, total_runout_tolerance_arena, TotalRunout;
291}
292
293impl Scene<'_> {
294 pub fn tolerances(&self) -> impl Iterator<Item = Tolerance<'_>> + '_ {
298 all_tolerances(self.ctx()).into_iter()
299 }
300}
301
302fn all_tolerances(cx: Ctx<'_>) -> Vec<Tolerance<'_>> {
305 let mut out: Vec<Tolerance<'_>> = Vec::new();
306 push_dedicated_tolerances(cx, &mut out);
307 for i in 0..cx.model.complex_unit_arena.items.len() {
308 let cu = cx.model.complex_unit_arena.get(i);
309 if cu
310 .parts
311 .iter()
312 .any(|p| matches!(p, m::UnitPart::GeometricTolerance { .. }))
313 {
314 out.push(Tolerance {
315 cx,
316 which: TolImpl::Complex(m::ComplexUnitId(i)),
317 });
318 }
319 }
320 out
321}
322
323impl<'m> Tolerance<'m> {
324 pub fn name(&self) -> &'m str {
326 self.dedicated_fields()
327 .or_else(|| self.complex_geometric_tolerance())
328 .map_or("", |(name, _)| name)
329 }
330
331 pub fn magnitude(&self) -> Option<f64> {
339 let (_, mag) = self
340 .dedicated_fields()
341 .or_else(|| self.complex_geometric_tolerance())?;
342 mag.and_then(|r| length_measure_f64(self.cx, r))
343 }
344
345 fn complex_geometric_tolerance(
348 &self,
349 ) -> Option<(&'m str, Option<&'m m::LengthMeasureWithUnitRef>)> {
350 let TolImpl::Complex(cuid) = self.which else {
351 return None;
352 };
353 let cu = self.cx.model.complex_unit_arena.get(cuid.0);
354 cu.parts.iter().find_map(|p| match p {
355 m::UnitPart::GeometricTolerance {
356 name, magnitude, ..
357 } => Some((name.as_str(), magnitude.as_ref())),
358 _ => None,
359 })
360 }
361
362 fn complex_kind(&self, cuid: m::ComplexUnitId) -> ToleranceKind {
367 let cu = self.cx.model.complex_unit_arena.get(cuid.0);
368 for p in &cu.parts {
369 let kind = match p {
370 m::UnitPart::PositionTolerance => ToleranceKind::Position,
371 m::UnitPart::FlatnessTolerance => ToleranceKind::Flatness,
372 m::UnitPart::StraightnessTolerance => ToleranceKind::Straightness,
373 m::UnitPart::RoundnessTolerance => ToleranceKind::Roundness,
374 m::UnitPart::CylindricityTolerance => ToleranceKind::Cylindricity,
375 m::UnitPart::LineProfileTolerance => ToleranceKind::LineProfile,
376 m::UnitPart::SurfaceProfileTolerance => ToleranceKind::SurfaceProfile,
377 m::UnitPart::ParallelismTolerance => ToleranceKind::Parallelism,
378 m::UnitPart::PerpendicularityTolerance => ToleranceKind::Perpendicularity,
379 m::UnitPart::CircularRunoutTolerance => ToleranceKind::CircularRunout,
380 _ => continue,
381 };
382 return kind;
383 }
384 self.cx
385 .warn("geometric tolerance has no recognized characteristic subtype".to_owned());
386 ToleranceKind::Other
387 }
388}
389
390fn length_measure_f64(cx: Ctx<'_>, r: &m::LengthMeasureWithUnitRef) -> Option<f64> {
393 match r {
394 m::LengthMeasureWithUnitRef::LengthMeasureWithUnit(id) => Some(
395 cx.model
396 .length_measure_with_unit_arena
397 .get(id.0)
398 .value_component
399 .value
400 .as_f64(),
401 ),
402 m::LengthMeasureWithUnitRef::Complex(_) => {
403 cx.warn("tolerance magnitude is a complex measure instance".to_owned());
404 None
405 }
406 }
407}
408
409#[derive(Clone, Copy)]
412pub struct Datum<'m> {
413 cx: Ctx<'m>,
414 which: DatumImpl,
415}
416
417#[derive(Clone, Copy)]
418enum DatumImpl {
419 Datum(m::DatumId),
420 CommonDatum(m::CommonDatumId),
421}
422
423impl Scene<'_> {
424 pub fn datums(&self) -> impl Iterator<Item = Datum<'_>> + '_ {
428 all_datums(self.ctx()).into_iter()
429 }
430}
431
432fn all_datums(cx: Ctx<'_>) -> Vec<Datum<'_>> {
435 let mut out: Vec<Datum<'_>> = Vec::new();
436 for i in 0..cx.model.datum_arena.items.len() {
437 out.push(Datum {
438 cx,
439 which: DatumImpl::Datum(m::DatumId(i)),
440 });
441 }
442 for i in 0..cx.model.common_datum_arena.items.len() {
443 out.push(Datum {
444 cx,
445 which: DatumImpl::CommonDatum(m::CommonDatumId(i)),
446 });
447 }
448 out
449}
450
451impl<'m> Datum<'m> {
452 pub fn letter(&self) -> &'m str {
455 match self.which {
456 DatumImpl::Datum(i) => &self.cx.model.datum_arena.get(i.0).identification,
457 DatumImpl::CommonDatum(i) => &self.cx.model.common_datum_arena.get(i.0).identification,
458 }
459 }
460
461 pub fn name(&self) -> &'m str {
463 match self.which {
464 DatumImpl::Datum(i) => &self.cx.model.datum_arena.get(i.0).name,
465 DatumImpl::CommonDatum(i) => &self.cx.model.common_datum_arena.get(i.0).name,
466 }
467 }
468
469 pub fn key(&self) -> m::EntityKey {
471 match self.which {
472 DatumImpl::Datum(i) => m::EntityKey::Datum(i),
473 DatumImpl::CommonDatum(i) => m::EntityKey::CommonDatum(i),
474 }
475 }
476
477 pub fn datum_feature(&self) -> Option<Feature<'m>> {
482 let cx = self.cx;
483 let rg = cx.ref_graph();
484 for r in rg.referrers(self.key()) {
485 let m::EntityKey::ShapeAspectRelationship(sid) = r else {
486 continue;
487 };
488 let sar = cx.model.shape_aspect_relationship_arena.get(sid.0);
489 for aspect in [&sar.relating_shape_aspect, &sar.related_shape_aspect] {
492 if let m::ShapeAspectRef::DatumFeature(fid) = aspect {
493 return Some(Feature {
494 cx,
495 which: FeatureImpl::DatumFeature(*fid),
496 });
497 }
498 }
499 }
500 None
501 }
502}
503
504impl<'m> Tolerance<'m> {
505 pub fn datums(&self) -> Vec<Datum<'m>> {
513 let mut keyed: Vec<(i64, Datum<'m>)> = Vec::new();
518 for r in self.datum_system() {
519 self.collect_system_ref(r, &mut keyed);
520 }
521 keyed.sort_by_key(|(k, _)| *k);
522 keyed.into_iter().map(|(_, d)| d).collect()
523 }
524
525 fn datum_system(&self) -> &'m [m::DatumSystemOrReferenceRef] {
529 let model = self.cx.model;
530 match self.which {
531 TolImpl::AngularityTolerance(i) => {
532 &model.angularity_tolerance_arena.get(i.0).datum_system
533 }
534 TolImpl::CircularRunoutTolerance(i) => {
535 &model.circular_runout_tolerance_arena.get(i.0).datum_system
536 }
537 TolImpl::CoaxialityTolerance(i) => {
538 &model.coaxiality_tolerance_arena.get(i.0).datum_system
539 }
540 TolImpl::ConcentricityTolerance(i) => {
541 &model.concentricity_tolerance_arena.get(i.0).datum_system
542 }
543 TolImpl::ParallelismTolerance(i) => {
544 &model.parallelism_tolerance_arena.get(i.0).datum_system
545 }
546 TolImpl::PerpendicularityTolerance(i) => {
547 &model.perpendicularity_tolerance_arena.get(i.0).datum_system
548 }
549 TolImpl::SymmetryTolerance(i) => &model.symmetry_tolerance_arena.get(i.0).datum_system,
550 TolImpl::TotalRunoutTolerance(i) => {
551 &model.total_runout_tolerance_arena.get(i.0).datum_system
552 }
553 TolImpl::Complex(cuid) => model
554 .complex_unit_arena
555 .get(cuid.0)
556 .parts
557 .iter()
558 .find_map(|p| match p {
559 m::UnitPart::GeometricToleranceWithDatumReference { datum_system } => {
560 Some(datum_system.as_slice())
561 }
562 _ => None,
563 })
564 .unwrap_or(&[]),
565 _ => &[],
566 }
567 }
568
569 fn collect_system_ref(
573 &self,
574 r: &m::DatumSystemOrReferenceRef,
575 out: &mut Vec<(i64, Datum<'m>)>,
576 ) {
577 match r {
578 m::DatumSystemOrReferenceRef::DatumReference(id) => {
579 let dr = self.cx.model.datum_reference_arena.get(id.0);
580 self.collect_datum_ref(dr.precedence, &dr.referenced_datum, out);
581 }
582 m::DatumSystemOrReferenceRef::DatumSystem(id) => {
583 let ds = self.cx.model.datum_system_arena.get(id.0);
584 for (idx, c) in ds.constituents.iter().enumerate() {
585 let m::DatumReferenceCompartmentRef::DatumReferenceCompartment(cid) = c;
586 let comp = self.cx.model.datum_reference_compartment_arena.get(cid.0);
587 let key = i64::try_from(idx).unwrap_or(i64::MAX);
588 self.collect_base(key, &comp.base, out, 0);
589 }
590 }
591 m::DatumSystemOrReferenceRef::Complex(_) => {
592 self.cx
593 .warn("tolerance datum reference is a complex instance".to_owned());
594 }
595 }
596 }
597
598 fn collect_datum_ref(&self, key: i64, r: &m::DatumRef, out: &mut Vec<(i64, Datum<'m>)>) {
600 match r {
601 m::DatumRef::Datum(id) => out.push((key, self.datum(DatumImpl::Datum(*id)))),
602 m::DatumRef::CommonDatum(id) => {
603 out.push((key, self.datum(DatumImpl::CommonDatum(*id))));
604 }
605 m::DatumRef::Complex(_) => {
606 self.cx
607 .warn("tolerance references a complex datum instance".to_owned());
608 }
609 }
610 }
611
612 fn collect_base(
615 &self,
616 key: i64,
617 r: &m::DatumOrCommonDatumRef,
618 out: &mut Vec<(i64, Datum<'m>)>,
619 depth: u32,
620 ) {
621 if depth > 8 {
622 self.cx
623 .warn("datum reference nesting is too deep to resolve".to_owned());
624 return;
625 }
626 match r {
627 m::DatumOrCommonDatumRef::Datum(id) => {
628 out.push((key, self.datum(DatumImpl::Datum(*id))));
629 }
630 m::DatumOrCommonDatumRef::CommonDatum(id) => {
631 out.push((key, self.datum(DatumImpl::CommonDatum(*id))));
632 }
633 m::DatumOrCommonDatumRef::DatumReferenceElement(id) => {
634 let e = self.cx.model.datum_reference_element_arena.get(id.0);
635 self.collect_base(key, &e.base, out, depth + 1);
636 }
637 m::DatumOrCommonDatumRef::CommonDatumList(refs) => {
638 for er in refs {
639 let m::DatumReferenceElementRef::DatumReferenceElement(eid) = er;
640 let e = self.cx.model.datum_reference_element_arena.get(eid.0);
641 self.collect_base(key, &e.base, out, depth + 1);
642 }
643 }
644 m::DatumOrCommonDatumRef::Complex(_) => {
645 self.cx
646 .warn("tolerance references a complex datum instance".to_owned());
647 }
648 }
649 }
650
651 fn datum(&self, which: DatumImpl) -> Datum<'m> {
652 Datum { cx: self.cx, which }
653 }
654}
655
656#[derive(Clone, Copy)]
665pub struct Feature<'m> {
666 cx: Ctx<'m>,
667 which: FeatureImpl,
668}
669
670macro_rules! feature_family {
682 (
683 regions: { $( $rent:ident, $rid:ident, $rarena:ident );+ $(;)? }
684 aspects: { $( $aent:ident, $aid:ident, $aarena:ident );+ $(;)? }
685 ) => {
686 #[derive(Clone, Copy)]
687 enum FeatureImpl {
688 $( $rent(m::$rid), )+
689 $( $aent(m::$aid), )+
690 }
691
692 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
695 pub enum FeatureKind {
696 $( $rent, )+
697 $( $aent, )+
698 }
699
700 impl<'m> Feature<'m> {
701 pub fn kind(&self) -> FeatureKind {
703 match self.which {
704 $( FeatureImpl::$rent(_) => FeatureKind::$rent, )+
705 $( FeatureImpl::$aent(_) => FeatureKind::$aent, )+
706 }
707 }
708 pub fn name(&self) -> &'m str {
710 match self.which {
711 $( FeatureImpl::$rent(i) => &self.cx.model.$rarena.get(i.0).name, )+
712 $( FeatureImpl::$aent(i) => &self.cx.model.$aarena.get(i.0).name, )+
713 }
714 }
715
716 pub fn description(&self) -> Option<&'m str> {
718 match self.which {
719 $( FeatureImpl::$rent(i) => {
720 self.cx.model.$rarena.get(i.0).description.as_deref()
721 } )+
722 $( FeatureImpl::$aent(i) => {
723 self.cx.model.$aarena.get(i.0).description.as_deref()
724 } )+
725 }
726 }
727
728 pub fn key(&self) -> m::EntityKey {
730 match self.which {
731 $( FeatureImpl::$rent(i) => m::EntityKey::$rent(i), )+
732 $( FeatureImpl::$aent(i) => m::EntityKey::$aent(i), )+
733 }
734 }
735
736 fn of_shape(&self) -> &'m m::ProductDefinitionShapeRef {
738 match self.which {
739 $( FeatureImpl::$rent(i) => &self.cx.model.$rarena.get(i.0).of_shape, )+
740 $( FeatureImpl::$aent(i) => &self.cx.model.$aarena.get(i.0).of_shape, )+
741 }
742 }
743 }
744
745 fn feature_impl_from_shape_aspect_ref(r: &m::ShapeAspectRef) -> Option<FeatureImpl> {
748 match r {
749 $( m::ShapeAspectRef::$rent(i) => Some(FeatureImpl::$rent(*i)), )+
750 $( m::ShapeAspectRef::$aent(i) => Some(FeatureImpl::$aent(*i)), )+
751 m::ShapeAspectRef::Complex(_) => None,
752 }
753 }
754
755 fn feature_impl_from_tolerance_target(
759 r: &m::GeometricToleranceTargetRef,
760 ) -> Option<FeatureImpl> {
761 match r {
762 $( m::GeometricToleranceTargetRef::$rent(i) => Some(FeatureImpl::$rent(*i)), )+
763 $( m::GeometricToleranceTargetRef::$aent(i) => Some(FeatureImpl::$aent(*i)), )+
764 _ => None,
765 }
766 }
767
768 fn push_all_features<'m>(cx: Ctx<'m>, out: &mut Vec<Feature<'m>>) {
771 $(
772 for i in 0..cx.model.$rarena.items.len() {
773 out.push(Feature { cx, which: FeatureImpl::$rent(m::$rid(i)) });
774 }
775 )+
776 }
777 };
778}
779
780feature_family! {
781 regions: {
783 ShapeAspect, ShapeAspectId, shape_aspect_arena;
784 DatumFeature, DatumFeatureId, datum_feature_arena;
785 AllAroundShapeAspect, AllAroundShapeAspectId, all_around_shape_aspect_arena;
786 CentreOfSymmetry, CentreOfSymmetryId, centre_of_symmetry_arena;
787 CompositeShapeAspect, CompositeShapeAspectId, composite_shape_aspect_arena;
788 CompositeGroupShapeAspect, CompositeGroupShapeAspectId, composite_group_shape_aspect_arena;
789 ContinuousShapeAspect, ContinuousShapeAspectId, continuous_shape_aspect_arena;
790 DerivedShapeAspect, DerivedShapeAspectId, derived_shape_aspect_arena;
791 }
792 aspects: {
795 Datum, DatumId, datum_arena;
796 CommonDatum, CommonDatumId, common_datum_arena;
797 DatumTarget, DatumTargetId, datum_target_arena;
798 DatumReferenceCompartment, DatumReferenceCompartmentId, datum_reference_compartment_arena;
799 DatumReferenceElement, DatumReferenceElementId, datum_reference_element_arena;
800 DatumSystem, DatumSystemId, datum_system_arena;
801 DefaultModelGeometricView, DefaultModelGeometricViewId, default_model_geometric_view_arena;
802 GeneralDatumReference, GeneralDatumReferenceId, general_datum_reference_arena;
803 PlacedDatumTargetFeature, PlacedDatumTargetFeatureId, placed_datum_target_feature_arena;
804 ToleranceZone, ToleranceZoneId, tolerance_zone_arena;
805 ToleranceZoneWithDatum, ToleranceZoneWithDatumId, tolerance_zone_with_datum_arena;
806 DimensionalSizeWithDatumFeature, DimensionalSizeWithDatumFeatureId, dimensional_size_with_datum_feature_arena;
807 }
808}
809
810impl Scene<'_> {
811 pub fn features(&self) -> impl Iterator<Item = Feature<'_>> + '_ {
818 let cx = self.ctx();
819 let mut out: Vec<Feature<'_>> = Vec::new();
820 push_all_features(cx, &mut out);
821 out.into_iter()
822 }
823}
824
825impl<'m> Dimension<'m> {
826 pub fn features(&self) -> Vec<Feature<'m>> {
831 let model = self.cx.model;
832 let refs: Vec<&m::ShapeAspectRef> = match self.which {
833 DimImpl::Size(i) => vec![&model.dimensional_size_arena.get(i.0).applies_to],
834 DimImpl::AngularSize(i) => vec![&model.angular_size_arena.get(i.0).applies_to],
835 DimImpl::Location(i) => {
836 let e = model.dimensional_location_arena.get(i.0);
837 vec![&e.relating_shape_aspect, &e.related_shape_aspect]
838 }
839 DimImpl::AngularLocation(i) => {
840 let e = model.angular_location_arena.get(i.0);
841 vec![&e.relating_shape_aspect, &e.related_shape_aspect]
842 }
843 };
844 refs.into_iter()
845 .filter_map(|r| self.feature_from_sa_ref(r))
846 .collect()
847 }
848
849 fn feature_from_sa_ref(&self, r: &m::ShapeAspectRef) -> Option<Feature<'m>> {
850 if matches!(r, m::ShapeAspectRef::Complex(_)) {
851 self.cx
852 .warn("dimension feature is a complex instance".to_owned());
853 return None;
854 }
855 feature_impl_from_shape_aspect_ref(r).map(|which| Feature { cx: self.cx, which })
856 }
857}
858
859impl<'m> Tolerance<'m> {
860 pub fn feature(&self) -> Option<Feature<'m>> {
867 let target = self.tolerance_target()?;
868 if matches!(target, m::GeometricToleranceTargetRef::Complex(_)) {
869 self.cx
870 .warn("tolerance target is a complex instance".to_owned());
871 return None;
872 }
873 feature_impl_from_tolerance_target(target).map(|which| Feature { cx: self.cx, which })
874 }
875
876 fn tolerance_target(&self) -> Option<&'m m::GeometricToleranceTargetRef> {
879 if let Some(t) = self.dedicated_target() {
880 return Some(t);
881 }
882 let TolImpl::Complex(cuid) = self.which else {
883 return None;
884 };
885 self.cx
886 .model
887 .complex_unit_arena
888 .get(cuid.0)
889 .parts
890 .iter()
891 .find_map(|p| match p {
892 m::UnitPart::GeometricTolerance {
893 toleranced_shape_aspect,
894 ..
895 } => Some(toleranced_shape_aspect),
896 _ => None,
897 })
898 }
899}
900
901pub enum FeatureGeometry<'m> {
905 Face(Face<'m>),
907 Edge(Edge<'m>),
909 Point(Point<'m>),
911 Solid(Solid<'m>),
913 Curve(Curve<'m>),
916 Other(m::EntityKey),
918}
919
920impl<'m> Feature<'m> {
921 pub fn geometry(&self) -> Vec<FeatureGeometry<'m>> {
930 let cx = self.cx;
931 let rg = cx.ref_graph();
932 let mut out = Vec::new();
933 for r in rg.referrers(self.key()) {
934 let m::EntityKey::GeometricItemSpecificUsage(gid) = r else {
935 continue;
936 };
937 let item = &cx
938 .model
939 .geometric_item_specific_usage_arena
940 .get(gid.0)
941 .identified_item;
942 out.push(match item {
943 m::GeometricModelItemRef::AdvancedFace(id) => {
944 FeatureGeometry::Face(Face::from_advanced_face(cx, *id))
945 }
946 m::GeometricModelItemRef::EdgeCurve(id) => {
947 FeatureGeometry::Edge(Edge::from_id(cx, *id))
948 }
949 m::GeometricModelItemRef::CartesianPoint(id) => {
950 FeatureGeometry::Point(Point::from_id(cx, *id))
951 }
952 m::GeometricModelItemRef::ManifoldSolidBrep(id) => {
953 FeatureGeometry::Solid(Solid::from_id(cx, *id))
954 }
955 other => match Curve::from_model_item(cx, other) {
956 Some(c) => FeatureGeometry::Curve(c),
957 None => FeatureGeometry::Other(other.entity_key()),
958 },
959 });
960 }
961 out
962 }
963}
964
965fn product_def_of_pds(cx: Ctx<'_>, id: m::ProductDefinitionShapeId) -> Option<m::EntityKey> {
975 match cx.model.product_definition_shape_arena.get(id.0).definition {
976 m::CharacterizedDefinitionRef::ProductDefinition(pd) => {
977 Some(m::EntityKey::ProductDefinition(pd))
978 }
979 m::CharacterizedDefinitionRef::ProductDefinitionWithAssociatedDocuments(w) => {
980 Some(m::EntityKey::ProductDefinitionWithAssociatedDocuments(w))
981 }
982 _ => None,
983 }
984}
985
986fn product_def_of_shape(cx: Ctx<'_>, r: &m::ProductDefinitionShapeRef) -> Option<m::EntityKey> {
989 match r {
990 m::ProductDefinitionShapeRef::ProductDefinitionShape(id) => product_def_of_pds(cx, *id),
991 m::ProductDefinitionShapeRef::Complex(_) => None,
992 }
993}
994
995impl Feature<'_> {
996 fn part_def_id(&self) -> Option<m::EntityKey> {
1000 product_def_of_shape(self.cx, self.of_shape())
1001 }
1002}
1003
1004impl Datum<'_> {
1005 fn part_def_id(&self) -> Option<m::EntityKey> {
1007 let of_shape = match self.which {
1008 DatumImpl::Datum(i) => &self.cx.model.datum_arena.get(i.0).of_shape,
1009 DatumImpl::CommonDatum(i) => &self.cx.model.common_datum_arena.get(i.0).of_shape,
1010 };
1011 product_def_of_shape(self.cx, of_shape)
1012 }
1013}
1014
1015impl Tolerance<'_> {
1016 fn part_def_id(&self) -> Option<m::EntityKey> {
1020 let target = self.tolerance_target()?;
1021 if let m::GeometricToleranceTargetRef::ProductDefinitionShape(id) = target {
1022 return product_def_of_pds(self.cx, *id);
1023 }
1024 let which = feature_impl_from_tolerance_target(target)?;
1025 Feature { cx: self.cx, which }.part_def_id()
1026 }
1027}
1028
1029pub(crate) fn features_of(cx: Ctx<'_>, part: m::EntityKey) -> Vec<Feature<'_>> {
1031 let mut all = Vec::new();
1032 push_all_features(cx, &mut all);
1033 all.retain(|f| f.part_def_id() == Some(part));
1034 all
1035}
1036
1037pub(crate) fn datums_of(cx: Ctx<'_>, part: m::EntityKey) -> Vec<Datum<'_>> {
1039 let mut all = all_datums(cx);
1040 all.retain(|d| d.part_def_id() == Some(part));
1041 all
1042}
1043
1044pub(crate) fn dimensions_of(cx: Ctx<'_>, part: m::EntityKey) -> Vec<Dimension<'_>> {
1046 let mut all = all_dimensions(cx);
1047 all.retain(|d| d.features().iter().any(|f| f.part_def_id() == Some(part)));
1048 all
1049}
1050
1051pub(crate) fn tolerances_of(cx: Ctx<'_>, part: m::EntityKey) -> Vec<Tolerance<'_>> {
1054 let mut all = all_tolerances(cx);
1055 all.retain(|t| t.part_def_id() == Some(part));
1056 all
1057}