use crate::mesh_frame::MeshFrame;
use ifc_lite_core::limits::coord_is_large;
use ifc_lite_core::EntityDecoder;
use ifc_lite_geometry::GeometryRouter;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MetaMode {
StreamingPartial { scanned_through: usize },
SmallFileSingle,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct StreamMeta {
pub length_unit_scale: f64,
pub plane_angle_to_radians: f64,
pub frame: MeshFrame,
pub building_rotation: Option<f64>,
}
pub fn resolve_stream_meta(
mode: MetaMode,
content: &[u8],
project_id: Option<u32>,
site_position: Option<(u32, usize, usize)>,
decoder: &mut EntityDecoder,
) -> StreamMeta {
let unit_scales = crate::prepass::resolve_unit_scales(content, project_id, decoder);
let length_unit_scale = unit_scales.length_unit_scale;
decoder.seed_unit_scales(length_unit_scale, unit_scales.plane_angle_to_radians);
let router = GeometryRouter::with_scale(length_unit_scale);
let detected = match mode {
MetaMode::StreamingPartial { scanned_through } => {
resolve_partial_rtc(&router, content, scanned_through, site_position, decoder)
}
MetaMode::SmallFileSingle => router.detect_rtc_offset_for_file(content, decoder),
};
let frame = MeshFrame::select(None, detected);
let building_rotation =
site_position.and_then(|pos| resolve_building_rotation(pos, &router, decoder));
StreamMeta {
length_unit_scale,
plane_angle_to_radians: unit_scales.plane_angle_to_radians,
frame,
building_rotation,
}
}
fn resolve_partial_rtc(
router: &GeometryRouter,
content: &[u8],
scanned_through: usize,
site_position: Option<(u32, usize, usize)>,
decoder: &mut EntityDecoder,
) -> Option<ifc_lite_core::RtcVerdict> {
let head = &content[..scanned_through.min(content.len())];
let mut rtc_offset = router.detect_rtc_anchor_for_file(head, decoder);
let found_large = rtc_offset.is_some_and(coord_is_large);
if !found_large && (site_position.is_none() || rtc_offset.is_none()) {
let full_index = crate::build_entity_index_parallel(content);
let mut full_decoder = EntityDecoder::with_index(content, full_index);
if let Some(full_rtc) = router.detect_rtc_anchor_for_file(content, &mut full_decoder) {
if coord_is_large(full_rtc) || rtc_offset.is_none() {
rtc_offset = Some(full_rtc);
}
}
}
rtc_offset.map(ifc_lite_core::RtcVerdict::of_anchor).or_else(|| {
ifc_lite_core::scan_placement_bounds(content).rtc_offset(router.unit_scale())
})
}
fn resolve_building_rotation(
site_pos: (u32, usize, usize),
router: &GeometryRouter,
decoder: &mut EntityDecoder,
) -> Option<f64> {
let (site_id, start, end) = site_pos;
let site_entity = decoder.decode_at_with_id(site_id, start, end).ok()?;
let matrix = router.resolve_scaled_placement(&site_entity, decoder).ok()?;
ifc_lite_geometry::rotation_angle_about_z(&matrix)
}
#[cfg(test)]
#[path = "stream_meta_tests.rs"]
mod tests;