1#![warn(
2 clippy::pedantic,
3 missing_docs,
4 missing_copy_implementations,
5 missing_debug_implementations
6)]
7
8use 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
17pub mod math_string;
19
20#[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#[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#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
68pub struct Complex {
69 #[serde(default)]
71 pub real: f64,
72 #[serde(default)]
74 pub imaginary: f64,
75}
76
77#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
79pub struct Ratio {
80 pub num: i64,
82 #[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#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
111pub struct Line {
112 pub origin: Complex,
114 pub direction: Complex,
116}
117
118#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
120pub struct Circle {
121 pub center: Complex,
123 pub radius: f64,
125}
126
127#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
129#[serde(tag = "type", rename_all = "kebab-case")]
130pub enum Value {
131 Complex(Complex),
133 Line(Line),
135 Circle(Circle),
137}
138
139#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
141#[serde(rename_all = "kebab-case")]
142pub enum Style {
143 #[default]
145 Solid,
146 Dotted,
148 Dashed,
150 Bold,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct Label {
157 pub position: Position,
159 pub content: MathString,
161}
162
163#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
165pub struct Position {
166 pub x: f64,
168 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#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct Figure {
197 pub width: f64,
199 pub height: f64,
201 pub expressions: Vec<Expression>,
203 pub entities: Vec<Entity>,
205 pub items: Vec<Item>,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct Expression {
212 pub hint: Value,
214 pub kind: ExpressionKind,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
220#[serde(tag = "type", rename_all = "kebab-case")]
221pub enum ExpressionKind {
222 Entity {
224 id: EntityIndex,
226 },
227 LineLineIntersection {
229 k: VarIndex,
231 l: VarIndex,
233 },
234 AveragePoint {
236 items: Vec<VarIndex>,
238 },
239 CircleCenter {
241 circle: VarIndex,
243 },
244 ComplexToPoint {
246 number: VarIndex,
248 },
249 Sum {
251 plus: Vec<VarIndex>,
253 minus: Vec<VarIndex>,
255 },
256 Product {
258 times: Vec<VarIndex>,
260 by: Vec<VarIndex>,
262 },
263 Const {
265 value: Complex,
267 },
268 Power {
270 value: VarIndex,
272 exponent: Ratio,
274 },
275 PointPointDistance {
277 p: VarIndex,
279 q: VarIndex,
281 },
282 PointLineDistance {
284 point: VarIndex,
286 line: VarIndex,
288 },
289 ThreePointAngle {
291 a: VarIndex,
293 b: VarIndex,
295 c: VarIndex,
297 },
298 ThreePointAngleDir {
300 a: VarIndex,
302 b: VarIndex,
304 c: VarIndex,
306 },
307 TwoLineAngle {
309 k: VarIndex,
311 l: VarIndex,
313 },
314 PointX {
316 point: VarIndex,
318 },
319 PointY {
321 point: VarIndex,
323 },
324 PointToComplex {
326 point: VarIndex,
328 },
329 Real {
331 number: VarIndex,
333 },
334 Imaginary {
336 number: VarIndex,
338 },
339 Log {
341 number: VarIndex,
343 },
344 Exp {
346 number: VarIndex,
348 },
349 Sin {
351 angle: VarIndex,
353 },
354 Cos {
356 angle: VarIndex,
358 },
359 Asin {
361 value: VarIndex,
363 },
364 Acos {
366 value: VarIndex,
368 },
369 Atan {
371 value: VarIndex,
373 },
374 Atan2 {
376 y: VarIndex,
378 x: VarIndex,
380 },
381 DirectionVector {
383 line: VarIndex,
385 },
386 PointPointLine {
388 p: VarIndex,
390 q: VarIndex,
392 },
393 PointVectorLine {
395 point: VarIndex,
397 vector: VarIndex,
399 },
400 AngleBisector {
402 p: VarIndex,
404 q: VarIndex,
406 r: VarIndex,
408 },
409 PerpendicularThrough {
411 point: VarIndex,
413 line: VarIndex,
415 },
416 ParallelThrough {
418 point: VarIndex,
420 line: VarIndex,
422 },
423 ConstructCircle {
425 center: VarIndex,
427 radius: VarIndex,
429 },
430}
431
432#[derive(Debug, Clone, Serialize, Deserialize)]
434pub struct Entity {
435 pub hint: Value,
437 pub kind: EntityKind,
439}
440
441#[derive(Debug, Clone, Serialize, Deserialize)]
443#[serde(tag = "type", rename_all = "kebab-case")]
444pub enum EntityKind {
445 FreePoint,
447 PointOnLine {
449 line: VarIndex,
451 },
452 PointOnCircle {
454 circle: VarIndex,
456 },
457 FreeReal,
459 DistanceUnit,
461}
462
463#[derive(Debug, Clone, Serialize, Deserialize)]
465#[serde(tag = "type", rename_all = "kebab-case")]
466pub enum Item {
467 Point(PointItem),
469 Line(LineItem),
471 #[doc(alias = "HalfLine")]
473 Ray(TwoPointItem),
474 Segment(TwoPointItem),
476 Circle(CircleItem),
478}
479
480impl Item {
481 #[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#[derive(Debug, Clone, Serialize, Deserialize)]
493pub struct PointItem {
494 pub position: Position,
496 pub id: VarIndex,
498 #[serde(default)]
500 pub display_dot: bool,
501 #[serde(default, skip_serializing_if = "Option::is_none")]
503 pub label: Option<Label>,
504}
505
506#[derive(Debug, Clone, Serialize, Deserialize)]
508pub struct LineItem {
509 pub points: (Position, Position),
511 pub id: VarIndex,
513 #[serde(default)]
515 pub style: Style,
516 #[serde(default, skip_serializing_if = "Option::is_none")]
518 pub label: Option<Label>,
519}
520
521#[derive(Debug, Clone, Serialize, Deserialize)]
523pub struct TwoPointItem {
524 pub points: (Position, Position),
526 pub p_id: VarIndex,
528 pub q_id: VarIndex,
530 #[serde(default)]
532 pub style: Style,
533 #[serde(default, skip_serializing_if = "Option::is_none")]
535 pub label: Option<Label>,
536}
537
538#[derive(Debug, Clone, Serialize, Deserialize)]
540pub struct CircleItem {
541 pub center: Position,
543 pub radius: f64,
545 pub id: VarIndex,
547 #[serde(default)]
549 pub style: Style,
550 #[serde(default, skip_serializing_if = "Option::is_none")]
552 pub label: Option<Label>,
553}