UNPKG

awv3

Version:
335 lines (292 loc) 13.5 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _assign = require('babel-runtime/core-js/object/assign'); var _assign2 = _interopRequireDefault(_assign); exports.drawArcBy_S_T_E = drawArcBy_S_T_E; exports.drawArcBy_S_E_Ch = drawArcBy_S_E_Ch; exports.drawArcBy_S_E_CPh = drawArcBy_S_E_CPh; exports.drawArcBy_S_E_M = drawArcBy_S_E_M; exports.drawArcBy_Angle_M = drawArcBy_Angle_M; exports.drawLineBy_Angle_M = drawLineBy_Angle_M; exports.getArcAngles = getArcAngles; exports.intersectLines = intersectLines; exports.getTangent = getTangent; exports.move = move; var _three = require('three'); var THREE = _interopRequireWildcard(_three); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function cross2d(a, b) { return a.x * b.y - a.y * b.x; } function drawArcBy_S_T_E(startPos, startTangent, endPos) { //should we create a line or an arc var dir = endPos.clone().sub(startPos); var onLine = startPos.clone().add(dir.clone().projectOnVector(startTangent)); var height = onLine.distanceTo(endPos); var isLinear = height <= dir.length() * 1e-3 + 1e-3; if (isLinear) return { start: startPos, end: endPos };else { //the ugly math of getting center point and orientation var normal = endPos.clone().sub(onLine).normalize(); var dist = endPos.distanceToSquared(startPos) / (2 * height); var cross = cross2d(normal, startTangent); var ctr = normal.clone().multiplyScalar(dist).add(startPos); return { start: startPos, end: endPos, center: ctr, clockwise: cross > 0.0 }; } } //Note: position of center is only partly used and may change function drawArcBy_S_E_Ch(startPos, endPos, centerPosHint, takeLarger) { var mid = endPos.clone().add(startPos).divideScalar(2); var dir = endPos.clone().sub(startPos); var per = new THREE.Vector3(-dir.y, dir.x, 0.0); var dirMidPos = centerPosHint.clone().sub(mid); var ctr = dirMidPos.clone().projectOnVector(per).add(mid); var dot = dirMidPos.dot(per); var clockwise = dot < 0.0 != !!takeLarger; return { start: startPos, end: endPos, center: ctr, clockwise: clockwise }; } //Note: position of control point is only partly used and may change function drawArcBy_S_E_CPh(startPos, endPos, controlPointHint) { var mid = endPos.clone().add(startPos).divideScalar(2); var dir = endPos.clone().sub(startPos); var per = new THREE.Vector3(-dir.y, dir.x, 0.0); var dirMidPos = controlPointHint.clone().sub(mid); var dirMidCtrl = dirMidPos.clone().projectOnVector(per); var height = dirMidCtrl.length(); if (height <= 1e-3) return { start: startPos, end: endPos }; var halfDist = dir.length() / 2.0; var coeff = -(halfDist * halfDist) / (height * height); var ctr = dirMidCtrl.clone().multiplyScalar(coeff).add(mid); var dot = dirMidPos.dot(per); var clockwise = dot > 0.0; return { start: startPos, end: endPos, center: ctr, clockwise: clockwise }; } //Note: middle point should be within circlewith diameter [SE], otherwise it would be corrected function drawArcBy_S_E_M(startPos, endPos, middlePos) { //since vector math is a huge pain in JS, we do it in a stupid way var lSE = startPos.distanceTo(endPos); var lSM = startPos.distanceTo(middlePos); var lEM = endPos.distanceTo(middlePos); var sArea = cross2d(endPos.clone().sub(startPos), middlePos.clone().sub(startPos)) / 2.0; var area = Math.abs(sArea); if (area <= 1e-3 * lSE + 1e-3) return { start: startPos, end: endPos }; //getting radius with school math var radius = lSE * lSM * lEM / (4.0 * area); radius = Math.max(radius, (0.5 + 1e-9) * lSE); var height = Math.sqrt(radius * radius - lSE * lSE / 4.0); //copy-pasting middle perpendicular var mid = endPos.clone().add(startPos).divideScalar(2); var dir = endPos.clone().sub(startPos); var per = new THREE.Vector3(-dir.y, dir.x, 0.0).normalize(); //getting center (choose best of two cases) var ctr, dist = 1e+50; for (var i = 0; i < 2; i++) { var tCtr = per.clone().multiplyScalar(height * (i ? 1 : -1)).add(mid); var tDist = Math.abs(tCtr.distanceTo(middlePos) - radius); if (dist > tDist) { dist = tDist; ctr = tCtr; } } //get polar angles of all points var getAngle = function getAngle(pos) { var dir = pos.clone().sub(ctr); return Math.atan2(dir.y, dir.x); }; var startAng = getAngle(startPos); var endAng = getAngle(endPos); var middleAng = getAngle(middlePos); //check which part of circle to take if (endAng < startAng) endAng += 2 * Math.PI; if (middleAng < startAng) middleAng += 2 * Math.PI; var clockwise = middleAng > endAng; return { start: startPos, end: endPos, center: ctr, clockwise: clockwise }; } function drawArcBy_Angle_M(vertexPos, startDir, endDir, middlePos) { // 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 }; } function drawLineBy_Angle_M(vertexPos, startDir, endDir, middlePos) { "use strict"; 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 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 }; } //returns intersection point of given two lines //if angularTolerance is set, then null is returned when lines are parallel function intersectLines(pntA, dirA, pntB, dirB, angularTolerance) { angularTolerance = angularTolerance || 1e-15; //mach.eps. by default dirA = dirA.clone().normalize(); dirB = dirB.clone().normalize(); if (dirA.length() < 0.9 || dirB.length() < 0.9) //must be 1 return null; var PxQ = cross2d(dirA, dirB); if (Math.abs(PxQ) <= angularTolerance) return null; var BmAxQ = cross2d(pntB.clone().sub(pntA), dirB); var BmAxP = cross2d(pntB.clone().sub(pntA), dirA); var paramA = BmAxQ / PxQ; var paramB = BmAxP / PxQ; var intersA = dirA.clone().multiplyScalar(paramA).add(pntA); var intersB = dirB.clone().multiplyScalar(paramB).add(pntB); var inters = intersA.clone().add(intersB).multiplyScalar(0.5); return inters; } // calculate a tangent of a line or an arc at a point as a normalized (possibly zero) vector 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) function move(geomParams, subType, displacement) { switch (subType) { case 'start': reverseCurve(geomParams); // fallthrough case 'end': geomParams.end.add(displacement); if (geomParams.center) { 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 (0, _assign2.default)(geomParams, newParams); geomParams.radius = geomParams.center.distanceTo(geomParams.start); } } if (subType === 'start') reverseCurve(geomParams); break; case 'center': // 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); 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 _ref = [geomParams.end, geomParams.start]; geomParams.start = _ref[0]; geomParams.end = _ref[1]; }if (geomParams.clockwise !== undefined) //Arc geomParams.clockwise = !geomParams.clockwise; return geomParams; }