refraction/src/bin/flat/main.rs
2024-09-14 15:02:10 +03:00

310 lines
7.8 KiB
Rust

use std::f32::consts::{FRAC_PI_2, PI};
use flo_canvas::*;
use flo_draw::*;
use glam::*;
use crate::types::FlatTraceResult;
use riemann::{trace_iter, Metric};
use tube::metric::Tube;
use tube::Space;
use tube::Subspace::{Boundary, Inner, Outer};
use types::{Location, Object, Ray};
mod float_fun;
mod fns;
mod riemann;
mod tube;
mod types;
const DT: f32 = 0.1;
fn draw_loop(gc: &mut Vec<Draw>, mut pts: impl Iterator<Item = Vec2>) {
gc.new_path();
let Some(first) = pts.next() else {
return;
};
gc.move_to(first.x, first.y);
for pt in pts {
gc.line_to(pt.x, pt.y);
}
gc.close_path();
gc.stroke();
}
pub fn main() {
with_2d_graphics(move || {
let canvas = create_drawing_window("Refraction");
canvas.draw(|gc| {
let tube = Tube {
inner_radius: 30.0,
outer_radius: 50.0,
internal_halflength: 100.0,
external_halflength: 300.0,
};
let objs: Vec<_> = [-1.25, -1.00, -0.85, -0.50, 0.00, 0.40, 0.70, 0.95, 1.05]
.iter()
.enumerate()
.map(|(k, &y)| Object {
id: k as i32,
loc: {
let pos = vec2(0.0, y * tube.external_halflength);
let adj: Mat2 = tube.sqrt_at(pos).inverse().into();
let rot = Mat2::from_angle(y);
Location {
pos,
rot: adj * rot,
}
},
r: 20.0,
})
.collect();
let space = Space { tube, objs };
gc.canvas_height(500.0);
gc.transform(Transform2D::rotate(FRAC_PI_2));
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, &space, vec2(-500.0, 0.0), vec2(1.0, 0.0), 1.0);
gc.stroke_color(Color::Rgba(0.5, 1.0, 0.0, 1.0));
draw_fan_2(
gc,
&space,
vec2(-2.5 * tube.outer_radius, 1.25 * tube.external_halflength),
vec2(1.0, -1.0),
1.0,
);
draw_track(gc, &space, vec2(-500.0, 0.0), vec2(1.0, 0.2));
draw_track(gc, &space, vec2(-500.0, 0.0), vec2(1.0, 0.5));
draw_track(
gc,
&space,
vec2(-0.5 * tube.inner_radius, -1.25 * tube.external_halflength),
vec2(0.1, 1.0),
);
let circle_segments = 47;
for obj in &space.objs {
let pos = obj.loc.pos;
gc.new_path();
gc.circle(pos.x, pos.y, 5.0);
gc.fill_color(Color::Rgba(0.0, 0.5, 1.0, 1.0));
gc.fill();
gc.stroke_color(Color::Rgba(0.0, 0.0, 0.0, 0.5));
draw_loop(
gc,
itertools_num::linspace(0.0, 2.0 * PI, circle_segments)
.skip(1)
.map(|φ| {
let dir = Vec2::from_angle(φ) * obj.r;
let dir = obj.loc.rot * dir;
pos + dir
}),
);
gc.stroke_color(Color::Rgba(0.0, 0.5, 1.0, 0.5));
draw_loop(
gc,
itertools_num::linspace(0.0, 2.0 * PI, circle_segments)
.skip(1)
.map(|φ| {
let dir = Vec2::from_angle(φ) * obj.r;
let dir = obj.loc.rot * dir;
space.trace_step(Ray { pos, dir }).pos
}),
);
gc.stroke_color(Color::Rgba(0.5, 0.0, 1.0, 1.0));
draw_loop(
gc,
itertools_num::linspace(0.0, 2.0 * PI, circle_segments)
.skip(1)
.map(|φ| {
let n = obj.r.floor();
let d = obj.r / n;
let dir = Vec2::from_angle(φ);
let dir = obj.loc.rot * dir * d;
space
.trace_iter(Ray { pos, dir })
.nth(n as usize)
.unwrap()
.pos
}),
);
}
});
});
}
fn rel_to_abs(space: &impl Metric, base: &Location, rel: Vec2, steps: usize) -> Vec2 {
let c = 1.0 / (steps as f32);
trace_iter(space, base.pos, base.rot * rel, c * rel.length())
.nth(steps - 1)
.unwrap()
}
fn draw_cross(gc: &mut Vec<Draw>, pos: Vec2, r: f32) {
gc.move_to(pos.x - r, pos.y - r);
gc.line_to(pos.x + r, pos.y + r);
gc.move_to(pos.x - r, pos.y + r);
gc.line_to(pos.x + r, pos.y - r);
}
fn draw_ray_2(gc: &mut Vec<Draw>, space: &Space, base: Vec2, dir: Vec2) {
fn trace_to_flat(gc: &mut Vec<Draw>, space: &Space, ray: Ray) -> (Ray, FlatTraceResult) {
for ray in space.trace_iter(ray).skip(1) {
gc.line_to(ray.pos.x, ray.pos.y);
match space.which_subspace(ray.pos) {
Inner => return (ray, space.trace_inner(ray)),
Outer => return (ray, space.trace_outer(ray)),
Boundary => continue,
};
}
unreachable!("Space::trace_iter terminated!")
}
let mut hits = Vec::<Draw>::new();
let dir = space.tube.globalize(base, dir);
gc.new_path();
gc.move_to(base.x, base.y);
let mut ray = Ray {
pos: base,
dir: space.tube.normalize_vec_at(base, dir) * DT,
};
for _ in 0..100 {
let ret;
(ray, ret) = trace_to_flat(gc, space, ray);
gc.stroke();
gc.new_dash_pattern();
// gc.dash_length(6.0);
gc.new_path();
gc.move_to(ray.pos.x, ray.pos.y);
for hit in ret.objects {
let obj = space.objs[hit.id as usize];
let apx_hit_pos = rel_to_abs(&space.tube, &obj.loc, hit.rel.pos, 128);
// assert_abs_diff_eq!(apx_hit_pos, hit.pos, epsilon=1.0);
let Ray { pos: rel, dir } = hit.rel;
let diff = rel.dot(dir).powi(2)
- dir.length_squared() * (rel.length_squared() - obj.r.powi(2));
assert!(diff >= 0.0);
let t = (-rel.dot(dir) + diff.sqrt()) / dir.length_squared();
let rel2 = hit.rel.forward(t).pos;
let pos2 = rel_to_abs(&space.tube, &obj.loc, rel2, 128);
draw_cross(&mut hits, pos2, 1.0);
}
let a = ray.pos;
if let Some(r) = ret.end {
ray = r
} else {
ray = ray.forward(1000.0 / DT);
gc.line_to(ray.pos.x, ray.pos.y);
break;
}
for p in space.line(a, ray.pos, 10.0) {
gc.line_to(p.x, p.y);
}
gc.stroke();
gc.new_dash_pattern();
gc.new_path();
gc.move_to(ray.pos.x, ray.pos.y);
}
gc.stroke();
gc.new_path();
gc.new_dash_pattern();
gc.append(&mut hits);
gc.stroke();
}
fn draw_fan_2(gc: &mut Vec<Draw>, 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, base, dir + y * v);
}
}
fn draw_ray(gc: &mut Vec<Draw>, space: &impl Metric, base: Vec2, dir: Vec2) {
let dir = space.globalize(base, dir);
gc.new_path();
gc.move_to(base.x, base.y);
for pt in trace_iter(space, base, dir, DT).take(10000) {
gc.line_to(pt.x, pt.y);
if pt.abs().cmpgt(Vec2::splat(1000.0)).any() {
break;
}
}
gc.stroke();
}
fn draw_track(gc: &mut Vec<Draw>, space: &Space, start: Vec2, dir: Vec2) {
const SCALE: f32 = 5.0;
const STEP: f32 = 2.0 * SCALE;
// let mut loc = Location { pos: start, rot: Mat2::IDENTITY };
// let dir = space.tube.globalize(start, dir);
// let v = space.tube.normalize(start, dir);
let mut loc = Location {
pos: start,
rot: mat2(dir, vec2(-dir.y, dir.x)),
};
let v = vec2(1.0, 0.0);
let mut draw = |loc: &Location| {
let p = loc.pos;
let ax = p + loc.rot.x_axis * SCALE;
let ay = p + loc.rot.y_axis * SCALE;
gc.new_path();
gc.stroke_color(Color::Rgba(0.7, 0.0, 0.0, 1.0));
gc.move_to(p.x, p.y);
gc.line_to(ax.x, ax.y);
gc.stroke();
gc.new_path();
gc.stroke_color(Color::Rgba(0.0, 0.7, 0.0, 1.0));
gc.move_to(p.x, p.y);
gc.line_to(ay.x, ay.y);
gc.stroke();
};
draw(&loc);
for _ in 0..1000 {
let N = (STEP / DT).floor() as i32;
for _ in 0..N {
loc = space.move_step(loc, v * DT);
}
draw(&loc);
}
}
fn draw_fan(gc: &mut Vec<Draw>, space: &impl Metric, 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(gc, space, base, dir + y * v);
}
}
trait Renderable {
fn render(&self, gc: &mut Vec<Draw>);
}
impl Renderable for Tube {
fn render(&self, gc: &mut Vec<Draw>) {
gc.new_path();
gc.rect(
-self.outer_radius,
-self.external_halflength,
self.outer_radius,
self.external_halflength,
);
gc.rect(
-self.inner_radius,
-self.external_halflength,
self.inner_radius,
self.external_halflength,
);
gc.winding_rule(WindingRule::EvenOdd);
gc.fill_color(Color::Rgba(0.8, 0.8, 0.8, 1.0));
gc.fill();
}
}