@firecms/neat 0.2.2 → 0.4.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.
@@ -32,9 +32,14 @@ export type NeatConfig = {
32
32
  colorBrightness?: number;
33
33
  colors: NeatColor[];
34
34
  colorBlending?: number;
35
+ grainScale?: number;
36
+ grainIntensity?: number;
37
+ grainSparsity?: number;
38
+ grainSpeed?: number;
35
39
  wireframe?: boolean;
36
40
  backgroundColor?: string;
37
41
  backgroundAlpha?: number;
42
+ yOffset?: number;
38
43
  };
39
44
 
40
45
  export type NeatColor = {
@@ -68,6 +73,11 @@ export class NeatGradient implements NeatController {
68
73
  private _saturation: number = -1;
69
74
  private _brightness: number = -1;
70
75
 
76
+ private _grainScale: number = -1;
77
+ private _grainIntensity: number = -1;
78
+ private _grainSparsity: number = -1;
79
+ private _grainSpeed: number = -1;
80
+
71
81
  private _colorBlending: number = -1;
72
82
 
73
83
  private _colors: NeatColor[] = [];
@@ -80,6 +90,8 @@ export class NeatGradient implements NeatController {
80
90
  private sizeObserver: ResizeObserver;
81
91
  private sceneState: SceneState;
82
92
 
93
+ private _yOffset: number = 0;
94
+
83
95
  constructor(config: NeatConfig & { ref: HTMLCanvasElement, resolution?: number, seed?: number }) {
84
96
 
85
97
  const {
@@ -96,11 +108,16 @@ export class NeatGradient implements NeatController {
96
108
  colorSaturation = 0,
97
109
  colorBrightness = 1,
98
110
  colorBlending = 5,
111
+ grainScale = 2,
112
+ grainIntensity = 0.55,
113
+ grainSparsity = 0.0,
114
+ grainSpeed = 0.1,
99
115
  wireframe = false,
100
116
  backgroundColor = "#FFFFFF",
101
117
  backgroundAlpha = 1.0,
102
118
  resolution = 1,
103
- seed
119
+ seed,
120
+ yOffset = 0
104
121
  } = config;
105
122
 
106
123
 
@@ -117,6 +134,10 @@ export class NeatGradient implements NeatController {
117
134
  this.waveFrequencyY = waveFrequencyY;
118
135
  this.waveAmplitude = waveAmplitude;
119
136
  this.colorBlending = colorBlending;
137
+ this.grainScale = grainScale;
138
+ this.grainIntensity = grainIntensity;
139
+ this.grainSparsity = grainSparsity;
140
+ this.grainSpeed = grainSpeed;
120
141
  this.colors = colors;
121
142
  this.shadows = shadows;
122
143
  this.highlights = highlights;
@@ -125,6 +146,7 @@ export class NeatGradient implements NeatController {
125
146
  this.wireframe = wireframe;
126
147
  this.backgroundColor = backgroundColor;
127
148
  this.backgroundAlpha = backgroundAlpha;
149
+ this.yOffset = yOffset;
128
150
 
129
151
  this.sceneState = this._initScene(resolution);
130
152
 
@@ -143,11 +165,15 @@ export class NeatGradient implements NeatController {
143
165
  height = this._ref.height;
144
166
 
145
167
  const colors = [
146
- ...this._colors.map(color => ({
147
- is_active: color.enabled,
148
- color: new THREE.Color(color.color),
149
- influence: color.influence
150
- })),
168
+ ...this._colors.map(color => {
169
+ let threeColor = new THREE.Color();
170
+ threeColor.setStyle(color.color, "");
171
+ return ({
172
+ is_active: color.enabled,
173
+ color: threeColor,
174
+ influence: color.influence
175
+ });
176
+ }),
151
177
  ...Array.from({ length: COLORS_COUNT - this._colors.length }).map(() => ({
152
178
  is_active: false,
153
179
  color: new THREE.Color(0x000000)
@@ -186,6 +212,16 @@ export class NeatGradient implements NeatController {
186
212
  // @ts-ignore
187
213
  mesh.material.uniforms.u_brightness = { value: this._brightness };
188
214
  // @ts-ignore
215
+ mesh.material.uniforms.u_grain_intensity = { value: this._grainIntensity };
216
+ // @ts-ignore
217
+ mesh.material.uniforms.u_grain_sparsity = { value: this._grainSparsity };
218
+ // @ts-ignore
219
+ mesh.material.uniforms.u_grain_speed = { value: this._grainSpeed };
220
+ // @ts-ignore
221
+ mesh.material.uniforms.u_grain_scale = { value: this._grainScale };
222
+ // @ts-ignore
223
+ mesh.material.uniforms.u_y_offset = { value: this._yOffset };
224
+ // @ts-ignore
189
225
  mesh.material.wireframe = this._wireframe;
190
226
  });
191
227
 
@@ -221,6 +257,13 @@ export class NeatGradient implements NeatController {
221
257
  }
222
258
  }
223
259
 
260
+ downloadAsPNG(filename = "neat.png") {
261
+ console.log("Downloading as PNG", this._ref);
262
+ const dataURL = this._ref.toDataURL("image/png");
263
+ console.log("data", dataURL);
264
+ downloadURI(dataURL, filename);
265
+ }
266
+
224
267
  set speed(speed: number) {
225
268
  this._speed = speed / 20;
226
269
  }
@@ -269,6 +312,22 @@ export class NeatGradient implements NeatController {
269
312
  this._colorBlending = colorBlending / 10;
270
313
  }
271
314
 
315
+ set grainScale(grainScale: number) {
316
+ this._grainScale = grainScale == 0 ? 1 : grainScale;
317
+ }
318
+
319
+ set grainIntensity(grainIntensity: number) {
320
+ this._grainIntensity = grainIntensity;
321
+ }
322
+
323
+ set grainSparsity(grainSparsity: number) {
324
+ this._grainSparsity = grainSparsity;
325
+ }
326
+
327
+ set grainSpeed(grainSpeed: number) {
328
+ this._grainSpeed = grainSpeed;
329
+ }
330
+
272
331
  set wireframe(wireframe: boolean) {
273
332
  this._wireframe = wireframe;
274
333
  }
@@ -285,6 +344,10 @@ export class NeatGradient implements NeatController {
285
344
  this._backgroundAlpha = backgroundAlpha;
286
345
  }
287
346
 
347
+ set yOffset(yOffset: number) {
348
+ this._yOffset = yOffset;
349
+ }
350
+
288
351
  _initScene(resolution: number): SceneState {
289
352
 
290
353
  const width = this._ref.width,
@@ -293,6 +356,7 @@ export class NeatGradient implements NeatController {
293
356
  const renderer = new THREE.WebGLRenderer({
294
357
  // antialias: true,
295
358
  alpha: true,
359
+ preserveDrawingBuffer: true,
296
360
  canvas: this._ref
297
361
  });
298
362
 
@@ -353,12 +417,16 @@ export class NeatGradient implements NeatController {
353
417
  u_plane_height: { value: PLANE_HEIGHT },
354
418
  u_shadows: { value: this._shadows },
355
419
  u_highlights: { value: this._highlights },
420
+ u_grain_intensity: { value: this._grainIntensity },
421
+ u_grain_sparsity: { value: this._grainSparsity },
422
+ u_grain_scale: { value: this._grainScale },
423
+ u_grain_speed: { value: this._grainSpeed },
356
424
  };
357
425
 
358
426
  const material = new THREE.ShaderMaterial({
359
427
  uniforms: uniforms,
360
428
  vertexShader: buildUniforms() + buildNoise() + buildColorFunctions() + buildVertexShader(),
361
- fragmentShader: buildUniforms() + buildColorFunctions() + buildFragmentShader()
429
+ fragmentShader: buildUniforms() + buildColorFunctions() + buildNoise() + buildFragmentShader()
362
430
  });
363
431
 
364
432
  material.wireframe = WIREFRAME;
@@ -417,51 +485,50 @@ void main() {
417
485
  u_wave_frequency_y * position.y + u_time,
418
486
  u_time
419
487
  ));
420
-
488
+
421
489
  vec3 color;
422
490
 
423
491
  // float t = mod(u_base_color, 100.0);
424
492
  color = u_colors[0].color;
425
-
493
+
494
+ // Apply y_offset to the noise coordinates
426
495
  vec2 noise_cord = vUv * u_color_pressure;
427
-
496
+ // Apply the y-offset to shift the pattern vertically (1:1 pixel ratio)
497
+ // Scale the offset to match the UV coordinate space
498
+ float scaledOffset = u_y_offset / u_resolution.y;
499
+ noise_cord.y -= scaledOffset;
500
+
428
501
  const float minNoise = .0;
429
502
  const float maxNoise = .9;
430
-
503
+
431
504
  for (int i = 1; i < u_colors_count; i++) {
432
-
505
+
433
506
  if(u_colors[i].is_active == 1.0){
434
507
  float noiseFlow = (1. + float(i)) / 30.;
435
508
  float noiseSpeed = (1. + float(i)) * 0.11;
436
509
  float noiseSeed = 13. + float(i) * 7.;
437
-
510
+
511
+ int reverseIndex = u_colors_count - i;
512
+
438
513
  float noise = snoise(
439
514
  vec3(
440
515
  noise_cord.x * u_color_pressure.x + u_time * noiseFlow * 2.,
441
516
  noise_cord.y * u_color_pressure.y,
442
517
  u_time * noiseSpeed
443
518
  ) + noiseSeed
444
- );
445
-
519
+ ) - (.1 * float(i)) + (.5 * u_color_blending);
520
+
446
521
  noise = clamp(minNoise, maxNoise + float(i) * 0.02, noise);
447
522
  vec3 nextColor = u_colors[i].color;
448
-
449
- // vec3 colorOklab = oklab2rgb(color);
450
- // vec3 nextColorOklab = oklab2rgb(nextColor);
451
- // vec3 mixColor = mix(colorOklab, nextColorOklab, smoothstep(0.0, u_color_blending, noise));
452
- //
453
- // color = rgb2oklab(mixColor);
454
-
455
523
  color = mix(color, nextColor, smoothstep(0.0, u_color_blending, noise));
456
524
  }
457
-
458
525
  }
459
-
526
+
460
527
  v_color = color;
461
-
528
+
462
529
  vec3 newPosition = position + normal * v_displacement_amount * u_wave_amplitude;
463
- gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
464
-
530
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
531
+
465
532
  v_new_position = gl_Position;
466
533
  }
467
534
  `;
@@ -469,18 +536,49 @@ void main() {
469
536
 
470
537
  function buildFragmentShader() {
471
538
  return `
539
+ float random(vec2 p) {
540
+ return fract(sin(dot(p, vec2(12.9898,78.233))) * 43758.5453);
541
+ }
542
+
543
+ float fbm(vec3 x) {
544
+ float value = 0.0;
545
+ float amplitude = 0.5;
546
+ float frequency = 1.0;
547
+ for (int i = 0; i < 4; i++) {
548
+ value += amplitude * snoise(x * frequency);
549
+ frequency *= 2.0;
550
+ amplitude *= 0.5;
551
+ }
552
+ return value;
553
+ }
472
554
 
473
- void main(){
555
+ void main() {
474
556
  vec3 color = v_color;
475
-
476
- color.rgb += pow(v_displacement_amount, 1.0) * u_highlights;
477
- color.rgb -= pow(1.0 - v_displacement_amount, 2.0) * u_shadows;
557
+ color += pow(v_displacement_amount, 1.0) * u_highlights;
558
+ color -= pow(1.0 - v_displacement_amount, 2.0) * u_shadows;
478
559
  color = saturation(color, 1.0 + u_saturation);
479
560
  color = color * u_brightness;
480
-
481
- gl_FragColor = vec4(color,1.0);
561
+
562
+ // Generate grain using fbm
563
+ vec2 noiseCoords = gl_FragCoord.xy / u_grain_scale;
564
+ float grain = (u_grain_speed != 0.0) ? fbm(vec3(noiseCoords, u_time * u_grain_speed)) : fbm(vec3(noiseCoords, 0.0));
565
+
566
+ // Center the grain around zero
567
+ grain = grain * 0.5 + 0.5;
568
+ grain -= 0.5;
569
+
570
+ // Add sparsity control
571
+ grain = (grain > u_grain_sparsity) ? grain : 0.0;
572
+
573
+ // Apply grain intensity
574
+ grain *= u_grain_intensity;
575
+
576
+ // Add grain to color
577
+ color += vec3(grain);
578
+
579
+ gl_FragColor = vec4(color, 1.0);
482
580
  }
483
- `;
581
+ `;
484
582
  }
485
583
 
486
584
  const buildUniforms = () => `
@@ -492,6 +590,10 @@ struct Color {
492
590
  float value;
493
591
  };
494
592
 
593
+ uniform float u_grain_intensity;
594
+ uniform float u_grain_sparsity;
595
+ uniform float u_grain_scale;
596
+ uniform float u_grain_speed;
495
597
  uniform float u_time;
496
598
 
497
599
  uniform float u_wave_amplitude;
@@ -514,6 +616,8 @@ uniform int u_colors_count;
514
616
  uniform Color u_colors[5];
515
617
  uniform vec2 u_resolution;
516
618
 
619
+ uniform float u_y_offset;
620
+
517
621
  varying vec2 vUv;
518
622
  varying vec4 v_new_position;
519
623
  varying vec3 v_color;
@@ -829,3 +933,12 @@ function generateRandomString(length: number = 6): string {
829
933
  }
830
934
  return result;
831
935
  }
936
+
937
+ function downloadURI(uri: string, name: string) {
938
+ const link = document.createElement("a");
939
+ link.download = name;
940
+ link.href = uri;
941
+ document.body.appendChild(link);
942
+ link.click();
943
+ document.body.removeChild(link);
944
+ }