diff --git a/__test__/Polyline/IntersectSelf.test.ts b/__test__/Polyline/IntersectSelf.test.ts new file mode 100644 index 000000000..964c4ee0a --- /dev/null +++ b/__test__/Polyline/IntersectSelf.test.ts @@ -0,0 +1,30 @@ +import { Polyline } from "../../src/api"; +import { LoadCurvesFromFileData } from "../Utils/LoadEntity.util"; + +describe("多段线自交测试", () => +{ + test("1", () => + { + let data = { "file": [1, "Polyline", 8, 2, 100, false, 1, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 2, 5, [-405.4658285340993, 346.15614953113254], 0, [199.05178654403426, 293.2185491719283], 0, [-41.37296878534835, -275.93381387612317], 0, [-295.29944682260975, -125.79927409254014], 0, [-405.4658285340993, 346.15614953113254], 0, false], "basePt": { "x": -405.4658285340993, "y": -275.93381387612317, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }; + let l = LoadCurvesFromFileData(data)[0] as Polyline; + expect(l.IsIntersectSelf()).toBeFalsy(); + }); + test("2", () => + { + let data = { "file": [1, "Polyline", 8, 2, 100, false, 1, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 2, 5, [-405.4658285340993, 346.15614953113254], 0, [199.05178654403426, 293.2185491719283], 0, [-41.37296878534835, -275.93381387612317], 0, [-329.7340533770039, -149.1338534587412], 0, [-168.03429997805506, 499.10567020310555], 0, false], "basePt": { "x": -405.4658285340993, "y": -275.93381387612317, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }; + let l = LoadCurvesFromFileData(data)[0] as Polyline; + expect(l.IsIntersectSelf()).toBeTruthy(); + }); + test("3", () => + { + let data = { "file": [2, "Polyline", 8, 2, 100, false, 1, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 2, 5, [-405.4658285340993, 346.15614953113254], 0, [199.05178654403426, 293.2185491719283], 0, [-41.37296878534835, -275.93381387612317], 0, [-329.7340533770039, -149.1338534587412], 0, [-168.03429997805506, 499.10567020310555], 0, false, "Polyline", 8, 2, 101, false, 1, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 2, 3, [350.9080380477244, -198.03631544369273], 0, [602.9728936312604, -450.10117102722876], 0, [450.7051441358635, -297.83342153183185], 0, false], "basePt": { "x": -405.4658285340993, "y": -450.10117102722876, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }; + let l = LoadCurvesFromFileData(data)[0] as Polyline; + expect(l.IsIntersectSelf()).toBeTruthy(); + }); + test("4", () => + { + let data = { "file": [1, "Polyline", 8, 2, 102, false, 1, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 2, 4, [299.09997619187925, -843.5733706604224], 0, [975.021500869887, -932.7190956496634], -0.9667799920499004, [909.3278130216058, -1605.3849557429785], 0.9667799920499004, [975.021500869887, -932.7190956496634], 0, false], "basePt": { "x": 299.09997619187925, "y": -1606.0680424764628, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }; + let l = LoadCurvesFromFileData(data)[0] as Polyline; + expect(l.IsIntersectSelf()).toBeTruthy(); + }); +}); diff --git a/src/Add-on/DrawBoard/FixIntersectSelfContour.ts b/src/Add-on/DrawBoard/FixIntersectSelfContour.ts new file mode 100644 index 000000000..65666afbf --- /dev/null +++ b/src/Add-on/DrawBoard/FixIntersectSelfContour.ts @@ -0,0 +1,38 @@ +import { app } from "../../ApplicationServices/Application"; +import { Log } from "../../Common/Log"; +import { Board } from "../../DatabaseServices/Entity/Board"; +import { Polyline } from "../../DatabaseServices/Entity/Polyline"; +import { Command } from "../../Editor/CommandMachine"; +import { PromptStatus } from "../../Editor/PromptResult"; +import { CreateContour2 } from "../../Geometry/CreateContour2"; + +export class FixIntersectSelfContour implements Command +{ + async exec() + { + let brRes = await app.Editor.GetSelection({ + Msg: "选择板件", + Filter: { filterTypes: [Board] }, + UseSelect: true, + }); + + if (brRes.Status === PromptStatus.Cancel) return; + + let brs = brRes.SelectSet.SelectEntityList as Board[]; + + for (let br of brs) + { + let contour = br.ContourCurve; + if (contour instanceof Polyline && + (contour.TempData?.IntSelf || contour.IsIntersectSelf())) + { + let newContor = CreateContour2(contour.Explode()); + if (newContor) + br.ContourCurve = newContor.Curve; + else + Log("修正自交失败," + br.Name + " 板件可能仍存在自交,请手动修复"); + } + } + + } +} diff --git a/src/Add-on/Erp/ErpCommands.ts b/src/Add-on/Erp/ErpCommands.ts index 1806cc0f2..06d952be8 100644 --- a/src/Add-on/Erp/ErpCommands.ts +++ b/src/Add-on/Erp/ErpCommands.ts @@ -20,6 +20,8 @@ import { ErpRoutes } from "./Models/ErpRoutes"; import { ErpParseData } from "./ParseData"; import { checkInterfereTool } from "../../Common/InterfereUtil"; import { AppConfirm } from "../../UI/Components/Common/Confirm"; +import { Polyline } from "../../DatabaseServices/Entity/Polyline"; +import { SelectSetBase } from "../../Editor/SelectBase"; export class ChaiDan implements Command { @@ -113,6 +115,35 @@ async function ExecChaiDan(chaiDanRoute: ErpRoutes) let selction = await GetProductsEntitys(); if (!selction) return; + let intSelfBoards = new Set(); + for (let br of selction.boardList) + { + let b = br.__OriginalEnt__ ?? br; + if (!intSelfBoards.has(b)) + { + let c = b.ContourCurve; + if (c instanceof Polyline && c.IsIntersectSelf()) + { + c.TempData = { IntSelf: true }; + intSelfBoards.add(br); + } + } + } + if (intSelfBoards.size > 0) + { + AppToaster.show({ + message: `有${intSelfBoards.size}个板件存在自交轮廓了,可以运行命令:FISC尝试修正`, + timeout: 8000, + intent: Intent.DANGER, + }); + let selectData = new SelectSetBase(app.Viewer); + for (let br of intSelfBoards) + selectData._SelectList.add(br.DrawObject); + app.Editor.SelectCtrl.AddSelect(selectData, true); + app.Editor.SelectCtrl.UpdateView(); + return; + } + if (userConfig.chaidanOption.isCheckInterfere) { let originEns = new Set(); @@ -124,7 +155,7 @@ async function ExecChaiDan(chaiDanRoute: ErpRoutes) { AppToaster.show({ message: `有${objMap.size}组实体干涉了,请退出检查确认`, - timeout: 3000, + timeout: 5000, intent: Intent.DANGER, }); } diff --git a/src/Common/CommandNames.ts b/src/Common/CommandNames.ts index 9f70c5fcc..16ad286f8 100644 --- a/src/Common/CommandNames.ts +++ b/src/Common/CommandNames.ts @@ -194,4 +194,5 @@ export enum CommandNames CheckEdge = "CHECKEDGE", Knife = "KNIFES",//编辑BBS R2B2 = "RECT2BOARD2", + FixIntSelfContour = "FIXINTSELFCONTOUR", } diff --git a/src/DatabaseServices/Entity/Polyline.ts b/src/DatabaseServices/Entity/Polyline.ts index c31f8b161..7f31b4604 100644 --- a/src/DatabaseServices/Entity/Polyline.ts +++ b/src/DatabaseServices/Entity/Polyline.ts @@ -1270,6 +1270,51 @@ export class Polyline extends Curve } return intParams; } + IsIntersectSelf() + { + let cus = this.Explode(); + for (let i = 0; i < cus.length - 1; i++) + { + let c1 = cus[i]; + let c1IsLine = c1 instanceof Line; + let d1 = c1.GetFistDeriv(c1IsLine ? 0 : 1).normalize(); + + for (let j = i + 1; j < cus.length; j++) + { + let c2 = cus[j]; + let c2IsLine = c2 instanceof Line; + let d2 = c2.GetFistDeriv(0).normalize(); + if (j === i + 1) + { + if (c1IsLine === c2IsLine) + { + if (c1IsLine) + { + if (equalv3(d1, d2.negate())) + return true; + continue; + } + else + { + if (equalv3(d1, d2.negate()) && equalv3((c1).Center, (c2).Center)) + return true; + } + } + } + + let intPtsLen = c1.IntersectWith2(c2, 0).length; + + if (intPtsLen > 0) + { + if (this.IsClose && i === 0 && j === cus.length - 1 && intPtsLen === 1) + continue; + return true; + } + } + + } + return false; + } get BoundingBox() { diff --git a/src/Editor/CommandRegister.ts b/src/Editor/CommandRegister.ts index e458b44e4..b6f953616 100644 --- a/src/Editor/CommandRegister.ts +++ b/src/Editor/CommandRegister.ts @@ -211,6 +211,7 @@ import { Interfere } from './../Add-on/interfere'; import { ShowKinfeManageModal } from './../Add-on/showModal/ShowKnifeManageModal'; import { commandMachine } from './CommandMachine'; import { Rect2Board } from "../Add-on/twoD2threeD/Rect2Board"; +import { FixIntersectSelfContour } from "../Add-on/DrawBoard/FixIntersectSelfContour"; export function registerCommand() { @@ -588,6 +589,8 @@ export function registerCommand() commandMachine.RegisterCommand(CommandNames.DrawTempByImport, new DrawTemplateByImport()); commandMachine.RegisterCommand(CommandNames.R2B2, new Rect2Board()); + + commandMachine.RegisterCommand(CommandNames.FixIntSelfContour, new FixIntersectSelfContour()); } export async function RegistCustomCommand() diff --git a/src/UI/Components/CommandPanel/CommandList.ts b/src/UI/Components/CommandPanel/CommandList.ts index 9c552192a..1bbfa4163 100644 --- a/src/UI/Components/CommandPanel/CommandList.ts +++ b/src/UI/Components/CommandPanel/CommandList.ts @@ -1462,6 +1462,15 @@ export const CommandList: ICommand[] = [ chName: "封边检查", chDes: "封边检查", }, + { + typeId: "util", + link: "#", + defaultCustom: "FISC", + command: CommandNames.FixIntSelfContour, + type: "工具", + chName: "修正板件自交轮廓", + chDes: "修正板件自交轮廓", + }, //#endregion //#region 文件命令