pub(crate) fn build_geometry_style_index(
content: &str,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> rustc_hash::FxHashMap<u32, [f32; 4]> {
use ifc_lite_core::EntityScanner;
use rustc_hash::FxHashMap;
let mut style_index: FxHashMap<u32, [f32; 4]> = FxHashMap::default();
let mut scanner = EntityScanner::new(content);
while let Some((id, type_name, start, end)) = scanner.next_entity() {
if type_name != "IFCSTYLEDITEM" {
continue;
}
let styled_item = match decoder.decode_at_with_id(id, start, end) {
Ok(entity) => entity,
Err(_) => continue,
};
let geometry_id = match styled_item.get_ref(0) {
Some(id) => id,
None => continue,
};
if style_index.contains_key(&geometry_id) {
continue;
}
let styles_attr = match styled_item.get(1) {
Some(attr) => attr,
None => continue,
};
if let Some(color) = extract_color_from_styles(styles_attr, decoder) {
style_index.insert(geometry_id, color);
}
}
style_index
}
pub(crate) fn build_element_style_index(
content: &str,
geometry_styles: &rustc_hash::FxHashMap<u32, [f32; 4]>,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> rustc_hash::FxHashMap<u32, [f32; 4]> {
use ifc_lite_core::EntityScanner;
use rustc_hash::FxHashMap;
let mut element_styles: FxHashMap<u32, [f32; 4]> = FxHashMap::default();
if geometry_styles.is_empty() {
return element_styles;
}
let mut scanner = EntityScanner::new(content);
while let Some((element_id, type_name, start, end)) = scanner.next_entity() {
if !ifc_lite_core::has_geometry_by_name(type_name) {
continue;
}
let element = match decoder.decode_at_with_id(element_id, start, end) {
Ok(entity) => entity,
Err(_) => continue,
};
let repr_id = match element.get_ref(6) {
Some(id) => id,
None => continue,
};
let product_shape = match decoder.decode_by_id(repr_id) {
Ok(entity) => entity,
Err(_) => continue,
};
let reprs_attr = match product_shape.get(2) {
Some(attr) => attr,
None => continue,
};
let reprs_list = match reprs_attr.as_list() {
Some(list) => list,
None => continue,
};
'repr_loop: for repr_item in reprs_list {
let shape_repr_id = match repr_item.as_entity_ref() {
Some(id) => id,
None => continue,
};
let shape_repr = match decoder.decode_by_id(shape_repr_id) {
Ok(entity) => entity,
Err(_) => continue,
};
let items_attr = match shape_repr.get(3) {
Some(attr) => attr,
None => continue,
};
let items_list = match items_attr.as_list() {
Some(list) => list,
None => continue,
};
for geom_item in items_list {
let geom_id = match geom_item.as_entity_ref() {
Some(id) => id,
None => continue,
};
if let Some(color) = find_color_for_geometry(geom_id, geometry_styles, decoder) {
element_styles.insert(element_id, color);
break 'repr_loop; }
}
}
}
element_styles
}
pub(crate) fn find_color_for_geometry(
geom_id: u32,
geometry_styles: &rustc_hash::FxHashMap<u32, [f32; 4]>,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> Option<[f32; 4]> {
use ifc_lite_core::IfcType;
if let Some(&color) = geometry_styles.get(&geom_id) {
return Some(color);
}
let geom = decoder.decode_by_id(geom_id).ok()?;
if geom.ifc_type == IfcType::IfcMappedItem {
let map_source_id = geom.get_ref(0)?;
let rep_map = decoder.decode_by_id(map_source_id).ok()?;
let mapped_repr_id = rep_map.get_ref(1)?;
let mapped_repr = decoder.decode_by_id(mapped_repr_id).ok()?;
let items_attr = mapped_repr.get(3)?;
let items_list = items_attr.as_list()?;
for item in items_list {
if let Some(underlying_geom_id) = item.as_entity_ref() {
if let Some(color) =
find_color_for_geometry(underlying_geom_id, geometry_styles, decoder)
{
return Some(color);
}
}
}
}
None
}
fn extract_color_from_styles(
styles_attr: &ifc_lite_core::AttributeValue,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> Option<[f32; 4]> {
if let Some(list) = styles_attr.as_list() {
for item in list {
if let Some(style_id) = item.as_entity_ref() {
if let Some(color) = extract_color_from_style_assignment(style_id, decoder) {
return Some(color);
}
}
}
} else if let Some(style_id) = styles_attr.as_entity_ref() {
return extract_color_from_style_assignment(style_id, decoder);
}
None
}
fn extract_color_from_style_assignment(
style_id: u32,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> Option<[f32; 4]> {
use ifc_lite_core::IfcType;
let style = decoder.decode_by_id(style_id).ok()?;
match style.ifc_type {
IfcType::IfcPresentationStyle => {
let styles_attr = style.get(0)?;
if let Some(list) = styles_attr.as_list() {
for item in list {
if let Some(inner_id) = item.as_entity_ref() {
if let Some(color) = extract_color_from_surface_style(inner_id, decoder) {
return Some(color);
}
}
}
}
}
IfcType::IfcSurfaceStyle => {
return extract_color_from_surface_style(style_id, decoder);
}
_ => {
let styles_attr = style.get(0)?;
if let Some(list) = styles_attr.as_list() {
for item in list {
if let Some(inner_id) = item.as_entity_ref() {
if let Some(color) = extract_color_from_surface_style(inner_id, decoder) {
return Some(color);
}
}
}
}
}
}
None
}
fn extract_color_from_surface_style(
style_id: u32,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> Option<[f32; 4]> {
use ifc_lite_core::IfcType;
let style = decoder.decode_by_id(style_id).ok()?;
if style.ifc_type != IfcType::IfcSurfaceStyle {
return None;
}
let styles_attr = style.get(2)?;
if let Some(list) = styles_attr.as_list() {
for item in list {
if let Some(element_id) = item.as_entity_ref() {
if let Some(color) = extract_color_from_rendering(element_id, decoder) {
return Some(color);
}
}
}
}
None
}
fn extract_color_from_rendering(
rendering_id: u32,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> Option<[f32; 4]> {
use ifc_lite_core::IfcType;
let rendering = decoder.decode_by_id(rendering_id).ok()?;
match rendering.ifc_type {
IfcType::IfcSurfaceStyleRendering | IfcType::IfcSurfaceStyleShading => {
let color_ref = rendering.get_ref(0)?;
let [r, g, b, _] = extract_color_rgb(color_ref, decoder)?;
let transparency = rendering.get_float(1).unwrap_or(0.0);
let alpha = 1.0 - transparency as f32;
return Some([r, g, b, alpha.max(0.0).min(1.0)]);
}
_ => {}
}
None
}
fn extract_color_rgb(
color_id: u32,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> Option<[f32; 4]> {
use ifc_lite_core::IfcType;
let color = decoder.decode_by_id(color_id).ok()?;
if color.ifc_type != IfcType::IfcColourRgb {
return None;
}
let red = color.get_float(1).unwrap_or(0.8);
let green = color.get_float(2).unwrap_or(0.8);
let blue = color.get_float(3).unwrap_or(0.8);
Some([red as f32, green as f32, blue as f32, 1.0])
}
pub(crate) struct PrePassData {
pub geometry_styles: rustc_hash::FxHashMap<u32, [f32; 4]>,
pub void_index: rustc_hash::FxHashMap<u32, Vec<u32>>,
pub faceted_brep_ids: Vec<u32>,
pub project_id: Option<u32>,
pub site_position: Option<(u32, usize, usize)>,
pub simple_jobs: Vec<(u32, usize, usize, ifc_lite_core::IfcType)>,
pub complex_jobs: Vec<(u32, usize, usize, ifc_lite_core::IfcType)>,
pub element_material_styles: rustc_hash::FxHashMap<u32, Vec<[f32; 4]>>,
pub material_layer_index: std::sync::Arc<ifc_lite_geometry::MaterialLayerIndex>,
}
pub(crate) fn combined_pre_pass(
content: &str,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> PrePassData {
use ifc_lite_core::EntityScanner;
use rustc_hash::FxHashMap;
let estimated_elements = content.len() / 2000;
let mut geometry_styles: FxHashMap<u32, [f32; 4]> = FxHashMap::default();
let mut void_index: FxHashMap<u32, Vec<u32>> = FxHashMap::default();
let mut faceted_brep_ids: Vec<u32> = Vec::with_capacity(estimated_elements / 10);
let mut project_id: Option<u32> = None;
let mut site_position: Option<(u32, usize, usize)> = None;
let mut simple_jobs = Vec::with_capacity(estimated_elements / 2);
let mut complex_jobs = Vec::with_capacity(estimated_elements / 2);
let mut orphan_styled_items: FxHashMap<u32, [f32; 4]> = FxHashMap::default();
let mut material_def_reprs: FxHashMap<u32, Vec<u32>> = FxHashMap::default();
let mut element_to_material: FxHashMap<u32, u32> = FxHashMap::default();
let mut scanner = EntityScanner::new(content);
while let Some((id, type_name, start, end)) = scanner.next_entity() {
match type_name {
"IFCSTYLEDITEM" => {
if let Ok(styled_item) = decoder.decode_at_with_id(id, start, end) {
if let Some(geometry_id) = styled_item.get_ref(0) {
if !geometry_styles.contains_key(&geometry_id) {
if let Some(styles_attr) = styled_item.get(1) {
if let Some(color) = extract_color_from_styles(styles_attr, decoder)
{
geometry_styles.insert(geometry_id, color);
}
}
}
} else {
if let Some(styles_attr) = styled_item.get(1) {
if let Some(color) = extract_color_from_styles(styles_attr, decoder) {
orphan_styled_items.insert(id, color);
}
}
}
}
}
"IFCMATERIALDEFINITIONREPRESENTATION" | "IFCRELASSOCIATESMATERIAL" => {
collect_material_entity(
id,
type_name,
start,
end,
decoder,
&mut orphan_styled_items,
&mut material_def_reprs,
&mut element_to_material,
);
}
"IFCRELVOIDSELEMENT" => {
if let Ok(entity) = decoder.decode_at_with_id(id, start, end) {
if let (Some(host_id), Some(opening_id)) =
(entity.get_ref(4), entity.get_ref(5))
{
void_index.entry(host_id).or_default().push(opening_id);
}
}
}
"IFCFACETEDBREP" => {
faceted_brep_ids.push(id);
}
"IFCPROJECT" => {
if project_id.is_none() {
project_id = Some(id);
}
}
"IFCSITE" => {
if site_position.is_none() {
site_position = Some((id, start, end));
}
let ifc_type = ifc_lite_core::IfcType::from_str(type_name);
complex_jobs.push((id, start, end, ifc_type));
}
_ => {
if ifc_lite_core::has_geometry_by_name(type_name) {
let ifc_type = ifc_lite_core::IfcType::from_str(type_name);
if is_simple_geometry_type(type_name) {
simple_jobs.push((id, start, end, ifc_type));
} else {
complex_jobs.push((id, start, end, ifc_type));
}
}
}
}
}
let material_styles =
build_material_style_index(&material_def_reprs, &orphan_styled_items, decoder);
let element_material_styles =
build_element_material_styles(&element_to_material, &material_styles, decoder);
for (&mat_id, &color) in flatten_material_color_index(&material_styles).iter() {
geometry_styles.entry(mat_id).or_insert(color);
}
let material_layer_index = std::sync::Arc::new(
ifc_lite_geometry::MaterialLayerIndex::from_content(content, decoder),
);
ifc_lite_geometry::propagate_voids_to_parts(&mut void_index, content, decoder);
PrePassData {
geometry_styles,
void_index,
faceted_brep_ids,
project_id,
site_position,
simple_jobs,
complex_jobs,
element_material_styles,
material_layer_index,
}
}
fn build_material_style_index(
material_def_reprs: &rustc_hash::FxHashMap<u32, Vec<u32>>,
orphan_styled_items: &rustc_hash::FxHashMap<u32, [f32; 4]>,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> rustc_hash::FxHashMap<u32, Vec<[f32; 4]>> {
use rustc_hash::FxHashMap;
let mut material_styles: FxHashMap<u32, Vec<[f32; 4]>> = FxHashMap::default();
for (&material_id, styled_repr_ids) in material_def_reprs {
for &styled_repr_id in styled_repr_ids {
let styled_repr = match decoder.decode_by_id(styled_repr_id) {
Ok(entity) => entity,
Err(_) => continue,
};
let items_attr = match styled_repr.get(3) {
Some(attr) => attr,
None => continue,
};
let items_list = match items_attr.as_list() {
Some(list) => list,
None => continue,
};
for item in items_list {
if let Some(styled_item_id) = item.as_entity_ref() {
if let Some(&color) = orphan_styled_items.get(&styled_item_id) {
material_styles.entry(material_id).or_default().push(color);
}
}
}
}
}
material_styles
}
fn build_element_material_styles(
element_to_material: &rustc_hash::FxHashMap<u32, u32>,
material_styles: &rustc_hash::FxHashMap<u32, Vec<[f32; 4]>>,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> rustc_hash::FxHashMap<u32, Vec<[f32; 4]>> {
use rustc_hash::FxHashMap;
let mut result: FxHashMap<u32, Vec<[f32; 4]>> = FxHashMap::default();
for (&element_id, &material_select_id) in element_to_material {
let mut colors: Vec<[f32; 4]> = Vec::new();
let material_ids = resolve_material_ids(material_select_id, decoder);
for material_id in material_ids {
if let Some(mat_colors) = material_styles.get(&material_id) {
colors.extend(mat_colors);
}
}
if !colors.is_empty() {
result.insert(element_id, colors);
}
}
result
}
fn resolve_material_ids(
material_select_id: u32,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> Vec<u32> {
resolve_material_ids_inner(material_select_id, decoder, 0)
}
const MAX_MATERIAL_RESOLVE_DEPTH: u8 = 4;
fn resolve_material_ids_inner(
material_select_id: u32,
decoder: &mut ifc_lite_core::EntityDecoder,
depth: u8,
) -> Vec<u32> {
if depth >= MAX_MATERIAL_RESOLVE_DEPTH {
return vec![];
}
use ifc_lite_core::IfcType;
let entity = match decoder.decode_by_id(material_select_id) {
Ok(e) => e,
Err(_) => return vec![],
};
match entity.ifc_type {
IfcType::IfcMaterial => {
vec![material_select_id]
}
IfcType::IfcMaterialList => {
extract_refs_from_list(&entity, 0)
}
IfcType::IfcMaterialLayerSetUsage => {
if let Some(layer_set_id) = entity.get_ref(0) {
resolve_material_ids_inner(layer_set_id, decoder, depth + 1)
} else {
vec![]
}
}
IfcType::IfcMaterialLayerSet => {
extract_nested_material_ids(&entity, 0, 0, decoder)
}
IfcType::IfcMaterialConstituentSet => {
extract_nested_material_ids(&entity, 2, 2, decoder)
}
IfcType::IfcMaterialProfileSet => {
extract_nested_material_ids(&entity, 2, 2, decoder)
}
IfcType::IfcMaterialProfileSetUsage | IfcType::IfcMaterialProfileSetUsageTapering => {
if let Some(profile_set_id) = entity.get_ref(0) {
resolve_material_ids_inner(profile_set_id, decoder, depth + 1)
} else {
vec![]
}
}
_ => {
vec![]
}
}
}
fn extract_nested_material_ids(
entity: &ifc_lite_core::DecodedEntity,
container_list_attr_idx: usize,
material_attr_idx: usize,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> Vec<u32> {
let container_ids = extract_refs_from_list(entity, container_list_attr_idx);
let mut materials = Vec::new();
for container_id in container_ids {
if let Ok(container) = decoder.decode_by_id(container_id) {
if let Some(mat_id) = container.get_ref(material_attr_idx) {
materials.push(mat_id);
}
}
}
materials
}
fn extract_refs_from_list(entity: &ifc_lite_core::DecodedEntity, index: usize) -> Vec<u32> {
entity
.get(index)
.and_then(|attr| attr.as_list())
.map(|list| list.iter().filter_map(|v| v.as_entity_ref()).collect())
.unwrap_or_default()
}
pub(crate) fn build_element_material_styles_from_content(
content: &str,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> rustc_hash::FxHashMap<u32, Vec<[f32; 4]>> {
let (orphan_styled_items, material_def_reprs, element_to_material) =
collect_material_data(content, decoder);
let material_styles =
build_material_style_index(&material_def_reprs, &orphan_styled_items, decoder);
build_element_material_styles(&element_to_material, &material_styles, decoder)
}
pub(crate) fn flatten_material_color_index(
material_styles: &rustc_hash::FxHashMap<u32, Vec<[f32; 4]>>,
) -> rustc_hash::FxHashMap<u32, [f32; 4]> {
use rustc_hash::FxHashMap;
let mut out: FxHashMap<u32, [f32; 4]> = FxHashMap::default();
for (&mat_id, colors) in material_styles {
if colors.is_empty() {
continue;
}
let color = colors
.iter()
.find(|c| c[3] >= TRANSPARENCY_ALPHA_THRESHOLD)
.copied()
.unwrap_or(colors[0]);
out.insert(mat_id, color);
}
out
}
pub(crate) fn build_material_color_index_from_content(
content: &str,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> rustc_hash::FxHashMap<u32, [f32; 4]> {
let (orphan_styled_items, material_def_reprs, _element_to_material) =
collect_material_data(content, decoder);
let material_styles =
build_material_style_index(&material_def_reprs, &orphan_styled_items, decoder);
flatten_material_color_index(&material_styles)
}
fn collect_material_data(
content: &str,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> (
rustc_hash::FxHashMap<u32, [f32; 4]>,
rustc_hash::FxHashMap<u32, Vec<u32>>,
rustc_hash::FxHashMap<u32, u32>,
) {
use ifc_lite_core::EntityScanner;
use rustc_hash::FxHashMap;
let mut orphan_styled_items: FxHashMap<u32, [f32; 4]> = FxHashMap::default();
let mut material_def_reprs: FxHashMap<u32, Vec<u32>> = FxHashMap::default();
let mut element_to_material: FxHashMap<u32, u32> = FxHashMap::default();
let mut scanner = EntityScanner::new(content);
while let Some((id, type_name, start, end)) = scanner.next_entity() {
collect_material_entity(
id,
type_name,
start,
end,
decoder,
&mut orphan_styled_items,
&mut material_def_reprs,
&mut element_to_material,
);
}
(orphan_styled_items, material_def_reprs, element_to_material)
}
fn collect_material_entity(
id: u32,
type_name: &str,
start: usize,
end: usize,
decoder: &mut ifc_lite_core::EntityDecoder,
orphan_styled_items: &mut rustc_hash::FxHashMap<u32, [f32; 4]>,
material_def_reprs: &mut rustc_hash::FxHashMap<u32, Vec<u32>>,
element_to_material: &mut rustc_hash::FxHashMap<u32, u32>,
) {
match type_name {
"IFCSTYLEDITEM" => {
if let Ok(styled_item) = decoder.decode_at_with_id(id, start, end) {
if styled_item.get_ref(0).is_none() {
if let Some(styles_attr) = styled_item.get(1) {
if let Some(color) = extract_color_from_styles(styles_attr, decoder) {
orphan_styled_items.insert(id, color);
}
}
}
}
}
"IFCMATERIALDEFINITIONREPRESENTATION" => {
if let Ok(entity) = decoder.decode_at_with_id(id, start, end) {
if let Some(material_id) = entity.get_ref(3) {
if let Some(reprs_attr) = entity.get(2) {
if let Some(list) = reprs_attr.as_list() {
for item in list {
if let Some(repr_id) = item.as_entity_ref() {
material_def_reprs
.entry(material_id)
.or_default()
.push(repr_id);
}
}
}
}
}
}
}
"IFCRELASSOCIATESMATERIAL" => {
if let Ok(entity) = decoder.decode_at_with_id(id, start, end) {
if let Some(material_select_id) = entity.get_ref(5) {
if let Some(related_attr) = entity.get(4) {
if let Some(list) = related_attr.as_list() {
for item in list {
if let Some(element_id) = item.as_entity_ref() {
element_to_material.insert(element_id, material_select_id);
}
}
}
}
}
}
}
_ => {}
}
}
pub(crate) fn resolve_submesh_color(
geometry_id: u32,
geometry_styles: &rustc_hash::FxHashMap<u32, [f32; 4]>,
decoder: &mut ifc_lite_core::EntityDecoder,
material_colors: Option<&Vec<[f32; 4]>>,
mat_color_idx: &mut usize,
element_color: Option<[f32; 4]>,
default_color: [f32; 4],
) -> [f32; 4] {
if let Some(color) = find_color_for_geometry(geometry_id, geometry_styles, decoder) {
return color;
}
if let Some(colors) = material_colors {
let prefer_transparent = *mat_color_idx % 2 == 0;
*mat_color_idx += 1;
if let Some(color) = pick_material_style_for_submesh(colors, prefer_transparent) {
return color;
}
}
element_color.unwrap_or(default_color)
}
const TRANSPARENCY_ALPHA_THRESHOLD: f32 = 0.95;
pub(crate) fn pick_material_style_for_submesh(
material_colors: &[[f32; 4]],
prefer_transparent: bool,
) -> Option<[f32; 4]> {
if material_colors.is_empty() {
return None;
}
if prefer_transparent {
if let Some(color) = material_colors
.iter()
.find(|c| c[3] < TRANSPARENCY_ALPHA_THRESHOLD)
{
return Some(*color);
}
} else {
if let Some(color) = material_colors
.iter()
.find(|c| c[3] >= TRANSPARENCY_ALPHA_THRESHOLD)
{
return Some(*color);
}
}
Some(material_colors[0])
}
pub(crate) fn is_simple_geometry_type(type_name: &str) -> bool {
use ifc_lite_core::{get_legacy_entity_info, IfcType};
let upper_owned;
let upper: &str = if type_name.bytes().any(|b| b.is_ascii_lowercase()) {
upper_owned = type_name.to_ascii_uppercase();
upper_owned.as_str()
} else {
type_name
};
let t = match get_legacy_entity_info(upper) {
Some(info) => info.base_type,
None => IfcType::from_str(upper),
};
if matches!(t, IfcType::Unknown(_)) {
return true;
}
let is_secondary = t.is_subtype_of(IfcType::IfcOpeningElement)
|| t.is_subtype_of(IfcType::IfcWindow)
|| t.is_subtype_of(IfcType::IfcDoor)
|| t.is_subtype_of(IfcType::IfcFurnishingElement)
|| t.is_subtype_of(IfcType::IfcDistributionElement)
|| matches!(
t,
IfcType::IfcSpace
| IfcType::IfcSite
| IfcType::IfcAnnotation
| IfcType::IfcVirtualElement
| IfcType::IfcBuildingElementProxy
);
!is_secondary
}
pub(crate) fn resolve_element_color(
entity: &ifc_lite_core::DecodedEntity,
geometry_styles: &rustc_hash::FxHashMap<u32, [f32; 4]>,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> Option<[f32; 4]> {
if geometry_styles.is_empty() {
return None;
}
let repr_id = entity.get_ref(6)?;
let product_shape = decoder.decode_by_id(repr_id).ok()?;
let reprs_list = product_shape.get(2)?.as_list()?;
for repr_item in reprs_list {
let shape_repr_id = repr_item.as_entity_ref()?;
let shape_repr = decoder.decode_by_id(shape_repr_id).ok()?;
let items_list = shape_repr.get(3)?.as_list()?;
for geom_item in items_list {
let geom_id = geom_item.as_entity_ref()?;
if let Some(color) = find_color_for_geometry(geom_id, geometry_styles, decoder) {
return Some(color);
}
}
}
None
}
pub(crate) fn get_default_color_for_type(ifc_type: &ifc_lite_core::IfcType) -> [f32; 4] {
use ifc_lite_core::IfcType;
match ifc_type {
IfcType::IfcWall | IfcType::IfcWallStandardCase => [0.85, 0.85, 0.85, 1.0],
IfcType::IfcSlab => [0.7, 0.7, 0.7, 1.0],
IfcType::IfcRoof => [0.6, 0.5, 0.4, 1.0],
IfcType::IfcColumn | IfcType::IfcBeam | IfcType::IfcMember => [0.6, 0.65, 0.7, 1.0],
IfcType::IfcWindow => [0.6, 0.8, 1.0, 0.4],
IfcType::IfcDoor => [0.6, 0.45, 0.3, 1.0],
IfcType::IfcStair => [0.75, 0.75, 0.75, 1.0],
IfcType::IfcRailing => [0.4, 0.4, 0.45, 1.0],
IfcType::IfcPlate | IfcType::IfcCovering => [0.8, 0.8, 0.8, 1.0],
IfcType::IfcCurtainWall => [0.5, 0.7, 0.9, 0.5],
IfcType::IfcFurnishingElement => [0.7, 0.55, 0.4, 1.0],
IfcType::IfcSpace => [0.2, 0.85, 1.0, 0.3],
IfcType::IfcOpeningElement => [1.0, 0.42, 0.29, 0.4],
IfcType::IfcSite => [0.4, 0.8, 0.3, 1.0],
_ => [0.8, 0.8, 0.8, 1.0],
}
}
pub(crate) fn extract_building_rotation_from_site(
site_pos: (u32, usize, usize),
decoder: &mut ifc_lite_core::EntityDecoder,
) -> Option<f64> {
let (site_id, start, end) = site_pos;
let site_entity = decoder.decode_at_with_id(site_id, start, end).ok()?;
let placement_attr = site_entity.get(5).filter(|a| !a.is_null())?;
let placement = decoder.resolve_ref(placement_attr).ok()??;
let top_level_placement = find_top_level_placement(&placement, decoder);
extract_rotation_from_placement(&top_level_placement, decoder)
}
pub(crate) fn extract_building_rotation(
content: &str,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> Option<f64> {
use ifc_lite_core::EntityScanner;
let mut scanner = EntityScanner::new(content);
while let Some((site_id, type_name, start, end)) = scanner.next_entity() {
if type_name != "IFCSITE" {
continue;
}
if let Ok(site_entity) = decoder.decode_at_with_id(site_id, start, end) {
let placement_attr = match site_entity.get(5) {
Some(attr) if !attr.is_null() => attr,
_ => continue,
};
let placement = match decoder.resolve_ref(placement_attr) {
Ok(Some(p)) => p,
_ => continue,
};
let top_level_placement = find_top_level_placement(&placement, decoder);
if let Some(rotation) = extract_rotation_from_placement(&top_level_placement, decoder) {
return Some(rotation);
}
}
}
None
}
fn find_top_level_placement(
placement: &ifc_lite_core::DecodedEntity,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> ifc_lite_core::DecodedEntity {
use ifc_lite_core::IfcType;
if placement.ifc_type != IfcType::IfcLocalPlacement {
return placement.clone();
}
let parent_attr = match placement.get(0) {
Some(attr) if !attr.is_null() => attr,
_ => return placement.clone(), };
if let Ok(Some(parent)) = decoder.resolve_ref(parent_attr) {
find_top_level_placement(&parent, decoder)
} else {
placement.clone() }
}
fn extract_rotation_from_placement(
placement: &ifc_lite_core::DecodedEntity,
decoder: &mut ifc_lite_core::EntityDecoder,
) -> Option<f64> {
use ifc_lite_core::IfcType;
let rel_attr = match placement.get(1) {
Some(attr) if !attr.is_null() => attr,
_ => return None,
};
let axis_placement = match decoder.resolve_ref(rel_attr) {
Ok(Some(p)) => p,
_ => return None,
};
if axis_placement.ifc_type != IfcType::IfcAxis2Placement3D {
return None;
}
let ref_dir_attr = match axis_placement.get(2) {
Some(attr) if !attr.is_null() => attr,
_ => return None,
};
let ref_dir = match decoder.resolve_ref(ref_dir_attr) {
Ok(Some(d)) => d,
_ => return None,
};
if ref_dir.ifc_type != IfcType::IfcDirection {
return None;
}
let ratios_attr = match ref_dir.get(0) {
Some(attr) => attr,
_ => return None,
};
let ratios = match ratios_attr.as_list() {
Some(list) => list,
_ => return None,
};
let dx = ratios.first().and_then(|v| v.as_float()).unwrap_or(0.0);
let dy = ratios.get(1).and_then(|v| v.as_float()).unwrap_or(0.0);
let len_sq = dx * dx + dy * dy;
if len_sq < 1e-10 {
return None; }
let rotation = dy.atan2(dx);
Some(rotation)
}