Skip to main content

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}