UNPKG

@tsparticles/path-perlin-noise

Version:

tsParticles perlin noise path

127 lines (126 loc) 4.84 kB
import { Vector, deepExtend, getRandom, } from "@tsparticles/engine"; import { PerlinNoise } from "@tsparticles/perlin-noise"; const double = 2, doublePI = Math.PI * double, defaultOptions = { draw: false, size: 20, increment: 0.004, columns: 0, rows: 0, layers: 0, width: 0, height: 0, factor: { angle: 0.02, length: 0.01, }, offset: { x: 40000, y: 40000, z: 40000, }, }; export class PerlinNoiseGenerator { constructor() { this.noiseGen = new PerlinNoise(); this.field = []; this.noiseW = 0; this.options = deepExtend({}, defaultOptions); } generate(particle) { const pos = particle.getPosition(), { size } = this.options, point = { x: Math.max(Math.floor(pos.x / size), 0), y: Math.max(Math.floor(pos.y / size), 0), z: Math.max(Math.floor(pos.z / size), 0), }, v = Vector.origin, { field } = this; return field?.[point.x]?.[point.y]?.[point.z] ? field[point.x][point.y][point.z].copy() : v; } init(container) { this.container = container; this._setup(); } reset() { } update() { if (!this.container) { return; } this._calculateField(); this.noiseW += this.options.increment; if (this.options.draw) { this.container.canvas.draw(ctx => this._drawField(ctx)); } } _calculateField() { const { field, noiseGen, options, noiseW } = this, lengthFactor = options.factor.length, angleFactor = options.factor.angle; for (let x = 0; x < options.columns; x++) { for (let y = 0; y < options.rows; y++) { for (let z = 0; z < options.layers; z++) { const cell = field[x][y][z]; cell.length = noiseGen.noise4d(x * lengthFactor + options.offset.x, y * lengthFactor + options.offset.y, z * lengthFactor + options.offset.z, noiseW); cell.angle = noiseGen.noise4d(x * angleFactor, y * angleFactor, z * angleFactor, noiseW) * doublePI; } } } } _drawField(ctx) { const { field, options } = this; for (let x = 0; x < options.columns; x++) { const column = field[x]; for (let y = 0; y < options.rows; y++) { const cell = column[y][0], { angle, length } = cell; ctx.setTransform(1, 0, 0, 1, x * this.options.size, y * this.options.size); ctx.rotate(angle); ctx.strokeStyle = "white"; ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(0, this.options.size * length); ctx.stroke(); ctx.setTransform(1, 0, 0, 1, 0, 0); } } } _initField() { const { columns, rows, layers } = this.options; this.field = new Array(columns); for (let x = 0; x < columns; x++) { this.field[x] = new Array(rows); for (let y = 0; y < rows; y++) { this.field[x][y] = new Array(layers); for (let z = 0; z < layers; z++) { this.field[x][y][z] = Vector.origin; } } } } _resetField() { const container = this.container; if (!container) { return; } const sourceOptions = container.actualOptions.particles.move.path.options, { options } = this; options.width = container.canvas.size.width; options.height = container.canvas.size.height; options.size = sourceOptions.size > 0 ? sourceOptions.size : defaultOptions.size; options.increment = sourceOptions.increment > 0 ? sourceOptions.increment : defaultOptions.increment; options.draw = !!sourceOptions.draw; const offset = sourceOptions.offset; options.offset.x = offset?.x ?? defaultOptions.offset.x; options.offset.y = offset?.y ?? defaultOptions.offset.y; options.offset.z = offset?.z ?? defaultOptions.offset.z; const factor = sourceOptions.factor; options.factor.angle = factor?.angle ?? defaultOptions.factor.angle; options.factor.length = factor?.length ?? defaultOptions.factor.length; options.seed = sourceOptions.seed; this.noiseGen.seed(options.seed ?? getRandom()); options.columns = Math.floor(options.width / options.size) + 1; options.rows = Math.floor(options.height / options.size) + 1; options.layers = Math.floor(container.zLayers / options.size) + 1; this._initField(); } _setup() { this.noiseW = 0; this._resetField(); addEventListener("resize", () => this._resetField()); } }