From c415641d399a6cd6c9b004f4c5d238df9fd8450e Mon Sep 17 00:00:00 2001 From: Daniel Ledda Date: Mon, 5 Jul 2021 19:21:43 +0200 Subject: [PATCH] reset files to before desktop config, added more ui cleanup, store classes, examples --- .idea/workspace.xml | 66 +- package.json | 9 +- public/favicon.png | Bin 3127 -> 58376 bytes public/index.html | 3 +- public/main.wasm | Bin 0 -> 12460 bytes public/worker.js | 525 ++++++++++++++++ src/desktop/main.js | 1 - src/desktop/preload.js | 12 - src/main.ts | 3 +- src/solve.ts | 128 ++++ src/store.ts | 337 +++------- src/stores/DimStore.ts | 34 + src/stores/PolycubeStore.ts | 122 ++++ src/ui/App.svelte | 7 +- src/ui/CubeInput.svelte | 9 +- src/ui/CubeInputSet.svelte | 47 ++ src/ui/ExamplesList.svelte | 35 ++ src/ui/IncDecNum.svelte | 6 +- src/ui/InputParameters.svelte | 55 ++ src/ui/List.svelte | 42 ++ src/ui/Sidebar.svelte | 140 +---- src/ui/SolutionList.svelte | 42 +- src/ui/SolutionViewer.svelte | 34 +- src/ui/SolveButton.svelte | 54 ++ src/ui/{Interactor.svelte => Stage.svelte} | 59 +- src/ui/Tabs.svelte | 44 ++ src/ui/threedee/GeometryManager.ts | 685 ++++++++++++++++++++- src/ui/threedee/PolycubeScene.ts | 2 +- src/utils.ts | 25 + 29 files changed, 2008 insertions(+), 518 deletions(-) create mode 100644 public/main.wasm create mode 100644 public/worker.js create mode 100644 src/solve.ts create mode 100644 src/stores/DimStore.ts create mode 100644 src/stores/PolycubeStore.ts create mode 100644 src/ui/CubeInputSet.svelte create mode 100644 src/ui/ExamplesList.svelte create mode 100644 src/ui/InputParameters.svelte create mode 100644 src/ui/List.svelte create mode 100644 src/ui/SolveButton.svelte rename src/ui/{Interactor.svelte => Stage.svelte} (52%) create mode 100644 src/ui/Tabs.svelte create mode 100644 src/utils.ts diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 253ed73..7caad99 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -26,14 +26,33 @@ - + + + + + + + + + + + + + + + + - + + + + + @@ -74,7 +100,7 @@ - + @@ -90,18 +116,18 @@ - - - - + + + + - - - + + - + + @@ -147,7 +173,9 @@ - + + + @@ -234,18 +262,20 @@ - - + + + - - + + + \ No newline at end of file diff --git a/package.json b/package.json index 0eb70c8..be14ae9 100644 --- a/package.json +++ b/package.json @@ -46,10 +46,11 @@ "win": { "target": ["portable"] }, - "files": [ - "**/*", - "./public/**", - "./src/desktop${/*}" + "linux": { + "target": ["AppImage"] + }, + "extraResources": [ + "public/solver" ], "icon": "./public/resources/soma_icon.png", "directories": { diff --git a/public/favicon.png b/public/favicon.png index 7e6f5eb5a2f1f1c882d265cf479de25caa925645..c8b9aba92974159f18c189e16ef658a4d3eac182 100644 GIT binary patch literal 58376 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%0A=g6++XE9}LuB~1M@TsvI&7Yl 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 z - + diff --git a/public/main.wasm b/public/main.wasm new file mode 100644 index 0000000000000000000000000000000000000000..822c6965fa96b74fccb1001bec9f39ff4402a11d GIT binary patch literal 12460 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 WYd23eav|%1`}@1UKl{Q9FZ>tE(wn~k literal 0 HcmV?d00001 diff --git a/public/worker.js b/public/worker.js new file mode 100644 index 0000000..0ee7003 --- /dev/null +++ b/public/worker.js @@ -0,0 +1,525 @@ +// asbind/assemblyscript loader +var t="0.7.0";const e="undefined"!=typeof BigUint64Array,r=Symbol(),n=new TextDecoder("utf-16le");function s(t, e){const r=new Uint32Array(t)[e+-4>>>2]>>>1,s=new Uint16Array(t,e,r);return r<=32?String.fromCharCode.apply(String,s):n.decode(s)}function i(t){const e={};function r(t, e){return t?s(t.buffer,e):""}const n=t.env=t.env||{};return n.abort=n.abort||function(t, s, i, o){const a=e.memory||n.memory;throw Error(`abort: ${r(a,t)} at ${r(a,s)}:${i}:${o}`)},n.trace=n.trace||function(t, s, ...i){const o=e.memory||n.memory;console.log(`trace: ${r(o,t)}${s?" ":""}${i.slice(0,s).join(", ")}`)},n.seed=n.seed||Date.now,t.Math=t.Math||Math,t.Date=t.Date||Date,e}const o=function(){throw Error("Operation requires compiling with --exportRuntime")};function a(t, r){const n=r.exports,i=n.memory,a=n.table,c=n.__new||o,u=n.__pin||o,y=n.__unpin||o,l=n.__collect||o,p=n.__rtti_base,d=p?function(t){return t[p>>>2]}:o;function b(t){const e=function(t){const e=new Uint32Array(i.buffer);if((t>>>=0)>=d(e))throw Error(`invalid id: ${t}`);return e[(p+4>>>2)+2*t]}(t);if(!(7&e))throw Error(`not an array: ${t}, flags=${e}`);return e}function h(t){const e=new Uint32Array(i.buffer);if((t>>>=0)>=d(e))throw Error(`invalid id: ${t}`);return e[(p+4>>>2)+2*t+1]}function m(t){return 31-Math.clz32(t>>>6&31)}function g(t, e, r){const n=i.buffer;if(r)switch(t){case 2:return new Float32Array(n);case 3:return new Float64Array(n)}else switch(t){case 0:return new(e?Int8Array:Uint8Array)(n);case 1:return new(e?Int16Array:Uint16Array)(n);case 2:return new(e?Int32Array:Uint32Array)(n);case 3:return new(e?BigInt64Array:BigUint64Array)(n)}throw Error(`unsupported align: ${t}`)}function A(t){const e=new Uint32Array(i.buffer),r=b(e[t+-8>>>2]),n=m(r);let s=4&r?t:e[t+4>>>2];const o=2&r?e[t+12>>>2]:e[s+-4>>>2]>>>n;return g(n,2048&r,4096&r).subarray(s>>>=n,s+o)}function w(t, e, r){return new t(_(t,e,r))}function _(t, e, r){const n=i.buffer,s=new Uint32Array(n),o=s[r+4>>>2];return new t(n,o,s[o+-4>>>2]>>>e)}function T(e, r, n){t[`__get${r}`]=w.bind(null,e,n),t[`__get${r}View`]=_.bind(null,e,n)}return t.__new=c,t.__pin=u,t.__unpin=y,t.__collect=l,t.__newString=function(t){if(null==t)return 0;const e=t.length,r=c(e<<1,1),n=new Uint16Array(i.buffer);for(var s=0,o=r>>>1; s>>2])throw Error(`not a string: ${t}`);return s(e,t)},t.__newArray=function(t, e){const r=b(t),n=m(r),s=e.length,o=c(s<>>2]=o,l[e+4>>>2]=o,l[e+8>>>2]=s<>>2]=s),a=e}const l=g(n,2048&r,4096&r);if(16384&r)for(let t=0; t>>n)+t]=r}else l.set(e,o>>>n);return a},t.__getArrayView=A,t.__getArray=function(t){const e=A(t),r=e.length,n=new Array(r);for(let t=0; t>>2];return e.slice(t,t+r)},[Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array].forEach((t=>{T(t,t.name,31-Math.clz32(t.BYTES_PER_ELEMENT))})),e&&[BigUint64Array,BigInt64Array].forEach((t=>{T(t,t.name.slice(3),3)})),t.__instanceof=function(t, e){const r=new Uint32Array(i.buffer);let n=r[t+-8>>>2];if(n<=d(r))do{if(n==e)return!0;n=h(n)}while(n);return!1},t.memory=t.memory||i,t.table=t.table||a,f(n,t)}function c(t){return"undefined"!=typeof Response&&t instanceof Response}function u(t){return t instanceof WebAssembly.Module}async function y(t, e={}){if(c(t=await t))return l(t,e);const r=u(t)?t:await WebAssembly.compile(t),n=i(e),s=await WebAssembly.instantiate(r,e);return{module:r,instance:s,exports:a(n,s)}}async function l(t, e={}){if(!WebAssembly.instantiateStreaming)return y(c(t=await t)?t.arrayBuffer():t,e);const r=i(e),n=await WebAssembly.instantiateStreaming(t,e),s=a(r,n.instance);return{...n,exports:s}}function f(t, e={}){const n=t.__argumentsLength? e=>{t.__argumentsLength.value=e}:t.__setArgumentsLength||t.__setargc||(()=>{});for(let s in t){if(!Object.prototype.hasOwnProperty.call(t,s))continue;const i=t[s];let o=s.split("."),a=e;for(; o.length>1;){let t=o.shift();Object.prototype.hasOwnProperty.call(a,t)||(a[t]={}),a=a[t]}let c=o[0],u=c.indexOf("#");if(u>=0){const e=c.substring(0,u),o=a[e];if(void 0===o||!o.prototype){const t=function(...e){return t.wrap(t.prototype.constructor(0,...e))};t.prototype={valueOf(){return this[r]}},t.wrap=function(e){return Object.create(t.prototype,{[r]:{value:e,writable:!1}})},o&&Object.getOwnPropertyNames(o).forEach((e=>Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(o,e)))),a[e]=t}if(c=c.substring(u+1),a=a[e].prototype,/^(get|set):/.test(c)){if(!Object.prototype.hasOwnProperty.call(a,c=c.substring(4))){let e=t[s.replace("set:","get:")],n=t[s.replace("get:","set:")];Object.defineProperty(a,c,{get(){return e(this[r])},set(t){n(this[r],t)},enumerable:!0})}}else"constructor"===c?(a[c]=(...t)=>(n(t.length),i(...t))).original=i:(a[c]=function(...t){return n(t.length),i(this[r],...t)}).original=i}else/^(get|set):/.test(c)?Object.prototype.hasOwnProperty.call(a,c=c.substring(4))||Object.defineProperty(a,c,{get:t[s.replace("set:","get:")],set:t[s.replace("get:","set:")],enumerable:!0}):"function"==typeof i&&i!==n?(a[c]=(...t)=>(n(t.length),i(...t))).original=i:a[c]=i}return e}var p={instantiate:y,instantiateSync:function(t, e={}){const r=u(t)?t:new WebAssembly.Module(t),n=i(e),s=new WebAssembly.Instance(r,e);return{module:r,instance:s,exports:a(n,s)}},instantiateStreaming:l,demangle:f};function d(t, e, r){return e}function b(t, e, r){return t.exports.__getArrayBuffer(e)}function h(t, e, r){return t.exports[`__get${function(t){return t.startsWith("~lib/typedarray/")?((t=t.slice("~lib/typedarray/".length)).startsWith("Big")&&(t=t.slice(3)),t):t}(r)}View`](e)}function m(t, e, r){return t.exports.__newArray(t.getTypeId(r),e)}function g(t){if(!t.startsWith("~lib/array/Array"))throw Error(`${JSON.stringify(t)} is not an array type`);return t.slice("~lib/array/Array<".length,-1)}const A=new Map([["void",{ascToJs:d,jsToAsc:d}],[/^(i|u|f)(8|16|32|64)|[ui]size|bool|externref$/,{ascToJs:d,jsToAsc:d}],["~lib/string/String",{ascToJs:function(t, e, r){return t.exports.__getString(e)},jsToAsc:function(t, e, r){return t.exports.__newString(e)}}],["~lib/typedarray/Int8Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Int16Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Int32Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Uint8Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Uint16Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Uint32Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Int64Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Uint64Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Uint8ClampedArray",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Float32Array",{ascToJs:b,jsToAsc:m}],["~lib/typedarray/Float64Array",{ascToJs:b,jsToAsc:m}],["~lib/arraybuffer/ArrayBuffer",{ascToJs:b,jsToAsc:function(t, e, r){const n=t.exports.__new(e.byteLength,t.getTypeId(r));return new Uint8Array(t.exports.memory.buffer,n,e.byteLength).set(new Uint8Array(e)),n}}],[/^~lib\/array\/Array<.+>$/,{ascToJs:function(t, e, r){const n=g(r),s=_(n);return t.exports.__getArray(e).map((e=>s.ascToJs(t,e,n)))},jsToAsc:function(t, e, r){const n=g(r),s=_(n),i=e.map((e=>s.jsToAsc(t,e,n)));return t.exports.__newArray(t.getTypeId(r),i)}}]]),w=new Set;function _(t){for(const[e,r]of A)if("string"!=typeof e){if(e.test(t))return r}else if(e===t)return r;return w.has(t)||(console.warn(`No converter for ${JSON.stringify(t)}, using pass-through`),w.add(t)),{ascToJs:d,jsToAsc:d}}function T(t){var e;return null===(e=_(t))||void 0===e?void 0:e.ascToJs}function j(t){var e;return null===(e=_(t))||void 0===e?void 0:e.jsToAsc}function O(t, e, r){const n=r.parameters.map(T),s=j(r.returnType);return function(...i){if(i.length!=n.length)throw Error(`Expected ${n.length} arguments, got ${i.length}`);const o=i.map(((e, s)=>n[s](t,e,r.parameters[s]))),a=e(...o);return s(t,a,r.returnType)}}function U(t, e, r){const n=r.parameters.map(j),s=T(r.returnType);return(...i)=>{if(i.length!=n.length)throw Error(`Expected ${n.length} arguments, got ${i.length}`);const o=i.map(((e, s)=>n[s](t,e,r.parameters[s]))),a=e(...o);return s(t,a,r.returnType)}}function x(t, {depth:e=Number.POSITIVE_INFINITY}={}){return e<=0||!t||"object"!=typeof t?t:Object.fromEntries(Object.entries(t).map((([t,r])=>[t,x(r,{depth:e-1})])))}function E(t){const e=WebAssembly.Module.customSections(t,"as-bind_bindings"),r=new TextDecoder("utf8").decode(new Uint8Array(e[0]));try{return JSON.parse(r)}catch(t){throw Error(`Couldn’t decode type descriptor: ${t.message}`)}}class S{constructor(){this.unboundExports={},this.exports={},this.importObject={}}getTypeId(t){if(t in this.typeDescriptor.typeIds)return this.typeDescriptor.typeIds[t].id;throw Error(`Unknown type ${JSON.stringify(t)}`)}getTypeSize(t){if(t in this.typeDescriptor.typeIds)return this.typeDescriptor.typeIds[t].byteSize;throw Error(`Unknown type ${JSON.stringify(t)}`)}_validate(){if(!WebAssembly.Module.exports(this.module).find((t=>"__new"===t.name)))throw Error("The AssemblyScript wasm module was not built with --exportRuntime, which is required.");if(1!==WebAssembly.Module.customSections(this.module,"as-bind_bindings").length)throw new Error("The AssemblyScript wasm module was not built with the as-bind transform.")}async _instantiate(t, e){this.module=await async function(t){if(t=await Promise.resolve(t),"undefined"!=typeof Response&&t instanceof Response){if(WebAssembly.compileStreaming)return WebAssembly.compileStreaming(t);t=await t.arrayBuffer()}return WebAssembly.compile(t)}(t),this._validate(),this.typeDescriptor=E(this.module),this._instantiateBindImportFunctions(e),this.loadedModule=await async function(t, e){return p.instantiate(t,e)}(this.module,this.importObject),this._instantiateBindUnboundExports()}_instantiateSync(t, e){this.module=new WebAssembly.Module(t),this._validate(),this.typeDescriptor=E(this.module),this._instantiateBindImportFunctions(e),this.loadedModule=function(t, e){return p.instantiateSync(t,e)}(this.module,this.importObject),this._instantiateBindUnboundExports()}_instantiateBindImportFunctions(t){this.importObject=x(t,{depth:2});for(const[e,r]of Object.entries(this.typeDescriptor.importedFunctions))for(const[n,s]of Object.entries(r))this.importObject[e][`__asbind_unbound_${n}`]=t[e][n],this.importObject[e][n]=O(this,t[e][n],s)}_instantiateBindUnboundExports(){const t=this.loadedModule.exports;this.exports=x(t,{depth:1});for(const[e,r]of Object.entries(this.typeDescriptor.exportedFunctions))this.exports[e]=U(this,t[e],r)}}async function I(t, e){let r=new S;return await r._instantiate(t,e),r}function $(t, e){let r=new S;return r._instantiateSync(t,e),r}; +const instantiate = I; + +// js solver +async function solveFnJs(polycubes, dimX, dimY, dimZ) { + const solver = new SomaSolver(dimX, dimY, dimZ); + const voxelSpaces = new Array(); + for (let i = 0; i < polycubes.length; i++) { + voxelSpaces.push(new VoxelSpaceBoolean({ + id: i, + dims: [dimX, dimY, dimZ], + space: polycubes[i], + cullEmpty: true + })); + } + await solver.solve(voxelSpaces); + return solver.getSolutions(); +} +class VoxelSpaceBoolean { + constructor(options) { + this.length = options.dims[0] * options.dims[1] * options.dims[2]; + if (!options.space) { + options.space = new Array(options.dims[0] * options.dims[1] * options.dims[2]); + options.space.fill(false); + } + else if (!Array.isArray(options.space)) { + const newSpace = []; + for (let i = 0; i < this.length; i++) { + const mask = 1n << BigInt(i); + newSpace.push((options.space & mask) !== 0n); + } + options.space = newSpace; + } + this.id = options.id; + this.dims = options.dims; + this.space = options.space; + this.color = options.color ?? "red"; + if (options.cullEmpty !== false) { + this.cullEmptySpace(); + } + } + setColor(color) { + this.color = color; + } + getColor() { + return this.color; + } + binaryRep() { + return this.space.reduce((prev, curr) => prev + (curr ? "1" : "0"), ""); + } + getExtrema() { + const extrema = { + xMax: 0, + xMin: this.dims[0], + yMax: 0, + yMin: this.dims[1], + zMax: 0, + zMin: this.dims[2], + }; + this.forEachCell((val, x, y, z) => { + if (val) { + extrema.xMax = Math.max(extrema.xMax, x); + extrema.xMin = Math.min(extrema.xMin, x); + extrema.yMax = Math.max(extrema.yMax, y); + extrema.yMin = Math.min(extrema.yMin, y); + extrema.zMax = Math.max(extrema.zMax, z); + extrema.zMin = Math.min(extrema.zMin, z); + } + }); + return extrema; + } + cullEmptySpace() { + const extrema = this.getExtrema(); + const newX = extrema.xMax - extrema.xMin + 1; + const newY = extrema.yMax - extrema.yMin + 1; + const newZ = extrema.zMax - extrema.zMin + 1; + const newSpace = new Array(newX * newY * newZ); + newSpace.fill(false); + let index = 0; + for (let x = extrema.xMin; x <= extrema.xMax; x++) { + for (let y = extrema.yMin; y <= extrema.yMax; y++) { + for (let z = extrema.zMin; z <= extrema.zMax; z++) { + if (this.at(x, y, z)) { + newSpace[index] = true; + } + index++; + } + } + } + this.dims[0] = newX; + this.dims[1] = newY; + this.dims[2] = newZ; + this.space = newSpace; + } + forEachCell(cb) { + loopStart: for (let x = 0; x < this.dims[0]; x++) { + for (let y = 0; y < this.dims[1]; y++) { + for (let z = 0; z < this.dims[2]; z++) { + if (cb(this.at(x, y, z), x, y, z) === 0) { + break loopStart; + } + } + } + } + } + getId() { + return this.id; + } + print() { + let accum = ""; + console.log("---"); + for (let i = 0; i < this.dims[0]; i++) { + for (let j = 0; j < this.dims[1]; j++) { + for (let k = 0; k < this.dims[2]; k++) { + accum += this.at(i, j, k) ? '#' : 'O'; + } + console.log(accum); + accum = ""; + } + if (i !== this.dims[0] - 1) { + console.log("-"); + } + } + console.log("---"); + } + getUniqueRotations() { + const rotations = []; + const refSpace = this.clone(); + VoxelSpaceBoolean.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x')); + refSpace.rot90('y'); + VoxelSpaceBoolean.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x')); + refSpace.rot90('y'); + VoxelSpaceBoolean.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x')); + refSpace.rot90('y'); + VoxelSpaceBoolean.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x')); + refSpace.rot90('z'); + VoxelSpaceBoolean.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x')); + refSpace.rot90('z'); + refSpace.rot90('z'); + VoxelSpaceBoolean.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x')); + return rotations; + } + getAllRotations() { + const rotations = []; + const refSpace = this.clone(); + rotations.push(...refSpace.getAxisSpins('x')); + refSpace.rot90('y'); + rotations.push(...refSpace.getAxisSpins('x')); + refSpace.rot90('y'); + rotations.push(...refSpace.getAxisSpins('x')); + refSpace.rot90('y'); + rotations.push(...refSpace.getAxisSpins('x')); + refSpace.rot90('z'); + rotations.push(...refSpace.getAxisSpins('x')); + refSpace.rot90('z'); + refSpace.rot90('z'); + rotations.push(...refSpace.getAxisSpins('x')); + return rotations; + } + static pushNewUniqueSpaces(existingSpaces, newSpaces) { + for (const newSpace of newSpaces) { + let matchFound = false; + for (const existingSpace of existingSpaces) { + if (newSpace.matches(existingSpace)) { + matchFound = true; + break; + } + } + if (!matchFound) { + existingSpaces.push(newSpace); + } + } + } + getAllPositionsInPrism(cubeDimX, cubeDimY, cubeDimZ) { + const cubePositions = []; + if (this.dims[0] > cubeDimX || this.dims[1] > cubeDimY || this.dims[2] > cubeDimZ) { + return cubePositions; + } + for (let xOffset = 0; xOffset < (cubeDimX - this.dims[0] + 1); xOffset++) { + for (let yOffset = 0; yOffset < (cubeDimY - this.dims[1] + 1); yOffset++) { + for (let zOffset = 0; zOffset < (cubeDimZ - this.dims[2] + 1); zOffset++) { + const cubePos = new VoxelSpaceBoolean({ id: this.id, dims: [cubeDimX, cubeDimY, cubeDimZ], color: this.color, cullEmpty: false }); + this.forEachCell((val, x, y, z) => { + cubePos.set(xOffset + x, yOffset + y, zOffset + z, val); + }); + cubePositions.push(cubePos); + } + } + } + return cubePositions; + } + matches(space) { + const otherDims = space.getDims(); + for (let i = 0; i < this.dims.length; i++) { + if (otherDims[i] !== this.dims[i]) { + return false; + } + } + const otherRaw = space.getRaw(); + if (typeof otherRaw === "bigint") { + return space.binaryRep() === this.binaryRep(); + } + return this.space.reduce((prev, unit, i) => (unit === otherRaw[i]) && prev, true); + } + clone() { + return new VoxelSpaceBoolean({ id: this.id, dims: this.getDims(), space: this.getRaw(), color: this.getColor(), cullEmpty: false }); + } + getAxisSpins(axis) { + const rotations = [this.clone()]; + for (let i = 0; i < 3; i++) { + rotations.push(rotations[i].rotated90(axis)); + } + return rotations; + } + getDims() { + return this.dims.slice(); + } + getRaw() { + return this.space.slice(); + } + // [1, 0, 0] [x] [ x] + // [0, 0, -1] * [y] = [-z] + // [0, 1, 0] [z] [ y] + newIndexRotX(x, y, z) { + return this.dims[2] * this.dims[1] * x + this.dims[1] * (this.dims[2] - 1 - z) + y; + } + // [ 0, 0, 1] [x] [ z] + // [ 0, 1, 0] * [y] = [ y] + // [-1, 0, 0] [z] [-x] + newIndexRotY(x, y, z) { + return this.dims[1] * this.dims[0] * z + this.dims[0] * y + (this.dims[0] - 1 - x); + } + // [0, -1, 0] [x] [-y] + // [1, 0, 0] * [y] = [ x] + // [0, 0, 1] [z] [ z] + newIndexRotZ(x, y, z) { + return this.dims[0] * this.dims[2] * (this.dims[1] - 1 - y) + this.dims[2] * x + z; + } + at(x, y, z) { + return this.space[this.index(x, y, z)]; + } + index(x, y, z) { + return this.dims[1] * this.dims[2] * x + this.dims[2] * y + z; + } + toggle(x, y, z) { + const index = this.index(x, y, z); + this.space[index] = !this.space[index]; + } + set(x, y, z, val) { + this.space[this.index(x, y, z)] = val; + } + rotated90(dim) { + const newSpace = new Array(this.dims[0] * this.dims[1] * this.dims[2]); + newSpace.fill(false); + let newDims; + let rotIndex; + if (dim === 'x') { + newDims = [this.dims[0], this.dims[2], this.dims[1]]; + rotIndex = this.newIndexRotX.bind(this); + } + else if (dim === 'y') { + newDims = [this.dims[2], this.dims[1], this.dims[0]]; + rotIndex = this.newIndexRotY.bind(this); + } + else { + newDims = [this.dims[1], this.dims[0], this.dims[2]]; + rotIndex = this.newIndexRotZ.bind(this); + } + this.forEachCell((val, i, j, k) => { + if (val) { + newSpace[rotIndex(i, j, k)] = true; + } + }); + return new VoxelSpaceBoolean({ id: this.id, dims: newDims, space: newSpace, color: this.color, cullEmpty: false }); + } + rot90(dim) { + const rot = this.rotated90(dim); + this.space = rot.getRaw(); + this.dims = rot.getDims(); + } + plus(space) { + const newSpace = new Array(this.dims[0] * this.dims[1] * this.dims[2]); + newSpace.fill(false); + let clash = false; + space.forEachCell((val, x, y, z) => { + if (this.at(x, y, z) !== val) { + newSpace[this.index(x, y, z)] = true; + } + else { + if (val) { + clash = true; + } + } + }); + if (!clash) { + return new VoxelSpaceBoolean({ id: this.id, dims: this.getDims(), space: newSpace, color: this.color, cullEmpty: false }); + } + return null; + } + size() { + let size = 0; + this.forEachCell((val) => { + if (val) { + size++; + } + }); + return size; + } + getDirectNeighbourProfile(x, y, z) { + let result = 0; + if (x < this.dims[0] - 1 && this.at(x + 1, y, z)) { + result += 1; + } + if (y < this.dims[1] - 1 && this.at(x, y + 1, z)) { + result += 2; + } + if (z < this.dims[2] - 1 && this.at(x, y, z + 1)) { + result += 4; + } + if (x > 0 && this.at(x - 1, y, z)) { + result += 8; + } + if (y > 0 && this.at(x, y - 1, z)) { + result += 16; + } + if (z > 0 && this.at(x, y, z - 1)) { + result += 32; + } + return result; + } + getAllPermutationsInPrism(prismDimX, prismDimY, prismDimZ) { + const rotations = this.getUniqueRotations(); + let result = new Array(); + for (let i = 0; i < rotations.length; i++) { + result = result.concat(rotations[i].getAllPositionsInPrism(prismDimX, prismDimY, prismDimZ)); + } + return result; + } +} +class SomaSolver { + constructor(dimX, dimY, dimZ) { + this.visualiser = { async showSoln(soln) { }, async showSpace(cube) { } }; + this.solutions = new Array(); + this.iterations = 0; + this.dimX = dimX; + this.dimY = dimY; + this.dimZ = dimZ; + this.solutionCube = new VoxelSpaceBoolean({ id: 0, dims: [dimX, dimY, dimZ], cullEmpty: false }); + } + setDebug(visualiser) { + this.visualiser = visualiser; + } + async solve(polycubes) { + if (polycubes.length === 0) { + throw new Error("You must pass at least one polycube to solve the puzzle."); + } + this.solutions.splice(0, this.solutions.length); + const combosWithRots = new Array(); + for (let i = 1; i < polycubes.length; i++) { + const rots = polycubes[i].getAllPermutationsInPrism(this.dimX, this.dimY, this.dimZ); + combosWithRots.push(rots); + } + let combos = new Array(); + combos.push(polycubes[0].getAllPositionsInPrism(this.dimX, this.dimY, this.dimZ)); + combos = combos.concat(combosWithRots); + for (const combo of combos) { + for (const rot of combo) { + await this.visualiser.showSpace(rot); + } + } + await this.backtrackSolve(this.solutionCube, combos, new SomaSolution(this.dimX, this.dimY, this.dimZ)); + this.solutions = SomaSolution.filterUnique(this.solutions); + } + getSolutions() { + return this.solutions.slice(); + } + async backtrackSolve(workingSolution, polycubes, currentSoln, depth = 0) { + const nextCubeGroup = polycubes[0]; + for (let i = 0; i < nextCubeGroup.length; i++) { + const fusionAttempt = workingSolution.plus(nextCubeGroup[i]); + ++this.iterations; + if (fusionAttempt) { + const nextSoln = currentSoln.clone(); + nextSoln.addSpace(nextCubeGroup[i]); + await this.visualiser.showSoln(nextSoln); + if (polycubes.length === 1) { + this.solutions.push(nextSoln); + currentSoln = new SomaSolution(this.dimX, this.dimY, this.dimZ); + return; + } + else { + await this.backtrackSolve(fusionAttempt, polycubes.slice(1), nextSoln, depth + 1); + } + } + } + } +} +class SomaSolution { + constructor(dimX, dimY, dimZ) { + this.dimX = dimX; + this.dimY = dimY; + this.dimZ = dimZ; + this.solutionSpaces = []; + } + static filterUnique(solutions) { + if (solutions.length === 0) { + return []; + } + const uniqueSolns = [solutions[0]]; + for (const solution of solutions) { + let foundMatch = false; + for (const rotation of solution.getRotations()) { + let end = uniqueSolns.length; + for (let i = 0; i < end; i++) { + if (rotation.matches(uniqueSolns[i])) { + foundMatch = true; + } + } + } + if (!foundMatch) { + uniqueSolns.push(solution); + } + } + return uniqueSolns; + } + getRotations() { + if (this.solutionSpaces.length === 0) { + return []; + } + const result = []; + const allRots = this.solutionSpaces.map(space => space.getAllRotations()); + for (let i = 0; i < allRots[0].length; i++) { + const solnRot = new SomaSolution(this.dimX, this.dimY, this.dimZ); + allRots.forEach(rotGroup => solnRot.addSpace(rotGroup[i])); + result.push(solnRot); + } + return result; + } + matches(solution) { + for (let i = 0; i < this.solutionSpaces.length; i++) { + if (!this.solutionSpaces[i].matches(solution.solutionSpaces[i])) { + return false; + } + } + return true; + } + addSpace(space) { + this.solutionSpaces.push(space); + } + print() { + let accum = ""; + console.log("---"); + for (let x = 0; x < this.dimX; x++) { + for (let y = 0; y < this.dimY; y++) { + for (let z = 0; z < this.dimZ; z++) { + for (const space of this.solutionSpaces) { + if (space.at(x, y, z)) { + accum += space.getId(); + } + } + } + console.log(accum); + accum = ""; + } + if (x !== this.dimX - 1) { + console.log("-"); + } + } + console.log("---"); + } + at(x, y, z) { + for (const space of this.solutionSpaces) { + if (space.at(x, y, z)) { + return space.getId(); + } + } + return 0; + } + clone() { + const clone = new SomaSolution(this.dimX, this.dimY, this.dimZ); + clone.solutionSpaces = this.solutionSpaces.slice(); + return clone; + } + getDims() { + return [this.dimX, this.dimY, this.dimZ]; + } + forEachCell(cb) { + loopStart: for (let x = 0; x < this.dimX; x++) { + for (let y = 0; y < this.dimY; y++) { + for (let z = 0; z < this.dimZ; z++) { + cb(this.at(x, y, z), x, y, z); + } + } + } + } + getPieces() { + return this.solutionSpaces.slice(); + } +} + + + +// init wasm +let solveFnWasm; +async function load() { + const file = fetch("./main.wasm"); + const instance = await instantiate(file); + solveFnWasm = instance.exports.solve; +} +load(); + +// worker +self.addEventListener('message', async (event) => { + const { type, polycubes, dimX, dimY, dimZ } = event.data; + if (type === "wasm") { + const solutions = solveFnWasm(polycubes, dimX, dimY, dimZ); + self.postMessage(solutions.map(soln => soln.toString())); + } else if (type === "js") { + const solutions = await solveFnJs(polycubes, dimX, dimY, dimZ); + self.postMessage({solns: solutions.map(soln => soln.getPieces().map(piece => piece.binaryRep())), log: JSON.stringify(solutions)}); + } else { + self.postMessage({error: `Invalid solver type passed to worker: ${type}`}) + } +}); diff --git a/src/desktop/main.js b/src/desktop/main.js index 78576d7..a3f6697 100644 --- a/src/desktop/main.js +++ b/src/desktop/main.js @@ -9,7 +9,6 @@ function createWindow() { preload: path.join(__dirname, 'preload.js'), } }); - win.loadFile(path.join(__dirname, '../../public/index.html')); } diff --git a/src/desktop/preload.js b/src/desktop/preload.js index 190801e..e69de29 100644 --- a/src/desktop/preload.js +++ b/src/desktop/preload.js @@ -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]); - } -}); \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 39e64dd..e3041cf 100644 --- a/src/main.ts +++ b/src/main.ts @@ -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() } }); diff --git a/src/solve.ts b/src/solve.ts new file mode 100644 index 0000000..9de804a --- /dev/null +++ b/src/solve.ts @@ -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((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); +// } \ No newline at end of file diff --git a/src/store.ts b/src/store.ts index 5d3b675..c8cf7ae 100644 --- a/src/store.ts +++ b/src/store.ts @@ -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(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(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([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((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); -// } \ No newline at end of file +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"}, + ], + }, +]; \ No newline at end of file diff --git a/src/stores/DimStore.ts b/src/stores/DimStore.ts new file mode 100644 index 0000000..43bb665 --- /dev/null +++ b/src/stores/DimStore.ts @@ -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; + + 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); + } +} diff --git a/src/stores/PolycubeStore.ts b/src/stores/PolycubeStore.ts new file mode 100644 index 0000000..2bf8ba2 --- /dev/null +++ b/src/stores/PolycubeStore.ts @@ -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; + private dimX: DimStore; + private dimY: DimStore; + private dimZ: DimStore; + private selectedCube: Writable; + + constructor(dimX: DimStore, dimY: DimStore, dimZ: DimStore) { + this.selectedCube = writable(0); + this.dimX = dimX; + this.dimY = dimY; + this.dimZ = dimZ; + this.store = writable([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); + } +} \ No newline at end of file diff --git a/src/ui/App.svelte b/src/ui/App.svelte index c0c1d69..ab400d8 100644 --- a/src/ui/App.svelte +++ b/src/ui/App.svelte @@ -1,6 +1,9 @@
@@ -8,7 +11,7 @@
- +
diff --git a/src/ui/CubeInput.svelte b/src/ui/CubeInput.svelte index 3234213..dac918a 100644 --- a/src/ui/CubeInput.svelte +++ b/src/ui/CubeInput.svelte @@ -1,11 +1,12 @@ + +
+ {#each {length: numCubes} as _, cubeNo} +
+
+ +
+
+ {/each} +
+ + \ No newline at end of file diff --git a/src/ui/ExamplesList.svelte b/src/ui/ExamplesList.svelte new file mode 100644 index 0000000..8ca9ecb --- /dev/null +++ b/src/ui/ExamplesList.svelte @@ -0,0 +1,35 @@ + + +
+ hydrateExample(i)} + /> +
+ + \ No newline at end of file diff --git a/src/ui/IncDecNum.svelte b/src/ui/IncDecNum.svelte index 159749f..184dad2 100644 --- a/src/ui/IncDecNum.svelte +++ b/src/ui/IncDecNum.svelte @@ -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;
- {#if title} + {#if title !== ""}

{title}

{/if}
@@ -24,7 +24,7 @@ display: inline-block; } .title { - margin-bottom: 0; + margin: 0; } .val { font-weight: bold; diff --git a/src/ui/InputParameters.svelte b/src/ui/InputParameters.svelte new file mode 100644 index 0000000..5e044a5 --- /dev/null +++ b/src/ui/InputParameters.svelte @@ -0,0 +1,55 @@ + + +
+

Dimensions:

+ + + + {#if $totalVolume > 32} +

The total number of units exceeds 32. Attempting to solve puzzles with more than 32 units results in significantly slower computation time.

+ {/if} +
+ +
+

Cubes:

+ +
+ + \ No newline at end of file diff --git a/src/ui/List.svelte b/src/ui/List.svelte new file mode 100644 index 0000000..299bd20 --- /dev/null +++ b/src/ui/List.svelte @@ -0,0 +1,42 @@ + + +
    + {#if items.length === 0} +
  • {defaultText}
  • + {/if} + {#each items as item, i} +
  • onClick(i)}> + {item} +
  • + {/each} +
+ + \ No newline at end of file diff --git a/src/ui/Sidebar.svelte b/src/ui/Sidebar.svelte index c68f6eb..4c2fe85 100644 --- a/src/ui/Sidebar.svelte +++ b/src/ui/Sidebar.svelte @@ -1,122 +1,34 @@

Somaesque

-
-

Dimensions:

-
- - - - {#if $totalVolume > 32} -

The total number of units exceeds 32. Attempting to solve puzzles with more than 32 units results in significantly slower computation time.

- {/if} -
-
- -
-

Cubes:

-
- -
-
- -
- -
+ +

Solutions: {$solutions.length}

\ No newline at end of file + `Solution ${i + 1}`)} + defaultText="No solutions yet..." + onClick={(i) => selectSolution(i)} +/> \ No newline at end of file diff --git a/src/ui/SolutionViewer.svelte b/src/ui/SolutionViewer.svelte index 6a1c1db..653af53 100644 --- a/src/ui/SolutionViewer.svelte +++ b/src/ui/SolutionViewer.svelte @@ -1,13 +1,15 @@ -
+
{#if $activeSolution !== null}
@@ -45,13 +45,17 @@
\ No newline at end of file diff --git a/src/ui/SolveButton.svelte b/src/ui/SolveButton.svelte new file mode 100644 index 0000000..3a2f6bc --- /dev/null +++ b/src/ui/SolveButton.svelte @@ -0,0 +1,54 @@ + + + + + \ No newline at end of file diff --git a/src/ui/Interactor.svelte b/src/ui/Stage.svelte similarity index 52% rename from src/ui/Interactor.svelte rename to src/ui/Stage.svelte index 5ee7dab..ae227af 100644 --- a/src/ui/Interactor.svelte +++ b/src/ui/Stage.svelte @@ -1,8 +1,9 @@ + +
+ {#each Object.keys(tabs) as key} +
{key}
+ {/each} +
+
+ +
+ + \ No newline at end of file diff --git a/src/ui/threedee/GeometryManager.ts b/src/ui/threedee/GeometryManager.ts index cd7ae83..7f84149 100644 --- a/src/ui/threedee/GeometryManager.ts +++ b/src/ui/threedee/GeometryManager.ts @@ -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; 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); } diff --git a/src/ui/threedee/PolycubeScene.ts b/src/ui/threedee/PolycubeScene.ts index 990ce36..c196a41 100644 --- a/src/ui/threedee/PolycubeScene.ts +++ b/src/ui/threedee/PolycubeScene.ts @@ -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); diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..50041f7 --- /dev/null +++ b/src/utils.ts @@ -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)}%)`)); +} \ No newline at end of file