@sg-pattern-engine/algorithms 1.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.
@@ -0,0 +1,19 @@
1
+
2
+ > @sg-pattern-engine/algorithms@1.0.0 build /workspace/packages/algorithms
3
+ > tsup src/index.ts --format cjs,esm --dts --clean
4
+
5
+ CLI Building entry: src/index.ts
6
+ CLI Using tsconfig: tsconfig.json
7
+ CLI tsup v8.5.1
8
+ CLI Target: es2020
9
+ CLI Cleaning output folder
10
+ CJS Build start
11
+ ESM Build start
12
+ ESM dist/index.mjs 4.55 KB
13
+ ESM ⚡️ Build success in 69ms
14
+ CJS dist/index.js 5.73 KB
15
+ CJS ⚡️ Build success in 73ms
16
+ DTS Build start
17
+ DTS ⚡️ Build success in 2012ms
18
+ DTS dist/index.d.ts 1.34 KB
19
+ DTS dist/index.d.mts 1.34 KB
@@ -0,0 +1,44 @@
1
+ import { AlgorithmPlugin, AlgorithmContext, AlgorithmOutput } from '@sg-pattern-engine/core';
2
+
3
+ declare class PerlinNoisePlugin implements AlgorithmPlugin {
4
+ name: string;
5
+ generate(ctx: AlgorithmContext): AlgorithmOutput;
6
+ private noise;
7
+ }
8
+
9
+ declare class FlowFieldPlugin implements AlgorithmPlugin {
10
+ name: string;
11
+ generate(ctx: AlgorithmContext): AlgorithmOutput;
12
+ }
13
+
14
+ declare class VoronoiPlugin implements AlgorithmPlugin {
15
+ name: string;
16
+ generate(ctx: AlgorithmContext): AlgorithmOutput;
17
+ }
18
+
19
+ /**
20
+ * Gray-Scott reaction-diffusion.
21
+ * Two chemical species A and B diffuse and react:
22
+ * A + 2B → 3B (A is consumed, B is produced)
23
+ * B → P (B decays at kill rate k)
24
+ *
25
+ * Classic parameter sets:
26
+ * Coral: f=0.0545, k=0.062
27
+ * Mitosis: f=0.0367, k=0.0649
28
+ * Solitons: f=0.030, k=0.060
29
+ * Worms: f=0.058, k=0.065
30
+ * Spots: f=0.035, k=0.060
31
+ */
32
+ declare class ReactionDiffusionPlugin implements AlgorithmPlugin {
33
+ name: string;
34
+ private readonly DA;
35
+ private readonly DB;
36
+ private readonly F;
37
+ private readonly K;
38
+ private readonly DT;
39
+ generate(ctx: AlgorithmContext): AlgorithmOutput;
40
+ }
41
+
42
+ declare const algorithms: (PerlinNoisePlugin | FlowFieldPlugin | VoronoiPlugin | ReactionDiffusionPlugin)[];
43
+
44
+ export { FlowFieldPlugin, PerlinNoisePlugin, ReactionDiffusionPlugin, VoronoiPlugin, algorithms };
@@ -0,0 +1,44 @@
1
+ import { AlgorithmPlugin, AlgorithmContext, AlgorithmOutput } from '@sg-pattern-engine/core';
2
+
3
+ declare class PerlinNoisePlugin implements AlgorithmPlugin {
4
+ name: string;
5
+ generate(ctx: AlgorithmContext): AlgorithmOutput;
6
+ private noise;
7
+ }
8
+
9
+ declare class FlowFieldPlugin implements AlgorithmPlugin {
10
+ name: string;
11
+ generate(ctx: AlgorithmContext): AlgorithmOutput;
12
+ }
13
+
14
+ declare class VoronoiPlugin implements AlgorithmPlugin {
15
+ name: string;
16
+ generate(ctx: AlgorithmContext): AlgorithmOutput;
17
+ }
18
+
19
+ /**
20
+ * Gray-Scott reaction-diffusion.
21
+ * Two chemical species A and B diffuse and react:
22
+ * A + 2B → 3B (A is consumed, B is produced)
23
+ * B → P (B decays at kill rate k)
24
+ *
25
+ * Classic parameter sets:
26
+ * Coral: f=0.0545, k=0.062
27
+ * Mitosis: f=0.0367, k=0.0649
28
+ * Solitons: f=0.030, k=0.060
29
+ * Worms: f=0.058, k=0.065
30
+ * Spots: f=0.035, k=0.060
31
+ */
32
+ declare class ReactionDiffusionPlugin implements AlgorithmPlugin {
33
+ name: string;
34
+ private readonly DA;
35
+ private readonly DB;
36
+ private readonly F;
37
+ private readonly K;
38
+ private readonly DT;
39
+ generate(ctx: AlgorithmContext): AlgorithmOutput;
40
+ }
41
+
42
+ declare const algorithms: (PerlinNoisePlugin | FlowFieldPlugin | VoronoiPlugin | ReactionDiffusionPlugin)[];
43
+
44
+ export { FlowFieldPlugin, PerlinNoisePlugin, ReactionDiffusionPlugin, VoronoiPlugin, algorithms };
package/dist/index.js ADDED
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ FlowFieldPlugin: () => FlowFieldPlugin,
24
+ PerlinNoisePlugin: () => PerlinNoisePlugin,
25
+ ReactionDiffusionPlugin: () => ReactionDiffusionPlugin,
26
+ VoronoiPlugin: () => VoronoiPlugin,
27
+ algorithms: () => algorithms
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/noise/perlin.ts
32
+ var PerlinNoisePlugin = class {
33
+ constructor() {
34
+ this.name = "perlin";
35
+ }
36
+ generate(ctx) {
37
+ const { width, height } = ctx.tile;
38
+ const field = ctx.fieldManager.createScalarField("perlin", width, height);
39
+ const scale = ctx.config.scale || 0.01;
40
+ for (let y = 0; y < height; y++) {
41
+ for (let x = 0; x < ctx.tile.width; x++) {
42
+ const val = this.noise(
43
+ (ctx.tile.offsetX + x) * scale,
44
+ (ctx.tile.offsetY + y) * scale,
45
+ ctx.rng.random()
46
+ );
47
+ field.set(x, y, val);
48
+ }
49
+ }
50
+ return { type: "scalarField", data: field.data, width, height };
51
+ }
52
+ noise(x, y, seed) {
53
+ return Math.sin(x * 12.9898 + y * 78.233 + seed * 43758.5453) * 0.5 + 0.5;
54
+ }
55
+ };
56
+
57
+ // src/particles/flowField.ts
58
+ var FlowFieldPlugin = class {
59
+ constructor() {
60
+ this.name = "flowField";
61
+ }
62
+ generate(ctx) {
63
+ const { width, height } = ctx.tile;
64
+ const field = ctx.fieldManager.createVectorField("flow", width, height);
65
+ for (let y = 0; y < height; y++) {
66
+ for (let x = 0; x < width; x++) {
67
+ const angle = ctx.rng.random() * Math.PI * 2;
68
+ field.set(x, y, Math.cos(angle), Math.sin(angle));
69
+ }
70
+ }
71
+ return { type: "vectorField", data: field.data, width, height };
72
+ }
73
+ };
74
+
75
+ // src/geometry/voronoi.ts
76
+ var VoronoiPlugin = class {
77
+ constructor() {
78
+ this.name = "voronoi";
79
+ }
80
+ generate(ctx) {
81
+ const pointsCount = ctx.config.density || 50;
82
+ const points = [];
83
+ for (let i = 0; i < pointsCount; i++) {
84
+ points.push({
85
+ x: ctx.rng.random() * ctx.tile.totalWidth,
86
+ y: ctx.rng.random() * ctx.tile.totalHeight
87
+ });
88
+ }
89
+ return { type: "points", data: points };
90
+ }
91
+ };
92
+
93
+ // src/automata/reactionDiffusion.ts
94
+ var ReactionDiffusionPlugin = class {
95
+ constructor() {
96
+ this.name = "reactionDiffusion";
97
+ // Default Gray-Scott parameters — coral growth preset
98
+ this.DA = 1;
99
+ // diffusion rate of A
100
+ this.DB = 0.5;
101
+ // diffusion rate of B
102
+ this.F = 0.0545;
103
+ // feed rate
104
+ this.K = 0.062;
105
+ // kill rate
106
+ this.DT = 1;
107
+ }
108
+ // time step
109
+ generate(ctx) {
110
+ const { width, height } = ctx.tile;
111
+ const iterations = ctx.config.iterations ?? 2e3;
112
+ let A = new Float32Array(width * height);
113
+ let B = new Float32Array(width * height);
114
+ let nextA = new Float32Array(width * height);
115
+ let nextB = new Float32Array(width * height);
116
+ const f = ctx.config.feedRate ?? this.F;
117
+ const k = ctx.config.killRate ?? this.K;
118
+ const da = this.DA;
119
+ const db = this.DB;
120
+ const dt = this.DT;
121
+ A.fill(1);
122
+ B.fill(0);
123
+ const patchCount = Math.max(1, Math.floor(width * height * 1e-3));
124
+ const patchRadius = Math.max(3, Math.floor(Math.min(width, height) * 0.04));
125
+ for (let p = 0; p < patchCount; p++) {
126
+ const cx = Math.floor(ctx.rng.random() * width);
127
+ const cy = Math.floor(ctx.rng.random() * height);
128
+ for (let dy = -patchRadius; dy <= patchRadius; dy++) {
129
+ for (let dx = -patchRadius; dx <= patchRadius; dx++) {
130
+ if (dx * dx + dy * dy > patchRadius * patchRadius) continue;
131
+ const nx = (cx + dx + width) % width;
132
+ const ny = (cy + dy + height) % height;
133
+ const i = ny * width + nx;
134
+ A[i] = 0.5 + ctx.rng.random() * 0.1;
135
+ B[i] = 0.25 + ctx.rng.random() * 0.1;
136
+ }
137
+ }
138
+ }
139
+ for (let iter = 0; iter < iterations; iter++) {
140
+ for (let y = 0; y < height; y++) {
141
+ for (let x = 0; x < width; x++) {
142
+ const i = y * width + x;
143
+ const xL = (x - 1 + width) % width;
144
+ const xR = (x + 1) % width;
145
+ const yU = (y - 1 + height) % height;
146
+ const yD = (y + 1) % height;
147
+ const lapA = A[yU * width + x] + A[yD * width + x] + A[y * width + xL] + A[y * width + xR] - 4 * A[i];
148
+ const lapB = B[yU * width + x] + B[yD * width + x] + B[y * width + xL] + B[y * width + xR] - 4 * B[i];
149
+ const a = A[i];
150
+ const b = B[i];
151
+ const reaction = a * b * b;
152
+ nextA[i] = Math.max(0, Math.min(1, a + dt * (da * lapA - reaction + f * (1 - a))));
153
+ nextB[i] = Math.max(0, Math.min(1, b + dt * (db * lapB + reaction - (k + f) * b)));
154
+ }
155
+ }
156
+ const tmpA = A;
157
+ A = nextA;
158
+ nextA = tmpA;
159
+ const tmpB = B;
160
+ B = nextB;
161
+ nextB = tmpB;
162
+ }
163
+ return { type: "scalarField", data: B, width, height };
164
+ }
165
+ };
166
+
167
+ // src/index.ts
168
+ var algorithms = [
169
+ new PerlinNoisePlugin(),
170
+ new FlowFieldPlugin(),
171
+ new VoronoiPlugin(),
172
+ new ReactionDiffusionPlugin()
173
+ ];
174
+ // Annotate the CommonJS export names for ESM import in node:
175
+ 0 && (module.exports = {
176
+ FlowFieldPlugin,
177
+ PerlinNoisePlugin,
178
+ ReactionDiffusionPlugin,
179
+ VoronoiPlugin,
180
+ algorithms
181
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,150 @@
1
+ // src/noise/perlin.ts
2
+ var PerlinNoisePlugin = class {
3
+ constructor() {
4
+ this.name = "perlin";
5
+ }
6
+ generate(ctx) {
7
+ const { width, height } = ctx.tile;
8
+ const field = ctx.fieldManager.createScalarField("perlin", width, height);
9
+ const scale = ctx.config.scale || 0.01;
10
+ for (let y = 0; y < height; y++) {
11
+ for (let x = 0; x < ctx.tile.width; x++) {
12
+ const val = this.noise(
13
+ (ctx.tile.offsetX + x) * scale,
14
+ (ctx.tile.offsetY + y) * scale,
15
+ ctx.rng.random()
16
+ );
17
+ field.set(x, y, val);
18
+ }
19
+ }
20
+ return { type: "scalarField", data: field.data, width, height };
21
+ }
22
+ noise(x, y, seed) {
23
+ return Math.sin(x * 12.9898 + y * 78.233 + seed * 43758.5453) * 0.5 + 0.5;
24
+ }
25
+ };
26
+
27
+ // src/particles/flowField.ts
28
+ var FlowFieldPlugin = class {
29
+ constructor() {
30
+ this.name = "flowField";
31
+ }
32
+ generate(ctx) {
33
+ const { width, height } = ctx.tile;
34
+ const field = ctx.fieldManager.createVectorField("flow", width, height);
35
+ for (let y = 0; y < height; y++) {
36
+ for (let x = 0; x < width; x++) {
37
+ const angle = ctx.rng.random() * Math.PI * 2;
38
+ field.set(x, y, Math.cos(angle), Math.sin(angle));
39
+ }
40
+ }
41
+ return { type: "vectorField", data: field.data, width, height };
42
+ }
43
+ };
44
+
45
+ // src/geometry/voronoi.ts
46
+ var VoronoiPlugin = class {
47
+ constructor() {
48
+ this.name = "voronoi";
49
+ }
50
+ generate(ctx) {
51
+ const pointsCount = ctx.config.density || 50;
52
+ const points = [];
53
+ for (let i = 0; i < pointsCount; i++) {
54
+ points.push({
55
+ x: ctx.rng.random() * ctx.tile.totalWidth,
56
+ y: ctx.rng.random() * ctx.tile.totalHeight
57
+ });
58
+ }
59
+ return { type: "points", data: points };
60
+ }
61
+ };
62
+
63
+ // src/automata/reactionDiffusion.ts
64
+ var ReactionDiffusionPlugin = class {
65
+ constructor() {
66
+ this.name = "reactionDiffusion";
67
+ // Default Gray-Scott parameters — coral growth preset
68
+ this.DA = 1;
69
+ // diffusion rate of A
70
+ this.DB = 0.5;
71
+ // diffusion rate of B
72
+ this.F = 0.0545;
73
+ // feed rate
74
+ this.K = 0.062;
75
+ // kill rate
76
+ this.DT = 1;
77
+ }
78
+ // time step
79
+ generate(ctx) {
80
+ const { width, height } = ctx.tile;
81
+ const iterations = ctx.config.iterations ?? 2e3;
82
+ let A = new Float32Array(width * height);
83
+ let B = new Float32Array(width * height);
84
+ let nextA = new Float32Array(width * height);
85
+ let nextB = new Float32Array(width * height);
86
+ const f = ctx.config.feedRate ?? this.F;
87
+ const k = ctx.config.killRate ?? this.K;
88
+ const da = this.DA;
89
+ const db = this.DB;
90
+ const dt = this.DT;
91
+ A.fill(1);
92
+ B.fill(0);
93
+ const patchCount = Math.max(1, Math.floor(width * height * 1e-3));
94
+ const patchRadius = Math.max(3, Math.floor(Math.min(width, height) * 0.04));
95
+ for (let p = 0; p < patchCount; p++) {
96
+ const cx = Math.floor(ctx.rng.random() * width);
97
+ const cy = Math.floor(ctx.rng.random() * height);
98
+ for (let dy = -patchRadius; dy <= patchRadius; dy++) {
99
+ for (let dx = -patchRadius; dx <= patchRadius; dx++) {
100
+ if (dx * dx + dy * dy > patchRadius * patchRadius) continue;
101
+ const nx = (cx + dx + width) % width;
102
+ const ny = (cy + dy + height) % height;
103
+ const i = ny * width + nx;
104
+ A[i] = 0.5 + ctx.rng.random() * 0.1;
105
+ B[i] = 0.25 + ctx.rng.random() * 0.1;
106
+ }
107
+ }
108
+ }
109
+ for (let iter = 0; iter < iterations; iter++) {
110
+ for (let y = 0; y < height; y++) {
111
+ for (let x = 0; x < width; x++) {
112
+ const i = y * width + x;
113
+ const xL = (x - 1 + width) % width;
114
+ const xR = (x + 1) % width;
115
+ const yU = (y - 1 + height) % height;
116
+ const yD = (y + 1) % height;
117
+ const lapA = A[yU * width + x] + A[yD * width + x] + A[y * width + xL] + A[y * width + xR] - 4 * A[i];
118
+ const lapB = B[yU * width + x] + B[yD * width + x] + B[y * width + xL] + B[y * width + xR] - 4 * B[i];
119
+ const a = A[i];
120
+ const b = B[i];
121
+ const reaction = a * b * b;
122
+ nextA[i] = Math.max(0, Math.min(1, a + dt * (da * lapA - reaction + f * (1 - a))));
123
+ nextB[i] = Math.max(0, Math.min(1, b + dt * (db * lapB + reaction - (k + f) * b)));
124
+ }
125
+ }
126
+ const tmpA = A;
127
+ A = nextA;
128
+ nextA = tmpA;
129
+ const tmpB = B;
130
+ B = nextB;
131
+ nextB = tmpB;
132
+ }
133
+ return { type: "scalarField", data: B, width, height };
134
+ }
135
+ };
136
+
137
+ // src/index.ts
138
+ var algorithms = [
139
+ new PerlinNoisePlugin(),
140
+ new FlowFieldPlugin(),
141
+ new VoronoiPlugin(),
142
+ new ReactionDiffusionPlugin()
143
+ ];
144
+ export {
145
+ FlowFieldPlugin,
146
+ PerlinNoisePlugin,
147
+ ReactionDiffusionPlugin,
148
+ VoronoiPlugin,
149
+ algorithms
150
+ };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@sg-pattern-engine/algorithms",
3
+ "version": "1.0.0",
4
+ "main": "./dist/index.js",
5
+ "module": "./dist/index.mjs",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js"
12
+ }
13
+ },
14
+ "dependencies": {
15
+ "@sg-pattern-engine/core": "1.0.0"
16
+ },
17
+ "devDependencies": {
18
+ "tsup": "^8.0.0",
19
+ "typescript": "^5.4.0"
20
+ },
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "scripts": {
25
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean"
26
+ }
27
+ }
@@ -0,0 +1,111 @@
1
+ import { AlgorithmPlugin, AlgorithmContext, AlgorithmOutput } from '@sg-pattern-engine/core';
2
+
3
+ /**
4
+ * Gray-Scott reaction-diffusion.
5
+ * Two chemical species A and B diffuse and react:
6
+ * A + 2B → 3B (A is consumed, B is produced)
7
+ * B → P (B decays at kill rate k)
8
+ *
9
+ * Classic parameter sets:
10
+ * Coral: f=0.0545, k=0.062
11
+ * Mitosis: f=0.0367, k=0.0649
12
+ * Solitons: f=0.030, k=0.060
13
+ * Worms: f=0.058, k=0.065
14
+ * Spots: f=0.035, k=0.060
15
+ */
16
+ export class ReactionDiffusionPlugin implements AlgorithmPlugin {
17
+ name = 'reactionDiffusion';
18
+
19
+ // Default Gray-Scott parameters — coral growth preset
20
+ private readonly DA = 1.0; // diffusion rate of A
21
+ private readonly DB = 0.5; // diffusion rate of B
22
+ private readonly F = 0.0545; // feed rate
23
+ private readonly K = 0.062; // kill rate
24
+ private readonly DT = 1.0; // time step
25
+
26
+ generate(ctx: AlgorithmContext): AlgorithmOutput {
27
+ const { width, height } = ctx.tile;
28
+ const iterations = ctx.config.iterations ?? 2000;
29
+
30
+ // Allocate two channels: A (activator) and B (inhibitor)
31
+ // Flat arrays: index = y * width + x
32
+ let A = new Float32Array(width * height);
33
+ let B = new Float32Array(width * height);
34
+ let nextA = new Float32Array(width * height);
35
+ let nextB = new Float32Array(width * height);
36
+
37
+ // Allow parameter overrides via config
38
+ const f = ctx.config.feedRate ?? this.F;
39
+ const k = ctx.config.killRate ?? this.K;
40
+ const da = this.DA;
41
+ const db = this.DB;
42
+ const dt = this.DT;
43
+
44
+ // ── Initialise ─────────────────────────────────────────────────────────
45
+ // Fill A=1 everywhere, seed random B=1 patches
46
+ A.fill(1.0);
47
+ B.fill(0.0);
48
+
49
+ const patchCount = Math.max(1, Math.floor(width * height * 0.001));
50
+ const patchRadius = Math.max(3, Math.floor(Math.min(width, height) * 0.04));
51
+
52
+ for (let p = 0; p < patchCount; p++) {
53
+ const cx = Math.floor(ctx.rng.random() * width);
54
+ const cy = Math.floor(ctx.rng.random() * height);
55
+ for (let dy = -patchRadius; dy <= patchRadius; dy++) {
56
+ for (let dx = -patchRadius; dx <= patchRadius; dx++) {
57
+ if (dx * dx + dy * dy > patchRadius * patchRadius) continue;
58
+ const nx = ((cx + dx) + width) % width;
59
+ const ny = ((cy + dy) + height) % height;
60
+ const i = ny * width + nx;
61
+ A[i] = 0.5 + ctx.rng.random() * 0.1;
62
+ B[i] = 0.25 + ctx.rng.random() * 0.1;
63
+ }
64
+ }
65
+ }
66
+
67
+ // ── Iterate Gray-Scott ──────────────────────────────────────────────────
68
+ for (let iter = 0; iter < iterations; iter++) {
69
+ for (let y = 0; y < height; y++) {
70
+ for (let x = 0; x < width; x++) {
71
+ const i = y * width + x;
72
+
73
+ // Wrap-around neighbour indices
74
+ const xL = (x - 1 + width) % width;
75
+ const xR = (x + 1) % width;
76
+ const yU = (y - 1 + height) % height;
77
+ const yD = (y + 1) % height;
78
+
79
+ // Discrete Laplacian (5-point stencil)
80
+ const lapA =
81
+ A[yU * width + x ] +
82
+ A[yD * width + x ] +
83
+ A[y * width + xL] +
84
+ A[y * width + xR] -
85
+ 4.0 * A[i];
86
+
87
+ const lapB =
88
+ B[yU * width + x ] +
89
+ B[yD * width + x ] +
90
+ B[y * width + xL] +
91
+ B[y * width + xR] -
92
+ 4.0 * B[i];
93
+
94
+ const a = A[i];
95
+ const b = B[i];
96
+ const reaction = a * b * b;
97
+
98
+ nextA[i] = Math.max(0, Math.min(1, a + dt * (da * lapA - reaction + f * (1 - a))));
99
+ nextB[i] = Math.max(0, Math.min(1, b + dt * (db * lapB + reaction - (k + f) * b)));
100
+ }
101
+ }
102
+
103
+ // Swap buffers without allocation
104
+ const tmpA = A; A = nextA; nextA = tmpA;
105
+ const tmpB = B; B = nextB; nextB = tmpB;
106
+ }
107
+
108
+ // Return B channel — this is where the patterns form
109
+ return { type: 'scalarField', data: B, width, height };
110
+ }
111
+ }
@@ -0,0 +1,19 @@
1
+ import { AlgorithmPlugin, AlgorithmContext, AlgorithmOutput } from '@sg-pattern-engine/core';
2
+
3
+ export class VoronoiPlugin implements AlgorithmPlugin {
4
+ name = 'voronoi';
5
+
6
+ generate(ctx: AlgorithmContext): AlgorithmOutput {
7
+ const pointsCount = ctx.config.density || 50;
8
+ const points = [];
9
+
10
+ for (let i = 0; i < pointsCount; i++) {
11
+ points.push({
12
+ x: ctx.rng.random() * ctx.tile.totalWidth,
13
+ y: ctx.rng.random() * ctx.tile.totalHeight
14
+ });
15
+ }
16
+
17
+ return { type: 'points', data: points };
18
+ }
19
+ }
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { PerlinNoisePlugin } from './noise/perlin';
2
+ import { FlowFieldPlugin } from './particles/flowField';
3
+ import { VoronoiPlugin } from './geometry/voronoi';
4
+ import { ReactionDiffusionPlugin } from './automata/reactionDiffusion';
5
+
6
+ export const algorithms = [
7
+ new PerlinNoisePlugin(),
8
+ new FlowFieldPlugin(),
9
+ new VoronoiPlugin(),
10
+ new ReactionDiffusionPlugin()
11
+ ];
12
+
13
+ export * from './noise/perlin';
14
+ export * from './particles/flowField';
15
+ export * from './geometry/voronoi';
16
+ export * from './automata/reactionDiffusion';
@@ -0,0 +1,29 @@
1
+ import { AlgorithmPlugin, AlgorithmContext, AlgorithmOutput } from '@sg-pattern-engine/core';
2
+
3
+ // Simplified deterministic Perlin implementation
4
+ export class PerlinNoisePlugin implements AlgorithmPlugin {
5
+ name = 'perlin';
6
+
7
+ generate(ctx: AlgorithmContext): AlgorithmOutput {
8
+ const { width, height } = ctx.tile;
9
+ const field = ctx.fieldManager.createScalarField('perlin', width, height);
10
+ const scale = ctx.config.scale || 0.01;
11
+
12
+ for (let y = 0; y < height; y++) {
13
+ for (let x = 0; x < ctx.tile.width; x++) {
14
+ const val = this.noise(
15
+ (ctx.tile.offsetX + x) * scale,
16
+ (ctx.tile.offsetY + y) * scale,
17
+ ctx.rng.random()
18
+ );
19
+ field.set(x, y, val);
20
+ }
21
+ }
22
+ return { type: 'scalarField', data: field.data, width, height };
23
+ }
24
+
25
+ private noise(x: number, y: number, seed: number): number {
26
+ // Deterministic hash-based noise approximation
27
+ return Math.sin(x * 12.9898 + y * 78.233 + seed * 43758.5453) * 0.5 + 0.5;
28
+ }
29
+ }
@@ -0,0 +1,18 @@
1
+ import { AlgorithmPlugin, AlgorithmContext, AlgorithmOutput } from '@sg-pattern-engine/core';
2
+
3
+ export class FlowFieldPlugin implements AlgorithmPlugin {
4
+ name = 'flowField';
5
+
6
+ generate(ctx: AlgorithmContext): AlgorithmOutput {
7
+ const { width, height } = ctx.tile;
8
+ const field = ctx.fieldManager.createVectorField('flow', width, height);
9
+
10
+ for (let y = 0; y < height; y++) {
11
+ for (let x = 0; x < width; x++) {
12
+ const angle = ctx.rng.random() * Math.PI * 2;
13
+ field.set(x, y, Math.cos(angle), Math.sin(angle));
14
+ }
15
+ }
16
+ return { type: 'vectorField', data: field.data, width, height };
17
+ }
18
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "moduleResolution": "bundler",
6
+ "paths": {
7
+ "@sg-pattern-engine/core": ["../core/dist/index.d.ts"]
8
+ }
9
+ },
10
+ "include": ["src"]
11
+ }