1use crate::counters::Counters;
2use crate::coupling::CouplingManager;
3use crate::geometry::{self, ContactManager, HGrid, HGridEntry};
4use crate::math::{Real, Vector};
5use crate::object::{Boundary, BoundaryHandle, BoundarySet};
6use crate::object::{Fluid, FluidHandle, FluidSet};
7use crate::solver::PressureSolver;
8use crate::TimestepManager;
9#[cfg(feature = "parry")]
10use {
11 crate::math::Isometry,
12 crate::object::ParticleId,
13 parry::{bounding_volume::Aabb, query::PointQuery, shape::Shape},
14};
15
16pub struct LiquidWorld {
18 pub counters: Counters,
20 nsubsteps_since_sort: usize,
21 particle_radius: Real,
22 h: Real,
23 fluids: FluidSet,
24 boundaries: BoundarySet,
25 solver: Box<dyn PressureSolver + Send + Sync>,
26 contact_manager: ContactManager,
27 timestep_manager: TimestepManager,
28 hgrid: HGrid<HGridEntry>,
29}
30
31impl LiquidWorld {
32 pub fn new(
40 solver: impl PressureSolver + Send + Sync + 'static,
41 particle_radius: Real,
42 smoothing_factor: Real,
43 ) -> Self {
44 let h = particle_radius * smoothing_factor * na::convert::<_, Real>(2.0);
45 Self {
46 counters: Counters::new(),
47 nsubsteps_since_sort: 0,
48 particle_radius,
49 h,
50 fluids: FluidSet::new(),
51 boundaries: BoundarySet::new(),
52 solver: Box::new(solver),
53 contact_manager: ContactManager::new(),
54 timestep_manager: TimestepManager::new(particle_radius),
55 hgrid: HGrid::new(h),
56 }
57 }
58
59 pub fn step(&mut self, dt: Real, gravity: &Vector<Real>) {
63 self.step_with_coupling(dt, gravity, &mut ())
64 }
65
66 pub fn step_with_coupling(
68 &mut self,
69 dt: Real,
70 gravity: &Vector<Real>,
71 coupling: &mut impl CouplingManager,
72 ) {
73 self.counters.reset();
74 self.counters.step_time.start();
75 self.timestep_manager.reset(dt);
76
77 self.solver.init_with_fluids(self.fluids.as_slice());
78
79 for fluid in self.fluids.as_mut_slice() {
80 fluid.apply_particles_removal();
81 }
82
83 while !self.timestep_manager.is_done() {
85 self.nsubsteps_since_sort += 1;
86 self.counters.nsubsteps += 1;
87
88 self.counters.stages.collision_detection_time.resume();
89 self.counters.cd.grid_insertion_time.resume();
90 self.hgrid.clear();
91 geometry::insert_fluids_to_grid(self.fluids.as_slice(), &mut self.hgrid);
92 self.counters.cd.grid_insertion_time.pause();
93
94 self.counters.cd.boundary_update_time.resume();
95 coupling.update_boundaries(
96 &self.timestep_manager,
97 self.h,
98 self.particle_radius,
99 &self.hgrid,
100 self.fluids.as_mut_slice(),
101 &mut self.boundaries,
102 );
103 self.counters.cd.boundary_update_time.pause();
104
105 self.counters.cd.grid_insertion_time.resume();
106 geometry::insert_boundaries_to_grid(self.boundaries.as_slice(), &mut self.hgrid);
107 self.counters.cd.grid_insertion_time.pause();
108
109 self.solver.init_with_boundaries(self.boundaries.as_slice());
110
111 self.contact_manager.update_contacts(
112 &mut self.counters,
113 self.h,
114 self.fluids.as_slice(),
115 self.boundaries.as_slice(),
116 &self.hgrid,
117 );
118
119 self.counters.cd.ncontacts = self.contact_manager.ncontacts();
120 self.counters.stages.collision_detection_time.pause();
121
122 self.counters.stages.solver_time.resume();
123 self.solver.evaluate_kernels(
124 self.h,
125 &mut self.contact_manager,
126 self.fluids.as_slice(),
127 self.boundaries.as_slice(),
128 );
129
130 self.solver.compute_densities(
131 &self.contact_manager,
132 self.fluids.as_slice(),
133 self.boundaries.as_mut_slice(),
134 );
135
136 self.solver.step(
137 &mut self.counters,
138 &mut self.timestep_manager,
139 gravity,
140 &mut self.contact_manager,
141 self.h,
142 self.fluids.as_mut_slice(),
143 self.boundaries.as_slice(),
144 );
145
146 coupling.transmit_forces(&self.timestep_manager, &self.boundaries);
147 self.counters.stages.solver_time.pause();
148 }
149
150 self.counters.step_time.pause();
157 }
159
160 pub fn add_fluid(&mut self, fluid: Fluid) -> FluidHandle {
162 self.fluids.insert(fluid)
163 }
164
165 pub fn add_boundary(&mut self, boundary: Boundary) -> BoundaryHandle {
167 self.boundaries.insert(boundary)
168 }
169
170 pub fn remove_fluid(&mut self, handle: FluidHandle) -> Option<Fluid> {
172 self.fluids.remove(handle)
173 }
174
175 pub fn remove_boundary(&mut self, handle: BoundaryHandle) -> Option<Boundary> {
177 self.boundaries.remove(handle)
178 }
179
180 pub fn fluids(&self) -> &FluidSet {
182 &self.fluids
183 }
184
185 pub fn fluids_mut(&mut self) -> &mut FluidSet {
187 &mut self.fluids
188 }
189
190 pub fn boundaries(&self) -> &BoundarySet {
192 &self.boundaries
193 }
194
195 pub fn boundaries_mut(&mut self) -> &mut BoundarySet {
197 &mut self.boundaries
198 }
199
200 pub fn h(&self) -> Real {
202 self.h
203 }
204
205 pub fn particle_radius(&self) -> Real {
207 self.particle_radius
208 }
209
210 #[cfg(feature = "parry")]
212 pub fn particles_intersecting_aabb<'a>(
213 &'a self,
214 aabb: Aabb,
215 ) -> impl Iterator<Item = ParticleId> + 'a {
216 self.hgrid
217 .cells_intersecting_aabb(&aabb.mins, &aabb.maxs)
218 .flat_map(|e| e.1)
219 .filter_map(move |entry| match entry {
220 HGridEntry::FluidParticle(fid, pid) => {
221 let (fluid, handle) = self.fluids.get_from_contiguous_index(*fid)?;
222 let pt = fluid.positions[*pid];
223
224 let id = &Isometry::identity();
226 if aabb.distance_to_point(id, &pt, true) < self.particle_radius {
227 Some(ParticleId::FluidParticle(handle, *pid))
228 } else {
229 None
230 }
231 }
232 HGridEntry::BoundaryParticle(bid, pid) => {
233 let (boundary, handle) = self.boundaries.get_from_contiguous_index(*bid)?;
234 let pt = boundary.positions[*pid]; let id = &Isometry::identity();
236 if aabb.distance_to_point(id, &pt, true) < self.particle_radius {
237 Some(ParticleId::BoundaryParticle(handle, *pid))
238 } else {
239 None
240 }
241 }
242 })
243 }
244
245 #[cfg(feature = "parry")]
247 pub fn particles_intersecting_shape<'a, S: ?Sized>(
248 &'a self,
249 pos: &'a Isometry<Real>,
250 shape: &'a S,
251 ) -> impl Iterator<Item = ParticleId> + 'a
252 where
253 S: Shape,
254 {
255 let aabb = shape.compute_aabb(pos);
256 self.hgrid
257 .cells_intersecting_aabb(&aabb.mins, &aabb.maxs)
258 .flat_map(|e| e.1)
259 .filter_map(move |entry| match entry {
260 HGridEntry::FluidParticle(fid, pid) => {
261 let (fluid, handle) = self.fluids.get_from_contiguous_index(*fid)?;
262 let pt = fluid.positions[*pid];
263
264 if shape.distance_to_point(pos, &pt, true) <= self.particle_radius {
265 Some(ParticleId::FluidParticle(handle, *pid))
266 } else {
267 None
268 }
269 }
270 HGridEntry::BoundaryParticle(bid, pid) => {
271 let (boundary, handle) = self.boundaries.get_from_contiguous_index(*bid)?;
272 let pt = boundary.positions[*pid]; if shape.distance_to_point(pos, &pt, true) <= self.particle_radius {
274 Some(ParticleId::BoundaryParticle(handle, *pid))
275 } else {
276 None
277 }
278 }
279 })
280 }
281}
282
283#[test]
284fn world_is_send_and_sync() {
285 fn check<T: Send + Sync>() {}
286 check::<LiquidWorld>();
287}