gardesk/garcalc / 68f2023

Browse files

Adjust graph rendering behavior and formatting

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
68f20234b699d8fecde6850cadb3a6221f11af4d
Parents
acc0efc
Tree
fea30ce

4 changed files

StatusFile+-
M garcalc-geometry/src/lib.rs 22 5
M garcalc-graph/src/lib.rs 26 6
M garcalc-graph/src/plot2d.rs 114 22
M garcalc-graph/src/plot3d.rs 64 28
garcalc-geometry/src/lib.rsmodified
@@ -21,11 +21,28 @@ pub type ShapeId = u64;
21
 #[derive(Debug, Clone, Serialize, Deserialize)]
21
 #[derive(Debug, Clone, Serialize, Deserialize)]
22
 pub enum Shape {
22
 pub enum Shape {
23
     Point(Point2D),
23
     Point(Point2D),
24
-    Line { p1: Point2D, p2: Point2D },
24
+    Line {
25
-    Segment { p1: Point2D, p2: Point2D },
25
+        p1: Point2D,
26
-    Ray { origin: Point2D, direction: Point2D },
26
+        p2: Point2D,
27
-    Circle { center: Point2D, radius: f64 },
27
+    },
28
-    Arc { center: Point2D, radius: f64, start_angle: f64, end_angle: f64 },
28
+    Segment {
29
+        p1: Point2D,
30
+        p2: Point2D,
31
+    },
32
+    Ray {
33
+        origin: Point2D,
34
+        direction: Point2D,
35
+    },
36
+    Circle {
37
+        center: Point2D,
38
+        radius: f64,
39
+    },
40
+    Arc {
41
+        center: Point2D,
42
+        radius: f64,
43
+        start_angle: f64,
44
+        end_angle: f64,
45
+    },
29
     Polygon(Vec<Point2D>),
46
     Polygon(Vec<Point2D>),
30
 }
47
 }
31
 
48
 
garcalc-graph/src/lib.rsmodified
@@ -6,8 +6,8 @@
6
 pub mod plot2d;
6
 pub mod plot2d;
7
 pub mod plot3d;
7
 pub mod plot3d;
8
 
8
 
9
-pub use plot2d::{Graph2D, PlotConfig, CURVE_COLORS};
9
+pub use plot2d::{CURVE_COLORS, Graph2D, PlotConfig};
10
-pub use plot3d::{Graph3D, Plot3DConfig, Camera3D, Viewport3D, Colormap, RenderMode};
10
+pub use plot3d::{Camera3D, Colormap, Graph3D, Plot3DConfig, RenderMode, Viewport3D};
11
 
11
 
12
 use garcalc_cas::Expr;
12
 use garcalc_cas::Expr;
13
 use serde::{Deserialize, Serialize};
13
 use serde::{Deserialize, Serialize};
@@ -22,10 +22,30 @@ pub struct Color {
22
 }
22
 }
23
 
23
 
24
 impl Color {
24
 impl Color {
25
-    pub const RED: Self = Self { r: 255, g: 0, b: 0, a: 255 };
25
+    pub const RED: Self = Self {
26
-    pub const BLUE: Self = Self { r: 0, g: 0, b: 255, a: 255 };
26
+        r: 255,
27
-    pub const GREEN: Self = Self { r: 0, g: 128, b: 0, a: 255 };
27
+        g: 0,
28
-    pub const BLACK: Self = Self { r: 0, g: 0, b: 0, a: 255 };
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
+    };
29
 }
49
 }
30
 
50
 
31
 /// Line style for curves
51
 /// Line style for curves
garcalc-graph/src/plot2d.rsmodified
@@ -40,10 +40,30 @@ pub struct PlotConfig {
40
 impl Default for PlotConfig {
40
 impl Default for PlotConfig {
41
     fn default() -> Self {
41
     fn default() -> Self {
42
         Self {
42
         Self {
43
-            background: Color { r: 30, g: 30, b: 46, a: 255 },
43
+            background: Color {
44
-            axis_color: Color { r: 166, g: 173, b: 200, a: 255 },
44
+                r: 30,
45
-            grid_color: Color { r: 69, g: 71, b: 90, a: 255 },
45
+                g: 30,
46
-            label_color: Color { r: 166, g: 173, b: 200, a: 255 },
46
+                b: 46,
47
+                a: 255,
48
+            },
49
+            axis_color: Color {
50
+                r: 166,
51
+                g: 173,
52
+                b: 200,
53
+                a: 255,
54
+            },
55
+            grid_color: Color {
56
+                r: 69,
57
+                g: 71,
58
+                b: 90,
59
+                a: 255,
60
+            },
61
+            label_color: Color {
62
+                r: 166,
63
+                g: 173,
64
+                b: 200,
65
+                a: 255,
66
+            },
47
             show_grid: true,
67
             show_grid: true,
48
             show_labels: true,
68
             show_labels: true,
49
             curve_width: 2.0,
69
             curve_width: 2.0,
@@ -56,12 +76,42 @@ impl Default for PlotConfig {
56
 
76
 
57
 /// Default curve colors (catppuccin palette)
77
 /// Default curve colors (catppuccin palette)
58
 pub const CURVE_COLORS: [Color; 6] = [
78
 pub const CURVE_COLORS: [Color; 6] = [
59
-    Color { r: 137, g: 180, b: 250, a: 255 }, // blue
79
+    Color {
60
-    Color { r: 166, g: 227, b: 161, a: 255 }, // green
80
+        r: 137,
61
-    Color { r: 249, g: 226, b: 175, a: 255 }, // yellow
81
+        g: 180,
62
-    Color { r: 243, g: 139, b: 168, a: 255 }, // red
82
+        b: 250,
63
-    Color { r: 203, g: 166, b: 247, a: 255 }, // mauve
83
+        a: 255,
64
-    Color { r: 148, g: 226, b: 213, a: 255 }, // teal
84
+    }, // blue
85
+    Color {
86
+        r: 166,
87
+        g: 227,
88
+        b: 161,
89
+        a: 255,
90
+    }, // green
91
+    Color {
92
+        r: 249,
93
+        g: 226,
94
+        b: 175,
95
+        a: 255,
96
+    }, // yellow
97
+    Color {
98
+        r: 243,
99
+        g: 139,
100
+        b: 168,
101
+        a: 255,
102
+    }, // red
103
+    Color {
104
+        r: 203,
105
+        g: 166,
106
+        b: 247,
107
+        a: 255,
108
+    }, // mauve
109
+    Color {
110
+        r: 148,
111
+        g: 226,
112
+        b: 213,
113
+        a: 255,
114
+    }, // teal
65
 ];
115
 ];
66
 
116
 
67
 /// 2D graph state and renderer
117
 /// 2D graph state and renderer
@@ -351,13 +401,29 @@ impl Graph2D {
351
 
401
 
352
     fn draw_function(&self, ctx: &Context, func: &Plottable, width: u32, height: u32) {
402
     fn draw_function(&self, ctx: &Context, func: &Plottable, width: u32, height: u32) {
353
         match func {
403
         match func {
354
-            Plottable::Explicit2D { expr, x_var, color, style } => {
404
+            Plottable::Explicit2D {
405
+                expr,
406
+                x_var,
407
+                color,
408
+                style,
409
+            } => {
355
                 self.draw_explicit(ctx, expr, x_var, *color, *style, width, height);
410
                 self.draw_explicit(ctx, expr, x_var, *color, *style, width, height);
356
             }
411
             }
357
-            Plottable::Implicit2D { expr, x_var, y_var, color } => {
412
+            Plottable::Implicit2D {
413
+                expr,
414
+                x_var,
415
+                y_var,
416
+                color,
417
+            } => {
358
                 self.draw_implicit(ctx, expr, x_var, y_var, *color, width, height);
418
                 self.draw_implicit(ctx, expr, x_var, y_var, *color, width, height);
359
             }
419
             }
360
-            Plottable::Parametric2D { x_expr, y_expr, t_var, t_range, color } => {
420
+            Plottable::Parametric2D {
421
+                x_expr,
422
+                y_expr,
423
+                t_var,
424
+                t_range,
425
+                color,
426
+            } => {
361
                 self.draw_parametric(ctx, x_expr, y_expr, t_var, *t_range, *color, width, height);
427
                 self.draw_parametric(ctx, x_expr, y_expr, t_var, *t_range, *color, width, height);
362
             }
428
             }
363
             _ => {}
429
             _ => {}
@@ -400,7 +466,8 @@ impl Graph2D {
400
 
466
 
401
             if let Ok(result) = evaluator.eval(expr) {
467
             if let Ok(result) = evaluator.eval(expr) {
402
                 if let Ok(math_y) = expr_to_f64(&result) {
468
                 if let Ok(math_y) = expr_to_f64(&result) {
403
-                    if math_y.is_finite() && math_y >= self.viewport.y_min - x_range
469
+                    if math_y.is_finite()
470
+                        && math_y >= self.viewport.y_min - x_range
404
                         && math_y <= self.viewport.y_max + x_range
471
                         && math_y <= self.viewport.y_max + x_range
405
                     {
472
                     {
406
                         let (sx, sy) = self.math_to_screen(math_x, math_y, width, height);
473
                         let (sx, sy) = self.math_to_screen(math_x, math_y, width, height);
@@ -543,18 +610,35 @@ impl Graph2D {
543
                 let s11 = v11 >= 0.0;
610
                 let s11 = v11 >= 0.0;
544
 
611
 
545
                 // Build case index (4-bit)
612
                 // Build case index (4-bit)
546
-                let case = (s00 as u8) | ((s10 as u8) << 1) | ((s01 as u8) << 2) | ((s11 as u8) << 3);
613
+                let case =
614
+                    (s00 as u8) | ((s10 as u8) << 1) | ((s01 as u8) << 2) | ((s11 as u8) << 3);
547
 
615
 
548
                 // Linear interpolation to find zero crossing on an edge
616
                 // Linear interpolation to find zero crossing on an edge
549
                 let interp = |va: f64, vb: f64| -> f64 {
617
                 let interp = |va: f64, vb: f64| -> f64 {
550
-                    if (va - vb).abs() < 1e-15 { 0.5 } else { va / (va - vb) }
618
+                    if (va - vb).abs() < 1e-15 {
619
+                        0.5
620
+                    } else {
621
+                        va / (va - vb)
622
+                    }
551
                 };
623
                 };
552
 
624
 
553
                 // Edge midpoints where contour crosses
625
                 // Edge midpoints where contour crosses
554
-                let e_bottom = || { let t = interp(v00, v10); (x0 + t * dx, y0) };
626
+                let e_bottom = || {
555
-                let e_top = || { let t = interp(v01, v11); (x0 + t * dx, y1) };
627
+                    let t = interp(v00, v10);
556
-                let e_left = || { let t = interp(v00, v01); (x0, y0 + t * dy) };
628
+                    (x0 + t * dx, y0)
557
-                let e_right = || { let t = interp(v10, v11); (x1, y0 + t * dy) };
629
+                };
630
+                let e_top = || {
631
+                    let t = interp(v01, v11);
632
+                    (x0 + t * dx, y1)
633
+                };
634
+                let e_left = || {
635
+                    let t = interp(v00, v01);
636
+                    (x0, y0 + t * dy)
637
+                };
638
+                let e_right = || {
639
+                    let t = interp(v10, v11);
640
+                    (x1, y0 + t * dy)
641
+                };
558
 
642
 
559
                 // Draw line segments based on marching squares case
643
                 // Draw line segments based on marching squares case
560
                 let draw_line = |p1: (f64, f64), p2: (f64, f64)| {
644
                 let draw_line = |p1: (f64, f64), p2: (f64, f64)| {
@@ -603,7 +687,15 @@ impl Graph2D {
603
         ctx.set_dash(&[], 0.0);
687
         ctx.set_dash(&[], 0.0);
604
 
688
 
605
         // Coordinate display
689
         // Coordinate display
606
-        set_color(ctx, Color { r: 30, g: 30, b: 46, a: 200 });
690
+        set_color(
691
+            ctx,
692
+            Color {
693
+                r: 30,
694
+                g: 30,
695
+                b: 46,
696
+                a: 200,
697
+            },
698
+        );
607
         let label = format!("({:.4}, {:.4})", mx, my);
699
         let label = format!("({:.4}, {:.4})", mx, my);
608
         let label_w = label.len() as f64 * 7.0 + 8.0;
700
         let label_w = label.len() as f64 * 7.0 + 8.0;
609
         let label_h = 18.0;
701
         let label_h = 18.0;
@@ -637,7 +729,7 @@ fn nice_step(rough: f64) -> f64 {
637
 
729
 
638
     let nice = if frac < 1.5 {
730
     let nice = if frac < 1.5 {
639
         1.0
731
         1.0
640
-    } else if frac < 3.0 {
732
+    } else if frac <= 3.0 {
641
         2.0
733
         2.0
642
     } else if frac < 7.0 {
734
     } else if frac < 7.0 {
643
         5.0
735
         5.0
garcalc-graph/src/plot3d.rsmodified
@@ -40,8 +40,8 @@ impl Camera3D {
40
     /// Rotate the camera by delta angles
40
     /// Rotate the camera by delta angles
41
     pub fn rotate(&mut self, d_azimuth: f64, d_elevation: f64) {
41
     pub fn rotate(&mut self, d_azimuth: f64, d_elevation: f64) {
42
         self.azimuth += d_azimuth;
42
         self.azimuth += d_azimuth;
43
-        self.elevation = (self.elevation + d_elevation)
43
+        self.elevation =
44
-            .clamp(-89.0_f64.to_radians(), 89.0_f64.to_radians());
44
+            (self.elevation + d_elevation).clamp(-89.0_f64.to_radians(), 89.0_f64.to_radians());
45
     }
45
     }
46
 
46
 
47
     /// Zoom by a factor
47
     /// Zoom by a factor
@@ -107,7 +107,12 @@ impl Colormap {
107
             Colormap::Coolwarm => coolwarm(t),
107
             Colormap::Coolwarm => coolwarm(t),
108
             Colormap::Grayscale => {
108
             Colormap::Grayscale => {
109
                 let v = (t * 255.0) as u8;
109
                 let v = (t * 255.0) as u8;
110
-                Color { r: v, g: v, b: v, a: 255 }
110
+                Color {
111
+                    r: v,
112
+                    g: v,
113
+                    b: v,
114
+                    a: 255,
115
+                }
111
             }
116
             }
112
         }
117
         }
113
     }
118
     }
@@ -158,9 +163,24 @@ pub struct Plot3DConfig {
158
 impl Default for Plot3DConfig {
163
 impl Default for Plot3DConfig {
159
     fn default() -> Self {
164
     fn default() -> Self {
160
         Self {
165
         Self {
161
-            background: Color { r: 30, g: 30, b: 46, a: 255 },
166
+            background: Color {
162
-            axis_color: Color { r: 166, g: 173, b: 200, a: 255 },
167
+                r: 30,
163
-            wireframe_color: Color { r: 100, g: 100, b: 120, a: 255 },
168
+                g: 30,
169
+                b: 46,
170
+                a: 255,
171
+            },
172
+            axis_color: Color {
173
+                r: 166,
174
+                g: 173,
175
+                b: 200,
176
+                a: 255,
177
+            },
178
+            wireframe_color: Color {
179
+                r: 100,
180
+                g: 100,
181
+                b: 120,
182
+                a: 255,
183
+            },
164
             colormap: Colormap::Viridis,
184
             colormap: Colormap::Viridis,
165
             render_mode: RenderMode::FilledWithWireframe,
185
             render_mode: RenderMode::FilledWithWireframe,
166
             grid_lines: 40,
186
             grid_lines: 40,
@@ -256,7 +276,11 @@ impl Graph3D {
256
             forward.0 * world_up.1 - forward.1 * world_up.0,
276
             forward.0 * world_up.1 - forward.1 * world_up.0,
257
         );
277
         );
258
         let right_len = (right.0 * right.0 + right.1 * right.1 + right.2 * right.2).sqrt();
278
         let right_len = (right.0 * right.0 + right.1 * right.1 + right.2 * right.2).sqrt();
259
-        let right = (right.0 / right_len, right.1 / right_len, right.2 / right_len);
279
+        let right = (
280
+            right.0 / right_len,
281
+            right.1 / right_len,
282
+            right.2 / right_len,
283
+        );
260
 
284
 
261
         // Actual up = right x forward
285
         // Actual up = right x forward
262
         let up = (
286
         let up = (
@@ -361,9 +385,17 @@ impl Graph3D {
361
             Surface3D::Explicit { expr, x_var, y_var } => {
385
             Surface3D::Explicit { expr, x_var, y_var } => {
362
                 self.draw_explicit_surface(ctx, expr, x_var, y_var, width, height);
386
                 self.draw_explicit_surface(ctx, expr, x_var, y_var, width, height);
363
             }
387
             }
364
-            Surface3D::Parametric { x_expr, y_expr, z_expr, u_var, v_var, u_range, v_range } => {
388
+            Surface3D::Parametric {
389
+                x_expr,
390
+                y_expr,
391
+                z_expr,
392
+                u_var,
393
+                v_var,
394
+                u_range,
395
+                v_range,
396
+            } => {
365
                 self.draw_parametric_surface(
397
                 self.draw_parametric_surface(
366
-                    ctx, x_expr, y_expr, z_expr, u_var, v_var, *u_range, *v_range, width, height
398
+                    ctx, x_expr, y_expr, z_expr, u_var, v_var, *u_range, *v_range, width, height,
367
                 );
399
                 );
368
             }
400
             }
369
         }
401
         }
@@ -446,8 +478,8 @@ impl Graph3D {
446
                     let cy = (p00.1 + p10.1 + p11.1 + p01.1) / 4.0;
478
                     let cy = (p00.1 + p10.1 + p11.1 + p01.1) / 4.0;
447
                     let cz = (p00.2 + p10.2 + p11.2 + p01.2) / 4.0;
479
                     let cz = (p00.2 + p10.2 + p11.2 + p01.2) / 4.0;
448
                     let depth = (cx - cam_pos.0).powi(2)
480
                     let depth = (cx - cam_pos.0).powi(2)
449
-                              + (cy - cam_pos.1).powi(2)
481
+                        + (cy - cam_pos.1).powi(2)
450
-                              + (cz - cam_pos.2).powi(2);
482
+                        + (cz - cam_pos.2).powi(2);
451
 
483
 
452
                     quads.push(Quad {
484
                     quads.push(Quad {
453
                         corners: [p00, p10, p11, p01],
485
                         corners: [p00, p10, p11, p01],
@@ -459,11 +491,16 @@ impl Graph3D {
459
         }
491
         }
460
 
492
 
461
         // Sort by depth (painter's algorithm - far to near)
493
         // Sort by depth (painter's algorithm - far to near)
462
-        quads.sort_by(|a, b| b.depth.partial_cmp(&a.depth).unwrap_or(std::cmp::Ordering::Equal));
494
+        quads.sort_by(|a, b| {
495
+            b.depth
496
+                .partial_cmp(&a.depth)
497
+                .unwrap_or(std::cmp::Ordering::Equal)
498
+        });
463
 
499
 
464
         // Draw quads
500
         // Draw quads
465
         for quad in &quads {
501
         for quad in &quads {
466
-            let corners: Vec<(f64, f64)> = quad.corners
502
+            let corners: Vec<(f64, f64)> = quad
503
+                .corners
467
                 .iter()
504
                 .iter()
468
                 .map(|p| self.project(p.0, p.1, p.2, width, height))
505
                 .map(|p| self.project(p.0, p.1, p.2, width, height))
469
                 .collect();
506
                 .collect();
@@ -532,7 +569,9 @@ impl Graph3D {
532
                 let z_result = evaluator.eval(z_expr);
569
                 let z_result = evaluator.eval(z_expr);
533
 
570
 
534
                 if let (Ok(xr), Ok(yr), Ok(zr)) = (x_result, y_result, z_result) {
571
                 if let (Ok(xr), Ok(yr), Ok(zr)) = (x_result, y_result, z_result) {
535
-                    if let (Ok(x), Ok(y), Ok(z)) = (expr_to_f64(&xr), expr_to_f64(&yr), expr_to_f64(&zr)) {
572
+                    if let (Ok(x), Ok(y), Ok(z)) =
573
+                        (expr_to_f64(&xr), expr_to_f64(&yr), expr_to_f64(&zr))
574
+                    {
536
                         if x.is_finite() && y.is_finite() && z.is_finite() {
575
                         if x.is_finite() && y.is_finite() && z.is_finite() {
537
                             z_min = z_min.min(z);
576
                             z_min = z_min.min(z);
538
                             z_max = z_max.max(z);
577
                             z_max = z_max.max(z);
@@ -574,8 +613,8 @@ impl Graph3D {
574
                     let cy = (p00.1 + p10.1 + p11.1 + p01.1) / 4.0;
613
                     let cy = (p00.1 + p10.1 + p11.1 + p01.1) / 4.0;
575
                     let cz = (p00.2 + p10.2 + p11.2 + p01.2) / 4.0;
614
                     let cz = (p00.2 + p10.2 + p11.2 + p01.2) / 4.0;
576
                     let depth = (cx - cam_pos.0).powi(2)
615
                     let depth = (cx - cam_pos.0).powi(2)
577
-                              + (cy - cam_pos.1).powi(2)
616
+                        + (cy - cam_pos.1).powi(2)
578
-                              + (cz - cam_pos.2).powi(2);
617
+                        + (cz - cam_pos.2).powi(2);
579
 
618
 
580
                     quads.push(Quad {
619
                     quads.push(Quad {
581
                         corners: [p00, p10, p11, p01],
620
                         corners: [p00, p10, p11, p01],
@@ -586,10 +625,15 @@ impl Graph3D {
586
             }
625
             }
587
         }
626
         }
588
 
627
 
589
-        quads.sort_by(|a, b| b.depth.partial_cmp(&a.depth).unwrap_or(std::cmp::Ordering::Equal));
628
+        quads.sort_by(|a, b| {
629
+            b.depth
630
+                .partial_cmp(&a.depth)
631
+                .unwrap_or(std::cmp::Ordering::Equal)
632
+        });
590
 
633
 
591
         for quad in &quads {
634
         for quad in &quads {
592
-            let corners: Vec<(f64, f64)> = quad.corners
635
+            let corners: Vec<(f64, f64)> = quad
636
+                .corners
593
                 .iter()
637
                 .iter()
594
                 .map(|p| self.project(p.0, p.1, p.2, width, height))
638
                 .map(|p| self.project(p.0, p.1, p.2, width, height))
595
                 .collect();
639
                 .collect();
@@ -675,21 +719,13 @@ fn plasma(t: f64) -> Color {
675
 
719
 
676
 fn coolwarm(t: f64) -> Color {
720
 fn coolwarm(t: f64) -> Color {
677
     // Blue (cool) to red (warm)
721
     // Blue (cool) to red (warm)
678
-    let r = if t < 0.5 {
722
+    let r = if t < 0.5 { 0.2 + t * 1.6 } else { 1.0 };
679
-        0.2 + t * 1.6
680
-    } else {
681
-        1.0
682
-    };
683
     let g = if t < 0.5 {
723
     let g = if t < 0.5 {
684
         0.2 + t * 1.2
724
         0.2 + t * 1.2
685
     } else {
725
     } else {
686
         0.8 - (t - 0.5) * 1.6
726
         0.8 - (t - 0.5) * 1.6
687
     };
727
     };
688
-    let b = if t < 0.5 {
728
+    let b = if t < 0.5 { 1.0 } else { 1.0 - (t - 0.5) * 1.6 };
689
-        1.0
690
-    } else {
691
-        1.0 - (t - 0.5) * 1.6
692
-    };
693
     Color {
729
     Color {
694
         r: (r.clamp(0.0, 1.0) * 255.0) as u8,
730
         r: (r.clamp(0.0, 1.0) * 255.0) as u8,
695
         g: (g.clamp(0.0, 1.0) * 255.0) as u8,
731
         g: (g.clamp(0.0, 1.0) * 255.0) as u8,