1use crate::ffi;
4use crate::types::Vec3;
5
6#[repr(C)]
8#[derive(Clone, Copy, Debug, Default)]
9pub struct StressNodeDesc {
10 pub com: Vec3,
11 pub mass: f32,
12 pub inertia: f32,
13}
14
15#[repr(C)]
17#[derive(Clone, Copy, Debug, Default)]
18pub struct StressBondDesc {
19 pub centroid: Vec3,
20 pub node0: u32,
21 pub node1: u32,
22}
23
24#[repr(C)]
26#[derive(Clone, Copy, Debug, Default)]
27pub struct StressVelocity {
28 pub ang: Vec3,
29 pub lin: Vec3,
30}
31
32#[repr(C)]
34#[derive(Clone, Copy, Debug, Default)]
35pub struct StressImpulse {
36 pub ang: Vec3,
37 pub lin: Vec3,
38}
39
40#[repr(C)]
42#[derive(Clone, Copy, Debug, Default)]
43pub struct StressDataParams {
44 pub equalize_masses: bool,
45 pub center_bonds: bool,
46}
47
48#[derive(Clone, Copy, Debug)]
50pub struct StressSolverParams {
51 pub max_iterations: u32,
52 pub tolerance: f32,
53 pub warm_start: bool,
54}
55
56impl Default for StressSolverParams {
57 fn default() -> Self {
58 Self {
59 max_iterations: 32,
60 tolerance: 1.0e-6,
61 warm_start: false,
62 }
63 }
64}
65
66#[derive(Clone, Copy, Debug, Default)]
68pub struct StressErrorSq {
69 pub ang: f32,
70 pub lin: f32,
71}
72
73pub struct StressProcessor {
79 handle: *mut ffi::StressProcessorHandle,
80}
81
82unsafe impl Send for StressProcessor {}
83unsafe impl Sync for StressProcessor {}
84
85impl StressProcessor {
86 pub fn new(
88 nodes: &[StressNodeDesc],
89 bonds: &[StressBondDesc],
90 params: StressDataParams,
91 ) -> Option<Self> {
92 if nodes.is_empty() || bonds.is_empty() {
93 return None;
94 }
95 let ffi_params = ffi::FfiStressDataParams {
96 equalize_masses: params.equalize_masses as u8,
97 center_bonds: params.center_bonds as u8,
98 };
99 let handle = unsafe {
100 ffi::stress_processor_create(
101 nodes.as_ptr() as *const ffi::FfiStressNodeDesc,
102 nodes.len() as u32,
103 bonds.as_ptr() as *const ffi::FfiStressBondDesc,
104 bonds.len() as u32,
105 ffi_params,
106 )
107 };
108 if handle.is_null() {
109 None
110 } else {
111 Some(Self { handle })
112 }
113 }
114
115 pub fn node_count(&self) -> u32 {
116 unsafe { ffi::stress_processor_node_count(self.handle) }
117 }
118
119 pub fn bond_count(&self) -> u32 {
120 unsafe { ffi::stress_processor_bond_count(self.handle) }
121 }
122
123 pub fn node_desc(&self, index: u32) -> Option<StressNodeDesc> {
124 let mut desc = ffi::FfiStressNodeDesc::default();
125 let ok = unsafe { ffi::stress_processor_get_node_desc(self.handle, index, &mut desc) };
126 if ok != 0 {
127 Some(StressNodeDesc {
128 com: desc.com,
129 mass: desc.mass,
130 inertia: desc.inertia,
131 })
132 } else {
133 None
134 }
135 }
136
137 pub fn bond_desc(&self, index: u32) -> Option<StressBondDesc> {
138 let mut desc = ffi::FfiStressBondDesc::default();
139 let ok = unsafe { ffi::stress_processor_get_bond_desc(self.handle, index, &mut desc) };
140 if ok != 0 {
141 Some(StressBondDesc {
142 centroid: desc.centroid,
143 node0: desc.node0,
144 node1: desc.node1,
145 })
146 } else {
147 None
148 }
149 }
150
151 pub fn remove_bond(&mut self, bond_index: u32) -> bool {
153 unsafe { ffi::stress_processor_remove_bond(self.handle, bond_index) != 0 }
154 }
155
156 pub fn solve(
160 &mut self,
161 impulses: &mut [StressImpulse],
162 velocities: &[StressVelocity],
163 params: StressSolverParams,
164 resume: bool,
165 ) -> Result<(i32, StressErrorSq), &'static str> {
166 if impulses.len() as u32 != self.bond_count() {
167 return Err("impulse array must match bond count");
168 }
169 if velocities.len() as u32 != self.node_count() {
170 return Err("velocity array must match node count");
171 }
172 let ffi_params = ffi::FfiStressSolverParams {
173 max_iterations: params.max_iterations,
174 tolerance: params.tolerance,
175 warm_start: params.warm_start as u8,
176 _pad: [0; 3],
177 };
178 let mut error = ffi::FfiStressErrorSq::default();
179 let iterations = unsafe {
180 ffi::stress_processor_solve(
181 self.handle,
182 impulses.as_mut_ptr() as *mut ffi::FfiStressImpulse,
183 velocities.as_ptr() as *const ffi::FfiStressVelocity,
184 ffi_params,
185 &mut error,
186 resume as u8,
187 )
188 };
189 if iterations < 0 {
190 Err("solver returned an error")
191 } else {
192 Ok((
193 iterations,
194 StressErrorSq {
195 ang: error.ang,
196 lin: error.lin,
197 },
198 ))
199 }
200 }
201
202 pub fn using_simd() -> bool {
204 unsafe { ffi::stress_processor_using_simd() != 0 }
205 }
206}
207
208impl Drop for StressProcessor {
209 fn drop(&mut self) {
210 unsafe { ffi::stress_processor_destroy(self.handle) }
211 }
212}