Collision Layers and No-Collide Masks

Most simulations want all objects to collide with each other and with boundaries, and this happens by default with no configuration needed.

MEDYAN already supports preventing collisions between specific linked objects via the no_collide flag on bond configurations in def_link_type!. This is useful when two objects are connected by a bond and shouldn’t also generate a repulsive collision force.

For cases where entire classes of objects should ignore each other (e.g. a piston ball that passes through walls, or particles that don’t self-collide), MEDYAN provides a layer and no-collide mask bitmask system. This avoids the need to explicitly enumerate no-collide pairs for every combination of objects.

How it works

Every collision object (ball, filament type, membrane type) and every boundary element (plane, capsule) carries two UInt32 bitmasks:

Field Meaning
collision_layers Which layers the object is on
no_collide_mask Which layers the object will not collide with

Two objects collide unless either one disables collisions with the other:

no_collide = (A.layers & B.no_collide_mask) ≠ 0  OR  (B.layers & A.no_collide_mask) ≠ 0

In other words: if A is on a layer that B has disabled, or B is on a layer that A has disabled, they don’t collide.

The same check applies between objects and boundary elements.

Defaults

collision_layers defaults to UInt32(1) (on layer 1) and no_collide_mask defaults to UInt32(0) (no collisions disabled). This means all objects collide with all other objects and all boundaries by default.

Example: piston ball that ignores the boundary

Suppose you have actin filaments inside a box, and a large ball acting as a piston that should push actin but not be pushed back by the boundary walls.

Object collision_layers no_collide_mask Notes
Actin (default) 1 0 Unchanged
Piston ball 2 2 On layer 2, won’t collide with layer 2
Boundary box 2 2 On layer 2, won’t collide with layer 2

The pattern is simple: put yourself on layer N, disable layer N. Actin stays completely at defaults.

Example: balls that don’t self-collide

Suppose you have many small balls representing diffusing particles. They should collide with actin and boundaries, but not with each other.

Object collision_layers no_collide_mask Notes
Actin (default) 1 0 Unchanged
Balls 2 2 On layer 2, won’t collide with layer 2
Boundary (default) 1 0 Unchanged

Example: selective membrane permeability

Suppose you have two membranes: membrane A collides with filaments but lets balls pass through, and membrane B collides with balls but lets filaments pass through. The membranes do not collide with each other.

Assign each object type its own layer. Filaments stay on layer 1 (default), balls go on UInt32(1)<<1, membranes go on UInt32(1)<<2. Each membrane then sets its no_collide_mask to the layers it should not collide with.

const FILA_LAYER = UInt32(1)        # bit 1
const BALL_LAYER = UInt32(1) << 1   # bit 2
const MEMB_LAYER = UInt32(1) << 2   # bit 3
Object collision_layers no_collide_mask Notes
Filaments FILA_LAYER UInt32(0) Default layer, collides with everything
Balls BALL_LAYER UInt32(0) Own layer, collides with everything
Membrane A MEMB_LAYER BALL_LAYER \| MEMB_LAYER Won’t collide with balls or membranes
Membrane B MEMB_LAYER FILA_LAYER \| MEMB_LAYER Won’t collide with filaments or membranes

Where layers are set

Object type Where to set
Balls make_ball! / update_ball! kwargs
Filaments FilamentMechParams fields
Membranes MembraneMechParams fields
Boundary planes boundary_plane / boundary_box / boundary_cylinder kwargs
Boundary capsules boundary_capsule / boundary_cylinder kwargs

Design notes

The layer/mask approach is inspired by the collision layer system in the Godot game engine, though MEDYAN uses different semantics. The choice of a no-collide mask (blocklist) over Godot’s collision mask (allowlist) is inspired by Unreal Engine’s collision filtering.

Why a no-collide mask?

The no_collide_mask specifies which layers to disable collisions with: bits set = layers you don’t collide with. The default is UInt32(0) — no collisions disabled, collide with everything. This is more intuitive than an allowlist where the default would need to be all-bits-set (0xFFFFFFFF) to mean “collide with everything.”

The no-collide check uses OR: if either object disables collisions with the other, the pair is excluded. This preserves Newton’s third law — MEDYAN’s collision forces are symmetric repulsive potentials, so if a pair exists both objects must feel the force. There is no concept of one object pushing another without being pushed back.