Skip to main content

ifc_lite_processing/appearance/
types.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize, Serialize)]
8#[serde(rename_all = "camelCase")]
9pub enum RepresentationPolicy {
10    #[default]
11    Preserve,
12    EvaluatedOccurrence,
13}
14
15#[derive(Debug, Clone, Deserialize, Serialize)]
16#[serde(rename_all = "camelCase", deny_unknown_fields)]
17pub struct AppearanceRequest {
18    /// Parametric conversion is never implicit.
19    #[serde(default)]
20    pub representation_policy: RepresentationPolicy,
21    pub schema: String,
22    pub source_revision: String,
23    pub next_express_id: u32,
24    pub product_ids: Vec<u32>,
25    pub image_uri: String,
26    pub repeat_s: bool,
27    pub repeat_t: bool,
28    pub mapping: Mapping,
29    /// Reviewable surface targeting for evaluated occurrence conversions only.
30    /// A mask binds to the `surface_fingerprint` a previous plan reported.
31    #[serde(default, skip_serializing_if = "Vec::is_empty")]
32    pub face_masks: Vec<FaceMask>,
33}
34/// Ascending or unordered source triangle ordinals of one product's evaluated
35/// surface. A stale fingerprint is an explicit exclusion, never a silent reuse.
36#[derive(Debug, Clone, Deserialize, Serialize)]
37#[serde(rename_all = "camelCase", deny_unknown_fields)]
38pub struct FaceMask {
39    pub product_id: u32,
40    pub surface_fingerprint: String,
41    pub triangles: Vec<u32>,
42}
43#[derive(Debug, Clone, Deserialize, Serialize)]
44#[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)]
45pub enum Mapping {
46    #[serde(rename_all = "camelCase")]
47    ExistingUv {
48        scale: [f64; 2],
49        offset: [f64; 2],
50        rotation_radians: f64,
51    },
52    #[serde(rename_all = "camelCase")]
53    Planar {
54        frame: MappingFrame,
55        origin: [f64; 3],
56        axis_u: [f64; 3],
57        axis_v: [f64; 3],
58        metres_per_tile: [f64; 2],
59    },
60    #[serde(rename_all = "camelCase")]
61    Box {
62        frame: MappingFrame,
63        origin: [f64; 3],
64        metres_per_tile: [f64; 3],
65    },
66}
67#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
68#[serde(rename_all = "camelCase")]
69pub enum MappingFrame {
70    Item,
71    World,
72}
73#[derive(Debug, Clone, Serialize)]
74#[serde(rename_all = "camelCase")]
75pub struct CreatedEntity {
76    pub express_id: u32,
77    pub r#type: String,
78    /// StoreEditor wire values: '#N' references, '.ENUM.' tokens, JSON lists/numbers/null.
79    pub attributes: Vec<Value>,
80}
81#[derive(Debug, Clone, Serialize)]
82#[serde(rename_all = "camelCase")]
83pub struct PositionalEdit {
84    pub express_id: u32,
85    pub index: usize,
86    pub value: Value,
87}
88#[derive(Debug, Clone, Serialize)]
89#[serde(rename_all = "camelCase")]
90pub struct AppearanceItem {
91    pub product_id: u32,
92    pub geometry_item_id: u32,
93    /// Source IFC bottom-left texture coordinates, before mesher seam expansion.
94    pub tex_coords: Vec<[f64; 2]>,
95    /// 1-based, parallel to source CoordIndex (before winding correction).
96    pub tex_coord_index: Vec<[u32; 3]>,
97    /// Actual final source topology after canonical placement and welding.
98    pub source_indices: Vec<u32>,
99    /// Final canonical target topology; becomes provenance after Apply.
100    pub target_indices: Vec<u32>,
101    /// Canonical target vertex pool size; removed degenerate triangles can leave
102    /// unused vertices, so the maximum index is not bounded by corner count.
103    pub target_vertex_count: usize,
104    /// UV pairs in triangle-corner order, with GPU V flip. Fragment consumers
105    /// remap canonical corner slots and expand seams without moving geometry.
106    pub preview_corner_uvs: Vec<f32>,
107    /// Final canonical shading normals in triangle-corner order, converted from
108    /// IFC Z-up to renderer Y-up as [nx, nz, -ny], matching MeshDataJs.
109    /// UV-dependent welding may choose a different normal representative.
110    pub target_corner_normals: Vec<f32>,
111}
112#[derive(Debug, Clone, Serialize)]
113#[serde(rename_all = "camelCase")]
114pub struct Exclusion {
115    pub product_id: u32,
116    pub reason: String,
117}
118#[derive(Debug, Clone, Default, Serialize)]
119#[serde(rename_all = "camelCase")]
120pub struct AppearancePlan {
121    /// Explicit occurrence-local conversions, with original renderer provenance.
122    pub conversions: Vec<AppearanceConversion>,
123    pub source_revision: String,
124    pub next_express_id: u32,
125    pub next_available_express_id: u32,
126    pub created: Vec<CreatedEntity>,
127    pub edits: Vec<PositionalEdit>,
128    pub removed: Vec<u32>,
129    pub items: Vec<AppearanceItem>,
130    pub exclusions: Vec<Exclusion>,
131}
132
133#[derive(Debug, Clone, Serialize)]
134#[serde(rename_all = "camelCase")]
135pub struct AppearanceConversion {
136    pub product_id: u32,
137    pub representation_id: u32,
138    pub source_geometry_item_id: u32,
139    pub geometry_item_id: u32,
140    pub source_indices: Vec<u32>,
141    /// Canonical evaluated IFC Z-up geometry, before any appearance replacement.
142    /// Coordinates are local f32 values plus source_origin and rtc_offset in metres.
143    pub source_positions: Vec<f32>,
144    pub source_normals: Vec<f32>,
145    pub source_origin: [f64; 3],
146    pub source_color: [f32; 4],
147    pub rtc_offset: [f64; 3],
148    /// Hex SHA-256 of the product GlobalId, its quantised local evaluated
149    /// surface and topology. Face masks bind to this value; express ids are
150    /// excluded so a renumbered export keeps its selection. The surface uses a
151    /// precision-preserving product-local evaluation on every target, so a pure
152    /// placement edit keeps the value while geometry or topology edits change it.
153    pub surface_fingerprint: String,
154    /// Accepted ascending source triangle ordinals that received the
155    /// appearance. Absent when the whole surface was converted.
156    #[serde(skip_serializing_if = "Option::is_none")]
157    pub masked_triangles: Option<Vec<u32>>,
158    /// The unmasked IfcTriangulatedFaceSet under the same Body wrapper. It keeps
159    /// the source surface style and the complementary source triangles.
160    #[serde(skip_serializing_if = "Option::is_none")]
161    pub retained_geometry_item_id: Option<u32>,
162    /// Canonical opening meshes removed when their Body becomes Reference.
163    /// Inner fields use the shared MeshData wire contract; origin + rtc_offset
164    /// restores IFC Z-up metres. These owners are companion dependency roots.
165    #[serde(skip_serializing_if = "Vec::is_empty")]
166    pub source_removed_meshes: Vec<crate::types::mesh::MeshData>,
167}