From e89cdfb0ad412bf8e1b7ce5ebbb3fd1130cccb63 Mon Sep 17 00:00:00 2001 From: Misode Date: Thu, 10 Nov 2022 02:01:50 +0100 Subject: [PATCH] Show density/noise value at cursor (#282) * Fix #277 WIP show density at cursor * Correctly implement hovered value for noise and df previews --- .../previews/DensityFunctionPreview.tsx | 26 +++++++++++-------- src/app/components/previews/NoisePreview.tsx | 19 +++++++++++++- src/app/previews/Deepslate.ts | 10 +++---- src/app/previews/NoiseSettings.ts | 25 ++++++++++++------ src/app/previews/NormalNoise.ts | 16 ++++++++++-- src/locales/en.json | 2 ++ 6 files changed, 71 insertions(+), 27 deletions(-) diff --git a/src/app/components/previews/DensityFunctionPreview.tsx b/src/app/components/previews/DensityFunctionPreview.tsx index b626193c..4e67bd33 100644 --- a/src/app/components/previews/DensityFunctionPreview.tsx +++ b/src/app/components/previews/DensityFunctionPreview.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'preact/hooks' import { useLocale, useProject } from '../../contexts/index.js' import { useCanvas } from '../../hooks/index.js' -import { densityFunction } from '../../previews/index.js' +import { densityFunction, densityPoint } from '../../previews/index.js' import { randomSeed } from '../../Utils.js' import { Btn, BtnMenu } from '../index.js' import type { PreviewProps } from './index.js' @@ -10,19 +10,21 @@ export const DensityFunctionPreview = ({ data, shown, version }: PreviewProps) = const { locale } = useLocale() const { project } = useProject() const [seed, setSeed] = useState(randomSeed()) + const [minY] = useState(0) + const [height] = useState(256) const [autoScroll, setAutoScroll] = useState(false) - const [focused, setFocused] = useState(undefined) + const [focused, setFocused] = useState([]) const offset = useRef(0) const scrollInterval = useRef(undefined) const state = JSON.stringify([data]) - const size = data?.noise?.height ?? 256 + const size = 256 const { canvas, redraw } = useCanvas({ size() { return [size, size] }, async draw(img) { - const options = { offset: offset.current, width: img.width, seed, version, project } + const options = { offset: offset.current, width: img.width, seed, version, project, minY, height } await densityFunction(data, img, options) }, async onDrag(dx) { @@ -30,14 +32,16 @@ export const DensityFunctionPreview = ({ data, shown, version }: PreviewProps) = redraw() }, async onHover(x, y) { - const worldX = Math.floor(x * size - offset.current) - const worldY = size - Math.max(1, Math.ceil(y * size)) + (data?.noise?.min_y ?? 0) - setFocused(`X=${worldX} Y=${worldY}`) + const worldX = Math.floor(x * size) + const worldY = Math.floor(y * (height - minY)) + const options = { offset: offset.current, width: size, seed, version, project, minY, height } + const density = await densityPoint(data, worldX, worldY, options) + setFocused([density.toPrecision(3), `X=${Math.floor(worldX - offset.current)} Y=${(height - minY) - worldY}`]) }, onLeave() { - setFocused(undefined) + setFocused([]) }, - }, [version, state, seed, project]) + }, [version, state, seed, minY, height, project]) useEffect(() => { if (scrollInterval.current) { @@ -52,11 +56,11 @@ export const DensityFunctionPreview = ({ data, shown, version }: PreviewProps) = }, 100) as any } } - }, [version, state, seed, project, shown, autoScroll]) + }, [version, state, seed, minY, height, project, shown, autoScroll]) return <>
- {focused && } + {focused.map(s => )} setAutoScroll(!autoScroll)} /> diff --git a/src/app/components/previews/NoisePreview.tsx b/src/app/components/previews/NoisePreview.tsx index b811795d..6f85012b 100644 --- a/src/app/components/previews/NoisePreview.tsx +++ b/src/app/components/previews/NoisePreview.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'preact/hooks' import { useLocale } from '../../contexts/index.js' import { useCanvas } from '../../hooks/index.js' -import { normalNoise } from '../../previews/index.js' +import { normalNoise, normalNoisePoint } from '../../previews/index.js' import { randomSeed } from '../../Utils.js' import { Btn } from '../index.js' import type { PreviewProps } from './index.js' @@ -10,6 +10,7 @@ export const NoisePreview = ({ data, shown, version }: PreviewProps) => { const { locale } = useLocale() const [seed, setSeed] = useState(randomSeed()) const [scale, setScale] = useState(2) + const [focused, setFocused] = useState([]) const offset = useRef<[number, number]>([0, 0]) const state = JSON.stringify([data]) @@ -26,6 +27,21 @@ export const NoisePreview = ({ data, shown, version }: PreviewProps) => { offset.current[1] = offset.current[1] + dy * 256 redraw() }, + onHover(x, y) { + const x2 = Math.floor(x * 256) + const y2 = Math.floor(y * 256) + const options = { offset: offset.current, scale, seed, version } + const value = normalNoisePoint(data, x2, y2, options) + + const ox = -options.offset[0] - 100 + const oy = -options.offset[1] - 100 + const xx = (x2 + ox) * options.scale + const yy = (y2 + oy) * options.scale + setFocused([value.toPrecision(3), `X=${Math.floor(xx)} Y=${Math.floor(yy)}`]) + }, + onLeave() { + setFocused([]) + }, }, [version, state, scale, seed]) useEffect(() => { @@ -42,6 +58,7 @@ export const NoisePreview = ({ data, shown, version }: PreviewProps) => { return <>
+ {focused.map(s => )} changeScale(scale * 1.5)} /> = { @@ -82,22 +84,22 @@ export function getNoiseBlock(x: number, y: number) { export async function densityFunction(state: any, img: ImageData, options: NoiseSettingsOptions) { await DEEPSLATE.loadVersion(options.version, getProjectData(options.project)) - const fn = DEEPSLATE.loadDensityFunction(DataModel.unwrapLists(state), options.seed) + const fn = DEEPSLATE.loadDensityFunction(DataModel.unwrapLists(state), options.minY ?? 0, options.height ?? 256, options.seed) const noise = DEEPSLATE.getNoiseSettings() const arr = Array(options.width * noise.height) - let min = Infinity - let max = -Infinity + let limit = 0 for (let x = 0; x < options.width; x += 1) { - for (let y = 0; y < noise.height; y += 1) { - const i = x + (noise.height-y-1) * options.width - const density = fn.compute(DensityFunction.context(x - options.offset, y, 0)) - min = Math.min(min, density) - max = Math.max(max, density) + for (let y = 0; y < noise.height - noise.minY; y += 1) { + const i = x + y * options.width + const density = fn.compute(DensityFunction.context(x - options.offset, noise.height - y - 1, 0)) + limit = Math.max(limit, Math.abs(density)) arr[i] = density } } + const min = -limit + const max = limit const data = img.data for (let i = 0; i < options.width * noise.height; i += 1) { const color = Math.floor(clampedMap(arr[i], min, max, 0, 256)) @@ -108,6 +110,13 @@ export async function densityFunction(state: any, img: ImageData, options: Noise } } +export async function densityPoint(state: any, x: number, y: number, options: NoiseSettingsOptions) { + await DEEPSLATE.loadVersion(options.version, getProjectData(options.project)) + const fn = DEEPSLATE.loadDensityFunction(DataModel.unwrapLists(state), options.minY ?? 0, options.height ?? 256, options.seed) + + return fn.compute(DensityFunction.context(Math.floor(x - options.offset), (options.height ?? 256) - y, 0)) +} + export function getProjectData(project: Project) { return Object.fromEntries(['worldgen/noise_settings', 'worldgen/noise', 'worldgen/density_function'].map(type => { const resources = Object.fromEntries( diff --git a/src/app/previews/NormalNoise.ts b/src/app/previews/NormalNoise.ts index 40364780..32b21790 100644 --- a/src/app/previews/NormalNoise.ts +++ b/src/app/previews/NormalNoise.ts @@ -15,13 +15,13 @@ export function normalNoise(state: any, img: ImageData, options: NoiseOptions) { const noise = new NormalNoise(random, params) const ox = -options.offset[0] - 100 - const oz = -options.offset[1] - 100 + const oy = -options.offset[1] - 100 const data = img.data for (let x = 0; x < 256; x += 1) { for (let y = 0; y < 256; y += 1) { const i = x * 4 + y * 4 * 256 const xx = (x + ox) * options.scale - const yy = (y + oz) * options.scale + const yy = (y + oy) * options.scale const color = (noise.sample(xx, yy, 0) + 1) * 128 data[i] = color data[i + 1] = color @@ -30,3 +30,15 @@ export function normalNoise(state: any, img: ImageData, options: NoiseOptions) { } } } + +export function normalNoisePoint(state: any, x: number, y: number, options: NoiseOptions) { + const random = XoroshiroRandom.create(options.seed) + const params = NoiseParameters.fromJson(DataModel.unwrapLists(state)) + const noise = new NormalNoise(random, params) + + const ox = -options.offset[0] - 100 + const oy = -options.offset[1] - 100 + const xx = (x + ox) * options.scale + const yy = (y + oy) * options.scale + return noise.sample(xx, yy, 0) +} diff --git a/src/locales/en.json b/src/locales/en.json index 3995d460..814a93ea 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -139,6 +139,8 @@ "preview.weather.rain": "Rain", "preview.weather.thunder": "Thunder", "preview.width": "Width", + "preview.height": "Height", + "preview.min_y": "Min Y", "project.new": "New project", "project.cancel": "Cancel", "project.create": "Create a new project",