geo_aid_script/unroll/library/
triangle.rs

1//! Triangle-related functions
2
3use num_traits::FromPrimitive;
4
5use crate::{
6    token::{number::ProcNum, Span},
7    unroll::{figure::PCNode, PointCollection, PointCollectionData},
8};
9
10use super::{bisector, prelude::*};
11
12fn triangle(context: &CompileContext, mut props: Properties) -> Pc<3> {
13    let points = (0..3).map(|_| context.free_point()).collect::<Vec<_>>();
14
15    let mut expr = Expr {
16        data: Rc::new(PointCollection {
17            length: 3,
18            data: PointCollectionData::PointCollection(points.into()),
19        }),
20        span: Span::empty(),
21        node: None,
22    };
23
24    let node = PCNode {
25        display: props.get("display").maybe_unset(true),
26        children: (0..3).map(|_| None).collect(),
27        props: None,
28        expr: expr.clone_without_node(),
29    };
30    let mut node = HierarchyNode::new(node);
31    node.set_associated(super::polygon::Associated);
32    node.insert_data(
33        "display_segments",
34        props.get("displaysegments").maybe_unset(true),
35    );
36    node.insert_data("style", props.get("style").maybe_unset(Style::Solid));
37    node.root.props = Some(props);
38    expr.node = Some(node);
39
40    expr.into()
41}
42
43fn main_triangle(context: &mut CompileContext, props: Properties) -> Pc<3> {
44    let pc = triangle(context, props);
45
46    let a_y = context.point_y(pc.index_without_node(0));
47    let a_x = context.point_x(pc.index_without_node(0));
48    let b_y = context.point_y(pc.index_without_node(1));
49    let b_x = context.point_x(pc.index_without_node(1));
50    let c_y = context.point_y(pc.index_without_node(2));
51    context.scalar_eq(a_y, b_y.clone_without_node(), false);
52    context.gt(c_y, b_y, false);
53    context.gt(b_x, a_x, false);
54
55    pc
56}
57
58fn isosceles_triangle(context: &mut CompileContext, props: Properties) -> Pc<3> {
59    let pc = triangle(context, props);
60
61    let ac = context.distance_pp(pc.index_without_node(0), pc.index_without_node(2));
62    let bc = context.distance_pp(pc.index_without_node(1), pc.index_without_node(2));
63    context.scalar_eq(ac, bc, false);
64
65    pc
66}
67
68fn main_isosceles_triangle(context: &mut CompileContext, props: Properties) -> Pc<3> {
69    let pc = isosceles_triangle(context, props);
70
71    let a_y = context.point_y(pc.index_without_node(0));
72    let a_x = context.point_x(pc.index_without_node(0));
73    let b_y = context.point_y(pc.index_without_node(1));
74    let b_x = context.point_x(pc.index_without_node(1));
75    let c_y = context.point_y(pc.index_without_node(2));
76    context.scalar_eq(a_y, b_y.clone_without_node(), false);
77    context.gt(c_y, b_y, false);
78    context.gt(b_x, a_x, false);
79
80    pc
81}
82
83fn equilateral_triangle(context: &mut CompileContext, props: Properties) -> Pc<3> {
84    let pc = triangle(context, props);
85
86    let ac = context.distance_pp(pc.index_without_node(0), pc.index_without_node(2));
87    let bc = context.distance_pp(pc.index_without_node(1), pc.index_without_node(2));
88    let ab = context.distance_pp(pc.index_without_node(0), pc.index_without_node(1));
89    context.scalar_eq(ac.clone_without_node(), bc, false);
90    context.scalar_eq(ac, ab, false);
91
92    pc
93}
94
95fn main_equilateral_triangle(context: &mut CompileContext, props: Properties) -> Pc<3> {
96    let pc = equilateral_triangle(context, props);
97
98    let a_y = context.point_y(pc.index_without_node(0));
99    let a_x = context.point_x(pc.index_without_node(0));
100    let b_y = context.point_y(pc.index_without_node(1));
101    let b_x = context.point_x(pc.index_without_node(1));
102    let c_y = context.point_y(pc.index_without_node(2));
103    context.scalar_eq(a_y, b_y.clone_without_node(), false);
104    context.gt(c_y, b_y, false);
105    context.gt(b_x, a_x, false);
106
107    pc
108}
109
110fn right_triangle(context: &mut CompileContext, props: Properties) -> Pc<3> {
111    let pc = triangle(context, props);
112
113    let acb = context.angle_ppp(
114        pc.index_without_node(0),
115        pc.index_without_node(2),
116        pc.index_without_node(1),
117    );
118    context.scalar_eq(
119        acb,
120        number!(ANGLE ProcNum::pi() / &ProcNum::from_i8(2).unwrap()),
121        false,
122    );
123
124    pc
125}
126
127fn main_right_triangle(context: &mut CompileContext, props: Properties) -> Pc<3> {
128    let pc = right_triangle(context, props);
129
130    let a_y = context.point_y(pc.index_without_node(0));
131    let a_x = context.point_x(pc.index_without_node(0));
132    let b_y = context.point_y(pc.index_without_node(1));
133    let c_y = context.point_y(pc.index_without_node(2));
134    let c_x = context.point_x(pc.index_without_node(2));
135    context.scalar_eq(a_y, c_y.clone_without_node(), false);
136    context.gt(b_y, c_y, false);
137    context.gt(a_x, c_x, false);
138
139    pc
140}
141
142fn orthocenter(
143    mut a: Expr<Point>,
144    mut b: Expr<Point>,
145    mut c: Expr<Point>,
146    context: &CompileContext,
147    props: Properties,
148) -> Expr<Point> {
149    let a_node = a.take_node();
150    let b_node = b.take_node();
151    let c_node = c.take_node();
152    let mut alt1 = context.perpendicular_through(
153        context.line(a.clone_without_node(), b.clone_without_node()),
154        c.clone_without_node(),
155    );
156    let mut alt2 = context.perpendicular_through(context.line(b, c), a);
157
158    // Make sure the altitudes are not displayed.
159    alt1.take_node();
160    alt2.take_node();
161
162    let mut expr = context.intersection_display(alt1, alt2, props);
163
164    if let Some(node) = expr.node.as_mut() {
165        node.extend_children([a_node, b_node, c_node].into_iter().flatten());
166    }
167
168    expr
169}
170
171fn circumcenter(
172    mut a: Expr<Point>,
173    mut b: Expr<Point>,
174    mut c: Expr<Point>,
175    context: &CompileContext,
176    props: Properties,
177) -> Expr<Point> {
178    let a_node = a.take_node();
179    let b_node = b.take_node();
180    let c_node = c.take_node();
181    let mut bis1 = bisector::point_point(
182        a.clone_without_node(),
183        b.clone_without_node(),
184        context,
185        Properties::default(),
186    );
187    let mut bis2 = bisector::point_point(c, b, context, Properties::default());
188
189    // Make sure the altitudes are not displayed.
190    bis1.take_node();
191    bis2.take_node();
192
193    let mut expr = context.intersection_display(bis1, bis2, props);
194
195    if let Some(node) = expr.node.as_mut() {
196        node.extend_children([a_node, b_node, c_node].into_iter().flatten());
197    }
198
199    expr
200}
201
202fn incenter(
203    mut a: Expr<Point>,
204    mut b: Expr<Point>,
205    mut c: Expr<Point>,
206    context: &CompileContext,
207    props: Properties,
208) -> Expr<Point> {
209    let a_node = a.take_node();
210    let b_node = b.take_node();
211    let c_node = c.take_node();
212    let mut bis1 = bisector::point_point_point(
213        a.clone_without_node(),
214        b.clone_without_node(),
215        c.clone_without_node(),
216        context,
217        Properties::default(),
218    );
219    let mut bis2 = bisector::point_point_point(a, c, b, context, Properties::default());
220
221    // Make sure the altitudes are not displayed.
222    bis1.take_node();
223    bis2.take_node();
224
225    let mut expr = context.intersection_display(bis1, bis2, props);
226
227    if let Some(node) = expr.node.as_mut() {
228        node.extend_children([a_node, b_node, c_node].into_iter().flatten());
229    }
230
231    expr
232}
233
234/// Register the functions
235pub fn register(library: &mut Library) {
236    library
237        .add(
238            Function::new("orthocenter")
239                .alias("orthocentre")
240                .alias_method(ty::collection(3), "orthocenter")
241                .alias_method(ty::collection(3), "orthocentre")
242                .overload(orthocenter)
243                .overload(|mut col: Pc<3>, context: &CompileContext, props| {
244                    orthocenter(
245                        col.index_with_node(0),
246                        col.index_with_node(1),
247                        col.index_with_node(2),
248                        context,
249                        props,
250                    )
251                }),
252        )
253        .add(
254            Function::new("circumcenter")
255                .alias("circumcentre")
256                .alias_method(ty::collection(3), "circumcenter")
257                .alias_method(ty::collection(3), "circumcentre")
258                .overload(circumcenter)
259                .overload(|mut col: Pc<3>, context: &CompileContext, props| {
260                    circumcenter(
261                        col.index_with_node(0),
262                        col.index_with_node(1),
263                        col.index_with_node(2),
264                        context,
265                        props,
266                    )
267                }),
268        )
269        .add(
270            Function::new("incenter")
271                .alias("incentre")
272                .alias_method(ty::collection(3), "incenter")
273                .alias_method(ty::collection(3), "incentre")
274                .overload(incenter)
275                .overload(|mut col: Pc<3>, context: &CompileContext, props| {
276                    incenter(
277                        col.index_with_node(0),
278                        col.index_with_node(1),
279                        col.index_with_node(2),
280                        context,
281                        props,
282                    )
283                }),
284        )
285        .add(Function::new("triangle").overload(triangle))
286        .add(Function::new("maintriangle").overload(main_triangle))
287        .add(
288            Function::new("isoscelestriangle")
289                .alias("isosceles")
290                .overload(isosceles_triangle),
291        )
292        .add(
293            Function::new("mainisoscelestriangle")
294                .alias("mainisosceles")
295                .overload(main_isosceles_triangle),
296        )
297        .add(
298            Function::new("equilateraltriangle")
299                .alias("equilateral")
300                .overload(equilateral_triangle),
301        )
302        .add(
303            Function::new("mainequilateraltriangle")
304                .alias("mainequilateral")
305                .overload(main_equilateral_triangle),
306        )
307        .add(
308            Function::new("righttriangle")
309                .alias("right")
310                .overload(right_triangle),
311        )
312        .add(
313            Function::new("mainrighttriangle")
314                .alias("mainright")
315                .overload(main_right_triangle),
316        );
317}