Skip to content

Commit

Permalink
Fix to how groups ands matrix forces work
Browse files Browse the repository at this point in the history
Fix matrix forces to only apply to particles at or below the force in 
the group hierarchy. Update docs. Some code simplification alongside 
this.
  • Loading branch information
jonathanhogg committed Sep 12, 2024
1 parent a68408d commit f675d3c
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 70 deletions.
40 changes: 14 additions & 26 deletions docs/physics.md
Original file line number Diff line number Diff line change
Expand Up @@ -486,32 +486,20 @@ a new semi-isolated group of particles and forces. The `!physics` node counts
as the outermost group. Particle (and anchor) `id`s must be unique across the
entire system.

Forces are applied to particles according to the following rules:

`!distance`
: As distance forces reference a specific pair of particles (or a particle and
an anchor), they are applied to the referenced particles regardless of which
group they or the particles are declared in.

`!constant`, `!random`, `!field`, `!drag`, `!buoyancy`
: These forces apply to particles within the group in which the force is
declared and to particles in all sub-groups within that group. Declaring any of
these at the top level of the `!physics` system applies them to all particles.

`!barrier`
: Barriers apply to particles within the group in which they are declared,
and to particles in all sub-groups below that group.

`!collision`, `!electrostatic`, `!gravity`, `!adhesion`
: These pair-wise forces apply to pairs of particles within the group in which
the force is declared, within sub-groups of that group, and pairs of particles
between sub-groups and all of their parent groups leading up to the group in
which the force was declared. These forces do not, however, apply between
sibling sub-groups.

As the latter forces apply to all pairs of particles, they are the most
expensive to compute. Therefore, limiting them to not apply across sub-groups
allows for fine-grained control over which pairs the forces should apply.
Forces (and barriers) declared in a group apply to all particles declared in
that group, and all particles declared in sub-groups of that group. Forces
declared at the top level of a `!physics` system apply to *all* particle
within the system.

For forces that apply between pairs of particles, particles are only able to
"see" other particles in the same group, in sub-groups and in parent groups,
but not particles in sibling groups – with the exception of particles
referenced explicitly by `id`.

To restrict a force to apply to only a specific set of particles, declare the
force and the particles within a `!group`. To make specific particles visible
between two groups, declare the force and those particles in the parent of the
two groups.

## State interaction

Expand Down
70 changes: 26 additions & 44 deletions src/flitter/render/physics.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,11 @@ cdef class ParticleForceApplier(ForceApplier):


cdef class MatrixPairForceApplier(PairForceApplier):
cdef double max_distance_squared
cdef double max_distance

@cython.profile(False)
def __cinit__(self, Node node, double strength, Vector zero):
self.max_distance_squared = max(0, node.get_float('max_distance', 0)) ** 2
self.max_distance = max(0, node.get_float('max_distance', 0))


cdef class SpecificPairForceApplier(PairForceApplier):
Expand Down Expand Up @@ -589,12 +589,13 @@ cdef class PhysicsSystem:
cdef PyObject* from_particle
cdef PyObject* to_particle
cdef PyObject* force
cdef PyObject* barrier
cdef PyObject* groupptr
cdef PhysicsGroup group
cdef SpecificPairForceApplier specific_force
cdef bint more
while True:
delta = min(self.resolution, time-last_time)
last_time += delta
more = last_time < time and (not realtime or extra)
for group in groups:
for specific_force in group.specific_forces:
from_particle = PyDict_GetItem(particles_by_id, specific_force.from_particle_id)
Expand All @@ -613,20 +614,19 @@ cdef class PhysicsSystem:
direction.numbers[k] /= distance
specific_force.apply(<Particle>from_particle, <Particle>to_particle, direction, distance, distance_squared)
particles = group.particles
if group.parent is None:
all_particles = group.particles
matrix_forces = group.matrix_forces
else:
all_particles = list(group.particles)
matrix_forces = list(group.matrix_forces)
while (group := group.parent) is not None:
all_particles.extend(group.particles)
matrix_forces.extend(group.matrix_forces)
with nogil:
p = PyList_GET_SIZE(particles)
if not p:
continue
all_particles = []
while group is not None:
all_particles.extend(group.particles)
n = PyList_GET_SIZE(all_particles)
p = PyList_GET_SIZE(particles)
matrix_forces = group.matrix_forces
m = PyList_GET_SIZE(matrix_forces)
if m and p:
group = group.parent
if not m:
continue
with nogil:
for i in range(p):
from_particle = PyList_GET_ITEM(all_particles, i)
for j in range(i+1, n):
Expand All @@ -641,13 +641,12 @@ cdef class PhysicsSystem:
direction.numbers[k] /= distance
for k in range(m):
force = PyList_GET_ITEM(matrix_forces, k)
if not (<MatrixPairForceApplier>force).max_distance_squared or \
distance_squared < (<MatrixPairForceApplier>force).max_distance_squared:
if not (<MatrixPairForceApplier>force).max_distance or distance < (<MatrixPairForceApplier>force).max_distance:
(<MatrixPairForceApplier>force).apply(<Particle>from_particle, <Particle>to_particle,
direction, distance, distance_squared)
for group in groups:
particles = group.particles
p = len(particles)
p = PyList_GET_SIZE(particles)
if p == 0:
continue
if group.parent is None:
Expand All @@ -661,36 +660,19 @@ cdef class PhysicsSystem:
barriers.extend(group.barriers)
with nogil:
m = PyList_GET_SIZE(particle_forces)
if m and p:
for i in range(m):
force = PyList_GET_ITEM(particle_forces, i)
for j in range(p):
to_particle = PyList_GET_ITEM(particles, j)
if (<Particle>to_particle).non_anchor:
(<ParticleForceApplier>force).apply(<Particle>to_particle, delta)
n = PyList_GET_SIZE(barriers)
for i in range(p):
to_particle = PyList_GET_ITEM(particles, i)
if (<Particle>to_particle).non_anchor:
for j in range(m):
(<ParticleForceApplier>PyList_GET_ITEM(particle_forces, j)).apply(<Particle>to_particle, delta)
(<Particle>to_particle).step(self.speed_of_light, clock, delta)
m = PyList_GET_SIZE(barriers)
if m:
for i in range(m):
barrier = PyList_GET_ITEM(barriers, i)
for j in range(p):
to_particle = PyList_GET_ITEM(particles, j)
if (<Particle>to_particle).non_anchor:
(<Barrier>barrier).apply(<Particle>to_particle, self.speed_of_light, clock, delta)
last_time += delta
for j in range(n):
(<Barrier>PyList_GET_ITEM(barriers, j)).apply(<Particle>to_particle, self.speed_of_light, clock, delta)
if more:
(<Particle>to_particle).reset_force()
clock += delta
if last_time >= time or (realtime and not extra):
if not more:
break
extra = False
with nogil:
m = PyList_GET_SIZE(groups)
for i in range(m):
groupptr = PyList_GET_ITEM(groups, i)
p = PyList_GET_SIZE((<PhysicsGroup>groupptr).particles)
for j in range(p):
to_particle = PyList_GET_ITEM((<PhysicsGroup>groupptr).particles, j)
(<Particle>to_particle).reset_force()
return clock

0 comments on commit f675d3c

Please sign in to comment.