diff --git a/dist/bundle.js b/dist/bundle.js index 316e4d2..294303c 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 angle = (Math.atan2(dy, dx) - Math.PI / 2) % (2 * Math.PI)\n let a1 = angle - Math.PI / 6\n let a2 = angle + 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\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.pointsGroup = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_0__.Group() //0\n this.add(this.pointsGroup)\n\n this.arcPtsGroup = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_0__.Group() //1\n this.add(this.arcPtsGroup)\n\n this.linesGroup = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_0__.Group() //2\n this.add(this.linesGroup)\n\n this.arcsGroup = new _node_modules_three_src_Three__WEBPACK_IMPORTED_MODULE_0__.Group() //3\n this.add(this.arcsGroup)\n\n this.geom = [\n this.pointsGroup.children,\n this.arcPtsGroup.children,\n this.linesGroup.children,\n this.arcsGroup.children\n ]\n\n this.ptsArr = this.pointsGroup.children\n this.arcPtsArr = this.arcPtsGroup.children\n this.linesArr = this.linesGroup.children\n this.arcsArr = this.arcsGroup.children\n\n\n\n\n window.lg = this.linesArr\n window.pg = this.ptsArr\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 this.pickThreshold = 0.1\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.constraints = {}\n\n this.max_lines = 1000\n this.ptsBuf = new Float32Array(this.max_lines * 2)\n\n this.max_arcs = 1000\n this.arcBuf = new Float32Array(this.max_arcs * 2 * 3)\n\n this.max_constraints = 1000\n this.contraintsBuf = new Int32Array(this.max_constraints * 5)\n this.contraintsValBuf = new Float32Array(this.max_constraints)\n\n this.subsequent = false;\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.delete()\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 const hoverPts = this.raycaster.intersectObjects(this.ptsArr)\n // const hoverPts = this.raycaster.intersectObjects(this.arcPtsArr)\n\n if (hoverPts.length) {\n let minDist = hoverPts[0].distanceToRay\n let idx = 0\n\n for (let i = 1; i < hoverPts.length; i++) {\n if (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 // const hoverLines = this.raycaster.intersectObjects(this.linesArr)\n // if (hoverLines.length) {\n // hoverLines[0].object.material.color.set(0xff0000)\n // this.hovered = hoverLines[0].object\n // this.dispatchEvent({ type: 'change' })\n // return\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.ptsArr.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.ptsArr[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.ptsArr[this.grabPtIdx].geometry.computeBoundingSphere()\n // this.grabbedObject = null\n }\n\n\n\n delete() {\n const deleteArr = []\n for (let obj of this.selected) {\n const idx = obj.parent.children.indexOf(obj)\n if (obj.parent == this.linesGroup) {\n deleteArr.push([2, idx])\n deleteArr.push([0, idx * 2])\n deleteArr.push([0, idx * 2 + 1])\n } else if (obj.parent == this.pointsGroup) {\n deleteArr.push([0, idx])\n deleteArr.push([0, idx + (idx % 2 == 0 ? 1 : -1)])\n deleteArr.push([2, Math.floor(idx / 2)])\n } else if (obj.parent == this.arcsGroup) {\n\n } else if (obj.parent == this.arcPtsGroup) {\n\n }\n }\n\n deleteArr.sort((a, b) => a[0] == b[0] ? b[1] - a[1] : b[0] - a[0]);\n\n let li, idx;\n for (let i = 0; i < deleteArr.length; i++) {\n if (deleteArr[i][0] == li && deleteArr[i][1] == idx) {\n continue\n } else {\n [li, idx] = deleteArr[i]\n }\n const obj = this.geom[li][idx]\n obj.geometry.dispose()\n obj.material.dispose()\n obj.parent.children.splice(idx, 1)\n obj.parent = null;\n }\n\n this.selected.clear()\n this.dispatchEvent({ type: 'change' })\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 const lastLine = this.linesArr[this.linesArr.length - 1]\n this.linesGroup.remove(lastLine)\n lastLine.geometry.dispose()\n\n const lastPoints = this.ptsArr.slice(this.ptsArr.length - 2)\n this.pointsGroup.remove(...lastPoints)\n lastPoints.forEach(obj => obj.geometry.dispose())\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\n if (this.subsequent) {\n // this.constraints[constraint++] = [\n // [this.ptsArr[this.ptsArr.length-1], this.p1]\n // ]\n\n }\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\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.frustumCulled = false;\n\n\n this.linesGroup.add(this.line)\n this.pointsGroup.add(this.p1)\n this.pointsGroup.add(this.p2)\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 this.arcsGroup.add(this.arc)\n this.arcPtsGroup.add(this.p1)\n this.arcPtsGroup.add(this.p2)\n this.arcPtsGroup.add(this.p3)\n\n }\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 solve() {\n\n\n for (let i = 0, p = 0; i < this.ptsArr.length; i++) {\n this.ptsBuf[p++] = this.ptsArr[i].geometry.attributes.position.array[0]\n this.ptsBuf[p++] = this.ptsArr[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.ptsArr.length / 2, buffer)\n\n\n let ptr = buffer >> 2;\n\n\n for (let i = 0; i < this.ptsArr.length * 2; i += 4) {\n const pt1_pos = this.ptsArr[i >> 1].geometry.attributes.position;\n const pt2_pos = this.ptsArr[(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\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?"); /***/ }), diff --git a/src/Sketcher.js b/src/Sketcher.js index 7a4659b..1665018 100644 --- a/src/Sketcher.js +++ b/src/Sketcher.js @@ -10,9 +10,9 @@ function get2PtArc(p1, p2, divisions = 36) { const dx = p2[0] - p1[0] const dy = p2[1] - p1[1] const dist = Math.sqrt(dx ** 2 + dy ** 2) - const angle = (Math.atan2(dy, dx) - Math.PI / 2) % (2 * Math.PI) - let a1 = angle - Math.PI / 6 - let a2 = angle + Math.PI / 6 + const midAngle = (Math.atan2(dy, dx) - Math.PI / 2) % (2 * Math.PI) + let a1 = midAngle - Math.PI / 6 + let a2 = midAngle + Math.PI / 6 a1 = a1 < 0 ? a1 + 2 * Math.PI : a1 a2 = a2 < 0 ? a2 + 2 * Math.PI : a1 @@ -32,6 +32,29 @@ function get2PtArc(p1, p2, divisions = 36) { } +function get3PtArc(p1, p2, c, divisions = 36) { + + const v1 = [p1[0] - c[0], p1[1] - c[1]] + const v2 = [p2[0] - c[0], p2[1] - c[1]] + + let a1 = Math.atan2(v1[1], v1[0]) + let a2 = Math.atan2(v2[1], v2[0]) + + const radius = Math.sqrt(v1[0] ** 2 + v1[1] ** 2) + + const deltaAngle = a2 - a1 + + let points = new Float32Array((divisions + 1) * 3) + + for (let d = 0; d <= divisions; d++) { + const angle = a1 + (d / divisions) * deltaAngle; + points[3 * d] = c[0] + radius * Math.cos(angle); + points[3 * d + 1] = c[1] + radius * Math.sin(angle); + } + return points; +} + + export class Sketcher extends THREE.Group { constructor(camera, domElement, plane) { super() @@ -44,36 +67,6 @@ export class Sketcher extends THREE.Group { this.add(new THREE.PlaneHelper(this.plane, 1, 0xffff00)); - this.pointsGroup = new THREE.Group() //0 - this.add(this.pointsGroup) - - this.arcPtsGroup = new THREE.Group() //1 - this.add(this.arcPtsGroup) - - this.linesGroup = new THREE.Group() //2 - this.add(this.linesGroup) - - this.arcsGroup = new THREE.Group() //3 - this.add(this.arcsGroup) - - this.geom = [ - this.pointsGroup.children, - this.arcPtsGroup.children, - this.linesGroup.children, - this.arcsGroup.children - ] - - this.ptsArr = this.pointsGroup.children - this.arcPtsArr = this.arcPtsGroup.children - this.linesArr = this.linesGroup.children - this.arcsArr = this.arcsGroup.children - - - - - window.lg = this.linesArr - window.pg = this.ptsArr - this.colorPt = new THREE.Color('white') this.selected = new Set() @@ -103,7 +96,6 @@ export class Sketcher extends THREE.Group { this.raycaster.params.Line.threshold = 0.4; this.raycaster.params.Points.threshold = 0.2; - this.pickThreshold = 0.1 window.addEventListener('keydown', this.onKeyPress) domElement.addEventListener('pointerdown', this.onPick) @@ -112,19 +104,36 @@ export class Sketcher extends THREE.Group { this.mode = "" - this.constraints = {} + this.linkedObjs = new Map() + this.l_id = 0; - this.max_lines = 1000 - this.ptsBuf = new Float32Array(this.max_lines * 2) + this.constraints = new Map() + this.c_id = 0; - this.max_arcs = 1000 - this.arcBuf = new Float32Array(this.max_arcs * 2 * 3) + this.objIdx = new Map() + + this.max_pts = 1000 + this.ptsBuf = new Float32Array(this.max_pts * 2) + + this.max_links = 1000 + this.linksBuf = new Int32Array(this.max_links * 5) // [0]:type, [1]:pt1, [2]:pt2, [3]:pt3, [4]:pt4 this.max_constraints = 1000 - this.contraintsBuf = new Int32Array(this.max_constraints * 5) - this.contraintsValBuf = new Float32Array(this.max_constraints) + this.constraintsBuf = new Float32Array(this.max_constraints * 6) // [0]:type, [1]:pt1, [2]:pt2, [3]:lk1, [4]:lk2, [5]:val this.subsequent = false; + this.ptsBufPt = 0; + this.endBufPt = 0; + + this.linkNum = { + 'line': 0, + 'arc': 1 + } + + this.contraintNum = { + 'coincident': 0, + 'parallel': 1 + } } orientSketcher() { @@ -154,7 +163,7 @@ export class Sketcher extends THREE.Group { this.mode = "arc" break; case 'd': - this.delete() + this.deleteSelected() break; case '=': this.plane.applyMatrix4(new Matrix4().makeRotationY(0.1)) @@ -184,15 +193,14 @@ export class Sketcher extends THREE.Group { ), this.camera ); - const hoverPts = this.raycaster.intersectObjects(this.ptsArr) - // const hoverPts = this.raycaster.intersectObjects(this.arcPtsArr) + + const hoverPts = this.raycaster.intersectObjects(this.children) if (hoverPts.length) { - let minDist = hoverPts[0].distanceToRay + let minDist = Infinity; let idx = 0 - - for (let i = 1; i < hoverPts.length; i++) { - if (hoverPts[i].distanceToRay <= minDist) { + for (let i = 0; i < hoverPts.length; i++) { + if (hoverPts[i].distanceToRay && hoverPts[i].distanceToRay <= minDist) { minDist = hoverPts[i].distanceToRay idx = i } @@ -204,13 +212,6 @@ export class Sketcher extends THREE.Group { return } - // const hoverLines = this.raycaster.intersectObjects(this.linesArr) - // if (hoverLines.length) { - // hoverLines[0].object.material.color.set(0xff0000) - // this.hovered = hoverLines[0].object - // this.dispatchEvent({ type: 'change' }) - // return - // } if (this.hovered) { this.hovered = null; @@ -227,7 +228,7 @@ export class Sketcher extends THREE.Group { if (this.hovered) { this.selected.add(this.hovered) if (this.hovered.type === "Points") { - this.grabPtIdx = this.ptsArr.indexOf( + this.grabPtIdx = this.children.indexOf( this.hovered ) this.domElement.addEventListener('pointermove', this.onDrag); @@ -244,7 +245,7 @@ export class Sketcher extends THREE.Group { onDrag(e) { const mouseLoc = this.getLocation(e); - this.ptsArr[this.grabPtIdx].geometry.attributes.position.set(mouseLoc); + this.children[this.grabPtIdx].geometry.attributes.position.set(mouseLoc); this.solve() this.dispatchEvent({ type: 'change' }) } @@ -253,51 +254,98 @@ export class Sketcher extends THREE.Group { onRelease() { this.domElement.removeEventListener('pointermove', this.onDrag) this.domElement.removeEventListener('pointerup', this.onRelease) - this.ptsArr[this.grabPtIdx].geometry.computeBoundingSphere() + this.children[this.grabPtIdx].geometry.computeBoundingSphere() // this.grabbedObject = null } - delete() { - const deleteArr = [] + deleteSelected() { + let minI = this.children.length; + for (let obj of this.selected) { - const idx = obj.parent.children.indexOf(obj) - if (obj.parent == this.linesGroup) { - deleteArr.push([2, idx]) - deleteArr.push([0, idx * 2]) - deleteArr.push([0, idx * 2 + 1]) - } else if (obj.parent == this.pointsGroup) { - deleteArr.push([0, idx]) - deleteArr.push([0, idx + (idx % 2 == 0 ? 1 : -1)]) - deleteArr.push([2, Math.floor(idx / 2)]) - } else if (obj.parent == this.arcsGroup) { + minI = Math.min(minI, this.delete(obj)) - } else if (obj.parent == this.arcPtsGroup) { - - } } - deleteArr.sort((a, b) => a[0] == b[0] ? b[1] - a[1] : b[0] - a[0]); - - let li, idx; - for (let i = 0; i < deleteArr.length; i++) { - if (deleteArr[i][0] == li && deleteArr[i][1] == idx) { - continue - } else { - [li, idx] = deleteArr[i] - } - const obj = this.geom[li][idx] - obj.geometry.dispose() - obj.material.dispose() - obj.parent.children.splice(idx, 1) - obj.parent = null; - } + this.updateBuffer(minI) + this.resetConstraints() this.selected.clear() this.dispatchEvent({ type: 'change' }) } + deleteConstraints(c_id) { + for (let ob of this.constraints.get(c_id)[0]) { + if (ob == -1) continue + ob.constraints.delete(c_id) + } + this.constraints.delete(c_id) + } + + resetConstraints() { + let i = 0 + for (let [key,obj] of this.constraints) { + this.constraintsBuf.set( + [ + ...obj[0].map(ele => this.objIdx.get(ele.id) || -1), + this.contraintNum[obj[4]], obj[5] + ], + (i) * 6 + ) + i++ + } + + i = 0; + for (let [key,obj] of this.linkedObjs) { + this.linksBuf.set( + [ + ...obj[0].map(ele => this.objIdx.get(ele.id) || -1), + this.linkNum[obj[4]] + ], + (i) * 5 + ) + i++ + } + + } + + delete(obj) { + + const link = this.linkedObjs.get(obj.l_id) + + let i = this.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] + obj.geometry.dispose() + obj.material.dispose() + + for (let c_id of obj.constraints) { + this.deleteConstraints(c_id) + } + } + + this.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] + this.objIdx.set(obj.id, i) + if (obj.type == "Points") { + this.ptsBuf[2 * i] = obj.geometry.attributes.position.array[0] + this.ptsBuf[2 * i + 1] = obj.geometry.attributes.position.array[1] + } + } + } + + clear() { if (this.mode == "") return @@ -306,13 +354,7 @@ export class Sketcher extends THREE.Group { this.domElement.removeEventListener('pointermove', this.beforeClick_2); this.domElement.removeEventListener('pointerdown', this.onClick_2); - const lastLine = this.linesArr[this.linesArr.length - 1] - this.linesGroup.remove(lastLine) - lastLine.geometry.dispose() - - const lastPoints = this.ptsArr.slice(this.ptsArr.length - 2) - this.pointsGroup.remove(...lastPoints) - lastPoints.forEach(obj => obj.geometry.dispose()) + this.delete(this.children[this.children.length - 1]) this.dispatchEvent({ type: 'change' }) this.subsequent = false @@ -342,13 +384,7 @@ export class Sketcher extends THREE.Group { new THREE.PointsMaterial().copy(this.pointMaterial) ); this.p1.matrixAutoUpdate = false; - - if (this.subsequent) { - // this.constraints[constraint++] = [ - // [this.ptsArr[this.ptsArr.length-1], this.p1] - // ] - - } + this.p1.constraints = new Set() this.p2Geom = new THREE.BufferGeometry().setAttribute('position', new THREE.BufferAttribute(new Float32Array(3), 3) @@ -358,6 +394,9 @@ export class Sketcher extends THREE.Group { new THREE.PointsMaterial().copy(this.pointMaterial) ); this.p2.matrixAutoUpdate = false; + this.p2.constraints = new Set() + + let toPush; if (this.mode == "line") { this.lineGeom = new THREE.BufferGeometry().setAttribute('position', @@ -367,12 +406,33 @@ export class Sketcher extends THREE.Group { this.line = new THREE.Line(this.lineGeom, new THREE.LineBasicMaterial().copy(this.lineMaterial) ); + this.line.constraints = new Set() this.line.frustumCulled = false; + toPush = [this.p1, this.p2, this.line]; + + 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], + 'coincident', -1 + ] + ) + + this.p1.constraints.add(this.c_id) + this.children[this.children.length - 2].constraints.add(this.c_id) + this.c_id += 1 + } - this.linesGroup.add(this.line) - this.pointsGroup.add(this.p1) - this.pointsGroup.add(this.p2) } else if (this.mode == "arc") { this.arcGeom = new THREE.BufferGeometry().setAttribute('position', @@ -391,14 +451,34 @@ export class Sketcher extends THREE.Group { new THREE.PointsMaterial().copy(this.pointMaterial) ); - this.arcsGroup.add(this.arc) - this.arcPtsGroup.add(this.p1) - this.arcPtsGroup.add(this.p2) - this.arcPtsGroup.add(this.p3) - + toPush = [this.p1, this.p2, this.p3, this.arc] } + 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.linkedObjs.set(this.l_id, [toPush, this.mode]) + + for (let obj of toPush) { + obj.l_id = this.l_id + } + this.l_id += 1 + + this.domElement.removeEventListener('pointerdown', this.onClick_1) this.domElement.addEventListener('pointermove', this.beforeClick_2) @@ -456,27 +536,29 @@ export class Sketcher extends THREE.Group { this.p3Geom.computeBoundingSphere() } + solve() { - for (let i = 0, p = 0; i < this.ptsArr.length; i++) { - this.ptsBuf[p++] = this.ptsArr[i].geometry.attributes.position.array[0] - this.ptsBuf[p++] = this.ptsArr[i].geometry.attributes.position.array[1] + 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] } buffer = Module._malloc(this.ptsBuf.length * this.ptsBuf.BYTES_PER_ELEMENT) Module.HEAPF32.set(this.ptsBuf, buffer >> 2) - Module["_solver"](this.ptsArr.length / 2, buffer) + Module["_solver"](this.children.length / 2, buffer) let ptr = buffer >> 2; - for (let i = 0; i < this.ptsArr.length * 2; i += 4) { - const pt1_pos = this.ptsArr[i >> 1].geometry.attributes.position; - const pt2_pos = this.ptsArr[(i >> 1) + 1].geometry.attributes.position; + 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]