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