1use ifc_model::{EntityId, Model, Transaction, Value};
20use ifc_schema::Schema;
21
22use crate::authoring::{build_named, invalid_authoring, validate_ref};
23use crate::StyleResult;
24
25#[derive(Debug, Clone, Copy)]
27pub struct ImageTextureDraft<'a> {
28 pub repeat_s: bool,
30 pub repeat_t: bool,
32 pub mode: Option<&'a str>,
34 pub texture_transform: Option<EntityId>,
36 pub url_reference: &'a str,
38}
39
40pub fn create_image_texture(
54 tx: &mut Transaction,
55 model: &Model,
56 schema: &Schema,
57 draft: ImageTextureDraft<'_>,
58) -> StyleResult<EntityId> {
59 if draft.url_reference.trim().is_empty() {
60 return Err(invalid_authoring(
61 "IfcImageTexture",
62 "URLReference",
63 "expected a reference to an image, found nothing",
64 ));
65 }
66
67 if let Some(transform) = draft.texture_transform {
68 validate_ref(
69 tx,
70 model,
71 schema,
72 transform,
73 "IfcCartesianTransformationOperator2D",
74 )?;
75 }
76
77 let mut values = vec![
78 ("RepeatS", Value::Bool(draft.repeat_s)),
79 ("RepeatT", Value::Bool(draft.repeat_t)),
80 ("URLReference", Value::Text(draft.url_reference.into())),
81 ];
82 if let Some(mode) = draft.mode {
83 values.push(("Mode", Value::Text(mode.into())));
84 }
85 if let Some(transform) = draft.texture_transform {
86 values.push(("TextureTransform", Value::Ref(transform)));
87 }
88
89 Ok(tx.create(build_named(schema, "IfcImageTexture", values)?))
90}
91
92pub fn create_texture_map(
101 tx: &mut Transaction,
102 model: &Model,
103 schema: &Schema,
104 maps: &[EntityId],
105 vertices: &[EntityId],
106 mapped_to: EntityId,
107) -> StyleResult<EntityId> {
108 if maps.is_empty() {
109 return Err(invalid_authoring(
110 "IfcTextureMap",
111 "Maps",
112 "expected at least one surface texture, found none",
113 ));
114 }
115 if vertices.len() < 3 {
116 return Err(invalid_authoring(
117 "IfcTextureMap",
118 "Vertices",
119 format!("expected at least 3 vertices, found {}", vertices.len()),
120 ));
121 }
122
123 validate_ref(tx, model, schema, mapped_to, "IfcFace")?;
124
125 let values = vec![
126 (
127 "Maps",
128 Value::List(maps.iter().copied().map(Value::Ref).collect()),
129 ),
130 (
131 "Vertices",
132 Value::List(vertices.iter().copied().map(Value::Ref).collect()),
133 ),
134 ("MappedTo", Value::Ref(mapped_to)),
135 ];
136
137 Ok(tx.create(build_named(schema, "IfcTextureMap", values)?))
138}
139
140pub fn create_texture_coordinate_generator(
152 tx: &mut Transaction,
153 schema: &Schema,
154 maps: &[EntityId],
155 mode: &str,
156 parameters: &[f64],
157) -> StyleResult<EntityId> {
158 if maps.is_empty() {
159 return Err(invalid_authoring(
160 "IfcTextureCoordinateGenerator",
161 "Maps",
162 "expected at least one surface texture, found none",
163 ));
164 }
165 if mode.trim().is_empty() {
166 return Err(invalid_authoring(
167 "IfcTextureCoordinateGenerator",
168 "Mode",
169 "expected a generating function, found nothing",
170 ));
171 }
172 if let Some(value) = parameters.iter().find(|value| !value.is_finite()) {
173 return Err(invalid_authoring(
174 "IfcTextureCoordinateGenerator",
175 "Parameter",
176 format!("expected finite parameters, found {value}"),
177 ));
178 }
179
180 let mut values = vec![
181 (
182 "Maps",
183 Value::List(maps.iter().copied().map(Value::Ref).collect()),
184 ),
185 ("Mode", Value::Text(mode.into())),
186 ];
187 if !parameters.is_empty() {
188 values.push((
189 "Parameter",
190 Value::List(parameters.iter().copied().map(Value::Real).collect()),
191 ));
192 }
193
194 Ok(tx.create(build_named(
195 schema,
196 "IfcTextureCoordinateGenerator",
197 values,
198 )?))
199}