8786 lines
276 KiB
JavaScript
8786 lines
276 KiB
JavaScript
import { Vector3, Matrix4, Vector2, Box3, Object3D, Shape, EllipseCurve, ExtrudeGeometry, Mesh, LineDashedMaterial, MeshBasicMaterial, LineBasicMaterial, DoubleSide, ShaderMaterial, Color, BufferGeometry, ShapeGeometry, BufferAttribute, Plane, Line3, Line as Line$1, MathUtils } from 'three';
|
||
import { iaop } from 'xaop';
|
||
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial';
|
||
import { Line2 } from 'three/examples/jsm/lines/Line2';
|
||
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry';
|
||
|
||
/*! *****************************************************************************
|
||
Copyright (c) Microsoft Corporation.
|
||
|
||
Permission to use, copy, modify, and/or distribute this software for any
|
||
purpose with or without fee is hereby granted.
|
||
|
||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||
PERFORMANCE OF THIS SOFTWARE.
|
||
***************************************************************************** */
|
||
|
||
function __decorate(decorators, target, key, desc)
|
||
{
|
||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||
}
|
||
|
||
let HostApplicationServices = { ShowHistoryLog: true };
|
||
|
||
function observable() { };
|
||
function toJS() { };
|
||
|
||
/**
|
||
* 销毁Object对象的Geometry,并不会销毁材质.
|
||
*/
|
||
function DisposeThreeObj(obj)
|
||
{
|
||
for (let o of obj.children) {
|
||
let oany = o;
|
||
//文字的geometry缓存保留下来
|
||
if (oany.geometry && oany.geometry.name !== "Text")
|
||
oany.geometry.dispose();
|
||
DisposeThreeObj(o);
|
||
o.parent = null;
|
||
o.dispatchEvent({ type: "removed" });
|
||
}
|
||
obj.children.length = 0;
|
||
return obj;
|
||
}
|
||
function Object3DRemoveAll(obj)
|
||
{
|
||
for (let o of obj.children) {
|
||
o.parent = null;
|
||
o.dispatchEvent({ type: "removed" });
|
||
}
|
||
obj.children.length = 0;
|
||
return obj;
|
||
}
|
||
|
||
/**
|
||
* 坐标系运算.
|
||
*/
|
||
class CoordinateSystem
|
||
{
|
||
constructor(postion, xAxis, yAxis, zAxis)
|
||
{
|
||
this.Postion = postion || new Vector3(0, 0, 0);
|
||
this.XAxis = xAxis || new Vector3(1, 0, 0);
|
||
this.YAxis = yAxis || new Vector3(0, 1, 0);
|
||
this.ZAxis = zAxis || new Vector3(0, 0, 1);
|
||
}
|
||
applyMatrix4(mat4)
|
||
{
|
||
this.Postion.applyMatrix4(mat4);
|
||
let roMat = mat4.clone().setPosition(new Vector3());
|
||
this.XAxis.applyMatrix4(roMat);
|
||
this.YAxis.applyMatrix4(roMat);
|
||
this.ZAxis.applyMatrix4(roMat);
|
||
return this;
|
||
}
|
||
getMatrix4()
|
||
{
|
||
let m = new Matrix4();
|
||
m.makeBasis(this.XAxis, this.YAxis, this.ZAxis);
|
||
m.setPosition(this.Postion);
|
||
return m;
|
||
}
|
||
CopyForm(mat4)
|
||
{
|
||
this.Postion.setFromMatrixPosition(mat4);
|
||
mat4.extractBasis(this.XAxis, this.YAxis, this.ZAxis);
|
||
return this;
|
||
}
|
||
extractBasis(xAxisA, yAxisA, zAxisA)
|
||
{
|
||
xAxisA.copy(this.XAxis);
|
||
yAxisA.copy(this.YAxis);
|
||
zAxisA.copy(this.ZAxis);
|
||
}
|
||
copy(cs)
|
||
{
|
||
this.Postion.copy(cs.Postion);
|
||
this.XAxis.copy(cs.XAxis);
|
||
this.YAxis.copy(cs.YAxis);
|
||
this.ZAxis.copy(cs.ZAxis);
|
||
return this;
|
||
}
|
||
clone()
|
||
{
|
||
let r = new CoordinateSystem();
|
||
r.Postion = this.Postion.clone();
|
||
r.XAxis = this.XAxis.clone();
|
||
r.YAxis = this.YAxis.clone();
|
||
r.ZAxis = this.ZAxis.clone();
|
||
return r;
|
||
}
|
||
}
|
||
|
||
const IdentityMtx4 = new Matrix4();
|
||
const ZeroVec = new Vector3();
|
||
const XAxis = new Vector3(1, 0, 0);
|
||
const XAxisN = new Vector3(-1, 0, 0);
|
||
const YAxis = new Vector3(0, 1, 0);
|
||
const YAxisN = new Vector3(0, -1, 0);
|
||
const ZAxis = new Vector3(0, 0, 1);
|
||
function AsVector2(p)
|
||
{
|
||
return new Vector2(p.x, p.y);
|
||
}
|
||
function AsVector3(p)
|
||
{
|
||
return new Vector3(p.x, p.y, p.z);
|
||
}
|
||
/**
|
||
* 旋转一个点,旋转中心在原点
|
||
* @param {Vector3} p 点
|
||
* @param {number} a 角度.
|
||
* @returns {Vector3} 返回pt不拷贝.
|
||
*/
|
||
function rotatePoint(p, a)
|
||
{
|
||
let s = Math.sin(a);
|
||
let c = Math.cos(a);
|
||
let x = p.x * c - p.y * s;
|
||
let y = p.x * s + p.y * c;
|
||
p.x = x;
|
||
p.y = y;
|
||
return p;
|
||
}
|
||
function equaln(v1, v2, fuzz = 1e-5)
|
||
{
|
||
return Math.abs(v1 - v2) <= fuzz;
|
||
}
|
||
function equalv3(v1, v2, fuzz = 1e-8)
|
||
{
|
||
return equaln(v1.x, v2.x, fuzz) && equaln(v1.y, v2.y, fuzz) && equaln(v1.z, v2.z, fuzz);
|
||
}
|
||
function equalv2(v1, v2, fuzz = 1e-8)
|
||
{
|
||
return equaln(v1.x, v2.x, fuzz) && equaln(v1.y, v2.y, fuzz);
|
||
}
|
||
/**
|
||
* 按照极坐标的方式移动一个点
|
||
*
|
||
* @export
|
||
* @template
|
||
* @param {T} v 向量(2d,3d)
|
||
* @param {number} an 角度
|
||
* @param {number} dis 距离
|
||
* @returns {T}
|
||
*/
|
||
function polar(v, an, dis)
|
||
{
|
||
v.x += Math.cos(an) * dis;
|
||
v.y += Math.sin(an) * dis;
|
||
return v;
|
||
}
|
||
function angle(v)
|
||
{
|
||
let angle = Math.atan2(v.y, v.x);
|
||
if (equaln(angle, 0, 1e-8))
|
||
return 0;
|
||
if (angle < 0)
|
||
angle += Math.PI * 2;
|
||
return angle;
|
||
}
|
||
/**
|
||
* 求两个向量的夹角,顺时针为负,逆时针为正
|
||
*
|
||
* @param {Vector3} v1
|
||
* @param {Vector3} v2
|
||
* @param {Vector3} [ref] 参考向量,如果为世界坐标系则为0,0,1
|
||
* @returns
|
||
*/
|
||
function angleTo(v1, v2, ref = new Vector3(0, 0, 1))
|
||
{
|
||
if (!ref.equals(new Vector3(0, 0, 1))) {
|
||
ref = ref.clone();
|
||
v1 = v1.clone();
|
||
v2 = v2.clone();
|
||
//任意轴坐标系. 使用相机的构造矩阵.
|
||
ref.multiplyScalar(-1);
|
||
let up = getLoocAtUpVec(ref);
|
||
let refOcs = new Matrix4();
|
||
refOcs.lookAt(ZeroVec, ref, up);
|
||
let refOcsInv = new Matrix4().getInverse(refOcs);
|
||
v1.applyMatrix4(refOcsInv);
|
||
v2.applyMatrix4(refOcsInv);
|
||
v1.z = 0;
|
||
v2.z = 0;
|
||
}
|
||
if (v1.equals(ZeroVec) || v2.equals(ZeroVec))
|
||
return 0;
|
||
let cv = new Vector3().crossVectors(v1, v2).normalize();
|
||
return cv.z === 0 ? v1.angleTo(v2) : v1.angleTo(v2) * cv.z;
|
||
}
|
||
function getLoocAtUpVec(dir)
|
||
{
|
||
if (dir.equals(ZeroVec)) {
|
||
throw ("zero vector");
|
||
}
|
||
let norm = dir.clone().normalize();
|
||
if (norm.equals(ZAxis)) {
|
||
return new Vector3(0, 1, 0);
|
||
}
|
||
else if (norm.equals(ZAxis.clone().negate())) {
|
||
return new Vector3(0, -1, 0);
|
||
}
|
||
else {
|
||
let xv = new Vector3();
|
||
xv.crossVectors(ZAxis, norm);
|
||
let up = new Vector3();
|
||
up.crossVectors(norm, xv);
|
||
return up;
|
||
}
|
||
}
|
||
/**
|
||
* 判断2个向量是不是平行,尽量传入单位向量,才能保证计算精度
|
||
*/
|
||
function isParallelTo(v1, v2, fuzz = 1e-8)
|
||
{
|
||
return v1.clone().cross(v2).lengthSq() < fuzz;
|
||
}
|
||
function midPoint(v1, v2)
|
||
{
|
||
return v1.clone().add(v2).multiplyScalar(0.5);
|
||
}
|
||
function MoveMatrix(v)
|
||
{
|
||
return new Matrix4().setPosition(v);
|
||
}
|
||
/**
|
||
* 将角度调整为0-2pi之间
|
||
*/
|
||
function clampRad(an)
|
||
{
|
||
an = an % (Math.PI * 2);
|
||
if (an < 0)
|
||
an += Math.PI * 2;
|
||
return an;
|
||
}
|
||
function updateGeometry(l, geometry)
|
||
{
|
||
let geo = l.geometry;
|
||
geo.dispose();
|
||
l.geometry = geometry;
|
||
geometry.computeBoundingSphere();
|
||
}
|
||
function UpdateBoundingSphere(obj)
|
||
{
|
||
//@ts-ignore
|
||
let geo = obj.geometry;
|
||
if (geo)
|
||
geo.computeBoundingSphere();
|
||
}
|
||
const comparePointCache = new Map();
|
||
/**
|
||
* 构建返回一个用来排序的函数.根据key创建排序规则.
|
||
*
|
||
* 当key = "xyz" 时,点集按 x从小到大,y从小到大 z从小到大
|
||
* key = "X" 时,点集按 x从大到小
|
||
* 以此类推.
|
||
*
|
||
* 例子:
|
||
* let pts:Vector3[] =...;
|
||
* pts.sort(comparePoint("x")); //x从小到大排序
|
||
* pts.sort(comparePoint("zX")); //z从小到大 x从大到小
|
||
*
|
||
* @export
|
||
* @param {string} sortKey
|
||
* @returns {compareVectorFn}
|
||
*/
|
||
function comparePoint(sortKey)
|
||
{
|
||
if (comparePointCache.has(sortKey))
|
||
return comparePointCache.get(sortKey);
|
||
let sortIndex = [];
|
||
const keys = ['x', 'X', 'y', 'Y', 'z', 'Z'];
|
||
for (let char of sortKey) {
|
||
let index = keys.indexOf(char);
|
||
let i2 = index / 2;
|
||
let ci = Math.floor(i2);
|
||
sortIndex.push([ci, i2 > ci ? 1 : -1]);
|
||
}
|
||
let compareFunction = (v1, v2) =>
|
||
{
|
||
if (!v1)
|
||
return -1;
|
||
if (!v2)
|
||
return 1;
|
||
for (let s of sortIndex) {
|
||
let vv1 = v1.getComponent(s[0]);
|
||
let vv2 = v2.getComponent(s[0]);
|
||
if (equaln(vv1, vv2))
|
||
continue;
|
||
if (vv2 > vv1)
|
||
return s[1];
|
||
else
|
||
return -s[1];
|
||
}
|
||
return 0;
|
||
};
|
||
comparePointCache.set(sortKey, compareFunction);
|
||
return compareFunction;
|
||
}
|
||
function SelectNearP(pts, refPt)
|
||
{
|
||
if (pts.length > 1) {
|
||
let dist1 = refPt.distanceToSquared(pts[0]);
|
||
let dist2 = refPt.distanceToSquared(pts[1]);
|
||
return dist1 <= dist2 ? pts[0] : pts[1];
|
||
}
|
||
return pts[0];
|
||
}
|
||
|
||
/**
|
||
* 设置矩阵的某列的向量
|
||
* @param {Matrix4} mat 矩阵
|
||
* @param {number} col 列索引,0x 1y 2z 3org
|
||
* @param {Vector3} v 向量或点
|
||
*/
|
||
function matrixSetVector(mat, col, v)
|
||
{
|
||
let index = col * 4;
|
||
mat.elements[index] = v.x;
|
||
mat.elements[index + 1] = v.y;
|
||
mat.elements[index + 2] = v.z;
|
||
}
|
||
/**
|
||
* 返回矩阵,该坐标系将坐标系与原点的坐标系映射为坐标系,
|
||
* 并将坐标系与X轴坐标系,
|
||
* Y轴坐标轴以及Z轴坐标系统之间的坐标系统坐标系统的原点坐标系和原点坐标系统坐标轴的坐标系分别设置为XAxis,YAxis和ZAxis
|
||
* @returns {Matrix4} 返回新的矩阵
|
||
*/
|
||
function matrixAlignCoordSys(matrixFrom, matrixTo)
|
||
{
|
||
return new Matrix4().getInverse(matrixTo).multiply(matrixFrom);
|
||
}
|
||
/**
|
||
* 判断2个矩形共面
|
||
* @param {Matrix4} matrixFrom
|
||
* @param {Matrix4} matrixTo
|
||
* @returns {boolean} 2个矩阵共面
|
||
*/
|
||
function matrixIsCoplane(matrixFrom, matrixTo, fuzz = 1e-5)
|
||
{
|
||
let nor1 = new Vector3().setFromMatrixColumn(matrixFrom, 2);
|
||
let nor2 = new Vector3().setFromMatrixColumn(matrixTo, 2);
|
||
//法线共面
|
||
if (!isParallelTo(nor1, nor2))
|
||
return false;
|
||
//高共面
|
||
let pt = new Vector3().setFromMatrixPosition(matrixTo);
|
||
//变换到自身对象坐标系.
|
||
pt.applyMatrix4(new Matrix4().getInverse(matrixFrom));
|
||
return equaln(pt.z, 0, fuzz);
|
||
}
|
||
/**
|
||
* 修正镜像后矩阵
|
||
*/
|
||
function reviseMirrorMatrix(mtx)
|
||
{
|
||
let cs = new CoordinateSystem().applyMatrix4(mtx);
|
||
cs.YAxis.negate();
|
||
mtx.copy(cs.getMatrix4());
|
||
return mtx;
|
||
}
|
||
/**
|
||
* 把变换矩阵展平成2d矩阵,避免出现三维坐标.
|
||
*/
|
||
function MatrixPlanarizere(mtx, z0 = true)
|
||
{
|
||
mtx.elements[2] = 0;
|
||
mtx.elements[6] = 0;
|
||
mtx.elements[8] = 0;
|
||
mtx.elements[9] = 0;
|
||
mtx.elements[10] = Math.sign(mtx.elements[10]);
|
||
if (z0)
|
||
mtx.elements[14] = 0;
|
||
return mtx;
|
||
}
|
||
const tempMatrix1 = new Matrix4;
|
||
|
||
var Status;
|
||
(function (Status)
|
||
{
|
||
Status[Status["False"] = 0] = "False";
|
||
Status[Status["True"] = 1] = "True";
|
||
Status[Status["Canel"] = -1] = "Canel";
|
||
Status[Status["ConverToCircle"] = 101] = "ConverToCircle";
|
||
Status[Status["DuplicateRecordName"] = 102] = "DuplicateRecordName";
|
||
})(Status || (Status = {}));
|
||
var UpdateDraw;
|
||
(function (UpdateDraw)
|
||
{
|
||
UpdateDraw[UpdateDraw["None"] = 0] = "None";
|
||
UpdateDraw[UpdateDraw["Matrix"] = 1] = "Matrix";
|
||
UpdateDraw[UpdateDraw["Geometry"] = 2] = "Geometry";
|
||
UpdateDraw[UpdateDraw["Material"] = 4] = "Material";
|
||
UpdateDraw[UpdateDraw["All"] = 63] = "All";
|
||
})(UpdateDraw || (UpdateDraw = {}));
|
||
/**
|
||
* WblockClne时,遇到重复记录的操作方式
|
||
*/
|
||
var DuplicateRecordCloning;
|
||
(function (DuplicateRecordCloning)
|
||
{
|
||
DuplicateRecordCloning[DuplicateRecordCloning["Ignore"] = 1] = "Ignore";
|
||
DuplicateRecordCloning[DuplicateRecordCloning["Replace"] = 2] = "Replace";
|
||
DuplicateRecordCloning[DuplicateRecordCloning["Rename"] = 3] = "Rename";
|
||
})(DuplicateRecordCloning || (DuplicateRecordCloning = {}));
|
||
|
||
/**
|
||
* 场景的渲染类型.
|
||
*
|
||
* @export
|
||
* @enum {number}
|
||
*/
|
||
var RenderType;
|
||
(function (RenderType)
|
||
{
|
||
/**
|
||
* 线框模式
|
||
*/
|
||
RenderType[RenderType["Wireframe"] = 1] = "Wireframe";
|
||
/**
|
||
* 概念
|
||
*/
|
||
RenderType[RenderType["Conceptual"] = 2] = "Conceptual";
|
||
/**
|
||
* 物理着色PBR
|
||
*/
|
||
RenderType[RenderType["Physical"] = 3] = "Physical";
|
||
RenderType[RenderType["Jig"] = 4] = "Jig";
|
||
RenderType[RenderType["Print"] = 5] = "Print";
|
||
/**物理带线框 */
|
||
RenderType[RenderType["Physical2"] = 6] = "Physical2";
|
||
})(RenderType || (RenderType = {}));
|
||
|
||
var StoreageKeys;
|
||
(function (StoreageKeys)
|
||
{
|
||
StoreageKeys["IsLogin"] = "isLogin";
|
||
StoreageKeys["PlatSession"] = "platSession";
|
||
StoreageKeys["PlatToken"] = "platToken";
|
||
StoreageKeys["UserName"] = "userName";
|
||
StoreageKeys["UserPhone"] = "userPhone";
|
||
StoreageKeys["RenderType"] = "renderType";
|
||
StoreageKeys["ExactDrill"] = "openExactDrill";
|
||
StoreageKeys["ConfigName"] = "configName_";
|
||
StoreageKeys["IsNewErp"] = "isNewErp";
|
||
StoreageKeys["RoomName"] = "roomName";
|
||
StoreageKeys["LastOpenFileId"] = "lastfid";
|
||
StoreageKeys["Uid"] = "uid";
|
||
StoreageKeys["Goods"] = "Goods_";
|
||
StoreageKeys["DrillTemp"] = "drilltemp_";
|
||
StoreageKeys["DrillReactor"] = "drillRreactor";
|
||
StoreageKeys["kjlConfig"] = "kjl";
|
||
})(StoreageKeys || (StoreageKeys = {}));
|
||
|
||
var AAType;
|
||
(function (AAType)
|
||
{
|
||
AAType[AAType["FXAA"] = 0] = "FXAA";
|
||
AAType[AAType["SMAA"] = 1] = "SMAA";
|
||
})(AAType || (AAType = {}));
|
||
var ViewDirType;
|
||
(function (ViewDirType)
|
||
{
|
||
ViewDirType[ViewDirType["FS"] = 0] = "FS";
|
||
ViewDirType[ViewDirType["YAS"] = 1] = "YAS";
|
||
ViewDirType[ViewDirType["ZS"] = 2] = "ZS";
|
||
ViewDirType[ViewDirType["YS"] = 3] = "YS";
|
||
ViewDirType[ViewDirType["QS"] = 4] = "QS";
|
||
ViewDirType[ViewDirType["HS"] = 5] = "HS";
|
||
ViewDirType[ViewDirType["XN"] = 6] = "XN";
|
||
})(ViewDirType || (ViewDirType = {}));
|
||
|
||
class UserConfig
|
||
{
|
||
constructor()
|
||
{
|
||
this._version = 10;
|
||
this._renderType = RenderType.Wireframe;
|
||
this.maxSize = {
|
||
isShow: false,
|
||
height: 2440,
|
||
width: 1220,
|
||
};
|
||
this._drillConfigs = new Map();
|
||
this.openDrillingReactor = true;
|
||
this.openAutoCuttingReactor = true;
|
||
/**打开将检测排钻是否在板件内*/
|
||
this.openExactDrill = true;
|
||
this.userConfigName = {};
|
||
this.modeling2HoleRad = 20; //圆造型小于等于该值拆成孔数据
|
||
this.isAdmin = false;
|
||
this.kjlConfig = {
|
||
grooveAddLength: "0",
|
||
grooveAddWidth: "0",
|
||
grooveAddDepth: "1",
|
||
};
|
||
this.SystemConfig = {
|
||
maxHightightCount: 15000,
|
||
snapSize: 15,
|
||
aaType: AAType.FXAA
|
||
};
|
||
this.viewDirType = ViewDirType.XN;
|
||
this.useCtrlRotate = true;
|
||
this.cursorSize = {
|
||
D2: 1000,
|
||
D3: 100,
|
||
};
|
||
this.autoSaveConfig = {
|
||
enable: true,
|
||
time: 1,
|
||
};
|
||
this.showLines = false;
|
||
this.keepConfig = false;
|
||
this.autoClearHistory = true;
|
||
this.chaidanOption = {
|
||
changXiuBian: 6,
|
||
duanXiuBian: 6,
|
||
useDefaultRad: false,
|
||
radius: 2.5,
|
||
modeling2HoleRad: 20,
|
||
isCheckInterfere: false,
|
||
};
|
||
this.autoLines = false;
|
||
this.dimTextHeight = 60;
|
||
this.configName = "default";
|
||
this.configsNames = [];
|
||
this.Init();
|
||
}
|
||
Init()
|
||
{
|
||
let type = localStorage.getItem(StoreageKeys.RenderType);
|
||
if (type)
|
||
this._renderType = parseFloat(type);
|
||
}
|
||
set RenderType(t)
|
||
{
|
||
if (t !== this._renderType) {
|
||
this._renderType = t;
|
||
this.SetRenderTypeEvent();
|
||
localStorage.setItem(StoreageKeys.RenderType, t.toString());
|
||
}
|
||
}
|
||
get RenderType() { return this._renderType; }
|
||
SetRenderTypeEvent() { }
|
||
get DrillConfigs()
|
||
{
|
||
return this._drillConfigs || new Map();
|
||
}
|
||
set DrillConfigs(config)
|
||
{
|
||
observable(this._drillConfigs).replace(config);
|
||
this.SetDrillConfigsEvent();
|
||
}
|
||
SetDrillConfigsEvent() { }
|
||
InitOption()
|
||
{
|
||
this.openDrillingReactor = true;
|
||
this.openAutoCuttingReactor = true;
|
||
Object.assign(this.maxSize, {
|
||
height: 2440,
|
||
width: 1220
|
||
});
|
||
Object.assign(this.kjlConfig, {
|
||
grooveAddLength: "0",
|
||
grooveAddWidth: "0",
|
||
grooveAddDepth: "1"
|
||
});
|
||
Object.assign(this.chaidanOption, {
|
||
changXiuBian: 6,
|
||
duanXiuBian: 6,
|
||
useDefaultRad: false,
|
||
radius: 2.5,
|
||
modeling2HoleRad: 20,
|
||
});
|
||
this.dimTextHeight = 60;
|
||
}
|
||
SaveConfig()
|
||
{
|
||
return {
|
||
option: {
|
||
version: this._version,
|
||
openDrillingReactor: this.openDrillingReactor,
|
||
openAutoCuttingReactor: this.openAutoCuttingReactor,
|
||
maxSize: toJS(this.maxSize),
|
||
kjlConfig: toJS(this.kjlConfig),
|
||
systemConfig: toJS(this.SystemConfig),
|
||
viewDirType: this.viewDirType,
|
||
useCtrlRotate: this.useCtrlRotate,
|
||
cursorSize: toJS(this.cursorSize),
|
||
autoSaveConfig: toJS(this.autoSaveConfig),
|
||
showLines: this.showLines,
|
||
keepConfig: this.keepConfig,
|
||
autoClearHistory: this.autoClearHistory,
|
||
chaidanOption: toJS(this.chaidanOption),
|
||
autoLines: this.autoLines,
|
||
dimTextHeight: this.dimTextHeight,
|
||
}
|
||
};
|
||
}
|
||
UpdateOption(config)
|
||
{
|
||
this.openDrillingReactor = config.option.openDrillingReactor;
|
||
this.openAutoCuttingReactor = config.option.openAutoCuttingReactor;
|
||
Object.assign(this.maxSize, config.option.maxSize);
|
||
Object.assign(this.kjlConfig, config.option.kjlConfig);
|
||
this.modeling2HoleRad = config.option.modeling2HoleRad;
|
||
if (config.option.version > 1) {
|
||
Object.assign(this.SystemConfig, config.option.systemConfig);
|
||
}
|
||
if (config.option.version > 2) {
|
||
this.viewDirType = config.option.viewDirType;
|
||
this.useCtrlRotate = config.option.useCtrlRotate;
|
||
}
|
||
if (config.option.version > 3) {
|
||
Object.assign(this.cursorSize, config.option.cursorSize);
|
||
}
|
||
if (config.option.version > 4) {
|
||
Object.assign(this.autoSaveConfig, config.option.autoSaveConfig);
|
||
}
|
||
if (config.option.version > 5) {
|
||
this.showLines = config.option.showLines;
|
||
}
|
||
if (config.option.version > 6) {
|
||
this.keepConfig = config.option.keepConfig;
|
||
}
|
||
if (config.option.version > 7) {
|
||
this.autoClearHistory = config.option.autoClearHistory;
|
||
}
|
||
if (config.option.version > 8) {
|
||
Object.assign(this.chaidanOption, config.option.chaidanOption);
|
||
this.autoLines = config.option.autoLines;
|
||
}
|
||
else
|
||
this.chaidanOption.modeling2HoleRad = this.modeling2HoleRad;
|
||
if (config.option.version > 9)
|
||
this.dimTextHeight = config.option.dimTextHeight;
|
||
}
|
||
}
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "maxSize", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "_drillConfigs", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "openDrillingReactor", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "openAutoCuttingReactor", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "openExactDrill", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "isAdmin", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "kjlConfig", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "SystemConfig", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "viewDirType", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "useCtrlRotate", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "cursorSize", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "autoSaveConfig", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "showLines", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "keepConfig", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "autoClearHistory", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "chaidanOption", void 0);
|
||
__decorate([
|
||
observable
|
||
], UserConfig.prototype, "autoLines", void 0);
|
||
const userConfig = new UserConfig();
|
||
|
||
/**
|
||
* 盒子的切割类型
|
||
*/
|
||
var SplitType;
|
||
(function (SplitType)
|
||
{
|
||
SplitType[SplitType["X"] = 0] = "X";
|
||
SplitType[SplitType["Y"] = 1] = "Y";
|
||
SplitType[SplitType["Z"] = 2] = "Z";
|
||
})(SplitType || (SplitType = {}));
|
||
/**
|
||
* 扩展Box3,添加切割方法,体积等
|
||
*/
|
||
class Box3Ext extends Box3
|
||
{
|
||
get Volume()
|
||
{
|
||
let size = this.getSize(new Vector3());
|
||
return size.x * size.y * size.z;
|
||
}
|
||
//每个轴的大小必须大于最小的size
|
||
isSolid(minSize = 1)
|
||
{
|
||
return this.getSize(new Vector3()).toArray().every(x => x > minSize);
|
||
}
|
||
substract(b, spaceType)
|
||
{
|
||
let interBox = this.clone().intersect(b);
|
||
if (interBox.isEmpty() || !interBox.isSolid())
|
||
return [this];
|
||
let p1 = interBox.min.clone().setComponent(spaceType, this.min.getComponent(spaceType));
|
||
let p2 = interBox.max.clone().setComponent(spaceType, interBox.min.getComponent(spaceType));
|
||
let p3 = interBox.min.clone().setComponent(spaceType, interBox.max.getComponent(spaceType));
|
||
let p4 = interBox.max.clone().setComponent(spaceType, this.max.getComponent(spaceType));
|
||
return [
|
||
new Box3Ext(p1, p2),
|
||
new Box3Ext(p3, p4)
|
||
].filter(b => b.isSolid());
|
||
}
|
||
clampSpace(b2, splitType)
|
||
{
|
||
let interBox = this.clone();
|
||
interBox.min.max(b2.min);
|
||
interBox.max.min(b2.max);
|
||
interBox.min.setComponent(splitType, Math.min(this.max.getComponent(splitType), b2.max.getComponent(splitType)));
|
||
interBox.max.setComponent(splitType, Math.max(this.min.getComponent(splitType), b2.min.getComponent(splitType)));
|
||
return interBox;
|
||
}
|
||
intersectsBox(box, fuzz = 1e-8)
|
||
{
|
||
return IntersectsBox(this, box, fuzz);
|
||
}
|
||
}
|
||
function IntersectsBox(box1, box2, fuzz = 1e-6)
|
||
{
|
||
return box2.max.x < box1.min.x - fuzz || box2.min.x > box1.max.x + fuzz ||
|
||
box2.max.y < box1.min.y - fuzz || box2.min.y > box1.max.y + fuzz ||
|
||
box2.max.z < box1.min.z - fuzz || box2.min.z > box1.max.z + fuzz ? false : true;
|
||
}
|
||
/**盒子二维面是否相交 */
|
||
function IntersectBox2(box1, box2, fuzz = 1e-3)
|
||
{
|
||
return box2.max.x < box1.min.x - fuzz || box2.min.x > box1.max.x + fuzz ||
|
||
box2.max.y < box1.min.y - fuzz || box2.min.y > box1.max.y + fuzz ? false : true;
|
||
}
|
||
|
||
const ISPROXYKEY = "_isProxy";
|
||
/**
|
||
* 自动对CADObject的属性添加属性记录器,自动调用 `WriteAllObjectRecord`
|
||
* 如果属性是数组,那么自动添加`Proxy`.
|
||
* 可以使用`ISPROXYKEY`覆盖这个函数的代理行为(使用CADObject.CreateProxyArray快速覆盖)
|
||
*
|
||
* @param target
|
||
* @param property
|
||
* @param [descriptor]
|
||
*/
|
||
function AutoRecord(target, property, descriptor)
|
||
{
|
||
let privateKey = '__' + property;
|
||
Object.defineProperty(target, property, {
|
||
set: function (value)
|
||
{
|
||
if (value instanceof Array) {
|
||
if (!this[privateKey]) {
|
||
if (value[ISPROXYKEY])
|
||
this[privateKey] = value;
|
||
else
|
||
this[privateKey] = new Proxy(value, {
|
||
set: (target, key, value, receiver) =>
|
||
{
|
||
if (Reflect.get(target, key, receiver) !== value)
|
||
this.WriteAllObjectRecord();
|
||
return Reflect.set(target, key, value, receiver);
|
||
},
|
||
get: (target, key, receiver) =>
|
||
{
|
||
if (key === ISPROXYKEY)
|
||
return true;
|
||
//实体先被删除后在触发length = xxx
|
||
if (key === "splice" || key === "pop" || key === "shift")
|
||
this.WriteAllObjectRecord();
|
||
return Reflect.get(target, key, receiver);
|
||
}
|
||
});
|
||
}
|
||
else {
|
||
let arr = this[privateKey];
|
||
arr.length = 0;
|
||
arr.push(...value);
|
||
}
|
||
}
|
||
else {
|
||
let oldv = this[privateKey];
|
||
if (oldv !== value) {
|
||
this.WriteAllObjectRecord();
|
||
this[privateKey] = value;
|
||
}
|
||
}
|
||
},
|
||
get: function ()
|
||
{
|
||
return this[privateKey];
|
||
},
|
||
enumerable: true,
|
||
configurable: true
|
||
});
|
||
}
|
||
|
||
/**
|
||
* CAD对象工厂,通过注册 和暴露的创建方法,动态创建对象
|
||
*/
|
||
class CADFactory
|
||
{
|
||
constructor()
|
||
{
|
||
this.objectNameMap = new Map();
|
||
}
|
||
static RegisterObject(C)
|
||
{
|
||
this.factory.objectNameMap.set(C.name, C);
|
||
}
|
||
static RegisterObjectAlias(C, name)
|
||
{
|
||
this.factory.objectNameMap.set(name, C);
|
||
}
|
||
static CreateObject(name)
|
||
{
|
||
let C = this.factory.objectNameMap.get(name);
|
||
if (C)
|
||
return new C();
|
||
}
|
||
}
|
||
CADFactory.factory = new CADFactory();
|
||
//可以通过添加装饰器 在类前面(@Factory),自动注册工厂的序列化
|
||
function Factory(target)
|
||
{
|
||
CADFactory.RegisterObject(target);
|
||
}
|
||
|
||
var RelevancyType;
|
||
(function (RelevancyType)
|
||
{
|
||
RelevancyType[RelevancyType["General"] = 0] = "General";
|
||
RelevancyType[RelevancyType["Soft"] = 1] = "Soft";
|
||
RelevancyType[RelevancyType["Hard"] = 2] = "Hard";
|
||
})(RelevancyType || (RelevancyType = {}));
|
||
/*
|
||
CADObject对象拥有Id属性,用来记录引用关系.
|
||
通过id可以得到对应的关联实体,或者记录实体的关联关系.
|
||
|
||
ObjectId必须使用 Database分配(db里面会存id的列表,以便同时更新id指向实体)
|
||
|
||
*/
|
||
class ObjectId
|
||
{
|
||
constructor(index = 0, obj)
|
||
{
|
||
this.index = index;
|
||
this.obj = obj;
|
||
this._RelevancyType = RelevancyType.General;
|
||
}
|
||
get IsErase()
|
||
{
|
||
return !this.obj || this.obj.IsErase;
|
||
}
|
||
set Object(obj)
|
||
{
|
||
this.obj = obj;
|
||
}
|
||
get Object()
|
||
{
|
||
return this.obj;
|
||
}
|
||
get Index()
|
||
{
|
||
return this.index;
|
||
}
|
||
set Index(index)
|
||
{
|
||
this.index = index;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* CAD文件数据
|
||
*/
|
||
class CADFiler
|
||
{
|
||
constructor(_datas = [])
|
||
{
|
||
this._datas = _datas;
|
||
this.readIndex = 0;
|
||
}
|
||
Destroy()
|
||
{
|
||
delete this._datas;
|
||
delete this.readIndex;
|
||
}
|
||
get Data()
|
||
{
|
||
return this._datas;
|
||
}
|
||
set Data(data)
|
||
{
|
||
this._datas = data;
|
||
this.Reset();
|
||
}
|
||
Clear()
|
||
{
|
||
this._datas.length = 0;
|
||
return this.Reset();
|
||
}
|
||
Reset()
|
||
{
|
||
this.readIndex = 0;
|
||
return this;
|
||
}
|
||
WriteString(str)
|
||
{
|
||
this._datas.push(str);
|
||
return this;
|
||
}
|
||
ReadString()
|
||
{
|
||
return this._datas[this.readIndex++];
|
||
}
|
||
WriteObject(obj)
|
||
{
|
||
if (!obj) {
|
||
this.Write("");
|
||
return;
|
||
}
|
||
this.WriteString(obj.constructor.name);
|
||
obj.WriteFile(this);
|
||
return this;
|
||
}
|
||
ReadObject(obj)
|
||
{
|
||
let className = this.ReadString();
|
||
if (className) {
|
||
if (obj === undefined) {
|
||
obj = CADFactory.CreateObject(className);
|
||
if (this.database !== undefined && obj instanceof CADObject)
|
||
obj.SetDefaultDb(this.database);
|
||
}
|
||
obj.ReadFile(this);
|
||
return obj;
|
||
}
|
||
}
|
||
CloneObjects(objects, clonedObjects = [])
|
||
{
|
||
for (let o of objects)
|
||
this.WriteObject(o);
|
||
let count = objects.length;
|
||
for (let i = 0; i < count; i++) {
|
||
let obj = this.ReadObject();
|
||
if (obj instanceof Entity)
|
||
obj.CloneDrawObject(objects[i]);
|
||
clonedObjects.push(obj);
|
||
}
|
||
return clonedObjects;
|
||
}
|
||
Write(data)
|
||
{
|
||
if (data instanceof ObjectId)
|
||
this._datas.push(data.Index);
|
||
else
|
||
this._datas.push(data);
|
||
return this;
|
||
}
|
||
Read()
|
||
{
|
||
return this._datas[this.readIndex++];
|
||
}
|
||
ReadArray(count)
|
||
{
|
||
let arr = this._datas.slice(this.readIndex, this.readIndex + count);
|
||
this.readIndex += count;
|
||
return arr;
|
||
}
|
||
//------------------------ID序列化------------------------
|
||
/*
|
||
Id关联分为三种情况:
|
||
1.普通关联:关联对象未被拷贝时,关联到空对象.
|
||
2.软关联 :关联对象未被拷贝时,关联到原先的对象.
|
||
3.硬关联 :对象被拷贝时,被关联的对象必须也被拷贝.
|
||
*/
|
||
//-------1.普通关联
|
||
WriteObjectId(id)
|
||
{
|
||
if (id)
|
||
this.Write(id.Index);
|
||
else
|
||
this.Write(0);
|
||
return this;
|
||
}
|
||
ReadObjectId()
|
||
{
|
||
let index = this.Read();
|
||
if (this.database)
|
||
return this.database.GetObjectId(index, true);
|
||
}
|
||
//-------2.软关联
|
||
WriteSoftObjectId(id)
|
||
{
|
||
return this.WriteObjectId(id);
|
||
}
|
||
ReadSoftObjectId()
|
||
{
|
||
return this.ReadObjectId();
|
||
}
|
||
//-------3.硬关联
|
||
WriteHardObjectId(id)
|
||
{
|
||
return this.WriteObjectId(id);
|
||
}
|
||
ReadHardObjectId()
|
||
{
|
||
return this.ReadObjectId();
|
||
}
|
||
//序列化
|
||
ToString()
|
||
{
|
||
return JSON.stringify(this._datas);
|
||
}
|
||
FromString(str)
|
||
{
|
||
this._datas = JSON.parse(str);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 保存对象创建或者修改时的所有数据记录
|
||
*/
|
||
let AllObjectData = class AllObjectData
|
||
{
|
||
constructor(obj)
|
||
{
|
||
this.file = new CADFiler();
|
||
if (obj)
|
||
obj.WriteFile(this.file);
|
||
}
|
||
//#region -------------------------File-------------------------
|
||
//对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化
|
||
//对象从文件中读取数据,初始化自身
|
||
ReadFile(file)
|
||
{
|
||
let ver = file.Read();
|
||
let data = file.Read();
|
||
this.file.Data = data;
|
||
return this;
|
||
}
|
||
//对象将自身数据写入到文件.
|
||
WriteFile(file)
|
||
{
|
||
file.Write(1);
|
||
file.Write(this.file.Data);
|
||
return this;
|
||
}
|
||
};
|
||
AllObjectData = __decorate([
|
||
Factory
|
||
], AllObjectData);
|
||
|
||
let EraseEntityData = class EraseEntityData
|
||
{
|
||
constructor(isErase = true)
|
||
{
|
||
this.isErase = isErase;
|
||
}
|
||
ReadFile(file)
|
||
{
|
||
this.isErase = file.Read();
|
||
return this;
|
||
}
|
||
WriteFile(file)
|
||
{
|
||
file.Write(this.isErase);
|
||
return this;
|
||
}
|
||
};
|
||
EraseEntityData = __decorate([
|
||
Factory
|
||
], EraseEntityData);
|
||
|
||
class CADObject
|
||
{
|
||
constructor()
|
||
{
|
||
//-------------------------DB End-------------------------
|
||
// -------------------------isErase-------------------------
|
||
this._isErase = false;
|
||
}
|
||
set Owner(owner)
|
||
{
|
||
this._Owner = owner;
|
||
}
|
||
get Owner()
|
||
{
|
||
return this._Owner;
|
||
}
|
||
Destroy()
|
||
{
|
||
//在效果图同步反应器中,需要知道被删除的实体的id,所以不删除这个属性
|
||
// this.objectId = undefined;
|
||
this._db = undefined;
|
||
}
|
||
//对象被彻底遗弃
|
||
GoodBye()
|
||
{
|
||
this.Destroy();
|
||
this.Erase(true);
|
||
}
|
||
/**
|
||
* 当实体异步更新绘制实体完成后触发这个函数.
|
||
* Application通过注入的方式得知这个事件,刷新视图显示.
|
||
*/
|
||
AsyncUpdated()
|
||
{
|
||
}
|
||
get Db()
|
||
{
|
||
return this._db;
|
||
}
|
||
//对象在加入数据库时,必须指定一个源数据库,否则无法读取引用id.
|
||
SetDefaultDb(db)
|
||
{
|
||
if (!this._db)
|
||
this._db = db;
|
||
else
|
||
console.warn("重复设置默认Database!");
|
||
return this;
|
||
}
|
||
//private 私有的方法,暴露给Db的添加对象,方法使用.
|
||
//只用对象加入到db中,我们才初始化ObjectId.
|
||
//从db池中分配id给自身使用. 除非你创建对象往db里面加,否则不要调用该方法
|
||
SetOwnerDatabase(db)
|
||
{
|
||
if (!this._db) {
|
||
this._db = db;
|
||
this.objectId = db.AllocateId();
|
||
this.objectId.Object = this;
|
||
}
|
||
else
|
||
console.warn("重复设置源Database!");
|
||
return this;
|
||
}
|
||
/**
|
||
* WblockClone 的时候,id是db分配的,此刻我们只需要设置它的db
|
||
*/
|
||
SetDatabase(db)
|
||
{
|
||
this._db = db;
|
||
}
|
||
get IsErase()
|
||
{
|
||
return this._isErase;
|
||
}
|
||
Erase(isErase = true)
|
||
{
|
||
if (isErase === this._isErase)
|
||
return;
|
||
let undoData = this.UndoRecord();
|
||
if (undoData)
|
||
undoData.CreateEraseHistory(this, isErase);
|
||
this._isErase = isErase;
|
||
}
|
||
get Id()
|
||
{
|
||
return this.objectId;
|
||
}
|
||
// -------------------------id End-------------------------
|
||
// -------------------------File-------------------------
|
||
//对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化
|
||
//对象从文件中读取数据,初始化自身
|
||
ReadFile(file)
|
||
{
|
||
let ver = file.Read();
|
||
//write Id;
|
||
let id = file.ReadObjectId();
|
||
if (!this.objectId && id) //避免CopyFrom时错误的修改自身Id
|
||
{
|
||
this.objectId = id;
|
||
id.Object = this;
|
||
}
|
||
this._isErase = file.Read();
|
||
if (ver > 1)
|
||
this.Owner = file.ReadObjectId();
|
||
}
|
||
//对象将自身数据写入到文件.
|
||
WriteFile(file)
|
||
{
|
||
file.Write(2);
|
||
file.WriteObjectId(this.objectId);
|
||
file.Write(this._isErase);
|
||
file.WriteObjectId(this.Owner);
|
||
}
|
||
//局部撤销
|
||
ApplyPartialUndo(undoData)
|
||
{
|
||
if (undoData instanceof AllObjectData) {
|
||
undoData.file.database = this._db;
|
||
undoData.file.Reset();
|
||
this.ReadFile(undoData.file);
|
||
}
|
||
else if (undoData instanceof EraseEntityData) {
|
||
this.Erase(undoData.isErase);
|
||
}
|
||
}
|
||
//撤销所保存的位置
|
||
UndoRecord()
|
||
{
|
||
if (this._db && this.objectId)
|
||
return this._db.hm.UndoData;
|
||
}
|
||
//写入所有的对象数据 以便还原对象
|
||
WriteAllObjectRecord()
|
||
{
|
||
let undoData = this.UndoRecord();
|
||
if (undoData) {
|
||
undoData.WriteObjectSnapshoot(this);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
//复制出一个实体,如果存在关联,则指向原关联实体
|
||
Clone()
|
||
{
|
||
let newObject = CADFactory.CreateObject(this.constructor.name);
|
||
//备份
|
||
let bakId = this.objectId;
|
||
this.objectId = undefined;
|
||
let file = new CADFiler();
|
||
file.database = this._db;
|
||
this.WriteFile(file);
|
||
file.Reset();
|
||
newObject.ReadFile(file);
|
||
newObject.objectId = undefined;
|
||
newObject._db = undefined;
|
||
this.objectId = bakId;
|
||
return newObject;
|
||
}
|
||
DeepClone(ownerObject, cloneObejct, idMaping = undefined, isPrimary = true)
|
||
{
|
||
return this;
|
||
}
|
||
//从一个实体拷贝数据,实体类型必须相同.
|
||
CopyFrom(obj)
|
||
{
|
||
let idBak = this.objectId;
|
||
let ownerBak = this._Owner;
|
||
this.WriteAllObjectRecord();
|
||
let f = new CADFiler();
|
||
obj.WriteFile(f);
|
||
this.ReadFile(f);
|
||
this.objectId = idBak;
|
||
this._Owner = ownerBak;
|
||
}
|
||
//-------------------------File End-------------------------
|
||
//Utils
|
||
/**
|
||
* 配合 `@AutoRecord` 使用
|
||
* 使用这个方法来覆盖AutoRecord的监听行为.
|
||
* 这个行为只能用来监听实体添加和实体修改.
|
||
* 实体删除行为暂时无法监听
|
||
* @param setCallback 设置新的实体到数组时的回调函数
|
||
*/
|
||
CreateProxyArray(setCallback)
|
||
{
|
||
return new Proxy([], {
|
||
set: (target, key, value, receiver) =>
|
||
{
|
||
if (Reflect.get(target, key, receiver) !== value) {
|
||
this.WriteAllObjectRecord();
|
||
setCallback(value);
|
||
}
|
||
return Reflect.set(target, key, value, receiver);
|
||
},
|
||
get: (target, key, receiver) =>
|
||
{
|
||
if (key === ISPROXYKEY)
|
||
return true;
|
||
//实体先被删除后在触发length = xxx
|
||
if (key === "splice" || key === "pop" || key === "shift") {
|
||
this.WriteAllObjectRecord();
|
||
setCallback(undefined);
|
||
}
|
||
return Reflect.get(target, key, receiver);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
__decorate([
|
||
iaop
|
||
], CADObject.prototype, "AsyncUpdated", null);
|
||
|
||
var Entity_1;
|
||
/**
|
||
* Entity 是所有图元的基类,绘制的实体都集成该类.
|
||
*/
|
||
let Entity = Entity_1 = class Entity extends CADObject
|
||
{
|
||
constructor()
|
||
{
|
||
super(...arguments);
|
||
this.IsEmbedEntity = false;
|
||
/**
|
||
* 该实体的只有一个渲染类型,任何渲染类型都一个样
|
||
*/
|
||
this.OnlyRenderType = false;
|
||
this._CacheDrawObject = new Map();
|
||
this._Color = 7;
|
||
//自身坐标系
|
||
this._Matrix = new Matrix4();
|
||
//模块空间的标系
|
||
this._SpaceOCS = new Matrix4();
|
||
this._Visible = true;
|
||
//加工组
|
||
this.ProcessingGroupList = [];
|
||
/**
|
||
* 当AutoUpdate为false时,记录需要更新的标志.
|
||
* 以便延迟更新时找到相应的更新标志.
|
||
*/
|
||
this.NeedUpdateFlag = UpdateDraw.None;
|
||
this.AutoUpdate = true;
|
||
//实体绘制更新版本号
|
||
this.__UpdateVersion__ = 0;
|
||
//#endregion
|
||
}
|
||
get SpaceOCS()
|
||
{
|
||
return this._SpaceOCS.clone();
|
||
}
|
||
get SpaceOCSInv()
|
||
{
|
||
return new Matrix4().getInverse(this._SpaceOCS);
|
||
}
|
||
set SpaceOCS(m)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this._SpaceOCS.copy(m);
|
||
}
|
||
set Material(materialId)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this.materialId = materialId;
|
||
this.Update();
|
||
}
|
||
get Material()
|
||
{
|
||
return this.materialId;
|
||
}
|
||
set ColorIndex(color)
|
||
{
|
||
if (color !== this._Color) {
|
||
this.WriteAllObjectRecord();
|
||
this._Color = color;
|
||
this.Update(UpdateDraw.Material);
|
||
}
|
||
}
|
||
get ColorIndex()
|
||
{
|
||
return this._Color;
|
||
}
|
||
/**
|
||
* 炸开实体
|
||
*/
|
||
Explode() { return []; }
|
||
/**
|
||
* 返回对象的包围框.
|
||
*/
|
||
get BoundingBox()
|
||
{
|
||
return new Box3();
|
||
}
|
||
/**
|
||
* 返回对象在自身坐标系下的Box
|
||
*/
|
||
get BoundingBoxInOCS()
|
||
{
|
||
let mtxBak = this._Matrix;
|
||
this._Matrix = IdentityMtx4;
|
||
let box = this.BoundingBox;
|
||
this._Matrix = mtxBak;
|
||
return new Box3Ext().copy(box);
|
||
}
|
||
GetBoundingBoxInMtx(mtx)
|
||
{
|
||
return this.BoundingBoxInOCS.applyMatrix4(this.OCS.premultiply(mtx));
|
||
}
|
||
get BoundingBoxInSpaceCS()
|
||
{
|
||
return this.GetBoundingBoxInMtx(this.SpaceOCSInv);
|
||
}
|
||
get OCS()
|
||
{
|
||
return this._Matrix.clone();
|
||
}
|
||
get OCSNoClone()
|
||
{
|
||
return this._Matrix;
|
||
}
|
||
//直接设置实体的矩阵,谨慎使用该函数,没有更新实体.
|
||
set OCS(mat4)
|
||
{
|
||
this._Matrix.copy(mat4);
|
||
}
|
||
get Normal()
|
||
{
|
||
return new Vector3().setFromMatrixColumn(this._Matrix, 2);
|
||
}
|
||
get Position()
|
||
{
|
||
return new Vector3().setFromMatrixPosition(this._Matrix);
|
||
}
|
||
set Position(pt)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
let moveX = pt.x - this._Matrix.elements[12];
|
||
let moveY = pt.y - this._Matrix.elements[13];
|
||
let moveZ = pt.z - this._Matrix.elements[14];
|
||
this._Matrix.setPosition(pt);
|
||
this._SpaceOCS.elements[12] += moveX;
|
||
this._SpaceOCS.elements[13] += moveY;
|
||
this._SpaceOCS.elements[14] += moveZ;
|
||
this.Update(UpdateDraw.Matrix);
|
||
}
|
||
//Z轴归0
|
||
Z0()
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this._Matrix.elements[14] = 0;
|
||
this.Update(UpdateDraw.Matrix);
|
||
return this;
|
||
}
|
||
//坐标系二维化
|
||
MatrixPlanarizere()
|
||
{
|
||
let z = this._Matrix.elements[10];
|
||
if (equaln(Math.abs(z), 1, 1e-4)) {
|
||
this.WriteAllObjectRecord();
|
||
MatrixPlanarizere(this._Matrix, false);
|
||
}
|
||
return this;
|
||
}
|
||
get OCSInv()
|
||
{
|
||
return new Matrix4().getInverse(this._Matrix);
|
||
}
|
||
/**
|
||
* 与指定实体是否共面.
|
||
*/
|
||
IsCoplaneTo(e)
|
||
{
|
||
return matrixIsCoplane(this._Matrix, e.OCS, 1e-4);
|
||
}
|
||
/**
|
||
* 测试两个实体的包围盒是否相交.
|
||
*/
|
||
BoundingBoxIntersectWith(en)
|
||
{
|
||
let box = this.BoundingBox;
|
||
let box2 = en.BoundingBox;
|
||
return box && box2 && box.intersectsBox(box2);
|
||
}
|
||
//#region Draw
|
||
ClearDraw()
|
||
{
|
||
if (this._drawObject) {
|
||
DisposeThreeObj(this._drawObject);
|
||
this._drawObject = undefined;
|
||
}
|
||
for (let [, obj] of this._CacheDrawObject)
|
||
DisposeThreeObj(obj);
|
||
this._CacheDrawObject.clear();
|
||
return this;
|
||
}
|
||
ClearDrawOfJig()
|
||
{
|
||
let jig = this._CacheDrawObject.get(RenderType.Jig);
|
||
if (jig)
|
||
this._CacheDrawObject.delete(RenderType.Jig);
|
||
for (let [type, obj] of this._CacheDrawObject)
|
||
DisposeThreeObj(obj);
|
||
this._CacheDrawObject.clear();
|
||
if (jig)
|
||
this._CacheDrawObject.set(RenderType.Jig, jig);
|
||
}
|
||
get DrawObject()
|
||
{
|
||
var _a;
|
||
if (this._drawObject)
|
||
return this._drawObject;
|
||
this._drawObject = new Object3D();
|
||
if (!this.IsEmbedEntity)
|
||
this._drawObject.userData.Entity = this;
|
||
if (this.IsVisible) {
|
||
let obj = this.GetDrawObjectFromRenderType((_a = this.__CacheRenderType__) !== null && _a !== void 0 ? _a : userConfig.RenderType);
|
||
if (obj)
|
||
this._drawObject.add(obj);
|
||
}
|
||
else
|
||
this._drawObject.visible = false;
|
||
return this._drawObject;
|
||
}
|
||
get JigObject()
|
||
{
|
||
let obj = this.GetDrawObjectFromRenderType(RenderType.Jig);
|
||
if (obj && !this.IsEmbedEntity)
|
||
obj.userData.Entity = this;
|
||
return obj;
|
||
}
|
||
DestroyJigObject()
|
||
{
|
||
let obj = this._CacheDrawObject.get(RenderType.Jig);
|
||
if (obj) {
|
||
this._CacheDrawObject.delete(RenderType.Jig);
|
||
DisposeThreeObj(obj);
|
||
if (obj.parent)
|
||
obj.parent.remove(obj);
|
||
}
|
||
}
|
||
UpdateRenderType(type)
|
||
{
|
||
if ((this.OnlyRenderType && this.DrawObject.children.length > 0) || !this.Visible)
|
||
return;
|
||
Object3DRemoveAll(this.DrawObject);
|
||
let obj = this.GetDrawObjectFromRenderType(type);
|
||
if (obj)
|
||
this.DrawObject.add(obj);
|
||
}
|
||
GetDrawObjectFromRenderType(renderType = RenderType.Wireframe)
|
||
{
|
||
var _a;
|
||
if (this.OnlyRenderType) {
|
||
if (renderType === RenderType.Jig)
|
||
return;
|
||
renderType = (_a = this.__CacheRenderType__) !== null && _a !== void 0 ? _a : userConfig.RenderType;
|
||
}
|
||
if (this._CacheDrawObject.has(renderType)) {
|
||
return this._CacheDrawObject.get(renderType);
|
||
}
|
||
else {
|
||
let drawObj = this.InitDrawObject(renderType);
|
||
if (drawObj === undefined)
|
||
return;
|
||
//矩阵直接使用指针,因为已经关闭自动更新,所以矩阵不会被Object3D修改.
|
||
drawObj.matrixAutoUpdate = false;
|
||
drawObj.matrix = this._Matrix;
|
||
drawObj.updateMatrixWorld(true);
|
||
drawObj.traverse(UpdateBoundingSphere);
|
||
if (!this.IsEmbedEntity)
|
||
drawObj.userData.Entity = this;
|
||
this._CacheDrawObject.set(renderType, drawObj);
|
||
return drawObj;
|
||
}
|
||
}
|
||
/**
|
||
* 初始化绘制的threejs实体,子类型重载该函数初始化绘制实体.
|
||
*/
|
||
InitDrawObject(renderType = RenderType.Wireframe)
|
||
{
|
||
return undefined;
|
||
}
|
||
/**
|
||
* 当实体数据改变时,绘制的实体必须做出改变.供框架调用
|
||
*/
|
||
Update(mode = UpdateDraw.All)
|
||
{
|
||
this.__UpdateVersion__++;
|
||
this.NeedUpdateFlag |= mode;
|
||
if (this.AutoUpdate)
|
||
this.DeferUpdate();
|
||
}
|
||
//三维实体总是一起生成线框实体和网格实体,这个通知更新,然后统一更新就好了
|
||
//避免重复更新
|
||
UpdateDrawGeometry() { }
|
||
DeferUpdate()
|
||
{
|
||
let mode = this.NeedUpdateFlag;
|
||
if (mode === 0)
|
||
return;
|
||
if (mode & UpdateDraw.Geometry && this._CacheDrawObject.size > 0)
|
||
this.UpdateDrawGeometry();
|
||
this.UpdateVisible();
|
||
let isJigIng = this._CacheDrawObject.has(RenderType.Jig);
|
||
for (let [type, obj] of this._CacheDrawObject) {
|
||
if (isJigIng && type !== RenderType.Jig)
|
||
continue;
|
||
if (mode & UpdateDraw.Geometry) {
|
||
if (obj.userData.IsClone) {
|
||
let parent = obj.parent;
|
||
DisposeThreeObj(obj);
|
||
this._CacheDrawObject.delete(type);
|
||
let newObj = this.GetDrawObjectFromRenderType(type);
|
||
if (parent) {
|
||
parent.remove(obj);
|
||
parent.add(newObj);
|
||
}
|
||
obj = newObj;
|
||
}
|
||
else
|
||
this.UpdateDrawObject(type, obj);
|
||
}
|
||
if (mode & UpdateDraw.Material)
|
||
this.UpdateDrawObjectMaterial(type, obj);
|
||
if (mode & UpdateDraw.Matrix || mode & UpdateDraw.Geometry) {
|
||
obj.updateMatrixWorld(true);
|
||
obj.traverse(UpdateBoundingSphere);
|
||
}
|
||
}
|
||
this.NeedUpdateFlag = UpdateDraw.None;
|
||
}
|
||
/**
|
||
* 当实体需要更新时,需要重载该方法,实现实体更新
|
||
*/
|
||
UpdateDrawObject(type, en)
|
||
{
|
||
}
|
||
/**
|
||
* 当实体需要被更新时,更新实体材质
|
||
*/
|
||
UpdateDrawObjectMaterial(type, obj, material)
|
||
{
|
||
}
|
||
get MeshMaterial()
|
||
{
|
||
if (this.materialId && this.materialId.Object)
|
||
return this.materialId.Object.Material;
|
||
return HostApplicationServices.DefaultMeshMaterial;
|
||
}
|
||
/**
|
||
* 更新实体Jig状态时的材质
|
||
*/
|
||
UpdateJigMaterial(color = 8)
|
||
{
|
||
}
|
||
RestoreJigMaterial()
|
||
{
|
||
for (let [type, en] of this._CacheDrawObject)
|
||
this.UpdateDrawObjectMaterial(type, en);
|
||
}
|
||
get Visible()
|
||
{
|
||
return this._Visible;
|
||
}
|
||
set Visible(v)
|
||
{
|
||
if (v !== this._Visible) {
|
||
this.WriteAllObjectRecord();
|
||
this._Visible = v;
|
||
this.UpdateVisible();
|
||
}
|
||
}
|
||
get IsVisible()
|
||
{
|
||
return !this._isErase && this._Visible;
|
||
}
|
||
UpdateVisible()
|
||
{
|
||
var _a;
|
||
if (this._drawObject) {
|
||
this._drawObject.visible = this.IsVisible;
|
||
if (this.IsVisible)
|
||
this.UpdateRenderType((_a = this.__CacheRenderType__) !== null && _a !== void 0 ? _a : userConfig.RenderType);
|
||
}
|
||
}
|
||
//#endregion
|
||
GoodBye()
|
||
{
|
||
super.GoodBye();
|
||
if (this._drawObject && this._drawObject.parent)
|
||
this._drawObject.parent.remove(this._drawObject);
|
||
this.ClearDraw();
|
||
}
|
||
Erase(isErase = true)
|
||
{
|
||
if (isErase === this._isErase)
|
||
return;
|
||
this.__UpdateVersion__++;
|
||
super.Erase(isErase);
|
||
this.UpdateVisible();
|
||
this.EraseEvent(isErase);
|
||
}
|
||
EraseEvent(isErase)
|
||
{
|
||
}
|
||
/**
|
||
* 使用统一的方法设置对象的矩阵.
|
||
* 需要对缩放矩形进行重载.避免对象矩阵不是单位矩阵
|
||
*/
|
||
ApplyMatrix(m)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
if (equaln(m.getMaxScaleOnAxis(), 1)) {
|
||
let xA = new Vector3();
|
||
let yA = new Vector3();
|
||
let zA = new Vector3();
|
||
m.extractBasis(xA, yA, zA);
|
||
this._Matrix.multiplyMatrices(m, this._Matrix);
|
||
this._SpaceOCS.multiplyMatrices(m, this._SpaceOCS);
|
||
if (!equalv3(xA.clone().cross(yA).normalize(), zA))
|
||
this.ApplyMirrorMatrix(m);
|
||
else
|
||
this.Update(UpdateDraw.Matrix);
|
||
}
|
||
else {
|
||
this.ApplyScaleMatrix(m);
|
||
}
|
||
return this;
|
||
}
|
||
ApplyScaleMatrix(m)
|
||
{
|
||
return this;
|
||
}
|
||
ApplyMirrorMatrix(m)
|
||
{
|
||
return this;
|
||
}
|
||
GetGripPoints()
|
||
{
|
||
return [];
|
||
}
|
||
MoveGripPoints(indexList, vec)
|
||
{
|
||
}
|
||
/**
|
||
*
|
||
* @param snapMode 捕捉模式(单一)
|
||
* @param pickPoint const
|
||
* @param lastPoint const
|
||
* @param viewXform const 最近点捕捉需要这个变量
|
||
* @returns object snap points
|
||
*/
|
||
GetObjectSnapPoints(snapMode, pickPoint, lastPoint, viewXform)
|
||
{
|
||
return [];
|
||
}
|
||
GetStretchPoints()
|
||
{
|
||
return [];
|
||
}
|
||
/**
|
||
* 拉伸夹点,用于Stretch命令
|
||
*
|
||
* @param {Array<number>} indexList 拉伸点索引列表.
|
||
* @param {Vector3} vec 移动向量
|
||
* @memberof Entity
|
||
*/
|
||
MoveStretchPoints(indexList, vec)
|
||
{
|
||
}
|
||
IntersectWith(curve, intType) { return; }
|
||
//#region -------------------------File-------------------------
|
||
Clone()
|
||
{
|
||
let ent = super.Clone();
|
||
ent.Template = undefined;
|
||
ent.CloneDrawObject(this);
|
||
return ent;
|
||
}
|
||
CloneDrawObject(from)
|
||
{
|
||
for (let [type, obj] of from._CacheDrawObject) {
|
||
let oldUserDaata = obj.userData;
|
||
obj.traverse(o => o.userData = {});
|
||
let newObj = obj.clone();
|
||
obj.userData = oldUserDaata;
|
||
obj.userData.IsClone = true;
|
||
newObj.matrix = this._Matrix;
|
||
newObj.userData = { Entity: this };
|
||
newObj.userData.IsClone = true;
|
||
this._CacheDrawObject.set(type, newObj);
|
||
}
|
||
this.NeedUpdateFlag = UpdateDraw.None;
|
||
}
|
||
get ReadFileIng()
|
||
{
|
||
return this.__ReadFileIng__ || Entity_1.__ReadFileIng__;
|
||
}
|
||
ReadFile(file)
|
||
{
|
||
this.__ReadFileIng__ = true;
|
||
this._ReadFile(file);
|
||
this.Update();
|
||
this.__ReadFileIng__ = false;
|
||
}
|
||
//对象从文件中读取数据,初始化自身
|
||
_ReadFile(file)
|
||
{
|
||
let ver = file.Read();
|
||
super.ReadFile(file);
|
||
this._Color = file.Read();
|
||
this.materialId = file.ReadHardObjectId();
|
||
this._Matrix.fromArray(file.Read());
|
||
if (ver === 2)
|
||
this.Owner = file.ReadObjectId();
|
||
if (ver > 3)
|
||
this.Template = file.ReadObjectId();
|
||
if (ver > 4)
|
||
this.GroupId = file.ReadHardObjectId();
|
||
if (ver > 5)
|
||
this._Visible = file.Read();
|
||
if (ver > 6)
|
||
this._SpaceOCS.fromArray(file.Read());
|
||
if (ver > 7) {
|
||
let count = file.Read();
|
||
this.ProcessingGroupList.length = 0;
|
||
for (let i = 0; i < count; i++)
|
||
this.ProcessingGroupList.push(file.ReadHardObjectId());
|
||
}
|
||
}
|
||
//对象将自身数据写入到文件.
|
||
WriteFile(file)
|
||
{
|
||
file.Write(8);
|
||
super.WriteFile(file);
|
||
file.Write(this._Color);
|
||
file.WriteHardObjectId(this.materialId);
|
||
file.Write(this._Matrix.toArray());
|
||
file.WriteObjectId(this.Template);
|
||
file.WriteHardObjectId(this.GroupId);
|
||
file.Write(this._Visible);
|
||
file.Write(this._SpaceOCS.toArray());
|
||
file.Write(this.ProcessingGroupList.length);
|
||
for (let id of this.ProcessingGroupList)
|
||
file.WriteHardObjectId(id);
|
||
}
|
||
//局部撤销
|
||
ApplyPartialUndo(undoData)
|
||
{
|
||
super.ApplyPartialUndo(undoData);
|
||
}
|
||
CopyFrom(obj)
|
||
{
|
||
let templateIdBak = this.Template;
|
||
super.CopyFrom(obj);
|
||
this.Update();
|
||
this.Template = templateIdBak;
|
||
}
|
||
};
|
||
__decorate([
|
||
AutoRecord
|
||
], Entity.prototype, "GroupId", void 0);
|
||
__decorate([
|
||
AutoRecord
|
||
], Entity.prototype, "Template", void 0);
|
||
__decorate([
|
||
AutoRecord
|
||
], Entity.prototype, "ProcessingGroupList", void 0);
|
||
__decorate([
|
||
iaop
|
||
], Entity.prototype, "Update", null);
|
||
__decorate([
|
||
iaop
|
||
], Entity.prototype, "EraseEvent", null);
|
||
Entity = Entity_1 = __decorate([
|
||
Factory
|
||
], Entity);
|
||
|
||
function clamp(value, min, max)
|
||
{
|
||
return Math.max(min, Math.min(max, value));
|
||
}
|
||
function FixIndex(index, arr)
|
||
{
|
||
let count = (arr instanceof Array) ? arr.length : arr;
|
||
if (index < 0)
|
||
return count + index;
|
||
else if (index >= count)
|
||
return index - count;
|
||
else
|
||
return index;
|
||
}
|
||
|
||
class Shape2 extends Shape
|
||
{
|
||
getPoints(divisions = 12)
|
||
{
|
||
divisions = divisions || 12;
|
||
let points = [], last;
|
||
for (let i = 0, curves = this.curves; i < curves.length; i++) {
|
||
let curve = curves[i];
|
||
//@ts-ignore
|
||
let resolution = (curve && curve.isEllipseCurve) ? clamp(curve.getLength() / 20, divisions * 2, 60)
|
||
//@ts-ignore
|
||
: (curve && (curve.isLineCurve || curve.isLineCurve3)) ? 1
|
||
//@ts-ignore
|
||
: (curve && curve.isSplineCurve) ? divisions * curve.points.length
|
||
: divisions;
|
||
let pts = curve.getPoints(resolution);
|
||
for (let j = 0; j < pts.length; j++) {
|
||
let point = pts[j];
|
||
if (last && equalv2(last, point, 1e-4))
|
||
continue; // ensures no consecutive points are duplicates
|
||
points.push(point);
|
||
last = point;
|
||
if (j === pts.length - 1)
|
||
point["_mask_"] = true;
|
||
}
|
||
}
|
||
if (this.autoClose
|
||
&& points.length > 1
|
||
&& !points[points.length - 1].equals(points[0])) {
|
||
points.push(points[0]);
|
||
}
|
||
return points;
|
||
}
|
||
absellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation)
|
||
{
|
||
let curve = new EllipseCurve(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation);
|
||
/*
|
||
if (this.curves.length > 0)
|
||
{
|
||
// if a previous curve is present, attempt to join
|
||
let firstPoint = curve.getPoint(0);
|
||
if (!equalv2(firstPoint, this.currentPoint))
|
||
{
|
||
this.lineTo(firstPoint.x, firstPoint.y);
|
||
}
|
||
}
|
||
*/
|
||
this.curves.push(curve);
|
||
let lastPoint = curve.getPoint(1);
|
||
this.currentPoint.copy(lastPoint);
|
||
return this;
|
||
}
|
||
}
|
||
|
||
class Matrix2
|
||
{
|
||
constructor()
|
||
{
|
||
//column-major
|
||
this.el = [1, 0, 0, 1]; //ix iy jx jy [a c b d]
|
||
}
|
||
set(ix, iy, jx, jy)
|
||
{
|
||
this.el[0] = ix;
|
||
this.el[1] = iy;
|
||
this.el[2] = jx;
|
||
this.el[3] = jy;
|
||
}
|
||
applyVector(vec)
|
||
{
|
||
let x = vec.x;
|
||
let y = vec.y;
|
||
let e = this.el;
|
||
vec.x = e[0] * x + e[2] * y;
|
||
vec.y = e[1] * x + e[3] * y;
|
||
return this;
|
||
}
|
||
fromMatrix4(mtx4)
|
||
{
|
||
this.set(mtx4.elements[0], mtx4.elements[1], mtx4.elements[3], mtx4.elements[4]);
|
||
}
|
||
setRotate(theta)
|
||
{
|
||
let c = Math.cos(theta);
|
||
let s = Math.sin(theta);
|
||
this.set(c, s, -s, c);
|
||
return this;
|
||
}
|
||
//自我求逆矩阵,返回自身
|
||
invert()
|
||
{
|
||
//ref:https://www.mathsisfun.com/algebra/matrix-inverse.html
|
||
let [a, c, b, d] = this.el;
|
||
let det = 1 / (a * d - b * c);
|
||
this.set(d * det, -c * det, -b * det, a * det);
|
||
return this;
|
||
}
|
||
}
|
||
|
||
let r = new Matrix2();
|
||
function RotateUVs(geo)
|
||
{
|
||
r.set(0, -1, 1, 0);
|
||
for (let uvs of geo.faceVertexUvs) {
|
||
for (let uv of uvs) {
|
||
for (let v of uv)
|
||
r.applyVector(v);
|
||
}
|
||
}
|
||
geo.uvsNeedUpdate = true;
|
||
}
|
||
|
||
var CreateBoardUtil;
|
||
(function (CreateBoardUtil)
|
||
{
|
||
//解析二维圆弧
|
||
class Arc2d
|
||
{
|
||
constructor(p1, p2, bul)
|
||
{
|
||
this._StartPoint = p1.clone();
|
||
this._EndPoint = p2.clone();
|
||
let vec = p2.clone().sub(p1);
|
||
let len = vec.length();
|
||
let an = vec.angle();
|
||
this._Radius = len / Math.sin(2 * Math.atan(bul)) / 2;
|
||
let delDis = bul * len / 2;
|
||
let toDis = this._Radius - delDis;
|
||
an += Math.PI * 0.5;
|
||
this._Center = p1.clone().add(p2);
|
||
this._Center.multiplyScalar(0.5);
|
||
polar(this._Center, an, toDis);
|
||
this._StartAn = p1.clone().sub(this._Center).angle();
|
||
this._EndAn = p2.clone().sub(this._Center).angle();
|
||
if (bul < 0) {
|
||
//一个神奇的特性 它需要这么做
|
||
this._StartAn -= Math.PI;
|
||
this._EndAn -= Math.PI;
|
||
}
|
||
}
|
||
}
|
||
CreateBoardUtil.Arc2d = Arc2d;
|
||
//创建轮廓 通过点表和凸度
|
||
function CreatePath(pts, buls)
|
||
{
|
||
let shape = new Shape2();
|
||
if (pts.length === 0)
|
||
return shape;
|
||
let firstPt = pts[0];
|
||
shape.moveTo(firstPt.x, firstPt.y);
|
||
for (let i = 0; i < pts.length - 1; i++) {
|
||
let nextPt = pts[i + 1];
|
||
if (equaln(buls[i], 0, 1e-8)) {
|
||
shape.lineTo(nextPt.x, nextPt.y);
|
||
}
|
||
else {
|
||
let pt = pts[i];
|
||
//参考
|
||
//http://www.dorodnic.com/blog/tag/three-js/ 绘制一个齿轮
|
||
//https://www.kirupa.com/html5/drawing_circles_canvas.htm //html5
|
||
let arc2 = new Arc2d(pt, nextPt, buls[i]);
|
||
let cen = arc2._Center;
|
||
shape.absarc(cen.x, cen.y, arc2._Radius, arc2._StartAn, arc2._EndAn, buls[i] < 0);
|
||
}
|
||
}
|
||
return shape;
|
||
}
|
||
CreateBoardUtil.CreatePath = CreatePath;
|
||
//创建板件 暂时这么写
|
||
function createBoard(boardData)
|
||
{
|
||
var pts = new Array();
|
||
var buls = new Array();
|
||
var boardPts = boardData["Pts"];
|
||
var boardBuls = boardData["Buls"];
|
||
let boardHeight = boardData["H"];
|
||
var boardMat = new Matrix4();
|
||
var matInv = new Matrix4();
|
||
//InitBoardMat
|
||
{
|
||
var xD = AsVector3(boardData["XVec"]);
|
||
var yD = AsVector3(boardData["YVec"]);
|
||
var ZD = AsVector3(boardData["ZVec"]);
|
||
var pBase = AsVector3(boardData["BasePoint"]).multiplyScalar(0.001);
|
||
boardMat.makeBasis(xD, yD, ZD);
|
||
boardMat.setPosition(pBase);
|
||
matInv.getInverse(boardMat);
|
||
}
|
||
for (let i = 0; i < boardPts.length; i++) {
|
||
var pt = AsVector3(boardPts[i]).multiplyScalar(0.001);
|
||
pt.applyMatrix4(matInv);
|
||
pts.push(new Vector2(pt.x, pt.y));
|
||
buls.push(boardBuls[i]);
|
||
}
|
||
var sp = CreatePath(pts, buls);
|
||
var extrudeSettings = {
|
||
steps: 1,
|
||
bevelEnabled: false,
|
||
amount: boardHeight * 0.001
|
||
};
|
||
var ext = new ExtrudeGeometry(sp, extrudeSettings);
|
||
ext.translate(0, 0, -boardHeight * 0.001);
|
||
ext.applyMatrix4(boardMat);
|
||
if (boardData["BoardName"] === "地脚线") {
|
||
RotateUVs(ext);
|
||
}
|
||
var mesh = new Mesh(ext);
|
||
return mesh;
|
||
}
|
||
CreateBoardUtil.createBoard = createBoard;
|
||
})(CreateBoardUtil || (CreateBoardUtil = {}));
|
||
|
||
/**
|
||
* 删除数组中指定的元素,返回数组本身
|
||
* @param {Array<any>} arr 需要操作的数组
|
||
* @param {*} el 需要移除的元素
|
||
*/
|
||
function arrayRemoveOnce(arr, el)
|
||
{
|
||
let index = arr.indexOf(el);
|
||
if (index !== -1)
|
||
arr.splice(index, 1);
|
||
return arr;
|
||
}
|
||
/**
|
||
* 删除通过函数校验的元素
|
||
* @param {(e: T) => boolean} checkFuntion 校验函数
|
||
*/
|
||
function arrayRemoveIf(arr, checkFuntion)
|
||
{
|
||
let j = 0;
|
||
for (let i = 0, l = arr.length; i < l; i++) {
|
||
if (!checkFuntion(arr[i])) {
|
||
arr[j++] = arr[i];
|
||
}
|
||
}
|
||
arr.length = j;
|
||
return arr;
|
||
}
|
||
function arrayLast(arr)
|
||
{
|
||
return arr[arr.length - 1];
|
||
}
|
||
/**
|
||
* 根据数值从小到大排序数组
|
||
* @param {Array<T>} arr
|
||
* @returns {Array<T>} 返回自身
|
||
*/
|
||
function arraySortByNumber(arr)
|
||
{
|
||
arr.sort(sortNumberCompart);
|
||
return arr;
|
||
}
|
||
/**
|
||
* 对排序好的数组进行去重操作
|
||
* @param {(e1, e2) => boolean} [checkFuction] 校验对象相等函数
|
||
* @returns {Array<T>} 返回自身
|
||
*/
|
||
function arrayRemoveDuplicateBySort(arr, checkFuction = checkEqual)
|
||
{
|
||
if (arr.length < 2)
|
||
return arr;
|
||
let j = 1;
|
||
for (let i = 1, l = arr.length; i < l; i++)
|
||
if (!checkFuction(arr[j - 1], arr[i]))
|
||
arr[j++] = arr[i];
|
||
arr.length = j;
|
||
return arr;
|
||
}
|
||
function sortNumberCompart(e1, e2)
|
||
{
|
||
return e1 - e2;
|
||
}
|
||
function checkEqual(e1, e2)
|
||
{
|
||
return e1 === e2;
|
||
}
|
||
/**
|
||
* 改变数组的值顺序
|
||
* @param arr 需要改变初始值位置的数组
|
||
* @param index //将index位置以后的值放到起始位置
|
||
*/
|
||
function changeArrayStartIndex(arr, index)
|
||
{
|
||
arr.unshift(...arr.splice(index));
|
||
return arr;
|
||
}
|
||
function equalArray(a, b, checkF = checkEqual)
|
||
{
|
||
if (a === b)
|
||
return true;
|
||
if (a.length !== b.length)
|
||
return false;
|
||
for (var i = 0; i < a.length; ++i)
|
||
if (!checkF(a[i], b[i]))
|
||
return false;
|
||
return true;
|
||
}
|
||
|
||
function GetGoodShaderSimple(color = new Vector3)
|
||
{
|
||
return {
|
||
uniforms: {
|
||
"SurfaceColor": { value: color }
|
||
},
|
||
polygonOffset: true,
|
||
polygonOffsetFactor: 1,
|
||
polygonOffsetUnits: 1
|
||
};
|
||
}
|
||
|
||
const ColorPalette = [
|
||
[255, 0, 0, 255],
|
||
//[255, 255, 255, 255],//----- 0 - ByBlock - White
|
||
[255, 0, 0, 255],
|
||
// [255, 0, 0, 255], //----- 1 - Red
|
||
[255, 255, 0, 255],
|
||
[0, 255, 0, 255],
|
||
[0, 255, 255, 255],
|
||
[0, 0, 255, 255],
|
||
[255, 0, 255, 255],
|
||
// [255, 0, 0, 255], //----- 7 - More red Red
|
||
// [255, 0, 0, 255], //----- 8 - More red Red
|
||
// [255, 0, 0, 255], //----- 9 - More red Red
|
||
[255, 255, 255, 255],
|
||
[128, 128, 128, 255],
|
||
[192, 192, 192, 255],
|
||
[255, 0, 0, 255],
|
||
[255, 127, 127, 255],
|
||
[165, 0, 0, 255],
|
||
[165, 82, 82, 255],
|
||
[127, 0, 0, 255],
|
||
[127, 63, 63, 255],
|
||
[76, 0, 0, 255],
|
||
[76, 38, 38, 255],
|
||
[38, 0, 0, 255],
|
||
[38, 19, 19, 255],
|
||
[255, 63, 0, 255],
|
||
[255, 159, 127, 255],
|
||
[165, 41, 0, 255],
|
||
[165, 103, 82, 255],
|
||
[127, 31, 0, 255],
|
||
[127, 79, 63, 255],
|
||
[76, 19, 0, 255],
|
||
[76, 47, 38, 255],
|
||
[38, 9, 0, 255],
|
||
[38, 23, 19, 255],
|
||
[255, 127, 0, 255],
|
||
[255, 191, 127, 255],
|
||
[165, 82, 0, 255],
|
||
[165, 124, 82, 255],
|
||
[127, 63, 0, 255],
|
||
[127, 95, 63, 255],
|
||
[76, 38, 0, 255],
|
||
[76, 57, 38, 255],
|
||
[38, 19, 0, 255],
|
||
[38, 28, 19, 255],
|
||
[255, 191, 0, 255],
|
||
[255, 223, 127, 255],
|
||
[165, 124, 0, 255],
|
||
[165, 145, 82, 255],
|
||
[127, 95, 0, 255],
|
||
[127, 111, 63, 255],
|
||
[76, 57, 0, 255],
|
||
[76, 66, 38, 255],
|
||
[38, 28, 0, 255],
|
||
[38, 33, 19, 255],
|
||
[255, 255, 0, 255],
|
||
[255, 255, 127, 255],
|
||
[165, 165, 0, 255],
|
||
[165, 165, 82, 255],
|
||
[127, 127, 0, 255],
|
||
[127, 127, 63, 255],
|
||
[76, 76, 0, 255],
|
||
[76, 76, 38, 255],
|
||
[38, 38, 0, 255],
|
||
[38, 38, 19, 255],
|
||
[191, 255, 0, 255],
|
||
[223, 255, 127, 255],
|
||
[124, 165, 0, 255],
|
||
[145, 165, 82, 255],
|
||
[95, 127, 0, 255],
|
||
[111, 127, 63, 255],
|
||
[57, 76, 0, 255],
|
||
[66, 76, 38, 255],
|
||
[28, 38, 0, 255],
|
||
[33, 38, 19, 255],
|
||
[127, 255, 0, 255],
|
||
[191, 255, 127, 255],
|
||
[82, 165, 0, 255],
|
||
[124, 165, 82, 255],
|
||
[63, 127, 0, 255],
|
||
[95, 127, 63, 255],
|
||
[38, 76, 0, 255],
|
||
[57, 76, 38, 255],
|
||
[19, 38, 0, 255],
|
||
[28, 38, 19, 255],
|
||
[63, 255, 0, 255],
|
||
[159, 255, 127, 255],
|
||
[41, 165, 0, 255],
|
||
[103, 165, 82, 255],
|
||
[31, 127, 0, 255],
|
||
[79, 127, 63, 255],
|
||
[19, 76, 0, 255],
|
||
[47, 76, 38, 255],
|
||
[9, 38, 0, 255],
|
||
[23, 38, 19, 255],
|
||
[0, 255, 0, 255],
|
||
[127, 255, 127, 255],
|
||
[0, 165, 0, 255],
|
||
[82, 165, 82, 255],
|
||
[0, 127, 0, 255],
|
||
[63, 127, 63, 255],
|
||
[0, 76, 0, 255],
|
||
[38, 76, 38, 255],
|
||
[0, 38, 0, 255],
|
||
[19, 38, 19, 255],
|
||
[0, 255, 63, 255],
|
||
[127, 255, 159, 255],
|
||
[0, 165, 41, 255],
|
||
[82, 165, 103, 255],
|
||
[0, 127, 31, 255],
|
||
[63, 127, 79, 255],
|
||
[0, 76, 19, 255],
|
||
[38, 76, 47, 255],
|
||
[0, 38, 9, 255],
|
||
[19, 38, 23, 255],
|
||
[0, 255, 127, 255],
|
||
[127, 255, 191, 255],
|
||
[0, 165, 82, 255],
|
||
[82, 165, 124, 255],
|
||
[0, 127, 63, 255],
|
||
[63, 127, 95, 255],
|
||
[0, 76, 38, 255],
|
||
[38, 76, 57, 255],
|
||
[0, 38, 19, 255],
|
||
[19, 38, 28, 255],
|
||
[0, 255, 191, 255],
|
||
[127, 255, 223, 255],
|
||
[0, 165, 124, 255],
|
||
[82, 165, 145, 255],
|
||
[0, 127, 95, 255],
|
||
[63, 127, 111, 255],
|
||
[0, 76, 57, 255],
|
||
[38, 76, 66, 255],
|
||
[0, 38, 28, 255],
|
||
[19, 38, 33, 255],
|
||
[0, 255, 255, 255],
|
||
[127, 255, 255, 255],
|
||
[0, 165, 165, 255],
|
||
[82, 165, 165, 255],
|
||
[0, 127, 127, 255],
|
||
[63, 127, 127, 255],
|
||
[0, 76, 76, 255],
|
||
[38, 76, 76, 255],
|
||
[0, 38, 38, 255],
|
||
[19, 38, 38, 255],
|
||
[0, 191, 255, 255],
|
||
[127, 223, 255, 255],
|
||
[0, 124, 165, 255],
|
||
[82, 145, 165, 255],
|
||
[0, 95, 127, 255],
|
||
[63, 111, 127, 255],
|
||
[0, 57, 76, 255],
|
||
[38, 66, 76, 255],
|
||
[0, 28, 38, 255],
|
||
[19, 33, 38, 255],
|
||
[0, 127, 255, 255],
|
||
[127, 191, 255, 255],
|
||
[0, 82, 165, 255],
|
||
[82, 124, 165, 255],
|
||
[0, 63, 127, 255],
|
||
[63, 95, 127, 255],
|
||
[0, 38, 76, 255],
|
||
[38, 57, 76, 255],
|
||
[0, 19, 38, 255],
|
||
[19, 28, 38, 255],
|
||
[0, 63, 255, 255],
|
||
[127, 159, 255, 255],
|
||
[0, 41, 165, 255],
|
||
[82, 103, 165, 255],
|
||
[0, 31, 127, 255],
|
||
[63, 79, 127, 255],
|
||
[0, 19, 76, 255],
|
||
[38, 47, 76, 255],
|
||
[0, 9, 38, 255],
|
||
[19, 23, 38, 255],
|
||
[0, 0, 255, 255],
|
||
[127, 127, 255, 255],
|
||
[0, 0, 165, 255],
|
||
[82, 82, 165, 255],
|
||
[0, 0, 127, 255],
|
||
[63, 63, 127, 255],
|
||
[0, 0, 76, 255],
|
||
[38, 38, 76, 255],
|
||
[0, 0, 38, 255],
|
||
[19, 19, 38, 255],
|
||
[63, 0, 255, 255],
|
||
[159, 127, 255, 255],
|
||
[41, 0, 165, 255],
|
||
[103, 82, 165, 255],
|
||
[31, 0, 127, 255],
|
||
[79, 63, 127, 255],
|
||
[19, 0, 76, 255],
|
||
[47, 38, 76, 255],
|
||
[9, 0, 38, 255],
|
||
[23, 19, 38, 255],
|
||
[127, 0, 255, 255],
|
||
[191, 127, 255, 255],
|
||
[82, 0, 165, 255],
|
||
[124, 82, 165, 255],
|
||
[63, 0, 127, 255],
|
||
[95, 63, 127, 255],
|
||
[38, 0, 76, 255],
|
||
[57, 38, 76, 255],
|
||
[19, 0, 38, 255],
|
||
[28, 19, 38, 255],
|
||
[191, 0, 255, 255],
|
||
[223, 127, 255, 255],
|
||
[124, 0, 165, 255],
|
||
[145, 82, 165, 255],
|
||
[95, 0, 127, 255],
|
||
[111, 63, 127, 255],
|
||
[57, 0, 76, 255],
|
||
[66, 38, 76, 255],
|
||
[28, 0, 38, 255],
|
||
[33, 19, 38, 255],
|
||
[255, 0, 255, 255],
|
||
[255, 127, 255, 255],
|
||
[165, 0, 165, 255],
|
||
[165, 82, 165, 255],
|
||
[127, 0, 127, 255],
|
||
[127, 63, 127, 255],
|
||
[76, 0, 76, 255],
|
||
[76, 38, 76, 255],
|
||
[38, 0, 38, 255],
|
||
[38, 19, 38, 255],
|
||
[255, 0, 191, 255],
|
||
[255, 127, 223, 255],
|
||
[165, 0, 124, 255],
|
||
[165, 82, 145, 255],
|
||
[127, 0, 95, 255],
|
||
[127, 63, 111, 255],
|
||
[76, 0, 57, 255],
|
||
[76, 38, 66, 255],
|
||
[38, 0, 28, 255],
|
||
[38, 19, 33, 255],
|
||
[255, 0, 127, 255],
|
||
[255, 127, 191, 255],
|
||
[165, 0, 82, 255],
|
||
[165, 82, 124, 255],
|
||
[127, 0, 63, 255],
|
||
[127, 63, 95, 255],
|
||
[76, 0, 38, 255],
|
||
[76, 38, 57, 255],
|
||
[38, 0, 19, 255],
|
||
[38, 19, 28, 255],
|
||
[255, 0, 63, 255],
|
||
[255, 127, 159, 255],
|
||
[165, 0, 41, 255],
|
||
[165, 82, 103, 255],
|
||
[127, 0, 31, 255],
|
||
[127, 63, 79, 255],
|
||
[76, 0, 19, 255],
|
||
[76, 38, 47, 255],
|
||
[38, 0, 9, 255],
|
||
[38, 19, 23, 255],
|
||
[84, 84, 84, 255],
|
||
[118, 118, 118, 255],
|
||
[152, 152, 152, 255],
|
||
[186, 186, 186, 255],
|
||
[220, 220, 220, 255],
|
||
[255, 255, 255, 255],
|
||
[0, 0, 0, 0] //----- ByLayer - White
|
||
];
|
||
const LINE_WIDTH = 2;
|
||
//颜色材质,对于二维图像来说可能有用,应该不对三维对象使用该材质
|
||
class ColorMaterial
|
||
{
|
||
constructor() { }
|
||
static GetLineMaterial(color)
|
||
{
|
||
if (this._LineMaterialMap.has(color))
|
||
return this._LineMaterialMap.get(color);
|
||
let mat = new LineBasicMaterial({ color: this.GetColor(color) });
|
||
this._LineMaterialMap.set(color, mat);
|
||
return mat;
|
||
}
|
||
static GetBasicMaterial(color)
|
||
{
|
||
if (this._BasicMaterialMap.has(color))
|
||
return this._BasicMaterialMap.get(color);
|
||
let mtl = new MeshBasicMaterial({ color: this.GetColor(color) });
|
||
this._BasicMaterialMap.set(color, mtl);
|
||
return mtl;
|
||
}
|
||
static GetBasicMaterialDoubleSide(color)
|
||
{
|
||
if (this._BasicDoubleSideMaterialMap.has(color))
|
||
return this._BasicDoubleSideMaterialMap.get(color);
|
||
let mtl = new MeshBasicMaterial({ color: this.GetColor(color), side: DoubleSide });
|
||
this._BasicDoubleSideMaterialMap.set(color, mtl);
|
||
return mtl;
|
||
}
|
||
static GetConceptualMaterial(color)
|
||
{
|
||
if (this._ConceptualMaterial.has(color))
|
||
return this._ConceptualMaterial.get(color);
|
||
let shaderParams = GetGoodShaderSimple(new Vector3().fromArray(this.GetColor(color).toArray()));
|
||
let mtl = new ShaderMaterial(shaderParams);
|
||
this._ConceptualMaterial.set(color, mtl);
|
||
return mtl;
|
||
}
|
||
static GetPrintConceptualMaterial()
|
||
{
|
||
if (!this._printConceptualMaterial) {
|
||
this._printConceptualMaterial = new ShaderMaterial({
|
||
uniforms: {
|
||
"SurfaceColor": { value: [1.0, 1.0, 1.0] }
|
||
},
|
||
polygonOffset: true,
|
||
polygonOffsetFactor: 1,
|
||
polygonOffsetUnits: LINE_WIDTH
|
||
});
|
||
}
|
||
return this._printConceptualMaterial;
|
||
}
|
||
static GetBasicMaterialTransparent(color, opacity)
|
||
{
|
||
let key = `${color},${opacity}`;
|
||
let mat = this._BasicTransparentMaterialMap.get(key);
|
||
if (mat)
|
||
return mat;
|
||
mat = new MeshBasicMaterial({ transparent: true, opacity: opacity, side: DoubleSide });
|
||
this._BasicTransparentMaterialMap.set(key, mat);
|
||
return mat;
|
||
}
|
||
static GetBasicMaterialTransparent2(color, opacity)
|
||
{
|
||
let key = `${color},${opacity}`;
|
||
let mat = this._BasicTransparentMaterialMap2.get(key);
|
||
if (mat)
|
||
return mat;
|
||
mat = new MeshBasicMaterial({ transparent: true, opacity: opacity });
|
||
this._BasicTransparentMaterialMap2.set(key, mat);
|
||
return mat;
|
||
}
|
||
static GetColor(color)
|
||
{
|
||
let rgb = ColorPalette[color];
|
||
if (rgb)
|
||
return new Color(rgb[0] / 255, rgb[1] / 255, rgb[2] / 255);
|
||
}
|
||
}
|
||
ColorMaterial._LineMaterialMap = new Map();
|
||
ColorMaterial._BasicMaterialMap = new Map();
|
||
ColorMaterial._BasicDoubleSideMaterialMap = new Map();
|
||
ColorMaterial._ConceptualMaterial = new Map();
|
||
ColorMaterial._BasicTransparentMaterialMap = new Map();
|
||
ColorMaterial._BasicTransparentMaterialMap2 = new Map();
|
||
//橡皮筋材质: 黄色 点划线
|
||
ColorMaterial.RubberBandMaterial = new LineDashedMaterial({
|
||
color: 0xF0B41E,
|
||
dashSize: 20,
|
||
gapSize: 8,
|
||
});
|
||
//极轴材质: 绿色 点划线
|
||
ColorMaterial.SnapAxisMaterial = new LineDashedMaterial({
|
||
color: 0x008B00,
|
||
dashSize: 5,
|
||
gapSize: 5
|
||
});
|
||
ColorMaterial.PrintLineMatrial = new LineMaterial({
|
||
color: 0x000000,
|
||
linewidth: LINE_WIDTH / 1000,
|
||
dashed: false
|
||
});
|
||
ColorMaterial.GrayTransparentMeshMaterial = new MeshBasicMaterial({
|
||
color: 0xcccccc,
|
||
transparent: true,
|
||
opacity: 0.3,
|
||
});
|
||
ColorMaterial.TransparentMeshMaterial = new MeshBasicMaterial({
|
||
transparent: true,
|
||
opacity: 0,
|
||
});
|
||
ColorMaterial.TransparentLineMaterial = new MeshBasicMaterial({
|
||
transparent: true,
|
||
opacity: 0,
|
||
});
|
||
|
||
/**
|
||
* OSMODE
|
||
*/
|
||
var ObjectSnapMode;
|
||
(function (ObjectSnapMode)
|
||
{
|
||
ObjectSnapMode[ObjectSnapMode["None"] = 0] = "None";
|
||
ObjectSnapMode[ObjectSnapMode["End"] = 1] = "End";
|
||
ObjectSnapMode[ObjectSnapMode["Mid"] = 2] = "Mid";
|
||
ObjectSnapMode[ObjectSnapMode["Cen"] = 4] = "Cen";
|
||
ObjectSnapMode[ObjectSnapMode["Node"] = 8] = "Node";
|
||
ObjectSnapMode[ObjectSnapMode["Qua"] = 16] = "Qua";
|
||
ObjectSnapMode[ObjectSnapMode["Int"] = 32] = "Int";
|
||
ObjectSnapMode[ObjectSnapMode["Ins"] = 64] = "Ins";
|
||
ObjectSnapMode[ObjectSnapMode["Per"] = 128] = "Per";
|
||
ObjectSnapMode[ObjectSnapMode["Tan"] = 256] = "Tan";
|
||
ObjectSnapMode[ObjectSnapMode["Nea"] = 512] = "Nea";
|
||
ObjectSnapMode[ObjectSnapMode["NotEntitySnap"] = 1024] = "NotEntitySnap";
|
||
ObjectSnapMode[ObjectSnapMode["App"] = 2048] = "App";
|
||
ObjectSnapMode[ObjectSnapMode["Ext"] = 4096] = "Ext";
|
||
ObjectSnapMode[ObjectSnapMode["Par"] = 8192] = "Par";
|
||
ObjectSnapMode[ObjectSnapMode["Axis"] = 16384] = "Axis";
|
||
ObjectSnapMode[ObjectSnapMode["All"] = 31743] = "All";
|
||
})(ObjectSnapMode || (ObjectSnapMode = {}));
|
||
|
||
var BufferGeometryUtils;
|
||
(function (BufferGeometryUtils)
|
||
{
|
||
function CreateFromPts(pts)
|
||
{
|
||
return new BufferGeometry().setFromPoints(pts);
|
||
}
|
||
BufferGeometryUtils.CreateFromPts = CreateFromPts;
|
||
/**
|
||
* 更新BufferGeometry的顶点
|
||
* @param geo
|
||
* @param pts
|
||
* @returns 当成功时返回true,更新失败时返回false
|
||
*/
|
||
function UpdatePts(geo, pts)
|
||
{
|
||
let bf = geo.getAttribute("position");
|
||
if (bf === undefined)
|
||
geo.setFromPoints(pts);
|
||
else if (bf.count >= pts.length) {
|
||
bf.copyVector3sArray(pts);
|
||
bf.needsUpdate = true;
|
||
geo.drawRange.count = pts.length;
|
||
}
|
||
else
|
||
return false;
|
||
return true;
|
||
}
|
||
BufferGeometryUtils.UpdatePts = UpdatePts;
|
||
let arrowGeometry;
|
||
function ArrowGeometry()
|
||
{
|
||
if (arrowGeometry)
|
||
return arrowGeometry;
|
||
else {
|
||
let arrowShape = new Shape();
|
||
arrowShape.lineTo(-0.5, -1.8);
|
||
arrowShape.lineTo(0.5, -1.8);
|
||
arrowGeometry = new ShapeGeometry(arrowShape);
|
||
arrowGeometry.computeBoundingBox();
|
||
return arrowGeometry;
|
||
}
|
||
}
|
||
BufferGeometryUtils.ArrowGeometry = ArrowGeometry;
|
||
function MergeBufferGeometries(geometries, useGroups = false)
|
||
{
|
||
if (geometries.length === 0)
|
||
return new BufferGeometry();
|
||
let isIndexed = geometries[0].index !== null;
|
||
let attributesUsed = new Set(Object.keys(geometries[0].attributes));
|
||
let morphAttributesUsed = new Set(Object.keys(geometries[0].morphAttributes));
|
||
let attributes = {};
|
||
let morphAttributes = {};
|
||
let morphTargetsRelative = geometries[0].morphTargetsRelative;
|
||
let mergedGeometry = new BufferGeometry();
|
||
let offset = 0;
|
||
for (let i = 0; i < geometries.length; ++i) {
|
||
let geometry = geometries[i];
|
||
// ensure that all geometries are indexed, or none
|
||
if (isIndexed !== (geometry.index !== null))
|
||
return null;
|
||
// gather attributes, exit early if they're different
|
||
for (let name in geometry.attributes) {
|
||
if (!attributesUsed.has(name))
|
||
continue;
|
||
if (attributes[name] === undefined)
|
||
attributes[name] = [];
|
||
attributes[name].push(geometry.attributes[name]);
|
||
}
|
||
// gather morph attributes, exit early if they're different
|
||
if (morphTargetsRelative !== geometry.morphTargetsRelative)
|
||
return null;
|
||
for (let name in geometry.morphAttributes) {
|
||
if (!morphAttributesUsed.has(name))
|
||
continue;
|
||
if (morphAttributes[name] === undefined)
|
||
morphAttributes[name] = [];
|
||
morphAttributes[name].push(geometry.morphAttributes[name]);
|
||
}
|
||
// gather .userData
|
||
mergedGeometry.userData.mergedUserData = mergedGeometry.userData.mergedUserData || [];
|
||
mergedGeometry.userData.mergedUserData.push(geometry.userData);
|
||
if (useGroups) {
|
||
let count;
|
||
if (isIndexed) {
|
||
count = geometry.index.count;
|
||
}
|
||
else if (geometry.attributes.position !== undefined) {
|
||
count = geometry.attributes.position.count;
|
||
}
|
||
else {
|
||
return null;
|
||
}
|
||
mergedGeometry.addGroup(offset, count, i);
|
||
offset += count;
|
||
}
|
||
}
|
||
// merge indices
|
||
if (isIndexed) {
|
||
let indexOffset = 0;
|
||
let mergedIndex = [];
|
||
for (let i = 0; i < geometries.length; ++i) {
|
||
let index = geometries[i].index;
|
||
for (let j = 0; j < index.count; ++j) {
|
||
mergedIndex.push(index.getX(j) + indexOffset);
|
||
}
|
||
indexOffset += geometries[i].attributes.position.count;
|
||
}
|
||
mergedGeometry.setIndex(mergedIndex);
|
||
}
|
||
// merge attributes
|
||
for (let name in attributes) {
|
||
let mergedAttribute = MergeBufferAttributes(attributes[name]);
|
||
if (!mergedAttribute)
|
||
return null;
|
||
mergedGeometry.setAttribute(name, mergedAttribute);
|
||
}
|
||
// merge morph attributes
|
||
for (let name in morphAttributes) {
|
||
let numMorphTargets = morphAttributes[name][0].length;
|
||
if (numMorphTargets === 0)
|
||
break;
|
||
mergedGeometry.morphAttributes = mergedGeometry.morphAttributes || {};
|
||
mergedGeometry.morphAttributes[name] = [];
|
||
for (let i = 0; i < numMorphTargets; ++i) {
|
||
let morphAttributesToMerge = [];
|
||
for (let j = 0; j < morphAttributes[name].length; ++j) {
|
||
morphAttributesToMerge.push(morphAttributes[name][j][i]);
|
||
}
|
||
let mergedMorphAttribute = MergeBufferAttributes(morphAttributesToMerge);
|
||
if (!mergedMorphAttribute)
|
||
return null;
|
||
mergedGeometry.morphAttributes[name].push(mergedMorphAttribute);
|
||
}
|
||
}
|
||
return mergedGeometry;
|
||
}
|
||
BufferGeometryUtils.MergeBufferGeometries = MergeBufferGeometries;
|
||
function MergeBufferAttributes(attributes)
|
||
{
|
||
let TypedArray;
|
||
let itemSize;
|
||
let normalized;
|
||
let arrayLength = 0;
|
||
for (let i = 0; i < attributes.length; ++i) {
|
||
let attribute = attributes[i];
|
||
if (TypedArray === undefined)
|
||
TypedArray = attribute.array.constructor;
|
||
if (TypedArray !== attribute.array.constructor)
|
||
return null;
|
||
if (itemSize === undefined)
|
||
itemSize = attribute.itemSize;
|
||
if (itemSize !== attribute.itemSize)
|
||
return null;
|
||
if (normalized === undefined)
|
||
normalized = attribute.normalized;
|
||
if (normalized !== attribute.normalized)
|
||
return null;
|
||
arrayLength += attribute.array.length;
|
||
}
|
||
let array = new TypedArray(arrayLength);
|
||
let offset = 0;
|
||
for (let i = 0; i < attributes.length; ++i) {
|
||
array.set(attributes[i].array, offset);
|
||
offset += attributes[i].array.length;
|
||
}
|
||
return new BufferAttribute(array, itemSize, normalized);
|
||
}
|
||
BufferGeometryUtils.MergeBufferAttributes = MergeBufferAttributes;
|
||
})(BufferGeometryUtils || (BufferGeometryUtils = {}));
|
||
|
||
/**
|
||
* 相交延伸选项.
|
||
*
|
||
* @export
|
||
* @enum {number}
|
||
*/
|
||
var IntersectOption;
|
||
(function (IntersectOption)
|
||
{
|
||
/**
|
||
* 两者都不延伸
|
||
*/
|
||
IntersectOption[IntersectOption["OnBothOperands"] = 0] = "OnBothOperands";
|
||
/**
|
||
* 延伸自身
|
||
*/
|
||
IntersectOption[IntersectOption["ExtendThis"] = 1] = "ExtendThis";
|
||
/**
|
||
* 延伸参数
|
||
*/
|
||
IntersectOption[IntersectOption["ExtendArg"] = 2] = "ExtendArg";
|
||
/**
|
||
* 延伸两者
|
||
*/
|
||
IntersectOption[IntersectOption["ExtendBoth"] = 3] = "ExtendBoth";
|
||
})(IntersectOption || (IntersectOption = {}));
|
||
//延伸自身还是参数反转
|
||
function reverseIntersectOption(intType)
|
||
{
|
||
if (intType === IntersectOption.ExtendThis)
|
||
intType = IntersectOption.ExtendArg;
|
||
else if (intType === IntersectOption.ExtendArg)
|
||
intType = IntersectOption.ExtendThis;
|
||
return intType;
|
||
}
|
||
/**
|
||
* 校验相交点是否满足延伸选项
|
||
* 算法会计算无限延伸状态下的曲线交点,调用该方法进行校验返回校验后的点表
|
||
*
|
||
* @param {Vector3[]} intRes 相交点.曲线当作完全状态下的相交点
|
||
* @param {Curve} c1 曲线1 由this参数传入
|
||
* @param {Curve} c2 曲线2 由arg 参数传入
|
||
* @param {Intersect} extType 延伸选项.
|
||
* @returns {Array<Vector3>} 校验完成后的点表
|
||
*/
|
||
function CheckPointOnCurve(intRes, c1, c2, extType, tolerance = 1e-6)
|
||
{
|
||
return intRes.filter(r =>
|
||
{
|
||
if (!(extType & IntersectOption.ExtendThis))
|
||
if (!c1.ParamOnCurve(r.thisParam, tolerance))
|
||
return false;
|
||
if (!(extType & IntersectOption.ExtendArg))
|
||
if (!c2.ParamOnCurve(r.argParam, tolerance))
|
||
return false;
|
||
return true;
|
||
});
|
||
}
|
||
function IntersectCircleAndCircle(cu1, cu2)
|
||
{
|
||
if (!cu1.IsCoplaneTo(cu2))
|
||
return [];
|
||
let c1OcsInv = cu1.OCSInv;
|
||
let c1Ocs = cu1.OCS;
|
||
let center1 = cu1.Center.applyMatrix4(c1OcsInv);
|
||
let center2 = cu2.Center.applyMatrix4(c1OcsInv);
|
||
let radius1 = cu1.Radius;
|
||
let radius2 = cu2.Radius;
|
||
let pts = [];
|
||
let dist = center2.distanceTo(center1);
|
||
if (dist < Math.abs(radius1 - radius2) - 1e-3
|
||
|| dist > (radius1 + radius2 + 1e-3))
|
||
return pts;
|
||
if (equaln(dist, 0, 1e-6))
|
||
return pts;
|
||
let dstsqr = dist * dist;
|
||
let r1sqr = radius1 * radius1;
|
||
let r2sqr = radius2 * radius2;
|
||
let a = (dstsqr - r2sqr + r1sqr) / (2 * dist);
|
||
let h = Math.sqrt(Math.abs(r1sqr - (a * a)));
|
||
let ratio_a = a / dist;
|
||
let ratio_h = h / dist;
|
||
let dx = center2.x - center1.x;
|
||
let dy = center2.y - center1.y;
|
||
let phix = center1.x + (ratio_a * dx);
|
||
let phiy = center1.y + (ratio_a * dy);
|
||
dx *= ratio_h;
|
||
dy *= ratio_h;
|
||
let p1 = new Vector3(phix + dy, phiy - dx);
|
||
let p2 = new Vector3(phix - dy, phiy + dx);
|
||
p1.applyMatrix4(c1Ocs);
|
||
p2.applyMatrix4(c1Ocs);
|
||
pts.push({
|
||
pt: p1,
|
||
thisParam: cu1.GetParamAtPoint(p1),
|
||
argParam: cu2.GetParamAtPoint(p1),
|
||
});
|
||
if (!equalv3(p1, p2)) //防止点重复
|
||
pts.push({
|
||
pt: p2,
|
||
thisParam: cu1.GetParamAtPoint(p2),
|
||
argParam: cu2.GetParamAtPoint(p2),
|
||
});
|
||
return pts;
|
||
}
|
||
/**
|
||
* 计算圆与圆弧的交点.
|
||
*
|
||
* @export
|
||
* @param {Circle} circle 圆
|
||
* @param {Arc} arc 圆弧
|
||
* @param {IntersectOption} extType 延伸选项
|
||
* @returns 交点集合
|
||
*/
|
||
function IntersectCircleAndArc(circle, arc, extType, tolerance = 1e-6)
|
||
{
|
||
let pts = IntersectCircleAndCircle(circle, arc);
|
||
return CheckPointOnCurve(pts, circle, arc, extType | IntersectOption.ExtendThis, tolerance);
|
||
}
|
||
/**
|
||
* 计算圆弧与圆弧的交点
|
||
*
|
||
* @export
|
||
* @param {Arc} arc1 圆弧
|
||
* @param {Arc} arc2 圆弧
|
||
* @param {IntersectOption} extType 延伸选项
|
||
* @returns 交点集合
|
||
*/
|
||
function IntersectArcAndArc(arc1, arc2, extType, tolerance = 1e-6)
|
||
{
|
||
let pts = IntersectCircleAndCircle(arc1, arc2);
|
||
return CheckPointOnCurve(pts, arc1, arc2, extType, tolerance);
|
||
}
|
||
function IntersectEllipseAndLine(l, el, extType, tolerance = 1e-6)
|
||
{
|
||
let pts = IntersectLineAndEllipseFor2D(l, el);
|
||
return CheckPointOnCurve(pts, l, el, extType, tolerance);
|
||
}
|
||
/**
|
||
* 通用方法:计算直线与圆的交点,默认延伸全部
|
||
*
|
||
* @export
|
||
* @param {Line} line 直线
|
||
* @param {(Circle | Arc)} circle 圆或圆弧
|
||
* @returns 交点集合
|
||
*/
|
||
function IntersectLineAndCircleOrArc(line, circle)
|
||
{
|
||
let lineOrg = line.StartPoint;
|
||
let lineDirection = line.EndPoint.sub(lineOrg);
|
||
let dirLen = lineDirection.length();
|
||
if (equaln(dirLen, 0))
|
||
return [];
|
||
lineDirection.divideScalar(dirLen);
|
||
let diff = lineOrg.clone().sub(circle.Center);
|
||
let a0 = diff.dot(diff) - circle.Radius ** 2;
|
||
let a1 = lineDirection.dot(diff);
|
||
let discr = a1 ** 2 - a0;
|
||
if (equaln(discr, 0, 1e-7)) {
|
||
let pt = lineOrg.add(lineDirection.multiplyScalar(-a1));
|
||
return [{
|
||
pt,
|
||
thisParam: -a1 / dirLen,
|
||
argParam: circle.GetParamAtPoint(pt)
|
||
}];
|
||
}
|
||
else if (discr > 0) {
|
||
let root = Math.sqrt(discr);
|
||
let p1 = lineOrg.clone().add(lineDirection.clone().multiplyScalar(-a1 + root));
|
||
let p2 = lineOrg.add(lineDirection.multiplyScalar(-a1 - root));
|
||
return [
|
||
{
|
||
pt: p1,
|
||
thisParam: (-a1 + root) / dirLen,
|
||
argParam: circle.GetParamAtPoint(p1)
|
||
}, {
|
||
pt: p2,
|
||
thisParam: (-a1 - root) / dirLen,
|
||
argParam: circle.GetParamAtPoint(p2)
|
||
}
|
||
];
|
||
}
|
||
return [];
|
||
}
|
||
//直线和圆
|
||
function IntersectLineAndCircle(line, circle, extType, tolerance = 1e-6)
|
||
{
|
||
let ptArr = IntersectLineAndCircleOrArc(line, circle);
|
||
return CheckPointOnCurve(ptArr, line, circle, extType | IntersectOption.ExtendArg);
|
||
}
|
||
//直线和圆弧
|
||
function IntersectLineAndArc(line, arc, extType, tolerance = 1e-6)
|
||
{
|
||
let ptArr = IntersectLineAndCircleOrArc(line, arc);
|
||
return CheckPointOnCurve(ptArr, line, arc, extType, tolerance);
|
||
}
|
||
function IntersectLAndLFor2D2(p1, p2, p3, p4)
|
||
{
|
||
let dx1 = p1.x - p2.x;
|
||
let dx2 = p3.x - p4.x;
|
||
let dx3 = p4.x - p2.x;
|
||
let dy1 = p1.y - p2.y;
|
||
let dy2 = p3.y - p4.y;
|
||
let dy3 = p4.y - p2.y;
|
||
let det = (dx2 * dy1) - (dy2 * dx1);
|
||
if (equaln(det, 0.0, 1e-5)) {
|
||
if (equaln(dx2 * dy3, dy2 * dx3, 1e-5))
|
||
return [p1, p2, p3, p4];
|
||
return [];
|
||
}
|
||
let pt = new Vector3;
|
||
let ratio = ((dx1 * dy3) - (dy1 * dx3)) / det;
|
||
pt.x = (ratio * dx2) + p4.x;
|
||
pt.y = (ratio * dy2) + p4.y;
|
||
return [pt];
|
||
}
|
||
/**
|
||
* 三维中两行之间最短的直线
|
||
* ref:https://stackoverflow.com/questions/2316490/the-algorithm-to-find-the-point-of-intersection-of-two-3d-line-segment
|
||
* ref:http://paulbourke.net/geometry/pointlineplane/
|
||
* ref:http://paulbourke.net/geometry/pointlineplane/calclineline.cs
|
||
*
|
||
* @export
|
||
* @param {Vector3} p1 l1.start
|
||
* @param {Vector3} p2 l1.end
|
||
* @param {Vector3} p3 l2.start
|
||
* @param {Vector3} p4 l2.end
|
||
* @returns 交点集合
|
||
*/
|
||
function ShortestLine3AndLine3(p1, p2, p3, p4, epsilon = 1e-6)
|
||
{
|
||
let p43 = p4.clone().sub(p3);
|
||
if (p43.lengthSq() < epsilon)
|
||
return;
|
||
let p21 = p2.clone().sub(p1);
|
||
if (p21.lengthSq() < epsilon)
|
||
return;
|
||
let p13 = p1.clone().sub(p3);
|
||
let d1343 = p13.x * p43.x + p13.y * p43.y + p13.z * p43.z;
|
||
let d4321 = p43.x * p21.x + p43.y * p21.y + p43.z * p21.z;
|
||
let d1321 = p13.x * p21.x + p13.y * p21.y + p13.z * p21.z;
|
||
let d4343 = p43.x * p43.x + p43.y * p43.y + p43.z * p43.z;
|
||
let d2121 = p21.x * p21.x + p21.y * p21.y + p21.z * p21.z;
|
||
let denom = d2121 * d4343 - d4321 * d4321;
|
||
if (Math.abs(denom) < epsilon)
|
||
return;
|
||
let numer = d1343 * d4321 - d1321 * d4343;
|
||
let mua = numer / denom;
|
||
let mub = (d1343 + d4321 * (mua)) / d4343;
|
||
let resultSegmentPoint1 = new Vector3();
|
||
resultSegmentPoint1.x = p1.x + mua * p21.x;
|
||
resultSegmentPoint1.y = p1.y + mua * p21.y;
|
||
resultSegmentPoint1.z = p1.z + mua * p21.z;
|
||
let resultSegmentPoint2 = new Vector3();
|
||
resultSegmentPoint2.x = p3.x + mub * p43.x;
|
||
resultSegmentPoint2.y = p3.y + mub * p43.y;
|
||
resultSegmentPoint2.z = p3.z + mub * p43.z;
|
||
return [resultSegmentPoint1, resultSegmentPoint2];
|
||
}
|
||
//直线和直线
|
||
function IntersectLineAndLine(l1, l2, extType, fuzz = 1e-4)
|
||
{
|
||
let [pt1, pt2, pt3, pt4] = [l1.StartPoint, l1.EndPoint, l2.StartPoint, l2.EndPoint];
|
||
let ipts;
|
||
if (equaln(pt1.z, 0, fuzz) && equaln(pt2.z, 0, fuzz) && equaln(pt3.z, 0, fuzz) && equaln(pt4.z, 0, fuzz)) {
|
||
ipts = IntersectLAndLFor2D2(pt1, pt2, pt3, pt4);
|
||
ipts.sort(comparePoint("xy"));
|
||
arrayRemoveDuplicateBySort(ipts, (p1, p2) => equalv3(p1, p2, fuzz));
|
||
}
|
||
else {
|
||
ipts = ShortestLine3AndLine3(pt1, pt2, pt3, pt4);
|
||
if (!ipts)
|
||
return [];
|
||
if (ipts.length === 2)
|
||
ipts.pop();
|
||
}
|
||
let ints = [];
|
||
for (let pt of ipts) {
|
||
let { closestPt: p1, param: param1 } = l1.GetClosestAtPoint(pt, true);
|
||
if (!equalv3(pt, p1, fuzz))
|
||
return [];
|
||
if (!(extType & IntersectOption.ExtendThis))
|
||
if (!(l1.ParamOnCurve(param1, 0) || equalv3(pt1, pt, fuzz) || equalv3(pt2, pt, fuzz)))
|
||
return [];
|
||
let { closestPt: p2, param: param2 } = l2.GetClosestAtPoint(pt, true);
|
||
if (!equalv3(pt, p2, fuzz))
|
||
return [];
|
||
if (!(extType & IntersectOption.ExtendArg))
|
||
if (!(l2.ParamOnCurve(param2, 0) || equalv3(pt3, pt, fuzz) || equalv3(pt4, pt, fuzz)))
|
||
return [];
|
||
ints.push({ pt, thisParam: param1, argParam: param2 });
|
||
}
|
||
return ints;
|
||
}
|
||
function IntersectPolylineAndCurve(pl, cu, extType, tolerance = 1e-6)
|
||
{
|
||
let cus = pl.Explode();
|
||
let cus2;
|
||
if (cu instanceof Polyline)
|
||
cus2 = cu.Explode();
|
||
else
|
||
cus2 = [cu];
|
||
let intRes = [];
|
||
for (let i = 0; i < cus.length; i++) {
|
||
let cu1 = cus[i];
|
||
for (let j = 0; j < cus2.length; j++) {
|
||
let cu2 = cus2[j];
|
||
let ext = extType;
|
||
let isStart = i === 0;
|
||
let isEnd = i === cus.length - 1;
|
||
let isStart2 = j === 0;
|
||
let isEnd2 = j === cus2.length - 1;
|
||
//当曲线闭合时,或者当前的子曲线不是起始和不是结束,那么不延伸曲线.
|
||
if (pl.CloseMark || !(isStart || isEnd))
|
||
ext = ext & ~IntersectOption.ExtendThis;
|
||
if ((cu instanceof Polyline && cu.CloseMark) || !(isStart2 || isEnd2))
|
||
ext = ext & ~IntersectOption.ExtendArg;
|
||
let ptPars = cu1.IntersectWith2(cu2, ext, tolerance).filter(r1 => intRes.every(r2 => !equalv3(r1.pt, r2.pt)));
|
||
//校验延伸
|
||
if (IntersectOption.ExtendThis & ext) {
|
||
//如果曲线是起始又是结束,那么不校验.
|
||
if (isStart && isEnd);
|
||
else if (isStart) {
|
||
ptPars = ptPars.filter(res => res.thisParam <= 1);
|
||
}
|
||
else if (isEnd) {
|
||
ptPars = ptPars.filter(res => res.thisParam >= 0);
|
||
}
|
||
}
|
||
if (IntersectOption.ExtendArg & ext) {
|
||
//如果曲线是起始又是结束,那么不校验.
|
||
if (isStart2 && isEnd2);
|
||
else if (isStart2) {
|
||
ptPars = ptPars.filter(res => res.argParam + j <= cu2.EndParam);
|
||
}
|
||
else if (isEnd2) {
|
||
ptPars = ptPars.filter(res => res.argParam + j >= 0);
|
||
}
|
||
}
|
||
intRes.push(...ptPars.map(r =>
|
||
{
|
||
return {
|
||
pt: r.pt,
|
||
thisParam: i + r.thisParam,
|
||
argParam: j + r.argParam,
|
||
};
|
||
}));
|
||
}
|
||
}
|
||
return intRes;
|
||
}
|
||
function IntersectLineAndEllipseFor2D(l, el)
|
||
{
|
||
if (!l.IsCoplaneTo(el))
|
||
return [];
|
||
let mat = new Matrix4().makeRotationZ(-el.Rotation).multiply(el.OCSInv);
|
||
let a = el.RadX;
|
||
let b = el.RadY;
|
||
let sp = l.StartPoint.applyMatrix4(mat);
|
||
let ep = l.EndPoint.applyMatrix4(mat);
|
||
let pts = [];
|
||
if (equaln(sp.x, ep.x)) {
|
||
let c = sp.x;
|
||
let j = (b ** 2) * (1 - (c ** 2) / (a ** 2));
|
||
if (equaln(j, 0)) {
|
||
pts = [new Vector3(sp.x, 0)];
|
||
}
|
||
else if (j < 0)
|
||
return [];
|
||
else {
|
||
let y1 = Math.sqrt(j);
|
||
let y2 = -Math.sqrt(j);
|
||
pts = [
|
||
new Vector3(c, y1),
|
||
new Vector3(c, y2)
|
||
];
|
||
}
|
||
}
|
||
else {
|
||
let k = (sp.y - ep.y) / (sp.x - ep.x);
|
||
let c = sp.y - sp.x * k;
|
||
let j = (2 * a * a * k * c) * (2 * a * a * k * c) - 4 * (b * b + a * a * k * k) * a * a * (c * c - b * b);
|
||
if (equaln(j, 0)) {
|
||
let x1 = -2 * k * c * a * a / (2 * (b * b + a * a * k * k));
|
||
let y1 = k * x1 + c;
|
||
pts = [new Vector3(x1, y1)];
|
||
}
|
||
else if (j < 0)
|
||
return [];
|
||
else {
|
||
let x1 = (-2 * k * c * a * a + Math.sqrt(j)) / (2 * (b * b + a * a * k * k));
|
||
let y1 = k * x1 + c;
|
||
let x2 = (-2 * k * c * a * a - Math.sqrt(j)) / (2 * (b * b + a * a * k * k));
|
||
let y2 = k * x2 + c;
|
||
pts = [
|
||
new Vector3(x1, y1),
|
||
new Vector3(x2, y2)
|
||
];
|
||
}
|
||
}
|
||
let matInv = new Matrix4().getInverse(mat);
|
||
return pts.map(p =>
|
||
{
|
||
let pt = p.applyMatrix4(matInv);
|
||
return {
|
||
pt,
|
||
thisParam: l.GetParamAtPoint(pt),
|
||
argParam: el.GetParamAtPoint(pt)
|
||
};
|
||
});
|
||
}
|
||
function IntersectEllipseAndCircleOrArc(el, cir, type)
|
||
{
|
||
if (!el.IsCoplaneTo(cir))
|
||
return [];
|
||
let a = Math.max(el.RadX, el.RadY);
|
||
let dist = el.Center.distanceTo(cir.Center);
|
||
let disVail = dist > (a + cir.Radius);
|
||
if (disVail)
|
||
return [];
|
||
if (equalv3(el.Center, cir.Center)) {
|
||
let a = el.RadX;
|
||
let b = el.RadY;
|
||
let r = cir.Radius;
|
||
let j = ((a * b) ** 2 - (b * r) ** 2) / (a ** 2 - b ** 2);
|
||
let pts = [];
|
||
if (equaln(j, 0) || equaln(j, r ** 2)) {
|
||
if (equaln(j, 0))
|
||
pts = [
|
||
new Vector3(a, 0),
|
||
new Vector3(-a, 0)
|
||
];
|
||
else
|
||
pts = [
|
||
new Vector3(0, r),
|
||
new Vector3(0, -r)
|
||
];
|
||
}
|
||
else if (j < 0)
|
||
return [];
|
||
else {
|
||
let y1 = Math.sqrt(j);
|
||
let y2 = -Math.sqrt(j);
|
||
let n = r ** 2 - j;
|
||
let x1 = Math.sqrt(n);
|
||
let x2 = -Math.sqrt(n);
|
||
pts = [
|
||
new Vector3(x1, y1),
|
||
new Vector3(x1, y2),
|
||
new Vector3(x2, y1),
|
||
new Vector3(x2, y2),
|
||
];
|
||
}
|
||
let ro = new Matrix4().makeRotationZ(el.Rotation);
|
||
let res = pts.map(p =>
|
||
{
|
||
let pt = p.applyMatrix4(ro).applyMatrix4(el.OCS);
|
||
return {
|
||
pt,
|
||
thisParam: el.GetParamAtPoint(pt),
|
||
argParam: cir.GetParamAtPoint(pt)
|
||
};
|
||
});
|
||
return CheckPointOnCurve(res, el, cir, type);
|
||
}
|
||
else {
|
||
let pts = el.Shape.getPoints(60);
|
||
let lineData = pts.map(p =>
|
||
{
|
||
return { pt: p, bul: 0 };
|
||
});
|
||
let pl = new Polyline(lineData);
|
||
let cirClone = cir.Clone().ApplyMatrix(el.OCSInv);
|
||
if (type === IntersectOption.ExtendBoth)
|
||
type = IntersectOption.ExtendArg;
|
||
else if (type !== IntersectOption.ExtendArg)
|
||
type = IntersectOption.OnBothOperands;
|
||
let intPts = IntersectPolylineAndCurve(pl, cirClone, type);
|
||
intPts.forEach(r => r.pt.applyMatrix4(el.OCS));
|
||
return intPts;
|
||
}
|
||
}
|
||
function IntersectEllipse(el1, el2, type)
|
||
{
|
||
if (!el1.IsCoplaneTo(el2))
|
||
return [];
|
||
let isEqul = equalv3(el1.Center, el2.Center)
|
||
&& equaln(el1.RadX, el2.RadX)
|
||
&& equaln(el1.RadY, el2.RadY)
|
||
&& equalv3(el1.StartPoint, el2.StartPoint);
|
||
if (isEqul)
|
||
return [];
|
||
let a1 = Math.max(el1.RadX, el1.RadY);
|
||
let a2 = Math.max(el2.RadX, el2.RadY);
|
||
let dist = el1.Center.distanceToSquared(el2.Center);
|
||
if (dist > (a1 + a2) ** 2) {
|
||
return [];
|
||
}
|
||
if (!el1.BoundingBox.intersectsBox(el2.BoundingBox))
|
||
return [];
|
||
let diffMat = el1.OCSInv.multiply(el2.OCS);
|
||
let pts1 = el1.Shape.getPoints(60);
|
||
let pts2 = el2.Shape.getPoints(60);
|
||
let lineData1 = pts1.map(p =>
|
||
{
|
||
return { pt: p, bul: 0 };
|
||
});
|
||
let lineData2 = pts2.map(p =>
|
||
{
|
||
return { pt: p, bul: 0 };
|
||
});
|
||
let pl1 = new Polyline(lineData1);
|
||
let pl2 = new Polyline(lineData2).ApplyMatrix(diffMat);
|
||
let intPts = pl1.IntersectWith2(pl2, 0);
|
||
intPts.forEach(r => r.pt.applyMatrix4(el1.OCS));
|
||
return intPts;
|
||
}
|
||
|
||
var ExtendType;
|
||
(function (ExtendType)
|
||
{
|
||
/**
|
||
* 前后都不延伸
|
||
*/
|
||
ExtendType[ExtendType["None"] = 0] = "None";
|
||
/**
|
||
* 只允许延伸前面
|
||
*/
|
||
ExtendType[ExtendType["Front"] = 1] = "Front";
|
||
/**
|
||
* 只允许延伸后面
|
||
*/
|
||
ExtendType[ExtendType["Back"] = 2] = "Back";
|
||
/**
|
||
* 前后延伸
|
||
*/
|
||
ExtendType[ExtendType["Both"] = 3] = "Both";
|
||
})(ExtendType || (ExtendType = {}));
|
||
/**
|
||
* 曲线的基类,子类请实现以下方法.
|
||
*/
|
||
let Curve = class Curve extends Entity
|
||
{
|
||
constructor()
|
||
{
|
||
super();
|
||
//------------------绘制相关------------------
|
||
//重载
|
||
this.OnlyRenderType = true;
|
||
}
|
||
get Is2D()
|
||
{
|
||
return equaln(this._Matrix.elements[14], 0);
|
||
}
|
||
get StartPoint() { return; }
|
||
set StartPoint(v) { return; }
|
||
get StartParam() { return; }
|
||
get EndPoint() { return; }
|
||
set EndPoint(v) { return; }
|
||
/** 曲线中点 */
|
||
get Midpoint()
|
||
{
|
||
return this.GetPointAtParam(this.MidParam);
|
||
}
|
||
get MidParam()
|
||
{
|
||
if (this.EndParam === 1)
|
||
return 0.5;
|
||
else
|
||
return this.GetParamAtDist(this.Length * 0.5);
|
||
}
|
||
get EndParam() { return; }
|
||
get Area() { return 0; }
|
||
/**
|
||
*获得曲线的面积,逆时针为正,顺时针为负.
|
||
*/
|
||
get Area2() { return 0; }
|
||
get Length() { return 0; }
|
||
get IsClose() { return false; }
|
||
/** 曲线为顺时针 */
|
||
get IsClockWise() { return this.Area2 < 0; }
|
||
GetPointAtParam(param) { return; }
|
||
GetPointAtDistance(distance) { return; }
|
||
GetDistAtParam(param) { return; }
|
||
GetDistAtPoint(pt) { return; }
|
||
GetParamAtPoint(pt) { return; }
|
||
GetParamAtPoint2(pt) { return this.GetParamAtPoint(pt); }
|
||
GetParamAtDist(d) { return; }
|
||
/**
|
||
* 返回曲线在指定位置的一阶导数(在wcs内)
|
||
*
|
||
* @param {(number | Vector3)} param
|
||
* @returns {Vector3}
|
||
* @memberof Curve
|
||
*/
|
||
GetFistDeriv(param) { return; }
|
||
GetFistDerivAngle(param)
|
||
{
|
||
let d = this.GetFistDeriv(param);
|
||
return Math.atan2(d.y, d.x);
|
||
}
|
||
/**
|
||
* 返回切割曲线后的结果.总是从起点开始切割,并且按顺序返回曲线.
|
||
* @param {(number[] | number)} param
|
||
* @returns {Array<Curve>}
|
||
*/
|
||
GetSplitCurves(param) { return; }
|
||
//未完善
|
||
GetCurveAtParamRange(startParam, EndParam) { return; }
|
||
GetSplitCurvesByPts(pt)
|
||
{
|
||
let pts = Array.isArray(pt) ? pt : [pt];
|
||
let pars = pts.map(p => this.GetParamAtPoint(p));
|
||
return this.GetSplitCurves(pars);
|
||
}
|
||
SplitParamSort(param)
|
||
{
|
||
if (Array.isArray(param)) {
|
||
param = param.filter(p => this.ParamOnCurve(p));
|
||
if (param.length === 0)
|
||
return [];
|
||
param.push(0, this.EndParam);
|
||
arraySortByNumber(param);
|
||
arrayRemoveDuplicateBySort(param, (e1, e2) => equaln(e1, e2, 1e-7));
|
||
return param;
|
||
}
|
||
else if (this.ParamOnCurve(param))
|
||
return [0, param, this.EndParam];
|
||
else
|
||
return [];
|
||
}
|
||
Extend(newParam) { }
|
||
/**
|
||
* 连接曲线到本曲线,如果成功返回true
|
||
* @param {Curve} cu 需要连接的曲线
|
||
* @returns {boolean} 连接成功
|
||
* @memberof Curve
|
||
*/
|
||
Join(cu, allowGap = false, tolerance = 1e-4) { return Status.False; }
|
||
//翻转曲线.首尾调换.
|
||
Reverse() { return this; }
|
||
//点在曲线上
|
||
PtOnCurve(pt, fuzz = 1e-6)
|
||
{
|
||
return equalv3(this.StartPoint, pt, fuzz) || equalv3(this.EndPoint, pt, fuzz) || this.ParamOnCurve(this.GetParamAtPoint(pt));
|
||
}
|
||
//点在曲线中,不在起点或者终点.
|
||
PtOnCurve2(pt)
|
||
{
|
||
return !(equalv3(this.StartPoint, pt, 1e-6) || equalv3(this.EndPoint, pt, 1e-6)) && this.ParamOnCurve(this.GetParamAtPoint(pt), 0);
|
||
}
|
||
//点在曲线上,已经确定点在曲线的延伸线上
|
||
PtOnCurve3(p, fuzz = 1e-6)
|
||
{
|
||
return this.PtOnCurve(p, fuzz);
|
||
}
|
||
//参数在曲线上 容差,1e-6
|
||
ParamOnCurve(param, fuzz = 1e-6) { return !isNaN(param) && param >= -fuzz && param <= this.EndParam + fuzz; }
|
||
GetOffsetCurves(offsetDist) { return; }
|
||
GetClosestPointTo(pt, extend) { return; }
|
||
/**
|
||
* 曲线相交点
|
||
*/
|
||
IntersectWith(curve, intType, tolerance = 1e-6)
|
||
{
|
||
return this.IntersectWith2(curve, intType, tolerance).map(r => r.pt);
|
||
}
|
||
/**
|
||
* 曲线相交点和点的参数
|
||
*/
|
||
IntersectWith2(curve, intType, tolerance = 1e-6) { return []; }
|
||
/**
|
||
* 拽托点个数
|
||
*/
|
||
GetDragPointCount(drag) { return 0; }
|
||
/**
|
||
* 重载:更新实体材质
|
||
*/
|
||
UpdateDrawObjectMaterial(type, obj, material)
|
||
{
|
||
let m = obj;
|
||
m.material = material || ColorMaterial.GetLineMaterial(this._Color);
|
||
}
|
||
UpdateJigMaterial(color = 8)
|
||
{
|
||
for (let [type, obj] of this._CacheDrawObject) {
|
||
this.UpdateDrawObjectMaterial(type, obj, ColorMaterial.GetLineMaterial(color));
|
||
}
|
||
}
|
||
};
|
||
Curve = __decorate([
|
||
Factory
|
||
], Curve);
|
||
|
||
var DragPointType;
|
||
(function (DragPointType)
|
||
{
|
||
DragPointType[DragPointType["Grip"] = 0] = "Grip";
|
||
DragPointType[DragPointType["Stretch"] = 1] = "Stretch";
|
||
})(DragPointType || (DragPointType = {}));
|
||
|
||
class PlaneExt extends Plane
|
||
{
|
||
constructor(normal = new Vector3(0, 0, 1), constant)
|
||
{
|
||
super(normal);
|
||
if (typeof constant === "number")
|
||
this.constant = constant;
|
||
else if (constant)
|
||
this.constant = -this.normal.dot(constant);
|
||
}
|
||
intersectLine(line, optionalTarget = new Vector3(), extendLine = false)
|
||
{
|
||
let v1 = new Vector3();
|
||
let direction = line.delta(v1);
|
||
let denominator = this.normal.dot(direction);
|
||
if (denominator === 0) {
|
||
// line is coplanar, return origin
|
||
if (this.distanceToPoint(line.start) === 0) {
|
||
return optionalTarget.copy(line.start);
|
||
}
|
||
// Unsure if this is the correct method to handle this case.
|
||
return undefined;
|
||
}
|
||
let t = -(line.start.dot(this.normal) + this.constant) / denominator;
|
||
//If you not extendLine,check intersect point in Line
|
||
if (!extendLine && (t < -1e-6 || t > 1)) {
|
||
return undefined;
|
||
}
|
||
return optionalTarget.copy(direction).multiplyScalar(t).add(line.start);
|
||
}
|
||
intersectRay(ray, optionalTarget, extendLine)
|
||
{
|
||
// 从射线初始位置
|
||
let line = new Line3(ray.origin.clone(), ray.origin.clone().add(ray.direction));
|
||
return this.intersectLine(line, optionalTarget, extendLine);
|
||
}
|
||
}
|
||
|
||
var Line_1;
|
||
let Line = Line_1 = class Line extends Curve
|
||
{
|
||
constructor(sp, ep)
|
||
{
|
||
super();
|
||
this._StartPoint = sp || new Vector3(0, 0, 0);
|
||
this._EndPoint = ep || new Vector3(0, 0, 0);
|
||
}
|
||
get Is2D()
|
||
{
|
||
return super.Is2D && equaln(this._StartPoint.z, 0) && equaln(this._EndPoint.z, 0);
|
||
}
|
||
get Shape()
|
||
{
|
||
return new Shape([AsVector2(this._StartPoint), AsVector2(this._EndPoint)]);
|
||
}
|
||
Z0()
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this.StartPoint = this.StartPoint.setZ(0);
|
||
this.EndPoint = this.EndPoint.setZ(0);
|
||
return this;
|
||
}
|
||
ApplyScaleMatrix(m)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this.StartPoint = this.StartPoint.applyMatrix4(m);
|
||
this.EndPoint = this.EndPoint.applyMatrix4(m);
|
||
return this;
|
||
}
|
||
ApplyMirrorMatrix(m)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
let sp = this.StartPoint;
|
||
let ep = this.EndPoint;
|
||
reviseMirrorMatrix(this._Matrix);
|
||
this.StartPoint = sp;
|
||
this.EndPoint = ep;
|
||
return this;
|
||
}
|
||
InitDrawObject(renderType = RenderType.Wireframe)
|
||
{
|
||
let geo = BufferGeometryUtils.CreateFromPts([this._StartPoint, this._EndPoint]);
|
||
if (renderType === RenderType.Print) {
|
||
var geometry = new LineGeometry();
|
||
geometry.setPositions(geo.attributes.position.array);
|
||
return new Line2(geometry, ColorMaterial.PrintLineMatrial);
|
||
}
|
||
return new Line$1(geo, ColorMaterial.GetLineMaterial(this.ColorIndex));
|
||
}
|
||
UpdateDrawObject(type, lineObj)
|
||
{
|
||
BufferGeometryUtils.UpdatePts(lineObj.geometry, [this._StartPoint, this._EndPoint]);
|
||
}
|
||
GetObjectSnapPoints(snapMode, pickPoint, lastPoint, viewXform)
|
||
{
|
||
switch (snapMode) {
|
||
case ObjectSnapMode.End:
|
||
return [this.StartPoint, this.EndPoint];
|
||
case ObjectSnapMode.Mid:
|
||
return [this.GetPointAtParam(0.5)];
|
||
case ObjectSnapMode.Nea:
|
||
{
|
||
let derv = this.GetFistDeriv(0).normalize();
|
||
let viewNormal = new Vector3().fromArray(viewXform.elements, 2 * 3);
|
||
//平行不捕捉
|
||
if (isParallelTo(viewNormal, derv))
|
||
return [];
|
||
let fNormal = new Vector3().crossVectors(viewNormal, derv);
|
||
fNormal.crossVectors(derv, fNormal);
|
||
let plane = new PlaneExt(fNormal, this.StartPoint);
|
||
let plocal = plane.intersectLine(new Line3(pickPoint, pickPoint.clone().add(viewNormal)), new Vector3(), true);
|
||
let pclosest = this.GetClosestPointTo(plocal, false);
|
||
return [pclosest];
|
||
}
|
||
case ObjectSnapMode.Ext:
|
||
return [this.GetClosestPointTo(pickPoint, true)];
|
||
case ObjectSnapMode.Per:
|
||
if (lastPoint) {
|
||
let { closestPt, param } = this.GetClosestAtPoint(lastPoint, true);
|
||
if (this.ParamOnCurve(param))
|
||
return [closestPt];
|
||
}
|
||
}
|
||
return [];
|
||
}
|
||
GetGripPoints()
|
||
{
|
||
return [this.StartPoint, this.GetPointAtParam(0.5), this.EndPoint];
|
||
}
|
||
MoveGripPoints(indexList, vec)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
for (let index of indexList) {
|
||
if (index === 0)
|
||
this.StartPoint = this.StartPoint.add(vec);
|
||
else if (index === 2)
|
||
this.EndPoint = this.EndPoint.add(vec);
|
||
else {
|
||
let m = MoveMatrix(vec);
|
||
this.ApplyMatrix(m);
|
||
}
|
||
}
|
||
}
|
||
GetStretchPoints()
|
||
{
|
||
return [this.StartPoint, this.EndPoint];
|
||
}
|
||
MoveStretchPoints(indexList, vec)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
for (let index of indexList) {
|
||
if (index === 0)
|
||
this.StartPoint = this.StartPoint.add(vec);
|
||
else
|
||
this.EndPoint = this.EndPoint.add(vec);
|
||
}
|
||
}
|
||
GetFistDeriv(param)
|
||
{
|
||
return this.EndPoint.sub(this.StartPoint);
|
||
}
|
||
IntersectWith2(curve, intType, tolerance = 1e-4)
|
||
{
|
||
if (curve instanceof Line_1) {
|
||
return IntersectLineAndLine(this, curve, intType, tolerance);
|
||
}
|
||
if (curve instanceof Arc) {
|
||
return IntersectLineAndArc(this, curve, intType, tolerance);
|
||
}
|
||
if (curve instanceof Circle) {
|
||
return IntersectLineAndCircle(this, curve, intType, tolerance);
|
||
}
|
||
if (curve instanceof Polyline) {
|
||
return SwapParam(IntersectPolylineAndCurve(curve, this, reverseIntersectOption(intType), tolerance));
|
||
}
|
||
if (curve instanceof Ellipse)
|
||
return IntersectEllipseAndLine(this, curve, intType, tolerance);
|
||
//其他的尚未实现.
|
||
return [];
|
||
}
|
||
//Param
|
||
GetPointAtParam(param)
|
||
{
|
||
return this.StartPoint.add(this.GetFistDeriv(0).multiplyScalar(param));
|
||
}
|
||
GetParamAtPoint(pt)
|
||
{
|
||
let { closestPt, param } = this.GetClosestAtPoint(pt, true);
|
||
if (!equalv3(closestPt, pt, 1e-5))
|
||
return NaN;
|
||
return param;
|
||
}
|
||
GetParamAtDist(d)
|
||
{
|
||
return d / this.Length;
|
||
}
|
||
GetPointAtDistance(distance)
|
||
{
|
||
return this.GetPointAtParam(this.GetParamAtDist(distance));
|
||
}
|
||
GetDistAtParam(param)
|
||
{
|
||
return this.Length * param;
|
||
}
|
||
GetDistAtPoint(pt)
|
||
{
|
||
return this.GetDistAtParam(this.GetParamAtPoint(pt));
|
||
}
|
||
GetSplitCurves(param)
|
||
{
|
||
let params = this.SplitParamSort(param);
|
||
let pts = params.map(param => this.GetPointAtParam(param));
|
||
let ret = new Array();
|
||
if (pts.length >= 2) {
|
||
for (let i = 0; i < pts.length - 1; i++) {
|
||
let newLine = this.Clone();
|
||
newLine.StartPoint = pts[i];
|
||
newLine.EndPoint = pts[i + 1];
|
||
ret.push(newLine);
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
GetParamAtPoint2(pt)
|
||
{
|
||
let { param } = this.GetClosestAtPoint(pt, true);
|
||
return param;
|
||
}
|
||
//点在曲线上,已经确定点在曲线的延伸线上
|
||
PtOnCurve3(p, fuzz = 1e-6)
|
||
{
|
||
let { param } = this.GetClosestAtPoint(p, true);
|
||
return this.ParamOnCurve(param, fuzz);
|
||
}
|
||
GetClosestAtPoint(pt, extend)
|
||
{
|
||
let sp = this.StartPoint;
|
||
let ep = this.EndPoint;
|
||
if (equalv3(pt, sp, 1e-8))
|
||
return { closestPt: sp, param: 0 };
|
||
else if (equalv3(pt, ep, 1e-8))
|
||
return { closestPt: ep, param: 1 };
|
||
let direction = this.GetFistDeriv(0);
|
||
let length = direction.length();
|
||
if (length === 0) {
|
||
let param = NaN;
|
||
if (equalv3(pt, this.StartPoint, 1e-6))
|
||
param = 0;
|
||
return { closestPt: sp, param: param };
|
||
}
|
||
direction.divideScalar(length);
|
||
let diff = pt.clone().sub(sp);
|
||
let param = direction.dot(diff);
|
||
let closestPt;
|
||
if (extend)
|
||
closestPt = sp.add(direction.multiplyScalar(param));
|
||
else if (param < 0) {
|
||
closestPt = sp;
|
||
param = 0;
|
||
}
|
||
else if (param > length) {
|
||
closestPt = this.EndPoint;
|
||
param = length;
|
||
}
|
||
else
|
||
closestPt = sp.add(direction.multiplyScalar(param));
|
||
return {
|
||
closestPt: closestPt,
|
||
param: param / length
|
||
};
|
||
}
|
||
GetClosestPointTo(pt, extend)
|
||
{
|
||
return this.GetClosestAtPoint(pt, extend).closestPt;
|
||
}
|
||
Extend(newParam)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
if (newParam < this.StartParam) {
|
||
this.StartPoint = this.GetPointAtParam(newParam);
|
||
}
|
||
else if (newParam > this.EndParam) {
|
||
this.EndPoint = this.GetPointAtParam(newParam);
|
||
}
|
||
}
|
||
Join(cu, allowGap = false, tolerance = 1e-5)
|
||
{
|
||
if (cu instanceof Line_1) {
|
||
//平行
|
||
if (!isParallelTo(this.GetFistDeriv(0).normalize(), cu.GetFistDeriv(0).normalize()))
|
||
return Status.False;
|
||
let sp = cu.StartPoint;
|
||
let { closestPt: cp1, param: param1 } = this.GetClosestAtPoint(sp, true);
|
||
if (!equalv3(sp, cp1, tolerance)) //点在曲线上,允许较低的精度
|
||
return Status.False;
|
||
let ep = cu.EndPoint;
|
||
let { closestPt: cp2, param: param2 } = this.GetClosestAtPoint(ep, true);
|
||
if (!equalv3(ep, cp2, tolerance))
|
||
return Status.False;
|
||
if (param1 > param2) {
|
||
[param1, param2] = [param2, param1];
|
||
[sp, ep] = [ep, sp];
|
||
}
|
||
if (allowGap || Math.max(0, param1) < Math.min(1, param2) + tolerance) {
|
||
if (param1 < 0)
|
||
this.StartPoint = sp;
|
||
if (param2 > 1)
|
||
this.EndPoint = ep;
|
||
return Status.True;
|
||
}
|
||
}
|
||
return Status.False;
|
||
}
|
||
Reverse()
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
[this._StartPoint, this._EndPoint] = [this._EndPoint, this._StartPoint];
|
||
return this;
|
||
}
|
||
GetOffsetCurves(offsetDist)
|
||
{
|
||
let derv = this.GetFistDeriv(0).normalize().multiplyScalar(offsetDist);
|
||
derv.applyMatrix4(new Matrix4().makeRotationAxis(this.Normal, -Math.PI / 2));
|
||
let newLine = this.Clone();
|
||
newLine.StartPoint = this.StartPoint.add(derv);
|
||
newLine.EndPoint = this.EndPoint.add(derv);
|
||
return [newLine];
|
||
}
|
||
get BoundingBox()
|
||
{
|
||
return new Box3().setFromPoints([this.StartPoint, this.EndPoint]);
|
||
}
|
||
get StartParam()
|
||
{
|
||
return 0;
|
||
}
|
||
get EndParam()
|
||
{
|
||
return 1;
|
||
}
|
||
//属性
|
||
get Length() { return this._StartPoint.distanceTo(this._EndPoint); }
|
||
//#region -----------------------------File-----------------------------
|
||
//对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化
|
||
//对象从文件中读取数据,初始化自身
|
||
_ReadFile(file)
|
||
{
|
||
super._ReadFile(file);
|
||
let ver = file.Read(); //1
|
||
this._StartPoint.fromArray(file.Read());
|
||
this._EndPoint.fromArray(file.Read());
|
||
}
|
||
//对象将自身数据写入到文件.
|
||
WriteFile(file)
|
||
{
|
||
super.WriteFile(file);
|
||
file.Write(1); //ver
|
||
file.Write(this._StartPoint.toArray());
|
||
file.Write(this._EndPoint.toArray());
|
||
}
|
||
//#endregion-----------------------------File End-----------------------------
|
||
//#region 属性
|
||
set StartPoint(p)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this._StartPoint.copy(p).applyMatrix4(this.OCSInv);
|
||
this.Update();
|
||
}
|
||
get StartPoint()
|
||
{
|
||
return this._StartPoint.clone().applyMatrix4(this.OCS);
|
||
}
|
||
get EndPoint()
|
||
{
|
||
return this._EndPoint.clone().applyMatrix4(this.OCS);
|
||
}
|
||
set EndPoint(p)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this._EndPoint.copy(p).applyMatrix4(this.OCSInv);
|
||
this.Update();
|
||
}
|
||
};
|
||
Line = Line_1 = __decorate([
|
||
Factory
|
||
], Line);
|
||
|
||
var Ellipse_1;
|
||
let Ellipse = Ellipse_1 = class Ellipse extends Curve
|
||
{
|
||
constructor(center, radX = 1e-3, radY = 1e-3, angle = 0)
|
||
{
|
||
super();
|
||
this._startAngle = 0;
|
||
this._endAngle = Math.PI * 2;
|
||
center && this._Matrix.setPosition(center);
|
||
this._radX = radX;
|
||
this._radY = radY;
|
||
this._rotate = angle;
|
||
}
|
||
get StartParam()
|
||
{
|
||
return 0;
|
||
}
|
||
get EndParam()
|
||
{
|
||
return 1;
|
||
}
|
||
get StartPoint()
|
||
{
|
||
return this.GetPointAtParam(0);
|
||
}
|
||
get EndPoint()
|
||
{
|
||
return this.GetPointAtParam(1);
|
||
}
|
||
get Shape()
|
||
{
|
||
let sp = new Shape();
|
||
sp.ellipse(0, 0, this._radX, this._radY, this._startAngle, this._endAngle, false, this._rotate);
|
||
return sp;
|
||
}
|
||
get IsClose()
|
||
{
|
||
return equaln(this.TotalAngle, Math.PI * 2);
|
||
}
|
||
get Center()
|
||
{
|
||
return new Vector3().setFromMatrixPosition(this._Matrix);
|
||
}
|
||
set Center(v)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this._Matrix.setPosition(v);
|
||
this.Update();
|
||
}
|
||
get RadX()
|
||
{
|
||
return this._radX;
|
||
}
|
||
set RadX(v)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this._radX = v;
|
||
this.Update();
|
||
}
|
||
get RadY()
|
||
{
|
||
return this._radY;
|
||
}
|
||
set RadY(v)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this._radY = v;
|
||
this.Update();
|
||
}
|
||
get Rotation()
|
||
{
|
||
return this._rotate;
|
||
}
|
||
set Rotation(v)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this._rotate = v;
|
||
this.Update();
|
||
}
|
||
get StartAngle()
|
||
{
|
||
return this._startAngle;
|
||
}
|
||
get EndAngle()
|
||
{
|
||
return this._startAngle;
|
||
}
|
||
set StartAngle(v)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this._startAngle = v;
|
||
this.Update();
|
||
}
|
||
set EndAngle(v)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this._endAngle = v;
|
||
this.Update();
|
||
}
|
||
get Length()
|
||
{
|
||
let a = this._radX;
|
||
let b = this._radY;
|
||
return Math.PI * Math.abs(3 * (a + b) - Math.sqrt((3 * a + b) * (a + 3 * b))) * this.TotalAngle / Math.PI * 0.5;
|
||
}
|
||
get Area()
|
||
{
|
||
let area = Math.PI * this._radX * this._radY;
|
||
let an = this._endAngle - this._startAngle;
|
||
if (an < 0)
|
||
an = Math.PI * 2 + an;
|
||
area *= an / Math.PI * 0.5;
|
||
let area2 = Math.abs(getDeterminantFor2V(AsVector2(this.StartPoint.sub(this.Center)), AsVector2(this.EndPoint.sub(this.Center)))) / 2;
|
||
if (an < Math.PI)
|
||
area -= area2;
|
||
else
|
||
area += area2;
|
||
return area;
|
||
}
|
||
get TotalAngle()
|
||
{
|
||
let totolAngle = this._endAngle - this._startAngle;
|
||
if (totolAngle < 0)
|
||
totolAngle = Math.PI * 2 + totolAngle;
|
||
return totolAngle;
|
||
}
|
||
get BoundingBox()
|
||
{
|
||
return new Box3().setFromPoints(this.GetGripPoints());
|
||
}
|
||
PtInCurve(pt)
|
||
{
|
||
let p = rotatePoint(pt.clone().sub(this.Center), -this.Rotation);
|
||
return p.x ** 2 / this.RadX ** 2 + p.y ** 2 / this.RadY ** 2 < 1;
|
||
}
|
||
PtOnCurve(pt)
|
||
{
|
||
if (this.PtOnEllipse(pt)) {
|
||
let a = this.GetCircleAngleAtPoint(pt);
|
||
return a <= this.TotalAngle + 1e-6;
|
||
}
|
||
return false;
|
||
}
|
||
PtOnEllipse(pt)
|
||
{
|
||
let p = rotatePoint(pt.clone().applyMatrix4(this.OCSInv), -this.Rotation);
|
||
return equaln(p.x ** 2 / this.RadX ** 2 + p.y ** 2 / this.RadY ** 2, 1, 1e-3);
|
||
}
|
||
GetPointAtParam(param)
|
||
{
|
||
let an = this.TotalAngle * param + this._startAngle;
|
||
if (an > Math.PI)
|
||
an -= 2 * Math.PI;
|
||
let a = this.RadX;
|
||
let b = this.RadY;
|
||
let pt = new Vector3(a * Math.cos(an), b * Math.sin(an), 0);
|
||
pt.applyMatrix4(new Matrix4().makeRotationZ(this._rotate));
|
||
return pt.applyMatrix4(this.OCS);
|
||
}
|
||
GetParamAtPoint(pt)
|
||
{
|
||
if (!this.PtOnEllipse(pt)) {
|
||
return NaN;
|
||
}
|
||
let an = this.GetCircleAngleAtPoint(pt);
|
||
let par = an / this.TotalAngle;
|
||
if (this.IsClose || par < 1 + 1e-6)
|
||
return par;
|
||
else {
|
||
let diffPar = Math.PI * 2 / this.TotalAngle - 1;
|
||
if (par - 1 < diffPar / 2)
|
||
return par;
|
||
else
|
||
return par - 1 - diffPar;
|
||
}
|
||
}
|
||
GetPointAtDistance(distance)
|
||
{
|
||
let param = distance / this.Length;
|
||
return this.GetPointAtParam(param);
|
||
}
|
||
GetDistAtParam(param)
|
||
{
|
||
return this.Length * param;
|
||
}
|
||
GetDistAtPoint(pt)
|
||
{
|
||
let param = this.GetParamAtPoint(pt);
|
||
return this.GetDistAtParam(param);
|
||
}
|
||
GetParamAtDist(d)
|
||
{
|
||
return d / this.Length;
|
||
}
|
||
GetAngleAtParam(par)
|
||
{
|
||
let pt = this.GetPointAtParam(par).applyMatrix4(this.OCSInv).applyMatrix4(new Matrix4().makeRotationZ(-this.Rotation));
|
||
return angle(pt) + this._startAngle;
|
||
}
|
||
GetCircleAngleAtPoint(pt)
|
||
{
|
||
pt = pt.clone().applyMatrix4(this.OCSInv);
|
||
let an = angle(pt) - this._rotate;
|
||
if (an < 0)
|
||
an = Math.PI * 2 - an;
|
||
if (an > Math.PI * 2)
|
||
an -= Math.PI * 2;
|
||
let dist = pt.length();
|
||
let k = dist * Math.cos(an) / this._radX;
|
||
if (Math.abs(k) > 1)
|
||
k = Math.floor(Math.abs(k)) * Math.sign(k);
|
||
if (Math.abs(an) <= Math.PI)
|
||
an = Math.acos(k);
|
||
else
|
||
an = Math.PI * 2 - Math.acos(k);
|
||
an -= this._startAngle;
|
||
if (an < 0)
|
||
an = Math.PI * 2 + an;
|
||
return an;
|
||
}
|
||
GetFistDeriv(pt)
|
||
{
|
||
if (typeof pt === "number")
|
||
pt = this.GetPointAtParam(pt);
|
||
else
|
||
pt = pt.clone();
|
||
let refPts = this.GetGripPoints();
|
||
let p = pt.clone().applyMatrix4(this.OCSInv).applyMatrix4(new Matrix4().makeRotationZ(-this._rotate));
|
||
let vec = new Vector3();
|
||
if (equalv3(pt, refPts[0]))
|
||
vec.set(0, 1, 0);
|
||
else if (equalv3(pt, refPts[1])) {
|
||
vec.set(0, -1, 0);
|
||
}
|
||
else if (p.y > 0) {
|
||
let k = -(this._radY ** 2 * p.x) / (this._radX ** 2 * p.y);
|
||
vec.set(-1, -k, 0);
|
||
}
|
||
else {
|
||
let k = -(this._radY ** 2 * p.x) / (this._radX ** 2 * p.y);
|
||
vec.set(1, k, 0);
|
||
}
|
||
vec.applyMatrix4(new Matrix4().makeRotationZ(this._rotate));
|
||
return vec.applyMatrix4(new Matrix4().extractRotation(this.OCS));
|
||
}
|
||
GetClosestPointTo(p, extend)
|
||
{
|
||
//参考:https://wet-robots.ghost.io/simple-method-for-distance-to-ellipse/
|
||
let ro = new Matrix4().makeRotationZ(this._rotate);
|
||
let roInv = new Matrix4().getInverse(ro);
|
||
let pt = p.clone().applyMatrix4(this.OCSInv).setZ(0).applyMatrix4(roInv);
|
||
let px = pt.x;
|
||
let py = pt.y;
|
||
let t = angle(pt);
|
||
let a = this._radX;
|
||
let b = this._radY;
|
||
let x, y;
|
||
for (let i = 0; i < 3; i++) {
|
||
x = a * Math.cos(t);
|
||
y = b * Math.sin(t);
|
||
let ex = (a ** 2 - b ** 2) * Math.cos(t) ** 3 / a;
|
||
let ey = (b * b - a * a) * Math.sin(t) ** 3 / b;
|
||
let rx = x - ex;
|
||
let ry = y - ey;
|
||
let qx = px - ex;
|
||
let qy = py - ey;
|
||
let r = Math.sqrt(ry ** 2 + rx ** 2);
|
||
let q = Math.sqrt(qy ** 2 + qx ** 2);
|
||
let dc = r * Math.asin((rx * qy - ry * qx) / (r * q));
|
||
let dt = dc / Math.sqrt(a * a + b * b - x * x - y * y);
|
||
t += dt;
|
||
}
|
||
let retPt = new Vector3(x, y).applyMatrix4(ro).applyMatrix4(this.OCS);
|
||
if (this.IsClose || extend) {
|
||
return retPt;
|
||
}
|
||
else if (this.PtOnCurve(retPt)) {
|
||
return retPt;
|
||
}
|
||
else {
|
||
let d1 = p.distanceToSquared(this.StartPoint);
|
||
let d2 = p.distanceToSquared(this.EndPoint);
|
||
return d1 < d2 ? this.StartPoint : this.EndPoint;
|
||
}
|
||
}
|
||
GetOffsetCurves(offsetDist)
|
||
{
|
||
if ((offsetDist + Math.min(this._radX, this._radY)) > 0) {
|
||
let el = this.Clone();
|
||
el.RadX = this._radX + offsetDist;
|
||
el.RadY = this._radY + offsetDist;
|
||
return [el];
|
||
}
|
||
return [];
|
||
}
|
||
GetSplitCurves(param)
|
||
{
|
||
let params;
|
||
if (param instanceof Array) {
|
||
params = param.filter(p => this.ParamOnCurve(p));
|
||
params.sort((a1, a2) => a2 - a1); //从大到小
|
||
}
|
||
else
|
||
params = [param];
|
||
//补上最后一个到第一个的弧
|
||
if (this.IsClose)
|
||
params.unshift(arrayLast(params));
|
||
else {
|
||
params.unshift(1);
|
||
params.push(0);
|
||
}
|
||
arrayRemoveDuplicateBySort(params);
|
||
let anglelist = params.map(param => this.TotalAngle * param + this._startAngle);
|
||
let elllist = [];
|
||
for (let i = 0; i < anglelist.length - 1; i++) {
|
||
let sa = anglelist[i];
|
||
let ea = anglelist[i + 1];
|
||
let el = this.Clone();
|
||
if (!equaln(sa, ea, 1e-6)) {
|
||
el.StartAngle = ea;
|
||
el.EndAngle = equaln(sa, 0) ? Math.PI * 2 : sa;
|
||
elllist.push(el);
|
||
}
|
||
}
|
||
return elllist;
|
||
}
|
||
Join(el)
|
||
{
|
||
if (this.IsClose || el.IsClose || !this.IsCoplaneTo(el) || !equalv3(el.Center, this.Center))
|
||
return Status.False;
|
||
let status = Status.False;
|
||
if (equaln(this._endAngle, this._startAngle)) {
|
||
this.EndAngle = this._endAngle;
|
||
status = Status.True;
|
||
}
|
||
else if (equaln(this._startAngle, el._endAngle)) {
|
||
this.StartAngle = el._startAngle;
|
||
status = Status.True;
|
||
}
|
||
if (status === Status.True && !this.IsClose && equaln(this._startAngle, this._endAngle)) {
|
||
this.StartAngle = 0;
|
||
this.EndAngle = Math.PI * 2;
|
||
}
|
||
return status;
|
||
}
|
||
GetObjectSnapPoints(snapMode, pickPoint, lastPoint, viewXform)
|
||
{
|
||
switch (snapMode) {
|
||
case ObjectSnapMode.End:
|
||
{
|
||
let pts = this.GetGripPoints();
|
||
return pts;
|
||
}
|
||
case ObjectSnapMode.Cen:
|
||
return [this.Center];
|
||
case ObjectSnapMode.Nea:
|
||
{
|
||
return getArcOrCirNearPts(this, pickPoint, viewXform);
|
||
}
|
||
case ObjectSnapMode.Per:
|
||
if (lastPoint) {
|
||
if (equaln(lastPoint.distanceToSquared(this.Center), 0, 1e-10))
|
||
return [];
|
||
return [this.GetClosestPointTo(lastPoint, false)];
|
||
}
|
||
case ObjectSnapMode.Tan:
|
||
{
|
||
//TODO:过某点获取椭圆全部切点
|
||
if (lastPoint) {
|
||
return getTanPtsOnEllipse();
|
||
}
|
||
}
|
||
default:
|
||
return [];
|
||
}
|
||
}
|
||
IntersectWith2(curve, intType)
|
||
{
|
||
//TODO:优化椭圆和椭圆,椭圆和圆相交
|
||
if (curve instanceof Line) {
|
||
return SwapParam(IntersectEllipseAndLine(curve, this, reverseIntersectOption(intType)));
|
||
}
|
||
else if (curve instanceof Circle || curve instanceof Arc) {
|
||
return IntersectEllipseAndCircleOrArc(this, curve, intType);
|
||
}
|
||
else if (curve instanceof Polyline) {
|
||
return SwapParam(IntersectPolylineAndCurve(curve, this, intType));
|
||
}
|
||
else if (curve instanceof Ellipse_1) {
|
||
return IntersectEllipse(this, curve);
|
||
}
|
||
else
|
||
return [];
|
||
}
|
||
InitDrawObject(renderType = RenderType.Wireframe)
|
||
{
|
||
let line = new Line$1(this.UpdateGeometry(), ColorMaterial.GetLineMaterial(this._Color));
|
||
this.UpdateDrawObject(renderType, line);
|
||
return line;
|
||
}
|
||
UpdateDrawObject(type, obj)
|
||
{
|
||
let geo = obj.geometry;
|
||
this.UpdateGeometry(geo);
|
||
}
|
||
//更新Geometry
|
||
UpdateGeometry(geo)
|
||
{
|
||
if (!geo)
|
||
geo = new BufferGeometry();
|
||
let curve = this.Shape;
|
||
geo.setFromPoints(curve.getPoints(60));
|
||
return geo;
|
||
}
|
||
GetStretchPoints()
|
||
{
|
||
return this.GetGripPoints();
|
||
}
|
||
GetGripPoints()
|
||
{
|
||
let tmpMat4 = new Matrix4().makeRotationZ(this.Rotation);
|
||
let pts = [
|
||
new Vector3(this._radX, 0),
|
||
new Vector3(-this._radX, 0),
|
||
new Vector3(0, this._radY),
|
||
new Vector3(0, -this._radY)
|
||
].map(p => p.applyMatrix4(tmpMat4).applyMatrix4(this.OCS));
|
||
if (!equaln(0, this._startAngle))
|
||
pts.push(this.StartPoint);
|
||
if (!equaln(0, this._endAngle))
|
||
pts.push(this.EndPoint);
|
||
pts.push(this.Center);
|
||
return pts;
|
||
}
|
||
MoveStretchPoints(indexList, vec)
|
||
{
|
||
this.ApplyMatrix(MoveMatrix(vec));
|
||
}
|
||
MoveGripPoints(indexList, vec)
|
||
{
|
||
let pts = this.GetStretchPoints();
|
||
if (indexList.length > 0) {
|
||
let p = pts[indexList[0]].clone();
|
||
p.add(vec);
|
||
if (indexList[0] <= 1)
|
||
this.RadX = p.distanceTo(this.Center);
|
||
else if (indexList[0] <= 3)
|
||
this.RadY = p.distanceTo(this.Center);
|
||
else {
|
||
let p1 = pts[indexList[0]];
|
||
//TODO:跟cad不一致待优化
|
||
if (equalv3(p1, this.StartPoint)) {
|
||
let v1 = p1.clone().sub(this.Center);
|
||
let v2 = p.clone().sub(this.Center);
|
||
let an = angleTo(v1, v2);
|
||
this.StartAngle = this.StartAngle + an;
|
||
}
|
||
else if (equalv3(p1, this.EndPoint)) {
|
||
let v1 = p1.clone().sub(this.Center);
|
||
let v2 = p.clone().sub(this.Center);
|
||
let an = angleTo(v2, v1);
|
||
this.EndAngle = this.EndAngle + an;
|
||
}
|
||
else
|
||
this.Center = p;
|
||
}
|
||
}
|
||
}
|
||
Convert2Polyline(count = 0)
|
||
{
|
||
const MIN_LEN = 80;
|
||
const par = this.TotalAngle / Math.PI * 0.5;
|
||
if (!count) {
|
||
count = Math.floor(this.Length / par / MIN_LEN);
|
||
count = MathUtils.clamp(count, 15, 80);
|
||
}
|
||
count = Math.floor(count * par);
|
||
if ((count & 1) === 0)
|
||
count++;
|
||
let pts = this.Shape.getPoints(count);
|
||
if (this.IsClose)
|
||
pts.pop();
|
||
let pl = Pts2Polyline(pts, this.IsClose);
|
||
pl.ApplyMatrix(this.OCS);
|
||
if (this.IsClose)
|
||
pl.CloseMark = true;
|
||
return pl;
|
||
}
|
||
_ReadFile(file)
|
||
{
|
||
super._ReadFile(file);
|
||
let ver = file.Read();
|
||
this._radX = file.Read();
|
||
this._radY = file.Read();
|
||
this._rotate = file.Read();
|
||
this._startAngle = file.Read();
|
||
this._endAngle = file.Read();
|
||
this.Update();
|
||
}
|
||
//对象将自身数据写入到文件.
|
||
WriteFile(file)
|
||
{
|
||
super.WriteFile(file);
|
||
file.Write(1);
|
||
file.Write(this.RadX);
|
||
file.Write(this.RadY);
|
||
file.Write(this.Rotation);
|
||
file.Write(this._startAngle);
|
||
file.Write(this._endAngle);
|
||
}
|
||
};
|
||
Ellipse = Ellipse_1 = __decorate([
|
||
Factory
|
||
], Ellipse);
|
||
|
||
var Circle_1;
|
||
let circleGeometry;
|
||
function GetCircleGeometry()
|
||
{
|
||
if (!circleGeometry)
|
||
circleGeometry = BufferGeometryUtils.CreateFromPts(new EllipseCurve(0, 0, 1, 1, 0, 2 * Math.PI, false, 0).getPoints(360).map(AsVector3));
|
||
return circleGeometry;
|
||
}
|
||
let Circle = Circle_1 = class Circle extends Curve
|
||
{
|
||
constructor(center, radius = 1e-6)
|
||
{
|
||
super();
|
||
center && this._Matrix.setPosition(center);
|
||
this._Radius = radius;
|
||
}
|
||
get Shape()
|
||
{
|
||
let sp = new Shape2();
|
||
sp.ellipse(0, 0, this._Radius, this._Radius, 0, 2 * Math.PI, false, 0);
|
||
return sp;
|
||
}
|
||
get Center()
|
||
{
|
||
return new Vector3().setFromMatrixPosition(this._Matrix);
|
||
}
|
||
set Center(v)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this._Matrix.setPosition(v);
|
||
this.Update();
|
||
}
|
||
get Radius()
|
||
{
|
||
return this._Radius;
|
||
}
|
||
set Radius(v)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this._Radius = clamp(v, 1e-9, 1e19);
|
||
this.Update();
|
||
}
|
||
ApplyScaleMatrix(m)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this.Center = this.Center.applyMatrix4(m);
|
||
this.Radius = this.Radius * m.getMaxScaleOnAxis();
|
||
return this;
|
||
}
|
||
ApplyMirrorMatrix(m)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
reviseMirrorMatrix(this._Matrix);
|
||
return this;
|
||
}
|
||
//******************** Curve function start*****************//
|
||
get StartPoint()
|
||
{
|
||
return this.GetPointAtParam(0);
|
||
}
|
||
get StartParam()
|
||
{
|
||
return 0;
|
||
}
|
||
get EndPoint()
|
||
{
|
||
return this.GetPointAtParam(0);
|
||
}
|
||
get EndParam()
|
||
{
|
||
return 1;
|
||
}
|
||
PtInCurve(pt)
|
||
{
|
||
return pt.distanceToSquared(this.Center) < Math.pow(this.Radius, 2);
|
||
}
|
||
get Area()
|
||
{
|
||
return Math.PI * this._Radius ** 2;
|
||
}
|
||
get Area2()
|
||
{
|
||
return Math.PI * this._Radius ** 2;
|
||
}
|
||
get Length()
|
||
{
|
||
return Math.PI * 2 * this._Radius;
|
||
}
|
||
get IsClose()
|
||
{
|
||
return true;
|
||
}
|
||
//曲线为顺时针
|
||
get IsClockWise() { return false; }
|
||
GetPointAtParam(param)
|
||
{
|
||
return polar(new Vector3(), param * 2 * Math.PI, this._Radius).applyMatrix4(this._Matrix);
|
||
}
|
||
GetPointAtDistance(distance)
|
||
{
|
||
let param = distance / (Math.PI * 2 * this._Radius);
|
||
return this.GetPointAtParam(param);
|
||
}
|
||
GetDistAtParam(param)
|
||
{
|
||
return Math.PI * 2 * this._Radius * param;
|
||
}
|
||
GetDistAtPoint(pt)
|
||
{
|
||
let param = this.GetParamAtPoint(pt);
|
||
return this.GetDistAtParam(param);
|
||
}
|
||
GetParamAtDist(d)
|
||
{
|
||
return d / (Math.PI * 2 * this._Radius);
|
||
}
|
||
GetSplitCurves(param)
|
||
{
|
||
let params;
|
||
if (param instanceof Array) {
|
||
params = param.filter(p => this.ParamOnCurve(p));
|
||
params.sort((a1, a2) => a2 - a1); //从大到小
|
||
arrayRemoveDuplicateBySort(params);
|
||
if (params.length < 2)
|
||
return [];
|
||
}
|
||
else //圆不能被单个参数切割
|
||
return [];
|
||
//补上最后一个到第一个的弧
|
||
params.unshift(arrayLast(params));
|
||
let anglelist = params.map(param => Math.PI * 2 * param);
|
||
let curvelist = new Array();
|
||
for (let i = 0; i < anglelist.length - 1; i++) {
|
||
let sa = anglelist[i];
|
||
let ea = anglelist[i + 1];
|
||
if (!equaln(sa, ea, 1e-6)) {
|
||
let arc = new Arc(new Vector3(), this._Radius, ea, sa, false);
|
||
arc.ApplyMatrix(this.OCS);
|
||
curvelist.push(arc);
|
||
}
|
||
}
|
||
return curvelist;
|
||
}
|
||
GetParamAtPoint(pt)
|
||
{
|
||
if (!this.PtOnCurve(pt))
|
||
return NaN;
|
||
return angle(pt.clone().applyMatrix4(this.OCSInv)) / (Math.PI * 2);
|
||
}
|
||
PtOnCurve(pt)
|
||
{
|
||
return equaln(pt.distanceToSquared(this.Center), this._Radius * this._Radius, 1e-5);
|
||
}
|
||
GetOffsetCurves(offsetDist)
|
||
{
|
||
if ((offsetDist + this._Radius) > 0) {
|
||
let circle = this.Clone();
|
||
circle.Radius = this._Radius + offsetDist;
|
||
return [circle];
|
||
}
|
||
return [];
|
||
}
|
||
IntersectWith2(curve, intType)
|
||
{
|
||
if (curve instanceof Arc) {
|
||
return IntersectCircleAndArc(this, curve, intType);
|
||
}
|
||
if (curve instanceof Line) {
|
||
return SwapParam(IntersectLineAndCircle(curve, this, reverseIntersectOption(intType)));
|
||
}
|
||
if (curve instanceof Circle_1) {
|
||
return IntersectCircleAndCircle(this, curve);
|
||
}
|
||
if (curve instanceof Ellipse) {
|
||
return SwapParam(IntersectEllipseAndCircleOrArc(curve, this, intType));
|
||
}
|
||
if (curve instanceof Polyline)
|
||
return SwapParam(IntersectPolylineAndCurve(curve, this, reverseIntersectOption(intType)));
|
||
return [];
|
||
}
|
||
//******************** Curve function end*****************//
|
||
get BoundingBox()
|
||
{
|
||
return new Box3().setFromPoints(this.GetGripPoints());
|
||
}
|
||
InitDrawObject(renderType = RenderType.Wireframe)
|
||
{
|
||
let cirGeo = GetCircleGeometry();
|
||
if (renderType === RenderType.Print) {
|
||
var geometry = new LineGeometry().setPositions(cirGeo.attributes.position.array);
|
||
geometry.scale(this._Radius, this._Radius, this._Radius);
|
||
return new Line2(geometry, ColorMaterial.PrintLineMatrial);
|
||
}
|
||
let line = new Line$1(cirGeo, ColorMaterial.GetLineMaterial(this._Color));
|
||
let obj = new Object3D().add(line);
|
||
this.UpdateDrawObject(renderType, obj);
|
||
return obj;
|
||
}
|
||
UpdateDrawObject(type, obj)
|
||
{
|
||
obj.children[0].scale.set(this._Radius, this._Radius, this._Radius);
|
||
obj.children[0].updateMatrix();
|
||
}
|
||
UpdateDrawObjectMaterial(type, obj, material)
|
||
{
|
||
let m = obj.children[0];
|
||
m.material = material ? material : ColorMaterial.GetLineMaterial(this._Color);
|
||
}
|
||
GetDragPointCount(drag)
|
||
{
|
||
if (drag === DragPointType.Grip)
|
||
return 5;
|
||
else
|
||
return 1;
|
||
}
|
||
GetGripPoints()
|
||
{
|
||
let pts = [
|
||
new Vector3(),
|
||
new Vector3(0, this._Radius),
|
||
new Vector3(0, -this._Radius),
|
||
new Vector3(-this._Radius, 0),
|
||
new Vector3(this._Radius, 0),
|
||
];
|
||
let ocs = this.OCS;
|
||
pts.forEach(p => p.applyMatrix4(ocs));
|
||
return pts;
|
||
}
|
||
GetObjectSnapPoints(snapMode, pickPoint, lastPoint, viewXform)
|
||
{
|
||
switch (snapMode) {
|
||
case ObjectSnapMode.Nea:
|
||
{
|
||
return getArcOrCirNearPts(this, pickPoint, viewXform);
|
||
}
|
||
case ObjectSnapMode.Cen:
|
||
return [this.Center];
|
||
case ObjectSnapMode.Per:
|
||
if (lastPoint) {
|
||
if (equaln(lastPoint.distanceToSquared(this.Center), 0, 1e-10))
|
||
return [];
|
||
let l = new Line(this.Center, lastPoint);
|
||
return l.IntersectWith(this, IntersectOption.ExtendBoth);
|
||
}
|
||
case ObjectSnapMode.Tan:
|
||
let pts = GetTanPtsOnArcOrCircle(this, lastPoint);
|
||
if (pts)
|
||
return pts;
|
||
case ObjectSnapMode.End:
|
||
{
|
||
let pts = this.GetGripPoints();
|
||
pts.shift();
|
||
return pts;
|
||
}
|
||
}
|
||
return [];
|
||
}
|
||
MoveGripPoints(indexList, vec)
|
||
{
|
||
let pts = this.GetGripPoints();
|
||
if (indexList.length > 0) {
|
||
let index = indexList[0];
|
||
let p = pts[index];
|
||
if (p) {
|
||
if (index > 0) {
|
||
p.add(vec);
|
||
this.Radius = p.distanceTo(this.Center);
|
||
}
|
||
else {
|
||
this.Center = this.Center.add(vec);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
GetStretchPoints()
|
||
{
|
||
let pts = [new Vector3()];
|
||
let ocs = this.OCS;
|
||
pts.forEach(p => p.applyMatrix4(ocs));
|
||
return pts;
|
||
}
|
||
MoveStretchPoints(indexList, vec)
|
||
{
|
||
if (indexList.length > 0) {
|
||
let mat = MoveMatrix(vec);
|
||
this.ApplyMatrix(mat);
|
||
}
|
||
}
|
||
GetFistDeriv(pt)
|
||
{
|
||
if (typeof pt === "number")
|
||
pt = this.GetPointAtParam(pt);
|
||
else
|
||
pt = pt.clone();
|
||
pt.applyMatrix4(this.OCSInv);
|
||
let an = angle(pt) + Math.PI * 0.5;
|
||
return polar(new Vector3(), an, 1).applyMatrix4(new Matrix4().extractRotation(this.OCS));
|
||
}
|
||
GetClosestPointTo(pt, extend)
|
||
{
|
||
pt = pt.clone().applyMatrix4(this.OCSInv).setZ(0).applyMatrix4(this.OCS);
|
||
if (equaln(pt.distanceToSquared(this.Center), 0, 1e-10))
|
||
return this.GetPointAtParam(0);
|
||
let l = new Line(this.Center, pt);
|
||
let pts = l.IntersectWith(this, IntersectOption.ExtendBoth);
|
||
pts.sort((p1, p2) =>
|
||
{
|
||
return p1.distanceToSquared(pt) - p2.distanceToSquared(pt);
|
||
});
|
||
return pts[0];
|
||
}
|
||
//#region -------------------------File-------------------------
|
||
//对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化
|
||
//对象从文件中读取数据,初始化自身
|
||
_ReadFile(file)
|
||
{
|
||
super._ReadFile(file);
|
||
let ver = file.Read();
|
||
this._Radius = file.Read();
|
||
}
|
||
//对象将自身数据写入到文件.
|
||
WriteFile(file)
|
||
{
|
||
super.WriteFile(file);
|
||
file.Write(1);
|
||
file.Write(this._Radius);
|
||
}
|
||
};
|
||
Circle = Circle_1 = __decorate([
|
||
Factory
|
||
], Circle);
|
||
|
||
var Arc_1;
|
||
/**
|
||
* 圆弧实体类
|
||
* 与ACAD不同,这个类加入了时针变量,并且默认构造的圆弧为顺时针圆弧.
|
||
*
|
||
* 关于时针圆弧:
|
||
* 起始圆弧到终止圆弧总是在0-2PI之间.(一个完整的圆).
|
||
* 圆弧的绘制从起始圆弧绘制到终止圆弧. 按照时针绘制.
|
||
* 参考计算圆弧的完整角度方法查看该计算方式.
|
||
*/
|
||
let Arc = Arc_1 = class Arc extends Curve
|
||
{
|
||
constructor(center = new Vector3(), radius = 0.1, startAngle = 0.1, endAngle = 0, clockwise = true)
|
||
{
|
||
super();
|
||
/**
|
||
* 曲线为顺时针
|
||
*/
|
||
this.m_Clockwise = true;
|
||
this._Matrix.setPosition(center);
|
||
this.m_Radius = radius;
|
||
this.m_StartAngle = clampRad(startAngle);
|
||
this.m_EndAngle = clampRad(endAngle);
|
||
this.m_Clockwise = clockwise;
|
||
}
|
||
get Shape()
|
||
{
|
||
let sp = new Shape();
|
||
sp.absarc(0, 0, this.m_Radius, this.m_StartAngle, this.m_EndAngle, this.m_Clockwise);
|
||
return sp;
|
||
}
|
||
get Center()
|
||
{
|
||
return this.Position;
|
||
}
|
||
set Center(v)
|
||
{
|
||
this.Position = v;
|
||
}
|
||
get Normal()
|
||
{
|
||
return new Vector3().setFromMatrixColumn(this._Matrix, 2);
|
||
}
|
||
set Normal(v)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
matrixSetVector(this._Matrix, 2, v);
|
||
this.Update();
|
||
}
|
||
get Area()
|
||
{
|
||
return 0.5 * this.AllAngle * this.Radius * this.Radius;
|
||
}
|
||
//获得曲线的面积,逆时针为正,顺时针为负.
|
||
get Area2()
|
||
{
|
||
let clockwise = this.m_Clockwise ? -1 : 1;
|
||
return 0.5 * this.AllAngle * this.Radius * this.Radius * clockwise;
|
||
}
|
||
get IsClose()
|
||
{
|
||
return false;
|
||
}
|
||
get BoundingBox()
|
||
{
|
||
let pts = [this.StartPoint, this.EndPoint];
|
||
//TODO:考虑三维圆弧.
|
||
let addPts = [
|
||
this.Center.add(new Vector3(this.m_Radius, 0)),
|
||
this.Center.add(new Vector3(0, this.m_Radius)),
|
||
this.Center.add(new Vector3(-this.m_Radius, 0)),
|
||
this.Center.add(new Vector3(0, -this.m_Radius)),
|
||
];
|
||
addPts.forEach(p =>
|
||
{
|
||
if (this.PtOnCurve(p))
|
||
pts.push(p);
|
||
});
|
||
return new Box3().setFromPoints(pts);
|
||
}
|
||
get Radius()
|
||
{
|
||
return this.m_Radius;
|
||
}
|
||
set Radius(v)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this.m_Radius = v <= 0 ? 1e-19 : v;
|
||
this.Update();
|
||
}
|
||
get IsClockWise()
|
||
{
|
||
return this.m_Clockwise;
|
||
}
|
||
set IsClockWise(v)
|
||
{
|
||
if (v !== this.m_Clockwise) {
|
||
this.WriteAllObjectRecord();
|
||
this.m_Clockwise = v;
|
||
this.Update();
|
||
}
|
||
}
|
||
get StartAngle()
|
||
{
|
||
return this.m_StartAngle;
|
||
}
|
||
set StartAngle(v)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this.m_StartAngle = v;
|
||
this.Update();
|
||
}
|
||
get EndAngle()
|
||
{
|
||
return this.m_EndAngle;
|
||
}
|
||
set EndAngle(v)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this.m_EndAngle = v;
|
||
this.Update();
|
||
}
|
||
//******************** Curve function start*****************//
|
||
get StartPoint()
|
||
{
|
||
return polar(new Vector3(), this.m_StartAngle, this.m_Radius).applyMatrix4(this.OCS);
|
||
}
|
||
set StartPoint(v)
|
||
{
|
||
let vTemp = v.clone().applyMatrix4(this.OCSInv);
|
||
this.StartAngle = angle(vTemp);
|
||
}
|
||
get EndPoint()
|
||
{
|
||
return polar(new Vector3(), this.m_EndAngle, this.m_Radius).applyMatrix4(this.OCS);
|
||
}
|
||
set EndPoint(v)
|
||
{
|
||
let vTemp = v.clone().applyMatrix4(this.OCSInv);
|
||
this.EndAngle = angle(vTemp);
|
||
}
|
||
get StartParam()
|
||
{
|
||
return 0;
|
||
}
|
||
get EndParam()
|
||
{
|
||
return 1;
|
||
}
|
||
get Length()
|
||
{
|
||
return this.AllAngle * this.m_Radius;
|
||
}
|
||
GetParamAtPoint2(pt)
|
||
{
|
||
return this.GetParamAtAngle(this.GetAngleAtPoint(pt));
|
||
}
|
||
//点在曲线上,已经确定点在曲线的延伸线上
|
||
PtOnCurve3(p, fuzz = 1e-6)
|
||
{
|
||
let param = this.GetParamAtPoint2(p);
|
||
return this.ParamOnCurve(param, fuzz);
|
||
}
|
||
ApplyScaleMatrix(m)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this.Center = this.Center.applyMatrix4(m);
|
||
this.Radius = this.Radius * m.getMaxScaleOnAxis();
|
||
return this;
|
||
}
|
||
ApplyMirrorMatrix(m)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
let sp = this.StartPoint;
|
||
let ep = this.EndPoint;
|
||
reviseMirrorMatrix(this._Matrix);
|
||
this.m_Clockwise = !this.m_Clockwise;
|
||
this.StartPoint = sp;
|
||
this.EndPoint = ep;
|
||
return this;
|
||
}
|
||
GetPointAtParam(param)
|
||
{
|
||
let an = this.GetAngleAtParam(param);
|
||
return polar(new Vector3(), an, this.m_Radius).applyMatrix4(this.OCS);
|
||
}
|
||
GetPointAtDistance(distance)
|
||
{
|
||
let len = this.Length;
|
||
if (len == 0)
|
||
return;
|
||
return this.GetPointAtParam(distance / len);
|
||
}
|
||
GetDistAtParam(param)
|
||
{
|
||
return Math.abs(param * this.Length);
|
||
}
|
||
GetDistAtPoint(pt)
|
||
{
|
||
let param = this.GetParamAtPoint(pt);
|
||
return this.GetDistAtParam(param);
|
||
}
|
||
GetParamAtPoint(pt)
|
||
{
|
||
if (this.m_Radius == 0 ||
|
||
this.AllAngle == 0 ||
|
||
!equaln(pt.distanceTo(this.Center), this.m_Radius, 1e-6))
|
||
return NaN;
|
||
return this.GetParamAtAngle(this.GetAngleAtPoint(pt));
|
||
}
|
||
/**
|
||
* 利用角度计算该角度在圆弧中代表的参数.
|
||
* 如果角度在圆弧内,那么返回0-1
|
||
* 如果角度不在圆弧内,那么尝试返回离圆弧起始或者结束的较近的参数
|
||
*
|
||
* @param {number} an
|
||
* @returns
|
||
* @memberof Arc
|
||
*/
|
||
GetParamAtAngle(an)
|
||
{
|
||
//如果以pt为终点,那么所有的角度为
|
||
let ptAllAn = this.ComputeAnlge(an);
|
||
let allAn = this.AllAngle;
|
||
//减去圆弧角度,剩余角度的一半
|
||
let surplusAngleHalf = Math.PI - allAn / 2;
|
||
if (ptAllAn > allAn + surplusAngleHalf) //返回负数
|
||
{
|
||
return ((ptAllAn - allAn) - (surplusAngleHalf * 2)) / allAn;
|
||
}
|
||
else //返回正数
|
||
return ptAllAn / allAn;
|
||
}
|
||
/**
|
||
* Gets param at angle2
|
||
* @param an
|
||
* @param [isStart] true:返回负数,false 返回正数
|
||
* @returns
|
||
*/
|
||
GetParamAtAngle2(an, isStart = true)
|
||
{
|
||
//如果以pt为终点,那么所有的角度为
|
||
let ptAllAn = this.ComputeAnlge(an);
|
||
let allAn = this.AllAngle;
|
||
//减去圆弧角度,剩余角度的一半
|
||
let surplusAngleHalf = Math.PI - allAn / 2;
|
||
if (isStart) //返回负数
|
||
{
|
||
return ((ptAllAn - allAn) - (surplusAngleHalf * 2)) / allAn;
|
||
}
|
||
else //返回正数
|
||
return ptAllAn / allAn;
|
||
}
|
||
GetAngleAtPoint(pt)
|
||
{
|
||
let ptTmp = pt.clone().applyMatrix4(this.OCSInv);
|
||
return angle(ptTmp);
|
||
}
|
||
GetAngleAtParam(param)
|
||
{
|
||
return clampRad(this.m_StartAngle + param * this.AllAngle * (this.m_Clockwise ? -1 : 1));
|
||
}
|
||
GetSplitCurves(param)
|
||
{
|
||
let params = this.SplitParamSort(param);
|
||
//角度列表
|
||
let ans = params.map(p => this.GetAngleAtParam(p));
|
||
//返回圆弧表
|
||
let arcs = [];
|
||
for (let i = 0; i < ans.length - 1; i++) {
|
||
let arc = this.Clone();
|
||
arc.StartAngle = ans[i];
|
||
arc.EndAngle = ans[i + 1];
|
||
arcs.push(arc);
|
||
}
|
||
return arcs;
|
||
}
|
||
GetOffsetCurves(offsetDist)
|
||
{
|
||
if (this.m_Clockwise)
|
||
offsetDist *= -1;
|
||
if ((offsetDist + this.m_Radius) > 0) {
|
||
let arc = this.Clone();
|
||
arc.Radius = offsetDist + this.m_Radius;
|
||
return [arc];
|
||
}
|
||
return [];
|
||
}
|
||
Extend(newParam)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
if (newParam < 0) {
|
||
this.m_StartAngle = this.GetAngleAtParam(newParam);
|
||
}
|
||
else if (newParam > 1) {
|
||
this.m_EndAngle = this.GetAngleAtParam(newParam);
|
||
}
|
||
this.Update();
|
||
}
|
||
Join(cu)
|
||
{
|
||
if (cu instanceof Arc_1) {
|
||
if (equalv3(cu.Center, this.Center) && equaln(cu.m_Radius, this.m_Radius)) {
|
||
this.WriteAllObjectRecord();
|
||
let [sa, ea] = [cu.StartAngle, cu.EndAngle];
|
||
if (cu.m_Clockwise != this.m_Clockwise)
|
||
[sa, ea] = [ea, sa];
|
||
let allAn = this.AllAngle;
|
||
let saAllan = this.ComputeAnlge(sa);
|
||
let eaAllan = this.ComputeAnlge(ea);
|
||
if (equaln(sa, this.m_StartAngle)) //this起点对起点
|
||
{
|
||
if (eaAllan > allAn)
|
||
this.EndAngle = ea;
|
||
return Status.True;
|
||
}
|
||
else if (equaln(sa, this.m_EndAngle)) //this终点对起点
|
||
{
|
||
if (eaAllan < allAn || equaln(ea, this.m_StartAngle))
|
||
return Status.ConverToCircle;
|
||
else
|
||
this.EndAngle = ea;
|
||
return Status.True;
|
||
}
|
||
else if (equaln(ea, this.StartAngle)) //this起点对终点
|
||
{
|
||
if (saAllan < allAn)
|
||
return Status.ConverToCircle;
|
||
else
|
||
this.StartAngle = sa;
|
||
return Status.True;
|
||
}
|
||
else if (equaln(ea, this.m_EndAngle)) //this终点对终点
|
||
{
|
||
if (saAllan > allAn)
|
||
this.StartAngle = sa;
|
||
return Status.True;
|
||
}
|
||
else if (this.ParamOnCurve(this.GetParamAtAngle(sa))) {
|
||
if (eaAllan < saAllan)
|
||
return Status.ConverToCircle;
|
||
else if (eaAllan > allAn)
|
||
this.EndAngle = ea;
|
||
return Status.True;
|
||
}
|
||
else if (this.ParamOnCurve(this.GetParamAtAngle(ea))) {
|
||
this.StartAngle = sa;
|
||
return Status.True;
|
||
}
|
||
//使用按负方向去计算它的参数
|
||
let saParam;
|
||
if (saAllan > allAn)
|
||
saParam = (saAllan - Math.PI * 2) / allAn;
|
||
else
|
||
saParam = saAllan / allAn;
|
||
let eaParam;
|
||
if (eaAllan > saAllan && saAllan > allAn)
|
||
eaParam = (eaAllan - Math.PI * 2) / allAn;
|
||
else
|
||
eaParam = eaAllan / allAn;
|
||
let pMin = Math.max(0, saParam);
|
||
let pMax = Math.min(1, eaParam);
|
||
if (pMin <= pMax + 1e-5) {
|
||
if (saParam < 0)
|
||
this.StartAngle = sa;
|
||
if (eaParam > 1)
|
||
this.EndAngle = ea;
|
||
return Status.True;
|
||
}
|
||
}
|
||
}
|
||
return Status.False;
|
||
}
|
||
Reverse()
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this.m_Clockwise = !this.m_Clockwise;
|
||
[this.m_StartAngle, this.m_EndAngle] = [this.m_EndAngle, this.m_StartAngle];
|
||
return this;
|
||
}
|
||
IntersectWith2(curve, intType, tolerance = 1e-4)
|
||
{
|
||
if (curve instanceof Arc_1) {
|
||
return IntersectArcAndArc(this, curve, intType);
|
||
}
|
||
if (curve instanceof Line) {
|
||
return SwapParam(IntersectLineAndArc(curve, this, reverseIntersectOption(intType), tolerance));
|
||
}
|
||
if (curve instanceof Circle) {
|
||
return SwapParam(IntersectCircleAndArc(curve, this, reverseIntersectOption(intType), tolerance));
|
||
}
|
||
if (curve instanceof Polyline)
|
||
return SwapParam(IntersectPolylineAndCurve(curve, this, reverseIntersectOption(intType), tolerance));
|
||
if (curve instanceof Ellipse)
|
||
return SwapParam(IntersectEllipseAndCircleOrArc(curve, this, intType));
|
||
return [];
|
||
}
|
||
/**
|
||
* 计算出圆弧所包含的角度
|
||
*
|
||
* @readonly
|
||
* @type {number}
|
||
* @memberof Arc
|
||
*/
|
||
get AllAngle()
|
||
{
|
||
return this.ComputeAnlge(this.m_EndAngle);
|
||
}
|
||
get Bul()
|
||
{
|
||
if (equaln(this.AllAngle, Math.PI * 2))
|
||
return 1;
|
||
return Math.tan(this.AllAngle * 0.25) * (this.IsClockWise ? -1 : 1);
|
||
}
|
||
/**
|
||
* 计算所包含的角度
|
||
*
|
||
* @private
|
||
* @param {number} endAngle 结束的角度
|
||
* @returns
|
||
* @memberof Arc
|
||
*/
|
||
ComputeAnlge(endAngle)
|
||
{
|
||
//顺时针
|
||
if (this.m_Clockwise) {
|
||
if (this.m_StartAngle > endAngle)
|
||
return this.StartAngle - endAngle;
|
||
else //越过0点绘制圆弧
|
||
return (Math.PI * 2) - (endAngle - this.m_StartAngle);
|
||
}
|
||
else {
|
||
if (endAngle > this.m_StartAngle)
|
||
return endAngle - this.m_StartAngle;
|
||
else
|
||
return (Math.PI * 2) - (this.m_StartAngle - endAngle);
|
||
}
|
||
}
|
||
/**
|
||
* 解析两点和凸度所构成的圆弧
|
||
*
|
||
* @param {Vector2} p1
|
||
* @param {Vector2} p2
|
||
* @param {number} bul 凸度,在cad中,凸度为 <(四分之一圆心角)的正切值>
|
||
*/
|
||
ParseFromBul(p1, p2, bul)
|
||
{
|
||
if (p1 instanceof Vector2)
|
||
p1 = AsVector3(p1);
|
||
if (p2 instanceof Vector2)
|
||
p2 = AsVector3(p2);
|
||
let ocsInv = this.OCSInv;
|
||
p1 = p1.clone().applyMatrix4(ocsInv);
|
||
p2 = p2.clone().applyMatrix4(ocsInv);
|
||
//弦向量
|
||
let chordV = p2.clone().sub(p1);
|
||
//弦角度
|
||
let chordAn = angle(chordV);
|
||
//弦长度/2
|
||
let chordLengthHalf = chordV.length() / 2;
|
||
let allAngle = Math.atan(bul) * 4;
|
||
let HalfAngle = allAngle * 0.5;
|
||
//半径
|
||
this.m_Radius = chordLengthHalf / Math.sin(HalfAngle);
|
||
//指向圆心的角度
|
||
let toCenterAn = chordAn + Math.PI * 0.5; //弦角度转90
|
||
//圆心
|
||
let center = midPoint(p1, p2);
|
||
polar(center, toCenterAn, this.m_Radius - (bul * chordLengthHalf));
|
||
this.Center = center.clone().applyMatrix4(this.OCS);
|
||
this.m_Radius = Math.abs(this.m_Radius);
|
||
this.m_StartAngle = angle(p1.clone().sub(center));
|
||
this.m_EndAngle = angle(p2.clone().sub(center));
|
||
this.m_Clockwise = bul < 0;
|
||
return this;
|
||
}
|
||
FromThreePoint(pt1, pt2, pt3)
|
||
{
|
||
if (!(pt1 && pt2 && pt3))
|
||
return;
|
||
let ocsInv = this.OCSInv;
|
||
pt1 = pt1.clone().applyMatrix4(ocsInv);
|
||
pt2 = pt2.clone().applyMatrix4(ocsInv);
|
||
pt3 = pt3.clone().applyMatrix4(ocsInv);
|
||
let center = getCircleCenter(pt1, pt2, pt3);
|
||
this.Center = center.clone().applyMatrix4(this.OCS);
|
||
//用圆心和其中一个点求距离得到半径:
|
||
this.m_Radius = center.distanceTo(pt1);
|
||
//起始角度 端点角度
|
||
this.m_StartAngle = angle(pt1.clone().sub(center));
|
||
this.m_EndAngle = angle(pt3.clone().sub(center));
|
||
//求出向量p1->p2,p1->p3
|
||
let p1 = pt2.clone().sub(pt1);
|
||
let p2 = pt3.clone().sub(pt1);
|
||
this.m_Clockwise = p1.cross(p2).z < 0;
|
||
return this;
|
||
}
|
||
/**
|
||
* 重载: 初始化绘制实体.
|
||
*
|
||
* @param {RenderType} [renderType=RenderType.Wireframe]
|
||
*/
|
||
InitDrawObject(renderType = RenderType.Wireframe)
|
||
{
|
||
let geo = BufferGeometryUtils.CreateFromPts(this.Shape.getPoints(60).map(AsVector3));
|
||
if (renderType === RenderType.Print) {
|
||
var geometry = new LineGeometry();
|
||
geometry.setPositions(geo.attributes.position.array);
|
||
return new Line2(geometry, ColorMaterial.PrintLineMatrial);
|
||
}
|
||
return new Line$1(geo, ColorMaterial.GetLineMaterial(this._Color));
|
||
}
|
||
//更新Geometry
|
||
UpdateGeometry(geo)
|
||
{
|
||
let pts = this.Shape.getPoints(60).map(AsVector3);
|
||
BufferGeometryUtils.UpdatePts(geo, pts);
|
||
}
|
||
/**
|
||
* 重载:更新绘制的实体
|
||
*
|
||
* @param {RenderType} type
|
||
* @param {Object3D} obj
|
||
* @memberof Arc
|
||
*/
|
||
UpdateDrawObject(type, obj)
|
||
{
|
||
let geo = obj["geometry"];
|
||
this.UpdateGeometry(geo);
|
||
}
|
||
GetObjectSnapPoints(snapMode, pickPoint, lastPoint, viewXform)
|
||
{
|
||
switch (snapMode) {
|
||
case ObjectSnapMode.End:
|
||
return [this.StartPoint, this.EndPoint];
|
||
case ObjectSnapMode.Mid:
|
||
return [this.GetPointAtParam(0.5)];
|
||
case ObjectSnapMode.Nea:
|
||
return getArcOrCirNearPts(this, pickPoint, viewXform)
|
||
.filter(p => this.PtOnCurve(p));
|
||
case ObjectSnapMode.Ext:
|
||
return [this.GetClosestPointTo(pickPoint, true)];
|
||
case ObjectSnapMode.Cen:
|
||
return [this.Center];
|
||
case ObjectSnapMode.Per:
|
||
if (lastPoint) {
|
||
if (equaln(lastPoint.distanceToSquared(this.Center), 0, 1e-10))
|
||
return [];
|
||
let l = new Line(this.Center, lastPoint);
|
||
return l.IntersectWith(this, IntersectOption.ExtendBoth).filter(p => this.PtOnCurve(p));
|
||
}
|
||
case ObjectSnapMode.Tan:
|
||
let pts = GetTanPtsOnArcOrCircle(this, lastPoint);
|
||
if (pts)
|
||
return pts.filter(p => this.PtOnCurve(p));
|
||
}
|
||
return [];
|
||
}
|
||
GetGripPoints()
|
||
{
|
||
return [
|
||
this.StartPoint,
|
||
this.GetPointAtParam(0.5),
|
||
this.EndPoint,
|
||
this.Center.clone(),
|
||
];
|
||
}
|
||
MoveGripPoints(indexList, vec)
|
||
{
|
||
if (indexList.length > 0) {
|
||
this.WriteAllObjectRecord();
|
||
let ptsArr = this.GetGripPoints();
|
||
let index = indexList[0];
|
||
let p = ptsArr[index];
|
||
if (p) {
|
||
p.add(vec);
|
||
if (index > 2)
|
||
this.Center = this.Center.add(vec);
|
||
else
|
||
this.FromThreePoint(ptsArr[0], ptsArr[1], ptsArr[2]);
|
||
this.Update();
|
||
}
|
||
}
|
||
}
|
||
GetStretchPoints()
|
||
{
|
||
return [this.StartPoint, this.EndPoint];
|
||
}
|
||
MoveStretchPoints(indexList, vec)
|
||
{
|
||
if (indexList.length === 0)
|
||
return;
|
||
this.WriteAllObjectRecord();
|
||
if (indexList.length === 2)
|
||
this.ApplyMatrix(MoveMatrix(vec));
|
||
else
|
||
for (let index of indexList) {
|
||
let pts = [this.StartPoint, this.EndPoint];
|
||
let [sp, ep] = pts;
|
||
let oldChordLengthHalf = sp.distanceTo(ep) * 0.5;
|
||
let arcHeight = oldChordLengthHalf * this.Bul;
|
||
pts[index].add(vec);
|
||
let newChordLengthHalf = sp.distanceTo(ep) * 0.5;
|
||
let newBul = arcHeight / newChordLengthHalf;
|
||
//根据凸度构造新的弧
|
||
this.ParseFromBul(sp, ep, newBul);
|
||
this.Update();
|
||
}
|
||
}
|
||
GetParamAtDist(d)
|
||
{
|
||
return d / this.Length;
|
||
}
|
||
GetFistDeriv(pt)
|
||
{
|
||
let an;
|
||
if (typeof pt === "number")
|
||
an = this.GetAngleAtParam(pt);
|
||
else
|
||
an = angle(pt.clone().applyMatrix4(this.OCSInv));
|
||
an += Math.PI * 0.5 * (this.m_Clockwise ? -1 : 1);
|
||
let ocs = new Matrix4().extractRotation(this.OCS);
|
||
return polar(new Vector3(), an, this.m_Radius).applyMatrix4(ocs);
|
||
}
|
||
GetClosestPointTo(pt, extend)
|
||
{
|
||
let l = new Line(this.Center, pt);
|
||
let inPts = this.IntersectWith(l, extend ? IntersectOption.ExtendBoth : IntersectOption.ExtendArg);
|
||
if (inPts.length < 2)
|
||
inPts.push(this.StartPoint, this.EndPoint);
|
||
return inPts.reduce((p1, p2) => p1.distanceToSquared(pt) < p2.distanceToSquared(pt) ? p1 : p2);
|
||
}
|
||
//#region -------------------------File-------------------------
|
||
//对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化
|
||
//对象从文件中读取数据,初始化自身
|
||
_ReadFile(file)
|
||
{
|
||
super._ReadFile(file);
|
||
let ver = file.Read();
|
||
if (ver === 1) {
|
||
this.Center = new Vector3().fromArray(file.Read());
|
||
this.Normal = new Vector3().fromArray(file.Read());
|
||
}
|
||
this.m_Radius = file.Read();
|
||
this.m_StartAngle = file.Read();
|
||
this.m_EndAngle = file.Read();
|
||
this.m_Clockwise = file.Read();
|
||
}
|
||
//对象将自身数据写入到文件.
|
||
WriteFile(file)
|
||
{
|
||
super.WriteFile(file);
|
||
file.Write(2);
|
||
file.Write(this.m_Radius);
|
||
file.Write(this.m_StartAngle);
|
||
file.Write(this.m_EndAngle);
|
||
file.Write(this.m_Clockwise);
|
||
}
|
||
};
|
||
Arc = Arc_1 = __decorate([
|
||
Factory
|
||
], Arc);
|
||
|
||
/**
|
||
* 判断点在多段线内外
|
||
* @param pl 多段线
|
||
* @param pt 点
|
||
* @returns 点在多段线内部
|
||
*/
|
||
function IsPointInPolyLine(pl, pt)
|
||
{
|
||
let crossings = 0;
|
||
let insLine = new Line(pt, pt.clone().add(new Vector3(0, 10, 0)));
|
||
for (let i = 0; i < pl.EndParam; i++) {
|
||
if (equaln(pl.GetBuilgeAt(i), 0, 5e-6)) //直线
|
||
{
|
||
let sp = pl.GetPointAtParam(i);
|
||
let ep = pl.GetPointAtParam(i + 1);
|
||
//点位于线上面
|
||
if (pt.y > Math.max(sp.y, ep.y))
|
||
continue;
|
||
//线垂直Y轴
|
||
let derX = ep.x - sp.x;
|
||
if (equaln(derX, 0, 5e-6))
|
||
continue;
|
||
//起点
|
||
if (equaln(sp.x, pt.x, 5e-6)) {
|
||
if (sp.y > pt.y && derX < 0)
|
||
crossings++;
|
||
continue;
|
||
}
|
||
//终点
|
||
if (equaln(ep.x, pt.x, 5e-6)) {
|
||
if (ep.y > pt.y && derX > 0)
|
||
crossings++;
|
||
continue;
|
||
}
|
||
//快速求交,只验证有没有交点
|
||
let [x1, x2] = sp.x > ep.x ? [ep.x, sp.x] : [sp.x, ep.x];
|
||
if (pt.x > x1 && pt.x < x2) {
|
||
let derY = ep.y - sp.y;
|
||
let k = derY / derX;
|
||
if ((pt.x - sp.x) * k + sp.y > pt.y)
|
||
crossings++;
|
||
}
|
||
}
|
||
else //圆弧
|
||
{
|
||
let arc = pl.GetCurveAtIndex(i);
|
||
let sp = arc.StartPoint;
|
||
let ep = arc.EndPoint;
|
||
//如果相切
|
||
if (equaln(Math.abs(pt.x - arc.Center.x), arc.Radius)) {
|
||
//当点和起点或者终点和点相切时
|
||
if (equaln(sp.x, pt.x) && sp.y > pt.y) {
|
||
if (ep.x - sp.x < -1e-5)
|
||
crossings++;
|
||
}
|
||
else if (equaln(ep.x, pt.x) && ep.y > pt.y) {
|
||
if (ep.x - sp.x > 1e-5)
|
||
crossings++;
|
||
}
|
||
continue;
|
||
}
|
||
if (equaln(sp.x, pt.x) && sp.y > pt.y) {
|
||
let der = arc.GetFistDeriv(0);
|
||
if (der.x < -1e-5)
|
||
crossings++;
|
||
}
|
||
if (equaln(ep.x, pt.x) && ep.y > pt.y) {
|
||
let der = arc.GetFistDeriv(1);
|
||
if (der.x > 1e-5)
|
||
crossings++;
|
||
}
|
||
for (let pti of arc.IntersectWith(insLine, IntersectOption.ExtendArg)) {
|
||
if (pti.y < pt.y || equalv3(sp, pti, 1e-5) || equalv3(ep, pti, 1e-5))
|
||
continue;
|
||
let der = arc.GetFistDeriv(pti);
|
||
if (!equaln(der.x, 0)) //相切.
|
||
crossings++;
|
||
}
|
||
}
|
||
}
|
||
return (crossings % 2) === 1;
|
||
}
|
||
|
||
/**
|
||
* 一个简单的计数器实现,本质是使用一个Map来保存元素的个数
|
||
*
|
||
* 例:
|
||
* let count = new Count();
|
||
* count.AddCount("Test", 1);
|
||
* count.GetCount("Test");//现在 Test 的个数为1
|
||
*/
|
||
class Count
|
||
{
|
||
constructor()
|
||
{
|
||
this.m_CountMap = new WeakMap();
|
||
}
|
||
GetCount(obj)
|
||
{
|
||
let count = this.m_CountMap.get(obj);
|
||
if (!count) {
|
||
this.m_CountMap.set(obj, 0);
|
||
count = 0;
|
||
}
|
||
return count;
|
||
}
|
||
AddCount(obj, add)
|
||
{
|
||
this.m_CountMap.set(obj, this.GetCount(obj) + add);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 曲线连接图
|
||
* 所有的顶点和边的关系
|
||
*/
|
||
class CurveMap
|
||
{
|
||
constructor(numdimensions = 4, _RemoveSortLine = false, multiplier = 10 ** numdimensions)
|
||
{
|
||
this.numdimensions = numdimensions;
|
||
this._RemoveSortLine = _RemoveSortLine;
|
||
this.multiplier = multiplier;
|
||
/*
|
||
节点图.
|
||
每个节点对应下一个路口的路线表.
|
||
路口表使用逆时针排序,起始角度使用正x轴.
|
||
*/
|
||
this._VerticeMap = new Map();
|
||
this._Vertices = [];
|
||
this._LookupTable = {};
|
||
}
|
||
/**
|
||
* 得到节点图的所有站点列表
|
||
*/
|
||
get Stands()
|
||
{
|
||
return this._Vertices;
|
||
}
|
||
/**
|
||
* @param curve
|
||
* @param [isArc=curve instanceof Arc]
|
||
* @param [removeDuplicate=false]
|
||
* @returns 加入成功?
|
||
*/
|
||
AddCurveToMap(curve, isArc = curve instanceof Arc, removeDuplicate = false, parseAngle = false)
|
||
{
|
||
let sp = curve.StartPoint;
|
||
let ep = curve.EndPoint;
|
||
let startS = this.GetOnlyVertice(sp);
|
||
let endS = this.GetOnlyVertice(ep);
|
||
//在面域分析中,路线指向同一个顶点已经没有意义了
|
||
if (this._RemoveSortLine && startS === endS)
|
||
return false;
|
||
if (removeDuplicate) //删除重复
|
||
{
|
||
let index = startS.routes.findIndex(r =>
|
||
{
|
||
if (r.to === endS && r.curve.constructor.name === curve.constructor.name) {
|
||
if (isArc)
|
||
return equalv3(curve.GetPointAtParam(0.5), r.curve.GetPointAtParam(0.5));
|
||
return true;
|
||
}
|
||
});
|
||
if (index !== -1)
|
||
return false;
|
||
}
|
||
let length = curve.Length;
|
||
curve.TempData = 0;
|
||
let routeS2E = { curve, isReverse: false, length, from: startS, to: endS, s: sp, e: ep };
|
||
let routeE2S = { curve, isReverse: true, length, from: endS, to: startS, e: sp, s: ep };
|
||
if (!isArc && parseAngle) {
|
||
let an = angle(endS.position.clone().sub(startS.position));
|
||
routeS2E.an = an;
|
||
routeE2S.an = clampRad(an + Math.PI);
|
||
}
|
||
startS.routes.push(routeS2E);
|
||
endS.routes.push(routeE2S);
|
||
return true;
|
||
}
|
||
/**
|
||
* 获得唯一的顶点
|
||
*/
|
||
GetOnlyVertice(p)
|
||
{
|
||
let gp = this.GenerateP(p);
|
||
if (this._VerticeMap.has(gp))
|
||
return this._VerticeMap.get(gp);
|
||
let vertice = { position: gp, routes: [] };
|
||
this._VerticeMap.set(p, vertice);
|
||
this._Vertices.push(vertice);
|
||
return vertice;
|
||
}
|
||
/**
|
||
* 生成一个唯一的向量.
|
||
*/
|
||
GenerateP(p)
|
||
{
|
||
let key = "";
|
||
let els = p.toArray();
|
||
for (let n of els) {
|
||
let valueQuantized = Math.round(n * this.multiplier);
|
||
key += valueQuantized + '/';
|
||
}
|
||
if (key in this._LookupTable)
|
||
return this._LookupTable[key];
|
||
let hashparts = els.map((el) =>
|
||
{
|
||
let q0 = Math.floor(el * this.multiplier);
|
||
let q1 = q0 + 1;
|
||
return ['' + q0 + '/', '' + q1 + '/'];
|
||
});
|
||
let numelements = els.length;
|
||
let numhashes = 1 << numelements;
|
||
for (let hashmask = 0; hashmask < numhashes; ++hashmask) {
|
||
let hashmaskShifted = hashmask;
|
||
key = '';
|
||
hashparts.forEach(function (hashpart)
|
||
{
|
||
key += hashpart[hashmaskShifted & 1];
|
||
hashmaskShifted >>= 1;
|
||
});
|
||
this._LookupTable[key] = p;
|
||
}
|
||
return p;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 轨道控制的数学类,观察向量和角度的互相转换
|
||
* 当x当抬头或者低头到90度时,触发万向锁.
|
||
*/
|
||
class Orbit
|
||
{
|
||
constructor()
|
||
{
|
||
//抬头低头 正数抬头 负数低头
|
||
this.phi = 0; //Φ
|
||
//身体旋转 0为正右边 逆时针旋转
|
||
this.theta = 0; //θ
|
||
}
|
||
get RoX()
|
||
{
|
||
return this.phi;
|
||
}
|
||
set RoX(v)
|
||
{
|
||
this.phi = MathUtils.clamp(v, Math.PI * -0.49, Math.PI * 0.49);
|
||
}
|
||
/**
|
||
* 使用旋转角度 计算观察向量
|
||
* @param [outDirection] 引用传入,如果传入,那么就不构造新的向量
|
||
* @returns 返回观察向量
|
||
*/
|
||
UpdateDirection(outDirection = new Vector3())
|
||
{
|
||
outDirection.z = Math.sin(this.phi);
|
||
//归一化专用.
|
||
let d = Math.abs(Math.cos(this.phi));
|
||
outDirection.x = Math.cos(this.theta) * d;
|
||
outDirection.y = Math.sin(this.theta) * d;
|
||
return outDirection;
|
||
}
|
||
/**
|
||
* 使用观察向量,计算旋转角度
|
||
* @param dir 这个向量会被修改成单位向量.
|
||
*/
|
||
SetFromDirection(dir)
|
||
{
|
||
dir.normalize();
|
||
this.phi = Math.asin(dir.z);
|
||
if (equaln(dir.x, 0) && equaln(dir.y, 0))
|
||
if (dir.z > 0)
|
||
this.theta = Math.PI * -0.5;
|
||
else
|
||
this.theta = Math.PI * 0.5;
|
||
else
|
||
this.theta = Math.atan2(dir.y, dir.x);
|
||
}
|
||
/**
|
||
* 参考任意轴坐标系算法.
|
||
* http://help.autodesk.com/view/ACD/2017/CHS/?guid=GUID-E19E5B42-0CC7-4EBA-B29F-5E1D595149EE
|
||
*/
|
||
static ComputUpDirection(n, ay = new Vector3(), ax = new Vector3())
|
||
{
|
||
n.normalize();
|
||
if (Math.abs(n.x) < 0.015625 && Math.abs(n.y) < 0.015625)
|
||
ax.crossVectors(YAxis, n);
|
||
else
|
||
ax.crossVectors(ZAxis, n);
|
||
ay.crossVectors(n, ax);
|
||
ax.normalize();
|
||
ay.normalize();
|
||
return ay;
|
||
}
|
||
}
|
||
|
||
/**
|
||
面域分析,基于最小循环图重新实现的版本,拓展了实现求最大轮廓。
|
||
当最大轮廓=最小轮廓时,只绘制最大轮廓(独立轮廓无分裂)。
|
||
|
||
算法只实现去重模式,业务场景应该没有非去重模式。
|
||
如果需要非去重模式,那么应该获取到多个CurveMap,然后对多个CurveMap进行面域分析,得出多个重叠的面域。
|
||
*/
|
||
class RegionParse
|
||
{
|
||
/**
|
||
* @param cuList 请不要传递圆和椭圆.
|
||
* @param [numDimensions=3] 精度:小数点后个数
|
||
* @param [removeDuplicate=true] 删除重复(现在必须是true,请不要修改它)
|
||
*/
|
||
constructor(cuList, numDimensions = 3, removeDuplicate = true)
|
||
{
|
||
this.numDimensions = numDimensions;
|
||
this.removeDuplicate = removeDuplicate;
|
||
//区域列表 通常是外轮廓
|
||
this.RegionsOutline = [];
|
||
//区域列表 通常是内轮廓
|
||
this.RegionsInternal = [];
|
||
//碎线 曲线进入到这里会被炸开.
|
||
this.ExpLineMap = new Map();
|
||
//需要搜索的站
|
||
let vertices = this.GenerateVerticeMap(cuList);
|
||
//移除细丝
|
||
while (true) {
|
||
let v = vertices.find(v => v.routes.length < 2);
|
||
if (v)
|
||
this.RemoveFilamentAt(v, vertices);
|
||
else
|
||
break;
|
||
}
|
||
let lowerVertice;
|
||
while (vertices.length > 0) {
|
||
lowerVertice = (lowerVertice === null || lowerVertice === void 0 ? void 0 : lowerVertice.routes.length) > 1 ? lowerVertice : this.FindLowerLeftStand(vertices);
|
||
let minWalk = ClosedWalkFrom(lowerVertice, this._CurveCount, WalkType.Min);
|
||
let maxWalk = ClosedWalkFrom(lowerVertice, this._CurveCount, WalkType.Max);
|
||
this.RemoveEdge(minWalk[0]);
|
||
this.RemoveFilamentAt(minWalk[0].from, vertices);
|
||
this.RemoveFilamentAt(minWalk[0].to, vertices);
|
||
minWalk = ReduceWalk(minWalk);
|
||
maxWalk = ReduceWalk(maxWalk);
|
||
if (maxWalk.length > 1) {
|
||
this.RegionsOutline.push(maxWalk);
|
||
if (minWalk.length === maxWalk.length && minWalk.every((w1, index) => w1 === maxWalk[index])) //大小重叠
|
||
{
|
||
//直接remove,不用计算引用个数
|
||
for (let w of minWalk) {
|
||
this.RemoveEdge(w);
|
||
this.RemoveFilamentAt(w.from, vertices);
|
||
this.RemoveFilamentAt(w.to, vertices);
|
||
}
|
||
continue; //继续循环
|
||
}
|
||
else
|
||
for (let w of maxWalk)
|
||
w.curve.TempData = 1;
|
||
}
|
||
if (minWalk.length > 1) // && minWalk.every(w => <number>(w.curve.TempData) < 2) 没有重复线应该不会被用2次
|
||
{
|
||
this.RegionsInternal.push(minWalk);
|
||
for (let w of minWalk) {
|
||
w.curve.TempData++;
|
||
if (w.curve.TempData === 2) {
|
||
this.RemoveEdge(w);
|
||
this.RemoveFilamentAt(w.from, vertices);
|
||
this.RemoveFilamentAt(w.to, vertices);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
RemoveFilamentAt(v, vertices)
|
||
{
|
||
let current = v;
|
||
while (current && current.routes.length < 2) {
|
||
vertices = arrayRemoveOnce(vertices, current);
|
||
let r = current.routes[0];
|
||
if (r) {
|
||
this.RemoveEdge(r);
|
||
current = r.to;
|
||
}
|
||
else
|
||
current = undefined;
|
||
}
|
||
}
|
||
RemoveEdge(r)
|
||
{
|
||
let index = r.from.routes.findIndex(rr => rr.curve === r.curve);
|
||
if (index !== -1)
|
||
r.from.routes.splice(index, 1);
|
||
index = r.to.routes.findIndex(rr => rr.curve === r.curve);
|
||
if (index !== -1)
|
||
r.to.routes.splice(index, 1);
|
||
}
|
||
/**
|
||
* 找到最下方并且最左边的站 yx
|
||
*/
|
||
FindLowerLeftStand(vertices)
|
||
{
|
||
return vertices.reduce((m, v) =>
|
||
{
|
||
let dy = v.position.y - m.position.y;
|
||
if (dy < 0)
|
||
return v;
|
||
if (dy > 0)
|
||
return m;
|
||
return v.position.x - m.position.x < 0 ? v : m;
|
||
});
|
||
}
|
||
/**
|
||
* 构造路线图. 每个节点对应下一个路口的路线表. 路口表使用逆时针排序,起始角度使用正x轴.
|
||
* @returns 所有的顶点
|
||
*/
|
||
GenerateVerticeMap(curveList)
|
||
{
|
||
let curveMap = new CurveMap(this.numDimensions, true);
|
||
//将多段线炸开
|
||
let plcus = [];
|
||
arrayRemoveIf(curveList, c =>
|
||
{
|
||
if (c instanceof Polyline) {
|
||
let cus = c.Explode();
|
||
//如果为圆弧,提前打断
|
||
let arcs = [];
|
||
arrayRemoveIf(cus, c =>
|
||
{
|
||
if (c.Length < 1e-5)
|
||
return true;
|
||
if (c instanceof Arc) {
|
||
let arcBrs = this.BreakArc(c);
|
||
for (let arc of arcBrs)
|
||
arcs.push(arc);
|
||
}
|
||
return false;
|
||
});
|
||
//加入到计算
|
||
cus.push(...arcs);
|
||
this.ExpLineMap.set(c, cus);
|
||
plcus.push(...cus);
|
||
return true;
|
||
}
|
||
return false;
|
||
});
|
||
curveList.push(...plcus);
|
||
this._CurveCount = curveList.length;
|
||
for (let cu of curveList) {
|
||
//由于圆弧可能导致最低点计算错误的问题.
|
||
if (cu instanceof Arc) {
|
||
let arcs = this.BreakArc(cu);
|
||
if (arcs.length > 1) {
|
||
arcs.forEach(a => curveMap.AddCurveToMap(a, true, this.removeDuplicate, true));
|
||
this.ExpLineMap.set(cu, arcs);
|
||
continue;
|
||
}
|
||
else
|
||
curveMap.AddCurveToMap(cu, true, this.removeDuplicate, true);
|
||
}
|
||
else
|
||
curveMap.AddCurveToMap(cu, false, this.removeDuplicate, true);
|
||
}
|
||
//排序,根据角度逆时针排序.
|
||
for (let v of curveMap._Vertices) {
|
||
let minLength = Infinity;
|
||
for (let r of v.routes)
|
||
if (r.length < minLength)
|
||
minLength = r.length;
|
||
for (let r of v.routes)
|
||
CalcRouteAngle(r, minLength * 0.2);
|
||
v.routes.sort((r1, r2) => r1.an - r2.an);
|
||
}
|
||
return curveMap.Stands;
|
||
}
|
||
BreakArc(arc)
|
||
{
|
||
let underPt = arc.Center.add(new Vector3(0, -arc.Radius));
|
||
let param = arc.GetParamAtPoint(underPt);
|
||
if (param > 0.01 && param < 0.99)
|
||
return arc.GetSplitCurves(param);
|
||
else
|
||
return [arc];
|
||
}
|
||
/**
|
||
* 曲线是否已经被算法使用
|
||
*/
|
||
GetCueveUsed(cu)
|
||
{
|
||
if (this.ExpLineMap.has(cu)) {
|
||
let use = this.ExpLineMap.get(cu).some(c => c.TempData > 0);
|
||
if (!use)
|
||
this.ExpLineMap.delete(cu);
|
||
return use;
|
||
}
|
||
else
|
||
return cu.TempData > 0;
|
||
}
|
||
}
|
||
function CalcRouteAngle(r, length)
|
||
{
|
||
if (r.an !== undefined)
|
||
return;
|
||
let cu = r.curve;
|
||
let p = r.isReverse ?
|
||
cu.GetPointAtParam(cu.GetParamAtDist(r.length - length))
|
||
: cu.GetPointAtParam(cu.GetParamAtDist(length));
|
||
r.an = angle(p.sub(r.from.position));
|
||
}
|
||
var WalkType;
|
||
(function (WalkType)
|
||
{
|
||
WalkType[WalkType["Min"] = 1] = "Min";
|
||
WalkType[WalkType["Max"] = -1] = "Max";
|
||
})(WalkType || (WalkType = {}));
|
||
function ClosedWalkFrom(startVertice, maxRoute, type = WalkType.Min)
|
||
{
|
||
let walk = [];
|
||
let curVertice = startVertice;
|
||
let preRoute;
|
||
// console.log("start", type, startVertice.position.toArray());
|
||
do {
|
||
let route = GetNextRoute(curVertice, preRoute, type);
|
||
if (type === WalkType.Max && route.curve.TempData > 0)
|
||
return [];
|
||
// console.log(route.to.position.toArray());
|
||
walk.push(route);
|
||
[curVertice, preRoute] = [route.to, route];
|
||
if (walk.length > maxRoute * 2)
|
||
throw "超过计算次数限制";
|
||
} while (curVertice !== startVertice);
|
||
return walk;
|
||
}
|
||
/**
|
||
* 删除中途回路
|
||
*/
|
||
function ReduceWalk(w)
|
||
{
|
||
if (w.length === 0)
|
||
return w;
|
||
//未构成回路,直接回家
|
||
if (w[0].curve === arrayLast(w).curve)
|
||
return [];
|
||
for (let i = 0; i < w.length; i++) {
|
||
let r1 = w[i];
|
||
for (let j = w.length; j--;) {
|
||
if (i === j)
|
||
break;
|
||
let r2 = w[j];
|
||
if (r1.to === r2.to) {
|
||
if (j > i)
|
||
w.splice(i + 1, j - i);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return w;
|
||
}
|
||
function GetNextRoute(v, prev, type = WalkType.Min)
|
||
{
|
||
if (!prev)
|
||
return arrayLast(v.routes); //顺时针 cw \|/ 从左往右
|
||
//逆时针 ccw 往左
|
||
let index = v.routes.findIndex(r => r.curve === prev.curve);
|
||
let newIndex = FixIndex(index + 1 * type, v.routes);
|
||
return v.routes[newIndex];
|
||
}
|
||
|
||
var BoolOpeartionType;
|
||
(function (BoolOpeartionType)
|
||
{
|
||
BoolOpeartionType[BoolOpeartionType["Intersection"] = 0] = "Intersection";
|
||
BoolOpeartionType[BoolOpeartionType["Union"] = 1] = "Union";
|
||
BoolOpeartionType[BoolOpeartionType["Subtract"] = 2] = "Subtract";
|
||
})(BoolOpeartionType || (BoolOpeartionType = {}));
|
||
const fuzz = 1e-3;
|
||
let fuzzV3 = new Vector3(fuzz, fuzz, fuzz);
|
||
//判断曲线是否在源封闭曲线内
|
||
function isTargetCurInOrOnSourceCur(sourceCur, targetCur)
|
||
{
|
||
if (!sourceCur.BoundingBox.expandByVector(fuzzV3).containsBox(targetCur.BoundingBox))
|
||
return false;
|
||
let cus = [];
|
||
if (targetCur instanceof Polyline)
|
||
cus = targetCur.Explode();
|
||
else
|
||
cus = [targetCur];
|
||
return cus.every(c =>
|
||
{
|
||
let pts = getIntPtContextPts(sourceCur, c);
|
||
if (pts.length <= 1)
|
||
pts.push(c.StartPoint, c.EndPoint);
|
||
return IsPtsAllInOrOnReg(sourceCur, pts);
|
||
});
|
||
}
|
||
//获取交点处上下距0.01par的点
|
||
function getIntPtContextPts(sourceCur, cu, pts = [])
|
||
{
|
||
let interPts = cu.IntersectWith(sourceCur, IntersectOption.OnBothOperands);
|
||
if (interPts.length > 0) {
|
||
let pars = interPts.map(pt => cu.GetParamAtPoint(pt));
|
||
for (let par of pars) {
|
||
if (par >= 0.02)
|
||
pts.push(cu.GetPointAtParam(par - 0.01));
|
||
if (par <= (cu.EndParam - 0.02))
|
||
pts.push(cu.GetPointAtParam(par + 0.01));
|
||
}
|
||
}
|
||
return pts;
|
||
}
|
||
//判断点点是否全部都在封闭区域内或者在曲线上
|
||
function IsPtsAllInOrOnReg(sourceReg, pts)
|
||
{
|
||
return pts.every(pt =>
|
||
{
|
||
//是否点在封闭曲线内
|
||
return sourceReg.PtOnCurve(pt) || sourceReg.PtInCurve(pt);
|
||
});
|
||
}
|
||
|
||
let cache = new WeakMap();
|
||
class Contour
|
||
{
|
||
SetCurve(cu)
|
||
{
|
||
if (cu instanceof Polyline) {
|
||
if (cu.Area2 < 0)
|
||
cu.Reverse();
|
||
}
|
||
this._Curve = cu;
|
||
}
|
||
/**会将传入的闭合轮廓改为逆时针 */
|
||
static CreateContour(cus, needLink = true)
|
||
{
|
||
if (cus instanceof Curve) {
|
||
if (cus.IsClose) {
|
||
let c = new Contour();
|
||
c.SetCurve(cus);
|
||
return c;
|
||
}
|
||
return;
|
||
}
|
||
let closeCurve = Contour.Combine(cus, needLink, 1e-2);
|
||
if (closeCurve && closeCurve.IsClose) {
|
||
if (closeCurve instanceof Polyline && closeCurve.CloseMark === false) {
|
||
closeCurve.CloseMark = true;
|
||
closeCurve.RemoveVertexAt(closeCurve.NumberOfVertices - 1);
|
||
}
|
||
let c = new Contour();
|
||
c.SetCurve(closeCurve);
|
||
return c;
|
||
}
|
||
}
|
||
get Curve()
|
||
{
|
||
return this._Curve;
|
||
}
|
||
get Area()
|
||
{
|
||
return this._Curve.Area;
|
||
}
|
||
get BoundingBox()
|
||
{
|
||
return this._Curve.BoundingBox;
|
||
}
|
||
/**
|
||
* 不等比例缩放
|
||
* @param {number} ref 缩放参考值,大于该值的点缩放
|
||
* @param {number} dist 缩放距离
|
||
* @param {string} dir x y z
|
||
*/
|
||
UnEqualProportionScale(ref, dist, dir)
|
||
{
|
||
let cu = this._Curve;
|
||
if (cu instanceof Polyline) {
|
||
let lineData = cu.LineData;
|
||
let length = lineData.length;
|
||
let p = cu.Position[dir];
|
||
let moveIndexs = [];
|
||
for (let i = 0; i < length; i++) {
|
||
if (lineData[i].pt[dir] + p > ref)
|
||
moveIndexs.push(i);
|
||
}
|
||
let moveVec = new Vector3();
|
||
moveVec[dir] = dist;
|
||
cu.MoveStretchPoints(moveIndexs, moveVec);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
Clone()
|
||
{
|
||
return Contour.CreateContour([this._Curve.Clone()]);
|
||
}
|
||
//交集:结果数组为空则失败
|
||
IntersectionBoolOperation(target)
|
||
{
|
||
if (!IntersectBox2(this.BoundingBox, target.BoundingBox))
|
||
return [];
|
||
let resultCus = this.GetIntersetAndUnionList(target);
|
||
return Contour.GetAllContour(resultCus.intersectionList);
|
||
}
|
||
//并集:结果轮廓数组长度大于2,则失败.等于1则成功.
|
||
UnionBoolOperation(target)
|
||
{
|
||
let resultCus = this.GetIntersetAndUnionList(target);
|
||
//快速
|
||
if (resultCus.unionList.every(c => c.IsClose))
|
||
return {
|
||
contours: Contour.GetAllContour(resultCus.unionList),
|
||
holes: [],
|
||
};
|
||
//并集后的线段表如果有共线的直接合并起来
|
||
let cus = [];
|
||
for (let pl of resultCus.unionList) {
|
||
if (pl instanceof Polyline)
|
||
cus.push(...pl.Explode());
|
||
else
|
||
cus.push(pl);
|
||
}
|
||
let cuGroups = curveLinkGroup(cus);
|
||
for (let g of cuGroups) {
|
||
for (let i = 0; i < g.length; i++) {
|
||
let c1 = g[i];
|
||
let nextI = FixIndex(i + 1, g);
|
||
let c2 = g[nextI];
|
||
let status = c1.Join(c2);
|
||
if (status === Status.True) {
|
||
g.splice(nextI, 1);
|
||
i--;
|
||
}
|
||
else if (status === Status.ConverToCircle) {
|
||
g.length = 0;
|
||
let a = c1;
|
||
g.push(new Circle(a.Center, a.Radius));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
let allContour = Contour.GetAllContour(cuGroups);
|
||
if (allContour.length < 2) {
|
||
return {
|
||
contours: allContour,
|
||
holes: [],
|
||
};
|
||
}
|
||
else {
|
||
let cache = new WeakMap();
|
||
for (let c of allContour)
|
||
cache.set(c, c.Area);
|
||
allContour.sort((a, b) => cache.get(b) - cache.get(a));
|
||
return {
|
||
contours: [allContour[0]],
|
||
holes: allContour.slice(1)
|
||
};
|
||
}
|
||
}
|
||
//差集:等于0完全被减去
|
||
SubstactBoolOperation(target)
|
||
{
|
||
let subtractList = this.GetSubtractList(target);
|
||
//纯网洞
|
||
if (subtractList.every(c => c.IsClose))
|
||
return Contour.GetAllContour(subtractList);
|
||
let regParse = new RegionParse(subtractList, 2);
|
||
let contours = [];
|
||
//分析封闭包围区域
|
||
const parseRoute = (routeSet) =>
|
||
{
|
||
for (let routes of routeSet) {
|
||
let cs = routes.map(r => r.curve);
|
||
let c = Contour.CreateContour(cs, false);
|
||
if (c
|
||
&& !equalCurve(c.Curve, this.Curve)
|
||
&& !equalCurve(c.Curve, target.Curve)
|
||
&& c.Area > 1e-3)
|
||
contours.push(c);
|
||
}
|
||
};
|
||
parseRoute(regParse.RegionsOutline);
|
||
parseRoute(regParse.RegionsInternal);
|
||
return contours;
|
||
}
|
||
/**
|
||
* 计算与目标轮廓布尔运算后的结果曲线.
|
||
*/
|
||
GetIntersetAndUnionList(target)
|
||
{
|
||
let intersectionList = [];
|
||
let unionList = [];
|
||
let sourceOutline = this._Curve;
|
||
let targetOutline = target.Curve;
|
||
let isEqualNormal = equalv3(sourceOutline.Normal, targetOutline.Normal, 1e-3);
|
||
let interPts = sourceOutline.IntersectWith2(targetOutline, IntersectOption.OnBothOperands);
|
||
let sourceContainerTarget = this.CuInOutline(targetOutline);
|
||
let targetContainerSource = target.CuInOutline(sourceOutline);
|
||
//包含.相交.分离(三种状态)
|
||
if (sourceContainerTarget) //源包含目标
|
||
{
|
||
intersectionList.push(targetOutline);
|
||
unionList.push(sourceOutline);
|
||
}
|
||
else if (targetContainerSource) //目标包含源
|
||
{
|
||
unionList.push(targetOutline);
|
||
intersectionList.push(sourceOutline);
|
||
}
|
||
else if (interPts.length <= 1) //分离
|
||
{
|
||
unionList.push(sourceOutline, targetOutline);
|
||
}
|
||
else //相交 interPts.length > 0
|
||
{
|
||
let pars1 = interPts.map(r => r.thisParam);
|
||
let pars2 = interPts.map(r => r.argParam);
|
||
let sourceCus = sourceOutline.GetSplitCurves(pars1);
|
||
let targetCus = targetOutline.GetSplitCurves(pars2);
|
||
for (let pl of sourceCus) {
|
||
let hasEqualCus = false;
|
||
for (let i = 0; i < targetCus.length; i++) {
|
||
let cu = targetCus[i];
|
||
hasEqualCus = fastEqualCurve(cu, pl);
|
||
if (hasEqualCus) {
|
||
//方向相同
|
||
if (equalv3(cu.GetFistDeriv(cu.EndParam * 0.5).normalize(), pl.GetFistDeriv(pl.EndParam * 0.5).normalize(), 1e-3)
|
||
=== isEqualNormal) {
|
||
unionList.push(pl);
|
||
intersectionList.push(pl);
|
||
}
|
||
targetCus.splice(i, 1);
|
||
break;
|
||
}
|
||
}
|
||
if (hasEqualCus)
|
||
continue;
|
||
if (target.CuInOutline(pl))
|
||
intersectionList.push(pl);
|
||
else
|
||
unionList.push(pl);
|
||
}
|
||
for (let pl of targetCus) {
|
||
if (this.CuInOutline(pl))
|
||
intersectionList.push(pl);
|
||
else
|
||
unionList.push(pl);
|
||
}
|
||
//特殊的分离
|
||
if (intersectionList.length === 0 && unionList.length === (sourceCus.length + targetCus.length)) {
|
||
return { intersectionList, unionList: [sourceOutline, targetOutline] };
|
||
}
|
||
}
|
||
return { intersectionList, unionList };
|
||
}
|
||
GetSubtractList(target)
|
||
{
|
||
let sourceOutline = this._Curve;
|
||
let targetOutline = target.Curve;
|
||
let isEqualNormal = equalv3(sourceOutline.Normal, targetOutline.Normal, 1e-3);
|
||
let interPts = sourceOutline.IntersectWith2(targetOutline, IntersectOption.OnBothOperands, 1e-3);
|
||
if (interPts.length <= 1) {
|
||
//反包含
|
||
if (fastCurveInCurve2(targetOutline, sourceOutline) || equalCurve(targetOutline, sourceOutline))
|
||
return [];
|
||
//包含
|
||
if (fastCurveInCurve2(sourceOutline, targetOutline))
|
||
return [sourceOutline, targetOutline];
|
||
else //分离
|
||
return [sourceOutline];
|
||
}
|
||
//相交
|
||
let subtractList = [];
|
||
let sourceCus = sourceOutline.GetSplitCurves(interPts.map(r => r.thisParam));
|
||
let targetCus = targetOutline.GetSplitCurves(interPts.map(r => r.argParam));
|
||
for (let pl of sourceCus) {
|
||
let plMidParam = pl.MidParam;
|
||
let plDir = pl.GetFistDeriv(plMidParam).normalize();
|
||
let index = targetCus.findIndex(cu => fastEqualCurve(cu, pl));
|
||
if (index !== -1) {
|
||
let cu = targetCus[index];
|
||
let cuMidParam = cu.MidParam;
|
||
let cuDir = cu.GetFistDeriv(cuMidParam).normalize();
|
||
if (isEqualNormal === !equalv3(cuDir, plDir, 1e-3)) //不同向
|
||
subtractList.push(pl);
|
||
targetCus.splice(index, 1);
|
||
continue;
|
||
}
|
||
if (!fastCurveInCurve(targetOutline, pl))
|
||
subtractList.push(pl);
|
||
}
|
||
//源对象没有被破坏
|
||
let sourceNotBreak = subtractList.length === sourceCus.length;
|
||
for (let pl of targetCus)
|
||
if (fastCurveInCurve(sourceOutline, pl))
|
||
subtractList.push(pl);
|
||
if (sourceNotBreak && subtractList.length === sourceCus.length)
|
||
return [sourceOutline];
|
||
return subtractList;
|
||
}
|
||
GetSubtractListByMoreTargets(targets)
|
||
{
|
||
let { holes, subtractList } = this.GetSubListWithCus(targets);
|
||
//纯网洞
|
||
if (subtractList.every(c => c.IsClose))
|
||
return {
|
||
holes: holes.map(h => Contour.CreateContour(h)),
|
||
outlines: Contour.GetAllContour(subtractList)
|
||
};
|
||
let regParse = new RegionParse(subtractList, 2);
|
||
let contours = [];
|
||
//分析封闭包围区域
|
||
const parseRoute = (routeSet) =>
|
||
{
|
||
for (let routes of routeSet) {
|
||
let cs = routes.map(r => r.curve);
|
||
let c = Contour.CreateContour(cs, false);
|
||
if (c
|
||
&& !equalCurve(c.Curve, this.Curve)
|
||
&& targets.every(target => !equalCurve(c.Curve, target.Curve))
|
||
&& c.Area > 1e-3)
|
||
contours.push(c);
|
||
}
|
||
};
|
||
parseRoute(regParse.RegionsOutline);
|
||
parseRoute(regParse.RegionsInternal);
|
||
return {
|
||
holes: holes.map(h => Contour.CreateContour(h)),
|
||
outlines: contours
|
||
};
|
||
}
|
||
GetSubListWithCus(targets)
|
||
{
|
||
let sourceOutline = this._Curve;
|
||
let subtractList = [];
|
||
let holes = [];
|
||
let intPars = [];
|
||
let cuMap = new Map();
|
||
let outBox = sourceOutline.BoundingBox;
|
||
for (let con of targets) {
|
||
const targetOutline = con.Curve;
|
||
if (!IntersectBox2(outBox, targetOutline.BoundingBox))
|
||
continue;
|
||
let pts = sourceOutline.IntersectWith2(con.Curve, IntersectOption.OnBothOperands, 1e-3);
|
||
if (pts.length <= 1) {
|
||
//反包含
|
||
if (fastCurveInCurve2(targetOutline, sourceOutline) || equalCurve(targetOutline, sourceOutline))
|
||
return { holes, subtractList };
|
||
//包含
|
||
if (fastCurveInCurve2(sourceOutline, targetOutline))
|
||
holes.push(targetOutline);
|
||
}
|
||
else {
|
||
intPars.push(...pts.map(r => r.thisParam));
|
||
cuMap.set(targetOutline, pts.map(r => r.argParam));
|
||
}
|
||
}
|
||
intPars.sort((a, b) => a - b);
|
||
arrayRemoveDuplicateBySort(intPars, (e1, e2) => equaln(e1, e2, 1e-8));
|
||
let sourceCus = sourceOutline.GetSplitCurves(intPars);
|
||
let targetCus = [];
|
||
let targetMap = new WeakMap();
|
||
let isEqualNormal;
|
||
for (let [c, pars] of cuMap) {
|
||
let cus = c.GetSplitCurves(pars);
|
||
cus.forEach(cu => targetMap.set(cu, c));
|
||
targetCus.push(...cus);
|
||
}
|
||
for (let pl of sourceCus) {
|
||
let plMidParam = pl.MidParam;
|
||
let plDir = pl.GetFistDeriv(plMidParam).normalize();
|
||
let index = targetCus.findIndex(cu => fastEqualCurve(cu, pl, 0.05));
|
||
if (index !== -1) {
|
||
let cu = targetCus[index];
|
||
isEqualNormal = equalv3(sourceOutline.Normal, targetMap.get(cu).Normal, 1e-3);
|
||
let cuMidParam = cu.MidParam;
|
||
let cuDir = cu.GetFistDeriv(cuMidParam).normalize();
|
||
if (isEqualNormal === !equalv3(cuDir, plDir, 1e-3)) //不同向
|
||
subtractList.push(pl);
|
||
targetCus.splice(index, 1);
|
||
continue;
|
||
}
|
||
if (targets.every(t => !fastCurveInCurve(t.Curve, pl)))
|
||
subtractList.push(pl);
|
||
}
|
||
//源对象没有被破坏
|
||
let sourceNotBreak = subtractList.length === sourceCus.length;
|
||
for (let pl of targetCus)
|
||
if (fastCurveInCurve(sourceOutline, pl))
|
||
subtractList.push(pl);
|
||
if (sourceNotBreak && subtractList.length === sourceCus.length)
|
||
return { subtractList: [sourceOutline], holes };
|
||
return { subtractList, holes };
|
||
}
|
||
/**
|
||
* 获得全部闭合曲线
|
||
* @若传入二维曲线数据,将默认子数组为闭合曲线段
|
||
*/
|
||
static GetAllContour(cus)
|
||
{
|
||
if (cus.length === 0)
|
||
return [];
|
||
let cuGroups;
|
||
if (Array.isArray(cus[0]))
|
||
cuGroups = cus;
|
||
else
|
||
cuGroups = curveLinkGroup(cus);
|
||
let contours = [];
|
||
for (let g of cuGroups)
|
||
contours.push(Contour.CreateContour(g, false));
|
||
return contours.filter(c => c !== undefined && !equaln(c.Area, 0, 1e-6));
|
||
}
|
||
/**
|
||
* 合并曲线组成为多段线
|
||
* @param cus 曲线组
|
||
* @param [needLink=true] 需要解析成首尾连接状态
|
||
* @returns 单一曲线,如果返回超过1个,其他的将被遗弃.
|
||
*/
|
||
static Combine(cus, needLink = true, tolerance = 1e-3)
|
||
{
|
||
if (cus.length === 0)
|
||
return undefined;
|
||
let groups = needLink ? curveLinkGroup(cus) : [cus];
|
||
for (let g of groups) {
|
||
if (g.length === 1)
|
||
return g[0].Clone();
|
||
else {
|
||
if (cache.has(g))
|
||
return cache.get(g);
|
||
let gclone = g.map(c => c.Clone());
|
||
arrayRemoveDuplicateBySort(gclone, (cu1, cu2) => cu1.Join(cu2, false, tolerance) === Status.True);
|
||
if (gclone.length > 1 && gclone[0].Join(arrayLast(gclone), false, tolerance))
|
||
gclone.pop();
|
||
let pl = Polyline.Combine(gclone, tolerance);
|
||
cache.set(g, pl);
|
||
return pl;
|
||
}
|
||
}
|
||
}
|
||
get Shape()
|
||
{
|
||
return this._Curve.Shape;
|
||
}
|
||
CuInOutline(targetCur)
|
||
{
|
||
return isTargetCurInOrOnSourceCur(this._Curve, targetCur);
|
||
}
|
||
Equal(tar)
|
||
{
|
||
return equalCurve(this._Curve, tar._Curve);
|
||
}
|
||
}
|
||
/**
|
||
* 对于轮廓切割后的曲线判断相同,使用这个函数进行快速判断
|
||
*/
|
||
function fastEqualCurve(c1, c2, tolerance = 1e-3)
|
||
{
|
||
let sp1 = c1.StartPoint;
|
||
let ep1 = c1.EndPoint;
|
||
let sp2 = c2.StartPoint;
|
||
let ep2 = c2.EndPoint;
|
||
if (!((equalv3(sp1, sp2, tolerance) && equalv3(ep1, ep2, tolerance))
|
||
|| (equalv3(sp1, ep2, tolerance) && equalv3(ep1, sp2, tolerance))))
|
||
return false;
|
||
return equalv3(c1.Midpoint, c2.Midpoint, tolerance);
|
||
}
|
||
//对于双多段线互相切割后的结果,快速判断曲线是否在另一条曲线内部
|
||
function fastCurveInCurve(sourceCu, targetCu)
|
||
{
|
||
return sourceCu.PtInCurve(targetCu.GetPointAtParam(targetCu.EndParam * 0.5));
|
||
}
|
||
function fastCurveInCurve2(sourceCu, targetCu)
|
||
{
|
||
return sourceCu.PtInCurve(targetCu.StartPoint) &&
|
||
sourceCu.PtInCurve(targetCu.GetPointAtParam(targetCu.EndParam * 0.5));
|
||
}
|
||
|
||
/**
|
||
* 根据盒子x排序盒子
|
||
* @param {EBox[]} arr
|
||
*/
|
||
function SortEntityByBox(arr, sort = true)
|
||
{
|
||
let boxMap = new Map();
|
||
arr.forEach(e => boxMap.set(e, e.BoundingBox));
|
||
if (sort)
|
||
arr.sort((e1, e2) =>
|
||
{
|
||
let b1 = boxMap.get(e1);
|
||
let b2 = boxMap.get(e2);
|
||
if (!equaln(b1.min.x, b2.min.x))
|
||
return b1.min.x - b2.min.x;
|
||
else {
|
||
return b2.min.y - b1.min.y;
|
||
}
|
||
});
|
||
return boxMap;
|
||
}
|
||
|
||
class CurveTreeNode
|
||
{
|
||
constructor(curve, box)
|
||
{
|
||
this.curve = curve;
|
||
this.box = box || curve.BoundingBox;
|
||
}
|
||
TrimBy(contour, box)
|
||
{
|
||
if (IntersectsBox(box, this.box)) {
|
||
if (this.children !== undefined) {
|
||
for (let c of this.children)
|
||
c.TrimBy(contour, box);
|
||
}
|
||
else {
|
||
if (contour.Curve instanceof Circle && this.curve instanceof Arc) {
|
||
if (equalv3(contour.Curve.Center, this.curve.Center)) {
|
||
if (contour.Curve.Radius > this.curve.Radius + 1e-4)
|
||
this.children = [];
|
||
return;
|
||
}
|
||
}
|
||
//交点参数列表
|
||
let iParams = this.curve.IntersectWith(contour.Curve, IntersectOption.OnBothOperands)
|
||
.map(p => this.curve.GetParamAtPoint2(p));
|
||
let cus = this.curve.GetSplitCurves(iParams);
|
||
if (cus.length === 0) {
|
||
let p = this.curve.GetPointAtParam(0.5);
|
||
if ((contour.Curve.PtInCurve(p) && !contour.Curve.PtOnCurve(p)))
|
||
this.children = [];
|
||
}
|
||
else {
|
||
this.children = [];
|
||
for (let c of cus) {
|
||
let p = c.GetPointAtParam(0.5);
|
||
if (c.Length > 1e-5 && (!contour.Curve.PtInCurve(p) || contour.Curve.PtOnCurve(p)))
|
||
this.children.push(new CurveTreeNode(c));
|
||
}
|
||
if (this.children.length === cus.length)
|
||
this.children = undefined;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
get Nodes()
|
||
{
|
||
if (!this.children)
|
||
return [this];
|
||
else {
|
||
let cus = [];
|
||
for (let c of this.children)
|
||
cus.push(...c.Nodes);
|
||
return cus;
|
||
}
|
||
}
|
||
}
|
||
class OffsetPolyline
|
||
{
|
||
constructor(_Polyline, _OffsetDist, _ToolPath = false, _OffsetDistSq = (_OffsetDist ** 2) * 2.1 //对直角走刀不进行圆弧过度
|
||
)
|
||
{
|
||
this._Polyline = _Polyline;
|
||
this._OffsetDist = _OffsetDist;
|
||
this._ToolPath = _ToolPath;
|
||
this._OffsetDistSq = _OffsetDistSq;
|
||
}
|
||
Do()
|
||
{
|
||
this._OffsetDistSign = Math.sign(this._OffsetDist);
|
||
this._TrimPolylineContours = [];
|
||
this._TrimCircleContours = [];
|
||
this._TrimArcContours = [];
|
||
this._RetCurves = [];
|
||
this._CurveTreeNodes = [];
|
||
this.InitSubCurves();
|
||
if (this._SubCurves.length === 0)
|
||
return this._RetCurves;
|
||
this.GeneralCirclesAndVertexs();
|
||
this.OffsetSubCurves();
|
||
this.LinkSubCurves();
|
||
if (this._SubOffsetedCurves.length === 0) {
|
||
this._SubOffsetedCurves.push({ curve: this._Circles[0], index: 0, paddingCurve: this._Circles.slice(1) });
|
||
this._TrimPolylineContours.push(...this._Circles.map(c => Contour.CreateContour(c, false)), ...this._SubCurves.map(c => Contour.CreateContour([c, new Line(c.StartPoint, c.EndPoint)], false)));
|
||
}
|
||
else
|
||
this.GeneralTrimContours();
|
||
this.TrimByContours();
|
||
this.FilterInvalidCurve();
|
||
this.JoinCollinear();
|
||
this.LinkResultPolyline();
|
||
return this._RetCurves;
|
||
}
|
||
InitSubCurves()
|
||
{
|
||
this._CacheOCS = this._Polyline.OCS;
|
||
this._IsClose = this._Polyline.IsClose;
|
||
this._Polyline.OCS = IdentityMtx4;
|
||
this._SubCurves = this._Polyline.Explode().filter(c => c.Length > 1e-4);
|
||
this._Polyline.OCS = this._CacheOCS;
|
||
return this;
|
||
}
|
||
GeneralCirclesAndVertexs()
|
||
{
|
||
this._Vertexs = this._SubCurves.map(c => c.StartPoint);
|
||
let lastCu = arrayLast(this._SubCurves);
|
||
if (!equalv3(lastCu.EndPoint, this._Vertexs[0], 1e-3))
|
||
this._Vertexs.push(lastCu.EndPoint);
|
||
let radius = Math.abs(this._OffsetDist);
|
||
this._Circles = this._Vertexs.map(p => new Circle(p, radius));
|
||
}
|
||
OffsetSubCurves()
|
||
{
|
||
this._SubOffsetedCurves = [];
|
||
for (let index = 0; index < this._SubCurves.length; index++) {
|
||
let curveOld = this._SubCurves[index];
|
||
if (curveOld.Length > 1e-6) {
|
||
let curve = curveOld.GetOffsetCurves(this._OffsetDist)[0];
|
||
if (curve)
|
||
this._SubOffsetedCurves.push({ curve, index });
|
||
else
|
||
this._TrimArcContours.push(Contour.CreateContour([curveOld, new Line(curveOld.StartPoint, curveOld.EndPoint)], false));
|
||
}
|
||
}
|
||
}
|
||
//连接(延伸)曲线,或者补(圆弧,直线)
|
||
LinkSubCurves()
|
||
{
|
||
let count = this._SubOffsetedCurves.length;
|
||
if (!this._IsClose)
|
||
count--;
|
||
for (let i = 0; i < count; i++) {
|
||
let curveResNow = this._SubOffsetedCurves[i];
|
||
let iNext = FixIndex(i + 1, this._SubOffsetedCurves);
|
||
let curveResNext = this._SubOffsetedCurves[iNext];
|
||
let curveNow = curveResNow.curve;
|
||
let curveNext = curveResNext.curve;
|
||
let isNeighbor = FixIndex(curveResNow.index + 1, this._SubCurves) === curveResNext.index;
|
||
if (isNeighbor) {
|
||
let sp = curveNow.EndPoint;
|
||
let ep = curveNext.StartPoint;
|
||
//直连
|
||
if (equalv3(sp, ep, 1e-3))
|
||
continue;
|
||
let iPts = curveNow.IntersectWith(curveNext, IntersectOption.ExtendBoth);
|
||
let tPts = iPts.filter(p => curveNow.PtOnCurve3(p) && curveNext.PtOnCurve3(p));
|
||
let code = EntityEncode2(curveNow, curveNext);
|
||
let tp;
|
||
if (code === 1) {
|
||
if (tPts.length > 0) //不走刀或者有真交点 this._ToolPath === false ||
|
||
tp = iPts[0];
|
||
else {
|
||
if (iPts.length > 0 && curveNow.GetParamAtPoint(iPts[0]) > 1) {
|
||
let refP = this._Vertexs[curveResNext.index];
|
||
let distSq = iPts[0].distanceToSquared(refP);
|
||
if (this._ToolPath && distSq > this._OffsetDistSq) {
|
||
curveResNow.paddingCurve = [this.CreateArc(refP, sp, ep)];
|
||
this._TrimCircleContours.push(this._Circles[curveResNext.index]);
|
||
}
|
||
else
|
||
tp = iPts[0];
|
||
}
|
||
// else
|
||
// curveResNow.paddingCurve = [new Line(sp, ep)];
|
||
}
|
||
}
|
||
else {
|
||
let refP = this._Vertexs[curveResNext.index];
|
||
if (tPts.length > 0) //ipts = 1 or ipts = 2
|
||
tp = SelectNearP(iPts, refP);
|
||
else //补单圆 或者尝试连接
|
||
{
|
||
let arc = this.CreateArc(refP, sp, ep);
|
||
if (iPts.length > 0 && !this._ToolPath && this.IsSharpCorner(curveResNow, curveResNext, refP)) {
|
||
//设置新的连接点,并且备份旧点
|
||
let oldp;
|
||
if (curveResNow.sp) {
|
||
oldp = curveNow.StartPoint;
|
||
curveNow.StartPoint = curveResNow.sp;
|
||
}
|
||
let oldp2;
|
||
if (curveResNext.ep) {
|
||
oldp2 = curveNext.EndPoint;
|
||
curveNext.EndPoint = curveResNext.ep;
|
||
}
|
||
let p;
|
||
if (code === 2 && iPts.length === 2) {
|
||
let c = curveNow;
|
||
let minArc = new Arc(c.Center, c.Radius, c.EndAngle, 0, c.IsClockWise);
|
||
let p1 = iPts[0];
|
||
let a1 = minArc.GetAngleAtPoint(p1);
|
||
let anAll1 = c.ParamOnCurve(c.GetParamAtAngle(a1)) ? Infinity : minArc.ComputeAnlge(a1);
|
||
let p2 = iPts[1];
|
||
let a2 = minArc.GetAngleAtPoint(p2);
|
||
let anAll2 = c.ParamOnCurve(c.GetParamAtAngle(a2)) ? Infinity : minArc.ComputeAnlge(a2);
|
||
if (anAll2 < anAll1)
|
||
p = p2;
|
||
else
|
||
p = p1;
|
||
}
|
||
else
|
||
p = SelectNearP(iPts, refP);
|
||
let onPre;
|
||
let param = curveNow.GetParamAtPoint2(p);
|
||
if (curveNow instanceof Line)
|
||
onPre = param > 1;
|
||
else
|
||
onPre = param < 0 || param > 1;
|
||
let onNext = false;
|
||
if (onPre) {
|
||
let param2 = curveNext.GetParamAtPoint2(p);
|
||
if (curveNext instanceof Line)
|
||
onNext = param2 < 0;
|
||
else
|
||
onNext = param2 < 0 || param2 > 1;
|
||
}
|
||
if (curveResNow.sp)
|
||
curveNow.StartPoint = oldp;
|
||
if (curveResNext.ep)
|
||
curveNext.EndPoint = oldp2;
|
||
if (onPre && onNext)
|
||
tp = p;
|
||
else
|
||
curveResNow.paddingCurve = [arc];
|
||
}
|
||
else
|
||
curveResNow.paddingCurve = [arc];
|
||
this._TrimCircleContours.push(this._Circles[curveResNext.index]);
|
||
}
|
||
}
|
||
if (tp) {
|
||
curveResNow.ep = tp;
|
||
curveResNext.sp = tp;
|
||
curveResNow.nextArc = curveNext;
|
||
curveResNext.preArc = curveNow;
|
||
}
|
||
}
|
||
else {
|
||
let padCirs = [];
|
||
for (let s = FixIndex(curveResNow.index + 1, this._Circles); ; s = FixIndex(s + 1, this._Circles)) {
|
||
let c = this._Circles[s];
|
||
this._TrimCircleContours.push(c);
|
||
padCirs.push(c);
|
||
if (s === curveResNext.index)
|
||
break;
|
||
}
|
||
curveResNow.paddingCurve = padCirs;
|
||
}
|
||
}
|
||
}
|
||
IsSharpCorner(curveResNow, curveResNext, refP)
|
||
{
|
||
let v1 = this._SubCurves[curveResNow.index].GetPointAtParam(0.9);
|
||
let v2 = this._SubCurves[curveResNext.index].GetPointAtParam(0.1);
|
||
v1.subVectors(refP, v1);
|
||
v2.sub(refP);
|
||
v1.cross(v2);
|
||
return Math.sign(v1.z) === this._OffsetDistSign;
|
||
}
|
||
GeneralTrimContours()
|
||
{
|
||
for (let d of this._SubOffsetedCurves) {
|
||
let cu2 = d.curve;
|
||
if (d.sp && d.ep) {
|
||
let param1 = cu2.GetParamAtPoint(d.sp);
|
||
let param2 = cu2.GetParamAtPoint(d.ep);
|
||
if (cu2.ParamOnCurve(param1) && cu2.ParamOnCurve(param2) && param1 > param2)
|
||
[d.sp, d.ep] = [d.ep, d.sp];
|
||
}
|
||
if (d.sp)
|
||
cu2.StartPoint = d.sp;
|
||
if (d.ep)
|
||
cu2.EndPoint = d.ep;
|
||
}
|
||
for (let d of this._SubOffsetedCurves) {
|
||
let cu1 = this._SubCurves[d.index];
|
||
let cu2 = d.curve;
|
||
let [p1, p2, p3, p4] = [cu1.StartPoint, cu2.StartPoint, cu1.EndPoint, cu2.EndPoint];
|
||
let l1 = new Line(p1, p2);
|
||
let l2 = new Line(p3, p4);
|
||
let ipts = l1.IntersectWith(l2, IntersectOption.OnBothOperands, 1e-8);
|
||
if (ipts.length > 0) {
|
||
let p = ipts[0];
|
||
l1.EndPoint = p;
|
||
l2.EndPoint = p;
|
||
let cus = [cu1, l1, l2];
|
||
let contour = Contour.CreateContour(cus, false);
|
||
if (contour) {
|
||
this._TrimPolylineContours.push(contour);
|
||
continue;
|
||
}
|
||
else {
|
||
console.error("未预料到的错误,构建轮廓失败" + this._OffsetDist);
|
||
}
|
||
}
|
||
//真理1:针脚线不可能同时被两个圆弧所切割
|
||
let l1Intact = true;
|
||
let l2Intact = true;
|
||
if (cu2 instanceof Arc) {
|
||
if (Math.sign(cu2.Bul) !== this._OffsetDistSign) {
|
||
let ipts1 = cu2.IntersectWith(l1, IntersectOption.OnBothOperands);
|
||
let ipts2 = cu2.IntersectWith(l2, IntersectOption.OnBothOperands);
|
||
let sp;
|
||
let ep;
|
||
if (ipts1.length === 2)
|
||
sp = SelectNearP(ipts1, p1);
|
||
if (ipts2.length === 2)
|
||
ep = SelectNearP(ipts2, p3);
|
||
if (sp || ep)
|
||
cu2 = cu2.Clone();
|
||
if (sp) {
|
||
l1.EndPoint = sp;
|
||
cu2.StartPoint = sp;
|
||
l1Intact = false;
|
||
}
|
||
if (ep) {
|
||
l2.EndPoint = ep;
|
||
cu2.EndPoint = ep;
|
||
l2Intact = false;
|
||
}
|
||
}
|
||
}
|
||
let l1PadArc;
|
||
let l2PadArc;
|
||
//真理2:隔壁的圆弧不可能破坏当前的圆弧,只能破坏当前的针脚
|
||
if (l1Intact && d.preArc && d.preArc instanceof Arc) {
|
||
let a = d.preArc;
|
||
if (Math.sign(a.Bul) !== this._OffsetDistSign) {
|
||
let ipts = a.IntersectWith(l1, IntersectOption.OnBothOperands);
|
||
if (ipts.length === 2) {
|
||
let sp = SelectNearP(ipts, p1);
|
||
l1.EndPoint = sp;
|
||
l1PadArc = a.Clone();
|
||
l1PadArc.StartPoint = sp;
|
||
}
|
||
}
|
||
}
|
||
if (l2Intact && d.nextArc && d.nextArc instanceof Arc) {
|
||
let a = d.nextArc;
|
||
if (Math.sign(a.Bul) !== this._OffsetDistSign) {
|
||
let ipts = a.IntersectWith(l2, IntersectOption.OnBothOperands);
|
||
if (ipts.length === 2) {
|
||
let ep = SelectNearP(ipts, p3);
|
||
l2.EndPoint = ep;
|
||
l2PadArc = a.Clone();
|
||
l2PadArc.EndPoint = ep;
|
||
}
|
||
}
|
||
}
|
||
let pl = new Polyline();
|
||
let cus = [cu1, l1];
|
||
if (l1PadArc)
|
||
cus.push(l1PadArc);
|
||
cus.push(cu2, l2);
|
||
if (l2PadArc)
|
||
cus.push(l2PadArc);
|
||
for (let c of cus)
|
||
pl.Join(c);
|
||
let contour = Contour.CreateContour(pl, false);
|
||
if (contour)
|
||
this._TrimPolylineContours.push(contour);
|
||
else
|
||
console.error("未预料到的错误,构建轮廓失败" + this._OffsetDist);
|
||
}
|
||
if (!this._IsClose) {
|
||
if (this._TrimCircleContours[0] !== this._Circles[0])
|
||
this._TrimCircleContours.push(this._Circles[0]);
|
||
let lastTrimCircle = arrayLast(this._TrimCircleContours);
|
||
let lastCircle = arrayLast(this._Circles);
|
||
if (lastTrimCircle !== lastCircle)
|
||
this._TrimCircleContours.push(lastCircle);
|
||
if (this._SubOffsetedCurves[0].index !== 0)
|
||
this._TrimCircleContours.push(this._Circles[this._SubOffsetedCurves[0].index]);
|
||
let lastIndex = this._Circles.length - 1;
|
||
let lastD = arrayLast(this._SubOffsetedCurves);
|
||
if (lastIndex !== lastD.index)
|
||
this._TrimCircleContours.push(this._Circles[lastD.index + 1]);
|
||
}
|
||
this._TrimPolylineContours.push(...this._TrimCircleContours.map(c => Contour.CreateContour(c, false)), ...this._TrimArcContours);
|
||
}
|
||
// 通过构建的轮廓对偏移曲线进行裁剪
|
||
TrimByContours()
|
||
{
|
||
for (let d of this._SubOffsetedCurves) {
|
||
let c = d.curve;
|
||
this._CurveTreeNodes.push(new CurveTreeNode(c));
|
||
if (d.paddingCurve)
|
||
this._CurveTreeNodes.push(...d.paddingCurve.map(c => new CurveTreeNode(c)));
|
||
}
|
||
let boxContours = SortEntityByBox(this._TrimPolylineContours, false);
|
||
for (let i = 0; i < this._TrimPolylineContours.length; i++) {
|
||
let c = this._TrimPolylineContours[i];
|
||
for (let curveNode of this._CurveTreeNodes) {
|
||
curveNode.TrimBy(c, boxContours.get(c));
|
||
}
|
||
}
|
||
}
|
||
//过滤方向相反和0长度线
|
||
FilterInvalidCurve()
|
||
{
|
||
this._CurveTrimedTreeNodes = [];
|
||
for (let n of this._CurveTreeNodes) {
|
||
let ns = n.Nodes;
|
||
for (let sn of ns) {
|
||
let p = sn.curve.GetPointAtParam(0.5);
|
||
if (sn.curve.Length > 1e-5 && this.CheckPointDir(p))
|
||
this._CurveTrimedTreeNodes.push(sn);
|
||
}
|
||
}
|
||
}
|
||
//合并共线
|
||
JoinCollinear()
|
||
{
|
||
for (let i = 0; i < this._CurveTrimedTreeNodes.length; i++) {
|
||
let n = this._CurveTrimedTreeNodes[i];
|
||
if (n.used)
|
||
continue;
|
||
let sp = n.curve.StartPoint;
|
||
for (let j = i + 1; j < this._CurveTrimedTreeNodes.length; j++) {
|
||
let n2 = this._CurveTrimedTreeNodes[j];
|
||
if (n2.used)
|
||
continue;
|
||
let status = n.curve.Join(n2.curve);
|
||
if (status === Status.ConverToCircle) {
|
||
n.used = true;
|
||
n2.used = true;
|
||
let circle = new Circle(n.curve.Center, n.curve.Radius);
|
||
n.curve = circle;
|
||
this._RetCurves.push(ConverCircleToPolyline(circle).ApplyMatrix(this._CacheOCS));
|
||
}
|
||
else if (status === Status.True) {
|
||
if (equalv3(sp, n.curve.StartPoint))
|
||
n2.used = true;
|
||
else {
|
||
n.used = true;
|
||
n2.curve = n.curve;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
//连接结果曲线,返回最终多段线
|
||
LinkResultPolyline()
|
||
{
|
||
let used = new Set();
|
||
let cuMap = new CurveMap(1);
|
||
for (let n of this._CurveTrimedTreeNodes) {
|
||
if (!n.used)
|
||
cuMap.AddCurveToMap(n.curve);
|
||
}
|
||
let preP;
|
||
let searchNext = (s, pl) =>
|
||
{
|
||
let minDist = Infinity;
|
||
let minR;
|
||
for (let r of s.routes) {
|
||
if (used.has(r.curve))
|
||
continue;
|
||
if (preP) {
|
||
let d = r.s.distanceToSquared(preP);
|
||
if (d < minDist) {
|
||
minR = r;
|
||
minDist = d;
|
||
}
|
||
}
|
||
else {
|
||
minR = r;
|
||
break;
|
||
}
|
||
}
|
||
if (minR) {
|
||
used.add(minR.curve);
|
||
preP = minR.e;
|
||
let status = pl.Join(minR.curve, false, 5e-2);
|
||
if (status !== Status.True)
|
||
console.warn("连接失败");
|
||
return minR.to;
|
||
}
|
||
};
|
||
for (let s of cuMap.Stands) {
|
||
preP = undefined;
|
||
let pl = new Polyline();
|
||
let ss = s;
|
||
while (ss && !pl.IsClose)
|
||
ss = searchNext(ss, pl);
|
||
ss = s;
|
||
while (ss && !pl.IsClose)
|
||
ss = searchNext(ss, pl);
|
||
if (pl.NumberOfVertices > 0) {
|
||
let d = pl.LineData;
|
||
let ld = arrayLast(d);
|
||
if (equalv2(d[0].pt, ld.pt, 1e-2))
|
||
ld.pt.copy(d[0].pt);
|
||
this._RetCurves.push(pl.ApplyMatrix(this._CacheOCS));
|
||
}
|
||
}
|
||
}
|
||
CheckPointDir(pt)
|
||
{
|
||
return this.GetPointAtCurveDir(pt) === this._OffsetDistSign;
|
||
}
|
||
GetPointAtCurveDir(pt)
|
||
{
|
||
let minIndex = Infinity;
|
||
let minDist = Infinity;
|
||
let minCp;
|
||
for (let i = 0; i < this._SubCurves.length; i++) {
|
||
let c = this._SubCurves[i];
|
||
let cp = c.GetClosestPointTo(pt, false);
|
||
if (equalv3(cp, pt, 1e-5))
|
||
return 0;
|
||
let dist = cp.distanceToSquared(pt);
|
||
if (dist < minDist) {
|
||
minDist = dist;
|
||
minIndex = i;
|
||
minCp = cp;
|
||
}
|
||
}
|
||
let c = this._SubCurves[minIndex];
|
||
let param = c.GetParamAtPoint(minCp);
|
||
if (equaln(param, 0) && ((minIndex === 0) ? this._IsClose : true)) {
|
||
let preIndex = FixIndex(minIndex - 1, this._SubCurves);
|
||
let preCurve = this._SubCurves[preIndex];
|
||
if (!equalv3(c.GetFistDeriv(0).normalize(), preCurve.GetFistDeriv(1).normalize())) {
|
||
let p = c.StartPoint;
|
||
let l1 = c.Length;
|
||
let l2 = preCurve.Length;
|
||
let minLength = Math.min(l1, l2) * 0.2;
|
||
let nextP;
|
||
let preP;
|
||
if (c instanceof Arc)
|
||
nextP = c.GetPointAtDistance(minLength);
|
||
else
|
||
nextP = c.EndPoint;
|
||
if (preCurve instanceof Arc)
|
||
preP = preCurve.GetPointAtDistance(l2 - minLength);
|
||
else
|
||
preP = preCurve.StartPoint;
|
||
let arc = new Arc(p, 1, angle(preP.sub(p)), angle(nextP.sub(p)));
|
||
let dir = arc.PtOnCurve3(pt) ? -1 : 1;
|
||
return dir;
|
||
}
|
||
}
|
||
else if (equaln(param, 1) && ((minIndex === this._SubCurves.length - 1) ? this._IsClose : true)) {
|
||
let nextIndex = FixIndex(minIndex + 1, this._SubCurves);
|
||
let nextCurve = this._SubCurves[nextIndex];
|
||
if (!equalv3(c.GetFistDeriv(1).normalize(), nextCurve.GetFistDeriv(0).normalize())) {
|
||
let p = c.EndPoint;
|
||
let l1 = c.Length;
|
||
let l2 = nextCurve.Length;
|
||
let minLength = Math.min(l1, l2) * 0.2;
|
||
let nextP;
|
||
let preP;
|
||
if (c instanceof Arc)
|
||
preP = c.GetPointAtDistance(l1 - minLength);
|
||
else
|
||
preP = c.StartPoint;
|
||
if (nextCurve instanceof Arc)
|
||
nextP = nextCurve.GetPointAtDistance(minLength);
|
||
else
|
||
nextP = nextCurve.EndPoint;
|
||
let arc = new Arc(p, 1, angle(preP.sub(p)), angle(nextP.sub(p)));
|
||
let dir = arc.PtOnCurve3(pt) ? -1 : 1;
|
||
return dir;
|
||
}
|
||
}
|
||
let dri = c.GetFistDeriv(param);
|
||
let cross = dri.cross(pt.clone().sub(minCp));
|
||
return -Math.sign(cross.z);
|
||
}
|
||
CreateArc(center, startP, endP)
|
||
{
|
||
let sa = angle(startP.clone().sub(center));
|
||
let ea = endP ? angle(endP.clone().sub(center)) : sa;
|
||
let arc = new Arc(center, Math.abs(this._OffsetDist), sa, ea, this._OffsetDist < 0);
|
||
return arc;
|
||
}
|
||
}
|
||
function EntityEncode(c)
|
||
{
|
||
if (c instanceof Line)
|
||
return 1;
|
||
else
|
||
return 2;
|
||
}
|
||
function EntityEncode2(c1, c2)
|
||
{
|
||
return EntityEncode(c1) & EntityEncode(c2);
|
||
}
|
||
|
||
//3点获取圆心
|
||
function getCircleCenter(pt1, pt2, pt3)
|
||
{
|
||
if (!(pt1 && pt2 && pt3))
|
||
return;
|
||
let A1 = pt1.x - pt2.x;
|
||
let B1 = pt1.y - pt2.y;
|
||
let C1 = (Math.pow(pt1.x, 2) - Math.pow(pt2.x, 2) + Math.pow(pt1.y, 2) - Math.pow(pt2.y, 2)) / 2;
|
||
let A2 = pt3.x - pt2.x;
|
||
let B2 = pt3.y - pt2.y;
|
||
let C2 = (Math.pow(pt3.x, 2) - Math.pow(pt2.x, 2) + Math.pow(pt3.y, 2) - Math.pow(pt2.y, 2)) / 2;
|
||
//令temp = A1*B2 - A2*B1
|
||
let temp = A1 * B2 - A2 * B1;
|
||
let center = new Vector3();
|
||
//判断三点是否共线
|
||
if (temp === 0) {
|
||
//共线则将第一个点pt1作为圆心
|
||
center.x = pt1.x;
|
||
center.y = pt1.y;
|
||
}
|
||
else {
|
||
//不共线则求出圆心:
|
||
center.x = (C1 * B2 - C2 * B1) / temp;
|
||
center.y = (A1 * C2 - A2 * C1) / temp;
|
||
}
|
||
return center;
|
||
}
|
||
//行列式
|
||
function getDeterminantFor2V(v1, v2)
|
||
{
|
||
return v1.x * v2.y - v1.y * v2.x;
|
||
}
|
||
/**
|
||
* 曲线根据连接来分组,每组都是一条首尾相连的曲线表.
|
||
*
|
||
* @export
|
||
* @param {Curve[]} cus 传入的分组的曲线表
|
||
* @returns {Array<Array<Curve>>} 返回如下
|
||
* [
|
||
* [c1,c2,c3...],//后面的曲线的起点总是等于上一个曲线的终点
|
||
* [c1,c2,c3...],
|
||
* ]
|
||
*/
|
||
function curveLinkGroup(cus)
|
||
{
|
||
//返回的曲线组
|
||
let groupCus = new Array();
|
||
//将封闭的曲线先提取出来
|
||
cus = cus.filter(c =>
|
||
{
|
||
let isClose = c.IsClose;
|
||
if (isClose)
|
||
groupCus.push([c]);
|
||
return !isClose;
|
||
});
|
||
if (cus.length === 0)
|
||
return groupCus;
|
||
//曲线节点图
|
||
let cuMap = new CurveMap();
|
||
cus.forEach(c => cuMap.AddCurveToMap(c));
|
||
//曲线站点表
|
||
let stands = cuMap.Stands;
|
||
//曲线使用计数
|
||
let cuCount = new Count();
|
||
/**
|
||
* 从站点的路线中任意取一条,加入到曲线数组中.
|
||
*
|
||
* @param {Curve[]} cus 已经连接的曲线列表
|
||
* @param {boolean} isEndSeach true:从终点搜索,false:从起点搜索
|
||
* @returns {Stand} 如果站点中存在可以取得的曲线,返回下个站点,否则返回undefined
|
||
*/
|
||
function linkCurve(stand, cus, isEndSeach)
|
||
{
|
||
for (let route of stand.routes) {
|
||
let cu = route.curve;
|
||
if (cuCount.GetCount(cu) === 0) {
|
||
if (isEndSeach) {
|
||
//保证曲线总是从起点连接到终点
|
||
if (!equalv3(cu.StartPoint, stand.position))
|
||
cu.Reverse();
|
||
cus.push(cu);
|
||
}
|
||
else {
|
||
//保证曲线总是从起点连接到终点
|
||
if (!equalv3(cu.EndPoint, stand.position))
|
||
cu.Reverse();
|
||
cus.unshift(cu);
|
||
}
|
||
cuCount.AddCount(cu, 1);
|
||
return route.to;
|
||
}
|
||
}
|
||
}
|
||
for (let stand of stands) {
|
||
let startStand = stand;
|
||
let cus = []; //形成合并轮廓的曲线组
|
||
while (startStand)
|
||
startStand = linkCurve(startStand, cus, true);
|
||
if (cus.length > 0) {
|
||
startStand = cuMap.GetOnlyVertice(cus[0].StartPoint);
|
||
while (startStand)
|
||
startStand = linkCurve(startStand, cus, false);
|
||
}
|
||
if (cus.length > 0)
|
||
groupCus.push(cus);
|
||
}
|
||
return groupCus;
|
||
}
|
||
function equalCurve(cu1, cu2, tolerance = 1e-4)
|
||
{
|
||
if ((cu1 instanceof Polyline) && (cu2 instanceof Polyline)) {
|
||
if (cu1.IsClose !== cu2.IsClose || !isParallelTo(cu1.Normal, cu2.Normal))
|
||
return false;
|
||
let area1 = cu1.Area2;
|
||
let area2 = cu2.Area2;
|
||
if (!equaln(Math.abs(area1), Math.abs(area2), 0.1))
|
||
return false;
|
||
let ptsBuls1 = cu1.PtsBuls;
|
||
let ptsBuls2 = cu2.PtsBuls;
|
||
let pts1 = ptsBuls1.pts;
|
||
let pts2 = ptsBuls2.pts;
|
||
let buls1 = ptsBuls1.buls;
|
||
let buls2 = ptsBuls2.buls;
|
||
let isEqualArea = equaln(area1, area2, 0.1);
|
||
if (!equalv3(cu1.Normal, cu2.Normal)) {
|
||
if (isEqualArea) {
|
||
pts2.reverse();
|
||
buls2.reverse();
|
||
buls2.push(buls2.shift());
|
||
}
|
||
else
|
||
buls2 = buls2.map(bul => -bul);
|
||
}
|
||
else if (!isEqualArea) {
|
||
pts2.reverse();
|
||
buls2.reverse();
|
||
buls2 = buls2.map(bul => -bul);
|
||
buls2.push(buls2.shift());
|
||
}
|
||
if (cu1.IsClose && equalv2(pts1[0], arrayLast(pts1), tolerance)) {
|
||
pts1.pop();
|
||
buls1.pop();
|
||
}
|
||
if (cu2.IsClose && equalv2(pts2[0], arrayLast(pts2), tolerance)) {
|
||
pts2.pop();
|
||
buls2.pop();
|
||
}
|
||
let cu1Sp = AsVector2(cu1.StartPoint.applyMatrix4(cu2.OCSInv));
|
||
let index = pts2.findIndex(p => equalv2(cu1Sp, p, tolerance));
|
||
changeArrayStartIndex(buls2, index);
|
||
changeArrayStartIndex(pts2, index);
|
||
return equalArray(buls1, buls2, equaln) &&
|
||
equalArray(pts1, pts2, (p1, p2) => equalv3(AsVector3(p1).applyMatrix4(cu1.OCS), AsVector3(p2).applyMatrix4(cu2.OCS), tolerance));
|
||
}
|
||
else if (cu1 instanceof Circle && cu2 instanceof Circle) {
|
||
return equalv3(cu1.Center, cu2.Center) && equaln(cu1.Radius, cu2.Radius, 1e-6);
|
||
}
|
||
else if (cu1 instanceof Arc && cu2 instanceof Arc) {
|
||
if (!equalv3(cu1.StartPoint, cu2.EndPoint))
|
||
cu1.Reverse();
|
||
return equalv3(cu1.Center, cu2.Center)
|
||
&& equaln(cu1.Radius, cu2.Radius, 1e-6)
|
||
&& equaln(cu1.StartAngle, cu2.StartAngle)
|
||
&& equaln(cu1.EndAngle, cu2.EndAngle);
|
||
}
|
||
else if (cu1 instanceof Ellipse && cu2 instanceof Ellipse) {
|
||
return equalv3(cu1.Center, cu2.Center)
|
||
&& equaln(cu1.RadX, cu2.RadX)
|
||
&& equaln(cu1.RadY, cu2.RadY)
|
||
&& equalv3(cu1.StartPoint, cu2.StartPoint);
|
||
}
|
||
else if (cu1 instanceof Line && cu2 instanceof Line) {
|
||
let ps1 = [cu1.StartPoint, cu1.EndPoint];
|
||
let ps2 = [cu2.StartPoint, cu2.EndPoint];
|
||
return ps1.every(p => ps2.some(p1 => equalv3(p1, p)));
|
||
}
|
||
return false;
|
||
}
|
||
function ConverCircleToPolyline(cir)
|
||
{
|
||
//该写法不支持三维坐标系
|
||
// let pl = new Polyline();
|
||
// let bul = Math.tan(Math.PI * 0.125);
|
||
// for (let i = 0; i < 4; i++)
|
||
// {
|
||
// let p = cir.GetPointAtParam(i * 0.25);
|
||
// pl.AddVertexAt(i, Vec3DTo2D(p));
|
||
// pl.SetBulgeAt(i, bul);
|
||
// }
|
||
// pl.CloseMark = true;
|
||
// return pl;
|
||
let arcs = cir.GetSplitCurves([0, 0.5]);
|
||
let pl = new Polyline();
|
||
pl.OCS = cir.OCS;
|
||
pl.Join(arcs[0]);
|
||
pl.Join(arcs[1]);
|
||
return pl;
|
||
}
|
||
function GetTanPtsOnArcOrCircle(cu, lastPoint)
|
||
{
|
||
if (lastPoint) {
|
||
//ref:wykobi
|
||
let ocsInv = cu.OCSInv;
|
||
let v = lastPoint.clone().applyMatrix4(ocsInv);
|
||
let lengthSq = v.lengthSq();
|
||
let radiusSq = cu.Radius ** 2;
|
||
if (lengthSq >= radiusSq) {
|
||
let ratio = 1 / lengthSq;
|
||
let deltaDist = Math.sqrt(lengthSq - radiusSq);
|
||
let pts = [
|
||
new Vector3(cu.Radius * (cu.Radius * v.x - v.y * deltaDist) * ratio, cu.Radius * (cu.Radius * v.y + v.x * deltaDist) * ratio),
|
||
new Vector3(cu.Radius * (cu.Radius * v.x + v.y * deltaDist) * ratio, cu.Radius * (cu.Radius * v.y - v.x * deltaDist) * ratio),
|
||
];
|
||
for (let p of pts)
|
||
p.applyMatrix4(cu.OCS);
|
||
return pts;
|
||
}
|
||
}
|
||
}
|
||
function getArcOrCirNearPts(cu, pickPoint, viewXform)
|
||
{
|
||
let viewNormal = new Vector3().fromArray(viewXform.elements, 2 * 3);
|
||
let plane = new PlaneExt(cu.Normal, cu.Center);
|
||
let pickLocal = plane.intersectLine(new Line3(pickPoint, pickPoint.clone().add(viewNormal)), new Vector3(), true);
|
||
if (pickLocal) {
|
||
let x = new Vector3().fromArray(viewXform.elements, 0).add(pickLocal);
|
||
let y = new Vector3().fromArray(viewXform.elements, 3).add(pickLocal);
|
||
x = plane.intersectLine(new Line3(x, x.clone().add(viewNormal)), new Vector3(), true);
|
||
y = plane.intersectLine(new Line3(y, y.clone().add(viewNormal)), new Vector3(), true);
|
||
let lx = new Line(pickLocal, x);
|
||
let ly = new Line(pickLocal, y);
|
||
let ins = cu.IntersectWith(lx, IntersectOption.ExtendBoth);
|
||
ins.push(...cu.IntersectWith(ly, IntersectOption.ExtendBoth));
|
||
return ins;
|
||
}
|
||
else {
|
||
let ptLocal = plane.projectPoint(pickPoint, new Vector3());
|
||
let lz = new Line(ptLocal, ptLocal.clone().add(viewNormal));
|
||
return cu.IntersectWith(lz, IntersectOption.ExtendBoth);
|
||
}
|
||
}
|
||
function getTanPtsOnEllipse(cu, lastPoint)
|
||
{
|
||
return [];
|
||
}
|
||
function SwapParam(res)
|
||
{
|
||
for (let r of res) {
|
||
let p = r.thisParam;
|
||
r.thisParam = r.argParam;
|
||
r.argParam = p;
|
||
}
|
||
return res;
|
||
}
|
||
function ComputerCurvesNormalOCS(curves, allowAutoCalc = true)
|
||
{
|
||
if (!curves || curves.length === 0)
|
||
return;
|
||
//准备计算多段线的法向量
|
||
let normal;
|
||
let firstV;
|
||
for (let c of curves) {
|
||
if (c instanceof Arc) {
|
||
normal = c.Normal;
|
||
break;
|
||
}
|
||
else if (firstV) {
|
||
let v = c.GetFistDeriv(0);
|
||
v.cross(firstV);
|
||
if (!equalv3(v, ZeroVec)) {
|
||
normal = v.normalize();
|
||
break;
|
||
}
|
||
}
|
||
else {
|
||
let cus = c.Explode();
|
||
let ocs = ComputerCurvesNormalOCS(cus, false);
|
||
if (ocs)
|
||
return ocs;
|
||
firstV = c.GetFistDeriv(0);
|
||
}
|
||
}
|
||
if (!normal && !allowAutoCalc)
|
||
return;
|
||
let x = new Vector3();
|
||
let y = new Vector3();
|
||
if (!normal) {
|
||
normal = firstV.normalize();
|
||
Orbit.ComputUpDirection(normal, y, x);
|
||
[x, y, normal] = [normal, x, y];
|
||
}
|
||
else {
|
||
if (equalv3(normal, curves[0].Normal.negate()))
|
||
normal.negate();
|
||
Orbit.ComputUpDirection(normal, y, x);
|
||
}
|
||
return new Matrix4().makeBasis(x, y, normal).setPosition(curves[0].StartPoint);
|
||
}
|
||
function Pts2Polyline(pts, isClose)
|
||
{
|
||
let pl = new Polyline();
|
||
for (let i = 0; i < pts.length; i += 2) {
|
||
let p1 = AsVector3(pts[i]);
|
||
let arc;
|
||
let p2;
|
||
let p3;
|
||
if (isClose) {
|
||
p2 = AsVector3(pts[FixIndex(i + 1, pts.length)]);
|
||
p3 = AsVector3(pts[FixIndex(i + 2, pts.length)]);
|
||
}
|
||
else {
|
||
if (i >= pts.length - 2)
|
||
break;
|
||
p2 = AsVector3(pts[i + 1]);
|
||
p3 = AsVector3(pts[i + 2]);
|
||
}
|
||
let v1 = p1.clone().sub(p2);
|
||
let v2 = p2.clone().sub(p3);
|
||
if (equaln(v1.angleTo(v2), 0))
|
||
arc = new Line(p1, p3);
|
||
else
|
||
arc = new Arc().FromThreePoint(p1, p2, p3);
|
||
pl.Join(arc);
|
||
}
|
||
return pl;
|
||
}
|
||
|
||
var Polyline_1;
|
||
let Polyline = Polyline_1 = class Polyline extends Curve
|
||
{
|
||
constructor(_LineData = [])
|
||
{
|
||
super();
|
||
this._LineData = _LineData;
|
||
this._ClosedMark = false;
|
||
}
|
||
UpdateMatrixTo(m)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
let p = new Vector3().setFromMatrixPosition(m);
|
||
p.applyMatrix4(this.OCSInv);
|
||
if (equaln(p.z, 0)) {
|
||
let dir = Math.sign(this.Area2);
|
||
let tm = matrixAlignCoordSys(this.OCS, m);
|
||
for (let p of this._LineData) {
|
||
let p3 = AsVector3(p.pt);
|
||
p3.applyMatrix4(tm);
|
||
p.pt.set(p3.x, p3.y);
|
||
}
|
||
this.OCS = m;
|
||
let newDir = Math.sign(this.Area2);
|
||
if (dir !== newDir)
|
||
for (let p of this._LineData)
|
||
p.bul *= -1;
|
||
}
|
||
}
|
||
/**
|
||
* 原地翻转,仅改变法向量
|
||
*/
|
||
Flip()
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
let x = new Vector3();
|
||
let y = new Vector3();
|
||
let z = new Vector3();
|
||
this._Matrix.extractBasis(x, y, z);
|
||
z.negate();
|
||
y.crossVectors(z, x);
|
||
let p = this.Position;
|
||
this._Matrix.makeBasis(x, y, z).setPosition(p);
|
||
for (let d of this._LineData) {
|
||
d.pt.y *= -1;
|
||
d.bul *= -1;
|
||
}
|
||
this.Update();
|
||
return this;
|
||
}
|
||
//翻转曲线,首尾调换
|
||
Reverse()
|
||
{
|
||
if (this._LineData.length === 0)
|
||
return this;
|
||
this.WriteAllObjectRecord();
|
||
let pts = [];
|
||
let buls = [];
|
||
for (let data of this._LineData) {
|
||
pts.push(data.pt);
|
||
buls.push(-data.bul);
|
||
}
|
||
let lastBul = buls.pop();
|
||
buls.reverse();
|
||
buls.push(lastBul);
|
||
pts.reverse();
|
||
if (this._ClosedMark && !equalv2(pts[0], arrayLast(pts))) {
|
||
pts.unshift(pts.pop());
|
||
buls.unshift(buls.pop());
|
||
}
|
||
for (let i = 0; i < pts.length; i++) {
|
||
let d = this._LineData[i];
|
||
d.pt = pts[i];
|
||
d.bul = buls[i];
|
||
}
|
||
return this;
|
||
}
|
||
set LineData(data)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this._LineData = data;
|
||
this.Update();
|
||
}
|
||
get LineData()
|
||
{
|
||
return this._LineData;
|
||
}
|
||
get NumberOfVertices()
|
||
{
|
||
return this._LineData.length;
|
||
}
|
||
/**
|
||
* 在指定位置插入点.
|
||
* 例如:
|
||
* pl.AddVertexAt(pl.NumberOfVerticesk,p);//在末尾插入一个点
|
||
*
|
||
* @param {number} index 索引位置
|
||
* @param {Vector2} pt 点
|
||
* @returns {this}
|
||
* @memberof Polyline
|
||
*/
|
||
AddVertexAt(index, pt)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
let pts;
|
||
if (Array.isArray(pt)) {
|
||
pts = pt.map(p =>
|
||
{
|
||
return {
|
||
pt: p.clone(),
|
||
bul: 0
|
||
};
|
||
});
|
||
}
|
||
else
|
||
pts = [{ pt: pt.clone(), bul: 0 }];
|
||
this._LineData.splice(index, 0, ...pts);
|
||
this.Update();
|
||
return this;
|
||
}
|
||
RemoveVertexAt(index)
|
||
{
|
||
if (index < this._LineData.length) {
|
||
this.WriteAllObjectRecord();
|
||
this._LineData.splice(index, 1);
|
||
this.Update();
|
||
}
|
||
return this;
|
||
}
|
||
RemoveVertexIn(from, to)
|
||
{
|
||
if (from + 1 < this._LineData.length && to > from) {
|
||
this.WriteAllObjectRecord();
|
||
this._LineData.splice(from + 1, to - from - 1);
|
||
this.Update();
|
||
}
|
||
return this;
|
||
}
|
||
/**
|
||
* 重设闭合多段线的起点
|
||
* @param index 起始index,如果index非整数,将用最接近的整数作为起始索引
|
||
*/
|
||
ResetStartPoint(index)
|
||
{
|
||
if (!this.IsClose || index >= this.EndParam)
|
||
return false;
|
||
if (equalv2(this._LineData[0].pt, arrayLast(this._LineData).pt))
|
||
this._LineData.pop();
|
||
changeArrayStartIndex(this._LineData, Math.floor(index + 0.5));
|
||
this._LineData.push({
|
||
pt: this._LineData[0].pt.clone(),
|
||
bul: 0
|
||
});
|
||
return true;
|
||
}
|
||
GetPoint2dAt(index)
|
||
{
|
||
if (index >= 0 && this._LineData.length > index)
|
||
return this._LineData[index].pt.clone();
|
||
}
|
||
/**
|
||
* 设置指定点的位置
|
||
*
|
||
* @param {number} index
|
||
* @param {Vector2} pt
|
||
* @memberof Polyline
|
||
*/
|
||
SetPointAt(index, pt)
|
||
{
|
||
let d = this._LineData[index];
|
||
if (d) {
|
||
this.WriteAllObjectRecord();
|
||
d.pt.copy(pt);
|
||
this.Update();
|
||
}
|
||
return this;
|
||
}
|
||
ApplyScaleMatrix(m)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
for (let i = 0; i <= this.EndParam; i++) {
|
||
let p = this.GetPointAtParam(i);
|
||
p.applyMatrix4(m).applyMatrix4(this.OCSInv);
|
||
this.SetPointAt(i, AsVector2(p));
|
||
}
|
||
return this;
|
||
}
|
||
ApplyMirrorMatrix(m)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
let oldPts = this.GetStretchPoints();
|
||
reviseMirrorMatrix(this._Matrix);
|
||
for (let i = 0; i < oldPts.length; i++) {
|
||
let newP = oldPts[i].applyMatrix4(this.OCSInv);
|
||
let newBul = -this.GetBuilgeAt(i);
|
||
this.SetPointAt(i, AsVector2(newP));
|
||
this.SetBulgeAt(i, newBul);
|
||
}
|
||
this.Reverse();
|
||
return this;
|
||
}
|
||
SetBulgeAt(index, bul)
|
||
{
|
||
let d = this._LineData[index];
|
||
if (d) {
|
||
this.WriteAllObjectRecord();
|
||
d.bul = bul;
|
||
this.Update();
|
||
}
|
||
return this;
|
||
}
|
||
GetBuilgeAt(index)
|
||
{
|
||
return this._LineData[index].bul;
|
||
}
|
||
Rectangle(length, height)
|
||
{
|
||
this.LineData = [
|
||
{ pt: new Vector2(), bul: 0 },
|
||
{ pt: new Vector2(length), bul: 0 },
|
||
{ pt: new Vector2(length, height), bul: 0 },
|
||
{ pt: new Vector2(0, height), bul: 0 }
|
||
];
|
||
this.CloseMark = true;
|
||
return this;
|
||
}
|
||
RectangleFrom2Pt(p1, p2)
|
||
{
|
||
let box = new Box3();
|
||
box.setFromPoints([p2, p1].map((p) => p.clone().applyMatrix4(this.OCSInv)));
|
||
let px1 = AsVector2(box.min);
|
||
let px3 = AsVector2(box.max);
|
||
let px2 = new Vector2(px3.x, px1.y);
|
||
let px4 = new Vector2(px1.x, px3.y);
|
||
this.LineData = [
|
||
{ pt: px1, bul: 0 },
|
||
{ pt: px2, bul: 0 },
|
||
{ pt: px3, bul: 0 },
|
||
{ pt: px4, bul: 0 }
|
||
];
|
||
this.CloseMark = true;
|
||
return this;
|
||
}
|
||
//多段线起点
|
||
get StartPoint()
|
||
{
|
||
if (this._LineData.length > 0)
|
||
return AsVector3(this._LineData[0].pt).applyMatrix4(this.OCS);
|
||
return new Vector3();
|
||
}
|
||
set StartPoint(p)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
p = p.clone().applyMatrix4(this.OCSInv);
|
||
if (this._LineData.length === 0)
|
||
this.AddVertexAt(0, AsVector2(p));
|
||
else if (this._LineData.length === 1)
|
||
this.SetPointAt(0, AsVector2(p));
|
||
else {
|
||
let bul = this.GetBuilgeAt(0);
|
||
if (bul !== 0) {
|
||
let arc = this.GetCurveAtParam(0);
|
||
arc.StartPoint = p;
|
||
//前面线的凸度调整
|
||
this.SetBulgeAt(0, Math.tan(arc.AllAngle / 4) * Math.sign(bul));
|
||
}
|
||
this.SetPointAt(0, AsVector2(p));
|
||
}
|
||
}
|
||
get EndPoint()
|
||
{
|
||
if (this._ClosedMark)
|
||
return this.StartPoint;
|
||
if (this._LineData.length > 0)
|
||
return AsVector3(this._LineData[this.EndParam].pt).applyMatrix4(this.OCS);
|
||
return new Vector3();
|
||
}
|
||
set EndPoint(p)
|
||
{
|
||
if (this._LineData.length < 2 || this.CloseMark)
|
||
return;
|
||
this.WriteAllObjectRecord();
|
||
p = p.clone().applyMatrix4(this.OCSInv);
|
||
let bul = this.GetBuilgeAt(this.EndParam - 1);
|
||
if (bul !== 0) {
|
||
let arc = this.GetCurveAtParam(this.EndParam - 1);
|
||
arc.ApplyMatrix(this.OCSInv);
|
||
arc.EndPoint = p;
|
||
//前面线的凸度调整
|
||
this.SetBulgeAt(this.EndParam - 1, Math.tan(arc.AllAngle / 4) * Math.sign(bul));
|
||
}
|
||
this.SetPointAt(this.EndParam, AsVector2(p));
|
||
}
|
||
get CurveCount()
|
||
{
|
||
return this.EndParam;
|
||
}
|
||
get StartParam()
|
||
{
|
||
return 0;
|
||
}
|
||
/**
|
||
* 表示最后一条曲线的终止参数,使用该参数可以直接遍历到多段线的所有子线段. for(i<EndParam)...
|
||
*/
|
||
get EndParam()
|
||
{
|
||
if (this._LineData.length === 0)
|
||
return 0;
|
||
//闭合且起点不等于终点
|
||
if (this._ClosedMark &&
|
||
!equalv2(this._LineData[0].pt, arrayLast(this._LineData).pt))
|
||
return this._LineData.length;
|
||
return this._LineData.length - 1;
|
||
}
|
||
get Area2()
|
||
{
|
||
if (this.EndParam < 2)
|
||
return 0;
|
||
let { pts, buls } = this.PtsBuls;
|
||
let area = 0;
|
||
for (let i = 0; i < pts.length - 1; i++) {
|
||
let startV = pts[i];
|
||
let endV = pts[i + 1];
|
||
let det = getDeterminantFor2V(startV, endV);
|
||
let bul = buls[i];
|
||
if (bul !== 0) {
|
||
let arc = new Arc().ParseFromBul(startV, endV, bul);
|
||
let rSq = arc.Radius ** 2;
|
||
//弓形面积
|
||
let arcArea = rSq * arc.AllAngle * 0.5 - 0.5 * rSq * Math.sin(arc.AllAngle);
|
||
//我们可以知道 正的凸度总是增加逆时针的面积, 负的凸度总是增加顺时针的面积
|
||
det += arcArea * Math.sign(bul) * 2;
|
||
}
|
||
area += det;
|
||
}
|
||
return area / 2;
|
||
}
|
||
get Area()
|
||
{
|
||
return Math.abs(this.Area2);
|
||
}
|
||
//闭合标志
|
||
get CloseMark()
|
||
{
|
||
return this._ClosedMark;
|
||
}
|
||
//曲线是否闭合
|
||
get IsClose()
|
||
{
|
||
return this.CloseMark || (this.EndParam > 1 && (equalv3(this.StartPoint, this.EndPoint, 1e-4)));
|
||
}
|
||
set CloseMark(v)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
this._ClosedMark = v;
|
||
this.Update();
|
||
}
|
||
DigestionCloseMark()
|
||
{
|
||
if (this._ClosedMark && this._LineData.length > 1) {
|
||
this.WriteAllObjectRecord();
|
||
this._ClosedMark = false;
|
||
if (!equalv2(this._LineData[0].pt, arrayLast(this._LineData).pt))
|
||
this._LineData.push({ pt: AsVector2(this._LineData[0].pt), bul: 0 });
|
||
}
|
||
}
|
||
get Length()
|
||
{
|
||
return this.Explode().reduce((l, cu) => l + cu.Length, 0);
|
||
}
|
||
/**
|
||
* 获得指定参数所在的点.
|
||
* 当曲线存在闭合标志时,参数必须在曲线内部.
|
||
* 当曲线不存在闭合标志时,参数允许延伸出曲线.
|
||
*
|
||
* @param {number} param 参数
|
||
* @returns {Vector3} 三维点,可为空
|
||
* @memberof Polyline
|
||
*/
|
||
GetPointAtParam(param)
|
||
{
|
||
if (param === Math.floor(param) && this.ParamOnCurve(param))
|
||
return AsVector3(this.GetPoint2dAt(FixIndex(param, this.NumberOfVertices))).applyMatrix4(this.OCS);
|
||
let cu = this.GetCurveAtParam(param);
|
||
if (cu)
|
||
return cu.GetPointAtParam(this.GetCurveParamAtParam(param));
|
||
return undefined;
|
||
}
|
||
GetDistAtParam(param)
|
||
{
|
||
if (this._ClosedMark && !this.ParamOnCurve(param))
|
||
return NaN;
|
||
//参数 整数
|
||
let paramFloor = Math.floor(param);
|
||
//需要计算的曲线个数
|
||
let cuCout = paramFloor > this.EndParam ? this.EndParam : paramFloor;
|
||
let dist = 0;
|
||
//首先计算完整曲线的长度
|
||
for (let i = 0; i < cuCout; i++) {
|
||
dist += this.GetCurveAtIndex(i).Length;
|
||
}
|
||
//参数已经大于索引,证明参数在线外.
|
||
if (paramFloor !== cuCout) {
|
||
dist += this.GetCurveAtParam(param).GetDistAtParam(param - cuCout);
|
||
}
|
||
else if (param > paramFloor) {
|
||
let lastParam = param - paramFloor;
|
||
dist += this.GetCurveAtParam(param).GetDistAtParam(lastParam);
|
||
}
|
||
return dist;
|
||
}
|
||
GetPointAtDistance(dist)
|
||
{
|
||
let param = this.GetParamAtDist(dist);
|
||
return this.GetPointAtParam(param);
|
||
}
|
||
/**
|
||
* 返回参数所在的点. 如果曲线不闭合,会试图返回延伸点参数
|
||
*
|
||
* @param {Vector3} pt
|
||
* @returns {number}
|
||
* @memberof Polyline
|
||
*/
|
||
GetParamAtPoint(pt)
|
||
{
|
||
let cus = this.Explode();
|
||
if (cus.length === 0)
|
||
return NaN;
|
||
for (let i = 0; i < cus.length; i++) {
|
||
let cu = cus[i];
|
||
let param = cu.GetParamAtPoint(pt);
|
||
if (cu.ParamOnCurve(param))
|
||
return i + param; //返回点在曲线内部的参数
|
||
}
|
||
//当曲线闭合时,不需要延伸首尾去判断参数
|
||
if (this._ClosedMark)
|
||
return NaN;
|
||
//起点终点参数集合
|
||
let seParams = [];
|
||
//点在第一条曲线上的参数
|
||
let startParam = cus[0].GetParamAtPoint(pt);
|
||
if (!isNaN(startParam) && startParam < 0)
|
||
seParams.push(startParam);
|
||
//点在最后一条线上的参数
|
||
let endParam = cus[cus.length - 1].GetParamAtPoint(pt);
|
||
if (!isNaN(endParam) && endParam > 0)
|
||
seParams.push(endParam + this.EndParam - 1);
|
||
if (seParams.length == 1) {
|
||
return seParams[0];
|
||
}
|
||
else if (seParams.length == 2) {
|
||
//返回较近的参数
|
||
if (pt.distanceToSquared(this.StartPoint)
|
||
< pt.distanceToSquared(this.EndPoint))
|
||
return seParams[0];
|
||
else
|
||
return seParams[1];
|
||
}
|
||
return NaN;
|
||
}
|
||
GetParamAtDist(dist)
|
||
{
|
||
let cus = this.Explode();
|
||
for (let i = 0; i < cus.length; i++) {
|
||
let cu = cus[i];
|
||
let len = cu.Length;
|
||
if (dist <= len)
|
||
return i + cu.GetParamAtDist(dist);
|
||
else if (equaln(dist, len, 1e-8))
|
||
return i + 1;
|
||
dist -= len;
|
||
}
|
||
if (!this._ClosedMark)
|
||
return cus.length + cus[cus.length - 1].GetParamAtDist(dist);
|
||
return NaN;
|
||
}
|
||
GetDistAtPoint(pt)
|
||
{
|
||
let param = this.GetParamAtPoint(pt);
|
||
if (!this.ParamOnCurve(param))
|
||
return NaN;
|
||
return this.GetDistAtParam(param);
|
||
}
|
||
/**
|
||
* 返回曲线的一阶导数.
|
||
* 当曲线闭合(标志)且点不在曲线上.
|
||
* 或者曲线不闭合(标志) 且点不在曲线上也不在延伸上
|
||
*
|
||
* @param {(number | Vector3)} param
|
||
* @returns {Vector3}
|
||
* @memberof Polyline
|
||
*/
|
||
GetFistDeriv(param)
|
||
{
|
||
if (param instanceof Vector3)
|
||
param = this.GetParamAtPoint(param);
|
||
if (isNaN(param))
|
||
return undefined;
|
||
let cu = this.GetCurveAtParam(param);
|
||
if (!cu)
|
||
return undefined;
|
||
return cu.GetFistDeriv(this.GetCurveParamAtParam(param));
|
||
}
|
||
GetSplitCurves(param)
|
||
{
|
||
//参数需要转化为参数数组
|
||
let params;
|
||
if (typeof param == "number")
|
||
params = [param];
|
||
else
|
||
params = param;
|
||
//校验参数在曲线中,修正参数
|
||
let endParam = this.EndParam;
|
||
params = params.filter(p => this.ParamOnCurve(p) && p > -1e-6)
|
||
.map(a =>
|
||
{
|
||
if (a < 0)
|
||
return 0;
|
||
if (a > endParam)
|
||
return endParam;
|
||
if (equaln(a, Math.floor(a + 0.5), 1e-8))
|
||
return Math.floor(a + 0.5);
|
||
return a;
|
||
});
|
||
//排序
|
||
params.sort((a, b) => a - b);
|
||
let hasEndParam = arrayLast(params) === this.EndParam;
|
||
//必须加入最后一个参数,保证切割后的曲线完整
|
||
if (!hasEndParam)
|
||
params.push(this.EndParam);
|
||
arrayRemoveDuplicateBySort(params, (e1, e2) => equaln(e1, e2, 1e-8));
|
||
params = params.filter(p => this.ParamOnCurve(p));
|
||
if (params.length === 0)
|
||
return [];
|
||
//判断是否存在0参数
|
||
let hasZeroParam = params[0] === 0;
|
||
if (hasZeroParam)
|
||
params.shift();
|
||
let { pts, buls } = this.PtsBuls;
|
||
//返回的多段线集合
|
||
let pls = [];
|
||
let len = 0; //已经走过的参数长度(整数)
|
||
//上一个切割参数的位置 0-1
|
||
let prePa = 0;
|
||
for (let pa of params) {
|
||
//参数所在点
|
||
let pt = AsVector2(this.GetPointAtParam(pa).applyMatrix4(this.OCSInv));
|
||
pa -= len;
|
||
let pafloor = Math.floor(pa);
|
||
len += pafloor;
|
||
let plData = [];
|
||
//添加点
|
||
for (let i = 0; i < pafloor; i++) {
|
||
if (i === 0 && !equaln(buls[0], 0, 1e-8)) {
|
||
buls[0] = Math.tan((1 - prePa) * Math.atan(buls[0]));
|
||
}
|
||
plData.push({ pt: pts[0], bul: buls[0] });
|
||
pts.shift();
|
||
buls.shift();
|
||
}
|
||
if (equaln(pa, pafloor, 1e-8)) //如果pa在点上
|
||
{
|
||
plData.push({ pt: pts[0].clone(), bul: buls[0] });
|
||
}
|
||
else //在曲线上
|
||
{
|
||
let bul = buls[0];
|
||
if (!equaln(bul, 0, 1e-6))
|
||
bul = Math.tan((pa - pafloor - (0 === pafloor ? prePa : 0)) * Math.atan(buls[0])); //->凸度
|
||
//加入顶点+凸度
|
||
plData.push({ pt: pts[0].clone(), bul });
|
||
//终点
|
||
plData.push({ pt, bul: 0 });
|
||
//修正剩余的点表和凸度表
|
||
pts[0].copy(pt);
|
||
}
|
||
prePa = pa - pafloor;
|
||
if (plData.length > 1)
|
||
pls.push(new Polyline_1(plData).ApplyMatrix(this.OCS));
|
||
}
|
||
//当曲线为闭合曲线,并且不存在0切割参数时,首尾连接曲线
|
||
if (this._ClosedMark && !hasZeroParam && !hasEndParam) {
|
||
let lastPl = pls[pls.length - 1];
|
||
if (equalv2(arrayLast(lastPl._LineData).pt, pls[0]._LineData[0].pt))
|
||
lastPl._LineData.pop();
|
||
lastPl._LineData.push(...pls[0]._LineData);
|
||
pls.shift();
|
||
}
|
||
return pls;
|
||
}
|
||
//未完善
|
||
GetCurveAtParamRange(startParam, endParam)
|
||
{
|
||
let sfloor = Math.floor(startParam + 0.5);
|
||
if (equaln(sfloor, startParam, 1e-8))
|
||
startParam = sfloor;
|
||
else
|
||
sfloor = Math.floor(startParam);
|
||
let efloor = Math.floor(endParam + 0.5);
|
||
if (equaln(efloor, endParam, 1e-8))
|
||
endParam = efloor;
|
||
else
|
||
efloor = Math.floor(efloor);
|
||
const GetCurve = (index) =>
|
||
{
|
||
let d = this._LineData[index];
|
||
let next = this._LineData[index + 1];
|
||
if (!equaln(d.bul, 0, 1e-8))
|
||
return new Arc().ParseFromBul(d.pt, next.pt, d.bul);
|
||
else
|
||
return new Line(AsVector3(d.pt), AsVector3(next.pt));
|
||
};
|
||
let lined = [];
|
||
if (startParam === sfloor) {
|
||
let d = this._LineData[sfloor];
|
||
lined.push({ pt: d.pt.clone(), bul: d.bul });
|
||
}
|
||
else {
|
||
let d = this._LineData[sfloor];
|
||
let cu = GetCurve(sfloor);
|
||
let remParam = startParam - sfloor;
|
||
let p = cu.GetPointAtParam(remParam);
|
||
let bul = d.bul;
|
||
if (!equaln(bul, 0))
|
||
bul = Math.tan(Math.atan(bul) * (1 - remParam));
|
||
lined.push({ pt: AsVector2(p), bul: bul });
|
||
}
|
||
for (let i = sfloor + 1; i < efloor; i++) {
|
||
let d = this._LineData[i];
|
||
lined.push({ pt: d.pt.clone(), bul: d.bul });
|
||
}
|
||
if (efloor !== endParam) {
|
||
let d = this.LineData[efloor];
|
||
let remParam = endParam - efloor;
|
||
let cu = GetCurve(efloor);
|
||
let p = cu.GetPointAtParam(remParam);
|
||
let bul = d.bul;
|
||
if (!equaln(bul, 0)) {
|
||
arrayLast(lined).bul = Math.tan(Math.atan(bul) * remParam);
|
||
bul = Math.tan(Math.atan(bul) * (1 - remParam));
|
||
}
|
||
lined.push({ pt: AsVector2(p), bul });
|
||
}
|
||
let pl = new Polyline_1(lined);
|
||
pl.OCS = this.OCSNoClone;
|
||
return;
|
||
}
|
||
Extend(newParam)
|
||
{
|
||
if (this.CloseMark || this.ParamOnCurve(newParam))
|
||
return;
|
||
this.WriteAllObjectRecord();
|
||
let ptIndex;
|
||
let bulIndex;
|
||
if (newParam < 0) {
|
||
ptIndex = 0;
|
||
bulIndex = 0;
|
||
}
|
||
else if (newParam > this.EndParam) {
|
||
ptIndex = this.EndParam;
|
||
bulIndex = ptIndex - 1;
|
||
}
|
||
//修改顶点
|
||
this._LineData[ptIndex].pt = AsVector2(this.GetPointAtParam(newParam).applyMatrix4(this.OCSInv));
|
||
//修改凸度
|
||
let oldBul = this._LineData[bulIndex].bul;
|
||
if (oldBul != 0)
|
||
this._LineData[bulIndex].bul = Math.tan(Math.atan(oldBul) * (1 + newParam - ptIndex));
|
||
this.Update();
|
||
}
|
||
//const this
|
||
MatrixAlignTo2(toMatrix)
|
||
{
|
||
if (!matrixIsCoplane(this._Matrix, toMatrix, 1e-4))
|
||
return this.PtsBuls;
|
||
let m = matrixAlignCoordSys(this._Matrix, toMatrix);
|
||
let z1 = this.Normal;
|
||
let z2 = new Vector3().setFromMatrixColumn(toMatrix, 2);
|
||
let isMirror = equalv3(z1, z2.negate());
|
||
let pts = [];
|
||
let buls = [];
|
||
for (let d of this._LineData) {
|
||
let p = AsVector2(AsVector3(d.pt).applyMatrix4(m));
|
||
pts.push(p);
|
||
buls.push(isMirror ? -d.bul : d.bul);
|
||
}
|
||
return { pts, buls };
|
||
}
|
||
Join(cu, allowGap = false, tolerance = 1e-4)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
if (this._ClosedMark)
|
||
return Status.False;
|
||
let [sp, ep, cuSp, cuEp] = [this.StartPoint, this.EndPoint, cu.StartPoint, cu.EndPoint];
|
||
let ocsInv = this.OCSInv;
|
||
let [cuSp2, cuEp2] = [cuSp, cuEp].map(p => AsVector2(p.clone().applyMatrix4(ocsInv)));
|
||
if (this._LineData.length === 0) {
|
||
if (cu instanceof Line) {
|
||
this._LineData.push({ pt: cuSp2, bul: 0 });
|
||
this._LineData.push({ pt: cuEp2, bul: 0 });
|
||
}
|
||
else if (cu instanceof Arc) {
|
||
this._LineData.push({ pt: cuSp2, bul: cu.Bul });
|
||
this._LineData.push({ pt: cuEp2, bul: 0 });
|
||
}
|
||
else if (cu instanceof Polyline_1) {
|
||
let f = new CADFiler();
|
||
cu.WriteFile(f);
|
||
this.ReadFile(f);
|
||
}
|
||
else
|
||
return Status.False;
|
||
}
|
||
else {
|
||
let LinkType;
|
||
(function (LinkType)
|
||
{
|
||
LinkType[LinkType["None"] = 0] = "None";
|
||
LinkType[LinkType["SpSp"] = 1] = "SpSp";
|
||
LinkType[LinkType["SpEp"] = 2] = "SpEp";
|
||
LinkType[LinkType["EpSp"] = 3] = "EpSp";
|
||
LinkType[LinkType["EpEp"] = 4] = "EpEp";
|
||
})(LinkType || (LinkType = {}));
|
||
let spspDisSq = cuSp.distanceToSquared(sp);
|
||
let spepDisSq = cuSp.distanceToSquared(ep);
|
||
let epspDisSq = cuEp.distanceToSquared(sp);
|
||
let epepDisSq = cuEp.distanceToSquared(ep);
|
||
let minDis = tolerance * tolerance;
|
||
let linkType = LinkType.None;
|
||
if (spspDisSq < minDis) {
|
||
linkType = LinkType.SpSp;
|
||
minDis = spspDisSq;
|
||
}
|
||
if (spepDisSq < minDis) {
|
||
linkType = LinkType.SpEp;
|
||
minDis = spepDisSq;
|
||
}
|
||
if (epspDisSq < minDis) {
|
||
linkType = LinkType.EpSp;
|
||
minDis = epspDisSq;
|
||
}
|
||
if (epepDisSq < minDis)
|
||
linkType = LinkType.EpEp;
|
||
if (linkType === LinkType.None)
|
||
return Status.False;
|
||
if (cu instanceof Line) {
|
||
if (linkType === LinkType.SpSp) {
|
||
this._LineData.unshift({ pt: cuEp2, bul: 0 });
|
||
}
|
||
else if (linkType === LinkType.SpEp) {
|
||
this._LineData.push({ pt: cuEp2, bul: 0 });
|
||
}
|
||
else if (linkType === LinkType.EpSp) {
|
||
this._LineData.unshift({ pt: cuSp2, bul: 0 });
|
||
}
|
||
else if (linkType === LinkType.EpEp) {
|
||
this._LineData.push({ pt: cuSp2, bul: 0 });
|
||
}
|
||
}
|
||
else if (cu instanceof Arc) {
|
||
let dir = equalv3(this.Normal, cu.Normal.negate()) ? -1 : 1;
|
||
let bul = cu.Bul * dir;
|
||
if (linkType === LinkType.SpSp) {
|
||
this._LineData.unshift({ pt: cuEp2, bul: -bul });
|
||
}
|
||
else if (linkType === LinkType.SpEp) {
|
||
arrayLast(this._LineData).bul = bul;
|
||
this._LineData.push({ pt: cuEp2, bul: 0 });
|
||
}
|
||
else if (linkType === LinkType.EpSp) {
|
||
this._LineData.unshift({ pt: cuSp2, bul: bul });
|
||
}
|
||
else if (linkType === LinkType.EpEp) {
|
||
arrayLast(this._LineData).bul = -bul;
|
||
this._LineData.push({ pt: cuSp2, bul: 0 });
|
||
}
|
||
}
|
||
else if (cu instanceof Polyline_1) {
|
||
if (cu.CloseMark)
|
||
return Status.False;
|
||
let { pts, buls } = this.PtsBuls;
|
||
if (linkType === LinkType.SpSp) {
|
||
cu.Reverse();
|
||
let cuPtsBul = cu.MatrixAlignTo2(this.OCS);
|
||
cuPtsBul.pts.pop();
|
||
cuPtsBul.buls.pop();
|
||
pts = cuPtsBul.pts.concat(pts);
|
||
buls = cuPtsBul.buls.concat(buls);
|
||
}
|
||
else if (linkType === LinkType.SpEp) {
|
||
pts.pop();
|
||
buls.pop();
|
||
let cuPtsBul = cu.MatrixAlignTo2(this.OCS);
|
||
pts = pts.concat(cuPtsBul.pts);
|
||
buls = buls.concat(cuPtsBul.buls);
|
||
}
|
||
else if (linkType === LinkType.EpSp) {
|
||
let cuPtsBul = cu.MatrixAlignTo2(this.OCS);
|
||
cuPtsBul.pts.pop();
|
||
cuPtsBul.buls.pop();
|
||
pts = cuPtsBul.pts.concat(pts);
|
||
buls = cuPtsBul.buls.concat(buls);
|
||
}
|
||
else if (linkType === LinkType.EpEp) {
|
||
pts.pop();
|
||
buls.pop();
|
||
cu.Reverse();
|
||
let cuPtsBul = cu.MatrixAlignTo2(this.OCS);
|
||
pts = pts.concat(cuPtsBul.pts);
|
||
buls = buls.concat(cuPtsBul.buls);
|
||
}
|
||
this._LineData.length = 0;
|
||
for (let i = 0; i < pts.length; i++) {
|
||
this._LineData.push({ pt: pts[i], bul: buls[i] });
|
||
}
|
||
}
|
||
else
|
||
return Status.False;
|
||
}
|
||
//在上面的其他分支已经返回了假 所以这里直接返回真.
|
||
this.Update();
|
||
return Status.True;
|
||
}
|
||
/**
|
||
* 将曲线数组组合成多段线
|
||
* @param curves 已经使用CurveLinked的数组,总是首尾相连
|
||
* @returns
|
||
*/
|
||
static Combine(curves, tolerance = 1e-5)
|
||
{
|
||
if (!curves || curves.length === 0)
|
||
return;
|
||
let pl = new Polyline_1;
|
||
pl.OCS = ComputerCurvesNormalOCS(curves);
|
||
for (let cu of curves)
|
||
pl.Join(cu, false, tolerance);
|
||
let d = pl.LineData;
|
||
if (d.length > 1) {
|
||
let ld = arrayLast(d).pt;
|
||
if (equalv2(d[0].pt, ld, tolerance))
|
||
ld.copy(d[0].pt);
|
||
}
|
||
return pl;
|
||
}
|
||
PtOnCurve(pt)
|
||
{
|
||
for (let i = 0; i < this.EndParam; i++) {
|
||
let c = this.GetCurveAtIndex(i);
|
||
if (c.PtOnCurve(pt))
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
//点在曲线上,已经确定点在曲线的延伸线上
|
||
PtOnCurve3(p, fuzz = 1e-6)
|
||
{
|
||
for (let i = 0; i < this.EndParam; i++) {
|
||
let c = this.GetCurveAtIndex(i);
|
||
if (c.PtOnCurve3(p, fuzz))
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
PtInCurve(pt)
|
||
{
|
||
return this.IsClose && IsPointInPolyLine(this, pt);
|
||
}
|
||
GetClosestPointTo(pt, extend)
|
||
{
|
||
return this.GetClosestPointTo2(pt, extend ? ExtendType.Both : ExtendType.None);
|
||
}
|
||
GetClosestPointTo2(pt, extType)
|
||
{
|
||
//当曲线空时,返回空
|
||
if (this.EndParam < 1)
|
||
return undefined;
|
||
//当有闭合标志时,曲线在任何位置都不延伸
|
||
if (this._ClosedMark)
|
||
extType = ExtendType.None;
|
||
//最近点
|
||
let ptC = undefined;
|
||
//最近点的距离
|
||
let ptCDist = Infinity;
|
||
for (let i = 0; i < this.EndParam; i++) {
|
||
let cu = this.GetCurveAtIndex(i);
|
||
//前延伸
|
||
if (i === 0 && (extType & ExtendType.Front) > 0) {
|
||
let ptCFirst = cu.GetClosestPointTo(pt, true);
|
||
if (cu.GetParamAtPoint(ptCFirst) <= 1) {
|
||
ptC = ptCFirst;
|
||
ptCDist = ptC.distanceToSquared(pt);
|
||
}
|
||
if (extType === ExtendType.Front)
|
||
continue;
|
||
}
|
||
let ptCloseNew; //新的最近点
|
||
//后延伸 (此处与前延伸分开if 如果线只有一段,那么前后延伸都能同时触发)
|
||
if (i === (this.EndParam - 1) && (extType & ExtendType.Back) > 0) {
|
||
let ptCLast = cu.GetClosestPointTo(pt, true);
|
||
if (cu.GetParamAtPoint(ptCLast) >= 0)
|
||
ptCloseNew = ptCLast;
|
||
else //如果延伸之后并不在曲线或者曲线的后延伸上
|
||
ptCloseNew = cu.EndPoint;
|
||
}
|
||
else {
|
||
ptCloseNew = cu.GetClosestPointTo(pt, false);
|
||
}
|
||
let newDist = ptCloseNew.distanceToSquared(pt);
|
||
if (newDist < ptCDist) {
|
||
ptC = ptCloseNew;
|
||
ptCDist = newDist;
|
||
}
|
||
}
|
||
return ptC;
|
||
}
|
||
//偏移
|
||
GetOffsetCurves(offsetDist)
|
||
{
|
||
if (equaln(offsetDist, 0))
|
||
return [];
|
||
let polyOffestUtil = new OffsetPolyline(this, offsetDist);
|
||
let curves = polyOffestUtil.Do();
|
||
for (let cu of curves)
|
||
cu.ColorIndex = this.ColorIndex;
|
||
return curves;
|
||
}
|
||
GetFeedingToolPath(offsetDist)
|
||
{
|
||
if (equaln(offsetDist, 0))
|
||
return [];
|
||
let polyOffestUtil = new OffsetPolyline(this, offsetDist, true);
|
||
return polyOffestUtil.Do();
|
||
}
|
||
/**
|
||
* 分解
|
||
*/
|
||
Explode()
|
||
{
|
||
let exportCus = [];
|
||
for (let i = 0; i < this.EndParam; i++) {
|
||
exportCus.push(this.GetCurveAtIndex(i));
|
||
}
|
||
return exportCus;
|
||
}
|
||
/**
|
||
* 根据参数得到参数所在的子曲线.
|
||
*
|
||
* 当曲线存在闭合标志时,参数必须在曲线内部,否则返回空.
|
||
*
|
||
* @param {number} param 参数值
|
||
* @returns {Curve} 曲线(直线或者圆弧) 或空
|
||
* @memberof Polyline
|
||
*/
|
||
GetCurveAtParam(param)
|
||
{
|
||
if (this._ClosedMark && !this.ParamOnCurve(param))
|
||
return undefined;
|
||
if (param < 0)
|
||
return this.GetCurveAtIndex(0);
|
||
else if (param >= this.EndParam)
|
||
return this.GetCurveAtIndex(this.EndParam - 1);
|
||
else
|
||
return this.GetCurveAtIndex(Math.floor(param));
|
||
}
|
||
/**
|
||
* 得到参数在子曲线中的表示
|
||
*
|
||
* @param {number} param 参数在多段线中表示
|
||
* @returns {number} 参数在子曲线中表示
|
||
* @memberof Polyline
|
||
*/
|
||
GetCurveParamAtParam(param)
|
||
{
|
||
if (param >= this.EndParam)
|
||
param -= this.EndParam - 1;
|
||
else if (param > 0)
|
||
param -= Math.floor(param);
|
||
return param;
|
||
}
|
||
/**
|
||
* 获得曲线,来自索引位置.
|
||
* @param {number} i 索引位置 整数
|
||
*/
|
||
GetCurveAtIndex(i)
|
||
{
|
||
if (i >= this._LineData.length)
|
||
return undefined;
|
||
if (!this.ParamOnCurve(i))
|
||
return undefined;
|
||
if (!this._ClosedMark && i === this._LineData.length - 1)
|
||
return undefined;
|
||
let d1 = this._LineData[i];
|
||
let d2 = this._LineData[FixIndex(i + 1, this._LineData)];
|
||
let curve;
|
||
if (equaln(d1.bul, 0, 1e-8))
|
||
curve = new Line(AsVector3(d1.pt), AsVector3(d2.pt)).ApplyMatrix(this.OCS);
|
||
else
|
||
curve = new Arc().ParseFromBul(d1.pt, d2.pt, d1.bul).ApplyMatrix(this.OCS);
|
||
curve.ColorIndex = this._Color;
|
||
return curve;
|
||
}
|
||
IntersectWith2(curve, intType, tolerance = 1e-5)
|
||
{
|
||
return IntersectPolylineAndCurve(this, curve, intType, tolerance);
|
||
}
|
||
//计算自交点.
|
||
IntersectSelf()
|
||
{
|
||
let cus = this.Explode();
|
||
if (cus.length === 0)
|
||
return [];
|
||
let intParams = [];
|
||
for (let i = 0; i < cus.length; i++) {
|
||
let c = cus[i];
|
||
for (let j = i + 2; j < cus.length; j++) {
|
||
let c2 = cus[j];
|
||
let pts = c.IntersectWith(c2, IntersectOption.OnBothOperands);
|
||
for (let p of pts) {
|
||
intParams.push(i + c.GetParamAtPoint(p));
|
||
intParams.push(j + c2.GetParamAtPoint(p));
|
||
}
|
||
}
|
||
}
|
||
return intParams;
|
||
}
|
||
get BoundingBox()
|
||
{
|
||
let box = new Box3();
|
||
for (let i = 0; i < this.EndParam; i++) {
|
||
let cu = this.GetCurveAtIndex(i);
|
||
box.union(cu.BoundingBox);
|
||
}
|
||
return box;
|
||
}
|
||
/**
|
||
* 得到曲线有用的点表和凸度(闭合曲线首尾重复)
|
||
*/
|
||
get PtsBuls()
|
||
{
|
||
let pts = [];
|
||
let buls = [];
|
||
if (this._LineData.length === 0)
|
||
return { pts, buls };
|
||
for (let data of this._LineData) {
|
||
pts.push(data.pt.clone());
|
||
buls.push(data.bul);
|
||
}
|
||
//闭合且起点不等于终点
|
||
if (this._ClosedMark &&
|
||
!this._LineData[0].pt.equals(arrayLast(this._LineData).pt)) {
|
||
pts.push(pts[0].clone());
|
||
buls.push(buls[0]);
|
||
}
|
||
return { pts, buls };
|
||
}
|
||
get IsBulge()
|
||
{
|
||
if (!this.IsClose)
|
||
return false;
|
||
let refDir = Math.sign(this.Area2);
|
||
let c1;
|
||
let c2;
|
||
for (let i = 0; i < this.EndParam; i++) {
|
||
c1 = this.GetCurveAtIndex(i);
|
||
c2 = this.GetCurveAtIndex(FixIndex(i + 1, this.EndParam));
|
||
let len1 = c1.Length;
|
||
let len2 = c2.Length;
|
||
let minLen = Math.min(len1, len2) * 0.2;
|
||
let p = c1.EndPoint;
|
||
let p1;
|
||
let p2;
|
||
if (c1 instanceof Arc) {
|
||
let dir = c1.IsClockWise ? -1 : 1;
|
||
if (dir !== refDir)
|
||
return false;
|
||
p1 = c1.GetPointAtDistance(len1 - minLen);
|
||
}
|
||
else
|
||
p1 = c1.StartPoint;
|
||
if (c2 instanceof Arc) {
|
||
let dir = c2.IsClockWise ? -1 : 1;
|
||
if (dir !== refDir)
|
||
return false;
|
||
p2 = c2.GetPointAtDistance(minLen);
|
||
}
|
||
else
|
||
p2 = c2.EndPoint;
|
||
let vec1 = p.clone().sub(p1);
|
||
let vec2 = p2.sub(p);
|
||
let dir = Math.sign(vec1.cross(vec2).z);
|
||
if (dir !== 0 && dir !== refDir)
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
get Shape()
|
||
{
|
||
let { pts, buls } = this.PtsBuls;
|
||
let curve = CreateBoardUtil.CreatePath(pts, buls);
|
||
return curve;
|
||
}
|
||
get SVG()
|
||
{
|
||
let sp = this.StartPoint;
|
||
let str = `M${sp.x} ${sp.y} `;
|
||
for (let i = 1; i <= this.EndParam; i++) {
|
||
let bul = this.GetBuilgeAt(i - 1);
|
||
let p = this.GetPointAtParam(i);
|
||
if (bul === 0)
|
||
str += `L${p.x} ${p.y} `;
|
||
else {
|
||
let arc = this.GetCurveAtIndex(i - 1);
|
||
str += `A ${arc.Radius} ${arc.Radius} 0 ${Math.abs(bul) >= 1 ? 1 : 0} ${arc.IsClockWise ? 0 : 1} ${p.x} ${p.y}`;
|
||
}
|
||
}
|
||
return str;
|
||
}
|
||
InitDrawObject(renderType = RenderType.Wireframe)
|
||
{
|
||
let shape = this.Shape;
|
||
let geo = BufferGeometryUtils.CreateFromPts(shape.getPoints(50).map(AsVector3));
|
||
if (renderType === RenderType.Print) {
|
||
var geometry = new LineGeometry().setPositions(geo.attributes.position.array);
|
||
return new Line2(geometry, ColorMaterial.PrintLineMatrial);
|
||
}
|
||
let obj = new Line$1(geo, ColorMaterial.GetLineMaterial(this._Color));
|
||
return obj;
|
||
}
|
||
UpdateDrawObject(type, en)
|
||
{
|
||
let shape = this.Shape;
|
||
let pts = shape.getPoints(50).map(AsVector3);
|
||
let plObj = en;
|
||
let geo = plObj.geometry;
|
||
if (!BufferGeometryUtils.UpdatePts(geo, pts)) {
|
||
updateGeometry(plObj, BufferGeometryUtils.CreateFromPts(pts));
|
||
}
|
||
}
|
||
GetDragPointCount(drag)
|
||
{
|
||
if (drag === DragPointType.Grip) {
|
||
let count = this.EndParam * 2 + 1;
|
||
if (this.CloseMark)
|
||
count--;
|
||
return count;
|
||
}
|
||
else {
|
||
return this._LineData.length;
|
||
}
|
||
}
|
||
GetObjectSnapPoints(snapMode, pickPoint, lastPoint, viewXform)
|
||
{
|
||
switch (snapMode) {
|
||
case ObjectSnapMode.End:
|
||
return this.GetStretchPoints();
|
||
case ObjectSnapMode.Mid:
|
||
let midPts = [];
|
||
let enParam = this.EndParam;
|
||
for (let i = 0.5; i < enParam; i++) {
|
||
let p = this.GetPointAtParam(i);
|
||
p && midPts.push(p);
|
||
}
|
||
return midPts;
|
||
case ObjectSnapMode.Nea:
|
||
{
|
||
let nea = [];
|
||
for (let cu of this.Explode()) {
|
||
let neaa = cu.GetObjectSnapPoints(snapMode, pickPoint, lastPoint, viewXform);
|
||
if (neaa)
|
||
nea.push(...neaa);
|
||
}
|
||
return nea;
|
||
}
|
||
case ObjectSnapMode.Ext:
|
||
{
|
||
let cp = this.GetClosestPointTo(pickPoint, true);
|
||
if (cp)
|
||
return [cp];
|
||
break;
|
||
}
|
||
case ObjectSnapMode.Cen:
|
||
let cenPts = [];
|
||
for (let i = 0; i < this._LineData.length; i++) {
|
||
let data = this._LineData[i];
|
||
if (!equaln(data.bul, 0)) {
|
||
let cu = this.GetCurveAtIndex(i);
|
||
if (cu) //end bul !== 0 但是并没有圆弧
|
||
cenPts.push(cu.Center);
|
||
}
|
||
}
|
||
return cenPts;
|
||
case ObjectSnapMode.Per:
|
||
if (lastPoint) {
|
||
let cp = this.GetClosestPointTo(pickPoint, false);
|
||
if (!cp)
|
||
return [];
|
||
let cparam = this.GetParamAtPoint(cp);
|
||
let cu = this.GetCurveAtParam(cparam);
|
||
if (cu) {
|
||
let closestPt = cu.GetClosestPointTo(lastPoint, true);
|
||
if (closestPt && this.PtOnCurve(closestPt))
|
||
return [closestPt];
|
||
}
|
||
}
|
||
case ObjectSnapMode.Tan:
|
||
if (lastPoint) {
|
||
let clostPt = this.GetClosestPointTo(pickPoint, false);
|
||
if (!clostPt)
|
||
return [];
|
||
let par = this.GetParamAtPoint(clostPt);
|
||
let cu = this.GetCurveAtParam(par);
|
||
if (cu instanceof Arc)
|
||
return cu.GetObjectSnapPoints(snapMode, pickPoint, lastPoint);
|
||
return [];
|
||
}
|
||
}
|
||
return [];
|
||
}
|
||
GetGripPoints()
|
||
{
|
||
let ptList = [];
|
||
if (this._LineData.length < 2)
|
||
return ptList;
|
||
let enParam = this.EndParam;
|
||
if (this.CloseMark)
|
||
enParam -= 0.5;
|
||
for (let i = 0; i < enParam + 0.5; i += 0.5) {
|
||
let p = this.GetPointAtParam(i);
|
||
ptList.push(p);
|
||
}
|
||
return ptList;
|
||
}
|
||
MoveGripPoints(indexList, moveVec)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
let moveVLoc = AsVector2(moveVec.clone().applyMatrix4(new Matrix4().extractRotation(this.OCSInv)));
|
||
let calcIndexList = indexList;
|
||
if (indexList.length > 1) {
|
||
let centerIndexes = indexList.filter(i => i % 2 === 0);
|
||
if (centerIndexes.length > 0)
|
||
calcIndexList = centerIndexes;
|
||
}
|
||
for (let index of calcIndexList) {
|
||
if (index % 2 === 0) {
|
||
let cuIndex = index / 2;
|
||
let ptCout = this._LineData.length;
|
||
let frontIndex = cuIndex - 1;
|
||
if (this._ClosedMark)
|
||
frontIndex = FixIndex(frontIndex, ptCout);
|
||
if (frontIndex >= 0 && this.GetBuilgeAt(frontIndex)) {
|
||
let arc = this.GetCurveAtIndex(frontIndex);
|
||
arc.MoveGripPoints([2], moveVec);
|
||
this._LineData[frontIndex].bul = arc.Bul;
|
||
}
|
||
if ((cuIndex !== ptCout - 1) && this.GetBuilgeAt(cuIndex)) {
|
||
let arc = this.GetCurveAtIndex(cuIndex);
|
||
arc.MoveGripPoints([0], moveVec);
|
||
this._LineData[cuIndex].bul = arc.Bul;
|
||
}
|
||
this._LineData[cuIndex].pt.add(moveVLoc);
|
||
}
|
||
else {
|
||
let ptIndex = (index - 1) / 2;
|
||
let nextIndex = (FixIndex(ptIndex + 1, this._LineData));
|
||
let d = this._LineData[ptIndex];
|
||
if (d.bul == 0) {
|
||
this._LineData[ptIndex].pt.add(moveVLoc);
|
||
this._LineData[nextIndex].pt.add(moveVLoc);
|
||
}
|
||
else {
|
||
let arc = this.GetCurveAtIndex(ptIndex);
|
||
arc.MoveGripPoints([1], moveVec);
|
||
this._LineData[ptIndex].bul = arc.Bul;
|
||
}
|
||
}
|
||
}
|
||
this.Update();
|
||
}
|
||
GetStretchPoints()
|
||
{
|
||
let ocs = this.OCS;
|
||
let ptList = [];
|
||
for (let data of this._LineData) {
|
||
ptList.push(AsVector3(data.pt).applyMatrix4(ocs));
|
||
}
|
||
return ptList;
|
||
}
|
||
/**
|
||
* 范围拉伸(stretch),对夹点进行拉伸.
|
||
* 如果对圆弧的一侧进行拉伸,那么修改bul
|
||
*
|
||
* @param {Array<number>} indexList
|
||
* @param {Vector3} vec
|
||
* @memberof Polyline
|
||
*/
|
||
MoveStretchPoints(indexList, vec)
|
||
{
|
||
this.WriteAllObjectRecord();
|
||
//本地坐标系移动向量
|
||
let moveVLoc = vec.clone().applyMatrix4(new Matrix4().extractRotation(this.OCSInv));
|
||
let ptCout = this._LineData.length;
|
||
for (let index of indexList) {
|
||
let frontIndex = index - 1;
|
||
let nextIndex = index + 1;
|
||
if (this._ClosedMark) {
|
||
frontIndex = FixIndex(frontIndex, ptCout);
|
||
nextIndex = FixIndex(nextIndex, ptCout);
|
||
}
|
||
/**
|
||
* 根据新的拉伸点修改凸度.
|
||
*
|
||
* @param {number} nextIndex 隔壁点索引
|
||
* @param {number} bulIndex 需要修改凸度位置的索引
|
||
* @returns
|
||
*/
|
||
const ChangeBul = (nextIndex, bulIndex) =>
|
||
{
|
||
//需要修改的点的数据
|
||
let d = this._LineData[bulIndex];
|
||
if (d === undefined || d.bul == 0)
|
||
return;
|
||
//如果隔壁点不在拉伸列表中
|
||
if (indexList.indexOf(nextIndex) === -1) {
|
||
let needChangeP = this.GetPointAtParam(index);
|
||
let notChangeP = this.GetPointAtParam(nextIndex);
|
||
//原先的弦长的一半
|
||
let oldChordLengthHalf = needChangeP.distanceTo(notChangeP) * 0.5;
|
||
//弓高
|
||
let arcHeight = oldChordLengthHalf * d.bul;
|
||
needChangeP.add(vec);
|
||
let newChordLengthHalf = needChangeP.distanceTo(notChangeP) * 0.5;
|
||
d.bul = arcHeight / newChordLengthHalf;
|
||
}
|
||
};
|
||
ChangeBul(frontIndex, frontIndex);
|
||
ChangeBul(nextIndex, index);
|
||
//修改顶点
|
||
this._LineData[index].pt.add(AsVector2(moveVLoc));
|
||
}
|
||
this.Update();
|
||
}
|
||
_ReadFile(file)
|
||
{
|
||
super._ReadFile(file);
|
||
let ver = file.Read();
|
||
this._LineData.length = 0;
|
||
let cout = file.Read();
|
||
for (let i = 0; i < cout; i++) {
|
||
let v = new Vector2().fromArray(file.Read());
|
||
let bul = file.Read();
|
||
this._LineData.push({ pt: v, bul: bul });
|
||
}
|
||
if (ver > 1)
|
||
this._ClosedMark = file.Read();
|
||
}
|
||
//对象将自身数据写入到文件.
|
||
WriteFile(file)
|
||
{
|
||
super.WriteFile(file);
|
||
file.Write(2);
|
||
file.Write(this._LineData.length);
|
||
for (let l of this._LineData) {
|
||
file.Write(l.pt.toArray());
|
||
file.Write(l.bul);
|
||
}
|
||
file.Write(this._ClosedMark);
|
||
}
|
||
};
|
||
Polyline = Polyline_1 = __decorate([
|
||
Factory
|
||
], Polyline);
|
||
const TempPolyline = new Polyline();
|
||
|
||
export { Polyline, TempPolyline };
|
||
//# sourceMappingURL=api.esm.js.map
|