1use crate::generated::model as m;
10use crate::scene::Ctx;
11
12#[derive(Clone, Copy, Debug, PartialEq)]
14pub struct Rgb {
15 pub red: f64,
16 pub green: f64,
17 pub blue: f64,
18}
19
20pub(crate) fn colour_of(cx: Ctx<'_>, target: m::EntityKey) -> Option<Rgb> {
22 let rg = cx.ref_graph();
23 for r in rg.referrers(target) {
24 let m::EntityKey::StyledItem(sid) = r else {
27 continue;
28 };
29 let si = cx.model.styled_item_arena.get(sid.0);
30 if let Some(rgb) = si.styles.iter().find_map(|psa| psa_colour(cx, psa)) {
31 return Some(rgb);
32 }
33 }
34 None
35}
36
37fn psa_colour(cx: Ctx<'_>, r: &m::PresentationStyleAssignmentRef) -> Option<Rgb> {
38 let styles: &[m::PresentationStyleSelectRef] = match r {
39 m::PresentationStyleAssignmentRef::PresentationStyleAssignment(i) => {
40 &cx.model.presentation_style_assignment_arena.get(i.0).styles
41 }
42 m::PresentationStyleAssignmentRef::PresentationStyleByContext(i) => {
43 &cx.model.presentation_style_by_context_arena.get(i.0).styles
44 }
45 m::PresentationStyleAssignmentRef::Complex(_) => {
46 cx.warn("styled item uses a complex presentation style assignment".to_owned());
47 return None;
48 }
49 };
50 styles.iter().find_map(|s| style_select_colour(cx, s))
51}
52
53fn style_select_colour(cx: Ctx<'_>, r: &m::PresentationStyleSelectRef) -> Option<Rgb> {
54 match r {
55 m::PresentationStyleSelectRef::SurfaceStyleUsage(i) => {
56 let ssu = cx.model.surface_style_usage_arena.get(i.0);
57 side_style_colour(cx, &ssu.style)
58 }
59 m::PresentationStyleSelectRef::FillAreaStyle(i) => fill_area_colour(cx, *i),
60 m::PresentationStyleSelectRef::Complex(_) => {
61 cx.warn("complex presentation style".to_owned());
62 None
63 }
64 _ => None,
66 }
67}
68
69fn side_style_colour(cx: Ctx<'_>, r: &m::SurfaceSideStyleSelectRef) -> Option<Rgb> {
70 match r {
71 m::SurfaceSideStyleSelectRef::SurfaceSideStyle(i) => cx
72 .model
73 .surface_side_style_arena
74 .get(i.0)
75 .styles
76 .iter()
77 .find_map(|e| element_colour(cx, e)),
78 m::SurfaceSideStyleSelectRef::Complex(_) => {
79 cx.warn("complex surface side style".to_owned());
80 None
81 }
82 m::SurfaceSideStyleSelectRef::PreDefinedSurfaceSideStyle(_) => None,
83 }
84}
85
86fn element_colour(cx: Ctx<'_>, r: &m::SurfaceStyleElementSelectRef) -> Option<Rgb> {
87 match r {
88 m::SurfaceStyleElementSelectRef::SurfaceStyleFillArea(i) => fill_area_ref_colour(
89 cx,
90 &cx.model.surface_style_fill_area_arena.get(i.0).fill_area,
91 ),
92 m::SurfaceStyleElementSelectRef::SurfaceStyleRendering(i) => colour_to_rgb(
93 cx,
94 &cx.model
95 .surface_style_rendering_arena
96 .get(i.0)
97 .surface_colour,
98 ),
99 m::SurfaceStyleElementSelectRef::SurfaceStyleRenderingWithProperties(i) => colour_to_rgb(
100 cx,
101 &cx.model
102 .surface_style_rendering_with_properties_arena
103 .get(i.0)
104 .surface_colour,
105 ),
106 m::SurfaceStyleElementSelectRef::Complex(_) => {
107 cx.warn("complex surface style element".to_owned());
108 None
109 }
110 _ => None,
112 }
113}
114
115fn fill_area_ref_colour(cx: Ctx<'_>, r: &m::FillAreaStyleRef) -> Option<Rgb> {
116 match r {
117 m::FillAreaStyleRef::FillAreaStyle(i) => fill_area_colour(cx, *i),
118 m::FillAreaStyleRef::Complex(_) => {
119 cx.warn("complex fill area style".to_owned());
120 None
121 }
122 }
123}
124
125fn fill_area_colour(cx: Ctx<'_>, id: m::FillAreaStyleId) -> Option<Rgb> {
126 cx.model
127 .fill_area_style_arena
128 .get(id.0)
129 .fill_styles
130 .iter()
131 .find_map(|f| match f {
132 m::FillStyleSelectRef::FillAreaStyleColour(i) => colour_to_rgb(
133 cx,
134 &cx.model.fill_area_style_colour_arena.get(i.0).fill_colour,
135 ),
136 m::FillStyleSelectRef::Complex(_) => {
137 cx.warn("complex fill style".to_owned());
138 None
139 }
140 _ => None,
142 })
143}
144
145fn colour_to_rgb(cx: Ctx<'_>, r: &m::ColourRef) -> Option<Rgb> {
146 match r {
147 m::ColourRef::ColourRgb(i) => {
148 let c = cx.model.colour_rgb_arena.get(i.0);
149 Some(Rgb {
150 red: c.red,
151 green: c.green,
152 blue: c.blue,
153 })
154 }
155 m::ColourRef::DraughtingPreDefinedColour(i) => {
156 let name = &cx.model.draughting_pre_defined_colour_arena.get(i.0).name;
157 predefined_rgb(name).or_else(|| {
158 cx.warn(format!("unknown pre-defined colour: {name}"));
159 None
160 })
161 }
162 m::ColourRef::Complex(_) => {
163 cx.warn("complex colour".to_owned());
164 None
165 }
166 _ => None,
168 }
169}
170
171fn predefined_rgb(name: &str) -> Option<Rgb> {
173 let (red, green, blue) = match name.to_ascii_lowercase().as_str() {
174 "red" => (1.0, 0.0, 0.0),
175 "green" => (0.0, 1.0, 0.0),
176 "blue" => (0.0, 0.0, 1.0),
177 "yellow" => (1.0, 1.0, 0.0),
178 "cyan" => (0.0, 1.0, 1.0),
179 "magenta" => (1.0, 0.0, 1.0),
180 "black" => (0.0, 0.0, 0.0),
181 "white" => (1.0, 1.0, 1.0),
182 _ => return None,
183 };
184 Some(Rgb { red, green, blue })
185}
186
187fn surface_elements_of<T>(
197 cx: Ctx<'_>,
198 target: m::EntityKey,
199 leaf: impl Fn(Ctx<'_>, &m::SurfaceStyleElementSelectRef) -> Option<T>,
200) -> Option<T> {
201 let rg = cx.ref_graph();
202 for r in rg.referrers(target) {
203 let m::EntityKey::StyledItem(sid) = r else {
204 continue;
205 };
206 let si = cx.model.styled_item_arena.get(sid.0);
207 for psa in &si.styles {
208 let styles: &[m::PresentationStyleSelectRef] = match psa {
209 m::PresentationStyleAssignmentRef::PresentationStyleAssignment(i) => {
210 &cx.model.presentation_style_assignment_arena.get(i.0).styles
211 }
212 m::PresentationStyleAssignmentRef::PresentationStyleByContext(i) => {
213 &cx.model.presentation_style_by_context_arena.get(i.0).styles
214 }
215 m::PresentationStyleAssignmentRef::Complex(_) => {
216 cx.warn("styled item uses a complex presentation style assignment".to_owned());
217 continue;
218 }
219 };
220 for style in styles {
221 let m::PresentationStyleSelectRef::SurfaceStyleUsage(i) = style else {
222 continue;
223 };
224 let side = &cx.model.surface_style_usage_arena.get(i.0).style;
225 let elements: &[m::SurfaceStyleElementSelectRef] = match side {
226 m::SurfaceSideStyleSelectRef::SurfaceSideStyle(i) => {
227 &cx.model.surface_side_style_arena.get(i.0).styles
228 }
229 m::SurfaceSideStyleSelectRef::Complex(_) => {
230 cx.warn("complex surface side style".to_owned());
231 continue;
232 }
233 m::SurfaceSideStyleSelectRef::PreDefinedSurfaceSideStyle(_) => continue,
234 };
235 if let Some(found) = elements.iter().find_map(|e| leaf(cx, e)) {
236 return Some(found);
237 }
238 }
239 }
240 }
241 None
242}
243
244fn element_transparency(cx: Ctx<'_>, r: &m::SurfaceStyleElementSelectRef) -> Option<f64> {
248 let m::SurfaceStyleElementSelectRef::SurfaceStyleRenderingWithProperties(i) = r else {
249 return None;
250 };
251 cx.model
252 .surface_style_rendering_with_properties_arena
253 .get(i.0)
254 .properties
255 .iter()
256 .find_map(|p| match p {
257 m::RenderingPropertiesSelectRef::SurfaceStyleTransparent(t) => Some(
258 cx.model
259 .surface_style_transparent_arena
260 .get(t.0)
261 .transparency,
262 ),
263 _ => None,
264 })
265}
266
267pub(crate) fn transparency_of(cx: Ctx<'_>, target: m::EntityKey) -> Option<f64> {
270 surface_elements_of(cx, target, element_transparency)
271}
272
273fn styled_items_of(cx: Ctx<'_>, target: m::EntityKey) -> Vec<m::StyledItemId> {
283 cx.ref_graph()
284 .referrers(target)
285 .iter()
286 .filter_map(|r| match r {
287 m::EntityKey::StyledItem(sid) => Some(*sid),
288 _ => None,
289 })
290 .collect()
291}
292
293fn layers_of(cx: Ctx<'_>, target: m::EntityKey) -> Vec<m::PresentationLayerAssignmentId> {
296 let rg = cx.ref_graph();
297 let mut out: Vec<m::PresentationLayerAssignmentId> = rg
298 .referrers(target)
299 .iter()
300 .filter_map(|r| match r {
301 m::EntityKey::PresentationLayerAssignment(pid) => Some(*pid),
302 _ => None,
303 })
304 .collect();
305 for sid in styled_items_of(cx, target) {
306 for r in rg.referrers(m::EntityKey::StyledItem(sid)) {
307 if let m::EntityKey::PresentationLayerAssignment(pid) = r {
308 out.push(*pid);
309 }
310 }
311 }
312 out
313}
314
315fn referred_by_invisibility(cx: Ctx<'_>, key: m::EntityKey) -> bool {
317 cx.ref_graph()
318 .referrers(key)
319 .iter()
320 .any(|r| matches!(r, m::EntityKey::Invisibility(_)))
321}
322
323pub(crate) fn layer_of(cx: Ctx<'_>, target: m::EntityKey) -> Option<&str> {
325 let pid = *layers_of(cx, target).first()?;
326 Some(&cx.model.presentation_layer_assignment_arena.get(pid.0).name)
327}
328
329pub(crate) fn is_visible(cx: Ctx<'_>, target: m::EntityKey) -> bool {
332 let styled_hidden = styled_items_of(cx, target)
333 .into_iter()
334 .any(|sid| referred_by_invisibility(cx, m::EntityKey::StyledItem(sid)));
335 let layer_hidden = layers_of(cx, target)
336 .into_iter()
337 .any(|pid| referred_by_invisibility(cx, m::EntityKey::PresentationLayerAssignment(pid)));
338 !(styled_hidden || layer_hidden)
339}
340
341fn each_presentation_style<'m, T>(
353 cx: Ctx<'m>,
354 target: m::EntityKey,
355 leaf: impl Fn(Ctx<'m>, &'m m::PresentationStyleSelectRef) -> Option<T>,
356) -> Option<T> {
357 let rg = cx.ref_graph();
358 for r in rg.referrers(target) {
359 let m::EntityKey::StyledItem(sid) = r else {
360 continue;
361 };
362 for psa in &cx.model.styled_item_arena.get(sid.0).styles {
363 let styles: &[m::PresentationStyleSelectRef] = match psa {
364 m::PresentationStyleAssignmentRef::PresentationStyleAssignment(i) => {
365 &cx.model.presentation_style_assignment_arena.get(i.0).styles
366 }
367 m::PresentationStyleAssignmentRef::PresentationStyleByContext(i) => {
368 &cx.model.presentation_style_by_context_arena.get(i.0).styles
369 }
370 m::PresentationStyleAssignmentRef::Complex(_) => {
371 cx.warn("styled item uses a complex presentation style assignment".to_owned());
372 continue;
373 }
374 };
375 if let Some(found) = styles.iter().find_map(|s| leaf(cx, s)) {
376 return Some(found);
377 }
378 }
379 }
380 None
381}
382
383fn curve_style_of(cx: Ctx<'_>, target: m::EntityKey) -> Option<&m::CurveStyle> {
385 each_presentation_style(cx, target, |cx, s| match s {
386 m::PresentationStyleSelectRef::CurveStyle(i) => Some(cx.model.curve_style_arena.get(i.0)),
387 _ => None,
388 })
389}
390
391fn size_f64(cx: Ctx<'_>, r: &m::SizeSelectRef) -> Option<f64> {
393 let value = match r {
394 m::SizeSelectRef::PositiveLengthMeasure(v) => return Some(*v),
395 m::SizeSelectRef::LengthMeasureWithUnit(i) => {
396 &cx.model
397 .length_measure_with_unit_arena
398 .get(i.0)
399 .value_component
400 }
401 m::SizeSelectRef::MeasureWithUnit(i) => {
402 &cx.model.measure_with_unit_arena.get(i.0).value_component
403 }
404 m::SizeSelectRef::MeasureRepresentationItem(i) => {
405 &cx.model
406 .measure_representation_item_arena
407 .get(i.0)
408 .value_component
409 }
410 m::SizeSelectRef::PlaneAngleMeasureWithUnit(i) => {
411 &cx.model
412 .plane_angle_measure_with_unit_arena
413 .get(i.0)
414 .value_component
415 }
416 m::SizeSelectRef::RatioMeasureWithUnit(i) => {
417 &cx.model
418 .ratio_measure_with_unit_arena
419 .get(i.0)
420 .value_component
421 }
422 m::SizeSelectRef::UncertaintyMeasureWithUnit(i) => {
423 &cx.model
424 .uncertainty_measure_with_unit_arena
425 .get(i.0)
426 .value_component
427 }
428 m::SizeSelectRef::MassMeasureWithUnit(i) => {
429 &cx.model
430 .mass_measure_with_unit_arena
431 .get(i.0)
432 .value_component
433 }
434 m::SizeSelectRef::DescriptiveMeasure(_) | m::SizeSelectRef::Complex(_) => return None,
435 };
436 Some(value.value.as_f64())
437}
438
439fn curve_font_name<'m>(cx: Ctx<'m>, r: &m::CurveFontOrScaledCurveFontSelectRef) -> Option<&'m str> {
441 match r {
442 m::CurveFontOrScaledCurveFontSelectRef::CurveStyleFont(i) => {
443 Some(&cx.model.curve_style_font_arena.get(i.0).name)
444 }
445 m::CurveFontOrScaledCurveFontSelectRef::CurveStyleFontAndScaling(i) => {
446 Some(&cx.model.curve_style_font_and_scaling_arena.get(i.0).name)
447 }
448 m::CurveFontOrScaledCurveFontSelectRef::DraughtingPreDefinedCurveFont(i) => Some(
449 &cx.model
450 .draughting_pre_defined_curve_font_arena
451 .get(i.0)
452 .name,
453 ),
454 m::CurveFontOrScaledCurveFontSelectRef::PreDefinedCurveFont(i) => {
455 Some(&cx.model.pre_defined_curve_font_arena.get(i.0).name)
456 }
457 m::CurveFontOrScaledCurveFontSelectRef::ExternallyDefinedCurveFont(_) => None,
459 m::CurveFontOrScaledCurveFontSelectRef::Complex(_) => {
460 cx.warn("curve style uses a complex font".to_owned());
461 None
462 }
463 }
464}
465
466pub(crate) fn curve_colour_of(cx: Ctx<'_>, target: m::EntityKey) -> Option<Rgb> {
468 let cs = curve_style_of(cx, target)?;
469 colour_to_rgb(cx, cs.curve_colour.as_ref()?)
470}
471
472pub(crate) fn curve_width_of(cx: Ctx<'_>, target: m::EntityKey) -> Option<f64> {
474 let cs = curve_style_of(cx, target)?;
475 size_f64(cx, cs.curve_width.as_ref()?)
476}
477
478pub(crate) fn curve_font_of(cx: Ctx<'_>, target: m::EntityKey) -> Option<&str> {
480 let cs = curve_style_of(cx, target)?;
481 curve_font_name(cx, cs.curve_font.as_ref()?)
482}