Unify flat coordinate system handling

This commit is contained in:
numzero 2024-06-28 15:10:53 +03:00
parent 64344659e3
commit 41448d2226

View File

@ -3,7 +3,7 @@ use crate::riemann;
use crate::riemann::Metric; use crate::riemann::Metric;
use Subspace::{Boundary, Inner, Outer}; use Subspace::{Boundary, Inner, Outer};
use metric::Tube; use metric::Tube;
use coords::{MapperInner, MapperOuter}; use coords::{FlatCoordinateSystem, InnerCS, OuterCS};
use crate::types::{FlatTraceResult, Hit, Location, Object, Ray}; use crate::types::{FlatTraceResult, Hit, Location, Object, Ray};
pub mod metric; pub mod metric;
@ -64,22 +64,24 @@ impl Space {
pub fn trace_inner(&self, ray: Ray) -> FlatTraceResult { pub fn trace_inner(&self, ray: Ray) -> FlatTraceResult {
assert_eq!(self.which_subspace(ray.pos), Inner); assert_eq!(self.which_subspace(ray.pos), Inner);
let cs = InnerCS(self.tube);
let inner = Rect { size: vec2(self.tube.inner_radius, self.tube.internal_halflength) }; let inner = Rect { size: vec2(self.tube.inner_radius, self.tube.internal_halflength) };
let ray = self.tube.global_to_inner(ray); let ray = cs.global_to_flat(ray);
assert!(inner.is_inside(ray.pos)); assert!(inner.is_inside(ray.pos));
let dist = inner.trace_out_of(ray).expect("Can't get outta here!"); let dist = inner.trace_out_of(ray).expect("Can't get outta here!");
let objs = self.list_objects(|loc| self.tube.global_to_inner(loc)); let objs = self.list_objects(|loc| cs.global_to_flat(loc));
FlatTraceResult { FlatTraceResult {
end: Some(self.tube.inner_to_global(ray.forward(dist))), end: Some(cs.flat_to_global(ray.forward(dist))),
objects: Self::hit_objects(objs.as_slice(), ray, Some(dist), |pos| self.tube.inner_to_global(pos)), objects: Self::hit_objects(objs.as_slice(), ray, Some(dist), |pos| cs.flat_to_global(pos)),
} }
} }
pub fn trace_outer(&self, ray: Ray) -> FlatTraceResult { pub fn trace_outer(&self, ray: Ray) -> FlatTraceResult {
assert_eq!(self.which_subspace(ray.pos), Outer); assert_eq!(self.which_subspace(ray.pos), Outer);
let cs = OuterCS(self.tube);
let outer = Rect { size: vec2(self.tube.outer_radius, self.tube.external_halflength) }; let outer = Rect { size: vec2(self.tube.outer_radius, self.tube.external_halflength) };
let dist = outer.trace_into(ray); let dist = outer.trace_into(ray);
let objs = self.list_objects(|loc| self.tube.global_to_outer(loc)); let objs = self.list_objects(|loc| cs.global_to_flat(loc));
FlatTraceResult { FlatTraceResult {
end: dist.map(|dist| ray.forward(dist)), end: dist.map(|dist| ray.forward(dist)),
objects: Self::hit_objects(objs.as_slice(), ray, dist, |pos| pos), objects: Self::hit_objects(objs.as_slice(), ray, dist, |pos| pos),
@ -123,10 +125,11 @@ impl Space {
match self.which_subspace(a) { match self.which_subspace(a) {
Outer => vec![b], Outer => vec![b],
Inner => { Inner => {
let cs = InnerCS(self.tube);
let n = ((b - a).length() / step) as usize + 1; let n = ((b - a).length() / step) as usize + 1;
let a = self.tube.global_to_inner(a); let a = cs.global_to_flat(a);
let b = self.tube.global_to_inner(b); let b = cs.global_to_flat(b);
(1..=n).map(|k| self.tube.inner_to_global(a.lerp(b, k as f32 / n as f32))).collect() (1..=n).map(|k| cs.flat_to_global(a.lerp(b, k as f32 / n as f32))).collect()
} }
Boundary => panic!("Can't draw a line here!"), Boundary => panic!("Can't draw a line here!"),
} }
@ -200,70 +203,68 @@ mod coords {
use crate::types::{Location, Ray}; use crate::types::{Location, Ray};
use super::{Rect, Tube}; use super::{Rect, Tube};
pub trait MapperInner<T> { pub trait FlatCoordinateSystem<T> {
fn inner_to_global(self, v: T) -> T; fn flat_to_global(&self, v: T) -> T;
fn global_to_inner(self, v: T) -> T; fn global_to_flat(&self, v: T) -> T;
} }
pub trait MapperOuter<T> { pub struct InnerCS(pub Tube);
fn outer_to_global(self, v: T) -> T;
fn global_to_outer(self, v: T) -> T;
}
impl MapperInner<Vec2> for Tube { impl FlatCoordinateSystem<Vec2> for InnerCS {
fn inner_to_global(self, pos: Vec2) -> Vec2 { fn flat_to_global(&self, pos: Vec2) -> Vec2 {
vec2(pos.x, self.y(pos.y)) vec2(pos.x, self.0.y(pos.y))
} }
fn global_to_flat(&self, pos: Vec2) -> Vec2 {
fn global_to_inner(self, pos: Vec2) -> Vec2 { vec2(pos.x, self.0.v(pos.y))
vec2(pos.x, self.v(pos.y))
} }
} }
impl MapperInner<Ray> for Tube { impl FlatCoordinateSystem<Ray> for InnerCS {
fn inner_to_global(self, ray: Ray) -> Ray { fn flat_to_global(&self, ray: Ray) -> Ray {
Ray { Ray {
pos: self.inner_to_global(ray.pos), pos: self.flat_to_global(ray.pos),
dir: vec2(ray.dir.x, self.dy(ray.pos.y) * ray.dir.y), dir: vec2(ray.dir.x, self.0.dy(ray.pos.y) * ray.dir.y),
} }
} }
fn global_to_inner(self, ray: Ray) -> Ray { fn global_to_flat(&self, ray: Ray) -> Ray {
Ray { Ray {
pos: self.global_to_inner(ray.pos), pos: self.global_to_flat(ray.pos),
dir: vec2(ray.dir.x, self.dv(ray.pos.y) * ray.dir.y), dir: vec2(ray.dir.x, self.0.dv(ray.pos.y) * ray.dir.y),
} }
} }
} }
impl MapperInner<Location> for Tube { impl FlatCoordinateSystem<Location> for InnerCS {
fn inner_to_global(self, loc: Location) -> Location { fn flat_to_global(&self, loc: Location) -> Location {
todo!() todo!()
} }
// NB: не работает для частей Outer с |y| < external_halflength. Но они и не нужны. // NB: не работает для частей Outer с |y| < external_halflength. Но они и не нужны.
fn global_to_inner(self, loc: Location) -> Location { fn global_to_flat(&self, loc: Location) -> Location {
Location { Location {
pos: vec2(loc.pos.x, self.v(loc.pos.y)), // в плоской СК для Inner или её продолжении на Outer pos: vec2(loc.pos.x, self.0.v(loc.pos.y)), // в плоской СК для Inner или её продолжении на Outer
rot: Mat2::from(self.sqrt_at(loc.pos)) * loc.rot, rot: Mat2::from(self.0.sqrt_at(loc.pos)) * loc.rot,
} }
} }
} }
impl MapperOuter<Location> for Tube { pub struct OuterCS(pub Tube);
fn outer_to_global(self, loc: Location) -> Location {
impl FlatCoordinateSystem<Location> for OuterCS {
fn flat_to_global(&self, loc: Location) -> Location {
todo!() todo!()
} }
// NB: имеет разрыв в области Inner на y = 0. // NB: имеет разрыв в области Inner на y = 0.
fn global_to_outer(self, loc: Location) -> Location { fn global_to_flat(&self, loc: Location) -> Location {
let inner = Rect { size: vec2(self.inner_radius, self.external_halflength) }; let inner = Rect { size: vec2(self.0.inner_radius, self.0.external_halflength) };
if inner.is_inside(loc.pos) { if inner.is_inside(loc.pos) {
let Vec2 { x: u, y } = loc.pos; // в основной СК let Vec2 { x: u, y } = loc.pos; // в основной СК
let v = self.v(y) + y.signum() * (self.external_halflength - self.internal_halflength); let v = self.0.v(y) + y.signum() * (self.0.external_halflength - self.0.internal_halflength);
Location { Location {
pos: vec2(u, v), // в плоском продолжении СК Outer на область Inner pos: vec2(u, v), // в плоском продолжении СК Outer на область Inner
rot: Mat2::from(self.sqrt_at(loc.pos)) * loc.rot, rot: Mat2::from(self.0.sqrt_at(loc.pos)) * loc.rot,
} }
} else { } else {
loc loc
@ -273,31 +274,31 @@ mod coords {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::{Location, Tube, MapperOuter}; use super::{Location, Tube, OuterCS, FlatCoordinateSystem};
use glam::{Mat2, vec2}; use glam::{Mat2, vec2};
use itertools_num::linspace; use itertools_num::linspace;
#[test] #[test]
fn test_mapper_outer() { fn test_mapper_outer() {
let mapper = Tube { let mapper = OuterCS(Tube {
inner_radius: 30.0, inner_radius: 30.0,
outer_radius: 50.0, outer_radius: 50.0,
internal_halflength: 100.0, internal_halflength: 100.0,
external_halflength: 300.0, external_halflength: 300.0,
}; });
// straight // straight
for x in linspace(-60., 60., 20) { for x in linspace(-60., 60., 20) {
for y in linspace(-320., 320., 20) { for y in linspace(-320., 320., 20) {
assert_eq!(mapper.global_to_outer(Location { pos: vec2(x, y), rot: Mat2::IDENTITY }).pos.x, x); assert_eq!(mapper.global_to_flat(Location { pos: vec2(x, y), rot: Mat2::IDENTITY }).pos.x, x);
} }
} }
// symmetrical // symmetrical
for x in linspace(0., 60., 20) { for x in linspace(0., 60., 20) {
for y in linspace(0., 320., 20) { for y in linspace(0., 320., 20) {
let pp = mapper.global_to_outer(Location { pos: vec2(x, y), rot: Mat2::IDENTITY }).pos; let pp = mapper.global_to_flat(Location { pos: vec2(x, y), rot: Mat2::IDENTITY }).pos;
let np = mapper.global_to_outer(Location { pos: vec2(-x, y), rot: Mat2::IDENTITY }).pos; let np = mapper.global_to_flat(Location { pos: vec2(-x, y), rot: Mat2::IDENTITY }).pos;
let pn = mapper.global_to_outer(Location { pos: vec2(x, -y), rot: Mat2::IDENTITY }).pos; let pn = mapper.global_to_flat(Location { pos: vec2(x, -y), rot: Mat2::IDENTITY }).pos;
let nn = mapper.global_to_outer(Location { pos: vec2(-x, -y), rot: Mat2::IDENTITY }).pos; let nn = mapper.global_to_flat(Location { pos: vec2(-x, -y), rot: Mat2::IDENTITY }).pos;
assert_eq!(np, vec2(-pp.x, pp.y)); assert_eq!(np, vec2(-pp.x, pp.y));
assert_eq!(pn, vec2(pp.x, -pp.y)); assert_eq!(pn, vec2(pp.x, -pp.y));
assert_eq!(nn, vec2(-pp.x, -pp.y)); assert_eq!(nn, vec2(-pp.x, -pp.y));
@ -306,18 +307,18 @@ mod coords {
// clean boundary // clean boundary
for x in linspace(50., 60., 20) { for x in linspace(50., 60., 20) {
for y in linspace(0., 320., 20) { for y in linspace(0., 320., 20) {
assert_eq!(mapper.global_to_outer(Location { pos: vec2(x, y), rot: Mat2::IDENTITY }).pos.y, y); assert_eq!(mapper.global_to_flat(Location { pos: vec2(x, y), rot: Mat2::IDENTITY }).pos.y, y);
} }
} }
for x in linspace(0., 60., 20) { for x in linspace(0., 60., 20) {
for y in linspace(300., 320., 20) { for y in linspace(300., 320., 20) {
assert_eq!(mapper.global_to_outer(Location { pos: vec2(x, y), rot: Mat2::IDENTITY }).pos.y, y); assert_eq!(mapper.global_to_flat(Location { pos: vec2(x, y), rot: Mat2::IDENTITY }).pos.y, y);
} }
} }
// accelerating // accelerating
for x in linspace(-29., 29., 20) { for x in linspace(-29., 29., 20) {
for y in linspace(1., 299., 20) { for y in linspace(1., 299., 20) {
let v = mapper.global_to_outer(Location { pos: vec2(x, y), rot: Mat2::IDENTITY }).pos.y; let v = mapper.global_to_flat(Location { pos: vec2(x, y), rot: Mat2::IDENTITY }).pos.y;
assert!(v > 200.0); assert!(v > 200.0);
assert!(v > y); assert!(v > y);
} }