diff --git a/__test__/Geometry/intersect.test.ts b/__test__/Geometry/intersect.test.ts index b8f77f5c4..0afe5bc24 100644 --- a/__test__/Geometry/intersect.test.ts +++ b/__test__/Geometry/intersect.test.ts @@ -90,7 +90,19 @@ test('三维空间直线和圆相交测试', () => testLineAndCirIntersect(data); }) - +test('补充相交测试', () => +{ + let data = [["Line", 1, 1, 2308, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1.3934337707580937, 0.039495588796686576, 0, 1], 1, [12.270990240675426, 8.520323796959827, 0], [12.270990240675426, 4.4951100391838885, 0]], ["Arc", 1, 1, 2313, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 13.662687223232803, 4.584664645783669, 0, 1], 2, 0.003163048700428762, 6.156784789283385, 3.3367334474492862, true]] + let cus = loadFile(data); + let [cu1, cu2] = cus[0] instanceof Line ? cus : cus.reverse(); + let pts = cu1.IntersectWith(cu2, 0); + expect(pts.length).toBe(1); + data = [["Line", 1, 1, 2308, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1.3934337707580937, 0.039495588796686576, 0, 1], 1, [12.270990240675426, 8.520323796959827, 0], [12.270990240675426, 4.4951100391838885, 0]], ["Circle", 1, 1, 2320, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 13.662687223232803, 4.584664645783669, 0, 1], 1, 0.0031630487004287015]]; + cus = loadFile(data); + [cu1, cu2] = cus[0] instanceof Line ? cus : cus.reverse(); + pts = cu1.IntersectWith(cu2, 0); + expect(pts.length).toBe(2); +}) function loadFile(data) { let file = new CADFile(); diff --git a/__test__/Polyline/offset.test.ts b/__test__/Polyline/offset.test.ts index 82b5d5178..b9fc8d02a 100644 --- a/__test__/Polyline/offset.test.ts +++ b/__test__/Polyline/offset.test.ts @@ -1,8 +1,21 @@ import { Factory } from "../../src/DatabaseServices/CADFactory"; import { CADFile } from "../../src/DatabaseServices/CADFile"; import { Polyline } from "../../src/DatabaseServices/Polyline"; +import { Curve } from "../../src/DatabaseServices/Curve"; Factory(Polyline); +function loadFile(data) +{ + let file = new CADFile(); + file.Data = data; + let cus: Curve[] = []; + for (let i = 0; i < file.Data.length; i++) + { + cus.push(file.ReadObject(undefined) as Curve); + } + return cus; +} + test('IKKGK圆与直线补圆弧', () => { let f = new CADFile(); @@ -37,3 +50,57 @@ test('IKKGK圆与直线补圆弧', () => cus = pl.GetOffsetCurves(-1.926388985025112); expect(cus.length).toBe(1); }); + +test('多段线偏移测试1', () => +{ + let data = [["Polyline", 1, 1, 3, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 4, [6.059716713881022, 3.983456090651555], 1.1217395195062214, [6.383002832861191, 4.064022662889515], 0, [4.929178470254959, 8.399433427762037], 1.2642365052895204, [5.750708215297452, 4.050991501416427], 0.7171789779484218, true]]; + + let cus = loadFile(data); + for (let i = 0; i < 10; i += 0.01) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = -1.5; i < 0; i += 0.01) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = 2; i < 10; i += 0.01) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(0); + } +}) +test('多段线偏移测试2', () => +{ + let data = [["Polyline", 1, 1, 734, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 4, [8.176442643449754, 4.003694129881114], 0.6482011490054378, [8.554554536583165, 4.090460859042256], 0, [7.7292787370841225, 8.733182129315965], 0, [7.8227800907745895, 4.037582052934876], 1.223388515290821, true]]; + + let cus = loadFile(data); + expect(cus[0].GetOffsetCurves(-0.1799).length).toBe(2); + + for (let i = 0; i < 10; i += 1) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = -0.17; i < 0; i += 0.01) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = -0.18; i < -0.34; i -= 0.01) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = 0.35; i < 3; i += 0.01) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(0); + } +}) +test('多段线偏移测试3', () => +{ + let data = [["Polyline", 1, 1, 1172, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 5, [12.52535684411379, 4.512623511896158], 0.39558516940595195, [13.121416971132648, 4.487590129452889], 0, [12.414545456418505, 8.796498405316765], 0, [12.015990240675425, 8.743859037199755], 0, [12.015990240675425, 4.4951100391838885], 0.8508932598252141, true]]; + + let cus = loadFile(data); + for (let i = 0; i < 10; i += 1) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + +}) diff --git a/src/Add-on/Offset.ts b/src/Add-on/Offset.ts index 70a66d012..7c864d1fd 100644 --- a/src/Add-on/Offset.ts +++ b/src/Add-on/Offset.ts @@ -74,10 +74,6 @@ export class Command_TestOffset implements Command let dir = GetPointAtCurveDir(cu, p) ? 1 : -1 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/Add-on/test/testIntersect.ts b/src/Add-on/test/testIntersect.ts index 7e3cdb11f..5129725a4 100644 --- a/src/Add-on/test/testIntersect.ts +++ b/src/Add-on/test/testIntersect.ts @@ -94,7 +94,7 @@ export class TestIntersect implements Command let cus = exRefSsRes.SelectSet.SelectEntityList as Curve[]; if (cus.length === 2) { - let pt = cus[0].IntersectWith(cus[1], IntersectOption.ExtendBoth); + let pt = cus[0].IntersectWith(cus[1], 0); console.log('pt: ', pt); if (pt.length) { diff --git a/src/Add-on/testEntity/TestCurve.ts b/src/Add-on/testEntity/TestCurve.ts index 1c8811ff4..c698d61c4 100644 --- a/src/Add-on/testEntity/TestCurve.ts +++ b/src/Add-on/testEntity/TestCurve.ts @@ -6,6 +6,9 @@ import { Curve } from "../../DatabaseServices/Curve"; import { Polyline } from "../../DatabaseServices/Polyline"; import { Command } from "../../Editor/CommandMachine"; import { PromptStatus } from "../../Editor/PromptResult"; +import { arraySortByNumber, arrayLast, arrayRemoveIf } from "../../Common/ArrayExt"; +import { equaln } from "../../Geometry/GeUtils"; +import { IsPtsAllOutOrOnReg } from "../../GraphicsSystem/BoolOperateUtils"; export class TestTargeOnCurve implements Command { @@ -29,14 +32,19 @@ export class TestTargeOnCurve implements Command { let target = ssRes.SelectSet.SelectEntityList[0] as Curve; - let con1 = Contour.CreateContour([source]); - let con2 = Contour.CreateContour([target]); - let res = con1.getOperatedCurves(con2); - testCurve(res.unionList, 1); - testCurve(res.intersectionList, 2); - testCurve(res.subtractList, 3); - target.Erase(); - source.Erase(); + // let con1 = Contour.CreateContour([source]); + // let con2 = Contour.CreateContour([target]); + // let res = con1.getOperatedCurves(con2); + // testCurve(res.unionList, 1); + // testCurve(res.intersectionList, 2); + // testCurve(res.subtractList, 3); + this.testClip(source as Polyline, target).forEach((c, i) => + { + c.ColorIndex = i + 1; + app.m_Database.ModelSpace.Append(c); + }) + // target.Erase(); + // source.Erase(); } } catch (err) @@ -44,7 +52,45 @@ export class TestTargeOnCurve implements Command console.log(err); } } + testClip(outline: Polyline, l: Curve) + { + let tmpCus = []; + //交点参数列表 + let iParams = l.IntersectWith(outline, 0) + .map(p => l.GetParamAtPoint(p)); + arraySortByNumber(iParams); + + //需要计算的点列表 + let needCaclPts: Vector3[] = []; + for (let i = 0; i < iParams.length - 1; i++) + { + needCaclPts.push(l.GetPointAtParam((iParams[i] + iParams[i + 1]) / 2)); + } + //如果交点不是首尾点,就加入首尾点 + if (!equaln(iParams[0], 0, 1e-6)) + needCaclPts.unshift(l.StartPoint); + if (!equaln(arrayLast(iParams), 1, 1e-6)) + needCaclPts.push(l.EndPoint); + + //切割曲线,缓存切割后曲线包围盒 + if (IsPtsAllOutOrOnReg(outline, needCaclPts)) + { + tmpCus.push(l); + } + else + { + let cus = l.GetSplitCurves(iParams); + + //移除0长度线和在轮廓内的线. + arrayRemoveIf(cus, cu => equaln(cu.Length, 0, 1e-6) + || outline.PtInCurve(cu.GetPointAtParam(0.5)) + ); + tmpCus.push(...cus); + } + return tmpCus; + } } + export function testPts(pts: Vector3[], color = 2) { pts.forEach(p => diff --git a/src/GraphicsSystem/IntersectWith.ts b/src/GraphicsSystem/IntersectWith.ts index 34fe4edff..3a081ab7f 100644 --- a/src/GraphicsSystem/IntersectWith.ts +++ b/src/GraphicsSystem/IntersectWith.ts @@ -165,7 +165,7 @@ function IntersectLineAndCircleOrArc(line: Line, circle: Circle | Arc) let c = center.lengthSq() + startPoint.lengthSq() - 2 * center.dot(startPoint) - radius * radius; let det = b * b - 4 * a * c; - if (equaln(det, 0)) + if (equaln(det, 0, 1e-4)) { let delta = -b / (2 * a); return [startPoint.add(lineV.multiplyScalar(delta))]; diff --git a/src/GraphicsSystem/OffestPolyline.ts b/src/GraphicsSystem/OffestPolyline.ts index 3e31ff227..1bbd640a6 100644 --- a/src/GraphicsSystem/OffestPolyline.ts +++ b/src/GraphicsSystem/OffestPolyline.ts @@ -12,6 +12,7 @@ import { equal, equaln } from "../Geometry/GeUtils"; import { EBox, SortEntityByBox } from "../Geometry/SortEntityByBox"; import { IsPtsAllOutOrOnReg } from "./BoolOperateUtils"; import { IntersectOption } from "./IntersectWith"; +import { testContours } from "../Add-on/testEntity/TestCurve"; interface offestRes { @@ -52,14 +53,18 @@ export class PolyOffestUtil // console.time("join") this.TrimAndBuildContour(offres); // console.timeEnd("join") - // testContours(this.m_Contours); + testContours(this.m_Contours); //裁剪 let { boxCurves, outputCus } = this.trimByContours(this.m_RetCurves); - - // 优化裁剪后的曲线 - outputCus = this.optimizeCus(boxCurves, outputCus); - outputCus.push(...this.unNeedCutCus); - return this.linkCurves(outputCus); + // // 优化裁剪后的曲线 + // this.m_RetCurves = this.optimizeCus(boxCurves, outputCus); + // this.m_RetCurves.push(...this.unNeedCutCus); + + // this.m_RetCurves = this.linkCurves(this.m_RetCurves); + // // 如果源线段闭合只保留闭合的部分 + // if (this.m_Polyline.IsClose) + // return this.m_RetCurves.filter(c => c.IsClose); + return this.m_RetCurves; } /** @@ -84,18 +89,22 @@ export class PolyOffestUtil } private optimizeCus(boxCurves: Map, outputCus: Curve[]) { - //过滤掉无效的线段 - outputCus = outputCus.filter(c => + if (!this.m_Polyline.IsClose) { - //与源线段自交 - if (c.IntersectWith(this.m_Polyline, IntersectOption.OnBothOperands).length !== 0) - return false; - //删除在反方向的无效线段 - return this.CheckPointDir(c.StartPoint); - // return this.CheckPointDist(c.StartPoint) && this.CheckPointDist(c.EndPoint) - // return (this.CheckPointDist(c.StartPoint) && this.CheckPointDist(c.EndPoint)) - // && this.CheckPointDir(c.StartPoint); - }); + //过滤掉无效的线段 + outputCus = outputCus.filter(c => + { + //与源线段自交 + if (c.IntersectWith(this.m_Polyline, IntersectOption.OnBothOperands).length !== 0) + return false; + //删除在反方向的无效线段 + return this.CheckPointDir(c.StartPoint) || this.CheckPointDir(c.EndPoint); + // return this.CheckPointDist(c.StartPoint) && this.CheckPointDist(c.EndPoint) + // return (this.CheckPointDist(c.StartPoint) && this.CheckPointDist(c.EndPoint)) + // && this.CheckPointDir(c.StartPoint); + }); + } + //处理自交的线段 let count = outputCus.length; @@ -358,7 +367,7 @@ export class PolyOffestUtil { this.appendNewCuAndContour(frontLine, nextPt, intPt, startIndex); } - else if (frontLine instanceof Arc && par1 < 0 && par2 < 1) + else if (frontLine instanceof Arc && par1 < 0 && par2 < 0) { this.appendNewCuAndContour(frontLine, nextPt, intPt, startIndex); }