///|
/// Test whether a 2D sample is collinear within a metric tolerance.
pub fn is_collinear2(points : ArrayView[Point2], tolerance~ : Double) -> Bool {
if points.length() < 3 {
return true
}
let origin = points[0]
let direction = points[1].minus(origin)
for point in points[2:] {
let delta = point.minus(origin)
if abs(direction.x * delta.y - direction.y * delta.x) > tolerance {
return false
}
}
true
}
///|
pub fn is_collinear3(points : ArrayView[Point3], tolerance~ : Double) -> Bool {
if points.length() < 3 {
return true
}
let origin = points[0]
let direction = points[1].minus(origin)
for point in points[2:] {
if direction.cross(point.minus(origin)).norm() > tolerance {
return false
}
}
true
}
///|
pub fn duplicate_count2(points : ArrayView[Point2], tolerance~ : Double) -> Int {
let mut count = 0
for i in 0.. Int {
let mut count = 0
for i in 0.. Double raise GeometryError {
if points.length() == 0 {
raise GeometryError::NotEnoughPoints("spread needs points")
}
let center = polygon_centroid(points)
let mut sum = 0.0
for point in points {
let d = point.minus(center).norm()
sum += d * d
}
(sum / points.length().to_double()).sqrt()
}
///|
pub fn spread3(points : ArrayView[Point3]) -> Double raise GeometryError {
let cloud = PointCloud::from_points(points)
cloud.rms_radius()
}
///|
pub fn support_ratio2(points : ArrayView[Point2], tolerance : Double) -> Double {
if points.length() == 0 {
0.0
} else {
(points.length() - duplicate_count2(points, tolerance~)).to_double() /
points.length().to_double()
}
}
///|
pub fn support_ratio3(points : ArrayView[Point3], tolerance : Double) -> Double {
if points.length() == 0 {
0.0
} else {
(points.length() - duplicate_count3(points, tolerance~)).to_double() /
points.length().to_double()
}
}
///|
pub fn condition_proxy2(
points : ArrayView[Point2],
) -> Double raise GeometryError {
let extent = coordinate_extent2(points)
if extent <= 0.0 {
raise GeometryError::DegenerateInput("zero 2D extent")
}
let spread = spread2(points)
spread / extent
}
///|
pub fn condition_proxy3(
points : ArrayView[Point3],
) -> Double raise GeometryError {
let extent = coordinate_extent3(points)
if extent <= 0.0 {
raise GeometryError::DegenerateInput("zero 3D extent")
}
let spread = spread3(points)
spread / extent
}
///|
pub fn has_area2(points : ArrayView[Point2], minimum_area : Double) -> Bool {
if points.length() < 3 {
false
} else {
let rect = rect2_from_points(points) catch {
_ => { min: Point2::new(x=0.0, y=0.0), max: Point2::new(x=0.0, y=0.0) }
}
rect.area() >= minimum_area
}
}
///|
pub fn has_volume3(points : ArrayView[Point3], minimum_volume : Double) -> Bool {
let box = Aabb3::from_points(points) catch {
_ =>
{
min: Point3::new(x=0.0, y=0.0, z=0.0),
max: Point3::new(x=0.0, y=0.0, z=0.0),
}
}
box.volume() >= minimum_volume
}
///|
pub fn normalize_quality_score(
value : Double,
lower : Double,
upper : Double,
) -> Double {
if upper <= lower {
0.0
} else {
((value - lower) / (upper - lower)).max(0.0).min(1.0)
}
}
///|
pub fn combine_quality_scores(
scores : ArrayView[Double],
weights : ArrayView[Double],
) -> Double raise GeometryError {
weighted_mean2(scores, weights)
}