awv3
Version:
⚡ AWV3 embedded CAD
438 lines (383 loc) • 16.9 kB
JavaScript
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);
}