1#![allow(clippy::must_use_candidate)]
13
14use std::collections::HashSet;
15
16use crate::generated::model as m;
17use crate::scene::geometry::Solid;
18use crate::scene::pmi;
19use crate::scene::{Ctx, Scene};
20
21impl Scene<'_> {
22 pub fn all_products(&self) -> impl Iterator<Item = Product<'_>> + '_ {
24 let cx = self.ctx();
25 (0..cx.model.product_arena.items.len()).map(move |i| Product {
26 cx,
27 id: m::ProductId(i),
28 })
29 }
30
31 pub fn all_product_definitions(&self) -> impl Iterator<Item = ProductDef<'_>> + '_ {
33 let cx = self.ctx();
34 (0..cx.model.product_definition_arena.items.len()).map(move |i| ProductDef {
35 cx,
36 id: m::ProductDefinitionId(i),
37 })
38 }
39
40 pub fn root_definitions(&self) -> impl Iterator<Item = ProductDef<'_>> + '_ {
44 let cx = self.ctx();
45 let rg = cx.ref_graph();
46 self.all_product_definitions().filter(move |def| {
47 !rg.referrers(def.key()).iter().any(|r| {
48 let m::EntityKey::NextAssemblyUsageOccurrence(nid) = r else {
49 return false;
50 };
51 let nauo = cx.model.next_assembly_usage_occurrence_arena.get(nid.0);
52 matches!(
53 &nauo.related_product_definition,
54 m::ProductDefinitionOrReferenceRef::ProductDefinition(d) if m::EntityKey::ProductDefinition(*d) == def.key()
55 )
56 })
57 })
58 }
59}
60
61#[derive(Clone, Copy)]
67pub struct Product<'m> {
68 cx: Ctx<'m>,
69 id: m::ProductId,
70}
71
72impl<'m> Product<'m> {
73 fn raw(&self) -> &'m m::Product {
74 self.cx.model.product_arena.get(self.id.0)
75 }
76
77 pub fn key(&self) -> m::EntityKey {
80 m::EntityKey::Product(self.id)
81 }
82
83 pub fn id(&self) -> &'m str {
84 &self.raw().id
85 }
86
87 pub fn name(&self) -> &'m str {
88 &self.raw().name
89 }
90
91 pub fn description(&self) -> Option<&'m str> {
92 self.raw().description.as_deref()
93 }
94
95 pub fn definitions(&self) -> impl Iterator<Item = ProductDef<'m>> + 'm {
103 let cx = self.cx;
104 let rg = cx.ref_graph();
105 let mut out: Vec<ProductDef<'m>> = Vec::new();
106 for &fr in rg.referrers(m::EntityKey::Product(self.id)) {
107 if !matches!(
108 fr,
109 m::EntityKey::ProductDefinitionFormation(_)
110 | m::EntityKey::ProductDefinitionFormationWithSpecifiedSource(_)
111 ) {
112 continue;
113 }
114 for &pr in rg.referrers(fr) {
115 if let m::EntityKey::ProductDefinition(pdid) = pr {
116 out.push(ProductDef { cx, id: pdid });
117 }
118 }
119 }
120 out.into_iter()
121 }
122
123 fn metadata_targets(&self) -> Vec<m::EntityKey> {
130 let rg = self.cx.ref_graph();
131 let mut targets = vec![self.key()];
132 for &fr in rg.referrers(self.key()) {
133 if !matches!(
134 fr,
135 m::EntityKey::ProductDefinitionFormation(_)
136 | m::EntityKey::ProductDefinitionFormationWithSpecifiedSource(_)
137 ) {
138 continue;
139 }
140 targets.push(fr);
141 for &pr in rg.referrers(fr) {
142 if matches!(pr, m::EntityKey::ProductDefinition(_)) {
143 targets.push(pr);
144 }
145 }
146 }
147 targets
148 }
149
150 pub fn contributors(&self) -> Vec<Contributor<'m>> {
153 contributors_of(self.cx, &self.metadata_targets())
154 }
155
156 pub fn approvals(&self) -> Vec<Approval<'m>> {
159 approvals_of(self.cx, &self.metadata_targets())
160 }
161
162 pub fn documents(&self) -> Vec<Document<'m>> {
164 documents_of(self.cx, &self.metadata_targets())
165 }
166
167 pub fn security_classifications(&self) -> Vec<SecurityClassification<'m>> {
169 security_classifications_of(self.cx, &self.metadata_targets())
170 }
171
172 pub fn versions(&self) -> Vec<Version<'m>> {
176 let cx = self.cx;
177 let rg = cx.ref_graph();
178 let mut out: Vec<Version<'m>> = Vec::new();
179 for r in rg.referrers(self.key()) {
180 let which = match r {
181 m::EntityKey::ProductDefinitionFormation(id) => FormImpl::Plain(*id),
182 m::EntityKey::ProductDefinitionFormationWithSpecifiedSource(id) => {
183 FormImpl::WithSource(*id)
184 }
185 _ => continue,
186 };
187 out.push(Version { cx, which });
188 }
189 out
190 }
191}
192
193#[derive(Clone, Copy)]
198pub struct Version<'m> {
199 cx: Ctx<'m>,
200 which: FormImpl,
201}
202
203#[derive(Clone, Copy)]
204enum FormImpl {
205 Plain(m::ProductDefinitionFormationId),
206 WithSource(m::ProductDefinitionFormationWithSpecifiedSourceId),
207}
208
209impl<'m> Version<'m> {
210 fn fields(&self) -> (&'m str, Option<&'m str>) {
213 let model = self.cx.model;
214 match self.which {
215 FormImpl::Plain(i) => {
216 let f = model.product_definition_formation_arena.get(i.0);
217 (&f.id, f.description.as_deref())
218 }
219 FormImpl::WithSource(i) => {
220 let f = model
221 .product_definition_formation_with_specified_source_arena
222 .get(i.0);
223 (&f.id, f.description.as_deref())
224 }
225 }
226 }
227
228 pub fn key(&self) -> m::EntityKey {
230 match self.which {
231 FormImpl::Plain(i) => m::EntityKey::ProductDefinitionFormation(i),
232 FormImpl::WithSource(i) => {
233 m::EntityKey::ProductDefinitionFormationWithSpecifiedSource(i)
234 }
235 }
236 }
237
238 pub fn id(&self) -> &'m str {
240 self.fields().0
241 }
242
243 pub fn description(&self) -> Option<&'m str> {
245 self.fields().1
246 }
247
248 pub fn definitions(&self) -> impl Iterator<Item = ProductDef<'m>> + 'm {
251 let cx = self.cx;
252 let rg = cx.ref_graph();
253 let mut out: Vec<ProductDef<'m>> = Vec::new();
254 for &r in rg.referrers(self.key()) {
255 if let m::EntityKey::ProductDefinition(pdid) = r {
256 out.push(ProductDef { cx, id: pdid });
257 }
258 }
259 out.into_iter()
260 }
261}
262
263#[derive(Clone, Copy)]
270pub struct ProductDef<'m> {
271 cx: Ctx<'m>,
272 id: m::ProductDefinitionId,
273}
274
275impl<'m> ProductDef<'m> {
276 fn raw(&self) -> &'m m::ProductDefinition {
277 self.cx.model.product_definition_arena.get(self.id.0)
278 }
279
280 pub fn key(&self) -> m::EntityKey {
283 m::EntityKey::ProductDefinition(self.id)
284 }
285
286 pub fn id(&self) -> &'m str {
287 &self.raw().id
288 }
289
290 pub fn description(&self) -> Option<&'m str> {
291 self.raw().description.as_deref()
292 }
293
294 pub fn product(&self) -> Option<Product<'m>> {
296 let model = self.cx.model;
297 let of_product: &m::ProductRef = match &self.raw().formation {
298 m::ProductDefinitionFormationRef::ProductDefinitionFormation(i) => {
299 &model.product_definition_formation_arena.get(i.0).of_product
300 }
301 m::ProductDefinitionFormationRef::ProductDefinitionFormationWithSpecifiedSource(i) => {
302 &model
303 .product_definition_formation_with_specified_source_arena
304 .get(i.0)
305 .of_product
306 }
307 m::ProductDefinitionFormationRef::Complex(_) => return None,
308 };
309 match of_product {
310 m::ProductRef::Product(pid) => Some(Product {
311 cx: self.cx,
312 id: *pid,
313 }),
314 m::ProductRef::Complex(_) => None,
315 }
316 }
317
318 pub fn occurrences(&self) -> impl Iterator<Item = Occurrence<'m>> + 'm {
322 let cx = self.cx;
323 let rg = cx.ref_graph();
324 let me = self.id;
325 let mut out: Vec<Occurrence<'m>> = Vec::new();
326 for r in rg.referrers(m::EntityKey::ProductDefinition(me)) {
327 if let m::EntityKey::NextAssemblyUsageOccurrence(nid) = r {
328 let nauo = cx.model.next_assembly_usage_occurrence_arena.get(nid.0);
329 if pdor_is(&nauo.relating_product_definition, me) {
330 out.push(Occurrence { cx, id: *nid });
331 }
332 }
333 }
334 out.into_iter()
335 }
336
337 pub fn children(&self) -> impl Iterator<Item = ProductDef<'m>> + 'm {
340 self.occurrences().filter_map(|o| o.definition())
341 }
342
343 pub fn parents(&self) -> impl Iterator<Item = ProductDef<'m>> + 'm {
346 let cx = self.cx;
347 let rg = cx.ref_graph();
348 let me = self.id;
349 let mut out: Vec<ProductDef<'m>> = Vec::new();
350 for r in rg.referrers(m::EntityKey::ProductDefinition(me)) {
351 if let m::EntityKey::NextAssemblyUsageOccurrence(nid) = r {
352 let nauo = cx.model.next_assembly_usage_occurrence_arena.get(nid.0);
353 if pdor_is(&nauo.related_product_definition, me) {
354 if let Some(d) = pdor_to_def(cx, &nauo.relating_product_definition) {
355 out.push(d);
356 }
357 }
358 }
359 }
360 out.into_iter()
361 }
362
363 pub fn solids(&self) -> impl Iterator<Item = Solid<'m>> + 'm {
371 let cx = self.cx;
372 let rg = cx.ref_graph();
373 let me = self.id;
374 let mut out: Vec<Solid<'m>> = Vec::new();
375 let mut visited: HashSet<m::EntityKey> = HashSet::new();
378 for r in rg.referrers(m::EntityKey::ProductDefinition(me)) {
379 let m::EntityKey::ProductDefinitionShape(pds_id) = r else {
380 continue;
381 };
382 let pds = cx.model.product_definition_shape_arena.get(pds_id.0);
383 if !matches!(&pds.definition, m::CharacterizedDefinitionRef::ProductDefinition(pd) if *pd == me)
384 {
385 continue;
386 }
387 for s in rg.referrers(m::EntityKey::ProductDefinitionShape(*pds_id)) {
388 let m::EntityKey::ShapeDefinitionRepresentation(sdr_id) = s else {
389 continue;
390 };
391 let sdr = cx.model.shape_definition_representation_arena.get(sdr_id.0);
392 if matches!(&sdr.definition, m::RepresentedDefinitionRef::ProductDefinitionShape(p) if *p == *pds_id)
393 {
394 collect_solids_from_repr(cx, &sdr.used_representation, &mut out, &mut visited);
395 }
396 }
397 }
398 let mut seen: HashSet<m::EntityKey> = HashSet::new();
400 out.retain(|s| seen.insert(s.key()));
401 out.into_iter()
402 }
403
404 pub fn features(&self) -> impl Iterator<Item = pmi::Feature<'m>> + 'm {
408 pmi::features_of(self.cx, self.id).into_iter()
409 }
410
411 pub fn dimensions(&self) -> impl Iterator<Item = pmi::Dimension<'m>> + 'm {
413 pmi::dimensions_of(self.cx, self.id).into_iter()
414 }
415
416 pub fn tolerances(&self) -> impl Iterator<Item = pmi::Tolerance<'m>> + 'm {
419 pmi::tolerances_of(self.cx, self.id).into_iter()
420 }
421
422 pub fn datums(&self) -> impl Iterator<Item = pmi::Datum<'m>> + 'm {
424 pmi::datums_of(self.cx, self.id).into_iter()
425 }
426
427 pub fn version(&self) -> Option<&'m str> {
429 let model = self.cx.model;
430 match &self.raw().formation {
431 m::ProductDefinitionFormationRef::ProductDefinitionFormation(i) => {
432 Some(&model.product_definition_formation_arena.get(i.0).id)
433 }
434 m::ProductDefinitionFormationRef::ProductDefinitionFormationWithSpecifiedSource(i) => {
435 Some(
436 &model
437 .product_definition_formation_with_specified_source_arena
438 .get(i.0)
439 .id,
440 )
441 }
442 m::ProductDefinitionFormationRef::Complex(_) => None,
443 }
444 }
445
446 fn metadata_targets(&self) -> Vec<m::EntityKey> {
454 let mut targets = Vec::new();
455 if let Some(p) = self.product() {
456 targets.push(p.key());
457 }
458 match &self.raw().formation {
459 m::ProductDefinitionFormationRef::ProductDefinitionFormation(i) => {
460 targets.push(m::EntityKey::ProductDefinitionFormation(*i));
461 }
462 m::ProductDefinitionFormationRef::ProductDefinitionFormationWithSpecifiedSource(i) => {
463 targets.push(m::EntityKey::ProductDefinitionFormationWithSpecifiedSource(
464 *i,
465 ));
466 }
467 m::ProductDefinitionFormationRef::Complex(_) => {}
468 }
469 targets.push(m::EntityKey::ProductDefinition(self.id));
470 targets
471 }
472
473 pub fn contributors(&self) -> Vec<Contributor<'m>> {
476 contributors_of(self.cx, &self.metadata_targets())
477 }
478
479 pub fn approvals(&self) -> Vec<Approval<'m>> {
482 approvals_of(self.cx, &self.metadata_targets())
483 }
484
485 pub fn documents(&self) -> Vec<Document<'m>> {
489 documents_of(self.cx, &self.metadata_targets())
490 }
491
492 pub fn security_classifications(&self) -> Vec<SecurityClassification<'m>> {
495 security_classifications_of(self.cx, &self.metadata_targets())
496 }
497}
498
499#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
518pub enum Scope {
519 Product,
521 Version,
523 Definition,
525}
526
527fn scope_of(k: m::EntityKey) -> Scope {
530 match k {
531 m::EntityKey::Product(_) => Scope::Product,
532 m::EntityKey::ProductDefinitionFormation(_)
533 | m::EntityKey::ProductDefinitionFormationWithSpecifiedSource(_) => Scope::Version,
534 _ => Scope::Definition,
535 }
536}
537
538#[derive(Clone, Copy)]
543pub enum Target<'m> {
544 Product(Product<'m>),
545 Version(Version<'m>),
546 Definition(ProductDef<'m>),
547}
548
549impl Target<'_> {
550 pub fn scope(&self) -> Scope {
552 match self {
553 Target::Product(_) => Scope::Product,
554 Target::Version(_) => Scope::Version,
555 Target::Definition(_) => Scope::Definition,
556 }
557 }
558}
559
560fn target_of(cx: Ctx<'_>, k: m::EntityKey) -> Option<Target<'_>> {
563 Some(match k {
564 m::EntityKey::Product(id) => Target::Product(Product { cx, id }),
565 m::EntityKey::ProductDefinitionFormation(id) => Target::Version(Version {
566 cx,
567 which: FormImpl::Plain(id),
568 }),
569 m::EntityKey::ProductDefinitionFormationWithSpecifiedSource(id) => {
570 Target::Version(Version {
571 cx,
572 which: FormImpl::WithSource(id),
573 })
574 }
575 m::EntityKey::ProductDefinition(id) => Target::Definition(ProductDef { cx, id }),
576 _ => return None,
577 })
578}
579
580fn targets_from_keys<'m>(cx: Ctx<'m>, keys: impl Iterator<Item = m::EntityKey>) -> Vec<Target<'m>> {
582 let mut out: Vec<Target<'m>> = Vec::new();
583 let mut seen: Vec<m::EntityKey> = Vec::new();
584 for k in keys {
585 if seen.contains(&k) {
586 continue;
587 }
588 seen.push(k);
589 if let Some(t) = target_of(cx, k) {
590 out.push(t);
591 }
592 }
593 out
594}
595
596fn contributors_of<'m>(cx: Ctx<'m>, targets: &[m::EntityKey]) -> Vec<Contributor<'m>> {
597 let rg = cx.ref_graph();
598 let mut out: Vec<Contributor<'m>> = Vec::new();
599 let mut seen: Vec<m::EntityKey> = Vec::new();
600 for &t in targets {
601 let scope = scope_of(t);
602 for r in rg.referrers(t) {
603 let which = match r {
604 m::EntityKey::CcDesignPersonAndOrganizationAssignment(id) => {
605 ContributorImpl::CcDesign(*id)
606 }
607 m::EntityKey::AppliedPersonAndOrganizationAssignment(id) => {
608 ContributorImpl::Applied(*id)
609 }
610 _ => continue,
611 };
612 if seen.contains(r) {
613 continue;
614 }
615 seen.push(*r);
616 out.push(Contributor { cx, which, scope });
617 }
618 }
619 out
620}
621
622fn approvals_of<'m>(cx: Ctx<'m>, targets: &[m::EntityKey]) -> Vec<Approval<'m>> {
623 let rg = cx.ref_graph();
624 let mut out: Vec<Approval<'m>> = Vec::new();
625 let mut seen: Vec<m::ApprovalId> = Vec::new();
626 for &t in targets {
627 let scope = scope_of(t);
628 for r in rg.referrers(t) {
629 let m::ApprovalRef::Approval(aid) = match r {
630 m::EntityKey::CcDesignApproval(id) => {
631 &cx.model
632 .cc_design_approval_arena
633 .get(id.0)
634 .assigned_approval
635 }
636 m::EntityKey::AppliedApprovalAssignment(id) => {
637 &cx.model
638 .applied_approval_assignment_arena
639 .get(id.0)
640 .assigned_approval
641 }
642 _ => continue,
643 };
644 if seen.contains(aid) {
645 continue;
646 }
647 seen.push(*aid);
648 out.push(Approval {
649 cx,
650 id: *aid,
651 scope,
652 });
653 }
654 }
655 out
656}
657
658fn documents_of<'m>(cx: Ctx<'m>, targets: &[m::EntityKey]) -> Vec<Document<'m>> {
661 let rg = cx.ref_graph();
662 let mut out: Vec<Document<'m>> = Vec::new();
663 let mut seen: Vec<m::EntityKey> = Vec::new();
664 for &t in targets {
665 let scope = scope_of(t);
666 for r in rg.referrers(t) {
667 let m::EntityKey::AppliedDocumentReference(id) = r else {
668 continue;
669 };
670 let which = match &cx
671 .model
672 .applied_document_reference_arena
673 .get(id.0)
674 .assigned_document
675 {
676 m::DocumentRef::Document(i) => DocImpl::Document(*i),
677 m::DocumentRef::DocumentFile(i) => DocImpl::DocumentFile(*i),
678 m::DocumentRef::Complex(_) => {
679 cx.warn(
680 "APPLIED_DOCUMENT_REFERENCE.assigned_document is a complex instance; \
681 document left unread"
682 .to_owned(),
683 );
684 continue;
685 }
686 };
687 let doc = Document { cx, which, scope };
688 let k = doc.key();
689 if seen.contains(&k) {
690 continue;
691 }
692 seen.push(k);
693 out.push(doc);
694 }
695 }
696 out
697}
698
699fn security_classifications_of<'m>(
700 cx: Ctx<'m>,
701 targets: &[m::EntityKey],
702) -> Vec<SecurityClassification<'m>> {
703 let rg = cx.ref_graph();
704 let mut out: Vec<SecurityClassification<'m>> = Vec::new();
705 let mut seen: Vec<m::SecurityClassificationId> = Vec::new();
706 for &t in targets {
707 let scope = scope_of(t);
708 for r in rg.referrers(t) {
709 let m::SecurityClassificationRef::SecurityClassification(sid) = match r {
710 m::EntityKey::CcDesignSecurityClassification(id) => {
711 &cx.model
712 .cc_design_security_classification_arena
713 .get(id.0)
714 .assigned_security_classification
715 }
716 m::EntityKey::AppliedSecurityClassificationAssignment(id) => {
717 &cx.model
718 .applied_security_classification_assignment_arena
719 .get(id.0)
720 .assigned_security_classification
721 }
722 _ => continue,
723 };
724 if seen.contains(sid) {
725 continue;
726 }
727 seen.push(*sid);
728 out.push(SecurityClassification {
729 cx,
730 id: *sid,
731 scope,
732 });
733 }
734 }
735 out
736}
737
738#[derive(Clone, Copy)]
743enum ContributorImpl {
744 CcDesign(m::CcDesignPersonAndOrganizationAssignmentId),
745 Applied(m::AppliedPersonAndOrganizationAssignmentId),
746}
747
748#[derive(Clone, Copy)]
751pub struct Contributor<'m> {
752 cx: Ctx<'m>,
753 which: ContributorImpl,
754 scope: Scope,
755}
756
757impl<'m> Contributor<'m> {
758 pub fn assigned_scope(&self) -> Scope {
760 self.scope
761 }
762
763 pub fn assigned_targets(&self) -> Vec<Target<'m>> {
766 let cx = self.cx;
767 let keys: Vec<m::EntityKey> = match self.which {
768 ContributorImpl::CcDesign(i) => cx
769 .model
770 .cc_design_person_and_organization_assignment_arena
771 .get(i.0)
772 .items
773 .iter()
774 .map(m::CcPersonOrganizationItemRef::entity_key)
775 .collect(),
776 ContributorImpl::Applied(i) => cx
777 .model
778 .applied_person_and_organization_assignment_arena
779 .get(i.0)
780 .items
781 .iter()
782 .map(m::PersonAndOrganizationItemRef::entity_key)
783 .collect(),
784 };
785 targets_from_keys(cx, keys.into_iter())
786 }
787
788 fn ids(&self) -> (m::PersonAndOrganizationId, m::PersonAndOrganizationRoleId) {
791 let model = self.cx.model;
792 let (pao, role) = match self.which {
793 ContributorImpl::CcDesign(i) => {
794 let a = model
795 .cc_design_person_and_organization_assignment_arena
796 .get(i.0);
797 (&a.assigned_person_and_organization, &a.role)
798 }
799 ContributorImpl::Applied(i) => {
800 let a = model
801 .applied_person_and_organization_assignment_arena
802 .get(i.0);
803 (&a.assigned_person_and_organization, &a.role)
804 }
805 };
806 let m::PersonAndOrganizationRef::PersonAndOrganization(pid) = pao;
807 let m::PersonAndOrganizationRoleRef::PersonAndOrganizationRole(rid) = role;
808 (*pid, *rid)
809 }
810
811 pub fn role(&self) -> &'m str {
813 let (_, rid) = self.ids();
814 &self
815 .cx
816 .model
817 .person_and_organization_role_arena
818 .get(rid.0)
819 .name
820 }
821
822 pub fn person(&self) -> Person<'m> {
824 let (pao, _) = self.ids();
825 let m::PersonRef::Person(pid) = &self
826 .cx
827 .model
828 .person_and_organization_arena
829 .get(pao.0)
830 .the_person;
831 Person {
832 cx: self.cx,
833 id: *pid,
834 }
835 }
836
837 pub fn organization(&self) -> &'m str {
839 let (pao, _) = self.ids();
840 let m::OrganizationRef::Organization(oid) = &self
841 .cx
842 .model
843 .person_and_organization_arena
844 .get(pao.0)
845 .the_organization;
846 &self.cx.model.organization_arena.get(oid.0).name
847 }
848}
849
850#[derive(Clone, Copy)]
852pub struct Person<'m> {
853 cx: Ctx<'m>,
854 id: m::PersonId,
855}
856
857impl<'m> Person<'m> {
858 fn raw(&self) -> &'m m::Person {
859 self.cx.model.person_arena.get(self.id.0)
860 }
861
862 pub fn key(&self) -> m::EntityKey {
864 m::EntityKey::Person(self.id)
865 }
866
867 pub fn id(&self) -> &'m str {
869 &self.raw().id
870 }
871
872 pub fn first_name(&self) -> Option<&'m str> {
874 self.raw().first_name.as_deref()
875 }
876
877 pub fn last_name(&self) -> Option<&'m str> {
879 self.raw().last_name.as_deref()
880 }
881}
882
883#[derive(Clone, Copy, Debug)]
890pub struct Placement {
891 pub origin: [f64; 3],
892 pub axis: [f64; 3],
893 pub ref_direction: [f64; 3],
894}
895
896#[derive(Clone, Copy, Debug)]
900pub struct Transform {
901 pub from: Placement,
902 pub to: Placement,
903}
904
905fn vdot(a: [f64; 3], b: [f64; 3]) -> f64 {
909 a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
910}
911fn vsub(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
912 [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
913}
914fn vscale(a: [f64; 3], s: f64) -> [f64; 3] {
915 [a[0] * s, a[1] * s, a[2] * s]
916}
917fn vcross(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
918 [
919 a[1] * b[2] - a[2] * b[1],
920 a[2] * b[0] - a[0] * b[2],
921 a[0] * b[1] - a[1] * b[0],
922 ]
923}
924fn vnorm(a: [f64; 3]) -> Option<[f64; 3]> {
925 let n = vdot(a, a).sqrt();
926 (n >= 1e-12).then(|| vscale(a, 1.0 / n))
927}
928
929impl Placement {
930 fn frame(&self) -> Option<([f64; 3], [f64; 3], [f64; 3])> {
935 let z = vnorm(self.axis)?;
936 let x = vnorm(vsub(
937 self.ref_direction,
938 vscale(z, vdot(self.ref_direction, z)),
939 ))?;
940 Some((x, vcross(z, x), z))
941 }
942}
943
944impl Transform {
945 #[allow(clippy::needless_range_loop)]
956 pub fn matrix(&self) -> Option<[[f64; 4]; 4]> {
957 let rf = self.from.frame()?; let rt = self.to.frame()?; let (rf, rt) = ([rf.0, rf.1, rf.2], [rt.0, rt.1, rt.2]);
960 let mut m = [[0.0_f64; 4]; 4];
961 for i in 0..3 {
963 for j in 0..3 {
964 m[i][j] = (0..3).map(|k| rt[k][i] * rf[k][j]).sum();
965 }
966 }
967 for i in 0..3 {
969 m[i][3] =
970 self.to.origin[i] - (0..3).map(|j| m[i][j] * self.from.origin[j]).sum::<f64>();
971 }
972 m[3][3] = 1.0;
973 Some(m)
974 }
975}
976
977#[derive(Clone, Copy)]
980pub struct Occurrence<'m> {
981 cx: Ctx<'m>,
982 id: m::NextAssemblyUsageOccurrenceId,
983}
984
985impl<'m> Occurrence<'m> {
986 fn raw(&self) -> &'m m::NextAssemblyUsageOccurrence {
987 self.cx
988 .model
989 .next_assembly_usage_occurrence_arena
990 .get(self.id.0)
991 }
992
993 pub fn key(&self) -> m::EntityKey {
995 m::EntityKey::NextAssemblyUsageOccurrence(self.id)
996 }
997
998 pub fn definition(&self) -> Option<ProductDef<'m>> {
1000 pdor_to_def(self.cx, &self.raw().related_product_definition)
1001 }
1002
1003 pub fn transform(&self) -> Option<Transform> {
1010 let cx = self.cx;
1011 let rg = cx.ref_graph();
1012 let me = self.id;
1013 for r in rg.referrers(m::EntityKey::NextAssemblyUsageOccurrence(me)) {
1014 let m::EntityKey::ProductDefinitionShape(pds_id) = r else {
1015 continue;
1016 };
1017 let pds = cx.model.product_definition_shape_arena.get(pds_id.0);
1018 if !matches!(&pds.definition, m::CharacterizedDefinitionRef::NextAssemblyUsageOccurrence(n) if *n == me)
1019 {
1020 continue;
1021 }
1022 for c in rg.referrers(m::EntityKey::ProductDefinitionShape(*pds_id)) {
1023 let m::EntityKey::ContextDependentShapeRepresentation(cdsr_id) = c else {
1024 continue;
1025 };
1026 let cdsr = cx
1027 .model
1028 .context_dependent_shape_representation_arena
1029 .get(cdsr_id.0);
1030 if !matches!(&cdsr.represented_product_relation, m::ProductDefinitionShapeRef::ProductDefinitionShape(p) if *p == *pds_id)
1031 {
1032 continue;
1033 }
1034 if let Some(idt) = idt_of_cdsr(cx, &cdsr.representation_relation) {
1035 let from = resolve_placement(cx, &idt.transform_item_1)?;
1036 let to = resolve_placement(cx, &idt.transform_item_2)?;
1037 return Some(Transform { from, to });
1038 }
1039 }
1040 }
1041 None
1042 }
1043}
1044
1045impl Scene<'_> {
1051 pub fn all_mapped_instances(&self) -> impl Iterator<Item = MappedInstance<'_>> + '_ {
1056 let cx = self.ctx();
1057 (0..cx.model.mapped_item_arena.items.len()).map(move |i| MappedInstance {
1058 cx,
1059 id: m::MappedItemId(i),
1060 })
1061 }
1062}
1063
1064#[derive(Clone, Copy)]
1067pub struct MappedInstance<'m> {
1068 cx: Ctx<'m>,
1069 id: m::MappedItemId,
1070}
1071
1072impl<'m> MappedInstance<'m> {
1073 fn raw(&self) -> &'m m::MappedItem {
1074 self.cx.model.mapped_item_arena.get(self.id.0)
1075 }
1076
1077 pub fn name(&self) -> &'m str {
1078 &self.raw().name
1079 }
1080
1081 pub fn key(&self) -> m::EntityKey {
1083 m::EntityKey::MappedItem(self.id)
1084 }
1085
1086 fn map(&self) -> Option<&'m m::RepresentationMap> {
1087 let m::RepresentationMapRef::RepresentationMap(id) = &self.raw().mapping_source else {
1088 self.cx
1089 .warn("MAPPED_ITEM.mapping_source is not a REPRESENTATION_MAP".to_owned());
1090 return None;
1091 };
1092 Some(self.cx.model.representation_map_arena.get(id.0))
1093 }
1094
1095 pub fn transform(&self) -> Option<Transform> {
1101 let cx = self.cx;
1102 let map = self.map()?;
1103 let from = resolve_placement(cx, &map.mapping_origin)?;
1104 let to = resolve_placement(cx, &self.raw().mapping_target)?;
1105 Some(Transform { from, to })
1106 }
1107
1108 pub fn solids(&self) -> Vec<Solid<'m>> {
1111 let cx = self.cx;
1112 let Some(map) = self.map() else {
1113 return Vec::new();
1114 };
1115 rep_items(cx, &map.mapped_representation)
1116 .into_iter()
1117 .flatten()
1118 .filter_map(|it| match it {
1119 m::RepresentationItemRef::ManifoldSolidBrep(id) => Some(Solid::from_id(cx, *id)),
1120 _ => None,
1121 })
1122 .collect()
1123 }
1124
1125 pub fn mapped_children(&self) -> Vec<MappedInstance<'m>> {
1128 let cx = self.cx;
1129 let Some(map) = self.map() else {
1130 return Vec::new();
1131 };
1132 rep_items(cx, &map.mapped_representation)
1133 .into_iter()
1134 .flatten()
1135 .filter_map(|it| match it {
1136 m::RepresentationItemRef::MappedItem(id) => Some(MappedInstance { cx, id: *id }),
1137 _ => None,
1138 })
1139 .collect()
1140 }
1141}
1142
1143fn rep_items<'m>(cx: Ctx<'m>, r: &m::RepresentationRef) -> Option<&'m [m::RepresentationItemRef]> {
1146 match r {
1147 m::RepresentationRef::ShapeRepresentation(id) => {
1148 Some(&cx.model.shape_representation_arena.get(id.0).items)
1149 }
1150 m::RepresentationRef::AdvancedBrepShapeRepresentation(id) => Some(
1151 &cx.model
1152 .advanced_brep_shape_representation_arena
1153 .get(id.0)
1154 .items,
1155 ),
1156 m::RepresentationRef::GeometricallyBoundedWireframeShapeRepresentation(id) => Some(
1157 &cx.model
1158 .geometrically_bounded_wireframe_shape_representation_arena
1159 .get(id.0)
1160 .items,
1161 ),
1162 _ => None,
1163 }
1164}
1165
1166fn idt_of_cdsr<'m>(
1175 cx: Ctx<'m>,
1176 rr: &m::ShapeRepresentationRelationshipRef,
1177) -> Option<&'m m::ItemDefinedTransformation> {
1178 let m::ShapeRepresentationRelationshipRef::Complex(cuid) = rr else {
1179 return None;
1180 };
1181 let op = cx
1182 .model
1183 .complex_unit_arena
1184 .get(cuid.0)
1185 .parts
1186 .iter()
1187 .find_map(|p| match p {
1188 m::UnitPart::RepresentationRelationshipWithTransformation {
1189 transformation_operator,
1190 } => Some(transformation_operator),
1191 _ => None,
1192 })?;
1193 let m::TransformationRef::ItemDefinedTransformation(id) = op else {
1194 return None;
1195 };
1196 Some(cx.model.item_defined_transformation_arena.get(id.0))
1197}
1198
1199fn resolve_placement(cx: Ctx<'_>, item: &m::RepresentationItemRef) -> Option<Placement> {
1204 let m::RepresentationItemRef::Axis2Placement3d(id) = item else {
1205 cx.warn("transform item is not an AXIS2_PLACEMENT_3D".to_owned());
1206 return None;
1207 };
1208 let a = cx.model.axis2_placement3d_arena.get(id.0);
1209 Some(Placement {
1210 origin: point3(cx, &a.location),
1211 axis: a.axis.as_ref().map_or([0.0, 0.0, 1.0], |d| dir3(cx, d)),
1212 ref_direction: a
1213 .ref_direction
1214 .as_ref()
1215 .map_or([1.0, 0.0, 0.0], |d| dir3(cx, d)),
1216 })
1217}
1218
1219fn point3(cx: Ctx<'_>, r: &m::CartesianPointRef) -> [f64; 3] {
1221 if let m::CartesianPointRef::CartesianPoint(id) = r {
1222 let c = &cx.model.cartesian_point_arena.get(id.0).coordinates;
1223 [
1224 c.first().copied().unwrap_or(0.0),
1225 c.get(1).copied().unwrap_or(0.0),
1226 c.get(2).copied().unwrap_or(0.0),
1227 ]
1228 } else {
1229 [0.0; 3]
1230 }
1231}
1232
1233fn dir3(cx: Ctx<'_>, r: &m::DirectionRef) -> [f64; 3] {
1235 if let m::DirectionRef::Direction(id) = r {
1236 let c = &cx.model.direction_arena.get(id.0).direction_ratios;
1237 [
1238 c.first().copied().unwrap_or(0.0),
1239 c.get(1).copied().unwrap_or(0.0),
1240 c.get(2).copied().unwrap_or(0.0),
1241 ]
1242 } else {
1243 [0.0; 3]
1244 }
1245}
1246
1247fn pdor_is(r: &m::ProductDefinitionOrReferenceRef, id: m::ProductDefinitionId) -> bool {
1249 matches!(r, m::ProductDefinitionOrReferenceRef::ProductDefinition(i) if *i == id)
1250}
1251
1252fn pdor_to_def<'m>(cx: Ctx<'m>, r: &m::ProductDefinitionOrReferenceRef) -> Option<ProductDef<'m>> {
1255 match r {
1256 m::ProductDefinitionOrReferenceRef::ProductDefinition(i) => Some(ProductDef { cx, id: *i }),
1257 _ => None,
1258 }
1259}
1260
1261fn collect_solids_from_repr<'m>(
1269 cx: Ctx<'m>,
1270 r: &m::RepresentationRef,
1271 out: &mut Vec<Solid<'m>>,
1272 visited: &mut HashSet<m::EntityKey>,
1273) {
1274 let (key, items): (m::EntityKey, &[m::RepresentationItemRef]) = match r {
1275 m::RepresentationRef::ShapeRepresentation(i) => (
1276 m::EntityKey::ShapeRepresentation(*i),
1277 &cx.model.shape_representation_arena.get(i.0).items,
1278 ),
1279 m::RepresentationRef::AdvancedBrepShapeRepresentation(i) => (
1280 m::EntityKey::AdvancedBrepShapeRepresentation(*i),
1281 &cx.model
1282 .advanced_brep_shape_representation_arena
1283 .get(i.0)
1284 .items,
1285 ),
1286 _ => return,
1287 };
1288 if !visited.insert(key) {
1289 return;
1290 }
1291 for it in items {
1292 if let m::RepresentationItemRef::ManifoldSolidBrep(sid) = it {
1293 out.push(Solid::from_id(cx, *sid));
1294 }
1295 }
1296 let rg = cx.ref_graph();
1298 for referrer in rg.referrers(key) {
1299 let m::EntityKey::ShapeRepresentationRelationship(srr_id) = referrer else {
1300 continue;
1301 };
1302 let srr = cx
1303 .model
1304 .shape_representation_relationship_arena
1305 .get(srr_id.0);
1306 let (k1, k2) = (srr.rep_1.entity_key(), srr.rep_2.entity_key());
1307 let other = if k1 == key {
1308 k2
1309 } else if k2 == key {
1310 k1
1311 } else {
1312 continue;
1313 };
1314 if let Ok(other_ref) = m::RepresentationRef::from_any(other) {
1315 collect_solids_from_repr(cx, &other_ref, out, visited);
1316 }
1317 }
1318}
1319
1320#[derive(Clone, Copy)]
1327pub struct Approval<'m> {
1328 cx: Ctx<'m>,
1329 id: m::ApprovalId,
1330 scope: Scope,
1331}
1332
1333impl<'m> Approval<'m> {
1334 fn raw(&self) -> &'m m::Approval {
1335 self.cx.model.approval_arena.get(self.id.0)
1336 }
1337
1338 pub fn key(&self) -> m::EntityKey {
1340 m::EntityKey::Approval(self.id)
1341 }
1342
1343 pub fn assigned_scope(&self) -> Scope {
1346 self.scope
1347 }
1348
1349 pub fn assigned_targets(&self) -> Vec<Target<'m>> {
1352 let cx = self.cx;
1353 let mut keys: Vec<m::EntityKey> = Vec::new();
1354 for r in cx.ref_graph().referrers(self.key()) {
1355 match r {
1356 m::EntityKey::CcDesignApproval(i) => keys.extend(
1357 cx.model
1358 .cc_design_approval_arena
1359 .get(i.0)
1360 .items
1361 .iter()
1362 .map(m::ApprovedItemRef::entity_key),
1363 ),
1364 m::EntityKey::AppliedApprovalAssignment(i) => keys.extend(
1365 cx.model
1366 .applied_approval_assignment_arena
1367 .get(i.0)
1368 .items
1369 .iter()
1370 .map(m::ApprovalItemRef::entity_key),
1371 ),
1372 _ => {}
1373 }
1374 }
1375 targets_from_keys(cx, keys.into_iter())
1376 }
1377
1378 pub fn status(&self) -> &'m str {
1380 let m::ApprovalStatusRef::ApprovalStatus(sid) = &self.raw().status;
1381 &self.cx.model.approval_status_arena.get(sid.0).name
1382 }
1383
1384 pub fn level(&self) -> &'m str {
1386 &self.raw().level
1387 }
1388
1389 pub fn approvers(&self) -> Vec<Approver<'m>> {
1392 let cx = self.cx;
1393 let mut out: Vec<Approver<'m>> = Vec::new();
1394 for r in cx.ref_graph().referrers(self.key()) {
1395 if let m::EntityKey::ApprovalPersonOrganization(id) = r {
1396 out.push(Approver { cx, id: *id });
1397 }
1398 }
1399 out
1400 }
1401
1402 pub fn date(&self) -> Option<ApprovalDate> {
1410 let cx = self.cx;
1411 for r in cx.ref_graph().referrers(self.key()) {
1412 if let m::EntityKey::ApprovalDateTime(id) = r {
1413 let dt = &cx.model.approval_date_time_arena.get(id.0).date_time;
1414 return resolve_datetime(cx, dt);
1415 }
1416 }
1417 None
1418 }
1419}
1420
1421#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1426pub struct ApprovalDate {
1427 pub year: Option<i64>,
1428 pub month: Option<i64>,
1429 pub day: Option<i64>,
1430 pub hour: Option<i64>,
1431 pub minute: Option<i64>,
1432}
1433
1434fn resolve_datetime(cx: Ctx<'_>, r: &m::DateTimeSelectRef) -> Option<ApprovalDate> {
1439 let mut d = ApprovalDate::default();
1440 match r {
1441 m::DateTimeSelectRef::CalendarDate(i) => set_calendar(cx, &mut d, *i),
1442 m::DateTimeSelectRef::Date(i) => {
1443 d.year = Some(cx.model.date_arena.get(i.0).year_component);
1444 }
1445 m::DateTimeSelectRef::DateAndTime(i) => {
1446 let dt = cx.model.date_and_time_arena.get(i.0);
1447 set_date(cx, &mut d, &dt.date_component);
1448 let m::LocalTimeRef::LocalTime(lt) = &dt.time_component;
1449 set_time(cx, &mut d, *lt);
1450 }
1451 m::DateTimeSelectRef::LocalTime(i) => set_time(cx, &mut d, *i),
1452 m::DateTimeSelectRef::Complex(_) => {
1453 cx.warn(
1454 "APPROVAL_DATE_TIME.date_time is a complex instance; date left unread".to_owned(),
1455 );
1456 return None;
1457 }
1458 }
1459 Some(d)
1460}
1461
1462fn set_date(cx: Ctx<'_>, d: &mut ApprovalDate, r: &m::DateRef) {
1464 match r {
1465 m::DateRef::CalendarDate(i) => set_calendar(cx, d, *i),
1466 m::DateRef::Date(i) => d.year = Some(cx.model.date_arena.get(i.0).year_component),
1467 m::DateRef::Complex(_) => {}
1468 }
1469}
1470
1471fn set_calendar(cx: Ctx<'_>, d: &mut ApprovalDate, i: m::CalendarDateId) {
1472 let c = cx.model.calendar_date_arena.get(i.0);
1473 d.year = Some(c.year_component);
1474 d.month = Some(c.month_component);
1475 d.day = Some(c.day_component);
1476}
1477
1478fn set_time(cx: Ctx<'_>, d: &mut ApprovalDate, i: m::LocalTimeId) {
1479 let t = cx.model.local_time_arena.get(i.0);
1480 d.hour = Some(t.hour_component);
1481 d.minute = t.minute_component;
1482}
1483
1484#[derive(Clone, Copy)]
1486pub struct Approver<'m> {
1487 cx: Ctx<'m>,
1488 id: m::ApprovalPersonOrganizationId,
1489}
1490
1491impl<'m> Approver<'m> {
1492 fn raw(&self) -> &'m m::ApprovalPersonOrganization {
1493 self.cx
1494 .model
1495 .approval_person_organization_arena
1496 .get(self.id.0)
1497 }
1498
1499 pub fn role(&self) -> &'m str {
1501 let m::ApprovalRoleRef::ApprovalRole(rid) = &self.raw().role;
1502 &self.cx.model.approval_role_arena.get(rid.0).role
1503 }
1504
1505 pub fn person(&self) -> Option<Person<'m>> {
1508 let pid = match &self.raw().person_organization {
1509 m::PersonOrganizationSelectRef::Person(i) => *i,
1510 m::PersonOrganizationSelectRef::PersonAndOrganization(i) => {
1511 let m::PersonRef::Person(p) = &self
1512 .cx
1513 .model
1514 .person_and_organization_arena
1515 .get(i.0)
1516 .the_person;
1517 *p
1518 }
1519 m::PersonOrganizationSelectRef::Organization(_) => return None,
1520 };
1521 Some(Person {
1522 cx: self.cx,
1523 id: pid,
1524 })
1525 }
1526
1527 pub fn organization(&self) -> Option<&'m str> {
1530 let oid = match &self.raw().person_organization {
1531 m::PersonOrganizationSelectRef::Organization(i) => *i,
1532 m::PersonOrganizationSelectRef::PersonAndOrganization(i) => {
1533 let m::OrganizationRef::Organization(o) = &self
1534 .cx
1535 .model
1536 .person_and_organization_arena
1537 .get(i.0)
1538 .the_organization;
1539 *o
1540 }
1541 m::PersonOrganizationSelectRef::Person(_) => return None,
1542 };
1543 Some(&self.cx.model.organization_arena.get(oid.0).name)
1544 }
1545}
1546
1547#[derive(Clone, Copy)]
1548enum DocImpl {
1549 Document(m::DocumentId),
1550 DocumentFile(m::DocumentFileId),
1551}
1552
1553#[derive(Clone, Copy)]
1559pub struct Document<'m> {
1560 cx: Ctx<'m>,
1561 which: DocImpl,
1562 scope: Scope,
1563}
1564
1565impl<'m> Document<'m> {
1566 pub fn assigned_scope(&self) -> Scope {
1568 self.scope
1569 }
1570
1571 pub fn assigned_targets(&self) -> Vec<Target<'m>> {
1574 let cx = self.cx;
1575 let mut keys: Vec<m::EntityKey> = Vec::new();
1576 for r in cx.ref_graph().referrers(self.key()) {
1577 if let m::EntityKey::AppliedDocumentReference(i) = r {
1578 keys.extend(
1579 cx.model
1580 .applied_document_reference_arena
1581 .get(i.0)
1582 .items
1583 .iter()
1584 .map(m::DocumentReferenceItemRef::entity_key),
1585 );
1586 }
1587 }
1588 targets_from_keys(cx, keys.into_iter())
1589 }
1590
1591 fn fields(&self) -> (&'m str, &'m str, Option<&'m str>, &'m m::DocumentTypeRef) {
1594 let model = self.cx.model;
1595 match self.which {
1596 DocImpl::Document(i) => {
1597 let d = model.document_arena.get(i.0);
1598 (&d.id, &d.name, d.description.as_deref(), &d.kind)
1599 }
1600 DocImpl::DocumentFile(i) => {
1601 let d = model.document_file_arena.get(i.0);
1602 (&d.id, &d.name, d.description.as_deref(), &d.kind)
1603 }
1604 }
1605 }
1606
1607 pub fn key(&self) -> m::EntityKey {
1609 match self.which {
1610 DocImpl::Document(i) => m::EntityKey::Document(i),
1611 DocImpl::DocumentFile(i) => m::EntityKey::DocumentFile(i),
1612 }
1613 }
1614
1615 pub fn id(&self) -> &'m str {
1617 self.fields().0
1618 }
1619
1620 pub fn name(&self) -> &'m str {
1622 self.fields().1
1623 }
1624
1625 pub fn description(&self) -> Option<&'m str> {
1627 self.fields().2
1628 }
1629
1630 pub fn kind(&self) -> &'m str {
1633 let m::DocumentTypeRef::DocumentType(tid) = self.fields().3;
1634 &self
1635 .cx
1636 .model
1637 .document_type_arena
1638 .get(tid.0)
1639 .product_data_type
1640 }
1641}
1642
1643#[derive(Clone, Copy)]
1646pub struct SecurityClassification<'m> {
1647 cx: Ctx<'m>,
1648 id: m::SecurityClassificationId,
1649 scope: Scope,
1650}
1651
1652impl<'m> SecurityClassification<'m> {
1653 fn raw(&self) -> &'m m::SecurityClassification {
1654 self.cx.model.security_classification_arena.get(self.id.0)
1655 }
1656
1657 pub fn key(&self) -> m::EntityKey {
1659 m::EntityKey::SecurityClassification(self.id)
1660 }
1661
1662 pub fn assigned_scope(&self) -> Scope {
1664 self.scope
1665 }
1666
1667 pub fn assigned_targets(&self) -> Vec<Target<'m>> {
1670 let cx = self.cx;
1671 let mut keys: Vec<m::EntityKey> = Vec::new();
1672 for r in cx.ref_graph().referrers(self.key()) {
1673 match r {
1674 m::EntityKey::CcDesignSecurityClassification(i) => keys.extend(
1675 cx.model
1676 .cc_design_security_classification_arena
1677 .get(i.0)
1678 .items
1679 .iter()
1680 .map(m::CcClassifiedItemRef::entity_key),
1681 ),
1682 m::EntityKey::AppliedSecurityClassificationAssignment(i) => keys.extend(
1683 cx.model
1684 .applied_security_classification_assignment_arena
1685 .get(i.0)
1686 .items
1687 .iter()
1688 .map(m::SecurityClassificationItemRef::entity_key),
1689 ),
1690 _ => {}
1691 }
1692 }
1693 targets_from_keys(cx, keys.into_iter())
1694 }
1695
1696 pub fn name(&self) -> &'m str {
1698 &self.raw().name
1699 }
1700
1701 pub fn purpose(&self) -> &'m str {
1703 &self.raw().purpose
1704 }
1705
1706 pub fn level(&self) -> &'m str {
1709 let m::SecurityClassificationLevelRef::SecurityClassificationLevel(lid) =
1710 &self.raw().security_level;
1711 &self
1712 .cx
1713 .model
1714 .security_classification_level_arena
1715 .get(lid.0)
1716 .name
1717 }
1718}