Skip to main content

runmat_geometry_core/model/
regions.rs

1use serde::{Deserialize, Serialize};
2
3use crate::selection::EntityKind;
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
6pub struct Region {
7    pub region_id: String,
8    pub name: String,
9    pub tag: Option<String>,
10    #[serde(default, skip_serializing_if = "Option::is_none")]
11    pub cad_ownership: Option<CadRegionOwnership>,
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "snake_case")]
16pub enum CadSemanticKind {
17    Assembly,
18    Component,
19    Reference,
20    Body,
21    Compound,
22    Face,
23    Subshape,
24    Layer,
25    Color,
26    Material,
27    Shape,
28    Unknown,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
32#[serde(rename_all = "camelCase")]
33pub struct CadLabelRef {
34    pub label_entry: String,
35    pub name: String,
36    pub kind: CadSemanticKind,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
40#[serde(rename_all = "camelCase")]
41pub struct CadColorEvidence {
42    pub source: String,
43    pub color_type: String,
44    pub hex_rgba: String,
45}
46
47#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
48#[serde(rename_all = "camelCase")]
49pub struct CadPhysicalMaterialEvidence {
50    pub label_entry: String,
51    pub name: String,
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    pub description: Option<String>,
54    #[serde(default, skip_serializing_if = "Option::is_none")]
55    pub density: Option<String>,
56    #[serde(default, skip_serializing_if = "Option::is_none")]
57    pub density_name: Option<String>,
58    #[serde(default, skip_serializing_if = "Option::is_none")]
59    pub density_value_type: Option<String>,
60}
61
62#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
63#[serde(rename_all = "camelCase")]
64pub struct CadRegionOwnership {
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub face_id: Option<u64>,
67    #[serde(default, skip_serializing_if = "Option::is_none")]
68    pub label: Option<CadLabelRef>,
69    #[serde(default, skip_serializing_if = "Vec::is_empty")]
70    pub owner_path: Vec<CadLabelRef>,
71    #[serde(default, skip_serializing_if = "Vec::is_empty")]
72    pub layers: Vec<String>,
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub color: Option<CadColorEvidence>,
75    #[serde(default, skip_serializing_if = "Option::is_none")]
76    pub material: Option<CadPhysicalMaterialEvidence>,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
80#[serde(rename_all = "camelCase")]
81pub struct EntityIdRange {
82    pub start: u64,
83    pub count: u64,
84}
85
86impl EntityIdRange {
87    pub fn new(start: u64, count: u64) -> Self {
88        Self { start, count }
89    }
90
91    pub fn end_exclusive(&self) -> Option<u64> {
92        self.start.checked_add(self.count)
93    }
94
95    pub fn contains(&self, entity_id: u64) -> bool {
96        self.end_exclusive()
97            .is_some_and(|end| entity_id >= self.start && entity_id < end)
98    }
99}
100
101#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
102#[serde(rename_all = "camelCase")]
103pub struct RegionEntityMapping {
104    pub region_id: String,
105    pub mesh_id: String,
106    pub entity_kind: EntityKind,
107    pub ranges: Vec<EntityIdRange>,
108}
109
110impl RegionEntityMapping {
111    pub fn new(
112        region_id: impl Into<String>,
113        mesh_id: impl Into<String>,
114        entity_kind: EntityKind,
115        ranges: Vec<EntityIdRange>,
116    ) -> Self {
117        Self {
118            region_id: region_id.into(),
119            mesh_id: mesh_id.into(),
120            entity_kind,
121            ranges,
122        }
123    }
124
125    pub fn all_faces(
126        region_id: impl Into<String>,
127        mesh_id: impl Into<String>,
128        face_count: u64,
129    ) -> Self {
130        Self::new(
131            region_id,
132            mesh_id,
133            EntityKind::Face,
134            if face_count == 0 {
135                Vec::new()
136            } else {
137                vec![EntityIdRange::new(0, face_count)]
138            },
139        )
140    }
141
142    pub fn entity_count(&self) -> u64 {
143        self.ranges.iter().map(|range| range.count).sum()
144    }
145
146    pub fn contains_entity(&self, entity_id: u64) -> bool {
147        self.ranges.iter().any(|range| range.contains(entity_id))
148    }
149}