UNPKG

@mlightcad/geometry-engine

Version:

The geometry-engine package provides comprehensive geometric entities, mathematical operations, and transformations for 2D and 3D space. This package mimics AutoCAD ObjectARX's AcGe (Geometry) classes and provides the mathematical foundation for CAD opera

412 lines 15.8 kB
var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; import { AcCmErrors } from '@mlightcad/common'; import { AcGeBox2d, AcGePoint2d, AcGeVector2d } from '../math'; import { AcGeMathUtil, TAU } from '../util'; import { AcGeCurve2d } from './AcGeCurve2d'; /** * Represent a circular arc. */ var AcGeCircArc2d = /** @class */ (function (_super) { __extends(AcGeCircArc2d, _super); function AcGeCircArc2d(a, b, c, d, e) { var _this = _super.call(this) || this; var argsLength = +(a !== undefined) + +(b !== undefined) + +(c !== undefined) + +(d !== undefined) + +(e !== undefined); if (argsLength == 3) { if (typeof a == 'object' && typeof b == 'object' && typeof c == 'object') { _this.createByThreePoints(a, b, c); } else { _this.createByStartEndPointsAndBulge(a, b, c); } } else if (argsLength == 5) { var center = a; _this.center = new AcGePoint2d(center.x, center.y); _this.radius = b; _this.startAngle = c; _this.endAngle = d; _this.clockwise = e; } else { throw AcCmErrors.ILLEGAL_PARAMETERS; } return _this; } /** * Create arc by three points * @param p1 Input the start point * @param p2 Input one point between the start point and the end point * @param p3 Input the end point */ AcGeCircArc2d.prototype.createByThreePoints = function (p1, p2, p3) { var midpoint = function (p1, p2) { return ({ x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2 }); }; var slope = function (p1, p2) { return (p2.y - p1.y) / (p2.x - p1.x); }; var perpSlope = function (m) { return -1 / m; }; var midpoint1 = midpoint(p1, p2); var midpoint2 = midpoint(p2, p3); var slope1 = slope(p1, p2); var slope2 = slope(p2, p3); var perpSlope1 = perpSlope(slope1); var perpSlope2 = perpSlope(slope2); var intersect = function (m1, b1, m2, b2) { var x = (b2 - b1) / (m1 - m2); var y = m1 * x + b1; return { x: x, y: y }; }; var b1 = midpoint1.y - perpSlope1 * midpoint1.x; var b2 = midpoint2.y - perpSlope2 * midpoint2.x; var center = intersect(perpSlope1, b1, perpSlope2, b2); var radius = Math.sqrt(Math.pow(p1.x - center.x, 2) + Math.pow(p1.y - center.y, 2)); var angle = function (p, center) { return Math.atan2(p.y - center.y, p.x - center.x); }; var startAngle = angle(p1, center); var midAngle = angle(p2, center); var endAngle = angle(p3, center); var isCounterclockwise = (endAngle > startAngle && endAngle < midAngle) || (startAngle > endAngle && startAngle < midAngle) || (midAngle > endAngle && midAngle < startAngle); this.center = center; this.radius = radius; this.startAngle = startAngle; this.endAngle = endAngle; this.clockwise = !isCounterclockwise; }; /** * Create circular arc by two points and one bugle factor * @param from Input start point * @param to Input end point * @param bulge Input the bulge factor used to indicate how much of an arc segment is present at this * vertex. The bulge factor is the tangent of one fourth the included angle for an arc segment, made * negative if the arc goes clockwise from the start point to the endpoint. A bulge of 0 indicates a * straight segment, and a bulge of 1 is a semicircle. Get more details from the following links. * - https://ezdxf.readthedocs.io/en/stable/dxfentities/lwpolyline.html * - https://www.afralisp.net/archive/lisp/Bulges1.htm */ AcGeCircArc2d.prototype.createByStartEndPointsAndBulge = function (from, to, bulge) { var theta; var a; var b; if (bulge < 0) { theta = Math.atan(-bulge) * 4; a = new AcGeVector2d(from); b = new AcGeVector2d(to); } else { // Default is counter-clockwise theta = Math.atan(bulge) * 4; a = new AcGeVector2d(to); b = new AcGeVector2d(from); } var ab = new AcGeVector2d().subVectors(b, a); var lengthAB = ab.length(); var c = new AcGeVector2d().addVectors(a, ab.multiplyScalar(0.5)); // Distance from center of arc to line between form and to points var lengthCD = Math.abs(lengthAB / 2 / Math.tan(theta / 2)); var normAB = ab.normalize(); var d; if (theta < Math.PI) { var normDC = new AcGeVector2d(normAB.x * Math.cos(Math.PI / 2) - normAB.y * Math.sin(Math.PI / 2), normAB.y * Math.cos(Math.PI / 2) + normAB.x * Math.sin(Math.PI / 2)); // d is the center of the arc d = c.add(normDC.multiplyScalar(-lengthCD)); } else { var normCD = new AcGeVector2d(normAB.x * Math.cos(Math.PI / 2) - normAB.y * Math.sin(Math.PI / 2), normAB.y * Math.cos(Math.PI / 2) + normAB.x * Math.sin(Math.PI / 2)); // d is the center of the arc d = c.add(normCD.multiplyScalar(lengthCD)); } // Add points between start start and eng angle relative // to the center point if (bulge < 0) { this.startAngle = Math.atan2(a.y - d.y, a.x - d.x); this.endAngle = Math.atan2(b.y - d.y, b.x - d.x); } else { this.startAngle = Math.atan2(b.y - d.y, b.x - d.x); this.endAngle = Math.atan2(a.y - d.y, a.x - d.x); } this.clockwise = bulge < 0; this.center = d; this.radius = b.sub(d).length(); }; Object.defineProperty(AcGeCircArc2d.prototype, "center", { /** * Center of circular arc */ get: function () { return this._center; }, set: function (value) { this._center = new AcGePoint2d(value.x, value.y); this._boundingBoxNeedsUpdate = true; }, enumerable: false, configurable: true }); Object.defineProperty(AcGeCircArc2d.prototype, "radius", { /** * Radius of circular arc */ get: function () { return this._radius; }, set: function (value) { this._radius = value; this._boundingBoxNeedsUpdate = true; }, enumerable: false, configurable: true }); Object.defineProperty(AcGeCircArc2d.prototype, "startAngle", { /** * Start angle in radians of circular arc in the range 0 to 2 * PI. */ get: function () { return this._startAngle; }, set: function (value) { this._startAngle = AcGeMathUtil.normalizeAngle(value); this._boundingBoxNeedsUpdate = true; }, enumerable: false, configurable: true }); Object.defineProperty(AcGeCircArc2d.prototype, "endAngle", { /** * End angle in radians of circular arc in the range 0 to 2 * PI. */ get: function () { return this._endAngle; }, set: function (value) { this._endAngle = this.startAngle == 0 && value == TAU ? value : AcGeMathUtil.normalizeAngle(value); this._boundingBoxNeedsUpdate = true; }, enumerable: false, configurable: true }); Object.defineProperty(AcGeCircArc2d.prototype, "deltaAngle", { /** * Angle between endAngle and startAngle in range 0 to 2*PI */ get: function () { return this.clockwise ? AcGeMathUtil.normalizeAngle(this.startAngle - this.endAngle) : AcGeMathUtil.normalizeAngle(this.endAngle - this.startAngle); }, enumerable: false, configurable: true }); Object.defineProperty(AcGeCircArc2d.prototype, "clockwise", { /** * Rotation direction of the arc. */ get: function () { return this._clockwise; }, set: function (value) { this._clockwise = value; this._boundingBoxNeedsUpdate = true; }, enumerable: false, configurable: true }); Object.defineProperty(AcGeCircArc2d.prototype, "startPoint", { /** * Start point of circular arc */ get: function () { return this.getPointAtAngle(this.startAngle); }, enumerable: false, configurable: true }); Object.defineProperty(AcGeCircArc2d.prototype, "endPoint", { /** * End point of circular arc */ get: function () { return this.getPointAtAngle(this.endAngle); }, enumerable: false, configurable: true }); Object.defineProperty(AcGeCircArc2d.prototype, "midPoint", { /** * Middle point of circular arc */ get: function () { var midAngle = AcGeMathUtil.normalizeAngle((this.startAngle + this.endAngle) / 2); return this.getPointAtAngle(midAngle); }, enumerable: false, configurable: true }); Object.defineProperty(AcGeCircArc2d.prototype, "closed", { /** * Return true if its start point is identical to its end point. Otherwise, return false. */ get: function () { return (Math.abs(this.endAngle - this.startAngle) / Math.PI) % 2 == 0; }, enumerable: false, configurable: true }); /** * @inheritdoc */ AcGeCircArc2d.prototype.calculateBoundingBox = function () { var e_1, _a; var points = [this.startPoint, this.endPoint]; var criticalAngles = [0, Math.PI / 2, Math.PI, (3 * Math.PI) / 2]; try { for (var criticalAngles_1 = __values(criticalAngles), criticalAngles_1_1 = criticalAngles_1.next(); !criticalAngles_1_1.done; criticalAngles_1_1 = criticalAngles_1.next()) { var angle = criticalAngles_1_1.value; if (AcGeMathUtil.isBetweenAngle(angle, this.startAngle, this.endAngle, this.clockwise)) { points.push(this.getPointAtAngle(angle)); } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (criticalAngles_1_1 && !criticalAngles_1_1.done && (_a = criticalAngles_1.return)) _a.call(criticalAngles_1); } finally { if (e_1) throw e_1.error; } } var xValues = points.map(function (p) { return p.x; }); var yValues = points.map(function (p) { return p.y; }); return new AcGeBox2d(new AcGePoint2d(Math.min.apply(Math, __spreadArray([], __read(xValues), false)), Math.min.apply(Math, __spreadArray([], __read(yValues), false))), new AcGePoint2d(Math.max.apply(Math, __spreadArray([], __read(xValues), false)), Math.max.apply(Math, __spreadArray([], __read(yValues), false)))); }; Object.defineProperty(AcGeCircArc2d.prototype, "length", { /** * Get length of circular arc */ get: function () { return Math.abs(this.deltaAngle * this.radius); }, enumerable: false, configurable: true }); /** * @inheritdoc */ AcGeCircArc2d.prototype.transform = function (_matrix) { // TODO: implement it this._boundingBoxNeedsUpdate = true; return this; }; /** * @inheritdoc */ AcGeCircArc2d.prototype.clone = function () { return new AcGeCircArc2d(this.center.clone(), this.radius, this.startAngle, this.endAngle, this.clockwise); }; /** * Calculate a point on the ellipse at a given angle. * @param angle Input the angle in radians where the point is to be calculated. * @returns Return the 2d coordinates of the point on the circular arc. */ AcGeCircArc2d.prototype.getPointAtAngle = function (angle) { var x = this.center.x + this.radius * Math.cos(angle); var y = this.center.y + this.radius * Math.sin(angle); return new AcGePoint2d(x, y); }; /** * Divide this arc into the specified nubmer of points and return those points as an array of points. * @param numPoints Input the nubmer of points returned * @returns Return an array of points */ AcGeCircArc2d.prototype.getPoints = function (numPoints) { if (numPoints === void 0) { numPoints = 100; } var points = []; var deltaAngle = this.deltaAngle; var startAngle = this.startAngle; if (this.closed) { deltaAngle = TAU; startAngle = 0; } if (this.clockwise) { for (var i = 0; i <= numPoints; i++) { var angle = startAngle - deltaAngle * (i / numPoints); var point = this.getPointAtAngle(angle); points.push(new AcGePoint2d(point.x, point.y)); } } else { for (var i = 0; i <= numPoints; i++) { var angle = startAngle + deltaAngle * (i / numPoints); var point = this.getPointAtAngle(angle); points.push(new AcGePoint2d(point.x, point.y)); } } return points; }; return AcGeCircArc2d; }(AcGeCurve2d)); export { AcGeCircArc2d }; //# sourceMappingURL=AcGeCircArc2d.js.map