Implement missing InnerCS APIs, and test them

This commit is contained in:
numzero 2024-06-30 11:52:37 +03:00
parent 382ce16822
commit 95d46b24c8

View File

@ -206,6 +206,8 @@ mod coords {
fn flat_to_global(&self, pos: Vec2) -> Vec2 { fn flat_to_global(&self, pos: Vec2) -> Vec2 {
vec2(pos.x, self.0.y(pos.y)) vec2(pos.x, self.0.y(pos.y))
} }
// Работает только при |pos.x| ≤ inner_radius или |pos.y| ≥ external_halflength.
fn global_to_flat(&self, pos: Vec2) -> Vec2 { fn global_to_flat(&self, pos: Vec2) -> Vec2 {
vec2(pos.x, self.0.v(pos.y)) vec2(pos.x, self.0.v(pos.y))
} }
@ -229,13 +231,15 @@ mod coords {
impl FlatCoordinateSystem<Location> for InnerCS { impl FlatCoordinateSystem<Location> for InnerCS {
fn flat_to_global(&self, loc: Location) -> Location { fn flat_to_global(&self, loc: Location) -> Location {
todo!() Location {
pos: self.flat_to_global(loc.pos),
rot: Mat2::from(self.0.sqrt_at(loc.pos).inverse()) * loc.rot,
}
} }
// NB: не работает для частей Outer с |y| < external_halflength. Но они и не нужны.
fn global_to_flat(&self, loc: Location) -> Location { fn global_to_flat(&self, loc: Location) -> Location {
Location { Location {
pos: vec2(loc.pos.x, self.0.v(loc.pos.y)), // в плоской СК для Inner или её продолжении на Outer pos: self.global_to_flat(loc.pos), // в плоской СК для Inner или её продолжении на Outer
rot: Mat2::from(self.0.sqrt_at(loc.pos)) * loc.rot, rot: Mat2::from(self.0.sqrt_at(loc.pos)) * loc.rot,
} }
} }
@ -282,10 +286,61 @@ mod coords {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::{Location, Tube, OuterCS, FlatCoordinateSystem}; use super::*;
use glam::{Mat2, vec2}; use approx::{AbsDiffEq, assert_abs_diff_eq};
use glam::{Mat2, vec2, Vec2};
use itertools_num::linspace; use itertools_num::linspace;
fn test_flat_region(region: &impl FlatRegion, range_global: (Vec2, Vec2), range_flat: (Vec2, Vec2)) {
const ε: f32 = 1e-3;
macro_rules! assert_eq_at {
($at: expr, $left: expr, $right: expr) => {
let at = $at;
let left = $left;
let right = $right;
assert!(left.abs_diff_eq(right, ε), "Assertion failed at {at}:\n left: {left} = {}\n right: {right} = {}", stringify!($left), stringify!($right));
};
}
fn check_range(name_a: &str, a: Vec2, range_a: (Vec2, Vec2), name_b: &str, b: Vec2, range_b: (Vec2, Vec2)) {
assert!(b.cmpge(range_b.0 - ε).all() && b.cmple(range_b.1 + ε).all(), "Assertion failed:\nAt {name_a}: {a}, from range: {range_a:?}\nGot {name_b}: {b}, which is out of range {range_b:?}");
if a.x.abs_diff_eq(&range_a.0.x, ε) { assert_abs_diff_eq!(b.x, range_b.0.x, epsilon=ε); }
if a.y.abs_diff_eq(&range_a.0.y, ε) { assert_abs_diff_eq!(b.y, range_b.0.y, epsilon=ε); }
if a.x.abs_diff_eq(&range_a.1.x, ε) { assert_abs_diff_eq!(b.x, range_b.1.x, epsilon=ε); }
if a.y.abs_diff_eq(&range_a.1.y, ε) { assert_abs_diff_eq!(b.y, range_b.1.y, epsilon=ε); }
}
for x in linspace(range_global.0.x, range_global.1.x, 20) {
for y in linspace(range_global.0.y, range_global.1.y, 20) {
let pos_global = vec2(x, y);
let pos_flat = region.global_to_flat(pos_global);
check_range("global", pos_global, range_global, "flat", pos_flat, range_flat);
assert_eq_at!(pos_global, region.global_to_flat(Location { pos: pos_global, rot: Mat2::IDENTITY }).pos, pos_flat);
assert_eq_at!(pos_global, region.flat_to_global(pos_flat), pos_global);
}
}
for x in linspace(range_flat.0.x, range_flat.1.x, 20) {
for y in linspace(range_flat.0.y, range_flat.1.y, 20) {
let pos_flat = vec2(x, y);
let pos_global = region.flat_to_global(pos_flat);
check_range("flat", pos_flat, range_flat, "global", pos_global, range_global);
assert_eq_at!(pos_flat, region.flat_to_global(Location { pos: pos_flat, rot: Mat2::IDENTITY }).pos, pos_global);
assert_eq_at!(pos_flat, region.global_to_flat(pos_global), pos_flat);
}
}
}
#[test]
fn test_mapper_inner() {
let mapper = InnerCS(Tube {
inner_radius: 30.0,
outer_radius: 50.0,
internal_halflength: 100.0,
external_halflength: 300.0,
});
test_flat_region(&mapper, (vec2(-30.0, -300.0), vec2(30.0, 300.0)), (vec2(-30.0, -100.0), vec2(30.0, 100.0)));
test_flat_region(&mapper, (vec2(-60.0, -400.0), vec2(60.0, -300.0)), (vec2(-60.0, -200.0), vec2(60.0, -100.0)));
test_flat_region(&mapper, (vec2(-60.0, 300.0), vec2(60.0, 400.0)), (vec2(-60.0, 100.0), vec2(60.0, 200.0)));
}
#[test] #[test]
fn test_mapper_outer() { fn test_mapper_outer() {
let mapper = OuterCS(Tube { let mapper = OuterCS(Tube {