Merge pull request !238 from 肖诗雅/qg4_pr
pull/238/MERGE
ChenX 6 years ago
parent 520fb58149
commit beb0a8982a

@ -0,0 +1,136 @@
import { Vector3 } from "three";
import { app } from "../../ApplicationServices/Application";
import { Board } from "../../DatabaseServices/Board";
import { Line } from "../../DatabaseServices/Line";
import { Polyline } from "../../DatabaseServices/Polyline";
import { Command } from "../../Editor/CommandMachine";
import { PromptStatus } from "../../Editor/PromptResult";
import { equaln } from "../../Geometry/GeUtils";
import { RegionParse } from "../../Geometry/RegionParse";
import { IntersectOption } from "../../GraphicsSystem/IntersectWith";
import { ModalPosition, ModalState } from "../../UI/Components/Modal/ModalsManage";
import { ReferenceCuttingModal, ReferenceCuttingStore, CuttingOffset } from "./ReferenceCuttingModal";
export class ReferenceCutting implements Command
{
async exec()
{
let brRes = await app.m_Editor.GetSelection({
Once: true,
Msg: "选择被切割的板件",
Filter: { filterTypes: [Board] },
AllowNone: false
});
if (brRes.Status !== PromptStatus.OK) return;
let br = brRes.SelectSet.SelectEntityList[0] as Board;
brRes = await app.m_Editor.GetSelection({
Once: true,
Msg: "选择切割的板件",
Filter: {
filterTypes: [Board],
filterFunction: (obj, board) => br !== board
},
AllowNone: false
});
if (brRes.Status !== PromptStatus.OK) return;
let brKnife = brRes.SelectSet.SelectEntityList[0] as Board;
let brNorm = br.Normal;
let brKnifNorm = brKnife.Normal;
if (!equaln(brNorm.dot(brKnifNorm), 0))//垂直测试
{
app.m_Editor.Prompt('板件不垂直,无法切割!');
return;
}
//切割刀线 方向的投影
let cutLineDir = brNorm.cross(brKnifNorm).applyMatrix4(br.OCSInv.setPosition(new Vector3()));
let brContour = br.ContourCurve;
//测试能否进行切割,使用极限位置的2条线尝试求交
let knifeP1 = brKnife.Position
.applyMatrix4(br.OCSInv).setZ(0);//基点
let knifeP2 = brKnife.Position
.add(brKnifNorm.clone().multiplyScalar(brKnife.Thickness))
.applyMatrix4(br.OCSInv).setZ(0);
let l1 = new Line(knifeP1, knifeP1.clone().add(cutLineDir));
let l2 = new Line(knifeP2, knifeP2.clone().add(cutLineDir));
let pts1 = brContour.IntersectWith(l1, IntersectOption.ExtendArg);
let pts2 = brContour.IntersectWith(l2, IntersectOption.ExtendArg);
if (pts1.length < 2 && pts2.length < 2)
{
app.m_Editor.Prompt("板件与板件没有交集.");
return;
}
//对话框 设置参数
let referenceCuttingStore = ReferenceCuttingStore.GetInstance() as ReferenceCuttingStore;
let option = referenceCuttingStore.m_Option;
option.halfThickness = br.Thickness / 2;
option.boardType = brKnife.BoardType;
app.m_Editor.m_ModalManage.RenderModal(ReferenceCuttingModal, ModalPosition.Center, { store: referenceCuttingStore });
let state = await app.m_Editor.m_ModalManage.Wait();
if (state === ModalState.Ok)
{
let offset: number;//偏移量
switch (option.CuttingPosSelected)
{
case CuttingOffset.Front:
offset = brKnife.Thickness / 2 - option.offset;
break;
case CuttingOffset.Middle:
offset = brKnife.Thickness / 2;
break;
case CuttingOffset.Back:
offset = brKnife.Thickness / 2 + option.offset;
break;
}
knifeP1 = brKnife.Position
.add(brKnifNorm.clone().multiplyScalar(offset))
.applyMatrix4(br.OCSInv).setZ(0);
let cutLine = new Line(knifeP1, knifeP1.clone().add(cutLineDir));
let ipts = brContour.IntersectWith(cutLine, IntersectOption.ExtendArg);
let brCus = brContour.GetSplitCurvesByPts(ipts);
//切割线延伸
for (let p of ipts)
cutLine.Extend(cutLine.GetParamAtPoint(p));
//切割线分裂,并且过滤掉在轮廓外的线
let cutCus = cutLine.GetSplitCurvesByPts(ipts);
cutCus = cutCus.filter(c => brContour.PtInCurve(c.GetPointAtParam(0.5)));
let regionParse = new RegionParse([...brCus, ...cutCus]);
let contours = regionParse.m_RegionsInternal.map(r =>
{
let pl = new Polyline();
for (let route of r)
pl.Join(route.curve);
return pl;
});
if (contours.length === 0)
{
//预期之外.
app.m_Editor.Prompt("切割失败了!");
return;
}
//造型应用
for (let i = 1; i < contours.length; i++)
{
let br2 = br.Clone();
br2.ContourCurve = contours[i];
app.m_Database.ModelSpace.Append(br2);
}
br.ContourCurve = contours[0];
}
}
}

@ -0,0 +1,188 @@
import React = require("react");
import { Classes, Button, RadioGroup, Radio } from "@blueprintjs/core";
import { app } from "../../ApplicationServices/Application";
import { CheckObjectType, CheckoutValid } from "../../Common/CheckoutVaildValue";
import { ToasterInput, AppToaster } from "../../UI/Components/Toaster";
import { observable } from "mobx";
import { DataAdapter } from "../../Common/DataAdapter";
import { Singleton } from "../../Common/Singleton";
import { inject, observer } from "mobx-react";
import { ModalState } from "../../UI/Components/Modal/ModalsManage";
import { begin } from "xaop";
import { KeyBoard } from "../../Common/KeyEnum";
import { BoardType } from "../../DatabaseServices/Board";
export enum CuttingOffset
{
Front = "front",
Middle = "middle",
Back = "back",
}
interface OffsetKeyWord
{
Front: string;
Back: string;
}
export interface ReferenceCuttingOptioins
{
boardType: BoardType;
offset: number;
halfThickness: number;
CuttingPosSelected: CuttingOffset;
}
export class ReferenceCuttingStore extends Singleton
{
protected m_UiOption;
@observable m_Option: ReferenceCuttingOptioins = {
boardType: BoardType.Layer,
offset: 0,
halfThickness: 9,
CuttingPosSelected: CuttingOffset.Middle,
};
get UIOption()
{
return this.m_UiOption = DataAdapter.ConvertUIData(this.m_Option);
}
Cancel()
{
this._Return(ModalState.Cancel);
}
OnOk()
{
this._Return(ModalState.Ok);
}
_Return(state: number)
{
app.m_Editor.m_ModalManage.m_PromisRes(state);
app.m_Editor.m_ModalManage.Clear();
}
HasInvailValue()
{
return CheckoutValid.HasInvailValue(this.m_UiOption, CheckObjectType.RC)
}
}
@inject("store")
@observer
export class ReferenceCuttingModal extends React.Component<{ store: ReferenceCuttingStore }, {}> {
private uiOption;
private handleChangeOffsetDir = (e) =>
{
const store = this.props.store;
store.m_Option.CuttingPosSelected = e.currentTarget.value as CuttingOffset;
if (store.m_Option.CuttingPosSelected === CuttingOffset.Middle)
{
store.m_Option.offset = 0;
this.uiOption["offset"] = "0";
}
else
{
store.m_Option.offset = store.m_Option.halfThickness;
this.uiOption["offset"] = store.m_Option.halfThickness.toString();
}
}
private getOffsetKeyWord = (): OffsetKeyWord =>
{
switch (this.props.store.m_Option.boardType)
{
case BoardType.Layer:
return { Front: "下偏", Back: "上偏" };
case BoardType.Vertical:
return { Front: "左偏", Back: "右偏" };
case BoardType.Behind:
return { Front: "前偏", Back: "后偏" };
}
}
registerEvent()
{
app.m_Editor.m_ModalManage.events.push(
begin(app.m_Editor.m_ModalManage, app.m_Editor.m_ModalManage.OnKeyDown, (e: KeyboardEvent) =>
{
if (e.keyCode === KeyBoard.Enter || e.keyCode === KeyBoard.Space)
{
if ((e.target as HTMLElement).nodeName !== "INPUT")
{
this.props.store.OnOk();
e.preventDefault();
}
}
else if (e.keyCode === KeyBoard.Escape)
this.props.store.Cancel();
e.stopPropagation();
return true;
}));
}
componentWillMount()
{
this.uiOption = this.props.store.UIOption;
this.registerEvent();
}
render()
{
const store = this.props.store;
return (
<div id="referenceCuttingModal" className={Classes.DIALOG_CONTAINER} >
<div className={Classes.DIALOG}>
<div
className={Classes.DIALOG_HEADER}
data-id="dragArea"
>
<h4 className="bp3-heading"></h4>
<Button
aria-label="Close"
minimal
icon="cross"
className={Classes.DIALOG_CLOSE_BUTTON}
onClick={() => store.Cancel()}
/>
</div>
<div className={Classes.DIALOG_BODY}>
<RadioGroup
inline={true}
selectedValue={store.m_Option.CuttingPosSelected}
onChange={e => { this.handleChangeOffsetDir(e) }}
>
<Radio label={this.getOffsetKeyWord().Front} value={CuttingOffset.Front} />
<Radio label="居中" value={CuttingOffset.Middle} />
<Radio label={this.getOffsetKeyWord().Back} value={CuttingOffset.Back} />
</RadioGroup>
<div>
<span>: </span>
<ToasterInput
type={CheckObjectType.RC}
optKey="offset"
isDisabled={store.m_Option.CuttingPosSelected === CuttingOffset.Middle}
option={store.m_Option}
uiOption={this.uiOption}
onChange={(e) => { store.m_Option.offset = e.currentTarget.value; }}
/>
</div>
</div>
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button
className="LeftRightBtn"
intent="success"
text="确定"
onClick={() =>
{
if (store.HasInvailValue())
AppToaster.show({ message: "存在无效数值,请修正", timeout: 1000 });
else
store.OnOk()
}}
/>
<Button
className="LeftRightBtn"
intent="danger"
text="取消"
onClick={() => { store.Cancel() }}
/>
</div>
</div>
</div>
</div>
)
}
}

@ -6,6 +6,7 @@ export enum CheckObjectType
DR = "drill",
AR = "array",
Do = "door",
RC = "referenceCutting",
}
export namespace CheckoutValid
@ -26,6 +27,10 @@ export namespace CheckoutValid
return !Object.keys(obj).every(k =>
CheckoutDoorOption(k, obj[k]) === ""
)
case CheckObjectType.RC:
return !Object.keys(obj).every(k =>
CheckOutReferenceCuttingOption(k, obj[k]) === ""
)
default:
return true;
}
@ -42,6 +47,8 @@ export namespace CheckoutValid
return CheckoutArrayOption(k, v);
case CheckObjectType.Do:
return CheckoutDoorOption(k, v);
case CheckObjectType.RC:
return CheckOutReferenceCuttingOption(k, v);
}
}
export function CheckoutBoardOption(k: string, v: string): string
@ -185,4 +192,18 @@ export namespace CheckoutValid
return CheckoutBoardOption(k, v)
}
}
export function CheckOutReferenceCuttingOption(k: string, v: string): string
{
switch (k)
{
case "offset":
let val = Number(v);
if (v === "" || isNaN(Number(v)))
{
return "数值不能为空且必须为数字";
}
default:
return "";
}
}
}

@ -262,7 +262,7 @@ export class Board extends ExtureSolid
roMat.makeBasis(
new Vector3(1, 0, 0),
new Vector3(0, 0, 1),
new Vector3(0, -1, 0)
new Vector3(0, 1, 0)
);
}
return roMat;

@ -210,7 +210,7 @@ export class Line extends Curve
let params = this.SplitParamSort(param);
let pts = params.map(param => this.GetPointAtParam(param));
let ret = new Array<Curve>();
if (pts.length > 2)
if (pts.length >= 2)
{
for (let i = 0; i < pts.length - 1; i++)
{

@ -100,6 +100,7 @@ import { ICommand } from '../UI/Components/CommandPanel/CommandList';
import { commandMachine } from './CommandMachine';
import { DrawDrawrer } from '../Add-on/DrawBoard/DrawDrawer';
import { CommandFilletBoard } from '../Add-on/FilletBoard';
import { ReferenceCutting } from '../Add-on/BoardCutting/ReferenceCutting';
export function registerCommand()
@ -238,6 +239,7 @@ export function registerCommand()
commandMachine.RegisterCommand("yxlk", new DrawSpecialShapedBoardByContour());
commandMachine.RegisterCommand("qg", new LinearCutting());
commandMachine.RegisterCommand("qg2", new NonAssociativeCutting());
commandMachine.RegisterCommand("qg4", new ReferenceCutting());
commandMachine.RegisterCommand("drawer", new DrawDrawrer());

@ -278,7 +278,20 @@ export function IntersectLineAndLine(l1: Line, l2: Line, extType: IntersectOptio
{
let pt = IntersectLine3AndLine3(l1.StartPoint, l1.EndPoint, l2.StartPoint, l2.EndPoint);
return pt ? CheckPointOnCurve([pt], l1, l2, extType) : [];
if (!pt) return [];
let { closestPt: p1, param: param1 } = l1.GetClosestAtPoint(pt, true);
let { closestPt: p2, param: param2 } = l2.GetClosestAtPoint(pt, true);
if (
!equalv3(pt, p1, 1e-6)
|| !equalv3(pt, p2, 1e-6)
|| !(extType & IntersectOption.ExtendThis || l1.ParamOnCurve(param1))
|| !(extType & IntersectOption.ExtendArg || l2.ParamOnCurve(param2))
)
return [];
return [pt];
}
export function IntersectPolylineAndCurve(pl: Polyline, cu: Curve, extType: IntersectOption): Vector3[]

@ -133,4 +133,5 @@
@import (less) "SnapModal.less";
@import (less) "../../RightPanel/RightPanel.less";
@import (less) "./DoorModal.less";
@import (less) "./ReferenceCuttingModal.less";

@ -0,0 +1,12 @@
/*---参照切割---*/
#referenceCuttingModal .bp3-dialog>.bp3-dialog-footer{
justify-content: flex-end;
padding: 0px 10px 10px 10px;
button{
min-height: 20px;
}
}
#referenceCuttingModal .bp3-dialog>.bp3-dialog-body{
margin:10px;
}
Loading…
Cancel
Save