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