reset files to before desktop config, added more ui cleanup, store classes, examples

This commit is contained in:
Daniel Ledda
2021-07-05 19:21:43 +02:00
parent 0f3696b497
commit c415641d39
29 changed files with 2008 additions and 518 deletions

66
.idea/workspace.xml generated
View File

@@ -26,14 +26,33 @@
</component>
<component name="ChangeListManager">
<list default="true" id="70635ef7-86ab-4681-b98d-dc8e4999995b" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/src/ui/IncDecNum.svelte" afterDir="false" />
<change afterPath="$PROJECT_DIR$/public/worker.js" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/solve.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/stores/DimStore.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/stores/PolycubeStore.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/ui/CubeInputSet.svelte" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/ui/ExamplesList.svelte" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/ui/InputParameters.svelte" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/ui/List.svelte" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/ui/SolveButton.svelte" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/ui/Tabs.svelte" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/utils.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/public/favicon.png" beforeDir="false" afterPath="$PROJECT_DIR$/public/favicon.png" afterDir="false" />
<change beforePath="$PROJECT_DIR$/public/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/public/index.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/desktop/main.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/desktop/main.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/desktop/preload.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/desktop/preload.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/main.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/store.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/store.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/ui/App.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/src/ui/App.svelte" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/ui/Interactor.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/src/ui/Interactor.svelte" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/ui/CubeInput.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/src/ui/CubeInput.svelte" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/ui/IncDecNum.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/src/ui/IncDecNum.svelte" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/ui/Interactor.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/src/ui/Stage.svelte" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/ui/Sidebar.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/src/ui/Sidebar.svelte" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/ui/SolutionList.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/src/ui/SolutionList.svelte" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/ui/SolutionViewer.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/src/ui/SolutionViewer.svelte" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/ui/threedee/GeometryManager.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/ui/threedee/GeometryManager.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/ui/threedee/PolycubeScene.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/ui/threedee/PolycubeScene.ts" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
@@ -50,9 +69,16 @@
<option value="store.svelte" />
<option value="SolutionList.svelte" />
<option value="HTML File" />
<option value="JavaScript File" />
<option value="TypeScript File" />
<option value="IncDecNum.svelte" />
<option value="JavaScript File" />
<option value="Cube2D.svelte" />
<option value="CubeInputSet.svelte" />
<option value="TypeScript File" />
<option value="Tabs.svelte" />
<option value="InputParameters.svelte" />
<option value="SolveButton.svelte" />
<option value="ExamplesList.svelte" />
<option value="List.svelte" />
</list>
</option>
</component>
@@ -74,7 +100,7 @@
<property name="DefaultHtmlFileTemplate" value="HTML File" />
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/src/solver/js" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/public" />
<property name="node.js.detected.package.eslint" value="true" />
<property name="node.js.detected.package.tslint" value="true" />
<property name="node.js.path.for.package.eslint" value="project" />
@@ -90,18 +116,18 @@
</component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/public/solver" />
<recent name="$PROJECT_DIR$/src" />
<recent name="$PROJECT_DIR$/src/solver/js" />
<recent name="$PROJECT_DIR$/src/solver/wasm" />
<recent name="$PROJECT_DIR$/resources/app" />
<recent name="$PROJECT_DIR$/src/ui" />
<recent name="$PROJECT_DIR$/public/resources" />
<recent name="$PROJECT_DIR$/public" />
<recent name="$PROJECT_DIR$/public/solver" />
</key>
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/src/solver/js" />
<recent name="$PROJECT_DIR$/resources/app" />
<recent name="$PROJECT_DIR$/src/ui" />
<recent name="$PROJECT_DIR$/public" />
<recent name="$PROJECT_DIR$/public/build" />
<recent name="$PROJECT_DIR$/public/solver" />
<recent name="$PROJECT_DIR$/src" />
<recent name="$PROJECT_DIR$/src/desktop" />
<recent name="$PROJECT_DIR$/src/solver/js" />
</key>
</component>
<component name="RunManager">
@@ -147,7 +173,9 @@
<workItem from="1624953370630" duration="10086000" />
<workItem from="1625293881668" duration="18235000" />
<workItem from="1625330866437" duration="4956000" />
<workItem from="1625389803555" duration="9379000" />
<workItem from="1625389803555" duration="21540000" />
<workItem from="1625471453862" duration="708000" />
<workItem from="1625489120282" duration="14952000" />
</task>
<servers />
</component>
@@ -234,18 +262,20 @@
<screen x="0" y="27" width="1920" height="1053" />
</state>
<state x="834" y="395" width="250" height="278" key="jetbrains.javascript.buildTools.run-task-popup/1920.0.1920.1080/0.27.1920.1053@0.27.1920.1053" timestamp="1623671403065" />
<state x="624" y="245" key="run.anything.popup" timestamp="1625390874883">
<screen x="0" y="27" width="1920" height="1053" />
<state x="2544" y="224" key="run.anything.popup" timestamp="1625494559570">
<screen x="1920" y="0" width="1920" height="1080" />
</state>
<state x="1248" y="245" key="run.anything.popup/0.27.3840.1053@0.27.3840.1053" timestamp="1625305747628" />
<state x="624" y="245" key="run.anything.popup/1920.0.1920.1080/0.27.1920.1053@0.27.1920.1053" timestamp="1625390874883" />
<state x="2544" y="224" key="run.anything.popup/1920.0.1920.1080/0.27.1920.1053@1920.0.1920.1080" timestamp="1625494559570" />
<state x="755" y="405" width="400" height="284" key="scopes" timestamp="1621932851405">
<screen x="0" y="27" width="1920" height="1053" />
</state>
<state x="755" y="405" width="400" height="284" key="scopes/1920.0.1920.1080/0.27.1920.1053@0.27.1920.1053" timestamp="1621932851405" />
<state x="623" y="239" width="672" height="678" key="search.everywhere.popup" timestamp="1623095042292">
<screen x="0" y="27" width="1920" height="1053" />
<state x="2543" y="217" width="672" height="696" key="search.everywhere.popup" timestamp="1625502533882">
<screen x="1920" y="0" width="1920" height="1080" />
</state>
<state x="623" y="239" width="672" height="678" key="search.everywhere.popup/1920.0.1920.1080/0.27.1920.1053@0.27.1920.1053" timestamp="1623095042292" />
<state x="2543" y="217" width="672" height="696" key="search.everywhere.popup/1920.0.1920.1080/0.27.1920.1053@1920.0.1920.1080" timestamp="1625502533882" />
</component>
</project>

View File

@@ -46,10 +46,11 @@
"win": {
"target": ["portable"]
},
"files": [
"**/*",
"./public/**",
"./src/desktop${/*}"
"linux": {
"target": ["AppImage"]
},
"extraResources": [
"public/solver"
],
"icon": "./public/resources/soma_icon.png",
"directories": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

@@ -12,7 +12,8 @@
<script defer src='./build/bundle.js'></script>
</head>
<script>
</script>
<body>
</body>
</html>

BIN
public/main.wasm Normal file

Binary file not shown.

525
public/worker.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -9,7 +9,6 @@ function createWindow() {
preload: path.join(__dirname, 'preload.js'),
}
});
win.loadFile(path.join(__dirname, '../../public/index.html'));
}

View File

@@ -1,12 +0,0 @@
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector);
if (element) {
element.innerText = text;
}
}
for (const dependency of ['chrome', 'node', 'electron']) {
replaceText(`${dependency}-version`, process.versions[dependency]);
}
});

View File

@@ -1,9 +1,10 @@
import App from './ui/App.svelte';
import PolycubeScene from "./ui/threedee/PolycubeScene";
const app = new App({
target: document.body,
props: {
name: 'world'
scene: new PolycubeScene()
}
});

128
src/solve.ts Normal file
View File

@@ -0,0 +1,128 @@
import SomaSolution from "./SomaSolution";
import {get} from "svelte/store";
import VoxelSpaceBigInt from "./VoxelSpaceBigInt";
import {
activeSolution,
polycubes,
showingSolution,
solutions,
solving,
somaDimX,
somaDimY,
somaDimZ,
totalVolume
} from "./store";
const worker = new Worker('./solver/main.js', {type: 'module'});
async function respondWasm(event: MessageEvent) {
solutions.set(event.data.map((wasmSolution) => {
const solnObj = new SomaSolution(somaDimX.currentVal(), somaDimY.currentVal(), somaDimZ.currentVal());
const spaceReps = wasmSolution.split(",");
for (let i = 0; i < spaceReps.length; i++) {
solnObj.addSpace(new VoxelSpaceBigInt({
id: i,
dims: [somaDimX.currentVal(), somaDimY.currentVal(), somaDimZ.currentVal()],
space: BigInt(parseInt(spaceReps[i])),
color: get(polycubes)[i].getColor(),
cullEmpty: false,
}));
}
return solnObj;
}));
if (event.data.length > 0) {
activeSolution.set(0);
showingSolution.set(true);
} else {
showingSolution.set(false);
activeSolution.set(null);
}
solving.set(false);
}
function respondJs(event: MessageEvent) {
solutions.set(event.data.solns.map(solnSpaces => {
const solnObj = new SomaSolution(somaDimX.currentVal(), somaDimY.currentVal(), somaDimZ.currentVal());
for (let i = 0; i < solnSpaces.length; i++) {
solnObj.addSpace(new VoxelSpaceBigInt({
id: i,
dims: [somaDimX.currentVal(), somaDimY.currentVal(), somaDimZ.currentVal()],
space: BigInt(`0b${ solnSpaces[i] }`),
color: get(polycubes)[i].getColor(),
cullEmpty: false,
}));
}
return solnObj;
}));
if (event.data.length > 0) {
activeSolution.set(0);
showingSolution.set(true);
} else {
showingSolution.set(false);
activeSolution.set(null);
}
solving.set(false);
}
export function solve() {
const doWasm = get(totalVolume) <= 32;
let inputCubes;
if (doWasm) {
worker.onmessage = (e) => respondWasm(e);
} else {
worker.onmessage = (e) => respondJs(e);
}
inputCubes = polycubes.currentVal().map(cubeInput => cubeInput.getRaw());
solving.set(true);
worker.postMessage({
type: doWasm ? 'wasm' : 'js',
polycubes: inputCubes,
dimX: somaDimX.currentVal(),
dimY: somaDimY.currentVal(),
dimZ: somaDimZ.currentVal()
});
}
// async function solveSync() {
// const solver = new SomaSolver(somaDimX.currentVal(), somaDimY.currentVal(), somaDimZ.currentVal());
// function showSolutionWaitUserFeedback(soln: SomaSolution) {
// activeSolution.set(0);
// solutions.set([soln]);
// showingSolution.set(true);
// return new Promise<void>((resolve) => {
// const callback = (e: KeyboardEvent) => {
// resolve();
// window.removeEventListener("keydown", callback);
// };
// window.addEventListener("keydown", callback);
// });
// }
// solver.setDebug({
// showSoln(soln: SomaSolution) {
// return showSolutionWaitUserFeedback(soln);
// },
// showSpace(cube: VoxelSpaceBoolean) {
// const testSoln = new SomaSolution(somaDimX.currentVal(), somaDimY.currentVal(), somaDimZ.currentVal());
// testSoln.addSpace(cube);
// return showSolutionWaitUserFeedback(testSoln);
// }
// });
// solving.set(true);
// await solver.solve(polycubes.currentVal().map(cubeInput => new VoxelSpaceBoolean({
// id: cubeInput.getId(),
// dims: cubeInput.getDims(),
// space: cubeInput.getRaw(),
// color: cubeInput.getColor(),
// cullEmpty: true
// })));
// const solns = solver.getSolutions();
//
// if (solns.length > 0) {
// activeSolution.set(0);
// solutions.set(solns);
// showingSolution.set(true);
// } else {
// showingSolution.set(false);
// activeSolution.set(null);
// }
// solving.set(false);
// }

View File

@@ -1,27 +1,22 @@
import { derived, writable } from 'svelte/store';
import { get } from 'svelte/store';
import SomaSolution from "./SomaSolution";
import VoxelSpaceBigInt from "./VoxelSpaceBigInt";
import type {DimensionDef} from "./VoxelSpaceBoolean";
import PolycubeScene from "./ui/threedee/PolycubeScene";
import type SomaSolution from "./SomaSolution";
import type VoxelSpaceBigInt from "./VoxelSpaceBigInt";
import DimStore from "./stores/DimStore";
import PolycubeStore from "./stores/PolycubeStore";
export const MAX_DIMS = 20;
export const MIN_DIMS = 1;
export const solving = writable(false);
export const debug = writable(false);
export const somaDimX = dimStore(3);
export const somaDimY = dimStore(3);
export const somaDimZ = dimStore(3);
export const polycubes = polycubeStore();
export const selectedCube = writable(0);
export const solutions = writable([] as SomaSolution[]);
export const activeSolution = writable<number | null>(null);
export const showingSolution = writable(false);
export const somaDimX = new DimStore(3);
export const somaDimY = new DimStore(3);
export const somaDimZ = new DimStore(3);
export const totalVolume = derived(
[somaDimX, somaDimY, somaDimZ],
([$dimX, $dimY, $dimZ]: [number, number, number]) => $dimX*$dimY*$dimZ
);
export const polycubes = new PolycubeStore(somaDimX, somaDimY, somaDimZ);
export const solving = writable(false);
export const debug = writable(false);
export const solutions = writable([] as SomaSolution[]);
export const activeSolution = writable<number | null>(null);
export const showingSolution = writable(false);
export const isMaxPolycubes = derived(
[polycubes, totalVolume],
([$cubes, $vol]: [VoxelSpaceBigInt[], number]) => $cubes.length >= $vol
@@ -30,221 +25,93 @@ export const isMinPolycubes = derived(
polycubes,
($polycubes: VoxelSpaceBigInt[]) => $polycubes.length <= 1
);
export const cubeScene = new PolycubeScene();
function dimStore(init: number) {
const dimStore = writable(init);
return {
subscribe: dimStore.subscribe,
inc() {
dimStore.set(get(dimStore) + 1);
},
dec() {
dimStore.set(get(dimStore) - 1);
},
set(dim: number) {
if (dim > MAX_DIMS || dim < MIN_DIMS) {
return;
}
dimStore.set(dim);
polycubes.reset();
},
}
}
function polycubeStore() {
function freshCube(id: number) {
return new VoxelSpaceBigInt({
id: id,
dims: [get(somaDimX), get(somaDimY), get(somaDimZ)],
color: colorFromIndex(id),
cullEmpty: false
});
}
const polycubeStore = writable<VoxelSpaceBigInt[]>([freshCube(0)]);
return {
subscribe: polycubeStore.subscribe,
setColor(cubeIndex: number, color: string) {
const cubes = get(polycubeStore);
cubes[cubeIndex].setColor(color);
polycubeStore.set(cubes);
},
addCube() {
if (!get(isMaxPolycubes)) {
polycubeStore.update((polycubes: VoxelSpaceBigInt[]) =>
polycubes.concat(freshCube(polycubes.length)));
}
},
removeCube() {
if (!get(isMinPolycubes)) {
polycubeStore.update((polycubes: VoxelSpaceBigInt[]) => polycubes.splice(0, polycubes.length - 1));
}
const newLength = get(polycubeStore).length;
if (newLength <= get(selectedCube)) {
selectedCube.set(newLength - 1);
}
},
toggle(cubeIndex: number, x: number, y: number, z: number) {
const cubes = get(polycubeStore);
cubes[cubeIndex].toggle(x, y, z);
polycubeStore.set(cubes);
},
set(cubeIndex: number, val: boolean, x: number, y: number, z: number) {
const cubes = get(polycubeStore);
cubes[cubeIndex].set(x, y, z, val);
polycubeStore.set(cubes);
},
reset() {
polycubeStore.update((polycubes: VoxelSpaceBigInt[]) => {
const result: VoxelSpaceBigInt[] = [];
for (let i = 0; i < Math.min(polycubes.length, get(totalVolume)); i++) {
result.push(freshCube(i));
}
return result;
});
}
}
}
function rgbToHex(rgbStr: string): string {
const sep = rgbStr.indexOf(",") > -1 ? "," : " ";
const rgb = rgbStr.substr(4).split(")")[0].split(sep);
const r = (+rgb[0]).toString(16).padStart(2, "0");
const g = (+rgb[1]).toString(16).padStart(2, "0");
const b = (+rgb[2]).toString(16).padStart(2, "0");
return "#" + r + g + b;
}
function hslToRgb(hslStr: string): string {
const opt = new Option();
opt.style.color = hslStr;
return opt.style.color;
}
export function colorFromIndex(index: number): string {
const colorWheelCycle = Math.floor(index / 6);
const darknessCycle = Math.floor(index / 12);
const spacing = (360 / 6);
const offset = colorWheelCycle === 0 ? 0 : spacing / (colorWheelCycle + 2);
let hue = spacing * (index % 6) + offset;
const saturation = 100;
const lightness = 1 / (2 + darknessCycle) * 100;
return rgbToHex(hslToRgb(`hsl(${hue},${saturation}%,${Math.round(lightness)}%)`));
}
const worker = new Worker('../solver/main.js', {type: "module"});
async function respondWasm(event: MessageEvent) {
solutions.set(event.data.map((wasmSolution) => {
const solnObj = new SomaSolution(get(somaDimX), get(somaDimY), get(somaDimZ));
const spaceReps = wasmSolution.split(",");
for (let i = 0; i < spaceReps.length; i++) {
solnObj.addSpace(new VoxelSpaceBigInt({
id: i,
dims: [get(somaDimX), get(somaDimY), get(somaDimZ)] as DimensionDef,
space: BigInt(parseInt(spaceReps[i])),
color: get(polycubes)[i].getColor(),
cullEmpty: false,
}));
}
return solnObj;
}));
if (event.data.length > 0) {
activeSolution.set(0);
showingSolution.set(true);
} else {
showingSolution.set(false);
activeSolution.set(null);
}
solving.set(false);
}
function respondJs(event: MessageEvent) {
solutions.set(event.data.solns.map(solnSpaces => {
const solnObj = new SomaSolution(get(somaDimX), get(somaDimY), get(somaDimZ));
for (let i = 0; i < solnSpaces.length; i++) {
solnObj.addSpace(new VoxelSpaceBigInt({
id: i,
dims: [get(somaDimX), get(somaDimY), get(somaDimZ)] as DimensionDef,
space: BigInt(`0b${ solnSpaces[i] }`),
color: get(polycubes)[i].getColor(),
cullEmpty: false,
}));
}
return solnObj;
}));
if (event.data.length > 0) {
activeSolution.set(0);
showingSolution.set(true);
} else {
showingSolution.set(false);
activeSolution.set(null);
}
solving.set(false);
}
export function solve() {
const doWasm = get(totalVolume) <= 32;
let inputCubes;
if (doWasm) {
worker.onmessage = (e) => respondWasm(e);
} else {
worker.onmessage = (e) => respondJs(e);
}
inputCubes = get(polycubes).map(cubeInput => cubeInput.getRaw());
solving.set(true);
worker.postMessage({
type: doWasm ? 'wasm' : 'js',
polycubes: inputCubes,
dimX: get(somaDimX),
dimY: get(somaDimY),
dimZ: get(somaDimZ)
});
}
// async function solveSync() {
// const solver = new SomaSolver(get(somaDimX), get(somaDimY), get(somaDimZ));
// function showSolutionWaitUserFeedback(soln: SomaSolution) {
// activeSolution.set(0);
// solutions.set([soln]);
// showingSolution.set(true);
// return new Promise<void>((resolve) => {
// const callback = (e: KeyboardEvent) => {
// resolve();
// window.removeEventListener("keydown", callback);
// };
// window.addEventListener("keydown", callback);
// });
// }
// if (get(debug)) {
// solver.setDebug({
// showSoln(soln: SomaSolution) {
// return showSolutionWaitUserFeedback(soln);
// },
// showSpace(cube: VoxelSpaceBoolean) {
// const testSoln = new SomaSolution(get(somaDimX), get(somaDimY), get(somaDimZ));
// testSoln.addSpace(cube);
// return showSolutionWaitUserFeedback(testSoln);
// }
// });
// }
// solving.set(true);
// await solver.solve(get(polycubes).map(cubeInput => new VoxelSpaceBoolean({
// id: cubeInput.getId(),
// dims: cubeInput.getDims(),
// space: cubeInput.getRaw(),
// color: cubeInput.getColor(),
// cullEmpty: true
// })));
// const solns = solver.getSolutions();
//
// if (solns.length > 0) {
// activeSolution.set(0);
// solutions.set(solns);
// showingSolution.set(true);
// } else {
// showingSolution.set(false);
// activeSolution.set(null);
// }
// solving.set(false);
// }
export const examples = [
{
name: "Standard Soma Cube",
dimX: 3,
dimY: 3,
dimZ: 3,
cubes: [
{space: 23n, color: "#ff0000"},
{space: 30n, color: "#ffff00"},
{space: 15n, color: "#00ff00"},
{space: 8344n, color: "#00ffff"},
{space: 9240n, color: "#0000ff"},
{space: 4632n, color: "#ff00ff"},
{space: 152n, color: "#ff5500"},
],
},
{
name: "Convolution TG 5850 (4x4x4)",
dimX: 4,
dimY: 4,
dimZ: 4,
cubes: [
{space: 12651033568030492808n, color: "#ff0000"},
{space: 1123868011502010368n, color: "#ffff00"},
{space: 124314703626304n, color: "#00ff00"},
{space: 263883079155712n, color: "#00ffff"},
{space: 3952148496n, color: "#0000ff"},
{space: 166723584n, color: "#ff00ff"},
{space: 1048576n, color: "#ff5500"},
],
},
{
name: "3x3x4, 7 Pieces",
dimX: 3,
dimY: 3,
dimZ: 4,
cubes: [
{space: 244n, color: "#ff0000"},
{space: 625n, color: "#ffff00"},
{space: 140080n, color: "#00ff00"},
{space: 31n, color: "#00ffff"},
{space: 738n, color: "#0000ff"},
{space: 537002290n, color: "#ff00ff"},
{space: 275n, color: "#ff5500"},
],
},
{
name: "3x3x3, 7 Pieces",
dimX: 3,
dimY: 3,
dimZ: 3,
cubes: [
{space: 23n, color: "#ff0000"},
{space: 47n, color: "#ffff00"},
{space: 474n, color: "#00ff00"},
{space: 8n, color: "#00ffff"},
{space: 24n, color: "#0000ff"},
{space: 316n, color: "#ff00ff"},
{space: 23n, color: "#ff5500"},
],
},
{
name: "3x3x3, 6 Pieces",
dimX: 3,
dimY: 3,
dimZ: 3,
cubes: [
{space: 30n, color: "#ff0000"},
{space: 29712n, color: "#ffff00"},
{space: 29216n, color: "#00ff00"},
{space: 15392n, color: "#00ffff"},
{space: 15364n, color: "#0000ff"},
{space: 536871032n, color: "#ff00ff"},
],
},
{
name: "3x3x3, 5 Pieces",
dimX: 3,
dimY: 3,
dimZ: 3,
cubes: [
{space: 376n, color: "#ff0000"},
{space: 2428n, color: "#ffff00"},
{space: 28960n, color: "#00ff00"},
{space: 48136n, color: "#00ffff"},
{space: 120n, color: "#0000ff"},
],
},
];

34
src/stores/DimStore.ts Normal file
View File

@@ -0,0 +1,34 @@
import {get, Writable, writable} from "svelte/store";
export default class DimStore {
static readonly MAX_DIMS = 20;
static readonly MIN_DIMS = 1;
private store: Writable<number>;
constructor(init: number) {
this.store = writable(init);
}
currentVal() {
return get(this.store);
}
subscribe(cb: (val: number) => any) {
return this.store.subscribe(cb);
}
inc() {
this.set(this.currentVal() + 1);
}
dec() {
this.set(this.currentVal() - 1);
}
set(dim: number) {
if (dim > DimStore.MAX_DIMS || dim < DimStore.MIN_DIMS) {
return;
}
this.store.set(dim);
}
}

122
src/stores/PolycubeStore.ts Normal file
View File

@@ -0,0 +1,122 @@
import VoxelSpaceBigInt from "../VoxelSpaceBigInt";
import {get, Writable, writable} from "svelte/store";
import type DimStore from "./DimStore";
import {colorFromIndex} from "../utils";
export default class PolycubeStore {
private store: Writable<VoxelSpaceBigInt[]>;
private dimX: DimStore;
private dimY: DimStore;
private dimZ: DimStore;
private selectedCube: Writable<number>;
constructor(dimX: DimStore, dimY: DimStore, dimZ: DimStore) {
this.selectedCube = writable(0);
this.dimX = dimX;
this.dimY = dimY;
this.dimZ = dimZ;
this.store = writable<VoxelSpaceBigInt[]>([this.freshCube(0)]);
this.dimX.subscribe(() => this.reset());
this.dimY.subscribe(() => this.reset());
this.dimZ.subscribe(() => this.reset());
}
private freshCube(id: number) {
return new VoxelSpaceBigInt({
id: id,
dims: [this.dimX.currentVal(), this.dimY.currentVal(), this.dimZ.currentVal()],
color: colorFromIndex(id),
cullEmpty: false
});
}
private volume() {
return this.dimX.currentVal() * this.dimY.currentVal() * this.dimZ.currentVal();
}
private isMaxPolycubes() {
return this.currentVal().length >= this.volume();
}
private isMinPolycubes() {
return this.currentVal().length <= 1;
}
private getSelectedCube() {
return get(this.selectedCube);
}
selected() {
return this.selectedCube;
}
currentVal() {
return get(this.store);
}
subscribe(cb: (cubes: VoxelSpaceBigInt[]) => any) {
return this.store.subscribe(cb);
}
setColor(cubeIndex: number, color: string) {
const cubes = this.currentVal();
cubes[cubeIndex].setColor(color);
this.store.set(cubes);
}
addCube() {
if (!this.isMaxPolycubes()) {
this.store.update((polycubes: VoxelSpaceBigInt[]) =>
polycubes.concat(this.freshCube(polycubes.length)));
}
}
removeCube() {
if (!this.isMinPolycubes()) {
this.store.update((polycubes: VoxelSpaceBigInt[]) => polycubes.splice(0, polycubes.length - 1));
}
const newLength = this.currentVal().length;
if (newLength <= this.getSelectedCube()) {
this.selectedCube.set(newLength - 1);
}
}
toggle(cubeIndex: number, x: number, y: number, z: number) {
const cubes = this.currentVal();
cubes[cubeIndex].toggle(x, y, z);
this.store.set(cubes);
}
set(cubeIndex: number, val: boolean, x: number, y: number, z: number) {
const cubes = this.currentVal();
cubes[cubeIndex].set(x, y, z, val);
this.store.set(cubes);
}
reset() {
this.store.update((polycubes: VoxelSpaceBigInt[]) => {
const result: VoxelSpaceBigInt[] = [];
for (let i = 0; i < Math.min(polycubes.length, this.volume()); i++) {
result.push(this.freshCube(i));
}
return result;
});
}
setCubes(cubes: VoxelSpaceBigInt[]) {
let lastDims = cubes[0].getDims();
for (const cube of cubes) {
const dimsMatch = !cube.getDims().some((dim, i) => lastDims[i] !== dim);
if (!dimsMatch) {
throw new Error("Error setting cubes: not all dimensions match.");
}
}
this.dimX.set(lastDims[0]);
this.dimY.set(lastDims[1]);
this.dimZ.set(lastDims[2]);
this.store.set(cubes);
this.selectedCube.set(0);
}
}

View File

@@ -1,6 +1,9 @@
<script lang="ts">
import Sidebar from "./Sidebar.svelte";
import SolutionInteractor from "./Interactor.svelte";
import Stage from "./Stage.svelte";
import PolycubeScene from "./threedee/PolycubeScene";
export let scene: PolycubeScene;
</script>
<main>
@@ -8,7 +11,7 @@
<Sidebar />
</div>
<div class="solutionBodyContainer">
<SolutionInteractor />
<Stage scene="{scene}" />
</div>
</main>

View File

@@ -1,11 +1,12 @@
<script lang="ts">
import {somaDimX, somaDimY, somaDimZ, polycubes, selectedCube, showingSolution} from "../store";
import {somaDimX, somaDimY, somaDimZ, polycubes, showingSolution} from "../store";
import VoxelSpaceBoolean from "../VoxelSpaceBoolean";
export let cubeNo: number;
$: cube = $polycubes[cubeNo] as VoxelSpaceBoolean;
$: cubeColor = cube.getColor();
$: currentlyVisualised = $selectedCube === cubeNo && !$showingSolution;
const currentlySelected = polycubes.selected();
$: currentlyVisualised = $currentlySelected === cubeNo && !$showingSolution;
let cellStartDragInitialVal: boolean = false;
let cellStartDrag: number = 0;
let cellDragStartPos: {x: number, y: number} = {x: 0, y: 0};
@@ -24,7 +25,7 @@
function onMouseOverCell(event: MouseEvent, x: number, y: number, z: number) {
if (event.buttons !== 0) {
polycubes.set(cubeNo, event.buttons === 1, x, y, z);
selectedCube.set(cubeNo);
polycubes.selected().set(cubeNo);
}
}
@@ -56,7 +57,7 @@
function onClickCube() {
showingSolution.set(false);
selectedCube.set(cubeNo);
currentlySelected.set(cubeNo);
}
function onColorChange(event: InputEvent) {

View File

@@ -0,0 +1,47 @@
<script lang="ts">
import CubeInput from "./CubeInput.svelte";
import {polycubes, somaDimX, somaDimY, somaDimZ} from "../store";
export let numCubes;
$: numCubes = $polycubes.length;
window.addEventListener("keypress", () => {
for (const cube of $polycubes) {
console.log({
name: "",
dimX: $somaDimX,
dimY: $somaDimY,
dimZ: $somaDimZ,
cubes: $polycubes.map(cube => ({space: cube.getRaw(), color: cube.getColor()})),
});
}
});
</script>
<div class="container">
{#each {length: numCubes} as _, cubeNo}
<div class="cube-input">
<div class="padder">
<CubeInput
cubeNo={cubeNo}
/>
</div>
</div>
{/each}
</div>
<style>
.padder {
padding: 1em;
}
.cube-input {
margin: auto;
}
.container {
flex: 1 1 auto;
overflow-x: scroll;
display: flex;
flex-flow: row;
margin: auto;
}
</style>

View File

@@ -0,0 +1,35 @@
<script lang="ts">
import List from "./List.svelte";
import {polycubes, examples} from "../store";
import VoxelSpaceBigInt from "../VoxelSpaceBigInt";
let lastClickedExample = 0;
function hydrateExample(exNo: number) {
const example = examples[exNo];
polycubes.setCubes(example.cubes.map((cube, i) => new VoxelSpaceBigInt({
id: i,
dims: [example.dimX, example.dimY, example.dimZ],
space: cube.space,
color: cube.color,
cullEmpty: false,
})));
lastClickedExample = exNo;
}
</script>
<div class="container">
<List
defaultText="No examples found..."
items="{examples.map(example => example.name)}"
activeItem={lastClickedExample}
onClick={(i) => hydrateExample(i)}
/>
</div>
<style>
.container {
max-height: 10em;
overflow-x: hidden;
}
</style>

View File

@@ -3,12 +3,12 @@
export let down: () => void;
export let val: number;
export let upDisabled: boolean;
export let title: string;
export let title: string = "";
export let downDisabled: boolean;
</script>
<div class="container">
{#if title}
{#if title !== ""}
<p class="title">{title}</p>
{/if}
<div class="controls">
@@ -24,7 +24,7 @@
display: inline-block;
}
.title {
margin-bottom: 0;
margin: 0;
}
.val {
font-weight: bold;

View File

@@ -0,0 +1,55 @@
<script lang="ts">
import {somaDimX, somaDimY, somaDimZ, totalVolume, polycubes, isMinPolycubes, isMaxPolycubes} from "../store";
import IncDecNum from "./IncDecNum.svelte";
import PolycubeStore from "../stores/PolycubeStore";
$: numCubes = $polycubes.length;
</script>
<div class="option">
<p>Dimensions:</p>
<IncDecNum
title="X"
val="{$somaDimX}"
upDisabled="{$somaDimX >= PolycubeStore.MAX_DIMS}"
up="{() => somaDimX.set($somaDimX + 1)}"
downDisabled="{$somaDimX <= PolycubeStore.MIN_DIMS}"
down="{() => somaDimX.set($somaDimX - 1)}"
/>
<IncDecNum
title="Y"
val="{$somaDimY}"
upDisabled="{$somaDimY >= PolycubeStore.MAX_DIMS}"
up="{() => somaDimY.set($somaDimY + 1)}"
downDisabled="{$somaDimY <= PolycubeStore.MIN_DIMS}"
down="{() => somaDimY.set($somaDimY - 1)}"
/>
<IncDecNum
title="Z"
val="{$somaDimZ}"
upDisabled="{$somaDimZ >= PolycubeStore.MAX_DIMS}"
up="{() => somaDimZ.set($somaDimZ + 1)}"
downDisabled="{$somaDimZ <= PolycubeStore.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}
</div>
<div class="option">
<p>Cubes:</p>
<IncDecNum
down="{() => polycubes.removeCube()}"
downDisabled="{$isMinPolycubes}"
up="{() => polycubes.addCube()}"
upDisabled="{$isMaxPolycubes}"
val="{numCubes}"
/>
</div>
<style>
.warn {
color: red;
}
</style>

42
src/ui/List.svelte Normal file
View File

@@ -0,0 +1,42 @@
<script lang="ts">
export let defaultText: string = "...";
export let activeItem: number = 0;
export let items: string[] = [];
export let onClick = (itemNo: number) => { activeItem = itemNo };
</script>
<ul>
{#if items.length === 0}
<li>{defaultText}</li>
{/if}
{#each items as item, i}
<li class:active={activeItem === i} on:click={() => onClick(i)}>
{item}
</li>
{/each}
</ul>
<style>
li:hover:not(.active) {
background-color: #aaaaaa;
}
li {
transition: background-color 100ms;
cursor: pointer;
list-style: none;
height: 2em;
line-height: 2em;
}
ul {
width: 100%;
overflow-y: scroll;
flex: 1;
padding: 0.5em;
margin: 0;
text-align: center;
background-color: #666;
}
.active {
background-color: #ff3e00;
}
</style>

View File

@@ -1,122 +1,34 @@
<script lang="ts">
import {
isMaxPolycubes,
isMinPolycubes,
polycubes,
solutions,
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;
let noEmpties: boolean;
let enoughSubcubes: boolean;
let readyToSolve: boolean;
let size: number;
$: {
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 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.");
}
return messages.join("\n");
}
import InputParameters from "./InputParameters.svelte";
import ExamplesList from "./ExamplesList.svelte";
import Tabs from "./Tabs.svelte";
import SolveButton from "./SolveButton.svelte";
</script>
<div class="container">
<h1>Somaesque</h1>
<div class="widgets">
<div class="option">
<p>Dimensions:</p>
<div class="choice">
<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}
</div>
</div>
<div class="option">
<p>Cubes:</p>
<div class="choice">
<IncDecNum
down="{polycubes.removeCube}"
downDisabled="{$isMinPolycubes}"
up="{polycubes.addCube}"
upDisabled="{$isMaxPolycubes}"
val="{numCubes}"
/>
</div>
</div>
<div class="option">
<button
class="solve"
on:click={solve}
title="{genTooltip(enoughSubcubes, noEmpties, size)}"
disabled="{$solving || !readyToSolve}">
{$solving ? "Solving..." : "Solve!"}
</button>
</div>
<Tabs
selectedTab={"Parameters"}
tabs={{
"Parameters": InputParameters,
"Examples": ExamplesList,
}}/>
<SolveButton/>
</div>
<h3>Solutions: {$solutions.length}</h3>
<SolutionList/>
</div>
<style>
.warn {
color: red;
}
p {
margin: 0;
display: inline-block;
}
.choice {
display: block;
text-align: center;
margin-top: 0.5em;
}
input {
display: inline-block;
background-color: #999999;
@@ -124,26 +36,6 @@
height: 2em;
border-style: none;
}
.selected:disabled {
color: white;
background-color: #ff3e00;
}
button.solve {
width: auto;
color: white;
background-color: #ff3e00;
font-size: 2em;
border-radius: 0.5em;
border-style: none;
margin: 0;
cursor: pointer;
}
button.solve:disabled {
width: auto;
color: #999999;
background-color: #a36754;
font-size: 2em;
}
.container {
display: flex;
align-items: center;
@@ -157,16 +49,6 @@
.widgets {
width: 100%;
}
.widgets:first-child {
padding-top: 0;
}
.widgets:last-child {
padding-bottom: 0;
}
.widgets > * {
padding-top: 0.5em;
padding-bottom: 0.5em;
}
h1 {
margin: 0;
color: #ff3e00;

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import {solutions, activeSolution, showingSolution} from "../store";
import List from "./List.svelte";
function selectSolution(i: number) {
activeSolution.set(i);
@@ -7,38 +8,9 @@
}
</script>
<ul>
{#if $solutions.length === 0}
<li>No solutions yet...</li>
{/if}
{#each $solutions as soln, i}
<li class:active={$activeSolution === i} on:click={() => selectSolution(i)}>
Solution #{i + 1}
</li>
{/each}
</ul>
<style>
li:hover:not(.active) {
background-color: #aaaaaa;
}
li {
transition: background-color 100ms;
cursor: pointer;
list-style: none;
height: 2em;
line-height: 2em;
}
ul {
width: 100%;
overflow-y: scroll;
flex: 1;
padding: 0.5em;
margin: 0;
text-align: center;
background-color: #666;
}
.active {
background-color: #ff3e00;
}
</style>
<List
activeItem={$activeSolution}
items={$solutions.map((soln, i) => `Solution ${i + 1}`)}
defaultText="No solutions yet..."
onClick={(i) => selectSolution(i)}
/>

View File

@@ -1,13 +1,15 @@
<script lang="ts">
import PolycubeScene from "./threedee/PolycubeScene";
import {onMount} from "svelte";
import {polycubes, selectedCube, solutions, activeSolution, showingSolution, cubeScene} from "../store";
import {polycubes, solutions, activeSolution, showingSolution} from "../store";
import Solution2D from "./Solution2D.svelte";
$: cube = $polycubes[$selectedCube];
export let scene: PolycubeScene;
const selectedStore = polycubes.selected();
$: selectedCube = $selectedStore;
$: cube = $polycubes[selectedCube];
$: soln = $solutions[$activeSolution];
let el: HTMLDivElement;
let scene: PolycubeScene;
let loaded: boolean = false;
const canvasStyle: Partial<CSSStyleDeclaration> = {
@@ -15,8 +17,8 @@
};
onMount(() => {
cubeScene.onLoaded(() => {
cubeScene.mount(el);
scene.onLoaded(() => {
scene.mount(el);
Object.assign((el.children.item(0) as HTMLElement).style, canvasStyle);
loaded = true;
});
@@ -25,17 +27,15 @@
$: {
if (loaded) {
if ($showingSolution) {
const colorMap = {};
$polycubes.forEach((polycube, i) => colorMap[i] = polycube.color);
cubeScene.showSolution(soln);
scene.showSolution(soln);
} else {
cubeScene.showPolycube(cube);
scene.showPolycube(cube);
}
}
}
</script>
<div class="top">
<div class="container">
{#if $activeSolution !== null}
<div class="soln2d-container">
<Solution2D/>
@@ -45,13 +45,17 @@
</div>
<style>
.top {
display: flex;
justify-content: space-evenly;
align-items: center;
}
.soln2d-container {
flex: 0 1 auto;
display: inline-block;
}
.container {
flex: 1 1 auto;
display: flex;
flex-direction: row;
align-content: center;
justify-content: space-evenly;
text-align: center;
align-items: center;
}
</style>

54
src/ui/SolveButton.svelte Normal file
View File

@@ -0,0 +1,54 @@
<script lang="ts">
import {polycubes, solving, somaDimX, somaDimY, somaDimZ, totalVolume} from "../store";
import {solve} from "../solve";
$: cubes = $polycubes;
let noEmpties: boolean;
let enoughSubcubes: boolean;
let readyToSolve: boolean;
let size: number;
$: {
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 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.");
}
return messages.join("\n");
}
</script>
<button
class="solve"
on:click={solve}
title="{genTooltip(enoughSubcubes, noEmpties, size)}"
disabled="{$solving || !readyToSolve}">
{$solving ? "Solving..." : "Solve!"}
</button>
<style>
button.solve {
width: auto;
color: white;
background-color: #ff3e00;
font-size: 2em;
border-radius: 0.5em;
border-style: none;
margin: 0;
cursor: pointer;
}
button.solve:disabled {
width: auto;
color: #999999;
background-color: #a36754;
font-size: 2em;
}
</style>

View File

@@ -1,8 +1,9 @@
<script lang="ts">
import {polycubes} from "../store";
import CubeInput from "./CubeInput.svelte";
import SolutionViewer from "./SolutionViewer.svelte";
$: numCubes = $polycubes.length;
import CubeInputSet from "./CubeInputSet.svelte";
import PolycubeScene from "./threedee/PolycubeScene";
export let scene: PolycubeScene;
let showInput = true;
let smallViewport = true;
@@ -24,37 +25,13 @@
<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>
<CubeInputSet/>
{:else}
<div class="threedee">
<SolutionViewer/>
</div>
<SolutionViewer scene="{scene}"/>
{/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>
<CubeInputSet/>
<SolutionViewer scene="{scene}"/>
{/if}
</div>
@@ -81,26 +58,6 @@
background-color: grey;
border-width: 1px 0 0 0;
}
.threedee {
flex: 1 1 auto;
display: flex;
flex-direction: column;
align-content: center;
justify-content: center;
text-align: center;
}
.padder {
padding: 1em;
}
.cube-input {
margin: auto;
}
.input-container {
flex: 1 1 auto;
overflow-x: scroll;
display: flex;
flex-flow: row;
}
.viewport {
overflow: scroll;
display: flex;

44
src/ui/Tabs.svelte Normal file
View File

@@ -0,0 +1,44 @@
<script lang="ts">
import {SvelteComponent} from "svelte/internal";
export let selectedTab: string;
export let tabs: Record<string, SvelteComponent>;
$: displayedComponent = tabs[selectedTab];
</script>
<div class="tabs">
{#each Object.keys(tabs) as key}
<div class="tab" class:selected={selectedTab === key} on:click="{() => selectedTab = key}">{key}</div>
{/each}
</div>
<div class="container">
<svelte:component this={displayedComponent}/>
</div>
<style>
.container {
background-color: grey;
padding: 1em;
}
.tabs {
height: 3em;
flex: 0 1 auto;
display: flex;
cursor: pointer;
}
.tab {
flex: 1;
text-align: center;
border-radius: 1em 1em 0 0;
background-color: #555555;
line-height: 3em;
transition: background-color 100ms;
}
.tab:hover {
background-color: #999999;
}
.tab.selected {
background-color: grey;
}
</style>

View File

@@ -88,6 +88,683 @@ const ROT_CODE_MAP = {
c(mesh: THREE.Object3D) { mesh.rotateZ(-Math.PI/2); },
} as const;
const OBJ = `
# Blender v2.82 (sub 7) OBJ File: ''
# www.blender.org
o Cube_Cube.001
v 0.399961 0.500000 -0.399961
v 0.500000 0.399961 -0.399961
v 0.399961 0.399961 -0.500000
v 0.438244 0.492385 -0.399961
v 0.470699 0.470699 -0.399961
v 0.399961 0.492385 -0.438244
v 0.439203 0.483194 -0.439203
v 0.465612 0.465612 -0.437212
v 0.457718 0.457718 -0.457718
v 0.492385 0.399961 -0.438244
v 0.470699 0.399961 -0.470699
v 0.492385 0.438244 -0.399961
v 0.483194 0.439203 -0.439203
v 0.465612 0.437212 -0.465612
v 0.399961 0.438244 -0.492385
v 0.399961 0.470699 -0.470699
v 0.438244 0.399961 -0.492385
v 0.439203 0.439203 -0.483194
v 0.437212 0.465612 -0.465612
v 0.500000 -0.399961 -0.399961
v 0.399961 -0.500000 -0.399961
v 0.399961 -0.399961 -0.500000
v 0.492385 -0.438244 -0.399961
v 0.470699 -0.470699 -0.399961
v 0.492385 -0.399961 -0.438244
v 0.483194 -0.439203 -0.439203
v 0.465612 -0.465612 -0.437212
v 0.457718 -0.457718 -0.457718
v 0.399961 -0.492385 -0.438244
v 0.399961 -0.470699 -0.470699
v 0.438244 -0.492385 -0.399961
v 0.439203 -0.483194 -0.439203
v 0.437212 -0.465612 -0.465612
v 0.438244 -0.399961 -0.492385
v 0.470699 -0.399961 -0.470699
v 0.399961 -0.438244 -0.492385
v 0.439203 -0.439203 -0.483194
v 0.465612 -0.437212 -0.465612
v 0.500000 0.399961 0.399961
v 0.399961 0.500000 0.399961
v 0.399961 0.399961 0.500000
v 0.492385 0.438244 0.399961
v 0.470699 0.470699 0.399961
v 0.492385 0.399961 0.438244
v 0.483194 0.439203 0.439203
v 0.465612 0.465612 0.437212
v 0.457718 0.457718 0.457718
v 0.399961 0.492385 0.438244
v 0.399961 0.470699 0.470699
v 0.438244 0.492385 0.399961
v 0.439203 0.483194 0.439203
v 0.437212 0.465612 0.465612
v 0.438244 0.399961 0.492385
v 0.470699 0.399961 0.470699
v 0.399961 0.438244 0.492385
v 0.439203 0.439203 0.483194
v 0.465612 0.437212 0.465612
v 0.399961 -0.399961 0.500000
v 0.399961 -0.500000 0.399961
v 0.500000 -0.399961 0.399961
v 0.399961 -0.438244 0.492385
v 0.399961 -0.470699 0.470699
v 0.438244 -0.399961 0.492385
v 0.439203 -0.439203 0.483194
v 0.437212 -0.465612 0.465612
v 0.457718 -0.457718 0.457718
v 0.438244 -0.492385 0.399961
v 0.470699 -0.470699 0.399961
v 0.399961 -0.492385 0.438244
v 0.439203 -0.483194 0.439203
v 0.465612 -0.465612 0.437212
v 0.492385 -0.399961 0.438244
v 0.470699 -0.399961 0.470699
v 0.492385 -0.438244 0.399961
v 0.483194 -0.439203 0.439203
v 0.465612 -0.437212 0.465612
v -0.500000 0.399961 -0.399961
v -0.399961 0.500000 -0.399961
v -0.399961 0.399961 -0.500000
v -0.492385 0.438244 -0.399961
v -0.470699 0.470699 -0.399961
v -0.492385 0.399961 -0.438244
v -0.483194 0.439203 -0.439203
v -0.465612 0.465612 -0.437212
v -0.457718 0.457718 -0.457718
v -0.399961 0.492385 -0.438244
v -0.399961 0.470699 -0.470699
v -0.438244 0.492385 -0.399961
v -0.439203 0.483194 -0.439203
v -0.437212 0.465612 -0.465612
v -0.438244 0.399961 -0.492385
v -0.470699 0.399961 -0.470699
v -0.399961 0.438244 -0.492385
v -0.439203 0.439203 -0.483194
v -0.465612 0.437212 -0.465612
v -0.399961 -0.399961 -0.500000
v -0.399961 -0.500000 -0.399961
v -0.500000 -0.399961 -0.399961
v -0.399961 -0.438244 -0.492385
v -0.399961 -0.470699 -0.470699
v -0.438244 -0.399961 -0.492385
v -0.439203 -0.439203 -0.483194
v -0.437212 -0.465612 -0.465612
v -0.457718 -0.457718 -0.457718
v -0.438244 -0.492385 -0.399961
v -0.470699 -0.470699 -0.399961
v -0.399961 -0.492385 -0.438244
v -0.439203 -0.483194 -0.439203
v -0.465612 -0.465612 -0.437212
v -0.492385 -0.399961 -0.438244
v -0.470699 -0.399961 -0.470699
v -0.492385 -0.438244 -0.399961
v -0.483194 -0.439203 -0.439203
v -0.465612 -0.437212 -0.465612
v -0.500000 0.399961 0.399961
v -0.399961 0.399961 0.500000
v -0.399961 0.500000 0.399961
v -0.492385 0.399961 0.438244
v -0.470699 0.399961 0.470699
v -0.492385 0.438244 0.399961
v -0.483194 0.439203 0.439203
v -0.465612 0.437212 0.465612
v -0.457718 0.457718 0.457718
v -0.399961 0.438244 0.492385
v -0.399961 0.470699 0.470699
v -0.438244 0.399961 0.492385
v -0.439203 0.439203 0.483194
v -0.437212 0.465612 0.465612
v -0.438244 0.492385 0.399961
v -0.470699 0.470699 0.399961
v -0.399961 0.492385 0.438244
v -0.439203 0.483194 0.439203
v -0.465612 0.465612 0.437212
v -0.399961 -0.399961 0.500000
v -0.500000 -0.399961 0.399961
v -0.399961 -0.500000 0.399961
v -0.438244 -0.399961 0.492385
v -0.470699 -0.399961 0.470699
v -0.399961 -0.438244 0.492385
v -0.439203 -0.439203 0.483194
v -0.465612 -0.437212 0.465612
v -0.457718 -0.457718 0.457718
v -0.492385 -0.438244 0.399961
v -0.470699 -0.470699 0.399961
v -0.492385 -0.399961 0.438244
v -0.483194 -0.439203 0.439203
v -0.465612 -0.465612 0.437212
v -0.399961 -0.492385 0.438244
v -0.399961 -0.470699 0.470699
v -0.438244 -0.492385 0.399961
v -0.439203 -0.483194 0.439203
v -0.437212 -0.465612 0.465612
vt 0.150010 0.525010
vt 0.349990 0.525010
vt 0.349990 0.724990
vt 0.150010 0.724990
vt 0.650010 0.525010
vt 0.849990 0.525010
vt 0.849990 0.724990
vt 0.650010 0.724990
vt 0.400010 0.275010
vt 0.599990 0.275010
vt 0.599990 0.474990
vt 0.400010 0.474990
vt 0.400010 0.775010
vt 0.599990 0.775010
vt 0.599990 0.974990
vt 0.400010 0.974990
vt 0.400010 0.025010
vt 0.599990 0.025010
vt 0.599990 0.224990
vt 0.400010 0.224990
vt 0.400010 0.525010
vt 0.390439 0.525010
vt 0.390199 0.515199
vt 0.400010 0.515439
vt 0.375000 0.525010
vt 0.375000 0.515697
vt 0.390697 0.500000
vt 0.400010 0.500000
vt 0.375000 0.510570
vt 0.599990 0.265439
vt 0.609801 0.265199
vt 0.609561 0.275010
vt 0.599990 0.250000
vt 0.609303 0.250000
vt 0.625000 0.265697
vt 0.625000 0.275010
vt 0.614430 0.250000
vt 0.859561 0.724990
vt 0.859801 0.734801
vt 0.849990 0.734561
vt 0.875000 0.724990
vt 0.875000 0.734303
vt 0.859303 0.750000
vt 0.849990 0.750000
vt 0.875000 0.739430
vt 0.390439 0.275010
vt 0.390199 0.265199
vt 0.400010 0.265439
vt 0.375000 0.275010
vt 0.375000 0.265697
vt 0.390697 0.250000
vt 0.400010 0.250000
vt 0.375000 0.260570
vt 0.599990 0.015439
vt 0.609801 0.015199
vt 0.609561 0.025010
vt 0.599990 0.000000
vt 0.609303 0.000000
vt 0.625000 0.015697
vt 0.625000 0.025010
vt 0.614430 0.000000
vt 0.650010 0.734561
vt 0.640199 0.734801
vt 0.640439 0.724990
vt 0.650010 0.750000
vt 0.640697 0.750000
vt 0.625000 0.734303
vt 0.625000 0.724990
vt 0.635570 0.750000
vt 0.609561 0.474990
vt 0.609801 0.484801
vt 0.599990 0.484561
vt 0.625000 0.474990
vt 0.625000 0.484303
vt 0.609303 0.500000
vt 0.599990 0.500000
vt 0.625000 0.489430
vt 0.400010 0.724990
vt 0.400010 0.734561
vt 0.390199 0.734801
vt 0.390439 0.724990
vt 0.400010 0.750000
vt 0.390697 0.750000
vt 0.375000 0.734303
vt 0.375000 0.724990
vt 0.385570 0.750000
vt 0.349990 0.515439
vt 0.359801 0.515199
vt 0.359561 0.525010
vt 0.349990 0.500000
vt 0.359303 0.500000
vt 0.375000 0.515697
vt 0.375000 0.525010
vt 0.364430 0.500000
vt 0.140439 0.525010
vt 0.140199 0.515199
vt 0.150010 0.515439
vt 0.125000 0.525010
vt 0.125000 0.515697
vt 0.140697 0.500000
vt 0.150010 0.500000
vt 0.125000 0.510570
vt 0.609561 0.224990
vt 0.609801 0.234801
vt 0.599990 0.234561
vt 0.625000 0.224990
vt 0.625000 0.234303
vt 0.609303 0.250000
vt 0.599990 0.250000
vt 0.625000 0.239430
vt 0.400010 0.484561
vt 0.390199 0.484801
vt 0.390439 0.474990
vt 0.400010 0.500000
vt 0.390697 0.500000
vt 0.375000 0.484303
vt 0.375000 0.474990
vt 0.385570 0.500000
vt 0.609561 0.974990
vt 0.609801 0.984801
vt 0.599990 0.984561
vt 0.625000 0.974990
vt 0.625000 0.984303
vt 0.609303 1.000000
vt 0.599990 1.000000
vt 0.625000 0.989430
vt 0.599990 0.525010
vt 0.599990 0.515439
vt 0.609801 0.515199
vt 0.609561 0.525010
vt 0.599990 0.500000
vt 0.609303 0.500000
vt 0.625000 0.515697
vt 0.625000 0.525010
vt 0.614430 0.500000
vt 0.849990 0.515439
vt 0.859801 0.515199
vt 0.859561 0.525010
vt 0.849990 0.500000
vt 0.859303 0.500000
vt 0.875000 0.515697
vt 0.875000 0.525010
vt 0.864430 0.500000
vt 0.640439 0.525010
vt 0.640199 0.515199
vt 0.650010 0.515439
vt 0.625000 0.525010
vt 0.625000 0.515697
vt 0.640697 0.500000
vt 0.650010 0.500000
vt 0.625000 0.510570
vt 0.390439 0.025010
vt 0.390199 0.015199
vt 0.400010 0.015439
vt 0.375000 0.025010
vt 0.375000 0.015697
vt 0.390697 0.000000
vt 0.400010 0.000000
vt 0.375000 0.010570
vt 0.400010 0.984561
vt 0.390199 0.984801
vt 0.390439 0.974990
vt 0.400010 1.000000
vt 0.390697 1.000000
vt 0.375000 0.984303
vt 0.375000 0.974990
vt 0.385570 1.000000
vt 0.599990 0.765439
vt 0.609801 0.765199
vt 0.609561 0.775010
vt 0.599990 0.750000
vt 0.609303 0.750000
vt 0.625000 0.765697
vt 0.625000 0.775010
vt 0.614430 0.750000
vt 0.359561 0.724990
vt 0.359801 0.734801
vt 0.349990 0.734561
vt 0.375000 0.724990
vt 0.375000 0.734303
vt 0.359303 0.750000
vt 0.349990 0.750000
vt 0.375000 0.739430
vt 0.599990 0.724990
vt 0.609561 0.724990
vt 0.609801 0.734801
vt 0.599990 0.734561
vt 0.625000 0.724990
vt 0.625000 0.734303
vt 0.609303 0.750000
vt 0.599990 0.750000
vt 0.625000 0.739430
vt 0.150010 0.734561
vt 0.140199 0.734801
vt 0.140439 0.724990
vt 0.150010 0.750000
vt 0.140697 0.750000
vt 0.125000 0.734303
vt 0.125000 0.724990
vt 0.135570 0.750000
vt 0.390439 0.775010
vt 0.390199 0.765199
vt 0.400010 0.765439
vt 0.375000 0.775010
vt 0.375000 0.765697
vt 0.390697 0.750000
vt 0.400010 0.750000
vt 0.375000 0.760570
vt 0.400010 0.234561
vt 0.390199 0.234801
vt 0.390439 0.224990
vt 0.400010 0.250000
vt 0.390697 0.250000
vt 0.375000 0.234303
vt 0.375000 0.224990
vt 0.385570 0.250000
vn 0.1004 -0.1004 0.9899
vn 0.1004 0.1004 0.9899
vn -0.1004 0.1004 0.9899
vn -0.1004 -0.1004 0.9899
vn -0.1004 -0.1004 -0.9899
vn -0.1004 0.1004 -0.9899
vn 0.1004 0.1004 -0.9899
vn 0.1004 -0.1004 -0.9899
vn 0.9899 -0.1004 -0.1004
vn 0.9899 0.1004 -0.1004
vn 0.9899 0.1004 0.1004
vn 0.9899 -0.1004 0.1004
vn -0.9899 -0.1004 0.1004
vn -0.9899 0.1004 0.1004
vn -0.9899 0.1004 -0.1004
vn -0.9899 -0.1004 -0.1004
vn -0.1004 -0.9899 -0.1004
vn 0.1004 -0.9899 -0.1004
vn 0.1004 -0.9899 0.1004
vn -0.1004 -0.9899 0.1004
vn 0.1004 0.9899 -0.1004
vn 0.3792 0.9201 -0.0981
vn 0.3673 0.8545 -0.3673
vn 0.0981 0.9201 -0.3792
vn 0.7041 0.7041 -0.0919
vn 0.6663 0.6663 -0.3347
vn 0.3347 0.6663 -0.6663
vn 0.0919 0.7041 -0.7041
vn 0.5774 0.5774 -0.5774
vn 0.9201 0.0981 -0.3792
vn 0.8545 0.3673 -0.3673
vn 0.9201 0.3792 -0.0981
vn 0.7041 0.0919 -0.7041
vn 0.6663 0.3347 -0.6663
vn 0.0981 0.3792 -0.9201
vn 0.3673 0.3673 -0.8545
vn 0.3792 0.0981 -0.9201
vn 0.9201 -0.3792 -0.0981
vn 0.8545 -0.3673 -0.3673
vn 0.9201 -0.0981 -0.3792
vn 0.7041 -0.7041 -0.0919
vn 0.6663 -0.6663 -0.3347
vn 0.6663 -0.3347 -0.6663
vn 0.7041 -0.0919 -0.7041
vn 0.5774 -0.5774 -0.5773
vn 0.0981 -0.9201 -0.3792
vn 0.3673 -0.8545 -0.3673
vn 0.3792 -0.9201 -0.0981
vn 0.0919 -0.7041 -0.7041
vn 0.3347 -0.6663 -0.6663
vn 0.3792 -0.0981 -0.9201
vn 0.3673 -0.3673 -0.8545
vn 0.0981 -0.3792 -0.9201
vn 0.9201 0.3792 0.0981
vn 0.8545 0.3673 0.3673
vn 0.9201 0.0981 0.3792
vn 0.7041 0.7041 0.0919
vn 0.6663 0.6663 0.3347
vn 0.6663 0.3347 0.6663
vn 0.7041 0.0919 0.7041
vn 0.5774 0.5774 0.5773
vn 0.1004 0.9899 0.1004
vn 0.0981 0.9201 0.3792
vn 0.3673 0.8545 0.3673
vn 0.3792 0.9201 0.0981
vn 0.0919 0.7041 0.7041
vn 0.3347 0.6663 0.6663
vn 0.3792 0.0981 0.9201
vn 0.3673 0.3673 0.8545
vn 0.0981 0.3792 0.9201
vn 0.0981 -0.3792 0.9201
vn 0.3673 -0.3673 0.8545
vn 0.3792 -0.0981 0.9201
vn 0.0919 -0.7041 0.7041
vn 0.3347 -0.6663 0.6663
vn 0.6663 -0.3347 0.6663
vn 0.7041 -0.0919 0.7041
vn 0.5773 -0.5774 0.5774
vn 0.3792 -0.9201 0.0981
vn 0.3673 -0.8545 0.3673
vn 0.0981 -0.9201 0.3792
vn 0.7041 -0.7041 0.0919
vn 0.6663 -0.6663 0.3347
vn 0.9201 -0.0981 0.3792
vn 0.8545 -0.3673 0.3673
vn 0.9201 -0.3792 0.0981
vn -0.9201 0.3792 -0.0981
vn -0.8545 0.3673 -0.3673
vn -0.9201 0.0981 -0.3792
vn -0.7041 0.7041 -0.0919
vn -0.6663 0.6663 -0.3347
vn -0.6663 0.3347 -0.6663
vn -0.7041 0.0919 -0.7041
vn -0.5774 0.5774 -0.5773
vn -0.1004 0.9899 -0.1004
vn -0.0981 0.9201 -0.3792
vn -0.3673 0.8545 -0.3673
vn -0.3792 0.9201 -0.0981
vn -0.0919 0.7041 -0.7041
vn -0.3347 0.6663 -0.6663
vn -0.3792 0.0981 -0.9201
vn -0.3673 0.3673 -0.8545
vn -0.0981 0.3792 -0.9201
vn -0.0981 -0.3792 -0.9201
vn -0.3673 -0.3673 -0.8545
vn -0.3792 -0.0981 -0.9201
vn -0.0919 -0.7041 -0.7041
vn -0.3347 -0.6663 -0.6663
vn -0.6663 -0.3347 -0.6663
vn -0.7041 -0.0919 -0.7041
vn -0.5773 -0.5774 -0.5774
vn -0.3792 -0.9201 -0.0981
vn -0.3673 -0.8545 -0.3673
vn -0.0981 -0.9201 -0.3792
vn -0.7041 -0.7041 -0.0919
vn -0.6663 -0.6663 -0.3347
vn -0.9201 -0.0981 -0.3792
vn -0.8545 -0.3673 -0.3673
vn -0.9201 -0.3792 -0.0981
vn -0.9201 0.0981 0.3792
vn -0.8545 0.3673 0.3673
vn -0.9201 0.3792 0.0981
vn -0.7041 0.0919 0.7041
vn -0.6663 0.3347 0.6663
vn -0.6663 0.6663 0.3347
vn -0.7041 0.7041 0.0919
vn -0.5774 0.5774 0.5773
vn -0.0981 0.3792 0.9201
vn -0.3673 0.3673 0.8545
vn -0.3792 0.0981 0.9201
vn -0.0919 0.7041 0.7041
vn -0.3347 0.6663 0.6663
vn -0.1004 0.9899 0.1004
vn -0.3792 0.9201 0.0981
vn -0.3673 0.8545 0.3673
vn -0.0981 0.9201 0.3792
vn -0.3792 -0.0981 0.9201
vn -0.3673 -0.3673 0.8545
vn -0.0981 -0.3792 0.9201
vn -0.7041 -0.0919 0.7041
vn -0.6663 -0.3347 0.6663
vn -0.3347 -0.6663 0.6663
vn -0.0919 -0.7041 0.7041
vn -0.5774 -0.5774 0.5773
vn -0.9201 -0.3792 0.0981
vn -0.8545 -0.3673 0.3673
vn -0.9201 -0.0981 0.3792
vn -0.7041 -0.7041 0.0919
vn -0.6663 -0.6663 0.3347
vn -0.0981 -0.9201 0.3792
vn -0.3673 -0.8545 0.3673
vn -0.3792 -0.9201 0.0981
s 1
f 58/1/1 41/2/2 116/3/3 134/4/4
f 96/5/5 79/6/6 3/7/7 22/8/8
f 20/9/9 2/10/10 39/11/11 60/12/12
f 135/13/13 115/14/14 77/15/15 98/16/16
f 97/17/17 21/18/18 59/19/19 136/20/20
f 1/21/21 4/22/22 7/23/23 6/24/24
f 4/22/22 5/25/25 8/26/26 7/23/23
f 6/24/24 7/23/23 19/27/27 16/28/28
f 7/23/23 8/26/26 9/29/29 19/27/27
f 2/10/10 10/30/30 13/31/31 12/32/32
f 10/30/30 11/33/33 14/34/34 13/31/31
f 12/32/32 13/31/31 8/35/26 5/36/25
f 13/31/31 14/34/34 9/37/29 8/35/26
f 3/7/7 15/38/35 18/39/36 17/40/37
f 15/38/35 16/41/28 19/42/27 18/39/36
f 17/40/37 18/39/36 14/43/34 11/44/33
f 18/39/36 19/42/27 9/45/29 14/43/34
f 20/9/9 23/46/38 26/47/39 25/48/40
f 23/46/38 24/49/41 27/50/42 26/47/39
f 25/48/40 26/47/39 38/51/43 35/52/44
f 26/47/39 27/50/42 28/53/45 38/51/43
f 21/18/18 29/54/46 32/55/47 31/56/48
f 29/54/46 30/57/49 33/58/50 32/55/47
f 31/56/48 32/55/47 27/59/42 24/60/41
f 32/55/47 33/58/50 28/61/45 27/59/42
f 22/8/8 34/62/51 37/63/52 36/64/53
f 34/62/51 35/65/44 38/66/43 37/63/52
f 36/64/53 37/63/52 33/67/50 30/68/49
f 37/63/52 38/66/43 28/69/45 33/67/50
f 39/11/11 42/70/54 45/71/55 44/72/56
f 42/70/54 43/73/57 46/74/58 45/71/55
f 44/72/56 45/71/55 57/75/59 54/76/60
f 45/71/55 46/74/58 47/77/61 57/75/59
f 40/78/62 48/79/63 51/80/64 50/81/65
f 48/79/63 49/82/66 52/83/67 51/80/64
f 50/81/65 51/80/64 46/84/58 43/85/57
f 51/80/64 52/83/67 47/86/61 46/84/58
f 41/2/2 53/87/68 56/88/69 55/89/70
f 53/87/68 54/90/60 57/91/59 56/88/69
f 55/89/70 56/88/69 52/92/67 49/93/66
f 56/88/69 57/91/59 47/94/61 52/92/67
f 58/1/1 61/95/71 64/96/72 63/97/73
f 61/95/71 62/98/74 65/99/75 64/96/72
f 63/97/73 64/96/72 76/100/76 73/101/77
f 64/96/72 65/99/75 66/102/78 76/100/76
f 59/19/19 67/103/79 70/104/80 69/105/81
f 67/103/79 68/106/82 71/107/83 70/104/80
f 69/105/81 70/104/80 65/108/75 62/109/74
f 70/104/80 71/107/83 66/110/78 65/108/75
f 60/12/12 72/111/84 75/112/85 74/113/86
f 72/111/84 73/114/77 76/115/76 75/112/85
f 74/113/86 75/112/85 71/116/83 68/117/82
f 75/112/85 76/115/76 66/118/78 71/116/83
f 77/15/15 80/119/87 83/120/88 82/121/89
f 80/119/87 81/122/90 84/123/91 83/120/88
f 82/121/89 83/120/88 95/124/92 92/125/93
f 83/120/88 84/123/91 85/126/94 95/124/92
f 78/127/95 86/128/96 89/129/97 88/130/98
f 86/128/96 87/131/99 90/132/100 89/129/97
f 88/130/98 89/129/97 84/133/91 81/134/90
f 89/129/97 90/132/100 85/135/94 84/133/91
f 79/6/6 91/136/101 94/137/102 93/138/103
f 91/136/101 92/139/93 95/140/92 94/137/102
f 93/138/103 94/137/102 90/141/100 87/142/99
f 94/137/102 95/140/92 85/143/94 90/141/100
f 96/5/5 99/144/104 102/145/105 101/146/106
f 99/144/104 100/147/107 103/148/108 102/145/105
f 101/146/106 102/145/105 114/149/109 111/150/110
f 102/145/105 103/148/108 104/151/111 114/149/109
f 97/17/17 105/152/112 108/153/113 107/154/114
f 105/152/112 106/155/115 109/156/116 108/153/113
f 107/154/114 108/153/113 103/157/108 100/158/107
f 108/153/113 109/156/116 104/159/111 103/157/108
f 98/16/16 110/160/117 113/161/118 112/162/119
f 110/160/117 111/163/110 114/164/109 113/161/118
f 112/162/119 113/161/118 109/165/116 106/166/115
f 113/161/118 114/164/109 104/167/111 109/165/116
f 115/14/14 118/168/120 121/169/121 120/170/122
f 118/168/120 119/171/123 122/172/124 121/169/121
f 120/170/122 121/169/121 133/173/125 130/174/126
f 121/169/121 122/172/124 123/175/127 133/173/125
f 116/3/3 124/176/128 127/177/129 126/178/130
f 124/176/128 125/179/131 128/180/132 127/177/129
f 126/178/130 127/177/129 122/181/124 119/182/123
f 127/177/129 128/180/132 123/183/127 122/181/124
f 117/184/133 129/185/134 132/186/135 131/187/136
f 129/185/134 130/188/126 133/189/125 132/186/135
f 131/187/136 132/186/135 128/190/132 125/191/131
f 132/186/135 133/189/125 123/192/127 128/190/132
f 134/4/4 137/193/137 140/194/138 139/195/139
f 137/193/137 138/196/140 141/197/141 140/194/138
f 139/195/139 140/194/138 152/198/142 149/199/143
f 140/194/138 141/197/141 142/200/144 152/198/142
f 135/13/13 143/201/145 146/202/146 145/203/147
f 143/201/145 144/204/148 147/205/149 146/202/146
f 145/203/147 146/202/146 141/206/141 138/207/140
f 146/202/146 147/205/149 142/208/144 141/206/141
f 136/20/20 148/209/150 151/210/151 150/211/152
f 148/209/150 149/212/143 152/213/142 151/210/151
f 150/211/152 151/210/151 147/214/149 144/215/148
f 151/210/151 152/213/142 142/216/144 147/214/149
f 136/20/20 59/19/19 69/105/81 148/209/150
f 148/209/150 69/105/81 62/109/74 149/212/143
f 149/199/143 62/98/74 61/95/71 139/195/139
f 139/195/139 61/95/71 58/1/1 134/4/4
f 59/19/19 21/18/18 31/56/48 67/103/79
f 67/103/79 31/56/48 24/60/41 68/106/82
f 68/117/82 24/49/41 23/46/38 74/113/86
f 74/113/86 23/46/38 20/9/9 60/12/12
f 1/21/21 40/78/62 50/81/65 4/22/22
f 4/22/22 50/81/65 43/85/57 5/25/25
f 5/36/25 43/73/57 42/70/54 12/32/32
f 12/32/32 42/70/54 39/11/11 2/10/10
f 21/18/18 97/17/17 107/154/114 29/54/46
f 29/54/46 107/154/114 100/158/107 30/57/49
f 30/68/49 100/147/107 99/144/104 36/64/53
f 36/64/53 99/144/104 96/5/5 22/8/8
f 117/184/133 78/127/95 88/130/98 129/185/134
f 129/185/134 88/130/98 81/134/90 130/188/126
f 130/174/126 81/122/90 80/119/87 120/170/122
f 120/170/122 80/119/87 77/15/15 115/14/14
f 97/17/17 136/20/20 150/211/152 105/152/112
f 105/152/112 150/211/152 144/215/148 106/155/115
f 106/166/115 144/204/148 143/201/145 112/162/119
f 112/162/119 143/201/145 135/13/13 98/16/16
f 40/78/62 117/184/133 131/187/136 48/79/63
f 48/79/63 131/187/136 125/191/131 49/82/66
f 49/93/66 125/179/131 124/176/128 55/89/70
f 55/89/70 124/176/128 116/3/3 41/2/2
f 41/2/2 58/1/1 63/97/73 53/87/68
f 53/87/68 63/97/73 73/101/77 54/90/60
f 54/76/60 73/114/77 72/111/84 44/72/56
f 44/72/56 72/111/84 60/12/12 39/11/11
f 2/10/10 20/9/9 25/48/40 10/30/30
f 10/30/30 25/48/40 35/52/44 11/33/33
f 11/44/33 35/65/44 34/62/51 17/40/37
f 17/40/37 34/62/51 22/8/8 3/7/7
f 134/4/4 116/3/3 126/178/130 137/193/137
f 137/193/137 126/178/130 119/182/123 138/196/140
f 138/207/140 119/171/123 118/168/120 145/203/147
f 145/203/147 118/168/120 115/14/14 135/13/13
f 78/127/95 1/21/21 6/24/24 86/128/96
f 86/128/96 6/24/24 16/28/28 87/131/99
f 87/142/99 16/41/28 15/38/35 93/138/103
f 93/138/103 15/38/35 3/7/7 79/6/6
f 79/6/6 96/5/5 101/146/106 91/136/101
f 91/136/101 101/146/106 111/150/110 92/139/93
f 92/125/93 111/163/110 110/160/117 82/121/89
f 82/121/89 110/160/117 98/16/16 77/15/15
f 1/21/21 78/127/95 117/184/133 40/78/62
`;
type GeomRecord = Record<SomaesqueGeometry, THREE.BufferGeometry>;
export default class GeometryManager {
@@ -108,12 +785,8 @@ export default class GeometryManager {
};
const load = (resolve: (geom: THREE.BufferGeometry) => any, reject: (err: string) => any) => {
const loader = new OBJLoader();
loader.load(
`${this.root}${id}.obj`,
obj => onLoaded(obj, resolve),
() => {},
(err) => reject(`Error loading OBJ file: ${err}`),
);
const result = loader.parse(OBJ);
onLoaded(result, resolve);
};
return new Promise(load);
}

View File

@@ -39,7 +39,7 @@ export default class PolycubeScene {
this.cubeScene.rotateX(Math.PI/4);
this.cubeScene.rotateY(Math.PI/4);
this.controls = new RotationControl(this.cubeScene, this.polycubeMeshes, this.camera, this.canvas);
this.geomManager = await new GeometryManager('../resources/', () => {
this.geomManager = await new GeometryManager('./', () => {
requestAnimationFrame((timestamp) => this.render(timestamp));
});
PolycubeMesh.setManager(this.geomManager);

25
src/utils.ts Normal file
View File

@@ -0,0 +1,25 @@
function hslToRgb(hslStr: string): string {
const opt = new Option();
opt.style.color = hslStr;
return opt.style.color;
}
function rgbToHex(rgbStr: string): string {
const sep = rgbStr.indexOf(",") > -1 ? "," : " ";
const rgb = rgbStr.substr(4).split(")")[0].split(sep);
const r = (+rgb[0]).toString(16).padStart(2, "0");
const g = (+rgb[1]).toString(16).padStart(2, "0");
const b = (+rgb[2]).toString(16).padStart(2, "0");
return "#" + r + g + b;
}
export function colorFromIndex(index: number): string {
const colorWheelCycle = Math.floor(index / 6);
const darknessCycle = Math.floor(index / 12);
const spacing = (360 / 6);
const offset = colorWheelCycle === 0 ? 0 : spacing / (colorWheelCycle + 2);
let hue = spacing * (index % 6) + offset;
const saturation = 100;
const lightness = 1 / (2 + darknessCycle) * 100;
return rgbToHex(hslToRgb(`hsl(${hue},${saturation}%,${Math.round(lightness)}%)`));
}