1#![allow(non_camel_case_types)]
2#![allow(non_snake_case)]
3
4use std::{
5 cell::RefCell,
6 ffi::c_int,
7 rc::Rc,
8 sync::{Arc, Weak},
9};
10
11use autocxx::subclass::{CppSubclass, CppSubclassCppPeerHolder};
12
13use crate::{
14 box2d::ffi,
15 ffi::{b2Fixture, b2ParticleSystem, b2Vec2, int32},
16};
17
18#[allow(unused_variables)]
19pub trait b2RayCastCallbackImpl {
20 fn report_fixture(
21 &mut self,
22 fixture: &mut b2Fixture,
23 point: &b2Vec2,
24 normal: &b2Vec2,
25 fraction: f32,
26 ) -> f32;
27
28 fn report_particle(
29 &mut self,
30 particle_system: &b2ParticleSystem,
31 index: i32,
32 point: &b2Vec2,
33 normal: &b2Vec2,
34 fraction: f32,
35 ) -> f32;
36
37 fn should_query_particle_system(&mut self, particle_system: *const b2ParticleSystem) -> bool;
38}
39
40pub struct b2RayCastCallbackWrapper {
41 cpp_peer: CppSubclassCppPeerHolder<ffi::b2RayCastCallbackWrapperCpp>,
42 wrapper_impl: Weak<RefCell<dyn b2RayCastCallbackImpl>>,
43}
44
45impl b2RayCastCallbackWrapper {
46 pub fn new(wrapper_impl: Arc<RefCell<dyn b2RayCastCallbackImpl>>) -> Rc<RefCell<Self>> {
47 Self::new_rust_owned(Self {
48 cpp_peer: Default::default(),
49 wrapper_impl: Arc::downgrade(&wrapper_impl),
50 })
51 }
52}
53
54impl ffi::b2RayCastCallback_methods for b2RayCastCallbackWrapper {
55 unsafe fn ReportFixture(
56 &mut self,
57 fixture: *mut b2Fixture,
58 point: &b2Vec2,
59 normal: &b2Vec2,
60 fraction: f32,
61 ) -> f32 {
62 if let Some(wrapper_impl_rc) = self.wrapper_impl.upgrade() {
63 let wrapper_impl = wrapper_impl_rc.as_ptr().as_mut().unwrap();
64 return wrapper_impl.report_fixture(fixture.as_mut().unwrap(), point, normal, fraction);
65 } else {
66 return 0.;
67 }
68 }
69
70 unsafe fn ReportParticle(
71 &mut self,
72 particle_system: *const b2ParticleSystem,
73 index: autocxx::c_int,
74 point: &b2Vec2,
75 normal: &b2Vec2,
76 fraction: f32,
77 ) -> f32 {
78 if let Some(wrapper_impl_rc) = self.wrapper_impl.upgrade() {
79 let wrapper_impl = wrapper_impl_rc.as_ptr().as_mut().unwrap();
80 return wrapper_impl.report_particle(
81 particle_system.as_ref().unwrap(),
82 i32::from(int32::from(c_int::from(index))),
83 point,
84 normal,
85 fraction,
86 );
87 } else {
88 return 0.;
89 }
90 }
91
92 unsafe fn ShouldQueryParticleSystem(
93 &mut self,
94 particle_system: *const b2ParticleSystem,
95 ) -> bool {
96 if let Some(wrapper_impl_rc) = self.wrapper_impl.upgrade() {
97 let wrapper_impl = wrapper_impl_rc.as_ptr().as_mut().unwrap();
98 return wrapper_impl.should_query_particle_system(particle_system.as_ref().unwrap());
99 } else {
100 return true;
101 }
102 }
103}
104
105impl CppSubclass<ffi::b2RayCastCallbackWrapperCpp> for b2RayCastCallbackWrapper {
106 fn peer_holder(&self) -> &CppSubclassCppPeerHolder<ffi::b2RayCastCallbackWrapperCpp> {
107 &self.cpp_peer
108 }
109
110 fn peer_holder_mut(
111 &mut self,
112 ) -> &mut CppSubclassCppPeerHolder<ffi::b2RayCastCallbackWrapperCpp> {
113 &mut self.cpp_peer
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use std::{cell::RefCell, pin::Pin, sync::Arc};
120
121 use autocxx::prelude::*;
122
123 use crate::{
124 ffi::{
125 b2BodyDef,
126 b2BodyType::b2_staticBody,
127 b2CircleShape,
128 b2Fixture,
129 b2ParticleSystem,
130 b2RayCastCallback,
131 b2Shape,
132 b2Vec2,
133 b2World,
134 SetCircleRadius,
135 },
136 ray_cast::{b2RayCastCallbackImpl, b2RayCastCallbackWrapper},
137 };
138
139 #[test]
140 fn raycast_hits_correct_fixture() {
141 unsafe {
142 let gravity = b2Vec2::new1(0., -10.).within_box();
143 let mut world = b2World::new(&*gravity).within_box();
144
145 let mut body_def = b2BodyDef::new().within_box();
146 body_def.type_ = b2_staticBody;
147 body_def.position = b2Vec2 { x: 0., y: 0. };
148 let body_def = &*body_def;
149 let body = world.as_mut().CreateBody(&*body_def);
150 let mut body = Pin::new_unchecked(body.as_mut().unwrap());
151
152 let mut shape = b2CircleShape::new().within_box();
153 SetCircleRadius(shape.as_mut(), 1.);
154 let shape: &b2Shape = (&*shape).as_ref();
155 let fixture = body.as_mut().CreateFixture1(&*shape, 5.);
156
157 let callback = CallbackImpl::new(fixture.as_ref().unwrap());
158 let callback_ref = Arc::new(RefCell::new(callback));
159 let b2_callback = b2RayCastCallbackWrapper::new(callback_ref.clone());
160 let b2_callback: *mut b2RayCastCallback = b2_callback
161 .as_ref()
162 .borrow_mut()
163 .pin_mut()
164 .as_mut()
165 .get_unchecked_mut();
166 world.as_ref().RayCast(
167 b2_callback,
168 &b2Vec2 { x: -2., y: 0. },
169 &b2Vec2 { x: 2., y: 0. },
170 );
171
172 assert!(callback_ref.borrow().did_encounter_fixture);
173 }
174
175 struct CallbackImpl<'a> {
176 expected_fixture: &'a b2Fixture,
177 did_encounter_fixture: bool,
178 }
179
180 impl<'a> CallbackImpl<'a> {
181 fn new(expected_fixture: &'a b2Fixture) -> Self {
182 Self {
183 expected_fixture,
184 did_encounter_fixture: false,
185 }
186 }
187 }
188
189 #[allow(unused_variables)]
190 impl b2RayCastCallbackImpl for CallbackImpl<'_> {
191 fn report_fixture(
192 &mut self,
193 fixture: &mut b2Fixture,
194 point: &b2Vec2,
195 normal: &b2Vec2,
196 fraction: f32,
197 ) -> f32 {
198 assert!(std::ptr::eq(fixture, self.expected_fixture));
199 self.did_encounter_fixture = true;
200 return 0.;
201 }
202
203 fn report_particle(
204 &mut self,
205 particle_system: &b2ParticleSystem,
206 index: i32,
207 point: &b2Vec2,
208 normal: &b2Vec2,
209 fraction: f32,
210 ) -> f32 {
211 assert!(false, "Should not get here");
212 return 0.;
213 }
214
215 fn should_query_particle_system(
216 &mut self,
217 particle_system: *const b2ParticleSystem,
218 ) -> bool {
219 assert!(false, "Should not get here");
220 return false;
221 }
222 }
223 }
224}