refactor
parent
e51d171709
commit
23da7dcf83
561
src/Sketcher.js
561
src/Sketcher.js
|
@ -1,561 +0,0 @@
|
||||||
|
|
||||||
import { Matrix4 } from 'three';
|
|
||||||
|
|
||||||
import * as THREE from '../node_modules/three/src/Three'
|
|
||||||
|
|
||||||
import { sketchLine } from './sketchTools'
|
|
||||||
|
|
||||||
|
|
||||||
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 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
|
|
||||||
|
|
||||||
const factor = Math.tan(Math.PI / 3)
|
|
||||||
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]];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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()
|
|
||||||
this.camera = camera;
|
|
||||||
this.domElement = domElement;
|
|
||||||
this.plane = plane;
|
|
||||||
this.matrixAutoUpdate = false;
|
|
||||||
this.sketchNormal = new THREE.Vector3(0, 0, 1)
|
|
||||||
this.orientSketcher(plane)
|
|
||||||
|
|
||||||
this.add(new THREE.PlaneHelper(this.plane, 1, 0xffff00));
|
|
||||||
|
|
||||||
this.colorPt = new THREE.Color('white')
|
|
||||||
this.selected = new Set()
|
|
||||||
|
|
||||||
|
|
||||||
this.lineMaterial = new THREE.LineBasicMaterial({
|
|
||||||
linewidth: 3,
|
|
||||||
color: 0x555555,
|
|
||||||
})
|
|
||||||
this.pointMaterial = new THREE.PointsMaterial({
|
|
||||||
color: 0x555555,
|
|
||||||
size: 3,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
this.onKeyPress = this.onKeyPress.bind(this);
|
|
||||||
|
|
||||||
this.onClick_1 = this.onClick_1.bind(this);
|
|
||||||
this.onClick_2 = this.onClick_2.bind(this);
|
|
||||||
this.beforeClick_2 = this.beforeClick_2.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.params.Line.threshold = 0.4;
|
|
||||||
this.raycaster.params.Points.threshold = 0.2;
|
|
||||||
|
|
||||||
|
|
||||||
window.addEventListener('keydown', this.onKeyPress)
|
|
||||||
domElement.addEventListener('pointerdown', this.onPick)
|
|
||||||
domElement.addEventListener('pointermove', this.onHover)
|
|
||||||
|
|
||||||
|
|
||||||
this.mode = ""
|
|
||||||
|
|
||||||
this.linkedObjs = new Map()
|
|
||||||
this.l_id = 0;
|
|
||||||
|
|
||||||
this.constraints = new Map()
|
|
||||||
this.c_id = 0;
|
|
||||||
|
|
||||||
this.objIdx = new Map()
|
|
||||||
|
|
||||||
this.max_pts = 1000
|
|
||||||
this.buffer = new ArrayBuffer(3*4*this.max_pts);
|
|
||||||
this.ptsBuf = new Float32Array(this.buffer).fill(NaN)
|
|
||||||
|
|
||||||
this.max_links = 1000
|
|
||||||
|
|
||||||
// [0]:type, [1]:pt1, [2]:pt2, [3]:pt3, [4]:pt4
|
|
||||||
this.linksBuf = new Float32Array(this.max_links * 5).fill(NaN)
|
|
||||||
|
|
||||||
this.max_constraints = 1000
|
|
||||||
|
|
||||||
// [0]:type, [1]:val, [2]:pt1, [3]:pt2, [4]:lk1, [5]:lk2
|
|
||||||
this.constraintsBuf = new Float32Array(this.max_constraints * 6).fill(NaN)
|
|
||||||
|
|
||||||
this.subsequent = false;
|
|
||||||
this.ptsBufPt = 0;
|
|
||||||
this.endBufPt = 0;
|
|
||||||
|
|
||||||
this.linkNum = {
|
|
||||||
'line': 0,
|
|
||||||
'arc': 1
|
|
||||||
}
|
|
||||||
|
|
||||||
this.contraintNum = {
|
|
||||||
'coincident': 0,
|
|
||||||
'parallel': 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
orientSketcher() {
|
|
||||||
|
|
||||||
const theta = this.sketchNormal.angleTo(this.plane.normal)
|
|
||||||
const axis = this.sketchNormal.clone().cross(this.plane.normal).normalize()
|
|
||||||
const rot = new THREE.Matrix4().makeRotationAxis(axis, theta)
|
|
||||||
const trans = new THREE.Matrix4().makeTranslation(0, 0, this.plane.constant)
|
|
||||||
|
|
||||||
this.matrix = rot.multiply(trans) // world matrix will auto update in next render
|
|
||||||
this.inverse = this.matrix.clone().invert()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyPress(e) {
|
|
||||||
switch (e.key) {
|
|
||||||
case 'Escape':
|
|
||||||
this.clear()
|
|
||||||
this.mode = ""
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
if (this.mode == 'line') {
|
|
||||||
this.clear()
|
|
||||||
}
|
|
||||||
this.domElement.addEventListener('pointerdown', this.onClick_1)
|
|
||||||
this.mode = "line"
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
this.domElement.addEventListener('pointerdown', this.onClick_1)
|
|
||||||
this.mode = "arc"
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
this.deleteSelected()
|
|
||||||
break;
|
|
||||||
case '=':
|
|
||||||
this.plane.applyMatrix4(new Matrix4().makeRotationY(0.1))
|
|
||||||
this.orientSketcher()
|
|
||||||
this.dispatchEvent({ type: 'change' })
|
|
||||||
break;
|
|
||||||
case '-':
|
|
||||||
this.plane.applyMatrix4(new Matrix4().makeRotationY(-0.1))
|
|
||||||
this.orientSketcher()
|
|
||||||
this.dispatchEvent({ type: 'change' })
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onHover(e) {
|
|
||||||
if (this.mode || e.buttons) return
|
|
||||||
|
|
||||||
if (this.hovered && !this.selected.has(this.hovered)) {
|
|
||||||
this.hovered.material.color.set(0x555555)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.raycaster.setFromCamera(
|
|
||||||
new THREE.Vector2(
|
|
||||||
(e.clientX / window.innerWidth) * 2 - 1,
|
|
||||||
- (e.clientY / window.innerHeight) * 2 + 1
|
|
||||||
),
|
|
||||||
this.camera
|
|
||||||
);
|
|
||||||
|
|
||||||
const hoverPts = this.raycaster.intersectObjects(this.children)
|
|
||||||
|
|
||||||
if (hoverPts.length) {
|
|
||||||
let minDist = Infinity;
|
|
||||||
let idx = 0
|
|
||||||
for (let i = 0; i < hoverPts.length; i++) {
|
|
||||||
if (hoverPts[i].distanceToRay && hoverPts[i].distanceToRay <= minDist) {
|
|
||||||
minDist = hoverPts[i].distanceToRay
|
|
||||||
idx = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hoverPts[idx].object.material.color.set(0xff0000)
|
|
||||||
this.hovered = hoverPts[idx].object
|
|
||||||
this.dispatchEvent({ type: 'change' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (this.hovered) {
|
|
||||||
this.hovered = null;
|
|
||||||
this.dispatchEvent({ type: 'change' })
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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.children.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);
|
|
||||||
|
|
||||||
this.ptsBuf.set(
|
|
||||||
mouseLoc,
|
|
||||||
this.objIdx.get(this.children[this.grabPtIdx].id) * 3
|
|
||||||
)
|
|
||||||
|
|
||||||
this.solve()
|
|
||||||
this.dispatchEvent({ type: 'change' })
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onRelease() {
|
|
||||||
this.domElement.removeEventListener('pointermove', this.onDrag)
|
|
||||||
this.domElement.removeEventListener('pointerup', this.onRelease)
|
|
||||||
this.children[this.grabPtIdx].geometry.computeBoundingSphere()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
deleteSelected() {
|
|
||||||
let minI = this.children.length;
|
|
||||||
|
|
||||||
for (let obj of this.selected) {
|
|
||||||
minI = Math.min(minI, this.delete(obj))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updatePointsBuffer(minI)
|
|
||||||
this.updateOtherBuffers()
|
|
||||||
|
|
||||||
this.selected.clear()
|
|
||||||
this.dispatchEvent({ type: 'change' })
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteConstraints(c_id) {
|
|
||||||
for (let ob of this.constraints.get(c_id)[2]) {
|
|
||||||
if (ob == -1) continue
|
|
||||||
ob.constraints.delete(c_id)
|
|
||||||
}
|
|
||||||
this.constraints.delete(c_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateOtherBuffers() {
|
|
||||||
let i = 0
|
|
||||||
for (let [key, obj] of this.constraints) {
|
|
||||||
this.constraintsBuf.set(
|
|
||||||
[
|
|
||||||
this.contraintNum[obj[0]], obj[1],
|
|
||||||
...obj[2].map(ele => this.objIdx.get(ele.id) ?? -1),
|
|
||||||
],
|
|
||||||
(i) * 6
|
|
||||||
)
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
for (let [key, obj] of this.linkedObjs) {
|
|
||||||
this.linksBuf.set(
|
|
||||||
[
|
|
||||||
this.linkNum[obj[0]],
|
|
||||||
...obj[1].map(ele => this.objIdx.get(ele.id) ?? -1),
|
|
||||||
],
|
|
||||||
(i) * 5
|
|
||||||
)
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(obj) {
|
|
||||||
|
|
||||||
const link = this.linkedObjs.get(obj.l_id)[1]
|
|
||||||
|
|
||||||
let i = this.children.indexOf(link[0])
|
|
||||||
if (i == -1) return Infinity
|
|
||||||
|
|
||||||
for (let j = 0; j < link.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.length)
|
|
||||||
|
|
||||||
this.linkedObjs.delete(obj.l_id)
|
|
||||||
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePointsBuffer(startingIdx = 0) {
|
|
||||||
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[3 * i] = obj.geometry.attributes.position.array[0]
|
|
||||||
this.ptsBuf[3 * i + 1] = obj.geometry.attributes.position.array[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
if (this.mode == "") return
|
|
||||||
|
|
||||||
if (this.mode == "line") {
|
|
||||||
this.domElement.removeEventListener('pointerdown', this.onClick_1)
|
|
||||||
this.domElement.removeEventListener('pointermove', this.beforeClick_2);
|
|
||||||
this.domElement.removeEventListener('pointerdown', this.onClick_2);
|
|
||||||
|
|
||||||
this.delete(this.children[this.children.length - 1])
|
|
||||||
|
|
||||||
this.dispatchEvent({ type: 'change' })
|
|
||||||
this.subsequent = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getLocation(e) {
|
|
||||||
this.raycaster.setFromCamera(
|
|
||||||
new THREE.Vector2(
|
|
||||||
(e.clientX / window.innerWidth) * 2 - 1,
|
|
||||||
- (e.clientY / window.innerHeight) * 2 + 1
|
|
||||||
),
|
|
||||||
this.camera
|
|
||||||
);
|
|
||||||
// return this.worldToLocal(this.raycaster.ray.intersectPlane(this.plane)).toArray()
|
|
||||||
return this.raycaster.ray.intersectPlane(this.plane)
|
|
||||||
.applyMatrix4(this.inverse).toArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
onClick_1(e) {
|
|
||||||
if (e.buttons !== 1) return
|
|
||||||
const mouseLoc = this.getLocation(e);
|
|
||||||
|
|
||||||
|
|
||||||
if (this.mode == "line") {
|
|
||||||
|
|
||||||
sketchLine.call(this, mouseLoc)
|
|
||||||
|
|
||||||
} else if (this.mode == "arc") {
|
|
||||||
this.p1Geom = new THREE.BufferGeometry().setAttribute('position',
|
|
||||||
new THREE.BufferAttribute(new Float32Array(mouseLoc), 3)
|
|
||||||
)
|
|
||||||
this.p1 = new THREE.Points(this.p1Geom,
|
|
||||||
new THREE.PointsMaterial().copy(this.pointMaterial)
|
|
||||||
);
|
|
||||||
this.p1.matrixAutoUpdate = false;
|
|
||||||
this.p1.constraints = new Set()
|
|
||||||
|
|
||||||
this.p2Geom = new THREE.BufferGeometry().setAttribute('position',
|
|
||||||
new THREE.BufferAttribute(new Float32Array(3), 3)
|
|
||||||
)
|
|
||||||
this.p2 = new THREE.Points(
|
|
||||||
this.p2Geom,
|
|
||||||
new THREE.PointsMaterial().copy(this.pointMaterial)
|
|
||||||
);
|
|
||||||
this.p2.matrixAutoUpdate = false;
|
|
||||||
this.p2.constraints = new Set()
|
|
||||||
|
|
||||||
this.arcGeom = new THREE.BufferGeometry().setAttribute('position',
|
|
||||||
new THREE.BufferAttribute(new Float32Array(3 * 37), 3)
|
|
||||||
)
|
|
||||||
|
|
||||||
this.arc = new THREE.Line(this.arcGeom,
|
|
||||||
new THREE.LineBasicMaterial().copy(this.lineMaterial)
|
|
||||||
);
|
|
||||||
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.toPush = [this.p1, this.p2, this.p3, this.arc]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.updatePoint = this.children.length
|
|
||||||
this.add(...this.toPush)
|
|
||||||
|
|
||||||
this.linkedObjs.set(this.l_id, [this.mode, this.toPush])
|
|
||||||
|
|
||||||
for (let obj of this.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)
|
|
||||||
this.domElement.addEventListener('pointerdown', this.onClick_2)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
beforeClick_2(e) {
|
|
||||||
const mouseLoc = this.getLocation(e);
|
|
||||||
|
|
||||||
this.p2Geom.attributes.position.set(mouseLoc);
|
|
||||||
this.p2Geom.attributes.position.needsUpdate = true;
|
|
||||||
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' })
|
|
||||||
}
|
|
||||||
|
|
||||||
onClick_2(e) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeClick_3(e) {
|
|
||||||
const mouseLoc = this.getLocation(e);
|
|
||||||
this.p3Geom.attributes.position.set(mouseLoc);
|
|
||||||
this.p3Geom.attributes.position.needsUpdate = true;
|
|
||||||
this.p3Geom.computeBoundingSphere()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
solve() {
|
|
||||||
|
|
||||||
const pts_buffer =
|
|
||||||
Module._malloc(this.ptsBuf.length * this.ptsBuf.BYTES_PER_ELEMENT)
|
|
||||||
Module.HEAPF32.set(this.ptsBuf, pts_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.children.length, pts_buffer,
|
|
||||||
this.constraints.size, constraints_buffer,
|
|
||||||
this.linkedObjs.size, links_buffer)
|
|
||||||
|
|
||||||
let ptr = pts_buffer >> 2;
|
|
||||||
|
|
||||||
|
|
||||||
for (let i = 0; i < this.children.length; i += 1) {
|
|
||||||
|
|
||||||
const pos = this.children[i].geometry.attributes.position;
|
|
||||||
if (isNaN(Module.HEAPF32[ptr])) {
|
|
||||||
pos.array[0] = Module.HEAPF32[ptr - 6]
|
|
||||||
pos.array[1] = Module.HEAPF32[ptr - 5]
|
|
||||||
pos.array[3] = Module.HEAPF32[ptr - 3]
|
|
||||||
pos.array[4] = Module.HEAPF32[ptr - 2]
|
|
||||||
} else {
|
|
||||||
pos.array[0] = Module.HEAPF32[ptr]
|
|
||||||
pos.array[1] = Module.HEAPF32[ptr + 1]
|
|
||||||
}
|
|
||||||
ptr += 3;
|
|
||||||
pos.needsUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.dispatchEvent({ type: 'change' })
|
|
||||||
|
|
||||||
Module._free(pts_buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
import * as THREE from '../node_modules/three/src/Three';
|
import * as THREE from '../node_modules/three/src/Three';
|
||||||
import { OrbitControls } from './OrbitControls'
|
import { OrbitControls } from './OrbitControls'
|
||||||
import { TrackballControls } from './trackball'
|
import { TrackballControls } from './trackball'
|
||||||
import { Sketcher } from './Sketcher'
|
import { Sketcher } from './sketcher/Sketcher'
|
||||||
import GUI from '../node_modules/dat.gui/src/dat/gui/GUI.js'
|
import GUI from '../node_modules/dat.gui/src/dat/gui/GUI.js'
|
||||||
import Stats from '../node_modules/three/examples/jsm/libs/stats.module.js';
|
import Stats from '../node_modules/three/examples/jsm/libs/stats.module.js';
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
import * as THREE from '../node_modules/three/src/Three'
|
|
||||||
|
|
||||||
|
|
||||||
export function sketchLine(mouseLoc) {
|
|
||||||
this.p1Geom = new THREE.BufferGeometry().setAttribute('position',
|
|
||||||
new THREE.BufferAttribute(new Float32Array(3), 3)
|
|
||||||
)
|
|
||||||
this.p1 = new THREE.Points(this.p1Geom,
|
|
||||||
new THREE.PointsMaterial().copy(this.pointMaterial)
|
|
||||||
);
|
|
||||||
this.p1.matrixAutoUpdate = false;
|
|
||||||
this.p1.constraints = new Set()
|
|
||||||
|
|
||||||
this.p2Geom = new THREE.BufferGeometry().setAttribute('position',
|
|
||||||
new THREE.BufferAttribute(new Float32Array(3), 3)
|
|
||||||
)
|
|
||||||
this.p2 = new THREE.Points(
|
|
||||||
this.p2Geom,
|
|
||||||
new THREE.PointsMaterial().copy(this.pointMaterial)
|
|
||||||
);
|
|
||||||
this.p2.matrixAutoUpdate = false;
|
|
||||||
this.p2.constraints = new Set()
|
|
||||||
|
|
||||||
this.lineGeom = new THREE.BufferGeometry().setAttribute('position',
|
|
||||||
new THREE.BufferAttribute(new Float32Array(6), 3)
|
|
||||||
);
|
|
||||||
this.line = new THREE.Line(this.lineGeom,
|
|
||||||
new THREE.LineBasicMaterial().copy(this.lineMaterial)
|
|
||||||
);
|
|
||||||
this.line.matrixAutoUpdate = false;
|
|
||||||
this.line.frustumCulled = false;
|
|
||||||
this.line.constraints = new Set()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.lineGeom.attributes.position.set(mouseLoc)
|
|
||||||
this.p1Geom.attributes.position.set(mouseLoc)
|
|
||||||
|
|
||||||
this.toPush = [this.p1, this.p2, this.line];
|
|
||||||
|
|
||||||
if (this.subsequent) {
|
|
||||||
|
|
||||||
this.constraints.set(this.c_id,
|
|
||||||
[
|
|
||||||
'coincident', -1,
|
|
||||||
[this.children[this.children.length - 2], this.p1, -1, -1]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
this.p1.constraints.add(this.c_id)
|
|
||||||
this.children[this.children.length - 2].constraints.add(this.c_id)
|
|
||||||
this.c_id += 1
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,281 @@
|
||||||
|
|
||||||
|
import { Matrix4 } from 'three';
|
||||||
|
|
||||||
|
import * as THREE from 'three/src/Three'
|
||||||
|
|
||||||
|
import { onClick_1, onClick_2, beforeClick_2, clear } from './drawEvents'
|
||||||
|
import { onHover, onDrag, onPick, onRelease } from './pickEvents'
|
||||||
|
|
||||||
|
|
||||||
|
const lineMaterial = new THREE.LineBasicMaterial({
|
||||||
|
linewidth: 2,
|
||||||
|
color: 0x555555,
|
||||||
|
})
|
||||||
|
|
||||||
|
const pointMaterial = new THREE.PointsMaterial({
|
||||||
|
color: 0x555555,
|
||||||
|
size: 4,
|
||||||
|
})
|
||||||
|
|
||||||
|
class Sketcher extends THREE.Group {
|
||||||
|
constructor(camera, domElement, plane) {
|
||||||
|
super()
|
||||||
|
this.camera = camera;
|
||||||
|
this.domElement = domElement;
|
||||||
|
this.matrixAutoUpdate = false;
|
||||||
|
|
||||||
|
this.plane = plane;
|
||||||
|
this.sketchNormal = new THREE.Vector3(0, 0, 1)
|
||||||
|
this.orientSketcher(plane)
|
||||||
|
this.add(new THREE.PlaneHelper(this.plane, 1, 0xffff00));
|
||||||
|
|
||||||
|
this.raycaster = new THREE.Raycaster();
|
||||||
|
this.raycaster.params.Line.threshold = 0.4;
|
||||||
|
this.raycaster.params.Points.threshold = 0.4;
|
||||||
|
|
||||||
|
// [0]:x, [1]:y, [2]:z
|
||||||
|
this.objIdx = new Map()
|
||||||
|
this.max_pts = 1000
|
||||||
|
this.ptsBuf = new Float32Array(this.max_pts * 3).fill(NaN)
|
||||||
|
|
||||||
|
// [0]:type, [1]:pt1, [2]:pt2, [3]:pt3, [4]:pt4
|
||||||
|
this.linkedObjs = new Map()
|
||||||
|
this.l_id = 0;
|
||||||
|
this.max_links = 1000
|
||||||
|
this.linksBuf = new Float32Array(this.max_links * 5).fill(NaN)
|
||||||
|
this.linkNum = {
|
||||||
|
'line': 0,
|
||||||
|
'arc': 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// [0]:type, [1]:val, [2]:pt1, [3]:pt2, [4]:lk1, [5]:lk2
|
||||||
|
this.constraints = new Map()
|
||||||
|
this.c_id = 0;
|
||||||
|
this.max_constraints = 1000
|
||||||
|
this.constraintsBuf = new Float32Array(this.max_constraints * 6).fill(NaN)
|
||||||
|
this.contraintNum = {
|
||||||
|
'coincident': 0,
|
||||||
|
'parallel': 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.onClick_1 = onClick_1.bind(this);
|
||||||
|
this.beforeClick_2 = beforeClick_2.bind(this);
|
||||||
|
this.onClick_2 = onClick_2.bind(this);
|
||||||
|
|
||||||
|
this.onHover = onHover.bind(this);
|
||||||
|
this.onPick = onPick.bind(this);
|
||||||
|
this.onDrag = onDrag.bind(this);
|
||||||
|
this.onRelease = onRelease.bind(this);
|
||||||
|
|
||||||
|
this.onKeyPress = this.onKeyPress.bind(this);
|
||||||
|
|
||||||
|
window.addEventListener('keydown', this.onKeyPress)
|
||||||
|
domElement.addEventListener('pointerdown', this.onPick)
|
||||||
|
domElement.addEventListener('pointermove', this.onHover)
|
||||||
|
|
||||||
|
this.mode = ""
|
||||||
|
this.subsequent = false;
|
||||||
|
this.selected = new Set()
|
||||||
|
this.target = new THREE.Vector3();
|
||||||
|
}
|
||||||
|
|
||||||
|
orientSketcher() {
|
||||||
|
const theta = this.sketchNormal.angleTo(this.plane.normal)
|
||||||
|
const axis = this.sketchNormal.clone().cross(this.plane.normal).normalize()
|
||||||
|
const rot = new THREE.Matrix4().makeRotationAxis(axis, theta)
|
||||||
|
const trans = new THREE.Matrix4().makeTranslation(0, 0, this.plane.constant)
|
||||||
|
|
||||||
|
this.matrix = rot.multiply(trans) // world matrix will auto update in next render
|
||||||
|
this.inverse = this.matrix.clone().invert()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyPress(e) {
|
||||||
|
switch (e.key) {
|
||||||
|
case 'Escape':
|
||||||
|
clear.bind(this)()
|
||||||
|
this.mode = ""
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
if (this.mode == 'line') {
|
||||||
|
this.clear()
|
||||||
|
}
|
||||||
|
this.domElement.addEventListener('pointerdown', this.onClick_1)
|
||||||
|
this.mode = "line"
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
this.domElement.addEventListener('pointerdown', this.onClick_1)
|
||||||
|
this.mode = "arc"
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
this.deleteSelected()
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
this.plane.applyMatrix4(new Matrix4().makeRotationY(0.1))
|
||||||
|
this.orientSketcher()
|
||||||
|
this.dispatchEvent({ type: 'change' })
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
this.plane.applyMatrix4(new Matrix4().makeRotationY(-0.1))
|
||||||
|
this.orientSketcher()
|
||||||
|
this.dispatchEvent({ type: 'change' })
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
deleteSelected() {
|
||||||
|
let minI = this.children.length;
|
||||||
|
|
||||||
|
for (let obj of this.selected) {
|
||||||
|
minI = Math.min(minI, this.delete(obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updatePointsBuffer(minI)
|
||||||
|
this.updateOtherBuffers()
|
||||||
|
|
||||||
|
this.selected.clear()
|
||||||
|
this.dispatchEvent({ type: 'change' })
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteConstraints(c_id) {
|
||||||
|
for (let ob of this.constraints.get(c_id)[2]) {
|
||||||
|
if (ob == -1) continue
|
||||||
|
ob.constraints.delete(c_id)
|
||||||
|
}
|
||||||
|
this.constraints.delete(c_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOtherBuffers() {
|
||||||
|
let i = 0
|
||||||
|
for (let [key, obj] of this.constraints) {
|
||||||
|
this.constraintsBuf.set(
|
||||||
|
[
|
||||||
|
this.contraintNum[obj[0]], obj[1],
|
||||||
|
...obj[2].map(ele => this.objIdx.get(ele.id) ?? -1),
|
||||||
|
],
|
||||||
|
(i) * 6
|
||||||
|
)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
for (let [key, obj] of this.linkedObjs) {
|
||||||
|
this.linksBuf.set(
|
||||||
|
[
|
||||||
|
this.linkNum[obj[0]],
|
||||||
|
...obj[1].map(ele => this.objIdx.get(ele.id) ?? -1),
|
||||||
|
],
|
||||||
|
(i) * 5
|
||||||
|
)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(obj) {
|
||||||
|
let link = this.linkedObjs.get(obj.l_id)
|
||||||
|
if (!link) return Infinity;
|
||||||
|
link = link[1]
|
||||||
|
|
||||||
|
let i = this.children.indexOf(link[0])
|
||||||
|
|
||||||
|
for (let j = 0; j < link.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.length)
|
||||||
|
|
||||||
|
this.linkedObjs.delete(obj.l_id)
|
||||||
|
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePointsBuffer(startingIdx = 0) {
|
||||||
|
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.set(obj.geometry.attributes.position.array, 3 * i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getLocation(e) {
|
||||||
|
this.raycaster.setFromCamera(
|
||||||
|
new THREE.Vector2(
|
||||||
|
(e.clientX / window.innerWidth) * 2 - 1,
|
||||||
|
- (e.clientY / window.innerHeight) * 2 + 1
|
||||||
|
),
|
||||||
|
this.camera
|
||||||
|
);
|
||||||
|
|
||||||
|
this.raycaster.ray.intersectPlane(this.plane, this.target).applyMatrix4(this.inverse)
|
||||||
|
|
||||||
|
return this.target.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
solve() {
|
||||||
|
|
||||||
|
const pts_buffer =
|
||||||
|
Module._malloc(this.ptsBuf.length * this.ptsBuf.BYTES_PER_ELEMENT)
|
||||||
|
Module.HEAPF32.set(this.ptsBuf, pts_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.children.length, pts_buffer,
|
||||||
|
this.constraints.size, constraints_buffer,
|
||||||
|
this.linkedObjs.size, links_buffer)
|
||||||
|
|
||||||
|
let ptr = pts_buffer >> 2;
|
||||||
|
|
||||||
|
|
||||||
|
for (let i = 0; i < this.children.length; i += 1) {
|
||||||
|
|
||||||
|
const pos = this.children[i].geometry.attributes.position;
|
||||||
|
if (isNaN(Module.HEAPF32[ptr])) {
|
||||||
|
|
||||||
|
pos.array[0] = Module.HEAPF32[ptr - 6]
|
||||||
|
pos.array[1] = Module.HEAPF32[ptr - 5]
|
||||||
|
pos.array[3] = Module.HEAPF32[ptr - 3]
|
||||||
|
pos.array[4] = Module.HEAPF32[ptr - 2]
|
||||||
|
} else {
|
||||||
|
pos.array[0] = Module.HEAPF32[ptr]
|
||||||
|
pos.array[1] = Module.HEAPF32[ptr + 1]
|
||||||
|
}
|
||||||
|
ptr += 3;
|
||||||
|
pos.needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.dispatchEvent({ type: 'change' })
|
||||||
|
|
||||||
|
Module._free(pts_buffer)
|
||||||
|
Module._free(links_buffer)
|
||||||
|
Module._free(constraints_buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export { Sketcher, lineMaterial, pointMaterial }
|
|
@ -0,0 +1,73 @@
|
||||||
|
|
||||||
|
import { sketchArc, sketchArc2 } from './sketchArc'
|
||||||
|
import { sketchLine, sketchLine2 } from './sketchLine'
|
||||||
|
|
||||||
|
export function onClick_1(e) {
|
||||||
|
if (e.buttons !== 1) return
|
||||||
|
this.domElement.removeEventListener('pointerdown', this.onClick_1)
|
||||||
|
const mouseLoc = this.getLocation(e);
|
||||||
|
|
||||||
|
if (this.mode == "line") {
|
||||||
|
this.toPush = sketchLine.call(this, mouseLoc)
|
||||||
|
} else if (this.mode == "arc") {
|
||||||
|
this.toPush = sketchArc(mouseLoc)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updatePoint = this.children.length
|
||||||
|
this.add(...this.toPush)
|
||||||
|
|
||||||
|
this.linkedObjs.set(this.l_id, [this.mode, this.toPush])
|
||||||
|
for (let obj of this.toPush) {
|
||||||
|
obj.l_id = this.l_id
|
||||||
|
}
|
||||||
|
this.l_id += 1
|
||||||
|
|
||||||
|
this.domElement.addEventListener('pointermove', this.beforeClick_2)
|
||||||
|
this.domElement.addEventListener('pointerdown', this.onClick_2)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function beforeClick_2(e) {
|
||||||
|
const mouseLoc = this.getLocation(e);
|
||||||
|
|
||||||
|
if (this.mode == "line") {
|
||||||
|
sketchLine2(mouseLoc, this.toPush)
|
||||||
|
} else if (this.mode == 'arc') {
|
||||||
|
sketchArc2(mouseLoc, this.toPush)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dispatchEvent({ type: 'change' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onClick_2(e) {
|
||||||
|
if (e.buttons !== 1) return;
|
||||||
|
this.domElement.removeEventListener('pointermove', this.beforeClick_2);
|
||||||
|
this.domElement.removeEventListener('pointerdown', this.onClick_2);
|
||||||
|
|
||||||
|
this.updatePointsBuffer(this.updatePoint)
|
||||||
|
this.updateOtherBuffers()
|
||||||
|
|
||||||
|
if (this.mode == "line") {
|
||||||
|
|
||||||
|
this.subsequent = true
|
||||||
|
this.onClick_1(e)
|
||||||
|
|
||||||
|
} else if (this.mode == "arc") {
|
||||||
|
// this.domElement.addEventListener('pointermove', this.beforeClick_3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clear() {
|
||||||
|
if (this.mode == "") return
|
||||||
|
|
||||||
|
if (this.mode == "line") {
|
||||||
|
this.domElement.removeEventListener('pointerdown', this.onClick_1)
|
||||||
|
this.domElement.removeEventListener('pointermove', this.beforeClick_2);
|
||||||
|
this.domElement.removeEventListener('pointerdown', this.onClick_2);
|
||||||
|
|
||||||
|
this.delete(this.children[this.children.length - 1])
|
||||||
|
|
||||||
|
this.dispatchEvent({ type: 'change' })
|
||||||
|
this.subsequent = false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
export 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 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
|
||||||
|
|
||||||
|
const factor = Math.tan(Math.PI / 3)
|
||||||
|
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 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;
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
import * as THREE from 'three/src/Three'
|
||||||
|
|
||||||
|
export function onHover(e) {
|
||||||
|
if (this.mode || e.buttons) return
|
||||||
|
if (this.hovered && !this.selected.has(this.hovered)) {
|
||||||
|
this.hovered.material.color.set(0x555555)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.raycaster.setFromCamera(
|
||||||
|
new THREE.Vector2(
|
||||||
|
(e.clientX / window.innerWidth) * 2 - 1,
|
||||||
|
- (e.clientY / window.innerHeight) * 2 + 1
|
||||||
|
),
|
||||||
|
this.camera
|
||||||
|
);
|
||||||
|
|
||||||
|
const hoverPts = this.raycaster.intersectObjects(this.children)
|
||||||
|
// console.log(hoverPts)
|
||||||
|
|
||||||
|
if (hoverPts.length) {
|
||||||
|
let minDist = Infinity;
|
||||||
|
let idx = 0
|
||||||
|
for (let i = 0; i < hoverPts.length; i++) {
|
||||||
|
if (hoverPts[i].distanceToRay && hoverPts[i].distanceToRay <= minDist) {
|
||||||
|
minDist = hoverPts[i].distanceToRay
|
||||||
|
idx = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hoverPts[idx].object.material.color.set(0xff0000)
|
||||||
|
this.hovered = hoverPts[idx].object
|
||||||
|
this.dispatchEvent({ type: 'change' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (this.hovered) {
|
||||||
|
this.hovered = null;
|
||||||
|
this.dispatchEvent({ type: 'change' })
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function 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.children.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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onDrag(e) {
|
||||||
|
const mouseLoc = this.getLocation(e);
|
||||||
|
|
||||||
|
this.ptsBuf.set(
|
||||||
|
mouseLoc,
|
||||||
|
this.objIdx.get(this.children[this.grabPtIdx].id) * 3
|
||||||
|
)
|
||||||
|
|
||||||
|
this.solve()
|
||||||
|
this.dispatchEvent({ type: 'change' })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function onRelease() {
|
||||||
|
this.domElement.removeEventListener('pointermove', this.onDrag)
|
||||||
|
this.domElement.removeEventListener('pointerup', this.onRelease)
|
||||||
|
this.children[this.grabPtIdx].geometry.computeBoundingSphere()
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
import * as THREE from 'three/src/Three'
|
||||||
|
|
||||||
|
import { get2PtArc, get3PtArc } from './geometry'
|
||||||
|
import {lineMaterial, pointMaterial} from './Sketcher'
|
||||||
|
|
||||||
|
export function sketchArc(mouseLoc) {
|
||||||
|
const p1Geom = new THREE.BufferGeometry().setAttribute('position',
|
||||||
|
new THREE.BufferAttribute(new Float32Array(mouseLoc), 3)
|
||||||
|
)
|
||||||
|
const p1 = new THREE.Points(p1Geom,
|
||||||
|
new THREE.PointsMaterial().copy(pointMaterial)
|
||||||
|
);
|
||||||
|
p1.matrixAutoUpdate = false;
|
||||||
|
p1.constraints = new Set()
|
||||||
|
|
||||||
|
const p2Geom = new THREE.BufferGeometry().setAttribute('position',
|
||||||
|
new THREE.BufferAttribute(new Float32Array(3), 3)
|
||||||
|
)
|
||||||
|
const p2 = new THREE.Points(
|
||||||
|
p2Geom,
|
||||||
|
new THREE.PointsMaterial().copy(pointMaterial)
|
||||||
|
);
|
||||||
|
p2.matrixAutoUpdate = false;
|
||||||
|
p2.constraints = new Set()
|
||||||
|
|
||||||
|
const arcGeom = new THREE.BufferGeometry().setAttribute('position',
|
||||||
|
new THREE.BufferAttribute(new Float32Array(3 * 37), 3)
|
||||||
|
)
|
||||||
|
|
||||||
|
const arc = new THREE.Line(arcGeom,
|
||||||
|
new THREE.LineBasicMaterial().copy(lineMaterial)
|
||||||
|
);
|
||||||
|
arc.frustumCulled = false;
|
||||||
|
|
||||||
|
const p3Geom = new THREE.BufferGeometry().setAttribute('position',
|
||||||
|
new THREE.BufferAttribute(new Float32Array(3), 3)
|
||||||
|
)
|
||||||
|
const p3 = new THREE.Points(p3Geom,
|
||||||
|
new THREE.PointsMaterial().copy(pointMaterial)
|
||||||
|
);
|
||||||
|
|
||||||
|
return [p1, p2, p3, arc]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sketchArc2(mouseLoc, toPush) {
|
||||||
|
const [p1, p2, p3, arc] = toPush
|
||||||
|
|
||||||
|
p2.geometry.attributes.position.set(mouseLoc);
|
||||||
|
p2.geometry.attributes.position.needsUpdate = true;
|
||||||
|
p2.geometry.computeBoundingSphere();
|
||||||
|
|
||||||
|
const [points, center] = get2PtArc(
|
||||||
|
p1.geometry.attributes.position.array,
|
||||||
|
p2.geometry.attributes.position.array
|
||||||
|
)
|
||||||
|
arc.geometry.attributes.position.set(
|
||||||
|
points
|
||||||
|
);
|
||||||
|
arc.geometry.attributes.position.needsUpdate = true;
|
||||||
|
p3.geometry.attributes.position.set(center);
|
||||||
|
p3.geometry.attributes.position.needsUpdate = true;
|
||||||
|
p3.geometry.computeBoundingSphere()
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
import * as THREE from 'three/src/Three'
|
||||||
|
|
||||||
|
import {lineMaterial, pointMaterial} from './Sketcher'
|
||||||
|
|
||||||
|
export function sketchLine(mouseLoc) {
|
||||||
|
|
||||||
|
const p1Geom = new THREE.BufferGeometry().setAttribute('position',
|
||||||
|
new THREE.BufferAttribute(new Float32Array(3), 3)
|
||||||
|
)
|
||||||
|
const p1 = new THREE.Points(p1Geom,
|
||||||
|
new THREE.PointsMaterial().copy(pointMaterial)
|
||||||
|
);
|
||||||
|
p1.matrixAutoUpdate = false;
|
||||||
|
p1.constraints = new Set()
|
||||||
|
|
||||||
|
const p2Geom = new THREE.BufferGeometry().setAttribute('position',
|
||||||
|
new THREE.BufferAttribute(new Float32Array(3), 3)
|
||||||
|
)
|
||||||
|
const p2 = new THREE.Points(
|
||||||
|
p2Geom,
|
||||||
|
new THREE.PointsMaterial().copy(pointMaterial)
|
||||||
|
);
|
||||||
|
p2.matrixAutoUpdate = false;
|
||||||
|
p2.constraints = new Set()
|
||||||
|
|
||||||
|
const lineGeom = new THREE.BufferGeometry().setAttribute('position',
|
||||||
|
new THREE.BufferAttribute(new Float32Array(6), 3)
|
||||||
|
);
|
||||||
|
const line = new THREE.Line(lineGeom,
|
||||||
|
new THREE.LineBasicMaterial().copy(lineMaterial)
|
||||||
|
);
|
||||||
|
line.matrixAutoUpdate = false;
|
||||||
|
line.frustumCulled = false;
|
||||||
|
line.constraints = new Set()
|
||||||
|
|
||||||
|
|
||||||
|
lineGeom.attributes.position.set(mouseLoc)
|
||||||
|
p1Geom.attributes.position.set(mouseLoc)
|
||||||
|
|
||||||
|
if (this.subsequent) {
|
||||||
|
|
||||||
|
this.constraints.set(this.c_id,
|
||||||
|
[
|
||||||
|
'coincident', -1,
|
||||||
|
[this.children[this.children.length - 2], p1, -1, -1]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
p1.constraints.add(this.c_id)
|
||||||
|
this.children[this.children.length - 2].constraints.add(this.c_id)
|
||||||
|
this.c_id += 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return [p1, p2, line];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sketchLine2(mouseLoc, toPush) {
|
||||||
|
|
||||||
|
const [p1, p2, line] = toPush
|
||||||
|
|
||||||
|
p2.geometry.attributes.position.set(mouseLoc);
|
||||||
|
p2.geometry.attributes.position.needsUpdate = true;
|
||||||
|
p2.geometry.computeBoundingSphere();
|
||||||
|
|
||||||
|
line.geometry.attributes.position.set(mouseLoc, 3)
|
||||||
|
line.geometry.attributes.position.needsUpdate = true;
|
||||||
|
}
|
10
src/utils.js
10
src/utils.js
|
@ -1,10 +0,0 @@
|
||||||
export function KeyboardController() {
|
|
||||||
this.state="";
|
|
||||||
window.addEventListener('keydown', (e)=> {
|
|
||||||
if (e.key == "Escape"){
|
|
||||||
this.state = ""
|
|
||||||
} else {
|
|
||||||
this.state = e.key
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -0,0 +1,269 @@
|
||||||
|
/*-----------------------------------------------------------------------------
|
||||||
|
* Some sample code for slvs.dll. We draw some geometric entities, provide
|
||||||
|
* initial guesses for their positions, and then constrain them. The solver
|
||||||
|
* calculates their new positions, in order to satisfy the constraints.
|
||||||
|
*
|
||||||
|
* Copyright 2008-2013 Jonathan Westhues.
|
||||||
|
*---------------------------------------------------------------------------*/
|
||||||
|
#ifdef WIN32
|
||||||
|
# include <windows.h>
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <slvs.h>
|
||||||
|
|
||||||
|
static Slvs_System sys;
|
||||||
|
|
||||||
|
static void *CheckMalloc(size_t n)
|
||||||
|
{
|
||||||
|
void *r = malloc(n);
|
||||||
|
if(!r) {
|
||||||
|
printf("out of memory!\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------
|
||||||
|
* An example of a constraint in 3d. We create a single group, with some
|
||||||
|
* entities and constraints.
|
||||||
|
*---------------------------------------------------------------------------*/
|
||||||
|
void Example3d()
|
||||||
|
{
|
||||||
|
/* This will contain a single group, which will arbitrarily number 1. */
|
||||||
|
Slvs_hGroup g = 1;
|
||||||
|
|
||||||
|
/* A point, initially at (x y z) = (10 10 10) */
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(1, g, 10.0);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(2, g, 10.0);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(3, g, 10.0);
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakePoint3d(101, g, 1, 2, 3);
|
||||||
|
/* and a second point at (20 20 20) */
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(4, g, 20.0);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(5, g, 20.0);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(6, g, 20.0);
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakePoint3d(102, g, 4, 5, 6);
|
||||||
|
/* and a line segment connecting them. */
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakeLineSegment(200, g,
|
||||||
|
SLVS_FREE_IN_3D, 101, 102);
|
||||||
|
|
||||||
|
/* The distance between the points should be 30.0 units. */
|
||||||
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
|
1, g,
|
||||||
|
SLVS_C_PT_PT_DISTANCE,
|
||||||
|
SLVS_FREE_IN_3D,
|
||||||
|
30.0,
|
||||||
|
101, 102, 0, 0);
|
||||||
|
|
||||||
|
/* Let's tell the solver to keep the second point as close to constant
|
||||||
|
* as possible, instead moving the first point. */
|
||||||
|
sys.dragged[0] = 4;
|
||||||
|
sys.dragged[1] = 5;
|
||||||
|
sys.dragged[2] = 6;
|
||||||
|
|
||||||
|
/* Now that we have written our system, we solve. */
|
||||||
|
Slvs_Solve(&sys, g);
|
||||||
|
|
||||||
|
if(sys.result == SLVS_RESULT_OKAY) {
|
||||||
|
printf("okay; now at (%.3f %.3f %.3f)\n"
|
||||||
|
" (%.3f %.3f %.3f)\n",
|
||||||
|
sys.param[0].val, sys.param[1].val, sys.param[2].val,
|
||||||
|
sys.param[3].val, sys.param[4].val, sys.param[5].val);
|
||||||
|
printf("%d DOF\n", sys.dof);
|
||||||
|
} else {
|
||||||
|
printf("solve failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------
|
||||||
|
* An example of a constraint in 2d. In our first group, we create a workplane
|
||||||
|
* along the reference frame's xy plane. In a second group, we create some
|
||||||
|
* entities in that group and dimension them.
|
||||||
|
*---------------------------------------------------------------------------*/
|
||||||
|
void Example2d()
|
||||||
|
{
|
||||||
|
Slvs_hGroup g;
|
||||||
|
double qw, qx, qy, qz;
|
||||||
|
|
||||||
|
g = 1;
|
||||||
|
/* First, we create our workplane. Its origin corresponds to the origin
|
||||||
|
* of our base frame (x y z) = (0 0 0) */
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(1, g, 0.0);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(2, g, 0.0);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(3, g, 0.0);
|
||||||
|
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 (0 1 0). */
|
||||||
|
Slvs_MakeQuaternion(1, 0, 0,
|
||||||
|
0, 1, 0, &qw, &qx, &qy, &qz);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(4, g, qw);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(5, g, qx);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(6, g, qy);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(7, g, qz);
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakeNormal3d(102, g, 4, 5, 6, 7);
|
||||||
|
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakeWorkplane(200, g, 101, 102);
|
||||||
|
|
||||||
|
/* Now create a second group. We'll solve group 2, while leaving group 1
|
||||||
|
* constant; so the workplane that we've created will be locked down,
|
||||||
|
* and the solver can't move it. */
|
||||||
|
g = 2;
|
||||||
|
/* These points are represented by their coordinates (u v) within the
|
||||||
|
* workplane, so they need only two parameters each. */
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(11, g, 10.0);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(12, g, 20.0);
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakePoint2d(301, g, 200, 11, 12);
|
||||||
|
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(13, g, 20.0);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(14, g, 10.0);
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakePoint2d(302, g, 200, 13, 14);
|
||||||
|
|
||||||
|
/* And we create a line segment with those endpoints. */
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakeLineSegment(400, g,
|
||||||
|
200, 301, 302);
|
||||||
|
|
||||||
|
/* Now three more points. */
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(15, g, 100.0);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(16, g, 120.0);
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakePoint2d(303, g, 200, 15, 16);
|
||||||
|
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(17, g, 120.0);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(18, g, 110.0);
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakePoint2d(304, g, 200, 17, 18);
|
||||||
|
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(19, g, 115.0);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(20, g, 115.0);
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakePoint2d(305, g, 200, 19, 20);
|
||||||
|
|
||||||
|
/* And arc, centered at point 303, starting at point 304, ending at
|
||||||
|
* point 305. */
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakeArcOfCircle(401, g, 200, 102,
|
||||||
|
303, 304, 305);
|
||||||
|
|
||||||
|
/* Now one more point, and a distance */
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(21, g, 200.0);
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(22, g, 200.0);
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakePoint2d(306, g, 200, 21, 22);
|
||||||
|
|
||||||
|
sys.param[sys.params++] = Slvs_MakeParam(23, g, 30.0);
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakeDistance(307, g, 200, 23);
|
||||||
|
|
||||||
|
/* And a complete circle, centered at point 306 with radius equal to
|
||||||
|
* distance 307. The normal is 102, the same as our workplane. */
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakeCircle(402, g, 200,
|
||||||
|
306, 102, 307);
|
||||||
|
|
||||||
|
|
||||||
|
/* The length of our line segment is 30.0 units. */
|
||||||
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
|
1, g,
|
||||||
|
SLVS_C_PT_PT_DISTANCE,
|
||||||
|
200,
|
||||||
|
30.0,
|
||||||
|
301, 302, 0, 0);
|
||||||
|
|
||||||
|
/* And the distance from our line segment to the origin is 10.0 units. */
|
||||||
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
|
2, g,
|
||||||
|
SLVS_C_PT_LINE_DISTANCE,
|
||||||
|
200,
|
||||||
|
10.0,
|
||||||
|
101, 0, 400, 0);
|
||||||
|
/* And the line segment is vertical. */
|
||||||
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
|
3, g,
|
||||||
|
SLVS_C_VERTICAL,
|
||||||
|
200,
|
||||||
|
0.0,
|
||||||
|
0, 0, 400, 0);
|
||||||
|
/* And the distance from one endpoint to the origin is 15.0 units. */
|
||||||
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
|
4, g,
|
||||||
|
SLVS_C_PT_PT_DISTANCE,
|
||||||
|
200,
|
||||||
|
15.0,
|
||||||
|
301, 101, 0, 0);
|
||||||
|
#if 0
|
||||||
|
/* And same for the other endpoint; so if you add this constraint then
|
||||||
|
* the sketch is overconstrained and will signal an error. */
|
||||||
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
|
5, g,
|
||||||
|
SLVS_C_PT_PT_DISTANCE,
|
||||||
|
200,
|
||||||
|
18.0,
|
||||||
|
302, 101, 0, 0);
|
||||||
|
#endif /* 0 */
|
||||||
|
|
||||||
|
/* The arc and the circle have equal radius. */
|
||||||
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
|
6, g,
|
||||||
|
SLVS_C_EQUAL_RADIUS,
|
||||||
|
200,
|
||||||
|
0.0,
|
||||||
|
0, 0, 401, 402);
|
||||||
|
/* The arc has radius 17.0 units. */
|
||||||
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
|
7, g,
|
||||||
|
SLVS_C_DIAMETER,
|
||||||
|
200,
|
||||||
|
17.0*2,
|
||||||
|
0, 0, 401, 0);
|
||||||
|
|
||||||
|
/* If the solver fails, then ask it to report which constraints caused
|
||||||
|
* the problem. */
|
||||||
|
sys.calculateFaileds = 1;
|
||||||
|
|
||||||
|
/* And solve. */
|
||||||
|
Slvs_Solve(&sys, g);
|
||||||
|
|
||||||
|
if(sys.result == SLVS_RESULT_OKAY) {
|
||||||
|
printf("solved okay\n");
|
||||||
|
printf("line from (%.3f %.3f) to (%.3f %.3f)\n",
|
||||||
|
sys.param[7].val, sys.param[8].val,
|
||||||
|
sys.param[9].val, sys.param[10].val);
|
||||||
|
|
||||||
|
printf("arc center (%.3f %.3f) start (%.3f %.3f) finish (%.3f %.3f)\n",
|
||||||
|
sys.param[11].val, sys.param[12].val,
|
||||||
|
sys.param[13].val, sys.param[14].val,
|
||||||
|
sys.param[15].val, sys.param[16].val);
|
||||||
|
|
||||||
|
printf("circle center (%.3f %.3f) radius %.3f\n",
|
||||||
|
sys.param[17].val, sys.param[18].val,
|
||||||
|
sys.param[19].val);
|
||||||
|
printf("%d DOF\n", sys.dof);
|
||||||
|
} else {
|
||||||
|
int i;
|
||||||
|
printf("solve failed: problematic constraints are:");
|
||||||
|
for(i = 0; i < sys.faileds; i++) {
|
||||||
|
printf(" %d", sys.failed[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
if(sys.result == SLVS_RESULT_INCONSISTENT) {
|
||||||
|
printf("system inconsistent\n");
|
||||||
|
} else {
|
||||||
|
printf("system nonconvergent\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
sys.param = CheckMalloc(50*sizeof(sys.param[0]));
|
||||||
|
sys.entity = CheckMalloc(50*sizeof(sys.entity[0]));
|
||||||
|
sys.constraint = CheckMalloc(50*sizeof(sys.constraint[0]));
|
||||||
|
|
||||||
|
sys.failed = CheckMalloc(50*sizeof(sys.failed[0]));
|
||||||
|
sys.faileds = 50;
|
||||||
|
|
||||||
|
/*Example3d();*/
|
||||||
|
for(;;) {
|
||||||
|
Example2d();
|
||||||
|
sys.params = sys.constraints = sys.entities = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,467 @@
|
||||||
|
|
||||||
|
INTRODUCTION
|
||||||
|
============
|
||||||
|
|
||||||
|
A sketch in SolveSpace consists of three basic elements: parameters,
|
||||||
|
entities, and constraints.
|
||||||
|
|
||||||
|
A parameter (Slvs_Param) is a single real number, represented internally
|
||||||
|
by a double-precision floating point variable. The parameters are unknown
|
||||||
|
variables that the solver modifies in order to satisfy the constraints.
|
||||||
|
|
||||||
|
An entity (Slvs_Entity) is a geometric thing, like a point or a line
|
||||||
|
segment or a circle. Entities are defined in terms of parameters,
|
||||||
|
and in terms of other entities. For example, a point in three-space
|
||||||
|
is represented by three parameters, corresponding to its x, y, and z
|
||||||
|
coordinates in our base coordinate frame. A line segment is represented
|
||||||
|
by two point entities, corresponding to its endpoints.
|
||||||
|
|
||||||
|
A constraint (Slvs_Constraint) is a geometric property of an entity,
|
||||||
|
or a relationship among multiple entities. For example, a point-point
|
||||||
|
distance constraint will set the distance between two point entities.
|
||||||
|
|
||||||
|
Parameters, entities, and constraints are typically referenced by their
|
||||||
|
handles (Slvs_hParam, Slvs_hEntity, Slvs_hConstraint). These handles are
|
||||||
|
32-bit integer values starting from 1. The zero handle is reserved. Each
|
||||||
|
object has a unique handle within its type (but it's acceptable, for
|
||||||
|
example to have a constraint with an Slvs_hConstraint of 7, and also to
|
||||||
|
have an entity with an Slvs_hEntity of 7). The use of handles instead
|
||||||
|
of pointers helps to avoid memory corruption.
|
||||||
|
|
||||||
|
Entities and constraints are assigned into groups. A group is a set of
|
||||||
|
entities and constraints that is solved simultaneously. In a parametric
|
||||||
|
CAD system, a single group would typically correspond to a single sketch.
|
||||||
|
Constraints within a group may refer to entities outside that group,
|
||||||
|
but only the entities within that group will be modified by the solver.
|
||||||
|
|
||||||
|
Consider point A in group 1, and point B in group 2. We have a constraint
|
||||||
|
in group 2 that makes the points coincident. When we solve group 2, the
|
||||||
|
solver is allowed to move point B to place it on top of point A. It is
|
||||||
|
not allowed to move point A to put it on top of point B, because point
|
||||||
|
A is outside the group being solved.
|
||||||
|
|
||||||
|
This corresponds to the typical structure of a parametric CAD system. In a
|
||||||
|
later sketch, we may constrain our entities against existing geometry from
|
||||||
|
earlier sketches. The constraints will move the entities in our current
|
||||||
|
sketch, but will not change the geometry from the earlier sketches.
|
||||||
|
|
||||||
|
To use the solver, we first define a set of parameters, entities, and
|
||||||
|
constraints. We provide an initial guess for each parameter; this is
|
||||||
|
necessary to achieve convergence, and also determines which solution
|
||||||
|
gets chosen when (finitely many) multiple solutions exist. Typically,
|
||||||
|
these initial guesses are provided by the initial configuration in which
|
||||||
|
the user drew the entities before constraining them.
|
||||||
|
|
||||||
|
We then run the solver for a given group. The entities within that group
|
||||||
|
are modified in an attempt to satisfy the constraints.
|
||||||
|
|
||||||
|
After running the solver, there are three possible outcomes:
|
||||||
|
|
||||||
|
* All constraints were satisfied to within our numerical
|
||||||
|
tolerance (i.e., success). The result is equal to SLVS_RESULT_OKAY,
|
||||||
|
and the parameters in param[] have been updated.
|
||||||
|
|
||||||
|
* The solver can prove that two constraints are inconsistent (for
|
||||||
|
example, if a line with nonzero length is constrained both
|
||||||
|
horizontal and vertical). In that case, a list of inconsistent
|
||||||
|
constraints is generated in failed[].
|
||||||
|
|
||||||
|
* The solver cannot prove that two constraints are inconsistent, but
|
||||||
|
it cannot find a solution. In that case, the list of unsatisfied
|
||||||
|
constraints is generated in failed[].
|
||||||
|
|
||||||
|
|
||||||
|
TYPES OF ENTITIES
|
||||||
|
=================
|
||||||
|
|
||||||
|
SLVS_E_POINT_IN_3D
|
||||||
|
|
||||||
|
A point in 3d. Defined by three parameters:
|
||||||
|
|
||||||
|
param[0] the point's x coordinate
|
||||||
|
param[1] y
|
||||||
|
param[1] z
|
||||||
|
|
||||||
|
|
||||||
|
SLVS_E_POINT_IN_2D
|
||||||
|
|
||||||
|
A point within a workplane. Defined by the workplane
|
||||||
|
|
||||||
|
wrkpl
|
||||||
|
|
||||||
|
and by two parameters
|
||||||
|
|
||||||
|
param[0] the point's u coordinate
|
||||||
|
param[1] v
|
||||||
|
|
||||||
|
within the coordinate system of the workplane. For example, if the
|
||||||
|
workplane is the zx plane, then u = z and v = x. If the workplane is
|
||||||
|
parallel to the zx plane, but translated so that the workplane's
|
||||||
|
origin is (3, 4, 5), then u = z - 5 and v = x - 3.
|
||||||
|
|
||||||
|
|
||||||
|
SLVS_E_NORMAL_IN_3D
|
||||||
|
|
||||||
|
A normal. In SolveSpace, "normals" represent a 3x3 rotation matrix
|
||||||
|
from our base coordinate system to a new frame. Defined by the
|
||||||
|
unit quaternion
|
||||||
|
|
||||||
|
param[0] w
|
||||||
|
param[1] x
|
||||||
|
param[2] y
|
||||||
|
param[3] z
|
||||||
|
|
||||||
|
where the quaternion is given by w + x*i + y*j + z*k.
|
||||||
|
|
||||||
|
It is useful to think of this quaternion as representing a plane
|
||||||
|
through the origin. This plane has three associated vectors: basis
|
||||||
|
vectors U, V that lie within the plane, and normal N that is
|
||||||
|
perpendicular to it. This means that
|
||||||
|
|
||||||
|
[ U V N ]'
|
||||||
|
|
||||||
|
defines a 3x3 rotation matrix. So U, V, and N all have unit length,
|
||||||
|
and are orthogonal so that
|
||||||
|
|
||||||
|
U cross V = N
|
||||||
|
V cross N = U
|
||||||
|
N cross U = V
|
||||||
|
|
||||||
|
Convenience functions (Slvs_Quaternion*) are provided to convert
|
||||||
|
between this representation as vectors U, V, N and the unit
|
||||||
|
quaternion.
|
||||||
|
|
||||||
|
A unit quaternion has only 3 degrees of freedom, but is specified in
|
||||||
|
terms of 4 parameters. An extra constraint is therefore generated
|
||||||
|
implicitly, that
|
||||||
|
|
||||||
|
w^2 + x^2 + y^2 + z^2 = 1
|
||||||
|
|
||||||
|
|
||||||
|
SLVS_E_NORMAL_IN_2D
|
||||||
|
|
||||||
|
A normal within a workplane. This is identical to the workplane's
|
||||||
|
normal, so it is simply defined by
|
||||||
|
|
||||||
|
wrkpl
|
||||||
|
|
||||||
|
This entity type is used, for example, to define a circle that lies
|
||||||
|
within a workplane. The circle's normal is the same as the workplane's
|
||||||
|
normal, so we can use an SLVS_E_NORMAL_IN_2D to copy the workplane's
|
||||||
|
normal.
|
||||||
|
|
||||||
|
|
||||||
|
SLVS_E_DISTANCE
|
||||||
|
|
||||||
|
A distance. This entity is used to define the radius of a circle, by
|
||||||
|
a single parameter
|
||||||
|
|
||||||
|
param[0] r
|
||||||
|
|
||||||
|
|
||||||
|
SLVS_E_WORKPLANE
|
||||||
|
|
||||||
|
An oriented plane, somewhere in 3d. This entity therefore has 6
|
||||||
|
degrees of freedom: three translational, and three rotational. It is
|
||||||
|
specified in terms of its origin
|
||||||
|
|
||||||
|
point[0] origin
|
||||||
|
|
||||||
|
and a normal
|
||||||
|
|
||||||
|
normal
|
||||||
|
|
||||||
|
The normal describes three vectors U, V, N, as discussed in the
|
||||||
|
documentation for SLVS_E_NORMAL_IN_3D. The plane is therefore given
|
||||||
|
by the equation
|
||||||
|
|
||||||
|
p = origin + s*U + t*V
|
||||||
|
|
||||||
|
for any scalar s and t.
|
||||||
|
|
||||||
|
|
||||||
|
SLVS_E_LINE_SEGMENT
|
||||||
|
|
||||||
|
A line segment between two endpoints
|
||||||
|
|
||||||
|
point[0]
|
||||||
|
point[1]
|
||||||
|
|
||||||
|
|
||||||
|
SLVS_E_CUBIC
|
||||||
|
|
||||||
|
A nonrational cubic Bezier segment
|
||||||
|
|
||||||
|
point[0] starting point P0
|
||||||
|
point[1] control point P1
|
||||||
|
point[2] control point P2
|
||||||
|
point[3] ending point P3
|
||||||
|
|
||||||
|
The curve then has equation
|
||||||
|
|
||||||
|
p(t) = P0*(1 - t)^3 + 3*P1*(1 - t)^2*t + 3*P2*(1 - t)*t^2 + P3*t^3
|
||||||
|
|
||||||
|
as t goes from 0 to 1.
|
||||||
|
|
||||||
|
|
||||||
|
SLVS_E_CIRCLE
|
||||||
|
|
||||||
|
A complete circle. The circle lies within a plane with normal
|
||||||
|
|
||||||
|
normal
|
||||||
|
|
||||||
|
The circle is centered at
|
||||||
|
|
||||||
|
point[0]
|
||||||
|
|
||||||
|
The circle's radius is
|
||||||
|
|
||||||
|
distance
|
||||||
|
|
||||||
|
|
||||||
|
SLVS_E_ARC_OF_CIRCLE
|
||||||
|
|
||||||
|
An arc of a circle. An arc must always lie within a workplane; it
|
||||||
|
cannot be free in 3d. So it is specified with a workplane
|
||||||
|
|
||||||
|
wrkpl
|
||||||
|
|
||||||
|
It is then defined by three points
|
||||||
|
|
||||||
|
point[0] center of the circle
|
||||||
|
point[1] beginning of the arc
|
||||||
|
point[2] end of the arc
|
||||||
|
|
||||||
|
and its normal
|
||||||
|
|
||||||
|
normal identical to the normal of the workplane
|
||||||
|
|
||||||
|
The arc runs counter-clockwise from its beginning to its end (with
|
||||||
|
the workplane's normal pointing towards the viewer). If the beginning
|
||||||
|
and end of the arc are coincident, then the arc is considered to
|
||||||
|
represent a full circle.
|
||||||
|
|
||||||
|
This representation has an extra degree of freedom. An extra
|
||||||
|
constraint is therefore generated implicitly, so that
|
||||||
|
|
||||||
|
distance(center, beginning) = distance(center, end)
|
||||||
|
|
||||||
|
|
||||||
|
TYPES OF CONSTRAINTS
|
||||||
|
====================
|
||||||
|
|
||||||
|
Many constraints can apply either in 3d, or in a workplane. This is
|
||||||
|
determined by the wrkpl member of the constraint. If that member is set
|
||||||
|
to SLVS_FREE_IN_3D, then the constraint applies in 3d. If that member
|
||||||
|
is set equal to a workplane, the constraint applies projected into that
|
||||||
|
workplane. (For example, a constraint on the distance between two points
|
||||||
|
actually applies to the projected distance).
|
||||||
|
|
||||||
|
Constraints that may be used in 3d or projected into a workplane are
|
||||||
|
marked with a single star (*). Constraints that must always be used with
|
||||||
|
a workplane are marked with a double star (**). Constraints that ignore
|
||||||
|
the wrkpl member are marked with no star.
|
||||||
|
|
||||||
|
SLVS_C_PT_PT_DISTANCE*
|
||||||
|
|
||||||
|
The distance between points ptA and ptB is equal to valA. This is an
|
||||||
|
unsigned distance, so valA must always be positive.
|
||||||
|
|
||||||
|
SLVS_C_PROJ_PT_DISTANCE
|
||||||
|
|
||||||
|
The distance between points ptA and ptB, as projected along the line
|
||||||
|
or normal entityA, is equal to valA. This is a signed distance.
|
||||||
|
|
||||||
|
SLVS_C_POINTS_COINCIDENT*
|
||||||
|
|
||||||
|
Points ptA and ptB are coincident (i.e., exactly on top of each
|
||||||
|
other).
|
||||||
|
|
||||||
|
SLVS_C_PT_PLANE_DISTANCE
|
||||||
|
|
||||||
|
The distance from point ptA to workplane entityA is equal to
|
||||||
|
valA. This is a signed distance; positive versus negative valA
|
||||||
|
correspond to a point that is above vs. below the plane.
|
||||||
|
|
||||||
|
SLVS_C_PT_LINE_DISTANCE*
|
||||||
|
|
||||||
|
The distance from point ptA to line segment entityA is equal to valA.
|
||||||
|
|
||||||
|
If the constraint is projected, then valA is a signed distance;
|
||||||
|
positive versus negative valA correspond to a point that is above
|
||||||
|
vs. below the line.
|
||||||
|
|
||||||
|
If the constraint applies in 3d, then valA must always be positive.
|
||||||
|
|
||||||
|
SLVS_C_PT_IN_PLANE
|
||||||
|
|
||||||
|
The point ptA lies in plane entityA.
|
||||||
|
|
||||||
|
SLVS_C_PT_ON_LINE*
|
||||||
|
|
||||||
|
The point ptA lies on the line entityA.
|
||||||
|
|
||||||
|
Note that this constraint removes one degree of freedom when projected
|
||||||
|
in to the plane, but two degrees of freedom in 3d.
|
||||||
|
|
||||||
|
SLVS_C_EQUAL_LENGTH_LINES*
|
||||||
|
|
||||||
|
The lines entityA and entityB have equal length.
|
||||||
|
|
||||||
|
SLVS_C_LENGTH_RATIO*
|
||||||
|
|
||||||
|
The length of line entityA divided by the length of line entityB is
|
||||||
|
equal to valA.
|
||||||
|
|
||||||
|
SLVS_C_LENGTH_DIFFERENCE*
|
||||||
|
|
||||||
|
The lengths of line entityA and line entityB differ by valA.
|
||||||
|
|
||||||
|
SLVS_C_EQ_LEN_PT_LINE_D*
|
||||||
|
|
||||||
|
The length of the line entityA is equal to the distance from point
|
||||||
|
ptA to line entityB.
|
||||||
|
|
||||||
|
SLVS_C_EQ_PT_LN_DISTANCES*
|
||||||
|
|
||||||
|
The distance from the line entityA to the point ptA is equal to the
|
||||||
|
distance from the line entityB to the point ptB.
|
||||||
|
|
||||||
|
SLVS_C_EQUAL_ANGLE*
|
||||||
|
|
||||||
|
The angle between lines entityA and entityB is equal to the angle
|
||||||
|
between lines entityC and entityD.
|
||||||
|
|
||||||
|
If other is true, then the angles are supplementary (i.e., theta1 =
|
||||||
|
180 - theta2) instead of equal.
|
||||||
|
|
||||||
|
SLVS_C_EQUAL_LINE_ARC_LEN*
|
||||||
|
|
||||||
|
The length of the line entityA is equal to the length of the circular
|
||||||
|
arc entityB.
|
||||||
|
|
||||||
|
SLVS_C_SYMMETRIC*
|
||||||
|
|
||||||
|
The points ptA and ptB are symmetric about the plane entityA. This
|
||||||
|
means that they are on opposite sides of the plane and at equal
|
||||||
|
distances from the plane, and that the line connecting ptA and ptB
|
||||||
|
is normal to the plane.
|
||||||
|
|
||||||
|
SLVS_C_SYMMETRIC_HORIZ
|
||||||
|
SLVS_C_SYMMETRIC_VERT**
|
||||||
|
|
||||||
|
The points ptA and ptB are symmetric about the horizontal or vertical
|
||||||
|
axis of the specified workplane.
|
||||||
|
|
||||||
|
SLVS_C_SYMMETRIC_LINE**
|
||||||
|
|
||||||
|
The points ptA and ptB are symmetric about the line entityA.
|
||||||
|
|
||||||
|
SLVS_C_AT_MIDPOINT*
|
||||||
|
|
||||||
|
The point ptA lies at the midpoint of the line entityA.
|
||||||
|
|
||||||
|
SLVS_C_HORIZONTAL
|
||||||
|
SLVS_C_VERTICAL**
|
||||||
|
|
||||||
|
The line connecting points ptA and ptB is horizontal or vertical. Or,
|
||||||
|
the line segment entityA is horizontal or vertical. If points are
|
||||||
|
specified then the line segment should be left zero, and if a line
|
||||||
|
is specified then the points should be left zero.
|
||||||
|
|
||||||
|
SLVS_C_DIAMETER
|
||||||
|
|
||||||
|
The diameter of circle or arc entityA is equal to valA.
|
||||||
|
|
||||||
|
SLVS_C_PT_ON_CIRCLE
|
||||||
|
|
||||||
|
The point ptA lies on the right cylinder obtained by extruding circle
|
||||||
|
or arc entityA normal to its plane.
|
||||||
|
|
||||||
|
SLVS_C_SAME_ORIENTATION
|
||||||
|
|
||||||
|
The normals entityA and entityB describe identical rotations. This
|
||||||
|
constraint therefore restricts three degrees of freedom.
|
||||||
|
|
||||||
|
SLVS_C_ANGLE*
|
||||||
|
|
||||||
|
The angle between lines entityA and entityB is equal to valA, where
|
||||||
|
valA is specified in degrees. This constraint equation is written
|
||||||
|
in the form
|
||||||
|
|
||||||
|
(A dot B)/(|A||B|) = cos(valA)
|
||||||
|
|
||||||
|
where A and B are vectors in the directions of lines A and B. This
|
||||||
|
equation does not specify the angle unambiguously; for example,
|
||||||
|
note that valA = +/- 90 degrees will produce the same equation.
|
||||||
|
|
||||||
|
If other is true, then the constraint is instead that
|
||||||
|
|
||||||
|
(A dot B)/(|A||B|) = -cos(valA)
|
||||||
|
|
||||||
|
SLVS_C_PERPENDICULAR*
|
||||||
|
|
||||||
|
Identical to SLVS_C_ANGLE with valA = 90 degrees.
|
||||||
|
|
||||||
|
SLVS_C_PARALLEL*
|
||||||
|
|
||||||
|
Lines entityA and entityB are parallel.
|
||||||
|
|
||||||
|
Note that this constraint removes one degree of freedom when projected
|
||||||
|
in to the plane, but two degrees of freedom in 3d.
|
||||||
|
|
||||||
|
SLVS_C_ARC_LINE_TANGENT**
|
||||||
|
|
||||||
|
The arc entityA is tangent to the line entityB. If other is false,
|
||||||
|
then the arc is tangent at its beginning (point[1]). If other is true,
|
||||||
|
then the arc is tangent at its end (point[2]).
|
||||||
|
|
||||||
|
SLVS_C_CUBIC_LINE_TANGENT*
|
||||||
|
|
||||||
|
The cubic entityA is tangent to the line entityB. The variable
|
||||||
|
other indicates:
|
||||||
|
|
||||||
|
if false: the cubic is tangent at its beginning
|
||||||
|
if true: the cubic is tangent at its end
|
||||||
|
|
||||||
|
The beginning of the cubic is point[0], and the end is point[3].
|
||||||
|
|
||||||
|
SLVS_C_CURVE_CURVE_TANGENT**
|
||||||
|
|
||||||
|
The two entities entityA and entityB are tangent. These entities can
|
||||||
|
each be either an arc or a cubic, in any combination. The flags
|
||||||
|
other and other2 indicate which endpoint of the curve is tangent,
|
||||||
|
for entityA and entityB respectively:
|
||||||
|
|
||||||
|
if false: the entity is tangent at its beginning
|
||||||
|
if true: the entity is tangent at its end
|
||||||
|
|
||||||
|
For cubics, point[0] is the beginning, and point[3] is the end. For
|
||||||
|
arcs, point[1] is the beginning, and point[2] is the end.
|
||||||
|
|
||||||
|
SLVS_C_EQUAL_RADIUS
|
||||||
|
|
||||||
|
The circles or arcs entityA and entityB have equal radius.
|
||||||
|
|
||||||
|
SLVS_C_WHERE_DRAGGED*
|
||||||
|
|
||||||
|
The point ptA is locked at its initial numerical guess, and cannot
|
||||||
|
be moved. This constrains two degrees of freedom in a workplane,
|
||||||
|
and three in free space. It's therefore possible for this constraint
|
||||||
|
to overconstrain the sketch, for example if it's applied to a point
|
||||||
|
with one remaining degree of freedom.
|
||||||
|
|
||||||
|
|
||||||
|
USING THE SOLVER
|
||||||
|
================
|
||||||
|
|
||||||
|
The solver is provided as a DLL, and will be usable with most
|
||||||
|
Windows-based development tools. Examples are provided:
|
||||||
|
|
||||||
|
in C/C++ - CDemo.c
|
||||||
|
|
||||||
|
in VB.NET - VbDemo.vb
|
||||||
|
|
||||||
|
|
||||||
|
Copyright 2009-2013 Jonathan Westhues.
|
||||||
|
|
|
@ -77,42 +77,53 @@ int solver(int nPts, float *p_ptr, int nConst, float *c_ptr, int nLinks, float *
|
||||||
{
|
{
|
||||||
if (isnan((float)*p_ptr))
|
if (isnan((float)*p_ptr))
|
||||||
{
|
{
|
||||||
p_ptr+=3;
|
p_ptr += 3;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(ph++, g, (float)*p_ptr++);
|
sys.param[sys.params++] = Slvs_MakeParam(ph++, g, (float)*p_ptr++);
|
||||||
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.entity[sys.entities++] = Slvs_MakePoint2d(i, g, 200, ph - 1, ph - 2);
|
||||||
p_ptr+=1;
|
p_ptr += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < nLinks; i++)
|
for (int i = 0; i < nLinks; i++)
|
||||||
{
|
{
|
||||||
if (*l_ptr++ == 0)
|
|
||||||
|
switch ((int)*l_ptr++)
|
||||||
{
|
{
|
||||||
|
case 0:
|
||||||
sys.entity[sys.entities++] = Slvs_MakeLineSegment(lh++, g,
|
sys.entity[sys.entities++] = Slvs_MakeLineSegment(lh++, g,
|
||||||
200, (int)*l_ptr, (int)*(l_ptr+1));
|
200, (int)*l_ptr, (int)*(l_ptr + 1));
|
||||||
l_ptr += 4;
|
break;
|
||||||
} else {
|
case 1:
|
||||||
l_ptr += 4;
|
/* And arc, centered at point 303, starting at point 304, ending at
|
||||||
|
* point 305. */
|
||||||
|
sys.entity[sys.entities++] = Slvs_MakeArcOfCircle(lh++, g, 200, 102,
|
||||||
|
(int)*(l_ptr + 2), (int)*(l_ptr), (int)*(l_ptr + 1));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
l_ptr += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < nConst; i++)
|
for (int i = 0; i < nConst; i++)
|
||||||
{
|
{
|
||||||
if ((int)*c_ptr == 0)
|
if ((int)*c_ptr == 0)
|
||||||
{
|
{
|
||||||
c_ptr+=2;
|
c_ptr += 2;
|
||||||
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
con_id++, g,
|
con_id++, g,
|
||||||
SLVS_C_POINTS_COINCIDENT,
|
SLVS_C_POINTS_COINCIDENT,
|
||||||
200,
|
200,
|
||||||
0.0,
|
0.0,
|
||||||
(int)*c_ptr, (int)*(c_ptr+1), 0, 0);
|
(int)*c_ptr, (int)*(c_ptr + 1), 0, 0);
|
||||||
|
|
||||||
c_ptr += 4;
|
c_ptr += 4;
|
||||||
|
}
|
||||||
} else {
|
else
|
||||||
|
{
|
||||||
c_ptr += 6;
|
c_ptr += 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,12 +139,12 @@ int solver(int nPts, float *p_ptr, int nConst, float *c_ptr, int nLinks, float *
|
||||||
{
|
{
|
||||||
if (isnan((float)*buf_pt_start))
|
if (isnan((float)*buf_pt_start))
|
||||||
{
|
{
|
||||||
buf_pt_start+=3;
|
buf_pt_start += 3;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
*buf_pt_start++ = (float)sys.param[p_start++].val;
|
*buf_pt_start++ = (float)sys.param[p_start++].val;
|
||||||
*buf_pt_start++ = (float)sys.param[p_start++].val;
|
*buf_pt_start++ = (float)sys.param[p_start++].val;
|
||||||
buf_pt_start+=1;
|
buf_pt_start += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
Loading…
Reference in New Issue