It all works....
This commit is contained in:
@@ -1,24 +1,24 @@
|
||||
<script lang="ts">
|
||||
import {somaDimension, polycubes, selectedCube, showingSolution} from "../store";
|
||||
import {somaDimX, somaDimY, somaDimZ, polycubes, selectedCube, showingSolution} from "../store";
|
||||
import VoxelSpaceBoolean from "../VoxelSpaceBoolean";
|
||||
export let cubeNo: number;
|
||||
|
||||
$: dimension = $somaDimension;
|
||||
$: cube = $polycubes[cubeNo];
|
||||
$: cubeColor = cube.color;
|
||||
$: cube = $polycubes[cubeNo] as VoxelSpaceBoolean;
|
||||
$: cubeColor = cube.getColor();
|
||||
$: currentlyVisualised = $selectedCube === cubeNo && !$showingSolution;
|
||||
let cellStartDragInitialVal: boolean = false;
|
||||
let cellStartDrag: number = 0;
|
||||
let cellDragStartPos: {x: number, y: number} = {x: 0, y: 0};
|
||||
let cellEndDrag: number = 0;
|
||||
let cellDragEndPos: {x: number, y: number} = {x: 0, y: 0};
|
||||
let picker: HTMLInputElement;
|
||||
|
||||
function cellNo(x: number, y: number, z: number) {
|
||||
return dimension ** 2 * x + dimension * y + z;
|
||||
return $somaDimY * $somaDimZ * x + $somaDimZ * y + z;
|
||||
}
|
||||
|
||||
function at(rep: bigint, x: number, y: number, z: number) {
|
||||
const mask = BigInt(1) << BigInt(cellNo(x, y, z));
|
||||
return (rep & mask) !== BigInt(0);
|
||||
function at(cube: VoxelSpaceBoolean, x: number, y: number, z: number) {
|
||||
return cube.at(x, y, z);
|
||||
}
|
||||
|
||||
function onMouseOverCell(event: MouseEvent, x: number, y: number, z: number) {
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
function onMouseDownCell(event: MouseEvent, x: number, y: number, z: number) {
|
||||
cellStartDrag = cellNo(x, y, z);
|
||||
cellStartDragInitialVal = at(cube.rep, x, y, z);
|
||||
cellStartDragInitialVal = at(cube, x, y, z);
|
||||
cellDragStartPos.x = event.clientX;
|
||||
cellDragStartPos.y = event.clientY;
|
||||
}
|
||||
@@ -56,29 +56,43 @@
|
||||
|
||||
function onClickCube() {
|
||||
showingSolution.set(false);
|
||||
selectedCube.set(cubeNo)
|
||||
selectedCube.set(cubeNo);
|
||||
}
|
||||
|
||||
function onColorChange(event: InputEvent) {
|
||||
polycubes.setColor(cubeNo, event.target.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="cube"
|
||||
class:active={currentlyVisualised}
|
||||
style="--color: {cubeColor}; --dimension: {dimension};"
|
||||
style="--color: {cubeColor};"
|
||||
on:contextmenu|preventDefault
|
||||
on:mousedown={onClickCube}
|
||||
>
|
||||
<h1>Cube: {cubeNo + 1}</h1>
|
||||
{#each {length: dimension} as _, x}
|
||||
<div class="header">
|
||||
<h1>Cube: {cubeNo + 1}</h1>
|
||||
<div class="colorPickerBtn" on:click={picker.click()}>
|
||||
<input
|
||||
bind:this={picker}
|
||||
class="colorPicker"
|
||||
type="color"
|
||||
value="{cubeColor}"
|
||||
on:change={(event) => onColorChange(event)}/>
|
||||
</div>
|
||||
</div>
|
||||
{#each {length: $somaDimX} as _, x}
|
||||
<div class="layer">
|
||||
{#each {length: dimension} as _, y}
|
||||
{#each {length: $somaDimY} as _, y}
|
||||
<div class="row">
|
||||
{#each {length: dimension} as _, z}
|
||||
{#each {length: $somaDimZ} as _, z}
|
||||
<div
|
||||
class="cell"
|
||||
class:filled={at(cube.rep, z, dimension-1-x, y)}
|
||||
on:mousemove={(event) => onMouseOverCell(event, z, dimension-1-x, y)}
|
||||
on:mousedown={(event) => onMouseDownCell(event, z, dimension-1-x, y)}
|
||||
on:mouseup={(event) => onMouseUpCell(event, z, dimension-1-x, y)}
|
||||
class:filled={at(cube, x, y, z)}
|
||||
on:mousemove={(event) => onMouseOverCell(event, x, y, z)}
|
||||
on:mousedown={(event) => onMouseDownCell(event, x, y, z)}
|
||||
on:mouseup={(event) => onMouseUpCell(event, x, y, z)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -91,6 +105,27 @@
|
||||
* {
|
||||
--cell-size: 30px;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.header > * {
|
||||
display: inline-block;
|
||||
}
|
||||
.colorPicker {
|
||||
visibility: hidden;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
.colorPickerBtn {
|
||||
align-self: center;
|
||||
background-image: url("../resources/ColorWheel.png");
|
||||
background-size: cover;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
}
|
||||
.cube.active {
|
||||
border: 3px solid #ff3e00;
|
||||
}
|
||||
@@ -125,6 +160,8 @@
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
}
|
||||
.layer {
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
<script lang="ts">
|
||||
import {isMaxPolycubes, isMinPolycubes, somaDimension, polycubes, solutions} from "../store";
|
||||
import SomaSolution from "../SomaSolution";
|
||||
import {
|
||||
isMaxPolycubes,
|
||||
isMinPolycubes,
|
||||
polycubes,
|
||||
solutions,
|
||||
colorFromIndex,
|
||||
activeSolution, showingSolution, totalVolume, somaDimX, somaDimY, somaDimZ, debug
|
||||
} from "../store";
|
||||
import SolutionList from "./SolutionList.svelte";
|
||||
import VoxelSpace from "../VoxelSpace";
|
||||
|
||||
$: numCubes = $polycubes.length;
|
||||
$: cubes = $polycubes;
|
||||
@@ -11,57 +16,16 @@
|
||||
let readyToSolve: boolean;
|
||||
let size: number;
|
||||
$: {
|
||||
const dim = $somaDimension as number;
|
||||
const polycubes: VoxelSpace[] = cubes.map(cubeInput => new VoxelSpace(0, [dim, dim, dim], cubeInput.rep));
|
||||
size = polycubes.reduce((prev, cube) => cube.size() + prev, 0);
|
||||
noEmpties = polycubes.reduce((prev, cube) => (cube.size() !== 0) && prev, true);
|
||||
enoughSubcubes = size === dim**3;
|
||||
readyToSolve = size === dim**3 && noEmpties;
|
||||
}
|
||||
let solving = false;
|
||||
const worker = new Worker('../solver/main.js', {type: "module"});
|
||||
|
||||
async function respondWasm(event: MessageEvent) {
|
||||
const dim = $somaDimension as number;
|
||||
solutions.set(event.data.map((wasmSolution) => {
|
||||
const solnObj = new SomaSolution(dim);
|
||||
const spaceReps = wasmSolution.split(",");
|
||||
for (let i = 0; i < spaceReps.length; i++) {
|
||||
solnObj.addSpace(new VoxelSpace(i, [dim, dim, dim], BigInt(parseInt(spaceReps[i]))));
|
||||
}
|
||||
return solnObj;
|
||||
}));
|
||||
solving = false;
|
||||
}
|
||||
|
||||
function respondJs(event: MessageEvent) {
|
||||
solutions.set(event.data.map(solnData => {
|
||||
const solution = new SomaSolution(solnData.dim);
|
||||
solnData.solutionSpaces.forEach((voxelSpace, i) => solution.addSpace(new VoxelSpace(i, [solnData.dim, solnData.dim, solnData.dim], voxelSpace.space)));
|
||||
return solution;
|
||||
}));
|
||||
solving = false;
|
||||
}
|
||||
|
||||
function solveJs() {
|
||||
worker.onmessage = (e) => respondJs(e);
|
||||
const polycubes = cubes.map(cubeInput => cubeInput.rep);
|
||||
solving = true;
|
||||
worker.postMessage({type: 'js', polycubes, dims: $somaDimension});
|
||||
}
|
||||
|
||||
function solveWasm() {
|
||||
worker.onmessage = (e) => respondWasm(e);
|
||||
const polycubes = cubes.map(cubeInput => cubeInput.rep);
|
||||
console.log(polycubes);
|
||||
solving = true;
|
||||
worker.postMessage({type: 'wasm', polycubes, dims: $somaDimension});
|
||||
size = cubes.reduce((prev, cube) => cube.size() + prev, 0);
|
||||
noEmpties = cubes.reduce((prev, cube) => (cube.size() !== 0) && prev, true);
|
||||
enoughSubcubes = size === $totalVolume;
|
||||
readyToSolve = enoughSubcubes && noEmpties;
|
||||
}
|
||||
|
||||
function genTooltip() {
|
||||
let messages = [];
|
||||
if (!enoughSubcubes) {
|
||||
messages.push(`You have not input enough subcubes to form a cube with a side length of ${$somaDimension}. Needed: ${$somaDimension**3}, current: ${size}.`);
|
||||
messages.push(`You have not input enough subcubes to form a rectangular prism with side lengths ${$somaDimX}, ${$somaDimY}, and ${$somaDimZ}. Needed: ${$totalVolume}, current: ${size}.`);
|
||||
}
|
||||
if (!noEmpties) {
|
||||
messages.push("You have left some of the polycube inputs empty. Remove them to solve.");
|
||||
@@ -74,34 +38,34 @@
|
||||
<h1>Somaesque</h1>
|
||||
<div class="widgets">
|
||||
<div class="option">
|
||||
<p>Dimension:</p>
|
||||
<p>Dimensions:</p>
|
||||
<div class="choice">
|
||||
<button
|
||||
class:selected={$somaDimension === 2}
|
||||
on:click={() => somaDimension.set(2)}
|
||||
disabled={$somaDimension === 2}>
|
||||
2
|
||||
</button>
|
||||
<button
|
||||
class:selected={$somaDimension === 3}
|
||||
on:click={() => somaDimension.set(3)}
|
||||
disabled={$somaDimension === 3}>
|
||||
3
|
||||
</button>
|
||||
<button
|
||||
class:selected={$somaDimension === 4}
|
||||
on:click={() => somaDimension.set(4)}
|
||||
disabled={$somaDimension === 4}>
|
||||
4
|
||||
</button>
|
||||
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)}/>
|
||||
{#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}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option">
|
||||
<p>Cubes:</p>
|
||||
<div class="choice">
|
||||
<p>{numCubes}</p>
|
||||
<button on:click={polycubes.removeCube} disabled={$isMinPolycubes}>-</button>
|
||||
<p>{numCubes}</p>
|
||||
<button on:click={polycubes.addCube} disabled={$isMaxPolycubes}>+</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -109,7 +73,7 @@
|
||||
<div class="option">
|
||||
<button
|
||||
class="solve"
|
||||
on:click={solveWasm}
|
||||
on:click={solve}
|
||||
title="{genTooltip(enoughSubcubes, noEmpties, size)}"
|
||||
disabled="{solving || !readyToSolve}">
|
||||
{solving ? "Solving..." : "Solve!"}
|
||||
@@ -121,6 +85,9 @@
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.warn {
|
||||
color: red;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
@@ -130,10 +97,10 @@
|
||||
text-align: center;
|
||||
margin-top: 1em;
|
||||
}
|
||||
button {
|
||||
input {
|
||||
display: inline-block;
|
||||
background-color: #999999;
|
||||
width: 2em;
|
||||
width: 3em;
|
||||
height: 2em;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script lang="ts">
|
||||
import {polycubes, activeSolution, showingSolution, solutions} from "../store";
|
||||
import {activeSolution, showingSolution, solutions} from "../store";
|
||||
import SomaSolution from "../SomaSolution";
|
||||
|
||||
$: solutionDisplayed = $solutions[$activeSolution];
|
||||
$: dimension = (solutionDisplayed && solutionDisplayed.getDims?.()[0]) ?? 3;
|
||||
$: solnToShow = $solutions[$activeSolution];
|
||||
$: dims = (solnToShow?.getDims?.()) ?? [3, 3, 3];
|
||||
|
||||
function colorAt(soln: SomaSolution, x: number, y: number, z: number) {
|
||||
return $polycubes[soln.at(z, dimension-1-x, y)].color;
|
||||
return solnToShow.getPieces()[soln.at(x, y, z)]?.getColor?.() ?? "red";
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -14,19 +14,18 @@
|
||||
<div
|
||||
class="cube"
|
||||
class:active={$showingSolution}
|
||||
style="--dimension: {dimension};"
|
||||
on:click={() => showingSolution.set(true)}
|
||||
>
|
||||
<h1>Solution #{$activeSolution + 1}</h1>
|
||||
<div class="center">
|
||||
{#each {length: dimension} as _, x}
|
||||
{#each {length: dims[0]} as _, x}
|
||||
<div class="layer">
|
||||
{#each {length: dimension} as _, y}
|
||||
{#each {length: dims[1]} as _, y}
|
||||
<div class="row">
|
||||
{#each {length: dimension} as _, z}
|
||||
{#each {length: dims[2]} as _, z}
|
||||
<div
|
||||
class="cell"
|
||||
style="background-color:{colorAt(solutionDisplayed, x, y, z)}; border-color: {colorAt(solutionDisplayed, x, y, z)}"
|
||||
style="background-color:{colorAt(solnToShow, x, y, z)}; border-color: {colorAt(solnToShow, x, y, z)}"
|
||||
class:filled={true}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
@@ -1,36 +1,49 @@
|
||||
<script lang="ts">
|
||||
import PolycubeScene from "./threedee/PolycubeScene.ts";
|
||||
import PolycubeScene from "./threedee/PolycubeScene";
|
||||
import {onMount} from "svelte";
|
||||
import {polycubes, somaDimension, selectedCube, solutions, activeSolution, showingSolution} from "../store";
|
||||
import {polycubes, selectedCube, solutions, activeSolution, showingSolution, somaDimX, somaDimY, somaDimZ} from "../store";
|
||||
import Solution2D from "./Solution2D.svelte";
|
||||
import VoxelSpaceBoolean from "../VoxelSpaceBoolean";
|
||||
|
||||
$: cube = $polycubes[$selectedCube];
|
||||
$: soln = $solutions[$activeSolution];
|
||||
let el: HTMLCanvasElement;
|
||||
let threeTest: PolycubeScene;
|
||||
let scene: PolycubeScene;
|
||||
let loaded: boolean = false;
|
||||
|
||||
onMount(() => {
|
||||
threeTest = new PolycubeScene(el, () => loaded = true, console.log);
|
||||
scene = new PolycubeScene(el, () => loaded = true, console.log);
|
||||
});
|
||||
|
||||
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);
|
||||
threeTest?.showSolution(soln, colorMap);
|
||||
scene?.showSolution(soln);
|
||||
} else {
|
||||
threeTest?.showPolycube(cube.rep, $somaDimension, cube.color);
|
||||
scene?.showPolycube(cube);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="top">
|
||||
<div class="soln2d-container">
|
||||
<Solution2D/>
|
||||
</div>
|
||||
{#if $activeSolution !== null}
|
||||
<div class="soln2d-container">
|
||||
<Solution2D/>
|
||||
</div>
|
||||
{/if}
|
||||
<canvas
|
||||
bind:this={el}
|
||||
width="640"
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import * as THREE from "three";
|
||||
import type VoxelSpace from "../../VoxelSpace";
|
||||
import type VoxelSpaceBoolean from "../../VoxelSpaceBoolean";
|
||||
import type GeometryManager from "./GeometryManager";
|
||||
import type VoxelSpaceBigInt from "../../VoxelSpaceBigInt";
|
||||
|
||||
export default class PolycubeMesh {
|
||||
private static geometryManager: GeometryManager;
|
||||
private group: THREE.Group;
|
||||
private meshes: THREE.Mesh[] = [];
|
||||
private currentPolycube: bigint = 0n;
|
||||
private currentPolycube: boolean[] | bigint = [];
|
||||
private material: THREE.MeshPhongMaterial;
|
||||
private numActiveCubes: number = 0;
|
||||
private flyDirection: THREE.Vector3 = new THREE.Vector3();
|
||||
|
||||
constructor(polycube: VoxelSpace, color: string) {
|
||||
this.material = new THREE.MeshPhongMaterial({color: 'red', shininess: 100, reflectivity: 100});
|
||||
constructor(polycube: VoxelSpaceBoolean | VoxelSpaceBigInt) {
|
||||
this.material = new THREE.MeshPhongMaterial({color: polycube.getColor(), shininess: 100, reflectivity: 100});
|
||||
this.group = new THREE.Group();
|
||||
this.swapColor(color);
|
||||
this.swapPolycube(polycube);
|
||||
}
|
||||
|
||||
@@ -22,11 +22,7 @@ export default class PolycubeMesh {
|
||||
PolycubeMesh.geometryManager = manager;
|
||||
}
|
||||
|
||||
swapColor(color: string) {
|
||||
this.material.color.set(color);
|
||||
}
|
||||
|
||||
swapPolycube(polycube: VoxelSpace) {
|
||||
swapPolycube(polycube: VoxelSpaceBoolean | VoxelSpaceBigInt) {
|
||||
if (polycube.getRaw() === this.currentPolycube) {
|
||||
return;
|
||||
}
|
||||
@@ -40,10 +36,11 @@ export default class PolycubeMesh {
|
||||
}
|
||||
});
|
||||
this.currentPolycube = polycube.getRaw();
|
||||
this.material.color.set(polycube.getColor());
|
||||
this.flyDirection = this.middlePosOfGroup().normalize();
|
||||
}
|
||||
|
||||
private addCube(refPolycube: VoxelSpace, x: number, y: number, z: number) {
|
||||
private addCube(refPolycube: VoxelSpaceBoolean | VoxelSpaceBigInt, x: number, y: number, z: number) {
|
||||
const dims = refPolycube.getDims();
|
||||
const neighbourProfile = refPolycube.getDirectNeighbourProfile(x, y, z);
|
||||
const mesh = new THREE.Mesh(
|
||||
@@ -51,9 +48,9 @@ export default class PolycubeMesh {
|
||||
this.material
|
||||
);
|
||||
mesh.position.set(
|
||||
-((dims[0] - 1)/2) + x,
|
||||
-((dims[1] - 1)/2) + y,
|
||||
-((dims[2] - 1)/2) + z,
|
||||
((dims[0] - 1)/2) - x,
|
||||
-((dims[1] - 1)/2) + y,
|
||||
);
|
||||
this.meshes.push(mesh);
|
||||
this.group.add(mesh);
|
||||
|
||||
@@ -2,7 +2,8 @@ import * as THREE from 'three';
|
||||
import type SomaSolution from "../../SomaSolution";
|
||||
import RotationControl from "./RotationControl";
|
||||
import PolycubeMesh from "./PolycubeMesh";
|
||||
import VoxelSpace, {DimensionDef} from "../../VoxelSpace";
|
||||
import type VoxelSpaceBoolean from "../../VoxelSpaceBoolean";
|
||||
import type VoxelSpaceBigInt from "../../VoxelSpaceBigInt";
|
||||
import GeometryManager from "./GeometryManager";
|
||||
|
||||
export default class PolycubeScene {
|
||||
@@ -44,20 +45,19 @@ export default class PolycubeScene {
|
||||
this.camera.lookAt(0, 0, 0);
|
||||
}
|
||||
|
||||
private showPolycube(polycube: bigint, dims: number, color: string) {
|
||||
showPolycube(voxelSpace: VoxelSpaceBoolean) {
|
||||
this.controls.disableFly();
|
||||
const voxelSpace = new VoxelSpace(0, [dims, dims, dims], polycube, true);
|
||||
this.clearScene();
|
||||
this.addPolycube(voxelSpace, color);
|
||||
this.addPolycube(voxelSpace);
|
||||
this.polycubeMeshes[0].center();
|
||||
}
|
||||
|
||||
private showSolution(solution: SomaSolution, colorMap: Record<number, string>) {
|
||||
showSolution(solution: SomaSolution) {
|
||||
this.controls.enableFly();
|
||||
this.clearScene();
|
||||
const pieces = solution.getPieces();
|
||||
for (let i = 0; i < pieces.length; i++) {
|
||||
this.addPolycube(pieces[i], colorMap[i]);
|
||||
this.addPolycube(pieces[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,8 +66,8 @@ export default class PolycubeScene {
|
||||
this.cubeScene.clear();
|
||||
}
|
||||
|
||||
private addPolycube(voxelSpace: VoxelSpace, color: string) {
|
||||
const newMesh = new PolycubeMesh(voxelSpace, color);
|
||||
private addPolycube(voxelSpace: VoxelSpaceBoolean | VoxelSpaceBigInt) {
|
||||
const newMesh = new PolycubeMesh(voxelSpace);
|
||||
this.polycubeMeshes.push(newMesh);
|
||||
this.cubeScene.add(newMesh.asObj3D());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user