mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-23 23:27:09 +00:00
Add NoiseSettingsVisualizer
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
import Split from 'split.js'
|
import Split from 'split.js'
|
||||||
import {
|
import {
|
||||||
AbstractView,
|
|
||||||
Base,
|
Base,
|
||||||
DataModel,
|
DataModel,
|
||||||
locale,
|
locale,
|
||||||
@@ -8,10 +7,10 @@ import {
|
|||||||
ModelPath,
|
ModelPath,
|
||||||
SourceView,
|
SourceView,
|
||||||
TreeView,
|
TreeView,
|
||||||
Path,
|
|
||||||
} from '@mcschema/core'
|
} from '@mcschema/core'
|
||||||
import { getCollections, getSchemas } from '@mcschema/java-1.16'
|
import { getCollections, getSchemas } from '@mcschema/java-1.16'
|
||||||
import { VisualizerView } from './visualization/VisualizerView'
|
import { VisualizerView } from './visualization/VisualizerView'
|
||||||
|
import { Visualizer } from './visualization/Visualizer'
|
||||||
import { RegistryFetcher } from './RegistryFetcher'
|
import { RegistryFetcher } from './RegistryFetcher'
|
||||||
import { ErrorsView } from './ErrorsView'
|
import { ErrorsView } from './ErrorsView'
|
||||||
import config from '../config.json'
|
import config from '../config.json'
|
||||||
@@ -64,9 +63,8 @@ const treeViewObserver = (el: HTMLElement) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const treeViewNodeInjector = (path: ModelPath, view: TreeView) => {
|
const treeViewNodeInjector = (path: ModelPath, view: TreeView) => {
|
||||||
return Object.keys(VisualizerView.visualizers)
|
return Visualizer.visualizers
|
||||||
.map(id => VisualizerView.visualizers[id])
|
.filter(v => v.onPath(path))
|
||||||
.filter(v => path.equals(v.path()))
|
|
||||||
.filter(v => v.active(path.getModel()))
|
.filter(v => v.active(path.getModel()))
|
||||||
.map(v => {
|
.map(v => {
|
||||||
const id = view.registerClick(() => {
|
const id = view.registerClick(() => {
|
||||||
|
|||||||
@@ -1,36 +1,34 @@
|
|||||||
import SimplexNoise from 'simplex-noise'
|
import SimplexNoise from 'simplex-noise'
|
||||||
import { DataModel, Path } from "@mcschema/core";
|
import { DataModel, Path } from "@mcschema/core"
|
||||||
import { Visualizer } from './VisualizerView';
|
import { Visualizer } from './Visualizer'
|
||||||
|
|
||||||
|
|
||||||
export class BiomeNoiseVisualizer implements Visualizer {
|
export class BiomeNoiseVisualizer extends Visualizer {
|
||||||
static readonly noiseMaps = ['altitude', 'temperature', 'humidity', 'weirdness']
|
static readonly noiseMaps = ['altitude', 'temperature', 'humidity', 'weirdness']
|
||||||
|
static readonly path = new Path(['generator', 'biome_source'])
|
||||||
private noise: SimplexNoise[]
|
private noise: SimplexNoise[]
|
||||||
private biomeSource: any
|
|
||||||
private offsetX: number = 0
|
private offsetX: number = 0
|
||||||
private offsetY: number = 0
|
private offsetY: number = 0
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
super()
|
||||||
this.noise = BiomeNoiseVisualizer.noiseMaps.map(e => new SimplexNoise())
|
this.noise = BiomeNoiseVisualizer.noiseMaps.map(e => new SimplexNoise())
|
||||||
}
|
}
|
||||||
|
|
||||||
path() {
|
onPath(path: Path) {
|
||||||
return new Path(['generator', 'biome_source'])
|
return path.equals(BiomeNoiseVisualizer.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
active(model: DataModel) {
|
active(model: DataModel) {
|
||||||
const biomeSource = new Path(['generator', 'biome_source'])
|
return model.get(BiomeNoiseVisualizer.path) !== undefined
|
||||||
return model.get(biomeSource) !== undefined
|
&& model.get(BiomeNoiseVisualizer.path.push('type')) === 'minecraft:multi_noise'
|
||||||
&& model.get(biomeSource.push('type')) === 'minecraft:multi_noise'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dirty(model: DataModel) {
|
getState(model: DataModel) {
|
||||||
return JSON.stringify(this.biomeSource)
|
return model.get(BiomeNoiseVisualizer.path)
|
||||||
!== JSON.stringify(model.get(new Path(['generator', 'biome_source'])))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(model: DataModel, img: ImageData) {
|
draw(model: DataModel, img: ImageData) {
|
||||||
this.biomeSource = JSON.parse(JSON.stringify(model.get(new Path(['generator', 'biome_source']))))
|
|
||||||
const data = img.data
|
const data = img.data
|
||||||
for (let x = 0; x < 200; x += 1) {
|
for (let x = 0; x < 200; x += 1) {
|
||||||
for (let y = 0; y < 100; y += 1) {
|
for (let y = 0; y < 100; y += 1) {
|
||||||
@@ -48,14 +46,13 @@ export class BiomeNoiseVisualizer implements Visualizer {
|
|||||||
onDrag(from: number[], to: number[]) {
|
onDrag(from: number[], to: number[]) {
|
||||||
this.offsetX += (to[0] - from[0]) / 128
|
this.offsetX += (to[0] - from[0]) / 128
|
||||||
this.offsetY += (to[1] - from[1]) / 128
|
this.offsetY += (to[1] - from[1]) / 128
|
||||||
this.biomeSource = {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private closestBiome(x: number, y: number): string {
|
private closestBiome(x: number, y: number): string {
|
||||||
const noise = this.getNoise(x, y)
|
const noise = this.getNoise(x, y)
|
||||||
if (!this.biomeSource.biomes) return ''
|
if (!this.state.biomes) return ''
|
||||||
|
|
||||||
return this.biomeSource.biomes
|
return this.state.biomes
|
||||||
.map((b: any) => ({
|
.map((b: any) => ({
|
||||||
biome: b.biome ?? '',
|
biome: b.biome ?? '',
|
||||||
distance: this.distance([...noise, 0], [...BiomeNoiseVisualizer.noiseMaps.map(s => b.parameters[s]), b.parameters.offset])
|
distance: this.distance([...noise, 0], [...BiomeNoiseVisualizer.noiseMaps.map(s => b.parameters[s]), b.parameters.offset])
|
||||||
@@ -74,7 +71,7 @@ export class BiomeNoiseVisualizer implements Visualizer {
|
|||||||
|
|
||||||
private getNoise(x: number, y: number): number[] {
|
private getNoise(x: number, y: number): number[] {
|
||||||
return BiomeNoiseVisualizer.noiseMaps.map((id, index) => {
|
return BiomeNoiseVisualizer.noiseMaps.map((id, index) => {
|
||||||
const config = this.biomeSource[`${id}_noise`]
|
const config = this.state[`${id}_noise`]
|
||||||
let n = 0
|
let n = 0
|
||||||
let scale = 2**config.firstOctave
|
let scale = 2**config.firstOctave
|
||||||
for (let i = 0; i < config.amplitudes.length; i++) {
|
for (let i = 0; i < config.amplitudes.length; i++) {
|
||||||
|
|||||||
108
src/app/visualization/NoiseSettingsVisualizer.ts
Normal file
108
src/app/visualization/NoiseSettingsVisualizer.ts
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import SimplexNoise from 'simplex-noise'
|
||||||
|
import { DataModel, Path } from "@mcschema/core"
|
||||||
|
import { Visualizer } from './Visualizer'
|
||||||
|
|
||||||
|
const debug = false
|
||||||
|
|
||||||
|
export class NoiseSettingsVisualizer extends Visualizer {
|
||||||
|
private noise: SimplexNoise
|
||||||
|
private offsetX: number
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.noise = new SimplexNoise()
|
||||||
|
this.offsetX = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
onPath(path: Path) {
|
||||||
|
return path.equals(new Path(['generator', 'settings', 'noise']))
|
||||||
|
|| path.equals(new Path(['noise']))
|
||||||
|
}
|
||||||
|
|
||||||
|
active(model: DataModel) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
getState(model: DataModel) {
|
||||||
|
return model.get(new Path(['generator', 'settings', 'noise']))
|
||||||
|
?? model.get(new Path(['noise']))
|
||||||
|
}
|
||||||
|
|
||||||
|
onDrag(from: number[], to: number[]) {
|
||||||
|
this.offsetX += (to[0] - from[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(model: DataModel, img: ImageData) {
|
||||||
|
const data = img.data
|
||||||
|
for (let x = 0; x < 200; x += 1) {
|
||||||
|
const densities = this.fillNoiseColumn(x - this.offsetX).reverse()
|
||||||
|
for (let y = 0; y < 100; y += 1) {
|
||||||
|
const i = (y * (img.width * 4)) + (x * 4)
|
||||||
|
const color = this.getColor(densities, y)
|
||||||
|
data[i] = (debug && densities[y] > 0) ? 255 : color
|
||||||
|
data[i + 1] = color
|
||||||
|
data[i + 2] = color
|
||||||
|
data[i + 3] = 255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getColor(densities: number[], y: number): number {
|
||||||
|
if (debug) {
|
||||||
|
return -densities[y] * 128 + 128
|
||||||
|
}
|
||||||
|
if (densities[y] > 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if (densities[y+1] > 0) {
|
||||||
|
return 150
|
||||||
|
}
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
|
||||||
|
private fillNoiseColumn(x: number) {
|
||||||
|
const data = Array(100)
|
||||||
|
for (let y = 0; y < 100; y += 1) {
|
||||||
|
let density = this.getNoise(x, y)
|
||||||
|
density = density < -1 ? -1 : density > 1 ? 1 : density
|
||||||
|
|
||||||
|
const heightFactor = (1 - y / 50) * this.state.density_factor + this.state.density_offset
|
||||||
|
density += heightFactor * (heightFactor > 0 ? 16 : 4)
|
||||||
|
|
||||||
|
if (this.state.top_slide.size > 0) {
|
||||||
|
density = this.clampedLerp(
|
||||||
|
this.state.top_slide.target / 100,
|
||||||
|
density,
|
||||||
|
(100 - y - (this.state.top_slide.offset * 4)) / (this.state.top_slide.size * 4)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.bottom_slide.size > 0) {
|
||||||
|
density = this.clampedLerp(
|
||||||
|
this.state.bottom_slide.target / 100,
|
||||||
|
density,
|
||||||
|
(y - (this.state.bottom_slide.offset * 4)) / (this.state.bottom_slide.size * 4)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
data[y] = density
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
private getNoise(x: number, y: number) {
|
||||||
|
const octaves = [ [64, 1], [32, 2], [16, 4], [8, 8], [4, 16] ]
|
||||||
|
return octaves
|
||||||
|
.map(o => this.noise.noise2D(x / o[0], y / o[0]) / o[1])
|
||||||
|
.reduce((prev, acc) => prev + acc)
|
||||||
|
}
|
||||||
|
|
||||||
|
private clampedLerp(a: number, b: number, c: number): number {
|
||||||
|
if (c < 0) {
|
||||||
|
return a;
|
||||||
|
} else if (c > 1) {
|
||||||
|
return b
|
||||||
|
} else {
|
||||||
|
return a + c * (b - a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/app/visualization/Visualizer.ts
Normal file
26
src/app/visualization/Visualizer.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { DataModel, Path } from "@mcschema/core"
|
||||||
|
import { BiomeNoiseVisualizer } from "./BiomeNoiseVisualizer"
|
||||||
|
import { NoiseSettingsVisualizer } from "./NoiseSettingsVisualizer"
|
||||||
|
|
||||||
|
export abstract class Visualizer {
|
||||||
|
state: any
|
||||||
|
|
||||||
|
dirty(model: DataModel): boolean {
|
||||||
|
return JSON.stringify(this.state) !== JSON.stringify(this.getState(model))
|
||||||
|
}
|
||||||
|
|
||||||
|
active(model: DataModel): boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract onPath(path: Path): boolean
|
||||||
|
abstract getState(model: DataModel): any
|
||||||
|
abstract draw(model: DataModel, img: ImageData): void
|
||||||
|
|
||||||
|
onDrag(from: number[], to: number[]): void {}
|
||||||
|
|
||||||
|
static visualizers: Visualizer[] = [
|
||||||
|
new BiomeNoiseVisualizer(),
|
||||||
|
new NoiseSettingsVisualizer()
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
import { AbstractView, DataModel, Path } from "@mcschema/core";
|
import { AbstractView, DataModel, Path } from "@mcschema/core"
|
||||||
import { BiomeNoiseVisualizer } from "./BiomeNoiseVisualizer";
|
import { BiomeNoiseVisualizer } from "./BiomeNoiseVisualizer"
|
||||||
|
import { NoiseSettingsVisualizer } from "./NoiseSettingsVisualizer"
|
||||||
export interface Visualizer {
|
import { Visualizer } from "./Visualizer"
|
||||||
path(): Path
|
|
||||||
active(model: DataModel): boolean
|
|
||||||
dirty(model: DataModel): boolean
|
|
||||||
draw(model: DataModel, img: ImageData): void
|
|
||||||
onDrag?(from: number[], to: number[]): void
|
|
||||||
}
|
|
||||||
|
|
||||||
export class VisualizerView extends AbstractView {
|
export class VisualizerView extends AbstractView {
|
||||||
ctx: CanvasRenderingContext2D
|
ctx: CanvasRenderingContext2D
|
||||||
@@ -34,9 +28,10 @@ export class VisualizerView extends AbstractView {
|
|||||||
if (this.dragStart === undefined) return
|
if (this.dragStart === undefined) return
|
||||||
if (this.visualizer?.onDrag) {
|
if (this.visualizer?.onDrag) {
|
||||||
this.visualizer.onDrag(this.dragStart, [evt.offsetX, evt.offsetY])
|
this.visualizer.onDrag(this.dragStart, [evt.offsetX, evt.offsetY])
|
||||||
|
this.visualizer.state = {}
|
||||||
|
this.invalidated()
|
||||||
}
|
}
|
||||||
this.dragStart = [evt.offsetX, evt.offsetY]
|
this.dragStart = [evt.offsetX, evt.offsetY]
|
||||||
this.invalidated()
|
|
||||||
})
|
})
|
||||||
canvas.addEventListener('mouseup', evt => {
|
canvas.addEventListener('mouseup', evt => {
|
||||||
this.dragStart = undefined
|
this.dragStart = undefined
|
||||||
@@ -44,9 +39,13 @@ export class VisualizerView extends AbstractView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
invalidated() {
|
invalidated() {
|
||||||
if (this.active && this.visualizer && this.visualizer.active(this.model)) {
|
let newState: any
|
||||||
|
if (this.active && this.visualizer
|
||||||
|
&& this.visualizer.active(this.model)
|
||||||
|
&& (newState = this.visualizer.getState(this.model))) {
|
||||||
if (this.visualizer.dirty(this.model)) {
|
if (this.visualizer.dirty(this.model)) {
|
||||||
const img = this.ctx.createImageData(200, 100)
|
const img = this.ctx.createImageData(200, 100)
|
||||||
|
this.visualizer.state = JSON.parse(JSON.stringify(newState))
|
||||||
this.visualizer.draw(this.model, img)
|
this.visualizer.draw(this.model, img)
|
||||||
this.ctx.putImageData(img, 0, 0)
|
this.ctx.putImageData(img, 0, 0)
|
||||||
}
|
}
|
||||||
@@ -68,10 +67,7 @@ export class VisualizerView extends AbstractView {
|
|||||||
set(visualizer: Visualizer) {
|
set(visualizer: Visualizer) {
|
||||||
this.active = true
|
this.active = true
|
||||||
this.visualizer = visualizer
|
this.visualizer = visualizer
|
||||||
|
this.visualizer.state = undefined
|
||||||
this.invalidated()
|
this.invalidated()
|
||||||
}
|
}
|
||||||
|
|
||||||
static visualizers: {[key: string]: Visualizer} = {
|
|
||||||
'biome-noise': new BiomeNoiseVisualizer()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user