microcad_core/geo2d/
line.rs

1// Copyright © 2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! 2D line geometry.
5
6use cgmath::{Angle, InnerSpace, Rad};
7use geo::AffineOps;
8
9use crate::*;
10
11/// A 2D line type.
12#[derive(Debug, Clone)]
13pub struct Line(pub geo2d::Point, pub geo2d::Point);
14
15impl Line {
16    /// Shorten edge on both ends by a certain amount.
17    pub fn shorter(&self, amount: Scalar) -> Self {
18        let d = self.vec();
19        let d = 0.5 * d * (1.0 - amount / d.magnitude());
20        let c = self.center();
21        Self(c - (d.x, d.y).into(), c + (d.x, d.y).into())
22    }
23
24    /// Calculate the radius edge from a circle.
25    ///
26    /// An edge from the circle's center to a point on its periphery.
27    pub fn radius_edge(circle: &Circle, angle: &Rad<Scalar>) -> Self {
28        let Circle {
29            radius,
30            offset: Vec2 { x, y },
31        } = &circle;
32        let x1 = x + radius * angle.cos();
33        let y1 = y + radius * angle.sin();
34        Self(geo::Point::new(*x, *y), geo::Point::new(x1, y1))
35    }
36
37    /// Return vector of this edge.
38    pub fn vec(&self) -> Vec2 {
39        Vec2::from(self.1.x_y()) - Vec2::from(self.0.x_y())
40    }
41
42    /// Return center of this edge.
43    pub fn center(&self) -> geo2d::Point {
44        (self.0 + self.1) * 0.5
45    }
46
47    /// Calculate angle in radians.
48    pub fn angle(&self) -> Rad<Scalar> {
49        let (p1, p2) = (self.0, self.1);
50        let dx = p2.x() - p1.x();
51        let dy = p2.y() - p1.y();
52        Rad(dy.atan2(dx))
53    }
54
55    /// 2D Transformation matrix for this edge (rotation and offset).
56    pub fn matrix(&self) -> Mat3 {
57        let rot = Mat2::from_angle(self.angle());
58        let mut m = Mat3::from_translation(self.0.x_y().into());
59        m.x = Vec3::new(rot.x.x, rot.x.y, 0.0);
60        m.y = Vec3::new(rot.y.x, rot.y.y, 0.0);
61        m
62    }
63}
64
65impl CalcBounds2D for Line {
66    fn calc_bounds_2d(&self) -> geo2d::Bounds2D {
67        geo2d::Bounds2D::new(self.0.x_y().into(), self.1.x_y().into())
68    }
69}
70
71impl Transformed2D for Line {
72    fn transformed_2d(&self, mat: &Mat3) -> Self {
73        let transform = &geo2d::mat3_to_affine_transform(mat);
74        Self(
75            self.0.affine_transform(transform),
76            self.1.affine_transform(transform),
77        )
78    }
79}