use ifc_lite_core::{EntityDecoder, IfcType};
use ifc_lite_geometry::{GeometryRouter, LARGE_COORD_THRESHOLD_METERS};
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 rtc_offset: (f64, f64, f64),
pub needs_shift: bool,
pub building_rotation: Option<f64>,
}
#[inline]
pub fn coord_is_large(offset: (f64, f64, f64)) -> bool {
offset.0.abs() > LARGE_COORD_THRESHOLD_METERS
|| offset.1.abs() > LARGE_COORD_THRESHOLD_METERS
|| offset.2.abs() > LARGE_COORD_THRESHOLD_METERS
}
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 rtc_offset = 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 needs_shift = coord_is_large(rtc_offset);
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,
rtc_offset,
needs_shift,
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,
) -> (f64, f64, f64) {
let detected_rtc = router.detect_rtc_offset_from_jobs(jobs, decoder);
let mut rtc_offset = detected_rtc.unwrap_or((0.0, 0.0, 0.0));
let mut detection_succeeded = detected_rtc.is_some();
if !coord_is_large(rtc_offset) && (site_position.is_none() || !detection_succeeded) {
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) {
detection_succeeded = true;
if coord_is_large(full_rtc) {
rtc_offset = full_rtc;
}
}
}
if !detection_succeeded && !coord_is_large(rtc_offset) {
let raw = ifc_lite_core::scan_placement_bounds(content).rtc_offset();
rtc_offset = (
raw.0 * length_unit_scale,
raw.1 * length_unit_scale,
raw.2 * length_unit_scale,
);
}
rtc_offset
}
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;