use super::normalize_optional_string;
use crate::style::GeometryStyleInfo;
use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcType};
use rustc_hash::{FxHashMap, FxHashSet};
pub(super) fn collect_presentation_layer_assignments(
layer_by_assigned_representation: &mut FxHashMap<u32, String>,
layer_assignment: &DecodedEntity,
) {
let Some(layer_name) = normalize_optional_string(layer_assignment.get_string(0)) else {
return;
};
let Some(assigned_items) = layer_assignment.get_refs(2) else {
return;
};
for assigned in assigned_items {
layer_by_assigned_representation
.entry(assigned)
.or_insert_with(|| layer_name.clone());
}
}
pub(crate) fn resolve_element_color_for_product_definition_shape(
product_definition_shape_id: u32,
geometry_styles: &FxHashMap<u32, GeometryStyleInfo>,
decoder: &mut EntityDecoder,
) -> Option<[f32; 4]> {
find_color_in_representation(product_definition_shape_id, geometry_styles, decoder)
}
pub(super) fn resolve_presentation_layer_for_product_definition_shape(
product_definition_shape_id: u32,
layer_by_assigned_representation: &FxHashMap<u32, String>,
cache_by_representation: &mut FxHashMap<u32, Option<String>>,
decoder: &mut EntityDecoder,
) -> Option<String> {
if let Some(layer_name) = layer_by_assigned_representation.get(&product_definition_shape_id) {
return Some(layer_name.clone());
}
let product_definition_shape = decoder.decode_by_id(product_definition_shape_id).ok()?;
let representation_ids = product_definition_shape.get_refs(2)?;
for representation_id in representation_ids {
if let Some(layer_name) = resolve_presentation_layer_name(
representation_id,
layer_by_assigned_representation,
cache_by_representation,
decoder,
) {
return Some(layer_name);
}
}
None
}
enum Step {
Representation { id: u32, parent: Option<u32> },
Item { item_id: u32, owner: u32 },
}
fn memoise_hit(
start: u32,
layer: &str,
parent_of: &FxHashMap<u32, u32>,
visited: FxHashSet<u32>,
cache_by_representation: &mut FxHashMap<u32, Option<String>>,
) {
let mut chain: FxHashSet<u32> = FxHashSet::default();
let mut at = Some(start);
while let Some(id) = at {
chain.insert(id);
at = parent_of.get(&id).copied();
}
for id in visited {
let answer = chain.contains(&id).then(|| layer.to_string());
cache_by_representation.insert(id, answer);
}
}
fn resolve_presentation_layer_name(
root_representation_id: u32,
layer_by_assigned_representation: &FxHashMap<u32, String>,
cache_by_representation: &mut FxHashMap<u32, Option<String>>,
decoder: &mut EntityDecoder,
) -> Option<String> {
if let Some(cached) = cache_by_representation.get(&root_representation_id) {
return cached.clone();
}
let mut visited: FxHashSet<u32> = FxHashSet::default();
let mut parent_of: FxHashMap<u32, u32> = FxHashMap::default();
let mut stack = vec![Step::Representation { id: root_representation_id, parent: None }];
while let Some(step) = stack.pop() {
match step {
Step::Representation { id: representation_id, parent } => {
if !visited.insert(representation_id) {
continue;
}
if let Some(parent) = parent {
parent_of.insert(representation_id, parent);
}
match cache_by_representation.get(&representation_id) {
Some(Some(layer_name)) => {
let hit = layer_name.clone();
memoise_hit(representation_id, &hit, &parent_of, visited, cache_by_representation);
return Some(hit);
}
Some(None) => continue,
None => {}
}
if let Some(layer_name) = layer_by_assigned_representation.get(&representation_id) {
let hit = layer_name.clone();
memoise_hit(representation_id, &hit, &parent_of, visited, cache_by_representation);
return Some(hit);
}
let Ok(representation) = decoder.decode_by_id(representation_id) else {
continue;
};
let Some(items) = representation.get_refs(3) else {
continue;
};
for item_id in items.into_iter().rev() {
stack.push(Step::Item { item_id, owner: representation_id });
}
}
Step::Item { item_id, owner } => {
if let Some(layer_name) = layer_by_assigned_representation.get(&item_id) {
let hit = layer_name.clone();
memoise_hit(owner, &hit, &parent_of, visited, cache_by_representation);
return Some(hit);
}
let Ok(item) = decoder.decode_by_id(item_id) else {
continue;
};
if item.ifc_type != IfcType::IfcMappedItem {
continue;
}
let Some(mapping_source_id) = item.get_ref(0) else {
continue;
};
let Ok(mapping_source) = decoder.decode_by_id(mapping_source_id) else {
continue;
};
let Some(mapped_representation_id) = mapping_source.get_ref(1) else {
continue;
};
stack.push(Step::Representation { id: mapped_representation_id, parent: Some(owner) });
}
}
}
for representation_id in visited {
cache_by_representation.insert(representation_id, None);
}
None
}
fn find_color_in_representation(
repr_id: u32,
geometry_styles: &FxHashMap<u32, GeometryStyleInfo>,
decoder: &mut EntityDecoder,
) -> Option<[f32; 4]> {
let repr = decoder.decode_by_id(repr_id).ok()?;
let repr_list = repr.get_refs(2)?;
for shape_repr_id in repr_list {
if let Some(color) =
find_color_in_shape_representation(shape_repr_id, geometry_styles, decoder)
{
return Some(color);
}
}
None
}
fn find_color_in_shape_representation(
repr_id: u32,
geometry_styles: &FxHashMap<u32, GeometryStyleInfo>,
decoder: &mut EntityDecoder,
) -> Option<[f32; 4]> {
let repr = decoder.decode_by_id(repr_id).ok()?;
let items = repr.get_refs(3)?;
for item_id in items {
if let Some(color) =
crate::element::find_geometry_item_color(item_id, geometry_styles, decoder)
{
return Some(color);
}
}
None
}
#[cfg(test)]
#[path = "color_layer_tests.rs"]
mod tests;