From 0d67c75d6475d9dea2fee223973654f4af547169 Mon Sep 17 00:00:00 2001 From: howard Date: Wed, 24 Mar 2021 10:49:40 -0700 Subject: [PATCH] not working --- dist/bundle.js | 2 +- dist/solver.wasm | Bin 135384 -> 138286 bytes src/Sketcher.js | 152 +++++++++++++++++++++-------------------------- wasm/solver.c | 65 ++++++++++++++------ 4 files changed, 116 insertions(+), 103 deletions(-) diff --git a/dist/bundle.js b/dist/bundle.js index 294303c..6f1b72e 100644 --- a/dist/bundle.js +++ b/dist/bundle.js @@ -2676,7 +2676,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \*************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Sketcher\": () => (/* binding */ Sketcher)\n/* harmony export */ });\n/* harmony import */ var three__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! three */ \"./node_modules/three/build/three.module.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/objects/Group.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/math/Vector3.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/helpers/PlaneHelper.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/math/Color.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/materials/Materials.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/core/Raycaster.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/math/Matrix4.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/math/Vector2.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/core/BufferGeometry.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/core/BufferAttribute.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/objects/Points.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/objects/Line.js\");\n\n\n\n\n\nconst factor = Math.tan(Math.PI / 3)\n\nfunction get2PtArc(p1, p2, divisions = 36) {\n\n const dx = p2[0] - p1[0]\n const dy = p2[1] - p1[1]\n const dist = Math.sqrt(dx ** 2 + dy ** 2)\n const midAngle = (Math.atan2(dy, dx) - Math.PI / 2) % (2 * Math.PI)\n let a1 = midAngle - Math.PI / 6\n let a2 = midAngle + Math.PI / 6\n\n a1 = a1 < 0 ? a1 + 2 * Math.PI : a1\n a2 = a2 < 0 ? a2 + 2 * Math.PI : a1\n const cx = (p1[0] + p2[0] - dy * factor) / 2\n const cy = (p1[1] + p2[1] + dx * factor) / 2\n\n const radius = dist\n const deltaAngle = Math.PI / 3\n let points = new Float32Array((divisions + 1) * 3)\n\n for (let d = 0; d <= divisions; d++) {\n const angle = a1 + (d / divisions) * deltaAngle;\n points[3 * d] = cx + radius * Math.cos(angle);\n points[3 * d + 1] = cy + radius * Math.sin(angle);\n }\n return [points, [cx, cy]];\n}\n\n\nfunction get3PtArc(p1, p2, c, divisions = 36) {\n\n const v1 = [p1[0] - c[0], p1[1] - c[1]]\n const v2 = [p2[0] - c[0], p2[1] - c[1]]\n\n let a1 = Math.atan2(v1[1], v1[0])\n let a2 = Math.atan2(v2[1], v2[0])\n\n const radius = Math.sqrt(v1[0] ** 2 + v1[1] ** 2)\n\n const deltaAngle = a2 - a1\n\n let points = new Float32Array((divisions + 1) * 3)\n\n for (let d = 0; d <= divisions; d++) {\n const angle = a1 + (d / divisions) * deltaAngle;\n points[3 * d] = c[0] + radius * Math.cos(angle);\n points[3 * d + 1] = c[1] + radius * Math.sin(angle);\n }\n return points;\n}\n\n\nclass Sketcher extends _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_0__.Group {\n constructor(camera, domElement, plane) {\n super()\n this.camera = camera;\n this.domElement = domElement;\n this.plane = plane;\n this.matrixAutoUpdate = false;\n this.sketchNormal = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_1__.Vector3(0, 0, 1)\n this.orientSketcher(plane)\n\n this.add(new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_2__.PlaneHelper(this.plane, 1, 0xffff00));\n\n this.colorPt = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_3__.Color('white')\n this.selected = new Set()\n\n\n this.lineMaterial = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__.LineBasicMaterial({\n linewidth: 3,\n color: 0x555555,\n })\n this.pointMaterial = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__.PointsMaterial({\n color: 0x555555,\n size: 3,\n })\n\n\n this.onKeyPress = this.onKeyPress.bind(this);\n\n this.onClick_1 = this.onClick_1.bind(this);\n this.onClick_2 = this.onClick_2.bind(this);\n this.beforeClick_2 = this.beforeClick_2.bind(this);\n this.beforeClick_3 = this.beforeClick_3.bind(this);\n this.onPick = this.onPick.bind(this);\n this.onHover = this.onHover.bind(this);\n this.onDrag = this.onDrag.bind(this);\n this.onRelease = this.onRelease.bind(this);\n\n this.raycaster = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_5__.Raycaster();\n this.raycaster.params.Line.threshold = 0.4;\n this.raycaster.params.Points.threshold = 0.2;\n\n\n window.addEventListener('keydown', this.onKeyPress)\n domElement.addEventListener('pointerdown', this.onPick)\n domElement.addEventListener('pointermove', this.onHover)\n\n\n this.mode = \"\"\n\n this.linkedObjs = new Map()\n this.l_id = 0;\n\n this.constraints = new Map()\n this.c_id = 0;\n\n this.objIdx = new Map()\n\n this.max_pts = 1000\n this.ptsBuf = new Float32Array(this.max_pts * 2)\n\n this.max_links = 1000\n this.linksBuf = new Int32Array(this.max_links * 5) // [0]:type, [1]:pt1, [2]:pt2, [3]:pt3, [4]:pt4\n\n this.max_constraints = 1000\n this.constraintsBuf = new Float32Array(this.max_constraints * 6) // [0]:type, [1]:pt1, [2]:pt2, [3]:lk1, [4]:lk2, [5]:val\n\n this.subsequent = false;\n this.ptsBufPt = 0;\n this.endBufPt = 0;\n\n this.linkNum = {\n 'line': 0,\n 'arc': 1\n }\n\n this.contraintNum = {\n 'coincident': 0,\n 'parallel': 1\n }\n }\n\n orientSketcher() {\n\n const theta = this.sketchNormal.angleTo(this.plane.normal)\n const axis = this.sketchNormal.clone().cross(this.plane.normal).normalize()\n const rot = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_6__.Matrix4().makeRotationAxis(axis, theta)\n const trans = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_6__.Matrix4().makeTranslation(0, 0, this.plane.constant)\n\n this.matrix = rot.multiply(trans) // world matrix will auto update in next render\n this.inverse = this.matrix.clone().invert()\n\n }\n\n onKeyPress(e) {\n switch (e.key) {\n case 'Escape':\n this.clear()\n this.mode = \"\"\n break;\n case 'l':\n this.domElement.addEventListener('pointerdown', this.onClick_1)\n this.mode = \"line\"\n break;\n case 'a':\n this.domElement.addEventListener('pointerdown', this.onClick_1)\n this.mode = \"arc\"\n break;\n case 'd':\n this.deleteSelected()\n break;\n case '=':\n this.plane.applyMatrix4(new three__WEBPACK_IMPORTED_MODULE_7__.Matrix4().makeRotationY(0.1))\n this.orientSketcher()\n this.dispatchEvent({ type: 'change' })\n break;\n case '-':\n this.plane.applyMatrix4(new three__WEBPACK_IMPORTED_MODULE_7__.Matrix4().makeRotationY(-0.1))\n this.orientSketcher()\n this.dispatchEvent({ type: 'change' })\n break;\n }\n }\n\n\n onHover(e) {\n if (this.mode || e.buttons) return\n\n if (this.hovered && !this.selected.has(this.hovered)) {\n this.hovered.material.color.set(0x555555)\n }\n\n this.raycaster.setFromCamera(\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_8__.Vector2(\n (e.clientX / window.innerWidth) * 2 - 1,\n - (e.clientY / window.innerHeight) * 2 + 1\n ),\n this.camera\n );\n\n const hoverPts = this.raycaster.intersectObjects(this.children)\n\n if (hoverPts.length) {\n let minDist = Infinity;\n let idx = 0\n for (let i = 0; i < hoverPts.length; i++) {\n if (hoverPts[i].distanceToRay && hoverPts[i].distanceToRay <= minDist) {\n minDist = hoverPts[i].distanceToRay\n idx = i\n }\n }\n\n hoverPts[idx].object.material.color.set(0xff0000)\n this.hovered = hoverPts[idx].object\n this.dispatchEvent({ type: 'change' })\n return\n }\n\n\n if (this.hovered) {\n this.hovered = null;\n this.dispatchEvent({ type: 'change' })\n }\n\n }\n\n\n\n onPick(e) {\n if (this.mode || e.buttons != 1) return\n\n if (this.hovered) {\n this.selected.add(this.hovered)\n if (this.hovered.type === \"Points\") {\n this.grabPtIdx = this.children.indexOf(\n this.hovered\n )\n this.domElement.addEventListener('pointermove', this.onDrag);\n this.domElement.addEventListener('pointerup', this.onRelease)\n }\n } else {\n for (let obj of this.selected) {\n obj.material.color.set(0x555555)\n }\n this.dispatchEvent({ type: 'change' })\n this.selected.clear()\n }\n }\n\n onDrag(e) {\n const mouseLoc = this.getLocation(e);\n this.children[this.grabPtIdx].geometry.attributes.position.set(mouseLoc);\n this.solve()\n this.dispatchEvent({ type: 'change' })\n }\n\n\n onRelease() {\n this.domElement.removeEventListener('pointermove', this.onDrag)\n this.domElement.removeEventListener('pointerup', this.onRelease)\n this.children[this.grabPtIdx].geometry.computeBoundingSphere()\n // this.grabbedObject = null\n }\n\n\n\n deleteSelected() {\n let minI = this.children.length;\n\n for (let obj of this.selected) {\n minI = Math.min(minI, this.delete(obj))\n\n }\n\n this.updateBuffer(minI)\n this.resetConstraints()\n\n this.selected.clear()\n this.dispatchEvent({ type: 'change' })\n }\n\n deleteConstraints(c_id) {\n for (let ob of this.constraints.get(c_id)[0]) {\n if (ob == -1) continue\n ob.constraints.delete(c_id)\n }\n this.constraints.delete(c_id)\n }\n\n resetConstraints() {\n let i = 0\n for (let [key,obj] of this.constraints) {\n this.constraintsBuf.set(\n [\n ...obj[0].map(ele => this.objIdx.get(ele.id) || -1),\n this.contraintNum[obj[4]], obj[5]\n ],\n (i) * 6\n )\n i++\n }\n\n i = 0;\n for (let [key,obj] of this.linkedObjs) {\n this.linksBuf.set(\n [\n ...obj[0].map(ele => this.objIdx.get(ele.id) || -1),\n this.linkNum[obj[4]]\n ],\n (i) * 5\n )\n i++\n }\n\n }\n\n delete(obj) {\n\n const link = this.linkedObjs.get(obj.l_id)\n\n let i = this.children.indexOf(link[0][0])\n if (i == -1) return Infinity\n\n for (let j = 0; j < link[0].length; j++) {\n const obj = this.children[i + j]\n obj.geometry.dispose()\n obj.material.dispose()\n\n for (let c_id of obj.constraints) {\n this.deleteConstraints(c_id)\n }\n }\n\n this.children.splice(i, link[0].length)\n\n this.linkedObjs.delete(obj.l_id)\n\n return i\n }\n\n updateBuffer(startingIdx) {\n for (let i = startingIdx; i < this.children.length; i++) {\n const obj = this.children[i]\n this.objIdx.set(obj.id, i)\n if (obj.type == \"Points\") {\n this.ptsBuf[2 * i] = obj.geometry.attributes.position.array[0]\n this.ptsBuf[2 * i + 1] = obj.geometry.attributes.position.array[1]\n }\n }\n }\n\n\n clear() {\n if (this.mode == \"\") return\n\n if (this.mode == \"line\") {\n this.domElement.removeEventListener('pointerdown', this.onClick_1)\n this.domElement.removeEventListener('pointermove', this.beforeClick_2);\n this.domElement.removeEventListener('pointerdown', this.onClick_2);\n\n this.delete(this.children[this.children.length - 1])\n\n this.dispatchEvent({ type: 'change' })\n this.subsequent = false\n }\n }\n\n getLocation(e) {\n this.raycaster.setFromCamera(\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_8__.Vector2(\n (e.clientX / window.innerWidth) * 2 - 1,\n - (e.clientY / window.innerHeight) * 2 + 1\n ),\n this.camera\n );\n // return this.worldToLocal(this.raycaster.ray.intersectPlane(this.plane)).toArray()\n return this.raycaster.ray.intersectPlane(this.plane).applyMatrix4(this.inverse).toArray()\n }\n\n onClick_1(e) {\n if (e.buttons !== 1) return\n const mouseLoc = this.getLocation(e);\n\n this.p1Geom = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_9__.BufferGeometry().setAttribute('position',\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_10__.BufferAttribute(new Float32Array(mouseLoc), 3)\n )\n this.p1 = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_11__.Points(this.p1Geom,\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__.PointsMaterial().copy(this.pointMaterial)\n );\n this.p1.matrixAutoUpdate = false;\n this.p1.constraints = new Set()\n\n this.p2Geom = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_9__.BufferGeometry().setAttribute('position',\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_10__.BufferAttribute(new Float32Array(3), 3)\n )\n this.p2 = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_11__.Points(\n this.p2Geom,\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__.PointsMaterial().copy(this.pointMaterial)\n );\n this.p2.matrixAutoUpdate = false;\n this.p2.constraints = new Set()\n\n let toPush;\n\n if (this.mode == \"line\") {\n this.lineGeom = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_9__.BufferGeometry().setAttribute('position',\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_10__.BufferAttribute(new Float32Array(6), 3)\n );\n this.lineGeom.attributes.position.set(mouseLoc)\n this.line = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_12__.Line(this.lineGeom,\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__.LineBasicMaterial().copy(this.lineMaterial)\n );\n this.line.constraints = new Set()\n this.line.frustumCulled = false;\n\n toPush = [this.p1, this.p2, this.line];\n\n if (this.subsequent) {\n\n this.constraintsBuf.set(\n [\n this.children.length - 2, this.children.length, -1, -1,\n this.contraintNum['coincident'], -1\n ],\n (this.constraints.size) * 6\n )\n\n this.constraints.set(this.c_id,\n [\n [this.children[this.children.length - 2], this.p1, -1, -1],\n 'coincident', -1\n ]\n )\n\n this.p1.constraints.add(this.c_id)\n this.children[this.children.length - 2].constraints.add(this.c_id)\n this.c_id += 1\n }\n\n } else if (this.mode == \"arc\") {\n\n this.arcGeom = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_9__.BufferGeometry().setAttribute('position',\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_10__.BufferAttribute(new Float32Array(3 * 37), 3)\n )\n\n this.arc = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_12__.Line(this.arcGeom,\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__.LineBasicMaterial().copy(this.lineMaterial)\n );\n this.arc.frustumCulled = false;\n\n this.p3Geom = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_9__.BufferGeometry().setAttribute('position',\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_10__.BufferAttribute(new Float32Array(3), 3)\n )\n this.p3 = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_11__.Points(this.p3Geom,\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__.PointsMaterial().copy(this.pointMaterial)\n );\n\n toPush = [this.p1, this.p2, this.p3, this.arc]\n }\n\n\n let ptr = this.children.length\n this.add(...toPush)\n this.updateBuffer(ptr)\n\n\n const link_entry = new Array(5).fill(-1)\n for (let i = ptr, j = 0; j < toPush.length - 1;) {\n link_entry[j++] = i++\n }\n link_entry[link_entry.length - 1] = this.linkNum[this.mode]\n\n this.linksBuf.set(\n link_entry,\n (this.linkedObjs.size) * 5\n )\n\n this.linkedObjs.set(this.l_id, [toPush, this.mode])\n\n for (let obj of toPush) {\n obj.l_id = this.l_id\n }\n this.l_id += 1\n\n\n\n this.domElement.removeEventListener('pointerdown', this.onClick_1)\n this.domElement.addEventListener('pointermove', this.beforeClick_2)\n this.domElement.addEventListener('pointerdown', this.onClick_2)\n }\n\n\n beforeClick_2(e) {\n const mouseLoc = this.getLocation(e);\n\n this.p2Geom.attributes.position.set(mouseLoc);\n this.p2Geom.attributes.position.needsUpdate = true;\n this.p2Geom.computeBoundingSphere()\n\n if (this.mode == \"line\") {\n this.lineGeom.attributes.position.set(mouseLoc, 3)\n this.lineGeom.attributes.position.needsUpdate = true;\n } else if (this.mode == 'arc') {\n const [points, center] = get2PtArc(\n this.p1Geom.attributes.position.array,\n this.p2Geom.attributes.position.array\n )\n this.arcGeom.attributes.position.set(\n points\n );\n this.arcGeom.attributes.position.needsUpdate = true;\n this.p3Geom.attributes.position.set(center);\n this.p3Geom.attributes.position.needsUpdate = true;\n this.p3Geom.computeBoundingSphere()\n\n }\n\n this.dispatchEvent({ type: 'change' })\n }\n\n onClick_2(e) {\n if (e.buttons !== 1) return;\n this.domElement.removeEventListener('pointermove', this.beforeClick_2);\n this.domElement.removeEventListener('pointerdown', this.onClick_2);\n if (this.mode == \"line\") {\n this.subsequent = true\n this.onClick_1(e)\n } else if (this.mode == \"arc\") {\n\n\n\n // this.domElement.addEventListener('pointermove', this.beforeClick_3)\n }\n }\n\n beforeClick_3(e) {\n const mouseLoc = this.getLocation(e);\n this.p3Geom.attributes.position.set(mouseLoc);\n this.p3Geom.attributes.position.needsUpdate = true;\n this.p3Geom.computeBoundingSphere()\n }\n\n\n solve() {\n\n\n for (let i = 0, p = 0; i < this.children.length; i++) {\n this.ptsBuf[p++] = this.children[i].geometry.attributes.position.array[0]\n this.ptsBuf[p++] = this.children[i].geometry.attributes.position.array[1]\n }\n\n buffer = Module._malloc(this.ptsBuf.length * this.ptsBuf.BYTES_PER_ELEMENT)\n Module.HEAPF32.set(this.ptsBuf, buffer >> 2)\n\n\n Module[\"_solver\"](this.children.length / 2, buffer)\n\n\n let ptr = buffer >> 2;\n\n\n for (let i = 0; i < this.linkedObjs.lengthxx; i += 1) {\n\n const pt1_pos = this.children[i >> 1].geometry.attributes.position;\n const pt2_pos = this.children[(i >> 1) + 1].geometry.attributes.position;\n const line_pos = this.linesArr[i >> 2].geometry.attributes.position;\n\n pt1_pos.array[0] = Module.HEAPF32[ptr]\n line_pos.array[0] = Module.HEAPF32[ptr++]\n\n pt1_pos.array[1] = Module.HEAPF32[ptr]\n line_pos.array[1] = Module.HEAPF32[ptr++]\n\n pt2_pos.array[0] = Module.HEAPF32[ptr]\n line_pos.array[3] = Module.HEAPF32[ptr++]\n\n pt2_pos.array[1] = Module.HEAPF32[ptr]\n line_pos.array[4] = Module.HEAPF32[ptr++]\n\n pt1_pos.needsUpdate = true;\n pt2_pos.needsUpdate = true;\n line_pos.needsUpdate = true;\n }\n\n this.dispatchEvent({ type: 'change' })\n\n Module._free(buffer)\n }\n\n}\n\n\n\n\n\n//# sourceURL=webpack:///./src/Sketcher.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Sketcher\": () => (/* binding */ Sketcher)\n/* harmony export */ });\n/* harmony import */ var three__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! three */ \"./node_modules/three/build/three.module.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/objects/Group.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/math/Vector3.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/helpers/PlaneHelper.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/math/Color.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/materials/Materials.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/core/Raycaster.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/math/Matrix4.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/math/Vector2.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/core/BufferGeometry.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/core/BufferAttribute.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/objects/Points.js\");\n/* harmony import */ var _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../node_modules/three/src/Three */ \"./node_modules/three/src/objects/Line.js\");\n\n\n\n\n\nconst factor = Math.tan(Math.PI / 3)\n\nfunction get2PtArc(p1, p2, divisions = 36) {\n\n const dx = p2[0] - p1[0]\n const dy = p2[1] - p1[1]\n const dist = Math.sqrt(dx ** 2 + dy ** 2)\n const midAngle = (Math.atan2(dy, dx) - Math.PI / 2) % (2 * Math.PI)\n let a1 = midAngle - Math.PI / 6\n let a2 = midAngle + Math.PI / 6\n\n a1 = a1 < 0 ? a1 + 2 * Math.PI : a1\n a2 = a2 < 0 ? a2 + 2 * Math.PI : a1\n const cx = (p1[0] + p2[0] - dy * factor) / 2\n const cy = (p1[1] + p2[1] + dx * factor) / 2\n\n const radius = dist\n const deltaAngle = Math.PI / 3\n let points = new Float32Array((divisions + 1) * 3)\n\n for (let d = 0; d <= divisions; d++) {\n const angle = a1 + (d / divisions) * deltaAngle;\n points[3 * d] = cx + radius * Math.cos(angle);\n points[3 * d + 1] = cy + radius * Math.sin(angle);\n }\n return [points, [cx, cy]];\n}\n\n\nfunction get3PtArc(p1, p2, c, divisions = 36) {\n\n const v1 = [p1[0] - c[0], p1[1] - c[1]]\n const v2 = [p2[0] - c[0], p2[1] - c[1]]\n\n let a1 = Math.atan2(v1[1], v1[0])\n let a2 = Math.atan2(v2[1], v2[0])\n\n const radius = Math.sqrt(v1[0] ** 2 + v1[1] ** 2)\n\n const deltaAngle = a2 - a1\n\n let points = new Float32Array((divisions + 1) * 3)\n\n for (let d = 0; d <= divisions; d++) {\n const angle = a1 + (d / divisions) * deltaAngle;\n points[3 * d] = c[0] + radius * Math.cos(angle);\n points[3 * d + 1] = c[1] + radius * Math.sin(angle);\n }\n return points;\n}\n\n\nclass Sketcher extends _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_0__.Group {\n constructor(camera, domElement, plane) {\n super()\n this.camera = camera;\n this.domElement = domElement;\n this.plane = plane;\n this.matrixAutoUpdate = false;\n this.sketchNormal = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_1__.Vector3(0, 0, 1)\n this.orientSketcher(plane)\n\n this.add(new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_2__.PlaneHelper(this.plane, 1, 0xffff00));\n\n this.colorPt = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_3__.Color('white')\n this.selected = new Set()\n\n this.geomGroup = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_0__.Group();\n scene.add(this.geomGroup);\n\n this.lineMaterial = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__.LineBasicMaterial({\n linewidth: 3,\n color: 0x555555,\n })\n this.pointMaterial = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__.PointsMaterial({\n color: 0x555555,\n size: 3,\n })\n\n\n this.onKeyPress = this.onKeyPress.bind(this);\n\n this.onClick_1 = this.onClick_1.bind(this);\n this.onClick_2 = this.onClick_2.bind(this);\n this.beforeClick_2 = this.beforeClick_2.bind(this);\n this.beforeClick_3 = this.beforeClick_3.bind(this);\n this.onPick = this.onPick.bind(this);\n this.onHover = this.onHover.bind(this);\n this.onDrag = this.onDrag.bind(this);\n this.onRelease = this.onRelease.bind(this);\n\n this.raycaster = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_5__.Raycaster();\n this.raycaster.params.Line.threshold = 0.4;\n this.raycaster.params.Points.threshold = 0.2;\n\n\n window.addEventListener('keydown', this.onKeyPress)\n domElement.addEventListener('pointerdown', this.onPick)\n domElement.addEventListener('pointermove', this.onHover)\n\n\n this.mode = \"\"\n\n this.linkedObjs = new Map()\n this.l_id = 0;\n\n this.constraints = new Map()\n this.c_id = 0;\n\n this.objIdx = new Map()\n\n this.max_pts = 1000\n this.ptsBuf = new Float32Array(this.max_pts * 2).fill(NaN)\n\n this.max_links = 1000\n this.linksBuf = new Float32Array(this.max_links * 5).fill(NaN) // [0]:type, [1]:pt1, [2]:pt2, [3]:pt3, [4]:pt4\n\n this.max_constraints = 1000\n this.constraintsBuf = new Float32Array(this.max_constraints * 6).fill(NaN) // [0]:type, [1]:val, [2]:pt1, [3]:pt2, [4]:lk1, [5]:lk2\n\n this.subsequent = false;\n this.ptsBufPt = 0;\n this.endBufPt = 0;\n\n this.linkNum = {\n 'line': 0,\n 'arc': 1\n }\n\n this.contraintNum = {\n 'coincident': 0,\n 'parallel': 1\n }\n }\n\n orientSketcher() {\n\n const theta = this.sketchNormal.angleTo(this.plane.normal)\n const axis = this.sketchNormal.clone().cross(this.plane.normal).normalize()\n const rot = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_6__.Matrix4().makeRotationAxis(axis, theta)\n const trans = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_6__.Matrix4().makeTranslation(0, 0, this.plane.constant)\n\n this.matrix = rot.multiply(trans) // world matrix will auto update in next render\n this.inverse = this.matrix.clone().invert()\n\n }\n\n onKeyPress(e) {\n switch (e.key) {\n case 'Escape':\n this.clear()\n this.mode = \"\"\n break;\n case 'l':\n this.domElement.addEventListener('pointerdown', this.onClick_1)\n this.mode = \"line\"\n break;\n case 'a':\n this.domElement.addEventListener('pointerdown', this.onClick_1)\n this.mode = \"arc\"\n break;\n case 'd':\n this.deleteSelected()\n break;\n case '=':\n this.plane.applyMatrix4(new three__WEBPACK_IMPORTED_MODULE_7__.Matrix4().makeRotationY(0.1))\n this.orientSketcher()\n this.dispatchEvent({ type: 'change' })\n break;\n case '-':\n this.plane.applyMatrix4(new three__WEBPACK_IMPORTED_MODULE_7__.Matrix4().makeRotationY(-0.1))\n this.orientSketcher()\n this.dispatchEvent({ type: 'change' })\n break;\n }\n }\n\n\n onHover(e) {\n if (this.mode || e.buttons) return\n\n if (this.hovered && !this.selected.has(this.hovered)) {\n this.hovered.material.color.set(0x555555)\n }\n\n this.raycaster.setFromCamera(\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_8__.Vector2(\n (e.clientX / window.innerWidth) * 2 - 1,\n - (e.clientY / window.innerHeight) * 2 + 1\n ),\n this.camera\n );\n\n const hoverPts = this.raycaster.intersectObjects(this.geomGroup.children)\n\n if (hoverPts.length) {\n let minDist = Infinity;\n let idx = 0\n for (let i = 0; i < hoverPts.length; i++) {\n if (hoverPts[i].distanceToRay && hoverPts[i].distanceToRay <= minDist) {\n minDist = hoverPts[i].distanceToRay\n idx = i\n }\n }\n\n hoverPts[idx].object.material.color.set(0xff0000)\n this.hovered = hoverPts[idx].object\n this.dispatchEvent({ type: 'change' })\n return\n }\n\n\n if (this.hovered) {\n this.hovered = null;\n this.dispatchEvent({ type: 'change' })\n }\n\n }\n\n\n\n onPick(e) {\n if (this.mode || e.buttons != 1) return\n\n if (this.hovered) {\n this.selected.add(this.hovered)\n if (this.hovered.type === \"Points\") {\n this.grabPtIdx = this.geomGroup.children.indexOf(\n this.hovered\n )\n this.domElement.addEventListener('pointermove', this.onDrag);\n this.domElement.addEventListener('pointerup', this.onRelease)\n }\n } else {\n for (let obj of this.selected) {\n obj.material.color.set(0x555555)\n }\n this.dispatchEvent({ type: 'change' })\n this.selected.clear()\n }\n }\n\n onDrag(e) {\n const mouseLoc = this.getLocation(e);\n this.geomGroup.children[this.grabPtIdx].geometry.attributes.position.set(mouseLoc);\n this.solve()\n this.dispatchEvent({ type: 'change' })\n }\n\n\n onRelease() {\n this.domElement.removeEventListener('pointermove', this.onDrag)\n this.domElement.removeEventListener('pointerup', this.onRelease)\n this.geomGroup.children[this.grabPtIdx].geometry.computeBoundingSphere()\n // this.grabbedObject = null\n }\n\n\n\n deleteSelected() {\n let minI = this.geomGroup.children.length;\n\n for (let obj of this.selected) {\n minI = Math.min(minI, this.delete(obj))\n\n }\n\n this.updatePointsBuffer(minI)\n this.updateOtherBuffers()\n\n this.selected.clear()\n this.dispatchEvent({ type: 'change' })\n }\n\n deleteConstraints(c_id) {\n for (let ob of this.constraints.get(c_id)[0]) {\n if (ob == -1) continue\n ob.constraints.delete(c_id)\n }\n this.constraints.delete(c_id)\n }\n\n updateOtherBuffers() {\n let i = 0\n for (let [key, obj] of this.constraints) {\n this.constraintsBuf.set(\n [\n this.contraintNum[obj[1]], obj[2],\n ...obj[0].map(ele => this.objIdx.get(ele.id) || -1),\n ],\n (i) * 6\n )\n i++\n }\n\n i = 0;\n for (let [key, obj] of this.linkedObjs) {\n console.log(obj[0])\n this.linksBuf.set(\n [\n this.linkNum[obj[1]],\n ...obj[0].map(ele => this.objIdx.get(ele.id) ?? -1),\n ],\n (i) * 5\n )\n console.log(this.linksBuf)\n i++\n }\n\n }\n\n delete(obj) {\n\n const link = this.linkedObjs.get(obj.l_id)\n\n let i = this.geomGroup.children.indexOf(link[0][0])\n if (i == -1) return Infinity\n\n for (let j = 0; j < link[0].length; j++) {\n const obj = this.geomGroup.children[i + j]\n obj.geometry.dispose()\n obj.material.dispose()\n\n for (let c_id of obj.constraints) {\n this.deleteConstraints(c_id)\n }\n }\n\n this.geomGroup.children.splice(i, link[0].length)\n\n this.linkedObjs.delete(obj.l_id)\n\n return i\n }\n\n updatePointsBuffer(startingIdx=0) {\n for (let i = startingIdx; i < this.geomGroup.children.length; i++) {\n const obj = this.geomGroup.children[i]\n this.objIdx.set(obj.id, i)\n if (obj.type == \"Points\") {\n this.ptsBuf[2 * i] = obj.geometry.attributes.position.array[0]\n this.ptsBuf[2 * i + 1] = obj.geometry.attributes.position.array[1]\n }\n }\n }\n\n\n clear() {\n if (this.mode == \"\") return\n\n if (this.mode == \"line\") {\n this.domElement.removeEventListener('pointerdown', this.onClick_1)\n this.domElement.removeEventListener('pointermove', this.beforeClick_2);\n this.domElement.removeEventListener('pointerdown', this.onClick_2);\n\n this.delete(this.geomGroup.children[this.geomGroup.children.length - 1])\n\n this.dispatchEvent({ type: 'change' })\n this.subsequent = false\n }\n }\n\n getLocation(e) {\n this.raycaster.setFromCamera(\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_8__.Vector2(\n (e.clientX / window.innerWidth) * 2 - 1,\n - (e.clientY / window.innerHeight) * 2 + 1\n ),\n this.camera\n );\n // return this.worldToLocal(this.raycaster.ray.intersectPlane(this.plane)).toArray()\n return this.raycaster.ray.intersectPlane(this.plane).applyMatrix4(this.inverse).toArray()\n }\n\n onClick_1(e) {\n if (e.buttons !== 1) return\n const mouseLoc = this.getLocation(e);\n\n this.p1Geom = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_9__.BufferGeometry().setAttribute('position',\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_10__.BufferAttribute(new Float32Array(mouseLoc), 3)\n )\n this.p1 = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_11__.Points(this.p1Geom,\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__.PointsMaterial().copy(this.pointMaterial)\n );\n this.p1.matrixAutoUpdate = false;\n this.p1.constraints = new Set()\n\n this.p2Geom = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_9__.BufferGeometry().setAttribute('position',\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_10__.BufferAttribute(new Float32Array(3), 3)\n )\n this.p2 = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_11__.Points(\n this.p2Geom,\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__.PointsMaterial().copy(this.pointMaterial)\n );\n this.p2.matrixAutoUpdate = false;\n this.p2.constraints = new Set()\n\n let toPush;\n\n if (this.mode == \"line\") {\n this.lineGeom = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_9__.BufferGeometry().setAttribute('position',\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_10__.BufferAttribute(new Float32Array(6), 3)\n );\n this.lineGeom.attributes.position.set(mouseLoc)\n this.line = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_12__.Line(this.lineGeom,\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__.LineBasicMaterial().copy(this.lineMaterial)\n );\n this.line.constraints = new Set()\n this.line.frustumCulled = false;\n\n toPush = [this.p1, this.p2, this.line];\n\n if (this.subsequent) {\n\n this.constraints.set(this.c_id,\n [\n [this.geomGroup.children[this.geomGroup.children.length - 2], this.p1, -1, -1],\n 'coincident', -1\n ]\n )\n\n this.p1.constraints.add(this.c_id)\n this.geomGroup.children[this.geomGroup.children.length - 2].constraints.add(this.c_id)\n this.c_id += 1\n }\n\n } else if (this.mode == \"arc\") {\n\n this.arcGeom = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_9__.BufferGeometry().setAttribute('position',\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_10__.BufferAttribute(new Float32Array(3 * 37), 3)\n )\n\n this.arc = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_12__.Line(this.arcGeom,\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__.LineBasicMaterial().copy(this.lineMaterial)\n );\n this.arc.frustumCulled = false;\n\n this.p3Geom = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_9__.BufferGeometry().setAttribute('position',\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_10__.BufferAttribute(new Float32Array(3), 3)\n )\n this.p3 = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_11__.Points(this.p3Geom,\n new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_4__.PointsMaterial().copy(this.pointMaterial)\n );\n\n toPush = [this.p1, this.p2, this.p3, this.arc]\n }\n\n\n this.updatePoint = this.geomGroup.children.length\n this.geomGroup.add(...toPush)\n\n this.linkedObjs.set(this.l_id, [toPush, this.mode])\n\n for (let obj of toPush) {\n obj.l_id = this.l_id\n }\n this.l_id += 1\n\n\n this.domElement.removeEventListener('pointerdown', this.onClick_1)\n this.domElement.addEventListener('pointermove', this.beforeClick_2)\n this.domElement.addEventListener('pointerdown', this.onClick_2)\n }\n\n\n beforeClick_2(e) {\n const mouseLoc = this.getLocation(e);\n\n this.p2Geom.attributes.position.set(mouseLoc);\n this.p2Geom.attributes.position.needsUpdate = true;\n this.p2Geom.computeBoundingSphere()\n\n if (this.mode == \"line\") {\n this.lineGeom.attributes.position.set(mouseLoc, 3)\n this.lineGeom.attributes.position.needsUpdate = true;\n } else if (this.mode == 'arc') {\n const [points, center] = get2PtArc(\n this.p1Geom.attributes.position.array,\n this.p2Geom.attributes.position.array\n )\n this.arcGeom.attributes.position.set(\n points\n );\n this.arcGeom.attributes.position.needsUpdate = true;\n this.p3Geom.attributes.position.set(center);\n this.p3Geom.attributes.position.needsUpdate = true;\n this.p3Geom.computeBoundingSphere()\n\n }\n\n this.dispatchEvent({ type: 'change' })\n }\n\n onClick_2(e) {\n if (e.buttons !== 1) return;\n this.domElement.removeEventListener('pointermove', this.beforeClick_2);\n this.domElement.removeEventListener('pointerdown', this.onClick_2);\n\n\n if (this.mode == \"line\") {\n\n\n this.updatePointsBuffer(this.updatePoint)\n this.updateOtherBuffers()\n\n\n this.subsequent = true\n this.onClick_1(e)\n } else if (this.mode == \"arc\") {\n // this.domElement.addEventListener('pointermove', this.beforeClick_3)\n }\n }\n\n beforeClick_3(e) {\n const mouseLoc = this.getLocation(e);\n this.p3Geom.attributes.position.set(mouseLoc);\n this.p3Geom.attributes.position.needsUpdate = true;\n this.p3Geom.computeBoundingSphere()\n }\n\n\n solve() {\n\n // for (let i = 0, p = 0; i < this.geomGroup.children.length; i++) {\n // this.ptsBuf[p++] = this.geomGroup.children[i].geometry.attributes.position.array[0]\n // this.ptsBuf[p++] = this.geomGroup.children[i].geometry.attributes.position.array[1]\n // }\n this.updatePointsBuffer()\n\n const pts_buffer = Module._malloc(this.ptsBuf.length * this.ptsBuf.BYTES_PER_ELEMENT)\n Module.HEAPF32.set(this.ptsBuf, pts_buffer >> 2)\n\n const constraints_buffer = Module._malloc(this.constraintsBuf.length * this.constraintsBuf.BYTES_PER_ELEMENT)\n Module.HEAPF32.set(this.constraintsBuf, constraints_buffer >> 2)\n\n const links_buffer = Module._malloc(this.linksBuf.length * this.linksBuf.BYTES_PER_ELEMENT)\n Module.HEAPF32.set(this.linksBuf, links_buffer >> 2)\n\n Module[\"_solver\"](this.geomGroup.children.length, pts_buffer, this.constraints.size, constraints_buffer, this.linkedObjs.size, links_buffer)\n\n let ptr = pts_buffer >> 2;\n\n\n for (let i = 0; i < this.geomGroup.children.length; i += 1) {\n\n const pos = this.geomGroup.children[i].geometry.attributes.position;\n if (isNaN(Module.HEAPF32[ptr])) {\n pos.array[0] = Module.HEAPF32[ptr-4]\n pos.array[1] = Module.HEAPF32[ptr-3]\n pos.array[3] = Module.HEAPF32[ptr-2]\n pos.array[4] = Module.HEAPF32[ptr-1]\n } else {\n pos.array[0] = Module.HEAPF32[ptr++]\n pos.array[1] = Module.HEAPF32[ptr++]\n }\n\n pos.needsUpdate = true;\n }\n\n this.dispatchEvent({ type: 'change' })\n\n Module._free(pts_buffer)\n }\n\n}\n\n\n\n\n\n//# sourceURL=webpack:///./src/Sketcher.js?"); /***/ }), diff --git a/dist/solver.wasm b/dist/solver.wasm index 0f2c6829be1365bb1936a8afe005b9b0e6e65727..e70429622c839855c0767a377396997dd6338795 100755 GIT binary patch delta 37694 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%0A{|xbpQYW delta 34495 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 this.objIdx.get(ele.id) || -1), - this.contraintNum[obj[4]], obj[5] ], (i) * 6 ) @@ -297,14 +299,16 @@ export class Sketcher extends THREE.Group { } i = 0; - for (let [key,obj] of this.linkedObjs) { + for (let [key, obj] of this.linkedObjs) { + console.log(obj[0]) this.linksBuf.set( [ - ...obj[0].map(ele => this.objIdx.get(ele.id) || -1), - this.linkNum[obj[4]] + this.linkNum[obj[1]], + ...obj[0].map(ele => this.objIdx.get(ele.id) ?? -1), ], (i) * 5 ) + console.log(this.linksBuf) i++ } @@ -314,11 +318,11 @@ export class Sketcher extends THREE.Group { const link = this.linkedObjs.get(obj.l_id) - let i = this.children.indexOf(link[0][0]) + let i = this.geomGroup.children.indexOf(link[0][0]) if (i == -1) return Infinity for (let j = 0; j < link[0].length; j++) { - const obj = this.children[i + j] + const obj = this.geomGroup.children[i + j] obj.geometry.dispose() obj.material.dispose() @@ -327,16 +331,16 @@ export class Sketcher extends THREE.Group { } } - this.children.splice(i, link[0].length) + this.geomGroup.children.splice(i, link[0].length) this.linkedObjs.delete(obj.l_id) return i } - updateBuffer(startingIdx) { - for (let i = startingIdx; i < this.children.length; i++) { - const obj = this.children[i] + updatePointsBuffer(startingIdx=0) { + for (let i = startingIdx; i < this.geomGroup.children.length; i++) { + const obj = this.geomGroup.children[i] this.objIdx.set(obj.id, i) if (obj.type == "Points") { this.ptsBuf[2 * i] = obj.geometry.attributes.position.array[0] @@ -354,7 +358,7 @@ export class Sketcher extends THREE.Group { this.domElement.removeEventListener('pointermove', this.beforeClick_2); this.domElement.removeEventListener('pointerdown', this.onClick_2); - this.delete(this.children[this.children.length - 1]) + this.delete(this.geomGroup.children[this.geomGroup.children.length - 1]) this.dispatchEvent({ type: 'change' }) this.subsequent = false @@ -413,23 +417,15 @@ export class Sketcher extends THREE.Group { if (this.subsequent) { - this.constraintsBuf.set( - [ - this.children.length - 2, this.children.length, -1, -1, - this.contraintNum['coincident'], -1 - ], - (this.constraints.size) * 6 - ) - this.constraints.set(this.c_id, [ - [this.children[this.children.length - 2], this.p1, -1, -1], + [this.geomGroup.children[this.geomGroup.children.length - 2], this.p1, -1, -1], 'coincident', -1 ] ) this.p1.constraints.add(this.c_id) - this.children[this.children.length - 2].constraints.add(this.c_id) + this.geomGroup.children[this.geomGroup.children.length - 2].constraints.add(this.c_id) this.c_id += 1 } @@ -455,21 +451,8 @@ export class Sketcher extends THREE.Group { } - let ptr = this.children.length - this.add(...toPush) - this.updateBuffer(ptr) - - - const link_entry = new Array(5).fill(-1) - for (let i = ptr, j = 0; j < toPush.length - 1;) { - link_entry[j++] = i++ - } - link_entry[link_entry.length - 1] = this.linkNum[this.mode] - - this.linksBuf.set( - link_entry, - (this.linkedObjs.size) * 5 - ) + this.updatePoint = this.geomGroup.children.length + this.geomGroup.add(...toPush) this.linkedObjs.set(this.l_id, [toPush, this.mode]) @@ -479,7 +462,6 @@ export class Sketcher extends THREE.Group { this.l_id += 1 - this.domElement.removeEventListener('pointerdown', this.onClick_1) this.domElement.addEventListener('pointermove', this.beforeClick_2) this.domElement.addEventListener('pointerdown', this.onClick_2) @@ -518,13 +500,18 @@ export class Sketcher extends THREE.Group { if (e.buttons !== 1) return; this.domElement.removeEventListener('pointermove', this.beforeClick_2); this.domElement.removeEventListener('pointerdown', this.onClick_2); + + if (this.mode == "line") { + + + this.updatePointsBuffer(this.updatePoint) + this.updateOtherBuffers() + + this.subsequent = true this.onClick_1(e) } else if (this.mode == "arc") { - - - // this.domElement.addEventListener('pointermove', this.beforeClick_3) } } @@ -539,48 +526,45 @@ export class Sketcher extends THREE.Group { solve() { + // for (let i = 0, p = 0; i < this.geomGroup.children.length; i++) { + // this.ptsBuf[p++] = this.geomGroup.children[i].geometry.attributes.position.array[0] + // this.ptsBuf[p++] = this.geomGroup.children[i].geometry.attributes.position.array[1] + // } + this.updatePointsBuffer() - for (let i = 0, p = 0; i < this.children.length; i++) { - this.ptsBuf[p++] = this.children[i].geometry.attributes.position.array[0] - this.ptsBuf[p++] = this.children[i].geometry.attributes.position.array[1] - } + const pts_buffer = Module._malloc(this.ptsBuf.length * this.ptsBuf.BYTES_PER_ELEMENT) + Module.HEAPF32.set(this.ptsBuf, pts_buffer >> 2) - buffer = Module._malloc(this.ptsBuf.length * this.ptsBuf.BYTES_PER_ELEMENT) - Module.HEAPF32.set(this.ptsBuf, buffer >> 2) + const constraints_buffer = Module._malloc(this.constraintsBuf.length * this.constraintsBuf.BYTES_PER_ELEMENT) + Module.HEAPF32.set(this.constraintsBuf, constraints_buffer >> 2) + + const links_buffer = Module._malloc(this.linksBuf.length * this.linksBuf.BYTES_PER_ELEMENT) + Module.HEAPF32.set(this.linksBuf, links_buffer >> 2) + + Module["_solver"](this.geomGroup.children.length, pts_buffer, this.constraints.size, constraints_buffer, this.linkedObjs.size, links_buffer) + + let ptr = pts_buffer >> 2; - Module["_solver"](this.children.length / 2, buffer) + for (let i = 0; i < this.geomGroup.children.length; i += 1) { + const pos = this.geomGroup.children[i].geometry.attributes.position; + if (isNaN(Module.HEAPF32[ptr])) { + pos.array[0] = Module.HEAPF32[ptr-4] + pos.array[1] = Module.HEAPF32[ptr-3] + pos.array[3] = Module.HEAPF32[ptr-2] + pos.array[4] = Module.HEAPF32[ptr-1] + } else { + pos.array[0] = Module.HEAPF32[ptr++] + pos.array[1] = Module.HEAPF32[ptr++] + } - let ptr = buffer >> 2; - - - for (let i = 0; i < this.linkedObjs.lengthxx; i += 1) { - - const pt1_pos = this.children[i >> 1].geometry.attributes.position; - const pt2_pos = this.children[(i >> 1) + 1].geometry.attributes.position; - const line_pos = this.linesArr[i >> 2].geometry.attributes.position; - - pt1_pos.array[0] = Module.HEAPF32[ptr] - line_pos.array[0] = Module.HEAPF32[ptr++] - - pt1_pos.array[1] = Module.HEAPF32[ptr] - line_pos.array[1] = Module.HEAPF32[ptr++] - - pt2_pos.array[0] = Module.HEAPF32[ptr] - line_pos.array[3] = Module.HEAPF32[ptr++] - - pt2_pos.array[1] = Module.HEAPF32[ptr] - line_pos.array[4] = Module.HEAPF32[ptr++] - - pt1_pos.needsUpdate = true; - pt2_pos.needsUpdate = true; - line_pos.needsUpdate = true; + pos.needsUpdate = true; } this.dispatchEvent({ type: 'change' }) - Module._free(buffer) + Module._free(pts_buffer) } } diff --git a/wasm/solver.c b/wasm/solver.c index 6a50493..7c3eb7d 100644 --- a/wasm/solver.c +++ b/wasm/solver.c @@ -12,7 +12,7 @@ #include #include #include - +#include #include "slvs.h" static Slvs_System sys; @@ -33,11 +33,9 @@ static void *CheckMalloc(size_t n) * entities and constraints. *---------------------------------------------------------------------------*/ -int solver(int nLines, float *ptr) +int solver(int nPts, float *p_ptr, int nConst, float *c_ptr, int nLinks, float *l_ptr) { - - float *buf_pt_start = ptr; - + // printf("first %i \n", (int)*(l_ptr + 1)); Slvs_hGroup g; double qw, qx, qy, qz; @@ -67,46 +65,77 @@ int solver(int nLines, float *ptr) /* These points are represented by their coordinates (u v) within the * workplane, so they need only two parameters each. */ - int p_start = sys.params; - - Slvs_hParam ph = 11, ph_s = 11; + Slvs_hParam ph = 11; Slvs_hParam vh = 301, vh_s = 301; Slvs_hParam lh = 400, lh_s = 400; Slvs_hParam con_id = 1; - for (int i = 0; i < nLines * 2; i++) + float *buf_pt_start = p_ptr; + int p_start = sys.params; + for (int i = 0; i < nPts; i++) { + printf("i: %i %f %f \n", i,(float)*p_ptr,(float)*(p_ptr+1)); + if (isnan((float)*p_ptr)) + { + p_ptr+=2; + continue; + } + sys.param[sys.params++] = Slvs_MakeParam(ph++, g, (float)*p_ptr++); + sys.param[sys.params++] = Slvs_MakeParam(ph++, g, (float)*p_ptr++); + sys.entity[sys.entities++] = Slvs_MakePoint2d(i, g, 200, ph - 1, ph - 2); + } - sys.param[sys.params++] = Slvs_MakeParam(ph++, g, (float)*ptr++); - sys.param[sys.params++] = Slvs_MakeParam(ph++, g, (float)*ptr++); - sys.entity[sys.entities++] = Slvs_MakePoint2d(vh++, g, 200, ph - 1, ph - 2); - - if (i % 2 == 1) + for (int i = 0; i < nLinks; i++) + { + if (*l_ptr++ == 0) { sys.entity[sys.entities++] = Slvs_MakeLineSegment(lh++, g, - 200, vh - 1, vh - 2); + 200, (int)*l_ptr++, (int)*l_ptr++); + l_ptr += 2; + } else { + l_ptr += 4; } - else if (i > 0) + } + + printf("nconst: %i \n", nConst); + for (int i = 0; i < nConst; i++) + { + if ((int)*c_ptr == 0) { + c_ptr+=2; + printf("const: %i %i \n", (int)*c_ptr, (int)*(c_ptr+1)); sys.constraint[sys.constraints++] = Slvs_MakeConstraint( con_id++, g, SLVS_C_POINTS_COINCIDENT, 200, 0.0, - vh - 2, vh - 1, 0, 0); + (int)*c_ptr++, (int)*c_ptr++, 0, 0); + + c_ptr += 2; + + } else { + c_ptr += 6; } } /* And solve. */ Slvs_Solve(&sys, g); + printf("npts: %i \n", nPts); if (sys.result == SLVS_RESULT_OKAY) { // printf("solved okay\n"); - for (int i = 0; i < nLines * 4; i++) + for (int i = 0; i < nPts; i++) { + if (isnan((float)*buf_pt_start)) + { + buf_pt_start+=2; + continue; + } + printf("res: %i %f %f \n",i, (float)sys.param[p_start].val, (float)sys.param[p_start+1].val); + *buf_pt_start++ = (float)sys.param[p_start++].val; *buf_pt_start++ = (float)sys.param[p_start++].val; } }