First 'release'L
This commit is contained in:
@@ -38,20 +38,12 @@
|
||||
color: white;
|
||||
background: #333333;
|
||||
}
|
||||
@media(max-width: 1600px) {
|
||||
.solutionBodyContainer {
|
||||
width: 100%;
|
||||
}
|
||||
.sidebarContainer {
|
||||
width: 20%;
|
||||
}
|
||||
}
|
||||
@media(max-width: 1200px) {
|
||||
.solutionBodyContainer {
|
||||
width: 100%;
|
||||
width: calc(100% - 18em);
|
||||
}
|
||||
.sidebarContainer {
|
||||
width: 15em;
|
||||
.sidebarContainer {
|
||||
width: 18em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
48
src/ui/IncDecNum.svelte
Normal file
48
src/ui/IncDecNum.svelte
Normal file
@@ -0,0 +1,48 @@
|
||||
<script lang="ts">
|
||||
export let up: () => void;
|
||||
export let down: () => void;
|
||||
export let val: number;
|
||||
export let upDisabled: boolean;
|
||||
export let title: string;
|
||||
export let downDisabled: boolean;
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
{#if title}
|
||||
<p class="title">{title}</p>
|
||||
{/if}
|
||||
<div class="controls">
|
||||
<button on:click={down} disabled={downDisabled}>-</button>
|
||||
<p class="val">{val}</p>
|
||||
<button on:click={up} disabled={upDisabled}>+</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
margin: 0 0.5em 0 0.5em;
|
||||
display: inline-block;
|
||||
}
|
||||
.title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.val {
|
||||
font-weight: bold;
|
||||
margin-left: 0.2em;
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
.controls {
|
||||
margin-top: 0;
|
||||
}
|
||||
.controls > * {
|
||||
display: inline-block;
|
||||
}
|
||||
button:hover:not(:disabled) {
|
||||
cursor: pointer;
|
||||
background-color: #c1c1c1;
|
||||
}
|
||||
button:disabled {
|
||||
color: #a7a7a7;
|
||||
background-color: #616161;
|
||||
}
|
||||
</style>
|
||||
@@ -3,26 +3,84 @@
|
||||
import CubeInput from "./CubeInput.svelte";
|
||||
import SolutionViewer from "./SolutionViewer.svelte";
|
||||
$: numCubes = $polycubes.length;
|
||||
let showInput = true;
|
||||
let smallViewport = true;
|
||||
|
||||
function onMediaChange() {
|
||||
smallViewport = queryListWidth.matches || queryListHeight.matches;
|
||||
}
|
||||
|
||||
const queryListWidth = window.matchMedia("(max-width: 1200px)");
|
||||
const queryListHeight = window.matchMedia("(max-height: 920px)");
|
||||
queryListWidth.addEventListener("change", onMediaChange);
|
||||
queryListHeight.addEventListener("change", onMediaChange);
|
||||
onMediaChange();
|
||||
</script>
|
||||
|
||||
<div class="viewport">
|
||||
<div class="input-container">
|
||||
{#each {length: numCubes} as _, cubeNo}
|
||||
<div class="cube-input">
|
||||
<div class="padder">
|
||||
<CubeInput
|
||||
cubeNo={cubeNo}
|
||||
/>
|
||||
</div>
|
||||
{#if smallViewport}
|
||||
<div class="tabs">
|
||||
<div class="tab" class:selected={showInput} on:click="{() => showInput = true}">Input</div>
|
||||
<div class="tab" class:selected={!showInput} on:click="{() => showInput = false}">3D</div>
|
||||
</div>
|
||||
{#if showInput}
|
||||
<div class="input-container">
|
||||
{#each {length: numCubes} as _, cubeNo}
|
||||
<div class="cube-input">
|
||||
<div class="padder">
|
||||
<CubeInput
|
||||
cubeNo={cubeNo}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="threedee">
|
||||
<SolutionViewer/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="threedee">
|
||||
<SolutionViewer/>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="input-container">
|
||||
{#each {length: numCubes} as _, cubeNo}
|
||||
<div class="cube-input">
|
||||
<div class="padder">
|
||||
<CubeInput
|
||||
cubeNo={cubeNo}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="threedee">
|
||||
<SolutionViewer/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.tabs {
|
||||
height: 3em;
|
||||
flex: 0 1 auto;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tab {
|
||||
flex: 1;
|
||||
border: solid black;
|
||||
text-align: center;
|
||||
border-width: 0 1px 1px 1px;
|
||||
background-color: #555555;
|
||||
line-height: 3em;
|
||||
transition: background-color 100ms;
|
||||
}
|
||||
.tab:hover {
|
||||
background-color: #999999;
|
||||
}
|
||||
.tab.selected {
|
||||
background-color: grey;
|
||||
border-width: 1px 0 0 0;
|
||||
}
|
||||
.threedee {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
@@ -38,16 +96,17 @@
|
||||
margin: auto;
|
||||
}
|
||||
.input-container {
|
||||
flex: 0 1 fit-content;
|
||||
flex: 1 1 auto;
|
||||
overflow-x: scroll;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
}
|
||||
.viewport {
|
||||
overflow: scroll;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
justify-content: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
@@ -4,10 +4,17 @@
|
||||
isMinPolycubes,
|
||||
polycubes,
|
||||
solutions,
|
||||
colorFromIndex,
|
||||
activeSolution, showingSolution, totalVolume, somaDimX, somaDimY, somaDimZ, debug
|
||||
solving,
|
||||
totalVolume,
|
||||
somaDimX,
|
||||
somaDimY,
|
||||
somaDimZ,
|
||||
MAX_DIMS,
|
||||
MIN_DIMS,
|
||||
solve
|
||||
} from "../store";
|
||||
import SolutionList from "./SolutionList.svelte";
|
||||
import IncDecNum from "./IncDecNum.svelte";
|
||||
|
||||
$: numCubes = $polycubes.length;
|
||||
$: cubes = $polycubes;
|
||||
@@ -40,21 +47,30 @@
|
||||
<div class="option">
|
||||
<p>Dimensions:</p>
|
||||
<div class="choice">
|
||||
X
|
||||
<input
|
||||
type="number"
|
||||
value="3"
|
||||
on:input={(e) => somaDimX.set(e.target.valueAsNumber)}/>
|
||||
Y
|
||||
<input
|
||||
type="number"
|
||||
value="3"
|
||||
on:input={(e) => somaDimY.set(e.target.valueAsNumber)}/>
|
||||
Z
|
||||
<input
|
||||
type="number"
|
||||
value="3"
|
||||
on:input={(e) => somaDimZ.set(e.target.valueAsNumber)}/>
|
||||
<IncDecNum
|
||||
title="X"
|
||||
val="{$somaDimX}"
|
||||
upDisabled="{$somaDimX >= MAX_DIMS}"
|
||||
up="{() => somaDimX.set($somaDimX + 1)}"
|
||||
downDisabled="{$somaDimX <= MIN_DIMS}"
|
||||
down="{() => somaDimX.set($somaDimX - 1)}"
|
||||
/>
|
||||
<IncDecNum
|
||||
title="Y"
|
||||
val="{$somaDimY}"
|
||||
upDisabled="{$somaDimY >= MAX_DIMS}"
|
||||
up="{() => somaDimY.set($somaDimY + 1)}"
|
||||
downDisabled="{$somaDimY <= MIN_DIMS}"
|
||||
down="{() => somaDimY.set($somaDimY - 1)}"
|
||||
/>
|
||||
<IncDecNum
|
||||
title="Z"
|
||||
val="{$somaDimZ}"
|
||||
upDisabled="{$somaDimZ >= MAX_DIMS}"
|
||||
up="{() => somaDimZ.set($somaDimZ + 1)}"
|
||||
downDisabled="{$somaDimZ <= MIN_DIMS}"
|
||||
down="{() => somaDimZ.set($somaDimZ - 1)}"
|
||||
/>
|
||||
{#if $totalVolume > 32}
|
||||
<p class="warn">The total number of units exceeds 32. Attempting to solve puzzles with more than 32 units results in significantly slower computation time.</p>
|
||||
{/if}
|
||||
@@ -64,9 +80,13 @@
|
||||
<div class="option">
|
||||
<p>Cubes:</p>
|
||||
<div class="choice">
|
||||
<button on:click={polycubes.removeCube} disabled={$isMinPolycubes}>-</button>
|
||||
<p>{numCubes}</p>
|
||||
<button on:click={polycubes.addCube} disabled={$isMaxPolycubes}>+</button>
|
||||
<IncDecNum
|
||||
down="{polycubes.removeCube}"
|
||||
downDisabled="{$isMinPolycubes}"
|
||||
up="{polycubes.addCube}"
|
||||
upDisabled="{$isMaxPolycubes}"
|
||||
val="{numCubes}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -75,8 +95,8 @@
|
||||
class="solve"
|
||||
on:click={solve}
|
||||
title="{genTooltip(enoughSubcubes, noEmpties, size)}"
|
||||
disabled="{solving || !readyToSolve}">
|
||||
{solving ? "Solving..." : "Solve!"}
|
||||
disabled="{$solving || !readyToSolve}">
|
||||
{$solving ? "Solving..." : "Solve!"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -95,7 +115,7 @@
|
||||
.choice {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-top: 1em;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
input {
|
||||
display: inline-block;
|
||||
@@ -108,14 +128,6 @@
|
||||
color: white;
|
||||
background-color: #ff3e00;
|
||||
}
|
||||
button:hover:not(:disabled) {
|
||||
cursor: pointer;
|
||||
background-color: #c1c1c1;
|
||||
}
|
||||
button:disabled {
|
||||
color: #a7a7a7;
|
||||
background-color: #616161;
|
||||
}
|
||||
button.solve {
|
||||
width: auto;
|
||||
color: white;
|
||||
@@ -124,6 +136,7 @@
|
||||
border-radius: 0.5em;
|
||||
border-style: none;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
button.solve:disabled {
|
||||
width: auto;
|
||||
@@ -151,8 +164,8 @@
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.widgets > * {
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
h1 {
|
||||
margin: 0;
|
||||
|
||||
@@ -1,38 +1,35 @@
|
||||
<script lang="ts">
|
||||
import PolycubeScene from "./threedee/PolycubeScene";
|
||||
import {onMount} from "svelte";
|
||||
import {polycubes, selectedCube, solutions, activeSolution, showingSolution, somaDimX, somaDimY, somaDimZ} from "../store";
|
||||
import {polycubes, selectedCube, solutions, activeSolution, showingSolution, cubeScene} from "../store";
|
||||
import Solution2D from "./Solution2D.svelte";
|
||||
import VoxelSpaceBoolean from "../VoxelSpaceBoolean";
|
||||
|
||||
$: cube = $polycubes[$selectedCube];
|
||||
$: soln = $solutions[$activeSolution];
|
||||
let el: HTMLCanvasElement;
|
||||
let el: HTMLDivElement;
|
||||
let scene: PolycubeScene;
|
||||
let loaded: boolean = false;
|
||||
|
||||
const canvasStyle: Partial<CSSStyleDeclaration> = {
|
||||
borderRadius: "1em",
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
scene = new PolycubeScene(el, () => loaded = true, console.log);
|
||||
cubeScene.onLoaded(() => {
|
||||
cubeScene.mount(el);
|
||||
Object.assign((el.children.item(0) as HTMLElement).style, canvasStyle);
|
||||
loaded = true;
|
||||
});
|
||||
});
|
||||
|
||||
window.getPermutations = () => {
|
||||
const newCube: VoxelSpaceBoolean = cube.clone() as VoxelSpaceBoolean;
|
||||
(newCube as VoxelSpaceBoolean).cullEmptySpace();
|
||||
return (newCube as VoxelSpaceBoolean).getAllPermutationsInPrism($somaDimX, $somaDimY, $somaDimZ);
|
||||
}
|
||||
|
||||
window.showRot = (rot: VoxelSpaceBoolean) => {
|
||||
scene?.showPolycube(rot);
|
||||
}
|
||||
|
||||
$: {
|
||||
if (loaded) {
|
||||
if ($showingSolution) {
|
||||
const colorMap = {};
|
||||
$polycubes.forEach((polycube, i) => colorMap[i] = polycube.color);
|
||||
scene?.showSolution(soln);
|
||||
cubeScene.showSolution(soln);
|
||||
} else {
|
||||
scene?.showPolycube(cube);
|
||||
cubeScene.showPolycube(cube);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,11 +41,7 @@
|
||||
<Solution2D/>
|
||||
</div>
|
||||
{/if}
|
||||
<canvas
|
||||
bind:this={el}
|
||||
width="640"
|
||||
height="480"
|
||||
></canvas>
|
||||
<div class="stage" bind:this={el}></div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -58,10 +51,7 @@
|
||||
align-items: center;
|
||||
}
|
||||
.soln2d-container {
|
||||
flex: 0 1 auto;
|
||||
display: inline-block;
|
||||
}
|
||||
canvas {
|
||||
display: inline-block;
|
||||
border-radius: 1em;
|
||||
}
|
||||
</style>
|
||||
@@ -6,34 +6,44 @@ import type VoxelSpaceBoolean from "../../VoxelSpaceBoolean";
|
||||
import type VoxelSpaceBigInt from "../../VoxelSpaceBigInt";
|
||||
import GeometryManager from "./GeometryManager";
|
||||
|
||||
const DEFAULT_WIDTH = 640;
|
||||
const DEFAULT_HEIGHT = 480;
|
||||
|
||||
export default class PolycubeScene {
|
||||
private renderer: THREE.WebGLRenderer;
|
||||
private camera: THREE.Camera;
|
||||
private camera: THREE.PerspectiveCamera;
|
||||
private mainScene: THREE.Scene;
|
||||
private polycubeMeshes: PolycubeMesh[] = [];
|
||||
private controls: RotationControl;
|
||||
private light: THREE.Light;
|
||||
private cubeScene: THREE.Scene;
|
||||
private geomManager: GeometryManager;
|
||||
private canvas: HTMLCanvasElement;
|
||||
private loadedCb: () => void = () => {};
|
||||
private loaded: boolean = false;
|
||||
|
||||
constructor(el: HTMLCanvasElement, onReady: () => any, onError: (err: Error) => any) {
|
||||
this.init(el).then(onReady).catch(onError);
|
||||
constructor() {
|
||||
this.init().then(() => this.loadedCb()).catch(e => console.log(e));
|
||||
}
|
||||
|
||||
private async init(el: HTMLCanvasElement) {
|
||||
this.renderer = new THREE.WebGLRenderer({canvas: el, antialias: true});
|
||||
this.setupCamera(el.clientWidth / el.clientHeight);
|
||||
private async init() {
|
||||
this.canvas = document.createElement("canvas");
|
||||
this.canvas.width = 0;
|
||||
this.canvas.height = 0;
|
||||
this.renderer = new THREE.WebGLRenderer({canvas: this.canvas, antialias: true});
|
||||
this.setupCamera(this.canvas.clientWidth / this.canvas.clientHeight);
|
||||
this.setupLight();
|
||||
this.mainScene = new THREE.Scene();
|
||||
this.cubeScene = new THREE.Scene();
|
||||
this.mainScene.add(this.cubeScene, this.camera, this.light);
|
||||
this.cubeScene.rotateX(Math.PI/4);
|
||||
this.cubeScene.rotateY(Math.PI/4);
|
||||
this.controls = new RotationControl(this.cubeScene, this.polycubeMeshes, this.camera, el);
|
||||
this.controls = new RotationControl(this.cubeScene, this.polycubeMeshes, this.camera, this.canvas);
|
||||
this.geomManager = await new GeometryManager('../resources/', () => {
|
||||
requestAnimationFrame((timestamp) => this.render(timestamp));
|
||||
});
|
||||
PolycubeMesh.setManager(this.geomManager);
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
private setupCamera(aspect: number) {
|
||||
@@ -45,6 +55,26 @@ export default class PolycubeScene {
|
||||
this.camera.lookAt(0, 0, 0);
|
||||
}
|
||||
|
||||
mount(el: HTMLDivElement) {
|
||||
this.canvas.width = DEFAULT_WIDTH;
|
||||
this.canvas.height = DEFAULT_HEIGHT;
|
||||
this.camera.aspect = this.canvas.width / this.canvas.height;
|
||||
this.camera.updateProjectionMatrix();
|
||||
this.renderer.setSize(this.canvas.width, this.canvas.height);
|
||||
el.append(this.canvas);
|
||||
}
|
||||
|
||||
onLoaded(cb: () => void) {
|
||||
if (this.loaded) {
|
||||
cb();
|
||||
}
|
||||
this.loadedCb = cb;
|
||||
}
|
||||
|
||||
isLoaded(): boolean {
|
||||
return this.loaded;
|
||||
}
|
||||
|
||||
showPolycube(voxelSpace: VoxelSpaceBoolean) {
|
||||
this.controls.disableFly();
|
||||
this.clearScene();
|
||||
|
||||
Reference in New Issue
Block a user