mirror of https://gitee.com/cf-fz/WebCAD.git
!2576 功能:一键检查功能
parent
1f3c7e186a
commit
d7ea6ec58c
@ -0,0 +1,139 @@
|
||||
import { ColorMaterial } from "../../Common/ColorPalette";
|
||||
import { CheckInterfereTool } from "../../Common/InterfereUtil";
|
||||
import { safeEval } from "../../Common/eval";
|
||||
import { ExtrudeHole } from "../../DatabaseServices/3DSolid/ExtrudeHole";
|
||||
import { SweepSolid } from "../../DatabaseServices/3DSolid/SweepSolid";
|
||||
import { BoardIsLong, BoardIsShort } from "../../DatabaseServices/BoardLinesReactor";
|
||||
import { Board } from "../../DatabaseServices/Entity/Board";
|
||||
import { ExtrudeSolid } from "../../DatabaseServices/Entity/Extrude";
|
||||
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
|
||||
import { HardwareCompositeEntity } from "../../DatabaseServices/Hardware/HardwareCompositeEntity";
|
||||
import { GetSealedBoardContour } from "../../GraphicsSystem/CalcEdgeSealing";
|
||||
import { FeedingToolPath } from "../../GraphicsSystem/ToolPath/FeedingToolPath";
|
||||
import { Production } from "../../Production/Product";
|
||||
import { InterferenceCheck } from "../CheckHoles";
|
||||
|
||||
export async function InterfereInspection(selectEnts: (ExtrudeSolid | SweepSolid | HardwareCompositeEntity)[])
|
||||
{
|
||||
const checkInterfereTool = new CheckInterfereTool(ColorMaterial.GetConceptualMaterial(1));
|
||||
const objMap = await checkInterfereTool.Check(selectEnts);
|
||||
const ents = objMap.map(([_, ens]) => [ens, "板件干涉"]);
|
||||
return ents;
|
||||
}
|
||||
|
||||
export function MaxSizeBoardInspection(boards: Board[])
|
||||
{
|
||||
const brs = boards.filter(br => BoardIsLong(br));
|
||||
return brs.map(br => [[br], "超长板"]);
|
||||
}
|
||||
|
||||
export function MinSizeBoardInspection(boards: Board[])
|
||||
{
|
||||
const brs = boards.filter(br => BoardIsShort(br));
|
||||
return brs.map(br => [[br], "超短板"]);
|
||||
}
|
||||
|
||||
export function ModelInspection(boards: Board[])
|
||||
{
|
||||
boards = boards.flatMap(br => br.SplitBoards);
|
||||
|
||||
let feedingTool = FeedingToolPath.GetInstance();
|
||||
|
||||
let errGrooves: ExtrudeSolid[] = [];
|
||||
let errHoles: ExtrudeHole[] = [];
|
||||
|
||||
for (let br of boards)
|
||||
{
|
||||
let errorIndexs = feedingTool.CheckModeling(br);
|
||||
let grooves = br.Grooves;
|
||||
for (let index of errorIndexs)
|
||||
{
|
||||
errGrooves.push(grooves[index]);
|
||||
}
|
||||
errHoles.push(...feedingTool.CheckCustomHole(br));
|
||||
}
|
||||
|
||||
return [...errGrooves.map(g => [[g], "槽错误"]), ...errHoles.map(h => [[h], "孔错误"])];
|
||||
}
|
||||
|
||||
export function SpecialBoardContourInspection(boards: Board[])
|
||||
{
|
||||
boards = boards.flatMap(br => br.SplitBoards);
|
||||
|
||||
const brs = [];
|
||||
for (const br of boards)
|
||||
{
|
||||
let c = br.ContourCurve;
|
||||
if (c instanceof Polyline && c.IsIntersectSelf())
|
||||
{
|
||||
brs.push([[br], "板件可能存在轮廓自交"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
let sealedData = GetSealedBoardContour(br);
|
||||
if (!sealedData)
|
||||
{
|
||||
brs.push([[br], "板件可能存在轮廓自交"]);
|
||||
}
|
||||
else if (sealedData.hasSealedErr)
|
||||
{
|
||||
brs.push([[br], "板封边异常"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return brs;
|
||||
}
|
||||
|
||||
export function SplitBoardInspection(boards: Board[])
|
||||
{
|
||||
const brs = boards.filter((br: Board) =>
|
||||
br.BoardProcessOption.spliteHeight
|
||||
|| br.BoardProcessOption.spliteWidth
|
||||
|| br.BoardProcessOption.spliteThickness);
|
||||
return brs.map(br =>
|
||||
{
|
||||
const [L, W, H] = [br.Height, br.Width, br.Thickness];
|
||||
let str = "存在";
|
||||
if (br.BoardProcessOption.spliteHeight)
|
||||
{
|
||||
str += "拆单高(" + safeEval(br.BoardProcessOption.spliteHeight, { L, W, H }, 'L') + "); ";
|
||||
}
|
||||
if (br.BoardProcessOption.spliteWidth)
|
||||
{
|
||||
str += "拆单宽(" + safeEval(br.BoardProcessOption.spliteWidth, { L, W, H }, 'W') + "); ";
|
||||
}
|
||||
if (br.BoardProcessOption.spliteThickness)
|
||||
{
|
||||
str += "拆单厚(" + safeEval(br.BoardProcessOption.spliteThickness, { L, W, H }, 'H') + "); ";
|
||||
}
|
||||
return [[br], str];
|
||||
});
|
||||
}
|
||||
|
||||
export async function DrillInspection(boards: Board[])
|
||||
{
|
||||
const brs = [];
|
||||
let res = await InterferenceCheck(boards);
|
||||
for (const collsionBr of res)
|
||||
{
|
||||
brs.push([[collsionBr.LocalBoard, collsionBr.InterBoard], "排钻干涉"]);
|
||||
}
|
||||
return brs;
|
||||
}
|
||||
|
||||
export function DrawHoleInspection(boards: Board[])
|
||||
{
|
||||
const brs = [];
|
||||
for (let br of boards)
|
||||
{
|
||||
let sealedData = GetSealedBoardContour(br);
|
||||
if (!sealedData) continue;
|
||||
let { brContour: outline, sealedContour } = sealedData;
|
||||
let offsetTanslation = outline.BoundingBox.min;
|
||||
let info = Production.GetBoardHolesData(br, offsetTanslation, sealedContour);
|
||||
if (info.frontBackHoles.length === 0 && info.sideHoles.length === 0)
|
||||
brs.push(br);
|
||||
}
|
||||
return brs.map(br => [[br], "没有排孔的板"]);
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
import { Intent } from "@blueprintjs/core";
|
||||
import { app } from "../../ApplicationServices/Application";
|
||||
import { ColorMaterial } from "../../Common/ColorPalette";
|
||||
import { CheckInterfereTool } from "../../Common/InterfereUtil";
|
||||
import { SweepSolid } from "../../DatabaseServices/3DSolid/SweepSolid";
|
||||
import { Board } from "../../DatabaseServices/Entity/Board";
|
||||
import { Entity } from "../../DatabaseServices/Entity/Entity";
|
||||
import { ExtrudeSolid } from "../../DatabaseServices/Entity/Extrude";
|
||||
import { HardwareCompositeEntity } from "../../DatabaseServices/Hardware/HardwareCompositeEntity";
|
||||
import { Command } from "../../Editor/CommandMachine";
|
||||
import { PromptStatus } from "../../Editor/PromptResult";
|
||||
import { ModalState } from "../../UI/Components/Modal/ModalInterface";
|
||||
import { AppToaster } from "../../UI/Components/Toaster";
|
||||
import { DrawHoleInspection, DrillInspection, InterfereInspection, MaxSizeBoardInspection, MinSizeBoardInspection, ModelInspection, SpecialBoardContourInspection, SplitBoardInspection } from "./Inspection";
|
||||
import { OneClickInspectionComponent } from "./OneClickInspectionComponent";
|
||||
import { oneClickInspectionStore } from "./OneClickInspectionStore";
|
||||
import { ViewInspection } from "./ViewInspection";
|
||||
|
||||
|
||||
export class OneClickInspection implements Command
|
||||
{
|
||||
checkInterfereTool: CheckInterfereTool;
|
||||
|
||||
async exec()
|
||||
{
|
||||
const errEntInfos = new Map<string, (string | Entity[])[][]>();
|
||||
const store = oneClickInspectionStore;
|
||||
app.Editor.ModalManage.RenderModal(OneClickInspectionComponent, { store });
|
||||
const res = await app.Editor.ModalManage.Wait();
|
||||
|
||||
if (res.Status === ModalState.Ok)
|
||||
{
|
||||
this.checkInterfereTool = new CheckInterfereTool(ColorMaterial.GetConceptualMaterial(1));
|
||||
|
||||
let enRes = await app.Editor.GetSelection({
|
||||
Msg: "选择需要检查的对象",
|
||||
Filter: { filterTypes: [Entity] }
|
||||
});
|
||||
if (enRes.Status !== PromptStatus.OK) return;
|
||||
|
||||
let ens = enRes.SelectSet.SelectEntityList;
|
||||
|
||||
const parseEntity = (ents: Entity[]) =>
|
||||
{
|
||||
const boards = [];
|
||||
const hardwareCompositeEntitys = [];
|
||||
const extrudeSolids = [];
|
||||
const sweepSoilds = [];
|
||||
|
||||
for (const ent of ents)
|
||||
{
|
||||
if (ent instanceof ExtrudeSolid)
|
||||
extrudeSolids.push(ent);
|
||||
|
||||
if (ent instanceof Board)
|
||||
boards.push(ent);
|
||||
else if (ent instanceof HardwareCompositeEntity)
|
||||
hardwareCompositeEntitys.push(ent);
|
||||
else if (ent instanceof SweepSolid)
|
||||
sweepSoilds.push(ent);
|
||||
}
|
||||
return { boards, hardwareCompositeEntitys, extrudeSolids, sweepSoilds };
|
||||
};
|
||||
|
||||
let { boards, hardwareCompositeEntitys, extrudeSolids, sweepSoilds } = parseEntity(ens);
|
||||
const InspectionOption = store.m_Option.InspectionOption;
|
||||
|
||||
if (InspectionOption.isMaxSizeBoard)
|
||||
{
|
||||
const res = MaxSizeBoardInspection(boards);
|
||||
errEntInfos.set("超长板", res);
|
||||
}
|
||||
if (InspectionOption.isMinSizeBoard)
|
||||
{
|
||||
const res = MinSizeBoardInspection(boards);
|
||||
errEntInfos.set("超短板", res);
|
||||
}
|
||||
if (InspectionOption.isModel)
|
||||
{
|
||||
const res = ModelInspection(boards);
|
||||
errEntInfos.set("造型槽错误", res);
|
||||
}
|
||||
if (InspectionOption.isSpecialBoardContour)
|
||||
{
|
||||
const res = SpecialBoardContourInspection(boards);
|
||||
errEntInfos.set("异形轮廓错误", res);
|
||||
}
|
||||
if (InspectionOption.isInterfere)
|
||||
{
|
||||
const res = await InterfereInspection([...hardwareCompositeEntitys, ...extrudeSolids, ...sweepSoilds]);
|
||||
errEntInfos.set("干涉", res);
|
||||
}
|
||||
if (InspectionOption.isDrill)
|
||||
{
|
||||
const res = await DrillInspection(boards);
|
||||
errEntInfos.set("排钻碰撞", res);
|
||||
}
|
||||
if (InspectionOption.isSplitBoard)
|
||||
{
|
||||
const res = SplitBoardInspection(boards);
|
||||
errEntInfos.set("有拆单尺寸的板", res);
|
||||
}
|
||||
if (InspectionOption.isDrawHole)
|
||||
{
|
||||
const res = DrawHoleInspection(boards);
|
||||
errEntInfos.set("没有排孔的板", res);
|
||||
}
|
||||
|
||||
if ([...errEntInfos.values()].every(v => v.length === 0))
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "恭喜! 没有问题!",
|
||||
timeout: 5000,
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
}
|
||||
app.Editor.ModalManage.RenderModeless(ViewInspection, { errEntInfos });
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
import { Button, Card, Checkbox, Intent } from "@blueprintjs/core";
|
||||
import { observer } from "mobx-react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { app } from "../../ApplicationServices/Application";
|
||||
import { BoardModalType } from "../../UI/Components/Board/BoardModalType";
|
||||
import { Config_ModalType } from "../../UI/Components/Board/UserConfigComponent";
|
||||
import { CommonModal } from "../../UI/Components/Modal/ModalContainer";
|
||||
import { ModalState } from "../../UI/Components/Modal/ModalInterface";
|
||||
import { AppToaster } from "../../UI/Components/Toaster";
|
||||
import { OneClickInspectionStore } from "./OneClickInspectionStore";
|
||||
|
||||
|
||||
export const OneClickInspectionComponent = (observer(({ store }: { store: OneClickInspectionStore; }) =>
|
||||
{
|
||||
const [inspectionOpt, setInspectionOpt] = useState([
|
||||
{ name: "超长板检查", key: "isMaxSizeBoard", checked: false, },
|
||||
{ name: "超短板检查", key: "isMinSizeBoard", checked: false, },
|
||||
{ name: "造型检查", key: "isModel", checked: false, },
|
||||
{ name: "异形轮廓错误检查", key: "isSpecialBoardContour", checked: false, },
|
||||
{ name: "干涉检查", key: "isInterfere", checked: false, },
|
||||
{ name: "排钻碰撞检查", key: "isDrill", checked: false, },
|
||||
{ name: "没有排孔的板", key: "isDrawHole", checked: false, },
|
||||
{ name: "拆单尺寸的板", key: "isSplitBoard", checked: false, },
|
||||
]);
|
||||
|
||||
const [selectAll, setSelectAll] = useState(false);
|
||||
const inspectionOption = store.m_Option.InspectionOption;
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const newOpt = inspectionOpt.map((opt) => { return { ...opt, checked: inspectionOption[opt.key] }; });
|
||||
setInspectionOpt(newOpt);
|
||||
|
||||
setSelectAll(newOpt.every(opt => opt.checked));
|
||||
}, [inspectionOption]);
|
||||
|
||||
return (
|
||||
<CommonModal
|
||||
title="一键检查"
|
||||
className="one-click-inspection"
|
||||
store={store}
|
||||
configType={Config_ModalType.ConfigListTagModal}
|
||||
type={BoardModalType.OneClickInspection}
|
||||
close={() => { app.Editor.ModalManage.Destory(); }}
|
||||
footerChildren={
|
||||
<>
|
||||
<Button
|
||||
text="确定"
|
||||
intent={Intent.SUCCESS}
|
||||
onClick={() =>
|
||||
{
|
||||
let error = store.HasInvailValue();
|
||||
if (error)
|
||||
{
|
||||
AppToaster.show({
|
||||
message: error,
|
||||
intent: Intent.WARNING,
|
||||
timeout: 3000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
app.Editor.ModalManage.m_PromisRes({ Status: ModalState.Ok });
|
||||
app.Editor.ModalManage.Destory();
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
text="取消"
|
||||
intent={Intent.DANGER}
|
||||
onClick={() => { app.Editor.ModalManage.Destory(); }}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Card style={{ padding: 20, minWidth: 200, minHeight: 300 }}>
|
||||
<div>
|
||||
{
|
||||
inspectionOpt.map((option) =>
|
||||
{
|
||||
return (
|
||||
<Checkbox
|
||||
key={option.key}
|
||||
checked={option.checked}
|
||||
label={option.name}
|
||||
onChange={() =>
|
||||
{
|
||||
const newOpt = inspectionOpt.map(opt => opt.key === option.key ? { ...opt, checked: !option.checked } : opt);
|
||||
setInspectionOpt(newOpt);
|
||||
|
||||
option.checked = !option.checked;
|
||||
inspectionOption[option.key] = option.checked;
|
||||
setSelectAll(newOpt.every(opt => opt.checked));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<Checkbox
|
||||
checked={selectAll}
|
||||
label={"全选"}
|
||||
onChange={() =>
|
||||
{
|
||||
setSelectAll(!selectAll);
|
||||
setInspectionOpt(inspectionOpt.map(option => { return { ...option, checked: !selectAll, }; }));
|
||||
for (const opt of inspectionOpt)
|
||||
{
|
||||
inspectionOption[opt.key] = !selectAll;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</CommonModal>
|
||||
);
|
||||
}));
|
@ -0,0 +1,42 @@
|
||||
import { observable, toJS } from "mobx";
|
||||
import { Singleton } from "../../Common/Singleton";
|
||||
import { DefaultOneClickInspectionOption } from "../../Editor/DefaultConfig";
|
||||
import { IConfigOption } from "../../UI/Components/Board/UserConfigComponent";
|
||||
import { IConfigStore } from "../../UI/Store/BoardStore";
|
||||
import { OneClickInspectionOption } from "../../UI/Store/OptionInterface/IOptionInterface";
|
||||
|
||||
export class OneClickInspectionStore extends Singleton implements IConfigStore
|
||||
{
|
||||
@observable configName = "默认";
|
||||
|
||||
@observable m_Option: OneClickInspectionOption = DefaultOneClickInspectionOption;
|
||||
@observable configsNames: string[] = [];
|
||||
|
||||
InitOption()
|
||||
{
|
||||
Object.assign(this.m_Option, DefaultOneClickInspectionOption);
|
||||
}
|
||||
UpdateOption(cof: IConfigOption<OneClickInspectionOption>)
|
||||
{
|
||||
Object.assign(this.m_Option, cof.option);
|
||||
}
|
||||
SaveConfig()
|
||||
{
|
||||
//新的配置
|
||||
let newConfig: IConfigOption<OneClickInspectionOption> = {};
|
||||
newConfig.option = toJS(this.m_Option);
|
||||
return newConfig;
|
||||
};
|
||||
|
||||
HasInvailValue()
|
||||
{
|
||||
let flag = "";
|
||||
if (Object.values(this.m_Option.InspectionOption).every(v => !v))
|
||||
{
|
||||
flag = "请选择至少一个选项";
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
|
||||
export const oneClickInspectionStore = OneClickInspectionStore.GetInstance() as OneClickInspectionStore;
|
@ -0,0 +1,127 @@
|
||||
import { Button, Card, Intent, Tab, Tabs } from "@blueprintjs/core";
|
||||
import React from "react";
|
||||
import { app } from "../../ApplicationServices/Application";
|
||||
import { Board } from "../../DatabaseServices/Entity/Board";
|
||||
import { Entity } from "../../DatabaseServices/Entity/Entity";
|
||||
import { ExtrudeSolid } from "../../DatabaseServices/Entity/Extrude";
|
||||
import { HardwareCompositeEntity } from "../../DatabaseServices/Hardware/HardwareCompositeEntity";
|
||||
import { CommonModal } from "../../UI/Components/Modal/ModalContainer";
|
||||
|
||||
|
||||
export function ViewInspection({ errEntInfos }: { errEntInfos: Map<string, (string | Entity[])[][]>; })
|
||||
{
|
||||
const getEntName = (ents: Entity[]) =>
|
||||
{
|
||||
let name = "";
|
||||
for (let i = 0; i < ents.length; i++)
|
||||
{
|
||||
const ent = ents[i];
|
||||
if (i > 0) name += "_";
|
||||
if (ent instanceof Board)
|
||||
name += ent.Name;
|
||||
else if (ent instanceof HardwareCompositeEntity)
|
||||
name += ent.HardwareOption.name;
|
||||
else if (ent instanceof ExtrudeSolid)
|
||||
name += "拉伸实体";
|
||||
else
|
||||
name += "实体";
|
||||
}
|
||||
return name;
|
||||
|
||||
};
|
||||
|
||||
const RenderEntityInfo = ({ entityInfo }: { entityInfo: (string | Entity[])[][]; }) =>
|
||||
{
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
overflow: "scroll",
|
||||
height: 427
|
||||
}}>
|
||||
{
|
||||
entityInfo.map(([ents, msg]: (string | Entity[])[], i) =>
|
||||
{
|
||||
const entitys = ents as Entity[];
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
border: "1px solid #ccc",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
padding: 3
|
||||
}}>
|
||||
<div style={{ width: 25 }}>{(i + 1) + "."}</div>
|
||||
<div style={{ width: 150 }}>{getEntName(entitys)}</div>
|
||||
<div style={{ flex: 1 }}>{msg}</div>
|
||||
<Button
|
||||
text="定位"
|
||||
intent={Intent.PRIMARY}
|
||||
style={{ width: 60 }}
|
||||
onClick={() =>
|
||||
{
|
||||
app.Editor.SetSelection(entitys);
|
||||
app.Viewer.ZoomtoEntitys(entitys);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<CommonModal
|
||||
title="一键检查"
|
||||
className="one-click-inspection"
|
||||
close={() => { app.Editor.ModalManage.Destory(); }}
|
||||
hasFooter={false}
|
||||
>
|
||||
<Card
|
||||
style={{
|
||||
margin: 20,
|
||||
minWidth: 500,
|
||||
height: 500
|
||||
}}>
|
||||
<div>
|
||||
<Tabs
|
||||
id="ViewInspection"
|
||||
className="view_inspection"
|
||||
>
|
||||
{
|
||||
[...errEntInfos.entries()].map(([key, ents]) =>
|
||||
{
|
||||
const count = ents.length;
|
||||
return <Tab
|
||||
key={key}
|
||||
id={key}
|
||||
title={
|
||||
<div>
|
||||
{key} <span style={{ color: count ? "red" : "green" }}>{"(" + count + ")"}</span>
|
||||
</div>
|
||||
}
|
||||
panel={
|
||||
count ?
|
||||
<RenderEntityInfo entityInfo={ents} /> :
|
||||
<div
|
||||
style={{
|
||||
borderTop: "1px solid #ccc",
|
||||
color: "green",
|
||||
fontWeight: "bold",
|
||||
paddingTop: 5
|
||||
}}>
|
||||
没有检查到错误
|
||||
</div>
|
||||
}
|
||||
/>;
|
||||
})
|
||||
}
|
||||
</Tabs>
|
||||
</div>
|
||||
</Card>
|
||||
</CommonModal>
|
||||
);
|
||||
}
|
Loading…
Reference in new issue