@@ -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, |