Add zooming the biome noise visualization

This commit is contained in:
Misode
2020-08-27 00:24:52 +02:00
parent 2c8eeceac3
commit 5afc8ef1ed
7 changed files with 77 additions and 32 deletions

View File

@@ -120,13 +120,13 @@ const treeControlsMenu = document.getElementById('tree-controls-menu')!
const treeControlsReset = document.getElementById('tree-controls-reset')!
const treeControlsUndo = document.getElementById('tree-controls-undo')!
const treeControlsRedo = document.getElementById('tree-controls-redo')!
const visualizerOutput = document.getElementById('visualizer-output')!
const visualizerContent = document.getElementById('visualizer-content')!
Split([treeViewEl, sourceViewEl], {
sizes: [66, 34]
})
Split([sourceViewOutput, visualizerOutput], {
Split([sourceViewOutput, visualizerContent], {
sizes: [60, 40],
direction: 'vertical'
})
@@ -143,7 +143,7 @@ const views = {
indentation: 2
}),
'errors': new ErrorsView(dummyModel, errorsViewEl),
'visualizer': new VisualizerView(dummyModel, visualizerOutput as HTMLCanvasElement)
'visualizer': new VisualizerView(dummyModel, visualizerContent)
}
const COLLECTIONS = getCollections()

View File

@@ -1,6 +1,7 @@
import SimplexNoise from 'simplex-noise'
import { DataModel, Path, ModelPath } from "@mcschema/core"
import { Visualizer } from './Visualizer'
import { VisualizerView } from './VisualizerView'
export class BiomeNoiseVisualizer extends Visualizer {
@@ -8,6 +9,7 @@ export class BiomeNoiseVisualizer extends Visualizer {
private noise: SimplexNoise[]
private offsetX: number = 0
private offsetY: number = 0
private viewScale: number = 0
private biomeColors: {[id: string]: number[]} = {}
constructor() {
@@ -26,10 +28,13 @@ export class BiomeNoiseVisualizer extends Visualizer {
draw(model: DataModel, img: ImageData) {
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 b = this.closestBiome(x, y)
const xx = (x - this.offsetX) * s - 100 * s
const yy = (y- this.offsetY) * s - 50 * s
const b = this.closestBiome(xx, yy)
const color = this.getBiomeColor(b)
data[i] = color[0]
data[i + 1] = color[1]
@@ -39,9 +44,21 @@ export class BiomeNoiseVisualizer extends Visualizer {
}
}
onDrag(from: number[], to: number[]) {
this.offsetX += to[0] - from[0]
this.offsetY += to[1] - from[1]
onDrag(fromX: number, fromY: number, toX: number, toY: number) {
this.offsetX += toX - fromX
this.offsetY += toY - fromY
}
addControls(el: HTMLElement, view: VisualizerView) {
el.insertAdjacentHTML('beforeend', `<button class="btn" id="visualizer-controls-toggle"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 2a.75.75 0 01.75.75v4.5h4.5a.75.75 0 010 1.5h-4.5v4.5a.75.75 0 01-1.5 0v-4.5h-4.5a.75.75 0 010-1.5h4.5v-4.5A.75.75 0 018 2z"></path></svg></button><button class="btn" id="visualizer-controls-toggle"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2 8a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H2.75A.75.75 0 012 8z"></path></svg></button>`)
el.childNodes[0].addEventListener('click', () => {
this.viewScale -= 0.5
view.redraw()
})
el.childNodes[1].addEventListener('click', () => {
this.viewScale += 0.5
view.redraw()
})
}
private closestBiome(x: number, y: number): string {
@@ -71,7 +88,7 @@ export class BiomeNoiseVisualizer extends Visualizer {
let n = 0
let scale = 2**config.firstOctave
for (let i = 0; i < config.amplitudes.length; i++) {
n += this.noise[index].noise2D((x - this.offsetX)*scale, (y- this.offsetY)*scale + i)
n += this.noise[index].noise2D(x * scale, y * scale + i)
* config.amplitudes[i] / (2**scale)
scale *= 2
}

View File

@@ -37,8 +37,8 @@ export class NoiseSettingsVisualizer extends Visualizer {
}
}
onDrag(from: number[], to: number[]) {
this.offsetX += (to[0] - from[0])
onDrag(fromX: number, fromY: number, toX: number, toY: number) {
this.offsetX += toX - fromX
}
private getColor(densities: number[], y: number): number {

View File

@@ -1,4 +1,5 @@
import { DataModel, ModelPath } from "@mcschema/core"
import { VisualizerView } from "./VisualizerView"
export abstract class Visualizer {
state: any
@@ -11,5 +12,7 @@ export abstract class Visualizer {
abstract active(path: ModelPath): boolean
abstract draw(model: DataModel, img: ImageData): void
onDrag(from: number[], to: number[]): void {}
onDrag(fromX: number, fromY: number, toX: number, toY: number): void {}
addControls(el: HTMLElement, view: VisualizerView): void {}
}

View File

@@ -8,37 +8,47 @@ export class VisualizerView extends AbstractView {
visualizer?: Visualizer
active: boolean
path?: ModelPath
canvas: HTMLElement
el: HTMLElement
canvas: HTMLCanvasElement
sourceView: HTMLElement
gutter: HTMLElement
controls: HTMLElement
lastHeight?: string
dragStart?: number[]
constructor(model: DataModel, canvas: HTMLCanvasElement) {
constructor(model: DataModel, el: HTMLElement) {
super(model)
this.ctx = canvas.getContext('2d')!
this.el = el
this.canvas = el.querySelector('canvas') as HTMLCanvasElement
this.ctx = this.canvas.getContext('2d')!
this.active = false
this.canvas = canvas
this.gutter = canvas.parentElement!.querySelector('.gutter') as HTMLElement
this.sourceView = canvas.parentElement!.getElementsByTagName('textarea')[0] as HTMLElement
this.gutter = el.parentElement!.querySelector('.gutter') as HTMLElement
this.sourceView = el.parentElement!.getElementsByTagName('textarea')[0] as HTMLElement
this.controls = el.querySelector('.visualizer-controls') as HTMLElement
canvas.addEventListener('mousedown', evt => {
this.canvas.addEventListener('mousedown', evt => {
this.dragStart = [evt.offsetX, evt.offsetY]
})
canvas.addEventListener('mousemove', evt => {
this.canvas.addEventListener('mousemove', evt => {
if (this.dragStart === undefined) return
if (this.visualizer?.onDrag) {
this.visualizer.onDrag(this.dragStart, [evt.offsetX, evt.offsetY])
this.visualizer.state = {}
this.invalidated()
this.visualizer.onDrag(this.dragStart[0], this.dragStart[1], evt.offsetX, evt.offsetY)
this.redraw()
}
this.dragStart = [evt.offsetX, evt.offsetY]
})
canvas.addEventListener('mouseup', evt => {
this.canvas.addEventListener('mouseup', evt => {
this.dragStart = undefined
})
}
redraw() {
if (this.active && this.visualizer) {
this.visualizer.state = {}
this.invalidated()
}
}
invalidated() {
this.path = this.path?.withModel(this.model)
let newState: any
@@ -51,14 +61,14 @@ export class VisualizerView extends AbstractView {
this.visualizer.draw(this.model, img)
this.ctx.putImageData(img, 0, 0)
}
this.canvas.style.display = 'block'
this.el.style.display = 'block'
this.gutter.style.display = 'block'
if (this.lastHeight) {
this.sourceView.style.height = this.lastHeight
this.lastHeight = undefined
}
} else {
this.canvas.style.display = 'none'
this.el.style.display = 'none'
this.gutter.style.display = 'none'
this.lastHeight = this.sourceView.style.height
this.sourceView.style.height = '100%'
@@ -70,7 +80,9 @@ export class VisualizerView extends AbstractView {
this.visualizer = visualizer
this.path = path
this.visualizer.state = undefined
this.invalidated()
this.controls.innerHTML = ''
this.visualizer.addControls(this.controls, this)
this.redraw()
}
static visualizers: Visualizer[] = [

View File

@@ -94,7 +94,10 @@
</div>
<div class="source-content">
<textarea id="source-view-output" spellcheck="false" autocorrect="off" autocapitalize="off"></textarea>
<canvas width="200" height="100" id="visualizer-output"></canvas>
<div class="visualizer-content" id="visualizer-content">
<div class="visualizer-controls" id="visualizer-controls"></div>
<canvas width="200" height="100" id="visualizer-output"></canvas>
</div>
</div>
</div>
<div id="home-view" class="home">

View File

@@ -193,7 +193,8 @@ body {
}
.tree-controls,
.source-controls {
.source-controls,
.visualizer-controls {
display: flex;
flex-direction: row-reverse;
position: absolute;
@@ -203,7 +204,8 @@ body {
}
.tree-controls .btn:not(:first-child),
.source-controls .btn:not(:first-child) {
.source-controls .btn:not(:first-child),
.visualizer-controls .btn:not(:first-child) {
margin-right: 5px;
}
@@ -235,11 +237,19 @@ body {
cursor: ew-resize;
}
.source canvas {
.visualizer-content {
width: 100%;
max-width: 100%;
/* position: absolute; */
/* bottom: 0; */
position: relative;
}
.visualizer-controls {
right: 0;
}
.visualizer-content canvas {
width: 100%;
height: 100%;
background-color: var(--nav-faded);
display: block;
cursor: grab;