重构倒角代码,添加单元测试代码

pull/198/MERGE
ChenX 6 years ago
parent f714ec0c49
commit 2c2bf1051b

@ -0,0 +1,29 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`双圆多段线倒角 1`] = `1208.3752684304043`;
exports[`双圆多段线倒角 2`] = `1208.375268430404`;
exports[`双圆多段线倒角 3`] = `1208.3752684304043`;
exports[`双圆多段线倒角 4`] = `1208.3752684304045`;
exports[`双圆多段线倒角 5`] = `1200.7933322000165`;
exports[`双圆多段线倒角 6`] = `1200.7933322000165`;
exports[`双圆多段线倒角 7`] = `1200.7933322000172`;
exports[`双圆多段线倒角 8`] = `1200.7933322000174`;
exports[`多段线闭合标志首尾有弧 1`] = `1357.0494514094473`;
exports[`多段线闭合标志首尾有弧 2`] = `1357.0494514094473`;
exports[`多段线闭合标志首尾有弧 3`] = `1357.0494514094473`;
exports[`多段线闭合标志首尾有弧 4`] = `1357.049451409447`;
exports[`多段线首尾倒角有圆弧 1`] = `1682.5946408278246`;
exports[`多段线首尾倒角有圆弧 2`] = `1682.5946408278244`;

@ -0,0 +1,88 @@
import { FilletUtils } from "../../src/Add-on/FilletUtils";
import { Polyline } from "../../src/DatabaseServices/Polyline";
import { LoadEntityFromFileData } from "../Utils/LoadEntity.util";
import { PromptEntityResult } from "../../src/Editor/PromptResult";
import { Vector3 } from "three";
//file.only
let fillet = new FilletUtils();
test('多段线首尾倒角有圆弧', () =>
{
TestFilletPolyline(
[1, ["Polyline", 1, 1, 3, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 375.4375364613715, 208.47861732064138, 0, 1], 2, 10, [-171.7455621301774, 180.2537770455558], -0.40012702806387124, [171.7455621301774, 180.2537770455558], 0, [221.74556213017775, 180.2537770455558], 0, [221.74556213017775, -72.32657236535573], 0, [-3.441731405810515, -260.5746253213081], 0, [-137.90517157255817, -260.5746253213081], 0, [-137.90517157255817, -37.838102302837456], 0, [-221.7455621301774, -37.838102302837456], 0, [-221.7455621301774, 180.2537770455558], 0, [-171.7455621301774, 180.2537770455558], 0, false]],
50,
[new Vector3(178, 388), new Vector3(224, 460)]
)
});
test('多段线闭合标志首尾有弧', () =>
{
TestFilletPolyline(
[1, ["Polyline", 1, 1, 30, false, 1, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2175.4664840507453, 164.7609132081223, 0, 1], 2, 6, [-171.7455621301774, 180.25377704555575], 0, [-221.7455621301774, 180.2537770455558], 0, [-221.7455621301774, -37.838102302837456], 0, [221.74556213017786, -37.838102302837456], 0, [221.74556213017786, 180.2537770455558], 0, [171.7455621301774, 180.25377704555575], 0.4001270280638712, true]],
50,
[
new Vector3().fromArray([1978.720921920568, 345.01469025367805, 0]),
new Vector3().fromArray([2059.1464975640683, 384.8918356028163, 0]),
new Vector3().fromArray([2316.870065964417, 369.68282338998426, 0]),
new Vector3().fromArray([2372.2120461809227, 345.01469025367805, 0])
]
);
});
test('双圆多段线倒角', () =>
{
//双圆
TestFilletPolyline(
[1, ["Polyline", 1, 1, 36, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 669.3126866217343, -478.9366812060718, 0, 1], 2, 3, [76.66953810162238, -86.81133085285757], -2.219258954152199, [71.33090803386779, 64.97443486580221], -3.5679817627813364, [76.66953810162238, -86.81133085285757], 0, false]],
50,
[
new Vector3().fromArray([655.0295275228787, -390.0373584486947, 0]),
new Vector3().fromArray([794.1254343980869, -359.44086563487383, 0]),
new Vector3().fromArray([695.0427029021831, -579.0860583389696, 0]),
new Vector3().fromArray([776.3783481250921, -599.2347834977858, 0])
]
);
//双圆 CloseMark
TestFilletPolyline(
[1, ["Polyline", 1, 1, 37, false, 1, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 669.3126866217344, -478.9366812060718, 0, 1], 2, 2, [70.71115893702552, -76.919569940087], -2.2862716785205572, [71.33090803386779, 64.97443486580221], -3.8599589051482246, true]],
50,
[
new Vector3().fromArray([655.0295275228787, -390.0373584486947, 0]),
new Vector3().fromArray([794.1254343980869, -359.44086563487383, 0]),
new Vector3().fromArray([695.0427029021831, -579.0860583389696, 0]),
new Vector3().fromArray([776.3783481250921, -599.2347834977858, 0])
]
);
});
function TestFilletPolyline(
curveData: any,
radius: number,
filletPts: Vector3[]
)
{
let pl = LoadEntityFromFileData(curveData)[0] as Polyline;
fillet.m_FilletRadius = radius;
let es1 = new PromptEntityResult(pl, undefined);
let es2 = new PromptEntityResult(pl, undefined);
for (let i = 0; i < filletPts.length / 2; i++)
{
es1.Point = filletPts[i * 2];
es2.Point = filletPts[i * 2 + 1];
let res = fillet.Fillet(es1, es2);
expect(res.cu1.Length).toMatchSnapshot();
pl.Reverse();
res = fillet.Fillet(es1, es2);
expect(res.cu1.Length).toMatchSnapshot();
}
}

@ -0,0 +1,18 @@
import { CADFile } from "../../src/DatabaseServices/CADFile";
import { Entity } from "../../src/DatabaseServices/Entity";
import { Polyline } from "../../src/DatabaseServices/Polyline";
import { Factory } from "../../src/DatabaseServices/CADFactory";
Factory(Polyline);
export function LoadEntityFromFileData(data)
{
let file = new CADFile();
file.Data = data;
let ens: Entity[] = [];
let count = file.Read();
for (let i = 0; i < count; i++)
{
ens.push(file.ReadObject(undefined) as Entity);
}
return ens;
}

@ -1,93 +1,26 @@
import { Matrix4, Vector3 } from 'three';
import { end } from 'xaop';
import { app } from '../ApplicationServices/Application';
import { curveLinkGroup, GetPointAtCurveDir, Vec3DTo2D } from '../Common/CurveUtils';
import { KeyWord } from '../Common/InputState';
import { KeyBoard } from '../Common/KeyEnum';
import { FixedNotZero } from '../Common/Utils';
import { Arc } from '../DatabaseServices/Arc';
import { Circle } from '../DatabaseServices/Circle';
import { Curve } from '../DatabaseServices/Curve';
import { Line } from '../DatabaseServices/Line';
import { Polyline } from '../DatabaseServices/Polyline';
import { Command } from '../Editor/CommandMachine';
import { JigUtils } from '../Editor/JigUtils';
import { PromptEntityResult, PromptStatus } from '../Editor/PromptResult';
import { angle, equalv3, isParallelTo, midPoint } from '../Geometry/GeUtils';
import { IntersectOption } from '../GraphicsSystem/IntersectWith';
enum ExtendType
{
Start = 1,
End = 2,
}
interface FilletRes
{
cu1: Curve;
cu2: Curve;
arc: Arc;
}
type CurveExtend = { Curve: Curve, ExtType: ExtendType };
import { FilletUtils } from './FilletUtils';
const RADKEY = 'filletRadius';
function Encode(res: PromptEntityResult, enMap: (PromptEntityResult[])[])
{
if (res.Entity instanceof Line)
{
enMap[0].push(res);
return 1;
}
else if (res.Entity instanceof Arc)
{
enMap[1].push(res);
return 2;
}
else if (res.Entity instanceof Polyline)
{
enMap[2].push(res);
return 4;
}
}
//把圆转换成圆弧,避免圆参与计算.
function CircleEnResToArc(enRes: PromptEntityResult)
{
if (enRes.Entity instanceof Circle)
{
let an = angle(enRes.Point.clone().applyMatrix4(enRes.Entity.OCSInv)) + Math.PI;
let arc = new Arc(new Vector3(), enRes.Entity.Radius, an, an + 0.1);
arc.ApplyMatrix(enRes.Entity.OCS);
arc.Center = enRes.Entity.Center;
enRes.Entity = arc;
//@ts-ignore
enRes.IsCircle = true;
}
}
function GetFilletCurve(enRes: PromptEntityResult): any[]
{
if (enRes.Entity instanceof Polyline)
{
let pl = enRes.Entity;
let param = pl.GetParamAtPoint(pl.GetClosestPointTo(enRes.Point, false));
let paramF = Math.floor(param);
return [pl.GetCurveAtParam(param), paramF];
}
else
return [enRes.Entity, NaN];
}
export class CommandFillet implements Command
{
m_FilletRadius: number = 0;
private m_FilletUtils = new FilletUtils();
constructor()
{
let radStr = window.localStorage.getItem(RADKEY);
if (radStr)
this.m_FilletRadius = parseFloat(radStr);
this.UpdateFilletRadius(parseFloat(radStr));
}
async exec()
{
@ -105,8 +38,6 @@ export class CommandFillet implements Command
app.m_Editor.UCSMatrix = oldUcs;
if (!enRes1) return;
CircleEnResToArc(enRes1);
enRes1.Entity.UpdateJigMaterial();
let lastCu: Curve;
@ -125,10 +56,9 @@ export class CommandFillet implements Command
app.m_Editor.UpdateScreen();
return;
}
CircleEnResToArc(res2);
app.m_Editor.UCSMatrix = res2.Entity.OCS;
res2.Entity.UpdateJigMaterial();
let fres = this.Fillet(enRes1, res2);
let fres = this.m_FilletUtils.Fillet(enRes1, res2);
for (let v in fres)
if (fres[v])
JigUtils.Draw(fres[v])
@ -164,7 +94,7 @@ export class CommandFillet implements Command
if (enRes2.Status === PromptStatus.String)
{
let rad = parseFloat(enRes2.StringResult);
if (rad !== 0)
if (rad !== 0 && !isNaN(rad))
this.UpdateFilletRadius(rad);
}
else if (enRes2.Status !== PromptStatus.None)
@ -180,8 +110,7 @@ export class CommandFillet implements Command
if (enRes2.Status === PromptStatus.OK)
{
CircleEnResToArc(enRes2);
let res = this.Fillet(enRes1, enRes2);
let res = this.m_FilletUtils.Fillet(enRes1, enRes2);
if (!res)
return;
@ -206,743 +135,12 @@ export class CommandFillet implements Command
UpdateFilletRadius(newRadius: number)
{
if (newRadius < 0)
if (newRadius < 0 || isNaN(newRadius))
app.m_Editor.Prompt("半径不能为负!");
this.m_FilletRadius = Math.abs(newRadius);
window.localStorage.setItem(RADKEY, this.m_FilletRadius.toString());
}
Fillet(enRes1: PromptEntityResult, enRes2: PromptEntityResult): FilletRes
{
let { enType, enMap } = this.EnCode(enRes1, enRes2);
if (enType === 4 && enRes1.Entity === enRes2.Entity)
return this.FilletPolyLineSelf(enRes1, enRes2);
else if (enType >= 4)
return this.FilletPolylineAndCurve(enRes1, enRes2);
let interPts = this.GetIntersectAndSort(enRes1, enRes2, enType, enMap);
if (interPts.length === 0
|| (interPts.length === 1 && (enType & 2)))//圆弧相切
{
if (enType === 1)
return this.FilletParallelLine(enRes1, enRes2);
else if (enType === 3)
return this.FilletLineAndArc(enMap, enRes1);
else if (enType === 2)
return this.FilletArcAndArc(enRes1, enRes2);
return;
}
return this.FilletLineOrArc(enRes1, enRes2, interPts);
}
FilletLineOrArc(enRes1: PromptEntityResult, enRes2: PromptEntityResult, interPts: Vector3[]): FilletRes
{
let iPt = interPts[0];
//裁剪延伸,使两条线组成一个尖角
let splitedCu1 = this.SplitCurve(enRes1, iPt, interPts);
let splitedCu2 = this.SplitCurve(enRes2, iPt, interPts);
let fRadius = this.m_FilletRadius;
//按下shift键时半径归0
if (app.m_Editor.m_KeyCtrl.KeyIsDown(KeyBoard.Shift))
fRadius = 0;
if (enRes1.Entity instanceof Arc && enRes2.Entity instanceof Arc)
return this.FilletArcAndArc(enRes1, enRes2);
let res: FilletRes = { cu1: splitedCu1.Curve, cu2: splitedCu2.Curve, arc: undefined };
if (fRadius > 0)
{
//角平分线向量.
let bisectorVec: Vector3 = new Vector3();
let c1Derv = this.ComputerDerv(splitedCu1, bisectorVec);
let c2Derv = this.ComputerDerv(splitedCu2, bisectorVec);
//方向相反
if (equalv3(bisectorVec, new Vector3()))
return;
//相切
if (equalv3(c2Derv, c1Derv))
{
bisectorVec.set(0, 0, 0);
c1Derv = this.ComputerDerv2(splitedCu1, bisectorVec);
c2Derv = this.ComputerDerv2(splitedCu2, bisectorVec);
}
let cu1RoOcsInv = new Matrix4().extractRotation(splitedCu1.Curve.OCSInv);
[c1Derv, c2Derv, bisectorVec].forEach(v => v.applyMatrix4(cu1RoOcsInv));
let offCu1 = splitedCu1.Curve.GetOffsetCurves(
fRadius * -Math.sign(c1Derv.cross(bisectorVec).z))[0];
let offCu2 = splitedCu2.Curve.GetOffsetCurves(
fRadius * -Math.sign(c2Derv.cross(bisectorVec).z))[0];
if (!offCu1 || !offCu2)
return;
//测试绘制
offCu1.ColorIndex = 6;
offCu2.ColorIndex = 6;
JigUtils.Draw(offCu1.Clone());
JigUtils.Draw(offCu2.Clone());
let center = offCu1.IntersectWith(offCu2, IntersectOption.OnBothOperands)
.sort((p1, p2) =>
{
return p1.distanceToSquared(iPt) - p2.distanceToSquared(iPt);
})[0];
if (!center)
return;
let arcP1 = splitedCu1.Curve.GetClosestPointTo(center, true);
let arcP2 = splitedCu2.Curve.GetClosestPointTo(center, true);
if (!splitedCu1.Curve.PtOnCurve(arcP1) || !splitedCu2.Curve.PtOnCurve(arcP2))
return;
//时针校验
let v1 = arcP1.clone().sub(center).applyMatrix4(cu1RoOcsInv);
let v2 = arcP2.clone().sub(center).applyMatrix4(cu1RoOcsInv);
//绘制圆弧
let arc = new Arc(new Vector3(), this.m_FilletRadius, angle(v1), angle(v2), v1.cross(v2).z < 0);
arc.ApplyMatrix(splitedCu1.Curve.OCS);
arc.Center = center;
res.arc = arc;
//延伸或者裁剪到圆弧点
this.ExtendPt(splitedCu1, arcP1);
this.ExtendPt(splitedCu2, arcP2);
}
return res;
}
FilletPolyLineSelf(enRes1: PromptEntityResult, enRes2: PromptEntityResult): FilletRes
{
let pl = enRes1.Entity as Polyline;
let param1 = pl.GetParamAtPoint(pl.GetClosestPointTo(enRes1.Point, false));
let param2 = pl.GetParamAtPoint(pl.GetClosestPointTo(enRes2.Point, false));
if (param1 > param2) [param1, param2] = [param2, param1];
let parF1 = Math.floor(param1);
let parF2 = Math.floor(param2);
//共线
if (parF1 === parF2)
return;
let c1 = pl.GetCurveAtParam(param1);
let c2 = pl.GetCurveAtParam(param2);
if (equalv3(c1.GetFistDeriv(1).normalize(), c2.GetFistDeriv(0).normalize()))
return;
let rem = parF2 - parF1;
if (rem === 1 || (rem + 1 === pl.EndParam))//相邻线段倒角
{
let es1 = new PromptEntityResult();
es1.Entity = c1;
es1.Point = enRes1.Point;
let es2 = new PromptEntityResult();
es2.Entity = c2;
es2.Point = enRes2.Point;
let res = this.Fillet(es1, es2);
if (res && res.arc)
{
let pln = pl.Clone();
//修正凸度
if (res.cu1 instanceof Arc)
pln.SetBulgeAt(parF1, res.cu1.Bul);
if (res.cu2 instanceof Arc)
pln.SetBulgeAt(parF2, res.cu2.Bul);
let sp2d = Vec3DTo2D(res.arc.StartPoint.applyMatrix4(pln.OCSInv));
let ep2d = Vec3DTo2D(res.arc.EndPoint.applyMatrix4(pln.OCSInv));
let isNeighbor = rem === 1;
//#IOX26
if (isNeighbor && res.cu1 instanceof Arc && res.cu2 instanceof Arc)
{
let ins = res.cu1.IntersectWith(res.cu2, IntersectOption.OnBothOperands);
if (ins.length === 1 && !equalv3(pln.StartPoint, ins[0]))
isNeighbor = false;
}
if (isNeighbor)
{
pln.SetPointAt(parF2, ep2d);
pln.AddVertexAt(parF2, sp2d);
pln.SetBulgeAt(parF2, res.arc.Bul);
}
else//首尾
{
pln.SetPointAt(0, sp2d);
let plPtCount = pln.NumberOfVertices;
if (pln.EndParam === plPtCount)//CloseMark
{
pln.AddVertexAt(plPtCount, ep2d);
pln.SetBulgeAt(plPtCount, -res.arc.Bul);
}
else
{
pln.SetPointAt(plPtCount - 1, ep2d);
pln.SetBulgeAt(plPtCount - 1, -res.arc.Bul);
pln.AddVertexAt(plPtCount, sp2d);
}
}
return {
cu1: pln,
cu2: undefined,
arc: undefined
};
}
}
else//自交多段线
{
let interPts = c1.IntersectWith(c2, IntersectOption.OnBothOperands);
if (interPts.length === 0)
return;
if (interPts.length === 2 && c1.GetParamAtPoint(interPts[0]) > c1.GetParamAtPoint(interPts[1]))
interPts.reverse();
let ipt = interPts[0];
let splitParam1 = Math.floor(param1) + c1.GetParamAtPoint(ipt);
let splitParam2 = Math.floor(param2) + c2.GetParamAtPoint(ipt);
let cus = pl.GetSplitCurves([splitParam1, splitParam2]);
if (cus.length >= 2)
{
cus.splice(1, 1);
let pl1 = cus[0];
for (let i = 1; i < cus.length; i++)
pl1.Join(cus[i]);
let es1 = new PromptEntityResult();
es1.Entity = pl1;
es1.Point = c1.GetPointAtParam(0.1);
let es2 = new PromptEntityResult();
es2.Entity = pl1;
es2.Point = c2.GetPointAtParam(0.9);
return this.FilletPolyLineSelf(es1, es2);
}
}
}
FilletPolylineAndCurve(enRes1: PromptEntityResult, enRes2: PromptEntityResult): FilletRes
{
let arr1 = GetFilletCurve(enRes1);
let arr2 = GetFilletCurve(enRes2);
let [cu1, paramF1] = arr1;
let [cu2, paramF2] = arr2;
let es1 = new PromptEntityResult();
es1.Entity = cu1;
es1.Point = enRes1.Point;
let es2 = new PromptEntityResult();
es2.Entity = cu2;
es2.Point = enRes2.Point;
let fres = this.Fillet(es1, es2);
if (fres)
{
let pln: Polyline;
let isFirst = false;
let cus: Curve[] = [];
if (fres.cu1)
{
if (enRes1.Entity instanceof Polyline)
{
isFirst = true;
pln = enRes1.Entity.Clone();
let xcus = enRes1.Entity.Explode();
xcus[paramF1] = fres.cu1;
cus.push(...xcus);
}
//@ts-ignore
else if (!enRes1.IsCircle)
cus.push(fres.cu1);
}
if (fres.arc)
cus.push(fres.arc);
if (fres.cu2)
{
if (enRes2.Entity instanceof Polyline)
{
if (!pln)
pln = enRes2.Entity.Clone();
let xcus = enRes2.Entity.Explode();
xcus[paramF2] = fres.cu2;
cus.push(...xcus);
}
//@ts-ignore
else if (!enRes2.IsCircle)
cus.push(fres.cu2);
}
let groups = curveLinkGroup(cus);
for (let g of groups)
{
if (g.includes(fres.cu1) || g.includes(fres.cu2))
{
pln.LineData = [];
pln.ApplyMatrix(pln.OCSInv);
pln.CloseMark = false;
for (let cu of g)
pln.Join(cu);
if (isFirst)
return { cu1: pln, cu2: undefined, arc: undefined };
else
return { cu1: undefined, cu2: pln, arc: undefined };
}
}
}
return undefined;
}
FilletPolyLineAllAngular(enRes1: PromptEntityResult): FilletRes
{
let pl = enRes1.Entity as Polyline;
let cus = pl.Explode();
let count = cus.length;
if (pl.IsClose)
cus.push(cus[0]);
let ncus = [];
for (let i = 0; i < count; i++)
{
let c1 = cus[i];
let c2 = cus[i + 1];
ncus.push(c1);
if (!c2)
break;
if (equalv3(c1.GetFistDeriv(1).normalize(), c2.GetFistDeriv(0).normalize()))
continue;
let es1 = new PromptEntityResult();
es1.Entity = c1;
es1.Point = c1.EndPoint;
let es2 = new PromptEntityResult();
es2.Entity = c2;
es2.Point = c2.StartPoint;
let fres = this.Fillet(es1, es2);
if (fres)
{
if (fres.cu1)
c1.CopyFrom(fres.cu1);
if (fres.cu2)
c2.CopyFrom(fres.cu2);
if (fres.arc)
ncus.push(fres.arc);
}
}
let pln = pl.Clone();
pln.LineData = [];
pln.ApplyMatrix(pln.OCSInv);
pln.CloseMark = false;
for (let cu of ncus)
pln.Join(cu);
pln.CloseMark = pl.CloseMark;
return {
cu1: pln,
cu2: undefined,
arc: undefined
};
}
/**
* 线
* @param enRes1
* @param enRes2
* @returns parallel line
*/
FilletParallelLine(enRes1: PromptEntityResult, enRes2: PromptEntityResult): FilletRes
{
let l1 = enRes1.Entity as Line;
let l2 = enRes2.Entity as Line;
let l1Derv = l1.GetFistDeriv(0);
if (!isParallelTo(l1Derv, l2.GetFistDeriv(0)))
return;
let par1 = l2.GetClosestAtPoint(l1.StartPoint, true).param;
let par2 = l2.GetClosestAtPoint(l1.EndPoint, true).param;
if (!l1.ParamOnCurve(par1) && !l1.ParamOnCurve(par2))
return;
let lineClone1 = l1.Clone();
let lineClone2 = l2.Clone();
let par = l1.GetClosestAtPoint(enRes1.Point, true).param;
let parFix = Math.round(par);
let ptFix = lineClone1.GetPointAtParam(parFix);
let ptL2Fix = lineClone2.GetClosestAtPoint(ptFix, true).closestPt;
if ((par1 > par2) === (parFix === 1))
lineClone2.StartPoint = ptL2Fix;
else
lineClone2.EndPoint = ptL2Fix;
let arcCenter = midPoint(ptFix, ptL2Fix);
let sv = ptFix.sub(arcCenter).applyMatrix4(l1.OCSInv);
let ev = ptL2Fix.sub(arcCenter).applyMatrix4(l2.OCSInv);
if (parFix === 0)
l1Derv.negate();
let arc = new Arc(new Vector3(), ptFix.distanceTo(ptL2Fix) / 2, angle(sv), angle(ev), ev.cross(l1Derv.applyMatrix4(l1.OCSInv)).z > 0);
arc.ApplyMatrix(l1.OCS);
arc.Center = arcCenter;
return {
cu1: lineClone1,
cu2: lineClone2,
arc
};
}
/**
* .
* @param enRes1
* @param enRes2
* @returns arc and arc
*/
FilletArcAndArc(enRes1: PromptEntityResult, enRes2: PromptEntityResult): FilletRes
{
let a1 = enRes1.Entity as Arc;
let a2 = enRes2.Entity as Arc;
let arcO1 = a1.GetOffsetCurves(this.m_FilletRadius * (a1.IsClockWise ? -1 : 1))[0];
let arcO2 = a2.GetOffsetCurves(this.m_FilletRadius * (a2.IsClockWise ? -1 : 1))[0];
arcO1.ColorIndex = 6;
arcO2.ColorIndex = 6;
JigUtils.Draw(arcO1);
JigUtils.Draw(arcO2);
//求交
let intPts = arcO1.IntersectWith(arcO2, IntersectOption.ExtendBoth);
if (intPts.length === 0)
return;//无交点无法倒角
//两选择点的中点
let clickMidp = midPoint(enRes1.Point, enRes2.Point);//用来选择合适的交点
//选择合适的交点
intPts.sort((p1, p2) =>
{
return p1.distanceToSquared(clickMidp) - p2.distanceToSquared(clickMidp);
});
//圆弧圆心
let narcCenter = intPts[0];
let narcP1 = a1.GetClosestPointTo(narcCenter, true);//两圆弧和相切弧的交点
let narcP2 = a2.GetClosestPointTo(narcCenter, true);
let tempCircle = new Circle(narcCenter, this.m_FilletRadius);
let closestPt = a1.GetClosestPointTo(a2.Center, true);//两曲线距离对方圆心最近的点
let narcMP = tempCircle.GetClosestPointTo(closestPt, false);//相切圆距离closestPt最近的点
//构造圆弧
let narc = new Arc().ApplyMatrix(a1.OCS).FromThreePoint(narcP1, narcMP, narcP2);
let a1Clone = a1.Clone();
let a2Clone = a2.Clone();
let a1Param = a1.GetParamAtPoint(narcP1);
let a2Param = a2.GetParamAtPoint(narcP2);
let a1Derv = a1.GetFistDeriv(a1Param).normalize();
let a2Derv = a2.GetFistDeriv(a2Param).normalize();
let narcDerv0 = narc.GetFistDeriv(0).normalize();
let narcDerv1 = narc.GetFistDeriv(1).normalize();
//裁剪圆弧
if (equalv3(a1Derv, narcDerv0))
a1Clone.EndPoint = narcP1;
else
a1Clone.StartPoint = narcP1
if (equalv3(a2Derv, narcDerv1))
a2Clone.StartPoint = narcP2;
else
a2Clone.EndPoint = narcP2
return {
cu1: a1Clone,
cu2: a2Clone,
arc: narc
};
}
/**
* 线.
* @param enRes1
* @param enRes2
* @returns line and cir
*/
FilletLineAndArc(enMap: (PromptEntityResult[])[], enRes1: PromptEntityResult): FilletRes | undefined
{
let lineRes = enMap[0][0];
let arcRes = enMap[1][0];
let line = lineRes.Entity as Line;
let arc = arcRes.Entity as Arc;
let dir = GetPointAtCurveDir(line, arc.Center) ? 1 : -1;
let lineO = line.GetOffsetCurves(this.m_FilletRadius * dir)[0];
let arcO = arc.GetOffsetCurves(this.m_FilletRadius * (arc.IsClockWise ? -1 : 1))[0];// tip面积逆时针为正, 顺时针为负.
lineO.ColorIndex = 6;
arcO.ColorIndex = 6;
JigUtils.Draw(lineO);
JigUtils.Draw(arcO);
//求交
let intPts = lineO.IntersectWith(arcO, IntersectOption.ExtendBoth);
if (intPts.length === 0)
return;//无交点无法倒角
//两选择点的中点
let clickMidp = midPoint(lineRes.Point, arcRes.Point);
//选择适合的交点。
intPts.sort((p1, p2) =>
{
return p1.distanceToSquared(clickMidp) - p2.distanceToSquared(clickMidp);
});
//圆弧圆心
let arcCenter = intPts[0];
let arcP1 = line.GetClosestPointTo(arcCenter, true);//直线与相切圆的交点
let arcP2 = arc.GetClosestPointTo(arcCenter, true);//圆弧与相切圆的交点
let tempCircle = new Circle(arcCenter, this.m_FilletRadius);
let { closestPt, param } = line.GetClosestAtPoint(arc.Center, true);
let arcMP = tempCircle.GetClosestPointTo(closestPt, false);
//构造圆弧
let narc = new Arc().ApplyMatrix(arc.OCS).FromThreePoint(arcP1, arcMP, arcP2);
//裁剪线
let lineClone = line.Clone();
let arcClone = arc.Clone();
let p1Param = line.GetParamAtPoint(arcP1);
if (p1Param > param)
lineClone.StartPoint = arcP1;
else
lineClone.EndPoint = arcP1;
//裁剪圆弧
let arcParam = arc.GetParamAtPoint(arcP2);
let arcDerv = arc.GetFistDeriv(arcParam).normalize();
let narcDerv = narc.GetFistDeriv(1).normalize();
if (equalv3(arcDerv, narcDerv))
arcClone.StartPoint = arcP2;
else
arcClone.EndPoint = arcP2;
//先选直线为真
if (enRes1.Entity === line)
return {
cu1: lineClone,
cu2: arcClone,
arc: narc
}
else
return {
cu1: arcClone,
cu2: lineClone,
arc: narc
}
}
//获得两曲线的交点,并且排序交点.
private GetIntersectAndSort(enRes: PromptEntityResult, enRes2: PromptEntityResult, enType: number, enMap: PromptEntityResult[][])
{
let interPts = enRes.Entity.IntersectWith(enRes2.Entity, IntersectOption.ExtendBoth);
if (interPts.length > 1)
{
let baseP: Vector3;
if (enType & 1) //如果有直线,那么用直线
baseP = enMap[0][0].Point;
else if (enType === 2) //如果都是圆弧,那么取中点
baseP = midPoint(enMap[1][0].Point, enMap[1][1].Point);
interPts.sort((p1, p2) => p1.distanceToSquared(baseP) - p2.distanceToSquared(baseP));
}
return interPts;
}
/**
* ,:
* # 1:line 2:arc 4:polyline
* @param enRes
* @param enRes2
* @returns
*/
private EnCode(enRes: PromptEntityResult, enRes2: PromptEntityResult)
{
let enMap: (PromptEntityResult[])[] = [[], [], []];
let enType = 0;
enType |= Encode(enRes, enMap);
enType |= Encode(enRes2, enMap);
return { enType, enMap };
}
//计算曲线在相交处的切线,取真实的切线
private ComputerDerv(cuRes: CurveExtend, dervSum: Vector3)
{
let derv: Vector3;
let cu = cuRes.Curve;
if (cuRes.ExtType === ExtendType.Start)
{
derv = cu.GetFistDeriv(0).normalize();
dervSum.add(derv);
}
else
{
derv = cu.GetFistDeriv(cu.EndParam).normalize();
dervSum.add(derv.clone().negate());
}
return derv;
}
// 计算曲线在相交处的切线,取起点到终点的切线.(当曲线相切时调用此方法.)
private ComputerDerv2(cuRes: CurveExtend, dervSum: Vector3)
{
let cu = cuRes.Curve;
let derv = cu.EndPoint.sub(cu.StartPoint);
if (cuRes.ExtType === ExtendType.Start)
dervSum.add(derv);
else
dervSum.add(derv.clone().negate());
return derv;
}
// 延伸或者裁剪到指定的圆弧的点.
ExtendPt(cu: CurveExtend, newP: Vector3)
{
if (cu.ExtType === ExtendType.Start)
cu.Curve.StartPoint = newP;
else
cu.Curve.EndPoint = newP;
}
/**
* 线,
*
* @param cu 线
* @param interPt
* @param pickPoint
* @returns 线
*/
SplitCurve(enRes: PromptEntityResult, interPt: Vector3, interPts: Vector3[]): CurveExtend
{
let cu = enRes.Entity as Curve;
let pickPoint = enRes.Point;
let cp = cu.GetClosestPointTo(pickPoint, false);
let cus = cu.GetSplitCurvesByPts([interPt]);
if (cus.length === 0)
cus.push(cu.Clone() as Curve);
else if (cus.length === 2)
cus.sort((c1: Curve, c2: Curve) =>
{
return c1.GetClosestPointTo(cp, false).distanceTo(cp)
< c2.GetClosestPointTo(cp, false).distanceTo(cp) ? -1 : 1;
});
let exType = undefined;
let newCu = cus[0];
if (newCu instanceof Line)
newCu.Extend(newCu.GetParamAtPoint(interPt));//延伸到需要的长度
else if (newCu instanceof Arc)
{
let arc = newCu as Arc;
if (cus.length === 1)
if (!cu.PtOnCurve(interPt))
{
if (cu.PtOnCurve(interPts[1]))
{
//交点参数
let iparam = arc.GetParamAtPoint(interPts[1]);
let pickParam = arc.GetParamAtAngle(arc.GetAngleAtPoint(pickPoint));
if (pickParam > iparam)
{
arc.EndAngle = arc.GetAngleAtPoint(interPt);
exType = ExtendType.End;
}
else
{
arc.StartAngle = arc.GetAngleAtPoint(interPt);
exType = ExtendType.Start;
}
}
else
{
//终点,起点
interPts = interPts.sort((p1, p2) =>
{
return arc.ComputeAnlge(arc.GetAngleAtPoint(p1)) - arc.ComputeAnlge(arc.GetAngleAtPoint(p2));
});
if (interPt === interPts[0])
{
arc.EndAngle = arc.GetAngleAtPoint(interPt);
exType = ExtendType.End;
}
else
{
arc.StartAngle = arc.GetAngleAtPoint(interPt);
exType = ExtendType.Start;
}
}
}
}
if (exType === undefined)
{
//使用equalv3时由于精度误差导致的判断错误
if (interPt.manhattanDistanceTo(newCu.StartPoint) < interPt.manhattanDistanceTo(newCu.EndPoint))
exType = ExtendType.Start;
else
exType = ExtendType.End;
}
return { Curve: newCu, ExtType: exType };
this.m_FilletUtils.m_FilletRadius = this.m_FilletRadius;
}
async SelectCurve(keyword: KeyWord[]): Promise<PromptEntityResult>
@ -1010,7 +208,7 @@ export class CommandFillet implements Command
return;
}
let fres = this.FilletPolyLineAllAngular(e);
let fres = this.m_FilletUtils.FilletPolyLineAllAngular(e);
if (fres && fres.cu1)
JigUtils.Draw(fres.cu1);
@ -1041,7 +239,7 @@ export class CommandFillet implements Command
lastPl.RestoreJigMaterial();
if (enRes.Status === PromptStatus.OK)
{
let fres = this.FilletPolyLineAllAngular(enRes);
let fres = this.m_FilletUtils.FilletPolyLineAllAngular(enRes);
if (fres && fres.cu1)
enRes.Entity.CopyFrom(fres.cu1);
}

@ -0,0 +1,811 @@
import { Matrix4, Vector3 } from "three";
import { curveLinkGroup, GetPointAtCurveDir, Vec3DTo2D } from "../Common/CurveUtils";
import { Arc } from "../DatabaseServices/Arc";
import { Circle } from "../DatabaseServices/Circle";
import { Curve } from "../DatabaseServices/Curve";
import { Line } from "../DatabaseServices/Line";
import { Polyline } from "../DatabaseServices/Polyline";
// import { JigUtils } from "../Editor/JigUtils";
import { PromptEntityResult } from "../Editor/PromptResult";
import { angle, equalv3, isParallelTo, midPoint } from "../Geometry/GeUtils";
import { IntersectOption } from "../GraphicsSystem/IntersectWith";
function Encode(res: PromptEntityResult, enMap: (PromptEntityResult[])[])
{
if (res.Entity instanceof Line)
{
enMap[0].push(res);
return 1;
}
else if (res.Entity instanceof Arc)
{
enMap[1].push(res);
return 2;
}
else if (res.Entity instanceof Polyline)
{
enMap[2].push(res);
return 4;
}
}
//把圆转换成圆弧,避免圆参与计算.
function CircleEnResToArc(enRes: PromptEntityResult)
{
if (enRes.Entity instanceof Circle)
{
let an = angle(enRes.Point.clone().applyMatrix4(enRes.Entity.OCSInv)) + Math.PI;
let arc = new Arc(new Vector3(), enRes.Entity.Radius, an, an + 0.1);
arc.ApplyMatrix(enRes.Entity.OCS);
arc.Center = enRes.Entity.Center;
enRes.Entity = arc;
//@ts-ignore
enRes.IsCircle = true;
}
}
function GetFilletCurve(enRes: PromptEntityResult): any[]
{
if (enRes.Entity instanceof Polyline)
{
let pl = enRes.Entity;
let param = pl.GetParamAtPoint(pl.GetClosestPointTo(enRes.Point, false));
let paramF = Math.floor(param);
return [pl.GetCurveAtParam(param), paramF];
}
else
return [enRes.Entity, NaN];
}
enum ExtendType
{
Start = 1,
End = 2,
}
interface FilletRes
{
cu1: Curve;
cu2: Curve;
arc: Arc;
}
type CurveExtend = { Curve: Curve, ExtType: ExtendType };
export class FilletUtils
{
m_FilletRadius: number;
Fillet(enRes1: PromptEntityResult, enRes2: PromptEntityResult): FilletRes
{
CircleEnResToArc(enRes1);
CircleEnResToArc(enRes2);
let { enType, enMap } = this.EnCode(enRes1, enRes2);
if (enType === 4 && enRes1.Entity === enRes2.Entity)
return this.FilletPolyLineSelf(enRes1, enRes2);
else if (enType >= 4)
return this.FilletPolylineAndCurve(enRes1, enRes2);
let interPts = this.GetIntersectAndSort(enRes1, enRes2, enType, enMap);
if (interPts.length === 0
|| (interPts.length === 1 && (enType & 2)))//圆弧相切
{
if (enType === 1)
return this.FilletParallelLine(enRes1, enRes2);
else if (enType === 3)
return this.FilletLineAndArc(enMap, enRes1);
else if (enType === 2)
return this.FilletArcAndArc(enRes1, enRes2);
return;
}
return this.FilletLineOrArc(enRes1, enRes2, interPts);
}
private FilletLineOrArc(enRes1: PromptEntityResult, enRes2: PromptEntityResult, interPts: Vector3[]): FilletRes
{
let iPt = interPts[0];
//裁剪延伸,使两条线组成一个尖角
let splitedCu1 = this.SplitCurve(enRes1, iPt, interPts);
let splitedCu2 = this.SplitCurve(enRes2, iPt, interPts);
let fRadius = this.m_FilletRadius;
if (enRes1.Entity instanceof Arc && enRes2.Entity instanceof Arc)
return this.FilletArcAndArc(enRes1, enRes2);
let res: FilletRes = { cu1: splitedCu1.Curve, cu2: splitedCu2.Curve, arc: undefined };
if (fRadius > 0)
{
//角平分线向量.
let bisectorVec: Vector3 = new Vector3();
let c1Derv = this.ComputerDerv(splitedCu1, bisectorVec);
let c2Derv = this.ComputerDerv(splitedCu2, bisectorVec);
//方向相反
if (equalv3(bisectorVec, new Vector3()))
return;
//相切
if (equalv3(c2Derv, c1Derv))
{
bisectorVec.set(0, 0, 0);
c1Derv = this.ComputerDerv2(splitedCu1, bisectorVec);
c2Derv = this.ComputerDerv2(splitedCu2, bisectorVec);
}
let cu1RoOcsInv = new Matrix4().extractRotation(splitedCu1.Curve.OCSInv);
[c1Derv, c2Derv, bisectorVec].forEach(v => v.applyMatrix4(cu1RoOcsInv));
let offCu1 = splitedCu1.Curve.GetOffsetCurves(
fRadius * -Math.sign(c1Derv.cross(bisectorVec).z))[0];
let offCu2 = splitedCu2.Curve.GetOffsetCurves(
fRadius * -Math.sign(c2Derv.cross(bisectorVec).z))[0];
if (!offCu1 || !offCu2)
return;
// //测试绘制
// offCu1.ColorIndex = 6;
// offCu2.ColorIndex = 6;
// JigUtils.Draw(offCu1.Clone());
// JigUtils.Draw(offCu2.Clone());
let center = offCu1.IntersectWith(offCu2, IntersectOption.OnBothOperands)
.sort((p1, p2) =>
{
return p1.distanceToSquared(iPt) - p2.distanceToSquared(iPt);
})[0];
if (!center)
return;
let arcP1 = splitedCu1.Curve.GetClosestPointTo(center, true);
let arcP2 = splitedCu2.Curve.GetClosestPointTo(center, true);
if (!splitedCu1.Curve.PtOnCurve(arcP1) || !splitedCu2.Curve.PtOnCurve(arcP2))
return;
//时针校验
let v1 = arcP1.clone().sub(center).applyMatrix4(cu1RoOcsInv);
let v2 = arcP2.clone().sub(center).applyMatrix4(cu1RoOcsInv);
//绘制圆弧
let arc = new Arc(new Vector3(), this.m_FilletRadius, angle(v1), angle(v2), v1.cross(v2).z < 0);
arc.ApplyMatrix(splitedCu1.Curve.OCS);
arc.Center = center;
res.arc = arc;
//延伸或者裁剪到圆弧点
this.ExtendPt(splitedCu1, arcP1);
this.ExtendPt(splitedCu2, arcP2);
}
return res;
}
private FilletPolyLineSelf(enRes1: PromptEntityResult, enRes2: PromptEntityResult): FilletRes
{
let pl = enRes1.Entity as Polyline;
let param1 = pl.GetParamAtPoint(pl.GetClosestPointTo(enRes1.Point, false));
let param2 = pl.GetParamAtPoint(pl.GetClosestPointTo(enRes2.Point, false));
if (param1 > param2) [param1, param2] = [param2, param1];
let parF1 = Math.floor(param1);
let parF2 = Math.floor(param2);
//共线
if (parF1 === parF2)
return;
let c1 = pl.GetCurveAtParam(param1);
let c2 = pl.GetCurveAtParam(param2);
if (equalv3(c1.GetFistDeriv(1).normalize(), c2.GetFistDeriv(0).normalize()))
return;
let rem = parF2 - parF1;
if (rem === 1 || (rem + 1 === pl.EndParam))//相邻线段倒角
{
let es1 = new PromptEntityResult();
es1.Entity = c1;
es1.Point = enRes1.Point;
let es2 = new PromptEntityResult();
es2.Entity = c2;
es2.Point = enRes2.Point;
let res = this.Fillet(es1, es2);
if (res && res.arc)
{
let pln = pl.Clone();
//修正凸度
if (res.cu1 instanceof Arc)
pln.SetBulgeAt(parF1, res.cu1.Bul);
if (res.cu2 instanceof Arc)
pln.SetBulgeAt(parF2, res.cu2.Bul);
let sp2d = Vec3DTo2D(res.arc.StartPoint.applyMatrix4(pln.OCSInv));
let ep2d = Vec3DTo2D(res.arc.EndPoint.applyMatrix4(pln.OCSInv));
let isNeighbor = rem === 1;
//#IOX26
if (isNeighbor && res.cu1 instanceof Arc && res.cu2 instanceof Arc)
{
let ins = res.cu1.IntersectWith(res.cu2, IntersectOption.OnBothOperands);
if (ins.length === 1 && !equalv3(pln.StartPoint, ins[0]))
isNeighbor = false;
}
if (isNeighbor)
{
pln.SetPointAt(parF2, ep2d);
pln.AddVertexAt(parF2, sp2d);
pln.SetBulgeAt(parF2, res.arc.Bul);
}
else//首尾
{
pln.SetPointAt(0, sp2d);
let plPtCount = pln.NumberOfVertices;
if (pln.EndParam === plPtCount)//CloseMark
{
pln.AddVertexAt(plPtCount, ep2d);
pln.SetBulgeAt(plPtCount, -res.arc.Bul);
}
else
{
pln.SetPointAt(plPtCount - 1, ep2d);
pln.SetBulgeAt(plPtCount - 1, -res.arc.Bul);
pln.AddVertexAt(plPtCount, sp2d);
}
}
return {
cu1: pln,
cu2: undefined,
arc: undefined
};
}
}
else//自交多段线
{
let interPts = c1.IntersectWith(c2, IntersectOption.OnBothOperands);
if (interPts.length === 0)
return;
if (interPts.length === 2 && c1.GetParamAtPoint(interPts[0]) > c1.GetParamAtPoint(interPts[1]))
interPts.reverse();
let ipt = interPts[0];
let splitParam1 = Math.floor(param1) + c1.GetParamAtPoint(ipt);
let splitParam2 = Math.floor(param2) + c2.GetParamAtPoint(ipt);
let cus = pl.GetSplitCurves([splitParam1, splitParam2]);
if (cus.length >= 2)
{
cus.splice(1, 1);
let pl1 = cus[0];
for (let i = 1; i < cus.length; i++)
pl1.Join(cus[i]);
let es1 = new PromptEntityResult();
es1.Entity = pl1;
es1.Point = c1.GetPointAtParam(0.1);
let es2 = new PromptEntityResult();
es2.Entity = pl1;
es2.Point = c2.GetPointAtParam(0.9);
return this.FilletPolyLineSelf(es1, es2);
}
}
}
private FilletPolylineAndCurve(enRes1: PromptEntityResult, enRes2: PromptEntityResult): FilletRes
{
let arr1 = GetFilletCurve(enRes1);
let arr2 = GetFilletCurve(enRes2);
let [cu1, paramF1] = arr1;
let [cu2, paramF2] = arr2;
let es1 = new PromptEntityResult();
es1.Entity = cu1;
es1.Point = enRes1.Point;
let es2 = new PromptEntityResult();
es2.Entity = cu2;
es2.Point = enRes2.Point;
let fres = this.Fillet(es1, es2);
if (fres)
{
let pln: Polyline;
let isFirst = false;
let cus: Curve[] = [];
if (fres.cu1)
{
if (enRes1.Entity instanceof Polyline)
{
isFirst = true;
pln = enRes1.Entity.Clone();
let xcus = enRes1.Entity.Explode();
xcus[paramF1] = fres.cu1;
cus.push(...xcus);
}
//@ts-ignore
else if (!enRes1.IsCircle)
cus.push(fres.cu1);
}
if (fres.arc)
cus.push(fres.arc);
if (fres.cu2)
{
if (enRes2.Entity instanceof Polyline)
{
if (!pln)
pln = enRes2.Entity.Clone();
let xcus = enRes2.Entity.Explode();
xcus[paramF2] = fres.cu2;
cus.push(...xcus);
}
//@ts-ignore
else if (!enRes2.IsCircle)
cus.push(fres.cu2);
}
let groups = curveLinkGroup(cus);
for (let g of groups)
{
if (g.includes(fres.cu1) || g.includes(fres.cu2))
{
pln.LineData = [];
pln.ApplyMatrix(pln.OCSInv);
pln.CloseMark = false;
for (let cu of g)
pln.Join(cu);
if (isFirst)
return { cu1: pln, cu2: undefined, arc: undefined };
else
return { cu1: undefined, cu2: pln, arc: undefined };
}
}
}
return undefined;
}
FilletPolyLineAllAngular(enRes1: PromptEntityResult): FilletRes
{
let pl = enRes1.Entity as Polyline;
let cus = pl.Explode();
let count = cus.length;
if (pl.IsClose)
cus.push(cus[0]);
let ncus = [];
for (let i = 0; i < count; i++)
{
let c1 = cus[i];
let c2 = cus[i + 1];
ncus.push(c1);
if (!c2)
break;
if (equalv3(c1.GetFistDeriv(1).normalize(), c2.GetFistDeriv(0).normalize()))
continue;
let es1 = new PromptEntityResult();
es1.Entity = c1;
es1.Point = c1.EndPoint;
let es2 = new PromptEntityResult();
es2.Entity = c2;
es2.Point = c2.StartPoint;
let fres = this.Fillet(es1, es2);
if (fres)
{
if (fres.cu1)
c1.CopyFrom(fres.cu1);
if (fres.cu2)
c2.CopyFrom(fres.cu2);
if (fres.arc)
ncus.push(fres.arc);
}
}
let pln = pl.Clone();
pln.LineData = [];
pln.ApplyMatrix(pln.OCSInv);
pln.CloseMark = false;
for (let cu of ncus)
pln.Join(cu);
pln.CloseMark = pl.CloseMark;
return {
cu1: pln,
cu2: undefined,
arc: undefined
};
}
/**
* 线
* @param enRes1
* @param enRes2
* @returns parallel line
*/
private FilletParallelLine(enRes1: PromptEntityResult, enRes2: PromptEntityResult): FilletRes
{
let l1 = enRes1.Entity as Line;
let l2 = enRes2.Entity as Line;
let l1Derv = l1.GetFistDeriv(0);
if (!isParallelTo(l1Derv, l2.GetFistDeriv(0)))
return;
let par1 = l2.GetClosestAtPoint(l1.StartPoint, true).param;
let par2 = l2.GetClosestAtPoint(l1.EndPoint, true).param;
if (!l1.ParamOnCurve(par1) && !l1.ParamOnCurve(par2))
return;
let lineClone1 = l1.Clone();
let lineClone2 = l2.Clone();
let par = l1.GetClosestAtPoint(enRes1.Point, true).param;
let parFix = Math.round(par);
let ptFix = lineClone1.GetPointAtParam(parFix);
let ptL2Fix = lineClone2.GetClosestAtPoint(ptFix, true).closestPt;
if ((par1 > par2) === (parFix === 1))
lineClone2.StartPoint = ptL2Fix;
else
lineClone2.EndPoint = ptL2Fix;
let arcCenter = midPoint(ptFix, ptL2Fix);
let sv = ptFix.sub(arcCenter).applyMatrix4(l1.OCSInv);
let ev = ptL2Fix.sub(arcCenter).applyMatrix4(l2.OCSInv);
if (parFix === 0)
l1Derv.negate();
let arc = new Arc(new Vector3(), ptFix.distanceTo(ptL2Fix) / 2, angle(sv), angle(ev), ev.cross(l1Derv.applyMatrix4(l1.OCSInv)).z > 0);
arc.ApplyMatrix(l1.OCS);
arc.Center = arcCenter;
return {
cu1: lineClone1,
cu2: lineClone2,
arc
};
}
/**
* .
* @param enRes1
* @param enRes2
* @returns arc and arc
*/
private FilletArcAndArc(enRes1: PromptEntityResult, enRes2: PromptEntityResult): FilletRes
{
let a1 = enRes1.Entity as Arc;
let a2 = enRes2.Entity as Arc;
let arcO1 = a1.GetOffsetCurves(this.m_FilletRadius * (a1.IsClockWise ? -1 : 1))[0];
let arcO2 = a2.GetOffsetCurves(this.m_FilletRadius * (a2.IsClockWise ? -1 : 1))[0];
// arcO1.ColorIndex = 6;
// arcO2.ColorIndex = 6;
// JigUtils.Draw(arcO1);
// JigUtils.Draw(arcO2);
//求交
let intPts = arcO1.IntersectWith(arcO2, IntersectOption.ExtendBoth);
if (intPts.length === 0)
return;//无交点无法倒角
//两选择点的中点
let clickMidp = midPoint(enRes1.Point, enRes2.Point);//用来选择合适的交点
//选择合适的交点
intPts.sort((p1, p2) =>
{
return p1.distanceToSquared(clickMidp) - p2.distanceToSquared(clickMidp);
});
//圆弧圆心
let narcCenter = intPts[0];
let narcP1 = a1.GetClosestPointTo(narcCenter, true);//两圆弧和相切弧的交点
let narcP2 = a2.GetClosestPointTo(narcCenter, true);
let tempCircle = new Circle(narcCenter, this.m_FilletRadius);
let closestPt = a1.GetClosestPointTo(a2.Center, true);//两曲线距离对方圆心最近的点
let narcMP = tempCircle.GetClosestPointTo(closestPt, false);//相切圆距离closestPt最近的点
//构造圆弧
let narc = new Arc().ApplyMatrix(a1.OCS).FromThreePoint(narcP1, narcMP, narcP2);
let a1Clone = a1.Clone();
let a2Clone = a2.Clone();
let a1Param = a1.GetParamAtPoint(narcP1);
let a2Param = a2.GetParamAtPoint(narcP2);
let a1Derv = a1.GetFistDeriv(a1Param).normalize();
let a2Derv = a2.GetFistDeriv(a2Param).normalize();
let narcDerv0 = narc.GetFistDeriv(0).normalize();
let narcDerv1 = narc.GetFistDeriv(1).normalize();
//裁剪圆弧
if (equalv3(a1Derv, narcDerv0))
a1Clone.EndPoint = narcP1;
else
a1Clone.StartPoint = narcP1
if (equalv3(a2Derv, narcDerv1))
a2Clone.StartPoint = narcP2;
else
a2Clone.EndPoint = narcP2
return {
cu1: a1Clone,
cu2: a2Clone,
arc: narc
};
}
/**
* 线.
* @param enRes1
* @param enRes2
* @returns line and cir
*/
private FilletLineAndArc(enMap: (PromptEntityResult[])[], enRes1: PromptEntityResult): FilletRes | undefined
{
let lineRes = enMap[0][0];
let arcRes = enMap[1][0];
let line = lineRes.Entity as Line;
let arc = arcRes.Entity as Arc;
let dir = GetPointAtCurveDir(line, arc.Center) ? 1 : -1;
let lineO = line.GetOffsetCurves(this.m_FilletRadius * dir)[0];
let arcO = arc.GetOffsetCurves(this.m_FilletRadius * (arc.IsClockWise ? -1 : 1))[0];// tip面积逆时针为正, 顺时针为负.
// lineO.ColorIndex = 6;
// arcO.ColorIndex = 6;
// JigUtils.Draw(lineO);
// JigUtils.Draw(arcO);
//求交
let intPts = lineO.IntersectWith(arcO, IntersectOption.ExtendBoth);
if (intPts.length === 0)
return;//无交点无法倒角
//两选择点的中点
let clickMidp = midPoint(lineRes.Point, arcRes.Point);
//选择适合的交点。
intPts.sort((p1, p2) =>
{
return p1.distanceToSquared(clickMidp) - p2.distanceToSquared(clickMidp);
});
//圆弧圆心
let arcCenter = intPts[0];
let arcP1 = line.GetClosestPointTo(arcCenter, true);//直线与相切圆的交点
let arcP2 = arc.GetClosestPointTo(arcCenter, true);//圆弧与相切圆的交点
let tempCircle = new Circle(arcCenter, this.m_FilletRadius);
let { closestPt, param } = line.GetClosestAtPoint(arc.Center, true);
let arcMP = tempCircle.GetClosestPointTo(closestPt, false);
//构造圆弧
let narc = new Arc().ApplyMatrix(arc.OCS).FromThreePoint(arcP1, arcMP, arcP2);
//裁剪线
let lineClone = line.Clone();
let arcClone = arc.Clone();
let p1Param = line.GetParamAtPoint(arcP1);
if (p1Param > param)
lineClone.StartPoint = arcP1;
else
lineClone.EndPoint = arcP1;
//裁剪圆弧
let arcParam = arc.GetParamAtPoint(arcP2);
let arcDerv = arc.GetFistDeriv(arcParam).normalize();
let narcDerv = narc.GetFistDeriv(1).normalize();
if (equalv3(arcDerv, narcDerv))
arcClone.StartPoint = arcP2;
else
arcClone.EndPoint = arcP2;
//先选直线为真
if (enRes1.Entity === line)
return {
cu1: lineClone,
cu2: arcClone,
arc: narc
}
else
return {
cu1: arcClone,
cu2: lineClone,
arc: narc
}
}
//获得两曲线的交点,并且排序交点.
private GetIntersectAndSort(enRes: PromptEntityResult, enRes2: PromptEntityResult, enType: number, enMap: PromptEntityResult[][])
{
let interPts = enRes.Entity.IntersectWith(enRes2.Entity, IntersectOption.ExtendBoth);
if (interPts.length > 1)
{
let baseP: Vector3;
if (enType & 1) //如果有直线,那么用直线
baseP = enMap[0][0].Point;
else if (enType === 2) //如果都是圆弧,那么取中点
baseP = midPoint(enMap[1][0].Point, enMap[1][1].Point);
interPts.sort((p1, p2) => p1.distanceToSquared(baseP) - p2.distanceToSquared(baseP));
}
return interPts;
}
/**
* ,:
* # 1:line 2:arc 4:polyline
* @param enRes
* @param enRes2
* @returns
*/
private EnCode(enRes: PromptEntityResult, enRes2: PromptEntityResult)
{
let enMap: (PromptEntityResult[])[] = [[], [], []];
let enType = 0;
enType |= Encode(enRes, enMap);
enType |= Encode(enRes2, enMap);
return { enType, enMap };
}
//计算曲线在相交处的切线,取真实的切线
private ComputerDerv(cuRes: CurveExtend, dervSum: Vector3)
{
let derv: Vector3;
let cu = cuRes.Curve;
if (cuRes.ExtType === ExtendType.Start)
{
derv = cu.GetFistDeriv(0).normalize();
dervSum.add(derv);
}
else
{
derv = cu.GetFistDeriv(cu.EndParam).normalize();
dervSum.add(derv.clone().negate());
}
return derv;
}
// 计算曲线在相交处的切线,取起点到终点的切线.(当曲线相切时调用此方法.)
private ComputerDerv2(cuRes: CurveExtend, dervSum: Vector3)
{
let cu = cuRes.Curve;
let derv = cu.EndPoint.sub(cu.StartPoint);
if (cuRes.ExtType === ExtendType.Start)
dervSum.add(derv);
else
dervSum.add(derv.clone().negate());
return derv;
}
// 延伸或者裁剪到指定的圆弧的点.
private ExtendPt(cu: CurveExtend, newP: Vector3)
{
if (cu.ExtType === ExtendType.Start)
cu.Curve.StartPoint = newP;
else
cu.Curve.EndPoint = newP;
}
/**
* 线,
*
* @param cu 线
* @param interPt
* @param pickPoint
* @returns 线
*/
private SplitCurve(enRes: PromptEntityResult, interPt: Vector3, interPts: Vector3[]): CurveExtend
{
let cu = enRes.Entity as Curve;
let pickPoint = enRes.Point;
let cp = cu.GetClosestPointTo(pickPoint, false);
let cus = cu.GetSplitCurvesByPts([interPt]);
if (cus.length === 0)
cus.push(cu.Clone() as Curve);
else if (cus.length === 2)
cus.sort((c1: Curve, c2: Curve) =>
{
return c1.GetClosestPointTo(cp, false).distanceTo(cp)
< c2.GetClosestPointTo(cp, false).distanceTo(cp) ? -1 : 1;
});
let exType = undefined;
let newCu = cus[0];
if (newCu instanceof Line)
newCu.Extend(newCu.GetParamAtPoint(interPt));//延伸到需要的长度
else if (newCu instanceof Arc)
{
let arc = newCu as Arc;
if (cus.length === 1)
if (!cu.PtOnCurve(interPt))
{
if (cu.PtOnCurve(interPts[1]))
{
//交点参数
let iparam = arc.GetParamAtPoint(interPts[1]);
let pickParam = arc.GetParamAtAngle(arc.GetAngleAtPoint(pickPoint));
if (pickParam > iparam)
{
arc.EndAngle = arc.GetAngleAtPoint(interPt);
exType = ExtendType.End;
}
else
{
arc.StartAngle = arc.GetAngleAtPoint(interPt);
exType = ExtendType.Start;
}
}
else
{
//终点,起点
interPts = interPts.sort((p1, p2) =>
{
return arc.ComputeAnlge(arc.GetAngleAtPoint(p1)) - arc.ComputeAnlge(arc.GetAngleAtPoint(p2));
});
if (interPt === interPts[0])
{
arc.EndAngle = arc.GetAngleAtPoint(interPt);
exType = ExtendType.End;
}
else
{
arc.StartAngle = arc.GetAngleAtPoint(interPt);
exType = ExtendType.Start;
}
}
}
}
if (exType === undefined)
{
//使用equalv3时由于精度误差导致的判断错误
if (interPt.manhattanDistanceTo(newCu.StartPoint) < interPt.manhattanDistanceTo(newCu.EndPoint))
exType = ExtendType.Start;
else
exType = ExtendType.End;
}
return { Curve: newCu, ExtType: exType };
}
}

@ -183,7 +183,7 @@ export function registerCommand()
commandMachine.RegisterCommand("ptl", new TestDrawPointLight());
commandMachine.RegisterCommand("pt", new Command_CopyPoint());
commandMachine.RegisterCommand("ptcopy", new Command_CopyPoint());
commandMachine.RegisterCommand("dl", new TestDrawDirectionalLight());

@ -73,10 +73,15 @@ export class PromptDistendResult extends PromptResult
export class PromptEntityResult extends PromptResult
{
//选择到的图形
Entity?: Entity;
//点取的点
Point?: Vector3;
constructor(
//选择到的图形
public Entity?: Entity,
//点取的点
public Point?: Vector3
)
{
super();
}
}
export class PromptSsgetResult extends PromptResult

@ -2,10 +2,11 @@ module.exports = function (wallaby)
{
return {
files: [
'tsconfig.json', // <--
'tsconfig.json',
'src/**/*.ts*',
'src/**/*.tsx*',
'src/**/*.js*'
'src/**/*.js*',
'__test__/**/*.util.ts',
],
tests: ['__test__/**/*.test.ts*'],

Loading…
Cancel
Save