Add shapes

This commit is contained in:
numzero 2024-05-26 23:56:33 +03:00
parent 664077b46e
commit 44efe70348

View File

@ -222,11 +222,164 @@ impl Metric for Coil {
}
}
#[derive(Debug, PartialEq)]
struct Ray {
pos: Vec2,
dir: Vec2,
}
mod basic_shapes {
use glam::{Vec2, vec2};
use crate::{Ray, shape};
pub struct Rect {
pub size: Vec2,
}
impl Rect {
/// Отражает луч, чтобы все координаты направления были положительны (допустимо благодаря симметрии Rect).
fn flip_ray(ray: Ray) -> Ray {
Ray { pos: ray.pos * ray.dir.signum(), dir: ray.dir.abs() }
}
}
impl shape::Shape for Rect {
fn is_inside(&self, pt: Vec2) -> bool {
pt.abs().cmplt(self.size).all()
}
fn trace_into(&self, ray: Ray) -> Option<f32> {
let ray = Self::flip_ray(ray);
// ray.pos.x + t * ray.dir.x = size.x
let ts = (-self.size - ray.pos) / ray.dir;
let t = ts.max_element();
let pt = ray.pos + t * ray.dir;
if t < 0.0 { return None; }
if pt.cmpgt(self.size).any() { return None; }
Some(t)
}
fn trace_out_of(&self, ray: Ray) -> Option<f32> {
let ray = Self::flip_ray(ray);
// ray.pos.x + t * ray.dir.x = +size.x
let ts = (self.size - ray.pos) / ray.dir;
let t = ts.min_element();
Some(t)
}
fn visualise(&self) -> Vec<Vec2> {
vec![vec2(-self.size.x, -self.size.y), vec2(self.size.x, -self.size.y), vec2(self.size.x, self.size.y), vec2(-self.size.x, self.size.y)]
}
}
#[cfg(test)]
use crate::shape::Shape;
#[test]
fn test_rect() {
assert_eq!(Rect::flip_ray(Ray { pos: vec2(2.0, 3.0), dir: vec2(4.0, 5.0) }), Ray { pos: vec2(2.0, 3.0), dir: vec2(4.0, 5.0) });
assert_eq!(Rect::flip_ray(Ray { pos: vec2(2.0, 3.0), dir: vec2(-4.0, 5.0) }), Ray { pos: vec2(-2.0, 3.0), dir: vec2(4.0, 5.0) });
assert_eq!(Rect::flip_ray(Ray { pos: vec2(2.0, 3.0), dir: vec2(4.0, -5.0) }), Ray { pos: vec2(2.0, -3.0), dir: vec2(4.0, 5.0) });
assert_eq!(Rect::flip_ray(Ray { pos: vec2(2.0, 3.0), dir: vec2(4.0, 0.0) }), Ray { pos: vec2(2.0, 3.0), dir: vec2(4.0, 0.0) });
let r = Rect { size: vec2(2.0, 3.0) };
assert_eq!(r.trace_into(Ray { pos: vec2(3.0, 3.0), dir: vec2(1.0, 1.0) }), None);
assert_eq!(r.trace_into(Ray { pos: vec2(-3.0, 2.0), dir: vec2(1.0, 0.0) }), Some(1.0));
assert_eq!(r.trace_into(Ray { pos: vec2(-3.0, 2.0), dir: vec2(-1.0, 0.0) }), None);
assert_eq!(r.trace_into(Ray { pos: vec2(-3.0, 1.0), dir: vec2(2.0, 2.0) }), Some(0.5));
assert_eq!(r.trace_into(Ray { pos: vec2(-3.0, 2.1), dir: vec2(2.0, 2.0) }), None);
assert_eq!(r.trace_into(Ray { pos: vec2(2.0, 3.0), dir: vec2(1.0, 1.0) }), None);
assert_eq!(r.trace_into(Ray { pos: vec2(-2.0, 3.0), dir: vec2(-1.0, 1.0) }), None);
assert_eq!(r.trace_into(Ray { pos: vec2(2.0, 3.0), dir: vec2(-1.0, -1.0) }), Some(0.0));
assert_eq!(r.trace_into(Ray { pos: vec2(2.0, -3.0), dir: vec2(-1.0, 1.0) }), Some(0.0));
assert_eq!(r.trace_out_of(Ray { pos: vec2(0.0, 0.0), dir: vec2(1.0, 1.0) }), Some(2.0));
assert_eq!(r.trace_out_of(Ray { pos: vec2(0.0, 0.0), dir: vec2(0.0, 1.0) }), Some(3.0));
assert_eq!(r.trace_out_of(Ray { pos: vec2(0.0, 1.0), dir: vec2(0.0, -1.0) }), Some(4.0));
assert_eq!(r.trace_out_of(Ray { pos: vec2(1.0, 1.0), dir: vec2(0.0, -1.0) }), Some(4.0));
assert_eq!(r.trace_out_of(Ray { pos: vec2(2.0, 3.0), dir: vec2(1.0, 1.0) }), Some(0.0));
}
}
mod shape {
use glam::{Affine2, Vec2};
use crate::Ray;
pub trait Shape {
fn is_inside(&self, pt: Vec2) -> bool;
/// Ищет ближайшее пересечение луча с границей в направлении внутрь контура. Возвращает расстояние (в ray.dir).
fn trace_into(&self, ray: Ray) -> Option<f32>;
/// Ищет ближайшее пересечение луча с границей в направлении вовне контура. Возвращает расстояние (в ray.dir).
fn trace_out_of(&self, ray: Ray) -> Option<f32>;
/// Возвращает визуальное представление контура, для отладки.
fn visualise(&self) -> Vec<Vec2>;
}
pub struct MovedShape<S: Shape> {
pub shape: S,
pub transform: Affine2, // transform(координаты контура) = 0
}
impl<S: Shape> MovedShape<S> {
fn pt_to_inner(&self, pt: Vec2) -> Vec2 {
self.transform.transform_point2(pt)
}
fn pt_to_outer(&self, pt: Vec2) -> Vec2 {
self.transform.inverse().transform_point2(pt)
}
fn vec_to_inner(&self, vec: Vec2) -> Vec2 {
self.transform.transform_vector2(vec)
}
fn vec_to_outer(&self, vec: Vec2) -> Vec2 {
self.transform.inverse().transform_vector2(vec)
}
fn ray_to_inner(&self, ray: Ray) -> Ray {
Ray { pos: self.pt_to_inner(ray.pos), dir: self.vec_to_inner(ray.dir) }
}
fn ray_to_outer(&self, ray: Ray) -> Ray {
Ray { pos: self.pt_to_outer(ray.pos), dir: self.vec_to_outer(ray.dir) }
}
}
impl<S: Shape> Shape for MovedShape<S> {
fn is_inside(&self, pt: Vec2) -> bool {
self.shape.is_inside(self.pt_to_inner(pt))
}
fn trace_into(&self, ray: Ray) -> Option<f32> {
self.shape.trace_into(self.ray_to_inner(ray))
}
fn trace_out_of(&self, ray: Ray) -> Option<f32> {
self.shape.trace_out_of(self.ray_to_inner(ray))
}
fn visualise(&self) -> Vec<Vec2> {
self.shape.visualise().iter().map(|pt| self.pt_to_outer(*pt)).collect()
}
}
pub struct InvertedShape<S: Shape> {
pub shape: S,
}
impl<S: Shape> Shape for InvertedShape<S> {
fn is_inside(&self, pt: Vec2) -> bool {
!self.shape.is_inside(pt)
}
fn trace_into(&self, ray: Ray) -> Option<f32> {
self.shape.trace_out_of(ray)
}
fn trace_out_of(&self, ray: Ray) -> Option<f32> {
self.shape.trace_into(ray)
}
fn visualise(&self) -> Vec<Vec2> {
self.shape.visualise()
}
}
}
struct Grid {
hlines: Vec<f32>,
vlines: Vec<f32>,