@guinetik/gcanvas 1.0.2 → 1.0.4

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.
Files changed (217) hide show
  1. package/dist/gcanvas.es.js +25656 -0
  2. package/dist/gcanvas.es.min.js +1 -0
  3. package/dist/gcanvas.umd.js +1 -0
  4. package/dist/gcanvas.umd.min.js +1 -0
  5. package/package.json +23 -6
  6. package/src/game/objects/index.js +1 -0
  7. package/src/game/objects/spritesheet.js +260 -0
  8. package/src/game/ui/theme.js +6 -0
  9. package/src/io/keys.js +9 -1
  10. package/src/math/boolean.js +481 -0
  11. package/src/math/index.js +1 -0
  12. package/.github/workflows/release.yaml +0 -70
  13. package/.jshintrc +0 -4
  14. package/.vscode/settings.json +0 -22
  15. package/CLAUDE.md +0 -310
  16. package/blackhole.jpg +0 -0
  17. package/demo.png +0 -0
  18. package/demos/CNAME +0 -1
  19. package/demos/animations.html +0 -31
  20. package/demos/basic.html +0 -38
  21. package/demos/baskara.html +0 -31
  22. package/demos/bezier.html +0 -35
  23. package/demos/beziersignature.html +0 -29
  24. package/demos/blackhole.html +0 -28
  25. package/demos/blob.html +0 -35
  26. package/demos/coordinates.html +0 -698
  27. package/demos/cube3d.html +0 -23
  28. package/demos/demos.css +0 -303
  29. package/demos/dino.html +0 -42
  30. package/demos/easing.html +0 -28
  31. package/demos/events.html +0 -195
  32. package/demos/fluent.html +0 -647
  33. package/demos/fluid-simple.html +0 -22
  34. package/demos/fluid.html +0 -37
  35. package/demos/fractals.html +0 -36
  36. package/demos/gameobjects.html +0 -626
  37. package/demos/genart.html +0 -26
  38. package/demos/gendream.html +0 -26
  39. package/demos/group.html +0 -36
  40. package/demos/home.html +0 -587
  41. package/demos/index.html +0 -376
  42. package/demos/isometric.html +0 -34
  43. package/demos/js/animations.js +0 -452
  44. package/demos/js/basic.js +0 -204
  45. package/demos/js/baskara.js +0 -751
  46. package/demos/js/bezier.js +0 -692
  47. package/demos/js/beziersignature.js +0 -241
  48. package/demos/js/blackhole/accretiondisk.obj.js +0 -379
  49. package/demos/js/blackhole/blackhole.obj.js +0 -318
  50. package/demos/js/blackhole/index.js +0 -409
  51. package/demos/js/blackhole/particle.js +0 -56
  52. package/demos/js/blackhole/starfield.obj.js +0 -218
  53. package/demos/js/blob.js +0 -2276
  54. package/demos/js/coordinates.js +0 -840
  55. package/demos/js/cube3d.js +0 -789
  56. package/demos/js/dino.js +0 -1420
  57. package/demos/js/easing.js +0 -477
  58. package/demos/js/fluent.js +0 -183
  59. package/demos/js/fluid-simple.js +0 -253
  60. package/demos/js/fluid.js +0 -527
  61. package/demos/js/fractals.js +0 -931
  62. package/demos/js/fractalworker.js +0 -93
  63. package/demos/js/gameobjects.js +0 -176
  64. package/demos/js/genart.js +0 -268
  65. package/demos/js/gendream.js +0 -209
  66. package/demos/js/group.js +0 -140
  67. package/demos/js/info-toggle.js +0 -25
  68. package/demos/js/isometric.js +0 -863
  69. package/demos/js/kerr.js +0 -1556
  70. package/demos/js/lavalamp.js +0 -590
  71. package/demos/js/layout.js +0 -354
  72. package/demos/js/mondrian.js +0 -285
  73. package/demos/js/opacity.js +0 -275
  74. package/demos/js/painter.js +0 -484
  75. package/demos/js/particles-showcase.js +0 -514
  76. package/demos/js/particles.js +0 -299
  77. package/demos/js/patterns.js +0 -397
  78. package/demos/js/penrose/artifact.js +0 -69
  79. package/demos/js/penrose/blackhole.js +0 -121
  80. package/demos/js/penrose/constants.js +0 -73
  81. package/demos/js/penrose/game.js +0 -943
  82. package/demos/js/penrose/lore.js +0 -278
  83. package/demos/js/penrose/penrosescene.js +0 -892
  84. package/demos/js/penrose/ship.js +0 -216
  85. package/demos/js/penrose/sounds.js +0 -211
  86. package/demos/js/penrose/voidparticle.js +0 -55
  87. package/demos/js/penrose/voidscene.js +0 -258
  88. package/demos/js/penrose/voidship.js +0 -144
  89. package/demos/js/penrose/wormhole.js +0 -46
  90. package/demos/js/pipeline.js +0 -555
  91. package/demos/js/plane3d.js +0 -256
  92. package/demos/js/platformer.js +0 -1579
  93. package/demos/js/scene.js +0 -304
  94. package/demos/js/scenes.js +0 -320
  95. package/demos/js/schrodinger.js +0 -410
  96. package/demos/js/schwarzschild.js +0 -1023
  97. package/demos/js/shapes.js +0 -628
  98. package/demos/js/space/alien.js +0 -171
  99. package/demos/js/space/boom.js +0 -98
  100. package/demos/js/space/boss.js +0 -353
  101. package/demos/js/space/buff.js +0 -73
  102. package/demos/js/space/bullet.js +0 -102
  103. package/demos/js/space/constants.js +0 -85
  104. package/demos/js/space/game.js +0 -1884
  105. package/demos/js/space/hud.js +0 -112
  106. package/demos/js/space/laserbeam.js +0 -179
  107. package/demos/js/space/lightning.js +0 -277
  108. package/demos/js/space/minion.js +0 -192
  109. package/demos/js/space/missile.js +0 -212
  110. package/demos/js/space/player.js +0 -430
  111. package/demos/js/space/powerup.js +0 -90
  112. package/demos/js/space/starfield.js +0 -58
  113. package/demos/js/space/starpower.js +0 -90
  114. package/demos/js/spacetime.js +0 -559
  115. package/demos/js/sphere3d.js +0 -229
  116. package/demos/js/sprite.js +0 -473
  117. package/demos/js/svgtween.js +0 -204
  118. package/demos/js/tde/accretiondisk.js +0 -471
  119. package/demos/js/tde/blackhole.js +0 -219
  120. package/demos/js/tde/blackholescene.js +0 -209
  121. package/demos/js/tde/config.js +0 -59
  122. package/demos/js/tde/index.js +0 -820
  123. package/demos/js/tde/jets.js +0 -290
  124. package/demos/js/tde/lensedstarfield.js +0 -154
  125. package/demos/js/tde/tdestar.js +0 -297
  126. package/demos/js/tde/tidalstream.js +0 -372
  127. package/demos/js/tde_old/blackhole.obj.js +0 -354
  128. package/demos/js/tde_old/debris.obj.js +0 -791
  129. package/demos/js/tde_old/flare.obj.js +0 -239
  130. package/demos/js/tde_old/index.js +0 -448
  131. package/demos/js/tde_old/star.obj.js +0 -812
  132. package/demos/js/tiles.js +0 -312
  133. package/demos/js/tweendemo.js +0 -79
  134. package/demos/js/visibility.js +0 -102
  135. package/demos/kerr.html +0 -28
  136. package/demos/lavalamp.html +0 -27
  137. package/demos/layouts.html +0 -37
  138. package/demos/logo.svg +0 -4
  139. package/demos/loop.html +0 -84
  140. package/demos/mondrian.html +0 -32
  141. package/demos/og_image.png +0 -0
  142. package/demos/opacity.html +0 -36
  143. package/demos/painter.html +0 -39
  144. package/demos/particles-showcase.html +0 -28
  145. package/demos/particles.html +0 -24
  146. package/demos/patterns.html +0 -33
  147. package/demos/penrose-game.html +0 -31
  148. package/demos/pipeline.html +0 -737
  149. package/demos/plane3d.html +0 -24
  150. package/demos/platformer.html +0 -43
  151. package/demos/scene.html +0 -33
  152. package/demos/scenes.html +0 -96
  153. package/demos/schrodinger.html +0 -27
  154. package/demos/schwarzschild.html +0 -27
  155. package/demos/shapes.html +0 -16
  156. package/demos/space.html +0 -85
  157. package/demos/spacetime.html +0 -27
  158. package/demos/sphere3d.html +0 -24
  159. package/demos/sprite.html +0 -18
  160. package/demos/svgtween.html +0 -29
  161. package/demos/tde.html +0 -28
  162. package/demos/tiles.html +0 -28
  163. package/demos/transforms.html +0 -400
  164. package/demos/tween.html +0 -45
  165. package/demos/visibility.html +0 -33
  166. package/docs/README.md +0 -230
  167. package/docs/api/FluidSystem.md +0 -173
  168. package/docs/concepts/architecture-overview.md +0 -204
  169. package/docs/concepts/coordinate-system.md +0 -384
  170. package/docs/concepts/lifecycle.md +0 -255
  171. package/docs/concepts/rendering-pipeline.md +0 -279
  172. package/docs/concepts/shapes-vs-gameobjects.md +0 -187
  173. package/docs/concepts/tde-zorder.md +0 -106
  174. package/docs/concepts/two-layer-architecture.md +0 -229
  175. package/docs/fluid-dynamics.md +0 -99
  176. package/docs/getting-started/first-game.md +0 -354
  177. package/docs/getting-started/hello-world.md +0 -269
  178. package/docs/getting-started/installation.md +0 -175
  179. package/docs/modules/collision/README.md +0 -453
  180. package/docs/modules/fluent/README.md +0 -1075
  181. package/docs/modules/game/README.md +0 -303
  182. package/docs/modules/isometric-camera.md +0 -210
  183. package/docs/modules/isometric.md +0 -275
  184. package/docs/modules/painter/README.md +0 -328
  185. package/docs/modules/particle/README.md +0 -559
  186. package/docs/modules/shapes/README.md +0 -221
  187. package/docs/modules/shapes/base/euclidian.md +0 -123
  188. package/docs/modules/shapes/base/geometry2d.md +0 -204
  189. package/docs/modules/shapes/base/renderable.md +0 -215
  190. package/docs/modules/shapes/base/shape.md +0 -262
  191. package/docs/modules/shapes/base/transformable.md +0 -243
  192. package/docs/modules/shapes/hierarchy.md +0 -218
  193. package/docs/modules/state/README.md +0 -577
  194. package/docs/modules/util/README.md +0 -99
  195. package/docs/modules/util/camera3d.md +0 -412
  196. package/docs/modules/util/scene3d.md +0 -395
  197. package/index.html +0 -17
  198. package/jsdoc.json +0 -50
  199. package/scripts/build-demo.js +0 -69
  200. package/scripts/bundle4llm.js +0 -276
  201. package/scripts/clearconsole.js +0 -48
  202. package/test/math/orbital.test.js +0 -61
  203. package/test/math/tensor.test.js +0 -114
  204. package/test/particle/emitter.test.js +0 -204
  205. package/test/particle/particle-system.test.js +0 -310
  206. package/test/particle/particle.test.js +0 -116
  207. package/test/particle/updaters.test.js +0 -386
  208. package/test/setup.js +0 -120
  209. package/test/shapes/euclidian.test.js +0 -44
  210. package/test/shapes/geometry.test.js +0 -86
  211. package/test/shapes/group.test.js +0 -86
  212. package/test/shapes/rectangle.test.js +0 -64
  213. package/test/shapes/transform.test.js +0 -379
  214. package/test/util/camera3d.test.js +0 -428
  215. package/test/util/scene3d.test.js +0 -352
  216. package/vite.config.js +0 -50
  217. package/vitest.config.js +0 -13
@@ -1,219 +0,0 @@
1
- import { GameObject, Sphere3D, Painter, Easing } from "../../../src/index.js";
2
- import { CONFIG } from "./config.js";
3
-
4
- export class BlackHole extends GameObject {
5
- constructor(game, options = {}) {
6
- super(game, options);
7
- this.mass = options.initialMass ?? CONFIG.blackHole.initialMass;
8
- this.baseRadius = game.baseScale ? game.baseScale * CONFIG.bhRadiusRatio : 50;
9
- this.currentRadius = this.baseRadius;
10
-
11
- // Awakening state - BH starts dormant, wakes up as it feeds
12
- this.awakeningLevel = 0; // 0 = dormant (pure black), 1 = fully awake
13
- this.feedingPulse = 0; // Temporary glow boost when consuming
14
- this.totalConsumed = 0; // Track total mass consumed
15
-
16
- // Dynamic growth animation
17
- this.growthSpurt = 0; // Overshoot when consuming (decays to 0)
18
- this.breathPhase = 0; // Oscillation phase for breathing effect
19
- this.targetRadius = this.baseRadius; // Smooth radius target
20
-
21
- // Rotation - black holes spin!
22
- this.rotation = 0;
23
- this.rotationSpeed = options.rotationSpeed ?? 2.9; // Slow, ominous spin
24
-
25
- // Use WebGL shaders for rendering
26
- this.useShader = options.useShader ?? true;
27
- }
28
-
29
- init() {
30
- this.updateVisual();
31
- }
32
-
33
- /**
34
- * Add mass from consumed particles - triggers awakening and pulse
35
- *
36
- * @param {number} amount - Amount of mass to add
37
- *
38
- * Note: Feeding pulse is only triggered before the stable phase.
39
- * Once stabilizing, particles can still be consumed but won't
40
- * cause the visual pulse effect.
41
- */
42
- addConsumedMass(amount) {
43
- this.totalConsumed += amount;
44
-
45
- // Skip pulse effects if we're in the stable phase
46
- if (this.isStabilizing) {
47
- return;
48
- }
49
-
50
- // Awakening increases as BH feeds (slow ramp up)
51
- const awakeningProgress = Math.min(1, this.totalConsumed * 0.1);
52
- this.awakeningLevel = Math.max(this.awakeningLevel, awakeningProgress);
53
-
54
- // Feeding pulse - temporary glow boost
55
- this.feedingPulse = Math.min(1, this.feedingPulse + amount * 0.2);
56
-
57
- // Growth spurt - overshoot effect when consuming
58
- // More dramatic spurts as awakening increases
59
- const spurtIntensity = 0.03 + this.awakeningLevel * 0.05;
60
- this.growthSpurt = Math.min(0.15, this.growthSpurt + amount * spurtIntensity);
61
- }
62
-
63
- /**
64
- * Reset to dormant state
65
- */
66
- resetAwakening() {
67
- this.awakeningLevel = 0;
68
- this.feedingPulse = 0;
69
- this.totalConsumed = 0;
70
- this.rotation = 0;
71
- this.growthSpurt = 0;
72
- this.breathPhase = 0;
73
- this.isStabilizing = false; // Reset stabilization state
74
- }
75
-
76
- updateVisual() {
77
- // Calculate how much mass has been absorbed (0 = none, 1 = full star)
78
- const massAbsorbed = Math.max(0, this.mass - CONFIG.blackHole.initialMass);
79
- const absorptionProgress = massAbsorbed / CONFIG.star.initialMass;
80
-
81
- // Apply easing to make growth feel more organic
82
- // easeOutCubic: rapid initial growth that slows as it fills
83
- const easedProgress = Easing.easeOutCubic(absorptionProgress);
84
-
85
- // Base radius from absorption with easing
86
- const baseScale = this.baseRadius / CONFIG.bhRadiusRatio;
87
- const radiusFraction = CONFIG.bhRadiusRatio +
88
- easedProgress * (CONFIG.bhFinalRadiusRatio - CONFIG.bhRadiusRatio);
89
- this.targetRadius = baseScale * radiusFraction;
90
-
91
- // Breathing oscillation - subtle when dormant, stronger when awake
92
- const breathAmplitude = 0.01 + this.awakeningLevel * 0.02 + this.feedingPulse * 0.03;
93
- const breathSpeed = 1.5 + this.awakeningLevel * 0.5; // Faster when active
94
- const breathOffset = Math.sin(this.breathPhase * breathSpeed) * breathAmplitude;
95
-
96
- // Growth spurt overshoot effect (elastic rebound)
97
- const spurtOffset = this.growthSpurt * (1 + Math.sin(this.breathPhase * 8) * 0.3);
98
-
99
- // Combine all effects
100
- this.currentRadius = this.targetRadius * (1 + breathOffset + spurtOffset);
101
-
102
- if (this.currentRadius <= 0) {
103
- this.currentRadius = this.baseRadius;
104
- }
105
-
106
- // Edge brightness increases with awakening
107
- const awakeFactor = this.awakeningLevel;
108
- const pulseFactor = this.feedingPulse;
109
-
110
- // For Canvas 2D fallback - gradient rendering
111
- // Dormant: pure black edges (#101010)
112
- // Awake: warmer edges with hint of orange/red glow
113
- const edgeBase = 16 + Math.round(awakeFactor * 24 + pulseFactor * 16); // 16-56
114
- const edgeR = Math.min(255, edgeBase + Math.round(awakeFactor * 40 + pulseFactor * 60));
115
- const edgeG = Math.min(255, edgeBase + Math.round(awakeFactor * 20 + pulseFactor * 30));
116
- const edgeB = edgeBase;
117
-
118
- const midBase = 8 + Math.round(awakeFactor * 12 + pulseFactor * 8);
119
- const midR = Math.min(255, midBase + Math.round(awakeFactor * 20 + pulseFactor * 30));
120
- const midG = Math.min(255, midBase + Math.round(awakeFactor * 10 + pulseFactor * 15));
121
- const midB = midBase;
122
-
123
- const gradient = Painter.colors.radialGradient(
124
- 0, 0, 0.01 * this.currentRadius,
125
- 0, 0, this.currentRadius,
126
- [
127
- { offset: 0, color: "#000" },
128
- { offset: 0.5, color: "#000" },
129
- { offset: 0.85, color: `rgb(${midR}, ${midG}, ${midB})` },
130
- { offset: 1, color: `rgb(${edgeR}, ${edgeG}, ${edgeB})` },
131
- ]
132
- );
133
-
134
- if (!this.core) {
135
- this.core = new Sphere3D(this.currentRadius, {
136
- color: gradient,
137
- camera: this.game.camera,
138
- stroke: null, // No wireframe
139
- debug: false,
140
- segments: 32, // Smoother sphere
141
- // WebGL shader options
142
- useShader: this.useShader,
143
- shaderType: "blackHole",
144
- shaderUniforms: {
145
- uAwakeningLevel: awakeFactor,
146
- uFeedingPulse: pulseFactor,
147
- uRotation: this.rotation,
148
- },
149
- });
150
- } else {
151
- this.core.radius = this.currentRadius;
152
- this.core.color = gradient; // Keep gradient for Canvas 2D fallback
153
- // Update shader uniforms
154
- if (this.core.useShader) {
155
- this.core.setShaderUniforms({
156
- uAwakeningLevel: awakeFactor,
157
- uFeedingPulse: pulseFactor,
158
- uRotation: this.rotation,
159
- });
160
- }
161
- this.core._generateGeometry();
162
- }
163
- }
164
-
165
- update(dt) {
166
- super.update(dt);
167
-
168
- // Animate breathing phase
169
- this.breathPhase += dt * Math.PI * 2; // Full cycle per second
170
-
171
- // Spin the black hole - rotation speeds up when feeding
172
- const spinMultiplier = 1 + this.feedingPulse * 2 + this.awakeningLevel * 0.5;
173
- this.rotation += this.rotationSpeed * spinMultiplier * dt;
174
-
175
- // Decay feeding pulse over time
176
- if (this.feedingPulse > 0) {
177
- this.feedingPulse = Math.max(0, this.feedingPulse - dt * 1.5);
178
- }
179
-
180
- // Decay growth spurt with elastic damping
181
- if (this.growthSpurt > 0) {
182
- // Fast initial decay, slows down (feels like elastic settling)
183
- const decayRate = 3 + this.growthSpurt * 5; // Faster when larger
184
- this.growthSpurt = Math.max(0, this.growthSpurt - dt * decayRate);
185
- }
186
-
187
- // Decay awakening level when stabilizing (slow cool-down)
188
- // Minimum level is 0.3 - never goes fully dormant after feeding
189
- if (this.isStabilizing && this.awakeningLevel > 0.3) {
190
- this.awakeningLevel = Math.max(0.3, this.awakeningLevel - dt * 0.15);
191
- }
192
-
193
- this.updateVisual();
194
- }
195
-
196
- /**
197
- * Start the stabilization phase - black hole calms down
198
- */
199
- startStabilizing() {
200
- this.isStabilizing = true;
201
- }
202
-
203
- /**
204
- * Reset stabilization state
205
- */
206
- resetStabilizing() {
207
- this.isStabilizing = false;
208
- }
209
-
210
- onResize(baseRadius) {
211
- this.baseRadius = baseRadius;
212
- this.updateVisual();
213
- }
214
-
215
- render() {
216
- super.render();
217
- this.core.render();
218
- }
219
- }
@@ -1,209 +0,0 @@
1
- import { Scene3D } from "../../../src/index.js";
2
- import { BlackHole } from "./blackhole.js";
3
- import { Star } from "./tdestar.js";
4
- import { TidalStream } from "./tidalstream.js";
5
- import { AccretionDisk } from "./accretiondisk.js";
6
- import { RelativisticJets } from "./jets.js";
7
- import { CONFIG } from "./config.js";
8
-
9
- /**
10
- * BlackHoleScene - Main 3D scene containing the TDE visualization
11
- *
12
- * Z-Order Rendering Rules:
13
- * 1. BlackHole renders at the back (dark shadow)
14
- * 2. AccretionDisk renders over the black hole (particles curve around)
15
- * 3. TidalStream always on top of star and black hole
16
- * 4. Star position relative to BlackHole changes based on camera depth
17
- *
18
- * Z-Index Buckets (lower = renders first = behind):
19
- * - starBack: 10 (star when behind BH)
20
- * - blackHole: 15 (dark shadow at back)
21
- * - disk: 20 (accretion disk over BH)
22
- * - starFront: 25 (star when in front of BH)
23
- * - stream: 30 (particles - always on top)
24
- * - jets: 40 (always on top)
25
- *
26
- * @extends Scene3D
27
- */
28
- export class BlackHoleScene extends Scene3D {
29
- constructor(game, options = {}) {
30
- super(game, options);
31
- // Camera is passed via options and handled by Scene3D
32
-
33
- /**
34
- * Z-index buckets for render ordering
35
- * Scene3D sorts by zIndex first (lower renders first/behind),
36
- * then uses camera depth as tie-breaker
37
- *
38
- * Layering (simple):
39
- * - BlackHole at back (dark shadow)
40
- * - Disk renders over BH (particles curve around it)
41
- * - Stream always on top of star and BH
42
- * - Star moves in front of/behind BH based on camera view
43
- */
44
- this.Z = {
45
- starBack: 10, // Star when behind BH
46
- blackHole: 15, // BlackHole: dark shadow at back
47
- disk: 1, // AccretionDisk: over the black hole
48
- starFront: 25, // Star when in front of BH
49
- stream: 30, // TidalStream: always on top of star and BH
50
- jets: 1, // Jets: always on top
51
- };
52
- }
53
-
54
- init() {
55
- // Z-order buckets: lower draws first, equal uses depth
56
- // IMPORTANT: Set zIndex AFTER add() because ZOrderedCollection.add()
57
- // overwrites zIndex with array index
58
-
59
- // Add BlackHole at (0,0,0)
60
- this.bh = new BlackHole(this.game);
61
- this.add(this.bh);
62
- this.bh.zIndex = this.Z.blackHole; // Set AFTER add()
63
-
64
- // Add a Star orbiting the black hole
65
- this.star = new Star(this.game);
66
- this.add(this.star);
67
- this.star.zIndex = this.Z.starFront; // Set AFTER add(), will be updated per-frame
68
-
69
- // Add accretion disk (starts inactive)
70
- this.disk = new AccretionDisk(this.game, {
71
- camera: this.game.camera,
72
- bhRadius: this.game.baseScale * CONFIG.bhRadiusRatio,
73
- bhMass: CONFIG.blackHole.initialMass,
74
- onParticleConsumed: () => {
75
- this.bh.addConsumedMass(0.1); // Disk particles worth more
76
- },
77
- });
78
- this.add(this.disk);
79
- this.disk.zIndex = this.Z.disk; // Set AFTER add()
80
-
81
- // Add tidal stream for particles
82
- // When particles are consumed, feed the black hole
83
- // When particles circularize, transfer to accretion disk
84
- this.stream = new TidalStream(this.game, {
85
- camera: this.game.camera,
86
- scene: this, // Pass scene reference for screen center
87
- bhRadius: this.game.baseScale * CONFIG.bhRadiusRatio,
88
- diskInnerRadius: this.disk.innerRadius,
89
- diskOuterRadius: this.disk.outerRadius,
90
- onParticleConsumed: () => {
91
- this.bh.addConsumedMass(0.05); // Each particle adds small amount
92
- },
93
- onParticleCaptured: (p) => {
94
- this.disk.captureParticle(p);
95
- },
96
- });
97
- this.add(this.stream);
98
- this.stream.zIndex = this.Z.stream; // Set AFTER add()
99
-
100
- // Add relativistic jets (activated during flare phase)
101
- this.jets = new RelativisticJets(this.game, {
102
- camera: this.game.camera,
103
- bhRadius: this.game.baseScale * CONFIG.bhRadiusRatio,
104
- });
105
- this.add(this.jets);
106
- this.jets.zIndex = this.Z.jets; // Set AFTER add()
107
-
108
- // Mark z-order as dirty to ensure initial sort
109
- if (this._collection) {
110
- this._collection._zOrderDirty = true;
111
- }
112
- }
113
-
114
- update(dt) {
115
- super.update(dt);
116
-
117
- // Sync all particle systems with current BH radius (grows as it feeds)
118
- if (this.bh) {
119
- if (this.stream) {
120
- this.stream.updateBHRadius(this.bh.currentRadius);
121
- }
122
- if (this.disk) {
123
- this.disk.updateBHRadius(this.bh.currentRadius);
124
- // Also sync stream's disk bounds for capture detection
125
- if (this.stream) {
126
- this.stream.updateDiskBounds(this.disk.innerRadius, this.disk.outerRadius);
127
- }
128
- }
129
- if (this.jets) {
130
- this.jets.updateBHRadius(this.bh.currentRadius);
131
- }
132
- }
133
-
134
- // Note: Z-order update is called from TDEDemo.update() AFTER star position
135
- // is updated, to ensure we use the current frame's position, not the previous frame's.
136
- }
137
-
138
- /**
139
- * Update star's z-index based on its depth relative to the black hole
140
- *
141
- * Dynamic z-ordering:
142
- * - When star in front of BH: star renders on top of BH
143
- * - When star behind BH: BH renders on top of star
144
- * - Stream (particles) always stays on top of both
145
- *
146
- * Camera3D.project() returns z as depth after rotation:
147
- * - Lower z = closer to camera
148
- * - Higher z = farther from camera
149
- *
150
- * IMPORTANT: Must be called AFTER star position is updated each frame.
151
- */
152
- updateStarZOrder() {
153
- if (!this.star || !this.bh || !this.game?.camera) {
154
- return;
155
- }
156
-
157
- const camera = this.game.camera;
158
-
159
- // Project star position to get its depth after camera rotation
160
- const projStar = camera.project(
161
- this.star.x || 0,
162
- this.star.y || 0,
163
- this.star.z || 0
164
- );
165
-
166
- // Black hole is always at origin (0, 0, 0)
167
- const projBH = camera.project(0, 0, 0);
168
-
169
- // Star is "in front" if its depth is LESS than BH's depth
170
- const starInFront = projStar.z < projBH.z;
171
-
172
- // Update star's z-index
173
- const newZIndex = starInFront ? this.Z.starFront : this.Z.starBack;
174
-
175
- if (this.star.zIndex !== newZIndex) {
176
- this.star.zIndex = newZIndex;
177
- // Mark ordering dirty so Scene3D resort happens this frame
178
- if (this._collection) {
179
- this._collection._zOrderDirty = true;
180
- }
181
- }
182
- }
183
-
184
- onResize() {
185
- const game = this.game;
186
- if (this.bh) {
187
- this.bh.onResize(game.baseScale * CONFIG.bhRadiusRatio);
188
- }
189
- if (this.star) {
190
- this.star.onResize(
191
- game.baseScale * CONFIG.starRadiusRatio,
192
- game.baseScale * CONFIG.star.initialOrbitRadius
193
- );
194
- }
195
- if (this.disk && this.bh) {
196
- this.disk.updateBHRadius(this.bh.currentRadius);
197
- }
198
- if (this.stream && this.bh) {
199
- this.stream.updateBHRadius(this.bh.currentRadius);
200
- // Update disk bounds for capture detection
201
- if (this.disk) {
202
- this.stream.updateDiskBounds(this.disk.innerRadius, this.disk.outerRadius);
203
- }
204
- }
205
- if (this.jets && this.bh) {
206
- this.jets.updateBHRadius(this.bh.currentRadius);
207
- }
208
- }
209
- }
@@ -1,59 +0,0 @@
1
- export const CONFIG = {
2
- // Sizing (as fraction of screen baseScale)
3
- bhRadiusRatio: 0.01, // Initial dormant black hole size (larger for visible lensing)
4
- bhFinalRadiusRatio: 0.12, // Final size after consuming star
5
- starRadiusRatio: 0.08,
6
-
7
- // Phase durations (seconds)
8
- durations: {
9
- approach: 12.0, // Stable wide orbit
10
- stretch: 10.0, // Orbit begins to decay
11
- disrupt: 20.0, // Mass transfer (event-based exit)
12
- accrete: 1.0, // Debris accretion
13
- flare: 5.0, // Jets firing - spectacular cosmic event!
14
- stable: Infinity, // Final stable state
15
- },
16
-
17
- // Flash effect (now handled by Tweenetik in FSM)
18
-
19
- // Physics params
20
- blackHole: {
21
- initialMass: 2,
22
- color: "#000",
23
- },
24
- star: {
25
- initialMass: 25,
26
- color: "#FF6030", // Deep red-orange (cooler K/M type star)
27
- // Orbit sizing (fraction of half the smaller screen dimension)
28
- // apoapsis = initialOrbitRadius * (1 + eccentricity)
29
- // periapsis = initialOrbitRadius * (1 - eccentricity)
30
- initialOrbitRadius: 1.2, // Semi-major axis
31
- eccentricity: 0.25, // Lower = more circular, periapsis closer to edge
32
- // With these values:
33
- // periapsis (right) = 1.2 * 0.75 = 0.9 (90% to edge - visible on RIGHT side!)
34
- // apoapsis (left) = 1.2 * 1.25 = 1.5 (goes off screen left)
35
- orbitSpeed: 0.4,
36
- decayRate: 0.4,
37
- massTransferStart: 0.1,
38
- rotationSpeed: 0.71,
39
- temperature: 3800,
40
- orbitCenterX: 0,
41
- orbitCenterY: 0,
42
- bypassConstraints: true,
43
- // Start angle: star should be VISIBLE at start, then swing through orbit
44
- // 0 = right (periapsis), π/2 = top, π = left (apoapsis), 3π/2 = bottom
45
- startAngle: Math.PI * 1.85, // Start lower-right, comes FROM right, swings up and around
46
- },
47
- sceneOptions: {
48
- starCount: 5000,
49
- },
50
-
51
- // Accretion disk settings
52
- disk: {
53
- innerRadiusRatio: 0.03, // ISCO (innermost stable orbit)
54
- outerRadiusRatio: 0.15, // Outer halo edge
55
- maxParticles: 2000,
56
- orbitalSpeed: 0.8,
57
- activationProgress: 0.8, // Start at 80% of disrupt phase
58
- },
59
- };