use super::annotation::invalid;
use crate::Result;
use ifc_lite_core::{DecodedEntity, EntityDecoder, EntityScanner, IfcType};
pub(super) fn validate(item_id: u32, decoder: &mut EntityDecoder) -> Result<()> {
let ids = decoder.styled_item_ids(item_id)?.to_vec();
let mut work = 512usize;
let mut fills = 0usize;
for id in ids {
let styled = decoder.decode_by_id(id)?;
for style_id in references(&styled, 1, &mut work)? {
let style = decoder.decode_by_id(style_id)?;
let assignment = decoder.get_raw_bytes(style_id).and_then(|raw| {
EntityScanner::new(raw)
.next_entity()
.map(|(_, name, _, _)| name == "IFCPRESENTATIONSTYLEASSIGNMENT")
}) == Some(true);
if assignment {
for inner in references(&style, 0, &mut work)? {
check_fill(&decoder.decode_by_id(inner)?, decoder, &mut work)?;
fills += 1;
}
} else {
check_fill(&style, decoder, &mut work)?;
fills += 1;
}
if fills > 1 {
return Err(invalid("multiple fill styles are unsupported"));
}
}
}
Ok(())
}
fn references(entity: &DecodedEntity, index: usize, work: &mut usize) -> Result<Vec<u32>> {
let list = entity
.get_list(index)
.ok_or_else(|| invalid("missing presentation references"))?;
if list.is_empty() || list.len() > 64 || list.len() > *work {
return Err(invalid(
"presentation reference budget or cardinality invalid",
));
}
*work -= list.len();
list.iter()
.map(|v| {
v.as_entity_ref()
.ok_or_else(|| invalid("invalid presentation reference"))
})
.collect()
}
fn check_fill(style: &DecodedEntity, decoder: &mut EntityDecoder, work: &mut usize) -> Result<()> {
if style.ifc_type != IfcType::IfcFillAreaStyle {
return Err(invalid("unsupported annotation presentation style"));
}
let leaves = references(style, 1, work)?;
if leaves.len() != 1 {
return Err(invalid("mixed fill paint is unsupported"));
}
let leaf = decoder.decode_by_id(leaves[0])?;
if leaf.ifc_type != IfcType::IfcColourRgb {
return Err(invalid("hatch/tile or non-RGB fill paint is unsupported"));
}
for i in 1..=3 {
if !leaf
.get(i)
.and_then(|a| a.as_float())
.is_some_and(|v| v.is_finite() && (0.0..=1.0).contains(&v))
{
return Err(invalid("invalid RGB fill component"));
}
}
Ok(())
}