rs_math/graphical/
triangle.rs

1use crate::graphical::circle::Circle;
2use crate::graphical::point_2d::Point2D;
3
4/// 三角形结构体,由三个顶点组成。
5#[derive(Debug, PartialEq)]
6pub struct Triangle {
7    /// 三角形的第一个顶点
8    pub vertex_a: Point2D,
9    /// 三角形的第二个顶点
10    pub vertex_b: Point2D,
11    /// 三角形的第三个顶点
12    pub vertex_c: Point2D,
13}
14/// 枚举类型,表示三角形的类型。
15#[derive(Debug, PartialEq)]
16pub enum TriangleType {
17    /// 锐角三角形
18    Acute,
19    /// 直角三角形
20    Right,
21    /// 钝角三角形
22    Obtuse,
23}
24#[allow(dead_code)]
25impl Triangle {
26    /// 构造函数,用于创建新的三角形实例。
27    ///
28    /// # 参数
29    ///
30    /// * `vertex_a` - 三角形的顶点 A。
31    /// * `vertex_b` - 三角形的顶点 B。
32    /// * `vertex_c` - 三角形的顶点 C。
33    ///
34    /// # 示例
35    ///
36    /// ```
37    /// use rs_math::graphical::point_2d::Point2D;
38    /// use rs_math::graphical::triangle::Triangle;
39    ///
40    /// let vertex_a = Point2D { x: 0.0, y: 0.0 };
41    /// let vertex_b = Point2D { x: 1.0, y: 0.0 };
42    /// let vertex_c = Point2D { x: 0.0, y: 1.0 };
43    ///
44    /// let triangle = Triangle::new(vertex_a, vertex_b, vertex_c);
45    /// ```
46    pub fn new(vertex_a: Point2D, vertex_b: Point2D, vertex_c: Point2D) -> Self {
47        Triangle {
48            vertex_a,
49            vertex_b,
50            vertex_c,
51        }
52    }
53
54    /// 计算三角形的边长。
55    ///
56    /// # 参数
57    ///
58    /// * `start` - 边的起点。
59    /// * `end` - 边的终点。
60    ///
61    /// # 返回值
62    ///
63    /// 返回边的长度。
64    ///
65    /// # 示例
66    ///
67    /// ```
68    /// use rs_math::graphical::point_2d::Point2D;
69    /// use rs_math::graphical::triangle::Triangle;
70    ///
71    /// let vertex_a = Point2D { x: 0.0, y: 0.0 };
72    /// let vertex_b = Point2D { x: 1.0, y: 0.0 };
73    /// let vertex_c = Point2D { x: 0.0, y: 1.0 };
74    ///
75    /// let triangle = Triangle::new(vertex_a, vertex_b, vertex_c);
76    /// let length = triangle.side_length(&vertex_a, &vertex_b);
77    /// ```
78    pub fn side_length(&self, start: &Point2D, end: &Point2D) -> f64 {
79        ((end.x - start.x).powi(2) + (end.y - start.y).powi(2)).sqrt()
80    }
81
82    /// 计算三角形的周长。
83    ///
84    /// # 返回值
85    ///
86    /// 返回三角形的周长。
87    ///
88    /// # 示例
89    ///
90    /// ```
91    /// use rs_math::graphical::point_2d::Point2D;
92    /// use rs_math::graphical::triangle::{Triangle, TriangleType};
93    ///
94    /// let vertex_a = Point2D { x: 0.0, y: 0.0 };
95    /// let vertex_b = Point2D { x: 1.0, y: 0.0 };
96    /// let vertex_c = Point2D { x: 0.0, y: 1.0 };
97    ///
98    /// let triangle = Triangle::new(vertex_a, vertex_b, vertex_c);
99    /// let perimeter = triangle.perimeter();
100    /// ```
101    pub fn perimeter(&self) -> f64 {
102        let side_ab = self.side_length(&self.vertex_a, &self.vertex_b);
103        let side_bc = self.side_length(&self.vertex_b, &self.vertex_c);
104        let side_ca = self.side_length(&self.vertex_c, &self.vertex_a);
105        side_ab + side_bc + side_ca
106    }
107
108    /// 计算三角形的面积。
109    ///
110    /// # 返回值
111    ///
112    /// 返回三角形的面积。
113    ///
114    /// # 示例
115    ///
116    /// ```
117    /// use rs_math::graphical::point_2d::Point2D;
118    /// use rs_math::graphical::triangle::{Triangle, TriangleType};
119    ///
120    /// let vertex_a = Point2D { x: 0.0, y: 0.0 };
121    /// let vertex_b = Point2D { x: 1.0, y: 0.0 };
122    /// let vertex_c = Point2D { x: 0.0, y: 1.0 };
123    ///
124    /// let triangle = Triangle::new(vertex_a, vertex_b, vertex_c);
125    /// let area = triangle.area();
126    /// ```
127    pub fn area(&self) -> f64 {
128        let side_ab = self.side_length(&self.vertex_a, &self.vertex_b);
129        let side_bc = self.side_length(&self.vertex_b, &self.vertex_c);
130        let side_ca = self.side_length(&self.vertex_c, &self.vertex_a);
131        let s = self.perimeter() / 2.0; // 半周长
132        (s * (s - side_ab) * (s - side_bc) * (s - side_ca)).sqrt()
133    }
134
135
136    /// 计算三角形的半周长。
137    ///
138    /// # 返回值
139    ///
140    /// 返回三角形的半周长。
141    ///
142    /// # 示例
143    ///
144    /// ```
145    /// use rs_math::graphical::point_2d::Point2D;
146    /// use rs_math::graphical::triangle::{Triangle, TriangleType};
147    ///
148    /// let vertex_a = Point2D { x: 0.0, y: 0.0 };
149    /// let vertex_b = Point2D { x: 1.0, y: 0.0 };
150    /// let vertex_c = Point2D { x: 0.0, y: 1.0 };
151    ///
152    /// let triangle = Triangle::new(vertex_a, vertex_b, vertex_c);
153    /// let semi_perimeter = triangle.semi_perimeter();
154    /// ```
155    pub fn semi_perimeter(&self) -> f64 {
156        let side_ab = self.side_length(&self.vertex_a, &self.vertex_b);
157        let side_bc = self.side_length(&self.vertex_b, &self.vertex_c);
158        let side_ca = self.side_length(&self.vertex_c, &self.vertex_a);
159        (side_ab + side_bc + side_ca) / 2.0
160    }
161
162
163    /// 计算三角形的内切圆。
164    ///
165    /// # 返回值
166    ///
167    /// 返回一个 `Circle` 结构体,表示三角形的内切圆。
168    ///
169    /// # 示例
170    ///
171    /// ```
172    /// use rs_math::graphical::point_2d::Point2D;
173    /// use rs_math::graphical::triangle::{Triangle, TriangleType};
174    ///
175    /// let vertex_a = Point2D { x: 0.0, y: 0.0 };
176    /// let vertex_b = Point2D { x: 1.0, y: 0.0 };
177    /// let vertex_c = Point2D { x: 0.0, y: 1.0 };
178    ///
179    /// let triangle = Triangle::new(vertex_a, vertex_b, vertex_c);
180    /// let in_circle = triangle.in_circle();
181    /// ```
182    pub fn in_circle(&self) -> Circle {
183        let s = self.semi_perimeter();
184        let side_ab = self.side_length(&self.vertex_a, &self.vertex_b);
185        let side_bc = self.side_length(&self.vertex_b, &self.vertex_c);
186        let side_ca = self.side_length(&self.vertex_c, &self.vertex_a);
187
188        let radius = self.area() / s;
189
190        // 计算内切圆的中心坐标
191        let center_x = (side_bc * self.vertex_a.x + side_ca * self.vertex_b.x + side_ab * self.vertex_c.x) / (side_ab + side_bc + side_ca);
192        let center_y = (side_bc * self.vertex_a.y + side_ca * self.vertex_b.y + side_ab * self.vertex_c.y) / (side_ab + side_bc + side_ca);
193
194        Circle {
195            x: center_x,
196            y: center_y,
197            radius,
198        }
199    }
200    /// 绕着指定点旋转三角形,返回新的三角形实例。
201    ///
202    /// # 参数
203    ///
204    /// - `center`: 旋转中心点坐标。
205    /// - `angle_degrees`: 旋转角度(度数)。
206    ///
207    /// # 返回值
208    ///
209    /// 返回一个新的 `Triangle` 结构体,表示旋转后的三角形。
210    ///
211    /// # 示例
212    ///
213    /// ```
214    /// use rs_math::graphical::point_2d::Point2D;
215    /// use rs_math::graphical::triangle::{Triangle, TriangleType};
216    ///
217    /// let vertex_a = Point2D { x: 0.0, y: 0.0 };
218    /// let vertex_b = Point2D { x: 1.0, y: 0.0 };
219    /// let vertex_c = Point2D { x: 0.0, y: 1.0 };
220    ///
221    /// let triangle = Triangle::new(vertex_a, vertex_b, vertex_c);
222    /// let center = Point2D { x: 0.5, y: 0.5 };
223    /// let rotated_triangle = triangle.rotate_around_point(&center, 45.0);
224    /// ```
225    pub fn rotate_around_point(&self, center: &Point2D, angle_degrees: f64) -> Triangle {
226        // 将角度转换为弧度
227        let angle_radians = angle_degrees.to_radians();
228
229        // 定义旋转矩阵
230        let rotation_matrix = [
231            [angle_radians.cos(), -angle_radians.sin()],
232            [angle_radians.sin(), angle_radians.cos()],
233        ];
234
235        // 对每个顶点进行旋转
236        let new_vertex_a = rotate_point(&self.vertex_a, center, rotation_matrix);
237        let new_vertex_b = rotate_point(&self.vertex_b, center, rotation_matrix);
238        let new_vertex_c = rotate_point(&self.vertex_c, center, rotation_matrix);
239
240        Triangle {
241            vertex_a: new_vertex_a,
242            vertex_b: new_vertex_b,
243            vertex_c: new_vertex_c,
244        }
245    }
246
247    /// 判断点是否在三角形内。
248    ///
249    /// # 参数
250    ///
251    /// - `p`: 要判断的点坐标。
252    ///
253    /// # 返回值
254    ///
255    /// 如果点在三角形内,返回 `true`,否则返回 `false`。
256    ///
257    /// # 示例
258    ///
259    /// ```
260    /// use rs_math::graphical::point_2d::Point2D;
261    /// use rs_math::graphical::triangle::{Triangle, TriangleType};
262    ///
263    /// let triangle = Triangle {
264    ///     vertex_a: Point2D { x: 0.0, y: 0.0 },
265    ///     vertex_b: Point2D { x: 1.0, y: 0.0 },
266    ///     vertex_c: Point2D { x: 0.0, y: 1.0 },
267    /// };
268    /// let point_inside = triangle.point_inside_triangle(&Point2D { x: 0.5, y: 0.5 });
269    /// ```
270    pub fn point_inside_triangle(&self, p: &Point2D) -> bool {
271        // 计算重心坐标
272        let barycentric_coords = self.barycentric_coordinates(p);
273
274        // 判断点是否在三角形内
275        barycentric_coords.iter().all(|&coord| coord >= 0.0 && coord <= 1.0)
276    }
277
278    /// 辅助方法,计算点的重心坐标。
279    ///
280    /// # 参数
281    ///
282    /// - `p`: 要计算重心坐标的点。
283    ///
284    /// # 返回值
285    ///
286    /// 返回包含三个元素的数组,表示点的重心坐标。
287    ///
288    /// # 示例
289    ///
290    /// ```
291    /// use rs_math::graphical::point_2d::Point2D;
292    /// use rs_math::graphical::triangle::{Triangle, TriangleType};
293    ///
294    /// let triangle = Triangle {
295    ///     vertex_a: Point2D { x: 0.0, y: 0.0 },
296    ///     vertex_b: Point2D { x: 1.0, y: 0.0 },
297    ///     vertex_c: Point2D { x: 0.0, y: 1.0 },
298    /// };
299    /// let barycentric_coords = triangle.barycentric_coordinates(&Point2D { x: 0.5, y: 0.5 });
300    /// ```
301    pub fn barycentric_coordinates(&self, p: &Point2D) -> [f64; 3] {
302        // 计算三个子三角形的面积
303        let area_triangle = self.area();
304        let area_sub_triangle_a = Triangle::new(p.clone(), self.vertex_b.clone(), self.vertex_c.clone()).area();
305        let area_sub_triangle_b = Triangle::new(self.vertex_a.clone(), p.clone(), self.vertex_c.clone()).area();
306        let area_sub_triangle_c = Triangle::new(self.vertex_a.clone(), self.vertex_b.clone(), p.clone()).area();
307
308        // 计算重心坐标
309        let p1 = area_sub_triangle_a / area_triangle;
310        let p2 = area_sub_triangle_b / area_triangle;
311        let p3 = area_sub_triangle_c / area_triangle;
312
313        [p1, p2, p3]
314    }
315
316    /// 计算垂心。
317    ///
318    /// # 返回值
319    ///
320    /// 返回垂心的坐标。
321    ///
322    /// # 示例
323    ///
324    /// ```
325    /// use rs_math::graphical::point_2d::Point2D;
326    /// use rs_math::graphical::triangle::{Triangle, TriangleType};
327    ///
328    /// let triangle = Triangle {
329    ///     vertex_a: Point2D { x: 0.0, y: 0.0 },
330    ///     vertex_b: Point2D { x: 1.0, y: 0.0 },
331    ///     vertex_c: Point2D { x: 0.0, y: 1.0 },
332    /// };
333    /// let orthocenter = triangle.orthocenter();
334    /// ```
335    pub fn orthocenter(&self) -> Point2D {
336        let x_h = self.vertex_a.x;
337        let y_h = self.vertex_b.y;
338        Point2D { x: x_h, y: y_h }
339    }
340
341    /// 计算重心。
342    ///
343    /// # 返回值
344    ///
345    /// 返回重心的坐标。
346    ///
347    /// # 示例
348    ///
349    /// ```
350    /// use rs_math::graphical::point_2d::Point2D;
351    /// use rs_math::graphical::triangle::{Triangle, TriangleType};
352    ///
353    /// let triangle = Triangle {
354    ///     vertex_a: Point2D { x: 0.0, y: 0.0 },
355    ///     vertex_b: Point2D { x: 1.0, y: 0.0 },
356    ///     vertex_c: Point2D { x: 0.0, y: 1.0 },
357    /// };
358    /// let centroid = triangle.centroid();
359    /// ```
360    pub fn centroid(&self) -> Point2D {
361        let x_g = (self.vertex_a.x + self.vertex_b.x + self.vertex_c.x) / 3.0;
362        let y_g = (self.vertex_a.y + self.vertex_b.y + self.vertex_c.y) / 3.0;
363        Point2D { x: x_g, y: y_g }
364    }
365
366    /// 计算内心。
367    ///
368    /// # 返回值
369    ///
370    /// 返回内心的坐标。
371    ///
372    /// # 示例
373    ///
374    /// ```
375    /// use rs_math::graphical::point_2d::Point2D;
376    /// use rs_math::graphical::triangle::{Triangle, TriangleType};
377    ///
378    /// let triangle = Triangle {
379    ///     vertex_a: Point2D { x: 0.0, y: 0.0 },
380    ///     vertex_b: Point2D { x: 1.0, y: 0.0 },
381    ///     vertex_c: Point2D { x: 0.0, y: 1.0 },
382    /// };
383    /// let incenter = triangle.incenter();
384    /// ```
385    pub fn incenter(&self) -> Point2D {
386        let a = self.vertex_b.distance_to(&self.vertex_c);
387        let b = self.vertex_c.distance_to(&self.vertex_a);
388        let c = self.vertex_a.distance_to(&self.vertex_b);
389
390        let x_i = (a * self.vertex_a.x + b * self.vertex_b.x + c * self.vertex_c.x) / (a + b + c);
391        let y_i = (a * self.vertex_a.y + b * self.vertex_b.y + c * self.vertex_c.y) / (a + b + c);
392
393        Point2D { x: x_i, y: y_i }
394    }
395
396    /// 计算外心。
397    ///
398    /// # 返回值
399    ///
400    /// 返回外心的坐标。
401    ///
402    /// # 示例
403    ///
404    /// ```
405    /// use rs_math::graphical::point_2d::Point2D;
406    /// use rs_math::graphical::triangle::{Triangle, TriangleType};
407    ///
408    /// let triangle = Triangle {
409    ///     vertex_a: Point2D { x: 0.0, y: 0.0 },
410    ///     vertex_b: Point2D { x: 1.0, y: 0.0 },
411    ///     vertex_c: Point2D { x: 0.0, y: 1.0 },
412    /// };
413    /// let circumcenter = triangle.circumcenter();
414    /// ```
415    pub fn circumcenter(&self) -> Point2D {
416        let m_ab = Point2D {
417            x: (self.vertex_a.x + self.vertex_b.x) / 2.0,
418            y: (self.vertex_a.y + self.vertex_b.y) / 2.0,
419        };
420
421        let m_bc = Point2D {
422            x: (self.vertex_b.x + self.vertex_c.x) / 2.0,
423            y: (self.vertex_b.y + self.vertex_c.y) / 2.0,
424        };
425
426        let m_ca = Point2D {
427            x: (self.vertex_c.x + self.vertex_a.x) / 2.0,
428            y: (self.vertex_c.y + self.vertex_a.y) / 2.0,
429        };
430
431        let m_ab_slope = -(self.vertex_b.x - self.vertex_a.x) / (self.vertex_b.y - self.vertex_a.y);
432        let m_bc_slope = -(self.vertex_c.x - self.vertex_b.x) / (self.vertex_c.y - self.vertex_b.y);
433        let m_ca_slope = -(self.vertex_a.x - self.vertex_c.x) / (self.vertex_a.y - self.vertex_c.y);
434
435        let x_o = (m_ab.y - m_ab_slope * m_ab.x + m_bc.y - m_bc_slope * m_bc.x + m_ca.y - m_ca_slope * m_ca.x)
436            / (m_ab_slope + m_bc_slope + m_ca_slope);
437
438        let y_o = m_ab_slope * (x_o - m_ab.x) + m_ab.y;
439
440        Point2D { x: x_o, y: y_o }
441    }
442
443    // 计算三角形的类型
444    pub fn classify_triangle(&self) -> TriangleType {
445        let (angle_a, angle_b, angle_c) = self.classify_angles();
446
447        if angle_a < 90.0 && angle_b < 90.0 && angle_c < 90.0 {
448            TriangleType::Acute
449        } else if angle_a == 90.0 || angle_b == 90.0 || angle_c == 90.0 {
450            TriangleType::Right
451        } else {
452            TriangleType::Obtuse
453        }
454    }
455    /// 计算三角形的类型。
456    ///
457    /// 三角形的类型可以分为三种:锐角三角形、直角三角形和钝角三角形。
458    ///
459    /// # 返回值
460    ///
461    /// 返回三角形的类型。
462    ///
463    /// # 示例
464    ///
465    /// ```rust
466    /// use rs_math::graphical::triangle::{Triangle, TriangleType};
467    /// use rs_math::graphical::point_2d::{ Point2D};
468    ///
469    /// let triangle = Triangle {
470    ///     vertex_a: Point2D { x: 0.0, y: 0.0 },
471    ///     vertex_b: Point2D { x: 1.0, y: 0.0 },
472    ///     vertex_c: Point2D { x: 0.0, y: 1.0 },
473    /// };
474    /// let triangle_type = triangle.classify_triangle();
475    /// ```
476    pub fn classify_angles(&self) -> (f64, f64, f64) {
477        let angle_a = self.angle_between(&self.vertex_b, &self.vertex_c, &self.vertex_a);
478        let angle_b = self.angle_between(&self.vertex_a, &self.vertex_c, &self.vertex_b);
479        let angle_c = self.angle_between(&self.vertex_a, &self.vertex_b, &self.vertex_c);
480
481        (angle_a, angle_b, angle_c)
482    }
483
484    /// 计算两条边之间的角度。
485    ///
486    /// 该函数使用余弦定理计算两条边之间的角度。
487    ///
488    /// # 参数
489    ///
490    /// * `p1`:三角形的第一个顶点。
491    /// * `p2`:三角形的第二个顶点。
492    /// * `p3`:三角形的第三个顶点。
493    ///
494    /// # 返回值
495    ///
496    /// 返回两条边之间的角度,单位为度。
497    ///
498    /// # 示例
499    ///
500    /// ```rust
501    /// use rs_math::graphical::point_2d::Point2D;
502    /// use rs_math::graphical::triangle::{Triangle, TriangleType};
503    ///
504    /// let triangle = Triangle {
505    ///     vertex_a: Point2D { x: 0.0, y: 0.0 },
506    ///     vertex_b: Point2D { x: 1.0, y: 0.0 },
507    ///     vertex_c: Point2D { x: 0.0, y: 1.0 },
508    /// };
509    ///
510    /// let angle = triangle.angle_between(&triangle.vertex_a, &triangle.vertex_b, &triangle.vertex_c);
511    /// ```
512    fn angle_between(&self, p1: &Point2D, p2: &Point2D, p3: &Point2D) -> f64 {
513        let a = p1.distance_to(p2);
514        let b = p2.distance_to(p3);
515        let c = p3.distance_to(p1);
516
517        let cos_angle = (a.powi(2) + b.powi(2) - c.powi(2)) / (2.0 * a * b);
518        let angle_rad = cos_angle.acos();
519
520        // 将弧度转换为角度
521        let angle_deg = angle_rad.to_degrees();
522
523        angle_deg
524    }
525}
526
527/// 旋转单个点。
528///
529/// # 参数
530///
531/// - `point`: 要旋转的点坐标。
532/// - `center`: 旋转中心点坐标。
533/// - `matrix`: 2x2 旋转矩阵。
534///
535/// # 返回值
536///
537/// 返回一个新的 `Point2D` 结构体,表示旋转后的点坐标。
538///
539/// # 示例
540///
541/// ```
542/// use rs_math::graphical::point_2d::Point2D;
543/// use rs_math::graphical::triangle::Triangle;
544///
545/// let point = Point2D { x: 1.0, y: 0.0 };
546/// let center = Point2D { x: 0.0, y: 0.0 };
547/// let matrix = [
548///     [0.0, -1.0],
549///     [1.0, 0.0],
550/// ];
551/// let rotated_point = Triangle::rotate_point(&point, &center, matrix);
552/// ```
553fn rotate_point( point: &Point2D, center: &Point2D, matrix: [[f64; 2]; 2]) -> Point2D {
554    let translated_x = point.x - center.x;
555    let translated_y = point.y - center.y;
556
557    let rotated_x = matrix[0][0] * translated_x + matrix[0][1] * translated_y;
558    let rotated_y = matrix[1][0] * translated_x + matrix[1][1] * translated_y;
559
560    Point2D {
561        x: rotated_x + center.x,
562        y: rotated_y + center.y,
563    }
564}