use crate::mesh_frame::MeshFrame;
use ifc_lite_core::limits::coord_is_large;
use ifc_lite_core::{EntityDecoder, IfcType};
use ifc_lite_geometry::GeometryRouter;
pub type Job = (u32, usize, usize, IfcType);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MetaMode {
StreamingPartial,
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)>,
jobs: &[Job],
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 => resolve_partial_rtc(
&router,
content,
site_position,
jobs,
decoder,
length_unit_scale,
),
MetaMode::SmallFileSingle => {
router.detect_rtc_offset_with_fallback(jobs, decoder, content)
}
};
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],
site_position: Option<(u32, usize, usize)>,
jobs: &[Job],
decoder: &mut EntityDecoder,
length_unit_scale: f64,
) -> Option<ifc_lite_core::RtcVerdict> {
let mut rtc_offset = router.detect_rtc_offset_from_jobs(jobs, 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_offset_from_jobs(jobs, &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(length_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;