1#[cfg(feature = "dim3")]
4use na;
5use na::{DVector, DVectorSlice, RealField, Unit};
6use std::ops::Neg;
7
8use crate::math::{AngularVector, Force, Point, Rotation, Vector};
9use crate::object::{Body, BodyHandle, BodyPart, BodyPartHandle};
10use crate::solver::{
11 BilateralConstraint, BilateralGroundConstraint, ConstraintGeometry, GenericNonlinearConstraint,
12 ImpulseLimits, IntegrationParameters, LinearConstraints,
13};
14
15#[derive(Copy, Clone, Debug)]
17pub enum ForceDirection<N: RealField + Copy> {
18 Linear(Unit<Vector<N>>),
20 Angular(Unit<AngularVector<N>>),
22}
23
24impl<N: RealField + Copy> ForceDirection<N> {
25 #[inline]
27 pub fn at_point(&self, pt: &Point<N>) -> Force<N> {
28 match self {
29 ForceDirection::Linear(normal) => Force::linear_at_point(**normal, pt),
30 ForceDirection::Angular(axis) => Force::torque_from_vector(**axis),
31 }
32 }
33}
34
35impl<N: RealField + Copy> Neg for ForceDirection<N> {
36 type Output = Self;
37
38 #[inline]
39 fn neg(self) -> Self {
40 match self {
41 ForceDirection::Linear(n) => ForceDirection::Linear(-n),
42 ForceDirection::Angular(a) => ForceDirection::Angular(-a),
43 }
44 }
45}
46
47#[inline]
53pub fn constraint_pair_geometry<N: RealField + Copy, B: ?Sized + Body<N>, H: BodyHandle>(
54 body1: &B,
55 part1: &dyn BodyPart<N>,
56 handle1: BodyPartHandle<H>,
57 body2: &B,
58 part2: &dyn BodyPart<N>,
59 handle2: BodyPartHandle<H>,
60 center1: &Point<N>,
61 center2: &Point<N>,
62 dir: &ForceDirection<N>,
63 ground_j_id: &mut usize,
64 j_id: &mut usize,
65 jacobians: &mut [N],
66 ext_vels1: Option<&DVectorSlice<N>>,
67 ext_vels2: Option<&DVectorSlice<N>>,
68 mut out_vel: Option<&mut N>,
69) -> ConstraintGeometry<N> {
70 let mut res = ConstraintGeometry::new();
71
72 res.ndofs1 = body1.status_dependent_ndofs();
73 res.ndofs2 = body2.status_dependent_ndofs();
74
75 let out_j_id;
76 if res.ndofs1 == 0 || res.ndofs2 == 0 {
77 res.j_id1 = *ground_j_id;
78 out_j_id = ground_j_id;
79 } else {
80 res.j_id1 = *j_id;
81 out_j_id = j_id;
82 }
83
84 res.j_id2 = res.j_id1 + res.ndofs1;
85 res.wj_id1 = res.j_id2 + res.ndofs2;
86 res.wj_id2 = res.wj_id1 + res.ndofs1;
87
88 let mut inv_r = N::zero();
89
90 body1.fill_constraint_geometry(
91 part1,
92 res.ndofs1,
93 center1,
94 dir,
95 res.j_id1,
96 res.wj_id1,
97 jacobians,
98 &mut inv_r,
99 ext_vels1,
100 out_vel.as_mut().map(|v| &mut **v),
103 );
104
105 body2.fill_constraint_geometry(
106 part2,
107 res.ndofs2,
108 center2,
109 &dir.neg(),
110 res.j_id2,
111 res.wj_id2,
112 jacobians,
113 &mut inv_r,
114 ext_vels2,
115 out_vel,
116 );
117
118 if handle1.0 == handle2.0 {
119 let j1 = DVectorSlice::from_slice(&jacobians[res.j_id1..], res.ndofs1);
120 let j2 = DVectorSlice::from_slice(&jacobians[res.j_id2..], res.ndofs2);
121 let invm_j1 = DVectorSlice::from_slice(&jacobians[res.wj_id1..], res.ndofs1);
122 let invm_j2 = DVectorSlice::from_slice(&jacobians[res.wj_id2..], res.ndofs2);
123
124 inv_r += j2.dot(&invm_j1) + j1.dot(&invm_j2);
125 }
126
127 if !inv_r.is_zero() {
128 res.r = N::one() / inv_r;
129 } else {
130 res.r = N::one()
131 }
132
133 *out_j_id += (res.ndofs1 + res.ndofs2) * 2;
134 res
135}
136
137#[inline]
140pub fn constraints_are_ground_constraints<N: RealField + Copy, B: ?Sized + Body<N>>(
141 body1: &B,
142 body2: &B,
143) -> bool {
144 body1.status_dependent_ndofs() == 0 || body2.status_dependent_ndofs() == 0
145}
146
147#[inline(always)]
149pub fn split_ext_vels<'a, N: RealField + Copy, B: ?Sized + Body<N>>(
150 body1: &B,
151 body2: &B,
152 assembly_id1: usize,
153 assembly_id2: usize,
154 ext_vels: &'a DVector<N>,
155) -> (DVectorSlice<'a, N>, DVectorSlice<'a, N>) {
156 let ndofs1 = body1.status_dependent_ndofs();
157 let ndofs2 = body2.status_dependent_ndofs();
158 (
159 ext_vels.rows(assembly_id1, ndofs1),
160 ext_vels.rows(assembly_id2, ndofs2),
161 )
162}
163
164pub fn cancel_relative_linear_velocity_wrt_axis<
168 N: RealField + Copy,
169 B: ?Sized + Body<N>,
170 H: BodyHandle,
171 Id,
172>(
173 body1: &B,
174 part1: &dyn BodyPart<N>,
175 handle1: BodyPartHandle<H>,
176 body2: &B,
177 part2: &dyn BodyPart<N>,
178 handle2: BodyPartHandle<H>,
179 assembly_id1: usize,
180 assembly_id2: usize,
181 anchor1: &Point<N>,
182 anchor2: &Point<N>,
183 axis: &Unit<Vector<N>>,
184 ext_vels: &DVector<N>,
185 impulse: N,
186 impulse_id: Id,
187 ground_j_id: &mut usize,
188 j_id: &mut usize,
189 jacobians: &mut [N],
190 constraints: &mut LinearConstraints<N, Id>,
191) {
192 let limits = ImpulseLimits::Independent {
193 min: -N::max_value(),
194 max: N::max_value(),
195 };
196
197 let force = ForceDirection::Linear(*axis);
198 let mut rhs = N::zero();
199 let (ext_vels1, ext_vels2) = split_ext_vels(body1, body2, assembly_id1, assembly_id2, ext_vels);
200
201 let geom = constraint_pair_geometry(
202 body1,
203 part1,
204 handle1,
205 body2,
206 part2,
207 handle2,
208 anchor1,
209 anchor2,
210 &force,
211 ground_j_id,
212 j_id,
213 jacobians,
214 Some(&ext_vels1),
215 Some(&ext_vels2),
216 Some(&mut rhs),
217 );
218
219 if geom.ndofs1 == 0 || geom.ndofs2 == 0 {
220 constraints
221 .bilateral_ground
222 .push(BilateralGroundConstraint::new(
223 geom,
224 assembly_id1,
225 assembly_id2,
226 limits,
227 rhs,
228 impulse,
229 impulse_id,
230 ));
231 } else {
232 constraints.bilateral.push(BilateralConstraint::new(
233 geom,
234 assembly_id1,
235 assembly_id2,
236 limits,
237 rhs,
238 impulse,
239 impulse_id,
240 ));
241 }
242}
243
244pub fn cancel_relative_linear_velocity<N: RealField + Copy, B: ?Sized + Body<N>, H: BodyHandle>(
248 body1: &B,
249 part1: &dyn BodyPart<N>,
250 handle1: BodyPartHandle<H>,
251 body2: &B,
252 part2: &dyn BodyPart<N>,
253 handle2: BodyPartHandle<H>,
254 assembly_id1: usize,
255 assembly_id2: usize,
256 anchor1: &Point<N>,
257 anchor2: &Point<N>,
258 ext_vels: &DVector<N>,
259 impulses: &Vector<N>,
260 impulse_id: usize,
261 ground_j_id: &mut usize,
262 j_id: &mut usize,
263 jacobians: &mut [N],
264 constraints: &mut LinearConstraints<N, usize>,
265) {
266 #[cfg(feature = "dim2")]
267 let canonical_basis = [Vector::x(), Vector::y()];
268 #[cfg(feature = "dim3")]
269 let canonical_basis = [Vector::x(), Vector::y(), Vector::z()];
270
271 for (i, dir) in canonical_basis.iter().enumerate() {
272 cancel_relative_linear_velocity_wrt_axis(
273 body1,
274 part1,
275 handle1,
276 body2,
277 part2,
278 handle2,
279 assembly_id1,
280 assembly_id2,
281 anchor1,
282 anchor2,
283 &Unit::new_unchecked(*dir),
284 ext_vels,
285 impulses[i],
286 impulse_id + i,
287 ground_j_id,
288 j_id,
289 jacobians,
290 constraints,
291 );
292 }
293}
294
295pub fn cancel_relative_translation_wrt_axis<N: RealField + Copy, B: ?Sized + Body<N>, H: BodyHandle>(
299 parameters: &IntegrationParameters<N>,
300 body1: &B,
301 part1: &dyn BodyPart<N>,
302 handle1: BodyPartHandle<H>,
303 body2: &B,
304 part2: &dyn BodyPart<N>,
305 handle2: BodyPartHandle<H>,
306 anchor1: &Point<N>,
307 anchor2: &Point<N>,
308 axis: &Unit<Vector<N>>,
309 jacobians: &mut [N],
310) -> Option<GenericNonlinearConstraint<N, H>> {
311 let mut depth = axis.dot(&(anchor2 - anchor1));
312
313 let force = if depth < N::zero() {
314 depth = -depth;
315 ForceDirection::Linear(-*axis)
316 } else {
317 ForceDirection::Linear(*axis)
318 };
319
320 if depth > parameters.allowed_linear_error {
321 let mut j_id = 0;
322 let mut ground_j_id = 0;
323
324 let geom = constraint_pair_geometry(
325 body1,
326 part1,
327 handle1,
328 body2,
329 part2,
330 handle2,
331 anchor1,
332 anchor2,
333 &force,
334 &mut ground_j_id,
335 &mut j_id,
336 jacobians,
337 None,
338 None,
339 None,
340 );
341
342 let rhs = -depth;
343 let constraint = GenericNonlinearConstraint::new(
344 handle1,
345 Some(handle2),
346 false,
347 geom.ndofs1,
348 geom.ndofs2,
349 geom.wj_id1,
350 geom.wj_id2,
351 rhs,
352 geom.r,
353 );
354
355 Some(constraint)
356 } else {
357 None
358 }
359}
360
361pub fn cancel_relative_translation<N: RealField + Copy, B: ?Sized + Body<N>, H: BodyHandle>(
365 parameters: &IntegrationParameters<N>,
366 body1: &B,
367 part1: &dyn BodyPart<N>,
368 handle1: BodyPartHandle<H>,
369 body2: &B,
370 part2: &dyn BodyPart<N>,
371 handle2: BodyPartHandle<H>,
372 anchor1: &Point<N>,
373 anchor2: &Point<N>,
374 jacobians: &mut [N],
375) -> Option<GenericNonlinearConstraint<N, H>> {
376 let error = anchor2 - anchor1;
377
378 if let Some((dir, depth)) = Unit::try_new_and_get(error, parameters.allowed_linear_error) {
379 let mut j_id = 0;
380 let mut ground_j_id = 0;
381
382 let geom = constraint_pair_geometry(
383 body1,
384 part1,
385 handle1,
386 body2,
387 part2,
388 handle2,
389 anchor1,
390 anchor2,
391 &ForceDirection::Linear(dir),
392 &mut ground_j_id,
393 &mut j_id,
394 jacobians,
395 None,
396 None,
397 None,
398 );
399
400 let rhs = -depth;
401 let constraint = GenericNonlinearConstraint::new(
402 handle1,
403 Some(handle2),
404 false,
405 geom.ndofs1,
406 geom.ndofs2,
407 geom.wj_id1,
408 geom.wj_id2,
409 rhs,
410 geom.r,
411 );
412
413 Some(constraint)
414 } else {
415 None
416 }
417}
418
419pub fn cancel_relative_angular_velocity_wrt_axis<
423 N: RealField + Copy,
424 B: ?Sized + Body<N>,
425 H: BodyHandle,
426 Id,
427>(
428 body1: &B,
429 part1: &dyn BodyPart<N>,
430 handle1: BodyPartHandle<H>,
431 body2: &B,
432 part2: &dyn BodyPart<N>,
433 handle2: BodyPartHandle<H>,
434 assembly_id1: usize,
435 assembly_id2: usize,
436 anchor1: &Point<N>,
437 anchor2: &Point<N>,
438 axis: &Unit<AngularVector<N>>,
439 ext_vels: &DVector<N>,
440 impulse: N,
441 impulse_id: Id,
442 ground_j_id: &mut usize,
443 j_id: &mut usize,
444 jacobians: &mut [N],
445 constraints: &mut LinearConstraints<N, Id>,
446) {
447 let limits = ImpulseLimits::Independent {
448 min: -N::max_value(),
449 max: N::max_value(),
450 };
451
452 let (ext_vels1, ext_vels2) = split_ext_vels(body1, body2, assembly_id1, assembly_id2, ext_vels);
453 let force = ForceDirection::Angular(*axis);
454 let mut rhs = N::zero();
455
456 let geom = constraint_pair_geometry(
457 body1,
458 part1,
459 handle1,
460 body2,
461 part2,
462 handle2,
463 anchor1,
464 anchor2,
465 &force,
466 ground_j_id,
467 j_id,
468 jacobians,
469 Some(&ext_vels1),
470 Some(&ext_vels2),
471 Some(&mut rhs),
472 );
473
474 if geom.ndofs1 == 0 || geom.ndofs2 == 0 {
475 constraints
476 .bilateral_ground
477 .push(BilateralGroundConstraint::new(
478 geom,
479 assembly_id1,
480 assembly_id2,
481 limits,
482 rhs,
483 impulse,
484 impulse_id,
485 ));
486 } else {
487 constraints.bilateral.push(BilateralConstraint::new(
488 geom,
489 assembly_id1,
490 assembly_id2,
491 limits,
492 rhs,
493 impulse,
494 impulse_id,
495 ));
496 }
497}
498
499pub fn cancel_relative_angular_velocity<N: RealField + Copy, B: ?Sized + Body<N>, H: BodyHandle>(
503 body1: &B,
504 part1: &dyn BodyPart<N>,
505 handle1: BodyPartHandle<H>,
506 body2: &B,
507 part2: &dyn BodyPart<N>,
508 handle2: BodyPartHandle<H>,
509 assembly_id1: usize,
510 assembly_id2: usize,
511 anchor1: &Point<N>,
512 anchor2: &Point<N>,
513 ext_vels: &DVector<N>,
514 impulses: &AngularVector<N>,
515 impulse_id: usize,
516 ground_j_id: &mut usize,
517 j_id: &mut usize,
518 jacobians: &mut [N],
519 constraints: &mut LinearConstraints<N, usize>,
520) {
521 #[cfg(feature = "dim2")]
522 let canonical_basis = [AngularVector::x()];
523 #[cfg(feature = "dim3")]
524 let canonical_basis = [Vector::x(), Vector::y(), Vector::z()];
525
526 for (i, dir) in canonical_basis.iter().enumerate() {
527 cancel_relative_angular_velocity_wrt_axis(
528 body1,
529 part1,
530 handle1,
531 body2,
532 part2,
533 handle2,
534 assembly_id1,
535 assembly_id2,
536 anchor1,
537 anchor2,
538 &Unit::new_unchecked(*dir),
539 ext_vels,
540 impulses[i],
541 impulse_id + i,
542 ground_j_id,
543 j_id,
544 jacobians,
545 constraints,
546 );
547 }
548}
549
550pub fn cancel_relative_rotation<N: RealField + Copy, B: ?Sized + Body<N>, H: BodyHandle>(
554 parameters: &IntegrationParameters<N>,
555 body1: &B,
556 part1: &dyn BodyPart<N>,
557 handle1: BodyPartHandle<H>,
558 body2: &B,
559 part2: &dyn BodyPart<N>,
560 handle2: BodyPartHandle<H>,
561 anchor1: &Point<N>,
562 anchor2: &Point<N>,
563 rotation1: &Rotation<N>,
564 rotation2: &Rotation<N>,
565 jacobians: &mut [N],
566) -> Option<GenericNonlinearConstraint<N, H>> {
567 let error = (rotation2 / rotation1).scaled_axis();
568
569 if let Some((dir, depth)) = Unit::try_new_and_get(error, parameters.allowed_angular_error) {
570 let mut j_id = 0;
571 let mut ground_j_id = 0;
572
573 let geom = constraint_pair_geometry(
574 body1,
575 part1,
576 handle1,
577 body2,
578 part2,
579 handle2,
580 anchor1,
581 anchor2,
582 &ForceDirection::Angular(dir),
583 &mut ground_j_id,
584 &mut j_id,
585 jacobians,
586 None,
587 None,
588 None,
589 );
590
591 let rhs = -depth;
592 let constraint = GenericNonlinearConstraint::new(
593 handle1,
594 Some(handle2),
595 true,
596 geom.ndofs1,
597 geom.ndofs2,
598 geom.wj_id1,
599 geom.wj_id2,
600 rhs,
601 geom.r,
602 );
603
604 Some(constraint)
605 } else {
606 None
607 }
608}
609
610#[cfg(feature = "dim3")]
614pub fn restrict_relative_angular_velocity_to_axis<
615 N: RealField + Copy,
616 B: ?Sized + Body<N>,
617 H: BodyHandle,
618>(
619 body1: &B,
620 part1: &dyn BodyPart<N>,
621 handle1: BodyPartHandle<H>,
622 body2: &B,
623 part2: &dyn BodyPart<N>,
624 handle2: BodyPartHandle<H>,
625 assembly_id1: usize,
626 assembly_id2: usize,
627 axis: &Unit<AngularVector<N>>,
628 anchor1: &Point<N>,
629 anchor2: &Point<N>,
630 ext_vels: &DVector<N>,
631 impulses: &[N],
632 impulse_id: usize,
633 ground_j_id: &mut usize,
634 j_id: &mut usize,
635 jacobians: &mut [N],
636 constraints: &mut LinearConstraints<N, usize>,
637) {
638 let limits = ImpulseLimits::Independent {
639 min: -N::max_value(),
640 max: N::max_value(),
641 };
642
643 let (ext_vels1, ext_vels2) = split_ext_vels(body1, body2, assembly_id1, assembly_id2, ext_vels);
644
645 let mut i = 0;
646 AngularVector::orthonormal_subspace_basis(&[axis.into_inner()], |dir| {
647 let dir = ForceDirection::Angular(Unit::new_unchecked(*dir));
648 let mut rhs = N::zero();
649 let geom = constraint_pair_geometry(
650 body1,
651 part1,
652 handle1,
653 body2,
654 part2,
655 handle2,
656 anchor1,
657 anchor2,
658 &dir,
659 ground_j_id,
660 j_id,
661 jacobians,
662 Some(&ext_vels1),
663 Some(&ext_vels2),
664 Some(&mut rhs),
665 );
666
667 if geom.ndofs1 == 0 || geom.ndofs2 == 0 {
668 constraints
669 .bilateral_ground
670 .push(BilateralGroundConstraint::new(
671 geom,
672 assembly_id1,
673 assembly_id2,
674 limits,
675 rhs,
676 impulses[i],
677 impulse_id + i,
678 ));
679 } else {
680 constraints.bilateral.push(BilateralConstraint::new(
681 geom,
682 assembly_id1,
683 assembly_id2,
684 limits,
685 rhs,
686 impulses[i],
687 impulse_id + i,
688 ));
689 }
690
691 i += 1;
692
693 true
694 });
695}
696
697#[cfg(feature = "dim3")]
701pub fn align_axis<N: RealField + Copy, B: ?Sized + Body<N>, H: BodyHandle>(
702 parameters: &IntegrationParameters<N>,
703 body1: &B,
704 part1: &dyn BodyPart<N>,
705 handle1: BodyPartHandle<H>,
706 body2: &B,
707 part2: &dyn BodyPart<N>,
708 handle2: BodyPartHandle<H>,
709 anchor1: &Point<N>,
710 anchor2: &Point<N>,
711 axis1: &Unit<Vector<N>>,
712 axis2: &Unit<Vector<N>>,
713 jacobians: &mut [N],
714) -> Option<GenericNonlinearConstraint<N, H>> {
715 let mut error;
717 if let Some(error_rot) = Rotation::rotation_between_axis(&axis1, &axis2) {
718 error = error_rot.scaled_axis();
719 } else {
720 let imin = axis1.iamin();
722 error = Vector::zeros();
723 error[imin] = N::one();
724 error = error.cross(&axis1).normalize() * N::pi();
725 }
726
727 if let Some((dir, depth)) = Unit::try_new_and_get(error, parameters.allowed_angular_error) {
728 let mut j_id = 0;
729 let mut ground_j_id = 0;
730
731 let geom = constraint_pair_geometry(
732 body1,
733 part1,
734 handle1,
735 body2,
736 part2,
737 handle2,
738 anchor1,
739 anchor2,
740 &ForceDirection::Angular(dir),
741 &mut ground_j_id,
742 &mut j_id,
743 jacobians,
744 None,
745 None,
746 None,
747 );
748
749 let rhs = -depth;
750 let constraint = GenericNonlinearConstraint::new(
751 handle1,
752 Some(handle2),
753 true,
754 geom.ndofs1,
755 geom.ndofs2,
756 geom.wj_id1,
757 geom.wj_id2,
758 rhs,
759 geom.r,
760 );
761
762 Some(constraint)
763 } else {
764 None
765 }
766}
767
768pub fn restrict_relative_linear_velocity_to_axis<
772 N: RealField + Copy,
773 B: ?Sized + Body<N>,
774 H: BodyHandle,
775>(
776 body1: &B,
777 part1: &dyn BodyPart<N>,
778 handle1: BodyPartHandle<H>,
779 body2: &B,
780 part2: &dyn BodyPart<N>,
781 handle2: BodyPartHandle<H>,
782 assembly_id1: usize,
783 assembly_id2: usize,
784 anchor1: &Point<N>,
785 anchor2: &Point<N>,
786 axis1: &Unit<Vector<N>>,
787 ext_vels: &DVector<N>,
788 impulses: &[N],
789 impulse_id: usize,
790 ground_j_id: &mut usize,
791 j_id: &mut usize,
792 jacobians: &mut [N],
793 constraints: &mut LinearConstraints<N, usize>,
794) {
795 let limits = ImpulseLimits::Independent {
796 min: -N::max_value(),
797 max: N::max_value(),
798 };
799
800 let (ext_vels1, ext_vels2) = split_ext_vels(body1, body2, assembly_id1, assembly_id2, ext_vels);
801
802 let mut i = 0;
803 Vector::orthonormal_subspace_basis(&[axis1.into_inner()], |dir| {
804 let dir = ForceDirection::Linear(Unit::new_unchecked(*dir));
805 let mut rhs = N::zero();
806
807 let geom = constraint_pair_geometry(
808 body1,
809 part1,
810 handle1,
811 body2,
812 part2,
813 handle2,
814 anchor1,
815 anchor2,
816 &dir,
817 ground_j_id,
818 j_id,
819 jacobians,
820 Some(&ext_vels1),
821 Some(&ext_vels2),
822 Some(&mut rhs),
823 );
824
825 if geom.ndofs1 == 0 || geom.ndofs2 == 0 {
826 constraints
827 .bilateral_ground
828 .push(BilateralGroundConstraint::new(
829 geom,
830 assembly_id1,
831 assembly_id2,
832 limits,
833 rhs,
834 impulses[i],
835 impulse_id + i,
836 ));
837 } else {
838 constraints.bilateral.push(BilateralConstraint::new(
839 geom,
840 assembly_id1,
841 assembly_id2,
842 limits,
843 rhs,
844 impulses[i],
845 impulse_id + i,
846 ));
847 }
848
849 i += 1;
850
851 true
852 });
853}
854
855pub fn project_anchor_to_axis<N: RealField + Copy, B: ?Sized + Body<N>, H: BodyHandle>(
859 parameters: &IntegrationParameters<N>,
860 body1: &B,
861 part1: &dyn BodyPart<N>,
862 handle1: BodyPartHandle<H>,
863 body2: &B,
864 part2: &dyn BodyPart<N>,
865 handle2: BodyPartHandle<H>,
866 anchor1: &Point<N>,
867 anchor2: &Point<N>,
868 axis1: &Unit<Vector<N>>,
869 jacobians: &mut [N],
870) -> Option<GenericNonlinearConstraint<N, H>> {
871 let dpt = anchor2 - anchor1;
873 let proj = anchor1 + axis1.into_inner() * axis1.dot(&dpt);
874 let error = anchor2 - proj;
875
876 if let Some((dir, depth)) = Unit::try_new_and_get(error, parameters.allowed_linear_error) {
877 let mut j_id = 0;
878 let mut ground_j_id = 0;
879
880 let geom = constraint_pair_geometry(
881 body1,
882 part1,
883 handle1,
884 body2,
885 part2,
886 handle2,
887 anchor1,
888 anchor2,
889 &ForceDirection::Linear(dir),
890 &mut ground_j_id,
891 &mut j_id,
892 jacobians,
893 None,
894 None,
895 None,
896 );
897
898 let rhs = -depth;
899 let constraint = GenericNonlinearConstraint::new(
900 handle1,
901 Some(handle2),
902 false,
903 geom.ndofs1,
904 geom.ndofs2,
905 geom.wj_id1,
906 geom.wj_id2,
907 rhs,
908 geom.r,
909 );
910
911 Some(constraint)
912 } else {
913 None
914 }
915}
916
917#[cfg(feature = "dim3")]
921pub fn restore_angle_between_axis<N: RealField + Copy, B: ?Sized + Body<N>, H: BodyHandle>(
922 parameters: &IntegrationParameters<N>,
923 body1: &B,
924 part1: &dyn BodyPart<N>,
925 handle1: BodyPartHandle<H>,
926 body2: &B,
927 part2: &dyn BodyPart<N>,
928 handle2: BodyPartHandle<H>,
929 anchor1: &Point<N>,
930 anchor2: &Point<N>,
931 axis1: &Unit<Vector<N>>,
932 axis2: &Unit<Vector<N>>,
933 angle: N,
934 jacobians: &mut [N],
935) -> Option<GenericNonlinearConstraint<N, H>> {
936 let mut separation;
938 if let Some(separation_rot) = Rotation::rotation_between_axis(&axis1, &axis2) {
939 separation = separation_rot.scaled_axis();
940 } else {
941 let imin = axis1.iamin();
943 separation = Vector::zeros();
944 separation[imin] = N::one();
945 separation = separation.cross(&axis1).normalize() * N::pi();
946 }
947
948 if let Some((mut dir, curr_ang)) = Unit::try_new_and_get(separation, N::default_epsilon()) {
949 let mut error = curr_ang - angle;
950
951 if error < N::zero() {
952 error = -error;
953 dir = -dir;
954 }
955
956 if error < parameters.allowed_angular_error {
957 return None;
958 }
959
960 let mut j_id = 0;
961 let mut ground_j_id = 0;
962
963 let geom = constraint_pair_geometry(
964 body1,
965 part1,
966 handle1,
967 body2,
968 part2,
969 handle2,
970 anchor1,
971 anchor2,
972 &ForceDirection::Angular(dir),
973 &mut ground_j_id,
974 &mut j_id,
975 jacobians,
976 None,
977 None,
978 None,
979 );
980
981 let rhs = -error;
982 let constraint = GenericNonlinearConstraint::new(
983 handle1,
984 Some(handle2),
985 true,
986 geom.ndofs1,
987 geom.ndofs2,
988 geom.wj_id1,
989 geom.wj_id2,
990 rhs,
991 geom.r,
992 );
993
994 Some(constraint)
995 } else {
996 None
997 }
998}