ifc_style/texture/map.rs
1//! Indexed texture-map projection.
2
3use ifc_model::{EntityId, Value};
4
5use crate::error::StyleResult;
6use crate::view::Record;
7
8mod triangle;
9
10pub use triangle::TriangleTextureCoordinates;
11
12/// Borrowed projection of an indexed texture map (`IfcIndexedTriangleTextureMap`
13/// or `IfcIndexedPolygonalTextureMap`): binds a texture to a tessellated
14/// face set via per-vertex texture-coordinate indices.
15#[derive(Debug, Clone, Copy)]
16pub struct IndexedTextureMap<'m, 's> {
17 record: Record<'m, 's>,
18}
19
20impl<'m, 's> IndexedTextureMap<'m, 's> {
21 pub(crate) fn from_record(record: Record<'m, 's>) -> Self {
22 Self { record }
23 }
24
25 /// The entity id of this texture map.
26 pub fn id(&self) -> EntityId {
27 self.record.id
28 }
29
30 /// The concrete IFC entity type name.
31 pub fn type_name(&self) -> &'m str {
32 &self.record.entity.type_name
33 }
34
35 /// The `Maps` attribute: the textures this map applies. Mandatory.
36 ///
37 /// `LIST [1:?] OF IfcSurfaceTexture` in both IFC4 and IFC4X3. It is a
38 /// list even when, as is usual, it holds a single texture.
39 pub fn maps(&self) -> StyleResult<Vec<EntityId>> {
40 self.record
41 .required_refs("Maps", "IfcSurfaceTexture", 1, None)
42 }
43
44 /// The `MappedTo` attribute: the tessellated face set this map applies to. Mandatory.
45 pub fn mapped_to(&self) -> StyleResult<EntityId> {
46 self.record
47 .required_ref("MappedTo", "IfcTessellatedFaceSet")
48 }
49
50 /// The `TexCoordIndex` attribute: per-face indices into `TexCoords`, when authored.
51 pub fn tex_coord_index(&self) -> StyleResult<Option<&'m Value>> {
52 self.record.optional_raw("TexCoordIndex")
53 }
54
55 /// The `TexCoords` attribute: the indexed texture-vertex list. Mandatory.
56 pub fn tex_coords(&self) -> StyleResult<EntityId> {
57 self.record
58 .required_ref("TexCoords", "IfcTextureVertexList")
59 }
60
61 /// Per-corner texture coordinates for an `IfcIndexedTriangleTextureMap`.
62 ///
63 /// `triangle_count` is the `CoordIndex` length of the mapped face set.
64 /// Returns `Ok(None)` for the polygonal variant, which indexes through
65 /// `IfcTextureCoordinateIndices` instead, and when `TexCoordIndex` is
66 /// omitted, whose meaning the schema leaves undefined.
67 ///
68 /// # Errors
69 ///
70 /// A malformed `TexCoordsList`, a `TexCoordIndex` entry that addresses a
71 /// missing texture vertex, or more mapped triangles than the face set has.
72 pub fn triangle_coordinates(
73 &self,
74 triangle_count: usize,
75 ) -> StyleResult<Option<TriangleTextureCoordinates>> {
76 let record = &self.record;
77 if !record
78 .schema
79 .is_a(&record.entity.type_name, "IfcIndexedTriangleTextureMap")
80 {
81 return Ok(None);
82 }
83 let Some(index) = self.tex_coord_index()? else {
84 return Ok(None);
85 };
86 let list = crate::StyleView::new(record.model, record.schema)
87 .texture_vertex_list(self.tex_coords()?)?;
88 triangle::resolve(
89 self.id(),
90 self.type_name(),
91 index,
92 list.coordinates()?,
93 triangle_count,
94 )
95 .map(Some)
96 }
97}