From 45a60be7ffcf4b642bd5117caf980266f86b4b2d Mon Sep 17 00:00:00 2001 From: Zoe Date: Tue, 29 May 2018 14:39:21 +0800 Subject: [PATCH] =?UTF-8?q?bug=E4=BF=AE=E5=A4=8D,=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Add-on/Offset.ts | 5 + src/DatabaseServices/Polyline.ts | 4 +- src/GraphicsSystem/OffestPolyline.ts | 1155 ++------------------------ 3 files changed, 82 insertions(+), 1082 deletions(-) diff --git a/src/Add-on/Offset.ts b/src/Add-on/Offset.ts index 7e549edfa..9850d2cfa 100644 --- a/src/Add-on/Offset.ts +++ b/src/Add-on/Offset.ts @@ -123,6 +123,11 @@ export class Command_TestOffset implements Command } } lastpls = cu.GetOffsetCurves(p.distanceTo(cu.GetClosestPointTo(p, !cu.IsClose)) * dir); + + // let a = cu.GetOffsetCurves(p.distanceTo(cu.GetClosestPointTo(p, !cu.IsClose)) * -dir); + // a.forEach(c => c.ColorIndex = 2); + // lastpls.push(...a); + lastpls.forEach((offCur) => { app.m_Database.ModelSpace.Append(offCur); diff --git a/src/DatabaseServices/Polyline.ts b/src/DatabaseServices/Polyline.ts index 2bcee060b..dc81cf619 100644 --- a/src/DatabaseServices/Polyline.ts +++ b/src/DatabaseServices/Polyline.ts @@ -9,7 +9,7 @@ import { FixIndex } from '../Common/Utils'; import { equal, equaln, rotatePoint, updateGeometry } from '../Geometry/GeUtils'; import { RenderType } from '../GraphicsSystem/Enum'; import { IntersectOption, IntersectPolylineAndCurve } from '../GraphicsSystem/IntersectWith'; -import { PolyOffestUtil3 } from '../GraphicsSystem/OffestPolyline'; +import { PolyOffestUtil } from '../GraphicsSystem/OffestPolyline'; import { Arc } from './Arc'; import { Factory } from './CADFactory'; import { CADFile } from './CADFile'; @@ -746,7 +746,7 @@ export class Polyline extends Curve //偏移 GetOffsetCurves(offsetDist: number): Array { - let polyOffestUtil = new PolyOffestUtil3(this, offsetDist); + let polyOffestUtil = new PolyOffestUtil(this, offsetDist); return polyOffestUtil.GetOffsetCurves(); } /** diff --git a/src/GraphicsSystem/OffestPolyline.ts b/src/GraphicsSystem/OffestPolyline.ts index bc949436b..48ffe4943 100644 --- a/src/GraphicsSystem/OffestPolyline.ts +++ b/src/GraphicsSystem/OffestPolyline.ts @@ -1,1057 +1,23 @@ -import { Vector2, Vector3 } from "three"; +import { Vector3 } from "three"; import { arrayLast } from "../Common/ArrayExt"; -import { Vec2DTo3D, Vec3DTo2D, curveLinkGroup, getCirAngleByChordAndTangent } from "../Common/CurveUtils"; +import { Vec3DTo2D, curveLinkGroup, getCirAngleByChordAndTangent } from "../Common/CurveUtils"; import { FixIndex } from "../Common/Utils"; import { Arc } from "../DatabaseServices/Arc"; import { Circle } from "../DatabaseServices/Circle"; import { Contour } from "../DatabaseServices/Contour"; import { Curve } from "../DatabaseServices/Curve"; import { Line } from "../DatabaseServices/Line"; -import { Polyline, PolylineProps } from '../DatabaseServices/Polyline'; +import { Polyline } from '../DatabaseServices/Polyline'; import { equal, equaln } from "../Geometry/GeUtils"; import { isTargetCurInSourceCur, isTargetCurOutOrOnSourceCur } from "./BoolOperateUtils"; import { IntersectOption } from "./IntersectWith"; -interface offestRes2 -{ - index: number, - pl: Polyline -} interface offestRes { index: number, curve: Curve } -/** - * 先把线段首尾尝试相连,分离出与源线段方向相反的线段 - * 然后分离出自交的曲线, - * 再用源线段上的点以及偏移线段上离源线段小于偏移距离的最近点最为圆心去切割偏移线段 - * 移除自交线段时部分情况不正确 - * 存在多封闭区域的时候结果不对 - * 分离自交曲线 存在问题 - * 当源线段有多个自交点时结果不正确 - * @export - * @class PolyOffestUtil - */ export class PolyOffestUtil -{ - private m_Polyline: Polyline; - private m_OffestDist: number; - offDir: number; - constructor(pl: Polyline, offest: number) - { - this.m_Polyline = pl; - this.m_OffestDist = offest; - this.offDir = Math.sign(this.m_OffestDist) * Math.sign(this.m_Polyline.Area2); - } - //偏移 - GetOffsetCurves(): Curve[] - { - let plList1: offestRes2[] = this.offestCurve(this.m_Polyline.Explode()); - let plList2: offestRes2[] = this.offestCurve(this.m_Polyline.Explode(), true); - - // //连接全部连接点 - let newPlList: Array = this.trimAndJointOffestPolyline(plList1); - - //分离出自交的线 - let outputPls = this.removeSelfIntersect(newPlList); - - //尝试将分离处自交线段后的线段首尾相连 - this.linkPLine(newPlList); - - // //用源点和最近点对线段进行切割 - this.cuttingPolyLines(newPlList); - - // 闭合时向内偏移,偏移线段应在封闭区域内 - if (this.m_Polyline.IsClose && this.offDir < 0) - { - newPlList = newPlList.filter(pl => isTargetCurInSourceCur(this.m_Polyline, pl)) - } - - //如果源线段是闭合得,那偏移出来的应该也闭合 - outputPls.push(...(Contour.Combine(newPlList) as Polyline[]).filter(pl => this.m_Polyline.IsClose ? pl.IsClose : true)); - - return outputPls; - } - private offestCurve(pls: Curve[], isContrary: boolean = false): offestRes2[] - { - let plList: offestRes2[] = []; - pls.forEach((cu, index) => - { - let ocu = cu.GetOffsetCurves(isContrary ? -this.m_OffestDist : this.m_OffestDist)[0]; - if (ocu) - { - let pl = new Polyline(); - pl.Join(ocu); - plList.push({ pl, index }); - } - else - { - plList.push({ pl: undefined, index }); - } - }); - return plList; - } - /** - * 连接多段线数组 - * 1.两两取交点 - * 2.去除斜率相反线 - * 3.不相交添加圆弧连接 - * @ - * @param {Array} offResList - * @param {number} [offsetDist] - * @returns - * @memberof Polyline - */ - private trimAndJointOffestPolyline(offResList: offestRes2[]) - { - offResList = offResList.filter(r => r.pl) - if (offResList.length <= 1) - { - return offResList[0] ? [offResList[0].pl] : []; - } - let newPlList: Array = []; - - //默认起始点为偏移后第一条线段的起始点 - let startV = this.clonePlData(offResList[0].pl.LineData[0]); - - //直线获得斜率,圆弧获得圆弧方向 - const getSlope = (pl: Polyline) => - { - //原先的斜率 - let cu = pl.GetCurveAtIndex(0); - return cu instanceof Line ? cu.GetFistDeriv(0) : cu.IsClockWise; - } - //是否有效,直线斜率相等.圆弧方向相等 - const isDisVail = (oldSlope: Vector3 | boolean, newSlope: Vector3 | boolean) => - { - if (oldSlope instanceof Vector3) - { - return equaln((newSlope).angleTo(oldSlope), Math.PI, 1e-7) - } else return oldSlope !== newSlope - } - - for (let i = 0; i < offResList.length; i++) - { - //前面线 - let frontLine = offResList[i].pl; - - //原先的斜率 - let oldSlope = getSlope(frontLine); - - //后面线 - let laterLine = i === offResList.length - 1 ? undefined : offResList[i + 1].pl; - - //如果是闭合的,继续循环,否则检查当前首尾是否相交 - if (i === offResList.length - 1) - { - if (this.m_Polyline.IsClose) - laterLine = newPlList[0]; - else - { - let frontLine = offResList[offResList.length - 1].pl; - - let oldSlope = getSlope(frontLine); - - let endV = this.clonePlData(frontLine.LineData[1]); - //如果原先不是闭合的,判断偏移后首尾是否相交,相交则连接 - if (offResList.length >= 3) - { - let laterLine = newPlList[0]; - let pts = frontLine.IntersectWith(laterLine, IntersectOption.OnBothOperands); - if (pts.length === 1) - { - this.adjustFrontLine(startV, endV, pts[0]); - endV.pt = Vec3DTo2D(pts[0]); - if (newPlList.length > 0) - { - this.adjustLaterLine(laterLine.LineData[0], laterLine.LineData[1], pts[0]); - } - } - } - let pl = new Polyline([startV, endV]); - let newSlope = getSlope(pl); - - !isDisVail(oldSlope, newSlope) && newPlList.push(pl); - break; - } - } - - if (!laterLine) return []; - - //终点data - let endV = i === offResList.length - 1 ? laterLine.LineData[0] : this.clonePlData(laterLine.LineData[0]); - - //判断线段是否为补圆弧,若是补圆弧,则不受斜率和圆弧方向影响 - let isMendPl = false; - - // 如果两线结合点不相等,调整交点位置 - if (!equal(frontLine.EndPoint, laterLine.StartPoint)) - { - // 多段线交点数组 - let interPts = frontLine.IntersectWith(laterLine, IntersectOption.ExtendBoth); - - //如果没有交点,则用圆弧连接 - if (interPts.length === 0) - { - //偏移线段对应的源线段,取其起始点作为圆心 - let srcOffestRes = offResList[i + 1] ? offResList[i + 1] : offResList[0]; - let centerPt = this.m_Polyline.GetCurveAtIndex(srcOffestRes.index).StartPoint; - - // 圆心到前面线end点距离 - let startDist = frontLine.EndPoint.distanceToSquared(centerPt); - // 圆心到后面线start点距离 - let endDist = laterLine.StartPoint.distanceToSquared(centerPt); - //补圆弧的起始交点 - let intPt = Vec2DTo3D(frontLine.LineData[1].pt); - - if (!equaln(startDist, endDist)) - { - // 以centerPt为圆心,偏移距离为为半径画圆 - let tmpCir = new Circle(centerPt, Math.abs(this.m_OffestDist)); - //只会和前面线相交,因为圆心选取是根据后面线startPoint偏移距离处的点,只会与后面线相切,允许前面线延伸 - let pts = tmpCir.IntersectWith(frontLine, IntersectOption.OnBothOperands); - if (pts.length > 0) - { - //选取交点,如果有多个选离前面线start点近的 - intPt = this.selectPlInterPt(pts, frontLine.StartPoint); - let insLine = intPt.clone().sub(centerPt); - this.adjustFrontLine(startV, frontLine.LineData[1], intPt); - } - else - { - //TODO: 与补线圆无交点,放弃补圆弧,若按CAD可以补 - newPlList.push(new Polyline([ - startV, this.clonePlData(frontLine.LineData[1]) - ])) - startV = this.clonePlData(laterLine.LineData[0]); - continue; - } - } - //改用根据laterline切线和弦判断圆心角及方向 - let chord = intPt.clone().sub(laterLine.StartPoint); - let tangent = laterLine.GetFistDeriv(0).negate().normalize(); - let circleAngle = -getCirAngleByChordAndTangent(chord, tangent); - - let tmpPlData = { - pt: Vec3DTo2D(intPt), - bul: Math.tan(circleAngle / 4) - }; - newPlList.push(new Polyline([ - startV, tmpPlData - ])) - startV = this.clonePlData(tmpPlData); - isMendPl = true; - } - else - { - let pts = frontLine.IntersectWith(laterLine, IntersectOption.OnBothOperands); - let interPt = this.selectPlInterPt(pts.length ? pts : interPts, laterLine.StartPoint) - this.adjustFrontLine(startV, frontLine.LineData[1], interPt); - - let oldSlope = getSlope(laterLine); - this.adjustLaterLine(endV, laterLine.LineData[1], interPt); - let newSlope = getSlope(laterLine); - if (i === offResList.length - 1 && isDisVail(oldSlope, newSlope)) - { - newPlList.splice(0, 1); - } - } - } - - // 根据交点连接后的新线段 - let pl = new Polyline([startV, endV]); - - // 新的斜率 - let newSlope = getSlope(pl); - - // 取终点作为下一段的起始点 - startV = this.clonePlData(endV); - - //排除0长度和无效线段 - if ((equaln(pl.Length, 0) || isDisVail(oldSlope, newSlope)) && !isMendPl) - { - continue; - } - newPlList.push(pl); - } - return newPlList; - } - /** - * 尝试相邻多段线段收尾相连 - * @ - * @param {Polyline[]} pls - * @memberof Polyline - */ - private linkPLine(pls: Polyline[]) - { - for (let i = 0; i < pls.length; i++) - { - let frontLine = pls[i]; - let laterLine = pls[i + 1]; - if (i === pls.length - 1 && i >= 3) - { - laterLine = pls[0]; - } - let interPts = frontLine.IntersectWith(laterLine, i === pls.length - 1 ? IntersectOption.OnBothOperands : IntersectOption.ExtendBoth); - if (interPts.length > 0) - { - let pt = this.selectPlInterPt(interPts, laterLine.StartPoint); - //如果点在反方向,则不相连 - let par = frontLine.GetParamAtPoint(pt); - if (par < 0) continue; - //调整前后两条多段线的首尾线 - this.adjustFrontLine(frontLine.LineData[0], frontLine.LineData[1], pt); - frontLine.LineData[1].pt = Vec3DTo2D(pt); - this.adjustLaterLine(laterLine.LineData[0], laterLine.LineData[1], pt); - } - } - } - /** - * 删除自交多段线 - * 遍历多段线数组,分离出自交的部分,包括起始和终止被切割的部分 - * @ - * @param {Array} plList - * @param {number} offsetDist - * @returns - * @memberof Polyline - */ - private removeSelfIntersect(plList: Polyline[]) - { - let plLists: Polyline[] = []; - let deletePlList: Polyline[] = []; - for (let i = 0; i < plList.length - 1; i++) - { - //前一根线 - let frontLine = plList[i]; - //要删除自交线的索引 - let deleteIndex: number; - //截取到的自交线交点 - let insertPt = new Vector3(); - for (let j = i + 1; j < plList.length; j++) - { - // 后一根线 - let laterLine = plList[j]; - let intPts = frontLine.IntersectWith(laterLine, IntersectOption.OnBothOperands); - if (intPts.length === 2) - { - intPts[0].distanceTo(frontLine.StartPoint) < intPts[1].distanceTo(frontLine.StartPoint) ? intPts.pop() : intPts.shift(); - } - if (intPts.length > 0 && !equal(intPts[0], laterLine.StartPoint) && !equal(intPts[0], laterLine.EndPoint)) - { - deleteIndex = j; - insertPt.copy(intPts[0]); - } - } - if (deleteIndex) - { - // 删除的自交线 - let deleteLine = plList[deleteIndex]; - let clonedeleteLine = deleteLine.Clone() as Polyline; - let cloneStartLine = frontLine.Clone() as Polyline - - this.adjustFrontLine(frontLine.LineData[0], frontLine.LineData[1], insertPt); - frontLine.LineData[1].pt = Vec3DTo2D(insertPt); - //留下剩余的 - this.adjustLaterLine(cloneStartLine.LineData[0], cloneStartLine.LineData[1], insertPt); - - this.adjustLaterLine(deleteLine.LineData[0], deleteLine.LineData[1], insertPt); - //留下剩余的 - this.adjustFrontLine(clonedeleteLine.LineData[0], clonedeleteLine.LineData[1], insertPt); - clonedeleteLine.LineData[1].pt = Vec3DTo2D(insertPt); - - //分离出的线 - deletePlList = plList.splice(i + 1, deleteIndex - i - 1); - if (this.m_Polyline.IsClose && this.offDir < 0) - deletePlList = deletePlList.filter(l => isTargetCurInSourceCur(this.m_Polyline, l)) - - if (deletePlList.length) - { - //添加自交开始结束的部分 - deletePlList.unshift(cloneStartLine); - deletePlList.push(clonedeleteLine); - this.cuttingPolyLines(deletePlList); - - //默认分离出的自交部分应该都是闭合的 - plLists.push(...(Contour.Combine(this.handleRemoveLine(deletePlList)) as Polyline[]).filter(pl => pl.IsClose)); - } - i = deleteIndex - 2; - } - } - return plLists; - } - //处理分离出的多段线,把相交的部分提取出来 - private handleRemoveLine(plList: Polyline[]) - { - let startIndex: number; - let endIndex: number; - let insertPt = new Vector3(); - for (let i = 0; i < plList.length - 1; i++) - { - //前一根线 - let frontLine = plList[i]; - //截取到的自交线交点 - for (let j = i + 1; j < plList.length; j++) - { - // 后一根线 - let laterLine = plList[j]; - let intPts = frontLine.IntersectWith(laterLine, IntersectOption.OnBothOperands); - if (intPts.length === 2) - { - intPts[0].distanceTo(laterLine.StartPoint) < intPts[1].distanceTo(laterLine.StartPoint) ? intPts.pop() : intPts.shift(); - } - if (intPts.length > 0 && !equal(intPts[0], laterLine.StartPoint) && !equal(intPts[0], laterLine.EndPoint)) - { - startIndex = i; - endIndex = j; - insertPt.copy(intPts[0]); - } - } - } - if (startIndex !== undefined) - { - // 删除的自交线 - let startLine = plList[startIndex]; - let endLine = plList[endIndex]; - - this.adjustFrontLine(endLine.LineData[0], endLine.LineData[1], insertPt); - endLine.LineData[1].pt = Vec3DTo2D(insertPt); - this.adjustLaterLine(startLine.LineData[0], startLine.LineData[1], insertPt); - - return plList.slice(startIndex, endIndex + 1); - } - - // this.cuttingPolyLines(plList); - return plList; - - } - // 根据交点调整前面线段的凸度 - private adjustFrontLine(startV: PolylineProps, endV: PolylineProps, insertPt: Vector3) - { - if (startV.bul !== 0) - { - let arc = new Arc().ParseFromBul(startV.pt, endV.pt, startV.bul); - arc.EndPoint = insertPt; - //前面线的凸度调整 - startV.bul = Math.tan(arc.AllAngle / 4) * Math.sign(startV.bul); - } - } - // 根据交点调整后面线段的凸度和起始点 - private adjustLaterLine(startV: PolylineProps, endV: PolylineProps, interPt: Vector3) - { - let bul = 0; - if (startV.bul !== 0) - { - let arc = new Arc().ParseFromBul(startV.pt, endV.pt, startV.bul); - arc.StartPoint = interPt; - bul = Math.tan(arc.AllAngle * 0.25) * Math.sign(startV.bul); - } - // 调整后面线起始点和凸度 - startV.pt = Vec3DTo2D(interPt); - startV.bul = bul; - } - /** - * 选取离spt近的点作为交点 - * - * @ - * @param {Vector3[]} pts - * @param {Vector3} spt - * @returns - * @memberof Polyline - */ - private selectPlInterPt(pts: Vector3[], spt: Vector3, refLine?: Polyline) - { - let pt: Vector3; - // 多段线交点数组 - if (pts.length === 0) return; - else if (pts.length === 1) - { - pt = pts[0]; - } - else - { - // 在当前线上不存在交点,取离后面线startPt最近的交点; - pt = pts[0].distanceTo(spt) < pts[1].distanceTo(spt) ? pts[0] : pts[1]; - } - return pt; - } - /** - * 优化切割无效多段线 - * 源线段中各点画圆,半径为偏移距离 - * 若偏移线段在圆内,则切除 - * @ - * @param {Polyline[]} plList - * @param {number} rad - * @returns - * @memberof Polyline - */ - private cuttingPolyLines(plList: Polyline[]) - { - let rad = Math.abs(this.m_OffestDist); - let originPts = this.m_Polyline.PtsBuls.pts; - let closePts = this.getAllClosestPtAndInterPtOffToSrc(plList); - - // 每个原线段点画圆分割多段线 - for (let pt of [...originPts, ...closePts]) - { - //圆心点 - let centerPt = (pt instanceof Vector2) ? Vec2DTo3D(pt) : pt; - let circle = new Circle(centerPt, rad); - for (let j = 0; j < plList.length; j++) - { - let pl = plList[j]; - let sDist = pl.StartPoint.distanceTo(centerPt); - let eDist = pl.EndPoint.distanceTo(centerPt); - // 如果线段在圆内,则为无效线 - if (sDist - rad <= 1e-7 && eDist - rad <= 1e-7) - { - //该段多段线和切割圆弧是否重合 - if (pl.LineData[0].bul !== 0) - { - let center = (pl.GetCurveAtIndex(0) as Arc).Center; - if (equal(center, centerPt)) continue; - } - plList.splice(j, 1); - j--; - continue; - } - // 跟圆弧相交除非相切,否则都为2个交点 - let pts = pl.IntersectWith(circle, IntersectOption.ExtendBoth); - if (pts.length <= 1) continue; - // 过滤掉不在线上的点 - pts = pts.filter(p => plList[j].PtOnCurve(p)); - - if (pts.length === 0) continue; - else if (pts.length === 1) - { - // 线段一端在圆上,另一端在圆外而且没有其他交点 - if ((equaln(sDist, rad) && eDist > rad) || (equaln(eDist, rad) && sDist > rad)) - { - continue; - } - if (sDist < rad) - this.adjustLaterLine(pl.LineData[0], pl.LineData[1], pts[0]) - else - { - this.adjustFrontLine(pl.LineData[0], pl.LineData[1], pts[0]) - pl.LineData[1].pt = Vec3DTo2D(pts[0]); - } - } - else - { - //有可能误差错误漏掉切点 - if (pts[0].distanceToSquared(pts[1]) <= 1e-8) continue; - let dist = pl.StartPoint.distanceTo(pts[0]); - let dist1 = pl.StartPoint.distanceTo(pts[1]); - //区分到pl的startPT近的和远的交点 - let [pt, pt1] = dist < dist1 ? [pts[0], pts[1]] : [pts[1], pts[0]] - let clonePl = pl.Clone() as Polyline; - // let pars = pts.map(pt => pl.GetParamAtPoint(pt)); - // let tmpPls = pl.GetSplitCurves(pars); - - this.adjustFrontLine(pl.LineData[0], pl.LineData[1], pt); - pl.LineData[1].pt = Vec3DTo2D(pt) - this.adjustLaterLine(clonePl.LineData[0], clonePl.LineData[1], pt1); - // pl = tmpPls[0]; - plList.splice(j + 1, 0, clonePl); - j++; - } - } - } - - - - } - - private getAllClosestPtAndInterPtOffToSrc(pllist: Polyline[]) - { - let cuttingPts: Vector3[] = []; - pllist.forEach(pl => - { - let closePt1 = this.m_Polyline.GetClosestPointTo(pl.StartPoint, false); - let closePt2 = this.m_Polyline.GetClosestPointTo(pl.EndPoint, false); - closePt1.distanceToSquared(pl.StartPoint) < Math.pow(this.m_OffestDist, 2) && cuttingPts.push(closePt1); - closePt2.distanceToSquared(pl.EndPoint) < Math.pow(this.m_OffestDist, 2) && cuttingPts.push(closePt2); - let pts = pl.IntersectWith(this.m_Polyline, IntersectOption.OnBothOperands); - cuttingPts.push(...pts); - }) - return cuttingPts; - } - private clonePlData(pl: PolylineProps) - { - return { - pt: pl.pt, - bul: pl.bul - } - } -} - -/** - * 首先,若自交则先把自交点作为断点,裁剪和连接偏移后的线段,其中如果曲线和源曲线相反,则不做裁剪用曲线连接 - * 再用另一方向的偏移曲线和结果偏移曲线做运算,排除掉线之间的线段 - * 用最近点和源线段点做圆切割 - * 问题为存在大量无效线段 - * 有bug未修复 - * 可以识别多封闭区域 - * @export - * @class PolyOffestUtil2 - */ -export class PolyOffestUtil2 -{ - private m_Polyline: Polyline; - private m_OffestDist: number; - offDir: number; - constructor(pl: Polyline, offest: number) - { - this.m_Polyline = pl; - this.m_OffestDist = offest; - this.offDir = Math.sign(this.m_OffestDist) * Math.sign(this.m_Polyline.Area2); - } - - //偏移 - GetOffsetCurves(): Curve[] - { - let plList1: offestRes2[] = this.offestCurve(this.m_Polyline.Explode()); - let plList2: offestRes2[] = this.offestCurve(this.m_Polyline.Explode(), true); - - let contours: Contour[] = []; - for (let i = 0; i < plList1.length; i++) - { - contours.push(this.buildContourByTwoSideOfest(plList1[i].pl, plList2[i].pl)); - } - // contours.forEach(c => - // { - // c.Outline.ColorIndex = 3; - // app.m_Database.ModelSpace.Append(c.Outline); - // }) - let newProps = this.trimAndJointOffestPolyline(plList1, this.m_Polyline); - // let refProps = this.trimAndJointOffestPolyline(plList2, this.m_Polyline); - - let newPl = new Polyline(newProps); - // let refPl = new Polyline(refProps); - // newPl.ColorIndex = 1; - // refPl.ColorIndex = 2; - // return [newPl] - // return [newPl, refPl] - - let cus = this.trimByContours(newPl, contours); - // let cus1 = this.trimByContours(refPl, contours); - // 闭合时向内偏移,偏移线段应在封闭区域内 - if (this.m_Polyline.IsClose && this.offDir < 0) - { - cus = cus.filter(pl => isTargetCurInSourceCur(this.m_Polyline, pl)) - } - // let outpus = this.getIntersectWithSelf(cus); - // console.log('outpus: ', outpus); - // outpus.push(...this.linkCurves(cus)) - // return outpus; - return this.linkCurves(cus); - } - //偏移曲线 - private offestCurve(pls: Curve[], isContrary: boolean = false): offestRes2[] - { - let plList: offestRes2[] = []; - pls.forEach((cu, index) => - { - let ocu = cu.GetOffsetCurves(isContrary ? -this.m_OffestDist : this.m_OffestDist)[0]; - if (ocu) - { - let pl = new Polyline(); - pl.Join(ocu); - plList.push({ pl, index }); - } - else - { - plList.push({ pl: undefined, index }); - } - }); - return plList; - } - //通过2侧偏移曲线构建封闭轮廓 - private buildContourByTwoSideOfest(pl1: Polyline, pl2: Polyline) - { - if (!pl1 || !pl2) - { - let arc = pl1 ? pl1.GetCurveAtIndex(0) as Arc : pl2.GetCurveAtIndex(0) as Arc; - let l1 = new Line(arc.Center, arc.StartPoint); - let l2 = new Line(arc.Center, arc.EndPoint); - return new Contour([arc, l1, l2]); - } - else - { - let dir = Math.sign(this.m_OffestDist) - let arc1 = new Arc().ParseFromBul(pl1.StartPoint, pl2.StartPoint, -dir); - let arc2 = new Arc().ParseFromBul(pl1.EndPoint, pl2.EndPoint, dir); - - return new Contour([pl1.Clone() as Curve, pl2.Clone() as Curve, arc1, arc2]); - } - } - // 修剪连接相邻曲线 - private trimAndJointOffestPolyline(offestPlList: offestRes2[], originLine: Polyline) - { - offestPlList = offestPlList.filter(r => r.pl) - if (offestPlList.length === 0) return []; - if (offestPlList.length === 1) return offestPlList[0].pl.LineData; - - const addArc = (i: number, frontLine: Polyline, laterLine: Polyline, newProps: PolylineProps[]) => - { - let center = originLine.GetCurveAtParam(i).StartPoint; - - // 圆心到前面线end点距离 - let startDist = frontLine.EndPoint.distanceToSquared(center); - // 圆心到后面线start点距离 - let endDist = laterLine.StartPoint.distanceToSquared(center); - //补圆弧的起始交点 - let intPt = Vec2DTo3D(frontLine.LineData[1].pt); - - if (!equaln(startDist, endDist)) - { - // 以centerPt为圆心,偏移距离为为半径画圆 - let tmpCir = new Circle(center, Math.abs(this.m_OffestDist)); - //只会和前面线相交,因为圆心选取是根据后面线startPoint偏移距离处的点,只会与后面线相切,允许前面线延伸 - let pts = tmpCir.IntersectWith(frontLine, IntersectOption.OnBothOperands); - if (pts.length > 0) - { - //选取交点,如果有多个选离前面线start点近的 - intPt = this.selectFitInterPt(pts, frontLine.StartPoint); - let insLine = intPt.clone().sub(center); - this.adjustFrontLine(newProps[newProps.length - 1], frontLine.LineData[1], intPt); - } - } - //改用根据laterline切线和弦判断圆心角及方向 - let chord = intPt.clone().sub(laterLine.StartPoint); - let tangent = laterLine.GetFistDeriv(0).negate().normalize(); - let circleAngle = -getCirAngleByChordAndTangent(chord, tangent); - - newProps.push({ - pt: Vec3DTo2D(intPt), - bul: Math.tan(circleAngle / 4) - }); - } - let newProps = [offestPlList[0].pl.LineData[0]]; - for (let i = 0; i < offestPlList.length; i++) - { - let frontLine = offestPlList[i].pl; - let laterLine = i !== offestPlList.length - 1 ? offestPlList[i + 1].pl : undefined; - let newProp: PolylineProps = laterLine ? this.clonePlData(laterLine.LineData[0]) : undefined; - - if (i === offestPlList.length - 1) - { - if (newProps.length === 1) return []; - if (this.m_Polyline.IsClose) - { - laterLine = new Polyline([newProps[0], newProps[1]]); - newProp = newProps[0]; - } - else - { - frontLine && newProps.push(this.clonePlData(frontLine.LineData[1])); - break; - } - } - - if (!equal(frontLine.EndPoint, laterLine.StartPoint)) - { - //在两段上交点 - let interPts = frontLine.IntersectWith(laterLine, IntersectOption.OnBothOperands); - if (interPts.length > 0) - { - this.adjustFrontLine(newProps[newProps.length - 1], frontLine.LineData[1], interPts[0]); - this.adjustLaterLine(newProp, laterLine.LineData[1], interPts[0]); - } - else - { - let intPts = frontLine.IntersectWith(laterLine, IntersectOption.ExtendBoth); - if (intPts.length > 0) - { - let intPt = this.selectFitInterPt(intPts, frontLine.EndPoint); - let par1 = frontLine.GetParamAtPoint(intPt); - let par2 = laterLine.GetParamAtPoint(intPt); - let isOnFline = frontLine.PtOnCurve(intPt); - let isOnLline = laterLine.PtOnCurve(intPt); - if (!(frontLine.LineData[0].bul || laterLine.LineData[0].bul)) - { - if (par1 > 1) - newProp.pt = Vec3DTo2D(intPt) - else - newProps.push( - { - pt: frontLine.LineData[1].pt, - bul: 0 - }) - } - else if (frontLine.LineData[0].bul === 0 && laterLine.LineData[0].bul !== 0) - { - if (par1 > 1) - { - this.adjustLaterLine(newProp, laterLine.LineData[1], intPt); - } - else - { - newProps.push( - { - pt: frontLine.LineData[1].pt, - bul: 0 - }) - } - } - else if (frontLine.LineData[0].bul !== 0 && laterLine.LineData[0].bul === 0) - { - - if (par1 > 1) - { - this.adjustFrontLine(newProps[newProps.length - 1], frontLine.LineData[1], intPt); - this.adjustLaterLine(newProp, laterLine.LineData[1], intPt); - } - else if (par1 < 0 && par2 < 0) - { - addArc(offestPlList[i + 1].index, frontLine, laterLine, newProps); - } - else - { - newProps.push( - { - pt: frontLine.LineData[1].pt, - bul: 0 - }) - } - } - else - { - if ((!isOnFline && !isOnLline)) - { - this.adjustFrontLine(newProps[newProps.length - 1], frontLine.LineData[1], intPt); - this.adjustLaterLine(newProp, laterLine.LineData[1], intPt); - } - else - { - addArc(offestPlList[i + 1].index, frontLine, laterLine, newProps); - } - } - } - else - { - if (!offestPlList[i + 1]) - addArc(offestPlList[0].index, frontLine, laterLine, newProps); - else - addArc(offestPlList[i + 1].index, frontLine, laterLine, newProps); - } - } - } - newProps.push(newProp) - - } - return newProps; - } - // 通过构建的轮廓对偏移曲线进行裁剪 - private trimByContours(pl: Polyline, cons: Contour[]) - { - let needCutPls = pl.Explode(); - for (let c of cons) - { - let tmpCus: Curve[] = []; - let outline = c.Outline; - - needCutPls.forEach(l => - { - if (isTargetCurOutOrOnSourceCur(outline, l)) - { - tmpCus.push(l) - } - else - { - let pts = l.IntersectWith(outline, IntersectOption.OnBothOperands); - if (pts.length > 0) - { - let par = pts.map(p => l.GetParamAtPoint(p)); - let cus = l.GetSplitCurves(par); - tmpCus.push(...cus.filter(cu => !isTargetCurInSourceCur(outline, cu))); - - } - } - }) - needCutPls = tmpCus; - } - return needCutPls; - } - getIntersectWithSelf(cus: Curve[]) - { - let pls: Polyline[] = []; - for (let i = 0; i < cus.length; i++) - { - let frontLine = cus[i]; - //要删除自交线的索引 - let deleteIndex: number; - //截取到的自交线交点 - let intertPt = new Vector3(); - - for (let j = cus.length - 1; j > i; j--) - { - let laterLine = cus[j]; - let intPts = frontLine.IntersectWith(laterLine, IntersectOption.OnBothOperands); - - if (intPts.length > 0) - { - if (j - i === 1) - break; - deleteIndex = j; - intertPt.copy(intPts[0]); - break; - } - } - if (deleteIndex) - { - // 删除的自交线 - let deleteLine = cus[deleteIndex]; - let parInFront = frontLine.GetParamAtPoint(intertPt); - let parInLater = deleteLine.GetParamAtPoint(intertPt); - let spliteCusByFront = frontLine.GetSplitCurves(parInFront); - let spliteCusByLater = deleteLine.GetSplitCurves(parInLater); - if (spliteCusByFront.length > 1) - frontLine = spliteCusByFront[0]; - if (spliteCusByLater.length > 1) - deleteLine = spliteCusByLater[1]; - - - //分离出的线 - let deletePlList = cus.splice(i + 1, deleteIndex - i - 1); - - if (deletePlList.length) - { - //添加自交开始结束的部分 - if (spliteCusByFront.length > 1) - deletePlList.unshift(spliteCusByFront[1]); - if (spliteCusByLater.length > 1) - deletePlList.push(spliteCusByLater[0]); - pls.push(...this.handleRemoveLine(deletePlList)) - } - } - } - return pls; - } - //处理分离出的多段线,把相交的部分提取出来 - private handleRemoveLine(plList: Curve[]) - { - let startIndex: number; - let endIndex: number; - let insertPt = new Vector3(); - for (let i = 0; i < plList.length - 1; i++) - { - //前一根线 - let frontLine = plList[i]; - //截取到的自交线交点 - for (let j = i + 1; j < plList.length; j++) - { - // 后一根线 - let laterLine = plList[j]; - let intPts = frontLine.IntersectWith(laterLine, IntersectOption.OnBothOperands); - if (intPts.length === 2) - { - intPts[0].distanceTo(laterLine.StartPoint) < intPts[1].distanceTo(laterLine.StartPoint) ? intPts.pop() : intPts.shift(); - } - if (intPts.length > 0 && !equal(intPts[0], laterLine.StartPoint) && !equal(intPts[0], laterLine.EndPoint)) - { - startIndex = i; - endIndex = j; - insertPt.copy(intPts[0]); - } - } - } - if (startIndex !== undefined) - { - // 删除的自交线 - let startLine = plList[startIndex]; - let endLine = plList[endIndex]; - - endLine.EndPoint = insertPt; - startLine.StartPoint = insertPt; - - return this.linkCurves(plList.slice(startIndex, endIndex + 1)); - } - - return this.linkCurves(plList); - - } - //连接最终曲线 - private linkCurves(cus: Curve[]) - { - let pl = new Polyline(); - let resultPls: Polyline[] = []; - while (cus.length) - { - let cu = cus.shift(); - for (let i = 0; i < cus.length; i++) - { - let linkedCu = cus[i]; - - let pts = cu.IntersectWith(linkedCu, IntersectOption.OnBothOperands); - if (pts.length) - { - cu.EndPoint = pts[0]; - pl.Join(cu) - linkedCu.StartPoint = pts[0]; - cu = cus.splice(i, 1)[0]; - i--; - } - else - { - continue; - } - } - pl.Join(cu); - resultPls.push(pl); - pl = new Polyline(); - } - return resultPls; - } - // 根据交点调整前面线段的凸度 - private adjustFrontLine(startV: PolylineProps, endV: PolylineProps, insertPt: Vector3) - { - if (startV.bul !== 0) - { - let arc = new Arc().ParseFromBul(startV.pt, endV.pt, startV.bul); - arc.EndPoint = insertPt; - //前面线的凸度调整 - startV.bul = Math.tan(arc.AllAngle / 4) * Math.sign(startV.bul); - } - } - // 根据交点调整后面线段的凸度和起始点 - private adjustLaterLine(startV: PolylineProps, endV: PolylineProps, interPt: Vector3) - { - let bul = 0; - if (startV.bul !== 0) - { - let arc = new Arc().ParseFromBul(startV.pt, endV.pt, startV.bul); - arc.StartPoint = interPt; - bul = Math.tan(arc.AllAngle * 0.25) * Math.sign(startV.bul); - } - // 调整后面线起始点和凸度 - startV.pt = Vec3DTo2D(interPt); - startV.bul = bul; - } - // 选择合适的交点 - private selectFitInterPt(pts: Vector3[], refPt: Vector3) - { - let pt = pts[0]; - if (pts.length > 1) - { - let dist1 = refPt.distanceToSquared(pts[0]); - let dist2 = refPt.distanceToSquared(pts[1]); - pt = dist1 <= dist2 ? pts[0] : pts[1]; - } - return pt; - } - private clonePlData(pl: PolylineProps) - { - return { - pt: pl.pt, - bul: pl.bul - } - } - -} - -export class PolyOffestUtil3 { private m_Polyline: Polyline; private m_OffestDist: number; @@ -1078,6 +44,7 @@ export class PolyOffestUtil3 } let newPls = this.trimAndJointOffestPolyline(plList1, this.m_Polyline); + let cus = this.trimByContours(newPls, contours); if (this.m_Polyline.IsClose && this.m_OffDir < 0) @@ -1093,41 +60,27 @@ export class PolyOffestUtil3 { let rets = this.linkSelfingCurves(cus); // 先尝试把线段首尾相连成封闭区域 - rets.forEach(cu => - { - //不闭合 并且线段数大于等于3 - //FIXME: - if (!cu.IsClose && cu.EndParam > 2) - { - let pts = cu.GetCurveAtParam(0).IntersectWith(cu.GetCurveAtParam(cu.EndParam), IntersectOption.OnBothOperands); - if (pts.length === 1) - { - cu.StartPoint = pts[0]; - cu.EndPoint = pts[0]; - } - else if (pts.length > 1) - console.warn("未预料到的情况"); - } - }) - rets = rets.filter((cu, index) => - { - if (index === 0) return true; - else - { - let pts = cu.IntersectWith(rets[0], IntersectOption.OnBothOperands); - return pts.length == 0; - } - }) - //提取出闭合的,如果和闭合的相交,也去除 - let closedCus = rets.filter(cu => cu.IsClose); - return rets.filter((cu, index) => - { - if (index === 0) return true; - else - { - return cu.IsClose || !closedCus.some(cu1 => cu.IntersectWith(cu1, IntersectOption.OnBothOperands).length > 0); - } - }) + rets = rets.map(cu => this.closePolyline(cu)); + return rets; + // rets = rets.filter((cu, index) => + // { + // if (index === 0) return true; + // else + // { + // let pts = cu.IntersectWith(rets[0], IntersectOption.OnBothOperands); + // return pts.length == 0; + // } + // }) + // //提取出闭合的,如果和闭合的相交,也去除 + // let closedCus = rets.filter(cu => cu.IsClose); + // return rets.filter((cu, index) => + // { + // if (index === 0) return true; + // else + // { + // return cu.IsClose || !closedCus.some(cu1 => cu.IntersectWith(cu1, IntersectOption.OnBothOperands).length > 0); + // } + // }) } } @@ -1257,7 +210,7 @@ export class PolyOffestUtil3 } } } - isVail(oldCu: Curve, newCu: Curve) + isVail(oldCu: Curve, newCu: Curve): boolean { if (oldCu instanceof Line) { @@ -1364,14 +317,14 @@ export class PolyOffestUtil3 } // 通过构建的轮廓对偏移曲线进行裁剪 - private trimByContours(needCutPls: Curve[], cons: Contour[]) + private trimByContours(needCutCus: Curve[], cons: Contour[]): Curve[] { for (let c of cons) { let tmpCus: Curve[] = []; let outline = c.Outline; - needCutPls.forEach(l => + needCutCus.forEach(l => { //在上面或者在外面 if (isTargetCurOutOrOnSourceCur(outline, l)) @@ -1389,9 +342,9 @@ export class PolyOffestUtil3 } } }) - needCutPls = tmpCus; + needCutCus = tmpCus; } - return needCutPls; + return needCutCus; } /** * 判断曲线是否自交 @@ -1401,7 +354,7 @@ export class PolyOffestUtil3 * @returns * @memberof PolyOffestUtil3 */ - private isSelfingCus(cus: Curve[]) + private isSelfingCus(cus: Curve[]): boolean { for (let i = 0; i < cus.length; i++) { @@ -1452,7 +405,7 @@ export class PolyOffestUtil3 * @returns * @memberof PolyOffestUtil3 */ - linkSelfingCurves(cus: Curve[]) + linkSelfingCurves(cus: Curve[]): Polyline[] { let retPls: Polyline[] = []; let isSelfing = this.isSelfingCus(cus); @@ -1488,9 +441,10 @@ export class PolyOffestUtil3 continue; } - if (equaln(parForFront, 0)) + if (equaln(parForFront, 0, 1e-6)) { //理论应该把该段移除出来 + continue; } else if (equaln(parForFront, 1, 1e-6)) { @@ -1530,6 +484,47 @@ export class PolyOffestUtil3 retPls.push(...this.linkCurves(cus)); return retPls; } + closePolyline(pl: Polyline) + { + //闭合或者只有一条线段或者2条线段都是直线的,直接返回 + if (pl.IsClose || pl.EndParam < 2 || ((pl.EndParam === 2 && pl.GetBuilgeAt(0) == 0 && pl.GetBuilgeAt(1) == 0))) + return pl; + + for (let i = 0; i < pl.EndParam; i++) + { + let frontCu = pl.GetCurveAtIndex(i); + for (let j = pl.EndParam - 1; j > i; j--) + { + let lastCu = pl.GetCurveAtIndex(j); + let pts = frontCu.IntersectWith(lastCu, IntersectOption.OnBothOperands); + + + pts = pts.filter(p => + { + return !(equal(p, frontCu.EndPoint) && equal(p, lastCu.StartPoint)) && !(equal(p, frontCu.StartPoint) && equal(p, lastCu.EndPoint)) + }) + + if (pts.length == 1) + { + let polyProps = pl.LineData.slice(i, j + 2); + polyProps[0].pt = Vec3DTo2D(pts[0]); + if (frontCu instanceof Arc) + { + frontCu.StartPoint = pts[0]; + polyProps[0].bul = Math.tan(frontCu.AllAngle / 4) * Math.sign(polyProps[0].bul); + } + arrayLast(polyProps).pt = Vec3DTo2D(pts[0]); + return new Polyline(polyProps); + + } + else if (pts.length > 1) + { + console.warn("未知情况"); + } + } + } + return pl; + } // 选择合适的交点 private selectFitInterPt(pts: Vector3[], refPt: Vector3) {