fixes
This commit is contained in:
+7
-17
@@ -1,12 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive } from 'vue'
|
||||
import BlockCanvas from './components/BlockCanvas.vue'
|
||||
import { generateShape, summarize, type ShapeMode } from './composables/useShapeGenerator'
|
||||
import { generateShape, summarize } from './composables/useShapeGenerator'
|
||||
|
||||
interface FormState {
|
||||
width: number
|
||||
height: number
|
||||
mode: ShapeMode
|
||||
useSlabs: boolean
|
||||
useStairs: boolean
|
||||
cellSize: number
|
||||
@@ -15,7 +14,6 @@ interface FormState {
|
||||
const form = reactive<FormState>({
|
||||
width: 12,
|
||||
height: 8,
|
||||
mode: 'arch',
|
||||
useSlabs: true,
|
||||
useStairs: true,
|
||||
cellSize: 20,
|
||||
@@ -35,7 +33,6 @@ const shape = computed(() => {
|
||||
return generateShape({
|
||||
width,
|
||||
height,
|
||||
mode: form.mode,
|
||||
useSlabs: form.useSlabs,
|
||||
useStairs: form.useStairs,
|
||||
})
|
||||
@@ -47,12 +44,13 @@ const stats = computed(() => summarize(shape.value))
|
||||
<template>
|
||||
<div class="page">
|
||||
<header>
|
||||
<h1>Minecraft Arch / Ellipse Generator</h1>
|
||||
<h1>Minecraft Arch Generator</h1>
|
||||
<p class="subtitle">
|
||||
Enter a width and height (in blocks) for the wall panel. An arch or
|
||||
oval opening is cut out of it, smoothed with slabs (half-block steps)
|
||||
and stairs (beveled corners) instead of a fully jagged hole outline.
|
||||
The result is the wall blocks you need to place, not the opening.
|
||||
Enter a width and height (in blocks) for the wall panel. An arch
|
||||
opening is cut out of it from the ground up, smoothed with slabs
|
||||
(half-block steps) and stairs (beveled corners) instead of a fully
|
||||
jagged hole outline. The result is the wall blocks you need to
|
||||
place, not the opening.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
@@ -68,14 +66,6 @@ const stats = computed(() => summarize(shape.value))
|
||||
<input type="number" v-model.number="form.height" :min="MIN_SIZE" :max="MAX_SIZE" />
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Opening shape
|
||||
<select v-model="form.mode">
|
||||
<option value="arch">Arch doorway (cut from the ground up)</option>
|
||||
<option value="oval">Oval window (cut from the middle)</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="form.useSlabs" />
|
||||
Use slabs (half-block smoothing)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Builds a Minecraft elevation-view (front-facing) block list for a wall
|
||||
// panel with an arch- or oval-shaped opening cut out of it.
|
||||
// panel with an arch-shaped opening cut out of it, grounded at the bottom.
|
||||
//
|
||||
// Each block is sub-sampled at 4 quarter-points (splitting the block into a
|
||||
// 2x2 grid) against the true ellipse boundary, so the resulting shape is
|
||||
@@ -41,12 +41,9 @@ export interface Shape {
|
||||
columns: Column[]
|
||||
}
|
||||
|
||||
export type ShapeMode = 'arch' | 'oval'
|
||||
|
||||
export interface GenerateOptions {
|
||||
width: number
|
||||
height: number
|
||||
mode: ShapeMode
|
||||
useSlabs: boolean
|
||||
useStairs: boolean
|
||||
}
|
||||
@@ -56,9 +53,9 @@ function insideOpening(x: number, y: number, a: number, b: number): boolean {
|
||||
return (x / a) ** 2 + (y / b) ** 2 <= 1
|
||||
}
|
||||
|
||||
/** Continuous y-coordinate of a row's center: arch shapes sit on the ground (y=0 at the bottom edge), oval shapes are centered vertically. */
|
||||
function rowCenterY(r: number, mode: ShapeMode, height: number): number {
|
||||
return mode === 'oval' ? r - (height - 1) / 2 : r + 0.5
|
||||
/** Continuous y-coordinate of a row's center; the arch sits on the ground, so y=0 is the bottom edge of the grid. */
|
||||
function rowCenterY(r: number): number {
|
||||
return r + 0.5
|
||||
}
|
||||
|
||||
function classifyCell(tl: boolean, tr: boolean, bl: boolean, br: boolean, useSlabs: boolean, useStairs: boolean): Cell | null {
|
||||
@@ -85,10 +82,10 @@ function classifyCell(tl: boolean, tr: boolean, bl: boolean, br: boolean, useSla
|
||||
export function generateShape(opts: GenerateOptions): Shape {
|
||||
const width = Math.max(1, Math.round(opts.width))
|
||||
const height = Math.max(1, Math.round(opts.height))
|
||||
const { mode, useSlabs, useStairs } = opts
|
||||
const { useSlabs, useStairs } = opts
|
||||
|
||||
const a = width / 2
|
||||
const b = mode === 'oval' ? height / 2 : height
|
||||
const b = height
|
||||
const centerOffsetX = (width - 1) / 2
|
||||
const useSubSampling = useSlabs || useStairs
|
||||
|
||||
@@ -98,7 +95,7 @@ export function generateShape(opts: GenerateOptions): Shape {
|
||||
const col: Column = []
|
||||
|
||||
for (let r = 0; r < height; r++) {
|
||||
const cy = rowCenterY(r, mode, height)
|
||||
const cy = rowCenterY(r)
|
||||
|
||||
if (!useSubSampling) {
|
||||
col.push(insideOpening(cx, cy, a, b) ? null : { type: 'full' })
|
||||
|
||||
Reference in New Issue
Block a user