@guinetik/gcanvas 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/demos/fluid-simple.html +22 -0
- package/demos/fluid.html +37 -0
- package/demos/index.html +2 -0
- package/demos/js/blob.js +18 -5
- package/demos/js/fluid-simple.js +253 -0
- package/demos/js/fluid.js +527 -0
- package/demos/js/tde/accretiondisk.js +64 -11
- package/demos/js/tde/blackholescene.js +2 -2
- package/demos/js/tde/config.js +2 -2
- package/demos/js/tde/index.js +152 -27
- package/demos/js/tde/lensedstarfield.js +32 -25
- package/demos/js/tde/tdestar.js +78 -98
- package/demos/js/tde/tidalstream.js +23 -7
- package/docs/README.md +230 -222
- package/docs/api/FluidSystem.md +173 -0
- package/docs/concepts/architecture-overview.md +204 -204
- package/docs/concepts/rendering-pipeline.md +279 -279
- package/docs/concepts/two-layer-architecture.md +229 -229
- package/docs/fluid-dynamics.md +97 -0
- package/docs/getting-started/first-game.md +354 -354
- package/docs/getting-started/installation.md +175 -157
- package/docs/modules/collision/README.md +2 -2
- package/docs/modules/fluent/README.md +6 -6
- package/docs/modules/game/README.md +303 -303
- package/docs/modules/isometric-camera.md +2 -2
- package/docs/modules/isometric.md +1 -1
- package/docs/modules/painter/README.md +328 -328
- package/docs/modules/particle/README.md +3 -3
- package/docs/modules/shapes/README.md +221 -221
- package/docs/modules/shapes/base/euclidian.md +123 -123
- package/docs/modules/shapes/base/shape.md +262 -262
- package/docs/modules/shapes/base/transformable.md +243 -243
- package/docs/modules/state/README.md +2 -2
- package/docs/modules/util/README.md +1 -1
- package/docs/modules/util/camera3d.md +3 -3
- package/docs/modules/util/scene3d.md +1 -1
- package/package.json +3 -1
- package/readme.md +19 -5
- package/src/collision/collision.js +75 -0
- package/src/game/index.js +2 -1
- package/src/game/pipeline.js +3 -3
- package/src/game/systems/FluidSystem.js +835 -0
- package/src/game/systems/index.js +11 -0
- package/src/game/ui/button.js +39 -18
- package/src/game/ui/cursor.js +14 -0
- package/src/game/ui/fps.js +12 -4
- package/src/game/ui/index.js +2 -0
- package/src/game/ui/stepper.js +549 -0
- package/src/game/ui/theme.js +121 -0
- package/src/game/ui/togglebutton.js +9 -3
- package/src/game/ui/tooltip.js +11 -4
- package/src/math/fluid.js +507 -0
- package/src/math/index.js +2 -0
- package/src/mixins/anchor.js +17 -7
- package/src/motion/tweenetik.js +16 -0
- package/src/shapes/index.js +1 -0
- package/src/util/camera3d.js +218 -12
- package/types/fluent.d.ts +361 -0
- package/types/game.d.ts +303 -0
- package/types/index.d.ts +144 -5
- package/types/math.d.ts +361 -0
- package/types/motion.d.ts +271 -0
- package/types/particle.d.ts +373 -0
- package/types/shapes.d.ts +107 -9
- package/types/util.d.ts +353 -0
- package/types/webgl.d.ts +109 -0
- package/disk_example.png +0 -0
- package/tde.png +0 -0
|
@@ -1,229 +1,229 @@
|
|
|
1
|
-
# Two-Layer Architecture
|
|
2
|
-
|
|
3
|
-
> Understanding when to use the Shape Layer vs the Game Layer.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
GCanvas provides two complementary approaches to building canvas applications:
|
|
8
|
-
|
|
9
|
-
1. **Shape Layer** - For declarative, static graphics
|
|
10
|
-
2. **Game Layer** - For interactive, dynamic applications
|
|
11
|
-
|
|
12
|
-
You can use either layer independently or combine them for complex applications.
|
|
13
|
-
|
|
14
|
-
## Shape Layer
|
|
15
|
-
|
|
16
|
-
The Shape Layer is for drawing graphics without a game loop. Shapes are self-contained objects that know how to render themselves.
|
|
17
|
-
|
|
18
|
-
### When to Use
|
|
19
|
-
|
|
20
|
-
- Static visualizations
|
|
21
|
-
- Data charts and graphs
|
|
22
|
-
- Decorative graphics
|
|
23
|
-
- Simple animations (manual redraw)
|
|
24
|
-
- Learning canvas fundamentals
|
|
25
|
-
|
|
26
|
-
### Key Classes
|
|
27
|
-
|
|
28
|
-
- `Shape` and subclasses (`Circle`, `Rectangle`, `Star`, etc.)
|
|
29
|
-
- `Group` for composing shapes
|
|
30
|
-
- `Painter` for direct canvas control
|
|
31
|
-
|
|
32
|
-
### Example
|
|
33
|
-
|
|
34
|
-
```js
|
|
35
|
-
import { Circle, Rectangle, Group, Painter } from 'gcanvas';
|
|
36
|
-
|
|
37
|
-
// Initialize Painter with canvas context
|
|
38
|
-
const canvas = document.getElementById('canvas');
|
|
39
|
-
Painter.init(canvas.getContext('2d'));
|
|
40
|
-
|
|
41
|
-
// Create shapes
|
|
42
|
-
const circle = new Circle(50, { x: 100, y: 100, color: 'red' });
|
|
43
|
-
const rect = new Rectangle(80, 40, { x: 200, y: 100, color: 'blue' });
|
|
44
|
-
|
|
45
|
-
// Draw immediately
|
|
46
|
-
Painter.clear();
|
|
47
|
-
circle.draw();
|
|
48
|
-
rect.draw();
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Characteristics
|
|
52
|
-
|
|
53
|
-
| Aspect | Shape Layer |
|
|
54
|
-
|--------|-------------|
|
|
55
|
-
| Loop | None (manual redraw) |
|
|
56
|
-
| State | Shapes hold their own state |
|
|
57
|
-
| Input | Manual event handling |
|
|
58
|
-
| Animation | Call draw() on each frame manually |
|
|
59
|
-
| Complexity | Simple, direct |
|
|
60
|
-
|
|
61
|
-
## Game Layer
|
|
62
|
-
|
|
63
|
-
The Game Layer provides a complete game loop with automatic updates, rendering, and input handling.
|
|
64
|
-
|
|
65
|
-
### When to Use
|
|
66
|
-
|
|
67
|
-
- Games and simulations
|
|
68
|
-
- Interactive applications
|
|
69
|
-
- Real-time animations
|
|
70
|
-
- Complex state management
|
|
71
|
-
- Multi-object coordination
|
|
72
|
-
|
|
73
|
-
### Key Classes
|
|
74
|
-
|
|
75
|
-
- `Game` - Main loop and canvas management
|
|
76
|
-
- `Pipeline` - Object lifecycle management
|
|
77
|
-
- `GameObject` - Interactive entities
|
|
78
|
-
- `Scene` - Hierarchical organization
|
|
79
|
-
|
|
80
|
-
### Example
|
|
81
|
-
|
|
82
|
-
```js
|
|
83
|
-
import { Game, Scene, GameObject, Circle } from 'gcanvas';
|
|
84
|
-
|
|
85
|
-
class Player extends GameObject {
|
|
86
|
-
constructor(game) {
|
|
87
|
-
super(game);
|
|
88
|
-
this.shape = new Circle(40, { color: 'blue' });
|
|
89
|
-
this.enableInteractivity(this.shape);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
update(dt) {
|
|
93
|
-
// Called every frame
|
|
94
|
-
if (this.game.input.isKeyDown('ArrowRight')) {
|
|
95
|
-
this.shape.x += 200 * dt;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
render() {
|
|
100
|
-
// Called every frame after update
|
|
101
|
-
this.shape.draw();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
onPointerDown(e) {
|
|
105
|
-
// Automatic input handling
|
|
106
|
-
console.log('Clicked!');
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
class MyGame extends Game {
|
|
111
|
-
init() {
|
|
112
|
-
this.enableFluidSize();
|
|
113
|
-
const scene = new Scene(this);
|
|
114
|
-
scene.add(new Player(this));
|
|
115
|
-
this.pipeline.add(scene);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const game = new MyGame(document.getElementById('canvas'));
|
|
120
|
-
game.start();
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Characteristics
|
|
124
|
-
|
|
125
|
-
| Aspect | Game Layer |
|
|
126
|
-
|--------|-------------|
|
|
127
|
-
| Loop | Automatic (requestAnimationFrame) |
|
|
128
|
-
| State | Managed by Pipeline |
|
|
129
|
-
| Input | Automatic dispatching to objects |
|
|
130
|
-
| Animation | Built-in with dt (delta time) |
|
|
131
|
-
| Complexity | Full-featured |
|
|
132
|
-
|
|
133
|
-
## Comparison
|
|
134
|
-
|
|
135
|
-
```
|
|
136
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
137
|
-
│ Shape Layer │
|
|
138
|
-
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
|
139
|
-
│ │ Circle │ │Rectangle│ │ Star │ ... more shapes │
|
|
140
|
-
│ └────┬────┘ └────┬────┘ └────┬────┘ │
|
|
141
|
-
│ │ │ │ │
|
|
142
|
-
│ └────────────┼────────────┘ │
|
|
143
|
-
│ ▼ │
|
|
144
|
-
│ ┌──────────┐ │
|
|
145
|
-
│ │ Group │ (optional composition) │
|
|
146
|
-
│ └────┬─────┘ │
|
|
147
|
-
│ │ │
|
|
148
|
-
│ ▼ │
|
|
149
|
-
│ ┌──────────┐ │
|
|
150
|
-
│ │ draw() │ (manual call) │
|
|
151
|
-
│ └──────────┘ │
|
|
152
|
-
└─────────────────────────────────────────────────────────────┘
|
|
153
|
-
|
|
154
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
155
|
-
│ Game Layer │
|
|
156
|
-
│ ┌─────────────────────────────────────────────────────┐ │
|
|
157
|
-
│ │ Game │ │
|
|
158
|
-
│ │ ┌─────────────────────────────────────────────┐ │ │
|
|
159
|
-
│ │ │ Pipeline │ │ │
|
|
160
|
-
│ │ │ ┌─────────────────────────────────────┐ │ │ │
|
|
161
|
-
│ │ │ │ Scene │ │ │ │
|
|
162
|
-
│ │ │ │ ┌───────────┐ ┌───────────┐ │ │ │ │
|
|
163
|
-
│ │ │ │ │GameObject │ │GameObject │ ... │ │ │ │
|
|
164
|
-
│ │ │ │ │ + Shape │ │ + Shape │ │ │ │ │
|
|
165
|
-
│ │ │ │ └───────────┘ └───────────┘ │ │ │ │
|
|
166
|
-
│ │ │ └─────────────────────────────────────┘ │ │ │
|
|
167
|
-
│ │ └─────────────────────────────────────────────┘ │ │
|
|
168
|
-
│ └─────────────────────────────────────────────────────┘ │
|
|
169
|
-
│ │ │
|
|
170
|
-
│ ▼ │
|
|
171
|
-
│ ┌────────────┐ │
|
|
172
|
-
│ │ Game Loop │ (automatic) │
|
|
173
|
-
│ │ update(dt) │ │
|
|
174
|
-
│ │ render() │ │
|
|
175
|
-
│ └────────────┘ │
|
|
176
|
-
└─────────────────────────────────────────────────────────────┘
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## Mixing Layers
|
|
180
|
-
|
|
181
|
-
You can use shapes without GameObjects, or combine both approaches:
|
|
182
|
-
|
|
183
|
-
```js
|
|
184
|
-
class HybridGame extends Game {
|
|
185
|
-
init() {
|
|
186
|
-
// Game layer for interactive elements
|
|
187
|
-
const scene = new Scene(this);
|
|
188
|
-
scene.add(new Player(this));
|
|
189
|
-
this.pipeline.add(scene);
|
|
190
|
-
|
|
191
|
-
// Shape layer for static background
|
|
192
|
-
this.background = new Group();
|
|
193
|
-
this.background.add(new Rectangle(this.width, this.height, { color: '#222' }));
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
render() {
|
|
197
|
-
// Draw static shapes first
|
|
198
|
-
this.background.draw();
|
|
199
|
-
|
|
200
|
-
// Then let pipeline draw interactive objects
|
|
201
|
-
super.render();
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
## Decision Guide
|
|
207
|
-
|
|
208
|
-
| If you need... | Use |
|
|
209
|
-
|---------------|-----|
|
|
210
|
-
| Static graphics | Shape Layer |
|
|
211
|
-
| Simple animations | Shape Layer + manual loop |
|
|
212
|
-
| Keyboard/mouse input | Game Layer |
|
|
213
|
-
| Multiple interactive objects | Game Layer |
|
|
214
|
-
| Collision detection | Game Layer |
|
|
215
|
-
| Scene organization | Game Layer |
|
|
216
|
-
| Quick prototype | Shape Layer |
|
|
217
|
-
| Full game | Game Layer |
|
|
218
|
-
|
|
219
|
-
## Related
|
|
220
|
-
|
|
221
|
-
- [Architecture Overview](./architecture-overview.md) - Full system architecture
|
|
222
|
-
- [Rendering Pipeline](./rendering-pipeline.md) - Shape inheritance chain
|
|
223
|
-
- [Game Lifecycle](./lifecycle.md) - Update/render cycle
|
|
224
|
-
|
|
225
|
-
## See Also
|
|
226
|
-
|
|
227
|
-
- [Shapes Module](../modules/shapes/README.md)
|
|
228
|
-
- [Game Module](../modules/game/README.md)
|
|
229
|
-
- [Getting Started: Hello World](../getting-started/hello-world.md)
|
|
1
|
+
# Two-Layer Architecture
|
|
2
|
+
|
|
3
|
+
> Understanding when to use the Shape Layer vs the Game Layer.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
GCanvas provides two complementary approaches to building canvas applications:
|
|
8
|
+
|
|
9
|
+
1. **Shape Layer** - For declarative, static graphics
|
|
10
|
+
2. **Game Layer** - For interactive, dynamic applications
|
|
11
|
+
|
|
12
|
+
You can use either layer independently or combine them for complex applications.
|
|
13
|
+
|
|
14
|
+
## Shape Layer
|
|
15
|
+
|
|
16
|
+
The Shape Layer is for drawing graphics without a game loop. Shapes are self-contained objects that know how to render themselves.
|
|
17
|
+
|
|
18
|
+
### When to Use
|
|
19
|
+
|
|
20
|
+
- Static visualizations
|
|
21
|
+
- Data charts and graphs
|
|
22
|
+
- Decorative graphics
|
|
23
|
+
- Simple animations (manual redraw)
|
|
24
|
+
- Learning canvas fundamentals
|
|
25
|
+
|
|
26
|
+
### Key Classes
|
|
27
|
+
|
|
28
|
+
- `Shape` and subclasses (`Circle`, `Rectangle`, `Star`, etc.)
|
|
29
|
+
- `Group` for composing shapes
|
|
30
|
+
- `Painter` for direct canvas control
|
|
31
|
+
|
|
32
|
+
### Example
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
import { Circle, Rectangle, Group, Painter } from '@guinetik/gcanvas';
|
|
36
|
+
|
|
37
|
+
// Initialize Painter with canvas context
|
|
38
|
+
const canvas = document.getElementById('canvas');
|
|
39
|
+
Painter.init(canvas.getContext('2d'));
|
|
40
|
+
|
|
41
|
+
// Create shapes
|
|
42
|
+
const circle = new Circle(50, { x: 100, y: 100, color: 'red' });
|
|
43
|
+
const rect = new Rectangle(80, 40, { x: 200, y: 100, color: 'blue' });
|
|
44
|
+
|
|
45
|
+
// Draw immediately
|
|
46
|
+
Painter.clear();
|
|
47
|
+
circle.draw();
|
|
48
|
+
rect.draw();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Characteristics
|
|
52
|
+
|
|
53
|
+
| Aspect | Shape Layer |
|
|
54
|
+
|--------|-------------|
|
|
55
|
+
| Loop | None (manual redraw) |
|
|
56
|
+
| State | Shapes hold their own state |
|
|
57
|
+
| Input | Manual event handling |
|
|
58
|
+
| Animation | Call draw() on each frame manually |
|
|
59
|
+
| Complexity | Simple, direct |
|
|
60
|
+
|
|
61
|
+
## Game Layer
|
|
62
|
+
|
|
63
|
+
The Game Layer provides a complete game loop with automatic updates, rendering, and input handling.
|
|
64
|
+
|
|
65
|
+
### When to Use
|
|
66
|
+
|
|
67
|
+
- Games and simulations
|
|
68
|
+
- Interactive applications
|
|
69
|
+
- Real-time animations
|
|
70
|
+
- Complex state management
|
|
71
|
+
- Multi-object coordination
|
|
72
|
+
|
|
73
|
+
### Key Classes
|
|
74
|
+
|
|
75
|
+
- `Game` - Main loop and canvas management
|
|
76
|
+
- `Pipeline` - Object lifecycle management
|
|
77
|
+
- `GameObject` - Interactive entities
|
|
78
|
+
- `Scene` - Hierarchical organization
|
|
79
|
+
|
|
80
|
+
### Example
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
import { Game, Scene, GameObject, Circle } from '@guinetik/gcanvas';
|
|
84
|
+
|
|
85
|
+
class Player extends GameObject {
|
|
86
|
+
constructor(game) {
|
|
87
|
+
super(game);
|
|
88
|
+
this.shape = new Circle(40, { color: 'blue' });
|
|
89
|
+
this.enableInteractivity(this.shape);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
update(dt) {
|
|
93
|
+
// Called every frame
|
|
94
|
+
if (this.game.input.isKeyDown('ArrowRight')) {
|
|
95
|
+
this.shape.x += 200 * dt;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
render() {
|
|
100
|
+
// Called every frame after update
|
|
101
|
+
this.shape.draw();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
onPointerDown(e) {
|
|
105
|
+
// Automatic input handling
|
|
106
|
+
console.log('Clicked!');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
class MyGame extends Game {
|
|
111
|
+
init() {
|
|
112
|
+
this.enableFluidSize();
|
|
113
|
+
const scene = new Scene(this);
|
|
114
|
+
scene.add(new Player(this));
|
|
115
|
+
this.pipeline.add(scene);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const game = new MyGame(document.getElementById('canvas'));
|
|
120
|
+
game.start();
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Characteristics
|
|
124
|
+
|
|
125
|
+
| Aspect | Game Layer |
|
|
126
|
+
|--------|-------------|
|
|
127
|
+
| Loop | Automatic (requestAnimationFrame) |
|
|
128
|
+
| State | Managed by Pipeline |
|
|
129
|
+
| Input | Automatic dispatching to objects |
|
|
130
|
+
| Animation | Built-in with dt (delta time) |
|
|
131
|
+
| Complexity | Full-featured |
|
|
132
|
+
|
|
133
|
+
## Comparison
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
137
|
+
│ Shape Layer │
|
|
138
|
+
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
|
139
|
+
│ │ Circle │ │Rectangle│ │ Star │ ... more shapes │
|
|
140
|
+
│ └────┬────┘ └────┬────┘ └────┬────┘ │
|
|
141
|
+
│ │ │ │ │
|
|
142
|
+
│ └────────────┼────────────┘ │
|
|
143
|
+
│ ▼ │
|
|
144
|
+
│ ┌──────────┐ │
|
|
145
|
+
│ │ Group │ (optional composition) │
|
|
146
|
+
│ └────┬─────┘ │
|
|
147
|
+
│ │ │
|
|
148
|
+
│ ▼ │
|
|
149
|
+
│ ┌──────────┐ │
|
|
150
|
+
│ │ draw() │ (manual call) │
|
|
151
|
+
│ └──────────┘ │
|
|
152
|
+
└─────────────────────────────────────────────────────────────┘
|
|
153
|
+
|
|
154
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
155
|
+
│ Game Layer │
|
|
156
|
+
│ ┌─────────────────────────────────────────────────────┐ │
|
|
157
|
+
│ │ Game │ │
|
|
158
|
+
│ │ ┌─────────────────────────────────────────────┐ │ │
|
|
159
|
+
│ │ │ Pipeline │ │ │
|
|
160
|
+
│ │ │ ┌─────────────────────────────────────┐ │ │ │
|
|
161
|
+
│ │ │ │ Scene │ │ │ │
|
|
162
|
+
│ │ │ │ ┌───────────┐ ┌───────────┐ │ │ │ │
|
|
163
|
+
│ │ │ │ │GameObject │ │GameObject │ ... │ │ │ │
|
|
164
|
+
│ │ │ │ │ + Shape │ │ + Shape │ │ │ │ │
|
|
165
|
+
│ │ │ │ └───────────┘ └───────────┘ │ │ │ │
|
|
166
|
+
│ │ │ └─────────────────────────────────────┘ │ │ │
|
|
167
|
+
│ │ └─────────────────────────────────────────────┘ │ │
|
|
168
|
+
│ └─────────────────────────────────────────────────────┘ │
|
|
169
|
+
│ │ │
|
|
170
|
+
│ ▼ │
|
|
171
|
+
│ ┌────────────┐ │
|
|
172
|
+
│ │ Game Loop │ (automatic) │
|
|
173
|
+
│ │ update(dt) │ │
|
|
174
|
+
│ │ render() │ │
|
|
175
|
+
│ └────────────┘ │
|
|
176
|
+
└─────────────────────────────────────────────────────────────┘
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Mixing Layers
|
|
180
|
+
|
|
181
|
+
You can use shapes without GameObjects, or combine both approaches:
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
class HybridGame extends Game {
|
|
185
|
+
init() {
|
|
186
|
+
// Game layer for interactive elements
|
|
187
|
+
const scene = new Scene(this);
|
|
188
|
+
scene.add(new Player(this));
|
|
189
|
+
this.pipeline.add(scene);
|
|
190
|
+
|
|
191
|
+
// Shape layer for static background
|
|
192
|
+
this.background = new Group();
|
|
193
|
+
this.background.add(new Rectangle(this.width, this.height, { color: '#222' }));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
render() {
|
|
197
|
+
// Draw static shapes first
|
|
198
|
+
this.background.draw();
|
|
199
|
+
|
|
200
|
+
// Then let pipeline draw interactive objects
|
|
201
|
+
super.render();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Decision Guide
|
|
207
|
+
|
|
208
|
+
| If you need... | Use |
|
|
209
|
+
|---------------|-----|
|
|
210
|
+
| Static graphics | Shape Layer |
|
|
211
|
+
| Simple animations | Shape Layer + manual loop |
|
|
212
|
+
| Keyboard/mouse input | Game Layer |
|
|
213
|
+
| Multiple interactive objects | Game Layer |
|
|
214
|
+
| Collision detection | Game Layer |
|
|
215
|
+
| Scene organization | Game Layer |
|
|
216
|
+
| Quick prototype | Shape Layer |
|
|
217
|
+
| Full game | Game Layer |
|
|
218
|
+
|
|
219
|
+
## Related
|
|
220
|
+
|
|
221
|
+
- [Architecture Overview](./architecture-overview.md) - Full system architecture
|
|
222
|
+
- [Rendering Pipeline](./rendering-pipeline.md) - Shape inheritance chain
|
|
223
|
+
- [Game Lifecycle](./lifecycle.md) - Update/render cycle
|
|
224
|
+
|
|
225
|
+
## See Also
|
|
226
|
+
|
|
227
|
+
- [Shapes Module](../modules/shapes/README.md)
|
|
228
|
+
- [Game Module](../modules/game/README.md)
|
|
229
|
+
- [Getting Started: Hello World](../getting-started/hello-world.md)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Fluid & Gas Dynamics (Math-Only)
|
|
2
|
+
|
|
3
|
+
Pure math helpers for SPH-style liquids and lightweight gas simulation. The math stays in `src/math/fluid.js`; consumers (games/demos) are responsible for applying forces to their particles.
|
|
4
|
+
|
|
5
|
+
## What’s Included
|
|
6
|
+
- SPH density, pressure, and viscosity kernels (`computeDensities`, `computePressures`, `computeFluidForces`).
|
|
7
|
+
- Simplified gas mixing with diffusion/pressure/turbulence (`computeGasForces`).
|
|
8
|
+
- Thermal buoyancy coupling (`computeThermalBuoyancy`) that pairs with `src/math/heat.js`.
|
|
9
|
+
- Force blending and pure Euler integration (`blendForces`, `integrateEuler`).
|
|
10
|
+
- Config factory with no magic numbers (`getDefaultFluidConfig`).
|
|
11
|
+
|
|
12
|
+
## Config
|
|
13
|
+
Defined in `src/math/fluid.js` and merged via `mergeConfig` internally.
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
const CONFIG = {
|
|
17
|
+
kernel: { smoothingRadius: 28 },
|
|
18
|
+
fluid: {
|
|
19
|
+
restDensity: 1.1,
|
|
20
|
+
particleMass: 1,
|
|
21
|
+
pressureStiffness: 1800,
|
|
22
|
+
viscosity: 0.18,
|
|
23
|
+
surfaceTension: 0,
|
|
24
|
+
maxForce: 6000,
|
|
25
|
+
},
|
|
26
|
+
gas: {
|
|
27
|
+
interactionRadius: 34,
|
|
28
|
+
pressure: 12,
|
|
29
|
+
diffusion: 0.08,
|
|
30
|
+
drag: 0.04,
|
|
31
|
+
buoyancy: 260,
|
|
32
|
+
neutralTemperature: 0.5,
|
|
33
|
+
turbulence: 16,
|
|
34
|
+
},
|
|
35
|
+
external: { gravity: { x: 0, y: 820 } },
|
|
36
|
+
};
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Override by passing `{ fluid: { … }, gas: { … }, kernel: { … } }` to any helper.
|
|
40
|
+
|
|
41
|
+
## Particle Shape
|
|
42
|
+
Any object with `{ x, y, vx, vy, size?, mass?, custom? }`. Mass is resolved from:
|
|
43
|
+
`custom.mass` → `mass` → `size` → `config.fluid.particleMass`.
|
|
44
|
+
|
|
45
|
+
## Core API (pure)
|
|
46
|
+
- `computeDensities(particles, cfg?)` → `Float32Array densities`.
|
|
47
|
+
- `computePressures(densities, cfg?)` → `Float32Array pressures`.
|
|
48
|
+
- `computeFluidForces(particles, cfg?)` → `{ forces, densities, pressures }`.
|
|
49
|
+
- `computeGasForces(particles, cfg?)` → `{ forces }`.
|
|
50
|
+
- `computeThermalBuoyancy(particles, cfg?)` → `forces[]` using `temperature` or `custom.temperature`.
|
|
51
|
+
- `blendForces(a, b, t)` → lerped forces.
|
|
52
|
+
- `integrateEuler(particles, forces, dt, cfg?)` → new `{ x, y, vx, vy }[]` (no mutation).
|
|
53
|
+
|
|
54
|
+
## Applying in a Game (pattern)
|
|
55
|
+
1) Build forces:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
import { computeFluidForces, computeGasForces, blendForces, computeThermalBuoyancy } from "../../src/math/fluid.js";
|
|
59
|
+
|
|
60
|
+
const liquid = computeFluidForces(particles, { kernel: { smoothingRadius: 26 } });
|
|
61
|
+
const gas = computeGasForces(particles, { gas: { interactionRadius: 40 } });
|
|
62
|
+
const buoyancy = computeThermalBuoyancy(particles);
|
|
63
|
+
|
|
64
|
+
// Combine as you see fit (example: mix liquid & gas modes, then add buoyancy)
|
|
65
|
+
const mixed = blendForces(liquid.forces, gas.forces, modeT); // modeT: 0..1
|
|
66
|
+
for (let i = 0; i < particles.length; i++) {
|
|
67
|
+
mixed[i].x += buoyancy[i].x;
|
|
68
|
+
mixed[i].y += buoyancy[i].y;
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
2) Apply to your particles (consumer-controlled):
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
// Mutate in your game loop; math module stays pure.
|
|
76
|
+
for (let i = 0; i < particles.length; i++) {
|
|
77
|
+
const p = particles[i];
|
|
78
|
+
const f = mixed[i];
|
|
79
|
+
const mass = p.custom.mass ?? 1;
|
|
80
|
+
p.vx += (f.x / mass) * dt;
|
|
81
|
+
p.vy += (f.y / mass) * dt;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
3) Let your normal updaters move/render (e.g., `ParticleSystem` velocity updater).
|
|
86
|
+
|
|
87
|
+
## Heat Coupling
|
|
88
|
+
Assign `p.temperature` or `p.custom.temperature` each frame (e.g., via `heat.zoneTemperature`). Pass the same particles to `computeThermalBuoyancy` and add the resulting forces.
|
|
89
|
+
|
|
90
|
+
## Notes & Tips
|
|
91
|
+
- Keep `smoothingRadius` proportional to particle spacing (roughly 2–3× average spacing).
|
|
92
|
+
- Clamp velocities in the consumer if you target 10k–20k particles to keep the frame budget.
|
|
93
|
+
- For gases, favor lower `pressure` and higher `diffusion` to avoid jitter.
|
|
94
|
+
- The math never allocates inside the hot path besides output arrays; reuse them between frames if you need fewer allocations (pass your own particles array).
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|