Skip to content

Commit 9063880

Browse files
committed
[orx-force-2d] Add kdoc strings
1 parent f827382 commit 9063880

20 files changed

Lines changed: 536 additions & 5 deletions

orx-force-2d/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# orx-force-2d
2+
3+
Implements 2D XPBD for simulating physical forces.

orx-force-2d/src/commonMain/kotlin/BodyAreaConstraint.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@ package org.openrndr.extra.force2d
22

33
import org.openrndr.math.Vector2
44

5+
/**
6+
* Represents a constraint that maintains a specified area for a deformable body.
7+
*
8+
* This class ensures that the area defined by the body's boundary remains close
9+
* to a target rest area during simulation. The constraint is solved iteratively
10+
* and includes compliance to allow some flexibility in maintaining the target area.
11+
*
12+
* @constructor Creates a BodyAreaConstraint for the specified body.
13+
* @param body The deformable body whose area is constrained.
14+
* @param compliance The compliance factor that governs the flexibility of the constraint.
15+
* Higher values allow more deviation from the rest area.
16+
* @param iterations The number of iterations for solving the constraint in each simulation step.
17+
*/
518
class BodyAreaConstraint(val body: Body, var compliance: Double = 0.0, var iterations: Int = 1) : Constraint {
619

720
var restArea: Double = area()
@@ -69,6 +82,14 @@ class BodyAreaConstraint(val body: Body, var compliance: Double = 0.0, var itera
6982
}
7083
}
7184

85+
/**
86+
* Adds a body area constraint to the body, ensuring that its area remains close
87+
* to a target rest area during simulation. The constraint includes compliance
88+
* and uses iterative solving to maintain this condition.
89+
*
90+
* @param configure A lambda function used to configure the `BodyAreaConstraint`
91+
* instance before it is added to the body's constraints list.
92+
*/
7293
fun Body.bodyAreaConstraint(configure : BodyAreaConstraint.() -> Unit = {}) {
7394
constraints.add(BodyAreaConstraint(this).apply(configure))
7495
}

orx-force-2d/src/commonMain/kotlin/BoundaryNodeRelaxForce.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
package org.openrndr.extra.force2d
22

3+
/**
4+
* BoundaryNodeRelaxForce is a type of force that acts on the boundary nodes of a [Body].
5+
* It aims to relax the boundary nodes by adjusting their velocities based on neighboring node positions.
6+
* Each boundary node is influenced by the midpoint of its neighboring boundary nodes, providing a smoothing effect.
7+
*
8+
* @property strength Controls the intensity of the relaxation force. A higher strength results in greater adjustment of the node velocities.
9+
*
10+
* This force operates frame-by-frame:
11+
* 1. In each frame, the [apply] method computes the velocity correction for each boundary node.
12+
* 2. The correction is calculated by determining the difference between the node's current position and the midpoint of its neighboring nodes.
13+
* 3. The computed difference is scaled by the time step `dt` and the `strength` property, then added to the node's velocity.
14+
*
15+
* The force assumes a cyclic relationship between boundary nodes, where the first and last nodes are neighbors.
16+
*/
317
class BoundaryNodeRelaxForce : Force {
418

519
var strength = 1.0
@@ -20,5 +34,13 @@ class BoundaryNodeRelaxForce : Force {
2034
}
2135
}
2236

37+
/**
38+
* Adds a [BoundaryNodeRelaxForce] to the [Body]'s list of forces.
39+
* This force acts on the boundary nodes of the body to relax their positions
40+
* by adjusting their velocities based on neighboring node positions.
41+
*
42+
* @param configure A lambda configuration function for the [BoundaryNodeRelaxForce].
43+
* Use this to set properties such as `strength` to customize force behavior.
44+
*/
2345
fun Body.boundaryNodeRelaxForce(configure: BoundaryNodeRelaxForce.() -> Unit) =
2446
forces.add(BoundaryNodeRelaxForce().apply(configure))

orx-force-2d/src/commonMain/kotlin/ContourToBlob.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@ package org.openrndr.extra.force2d
22
import org.openrndr.math.Vector2
33
import org.openrndr.shape.ShapeContour
44

5+
6+
/**
7+
* Converts a given shape contour into a physical body representation with configurable nodes and links.
8+
*
9+
* The function creates a series of nodes based on equidistant positions along the contour
10+
* and connects them using links to form a flexible structure. Additional neighbor links can
11+
* be added for increased connectivity. The resulting [Body] can be further configured
12+
* using the provided [configure] block.
13+
*
14+
* @param contour the shape contour to be converted into a physical body representation.
15+
* @param density the density of nodes along the contour, defining the spacing between nodes. Default is 10.0.
16+
* @param linkNeighbors the number of additional neighboring links to create for each node. Default is 1.
17+
* @param configure a lambda to configure the resulting [Body] instance.
18+
* @return a [Body] instance representing the given contour with nodes and links.
19+
*/
520
fun contourToBody(contour: ShapeContour, density: Double = 10.0, linkNeighbors: Int = 1, configure: Body.() -> Unit = {}): Body {
621
val points = contour.equidistantPositions((contour.length / density).toInt())
722

orx-force-2d/src/commonMain/kotlin/ForceSimulation.kt

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,22 @@ import org.openrndr.shape.Rectangle
55
import kotlin.coroutines.CoroutineContext
66
import kotlin.math.abs
77

8+
/**
9+
* Represents a single physical node with position, velocity, and related properties.
10+
*
11+
* A node serves as an element in simulations, with attributes including its current position,
12+
* previous position, velocity, inverse mass, radius, and a unique identifier. These properties
13+
* define the motion and interaction of the node with forces or other nodes in the simulation.
14+
*
15+
* @property position the current position of the node in the simulation space.
16+
* @property prevPosition the position of the node in the previous simulation step.
17+
* @property velocity the current velocity of the node.
18+
* @property inverseMass the reciprocal of the node's mass. A value of 0 represents an immovable object,
19+
* while higher values represent lower mass.
20+
* @property radius the radius of the node, used for interactions or spatial calculations.
21+
* @property id an optional unique identifier for the node. Default is 0.
22+
* @property bounds a bounding rectangle centered around the node, with dimensions based on its radius.
23+
*/
824
class Node(
925
var position: Vector2,
1026
var prevPosition: Vector2,
@@ -18,12 +34,43 @@ class Node(
1834

1935
}
2036

37+
/**
38+
* Represents a connection or relationship between two [Node]s, identified by their indices.
39+
*
40+
* This class is primarily used to model a connection between two nodes or objects in a system,
41+
* such as a physical simulation or graph structure. The `source` and `target` properties represent
42+
* the indices of the connected nodes.
43+
*
44+
* @property source the index of the source node.
45+
* @property target the index of the target node.
46+
*/
2147
class Link(
2248
var source: Int,
2349
var target: Int,
2450
)
2551

52+
/**
53+
* Represents a triangle defined by three node indices in a physical simulation.
54+
*
55+
* A Triangle is primarily used to perform calculations related to its geometry,
56+
* such as computing its signed area based on the positions of nodes in a given list.
57+
*
58+
* @constructor Creates a Triangle with indices pointing to nodes in a list.
59+
* @param a the index of the first node in the triangle.
60+
* @param b the index of the second node in the triangle.
61+
* @param c the index of the third node in the triangle.
62+
*/
2663
class Triangle(var a: Int, var b: Int, var c: Int) {
64+
65+
/**
66+
* Computes the signed area of the triangle defined by the nodes referenced in this triangle.
67+
* The signed area is positive if the nodes are ordered counterclockwise,
68+
* and negative if they are ordered clockwise.
69+
*
70+
* @param nodes a list of [Node] objects where this triangle's node indices correspond to the nodes in the list.
71+
* Each [Node] must have a valid position.
72+
* @return the signed area of the triangle defined by the referenced nodes.
73+
*/
2774
fun signedArea(nodes: List<Node>): Double {
2875
val p0 = nodes[a].position
2976
val p1 = nodes[b].position
@@ -34,30 +81,99 @@ class Triangle(var a: Int, var b: Int, var c: Int) {
3481
}
3582
}
3683

84+
/**
85+
* Represents a constraint in a physical system, which influences the behavior of a [Body].
86+
*
87+
* A [Constraint] typically applies rules or restrictions to the motion or interaction of
88+
* the elements within a [Body]. This can include conditions like maintaining distances
89+
* between points, restricting movement to certain boundaries, or other physical interactions.
90+
*/
3791
interface Constraint {
3892
suspend fun initialize()
93+
94+
/**
95+
* Resolves constraints and updates the state of nodes within the given physical body over a time step.
96+
*
97+
* This method applies all constraints associated with the [Body], ensuring that the system adheres
98+
* to the defined physical rules. For example, constraints might enforce distance limitations,
99+
* maintain structural rigidity, or apply boundary conditions. It leverages the constraint-solving
100+
* functionality for the provided time interval, potentially resulting in updated positions or states
101+
* of the body's components.
102+
*
103+
* @param body the [Body] whose constraints are to be resolved, representing the physical system.
104+
* @param dt the duration of the time step during which the constraints are solved, expressed in seconds.
105+
*/
39106
suspend fun solve(body: Body, dt: Double)
40107
}
41108

109+
/**
110+
* Represents a physical force that can be applied to a [Body] in a simulation.
111+
*
112+
* Implementing classes define specific behaviors for initializing and
113+
* applying forces on the [Body], altering the state of its nodes over time.
114+
*/
42115
interface Force {
43116
suspend fun initializeFrame(body: Body)
44117
suspend fun apply(body: Body, dt: Double)
45118
}
46119

120+
/**
121+
* Interface representing a force interaction between two physical bodies in a simulation.
122+
*
123+
* Implementations of this interface define the logic for detecting overlapping pairs of bodies,
124+
* initializing frames for force calculations, and applying interbody forces during simulation steps.
125+
*/
47126
interface InterbodyForce {
48127
suspend fun initializeFrame()
49128
suspend fun findOverlappingPairs(bodies: List<Body>): List<Pair<Int, Int>>
50129
suspend fun apply(body: Body, other: Body, dt: Double, substep: Int)
51130
}
52131

132+
/**
133+
* An interface representing a constraint that resolves collisions between two bodies in a simulation.
134+
*
135+
* Implementations of this interface must define the logic for resolving collisions between the specified
136+
* [body] and [other] based on their physical properties, positions, and velocities. This process
137+
* ensures that the bodies interact realistically while respecting the constraints defined for the simulation.
138+
*
139+
* The [solve] method is called with a time step [dt] to calculate and apply necessary adjustments to
140+
* the bodies involved in the collision.
141+
*/
53142
interface CollisionConstraint {
54143
suspend fun solve(body: Body, other: Body, dt: Double)
55144
}
56145

146+
/**
147+
* Interface for detecting broad-phase collisions in a physics simulation.
148+
*
149+
* The broad-phase collision detector is responsible for identifying potential overlapping
150+
* pairs of bodies in a physics simulation. It typically uses efficient spatial partitioning
151+
* or bounding volume techniques to narrow down the list of potential collisions from
152+
* a large number of bodies.
153+
*/
57154
interface BroadPhaseCollisionDetector {
58155
suspend fun findOverlappingPairs(bodies: List<Body>): List<Pair<Int, Int>>
59156
}
60157

158+
/**
159+
* Represents a physical body in a simulation composed of nodes, links, and triangles.
160+
*
161+
* A [Body] serves as the central structure in simulations, encapsulating physical properties,
162+
* relationships, and behaviors of its constituent parts. It supports initialization,
163+
* integration of forces, resolution of constraints, and updates to its boundaries.
164+
* The body can either be static or dynamic, depending on the `static` property.
165+
*
166+
* @property nodes the list of [Node]s that make up the physical body.
167+
* @property boundaryNodes the list of indices referring to nodes considered as boundary nodes.
168+
* @property links the list of [Link]s that define relationships or connections between nodes.
169+
* @property boundaryLinks the list of indices referring to links considered as boundary links.
170+
* @property triangles the list of [Triangle]s used for structural and mass-related calculations.
171+
* @property inverseBodyMass the reciprocal of the total mass of the body, used in weight calculations.
172+
* @property static a flag indicating whether the body is static (non-movable) or dynamic.
173+
* @property forces the list of [Force]s acting on the body in the simulation.
174+
* @property constraints the list of [Constraint]s influencing the behavior and interaction of the body.
175+
* @property bounds the bounding rectangle that encapsulates the body's nodes, updated automatically.
176+
*/
61177
class Body(
62178
val nodes: List<Node>,
63179
val boundaryNodes: List<Int> = emptyList(),
@@ -145,6 +261,26 @@ class Body(
145261
}
146262
}
147263

264+
/**
265+
* Represents a physics simulation of interacting bodies.
266+
*
267+
* The [ForceSimulation] class provides the infrastructure to simulate the dynamics
268+
* of a system of bodies under the influence of forces and constraints. It supports
269+
* customizable interbody forces, collision detection, and resolution, allowing for
270+
* a wide variety of physical interactions.
271+
*
272+
* @property bodies The list of [Body] objects participating in the simulation. Each body
273+
* has its own properties, forces, and constraints that determine its behavior.
274+
* @property broadPhaseCollisionDetector The collision detection algorithm used to identify
275+
* potential overlapping pairs of bodies during the simulation. It operates in the broad-phase
276+
* stage of collision resolution.
277+
* @property collisionConstraint The collision resolution handler responsible for determining
278+
* the outcome of detected collisions between bodies.
279+
* @property interbodyForces A list of interbody forces that affect pairs of bodies in the simulation.
280+
* These forces are computed per pair of bodies and influence their dynamics.
281+
* @property context The coroutine context in which the simulation runs, allowing for asynchronous
282+
* computations. By default, it uses [Dispatchers.Default].
283+
*/
148284
class ForceSimulation(val bodies: MutableList<Body> = mutableListOf()) {
149285

150286
var broadPhaseCollisionDetector: BroadPhaseCollisionDetector? = null

orx-force-2d/src/commonMain/kotlin/GravityForce.kt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
package org.openrndr.extra.force2d
22
import org.openrndr.math.Vector2
33

4+
/**
5+
* Represents a constant gravitational force applied to a physical body in a simulation.
6+
*
7+
* The `GravityForce` class is a specific implementation of the [Force] interface.
8+
* It applies a constant acceleration, defined by the `gravity` vector, to all nodes
9+
* of the body over time. The force is applied during every simulation frame,
10+
* updating the velocity of the nodes based on the gravitational vector and the elapsed time step.
11+
*
12+
* @property gravity the gravitational acceleration vector applied to the body.
13+
* Default value is [Vector2.ZERO], indicating no gravity unless explicitly configured.
14+
*
15+
* The `initializeFrame` method is a placeholder in this implementation and does not perform any initialization logic.
16+
*
17+
* The `apply` method applies the gravitational force by calculating the change in velocity
18+
* for each node in the body. It updates the velocity of each node over the given time step, `dt`.
19+
*/
420
class GravityForce : Force {
521
var gravity = Vector2.ZERO
622

@@ -14,6 +30,21 @@ class GravityForce : Force {
1430
}
1531
}
1632

33+
/**
34+
* Adds a gravitational force to the body and allows configuring its properties.
35+
*
36+
* This method attaches a new instance of [GravityForce] to the body, applying a constant
37+
* gravitational acceleration defined in the configuration block. The force affects all
38+
* nodes in the body during simulation steps.
39+
*
40+
* @param configure a lambda to configure the properties of the [GravityForce] instance,
41+
* such as setting the gravitational acceleration vector.
42+
*/
1743
fun Body.gravity(configure: GravityForce.() -> Unit) = forces.add(GravityForce().apply(configure))
1844

45+
/**
46+
* Applies a gravitational force to the body by adding it to the list of forces acting on the body.
47+
*
48+
* @param gravity the gravitational force to be applied, represented as a [GravityForce] instance.
49+
*/
1950
fun Body.gravity(gravity: GravityForce) = forces.add(gravity)

orx-force-2d/src/commonMain/kotlin/LinkLengthConstraint.kt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
package org.openrndr.extra.force2d
22

3-
class LinkLengthConstraint( val body: Body, var compliance: Double = 0.0, var iterations: Int = 1) :
3+
/**
4+
* A constraint that enforces the lengths of links between nodes within a [Body].
5+
*
6+
* The [LinkLengthConstraint] ensures that links connecting nodes in the [Body] adhere to their
7+
* defined rest lengths. It computes corrections over a number of iterations to resolve any
8+
* deviations in length while considering compliance, which introduces flexibility into the system.
9+
*
10+
* @property body the [Body] that this constraint operates on.
11+
* @property compliance a parameter controlling the flexibility of the links. Higher compliance
12+
* allows greater deviation from the rest lengths.
13+
* @property iterations the number of iterations to refine the resolution of the constraint for
14+
* each time step.
15+
*
16+
* This constraint works by computing the deviation of each link's current length from its rest
17+
* length and applying corrective forces iteratively to the connected nodes. It takes into
18+
* account the mass of the nodes and compliance to distribute corrections appropriately.
19+
*/
20+
class LinkLengthConstraint(val body: Body, var compliance: Double = 0.0, var iterations: Int = 1) :
421
Constraint {
522

623
private var lambdas: DoubleArray = DoubleArray(body.links.size)
@@ -48,6 +65,6 @@ class LinkLengthConstraint( val body: Body, var compliance: Double = 0.0, var it
4865
}
4966
}
5067

51-
fun Body.linkLengthConstraint(configure : LinkLengthConstraint.() -> Unit = {}) {
68+
fun Body.linkLengthConstraint(configure: LinkLengthConstraint.() -> Unit = {}) {
5269
constraints.add(LinkLengthConstraint(this).apply(configure))
5370
}

0 commit comments

Comments
 (0)