use crate::decoder::EntityDecoder;
use crate::error::Result;
pub use crate::unit_labels::{
get_conversion_based_unit_factor, get_si_prefix_multiplier, try_si_prefix_multiplier,
};
pub fn extract_length_unit_scale(decoder: &mut EntityDecoder, project_id: u32) -> Result<f64> {
let project = decoder.decode_by_id(project_id)?;
if project.ifc_type.as_str() != "IFCPROJECT" {
return Ok(1.0); }
let units_attr = match project.get(8) {
Some(attr) => attr,
None => return Ok(1.0), };
let units_ref = match units_attr.as_entity_ref() {
Some(ref_id) => ref_id,
None => return Ok(1.0), };
let unit_assignment = decoder.decode_by_id(units_ref)?;
if unit_assignment.ifc_type.as_str() != "IFCUNITASSIGNMENT" {
return Ok(1.0); }
let units_list_attr = match unit_assignment.get(0) {
Some(attr) => attr,
None => return Ok(1.0), };
let units_list = match units_list_attr.as_list() {
Some(list) => list,
None => return Ok(1.0), };
for unit_attr in units_list {
let unit_ref = match unit_attr.as_entity_ref() {
Some(ref_id) => ref_id,
None => continue,
};
let unit_entity = match decoder.decode_by_id(unit_ref) {
Ok(entity) => entity,
Err(_) => continue, };
let unit_type_str = unit_entity.ifc_type.as_str();
if unit_type_str == "IFCSIUNIT" {
let unit_type_attr = match unit_entity.get(1) {
Some(attr) => attr,
None => continue,
};
let unit_type = match unit_type_attr.as_enum() {
Some(type_str) => type_str,
None => continue,
};
if unit_type != "LENGTHUNIT" {
continue; }
let prefix_attr = match unit_entity.get(2) {
Some(attr) => attr,
None => return Ok(1.0), };
if prefix_attr.is_null() {
return Ok(1.0); }
let prefix = match prefix_attr.as_enum() {
Some(prefix_str) => prefix_str,
None => return Ok(1.0), };
return Ok(get_si_prefix_multiplier(prefix));
}
if unit_type_str == "IFCCONVERSIONBASEDUNIT" {
let unit_type_attr = match unit_entity.get(1) {
Some(attr) => attr,
None => continue,
};
let unit_type = match unit_type_attr.as_enum() {
Some(type_str) => type_str,
None => continue,
};
if unit_type != "LENGTHUNIT" {
continue; }
if let Some(name_attr) = unit_entity.get(2) {
if let Some(name) = name_attr.as_string() {
if let Some(factor) = get_conversion_based_unit_factor(name) {
return Ok(factor);
}
}
}
let conversion_factor_ref = match unit_entity.get_ref(3) {
Some(ref_id) => ref_id,
None => continue,
};
let measure_with_unit = match decoder.decode_by_id(conversion_factor_ref) {
Ok(entity) => entity,
Err(_) => continue,
};
let value_attr = match measure_with_unit.get(0) {
Some(attr) => attr,
None => continue,
};
let conversion_value = if let Some(val) = value_attr.as_float() {
val
} else if let Some(val) = value_attr.as_int() {
val as f64
} else {
1.0
};
if conversion_value > 0.0 {
let mut unit_component_scale = 1.0;
if let Some(unit_component_ref) = measure_with_unit.get_ref(1) {
if let Ok(unit_component) = decoder.decode_by_id(unit_component_ref) {
if unit_component.ifc_type.as_str() == "IFCSIUNIT" {
if let Some(prefix_attr) = unit_component.get(2) {
if !prefix_attr.is_null() {
if let Some(prefix) = prefix_attr.as_enum() {
unit_component_scale = get_si_prefix_multiplier(prefix);
}
}
}
}
}
}
return Ok(conversion_value * unit_component_scale);
}
}
}
Ok(1.0)
}
pub fn try_extract_length_unit_scale(decoder: &mut EntityDecoder, project_id: u32) -> Option<f64> {
let project = decoder.decode_by_id(project_id).ok()?;
if project.ifc_type.as_str() != "IFCPROJECT" {
return Some(1.0); }
let units_ref = match project.get(8).and_then(|a| a.as_entity_ref()) {
Some(r) => r,
None => return Some(1.0),
};
let unit_assignment = decoder.decode_by_id(units_ref).ok()?;
if unit_assignment.ifc_type.as_str() != "IFCUNITASSIGNMENT" {
return Some(1.0);
}
let units_list = match unit_assignment.get(0).and_then(|a| a.as_list()) {
Some(list) => list,
None => return Some(1.0),
};
let mut saw_undecodable = false;
for unit_attr in units_list {
let unit_ref = match unit_attr.as_entity_ref() {
Some(r) => r,
None => continue,
};
let unit_entity = match decoder.decode_by_id(unit_ref) {
Ok(e) => e,
Err(_) => {
saw_undecodable = true;
continue;
}
};
match unit_entity.ifc_type.as_str() {
"IFCSIUNIT" => {
let is_length = unit_entity
.get(1)
.and_then(|a| a.as_enum())
.map(|t| t == "LENGTHUNIT")
.unwrap_or(false);
if !is_length {
continue;
}
return match unit_entity.get(2) {
None => Some(1.0),
Some(p) if p.is_null() => Some(1.0),
Some(p) => Some(p.as_enum().map(get_si_prefix_multiplier).unwrap_or(1.0)),
};
}
"IFCCONVERSIONBASEDUNIT" => {
let is_length = unit_entity
.get(1)
.and_then(|a| a.as_enum())
.map(|t| t == "LENGTHUNIT")
.unwrap_or(false);
if is_length {
return None;
}
}
_ => {}
}
}
if saw_undecodable {
None
} else {
Some(1.0)
}
}
pub fn try_extract_plane_angle_to_radians(
decoder: &mut EntityDecoder,
project_id: u32,
) -> Option<f64> {
let project = decoder.decode_by_id(project_id).ok()?;
if project.ifc_type.as_str() != "IFCPROJECT" {
return Some(1.0); }
let units_ref = match project.get(8).and_then(|a| a.as_entity_ref()) {
Some(r) => r,
None => return Some(1.0),
};
let unit_assignment = decoder.decode_by_id(units_ref).ok()?;
if unit_assignment.ifc_type.as_str() != "IFCUNITASSIGNMENT" {
return Some(1.0);
}
let units_list = match unit_assignment.get(0).and_then(|a| a.as_list()) {
Some(list) => list,
None => return Some(1.0),
};
let mut saw_undecodable = false;
for unit_attr in units_list {
let unit_ref = match unit_attr.as_entity_ref() {
Some(r) => r,
None => continue,
};
let unit_entity = match decoder.decode_by_id(unit_ref) {
Ok(e) => e,
Err(_) => {
saw_undecodable = true;
continue;
}
};
match unit_entity.ifc_type.as_str() {
"IFCSIUNIT" => {
if unit_entity.get(1).and_then(|a| a.as_enum()) != Some("PLANEANGLEUNIT") {
continue;
}
let prefix_scale = match unit_entity.get(2) {
Some(p) if !p.is_null() => {
p.as_enum().map(get_si_prefix_multiplier).unwrap_or(1.0)
}
_ => 1.0,
};
return Some(prefix_scale);
}
"IFCCONVERSIONBASEDUNIT" => {
if unit_entity.get(1).and_then(|a| a.as_enum()) != Some("PLANEANGLEUNIT") {
continue;
}
let conv_ref = unit_entity.get_ref(3)?;
let measure = decoder.decode_by_id(conv_ref).ok()?;
let value = measure.get(0).and_then(|a| a.as_float()).unwrap_or(0.0);
if value > 0.0 && value.is_finite() {
return Some(value);
}
return None;
}
_ => {}
}
}
if saw_undecodable {
None
} else {
Some(1.0)
}
}
pub fn extract_plane_angle_to_radians(decoder: &mut EntityDecoder, project_id: u32) -> Result<f64> {
let project = decoder.decode_by_id(project_id)?;
if project.ifc_type.as_str() != "IFCPROJECT" {
return Ok(1.0);
}
let units_attr = match project.get(8) {
Some(attr) => attr,
None => return Ok(1.0),
};
let units_ref = match units_attr.as_entity_ref() {
Some(ref_id) => ref_id,
None => return Ok(1.0),
};
let unit_assignment = decoder.decode_by_id(units_ref)?;
if unit_assignment.ifc_type.as_str() != "IFCUNITASSIGNMENT" {
return Ok(1.0);
}
let units_list_attr = match unit_assignment.get(0) {
Some(attr) => attr,
None => return Ok(1.0),
};
let units_list = match units_list_attr.as_list() {
Some(list) => list,
None => return Ok(1.0),
};
for unit_attr in units_list {
let unit_ref = match unit_attr.as_entity_ref() {
Some(ref_id) => ref_id,
None => continue,
};
let unit_entity = match decoder.decode_by_id(unit_ref) {
Ok(entity) => entity,
Err(_) => continue,
};
let unit_type_str = unit_entity.ifc_type.as_str();
if unit_type_str == "IFCSIUNIT" {
let kind = unit_entity.get(1).and_then(|a| a.as_enum());
if kind != Some("PLANEANGLEUNIT") {
continue;
}
let prefix_scale = match unit_entity.get(2) {
Some(p) if !p.is_null() => p
.as_enum()
.map(get_si_prefix_multiplier)
.unwrap_or(1.0),
_ => 1.0,
};
return Ok(prefix_scale);
}
if unit_type_str == "IFCCONVERSIONBASEDUNIT" {
let kind = unit_entity.get(1).and_then(|a| a.as_enum());
if kind != Some("PLANEANGLEUNIT") {
continue;
}
let conv_ref = match unit_entity.get_ref(3) {
Some(r) => r,
None => continue,
};
let measure = match decoder.decode_by_id(conv_ref) {
Ok(e) => e,
Err(_) => continue,
};
let value_attr = match measure.get(0) {
Some(a) => a,
None => continue,
};
let value = value_attr.as_float().unwrap_or(0.0);
if value > 0.0 && value.is_finite() {
return Ok(value);
}
}
}
Ok(1.0)
}
#[cfg(test)]
#[path = "units_tests.rs"]
mod tests;