///|
fn distance_between(left : Point3, right : Point3) -> Double {
let dx = right.x - left.x
let dy = right.y - left.y
let dz = right.z - left.z
(dx * dx + dy * dy + dz * dz).sqrt()
}
///|
pub fn polyline_length(
points : Array[Point3],
closed? : Bool = false,
) -> Double {
if points.length() < 2 {
return 0.0
}
let mut length = 0.0
for index in 0..<(points.length() - 1) {
length += distance_between(points[index], points[index + 1])
}
if closed && points.length() > 2 {
length += distance_between(points[points.length() - 1], points[0])
}
length
}
///|
fn curve_points_inner(
index : ModelIndex,
id : Int,
stack : Array[Int],
) -> Array[Point3] {
if stack.contains(id) {
return []
}
let next_stack = stack.copy()
next_stack.push(id)
match index.entity(id) {
Some(entity) =>
match entity.name.to_upper() {
"IFCPOLYLINE" | "IFCPOLYLOOP" => points_from_entity(index, entity)
"IFCINDEXEDPOLYCURVE" =>
match optional_reference_argument(entity, 0) {
Some(points_id) =>
match index.entity(points_id) {
Some(point_list) => points_from_entity(index, point_list)
None => []
}
None => []
}
"IFCTRIMMEDCURVE" =>
match optional_reference_argument(entity, 0) {
Some(base_id) => curve_points_inner(index, base_id, next_stack)
None => []
}
"IFCCOMPOSITECURVE" => {
let result : Array[Point3] = []
for segment_id in reference_list(entity_value(entity, 0)) {
match index.entity(segment_id) {
Some(segment) => {
let candidates = entity_references(segment)
for candidate in candidates {
let points = curve_points_inner(index, candidate, next_stack)
if points.length() > 0 {
for point in points {
result.push(point)
}
break
}
}
}
None => ()
}
}
result
}
"IFCCIRCLE" | "IFCELLIPSE" => []
_ => []
}
None => []
}
}
///|
pub fn curve_points(index : ModelIndex, id : Int) -> Array[Point3] {
curve_points_inner(index, id, [])
}
///|
pub fn curve_length(
index : ModelIndex,
id : Int,
closed? : Bool = false,
) -> Double {
polyline_length(curve_points(index, id), closed~)
}
///|
pub fn resample_polyline(
points : Array[Point3],
maximum_segment : Double,
) -> Array[Point3] {
if points.length() < 2 || maximum_segment <= 0.0 {
return points
}
let result : Array[Point3] = [points[0]]
for index in 0..<(points.length() - 1) {
let left = points[index]
let right = points[index + 1]
let length = distance_between(left, right)
let pieces = if length <= maximum_segment {
1
} else {
(length / maximum_segment).ceil().to_int()
}
for piece in 1..<=pieces {
let ratio = piece.to_double() / pieces.to_double()
result.push({
x: left.x + (right.x - left.x) * ratio,
y: left.y + (right.y - left.y) * ratio,
z: left.z + (right.z - left.z) * ratio,
})
}
}
result
}
///|
pub fn curve_bounds(index : ModelIndex, id : Int) -> BoundingBox {
BoundingBox::from_points(curve_points(index, id))
}
///|
pub fn curves_in_entity(index : ModelIndex, entity : Entity) -> Array[Int] {
entity_references(entity).filter(id => {
match index.entity(id) {
Some(candidate) => {
let name = candidate.name.to_upper()
name == "IFCPOLYLINE" ||
name == "IFCINDEXEDPOLYCURVE" ||
name == "IFCTRIMMEDCURVE" ||
name == "IFCCOMPOSITECURVE"
}
None => false
}
})
}