diff --git a/src/Add-on/twoD2threeD/Modals/R2b2.tsx b/src/Add-on/twoD2threeD/Modals/R2b2.tsx new file mode 100644 index 000000000..b949a00e0 --- /dev/null +++ b/src/Add-on/twoD2threeD/Modals/R2b2.tsx @@ -0,0 +1,121 @@ +import { Button, Card, H5, Intent } from '@blueprintjs/core'; +import { observer } from 'mobx-react'; +import React, { Component } from 'react'; +import { app } from '../../../ApplicationServices/Application'; +import { CheckObjectType } from '../../../Common/CheckoutVaildValue'; +import { Input5Or4Component, Notes, SetBoardDataBlock } from '../../../UI/Components/Board/BoardCommon'; +import { BoardModalType } from '../../../UI/Components/Board/BoardModal'; +import { CommonModal } from '../../../UI/Components/Modal/ModalContainer'; +import { ModalState } from '../../../UI/Components/Modal/ModalInterface'; +import { AppToaster } from '../../../UI/Components/Toaster'; +import { R2B2Store } from '../R2B2Store'; +import { SelectDrillType } from '../R2BCommon'; + +const CabPars = [ + ["depthExpr", "柜深"], ["maxThickness", "最大板厚"], +]; +const ShrinkPars = [ + ["vertialShrink", "立板"], ["layerShrink", "层板"], +]; + +@observer +export class R2b2Panel extends Component<{ store: R2B2Store; }> +{ + render() + { + const { store } = this.props; + return ( + + + + > + } + > + + + 柜体参数 + + 前缩 + + 排钻 + + 封边 + + + + + + + + ); + } + private close = () => + { + app.Editor.ModalManage.Destory(); + }; + private confirm = () => + { + let error = this.props.store.HasInvailValue(); + if (error) + { + AppToaster.show({ + message: "存在无效数值,请修正", + timeout: 3000, + intent: Intent.DANGER + }); + return; + } + app.Editor.ModalManage.DestoryAndExec( + { + Status: ModalState.Ok, + Data: { cname: "矩形变层板立板" } + } + ); + }; +} diff --git a/src/Add-on/twoD2threeD/Modals/Rec2Br.less b/src/Add-on/twoD2threeD/Modals/Rec2Br.less index 48e0a93c6..39cb16115 100644 --- a/src/Add-on/twoD2threeD/Modals/Rec2Br.less +++ b/src/Add-on/twoD2threeD/Modals/Rec2Br.less @@ -6,15 +6,17 @@ width: 4rem; } } - .undefined.br-set{ - >span:first-child{ + + .undefined.br-set { + >span:first-child { width: 4.5rem; } } - .select-drillType - { + + .select-drillType { width: 9rem; } + .note-card { padding: 10px !important; @@ -65,3 +67,20 @@ #rec2Br .notes>div>input:last-child { width: 70%; } + +#modal .r2b2 { + .selectDrillType { + width: 80%; + } + + .flexWrap { + width: 100%; + + .br-set { + span { + width: 60px; + } + } + + } +} diff --git a/src/Add-on/twoD2threeD/R2B2Store.ts b/src/Add-on/twoD2threeD/R2B2Store.ts new file mode 100644 index 000000000..b35e4c720 --- /dev/null +++ b/src/Add-on/twoD2threeD/R2B2Store.ts @@ -0,0 +1,65 @@ +import { observable, toJS } from "mobx"; +import { begin } from "xaop"; +import { CheckObjectType, CheckoutValid } from "../../Common/CheckoutVaildValue"; +import { DataAdapter } from "../../Common/DataAdapter"; +import { DefaultR2b2Option } from "../../Editor/DefaultConfig"; +import { userConfig } from "../../Editor/UserConfig"; +import { IConfigOption } from "../../UI/Components/Board/UserConfig"; +import { IUiOption } from "../../UI/Store/BoardInterface"; +import { IConfigStore } from "../../UI/Store/BoardStore"; +import { IRect2Br2Option } from "./R2bInterface"; + +export class R2B2Store implements IConfigStore +{ + @observable configName = "默认"; + @observable configsNames: string[] = []; + @observable option: IRect2Br2Option = { ...DefaultR2b2Option, remarks: Array.from({ length: 12 }, () => ["", ""]) }; + @observable uiOption: IUiOption; + constructor() + { + begin(userConfig, userConfig.SetDrillConfigsEvent, () => + { + this.InitDrillType(); + }); + } + get UIOption(): IUiOption + { + if (!this.uiOption) + this.uiOption = DataAdapter.ConvertUIData(this.option); + return this.uiOption; + } + protected InitDrillType() + { + let drilltypes = [...userConfig.DrillConfigs.keys(), "不排"]; + if (drilltypes.length > 0) + { + if (!drilltypes.includes(this.option.drillType)) + this.option.drillType = drilltypes[0]; + } + } + InitOption() + { + Object.assign(this.option, DefaultR2b2Option, { remarks: Array.from({ length: 12 }, () => ["", ""]) }); + if (this.uiOption) + Object.assign(this.uiOption, DataAdapter.ConvertUIData(this.option)); + } + SaveConfig() + { + return { + option: toJS(this.option) + }; + } + UpdateOption(conf: IConfigOption) + { + Object.assign(this.option, conf.option); + if (this.uiOption) + Object.assign(this.uiOption, DataAdapter.ConvertUIData(conf.option)); + this.InitDrillType(); + } + HasInvailValue() + { + return CheckoutValid.HasInvailValue(this.UIOption, CheckObjectType.R2B); + } +} + +export const r2b2Store = new R2B2Store(); diff --git a/src/Add-on/twoD2threeD/R2BCommon.tsx b/src/Add-on/twoD2threeD/R2BCommon.tsx index 827ce6fdf..fcf7d7a6e 100644 --- a/src/Add-on/twoD2threeD/R2BCommon.tsx +++ b/src/Add-on/twoD2threeD/R2BCommon.tsx @@ -1,10 +1,11 @@ import * as React from 'react'; -import { Checkbox, Classes } from '@blueprintjs/core'; +import { Checkbox, Classes, HTMLSelect } from '@blueprintjs/core'; import { Rec2BrStore } from './Rec2BrStore'; import { observable } from 'mobx'; import { observer } from 'mobx-react'; import { arrayRemove } from '../../Common/ArrayExt'; import { updateBoardInfoStore } from '../../UI/Store/UpdateBoardInfoStore'; +import { userConfig } from '../../Editor/UserConfig'; export interface ISelectBrConfigNameProps { @@ -61,3 +62,38 @@ export class SelectBrConfigName extends React.Component void; + style?: React.CSSProperties; +} + +@observer +export class SelectDrillType extends React.Component +{ + render() + { + const { option } = this.props; + const selectOptions = userConfig.DrillConfigs.size > 0 ? [...userConfig.DrillConfigs.keys(), "不排"] : []; + + return ( + + ); + } + private handleChange = (e: React.ChangeEvent) => + { + let val = (e.target as HTMLSelectElement).value; + this.props.option.drillType = val; + if (this.props.onchange) + this.props.onchange(val); + }; +} diff --git a/src/Add-on/twoD2threeD/R2bInterface.ts b/src/Add-on/twoD2threeD/R2bInterface.ts index 09c2bb619..96abfbe73 100644 --- a/src/Add-on/twoD2threeD/R2bInterface.ts +++ b/src/Add-on/twoD2threeD/R2bInterface.ts @@ -1,4 +1,4 @@ -import { IBaseOption, IGrooveOption } from "../../UI/Store/BoardInterface"; +import { DrillType, IBaseOption, IGrooveOption } from "../../UI/Store/BoardInterface"; import { TemplateRecord } from "../../DatabaseServices/Template/TemplateRecord"; import { EBoardKeyList } from "../../Common/BoardKeyList"; @@ -67,3 +67,17 @@ export interface IRec2BrOption extends IBaseOption behindIsRelative: boolean; footerThickness: number; } + +export interface IRect2Br2Option extends IBaseOption +{ + depthExpr: string; + maxThickness: number; + layerShrink: number; + vertialShrink: number; + [EBoardKeyList.DrillType]: DrillType | string; + [EBoardKeyList.UpSealed]: string; + [EBoardKeyList.DownSealed]: string; + [EBoardKeyList.LeftSealed]: string; + [EBoardKeyList.RightSealed]: string; + remarks: [string, string][]; +} diff --git a/src/Add-on/twoD2threeD/Rect2Board.ts b/src/Add-on/twoD2threeD/Rect2Board.ts new file mode 100644 index 000000000..96100d801 --- /dev/null +++ b/src/Add-on/twoD2threeD/Rect2Board.ts @@ -0,0 +1,275 @@ +import { toJS } from "mobx"; +import { Box3, Matrix4, Object3D, Vector3 } from "three"; +import { app } from "../../ApplicationServices/Application"; +import { EBoardKeyList } from "../../Common/BoardKeyList"; +import { GetRectData } from "../../Common/CurveUtils"; +import { safeEval } from "../../Common/eval"; +import { Buy2To3 } from "../../Common/HostUrl"; +import { Log } from "../../Common/Log"; +import { FixDigits } from "../../Common/Utils"; +import { Board } from "../../DatabaseServices/Entity/Board"; +import { Polyline } from "../../DatabaseServices/Entity/Polyline"; +import { Command } from "../../Editor/CommandMachine"; +import { PromptStatus } from "../../Editor/PromptResult"; +import { isParallelTo, XAxis, YAxis, ZAxis } from "../../Geometry/GeUtils"; +import { ClampSpaceParseRay } from "../../Geometry/SpaceParse/ClampSpaceParseRay"; +import { ISpaceParse } from "../../Geometry/SpaceParse/ISpaceParse"; +import { PointSelectBoards } from "../../Geometry/SpaceParse/PointSelectBoards"; +import { RenderType } from "../../GraphicsSystem/RenderType"; +import { AppConfirm } from "../../UI/Components/Common/Confirm"; +import { BoardType, FaceDirection } from "../../UI/Store/BoardInterface"; +import { TopPanelStore } from "../../UI/Store/TopPanelStore"; +import { R2b2Panel } from "./Modals/R2b2"; +import { r2b2Store } from "./R2B2Store"; +import { IRect2Br2Option } from "./R2bInterface"; + + +export class Rect2Board implements Command +{ + async exec() + { + const tpStore = TopPanelStore.GetInstance() as TopPanelStore; + + if (!tpStore.canUse2to3) + { + let res = await AppConfirm.show({ message: "您还未购买该增值包,前往购买?" }); + if (res) + { + window.open(Buy2To3); + } + return; + } + + let ss = await app.Editor.GetSelection({ + Msg: "请选择曲线:", + UseSelect: true, + Filter: { filterTypes: [Polyline] } + }); + if (ss.Status === PromptStatus.Cancel) return; + + let pls = ss.SelectSet.SelectEntityList as Polyline[]; + + app.Editor.ModalManage.RenderModal(R2b2Panel, { store: r2b2Store }); + + app.Editor.ModalManage.SetCallback(async () => + { + await tool.Do(pls, toJS(r2b2Store.option)); + }); + } +} + +const PlCache = new Map(); + +class Rect2BoardTool2 +{ + private spaceParse: ISpaceParse; + private _option: IRect2Br2Option; + async Do(pls: Polyline[], option: IRect2Br2Option) + { + for (let [k,] of PlCache) + k = null; + PlCache.clear(); + + //默认认为用户传进来的pls都平行,都在一个平面内 + if (!pls || pls.length === 0) return; + + this._option = option; + + let ucs = new Matrix4().makeBasis(XAxis, ZAxis, YAxis.clone().negate()); + let ucsinv = new Matrix4().getInverse(ucs); + // pls = pls.filter(pl => CurveInOCS(pl, ucs, ucsinv)); + + const clonePls: Polyline[] = []; + for (let l of pls) + { + let cl = l.Clone(); + clonePls.push(cl); + PlCache.set(cl, l); + } + const totalBrs: Board[] = []; + + for (let pl of clonePls) + { + const box = pl.BoundingBox; + const spaceParse = await this.GetSpaceParse(box); + if (!spaceParse.ParseOK) + { + Log("没有对应的空间"); + return; + } + + pl.ApplyMatrix(spaceParse.SpaceOCSInv); + if (!isParallelTo(pl.Normal, ZAxis)) + { + pl.ApplyMatrix(ucsinv); + } + + pl.Position = pl.Position.setZ(0); + this.ParsePolyline(pl); + + this.spaceParse = spaceParse; + + const size = spaceParse.Size; + + const depth = safeEval(option.depthExpr, { L: size.x, W: size.y, H: size.z }); + + totalBrs.push(...this.DrawBoard([pl], depth)); + } + + totalBrs.forEach(b => app.Database.ModelSpace.Append(b)); + } + private ParsePolyline(pl: Polyline) + { + let firstDev = pl.GetFistDeriv(0); + let size: Vector3; + let box: Box3; + if (isParallelTo(firstDev, XAxis) || isParallelTo(firstDev, YAxis)) + { + box = pl.BoundingBox; + size = box.getSize(new Vector3); + } + else + { + let rd = GetRectData(pl); + if (rd.isRect) + { + pl.TempData = rd; + box = rd.box; + size = rd.size; + } + else + { + box = pl.BoundingBox; + size = box.getSize(new Vector3); + } + } + pl.__CacheSize__ = size; + pl.__CacheBox__ = box; + } + private async GetSpaceParse(box: Box3) + { + let center = box.getCenter(new Vector3); + const objects: Object3D[] = []; + for (let br of app.Database.ModelSpace.Entitys) + { + if (!br.IsErase && br.Visible && br instanceof Board) + { + objects.push(br.GetDrawObjectFromRenderType(RenderType.Physical)); + } + } + + app.Viewer.WorldToScreen(center); + + let ptSelect = new PointSelectBoards(center, app.Viewer, objects); + + const SpaceParse = new ClampSpaceParseRay(ptSelect.SelectBoards).SetRay(ptSelect.Ray); + await SpaceParse.Parse(); + + return SpaceParse; + } + + DrawBoard(pls: Polyline[], depth: number) + { + let maxThickness = this._option.maxThickness; + let spaceOCS = this.spaceParse.SpaceOCS; + let min = this.spaceParse.SpaceBox.min; + const verShrink = this._option.vertialShrink; + const layShrink = this._option.layerShrink; + const remarks: [string, string][] = []; + for (let re of this._option.remarks) + if (re[0] && re[1]) + { + remarks.push([...re]); + } + + let layers: Board[] = []; + let vertials: Board[] = []; + + for (let pl of pls) + { + let size = pl.__CacheSize__ as Vector3; + + let x = FixDigits(size.x); + let y = FixDigits(size.y); + + if (x < 10 || y < 10) + { + console.log("奇怪的矩形"); + continue; + } + let box = pl.__CacheBox__; + let minp = box.min.clone(); + + let ocs: Matrix4; + if (pl.TempData) + { + ocs = pl.TempData.OCS.clone(); + minp.applyMatrix4(pl.TempData.OCS); + } + + let br: Board; + + let mtx = new Matrix4().makeBasis(XAxis, ZAxis.clone().negate(), YAxis); + + let shrink = 0; + + if (x > y && y <= maxThickness) //层板 + { + br = Board.CreateBoard(x, depth - layShrink, y, BoardType.Layer); + br.BoardProcessOption[EBoardKeyList.BigHole] = FaceDirection.Back; + mtx.setPosition(new Vector3(x)); + shrink = layShrink; + [minp.x, minp.y, minp.z] = [minp.x + x, minp.z, minp.y]; + layers.push(br); + } + else if (x < y && x <= maxThickness)//立板 + { + br = Board.CreateBoard(y, depth - verShrink, x, BoardType.Vertical); + [minp.x, minp.y, minp.z] = [minp.x, minp.z, minp.y]; + vertials.push(br); + shrink = verShrink; + } + + if (!br) continue; + PlCache.get(pl)?.Erase(); + + //被旋转的矩形 + let mtxInv = new Matrix4().getInverse(mtx); + if (ocs) + br.ApplyMatrix(mtx).ApplyMatrix(ocs).ApplyMatrix(mtxInv); + + minp.y = min.y + shrink; + + let m = new Matrix4().setPosition(minp); + br.ApplyMatrix(m); + br.ApplyMatrix(spaceOCS); + // app.Database.ModelSpace.Append(br); + + br.BoardProcessOption[EBoardKeyList.DrillType] = this._option[EBoardKeyList.DrillType]; + br.BoardProcessOption.highDrill = new Array(4).fill(this._option[EBoardKeyList.DrillType]); + br.BoardProcessOption[EBoardKeyList.UpSealed] = this._option[EBoardKeyList.UpSealed]; + br.BoardProcessOption[EBoardKeyList.DownSealed] = this._option[EBoardKeyList.DownSealed]; + br.BoardProcessOption[EBoardKeyList.LeftSealed] = this._option[EBoardKeyList.LeftSealed]; + br.BoardProcessOption[EBoardKeyList.RightSealed] = this._option[EBoardKeyList.RightSealed]; + br.BoardProcessOption.remarks = remarks; + if (this.spaceParse.Boards.length > 0) + { + let b = this.spaceParse.Boards[0]; + br.BoardProcessOption[EBoardKeyList.RoomName] = b.BoardProcessOption[EBoardKeyList.RoomName]; + br.BoardProcessOption[EBoardKeyList.CabinetName] = b.BoardProcessOption[EBoardKeyList.CabinetName]; + + br.BoardProcessOption[EBoardKeyList.BrMat] = b.BoardProcessOption[EBoardKeyList.BrMat]; + br.BoardProcessOption[EBoardKeyList.Color] = b.BoardProcessOption[EBoardKeyList.Color]; + br.BoardProcessOption[EBoardKeyList.Mat] = b.BoardProcessOption[EBoardKeyList.Mat]; + } + } + + for (let br of layers) + { + br.Subtract(vertials); + } + return [...vertials, ...layers]; + } +} + +const tool = new Rect2BoardTool2(); diff --git a/src/Common/CheckoutVaildValue.ts b/src/Common/CheckoutVaildValue.ts index ac53e41ba..98db63a4d 100644 --- a/src/Common/CheckoutVaildValue.ts +++ b/src/Common/CheckoutVaildValue.ts @@ -278,6 +278,10 @@ export namespace CheckoutValid let val = safeEval(v); switch (k) { + case "depthExpr": + if (isNaN(safeEval(v, { L: 1, H: 1, W: 1 }))) + return "表达式错误"; + return ""; case "sealedUp": case "sealedDown": case "sealedLeft": @@ -314,6 +318,8 @@ export namespace CheckoutValid case "grooveAddLength": case "grooveAddDepth": case "grooveAddWidth": + case "vertialShrink": + case "layerShrink": if (isNaN(val)) return "数值不能为空且必须为数字"; default: diff --git a/src/Common/CommandNames.ts b/src/Common/CommandNames.ts index e3bdef999..9f70c5fcc 100644 --- a/src/Common/CommandNames.ts +++ b/src/Common/CommandNames.ts @@ -193,4 +193,5 @@ export enum CommandNames DrawTempByImport = "DRAWTEMPBYIMPORT", CheckEdge = "CHECKEDGE", Knife = "KNIFES",//编辑BBS + R2B2 = "RECT2BOARD2", } diff --git a/src/Editor/CommandRegister.ts b/src/Editor/CommandRegister.ts index bd381dc73..e458b44e4 100644 --- a/src/Editor/CommandRegister.ts +++ b/src/Editor/CommandRegister.ts @@ -210,6 +210,7 @@ import { BuyMaterial } from './../Add-on/BuyMaterial'; import { Interfere } from './../Add-on/interfere'; import { ShowKinfeManageModal } from './../Add-on/showModal/ShowKnifeManageModal'; import { commandMachine } from './CommandMachine'; +import { Rect2Board } from "../Add-on/twoD2threeD/Rect2Board"; export function registerCommand() { @@ -585,6 +586,8 @@ export function registerCommand() commandMachine.RegisterCommand(CommandNames.CuttingFace, new CuttingByFace()); commandMachine.RegisterCommand(CommandNames.DrawTempByImport, new DrawTemplateByImport()); + + commandMachine.RegisterCommand(CommandNames.R2B2, new Rect2Board()); } export async function RegistCustomCommand() diff --git a/src/Editor/DefaultConfig.ts b/src/Editor/DefaultConfig.ts index 72da8a1de..589dbc706 100644 --- a/src/Editor/DefaultConfig.ts +++ b/src/Editor/DefaultConfig.ts @@ -7,7 +7,7 @@ import { EBoardKeyList } from "../Common/BoardKeyList"; import { ICylMetalsOption, IExtMetalsOption, ICompHardwareOption, EMetalsType, IToplineOption } from "../UI/Components/RightPanel/RightPanelInterface"; import { Curve2RecOption } from "../Add-on/twoD2threeD/Modals/Curve2RecModal"; import { IUpdateBoardInfosOption } from "../UI/Components/Board/UpdateBoardInfointerface"; -import { IRec2BrOption } from "../Add-on/twoD2threeD/R2bInterface"; +import { IRec2BrOption, IRect2Br2Option } from "../Add-on/twoD2threeD/R2bInterface"; export const DefaultLayerBoardConfig: LayerBoardOption = { version: 2, @@ -656,3 +656,17 @@ export const DefaultR2bOption: IRec2BrOption = { footerThickness: 18, }; Object.freeze(DefaultR2bOption); +export const DefaultR2b2Option: IRect2Br2Option = { + version: 1, + depthExpr: "W", + drillType: "", + sealedDown: "1", + sealedLeft: "1", + sealedRight: "1", + sealedUp: "1", + remarks: Array.from({ length: 12 }, () => ["", ""]), + maxThickness: 20, + layerShrink: 0, + vertialShrink: 0, +}; +Object.freeze(DefaultR2b2Option); diff --git a/src/UI/Components/Board/BoardModal.tsx b/src/UI/Components/Board/BoardModal.tsx index c64ad339b..8bdf50e1d 100644 --- a/src/UI/Components/Board/BoardModal.tsx +++ b/src/UI/Components/Board/BoardModal.tsx @@ -49,6 +49,7 @@ export enum BoardModalType R2WR = "R2WR", ZX2 = "vmodeling", ZX3 = "3dmodeling", + R2B2 = "R2B2", } export interface BoardModalProps { diff --git a/src/UI/Components/CommandPanel/CommandList.ts b/src/UI/Components/CommandPanel/CommandList.ts index 676be8f54..9c552192a 100644 --- a/src/UI/Components/CommandPanel/CommandList.ts +++ b/src/UI/Components/CommandPanel/CommandList.ts @@ -384,6 +384,16 @@ export const CommandList: ICommand[] = [ chName: "矩形变板件", chDes: "将矩形转换为板件", }, + { + icon: IconEnum.R2b2, + typeId: "hb", + link: "#", + defaultCustom: "R2B2", + command: CommandNames.R2B2, + type: "画板", + chName: "矩形变层板立板", + chDes: "将矩形转换为层板立板", + }, { icon: IconEnum.Rect2Winerack, typeId: "hb", diff --git a/src/UI/Components/TopToolBar/TopToolBar.tsx b/src/UI/Components/TopToolBar/TopToolBar.tsx index 1f1cbdd90..c61c4d66c 100644 --- a/src/UI/Components/TopToolBar/TopToolBar.tsx +++ b/src/UI/Components/TopToolBar/TopToolBar.tsx @@ -70,6 +70,7 @@ export class TopToolBar extends React.Component<{}, {}> { svg: IconEnum.Lattice, title: "格子抽", command: CommandNames.Lattice }, { svg: IconEnum.Curve2Rec, title: "线条变矩形", command: CommandNames.Curve2Rect }, { svg: IconEnum.Rec2Br, title: "矩形变板件", command: CommandNames.R2b }, + { svg: IconEnum.R2b2, title: "矩形变层板立板", command: CommandNames.R2B2 }, { svg: IconEnum.UpdateInfo, title: "根据板名修改属性", command: CommandNames.UpdateBoardInfos }, { svg: IconEnum.ChangeColorByMat, title: "根据板材修改颜色", command: CommandNames.ChangeColorByMaterial }, { svg: IconEnum.RestoreColor, title: "恢复板件颜色", command: CommandNames.RestoreColor }, diff --git a/src/UI/IconEnum.ts b/src/UI/IconEnum.ts index 98af8f28f..8474d1779 100644 --- a/src/UI/IconEnum.ts +++ b/src/UI/IconEnum.ts @@ -182,4 +182,5 @@ export enum IconEnum CheckNoHoleBoard = "CHECKNOHOLEBOARD.svg", CheckDrawHole = "CHECKDRAWHOLE.svg", EditorBBS = "EDITORBBS.svg", + R2b2 = "R2B2.svg", }