use crate::processor::get_refs_from_list;
use crate::style::{FullIndexedColourMap, GeometryStyleInfo};
use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcType};
pub(crate) use ifc_lite_core::MAX_MAPPED_ITEM_DEPTH;
use rustc_hash::FxHashMap;
pub(crate) fn find_geometry_item_color(
geometry_id: u32,
geometry_styles: &FxHashMap<u32, GeometryStyleInfo>,
decoder: &mut EntityDecoder,
) -> Option<[f32; 4]> {
let mut visited = FxHashMap::default();
find_geometry_item_color_at(geometry_id, geometry_styles, decoder, 0, &mut visited)
}
fn find_geometry_item_color_at(
geometry_id: u32,
geometry_styles: &FxHashMap<u32, GeometryStyleInfo>,
decoder: &mut EntityDecoder,
depth: u32,
visited: &mut FxHashMap<u32, u32>,
) -> Option<[f32; 4]> {
if let Some(style) = geometry_styles.get(&geometry_id) {
return Some(style.color);
}
if depth >= MAX_MAPPED_ITEM_DEPTH {
return None;
}
match visited.get(&geometry_id) {
Some(&seen_at) if seen_at <= depth => return None,
_ => visited.insert(geometry_id, depth),
};
let geom = decoder.decode_by_id(geometry_id).ok()?;
if geom.ifc_type != IfcType::IfcMappedItem {
return None;
}
let mapping_source_id = geom.get_ref(0)?;
let representation_map = decoder.decode_by_id(mapping_source_id).ok()?;
let mapped_representation_id = representation_map.get_ref(1)?;
let mapped_representation = decoder.decode_by_id(mapped_representation_id).ok()?;
let items = get_refs_from_list(&mapped_representation, 3)?;
for underlying in items {
if let Some(color) =
find_geometry_item_color_at(underlying, geometry_styles, decoder, depth + 1, visited)
{
return Some(color);
}
}
None
}
pub(crate) fn resolve_color_for_representation_map(
rep_map_id: u32,
geometry_style_index: &FxHashMap<u32, GeometryStyleInfo>,
decoder: &mut EntityDecoder,
) -> Option<[f32; 4]> {
let rep_map = decoder.decode_by_id(rep_map_id).ok()?;
let mapped_rep_id = rep_map.get_ref(1)?;
let mapped_rep = decoder.decode_by_id(mapped_rep_id).ok()?;
let item_ids = get_refs_from_list(&mapped_rep, 3)?;
for item_id in item_ids {
if let Some(style) = geometry_style_index.get(&item_id) {
return Some(style.color);
}
if let Some(color) = find_geometry_item_color(item_id, geometry_style_index, decoder) {
return Some(color);
}
}
None
}
pub(crate) fn find_indexed_colour_for_element<'a>(
entity: &DecodedEntity,
indexed_colour_full: &'a FxHashMap<u32, FullIndexedColourMap>,
decoder: &mut EntityDecoder,
) -> Option<&'a FullIndexedColourMap> {
let pds_id = entity.get_ref(6)?;
let pds = decoder.decode_by_id(pds_id).ok()?;
let repr_ids = get_refs_from_list(&pds, 2)?;
for repr_id in repr_ids {
if let Ok(repr) = decoder.decode_by_id(repr_id) {
if let Some(items) = get_refs_from_list(&repr, 3) {
for item_id in items {
if let Some(full) = indexed_colour_full.get(&item_id) {
return Some(full);
}
}
}
}
}
None
}
fn is_opening_with_subparts(ifc_type: &IfcType) -> bool {
matches!(ifc_type, IfcType::IfcWindow | IfcType::IfcDoor)
}
pub(crate) fn infer_opening_subpart_material_name(
ifc_type: &IfcType,
color: [f32; 4],
geometry_id: u32,
) -> Option<String> {
if !is_opening_with_subparts(ifc_type) {
return None;
}
let prefix = match ifc_type {
IfcType::IfcDoor => "Door",
_ => "Window",
};
if color[3] <= 0.65 {
return Some(format!("{}_Glass", prefix));
}
Some(format!("{}_Frame_{}", prefix, geometry_id))
}