Rust · 5000 bytes Raw Blame History
1 //! garcalc-graph: 2D/3D graphing engine
2 //!
3 //! Provides function plotting, parametric curves, implicit curves,
4 //! and 3D surface visualization.
5
6 pub mod plot2d;
7 pub mod plot3d;
8
9 pub use plot2d::{CURVE_COLORS, Graph2D, PlotConfig, PlottedFunction, ViewportPreset};
10 pub use plot3d::{Camera3D, CameraPreset, Colormap, Graph3D, Plot3DConfig, RenderMode, Surface3D, Viewport3D};
11
12 use garcalc_cas::Expr;
13 use serde::{Deserialize, Serialize};
14
15 /// Color for graph elements
16 #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
17 pub struct Color {
18 pub r: u8,
19 pub g: u8,
20 pub b: u8,
21 pub a: u8,
22 }
23
24 impl Color {
25 pub const RED: Self = Self {
26 r: 255,
27 g: 0,
28 b: 0,
29 a: 255,
30 };
31 pub const BLUE: Self = Self {
32 r: 0,
33 g: 0,
34 b: 255,
35 a: 255,
36 };
37 pub const GREEN: Self = Self {
38 r: 0,
39 g: 128,
40 b: 0,
41 a: 255,
42 };
43 pub const BLACK: Self = Self {
44 r: 0,
45 g: 0,
46 b: 0,
47 a: 255,
48 };
49 }
50
51 /// Line style for curves
52 #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
53 pub enum LineStyle {
54 Solid,
55 Dashed,
56 Dotted,
57 }
58
59 /// A single piece of a piecewise function
60 #[derive(Debug, Clone, Serialize, Deserialize)]
61 pub struct PiecewisePiece {
62 pub expr: Expr,
63 pub condition: PiecewiseCondition,
64 }
65
66 /// Condition for a piecewise segment
67 #[derive(Debug, Clone, Serialize, Deserialize)]
68 pub enum PiecewiseCondition {
69 LessThan(f64),
70 LessEqual(f64),
71 GreaterThan(f64),
72 GreaterEqual(f64),
73 Between(f64, f64),
74 BetweenInclusive(f64, f64),
75 Always,
76 }
77
78 impl PiecewiseCondition {
79 pub fn matches(&self, x: f64) -> bool {
80 match self {
81 Self::LessThan(v) => x < *v,
82 Self::LessEqual(v) => x <= *v,
83 Self::GreaterThan(v) => x > *v,
84 Self::GreaterEqual(v) => x >= *v,
85 Self::Between(a, b) => x > *a && x < *b,
86 Self::BetweenInclusive(a, b) => x >= *a && x <= *b,
87 Self::Always => true,
88 }
89 }
90 }
91
92 /// A plottable function or relation
93 #[derive(Debug, Clone, Serialize, Deserialize)]
94 pub enum Plottable {
95 /// y = f(x)
96 Explicit2D {
97 expr: Expr,
98 x_var: String,
99 color: Color,
100 style: LineStyle,
101 },
102 /// F(x, y) = 0
103 Implicit2D {
104 expr: Expr,
105 x_var: String,
106 y_var: String,
107 color: Color,
108 },
109 /// (x(t), y(t))
110 Parametric2D {
111 x_expr: Expr,
112 y_expr: Expr,
113 t_var: String,
114 t_range: (f64, f64),
115 color: Color,
116 },
117 /// r = f(theta)
118 Polar2D {
119 expr: Expr,
120 theta_var: String,
121 theta_range: (f64, f64),
122 color: Color,
123 style: LineStyle,
124 },
125 /// Piecewise-defined function
126 Piecewise2D {
127 pieces: Vec<PiecewisePiece>,
128 x_var: String,
129 color: Color,
130 style: LineStyle,
131 },
132 /// z = f(x, y)
133 Explicit3D {
134 expr: Expr,
135 x_var: String,
136 y_var: String,
137 },
138 }
139
140 /// Extended color palette (6 base catppuccin + 6 additional)
141 pub const COLOR_PALETTE: [Color; 12] = [
142 // Original 6 from CURVE_COLORS
143 Color { r: 137, g: 180, b: 250, a: 255 }, // blue
144 Color { r: 166, g: 227, b: 161, a: 255 }, // green
145 Color { r: 249, g: 226, b: 175, a: 255 }, // yellow
146 Color { r: 243, g: 139, b: 168, a: 255 }, // red
147 Color { r: 203, g: 166, b: 247, a: 255 }, // mauve
148 Color { r: 148, g: 226, b: 213, a: 255 }, // teal
149 // Additional 6
150 Color { r: 245, g: 224, b: 220, a: 255 }, // rosewater
151 Color { r: 242, g: 205, b: 205, a: 255 }, // flamingo
152 Color { r: 245, g: 194, b: 231, a: 255 }, // pink
153 Color { r: 250, g: 179, b: 135, a: 255 }, // peach
154 Color { r: 180, g: 190, b: 254, a: 255 }, // lavender
155 Color { r: 137, g: 220, b: 235, a: 255 }, // sky
156 ];
157
158 impl Plottable {
159 pub fn color(&self) -> Option<Color> {
160 match self {
161 Self::Explicit2D { color, .. }
162 | Self::Implicit2D { color, .. }
163 | Self::Parametric2D { color, .. }
164 | Self::Polar2D { color, .. }
165 | Self::Piecewise2D { color, .. } => Some(*color),
166 Self::Explicit3D { .. } => None,
167 }
168 }
169
170 pub fn set_color(&mut self, new_color: Color) {
171 match self {
172 Self::Explicit2D { color, .. }
173 | Self::Implicit2D { color, .. }
174 | Self::Parametric2D { color, .. }
175 | Self::Polar2D { color, .. }
176 | Self::Piecewise2D { color, .. } => *color = new_color,
177 Self::Explicit3D { .. } => {}
178 }
179 }
180 }
181
182 /// 2D viewport bounds
183 #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
184 pub struct Viewport2D {
185 pub x_min: f64,
186 pub x_max: f64,
187 pub y_min: f64,
188 pub y_max: f64,
189 }
190
191 impl Default for Viewport2D {
192 fn default() -> Self {
193 Self {
194 x_min: -10.0,
195 x_max: 10.0,
196 y_min: -10.0,
197 y_max: 10.0,
198 }
199 }
200 }
201