Skip to content

Commit 47ca2a3

Browse files
update physicsBody naming
update documentation update examples
1 parent fdf2de1 commit 47ca2a3

20 files changed

Lines changed: 943 additions & 91 deletions

File tree

docs/bodies.md

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Bodies
22

3-
Bodies are registered via `Modifier.physicsBody(...)`. The modifier binds a Composable to a physics body and allows the runtime to drive its translation and rotation.
3+
Bodies (childs) are registered via `Modifier.physicsBody(...)` as a first childs of the PhysicsBox. The modifier binds a Composable to a physics body and allows the runtime to drive its translation and rotation.
44

55
## Basic usage
66
```kotlin
@@ -16,24 +16,31 @@ Box(
1616
- Use immutable types (`String`, `Long`, enums) when possible.
1717

1818
## PhysicsBodyConfig
19-
`PhysicsBodyConfig` defines material and motion parameters:
20-
- `bodyType`: `Dynamic`, `Static`, `Kinematic`
21-
- `density`, `friction`, `restitution`
22-
- `linearDamping`, `angularDamping`
23-
- `fixedRotation`, `allowSleep`, `isBullet`, `gravityScale`
24-
- `initialTransform` (position in px, rotation in degrees)
19+
`PhysicsBodyConfig` defines material and motion parameters for a physics body:
20+
- `bodyType`: Dynamic, Static, Kinematic
21+
- `density`: body density (applied only to Dynamic bodies; must be >= 0)
22+
- `friction`: surface friction coefficient (must be >= 0)
23+
- `restitution`: bounciness coefficient (must be >= 0)
24+
- `linearDamping`: linear velocity damping (must be >= 0)
25+
- `angularDamping`: angular velocity damping (must be >= 0)
26+
- `fixedRotation`: prevents rotation when true
27+
- `allowSleep`: enables sleeping/auto-deactivation (default true)
28+
- `isBullet`: enables continuous collision detection for fast-moving bodies
29+
- `gravityScale`: gravity multiplier for this body
30+
- `initialTransform`: initial position (px) and rotation (degrees)
2531

2632
Example:
2733
```kotlin
28-
.physicsBody(
34+
modifier = Modifier.physicsBody(
2935
key = "floor",
3036
config = PhysicsBodyConfig(
3137
bodyType = BodyType.Static,
38+
density = 1f,
3239
friction = 0.6f,
33-
restitution = 0.1f,
40+
restitution = 0.5f,
3441
initialTransform = PhysicsTransform(
3542
vector2 = PhysicsVector2(160f, 360f)
36-
)
43+
), // ...
3744
)
3845
)
3946
```
@@ -43,10 +50,31 @@ Example:
4350
- `vector2`: position in container px
4451
- `rotationDegrees`: rotation in degrees (clockwise in Y‑down screen space)
4552

53+
## Shapes
54+
Collision shape (Physical shape) used by the engine.
55+
56+
## CollisionFilter
57+
Collision filtering rules (category/mask/group or equivalent).
58+
59+
## Draggability
60+
`isDraggable` - enables pointer dragging for this body.
61+
62+
## DragConfig
63+
This configuration controls how a body follows the pointer (finger/mouse) during dragging and what happens when the drag ends (fling).
64+
65+
## CallBacks
66+
- `onCollision()`
67+
- `onSleepChanged()`
68+
- `onDragStart()`
69+
- `onDragEnd()`
70+
4671
## Common pitfalls
4772
!!! tip "Avoiding body re-creation"
4873
PhysicsBox tracks bodies by key. Reusing the same key for a different Composable can cause callbacks
4974
to be delivered to the wrong element. Make keys stable and unique.
5075

5176
!!! warning "Config changes"
5277
Changing shape or size triggers fixture rebuilds. This is supported but can be expensive if done every frame.
78+
79+
## Note
80+
PhysicsBox can also be a child of another PhysicsBox and contains its own rules and laws.

docs/concepts.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ PhysicsBox is built around a small set of concepts: a simulation container, a st
1111
## PhysicsBoxState
1212
`PhysicsBoxState` is the mutable controller for the world. It exposes:
1313
- pause/resume (`pause()`, `resume()`, `isPaused`)
14-
- gravity (`setWorldGravity(...)`, `gravity`)
14+
- gravity (`setWorldGravity(...)`, `gravity`, `updateGravity(...)`)
1515
- step configuration (`stepConfig`, `updateStepConfig(...)`)
1616
- command helpers (`enqueueImpulse`, `enqueueVelocity`)
1717
- step callback (`setOnStepListener`)
18-
19-
The runtime drains commands and applies them to the physics backend.
18+
- apply an impulse or set velocity for bodies by their keys (`enqueueImpulse(...)`, `enqueueVelocity(...)`).
19+
- etc. (see API)
2020

2121
## Fixed‑step stepping
2222
PhysicsBox uses a fixed timestep to keep simulation stable across frame rates. See `StepConfig`:
@@ -30,7 +30,7 @@ Gravity is configured in **physics units** (m/s²). The default is:
3030
```kotlin
3131
PhysicsDefaults.Gravity // (0f, 9.8f)
3232
```
33-
Update it via `PhysicsBoxState.setWorldGravity(...)`.
33+
Update it using `PhysicsBoxState.updateGravity(...)`.
3434

3535
## Boundaries
3636
`BoundariesConfig` creates static walls around the container. This keeps bodies inside the visible area and lets you control restitution/friction at the edges.

docs/getting-started.md

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -38,43 +38,50 @@ dependencies {
3838
}
3939
```
4040

41-
## Minimal example
41+
## Minimal example with two childs (dynamic and static) with minimum of parameters
4242
```kotlin
43-
import androidx.compose.foundation.background
44-
import androidx.compose.foundation.layout.Box
45-
import androidx.compose.foundation.layout.fillMaxSize
46-
import androidx.compose.foundation.layout.size
47-
import androidx.compose.runtime.Composable
48-
import androidx.compose.ui.Modifier
49-
import androidx.compose.ui.graphics.Color
50-
import androidx.compose.ui.platform.LocalDensity
51-
import androidx.compose.ui.unit.dp
52-
import dev.zinchenko.physicsbox.PhysicsVector2
53-
import dev.zinchenko.physicsbox.layout.PhysicsBox
54-
import dev.zinchenko.physicsbox.physicsbody.PhysicsBodyConfig
55-
import dev.zinchenko.physicsbox.physicsbody.PhysicsTransform
56-
import dev.zinchenko.physicsbox.physicsbody.physicsBody
57-
import dev.zinchenko.physicsbox.rememberPhysicsBoxState
58-
5943
@Composable
6044
fun SimplePhysicsScene() {
61-
val state = rememberPhysicsBoxState()
62-
val density = LocalDensity.current
63-
64-
val start = with(density) { PhysicsVector2(120.dp.toPx(), 40.dp.toPx()) }
6545

66-
PhysicsBox(modifier = Modifier.fillMaxSize(), state = state) {
46+
PhysicsBox(
47+
modifier = Modifier
48+
.fillMaxSize(),
49+
state = rememberPhysicsBoxState()
50+
) {
6751
Box(
6852
Modifier
6953
.size(80.dp)
54+
.clip(CircleShape)
55+
.physicsBody(
56+
key = "dynamic_circle",
57+
shape = PhysicsShape.Circle(),
58+
config = PhysicsBodyConfig(
59+
initialTransform = PhysicsTransform(
60+
vector2 = PhysicsVector2(x = 781f, y = 0f)
61+
),
62+
restitution = 0.8f
63+
),
64+
)
7065
.background(Color.Red)
66+
)
67+
68+
Box(
69+
Modifier
70+
.size(80.dp)
7171
.physicsBody(
72-
key = "box",
72+
key = "static_box",
7373
config = PhysicsBodyConfig(
74-
initialTransform = PhysicsTransform(vector2 = start),
74+
bodyType = BodyType.Static,
75+
initialTransform = PhysicsTransform(
76+
vector2 = PhysicsVector2(780f, 860f),
77+
rotationDegrees = 45f
78+
),
7579
),
7680
)
81+
.background(Color.LightGray)
7782
)
7883
}
7984
}
8085
```
86+
87+
The default shape for any .physicsBody() is Square. As you can see from the example, the Compose shape should match the PhysicsShape (but it is not required). You also do not have to define the size for PhysicsShape: it takes the size from the composable (but you can override it if you need different sizes for the physical and composable shapes).

docs/index.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,19 @@ The runtime uses JBox2D on JVM platforms, providing stable rigid‑body simulati
1717
- `Modifier.physicsBody` to register Composables as physics bodies.
1818
- Shapes: box, circle, polygon.
1919
- Dragging with joint‑style or direct control.
20-
- Collision, drag, and step events.
20+
- Collision, gravity simulation and step events.
2121

2222
## Minimal snippet
2323
```kotlin
2424
val state = rememberPhysicsBoxState()
2525

2626
PhysicsBox(modifier = Modifier.fillMaxSize(), state = state) {
2727
Box(
28-
Modifier
29-
.size(80.dp)
30-
.physicsBody(key = "box")
28+
modifier = Modifier
29+
.size(64.dp)
30+
.clip(CutCornerShape(0.dp))
31+
.physicsBody(key = "default_shape")
32+
.background(Color.Red),
3133
)
3234
}
3335
```

docs/recipes/dragging.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ PhysicsBox exposes pointer dragging via `Modifier.physicsBody(...)` using `DragC
44

55
## Joint‑style drag (default)
66
```kotlin
7-
.physicsBody(
7+
Modifier.physicsBody(
88
key = "ball",
99
dragConfig = DragConfig(
1010
useJointStyleDrag = true,
@@ -19,7 +19,7 @@ Joint‑style drag uses a spring‑like constraint (similar to a MouseJoint). Th
1919

2020
## Direct drag
2121
```kotlin
22-
.physicsBody(
22+
Modifier.physicsBody(
2323
key = "card",
2424
dragConfig = DragConfig(
2525
useJointStyleDrag = false,

docs/recipes/restitution.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
## Body tuning
66
```kotlin
7-
.physicsBody(
7+
Modifier.physicsBody(
88
key = "ball",
99
config = PhysicsBodyConfig(
1010
restitution = 0.8f,

docs/shapes.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ Shapes are provided by `PhysicsShape` and define collision geometry. The engine
66
`PhysicsShape.Box` uses the Composable bounds as a rectangle.
77

88
```kotlin
9-
.physicsBody(key = "box", shape = PhysicsShape.Box)
9+
Modifier.physicsBody(key = "box", shape = PhysicsShape.Box)
1010
```
1111

1212
## Circle
1313
`PhysicsShape.Circle(radiusPx)` optionally specifies a radius in pixels. If `radiusPx` is null, the runtime derives it from the Composable size.
1414

1515
```kotlin
16-
.physicsBody(
16+
Modifier.physicsBody(
1717
key = "ball",
1818
shape = PhysicsShape.Circle(radiusPx = 40f)
1919
)
@@ -30,7 +30,7 @@ val verts = listOf(
3030
PhysicsVector2(-0.2f, 0.5f),
3131
)
3232

33-
.physicsBody(
33+
Modifier.physicsBody(
3434
key = "poly",
3535
shape = PhysicsShape.Polygon(
3636
vertices = verts,

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ compileSdk = "36"
44
targetSdk = "36"
55
minSdk = "23"
66

7-
packageVersion = "1.0.1"
7+
packageVersion = "1.0.2"
88

99
kotlin = "2.3.0"
1010
compose-multiplatform = "1.10.0"

physicsbox/src/commonMain/kotlin/dev/zinchenko/physicsbox/layout/PhysicsBox.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ private object PhysicsBoxScopeImpl : PhysicsBoxScope {
259259
key = key,
260260
config = config,
261261
shape = shape,
262-
filter = filter,
262+
collisionFilter = filter,
263263
isDraggable = isDraggable,
264264
dragConfig = dragConfig,
265265
onCollision = onCollision,
@@ -269,6 +269,11 @@ private object PhysicsBoxScopeImpl : PhysicsBoxScope {
269269
)
270270
}
271271

272+
@Deprecated("Not yet implented")
272273
internal val LocalPhysicsBoxModifier = staticCompositionLocalOf<Modifier> { Modifier }
274+
275+
@Deprecated("Not yet implented")
273276
internal val LocalPhysicsBoxConfig = staticCompositionLocalOf { PhysicsBoxConfig() }
277+
278+
@Deprecated("Not yet implented")
274279
internal val LocalPhysicsDebugConfig = staticCompositionLocalOf { PhysicsDebugConfig() }

physicsbox/src/commonMain/kotlin/dev/zinchenko/physicsbox/layout/PhysicsBoxLayout.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import androidx.compose.ui.Modifier
99
import androidx.compose.ui.graphics.TransformOrigin
1010
import androidx.compose.ui.layout.Layout
1111
import androidx.compose.ui.layout.LayoutCoordinates
12-
import androidx.compose.ui.layout.onGloballyPositioned
1312
import androidx.compose.ui.layout.Placeable
13+
import androidx.compose.ui.layout.onGloballyPositioned
1414
import androidx.compose.ui.unit.Constraints
1515
import dev.zinchenko.physicsbox.LocalPhysicsBoxCoordinates
1616
import dev.zinchenko.physicsbox.engine.PhysicsWorldEngine
@@ -91,17 +91,14 @@ internal fun PhysicsBoxLayout(
9191
)
9292
}
9393

94-
if (!hasValidBounds) {
94+
if (hasValidBounds.not()) {
9595
return@Layout layout(layoutWidth, layoutHeight) {
9696
for (child in measuredChildren) {
9797
child.placeable.placeRelative(0, 0)
9898
}
9999
}
100100
}
101101

102-
@Suppress("UNUSED_VARIABLE")
103-
val tick = frameTick.value
104-
105102
val snapshot = engine.snapshotPx()
106103
val bodyByKey = snapshot.bodiesByKey
107104

0 commit comments

Comments
 (0)