UNPKG

awv3

Version:
438 lines (383 loc) 16.9 kB
import * as THREE from 'three'; /* * The following restrictions apply to points: xoffset yoffset. * Restrictions apply to lines in the following order: length angle xoffset yoffset. * Restrictions apply to arcs in the following order: angle radius clockwise. */ export function drawPointBy_S(_ref, restrictions) { var startHint = _ref[0]; var start = startHint.clone(); if (restrictions.xabsolute !== undefined) start.x = restrictions.xabsolute; if (restrictions.yabsolute !== undefined) start.y = restrictions.yabsolute; return { start: start }; } export function drawLineBy_S_E(_ref2, restrictions) { var start = _ref2[0], endHint = _ref2[1]; var xoffset = restrictions.xoffset, yoffset = restrictions.yoffset, xabsolute = restrictions.xabsolute, yabsolute = restrictions.yabsolute, angle = restrictions.angle, length = restrictions.length; if (xabsolute !== undefined) xoffset = xabsolute - start.x; if (yabsolute !== undefined) yoffset = yabsolute - start.y; var end = endHint.clone().sub(start); if (xoffset !== undefined) { if (yoffset !== undefined) end.set(xoffset, yoffset, 0);else if (angle !== undefined) end.set(xoffset, xoffset * Math.tan(angle), 0);else if (length !== undefined) end.set(xoffset, Math.sign(end.y) * cath(length, xoffset), 0);else end.set(xoffset, 0, 0); } else if (yoffset !== undefined) { if (angle !== undefined) end.set(yoffset / Math.tan(angle), yoffset, 0);else if (length !== undefined) end.set(Math.sign(end.x) * cath(length, yoffset), yoffset, 0);else end.set(0, yoffset, 0); } else if (angle !== undefined) { setVectorFromAngleLength(end, angle, length !== undefined ? length : end.length()); } else if (length !== undefined) { end.setLength(length); } end.add(start); return { start: start, end: end }; } // is unambigously defined if restrictions.angle and restrictions.clockwise are set // uses generalHint to disambiguate side if only restrictions.angle is set // uses generalHint to disambiguate side if restrictions.radius and restrictions.clockwise are set // uses generalHint and assumes smaller (<pi) angle if only restrictions.radius is set // throws an error otherwise function drawArcBy_S_E(_ref3, restrictions) { var start = _ref3[0], end = _ref3[1], generalHint = _ref3[2]; var halfChord = start.distanceTo(end) / 2; // calculate radius from the angle var radius = restrictions.angle !== undefined ? halfChord / Math.sin(restrictions.angle / 2) : restrictions.radius !== undefined ? restrictions.radius : /*throw*/ new Error("drawArcBy_S_E mustn't be called without angle or radius restrictions"); if (!(0 < radius && radius < 1e6)) return { start: start, end: end }; // perpend is the distance from the center to the start-end chord var perpend = cath(radius, halfChord), side = 1; // calculate side (sign of perpend) from angle and clockwise if (restrictions.angle !== undefined && restrictions.clockwise !== undefined) side = restrictions.angle < Math.PI === restrictions.clockwise ? -1 : 1; // otherwise use generalHint to disambiguate side else side = cross(end.clone().sub(start), generalHint.clone().sub(start)) < 0 ? -1 : 1; var center = setVectorFromOrthoCoords(end.clone().sub(start).normalize(), halfChord, side * perpend).add(start); var clockwise = restrictions.clockwise !== undefined ? restrictions.clockwise : restrictions.angle !== undefined ? restrictions.angle < Math.PI === (side === -1) : side === -1; return { start: start, end: end, center: center, clockwise: clockwise }; } export function drawArcBy_S_E_C(_ref4, restrictions) { var start = _ref4[0], end = _ref4[1], centerHint = _ref4[2]; if (restrictions.angle !== undefined || restrictions.radius !== undefined) return drawArcBy_S_E([start, end, centerHint], restrictions); var perpend = cross(end.clone().sub(start).normalize(), centerHint.clone().sub(start)); var radius = Math.hypot(perpend, start.distanceTo(end) / 2); return drawArcBy_S_E([start, end, centerHint], { radius: radius, clockwise: restrictions.clockwise }); } export function drawArcBy_S_E_CP(_ref5, restrictions) { var start = _ref5[0], end = _ref5[1], controlPointHint = _ref5[2]; var mid = start.clone().lerp(end, 0.5); var center = controlPointHint.clone().sub(mid); var perpend = cross(end.clone().sub(mid), center); var k = start.distanceToSquared(mid) / perpend; center.multiplyScalar(-k * k).add(mid); if (!Number.isFinite(-k * k)) center.copy(end); return drawArcBy_S_E_C([start, end, center], restrictions); } export function drawArcBy_S_E_M(_ref6, restrictions) { var start = _ref6[0], end = _ref6[1], middleHint = _ref6[2]; if (restrictions.angle !== undefined || restrictions.radius !== undefined) return drawArcBy_S_E([start, end, middleHint], restrictions); var MS = start.clone().sub(middleHint); var ME = end.clone().sub(middleHint); var inscribedAngle = Math.abs(Math.atan2(cross(MS, ME), MS.dot(ME))); var angle = 2 * (Math.PI - inscribedAngle); var clockwise = cross(MS, ME) > 0; return drawArcBy_S_E([start, end, middleHint], { angle: angle, clockwise: clockwise }); } export function drawArcBy_S_T_E(_ref7, restrictions) { var start = _ref7[0], startTangent = _ref7[1], endHint = _ref7[2]; // can't delegate to another drawArcBy* because they use end as-is but we must use startTangent as-is and end as a hint var xoffset = restrictions.xoffset, yoffset = restrictions.yoffset, xabsolute = restrictions.xabsolute, yabsolute = restrictions.yabsolute, angle = restrictions.angle, radius = restrictions.radius, clockwise = restrictions.clockwise; endHint = new THREE.Vector3(xabsolute !== undefined ? xabsolute : xoffset !== undefined ? start.x + xoffset : endHint.x, yabsolute !== undefined ? yabsolute : yoffset !== undefined ? start.y + yoffset : endHint.y, 0); var dirHint = endHint.clone().sub(start); if (clockwise === undefined) clockwise = cross(startTangent, dirHint) < 0; if (angle !== undefined && radius !== undefined) { var s = clockwise ? -1 : 1; var center = setVectorFromOrthoCoords(startTangent.clone(), 0, s * radius).add(start); var end = setVectorFromOrthoCoords(startTangent.clone(), Math.sin(angle), s * (1 - Math.cos(angle))).multiplyScalar(radius).add(start); return { start: start, end: end, center: center, clockwise: clockwise }; } else if (angle !== undefined) { var dirAngle = angle / (clockwise ? -2 : 2); // all possible ends lie on a dir ray from start var dir = setVectorFromOrthoCoords(startTangent.clone(), Math.cos(dirAngle), Math.sin(dirAngle)); var _end = dirHint.clone().projectOnVector(dir); if (dir.dot(_end) < 0) _end.negate(); _end.add(start); return drawArcBy_S_E([start, _end, endHint], { angle: angle, clockwise: clockwise }); } else if (radius !== undefined) { var _center = setVectorFromOrthoCoords(startTangent.clone(), 0, clockwise ? -radius : radius).add(start); var _end2 = endHint.clone().sub(_center).setLength(radius).add(_center); return { start: start, end: _end2, center: _center, clockwise: clockwise }; } else { var signedRadius = dirHint.lengthSq() / cross(startTangent, dirHint) / 2; var _center2 = setVectorFromOrthoCoords(startTangent.clone(), 0, signedRadius).add(start); return drawArcBy_S_E_C([start, endHint, _center2], { radius: Math.abs(signedRadius), clockwise: clockwise }); } } export function drawCircleBy_C_E(_ref8, restrictions) { var center = _ref8[0], endHint = _ref8[1]; var radius = restrictions.radius; if (radius === undefined) radius = center.distanceTo(endHint); return { center: center, radius: radius }; } export function drawArcBy_Angle_M(_ref9) { var vertexPos = _ref9[0], startDir = _ref9[1], endDir = _ref9[2], middlePos = _ref9[3]; // to return var len = undefined; var center = new THREE.Vector3(0, 0, 0); var centerDir = startDir.clone().add(endDir).multiplyScalar(0.5).normalize(); var vertexToMiddle = middlePos.clone().sub(vertexPos); // if radius is too small or middlepos and its projection to centerDir is out of filletAngle if (vertexToMiddle.length() < 1e-2 || vertexToMiddle.dot(centerDir) < 0) return null; // if middlepos is out of filletAngle but its projection to centerDir isn't var alpha = endDir.angleTo(startDir); var angleMidToStart = vertexToMiddle.angleTo(startDir); var angleMidToEnd = vertexToMiddle.angleTo(endDir); // project middlepos onto one of the directions if it is out of filletAngle var maxAngle = Math.max(angleMidToStart, angleMidToEnd, alpha); if (maxAngle !== alpha) { if (angleMidToStart === maxAngle) { len = vertexToMiddle.dot(endDir); } else if (angleMidToEnd === maxAngle) { len = vertexToMiddle.dot(startDir); } } if (len) { center = vertexPos.clone().add(centerDir.clone().multiplyScalar(len / Math.cos(alpha / 2))); // fillet angle < 180 => cos(alpha/2) != 0 return { start: vertexPos.clone().add(startDir.clone().multiplyScalar(len)), end: vertexPos.clone().add(endDir.clone().multiplyScalar(len)), center: center, clockwise: true }; } var middleNew = middlePos.clone().sub(vertexPos); var sinA2Sq = Math.pow(Math.sin(alpha / 2), 2); var cosA2Sq = 1 - sinA2Sq; var A0 = middleNew.lengthSq(); var A1 = -2 * middleNew.x; var A2 = -2 * middleNew.y; //if (centerDir.x === 0) { // var sign = centerDir.dot(new THREE.Vector3(0,1,0)); // var discr = Math.pow(A2, 2) - 4 * cosA2Sq * A0; // var Oy = (-A2 + sign * Math.sqrt(discr)) / (2 * cosA2Sq); // // center.x = 0; // center.y = Oy; //} else if (centerDir.y === 0) { var sign = centerDir.dot(new THREE.Vector3(1, 0, 0)); var discr = Math.pow(A1, 2) - 4 * cosA2Sq * A0; var Ox = (-A1 + sign * Math.sqrt(discr)) / (2 * cosA2Sq); center.x = Ox; center.y = 0; } else { var sign = centerDir.dot(new THREE.Vector3(0, 1, 0)); var proportion = centerDir.x / centerDir.y; var A = (proportion * proportion + 1) * cosA2Sq; var B = A1 * proportion + A2; var C = A0; var discr = Math.pow(B, 2) - 4 * A * C; var Oy = (-B + sign * Math.sqrt(discr)) / (2 * A); center.x = Oy * proportion; center.y = Oy; } var len = center.length() * Math.cos(alpha / 2); center.add(vertexPos); return { start: vertexPos.clone().add(startDir.clone().multiplyScalar(len)), end: vertexPos.clone().add(endDir.clone().multiplyScalar(len)), center: center, clockwise: true }; } export function drawLineBy_Angle_M(_ref10) { var vertexPos = _ref10[0], startDir = _ref10[1], endDir = _ref10[2], middlePos = _ref10[3]; var centerDir = startDir.clone().add(endDir).multiplyScalar(0.5).normalize(); var vertexToMiddle = middlePos.clone().sub(vertexPos); // if radius is too small or middlepos and its projection to centerDir is out of filletAngle if (vertexToMiddle.length() < 1e-2 || vertexToMiddle.dot(centerDir) < 0) return null; var alpha = endDir.angleTo(startDir); var len = vertexToMiddle.dot(centerDir) / Math.cos(alpha / 2); // we have appropriate fillet angle so cos != 0 return { start: vertexPos.clone().add(startDir.clone().multiplyScalar(len)), end: vertexPos.clone().add(endDir.clone().multiplyScalar(len)) }; } //converting arc from "three-points+flag" to "center+radius+angles" representation export function getArcAngles(params) { var vecStart = params.start.clone().sub(params.center); var vecEnd = params.end.clone().sub(params.center); var startAngle = Math.atan2(vecStart.y, vecStart.x); var endAngle = Math.atan2(vecEnd.y, vecEnd.x); var radius = 0.5 * (vecStart.length() + vecEnd.length()); if (params.clockwise) { if (startAngle < endAngle) startAngle += 2 * Math.PI; } else { if (startAngle > endAngle) endAngle += 2 * Math.PI; } var midAngle = (startAngle + endAngle) / 2; var vecMid = new THREE.Vector3(Math.cos(midAngle), Math.sin(midAngle), 0); var mid = params.center.clone().addScaledVector(vecMid, radius); return { center: params.center.clone(), mid: mid, radius: radius, start: startAngle, end: endAngle, bulge: Math.tan((endAngle - startAngle) / 4) }; } // return the intersection point of two lines or null if they are parallel export function intersectLines(pntA, dirA, pntB, dirB, angularTolerance, params) { if (angularTolerance === void 0) { angularTolerance = 1e-15; } if (params === void 0) { params = {}; } dirA = dirA.clone().normalize(); dirB = dirB.clone().normalize(); if (dirA.length() < 0.9 || dirB.length() < 0.9 //must be 1 ) return null; var PxQ = cross(dirA, dirB); if (Math.abs(PxQ) <= angularTolerance) return null; var BmAxQ = cross(pntB.clone().sub(pntA), dirB); var BmAxP = cross(pntB.clone().sub(pntA), dirA); params.A = BmAxQ / PxQ; params.B = BmAxP / PxQ; var intersA = dirA.clone().multiplyScalar(params.A).add(pntA); var intersB = dirB.clone().multiplyScalar(params.B).add(pntB); return intersA.clone().add(intersB).multiplyScalar(0.5); } export function getClass(geomParams) { if (geomParams.entities) return geomParams.cclass; if (geomParams.center) return geomParams.end ? 'CC_Arc' : 'CC_Circle'; if (geomParams.end) return 'CC_Line'; if (geomParams.start) return 'CC_Point'; return undefined; } // calculate a tangent of a line, arc or circle at a point as a normalized (possibly zero) vector export function getTangent(geomParams, point) { if (geomParams.center) { var radVec = point.clone().sub(geomParams.center).normalize(); var tangVec = new THREE.Vector3(-radVec.y, radVec.x, 0); if (geomParams.clockwise) tangVec.negate(); return tangVec; } else if (geomParams.end) { return geomParams.end.clone().sub(geomParams.start).normalize(); } } // modify geomParams inplace to represent movement of a subObject by the displacement // subType can be either 'start', 'end', 'center' or '' (for object itself) export function move(geomParams, subType, displacement) { switch (subType) { case 'start': reverseCurve(geomParams); // fallthrough case 'end': geomParams.end.add(displacement); if (getClass(geomParams) === 'CC_Arc') { var tangent = getTangent(geomParams, geomParams.start); if (tangent.lengthSq() === 0 // singular arc ) tangent.set(1, 0, 0); var newParams = drawArcBy_S_T_E([geomParams.start, tangent, geomParams.end], {}); if (newParams.center) { // only if the arc is still an arc Object.assign(geomParams, newParams); geomParams.radius = geomParams.center.distanceTo(geomParams.start); } } if (subType === 'start') reverseCurve(geomParams); break; case 'center': if (getClass(geomParams) === 'CC_Arc') { // center point must stay on the middle perpendicular line var mid = geomParams.end.clone().add(geomParams.start).multiplyScalar(0.5); var dir = geomParams.end.clone().sub(geomParams.start); geomParams.center.add(displacement).sub(mid).projectOnVector(new THREE.Vector3(-dir.y, dir.x, 0)).add(mid); geomParams.radius = geomParams.center.distanceTo(geomParams.start); } else { geomParams.center.add(displacement); } break; case '': if (geomParams.start) geomParams.start.add(displacement); if (geomParams.end) geomParams.end.add(displacement); if (geomParams.center) geomParams.center.add(displacement); break; } return geomParams; } // modify params inplace for reversed curve function reverseCurve(geomParams) { if (geomParams.end !== undefined // Line, Arc ) { var _ref11 = [geomParams.end, geomParams.start]; geomParams.start = _ref11[0]; geomParams.end = _ref11[1]; } if (geomParams.clockwise !== undefined //Arc ) geomParams.clockwise = !geomParams.clockwise; return geomParams; } // cross product function cross(a, b) { return a.x * b.y - a.y * b.x; } // cathetus (cf. Math.hypot): y = sqrt(z**2 - x**2) || 0 function cath(z, x) { var y = Math.sqrt(z * z - x * x); return Number.isNaN(y) ? 0 : y; } // set a vector from orthogonal coordinates: xdir = x * xdir + y * xdir^T function setVectorFromOrthoCoords(xdir, x, y) { return xdir.applyMatrix3(new THREE.Matrix3().fromArray([x, y, 0, -y, x, 0, 0, 0, 1])); } // set a vector from angle to OX and length function setVectorFromAngleLength(vector3, angle, length) { return vector3.set(Math.cos(angle), Math.sin(angle), 0).multiplyScalar(length); }