///|
pub(all) struct Line2 {
normal : Vec2
offset : Double
} derive(Debug, Eq)
///|
pub fn Line2::from_points(a : Point2, b : Point2) -> Line2 raise GeometryError {
let direction = a.minus(b)
let normal = Vec2::new(x=-direction.y, y=direction.x).normalize()
{ normal, offset: -(normal.x * a.x + normal.y * a.y) }
}
///|
pub fn Line2::signed_distance(line : Line2, point : Point2) -> Double {
line.normal.x * point.x + line.normal.y * point.y + line.offset
}
///|
pub fn Line2::project(line : Line2, point : Point2) -> Point2 {
point.translate(line.normal.scale(-line.signed_distance(point)))
}
///|
pub fn Line2::intersect(a : Line2, b : Line2) -> Point2? {
let det = a.normal.x * b.normal.y - a.normal.y * b.normal.x
if abs(det) <= 0.000000000001 {
None
} else {
Some(
Point2::new(
x=(a.normal.y * b.offset - b.normal.y * a.offset) / det,
y=(b.normal.x * a.offset - a.normal.x * b.offset) / det,
),
)
}
}
///|
pub(all) struct Plane3 {
normal : Vec3
offset : Double
} derive(Debug, Eq)
///|
pub fn Plane3::from_points(
a : Point3,
b : Point3,
c : Point3,
) -> Plane3 raise GeometryError {
let normal = b.minus(a).cross(c.minus(a)).normalize()
{ normal, offset: -normal.dot(a.to_vec()) }
}
///|
pub fn Plane3::signed_distance(plane : Plane3, point : Point3) -> Double {
plane.normal.dot(point.to_vec()) + plane.offset
}
///|
pub fn Plane3::project(plane : Plane3, point : Point3) -> Point3 {
point.translate(plane.normal.scale(-plane.signed_distance(point)))
}
///|
pub fn Plane3::ray_hit(plane : Plane3, ray : Ray3) -> Double? {
let denominator = plane.normal.dot(ray.direction)
if abs(denominator) <= 0.000000000001 {
None
} else {
Some(-(plane.normal.dot(ray.origin.to_vec()) + plane.offset) / denominator)
}
}
///|
pub(all) struct Circle2 {
center : Point2
radius : Double
} derive(Debug, Eq)
///|
pub fn Circle2::new(
center~ : Point2,
radius~ : Double,
) -> Circle2 raise GeometryError {
if radius < 0.0 {
raise GeometryError::DegenerateInput("circle radius must be non-negative")
}
{ center, radius }
}
///|
pub fn Circle2::contains(circle : Circle2, point : Point2) -> Bool {
circle.center.minus(point).norm2() <= circle.radius * circle.radius
}
///|
pub fn Circle2::signed_distance(circle : Circle2, point : Point2) -> Double {
circle.center.minus(point).norm() - circle.radius
}
///|
pub fn polygon_signed_area(
points : ArrayView[Point2],
) -> Double raise GeometryError {
if points.length() < 3 {
raise GeometryError::NotEnoughPoints("polygon needs at least three points")
}
let mut total = 0.0
for i in 0.. Point2 raise GeometryError {
let area = polygon_signed_area(points)
if abs(area) <= 0.000000000001 {
raise GeometryError::DegenerateInput("polygon area must be non-zero")
}
let mut x = 0.0
let mut y = 0.0
for i in 0.. Double {
let mut result = 0.0
if points.length() > 1 {
for i in 1.. Point2 {
let direction = b.minus(a)
let denominator = direction.norm2()
if denominator <= 0.000000000001 {
a
} else {
let t = clamp(direction.dot(point.minus(a)), lo=0.0, hi=denominator) /
denominator
a.translate(direction.scale(t))
}
}
///|
pub fn segment_distance(a : Point2, b : Point2, point : Point2) -> Double {
point.minus(closest_point_on_segment(a, b, point)).norm()
}
///|
pub fn barycentric_2d(
a : Point2,
b : Point2,
c : Point2,
p : Point2,
) -> (Double, Double, Double) raise GeometryError {
let v0 = b.minus(a)
let v1 = c.minus(a)
let v2 = p.minus(a)
let denominator = v0.x * v1.y - v1.x * v0.y
if abs(denominator) <= 0.000000000001 {
raise GeometryError::DegenerateInput("triangle is degenerate")
}
let v = (v2.x * v1.y - v1.x * v2.y) / denominator
let w = (v0.x * v2.y - v2.x * v0.y) / denominator
(1.0 - v - w, v, w)
}
///|
pub fn barycentric_contains(
weights : (Double, Double, Double),
tolerance? : Double = 0.000000001,
) -> Bool {
let (a, b, c) = weights
a >= -tolerance &&
b >= -tolerance &&
c >= -tolerance &&
a <= 1.0 + tolerance &&
b <= 1.0 + tolerance &&
c <= 1.0 + tolerance
}