mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-26 16:35:39 +00:00
Optimize biome noise visualizer
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import SimplexNoise from 'simplex-noise'
|
||||
import { DataModel, Path, ModelPath } from "@mcschema/core"
|
||||
import { NormalNoise } from './NormalNoise'
|
||||
import { Visualizer } from './Visualizer'
|
||||
import { VisualizerView } from './VisualizerView'
|
||||
|
||||
@@ -7,16 +7,18 @@ const LOCAL_STORAGE_BIOME_COLORS = 'biome_colors'
|
||||
|
||||
export class BiomeNoiseVisualizer extends Visualizer {
|
||||
static readonly noiseMaps = ['altitude', 'temperature', 'humidity', 'weirdness']
|
||||
private noise: SimplexNoise[]
|
||||
private seed: string
|
||||
private noise: NormalNoise[]
|
||||
private offsetX: number = 0
|
||||
private offsetY: number = 0
|
||||
private viewScale: number = 0
|
||||
private biomeColors: {[id: string]: number[]}
|
||||
private biomeColors: { [id: string]: number[] }
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.seed = this.hexId()
|
||||
this.biomeColors = JSON.parse(localStorage.getItem(LOCAL_STORAGE_BIOME_COLORS) ?? '{}')
|
||||
this.noise = BiomeNoiseVisualizer.noiseMaps.map(e => new SimplexNoise())
|
||||
this.noise = []
|
||||
}
|
||||
|
||||
getName() {
|
||||
@@ -29,13 +31,18 @@ export class BiomeNoiseVisualizer extends Visualizer {
|
||||
}
|
||||
|
||||
draw(model: DataModel, img: ImageData) {
|
||||
this.noise = BiomeNoiseVisualizer.noiseMaps.map((id, i) => {
|
||||
const config = this.state[`${id}_noise`]
|
||||
return new NormalNoise(this.seed + i, config.firstOctave, config.amplitudes)
|
||||
})
|
||||
|
||||
const data = img.data
|
||||
const s = (2 ** this.viewScale)
|
||||
for (let x = 0; x < 200; x += 1) {
|
||||
for (let y = 0; y < 100; y += 1) {
|
||||
const i = (y * (img.width * 4)) + (x * 4)
|
||||
const xx = (x - this.offsetX) * s - 100 * s
|
||||
const yy = (y- this.offsetY) * s - 50 * s
|
||||
const yy = (y - this.offsetY) * s - 50 * s
|
||||
const b = this.closestBiome(xx, yy)
|
||||
const color = this.getBiomeColor(b)
|
||||
data[i] = color[0]
|
||||
@@ -64,7 +71,7 @@ export class BiomeNoiseVisualizer extends Visualizer {
|
||||
}
|
||||
|
||||
private closestBiome(x: number, y: number): string {
|
||||
const noise = this.getNoise(x, y)
|
||||
const noise = this.noise.map(n => n.getValue(x, y))
|
||||
if (!this.state.biomes) return ''
|
||||
|
||||
return this.state.biomes
|
||||
@@ -73,48 +80,17 @@ export class BiomeNoiseVisualizer extends Visualizer {
|
||||
distance: this.distance([...noise, 0], [...BiomeNoiseVisualizer.noiseMaps.map(s => b.parameters[s]), b.parameters.offset])
|
||||
}))
|
||||
.sort((a: any, b: any) => a.distance - b.distance)
|
||||
[0].biome
|
||||
[0].biome
|
||||
}
|
||||
|
||||
private distance(a: number[], b: number[]) {
|
||||
let d = 0
|
||||
for (let i = 0; i < a.length; i ++) {
|
||||
d += (a[i]-b[i]) * (a[i]-b[i])
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
d += (a[i] - b[i]) * (a[i] - b[i])
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
private getNoise(x: number, y: number): number[] {
|
||||
return BiomeNoiseVisualizer.noiseMaps.map((id, index) => {
|
||||
const config = this.state[`${id}_noise`]
|
||||
|
||||
let min = +Infinity
|
||||
let max = -Infinity
|
||||
config.amplitudes
|
||||
.filter((a: number) => a !== 0)
|
||||
.forEach((a: number) => {
|
||||
min = Math.min(min, a)
|
||||
max = Math.max(max, a)
|
||||
})
|
||||
const expectedDeviation = 0.1 * (1 + 1 / (max - min + 1))
|
||||
const factor = (1/6) / expectedDeviation
|
||||
|
||||
let value = 0
|
||||
let inputF = Math.pow(2, config.firstOctave)
|
||||
let valueF = Math.pow(2, config.amplitudes.length - 1) / (Math.pow(2, config.amplitudes.length) - 1)
|
||||
for (let i = 0; i < config.amplitudes.length; i++) {
|
||||
value += config.amplitudes[i] * this.noise[index].noise2D(this.wrap(x * inputF), this.wrap(y * inputF) + i) * valueF
|
||||
inputF *= 2
|
||||
valueF /= 2
|
||||
}
|
||||
return 2 * value * factor
|
||||
})
|
||||
}
|
||||
|
||||
private wrap(value: number) {
|
||||
return value - Math.floor(value / 3.3554432E7 + 0.5) * 3.3554432E7
|
||||
}
|
||||
|
||||
getBiomeColor(biome: string): number[] {
|
||||
const color = this.biomeColors[biome]
|
||||
if (color === undefined) {
|
||||
@@ -138,6 +114,16 @@ export class BiomeNoiseVisualizer extends Visualizer {
|
||||
}
|
||||
|
||||
private hash(s: string) {
|
||||
return (s ?? '').split('').reduce((a,b)=>{a=((a<<5)-a)+b.charCodeAt(0);return a&a},0)
|
||||
return (s ?? '').split('').reduce((a, b) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a }, 0)
|
||||
}
|
||||
|
||||
private dec2hex(dec: number) {
|
||||
return ('0' + dec.toString(16)).substr(-2)
|
||||
}
|
||||
|
||||
private hexId(length = 12) {
|
||||
var arr = new Uint8Array(length / 2)
|
||||
window.crypto.getRandomValues(arr)
|
||||
return Array.from(arr, this.dec2hex).join('')
|
||||
}
|
||||
}
|
||||
|
||||
44
src/app/visualization/NormalNoise.ts
Normal file
44
src/app/visualization/NormalNoise.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import SimplexNoise from 'simplex-noise'
|
||||
|
||||
export class NormalNoise {
|
||||
private noiseLevels: SimplexNoise[]
|
||||
private amplitudes: number[]
|
||||
private valueFactor: number
|
||||
private lowestFreqInputFactor: number
|
||||
private lowestFreqValueFactor: number
|
||||
|
||||
constructor(seed: string, firstOctave: number, amplitudes: number[]) {
|
||||
this.amplitudes = amplitudes
|
||||
this.noiseLevels = amplitudes.map((a, i) => new SimplexNoise(seed + 'a' + i))
|
||||
|
||||
let min = +Infinity
|
||||
let max = -Infinity
|
||||
for (let a of amplitudes) {
|
||||
if (a !== 0) {
|
||||
min = Math.min(min, a)
|
||||
max = Math.max(max, a)
|
||||
}
|
||||
}
|
||||
const expectedDeviation = 0.1 * (1 + 1 / (max - min + 1))
|
||||
this.valueFactor = (1/6) / expectedDeviation
|
||||
|
||||
this.lowestFreqInputFactor = Math.pow(2, firstOctave)
|
||||
this.lowestFreqValueFactor = Math.pow(2, (amplitudes.length - 1)) / (Math.pow(2, amplitudes.length) - 1)
|
||||
}
|
||||
|
||||
getValue(x: number, y: number) {
|
||||
let value = 0
|
||||
let inputF = this.lowestFreqInputFactor
|
||||
let valueF = this.lowestFreqValueFactor
|
||||
for (let i = 0; i < this.amplitudes.length; i += 1) {
|
||||
value += this.amplitudes[i] * this.noiseLevels[i].noise2D(this.wrap(x * inputF), this.wrap(y * inputF) + i) * valueF
|
||||
inputF *= 2
|
||||
valueF /= 2
|
||||
}
|
||||
return 2 * value * this.valueFactor
|
||||
}
|
||||
|
||||
private wrap(value: number) {
|
||||
return value - Math.floor(value / 3.3554432E7 + 0.5) * 3.3554432E7
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user