semi workin arc

master
howard 2021-03-22 20:55:44 -07:00
parent 0d5d481910
commit b1a21feea1
9 changed files with 1094 additions and 139 deletions

0
dist/.nojekyll vendored Normal file
View File

16
dist/bundle.js vendored

File diff suppressed because one or more lines are too long

2
dist/index.html vendored
View File

@ -58,7 +58,7 @@
<canvas id="c"></canvas> <canvas id="c"></canvas>
<div class="split"> <div class="split">
<div id="view1"></div> <div id="view1"></div>
<div id="view2"></div> <!-- <div id="view2"></div> -->
</div> </div>
<script src="solver.js"> <script src="solver.js">

BIN
dist/solver.wasm vendored

Binary file not shown.

803
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,13 @@
"scripts": { "scripts": {
"start": "webpack --watch --config webpack.dev.js", "start": "webpack --watch --config webpack.dev.js",
"build": "webpack --config webpack.prod.js", "build": "webpack --config webpack.prod.js",
"get-bundle-size": "webpack --profile --json > stats.json --config webpack.prod.js && webpack-bundle-analyzer stats.json dist/" "get-bundle-size": "webpack --profile --json > stats.json --config webpack.prod.js && webpack-bundle-analyzer stats.json dist/",
"deploy": "gh-pages -d dist -t true"
}, },
"devDependencies": { "devDependencies": {
"css-loader": "^5.1.3", "css-loader": "^5.1.3",
"dat.gui": "^0.7.7", "dat.gui": "^0.7.7",
"gh-pages": "^3.1.0",
"redux": "^4.0.5", "redux": "^4.0.5",
"sass": "^1.32.8", "sass": "^1.32.8",
"sass-loader": "^11.0.1", "sass-loader": "^11.0.1",

View File

@ -3,59 +3,108 @@ import { Matrix4 } from 'three';
import * as THREE from '../node_modules/three/src/Three' import * as THREE from '../node_modules/three/src/Three'
const factor = Math.tan(Math.PI / 3)
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
a1 = a1 < 0 ? a1 + 2 * Math.PI : a1
a2 = a2 < 0 ? a2 + 2 * Math.PI : a1
const cx = (p1[0] + p2[0] - dy * factor) / 2
const cy = (p1[1] + p2[1] + dx * factor) / 2
const radius = dist
const deltaAngle = Math.PI / 3
let points = new Float32Array((divisions + 1) * 3)
for (let d = 0; d <= divisions; d++) {
const angle = a1 + (d / divisions) * deltaAngle;
points[3 * d] = cx + radius * Math.cos(angle);
points[3 * d + 1] = cy + radius * Math.sin(angle);
}
return [points, [cx, cy]];
}
export class Sketcher extends THREE.Group { export class Sketcher extends THREE.Group {
constructor(camera, domElement, plane) { constructor(camera, domElement, plane) {
super() super()
this.camera = camera; this.camera = camera;
this.domElement = domElement; this.domElement = domElement;
this.scene = scene;
this.plane = plane; this.plane = plane;
this.matrixAutoUpdate = false; this.matrixAutoUpdate = false;
this.sketchNormal = new THREE.Vector3(0, 0, 1) this.sketchNormal = new THREE.Vector3(0, 0, 1)
this.orientSketcher(plane) this.orientSketcher(plane)
this.add(new THREE.PlaneHelper(this.plane, 1, 0xffff00)); this.add(new THREE.PlaneHelper(this.plane, 1, 0xffff00));
this.pointsGroup = new THREE.Group() //0
this.linesGroup = new THREE.Group()
this.linesArr = this.linesGroup.children
this.pointsGroup = new THREE.Group()
this.ptsArr = this.pointsGroup.children
this.add(this.linesGroup)
this.add(this.pointsGroup) 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.linesArr = this.linesGroup.children
this.ptsArr = this.pointsGroup.children
this.arcPtsArr = this.arcPtsGroup.children
this.arcsArr = this.arcsGroup.children
window.lg = this.linesArr window.lg = this.linesArr
window.pg = this.ptsArr window.pg = this.ptsArr
this.pickThreshold = 100 this.colorPt = new THREE.Color('white')
this.grabbedObject = null this.selected = new Set()
this.lineMaterial = new THREE.LineBasicMaterial({ this.lineMaterial = new THREE.LineBasicMaterial({
linewidth: 3, linewidth: 3,
color: 0x555, color: 0x555555,
}) })
this.pointMaterial = new THREE.PointsMaterial({ this.pointMaterial = new THREE.PointsMaterial({
color: 0xAAA, color: 0x555555,
size: 3, size: 3,
}) })
this.pointStart = this.pointStart.bind(this);
this.pointEnd = this.pointEnd.bind(this); this.onKeyPress = this.onKeyPress.bind(this);
this.move = this.move.bind(this);
this.keyHandler = this.keyHandler.bind(this); this.onClick_1 = this.onClick_1.bind(this);
this.picker = this.picker.bind(this); this.onClick_2 = this.onClick_2.bind(this);
this.grabbedMove = this.grabbedMove.bind(this); this.beforeClick_2 = this.beforeClick_2.bind(this);
this.grabEnd = this.grabEnd.bind(this); this.beforeClick_3 = this.beforeClick_3.bind(this);
this.onPick = this.onPick.bind(this);
this.onHover = this.onHover.bind(this);
this.onDrag = this.onDrag.bind(this);
this.onRelease = this.onRelease.bind(this);
this.raycaster = new THREE.Raycaster(); this.raycaster = new THREE.Raycaster();
this.raycaster.params.Line.threshold = 0.4;
this.raycaster.params.Points.threshold = 0.2;
this.pickThreshold = 0.1
window.addEventListener('keydown', this.keyHandler) window.addEventListener('keydown', this.onKeyPress)
domElement.addEventListener('pointerdown', this.picker) domElement.addEventListener('pointerdown', this.onPick)
domElement.addEventListener('pointermove', this.onHover)
this.mode = "" this.mode = ""
this.contraints = []
this.contraintsVal = []
} }
@ -71,37 +120,44 @@ export class Sketcher extends THREE.Group {
} }
keyHandler(e) { onKeyPress(e) {
switch (e.key) { switch (e.key) {
case 'Escape': case 'Escape':
this.clear() this.clear()
this.mode = "" this.mode = ""
break; break;
case 'l': case 'l':
this.addLine() this.domElement.addEventListener('pointerdown', this.onClick_1)
this.mode = "line" this.mode = "line"
break; break;
case 'b': case 'a':
this.solve() this.domElement.addEventListener('pointerdown', this.onClick_1)
this.mode = "arc"
break;
case 'd':
this.delete()
break; break;
case '=': case '=':
this.plane.applyMatrix4(new Matrix4().makeRotationY(0.1)) this.plane.applyMatrix4(new Matrix4().makeRotationY(0.1))
this.orientSketcher() this.orientSketcher()
this.dispatchEvent({ type: 'change' }) this.dispatchEvent({ type: 'change' })
break; break;
case '-': case '-':
this.plane.applyMatrix4(new Matrix4().makeRotationY(-0.1)) this.plane.applyMatrix4(new Matrix4().makeRotationY(-0.1))
this.orientSketcher() this.orientSketcher()
this.dispatchEvent({ type: 'change' }) this.dispatchEvent({ type: 'change' })
break; break;
} }
} }
picker(e) { onHover(e) {
if (this.mode || e.buttons != 1) return if (this.mode || e.buttons) return
if (this.hovered && !this.selected.has(this.hovered)) {
this.hovered.material.color.set(0x555555)
}
this.raycaster.setFromCamera( this.raycaster.setFromCamera(
new THREE.Vector2( new THREE.Vector2(
(e.clientX / window.innerWidth) * 2 - 1, (e.clientX / window.innerWidth) * 2 - 1,
@ -110,73 +166,112 @@ export class Sketcher extends THREE.Group {
this.camera this.camera
); );
// console.log(this.ptsArr) const hoverPts = this.raycaster.intersectObjects(this.ptsArr)
const candidates = this.raycaster.intersectObjects(this.ptsArr)
// console.log(candidates)
if (hoverPts.length) {
let minDist = hoverPts[0].distanceToRay
let idx = 0
if (!candidates.length) return; for (let i = 1; i < hoverPts.length; i++) {
if (hoverPts[i].distanceToRay <= minDist) {
let minDist = candidates[0].distanceToRay minDist = hoverPts[i].distanceToRay
let idx = 0 idx = i
}
for (let i = 1; i < candidates.length; i++) {
if (candidates.distanceToRay < minDist) {
minDist = candidates.distanceToRay
idx = i
} }
}
if (minDist < this.pickThreshold) { hoverPts[idx].object.material.color.set(0xff0000)
this.grabPtIdx = this.ptsArr.indexOf( this.hovered = hoverPts[idx].object
candidates[idx].object this.dispatchEvent({ type: 'change' })
)
} else {
return return
} }
this.domElement.addEventListener('pointermove', this.grabbedMove); const hoverLines = this.raycaster.intersectObjects(this.linesArr)
this.domElement.addEventListener('pointerup', this.grabEnd); 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;
this.dispatchEvent({ type: 'change' })
}
} }
grabbedMove(e) {
onPick(e) {
if (this.mode || e.buttons != 1) return
if (this.hovered) {
this.selected.add(this.hovered)
if (this.hovered.type === "Points") {
this.grabPtIdx = this.ptsArr.indexOf(
this.hovered
)
this.domElement.addEventListener('pointermove', this.onDrag);
this.domElement.addEventListener('pointerup', this.onRelease)
}
} else {
for (let obj of this.selected) {
obj.material.color.set(0x555555)
}
this.dispatchEvent({ type: 'change' })
this.selected.clear()
}
}
onDrag(e) {
const mouseLoc = this.getLocation(e); const mouseLoc = this.getLocation(e);
this.ptsArr[this.grabPtIdx].geometry.attributes.position.set(mouseLoc);
this.moveLinePt(this.grabPtIdx, mouseLoc) this.solve()
this.dispatchEvent({ type: 'change' }) this.dispatchEvent({ type: 'change' })
} }
moveLinePt(ptIdx, absPos) {
this.ptsArr[ptIdx].geometry.attributes.position.set(absPos);
this.solve()
// this.ptsArr[ptIdx].geometry.attributes.position.needsUpdate = true;
// const lineIdx = Math.floor(ptIdx / 2) onRelease() {
// const endPtIdx = (ptIdx % 2) * 3 this.domElement.removeEventListener('pointermove', this.onDrag)
// this.linesArr[lineIdx].geometry.attributes.position.set(absPos, endPtIdx) this.domElement.removeEventListener('pointerup', this.onRelease)
// this.linesArr[lineIdx].geometry.attributes.position.needsUpdate = true;
}
grabEnd() {
this.domElement.removeEventListener('pointermove', this.grabbedMove)
this.domElement.removeEventListener('pointerup', this.grabEnd)
this.ptsArr[this.grabPtIdx].geometry.computeBoundingSphere() this.ptsArr[this.grabPtIdx].geometry.computeBoundingSphere()
// this.grabbedObject = null // this.grabbedObject = null
} }
addLine() {
this.domElement.addEventListener('pointerdown', this.pointStart) delete() {
const deleteSet = new Set()
let idx;
for (let obj of this.selected) {
deleteSet.add(obj)
if (obj.parent == this.linesGroup) {
idx = this.linesArr.indexOf(obj) * 2
deleteSet.add(this.ptsArr[idx])
deleteSet.add(this.ptsArr[idx + 1])
} else if (obj.parent == this.pointsGroup) {
idx = Math.floor(this.pointsArr.indexOf(obj) / 2)
deleteSet.add(this.linesArr[idx])
}
}
for (let obj of deleteSet) {
obj.geometry.dispose()
obj.material.dispose()
obj.parent.remove(obj)
}
this.selected.clear()
this.dispatchEvent({ type: 'change' })
} }
clear() { clear() {
if (this.mode == "") return if (this.mode == "") return
this.domElement.removeEventListener('pointerdown', this.pointStart) this.domElement.removeEventListener('pointerdown', this.onClick_1)
this.domElement.removeEventListener('pointermove', this.move); this.domElement.removeEventListener('pointermove', this.beforeClick_2);
this.domElement.removeEventListener('pointerdown', this.pointEnd); this.domElement.removeEventListener('pointerdown', this.onClick_2);
this.domElement.removeEventListener('pointerdown', this.pointEnd);
const lastLine = this.linesArr[this.linesArr.length - 1] const lastLine = this.linesArr[this.linesArr.length - 1]
this.linesGroup.remove(lastLine) this.linesGroup.remove(lastLine)
@ -201,64 +296,119 @@ export class Sketcher extends THREE.Group {
return this.raycaster.ray.intersectPlane(this.plane).applyMatrix4(this.inverse).toArray() return this.raycaster.ray.intersectPlane(this.plane).applyMatrix4(this.inverse).toArray()
} }
pointStart(e) { onClick_1(e) {
console.log(e)
if (e.buttons !== 1) return if (e.buttons !== 1) return
const mouseLoc = this.getLocation(e); const mouseLoc = this.getLocation(e);
this.lineGeom = new THREE.BufferGeometry() this.p1Geom = new THREE.BufferGeometry().setAttribute('position',
this.lineGeom.setAttribute('position', new THREE.BufferAttribute(new Float32Array(mouseLoc), 3)
new THREE.BufferAttribute( )
new Float32Array(6), 3 this.p1 = new THREE.Points(this.p1Geom,
) new THREE.PointsMaterial().copy(this.pointMaterial)
); );
this.lineGeom.attributes.position.set(mouseLoc)
this.line = new THREE.LineSegments(this.lineGeom, this.lineMaterial);
this.line.frustumCulled = false;
this.linesGroup.add(this.line)
this.p1Geom = new THREE.BufferGeometry() this.p2Geom = new THREE.BufferGeometry().setAttribute('position',
this.p1Geom.setAttribute('position', new THREE.BufferAttribute(new Float32Array(3), 3)
new THREE.BufferAttribute( )
new Float32Array(3), 3 this.p2 = new THREE.Points(this.p2Geom,
) new THREE.PointsMaterial().copy(this.pointMaterial)
); );
this.p1Geom.attributes.position.set(mouseLoc)
this.p1 = new THREE.Points(this.p1Geom, this.pointMaterial);
this.pointsGroup.add(this.p1)
this.p2Geom = new THREE.BufferGeometry() if (this.mode == "line") {
this.p2Geom.setAttribute('position', this.lineGeom = new THREE.BufferGeometry().setAttribute('position',
new THREE.BufferAttribute( new THREE.BufferAttribute(new Float32Array(6), 3)
new Float32Array(3), 3 );
this.lineGeom.attributes.position.set(mouseLoc)
this.line = new THREE.Line(this.lineGeom,
new THREE.LineBasicMaterial().copy(this.lineMaterial)
);
this.line.frustumCulled = false;
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',
new THREE.BufferAttribute(new Float32Array(3 * 37), 3)
) )
);
this.p2 = new THREE.Points(this.p2Geom, this.pointMaterial);
this.pointsGroup.add(this.p2)
this.domElement.removeEventListener('pointerdown', this.pointStart) this.arc = new THREE.Line(this.arcGeom,
this.domElement.addEventListener('pointermove', this.move) new THREE.LineBasicMaterial().copy(this.lineMaterial)
this.domElement.addEventListener('pointerdown', this.pointEnd) );
this.arc.frustumCulled = false;
this.p3Geom = new THREE.BufferGeometry().setAttribute('position',
new THREE.BufferAttribute(new Float32Array(3), 3)
)
this.p3 = new THREE.Points(this.p3Geom,
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)
}
this.domElement.removeEventListener('pointerdown', this.onClick_1)
this.domElement.addEventListener('pointermove', this.beforeClick_2)
this.domElement.addEventListener('pointerdown', this.onClick_2)
} }
move(e) { beforeClick_2(e) {
const mouseLoc = this.getLocation(e); const mouseLoc = this.getLocation(e);
this.lineGeom.attributes.position.set(mouseLoc, 3)
this.lineGeom.attributes.position.needsUpdate = true;
this.p2Geom.attributes.position.set(mouseLoc); this.p2Geom.attributes.position.set(mouseLoc);
this.p2Geom.attributes.position.needsUpdate = true; this.p2Geom.attributes.position.needsUpdate = true;
this.p2Geom.computeBoundingSphere(); this.p2Geom.computeBoundingSphere()
if (this.mode == "line") {
this.lineGeom.attributes.position.set(mouseLoc, 3)
this.lineGeom.attributes.position.needsUpdate = true;
} else if (this.mode == 'arc') {
const [points, center] = get2PtArc(
this.p1Geom.attributes.position.array,
this.p2Geom.attributes.position.array
)
this.arcGeom.attributes.position.set(
points
);
this.arcGeom.attributes.position.needsUpdate = true;
this.p3Geom.attributes.position.set(center);
this.p3Geom.attributes.position.needsUpdate = true;
this.p3Geom.computeBoundingSphere()
}
this.dispatchEvent({ type: 'change' }) this.dispatchEvent({ type: 'change' })
} }
pointEnd(e) { onClick_2(e) {
if (e.buttons !== 1) return; if (e.buttons !== 1) return;
this.domElement.removeEventListener('pointermove', this.move); this.domElement.removeEventListener('pointermove', this.beforeClick_2);
this.domElement.removeEventListener('pointerdown', this.pointEnd); this.domElement.removeEventListener('pointerdown', this.onClick_2);
if (this.mode == "line") {
this.onClick_1(e)
} else if (this.mode == "arc") {
this.pointStart(e)
// this.domElement.addEventListener('pointermove', this.beforeClick_3)
}
}
beforeClick_3(e) {
const mouseLoc = this.getLocation(e);
this.p3Geom.attributes.position.set(mouseLoc);
this.p3Geom.attributes.position.needsUpdate = true;
this.p3Geom.computeBoundingSphere()
} }
solve() { solve() {

View File

@ -73,17 +73,17 @@ function main() {
helpersGroup.add(cameraHelper); helpersGroup.add(cameraHelper);
const camera2 = new THREE.PerspectiveCamera( // const camera2 = new THREE.PerspectiveCamera(
60, // fov // 60, // fov
2, // aspect // 2, // aspect
0.1, // near // 0.1, // near
500, // far // 500, // far
); // );
camera2.position.set(16, 28, 40); // camera2.position.set(16, 28, 40);
camera2.lookAt(0, 5, 0); // camera2.lookAt(0, 5, 0);
const controls2 = new OrbitControls(camera2, view2Elem); // const controls2 = new OrbitControls(camera2, view2Elem);
controls2.target.set(0, 5, 0); // controls2.target.set(0, 5, 0);
controls2.update(); // controls2.update();
@ -155,13 +155,13 @@ function main() {
cameraHelper.visible = false; cameraHelper.visible = false;
renderer.render(scene, camera); renderer.render(scene, camera);
} }
{ // {
const aspect = setScissorForElement(view2Elem); // const aspect = setScissorForElement(view2Elem);
camera2.aspect = aspect; // camera2.aspect = aspect;
camera2.updateProjectionMatrix(); // camera2.updateProjectionMatrix();
cameraHelper.visible = true; // cameraHelper.visible = true;
renderer.render(scene, camera2); // renderer.render(scene, camera2);
} // }
stats.end(); stats.end();
@ -172,7 +172,7 @@ function main() {
controls.addEventListener('change', render); controls.addEventListener('change', render);
controls.addEventListener('start', render); controls.addEventListener('start', render);
controls2.addEventListener('change', render); // controls2.addEventListener('change', render);
sketcher.addEventListener('change', render); sketcher.addEventListener('change', render);
window.addEventListener('resize', render); window.addEventListener('resize', render);
render(); render();

View File

@ -49,7 +49,7 @@ int solver(int nLines, float *ptr)
sys.param[sys.params++] = Slvs_MakeParam(3, g, 0.0); sys.param[sys.params++] = Slvs_MakeParam(3, g, 0.0);
sys.entity[sys.entities++] = Slvs_MakePoint3d(101, g, 1, 2, 3); sys.entity[sys.entities++] = Slvs_MakePoint3d(101, g, 1, 2, 3);
/* and it is parallel to the xy plane, so it has basis vectors (1 0 0) /* and it is parallel to the xy plane, so it has basis vectors (1 0 0)
* and (0 1 0). */ * and (0 1 0). */
Slvs_MakeQuaternion(1, 0, 0, Slvs_MakeQuaternion(1, 0, 0,
0, 1, 0, &qw, &qx, &qy, &qz); 0, 1, 0, &qw, &qx, &qy, &qz);
sys.param[sys.params++] = Slvs_MakeParam(4, g, qw); sys.param[sys.params++] = Slvs_MakeParam(4, g, qw);
@ -94,7 +94,7 @@ int solver(int nLines, float *ptr)
SLVS_C_POINTS_COINCIDENT, SLVS_C_POINTS_COINCIDENT,
200, 200,
0.0, 0.0,
vh - 1, vh - 2, 0, 0); vh - 2, vh - 1, 0, 0);
} }
} }