@guinetik/gcanvas 1.0.5 → 2.0.0

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 (78) hide show
  1. package/dist/aizawa.html +27 -0
  2. package/dist/clifford.html +25 -0
  3. package/dist/cmb.html +24 -0
  4. package/dist/dadras.html +26 -0
  5. package/dist/dejong.html +25 -0
  6. package/dist/gcanvas.es.js +5130 -372
  7. package/dist/gcanvas.es.min.js +1 -1
  8. package/dist/gcanvas.umd.js +1 -1
  9. package/dist/gcanvas.umd.min.js +1 -1
  10. package/dist/halvorsen.html +27 -0
  11. package/dist/index.html +96 -48
  12. package/dist/js/aizawa.js +425 -0
  13. package/dist/js/bezier.js +5 -5
  14. package/dist/js/clifford.js +236 -0
  15. package/dist/js/cmb.js +594 -0
  16. package/dist/js/dadras.js +405 -0
  17. package/dist/js/dejong.js +257 -0
  18. package/dist/js/halvorsen.js +405 -0
  19. package/dist/js/isometric.js +34 -46
  20. package/dist/js/lorenz.js +425 -0
  21. package/dist/js/painter.js +8 -8
  22. package/dist/js/rossler.js +480 -0
  23. package/dist/js/schrodinger.js +314 -18
  24. package/dist/js/thomas.js +394 -0
  25. package/dist/lorenz.html +27 -0
  26. package/dist/rossler.html +27 -0
  27. package/dist/scene-interactivity-test.html +220 -0
  28. package/dist/thomas.html +27 -0
  29. package/package.json +1 -1
  30. package/readme.md +30 -22
  31. package/src/game/objects/go.js +7 -0
  32. package/src/game/objects/index.js +2 -0
  33. package/src/game/objects/isometric-scene.js +53 -3
  34. package/src/game/objects/layoutscene.js +57 -0
  35. package/src/game/objects/mask.js +241 -0
  36. package/src/game/objects/scene.js +19 -0
  37. package/src/game/objects/wrapper.js +14 -2
  38. package/src/game/pipeline.js +17 -0
  39. package/src/game/ui/button.js +101 -16
  40. package/src/game/ui/theme.js +0 -6
  41. package/src/game/ui/togglebutton.js +25 -14
  42. package/src/game/ui/tooltip.js +12 -4
  43. package/src/index.js +3 -0
  44. package/src/io/gesture.js +409 -0
  45. package/src/io/index.js +4 -1
  46. package/src/io/keys.js +9 -1
  47. package/src/io/screen.js +476 -0
  48. package/src/math/attractors.js +664 -0
  49. package/src/math/heat.js +106 -0
  50. package/src/math/index.js +1 -0
  51. package/src/mixins/draggable.js +15 -19
  52. package/src/painter/painter.shapes.js +11 -5
  53. package/src/particle/particle-system.js +165 -1
  54. package/src/physics/index.js +26 -0
  55. package/src/physics/physics-updaters.js +333 -0
  56. package/src/physics/physics.js +375 -0
  57. package/src/shapes/image.js +5 -5
  58. package/src/shapes/index.js +2 -0
  59. package/src/shapes/parallelogram.js +147 -0
  60. package/src/shapes/righttriangle.js +115 -0
  61. package/src/shapes/svg.js +281 -100
  62. package/src/shapes/text.js +22 -6
  63. package/src/shapes/transformable.js +5 -0
  64. package/src/sound/effects.js +807 -0
  65. package/src/sound/index.js +13 -0
  66. package/src/webgl/index.js +7 -0
  67. package/src/webgl/shaders/clifford-point-shaders.js +131 -0
  68. package/src/webgl/shaders/dejong-point-shaders.js +131 -0
  69. package/src/webgl/shaders/point-sprite-shaders.js +152 -0
  70. package/src/webgl/webgl-clifford-renderer.js +477 -0
  71. package/src/webgl/webgl-dejong-renderer.js +472 -0
  72. package/src/webgl/webgl-line-renderer.js +391 -0
  73. package/src/webgl/webgl-particle-renderer.js +410 -0
  74. package/types/index.d.ts +30 -2
  75. package/types/io.d.ts +217 -0
  76. package/types/physics.d.ts +299 -0
  77. package/types/shapes.d.ts +8 -0
  78. package/types/webgl.d.ts +188 -109
@@ -0,0 +1,477 @@
1
+ /**
2
+ * @module webgl/webgl-clifford-renderer
3
+ * @description WebGLCliffordRenderer - procedural Clifford attractor renderer.
4
+ *
5
+ * This renderer draws the Clifford attractor using GL_POINTS and performs
6
+ * the iterative map directly in the vertex shader (GPU).
7
+ *
8
+ * It renders into an offscreen WebGL canvas which can be composited onto
9
+ * the main 2D canvas using `compositeOnto()`.
10
+ *
11
+ * @example
12
+ * import { WebGLCliffordRenderer } from "@guinetik/gcanvas";
13
+ *
14
+ * const r = new WebGLCliffordRenderer(1 << 18, { width: 800, height: 600, shape: "glow" });
15
+ * r.setParams({ a: -1.4, b: 1.6, c: 1.0, d: 0.7 });
16
+ * r.setIterations(120);
17
+ * r.setZoom(1);
18
+ * r.setPointSize(1.0);
19
+ * r.render(performance.now() / 1000);
20
+ * r.compositeOnto(ctx, 0, 0);
21
+ */
22
+
23
+ import {
24
+ CLIFFORD_MAX_ITERATIONS,
25
+ CLIFFORD_POINT_FRAGMENTS,
26
+ CLIFFORD_POINT_VERTEX,
27
+ } from "./shaders/clifford-point-shaders.js";
28
+
29
+ /**
30
+ * @typedef {"alpha"|"additive"} WebGLBlendMode
31
+ * @typedef {"circle"|"glow"|"square"|"softSquare"} PointSpriteShape
32
+ */
33
+
34
+ export class WebGLCliffordRenderer {
35
+ /**
36
+ * @param {number} seedCount - Number of seeds (points) to render.
37
+ * @param {Object} [options]
38
+ * @param {number} [options.width=800] - Initial canvas width
39
+ * @param {number} [options.height=600] - Initial canvas height
40
+ * @param {PointSpriteShape} [options.shape="glow"] - Point sprite fragment shape
41
+ * @param {WebGLBlendMode} [options.blendMode="additive"] - WebGL blending mode
42
+ * @param {number} [options.pointSize=1] - gl_PointSize in pixels
43
+ * @param {number} [options.pointScale=0.5] - Mapping from attractor space to clip space
44
+ * @param {number} [options.iterations=120] - Iteration count (0..CLIFFORD_MAX_ITERATIONS)
45
+ * @param {{a:number,b:number,c:number,d:number}} [options.params] - Clifford parameters
46
+ * @param {{r:number,g:number,b:number,a:number}} [options.color] - RGBA (0..1)
47
+ * @param {0|1} [options.colorMode=0] - 0=flat, 1=speed→hue ramp
48
+ * @param {{minHue:number,maxHue:number}} [options.hueRange]
49
+ * @param {number} [options.maxSpeed=1.0] - Speed normalization threshold
50
+ * @param {number} [options.saturation=0.85] - 0..1
51
+ * @param {number} [options.lightness=0.55] - 0..1
52
+ * @param {number} [options.alpha] - 0..1 (defaults to color.a)
53
+ * @param {number} [options.hueShiftSpeed=0] - degrees per second
54
+ */
55
+ constructor(seedCount = 1 << 18, options = {}) {
56
+ this.seedCount = seedCount;
57
+
58
+ this.width = options.width ?? 800;
59
+ this.height = options.height ?? 600;
60
+
61
+ this.shape = options.shape ?? "glow";
62
+ this.blendMode = options.blendMode ?? "additive";
63
+
64
+ this.pointSize = options.pointSize ?? 1.0;
65
+ this.pointScale = options.pointScale ?? 0.5;
66
+
67
+ this.iterations = Math.max(0, Math.min(CLIFFORD_MAX_ITERATIONS, options.iterations ?? 120));
68
+ this.params = {
69
+ a: options.params?.a ?? -1.4,
70
+ b: options.params?.b ?? 1.6,
71
+ c: options.params?.c ?? 1.0,
72
+ d: options.params?.d ?? 0.7,
73
+ };
74
+
75
+ this.zoom = 1.0;
76
+ this.transform = WebGLCliffordRenderer.identityMat3();
77
+
78
+ this.color = options.color ?? { r: 1, g: 1, b: 1, a: 0.12 };
79
+
80
+ // Color mode + ramp settings
81
+ this.colorMode = options.colorMode ?? 0;
82
+ this.hueRange = options.hueRange ?? { minHue: 180, maxHue: 300 };
83
+ this.maxSpeed = options.maxSpeed ?? 1.0;
84
+ this.saturation = options.saturation ?? 0.85; // 0..1
85
+ this.lightness = options.lightness ?? 0.55; // 0..1
86
+ this.alpha = options.alpha ?? this.color.a ?? 0.12; // 0..1
87
+ this.hueShiftSpeed = options.hueShiftSpeed ?? 0; // degrees per second
88
+
89
+ // Offscreen canvas
90
+ this.canvas = document.createElement("canvas");
91
+ this.canvas.width = this.width;
92
+ this.canvas.height = this.height;
93
+
94
+ // Premultiplied alpha for correct compositing to Canvas 2D
95
+ this.gl = this.canvas.getContext("webgl", {
96
+ alpha: true,
97
+ premultipliedAlpha: true,
98
+ antialias: false,
99
+ preserveDrawingBuffer: true,
100
+ });
101
+
102
+ if (!this.gl) {
103
+ console.warn("WebGL not available for Clifford renderer");
104
+ this.available = false;
105
+ return;
106
+ }
107
+
108
+ this.available = true;
109
+
110
+ this._initGL();
111
+ this._createSeedBuffer(seedCount);
112
+ this._compileProgram();
113
+ this._setupBlending();
114
+ this._applyStaticUniforms();
115
+ }
116
+
117
+ /**
118
+ * @returns {boolean}
119
+ */
120
+ isAvailable() {
121
+ return Boolean(this.available);
122
+ }
123
+
124
+ /**
125
+ * Identity 3x3 matrix (column-major).
126
+ * @returns {Float32Array}
127
+ */
128
+ static identityMat3() {
129
+ return new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]);
130
+ }
131
+
132
+ /**
133
+ * 2D rotation matrix (column-major mat3).
134
+ * @param {number} angle - Radians
135
+ * @returns {Float32Array}
136
+ */
137
+ static rotationMat3(angle) {
138
+ const c = Math.cos(angle);
139
+ const s = Math.sin(angle);
140
+ return new Float32Array([c, s, 0, -s, c, 0, 0, 0, 1]);
141
+ }
142
+
143
+ /**
144
+ * @private
145
+ */
146
+ _initGL() {
147
+ const gl = this.gl;
148
+ gl.viewport(0, 0, this.width, this.height);
149
+ gl.enable(gl.BLEND);
150
+ }
151
+
152
+ /**
153
+ * @private
154
+ * @param {number} seedCount
155
+ */
156
+ _createSeedBuffer(seedCount) {
157
+ const gl = this.gl;
158
+
159
+ this._seeds = new Float32Array(seedCount * 2);
160
+ for (let i = 0; i < this._seeds.length; i++) {
161
+ this._seeds[i] = Math.random() * 2 - 1;
162
+ }
163
+
164
+ this.seedBuffer = gl.createBuffer();
165
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.seedBuffer);
166
+ gl.bufferData(gl.ARRAY_BUFFER, this._seeds, gl.STATIC_DRAW);
167
+ }
168
+
169
+ /**
170
+ * Re-generate random seeds.
171
+ */
172
+ regenerateSeeds() {
173
+ if (!this.available) return;
174
+ for (let i = 0; i < this._seeds.length; i++) {
175
+ this._seeds[i] = Math.random() * 2 - 1;
176
+ }
177
+ const gl = this.gl;
178
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.seedBuffer);
179
+ gl.bufferData(gl.ARRAY_BUFFER, this._seeds, gl.STATIC_DRAW);
180
+ }
181
+
182
+ /**
183
+ * Change seed count (recreates buffers).
184
+ * @param {number} seedCount
185
+ */
186
+ setSeedCount(seedCount) {
187
+ if (!this.available) return;
188
+ if (seedCount === this.seedCount) return;
189
+
190
+ this.seedCount = seedCount;
191
+
192
+ const gl = this.gl;
193
+ if (this.seedBuffer) gl.deleteBuffer(this.seedBuffer);
194
+
195
+ this._createSeedBuffer(seedCount);
196
+ }
197
+
198
+ /**
199
+ * @private
200
+ */
201
+ _compileProgram() {
202
+ const gl = this.gl;
203
+ const fragmentSource =
204
+ CLIFFORD_POINT_FRAGMENTS[this.shape] ?? CLIFFORD_POINT_FRAGMENTS.glow;
205
+
206
+ const vertexShader = gl.createShader(gl.VERTEX_SHADER);
207
+ gl.shaderSource(vertexShader, CLIFFORD_POINT_VERTEX);
208
+ gl.compileShader(vertexShader);
209
+ if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
210
+ console.error("Clifford vertex shader error:", gl.getShaderInfoLog(vertexShader));
211
+ this.available = false;
212
+ return;
213
+ }
214
+
215
+ const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
216
+ gl.shaderSource(fragmentShader, fragmentSource);
217
+ gl.compileShader(fragmentShader);
218
+ if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
219
+ console.error("Clifford fragment shader error:", gl.getShaderInfoLog(fragmentShader));
220
+ this.available = false;
221
+ return;
222
+ }
223
+
224
+ this.program = gl.createProgram();
225
+ gl.attachShader(this.program, vertexShader);
226
+ gl.attachShader(this.program, fragmentShader);
227
+ gl.linkProgram(this.program);
228
+ if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
229
+ console.error("Clifford program link error:", gl.getProgramInfoLog(this.program));
230
+ this.available = false;
231
+ return;
232
+ }
233
+
234
+ gl.useProgram(this.program);
235
+
236
+ // Locations
237
+ this.aPosition = gl.getAttribLocation(this.program, "aPosition");
238
+ this.uTime = gl.getUniformLocation(this.program, "uTime");
239
+ this.uParams = gl.getUniformLocation(this.program, "uParams");
240
+ this.uIterations = gl.getUniformLocation(this.program, "uIterations");
241
+ this.uTransform = gl.getUniformLocation(this.program, "uTransform");
242
+ this.uZoom = gl.getUniformLocation(this.program, "uZoom");
243
+ this.uPointScale = gl.getUniformLocation(this.program, "uPointScale");
244
+ this.uPointSize = gl.getUniformLocation(this.program, "uPointSize");
245
+
246
+ this.uColorMode = gl.getUniformLocation(this.program, "uColorMode");
247
+ this.uColor = gl.getUniformLocation(this.program, "uColor");
248
+ this.uHueRange = gl.getUniformLocation(this.program, "uHueRange");
249
+ this.uMaxSpeed = gl.getUniformLocation(this.program, "uMaxSpeed");
250
+ this.uSaturation = gl.getUniformLocation(this.program, "uSaturation");
251
+ this.uLightness = gl.getUniformLocation(this.program, "uLightness");
252
+ this.uAlpha = gl.getUniformLocation(this.program, "uAlpha");
253
+ this.uHueOffset = gl.getUniformLocation(this.program, "uHueOffset");
254
+
255
+ gl.deleteShader(vertexShader);
256
+ gl.deleteShader(fragmentShader);
257
+ }
258
+
259
+ /**
260
+ * @private
261
+ */
262
+ _setupBlending() {
263
+ const gl = this.gl;
264
+ if (this.blendMode === "additive") {
265
+ gl.blendFunc(gl.ONE, gl.ONE);
266
+ } else {
267
+ gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Set WebGL blend mode.
273
+ * @param {WebGLBlendMode} mode
274
+ */
275
+ setBlendMode(mode) {
276
+ this.blendMode = mode;
277
+ if (this.available) this._setupBlending();
278
+ }
279
+
280
+ /**
281
+ * Set point sprite shape (recompiles fragment shader).
282
+ * @param {PointSpriteShape} shape
283
+ */
284
+ setShape(shape) {
285
+ if (shape === this.shape) return;
286
+ this.shape = shape;
287
+
288
+ if (!this.available) return;
289
+ const gl = this.gl;
290
+ if (this.program) gl.deleteProgram(this.program);
291
+ this._compileProgram();
292
+ this._setupBlending();
293
+ this._applyStaticUniforms();
294
+ }
295
+
296
+ /**
297
+ * @private
298
+ */
299
+ _applyStaticUniforms() {
300
+ if (!this.available) return;
301
+ const gl = this.gl;
302
+ gl.useProgram(this.program);
303
+ if (this.uPointScale) gl.uniform1f(this.uPointScale, this.pointScale);
304
+ if (this.uPointSize) gl.uniform1f(this.uPointSize, this.pointSize);
305
+ }
306
+
307
+ /**
308
+ * Set color mode.
309
+ * @param {0|1} mode - 0=flat, 1=speed→hue
310
+ */
311
+ setColorMode(mode) {
312
+ this.colorMode = mode === 1 ? 1 : 0;
313
+ }
314
+
315
+ /**
316
+ * Set Lorenz-like color ramp settings.
317
+ * @param {Object} options
318
+ * @param {number} options.minHue - degrees (fast)
319
+ * @param {number} options.maxHue - degrees (slow)
320
+ * @param {number} options.maxSpeed - speed normalization threshold
321
+ * @param {number} options.saturation - 0..1
322
+ * @param {number} options.lightness - 0..1
323
+ * @param {number} options.alpha - 0..1
324
+ * @param {number} options.hueShiftSpeed - degrees per second
325
+ */
326
+ setColorRamp(options = {}) {
327
+ if (options.minHue !== undefined) this.hueRange.minHue = options.minHue;
328
+ if (options.maxHue !== undefined) this.hueRange.maxHue = options.maxHue;
329
+ if (options.maxSpeed !== undefined) this.maxSpeed = options.maxSpeed;
330
+ if (options.saturation !== undefined) this.saturation = options.saturation;
331
+ if (options.lightness !== undefined) this.lightness = options.lightness;
332
+ if (options.alpha !== undefined) this.alpha = options.alpha;
333
+ if (options.hueShiftSpeed !== undefined) this.hueShiftSpeed = options.hueShiftSpeed;
334
+ }
335
+
336
+ /**
337
+ * Set Clifford parameters.
338
+ * @param {{a:number,b:number,c:number,d:number}} params
339
+ */
340
+ setParams(params) {
341
+ this.params = { ...this.params, ...params };
342
+ }
343
+
344
+ /**
345
+ * Set iteration count (clamped to shader max).
346
+ * @param {number} iterations
347
+ */
348
+ setIterations(iterations) {
349
+ this.iterations = Math.max(0, Math.min(CLIFFORD_MAX_ITERATIONS, Math.floor(iterations)));
350
+ }
351
+
352
+ /**
353
+ * Set zoom factor.
354
+ * @param {number} zoom
355
+ */
356
+ setZoom(zoom) {
357
+ this.zoom = zoom;
358
+ }
359
+
360
+ /**
361
+ * Set transform matrix (mat3, column-major).
362
+ * @param {Float32Array} mat3
363
+ */
364
+ setTransform(mat3) {
365
+ this.transform = mat3;
366
+ }
367
+
368
+ /**
369
+ * Set point size in pixels.
370
+ * @param {number} size
371
+ */
372
+ setPointSize(size) {
373
+ this.pointSize = size;
374
+ if (this.available && this.uPointSize) {
375
+ this.gl.useProgram(this.program);
376
+ this.gl.uniform1f(this.uPointSize, size);
377
+ }
378
+ }
379
+
380
+ /**
381
+ * Set color (0..1 RGBA).
382
+ * @param {{r:number,g:number,b:number,a:number}} color
383
+ */
384
+ setColor(color) {
385
+ this.color = { ...this.color, ...color };
386
+ }
387
+
388
+ /**
389
+ * Resize the renderer.
390
+ * @param {number} width
391
+ * @param {number} height
392
+ */
393
+ resize(width, height) {
394
+ this.width = width;
395
+ this.height = height;
396
+ this.canvas.width = width;
397
+ this.canvas.height = height;
398
+ if (this.available) {
399
+ this.gl.viewport(0, 0, width, height);
400
+ }
401
+ }
402
+
403
+ /**
404
+ * Clear the WebGL canvas.
405
+ * @param {number} r - 0..1
406
+ * @param {number} g - 0..1
407
+ * @param {number} b - 0..1
408
+ * @param {number} a - 0..1
409
+ */
410
+ clear(r = 0, g = 0, b = 0, a = 0) {
411
+ if (!this.available) return;
412
+ const gl = this.gl;
413
+ gl.clearColor(r, g, b, a);
414
+ gl.clear(gl.COLOR_BUFFER_BIT);
415
+ }
416
+
417
+ /**
418
+ * Render one frame.
419
+ * @param {number} timeSeconds - Time in seconds (uTime)
420
+ */
421
+ render(timeSeconds = 0) {
422
+ if (!this.available) return;
423
+
424
+ const gl = this.gl;
425
+ gl.useProgram(this.program);
426
+
427
+ // Update uniforms
428
+ if (this.uTime) gl.uniform1f(this.uTime, timeSeconds);
429
+ if (this.uParams) gl.uniform4f(this.uParams, this.params.a, this.params.b, this.params.c, this.params.d);
430
+ if (this.uIterations) gl.uniform1i(this.uIterations, this.iterations);
431
+ if (this.uTransform) gl.uniformMatrix3fv(this.uTransform, false, this.transform);
432
+ if (this.uZoom) gl.uniform1f(this.uZoom, this.zoom);
433
+ if (this.uPointScale) gl.uniform1f(this.uPointScale, this.pointScale);
434
+ if (this.uPointSize) gl.uniform1f(this.uPointSize, this.pointSize);
435
+ if (this.uColor) gl.uniform4f(this.uColor, this.color.r, this.color.g, this.color.b, this.color.a);
436
+
437
+ if (this.uColorMode) gl.uniform1i(this.uColorMode, this.colorMode);
438
+ if (this.uHueRange) gl.uniform2f(this.uHueRange, this.hueRange.minHue, this.hueRange.maxHue);
439
+ if (this.uMaxSpeed) gl.uniform1f(this.uMaxSpeed, this.maxSpeed);
440
+ if (this.uSaturation) gl.uniform1f(this.uSaturation, this.saturation);
441
+ if (this.uLightness) gl.uniform1f(this.uLightness, this.lightness);
442
+ if (this.uAlpha) gl.uniform1f(this.uAlpha, this.alpha);
443
+ if (this.uHueOffset) gl.uniform1f(this.uHueOffset, (timeSeconds * this.hueShiftSpeed) % 360);
444
+
445
+ // Bind seeds
446
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.seedBuffer);
447
+ gl.enableVertexAttribArray(this.aPosition);
448
+ gl.vertexAttribPointer(this.aPosition, 2, gl.FLOAT, false, 0, 0);
449
+
450
+ gl.drawArrays(gl.POINTS, 0, this.seedCount);
451
+ }
452
+
453
+ /**
454
+ * Composite WebGL canvas onto a 2D canvas context.
455
+ * @param {CanvasRenderingContext2D} ctx
456
+ * @param {number} [x=0]
457
+ * @param {number} [y=0]
458
+ * @param {number} [width]
459
+ * @param {number} [height]
460
+ */
461
+ compositeOnto(ctx, x = 0, y = 0, width, height) {
462
+ if (!this.available) return;
463
+ ctx.drawImage(this.canvas, x, y, width ?? this.canvas.width, height ?? this.canvas.height);
464
+ }
465
+
466
+ /**
467
+ * Destroy and free resources.
468
+ */
469
+ destroy() {
470
+ if (!this.available) return;
471
+ const gl = this.gl;
472
+ if (this.program) gl.deleteProgram(this.program);
473
+ if (this.seedBuffer) gl.deleteBuffer(this.seedBuffer);
474
+ this._seeds = null;
475
+ }
476
+ }
477
+