@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
447 lines • 17.2 kB
JavaScript
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.");
};
import { AcCmErrors } from '@mlightcad/common';
import { AcGeBox3d, AcGePlane, AcGePoint3d, AcGeVector3d } from '../math';
import { AcGeMathUtil, ORIGIN_POINT_3D, TAU } from '../util';
import { AcGeCurve3d } from './AcGeCurve3d';
import { AcGeLine3d } from './AcGeLine3d';
/**
* The class represeting both full circles and circular arcs in 3d space. The ellipse arc is
* defined by a center point, radius, start angle, end angle, a normal vector, and a reference
* vector. If start angle is equal to 0 and end angle is equal to 2 * Math.PI, it represents
* a full circle.
*/
var AcGeCircArc3d = /** @class */ (function (_super) {
__extends(AcGeCircArc3d, _super);
/**
* Create a 3d circular arc.
* @param center The center point of the arc.
* @param radius The radius of the arc.
* @param startAngle The start angle of the arc.
* @param endAngle The end angle of the arc.
* @param normal The normal vector of the plane in which the arc lies.
* @param refVec The reference vector from which angles are measured. Default value is x axis.
*/
function AcGeCircArc3d(center, radius, startAngle, endAngle, normal, refVec) {
if (refVec === void 0) { refVec = AcGeVector3d.X_AXIS; }
var _this = _super.call(this) || this;
_this.center = center;
_this.radius = radius;
_this.startAngle = startAngle;
_this.endAngle = endAngle;
_this.normal = normal;
_this.refVec = refVec;
// Check whether it is a full ellipse
if ((endAngle - startAngle) % TAU == 0) {
_this.startAngle = 0;
_this.endAngle = TAU;
}
else {
_this.startAngle = startAngle;
_this.endAngle = endAngle;
}
return _this;
}
/**
* Compute center point of the arc given three points
* @param startPoint Input start point of the arc
* @param endPoint Input end point of the arc
* @param pointOnArc Input one point on the arc (P3)
* @returns Return center point of the arc
*/
AcGeCircArc3d.computeCenterPoint = function (startPoint, endPoint, pointOnArc) {
// Midpoints of the edges
var mid1 = new AcGeVector3d()
.addVectors(startPoint, endPoint)
.multiplyScalar(0.5);
var mid2 = new AcGeVector3d()
.addVectors(startPoint, pointOnArc)
.multiplyScalar(0.5);
// Vectors perpendicular to the edges
var vec1 = new AcGeVector3d().subVectors(endPoint, startPoint);
var vec2 = new AcGeVector3d().subVectors(pointOnArc, startPoint);
// Normal vector to the plane formed by the triangle
var normal = new AcGeVector3d().crossVectors(vec1, vec2).normalize();
if (normal.lengthSq() === 0) {
// If the points are collinear, the normal vector will have zero length.
console.error('Points are collinear and cannot form a valid arc.');
return null;
}
// Compute perpendicular vectors on the plane of the triangle
var perpendicular1 = new AcGeVector3d()
.crossVectors(vec1, normal)
.normalize();
var perpendicular2 = new AcGeVector3d()
.crossVectors(vec2, normal)
.normalize();
// Solve the system of equations to find the intersection point (center of the circle)
var direction1 = perpendicular1
.clone()
.multiplyScalar(Number.MAX_SAFE_INTEGER);
var direction2 = perpendicular2
.clone()
.multiplyScalar(Number.MAX_SAFE_INTEGER);
var line1 = new AcGeLine3d(mid1, mid1.clone().add(direction1));
var line2 = new AcGeLine3d(mid2, mid2.clone().add(direction2));
var center = new AcGeVector3d();
var result = line1.closestPointToPoint(line2.startPoint, true, center);
if (!result) {
console.error('Cannot find a valid center for the arc.');
return null;
}
return center;
};
/**
* Create arc by three points
* @param startPoint Input the start point
* @param endPoint Input the end point
* @param pointOnArc Input one point between the start point and the end point
*/
AcGeCircArc3d.createByThreePoints = function (startPoint, endPoint, pointOnArc) {
var center = AcGeCircArc3d.computeCenterPoint(startPoint, endPoint, pointOnArc);
if (center) {
var radius = center.distanceTo(startPoint);
// Compute the vectors from the center to the start and end points
var centerToStart = new AcGeVector3d().subVectors(startPoint, center);
var centerToEnd = new AcGeVector3d().subVectors(endPoint, center);
// Compute the start angle and end angle relative to the x-axis
var startAngle = Math.atan2(centerToStart.y, centerToStart.x);
var endAngle = Math.atan2(centerToEnd.y, centerToEnd.x);
return new AcGeCircArc3d(center, radius, startAngle, endAngle, AcGeVector3d.Z_AXIS);
}
};
Object.defineProperty(AcGeCircArc3d.prototype, "center", {
/**
* Center of circular arc
*/
get: function () {
return this._center;
},
set: function (value) {
this._center = new AcGePoint3d(value.x, value.y, value.z || 0);
this._boundingBoxNeedsUpdate = true;
},
enumerable: false,
configurable: true
});
Object.defineProperty(AcGeCircArc3d.prototype, "radius", {
/**
* Radius of circular arc
*/
get: function () {
return this._radius;
},
set: function (value) {
if (value < 0)
throw AcCmErrors.ILLEGAL_PARAMETERS;
this._radius = value;
this._boundingBoxNeedsUpdate = true;
},
enumerable: false,
configurable: true
});
Object.defineProperty(AcGeCircArc3d.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(AcGeCircArc3d.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(AcGeCircArc3d.prototype, "deltaAngle", {
/**
* Return angle between endAngle and startAngle in range 0 to 2*PI
*/
get: function () {
return AcGeMathUtil.normalizeAngle(this.endAngle - this.startAngle);
},
enumerable: false,
configurable: true
});
Object.defineProperty(AcGeCircArc3d.prototype, "isLargeArc", {
/**
* Return true if the arc is a large arc whose delta angle value is greater than PI.
*/
get: function () {
return Math.abs(this.deltaAngle) > Math.PI ? 1 : 0;
},
enumerable: false,
configurable: true
});
Object.defineProperty(AcGeCircArc3d.prototype, "clockwise", {
/**
* Return true if the arc is clockwise from startAngle to endAngle
*/
get: function () {
return this.deltaAngle <= 0;
},
enumerable: false,
configurable: true
});
Object.defineProperty(AcGeCircArc3d.prototype, "normal", {
/**
* Normal vector defining the plane of the circular arc
*/
get: function () {
return this._normal;
},
set: function (value) {
this._normal = new AcGeVector3d(value.x, value.y, value.z);
// Normalize the normal vector to ensure it's a unit vector
this._normal.normalize();
this._boundingBoxNeedsUpdate = true;
},
enumerable: false,
configurable: true
});
Object.defineProperty(AcGeCircArc3d.prototype, "refVec", {
/**
* The unit reference vector of circular arc
*/
get: function () {
return this._refVec;
},
set: function (value) {
this._refVec = new AcGeVector3d(value.x, value.y, value.z);
// Normalize the normal vector to ensure it's a unit vector
this._refVec.normalize();
this._boundingBoxNeedsUpdate = true;
},
enumerable: false,
configurable: true
});
Object.defineProperty(AcGeCircArc3d.prototype, "startPoint", {
/**
* The start point of circular arc
*/
get: function () {
return this.getPointAtAngle(this._startAngle);
},
enumerable: false,
configurable: true
});
Object.defineProperty(AcGeCircArc3d.prototype, "endPoint", {
/**
* The end point of circular arc
*/
get: function () {
return this.getPointAtAngle(this._endAngle);
},
enumerable: false,
configurable: true
});
Object.defineProperty(AcGeCircArc3d.prototype, "length", {
/**
* @inheritdoc
*/
get: function () {
return Math.abs(this.deltaAngle * this.radius);
},
enumerable: false,
configurable: true
});
/**
* @inheritdoc
*/
AcGeCircArc3d.prototype.calculateBoundingBox = function () {
var e_1, _a;
var angles = [this.startAngle, this.endAngle];
for (var i = 0; i < 2 * Math.PI; i += Math.PI / 2) {
if (AcGeMathUtil.isBetweenAngle(i, this.startAngle, this.endAngle)) {
angles.push(i);
}
}
var minX = Infinity, minY = Infinity, minZ = Infinity;
var maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
try {
for (var angles_1 = __values(angles), angles_1_1 = angles_1.next(); !angles_1_1.done; angles_1_1 = angles_1.next()) {
var angle = angles_1_1.value;
var point = this.getPointAtAngle(angle);
if (point.x < minX)
minX = point.x;
if (point.y < minY)
minY = point.y;
if (point.z < minZ)
minZ = point.z;
if (point.x > maxX)
maxX = point.x;
if (point.y > maxY)
maxY = point.y;
if (point.z > maxZ)
maxZ = point.z;
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (angles_1_1 && !angles_1_1.done && (_a = angles_1.return)) _a.call(angles_1);
}
finally { if (e_1) throw e_1.error; }
}
return new AcGeBox3d({ x: minX, y: minY, z: minZ }, { x: maxX, y: maxY, z: maxZ });
};
Object.defineProperty(AcGeCircArc3d.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
});
/**
* Divide this arc into the specified nubmer of points
* those points as an array of points.
* @param numPoints Input the nubmer of points returned
* @returns Return an array of point
*/
AcGeCircArc3d.prototype.getPoints = function (numPoints) {
var points = [];
var deltaAngle = this.deltaAngle;
var startAngle = this.startAngle;
if (this.closed) {
deltaAngle = TAU;
startAngle = 0;
}
for (var i = 0; i <= numPoints; i++) {
var angle = startAngle + deltaAngle * (i / numPoints);
var point = this.getPointAtAngle(angle);
points.push(point);
}
return points;
};
/**
* @inheritdoc
*/
AcGeCircArc3d.prototype.transform = function (matrix) {
var startVec = _vector3
.copy(this.refVec)
.applyAxisAngle(this.normal, this.startAngle)
.multiplyScalar(this.radius);
var endVec = _vector3
.copy(this.refVec)
.applyAxisAngle(this.normal, this.endAngle)
.multiplyScalar(this.radius);
this.center.applyMatrix3d(matrix);
startVec.applyMatrix3d(matrix);
endVec.applyMatrix3d(matrix);
this.normal.applyMatrix3d(matrix).normalize();
this.refVec.applyMatrix3d(matrix).normalize();
this.startAngle = this.getAngle(startVec);
this.endAngle = this.getAngle(endVec);
this._boundingBoxNeedsUpdate = true;
return this;
};
/**
* @inheritdoc
*/
AcGeCircArc3d.prototype.copy = function (value) {
this.center = value.center;
this.radius = value.radius;
this.startAngle = value.startAngle;
this.endAngle = value.endAngle;
this.normal = value.normal;
this.refVec = value.refVec;
this._boundingBoxNeedsUpdate = true;
return this;
};
/**
* @inheritdoc
*/
AcGeCircArc3d.prototype.clone = function () {
return new AcGeCircArc3d(this.center.clone(), this.radius, this.startAngle, this.endAngle, this.normal, this.refVec);
};
/**
* Calculate angle between the specified vec and the reference vector of this arc.
* @param vec Input one vector
* @returns Return angle between the specified vec and the reference vector of this arc.
*/
AcGeCircArc3d.prototype.getAngle = function (vec) {
vec.sub(this.center); // 转换到以圆心为中心的坐标系
return Math.atan2(vec.dot(_vector3.crossVectors(this.refVec, this.normal)), vec.dot(this.refVec));
};
/**
* Returns the point on the arc at a specific angle.
* @param angle The angle at which to get the point.
*/
AcGeCircArc3d.prototype.getPointAtAngle = function (angle) {
// Calculate orthogonal vector to refVec in the plane of the arc
var n = this.normal;
var r = this.refVec;
var orthogonalVec = {
x: n.y * r.z - n.z * r.y,
y: n.z * r.x - n.x * r.z,
z: n.x * r.y - n.y * r.x
};
// Point on the arc at the given angle
var center = this.center;
var radius = this.radius;
return new AcGePoint3d(center.x +
radius * (r.x * Math.cos(angle) + orthogonalVec.x * Math.sin(angle)), center.y +
radius * (r.y * Math.cos(angle) + orthogonalVec.y * Math.sin(angle)), center.z +
radius * (r.z * Math.cos(angle) + orthogonalVec.z * Math.sin(angle)));
};
Object.defineProperty(AcGeCircArc3d.prototype, "plane", {
/**
* Return the plane in which the circular arc lies.
*/
get: function () {
var distance = new AcGeVector3d(this.center).distanceTo(ORIGIN_POINT_3D);
return new AcGePlane(this.normal, distance);
},
enumerable: false,
configurable: true
});
return AcGeCircArc3d;
}(AcGeCurve3d));
export { AcGeCircArc3d };
var _vector3 = /*@__PURE__*/ new AcGeVector3d();
//# sourceMappingURL=AcGeCircArc3d.js.map