Skip to main content

geo_aid_figure/
lib.rs

1#![warn(
2    clippy::pedantic,
3    missing_docs,
4    missing_copy_implementations,
5    missing_debug_implementations
6)]
7
8//! This crate contains type definitions for Geo-AID's JSON format.
9
10use crate::math_string::MathString;
11use num_rational::Rational64;
12use serde::{Deserialize, Serialize};
13use std::fmt::{Display, Formatter};
14use std::num::NonZeroI64;
15use std::ops::{Add, Deref, DerefMut, Mul};
16
17/// Math strings are Geo-AID's way of handling text involving math-specific notation.
18pub mod math_string;
19
20/// Index of an expression.
21/// Isn't `Copy` for easier differentiation between moving and cloning the value.
22#[allow(missing_copy_implementations)]
23#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)]
24#[serde(transparent)]
25pub struct VarIndex(pub usize);
26
27impl Display for VarIndex {
28    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
29        write!(f, "#{}", self.0)
30    }
31}
32
33impl Deref for VarIndex {
34    type Target = usize;
35
36    fn deref(&self) -> &Self::Target {
37        &self.0
38    }
39}
40
41impl DerefMut for VarIndex {
42    fn deref_mut(&mut self) -> &mut Self::Target {
43        &mut self.0
44    }
45}
46
47/// Index of an expression or an entity
48#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)]
49#[serde(transparent)]
50pub struct EntityIndex(pub usize);
51
52impl Deref for EntityIndex {
53    type Target = usize;
54
55    fn deref(&self) -> &Self::Target {
56        &self.0
57    }
58}
59
60impl DerefMut for EntityIndex {
61    fn deref_mut(&mut self) -> &mut Self::Target {
62        &mut self.0
63    }
64}
65
66/// A complex number real + i*imaginary
67#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
68pub struct Complex {
69    /// The real component
70    #[serde(default)]
71    pub real: f64,
72    /// The imaginary component
73    #[serde(default)]
74    pub imaginary: f64,
75}
76
77/// A rational number
78#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
79pub struct Ratio {
80    /// The nominator of the ratio
81    pub num: i64,
82    /// The denominator of the ratio
83    #[serde(default = "one_i64")]
84    pub denom: NonZeroI64,
85}
86
87impl From<Rational64> for Ratio {
88    fn from(value: Rational64) -> Self {
89        Self {
90            num: *value.numer(),
91            denom: (*value.denom()).try_into().unwrap(),
92        }
93    }
94}
95
96fn one_i64() -> NonZeroI64 {
97    NonZeroI64::new(1).unwrap()
98}
99
100impl Default for Ratio {
101    fn default() -> Self {
102        Self {
103            num: 0,
104            denom: one_i64(),
105        }
106    }
107}
108
109/// A line
110#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
111pub struct Line {
112    /// The origin point of the line
113    pub origin: Complex,
114    /// The direction vector of the line
115    pub direction: Complex,
116}
117
118/// A circle
119#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
120pub struct Circle {
121    /// The center of the circle
122    pub center: Complex,
123    /// The radius of the circle. Must be positive
124    pub radius: f64,
125}
126
127/// A value of an expression or an entity
128#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
129#[serde(tag = "type", rename_all = "kebab-case")]
130pub enum Value {
131    /// A complex number
132    Complex(Complex),
133    /// A line
134    Line(Line),
135    /// A circle
136    Circle(Circle),
137}
138
139/// Defines how a line should be drawn
140#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
141#[serde(rename_all = "kebab-case")]
142pub enum Style {
143    /// A standard, solid line
144    #[default]
145    Solid,
146    /// A line made with dots
147    Dotted,
148    /// A line made with dashes (`-`)
149    Dashed,
150    /// A slightly thicker line
151    Bold,
152}
153
154/// Label-related information
155#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct Label {
157    /// Where the label should be drawn (figure space)
158    pub position: Position,
159    /// The label contents
160    pub content: MathString,
161}
162
163/// A figure-space position
164#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
165pub struct Position {
166    /// X coordinate
167    pub x: f64,
168    /// Y coordinate
169    pub y: f64,
170}
171
172impl Mul<f64> for Position {
173    type Output = Self;
174
175    fn mul(self, rhs: f64) -> Self::Output {
176        Self {
177            x: self.x * rhs,
178            y: self.y * rhs,
179        }
180    }
181}
182
183impl Add for Position {
184    type Output = Self;
185
186    fn add(self, rhs: Self) -> Self::Output {
187        Self {
188            x: self.x + rhs.x,
189            y: self.y + rhs.y,
190        }
191    }
192}
193
194/// A figure generated by Geo-AID
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct Figure {
197    /// The width of the image
198    pub width: f64,
199    /// The height of the image
200    pub height: f64,
201    /// Expressions used by the image
202    pub expressions: Vec<Expression>,
203    /// Entities in the image
204    pub entities: Vec<Entity>,
205    /// Items drawn on the image
206    pub items: Vec<Item>,
207}
208
209/// A single expression
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct Expression {
212    /// The calculated value of this expression
213    pub hint: Value,
214    /// The kind of an expression this is
215    pub kind: ExpressionKind,
216}
217
218/// The kind of an expression
219#[derive(Debug, Clone, Serialize, Deserialize)]
220#[serde(tag = "type", rename_all = "kebab-case")]
221pub enum ExpressionKind {
222    /// An entity
223    Entity {
224        /// The index in the `entities` vector
225        id: EntityIndex,
226    },
227    /// Intersection of k and l
228    LineLineIntersection {
229        /// Line 1
230        k: VarIndex,
231        /// Line 2
232        l: VarIndex,
233    },
234    /// The arithmetic average of points as complex numbers
235    AveragePoint {
236        /// The elements of the average
237        items: Vec<VarIndex>,
238    },
239    /// The center of a circle
240    CircleCenter {
241        /// Circle to query
242        circle: VarIndex,
243    },
244    /// Convert a complex number to a point
245    ComplexToPoint {
246        /// The number to convert
247        number: VarIndex,
248    },
249    /// Summation of numbers
250    Sum {
251        /// All the added ones
252        plus: Vec<VarIndex>,
253        /// All the subtracted ones
254        minus: Vec<VarIndex>,
255    },
256    /// Product of numbers
257    Product {
258        /// Multiply by them
259        times: Vec<VarIndex>,
260        /// Divide by them
261        by: Vec<VarIndex>,
262    },
263    /// A constant number value
264    Const {
265        /// The value
266        value: Complex,
267    },
268    /// Raising a value to a rational power
269    Power {
270        /// The base
271        value: VarIndex,
272        /// The exponent
273        exponent: Ratio,
274    },
275    /// Distance between `p` and `q`
276    PointPointDistance {
277        /// Point 1
278        p: VarIndex,
279        /// Point 2
280        q: VarIndex,
281    },
282    /// Distance between `point` and `line`
283    PointLineDistance {
284        /// The point
285        point: VarIndex,
286        /// The line
287        line: VarIndex,
288    },
289    /// Angle `abc`
290    ThreePointAngle {
291        /// Arm 1
292        a: VarIndex,
293        /// Vertex
294        b: VarIndex,
295        /// Arm 2
296        c: VarIndex,
297    },
298    /// Directed angle `abc`
299    ThreePointAngleDir {
300        /// Arm 1
301        a: VarIndex,
302        /// Vertex
303        b: VarIndex,
304        /// Arm 2
305        c: VarIndex,
306    },
307    /// Angle between `k` and `l`
308    TwoLineAngle {
309        /// Line 1
310        k: VarIndex,
311        /// Line 2
312        l: VarIndex,
313    },
314    /// X coordinate of a point
315    PointX {
316        /// The point
317        point: VarIndex,
318    },
319    /// Y coordinate of a point
320    PointY {
321        /// The point
322        point: VarIndex,
323    },
324    /// Convert a point to a complex number
325    PointToComplex {
326        /// The point to convert.
327        point: VarIndex,
328    },
329    /// Real part of a number
330    Real {
331        /// The number to query.
332        number: VarIndex,
333    },
334    /// Imaginary part of a number
335    Imaginary {
336        /// The number to query.
337        number: VarIndex,
338    },
339    /// Natural logarithm (base e)
340    Log {
341        /// The number to take the logarithm of
342        number: VarIndex,
343    },
344    /// Exponential function (e^this)
345    Exp {
346        /// The exponent
347        number: VarIndex,
348    },
349    /// Sine of an angle
350    Sin {
351        /// The angle to take sine of.
352        angle: VarIndex,
353    },
354    /// Cosine of an angle
355    Cos {
356        /// The angle to take cosine of
357        angle: VarIndex,
358    },
359    /// Arcsine function
360    Asin {
361        /// The value to take arcsine of.
362        value: VarIndex,
363    },
364    /// Arccosine function
365    Acos {
366        /// The value to take arccosine of
367        value: VarIndex,
368    },
369    /// Arctan function
370    Atan {
371        /// The value to take arctan of
372        value: VarIndex,
373    },
374    /// Arctan2 function
375    Atan2 {
376        /// Y value
377        y: VarIndex,
378        /// X value
379        x: VarIndex,
380    },
381    /// Direction vector of a line
382    DirectionVector {
383        /// Line to query
384        line: VarIndex,
385    },
386    /// Line `pq`
387    PointPointLine {
388        /// Point 1
389        p: VarIndex,
390        /// Point 2
391        q: VarIndex,
392    },
393    /// Line from point and direction vector
394    PointVectorLine {
395        /// Point
396        point: VarIndex,
397        /// Vector
398        vector: VarIndex,
399    },
400    /// Bisector of angle `abc`
401    AngleBisector {
402        /// Arm 1
403        p: VarIndex,
404        /// Vertex
405        q: VarIndex,
406        /// Arm 2
407        r: VarIndex,
408    },
409    /// Perpendicular line going through `point`
410    PerpendicularThrough {
411        /// The guiding point
412        point: VarIndex,
413        /// The reference line
414        line: VarIndex,
415    },
416    /// Parallel line going through `point`
417    ParallelThrough {
418        /// The guiding point
419        point: VarIndex,
420        /// The reference line
421        line: VarIndex,
422    },
423    /// A circle with center and radius
424    ConstructCircle {
425        /// The circle's center
426        center: VarIndex,
427        /// The circle's radius. Must be positive
428        radius: VarIndex,
429    },
430}
431
432/// A single entity
433#[derive(Debug, Clone, Serialize, Deserialize)]
434pub struct Entity {
435    /// The calculated value of this expression
436    pub hint: Value,
437    /// The kind of an entity this is
438    pub kind: EntityKind,
439}
440
441/// The kind of an entity
442#[derive(Debug, Clone, Serialize, Deserialize)]
443#[serde(tag = "type", rename_all = "kebab-case")]
444pub enum EntityKind {
445    /// A free point
446    FreePoint,
447    /// Point on a line
448    PointOnLine {
449        /// The reference line
450        line: VarIndex,
451    },
452    /// Point on a circle
453    PointOnCircle {
454        /// The reference circle
455        circle: VarIndex,
456    },
457    /// A free real
458    FreeReal,
459    /// A distance unit
460    DistanceUnit,
461}
462
463/// An item drawn on the image
464#[derive(Debug, Clone, Serialize, Deserialize)]
465#[serde(tag = "type", rename_all = "kebab-case")]
466pub enum Item {
467    /// A point
468    Point(PointItem),
469    /// A line
470    Line(LineItem),
471    /// A ray (half-line)
472    #[doc(alias = "HalfLine")]
473    Ray(TwoPointItem),
474    /// A segment
475    Segment(TwoPointItem),
476    /// A circle
477    Circle(CircleItem),
478}
479
480impl Item {
481    /// If it's a point, returns a mutable reference to it
482    #[must_use]
483    pub fn as_point_mut(&mut self) -> Option<&mut PointItem> {
484        match self {
485            Self::Point(p) => Some(p),
486            _ => None,
487        }
488    }
489}
490
491/// A point item. Usually depicted by a dot.
492#[derive(Debug, Clone, Serialize, Deserialize)]
493pub struct PointItem {
494    /// The point's position on the image
495    pub position: Position,
496    /// The defining expression index
497    pub id: VarIndex,
498    /// Whether to display the dot (circle)
499    #[serde(default)]
500    pub display_dot: bool,
501    /// The point's label
502    #[serde(default, skip_serializing_if = "Option::is_none")]
503    pub label: Option<Label>,
504}
505
506/// A line item. Usually depicted by a line.
507#[derive(Debug, Clone, Serialize, Deserialize)]
508pub struct LineItem {
509    /// Delimiting points of the drawn line segment
510    pub points: (Position, Position),
511    /// The defining expression index
512    pub id: VarIndex,
513    /// How the line should be drawn
514    #[serde(default)]
515    pub style: Style,
516    /// The line's label
517    #[serde(default, skip_serializing_if = "Option::is_none")]
518    pub label: Option<Label>,
519}
520
521/// A segment or a ray. Usually depicted by a line.
522#[derive(Debug, Clone, Serialize, Deserialize)]
523pub struct TwoPointItem {
524    /// Delimiting points of the drawn line segment
525    pub points: (Position, Position),
526    /// The first point's expression index (origin if ray)
527    pub p_id: VarIndex,
528    /// The second point's expression index
529    pub q_id: VarIndex,
530    /// How the line should be drawn
531    #[serde(default)]
532    pub style: Style,
533    /// The item's label
534    #[serde(default, skip_serializing_if = "Option::is_none")]
535    pub label: Option<Label>,
536}
537
538/// A circle item. Usually depicted by a circle.
539#[derive(Debug, Clone, Serialize, Deserialize)]
540pub struct CircleItem {
541    /// The center of the drawn circle
542    pub center: Position,
543    /// The radius of the drawn circle
544    pub radius: f64,
545    /// The defining expression index
546    pub id: VarIndex,
547    /// How the line should be drawn
548    #[serde(default)]
549    pub style: Style,
550    /// The circle's label
551    #[serde(default, skip_serializing_if = "Option::is_none")]
552    pub label: Option<Label>,
553}