diff --git a/package-lock.json b/package-lock.json index 51ab9989..0be68b34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,16 +5,16 @@ "requires": true, "dependencies": { "@mcschema/core": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@mcschema/core/-/core-0.6.1.tgz", - "integrity": "sha512-9DIrcDw/OLs/SrPWBKK/8Y7JVUP1zu9MZfGv+JUrHahkHGsG7MZEAzPP8xqTWiMino2Z80F2khh2UPZX4zlRAQ==" + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@mcschema/core/-/core-0.6.2.tgz", + "integrity": "sha512-L/ga6SSeJ1fzDxXN+kYs0YloFsVKipsK+WrRBtkdGm1PrccYw9jwGalVLneVs/JnlDicu60ExrZvbygV9TECag==" }, "@mcschema/java-1.16": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.16/-/java-1.16-0.3.1.tgz", - "integrity": "sha512-f9QOtKVSIngJGoBX2enC6cYJvmALzIy0sf7V7HCICYcWkzDj5Nj8uPEfKttpwDYgXC+jBcKI99niK/psP/dRYQ==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@mcschema/java-1.16/-/java-1.16-0.3.2.tgz", + "integrity": "sha512-wEWcAJdnFy8a6Q33sjub9Qrg21EIBiYYCswQe/kE6SBqHjYx6TsKt7733QKhJHPE71PtlZyGPkkw4+6x//nIiw==", "requires": { - "@mcschema/core": "^0.6.1" + "@mcschema/core": "^0.6.2" } }, "@mcschema/locales": { @@ -4403,6 +4403,11 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, + "simplex-noise": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/simplex-noise/-/simplex-noise-2.4.0.tgz", + "integrity": "sha512-OjyDWm/QZjVbMrPxDVi9b2as+SeNn9EBXlrWVRlFW+TSyWMSXouDryXkQN0vf5YP+QZKobrmkvx1eQYPLtuqfw==" + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", diff --git a/package.json b/package.json index bf6b5067..88517c3b 100644 --- a/package.json +++ b/package.json @@ -12,14 +12,15 @@ "author": "Misode", "license": "MIT", "dependencies": { - "@mcschema/core": "^0.6.1", + "@mcschema/core": "^0.6.2", + "@mcschema/java-1.16": "^0.3.2", "@mcschema/locales": "^0.1.5", - "@mcschema/java-1.16": "^0.3.1", "@types/google.analytics": "0.0.40", "@types/split.js": "^1.4.0", "copy-webpack-plugin": "^6.0.1", "html-webpack-plugin": "^4.3.0", "merge-jsons-webpack-plugin": "^1.0.21", + "simplex-noise": "^2.4.0", "split.js": "^1.5.11", "ts-loader": "^7.0.4", "typescript": "^3.9.3", diff --git a/src/app/app.ts b/src/app/app.ts index 72e4551a..bdac668a 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -2,14 +2,16 @@ import Split from 'split.js' import { AbstractView, Base, - CollectionRegistry, DataModel, locale, LOCALES, + ModelPath, SourceView, TreeView, + Path, } from '@mcschema/core' import { getCollections, getSchemas } from '@mcschema/java-1.16' +import { VisualizerView } from './visualization/VisualizerView' import { RegistryFetcher } from './RegistryFetcher' import { ErrorsView } from './ErrorsView' import config from '../config.json' @@ -61,6 +63,20 @@ const treeViewObserver = (el: HTMLElement) => { }) } +const treeViewNodeInjector = (path: ModelPath, view: TreeView) => { + return Object.keys(VisualizerView.visualizers) + .map(id => VisualizerView.visualizers[id]) + .filter(v => path.equals(v.path())) + .filter(v => v.active(path.getModel())) + .map(v => { + const id = view.registerClick(() => { + views.visualizer.set(v) + }) + return `` + }) + .join('') +} + const fetchLocale = async (id: string) => { const response = await fetch(publicPath + `locales/${id}.json`) LOCALES.register(id, await response.json()) @@ -95,18 +111,30 @@ const treeControlsVersionMenu = document.getElementById('tree-controls-version-m 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')! + +Split([treeViewEl, sourceViewEl], { + sizes: [66, 34] +}) + +Split([sourceViewOutput, visualizerOutput], { + sizes: [60, 40], + direction: 'vertical' +}) const dummyModel = new DataModel(Base) -const views: {[key: string]: AbstractView} = { +const views = { 'tree': new TreeView(dummyModel, treeViewOutput, { showErrors: true, - observer: treeViewObserver + observer: treeViewObserver, + nodeInjector: treeViewNodeInjector }), 'source': new SourceView(dummyModel, sourceViewOutput, { indentation: 2 }), - 'errors': new ErrorsView(dummyModel, errorsViewEl) + 'errors': new ErrorsView(dummyModel, errorsViewEl), + 'visualizer': new VisualizerView(dummyModel, visualizerOutput as HTMLCanvasElement) } const COLLECTIONS = getCollections() @@ -184,10 +212,6 @@ Promise.all([ } } - Split([treeViewEl, sourceViewEl], { - sizes: [66, 34] - }) - homeLink.addEventListener('click', evt => { reload(publicPath) }) diff --git a/src/app/visualization/BiomeNoiseVisualizer.ts b/src/app/visualization/BiomeNoiseVisualizer.ts new file mode 100644 index 00000000..616ad3aa --- /dev/null +++ b/src/app/visualization/BiomeNoiseVisualizer.ts @@ -0,0 +1,36 @@ +import SimplexNoise from 'simplex-noise' +import { DataModel, Path } from "@mcschema/core"; +import { Visualizer } from './VisualizerView'; + +export class BiomeNoiseVisualizer implements Visualizer { + private noise: SimplexNoise + + constructor() { + this.noise = new SimplexNoise() + } + + path() { + return new Path(['generator', 'biome_source']) + } + + active(model: DataModel) { + const biomeSource = new Path(['generator', 'biome_source']) + return model.get(biomeSource) !== undefined + && model.get(biomeSource.push('type')) === 'minecraft:multi_noise' + } + + draw(model: DataModel, img: ImageData) { + const biomeSource = model.get(new Path(['generator', 'biome_source'])) + const data = img.data + 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.noise.noise2D(x/50, y/50) > 0) ? 0 : 255 + data[i] = b + data[i + 1] = b + data[i + 2] = b + data[i + 3] = 255 + } + } + } +} diff --git a/src/app/visualization/VisualizerView.ts b/src/app/visualization/VisualizerView.ts new file mode 100644 index 00000000..2f314a41 --- /dev/null +++ b/src/app/visualization/VisualizerView.ts @@ -0,0 +1,57 @@ +import { AbstractView, DataModel, Path } from "@mcschema/core"; +import { BiomeNoiseVisualizer } from "./BiomeNoiseVisualizer"; + +export interface Visualizer { + path(): Path + active(model: DataModel): boolean + draw(model: DataModel, img: ImageData): void +} + +export class VisualizerView extends AbstractView { + ctx: CanvasRenderingContext2D + visualizer?: Visualizer + active: boolean + canvas: HTMLElement + sourceView: HTMLElement + gutter: HTMLElement + lastHeight?: string + + constructor(model: DataModel, canvas: HTMLCanvasElement) { + super(model) + this.ctx = 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 + } + + invalidated() { + if (this.active && this.visualizer && this.visualizer.active(this.model)) { + const img = this.ctx.createImageData(200, 100) + this.visualizer.draw(this.model, img) + this.ctx.putImageData(img, 0, 0) + this.canvas.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.gutter.style.display = 'none' + this.lastHeight = this.sourceView.style.height + this.sourceView.style.height = '100%' + this.ctx.clearRect(0, 0, 200, 100) + } + } + + set(visualizer: Visualizer) { + this.active = true + this.visualizer = visualizer + this.invalidated() + } + + static visualizers: {[key: string]: Visualizer} = { + 'biome-noise': new BiomeNoiseVisualizer() + } +} diff --git a/src/index.html b/src/index.html index e7ab1868..ff4a3852 100644 --- a/src/index.html +++ b/src/index.html @@ -105,7 +105,10 @@ - +