use crate::geometry::Geometry;
use crate::indexed_access::IndexedAccess;
use crate::point::{Point, PointMut};
use geometry_tag::SegmentTag;
pub trait Segment: Geometry<Kind = SegmentTag> + IndexedAccess {}
pub fn segment_start<S: Segment>(s: &S) -> S::Point
where
S::Point: Default + PointMut,
{
materialise::<S, 0>(s)
}
pub fn segment_end<S: Segment>(s: &S) -> S::Point
where
S::Point: Default + PointMut,
{
materialise::<S, 1>(s)
}
pub(crate) fn materialise<G, const I: usize>(g: &G) -> G::Point
where
G: IndexedAccess,
G::Point: Default + PointMut,
{
let mut p: G::Point = Default::default();
match <G::Point as Point>::DIM {
0 => {}
1 => <Step<0, 1> as WriteDims<0, 1>>::run::<G, I>(g, &mut p),
2 => <Step<0, 2> as WriteDims<0, 2>>::run::<G, I>(g, &mut p),
3 => <Step<0, 3> as WriteDims<0, 3>>::run::<G, I>(g, &mut p),
4 => <Step<0, 4> as WriteDims<0, 4>>::run::<G, I>(g, &mut p),
_ => panic!("materialise: Point::DIM exceeds MAX_DIM (4)"),
}
p
}
struct Step<const D: usize, const N: usize>;
trait WriteDims<const D: usize, const N: usize>: sealed::Sealed<D, N> {
fn run<G, const I: usize>(g: &G, p: &mut G::Point)
where
G: IndexedAccess,
G::Point: PointMut;
}
mod sealed {
pub trait Sealed<const D: usize, const N: usize> {}
}
impl<const N: usize> sealed::Sealed<N, N> for Step<N, N> {}
impl<const N: usize> WriteDims<N, N> for Step<N, N> {
#[inline]
fn run<G, const I: usize>(_g: &G, _p: &mut G::Point)
where
G: IndexedAccess,
G::Point: PointMut,
{
}
}
macro_rules! impl_write_dims {
($d:expr, $n:expr) => {
impl sealed::Sealed<$d, $n> for Step<$d, $n> {}
impl WriteDims<$d, $n> for Step<$d, $n> {
#[inline]
fn run<G, const I: usize>(g: &G, p: &mut G::Point)
where
G: IndexedAccess,
G::Point: PointMut,
{
let v = g.get_indexed::<I, $d>();
<G::Point as PointMut>::set::<$d>(p, v);
<Step<{ $d + 1 }, $n> as WriteDims<{ $d + 1 }, $n>>::run::<G, I>(g, p);
}
}
};
}
impl_write_dims!(0, 1);
impl_write_dims!(0, 2);
impl_write_dims!(1, 2);
impl_write_dims!(0, 3);
impl_write_dims!(1, 3);
impl_write_dims!(2, 3);
impl_write_dims!(0, 4);
impl_write_dims!(1, 4);
impl_write_dims!(2, 4);
impl_write_dims!(3, 4);
#[cfg(test)]
mod tests {
use super::*;
use geometry_cs::Cartesian;
use geometry_tag::PointTag;
#[derive(Default)]
struct Xy {
x: f64,
y: f64,
}
impl Geometry for Xy {
type Kind = PointTag;
type Point = Self;
}
impl Point for Xy {
type Scalar = f64;
type Cs = Cartesian;
const DIM: usize = 2;
fn get<const D: usize>(&self) -> f64 {
if D == 0 { self.x } else { self.y }
}
}
impl PointMut for Xy {
fn set<const D: usize>(&mut self, v: f64) {
if D == 0 {
self.x = v;
} else {
self.y = v;
}
}
}
struct MySegment {
e: [[f64; 2]; 2],
}
impl Geometry for MySegment {
type Kind = SegmentTag;
type Point = Xy;
}
impl IndexedAccess for MySegment {
fn get_indexed<const I: usize, const D: usize>(&self) -> f64 {
self.e[I][D]
}
fn set_indexed<const I: usize, const D: usize>(&mut self, v: f64) {
self.e[I][D] = v;
}
}
impl Segment for MySegment {}
#[test]
fn segment_indexed_round_trip() {
let mut s = MySegment { e: [[0.0; 2]; 2] };
s.set_indexed::<0, 0>(7.0);
s.set_indexed::<1, 1>(8.0);
assert_eq!(s.get_indexed::<0, 0>().to_bits(), 7.0_f64.to_bits());
assert_eq!(s.get_indexed::<1, 1>().to_bits(), 8.0_f64.to_bits());
}
#[test]
fn segment_endpoints_materialise() {
let mut s = MySegment { e: [[0.0; 2]; 2] };
s.set_indexed::<0, 0>(1.0);
s.set_indexed::<0, 1>(2.0);
s.set_indexed::<1, 0>(3.0);
s.set_indexed::<1, 1>(4.0);
let a = segment_start(&s);
let b = segment_end(&s);
assert_eq!(a.get::<0>().to_bits(), 1.0_f64.to_bits());
assert_eq!(a.get::<1>().to_bits(), 2.0_f64.to_bits());
assert_eq!(b.get::<0>().to_bits(), 3.0_f64.to_bits());
assert_eq!(b.get::<1>().to_bits(), 4.0_f64.to_bits());
}
}