ncollide3d/shape/
segment.rs
1use crate::math::{Isometry, Point, Vector};
4use crate::shape::{ConvexPolygonalFeature, ConvexPolyhedron, FeatureId, SupportMap};
5#[cfg(feature = "dim2")]
6use crate::utils;
7use na::{self, RealField, Unit};
8use std::f64;
9use std::mem;
10
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13#[repr(C)]
14#[derive(PartialEq, Debug, Copy, Clone)]
15pub struct Segment<N: RealField + Copy> {
16 pub a: Point<N>,
18 pub b: Point<N>,
20}
21
22#[derive(PartialEq, Debug, Clone, Copy)]
24pub enum SegmentPointLocation<N: RealField + Copy> {
25 OnVertex(usize),
27 OnEdge([N; 2]),
29}
30
31impl<N: RealField + Copy> SegmentPointLocation<N> {
32 pub fn barycentric_coordinates(&self) -> [N; 2] {
34 let mut bcoords = [N::zero(); 2];
35
36 match self {
37 SegmentPointLocation::OnVertex(i) => bcoords[*i] = N::one(),
38 SegmentPointLocation::OnEdge(uv) => {
39 bcoords[0] = uv[0];
40 bcoords[1] = uv[1];
41 }
42 }
43
44 bcoords
45 }
46}
47
48impl<N: RealField + Copy> Segment<N> {
49 #[inline]
51 pub fn new(a: Point<N>, b: Point<N>) -> Segment<N> {
52 Segment { a, b }
53 }
54
55 pub fn from_array(arr: &[Point<N>; 2]) -> &Segment<N> {
57 unsafe { mem::transmute(arr) }
58 }
59}
60
61impl<N: RealField + Copy> Segment<N> {
62 #[inline]
64 #[deprecated(note = "use the `self.a` public field directly.")]
65 pub fn a(&self) -> &Point<N> {
66 &self.a
67 }
68
69 #[inline]
71 #[deprecated(note = "use the `self.b` public field directly.")]
72 pub fn b(&self) -> &Point<N> {
73 &self.b
74 }
75}
76
77impl<N: RealField + Copy> Segment<N> {
78 pub fn scaled_direction(&self) -> Vector<N> {
82 self.b - self.a
83 }
84
85 pub fn length(&self) -> N {
87 self.scaled_direction().norm()
88 }
89
90 pub fn swap(&mut self) {
92 mem::swap(&mut self.a, &mut self.b)
93 }
94
95 pub fn direction(&self) -> Option<Unit<Vector<N>>> {
100 Unit::try_new(self.scaled_direction(), N::default_epsilon())
101 }
102
103 #[cfg(feature = "dim2")]
105 pub fn scaled_normal(&self) -> Vector<N> {
106 let dir = self.scaled_direction();
107 Vector::new(dir.y, -dir.x)
108 }
109
110 #[cfg(feature = "dim2")]
112 pub fn normal(&self) -> Option<Unit<Vector<N>>> {
113 Unit::try_new(self.scaled_normal(), N::default_epsilon())
114 }
115
116 #[cfg(feature = "dim3")]
118 pub fn normal(&self) -> Option<Unit<Vector<N>>> {
119 None
120 }
121
122 pub fn transformed(&self, m: &Isometry<N>) -> Self {
124 Segment::new(m * self.a, m * self.b)
125 }
126
127 pub fn point_at(&self, location: &SegmentPointLocation<N>) -> Point<N> {
129 match *location {
130 SegmentPointLocation::OnVertex(0) => self.a,
131 SegmentPointLocation::OnVertex(1) => self.b,
132 SegmentPointLocation::OnEdge(bcoords) => {
133 self.a * bcoords[0] + self.b.coords * bcoords[1]
134 }
135 _ => panic!(),
136 }
137 }
138
139 pub fn tangent_cone_contains_dir(
141 &self,
142 feature: FeatureId,
143 m: &Isometry<N>,
144 dir: &Unit<Vector<N>>,
145 ) -> bool {
146 let ls_dir = m.inverse_transform_unit_vector(dir);
147
148 if let Some(direction) = self.direction() {
149 match feature {
150 FeatureId::Vertex(id) => {
151 let dot = ls_dir.dot(&direction);
152 if id == 0 {
153 dot >= N::one() - N::default_epsilon()
154 } else {
155 -dot >= N::one() - N::default_epsilon()
156 }
157 }
158 #[cfg(feature = "dim3")]
159 FeatureId::Edge(_) => {
160 ls_dir.dot(&direction).abs() >= N::one() - N::default_epsilon()
161 }
162 FeatureId::Face(id) => {
163 let mut dir = Vector::zeros();
164 if id == 0 {
165 dir[0] = direction[1];
166 dir[1] = -direction[0];
167 } else {
168 dir[0] = -direction[1];
169 dir[1] = direction[0];
170 }
171
172 ls_dir.dot(&dir) <= N::zero()
173 }
174 _ => true,
175 }
176 } else {
177 false
178 }
179 }
180}
181
182impl<N: RealField + Copy> SupportMap<N> for Segment<N> {
183 #[inline]
184 fn local_support_point(&self, dir: &Vector<N>) -> Point<N> {
185 if self.a.coords.dot(dir) > self.b.coords.dot(dir) {
186 self.a
187 } else {
188 self.b
189 }
190 }
191}
192
193impl<N: RealField + Copy> ConvexPolyhedron<N> for Segment<N> {
194 fn vertex(&self, id: FeatureId) -> Point<N> {
195 if id.unwrap_vertex() == 0 {
196 self.a
197 } else {
198 self.b
199 }
200 }
201
202 #[cfg(feature = "dim3")]
203 fn edge(&self, _: FeatureId) -> (Point<N>, Point<N>, FeatureId, FeatureId) {
204 (self.a, self.b, FeatureId::Vertex(0), FeatureId::Vertex(1))
205 }
206
207 #[cfg(feature = "dim3")]
208 fn face(&self, _: FeatureId, _: &mut ConvexPolygonalFeature<N>) {
209 panic!("A segment does not have any face in dimensions higher than 2.")
210 }
211
212 #[cfg(feature = "dim2")]
213 fn face(&self, id: FeatureId, face: &mut ConvexPolygonalFeature<N>) {
214 face.clear();
215
216 if let Some(normal) = utils::ccw_face_normal([&self.a, &self.b]) {
217 face.set_feature_id(id);
218
219 match id.unwrap_face() {
220 0 => {
221 face.push(self.a, FeatureId::Vertex(0));
222 face.push(self.b, FeatureId::Vertex(1));
223 face.set_normal(normal);
224 }
225 1 => {
226 face.push(self.b, FeatureId::Vertex(1));
227 face.push(self.a, FeatureId::Vertex(0));
228 face.set_normal(-normal);
229 }
230 _ => unreachable!(),
231 }
232 } else {
233 face.push(self.a, FeatureId::Vertex(0));
234 face.set_feature_id(FeatureId::Vertex(0));
235 }
236 }
237
238 fn feature_normal(&self, feature: FeatureId) -> Unit<Vector<N>> {
239 if let Some(direction) = self.direction() {
240 match feature {
241 FeatureId::Vertex(id) => {
242 if id == 0 {
243 direction
244 } else {
245 -direction
246 }
247 }
248 #[cfg(feature = "dim3")]
249 FeatureId::Edge(_) => {
250 let iamin = direction.iamin();
251 let mut normal = Vector::zeros();
252 normal[iamin] = N::one();
253 normal -= *direction * direction[iamin];
254 Unit::new_normalize(normal)
255 }
256 FeatureId::Face(id) => {
257 let mut dir = Vector::zeros();
258 if id == 0 {
259 dir[0] = direction[1];
260 dir[1] = -direction[0];
261 } else {
262 dir[0] = -direction[1];
263 dir[1] = direction[0];
264 }
265 Unit::new_unchecked(dir)
266 }
267 _ => panic!("Invalid feature ID: {:?}", feature),
268 }
269 } else {
270 Vector::y_axis()
271 }
272 }
273
274 #[cfg(feature = "dim2")]
275 fn support_face_toward(
276 &self,
277 m: &Isometry<N>,
278 dir: &Unit<Vector<N>>,
279 face: &mut ConvexPolygonalFeature<N>,
280 ) {
281 let seg_dir = self.scaled_direction();
282
283 if dir.perp(&seg_dir) >= na::zero() {
284 self.face(FeatureId::Face(0), face);
285 } else {
286 self.face(FeatureId::Face(1), face);
287 }
288 face.transform_by(m)
289 }
290
291 #[cfg(feature = "dim3")]
292 fn support_face_toward(
293 &self,
294 m: &Isometry<N>,
295 _: &Unit<Vector<N>>,
296 face: &mut ConvexPolygonalFeature<N>,
297 ) {
298 face.clear();
299 face.push(self.a, FeatureId::Vertex(0));
300 face.push(self.b, FeatureId::Vertex(1));
301 face.push_edge_feature_id(FeatureId::Edge(0));
302 face.set_feature_id(FeatureId::Edge(0));
303 face.transform_by(m)
304 }
305
306 fn support_feature_toward(
307 &self,
308 transform: &Isometry<N>,
309 dir: &Unit<Vector<N>>,
310 eps: N,
311 face: &mut ConvexPolygonalFeature<N>,
312 ) {
313 face.clear();
314 let seg = self.transformed(transform);
315 let ceps = eps.sin();
316
317 if let Some(seg_dir) = seg.direction() {
318 let cang = dir.dot(&seg_dir);
319
320 if cang > ceps {
321 face.set_feature_id(FeatureId::Vertex(1));
322 face.push(seg.b, FeatureId::Vertex(1));
323 } else if cang < -ceps {
324 face.set_feature_id(FeatureId::Vertex(0));
325 face.push(seg.a, FeatureId::Vertex(0));
326 } else {
327 #[cfg(feature = "dim3")]
328 {
329 face.push(seg.a, FeatureId::Vertex(0));
330 face.push(seg.b, FeatureId::Vertex(1));
331 face.push_edge_feature_id(FeatureId::Edge(0));
332 face.set_feature_id(FeatureId::Edge(0));
333 }
334 #[cfg(feature = "dim2")]
335 {
336 if dir.perp(&seg_dir) >= na::zero() {
337 seg.face(FeatureId::Face(0), face);
338 } else {
339 seg.face(FeatureId::Face(1), face);
340 }
341 }
342 }
343 }
344 }
345
346 fn support_feature_id_toward(&self, local_dir: &Unit<Vector<N>>) -> FeatureId {
347 if let Some(seg_dir) = self.direction() {
348 let eps: N = na::convert(f64::consts::PI / 180.0);
349 let seps = eps.sin();
350 let dot = seg_dir.dot(local_dir.as_ref());
351
352 if dot <= seps {
353 #[cfg(feature = "dim2")]
354 {
355 if local_dir.perp(seg_dir.as_ref()) >= na::zero() {
356 FeatureId::Face(0)
357 } else {
358 FeatureId::Face(1)
359 }
360 }
361 #[cfg(feature = "dim3")]
362 {
363 FeatureId::Edge(0)
364 }
365 } else if dot >= na::zero() {
366 FeatureId::Vertex(1)
367 } else {
368 FeatureId::Vertex(0)
369 }
370 } else {
371 FeatureId::Vertex(0)
372 }
373 }
374}