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