1use std::iter::FromIterator;
6
7use euclid::{Point3D, RigidTransform3D, Rotation3D, Vector3D};
8use serde::{Deserialize, Serialize};
9
10use crate::{ApiSpace, Native, Space};
11
12#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
13pub struct Ray<Space> {
15 pub origin: Vector3D<f32, Space>,
17 pub direction: Vector3D<f32, Space>,
19}
20
21#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
22pub enum EntityType {
24 Point,
25 Plane,
26 Mesh,
27}
28
29#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
30pub struct HitTestSource {
32 pub id: HitTestId,
33 pub space: Space,
34 pub ray: Ray<ApiSpace>,
35 pub types: EntityTypes,
36}
37
38#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
39pub struct HitTestId(pub u32);
40
41#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
42pub struct EntityTypes {
44 pub point: bool,
45 pub plane: bool,
46 pub mesh: bool,
47}
48
49#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
50pub struct HitTestResult {
51 pub id: HitTestId,
52 pub space: RigidTransform3D<f32, HitTestSpace, Native>,
53}
54
55#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
56pub struct HitTestSpace;
58
59#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
60pub struct Triangle {
61 pub first: Point3D<f32, Native>,
62 pub second: Point3D<f32, Native>,
63 pub third: Point3D<f32, Native>,
64}
65
66impl EntityTypes {
67 pub fn is_type(self, ty: EntityType) -> bool {
68 match ty {
69 EntityType::Point => self.point,
70 EntityType::Plane => self.plane,
71 EntityType::Mesh => self.mesh,
72 }
73 }
74
75 pub fn add_type(&mut self, ty: EntityType) {
76 match ty {
77 EntityType::Point => self.point = true,
78 EntityType::Plane => self.plane = true,
79 EntityType::Mesh => self.mesh = true,
80 }
81 }
82}
83
84impl FromIterator<EntityType> for EntityTypes {
85 fn from_iter<T>(iter: T) -> Self
86 where
87 T: IntoIterator<Item = EntityType>,
88 {
89 iter.into_iter().fold(Default::default(), |mut acc, e| {
90 acc.add_type(e);
91 acc
92 })
93 }
94}
95
96impl Triangle {
97 pub fn intersect(
99 self,
100 ray: Ray<Native>,
101 ) -> Option<RigidTransform3D<f32, HitTestSpace, Native>> {
102 let Triangle {
103 first: v0,
104 second: v1,
105 third: v2,
106 } = self;
107
108 let edge1 = v1 - v0;
109 let edge2 = v2 - v0;
110
111 let h = ray.direction.cross(edge2);
112 let a = edge1.dot(h);
113 if a > -f32::EPSILON && a < f32::EPSILON {
114 return None;
116 }
117
118 let f = 1. / a;
119
120 let s = ray.origin - v0.to_vector();
121
122 let u = f * s.dot(h);
124 if !(0. ..=1.).contains(&u) {
126 return None;
128 }
129
130 let q = s.cross(edge1);
131 let v = f * ray.direction.dot(q);
133
134 if v < 0. || u + v > 1. {
137 return None;
139 }
140
141 let t = f * edge2.dot(q);
142
143 if t > f32::EPSILON {
144 let origin = ray.origin + ray.direction * t;
145
146 let normal = edge1.cross(edge2).normalize();
150 let y = Vector3D::new(0., 1., 0.);
151 let dot = normal.dot(y);
152 let rotation = if dot > -f32::EPSILON && dot < f32::EPSILON {
153 Rotation3D::identity()
157 } else {
158 let axis = normal.cross(y);
159 let cos = normal.dot(y);
160 Rotation3D::quaternion(axis.x, axis.y, axis.z, cos)
164 };
165
166 return Some(RigidTransform3D::new(rotation, origin));
167 }
168
169 None
171 }
172}