diff --git a/src/bin/flat.rs b/src/bin/flat.rs index f4e6152..a21a882 100644 --- a/src/bin/flat.rs +++ b/src/bin/flat.rs @@ -1,8 +1,9 @@ -use std::collections::HashMap; use flo_draw::*; use flo_canvas::*; use glam::*; use riemann::{Decomp2, Metric, trace_iter}; +use shape::Shape; +use Subspace::{Boundary, Inner, Outer}; #[cfg(test)] use approx::assert_abs_diff_eq; @@ -13,133 +14,115 @@ pub fn main() { with_2d_graphics(move || { let canvas = create_drawing_window("Refraction"); canvas.draw(|gc| { - let space = Coil { - scale: 3.0, - r: 300.0, - w: 50.0, - m: 10.0, - }; let tube = Rect { inner_radius: 30.0, outer_radius: 50.0, internal_halflength: 100.0, external_halflength: 300.0, }; - let mut grid = Grid { - hlines: vec![-tube.external_halflength, tube.external_halflength], - vlines: vec![-tube.outer_radius, -tube.inner_radius, tube.inner_radius, tube.outer_radius], - cells: HashMap::new(), - }; - fn take(k: i32, data: &[f32]) -> f32 { - if k < 0 { - -f32::INFINITY - } else if k as usize >= data.len() { - f32::INFINITY - } else { - data[k as usize] - } - } - for (i, j) in [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (0, 1), (4, 1), (0, 2), (1, 2), (2, 2), (3, 2), (4, 2)] { - grid.cells.insert((i as usize, j as usize), - Box::new(FlatRect { - min: vec2(take(i - 1, grid.vlines.as_slice()), take(j - 1, grid.hlines.as_slice())), - max: vec2(take(i, grid.vlines.as_slice()), take(j, grid.hlines.as_slice())), - })); - } - grid.cells.insert((2, 1), Box::new(RectInside { rect: tube })); - println!("{:?}", grid.cells); + + let space = Space { rect: tube }; + gc.canvas_height(1000.0); tube.render(gc); gc.line_width(0.5); gc.stroke_color(Color::Rgba(1.0, 0.5, 0.0, 0.5)); draw_fan(gc, &tube, vec2(-500.0, 0.0), vec2(1.0, 0.0), 1.0); gc.stroke_color(Color::Rgba(1.0, 0.5, 0.0, 1.0)); - draw_fan_2(gc, &tube, &grid, vec2(-500.0, 0.0), vec2(1.0, 0.0), 1.0); + draw_fan_2(gc, &space, vec2(-500.0, 0.0), vec2(1.0, 0.0), 1.0); gc.stroke_color(Color::Rgba(0.0, 0.5, 1.0, 0.5)); draw_fan(gc, &tube, vec2(0.0, -0.5 * tube.internal_halflength), vec2(1.0, 1.0), 1.0); gc.stroke_color(Color::Rgba(0.0, 0.5, 1.0, 1.0)); - draw_fan_2(gc, &tube, &grid, vec2(0.0, -0.5 * tube.internal_halflength), vec2(1.0, 1.0), 1.0); - - gc.new_path(); - for ((i, j), cell) in &grid.cells { - let (a, b) = cell.local_bounds(); - gc.rect(a.x.clamp(-1000.0, 1000.0), a.y.clamp(-1000.0, 1000.0), b.x.clamp(-1000.0, 1000.0), b.y.clamp(-1000.0, 1000.0)); - } - gc.fill_color(Color::Rgba(0.0, 1.0, 0.0, 0.3)); - gc.fill(); - - gc.new_dash_pattern(); - gc.dash_length(6.0); - gc.new_dash_pattern(); - gc.new_path(); - gc.stroke_color(Color::Rgba(0.0, 0.0, 0.0, 0.5)); - gc.line_width(1.0); - for hline in grid.hlines { - gc.move_to(-1000.0, hline); - gc.line_to(1000.0, hline); - } - for vline in grid.vlines { - gc.move_to(vline, -1000.0); - gc.line_to(vline, 1000.0); - } - gc.stroke(); + draw_fan_2(gc, &space, vec2(0.0, -0.5 * tube.internal_halflength), vec2(1.0, 1.0), 1.0); }); }); } -fn find_bucket(val: f32, splits: &[f32]) -> usize { - for (k, &split) in splits.iter().enumerate() { - if val < split { - return k; - } - } - splits.len() +struct Space { + rect: Rect, } -fn draw_ray_2(gc: &mut Vec, space: &impl Metric, grid: &Grid, base: Vec2, dir: Vec2) { - let dir = space.globalize(base, dir); +#[derive(PartialEq, Eq)] +enum Subspace { + Outer, + Boundary, + Inner, +} + +impl Space { + fn which_subspace(&self, pt: Vec2) -> Subspace { + if pt.y.abs() > self.rect.external_halflength { + Outer + } else if pt.x.abs() > self.rect.outer_radius { + Outer + } else if pt.x.abs() > self.rect.inner_radius { + Boundary + } else { + Inner + } + } +} + +fn draw_ray_2(gc: &mut Vec, space: &Space, base: Vec2, dir: Vec2) { + let dir = space.rect.globalize(base, dir); gc.new_path(); gc.move_to(base.x, base.y); let dt = DT; let mut p = base; - let mut v = space.normalize(base, dir); + let mut v = space.rect.normalize(base, dir); for _ in 0..10000 { - let a: Vec2 = -riemann::convolute(riemann::krist(space, p), v); + let a: Vec2 = -riemann::convolute(riemann::krist(&space.rect, p), v); v = v + a * dt; p = p + v * dt; gc.line_to(p.x, p.y); if p.abs().cmpgt(Vec2::splat(1000.0)).any() { break; } - let i = find_bucket(p.x, grid.vlines.as_slice()); - let j = find_bucket(p.y, grid.hlines.as_slice()); - if let Some(cell) = grid.cells.get(&(i, j)) { - gc.stroke(); - gc.new_dash_pattern(); - gc.dash_length(6.0); - gc.new_path(); - gc.move_to(p.x, p.y); - let ray = cell.ray_to_local(Ray { pos: p, dir: v }); - let Some(ray) = cell.to_boundary(ray) else { - break; - }; - Ray { pos: p, dir: v } = cell.ray_to_global(ray); - gc.line_to(p.x, p.y); - gc.stroke(); - gc.new_dash_pattern(); - gc.new_path(); - gc.move_to(p.x, p.y); + let sub = space.which_subspace(p); + if sub == Boundary { + continue; } + gc.stroke(); + gc.new_dash_pattern(); + gc.dash_length(6.0); + gc.new_path(); + gc.move_to(p.x, p.y); + match sub { + Inner => { + let cell = RectInside { rect: space.rect }; + let ray = cell.ray_to_local(Ray { pos: p, dir: v }); + let Some(ray) = cell.to_boundary(ray) else { + panic!("Can't get outta here!"); + }; + Ray { pos: p, dir: v } = cell.ray_to_global(ray); + } + Outer => { + let cell = basic_shapes::Rect { size: vec2(space.rect.outer_radius, space.rect.external_halflength) }; + let Some(dist) = cell.trace_into(Ray { pos: p, dir: v }) else { + p += v * 1000.0; + gc.line_to(p.x, p.y); + gc.stroke(); + break; + }; + p += v * dist; + } + Boundary => panic!(), + } + gc.line_to(p.x, p.y); + gc.stroke(); + gc.new_dash_pattern(); + gc.new_path(); + gc.move_to(p.x, p.y); } gc.stroke(); gc.new_dash_pattern(); } -fn draw_fan_2(gc: &mut Vec, space: &impl Metric, grid: &Grid, base: Vec2, dir: Vec2, spread: f32) { +fn draw_fan_2(gc: &mut Vec, space: &Space, base: Vec2, dir: Vec2, spread: f32) { let dir = dir.normalize(); let v = vec2(-dir.y, dir.x); for y in itertools_num::linspace(-spread, spread, 101) { - draw_ray_2(gc, space, grid, base, dir + y * v); + draw_ray_2(gc, space, base, dir + y * v); } } @@ -168,24 +151,6 @@ trait Renderable { fn render(&self, gc: &mut Vec); } -impl Renderable for Coil { - fn render(&self, gc: &mut Vec) { - gc.new_path(); - gc.circle(0.0, 0.0, self.r + self.w + self.m); - gc.circle(0.0, 0.0, self.r + self.w); - gc.circle(0.0, 0.0, self.r - self.w); - gc.circle(0.0, 0.0, self.r - self.w - self.m); - gc.winding_rule(WindingRule::EvenOdd); - gc.fill_color(Color::Rgba(0.8, 0.8, 0.8, 1.0)); - gc.fill(); - gc.line_width(0.5); - gc.stroke_color(Color::Rgba(1.0, 0.5, 0.0, 1.0)); - draw_fan(gc, self, vec2(-500.0, 0.0), vec2(1.0, 0.0), 1.0); - gc.stroke_color(Color::Rgba(0.0, 0.5, 1.0, 1.0)); - draw_fan(gc, self, vec2(0.0, self.r), vec2(1.0, 0.0), 1.0); - } -} - impl Renderable for Rect { fn render(&self, gc: &mut Vec) { gc.new_path(); @@ -197,31 +162,6 @@ impl Renderable for Rect { } } -struct Coil { - m: f32, - scale: f32, - r: f32, - w: f32, -} - -impl Metric for Coil { - fn halfmetric(&self, pos: Vec2) -> Decomp2 { - let r = pos.length(); - let dir = pos.normalize(); - - let s = smoothbox(r, vec2(self.r - self.w, self.r + self.w), self.m); - let t = 1.0.lerp(self.r / r / self.scale, s); - - Decomp2 { - ortho: Mat2::from_cols_array(&[ - dir.x, -dir.y, - dir.y, dir.x, - ]), - diag: vec2(1.0, t), - } - } -} - #[derive(Debug, PartialEq)] struct Ray { pos: Vec2, @@ -380,12 +320,6 @@ mod shape { } } -struct Grid { - hlines: Vec, - vlines: Vec, - cells: HashMap<(usize, usize), Box>, -} - trait FlatCell: std::fmt::Debug { fn pos_to_global(&self, pos: Vec2) -> Vec2; fn pos_to_local(&self, pos: Vec2) -> Vec2;