#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FunctionStatus {
NativePrimitive,
Scaffolded,
Implemented,
NotApplicable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FunctionSupport {
pub name: &'static str,
pub owner: &'static str,
pub status: FunctionStatus,
}
const NATIVE: FunctionStatus = FunctionStatus::NativePrimitive;
#[allow(dead_code)]
const SCAFFOLDED: FunctionStatus = FunctionStatus::Scaffolded;
const NOT_APPLICABLE: FunctionStatus = FunctionStatus::NotApplicable;
const IMPLEMENTED: FunctionStatus = FunctionStatus::Implemented;
pub const FUNCTIONS: &[FunctionSupport] = &[
FunctionSupport {
name: "IfcAssociatedSurface",
owner: "surface::basis",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcBaseAxis",
owner: "resource::axes",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcBuild2Axes",
owner: "resource::functions",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcBuildAxes",
owner: "transform",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcConsecutiveSegments",
owner: "rules::express",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcConstraintsParamBSpline",
owner: "rules::express",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcCrossProduct",
owner: "axiolid_core::Vec3::cross",
status: NATIVE,
},
FunctionSupport {
name: "IfcCurveDim",
owner: "rules::dimension",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcCurveWeightsPositive",
owner: "rules::bspline",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcDotProduct",
owner: "axiolid_core::Vec3::dot",
status: NATIVE,
},
FunctionSupport {
name: "IfcFirstProjAxis",
owner: "transform",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcGetBasisSurface",
owner: "surface::basis",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcListToArray",
owner: "curve::bspline",
status: NOT_APPLICABLE,
},
FunctionSupport {
name: "IfcMakeArrayOfArray",
owner: "surface::bspline",
status: NOT_APPLICABLE,
},
FunctionSupport {
name: "IfcNormalise",
owner: "axiolid_core::Vec3::normalize",
status: NATIVE,
},
FunctionSupport {
name: "IfcOrthogonalComplement",
owner: "resource::functions",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcSameAxis2Placement",
owner: "resource::functions",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcSameCartesianPoint",
owner: "resource::functions",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcSameDirection",
owner: "resource::functions",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcSameValue",
owner: "resource::functions",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcScalarTimesVector",
owner: "axiolid_core::Vec3::mul",
status: NATIVE,
},
FunctionSupport {
name: "IfcSecondProjAxis",
owner: "transform",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcSurfaceWeightsPositive",
owner: "rules::bspline",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcVectorDifference",
owner: "axiolid_core::Vec3::sub",
status: NATIVE,
},
FunctionSupport {
name: "IfcVectorSum",
owner: "axiolid_core::Vec3::add",
status: NATIVE,
},
FunctionSupport {
name: "IfcPointListDim",
owner: "resource::functions",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcTaperedSweptAreaProfiles",
owner: "rules::surface",
status: IMPLEMENTED,
},
FunctionSupport {
name: "IfcCorrectLocalPlacement",
owner: "rules::placement",
status: IMPLEMENTED,
},
];
const DEFAULT_EPSILON: f64 = 0.000_001;
#[must_use]
pub fn same_value(a: f64, b: f64, epsilon: Option<f64>) -> bool {
let eps = epsilon.unwrap_or(DEFAULT_EPSILON);
a + eps > b && a < b + eps
}
#[must_use]
pub fn same_components(a: &[f64], b: &[f64], epsilon: Option<f64>) -> bool {
let at = |v: &[f64], i: usize| v.get(i).copied().unwrap_or(0.0);
(0..3).all(|i| same_value(at(a, i), at(b, i), epsilon))
}
#[must_use]
pub fn point_list_dim(type_name: &str) -> Option<usize> {
match type_name.to_ascii_uppercase().as_str() {
"IFCCARTESIANPOINTLIST2D" => Some(2),
"IFCCARTESIANPOINTLIST3D" => Some(3),
_ => None,
}
}
#[must_use]
pub fn orthogonal_complement(v: &[f64]) -> Option<[f64; 2]> {
match v {
[x, y] => Some([-*y, *x]),
_ => None,
}
}
#[must_use]
pub fn build_2_axes(ref_direction: Option<[f64; 2]>) -> ([f64; 2], [f64; 2]) {
let x = ref_direction
.and_then(|d| {
let len = d[0].hypot(d[1]);
(len.is_finite() && len > 0.0).then(|| [d[0] / len, d[1] / len])
})
.unwrap_or([1.0, 0.0]);
(x, [-x[1], x[0]])
}
#[must_use]
pub fn same_axis2_placement(
a_axes: (&[f64], &[f64]),
a_location: &[f64],
b_axes: (&[f64], &[f64]),
b_location: &[f64],
epsilon: Option<f64>,
) -> bool {
same_components(a_axes.0, b_axes.0, epsilon)
&& same_components(a_axes.1, b_axes.1, epsilon)
&& same_components(a_location, b_location, epsilon)
}
pub fn function_support(name: &str) -> Option<&'static FunctionSupport> {
FUNCTIONS
.iter()
.find(|item| item.name.eq_ignore_ascii_case(name))
}
#[cfg(test)]
mod function_tests {
use super::*;
#[test]
fn same_value_boundary_is_exclusive() {
assert!(same_value(1.0, 1.0 + 0.5e-6, None));
assert!(!same_value(1.0, 1.0 + 1e-6, None), "exactly eps apart");
assert!(!same_value(1.0, 1.0 + 2e-6, None));
assert!(!same_value(1.0 + 1e-6, 1.0, None));
}
#[test]
fn same_value_default_epsilon_is_not_exact_equality() {
assert!(same_value(2.0, 2.0 + 1e-9, None));
assert!(!same_value(2.0, 2.0 + 1e-9, Some(1e-12)));
}
#[test]
fn missing_third_component_is_zero() {
assert!(same_components(&[1.0, 2.0], &[1.0, 2.0, 0.0], None));
assert!(!same_components(&[1.0, 2.0], &[1.0, 2.0, 5.0], None));
}
#[test]
fn point_list_dim_refuses_unknown_types() {
assert_eq!(point_list_dim("IfcCartesianPointList2D"), Some(2));
assert_eq!(point_list_dim("IFCCARTESIANPOINTLIST3D"), Some(3));
assert_eq!(point_list_dim("IFCCARTESIANPOINT"), None);
}
#[test]
fn orthogonal_complement_is_2d_only() {
assert_eq!(orthogonal_complement(&[1.0, 0.0]), Some([0.0, 1.0]));
assert_eq!(orthogonal_complement(&[0.0, 1.0]), Some([-1.0, 0.0]));
assert_eq!(orthogonal_complement(&[1.0, 0.0, 0.0]), None);
}
#[test]
fn build_2_axes_defaults_and_normalises() {
assert_eq!(build_2_axes(None), ([1.0, 0.0], [0.0, 1.0]));
assert_eq!(build_2_axes(Some([0.0, 0.0])), ([1.0, 0.0], [0.0, 1.0]));
let (x, y) = build_2_axes(Some([5.0, 0.0]));
assert!((x[0] - 1.0).abs() < 1e-12, "normalised: {x:?}");
assert!((y[1] - 1.0).abs() < 1e-12, "perpendicular: {y:?}");
}
#[test]
fn same_axis2_placement_actually_compares_locations() {
let x = [1.0, 0.0, 0.0];
let y = [0.0, 1.0, 0.0];
assert!(same_axis2_placement(
(&x, &y),
&[0.0, 0.0, 0.0],
(&x, &y),
&[0.0, 0.0, 0.0],
None
));
assert!(!same_axis2_placement(
(&x, &y),
&[0.0, 0.0, 0.0],
(&x, &y),
&[5.0, 0.0, 0.0],
None
));
assert!(!same_axis2_placement(
(&x, &y),
&[0.0, 0.0, 0.0],
(&y, &x),
&[0.0, 0.0, 0.0],
None
));
}
}