!2303 功能:吊顶,线条支持拾取天花板和地板

pull/2309/MERGE
林三 1 year ago committed by ChenX
parent 4ca28cee2b
commit f2435f2d0e

@ -11,7 +11,7 @@ import { PromptStatus } from "../../Editor/PromptResult";
import { SelectSetBase } from "../../Editor/SelectBase";
import { SelectSet } from "../../Editor/SelectSet";
import { CreateContour2 } from "../../Geometry/CreateContour2";
import { isParallelTo, MoveMatrix } from "../../Geometry/GeUtils";
import { MoveMatrix, isParallelTo } from "../../Geometry/GeUtils";
import { LastExtractBoardContour } from "../../UI/Components/Board/LastExtractBoardContour";
import { Ellipse } from './../../DatabaseServices/Entity/Ellipse';
import { Spline } from './../../DatabaseServices/Spline';
@ -65,10 +65,21 @@ export function BoardApplyContour(contour: ExtrudeContour, brs: Board[], isKeepS
app.Editor.UpdateScreen();
}
async function SelectEntitys(useExtrude = false, Once = true): Promise<ExtrudeContour[] | undefined>
async function SelectEntitys(useExtrude = false, Once = true, onlyUseCurve = false): Promise<ExtrudeContour[] | undefined>
{
let Msg = useExtrude ? "请选择(拉伸实体,面域或曲线)" : "请选择(面域或曲线)";
let filterTypes = useExtrude ? [Region, ExtrudeSolid, Curve] : [Region, Curve];
let Msg = "";
let filterTypes = [];
if (onlyUseCurve)
{
Msg = "请选择曲线";
filterTypes = [Curve];
}
else
{
Msg = useExtrude ? "请选择(拉伸实体,面域或曲线)" : "请选择(面域或曲线)";
filterTypes = useExtrude ? [Region, ExtrudeSolid, Curve] : [Region, Curve];
}
let allowNone = LastExtractBoardContour.Contour?.IsErase === false;
if (allowNone) Msg += "<空格使用提取的轮廓>";
@ -100,14 +111,14 @@ async function SelectEntitys(useExtrude = false, Once = true): Promise<ExtrudeCo
/**
* .
*/
export async function SelectExtrudeContour(selectInside = false, useExtrude = false)
export async function SelectExtrudeContour(selectInside = false, useExtrude = false, onlyUseCurve = false)
: Promise<{
contour?: ExtrudeContour,
group?: Curve[][],
useCurves?: (Curve | Region)[];
useCurves?: (Curve | Region)[]; //contour 使用的曲线
}>
{
let ens = await SelectEntitys(useExtrude);
let ens = await SelectEntitys(useExtrude, true, onlyUseCurve);
if (!ens) return {};
let en = ens[0];

@ -1,16 +1,21 @@
import { Scene } from "three";
import { app } from "../ApplicationServices/Application";
import { arrayRemoveIf } from "../Common/ArrayExt";
import { curveLinkGroup } from "../Common/CurveUtils";
import { DisposeThreeObj } from "../Common/Dispose";
import { BlockTableRecord } from "../DatabaseServices/BlockTableRecord";
import { Arc } from "../DatabaseServices/Entity/Arc";
import { Board } from "../DatabaseServices/Entity/Board";
import { Curve } from "../DatabaseServices/Entity/Curve";
import { Line } from "../DatabaseServices/Entity/Line";
import { Polyline } from "../DatabaseServices/Entity/Polyline";
import { HardwareTopline } from "../DatabaseServices/Hardware/HardwareTopline";
import { RoomFlatBase } from "../DatabaseServices/Room/Entity/Flat/RoomFlatBase";
import { RoomFlatFloor } from "../DatabaseServices/Room/Entity/Flat/RoomFlatFloor";
import { RoomHolePolyline } from "../DatabaseServices/Room/Entity/Wall/Hole/RoomHolePolyline";
import { JigUtils } from "../Editor/JigUtils";
import { PromptStatus } from "../Editor/PromptResult";
import { equalv3 } from "../Geometry/GeUtils";
import { equaln, equalv3 } from "../Geometry/GeUtils";
import { SurroundOutlineParse } from "../Geometry/SpaceParse/SurroundOutlineParse";
/**构建顶线 */
@ -19,7 +24,7 @@ export async function buildTopline(outline: Polyline, name: string)
let brsRes = await app.Editor.GetSelection({
Msg: "选择柜体或者曲线",
UseSelect: true,
Filter: { filterTypes: [Board, Arc, Line, Polyline] }
Filter: { filterTypes: [Board, Arc, Line, Polyline, RoomFlatBase] }
});
if (brsRes.Status !== PromptStatus.OK)
@ -27,14 +32,28 @@ export async function buildTopline(outline: Polyline, name: string)
let brs: Board[] = [];
let cus: Curve[] = [];
let roomFlatEnts: RoomFlatBase[] = [];
for (let en of brsRes.SelectSet.SelectEntityList)
{
if (en instanceof Board)
brs.push(en);
else if (en instanceof RoomFlatBase)
roomFlatEnts.push(en);
else
cus.push(en as Curve);
}
let metals: HardwareTopline[] = [];
if (roomFlatEnts.length > 0)
{
//吊顶
await drawCeiling(outline, roomFlatEnts);
return;
}
if (brs.length > 0)
metals = await handleBoard(outline, brs);
else
@ -43,6 +62,68 @@ export async function buildTopline(outline: Polyline, name: string)
metals.forEach(m => m.HardwareOption.name = name);
}
//吊顶 地脚线
async function drawCeiling(outline: Polyline, roomFlatEnts: RoomFlatBase[])
{
for (let ent of roomFlatEnts)
{
let name = "";
let polylines = [ent.Contour.Clone()];
if (ent instanceof RoomFlatFloor)
{
name = "地脚线";
let holes = (ent.Owner.Object as BlockTableRecord).Entitys.filter(e => { return !e.IsErase && e instanceof RoomHolePolyline; }) as RoomHolePolyline[];
let ocsInv = ent.OCSInv;
for (let hole of holes)
{
let entBox = ent.BoundingBoxInOCS;
let box = entBox.intersect(hole.GetBoundingBoxInMtx(ocsInv));
if (isFinite(box.min.x) && isFinite(box.max.x))
{
let params: number[] = [];
let cachePolyline = [];
let needRemoveIf = [];
let pts = [hole.LidCurves[0].StartPoint, hole.LidCurves[0].EndPoint, hole.LidCurves[1].StartPoint, hole.LidCurves[1].EndPoint];
for (let polyline of polylines)
{
for (let p of pts)
{
let param = polyline.GetParamAtPoint(p.clone().applyMatrix4(ocsInv));
if (!param) continue;
if (polyline.ParamOnCurve(param))
params.push(param);
}
if (params.length === 0) continue;
let cus = polyline.GetSplitCurves(params);
arrayRemoveIf(cus, (cu) => box.containsPoint(cu.Midpoint));
cachePolyline.push(...cus);
needRemoveIf.push(polyline);
}
arrayRemoveIf(polylines, (polyline) => needRemoveIf.includes(polyline));
polylines.push(...cachePolyline);
}
}
}
else name = "吊顶";
let group = curveLinkGroup(polylines);
for (let cus of group)
{
let path = Polyline.Combine(cus);
let sweepSolid = new HardwareTopline(outline, path);
JigUtils.Draw(sweepSolid);
if (!equaln(sweepSolid.BoundingBox.min.x, 0))
sweepSolid.Reverse();
sweepSolid.ApplyMatrix(ent.OCSNoClone);
sweepSolid.HardwareOption.name = name;
app.Database.ModelSpace.Append(sweepSolid);
}
}
}
async function handleBoard(outline: Polyline, brs: Board[])
{
let spaceParse = new SurroundOutlineParse(brs);

@ -1,25 +1,18 @@
import { CADFiler } from "../../DatabaseServices/CADFiler";
import { Region } from "../../DatabaseServices/Entity/Region";
import { app } from "../../ApplicationServices/Application";
import { Entity } from "../../DatabaseServices/Entity/Entity";
import { Command } from "../../Editor/CommandMachine";
import { BoolOpeartionType } from "../../GraphicsSystem/BoolOperateUtils";
import { PromptStatus } from "../../Editor/PromptResult";
import { HotCMD } from "../../Hot/HotCommand";
import { TestDraw } from "../test/TestUtil";
@HotCMD
export class Test implements Command
{
async exec()
{
let d = { "file": [2, "Region", 10, 2, 110, 0, 1, 7, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 863.9997678387444, -200, 0, 1], 0, 0, 1, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 863.9997678387444, -200, 0, 1], 0, 0, 1, 1, 1, 1, 1, "Polyline", 10, 2, 0, 0, 0, 1, 71, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 863.9997678387444, -200, 0, 1], 0, 0, 1, [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 863.9997678387444, -200, 0, 1], 0, 0, 1, 2, 4, [100, 0], 0, [400, 0], 0, [400, 864], 0, [100, 864], 0, true, 0, "Region", 10, 2, 111, 0, 1, 7, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, 1, 1, 1, 1, "Polyline", 10, 2, 0, 0, 0, 2, 71, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -614.9118387909318, -117.20906801007538, 0, 1], 0, 0, 1, 2, 4, [0, 0], 0, [1728.0001196034718, 0], 0, [1728.0001196034718, 9.5], 0, [0, 9.5], 0, true, 0], "basePt": { "x": -0.00023216125555336475, "y": -100, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
let f = new CADFiler(d.file);
let enRes = await app.Editor.GetEntity({ Filter: { filterTypes: [Entity] } });
if (enRes.Status !== PromptStatus.OK) return;
let pl = enRes.Entity as Entity;
console.log(pl);
f.Read();
let el1 = f.ReadObject() as Region;
let el2 = f.ReadObject() as Region;
TestDraw(el1.Clone());
TestDraw(el2.Clone());
el1.BooleanOper(el2, BoolOpeartionType.Intersection);
}
}

@ -457,6 +457,14 @@ export class ColorMaterial
side: DoubleSide,
});
static PrintLineWhiterMatrial = new LineMaterial({
color: 0xFFFFFF,
linewidth: LINE_WIDTH,
dashed: false,
resolution: new Vector2(1000, 1000),
side: DoubleSide,
});
//灰色半透明(板件为不拆单时候的显示样式)
static GrayTransparentMeshMaterial = new MeshBasicMaterial({
color: 0xcccccc,

@ -356,6 +356,11 @@ export enum CommandNames
ZJPC = "ZHUANJIAOPIAOCHUANG", //转角飘窗
YJHM = "YIJIANHUAMEN",//一键画门
//吊顶
HeadCeilingContour = "HEADCEILINGCONTOUR",//吊顶轮廓模板
HeadCeilingMaterialPanel = "HEADCEILINGMATERIALPANEL",//吊顶截面轮廓材质面板
HeadCeilingInfoConfigPanel = "HEADCEILINGINFOCONFIGPANEL",//吊顶轮廓面板
Gallery = "GALLERY", //打开画廊
Freeze = "FREEZE",//冻结实体
@ -363,6 +368,5 @@ export enum CommandNames
ShareView = "SHAREVIEW", //分享图纸(只读模式)
VisibleInRender = "VISIBLEINRENDER", //渲染器中显示实体
UnVisibleInRender = "UNVISIBLEINRENDER", //渲染器中隐藏实体
SendCADFileToKF = "SENDCADFILETOKF",//发送当前文件给在线客服
}

@ -15,6 +15,7 @@ export enum DirectoryId
DrillingDir = "6", //排钻目录
KnifePathDir = "7", //刀路目录
Frame = "8", //图框目录
CeilingContour = "9", //吊顶轮廓
HistoryDit = "-1",//历史编辑目录
}

@ -39,7 +39,7 @@ export function MaterialIn(fileData: Object[]): PhysicalMaterialRecord
return db.MaterialTable.Symbols.entries().next().value[1];
}
export function TemplateOut(template: TemplateRecord, tempDb = new Database(false, false, true))
export function TemplateOut(template: TemplateRecord, tempDb = new Database(false, false, true)): string
{
template = template.Root;
let idMap = new Map();
@ -55,9 +55,9 @@ export function TemplateOut(template: TemplateRecord, tempDb = new Database(fals
if (l.Id.Index > 99)
l.ApplyMatrix(scsInv);
return JSON.stringify(tempDb.FileWrite().Data);
return tempDb.FileWrite().ToString();
}
export function TemplateIn(fileData: Object[]): TemplateRecord
export function TemplateIn(fileData: any[]): TemplateRecord
{
let f = new CADFiler(fileData);
let db = new Database(false, false, true).FileRead(f);

@ -61,6 +61,9 @@ export class Database
//默认材质
DefaultMaterial: PhysicalMaterialRecord;
/** UE,WebCAD,使.
* ,
*/
ExtendedData: any = null;
private idIndex = 1;

@ -1,4 +1,4 @@
import { Box3, Matrix3, Matrix4, Shape, Vec2, Vector2, Vector3 } from 'three';
import { Box3, Matrix3, Matrix4, Vec2, Vector2, Vector3 } from 'three';
import { arrayLast, arrayRemoveDuplicateBySort, changeArrayStartIndex } from '../../Common/ArrayExt';
import { ComputerCurvesNormalOCS, getDeterminantFor2V } from '../../Common/CurveUtils';
import { matrixAlignCoordSys, matrixIsCoplane, reviseMirrorMatrix } from '../../Common/Matrix4Utils';
@ -13,6 +13,7 @@ import { OffsetPolyline } from '../../GraphicsSystem/OffsetPolyline';
import { Factory } from '../CADFactory';
import { CADFiler } from '../CADFiler';
import { IsPointInPolyLine } from '../PointInPolyline';
import { Shape2 } from '../Shape2';
import { Arc } from './Arc';
import { Curve, ExtendType } from './Curve';
import { DragPointType } from './DragPointType';
@ -1470,7 +1471,7 @@ export class Polyline extends Curve
}
return true;
}
get Shape(): Shape
get Shape(): Shape2
{
let { pts, buls } = this.PtsBuls;
return CreatePolylinePath(pts, buls);

@ -0,0 +1,541 @@
import { BufferAttribute, BufferGeometry, Float32BufferAttribute, InstancedInterleavedBuffer, InterleavedBufferAttribute, Intersection, LineSegments, Material, Mesh, Object3D, ShapeBufferGeometry, Vector3 } from "three";
import { Line2 } from "three/examples/jsm/lines/Line2";
import { LineGeometry } from "three/examples/jsm/lines/LineGeometry";
import { ColorMaterial } from "../../../../Common/ColorPalette";
import { DisposeThreeObj, Object3DRemoveAll } from "../../../../Common/Dispose";
import { BufferGeometryUtils } from "../../../../Geometry/BufferGeometryUtils";
import { ContourTreeNode } from "../../../../Geometry/ExtrudeMeshGeomBuilder/ExtrudeEdgeGeometry2";
import { AsVector3, equaln } from "../../../../Geometry/GeUtils";
import { SweepGeometry } from "../../../../Geometry/SweepGeometry";
import { RenderType } from "../../../../GraphicsSystem/RenderType";
import { AutoRecord } from "../../../AutoRecord";
import { Factory } from "../../../CADFactory";
import { CADFiler } from "../../../CADFiler";
import { Contour } from "../../../Contour";
import { Curve } from "../../../Entity/Curve";
import { Entity } from "../../../Entity/Entity";
import { Polyline } from "../../../Entity/Polyline";
import { ObjectId } from "../../../ObjectId";
import { PhysicalMaterialRecord } from "../../../PhysicalMaterialRecord";
import { Shape } from "../../../Shape";
import { TemplateRecord } from "../../../Template/TemplateRecord";
import { RoomFlatBase } from "../Flat/RoomFlatBase";
export enum BulkheadCeiling_ContourType
{
Hole = 0,
Land = 1,
Sweep = 2,//放样线条(不影响岛的建模)
}
export interface BulkheadCeiling_ContourData
{
ContourType: BulkheadCeiling_ContourType;//洞 还是岛
Path: Curve;//轮廓(可能闭合 可能是一条直线)
SweepShapeTempalteId: ObjectId<TemplateRecord>;//放样形状的模块id
Materials: ObjectId<PhysicalMaterialRecord>[];
Contour: Polyline; //截面轮廓
ContourId: number; //截面轮廓ID
ShapeMaterialSlotData: number[];
OverWriteMaterial?: Map<number, ObjectId<PhysicalMaterialRecord>>;//替代材质
}
/**
* ()
*/
@Factory
export class BulkheadCeiling extends Entity
{
constructor() { super(); }
private _ContourData: BulkheadCeiling_ContourData[] = [];
private _GemoIdMap = new Map<number, number>();
get ContourData(): BulkheadCeiling_ContourData[]
{
return this._ContourData;
}
set ContourData(value: BulkheadCeiling_ContourData[])
{
this.WriteAllObjectRecord();
this._ContourData = value;
this.Update();
}
private _Height = 300;//吊顶高度
get Height()
{
return this._Height;
}
set Height(value: number)
{
this.WriteAllObjectRecord();
if (!equaln(value, this._Height))
{
this._Height = value;
this.Update();
}
}
@AutoRecord RelativeRoomFlatTop: ObjectId<RoomFlatBase>;
private _EdgeGeometrys: BufferGeometry[];
get EdgeGeometrys(): BufferGeometry[]
{
if (!this._EdgeGeometrys)
this.MeshGeometry;
return this._EdgeGeometrys;
}
private _LineGeometry: LineGeometry[] = [];
get LineGeometry()
{
if (this._LineGeometry.length !== 0)
return this._LineGeometry;
this.MeshGeometry;
return this._LineGeometry;
}
private _FaceGeometry: BufferGeometry; //底面
get FaceGeometry()
{
if (this._FaceGeometry)
return this._FaceGeometry;
this.MeshGeometry;
return this._FaceGeometry;
}
private _MeshGeometry: SweepGeometry[] = [];
get MeshGeometry()
{
if (this._MeshGeometry.length !== 0) return this._MeshGeometry;
//绘制底面start
//#region //分析包含关系
let contours: ContourTreeNode[] = [];
let contour_data_Map = new Map<ContourTreeNode, BulkheadCeiling_ContourData>();
for (let con_data of this._ContourData)
{
if (!con_data.Path?.IsClose || con_data.ContourType === BulkheadCeiling_ContourType.Sweep) continue;
let contour = Contour.CreateContour(con_data.Path as Polyline);
if (!contour) continue;
let contourNode = new ContourTreeNode(contour);
contours.push(contourNode);
contour_data_Map.set(contourNode, con_data);
}
ContourTreeNode.ParseContourTree(contours);
//#endregion
let faceGeoms: BufferGeometry[] = [];
this._EdgeGeometrys = [];
this._GemoIdMap.clear();
for (let contourNode of contours)
{
let con_data = contour_data_Map.get(contourNode);
if (con_data.ContourType === BulkheadCeiling_ContourType.Land)//Land 岛
{
//#region 绘制平面
if (contourNode.parent && contour_data_Map.get(contourNode.parent).ContourType === BulkheadCeiling_ContourType.Land)//上级也是岛 不需要在绘制平面了
continue;
let holes: Contour[] = [];
for (let chiNode of contourNode.children)
{
let chi_con_data = contour_data_Map.get(chiNode);
if (chi_con_data.ContourType === BulkheadCeiling_ContourType.Hole)
holes.push(chiNode.contour.Clone());
}
let shape = new Shape(contourNode.contour, holes);
let geom = new ShapeBufferGeometry(shape.Shape, 30);
geom.applyMatrix4(shape.Outline.Curve.OCSNoClone);
{
const indices = Array.from(geom.getIndex().array);
// 翻转面片索引的顺序
const flippedIndices = [];
for (let i = 0; i < indices.length; i += 3)
flippedIndices.push(indices[i], indices[i + 2], indices[i + 1]);
// 创建新的面片索引属性
const newIndices = new BufferAttribute(new Uint16Array(flippedIndices), 1);
// 更新面片索引属性
geom.setIndex(newIndices);
}
faceGeoms.push(geom);
//#endregion
}
}
//绘制底面end
//Sweep
let i = 0;
for (; i < this._ContourData.length; i++)
{
let contour = this._ContourData[i].Contour;
if (contour)
{
let path = this._ContourData[i].Path;
if (this._ContourData[i].ContourType === BulkheadCeiling_ContourType.Hole)
path = path.Clone().Reverse();
let sweepGeo = new SweepGeometry(contour, path, this._ContourData[i].ShapeMaterialSlotData);
this._GemoIdMap.set(sweepGeo.id, i);
this._MeshGeometry.push(sweepGeo);
//line geo
this.GetLineGeometry(sweepGeo.edgePts);
//edge geo
let edgeGeom = new BufferGeometry().setAttribute('position', new Float32BufferAttribute(sweepGeo.edgePts, 3));
this._EdgeGeometrys.push(edgeGeom);
sweepGeo.edgePts = undefined;
}
}
this._FaceGeometry = BufferGeometryUtils.MergeBufferGeometries(faceGeoms);
this._GemoIdMap.set(this._FaceGeometry.id, i);
return this._MeshGeometry;
}
GetLineGeometry(pts: number[])
{
let lineGeo = new LineGeometry();
let lineSegments = new Float32Array(pts);
var instanceBuffer = new InstancedInterleavedBuffer(lineSegments, 6, 1);
lineGeo.setAttribute('instanceStart', new InterleavedBufferAttribute(instanceBuffer, 3, 0));
lineGeo.setAttribute('instanceEnd', new InterleavedBufferAttribute(instanceBuffer, 3, 3));
this._LineGeometry.push(lineGeo);
}
InitDrawObject(renderType: RenderType = RenderType.Wireframe): Object3D
{
let obj = new Object3D();
this.UpdateDrawObject(renderType, obj);
return obj;
}
/**
* :
* @param {RenderType} renderType
* @param {Object3D} obj
*/
UpdateDrawObject(renderType: RenderType, obj: Object3D)
{
DisposeThreeObj(obj);
Object3DRemoveAll(obj);
if (renderType === RenderType.Wireframe || renderType === RenderType.Jig)
{
for (let edgeGeo of this.EdgeGeometrys)
obj.add(new LineSegments(edgeGeo, ColorMaterial.GetLineMaterial(this.ColorIndex)));
}
else if (renderType === RenderType.Conceptual)
{
let material = ColorMaterial.GetConceptualMaterial(this.ColorIndex);
obj.add(new Mesh(this.FaceGeometry, material));
for (let meshGeo of this.MeshGeometry)
{
obj.add(new Mesh(meshGeo, material));
}
for (let edgeGeo of this.EdgeGeometrys)
obj.add(new LineSegments(edgeGeo, ColorMaterial.GetLineMaterial(this.ColorIndex)));
}
else if (renderType === RenderType.Physical)
{
obj.add(new Mesh(this.FaceGeometry, (this.Material?.Object as PhysicalMaterialRecord)?.Material ?? this.MeshMaterial));
for (let meshGeo of this.MeshGeometry)
obj.add(new Mesh(meshGeo, this.GetMaterials(this._GemoIdMap.get(meshGeo.id))));
}
// else if (renderType === RenderType.Physical2)
// {
// obj.add(new Mesh(this.FaceGeometry, (this.Material?.Object as PhysicalMaterialRecord)?.Material ?? this.MeshMaterial));
// for (let meshGeo of this.MeshGeometry)
// obj.add(new Mesh(meshGeo, this.GetMaterials(this._GemoIdMap.get(meshGeo.id))));
// for (let edgeGeo of this.EdgeGeometrys)
// obj.add(new LineSegments(edgeGeo, ColorMaterial.GetPhysical2EdgeMaterial()));
// }
else if (renderType === RenderType.Print)
{
for (let lineGeo of this.LineGeometry)
obj.add(new Line2(lineGeo, ColorMaterial.PrintLineWhiterMatrial));
}
return obj;
}
SetMaterial(mtl: ObjectId<PhysicalMaterialRecord>, intersection: Intersection)
{
let id = intersection.object["geometry"]?.id;
if (id)
{
this.WriteAllObjectRecord();
let contourIndex = this._GemoIdMap.get(id);
if (contourIndex === undefined)
this.Material = mtl;
else
{
//因为这个索引是底部面的索引
if (contourIndex === this._ContourData.length)
{
this.Material = mtl;
}
else
{
let contourData = this._ContourData[contourIndex];
if (!contourData.OverWriteMaterial)
contourData.OverWriteMaterial = new Map;
contourData.OverWriteMaterial.set(intersection.face.materialIndex, mtl);
}
}
this.Update();
}
}
//获取Sweep实体的材质列表
GetMaterials(index: number): Material[]
{
let contourData = this._ContourData[index];
return contourData.Materials.map((mtl, index) =>
{
return contourData.OverWriteMaterial?.get(index)?.Object?.Material ?? mtl?.Object?.Material ?? this.MeshMaterial as Material;
});
}
UpdateDrawObjectMaterial(renderType: RenderType, obj: Object3D, material?: Material)
{
if (renderType === RenderType.Physical)
obj.traverse(o =>
{
if (o instanceof Mesh)
{
let index = this._GemoIdMap.get(o.geometry.id);
if (index !== undefined)
{
if (Array.isArray(o.material))
{
let materials = this.GetMaterials(index);
o.material = materials;
}
}
else
o.material = this.MeshMaterial;
}
});
else if (renderType === RenderType.Conceptual)
{
let material = ColorMaterial.GetConceptualMaterial(this.ColorIndex);
let face = obj.children[0] as Mesh;//
face.material = material;
for (let i = 1; i < obj.children.length; i++)
{
let chiObj = obj.children[i];
if (chiObj instanceof Mesh)
chiObj.material = material;
else if (chiObj instanceof LineSegments)
chiObj.material = ColorMaterial.GetLineMaterial(this.ColorIndex);
}
}
else if (renderType === RenderType.Wireframe)
{
for (let lineSeg of obj.children as LineSegments[])
lineSeg.material = ColorMaterial.GetLineMaterial(this.ColorIndex);
}
}
override UpdateDrawGeometry()
{
for (let gemo of this._MeshGeometry)
gemo.dispose();
this._MeshGeometry = [];
for (let gemo of this._LineGeometry)
gemo.dispose();
this._LineGeometry = [];
if (this._FaceGeometry)
this._FaceGeometry.dispose();
this._FaceGeometry = undefined;
}
GetGripPoints(): Array<Vector3>
{
let pts = [];
for (let data of this._ContourData)
pts.push(...data.Path.Clone().ApplyMatrix(this.OCSNoClone).GetGripPoints());
return pts;
}
GetStretchPoints(): Vector3[]
{
let pts: Vector3[] = [];
let lineData = (this.ContourData[0].Path as Polyline).LineData;
for (let data of lineData)
{
let p = AsVector3(data.pt).applyMatrix4(this.ContourData[0].Path.OCS).applyMatrix4(this.OCSNoClone);
pts.push(p);
}
return pts;
}
MoveStretchPoints(indexList: Array<number>, vec: Vector3)
{
this.WriteAllObjectRecord();
let path = this.ContourData[0].Path;
path.ApplyMatrix(this.OCS);
path.MoveStretchPoints(indexList, vec);
path.ApplyMatrix(this.OCSInv);
this.Update();
}
MoveGripPoints(indexList: number[], moveVec: Vector3)
{
this.WriteAllObjectRecord();
for (let i = 0; i < this._ContourData.length; i++)
{
let path = this._ContourData[i].Path.Clone();
let newIndexlist = [];
let dataIndexList = [];
let ptLength = path.GetGripPoints().length;
for (let index of indexList)
{
if (ptLength < (index + 1))
newIndexlist.push(index - ptLength);
else
dataIndexList.push(index);
}
if (dataIndexList.length !== 0)
{
path.ApplyMatrix(this.OCS);
path.MoveGripPoints(dataIndexList, moveVec);
path.ApplyMatrix(this.OCSInv);
this.ContourData[i].Path = path;
}
indexList = newIndexlist;
}
this.Update();
}
//#region -------------------------File-------------------------
//对象从文件中读取数据,初始化自身
protected override _ReadFile(file: CADFiler)
{
let ver = file.Read();
super._ReadFile(file);
this.ContourData = [];
let contourDataCount = file.Read();
for (let i = 0; i < contourDataCount; i++)
{
let data: BulkheadCeiling_ContourData = {
ContourType: null,
Path: null,
SweepShapeTempalteId: null,
Materials: [],
Contour: null,
ContourId: null,
ShapeMaterialSlotData: [],
OverWriteMaterial: null,
};
data.ContourType = file.Read();
data.Contour = file.ReadObject();
data.ContourId = file.Read();
data.Path = file.ReadObject();
data.SweepShapeTempalteId = file.ReadObjectId() as ObjectId<TemplateRecord>;
//覆盖材质
let overwriteMtlSize = file.Read() as number;
if (overwriteMtlSize) data.OverWriteMaterial = new Map;
else data.OverWriteMaterial = undefined;
for (let i = 0; i < overwriteMtlSize; i++)
{
let index = file.Read();
let mtlId = file.ReadObjectId() as ObjectId<PhysicalMaterialRecord>;
data.OverWriteMaterial.set(index, mtlId);
}
//原始材质
let mtlCount = file.Read();
for (let i = 0; i < mtlCount; i++)
data.Materials.push(file.ReadObjectId() as ObjectId<PhysicalMaterialRecord>);
//材质槽
let materialCount = file.Read();
data.ShapeMaterialSlotData = file.ReadArray(materialCount);
this.ContourData.push(data);
}
this.RelativeRoomFlatTop = file.ReadObjectId() as ObjectId<RoomFlatBase>;
}
//对象将自身数据写入到文件.
override WriteFile(file: CADFiler)
{
file.Write(1);
super.WriteFile(file);
file.Write(this.ContourData.length);
for (let data of this.ContourData)
{
file.Write(data.ContourType);
file.WriteObject(data.Contour);
file.Write(data.ContourId);
file.WriteObject(data.Path);
file.WriteObjectId(data.SweepShapeTempalteId);
//覆盖材质
if (data.OverWriteMaterial)
{
file.Write(data.OverWriteMaterial.size);
for (let [index, mtlId] of data.OverWriteMaterial)
{
file.Write(index);
file.WriteObjectId(mtlId);
}
}
else
file.Write(0);
//原始材质
file.Write(data.Materials.length);
for (let material of data.Materials)
file.WriteObjectId(material);
//材质槽
file.Write(data.ShapeMaterialSlotData.length);
for (let matData of data.ShapeMaterialSlotData)
file.Write(matData);
}
file.WriteObjectId(this.RelativeRoomFlatTop);
}
//#endregion
}

@ -0,0 +1,37 @@
import { CADFiler } from "../../../CADFiler";
import { ISerialize } from "../../../ISerialize";
/**
* () ()
*/
export class BulkheadCeilingShapeTemplateExtendData implements ISerialize
{
//颜色与材质信息映射(为了这个模块能正常流通,这个模块还是成为内嵌模块吧)
Color_MaterialId: Map<number, number> = new Map();
constructor() { }
ReadFile(file: CADFiler): void
{
let ver = file.Read();
let size = file.Read();
this.Color_MaterialId.clear();
for (let i = 0; i < size; i++)
{
let color = file.Read();
let templateId = file.Read();
this.Color_MaterialId.set(color, templateId);
}
}
WriteFile(file: CADFiler): void
{
file.Write(1);
file.Write(this.Color_MaterialId.size);
for (let [color, templateId] of this.Color_MaterialId)
{
file.Write(color);
file.Write(templateId);
}
}
}

@ -10,7 +10,7 @@ import { Polyline } from "../../Entity/Polyline";
import { UpdateTempPolyline } from "../Entity/Flat/RoomFlatBase";
import { RoomFlatFloor } from "../Entity/Flat/RoomFlatFloor";
import { RoomFlatTop } from "../Entity/Flat/RoomFlatTop";
import { RoomRegion, ROOM_REGION_CURVES_KEY } from "../Entity/Region/RoomRegion";
import { ROOM_REGION_CURVES_KEY, RoomRegion } from "../Entity/Region/RoomRegion";
import { CURVE_DIR_TYPE_KEY, CURVE_FACE_TYPE_KEY, CURVE_WALL_TYPE_KEY, RoomWallBase, WallFaceType } from "../Entity/Wall/RoomWallBase";
import { ParseRegionTextPos } from "./ParseRegionTextPos";
import { RegionReplacement } from "./RegionReplacement";

@ -8,7 +8,8 @@ export class Shape2 extends Shape
{
getPoints(divisions: number = 12, optimizeArc = true): Vector2[]
{
let points = [], last: Vector2;
let points: Vector2[] = [],
last: Vector2;
for (let i = 0, curves = this.curves; i < curves.length; i++)
{
let curve = curves[i] as EllipseCurve;

@ -29,22 +29,22 @@ import { ChangeColor } from "../Add-on/ChangeColor";
import { ChangeColorByMaterial } from "../Add-on/ChangeColorByBoard/ChangeColorByMaterial";
import { CheckHoles } from "../Add-on/CheckHoles";
import { CheckModeling } from "../Add-on/CheckModeling";
import { Command_ClosePt } from "../Add-on/closetest";
import { Cmd_Freeze, Cmd_UnFreeze } from "../Add-on/Cmd_Freeze";
import { Cmd_UnVisibleInRender, Cmd_VisibleInRender } from "../Add-on/Cmd_VisibleInRender";
import { CombinatAttributeBrush } from "../Add-on/CombinatAttributeBrush";
import { FeedingCommand } from "../Add-on/CommandFeeding";
import { Command_CombineEntity } from "../Add-on/Command_CombineEntity";
import { Command_CommandPanel } from "../Add-on/Command_CommandPanel";
import { Command_Options } from "../Add-on/Command_Option";
import { Command_Purge } from "../Add-on/Command_Purge";
import { Command_SetBRXAxis } from "../Add-on/Command_SetBRXAxis";
import { FeedingCommand } from "../Add-on/CommandFeeding";
import { Command_Conver2Polyline } from "../Add-on/Conver2Polyline";
import { Command_Copy } from "../Add-on/Copy";
import { CopyClip } from "../Add-on/CopyClip";
import { Command_CopyPoint } from "../Add-on/CopyPoint";
import { CustomUcs } from "../Add-on/CostumUCS";
import { Command_CleanCustomNumber, Command_CustomNumber } from "../Add-on/CustomNumber/Command_CustomNumber";
import { Command_DWGDXFImport } from "../Add-on/DXFLoad";
import { DeleteCurve } from "../Add-on/DeleteCurve";
import { Command_Dist } from "../Add-on/Dist";
import { CMD_Divide } from "../Add-on/Divide";
@ -115,7 +115,6 @@ import { DrawWineRack } from "../Add-on/DrawWineRack/DrawWineRack";
import { EditorWineRack } from "../Add-on/DrawWineRack/EditorWineRack";
import { Polyline2Winerack } from "../Add-on/DrawWineRack/Polyline2Winerack";
import { DrawCircle0 } from "../Add-on/DrawZeroCircle";
import { Command_DWGDXFImport } from "../Add-on/DXFLoad";
import { Command_EndTempEditor } from "../Add-on/EndTempEditor";
import { Command_EntitytMoveToZ0 } from "../Add-on/EntityMoveToZ0";
import { Entsel } from "../Add-on/Entsel";
@ -130,27 +129,25 @@ import { Command_ExportView } from "../Add-on/Export2View";
import { Command_ExportData } from "../Add-on/ExportData";
import { Command_ExportSTL } from "../Add-on/Exports/ExportSTL";
import { Command_Extend } from "../Add-on/Extends";
import { Command_FZWL } from "../Add-on/FZWL";
import { Command_OpenHistory } from "../Add-on/File/OpenHistory";
import { OperLogs } from "../Add-on/File/OperLog";
import { CommandFillet } from "../Add-on/Fillet";
import { Command_FindBoardModelingKnife } from "../Add-on/FindBoardModelingKnife";
import { Command_Fix2DPath } from "../Add-on/Fix/Fix2DPath";
import { Command_FZWL } from "../Add-on/FZWL";
import { Command_Group, Command_UnGroup } from "../Add-on/Group";
import { Command_HideSelected, Command_HideUnselected, Command_ShowAll, Command_SwitchDoorOrDrawer, SelectAll, ShowHideSelectPanel } from "../Add-on/HideSelected";
import { Command_Insert } from "../Add-on/Insert";
import { Command_INsTest } from "../Add-on/instest";
import { Command_JiaJuImport } from "../Add-on/JiaJu/Import/JiaJuImport";
import { Command_Join } from "../Add-on/Join";
import { Command_KJLImport } from "../Add-on/KJL/Import/KJLImport";
import { ClearKjlToken, KjlExport } from "../Add-on/KJL/KjlExport";
import { Command_KJLImportConfig } from "../Add-on/KJL/KJLImportConfig/KJLImportConfigTool";
import { ClearKjlToken, KjlExport } from "../Add-on/KJL/KjlExport";
import { DrawLattice } from "../Add-on/LatticeDrawer/DrawLatticeDrawer";
import { EditorLattice } from "../Add-on/LatticeDrawer/EditorLattice";
import { Command_Length } from "../Add-on/Length";
import { Command_Lisp } from "../Add-on/Lisp";
import { DownLoadDConfig, DownloadHoleOption, UpLoadConfig, UploadHoleOption } from "../Add-on/LoadConfig";
import { Fbx } from "../Add-on/loadfbx";
import { LookOverBoardInfos } from "../Add-on/LookOverBoardInfos/LookOverBoardInfos";
import { Command_MatchProp } from "../Add-on/MatchProp";
import { MirrorCommand } from "../Add-on/Mirror";
@ -165,11 +162,10 @@ import { Open } from "../Add-on/Open";
import { Command_OpenCabinet } from "../Add-on/OpenCabinet/OpenCabinet";
import { PasteClip } from "../Add-on/PasteClip";
import { Pedit } from "../Add-on/Pedit";
import { Command_PLTest } from "../Add-on/polytest";
import { Print } from "../Add-on/Print";
import { CMD_Renderer } from "../Add-on/Renderer";
import { Command_RenderModulesState } from "../Add-on/RenderModulesState";
import { ReOpen } from "../Add-on/ReOpen";
import { Command_RenderModulesState } from "../Add-on/RenderModulesState";
import { CMD_Renderer } from "../Add-on/Renderer";
import { Command_ResetCustomCommand } from "../Add-on/ResetCustomCommand";
import { Command_RestoreColor } from "../Add-on/RestoreColor";
import { Command_Reverse } from "../Add-on/Reverse";
@ -190,8 +186,6 @@ import { Command_SendCADFileOnKf } from "../Add-on/SendCADFileOnKF";
import { SetSmoothEdge } from "../Add-on/SetSmoothEdge/SetSmoothEdge";
import { Command_ShareView } from "../Add-on/ShareView/Command_ShareView";
import { SwitchLines } from "../Add-on/ShowLines";
import { Command_GroovesModify } from "../Add-on/showModal/GroovesModify";
import { ShowEditorBBS } from "../Add-on/showModal/ShowModal";
import { ShowOpenDirLiens } from "../Add-on/ShowOpenDirLiens";
import { Command_ShowProcessingGroupModal } from "../Add-on/ShowProcessingGroupModal";
import { ShowRecycleBin } from "../Add-on/ShowRecycleBin";
@ -211,18 +205,36 @@ import { ShowTemplateDesign } from "../Add-on/Template/ShowTemplateDesign";
import { ShowTopLine } from "../Add-on/Template/ShowTopline";
import { Command_SplitTemplate, Command_SplitTemplateByDir } from "../Add-on/Template/SplitTemplate";
import { Command_TemplateSearch } from "../Add-on/TemplateSearch";
import { Command_ClosePt } from "../Add-on/closetest";
import { Command_INsTest } from "../Add-on/instest";
import { Fbx } from "../Add-on/loadfbx";
import { Command_PLTest } from "../Add-on/polytest";
import { Command_GroovesModify } from "../Add-on/showModal/GroovesModify";
import { ShowEditorBBS } from "../Add-on/showModal/ShowModal";
// import { DrawFloor } from '../Add-on/DrawFloor';
// import { RevTarget, SaveTarget } from '../Add-on/RenderTarget';
import { TestIntersect } from "../Add-on/test/testIntersect";
import { TestFb } from "../Add-on/TestFb";
import { Command_TestPointPickParse } from "../Add-on/TestPointPickParse";
import { Text2Curve } from "../Add-on/Text2Curve";
import { Command_ToggleUI } from "../Add-on/ToggleUI";
import { Command_Trim } from "../Add-on/Trim";
import { Redo, Undo } from "../Add-on/Undo";
import { ViewChange } from "../Add-on/ViewChange";
import { Command_FixView } from "../Add-on/ViewCtrl/FixView";
import { EditFrame } from "../Add-on/ViewortConfig/EditFrame";
import { EditViewport } from "../Add-on/ViewortConfig/EditViewport";
import { OneKeyLayout } from "../Add-on/Viewport/OneKeyLayout";
import { OneKeyPrint } from "../Add-on/Viewport/OneKeyPrint";
import { Command_Wblock } from "../Add-on/Wblock";
import { Command_ZoomObject, ZoomE } from "../Add-on/ZoomE";
import { Command_TestParseEdgeSealDir } from "../Add-on/test/TestParseEdgeSealDir";
import { Command_TestTape } from "../Add-on/test/TestTape";
import { Command_Test_IsRect } from "../Add-on/testEntity/Cmd_Test_IsRect";
import { TestIntersect } from "../Add-on/test/testIntersect";
import { Command_UpdateLight } from "../Add-on/testEntity/CMD_UpdateLight";
import { Command_Test_IsRect } from "../Add-on/testEntity/Cmd_Test_IsRect";
import { Command_DebugTemplateAssocCount } from "../Add-on/testEntity/DebugShowTemplateAssocEntityCount";
import { Test } from "../Add-on/testEntity/test";
import { Command_TestBoundaryBox } from "../Add-on/testEntity/TestBoundaryBox";
import { Command_TestBox } from "../Add-on/testEntity/TestBox";
import { TestCollision } from "../Add-on/testEntity/testCollision";
import { TestTargeOnCurve } from "../Add-on/testEntity/TestCurve";
import { Command_TestDrawEdgeGeometry } from "../Add-on/testEntity/TestDrawEdgeGeometry";
import { Command_TestLoadFbx } from "../Add-on/testEntity/TestFbx";
@ -232,25 +244,13 @@ import { Command_TestRegionParse } from "../Add-on/testEntity/TestRegionParse";
import { Command_TestSweepMaxLength } from "../Add-on/testEntity/TestSweepMaxLength";
import { Command_DeleteTemplate } from "../Add-on/testEntity/TestTemplateDelete";
import { Command_TestVPath } from "../Add-on/testEntity/TestVPath";
import { TestFb } from "../Add-on/TestFb";
import { Command_TestPointPickParse } from "../Add-on/TestPointPickParse";
import { Text2Curve } from "../Add-on/Text2Curve";
import { Command_ToggleUI } from "../Add-on/ToggleUI";
import { Command_Trim } from "../Add-on/Trim";
import { Test } from "../Add-on/testEntity/test";
import { TestCollision } from "../Add-on/testEntity/testCollision";
import { Command_Curve2Polyline } from "../Add-on/twoD2threeD/Command_Curve2Polyline";
import { Command_Curve2VSBox } from "../Add-on/twoD2threeD/Command_Curve2VSBox";
import { Command_ParseBoardName } from "../Add-on/twoD2threeD/ParseBoardName";
import { Polyline2Board } from "../Add-on/twoD2threeD/Polyline2Board";
import { Rect2Board } from "../Add-on/twoD2threeD/Rect2Board";
import { Redo, Undo } from "../Add-on/Undo";
import { ViewChange } from "../Add-on/ViewChange";
import { Command_FixView } from "../Add-on/ViewCtrl/FixView";
import { EditFrame } from "../Add-on/ViewortConfig/EditFrame";
import { EditViewport } from "../Add-on/ViewortConfig/EditViewport";
import { OneKeyLayout } from "../Add-on/Viewport/OneKeyLayout";
import { OneKeyPrint } from "../Add-on/Viewport/OneKeyPrint";
import { Command_Wblock } from "../Add-on/Wblock";
import { Command_ZoomObject, ZoomE } from "../Add-on/ZoomE";
import { CommandNames } from "../Common/CommandNames";
import { IsTest } from "../Common/Deving";
import { Command_SelectEntity } from "../Common/SelectEntity";
@ -274,6 +274,8 @@ import { Command_TestYHSingle } from "../Nest/Test/TestYHSingle";
import { Command_TestYHWorker } from "../Nest/Test/TestYHWorker";
import { Comman_SwitchServers, Command_ChangeLayout, Command_ModuleBar, Command_PropertiesBar, Command_RightPanel, RightTabCommandMap } from "../UI/Components/CommandPanel/SystemCommand/UICpmmand";
import Command_Gallery from "../UI/Components/Gallery/command/Command_Gallery";
import { Command_HeadCeilingContourManage } from "../UI/Components/HeadCeiling/HeadCeilingContourManageCommand";
import { Command_HeadCeilingInfoConfigPanel, Command_HeadCeilingMaterialPanel } from "../UI/Components/HeadCeiling/HeadCeilingInfoConfigPanelCommand";
import { EOptionTabId } from "../UI/Components/Modal/OptionModal/ConfigDialog";
import { Align } from "./../Add-on/Align";
import { BuyMaterial } from "./../Add-on/BuyMaterial";
@ -832,6 +834,11 @@ export function registerCommand()
commandMachine.RegisterCommand(CommandNames.ZJPC, new Command_OneKeyDrawZJPCWindow(DrawHoleType.L, IHoleType.Window, true)); //转角飘窗
commandMachine.RegisterCommand(CommandNames.YJHM, new Command_OneKeyDrawYJHMWindow(true)); //一键画门
commandMachine.RegisterCommand(CommandNames.HeadCeilingContour, new Command_HeadCeilingContourManage()); //吊顶轮廓材质面板
commandMachine.RegisterCommand(CommandNames.HeadCeilingMaterialPanel, new Command_HeadCeilingMaterialPanel()); //吊顶轮廓材质面板
commandMachine.RegisterCommand(CommandNames.HeadCeilingInfoConfigPanel, new Command_HeadCeilingInfoConfigPanel()); //吊顶轮廓材质面板
//SelectEntity
commandMachine.RegisterCommand(CommandNames.SelectBoard, new Command_SelectEntity(Board, "请选择板:"));
commandMachine.RegisterCommand(CommandNames.SelectCurve, new Command_SelectEntity(Curve, "请选择曲线:"));

@ -18,6 +18,8 @@ import { Light } from "../../DatabaseServices/Lights/Light";
import { PointLight } from "../../DatabaseServices/Lights/PointLight";
import { RectAreaLight } from "../../DatabaseServices/Lights/RectAreaLight";
import { SpotLight } from "../../DatabaseServices/Lights/SpotLight";
import { BulkheadCeiling } from "../../DatabaseServices/Room/Entity/Ceiling/BulkheadCeiling";
import { RoomFlatTop } from "../../DatabaseServices/Room/Entity/Flat/RoomFlatTop";
import { RoomRegion } from "../../DatabaseServices/Room/Entity/Region/RoomRegion";
import { RoomBase } from "../../DatabaseServices/Room/Entity/RoomBase";
import { Text } from "../../DatabaseServices/Text/Text";
@ -35,6 +37,7 @@ import { CompositeMatalPanel } from "../../UI/Components/RightPanel/CompositeMet
import { RightTabId } from "../../UI/Components/RightPanel/RightPanel";
import { ToplineMetalsPanel } from "../../UI/Components/RightPanel/ToplineMetalsPanel";
import { AppToaster } from "../../UI/Components/Toaster";
import RoomFlatTopParams from "../../UI/Components/ToolBar/ModifyModel/RoomFlatTopParams";
import { IConfigStore } from "../../UI/Store/BoardStore";
import { EntityStore } from "../../UI/Store/EntityStore";
import { compositeMetalsOptionStore, toplineMetalsStore } from "../../UI/Store/RightPanelStore/HardwareStore";
@ -292,8 +295,19 @@ export class DbClickManager extends Singleton
}
}, VP_TOATER_KEY);
}
else if (pickEnt instanceof BulkheadCeiling)
{
if (!pickEnt.RelativeRoomFlatTop?.Object) return;
app.Editor.ModalManage.RenderModeless(RoomFlatTopParams, { headCeilingEnt: pickEnt });
return;
}
else if (pickEnt instanceof RoomBase)
{
if (pickEnt instanceof RoomFlatTop)
{
app.Editor.ModalManage.RenderModeless(RoomFlatTopParams, { roomFlatTopEnt: pickEnt });
return;
}
let rightStore = RightPanelStore.GetInstance();
rightStore.m_IsShow = true;
rightStore.m_TabId = RightTabId.ModuelParams;

@ -14,6 +14,7 @@ import { ELatticeArrayType, ILatticeOption } from "../UI/Store/LatticeInterface"
import { ForBoardNameType, IAutoDimBrsOption } from "../UI/Store/OptionInterface/AutoDimBrsOption";
import { CurtailType, IBoardBatchCurtailOption } from "../UI/Store/OptionInterface/BoardBatchCurtailOption ";
import { BoardProcessOption } from "../UI/Store/OptionInterface/BoardProcessOption";
import { BulkheadCeilingOption } from "../UI/Store/OptionInterface/BulkheadCeilingOption";
import { ClosingStripOption, StripType } from "../UI/Store/OptionInterface/ClosingStripOption";
import { BehindBoardOption, ChangeColorByBoardMaterialOption, CommonPanelConfigOption, DatalistConfigOption, IBatchModifyPanelOption, IDimStyleOption, LayerBoardOption, LayerNailOption, ModifyTextsConfigOption, RightPlaneLightOption, ShareBoardInfConfigurationOption, SideBoardOption, SingleBoardOption, TBBoardOption, VerticalBoardOption, WindowPanelConfigOption } from "../UI/Store/OptionInterface/IOptionInterface";
import { PointLightOption, RectAreaLightOption, SpotLightOption } from "../UI/Store/OptionInterface/LightConfigOption";
@ -939,7 +940,6 @@ export const DefaultDimStyleOption: IDimStyleOption = {
};
Object.freeze(DefaultDimStyleOption);
export const DefaultChangeColorByBoardMaterialOption: ChangeColorByBoardMaterialOption = {
accordThickness: false,
accordMaterialColor: true,
@ -957,3 +957,8 @@ export const DefaultShareBoardInfConfigurationOption: ShareBoardInfConfiguration
showBom: true,
};
Object.freeze(DefaultShareBoardInfConfigurationOption);
export const DefaultBulkheadCeilingOption: BulkheadCeilingOption = {
Item: []
};
Object.freeze(DefaultBulkheadCeilingOption);

@ -8,7 +8,7 @@ import { BoxSolid } from "../../DatabaseServices/Entity/BoxSolid";
import { Curve } from "../../DatabaseServices/Entity/Curve";
import { Entity } from "../../DatabaseServices/Entity/Entity";
import { TemplateRecord } from "../../DatabaseServices/Template/TemplateRecord";
import { equalv3, isPerpendicularityTo, ZAxis, ZAxisN, ZeroVec } from "../../Geometry/GeUtils";
import { ZAxis, ZAxisN, ZeroVec, equalv3, isPerpendicularityTo } from "../../Geometry/GeUtils";
import { RenderType } from "../../GraphicsSystem/RenderType";
import { Raycast } from "../PointPick";
import { userConfig } from "../UserConfig";
@ -28,6 +28,7 @@ export class ParsePlacePos
{
let nowPos = _Inter.point;
if (!_Inter.face) return;
let normal = _Inter.face.normal.clone().transformDirection(_Inter.object.matrixWorld);
//在地上

@ -1,4 +1,4 @@
import { Shape, Vector2 } from 'three';
import { Vector2 } from 'three';
import { Shape2 } from '../DatabaseServices/Shape2';
import { angle, equaln, equalv2, polar } from './GeUtils';
@ -31,7 +31,7 @@ class Arc2d
}
//创建轮廓 通过点表和凸度
export function CreatePolylinePath(pts: Vector2[], buls: number[]): Shape
export function CreatePolylinePath(pts: Vector2[], buls: number[]): Shape2
{
let shape = new Shape2();
if (pts.length === 0) return shape;

@ -13,15 +13,15 @@ import { PlaneExt } from "./Plane";
export class SweepGeometry extends Geometry
{
edgePts: number[] = [];
constructor(contour: Polyline, path: Curve[] | Curve)
ShapeMaterialSlotData: number[];//[0,0,0,1,2,3,0] 指定多段线轮廓的材质槽索引 每个顶点指定一个材质槽位置
constructor(contour: Polyline, path: Curve[] | Curve, ShapeMaterialSlotData?: number[])
{
super();
this.ShapeMaterialSlotData = ShapeMaterialSlotData;
if (Array.isArray(path))
this.AddShape2(contour, path);
else
this.AddShape(contour, path);
this.computeVertexNormals();
this.computeFaceNormals();
}
@ -155,12 +155,20 @@ export class SweepGeometry extends Geometry
{
let addCount = 0; //补充个数
shapePts2d[0]["_mask_"] = true;
for (let p of shapePts2d) if (p["_mask_"]) addCount++;
for (let p of shapePts2d)
if (p["_mask_"])
{
addCount++;
if (this.ShapeMaterialSlotData)
p["_material_index_"] = this.ShapeMaterialSlotData[addCount - 1];
}
let sumCount = addCount + shapePts2d.length; //实际个数
const f4 = (a: number, b: number, c: number, d: number, uvs: Vector2[]) =>
const f4 = (a: number, b: number, c: number, d: number, uvs: Vector2[], materialIndex?: number) =>
{
let f1 = new Face3(a, b, c);
let f2 = new Face3(b, d, c);
let f1 = new Face3(a, b, c, undefined, undefined, materialIndex);
let f2 = new Face3(b, d, c, undefined, undefined, materialIndex);
this.faces.push(f1, f2);
this.faceVertexUvs[0].push([uvs[0].clone(), uvs[1].clone(), uvs[2].clone()], [uvs[1].clone(), uvs[3].clone(), uvs[2].clone()]);
};
@ -182,12 +190,16 @@ export class SweepGeometry extends Geometry
let tempStartX = 0;
let lastMaterialIndex = undefined;
for (let contourIndex = 0; contourIndex < shapePts2d.length; contourIndex++)
{
let p1 = pts[contourIndex];
let p2 = pts2[contourIndex];
let p2d = shapePts2d[contourIndex];
if (p2d["_mask_"])
lastMaterialIndex = p2d["_material_index_"] ?? lastMaterialIndex;
if (pathIndex !== verts.length - 1)
if (contourIndex === 0 || p2d["_mask_"])
this.edgePts.push(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
@ -219,7 +231,7 @@ export class SweepGeometry extends Geometry
new Vector2(v1, x3),
new Vector2(v2, x4),
];
f4(curIndex, nextIndex, curIndex2, nextIndex2, uvs);
f4(curIndex, nextIndex, curIndex2, nextIndex2, uvs, lastMaterialIndex);
}
this.vertices.push(p1);
}

@ -62,4 +62,7 @@ export enum BoardModalType
TemplateManage = "TemplateManage",//模板管理界面信息
DimStyleConfig = "DimStyleConfig",//标注配置
DatalistStore = "DatalistStore", //数据列表配置
BulkheadCeiling = "BulkheadCeiling",//吊顶轮廓
BulkheadCeilingContour = "BulkheadCeilingContour",//吊顶轮廓
HeadCeilingProfileMaterial = "HeadCeilingProfileMaterial",//吊顶截面材质
}

@ -2684,6 +2684,15 @@ export const CommandList: ICommand[] = [
chName: "排钻模块",
chDes: "",
},
{
typeId: "module",
link: `#`,
defaultCustom: "HCC",
command: CommandNames.HeadCeilingContour,
type: "模块",
chName: "吊顶轮廓模块",
chDes: "",
},
{
icon: IconEnum.DrawVSBox,
typeId: "module",
@ -2912,6 +2921,14 @@ export const CommandList: ICommand[] = [
chName: "显示右侧面板(三维刀路)",
chDes: "显示右侧面板(三维刀路)",
},
{
typeId: "interface",
defaultCustom: "HCMP",
command: CommandNames.HeadCeilingMaterialPanel,
type: "界面",
chName: "吊顶轮廓材质面板",
chDes: "显示吊顶轮廓材质面板",
},
{
typeId: "interface",
defaultCustom: "RPS",

@ -0,0 +1,216 @@
#BulkheadCeilingConfig {
.bp3-dialog-body {
width : 430px;
min-height : 495px;
padding : 10px;
overflow : auto;
max-height : 600px;
ul {
padding: 0;
margin : 0;
.firstLi {
height: 25px;
:first-child {
border-radius: 5px 0px 0px 0px;
}
:last-child {
border-radius: 0px 5px 0px 0px;
}
.bp3-label {
line-height: 2rem;
}
}
li {
height : 30px;
display : flex;
font-size : 13px;
text-align: center;
.bp3-label {
margin : 0;
line-height : 2.2rem;
border-right: 1.5px solid #AAAAAA;
border-top : 1.5px solid #AAAAAA;
border-left : 1.5px solid #AAAAAA;
&>div {
margin: 1px;
height: -webkit-fill-available;
}
}
&>:first-child {
width : 50px;
border-right: 0px;
}
&>:nth-child(2) {
width : 70px;
border-right: 0px
}
&>:last-child {
width: 100%;
}
.bp3-html-select {
margin: 1px;
select {
height: 26px;
}
.bp3-icon-caret-down {
top: 5px;
}
}
}
&>:last-child {
&>:first-child {
border-radius: 0px 0px 0px 5px;
}
&>:last-child {
border-radius: 0px 0px 5px 0px;
}
.bp3-label {
border-bottom: 1.5px solid #AAAAAA;
}
}
.material_detail {
display : flex;
white-space : nowrap;
justify-content: space-between;
.detail_name{
width : 70%;
display: flex;
.materialName{
width : 110px;
overflow : hidden;
text-align : left;
text-overflow: ellipsis;
}
}
.detail_type{
width : 100px;
display: flex;
.materialType{
width : 65px;
overflow : hidden;
text-align : left;
text-overflow: ellipsis;
}
}
}
.contour_detail {
position: relative;
display : flex;
.detail_logo {
height: 100%;
width : 38%;
display : flex;
align-items : center;
justify-content: space-evenly;
svg {
margin : -19px;
transform: scale(0.5);
}
}
.detail_name {
width : 62%;
display : flex;
align-items:center;
}
}
}
.itemContour {
display : flex;
justify-content: space-between;
&>div {
width: 100%;
}
.bp3-button {
width : 50px;
padding : 0px;
margin : 1px 2px;
font-size : 12px;
min-height: unset;
.bp3-button-text{
margin-top: 2px;
}
}
}
.contourLi {
height: 65px;
:nth-child(1) {
display : flex;
align-items : center;
justify-content: center;
}
.backgroudColor {
height: 28px;
width : 100%;
}
.bp3-html-select {
.bp3-icon-caret-down {
top: 23px;
}
}
.detail_name {
span{
width: 65px;
}
}
.materialName{
width : 110px;
overflow : hidden;
text-align : left;
text-overflow: ellipsis;
}
}
}
.bp3-dialog-footer {
.bp3-dialog-footer-actions {
justify-content: space-between;
}
.bp3-button {
margin-left: 3px;
}
}
}
.headCeilingContourManage{
.bp3-dialog-body{
width: 1000px;
height: 700px;
}
}

@ -0,0 +1,106 @@
import { ContextMenu, Intent, Menu, MenuItem } from '@blueprintjs/core';
import { IObservableValue } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ToplineUrls } from '../../../Common/HostUrl';
import { PostJson, RequestStatus } from '../../../Common/Request';
import { inflateBase64 } from '../../../Common/inflate';
import { CADFiler } from '../../../DatabaseServices/CADFiler';
import { Database } from '../../../DatabaseServices/Database';
import { Arc } from '../../../DatabaseServices/Entity/Arc';
import { Line } from '../../../DatabaseServices/Entity/Line';
import { Polyline } from '../../../DatabaseServices/Entity/Polyline';
import { DataList } from '../Common/Datalist';
import { IDirectoryProps } from '../SourceManage/CommonPanel';
export interface HeadCeilingListProps
{
deleteFun?: (topline?: { topline_id; }) => void;
dataList?: any[];
select?: (e: React.FormEvent<HTMLInputElement>, data: any) => void;
// draw: (id: string) => void;
getData?: () => void;
selectIds?: Set<string>;
showInfos?: boolean;
isRename: IObservableValue<boolean>;
editorCeilingContour?: (curves: (Polyline | Line | Arc)[], name?: string, currentDir?: IDirectoryProps, database?: Database) => void;
currentHeadCeilingInfo: { id: string, name: string; };
}
@observer
export class HeadCeilingContourList extends React.Component<HeadCeilingListProps, { isContextMenuOpen: boolean; }> {
constructor(props)
{
super(props);
this.state = {
isContextMenuOpen: false
};
}
_HandleMounseDown = (e: React.MouseEvent<HTMLElement>, tpline: any) =>
{
this.props.currentHeadCeilingInfo.id = tpline.topline_id;
this.props.currentHeadCeilingInfo.name = tpline.name;
};
_HandleEditor = async () =>
{
let data = await PostJson(ToplineUrls.detail, { topline_id: this.props.currentHeadCeilingInfo.id });
if (data.err_code === RequestStatus.Ok)
{
let json = inflateBase64(data.toplines.file);
let f = new CADFiler(JSON.parse(json));
let saveDb = new Database(false, false, true);
saveDb.FileRead(f);
let curves = saveDb.ModelSpace.Entitys as (Polyline | Line | Arc)[];
this.props.editorCeilingContour(curves, null, null, saveDb);
}
};
//展示右键菜单
_ShowContextMenu = (e: React.MouseEvent<HTMLElement>, tpline) =>
{
if (!tpline) return;
ContextMenu.show(
<Menu>
<MenuItem
icon="folder-new"
text="重命名"
onClick={() => this.props.isRename.set(true)}
/>
<MenuItem
icon="folder-new"
text="编辑"
onClick={this._HandleEditor}
/>
<MenuItem
icon="trash"
text="删除"
intent={Intent.DANGER}
onClick={() => { this.props.deleteFun(); }}
/>
</Menu>,
{ left: e.clientX, top: e.clientY },
() => this.setState({ isContextMenuOpen: false }),
);
this.setState({ isContextMenuOpen: true });
e.stopPropagation();
e.preventDefault();
};
public render()
{
return (
<DataList
dataList={this.props.dataList}
idKey={"topline_id"}
select={this.props.select}
showContextMenu={this._ShowContextMenu}
showInfos={this.props.showInfos}
selectData={this.props.selectIds}
handleMounseDown={this._HandleMounseDown}
isSvg
/>
);
}
}

@ -0,0 +1,483 @@
import { Button, Classes, Intent, MenuItem } from '@blueprintjs/core';
import { observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Matrix4, Vector3 } from 'three';
import { ViewChange } from '../../../Add-on/ViewChange';
import { app } from '../../../ApplicationServices/Application';
import { MaterialUrls, ToplineUrls } from '../../../Common/HostUrl';
import { DirectoryId, PostJson, RequestStatus } from '../../../Common/Request';
import { deflate, getPolylineSVG } from '../../../Common/SerializeMaterial';
import { DuplicateRecordCloning } from '../../../Common/Status';
import { GetEntity } from '../../../Common/Utils';
import { CADFiler } from '../../../DatabaseServices/CADFiler';
import { Contour } from '../../../DatabaseServices/Contour';
import { Database } from '../../../DatabaseServices/Database';
import { Arc } from '../../../DatabaseServices/Entity/Arc';
import { Line } from '../../../DatabaseServices/Entity/Line';
import { Polyline } from '../../../DatabaseServices/Entity/Polyline';
import { ObjectId } from '../../../DatabaseServices/ObjectId';
import { BulkheadCeilingShapeTemplateExtendData } from '../../../DatabaseServices/Room/Entity/Ceiling/BulkheadCeilingShapeExtendData';
import { TemplateRecord } from '../../../DatabaseServices/Template/TemplateRecord';
import { WblockCloneFiler } from '../../../DatabaseServices/WblockCloneFiler';
import { CommandWrap } from '../../../Editor/CommandMachine';
import { CommandState } from '../../../Editor/CommandState';
import { PromptStatus } from '../../../Editor/PromptResult';
import { TempEditor } from '../../../Editor/TempEditor';
import { ZAxisN } from '../../../Geometry/GeUtils';
import { HeadCeilingProfileMaterialStore, NONE_MATERIAL_ID } from '../../Store/HeadCeilingStore/HeadCeilingMaterialStore';
import { BoardModalType } from '../Board/BoardModalType';
import { DialogUserConfig } from '../Board/UserConfigComponent';
import { ModalContainer, ModalHeader } from '../Modal/ModalContainer';
import { ModalPosition } from '../Modal/ModalInterface';
import { CommonPanel, IDirectoryProps } from '../SourceManage/CommonPanel';
import { HandleDirComponent } from '../SourceManage/HandleDirComponent';
import { AppToaster } from '../Toaster';
import './HeadCeiling.less';
import { HeadCeilingContourList } from './HeadCeilingContourList';
import { HeadCeilingInfoConfigPanel } from './HeadCeilingInfoConfigPanel';
/**
*
*/
@observer
export class HeadCeilingContourManage extends React.Component<{}> {
constructor(props)
{
super(props);
this.state = {
defaultDirName: ""
};
}
_CanCreateCeilingContour = observable.box(false);
_CameraFiler: CADFiler;
_BulkheadCeilingStore = HeadCeilingProfileMaterialStore.GetInstance();
@observable _CurrentHeadCeilingInfo = { id: "", name: "" };
//创建吊顶轮廓
_CreateCeilingContour = async (name: string, currentDir: IDirectoryProps, callback: Function) =>
{
if (!name.trim())
{
AppToaster.show({
message: "名称不能为空",
intent: Intent.DANGER,
timeout: 1000
});
return;
}
if (this._CurrentHeadCeilingInfo.id)
{
await this._RenameBulkheadCeiling(name);
await callback();
}
else
{
if (!currentDir)
{
AppToaster.show({
message: "未知错误,请重试",
timeout: 1000
});
return;
}
if (!app.Viewer.CameraCtrl.Direction.equals(new Vector3(0, 0, -1)))
{
await new ViewChange(ZAxisN, true).exec();
AppToaster.show({
message: "自动切换至俯视视角!",
timeout: 5000,
intent: Intent.PRIMARY,
});
}
app.Editor.ModalManage.ToggleShow();
app.Editor.MaskManage.Clear();
//选择吊顶轮廓
let contourEnts = await this._SelectCeilingContour();
app.Editor.ModalManage.ToggleShow();
app.Editor.MaskManage.ShowMask();
if (contourEnts.length === 0) return;
//对选择的吊顶轮廓进行编辑后上传
this._EditorCeilingContour(contourEnts, name, currentDir);
}
};
//选择吊顶轮廓
async _SelectCeilingContour(): Promise<(Polyline | Line | Arc)[]>
{
let ents: (Polyline | Line | Arc)[] = [];
await CommandWrap(async () =>
{
let ssRes = await app.Editor.GetSelection({ Filter: { filterTypes: [Polyline, Line, Arc] } });
if (ssRes.Status !== PromptStatus.OK) return [];
ents = ssRes.SelectSet.SelectEntityList as (Polyline | Line | Arc)[];
let contour = Contour.CreateContour(ents, true);
if (!contour)
{
//警告错误 无法组成封闭的轮廓
AppToaster.show({
message: "无法组成封闭的轮廓",
intent: Intent.DANGER,
timeout: 3000,
});
ents = [];
}
}, "选择吊顶轮廓");
return ents;
};
//对选择的吊顶轮廓进行编辑
_EditorCeilingContour = async (curves: (Polyline | Line | Arc)[], name?: string, currentDir?: IDirectoryProps, database?: Database) =>
{
if (TempEditor.EditorIng || CommandState.CommandIng)
{
AppToaster.show({
message: "当前正处于编辑模式下",
timeout: 3000,
intent: Intent.WARNING,
});
return;
}
app.Editor.ModalManage.DestoryAll();
TempEditor.Start();
if (!app.Viewer.CameraCtrl.Direction.equals(new Vector3(0, 0, -1)))
app.Viewer.CameraCtrl.LookAt(new Vector3(0, 0, -1));
let config = new DialogUserConfig(this._BulkheadCeilingStore, BoardModalType.HeadCeilingProfileMaterial);
await config.LoadAndInitConfig(true);
//呼出吊顶材质对应的颜色面板
app.Editor.ModalManage.RenderModeless(
HeadCeilingInfoConfigPanel,
{ store: this._BulkheadCeilingStore, directoryId: DirectoryId.MaterialDir, configType: BoardModalType.HeadCeilingProfileMaterial, isNotUpdateStore: true, noClose: true },
{ position: ModalPosition.RightMid, canMinimize: false }
);
let extData = new BulkheadCeilingShapeTemplateExtendData;
if (database?.ExtendedData)
{
let f = new CADFiler(database.ExtendedData);
extData.ReadFile(f);
await this._BulkheadCeilingStore.InitEditorMaterialItems(extData);
}
await CommandWrap(async () =>
{
let newCurves: (Polyline | Arc | Line)[] = [];
for (let curve of curves)
{
let cu = curve.Clone();
cu.ColorIndex = extData.Color_MaterialId.get(cu.ColorIndex);
app.Database.ModelSpace.Append(cu);
newCurves.push(cu);
}
app.Database.hm.lockIndex++;//禁止初始化动作被撤销
this._CameraFiler = new CADFiler;
app.Viewer.CameraCtrl.WriteFile(this._CameraFiler);
app.Viewer.ZoomtoEntitys(newCurves);
AppToaster.show({
message: this._RenderToasterMessage(name, currentDir, database),
intent: Intent.PRIMARY,
timeout: 0,
onDismiss: this._ExitEditor
}, "编辑吊顶轮廓");
}, "编辑吊顶轮廓");
};
_RenderToasterMessage = (name: string, currentDir: IDirectoryProps, database?: Database) =>
{
return (
<div className="flex-between toaster-message">
<span>,线</span>
<div>
<Button text="保存" minimal onClick={() => { this._AddCeilingContourToDb(name, currentDir, database); }} />
<Button text="取消" minimal onClick={() => { AppToaster.clear(); }} />
</div>
</div>
);
};
//退出轮廓编辑
_ExitEditor = async () =>
{
app.Editor.ModalManage.DestoryAll();
await app.Editor.ModalManage.EndExecingCmd();
if (!this._CameraFiler) return;
app.Viewer.CameraCtrl.ReadFile(this._CameraFiler);
TempEditor.End();
this._CameraFiler = undefined;
app.Editor.ModalManage.RenderModal(HeadCeilingContourManage);
};
//上传吊顶轮廓(点击上方保存)
async _AddCeilingContourToDb(name: string, currentDir: IDirectoryProps, database?: Database): Promise<void>
{
let finalCurve: (Polyline | Line | Arc)[] = [];
//因为临时编辑器会新建一个场景,只需要遍历这个场景内的对象就可以了
for (let obj of app.Viewer.Scene.children)
{
if (obj.visible)
{
let en = GetEntity(obj);
if (en && en.Id?.Object && en.Visible && !en.IsErase && (en instanceof Polyline || en instanceof Line || en instanceof Arc))
finalCurve.push(en);
}
}
let contour = Contour.CreateContour(finalCurve, true);
if (!contour)
{
//警告错误 无法组成封闭的轮廓
AppToaster.show({
message: "无法组成封闭的轮廓",
intent: Intent.DANGER,
timeout: 3000,
});
return;
}
//拓展数据(材质槽信息)
let extData = new BulkheadCeilingShapeTemplateExtendData();
for (let curve of finalCurve)
{
if (extData.Color_MaterialId.has(curve.ColorIndex)) continue;
//根据UI面板对照获取材质id
let material_id = this._BulkheadCeilingStore.m_Option.Item[curve.ColorIndex - 1]?.example?.id;
if (material_id && material_id !== NONE_MATERIAL_ID)
{
//验证材质是否可用
let mtlData = await PostJson(MaterialUrls.detail, { material_id: material_id.toString() });
if (mtlData.err_code === RequestStatus.Ok)
extData.Color_MaterialId.set(curve.ColorIndex, material_id);
else
return;
}
else
extData.Color_MaterialId.set(curve.ColorIndex, NONE_MATERIAL_ID);
}
let f = new CADFiler;
extData.WriteFile(f);
let tempaltes = new Set<ObjectId>;
for (let e of finalCurve)
tempaltes.add((e.Template?.Object as TemplateRecord)?.Root?.objectId);
if (tempaltes.size > 1)
{
let keyRes = await app.Editor.GetKeyWords({
Msg: "选择的图形中来自不同的模块,忽略所有模块信息并继续?",
KeyWordList: [
{
key: "1",
msg: "好"
},
{
key: "2",
msg: "算了"
},
]
});
if (keyRes.StringResult !== "1")
return;
}
AppToaster.show({
message: "请点选该轮廓基点!",
timeout: 3000,
intent: Intent.PRIMARY,
}, "getPoint");
let res = await app.Editor.GetPoint({
Msg: "指定基点"
});
if (res.Status === PromptStatus.Cancel)
return;
let point = res.Point.negate();
let saveDb = new Database(false, false, true);
if (database)
{
saveDb = database;
saveDb.ModelSpace.Destroy();
saveDb.TemplateTable.Destroy();
saveDb.MaterialTable.Destroy();
}
if (tempaltes.size > 1 || [...tempaltes][0] === undefined)
{
let template = new TemplateRecord;
saveDb.TemplateTable.Add(template);
for (let e of finalCurve)
{
let newE = e.Clone().ApplyMatrix(new Matrix4().setPosition(point));
saveDb.ModelSpace.Append(newE);
template.Objects.push(newE.Id);
}
}
else
{
let template = [...tempaltes][0];
saveDb.WblockCloneObejcts([template.Object], saveDb.TemplateTable, new Map, DuplicateRecordCloning.Ignore, new WblockCloneFiler);
}
saveDb.ExtendedData = f.Data;
let logo = getPolylineSVG(contour.Curve as Polyline);
let fileJson = saveDb.FileWrite().ToString();
let data: { err_code: RequestStatus; };
if (this._CurrentHeadCeilingInfo.id)
{
data = await PostJson(ToplineUrls.update, {
topline_id: this._CurrentHeadCeilingInfo.id,
logo,
file: deflate(fileJson),
zip_type: "gzip",
});
}
else
{
data = await PostJson(ToplineUrls.create, {
dir_id: currentDir.id,
name,
logo,
file: deflate(fileJson),
zip_type: "gzip",
});
}
AppToaster.dismiss("编辑吊顶轮廓");
if (data.err_code === RequestStatus.Ok)
{
AppToaster.show({
message: this._CurrentHeadCeilingInfo.id ? "编辑成功!" : "添加成功!",
timeout: 2000,
intent: Intent.SUCCESS,
});
}
};
//吊顶轮廓重命名
_RenameBulkheadCeiling = async (name: string) =>
{
let data = await PostJson(ToplineUrls.update, {
topline_id: this._CurrentHeadCeilingInfo.id,
name
});
if (data.code === RequestStatus.Ok)
{
AppToaster.show({
message: "重命名成功",
timeout: 1000
});
}
this._CanCreateCeilingContour.set(false);
};
_StartCreateHeadCeilingContour = () =>
{
this._CurrentHeadCeilingInfo.id = "";
this._CurrentHeadCeilingInfo.name = "";
this._CanCreateCeilingContour.set(true);
};
_RenderNav = () =>
{
return (
<Button
icon="cloud-upload"
style={{ marginRight: 10 }}
text="新增轮廓模板"
intent={Intent.SUCCESS}
onClick={this._StartCreateHeadCeilingContour}
/>
);
};
_RenderMenuItems = () =>
{
return (
<MenuItem icon="cloud-upload" text="新增轮廓模板"
onClick={this._StartCreateHeadCeilingContour}
/>
);
};
public render()
{
return (
<ModalContainer className="headCeilingContourManage">
<ModalHeader
title="吊顶截面轮廓"
icon="bold"
close={() => app.Editor.ModalManage.Destory()}
/>
<div className={Classes.DIALOG_BODY}>
<CommonPanel
defaultDirId={DirectoryId.CeilingContour}
renderNav={this._RenderNav}
renderMenuItems={this._RenderMenuItems}
getUrl={ToplineUrls.get}
deleteUrl={ToplineUrls.delete}
handleDirComponent={
this._CanCreateCeilingContour.get() && <HandleDirComponent
defualtValue={this._CurrentHeadCeilingInfo.name}
isReset={false}
isOpen={this._CanCreateCeilingContour}
handleFunc={this._CreateCeilingContour}
title="吊顶截面轮廓模板名称:"
/>
}
clickTree={() =>
{
this._CurrentHeadCeilingInfo.id = "";
}}
>
<HeadCeilingContourList
isRename={this._CanCreateCeilingContour}
editorCeilingContour={this._EditorCeilingContour}
currentHeadCeilingInfo={this._CurrentHeadCeilingInfo}
/>
</CommonPanel>
</div>
<div className={Classes.DIALOG_FOOTER} >
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button
className={Classes.INTENT_DANGER}
text="取消"
onClick={() => app.Editor.ModalManage.Destory()}
/>
</div>
</div>
</ModalContainer >
);
}
}

@ -0,0 +1,11 @@
import { app } from "../../../ApplicationServices/Application";
import { Command } from "../../../Editor/CommandMachine";
import { HeadCeilingContourManage } from "./HeadCeilingContourManage";
export class Command_HeadCeilingContourManage implements Command
{
async exec()
{
app.Editor.ModalManage.RenderModal(HeadCeilingContourManage);
}
}

@ -0,0 +1,247 @@
import { Button, Classes, Icon, Label } from "@blueprintjs/core";
import { observable } from "mobx";
import { observer } from "mobx-react";
import React from "react";
import ReactDOM from "react-dom";
import { begin } from "xaop";
import { app } from "../../../ApplicationServices/Application";
import { KeyBoard } from "../../../Common/KeyEnum";
import { Log } from "../../../Common/Log";
import { DirectoryId } from "../../../Common/Request";
import { MaterialIn } from "../../../Common/SerializeMaterial";
import { PhysicalMaterialRecord } from "../../../DatabaseServices/PhysicalMaterialRecord";
import { TEMPLATE_SELECT_ID } from "../../../DatabaseServices/Room/Entity/Wall/Hole/Window/DrawWindowPanel";
import { GetCeilingContourDate, HeadCeilingInfoStore, NONE_CONTOUR_ID } from "../../Store/HeadCeilingStore/HeadCeilingInfoStore";
import { HeadCeilingProfileMaterialStore, NONE_MATERIAL_ID } from "../../Store/HeadCeilingStore/HeadCeilingMaterialStore";
import { BulkheadCeilingOption, BulkheadCeilingProfileMaterialOption } from "../../Store/OptionInterface/BulkheadCeilingOption";
import { BoardModalType } from "../Board/BoardModalType";
import { Config_ModalType, UserConfigComponent } from "../Board/UserConfigComponent";
import { ModalState } from "../Modal/ModalInterface";
import { OnlySelectPanelStore } from "../OnlySelectManage/OnlySelectPanelStore";
import { OnlySelectManagePanel } from "../OnlySelectManage/SelectManagePanel";
import { GetMtlJson } from "../SourceManage/MaterialList";
import './HeadCeiling.less';
import { HeadCeilingInfoItem } from "./HeadCeilingInfoItem";
export enum Display
{
Block = "block",
None = "none",
}
interface HeadCeilingInfoConfigPanelProps
{
store: HeadCeilingInfoStore | HeadCeilingProfileMaterialStore;
directoryId: DirectoryId;
configType: BoardModalType;
noClose?: boolean;
isNotUpdateStore?: boolean;
}
@observer
export class HeadCeilingInfoConfigPanel extends React.Component<HeadCeilingInfoConfigPanelProps, {}>
{
@observable _Display = Display.None;
_Event: Function;
_Title: string = "吊顶截面轮廓";
_CameraStateContainer: HTMLElement;
_SelectStore: OnlySelectPanelStore = OnlySelectPanelStore.GetSingleInstance(); //独立的材质选择Store
constructor(props)
{
super(props);
if (!this.props.configType) Log("无效configType");
this._Title += this.props.store instanceof HeadCeilingInfoStore ? "对照表" : "材质对照表";
//选择材质的UI放在 #modal 里有效居中
this._CameraStateContainer = document.createElement('div');
this._CameraStateContainer.id = TEMPLATE_SELECT_ID;
this._CameraStateContainer.style.zIndex = "35";
document.getElementById('modal').appendChild(this._CameraStateContainer);
if (window.screen.width <= 1450)
this._CameraStateContainer.style.left = '120px';
else
this._CameraStateContainer.style.left = `calc(50vw - 500px)`;
if (window.screen.height <= 750)
this._CameraStateContainer.style.top = `0px`;
else
this._CameraStateContainer.style.top = `calc(50vh - 395px)`;
this._CameraStateContainer.style.display = Display.None;
ReactDOM.render(<OnlySelectManagePanel
dataStore={this.props.store}
selectStore={this._SelectStore}
directoryId={this.props.directoryId}
setDisplay={this._OpenSelectTemplate}
dispaly={this._Display}
return={this._Return}
/>, this._CameraStateContainer);
}
componentDidMount()
{
this._Event = begin(app.Editor.ModalManage, app.Editor.ModalManage.OnKeyDown, (e: KeyboardEvent) =>
{
if (e.keyCode === KeyBoard.Escape)
{
if (this._CameraStateContainer.style.display === Display.Block)
{
this._OpenSelectTemplate(Display.None);
e.preventDefault();
e.stopPropagation();
return;
}
if (this.props.noClose)
app.Editor.ModalManage.stopKeyDownEvent = true;
}
});
}
componentWillUnmount()
{
this._Event();
this._Event = null;
document.getElementById('modal').removeChild(this._CameraStateContainer);
this._CameraStateContainer = undefined;
}
_Return = async (status: ModalState) =>
{
if (status === ModalState.Ok)
{
if (this.props.directoryId === DirectoryId.MaterialDir)
{
let material_id = "";
this._SelectStore.currentSelectId.forEach((e) => { material_id = e; });
let option = this.props.store.m_Option as BulkheadCeilingProfileMaterialOption;
let material = option.Item[this._SelectStore.itemIndex].example;
if (material_id)
{
let json = await GetMtlJson({ material_id });
if (json)
{
let mtl = MaterialIn(JSON.parse(json)) as PhysicalMaterialRecord;
if (mtl)
{
material.id = parseInt(material_id);
material.name = this._SelectStore.dataName ?? mtl.Name;
material.type = mtl.type;
}
}
}
else
{
material.id = NONE_MATERIAL_ID;
material.name = '';
material.type = '';
}
}
else if (this.props.directoryId === DirectoryId.CeilingContour)
{
let contour_id = "";
this._SelectStore.currentSelectId.forEach((e) => { contour_id = e; });
let option = this.props.store.m_Option as BulkheadCeilingOption;
let contour = option.Item[this._SelectStore.itemIndex].example;
if (contour_id)
{
let date = await GetCeilingContourDate(contour_id);
if (date)
{
contour.id = parseInt(contour_id);
contour.name = date.toplines.name;
contour.logo = date.toplines.logo;
}
}
else
{
contour.id = NONE_CONTOUR_ID;
contour.name = '';
contour.logo = '';
}
}
}
};
//开关选择材质的面板
_OpenSelectTemplate = (style: Display) =>
{
if (style === Display.Block)
{
this._SelectStore.Clear();
app.Editor.ModalManage.stopKeyDownEvent = true;
}
this._CameraStateContainer.style.display = style;
this._Display = style;
};
//窗口最小化
_Minimize()
{
app.Editor.ModalManage.CurrentModal.canMinimize = true;
app.Editor.ModalManage.CurrentModal.Minimize();
app.Editor.ModalManage.CurrentModal.canMinimize = false;
}
render()
{
return (
<div id="BulkheadCeilingConfig" className={Classes.DIALOG_CONTAINER}>
<div className={Classes.DIALOG} >
<div className={Classes.DIALOG_HEADER} data-id="dragArea">
<Icon icon="th" iconSize={18} />
<h4 className={Classes.HEADING}>{this._Title}</h4>
<Button
icon="minus"
aria-label="Close"
minimal={true}
onClick={() => { this._Minimize(); }}
/>
<Button
icon="cross"
aria-label="Close"
minimal={true}
onClick={() => { app.Editor.ModalManage.Destory(); }}
/>
</div>
<div className={Classes.DIALOG_BODY}>
<ul>
<li className="firstLi">
<Label></Label>
<Label>/</Label>
<Label></Label>
</li>
{
this.props.store.m_Option.Item.map((item, index) =>
{
return <HeadCeilingInfoItem
item={item}
display={this._OpenSelectTemplate}
itemIndex={index}
selectStore={this._SelectStore}
configType={this.props.configType}
/>;
})
}
</ul>
</div>
<div className={Classes.DIALOG_FOOTER} style={{ display: "unset" }}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<div className="foot_left">
<UserConfigComponent store={this.props.store} type={this.props.configType} configType={Config_ModalType.UserConfigModal} isNotUpdateStore={this.props.isNotUpdateStore} />
</div>
</div>
</div>
</div>
</div >
);
}
}

@ -0,0 +1,31 @@
import { app } from "../../../ApplicationServices/Application";
import { DirectoryId } from "../../../Common/Request";
import { Command } from "../../../Editor/CommandMachine";
import { HeadCeilingInfoStore } from "../../Store/HeadCeilingStore/HeadCeilingInfoStore";
import { HeadCeilingProfileMaterialStore } from "../../Store/HeadCeilingStore/HeadCeilingMaterialStore";
import { BoardModalType } from "../Board/BoardModalType";
import { HeadCeilingInfoConfigPanel } from "./HeadCeilingInfoConfigPanel";
export class Command_HeadCeilingMaterialPanel implements Command
{
async exec()
{
let store = HeadCeilingProfileMaterialStore.GetInstance();
app.Editor.ModalManage.RenderModeless(HeadCeilingInfoConfigPanel,
{ store, directoryId: DirectoryId.MaterialDir, configType: BoardModalType.HeadCeilingProfileMaterial },
{ canMinimize: false }
);
}
}
export class Command_HeadCeilingInfoConfigPanel implements Command
{
async exec()
{
let store = HeadCeilingInfoStore.GetInstance();
app.Editor.ModalManage.RenderModeless(HeadCeilingInfoConfigPanel,
{ store, directoryId: DirectoryId.CeilingContour, configType: BoardModalType.BulkheadCeilingContour },
{ canMinimize: false }
);
}
}

@ -0,0 +1,96 @@
import { Button, HTMLSelect, Icon } from "@blueprintjs/core";
import { observer } from "mobx-react";
import React from "react";
import { ColorMaterial } from "../../../Common/ColorPalette";
import { BulkheadCeiling_ContourType } from "../../../DatabaseServices/Room/Entity/Ceiling/BulkheadCeiling";
import { ICeilingProfileMaterialItem } from "../../Store/HeadCeilingStore/HeadCeilingMaterialStore";
import { ICeilingItem } from "../../Store/OptionInterface/BulkheadCeilingOption";
import { BoardModalType } from "../Board/BoardModalType";
import { OnlySelectPanelStore } from "../OnlySelectManage/OnlySelectPanelStore";
import './HeadCeiling.less';
import { Display } from "./HeadCeilingInfoConfigPanel";
interface ItemPorps
{
item: ICeilingItem | ICeilingProfileMaterialItem;
itemIndex: number;
selectStore: OnlySelectPanelStore;
configType: BoardModalType;
display: (display: Display) => void;
}
@observer
export class HeadCeilingInfoItem extends React.Component<ItemPorps, {}>
{
_IsMaterial = this.props.configType === BoardModalType.HeadCeilingProfileMaterial;
render()
{
let item = this.props.item as any;
return (
<li className={this._IsMaterial ? "" : "contourLi"}>
<div className="bp3-label">
<div className="backgroudColor" style={{ background: ColorMaterial.GetColor(this.props.item.color).getStyle() }}>
{item.color}
</div>
</div>
<div className="bp3-label">
<HTMLSelect
options={[{ value: 0, label: "洞" }, { value: 1, label: "岛" }]}
value={item.example.contourType ?? BulkheadCeiling_ContourType.Hole}
onChange={(e) =>
{
item.example.contourType = parseInt(e.currentTarget.value);
}}
iconProps={{ icon: "caret-down" }}
disabled={this._IsMaterial}
/>
</div>
<div className="itemContour bp3-label">
{
this._IsMaterial ?
<div className="material_detail" >
<div className="detail_name">
<span>:</span>
<div className="materialName">{item.example.name || "无"}</div>
</div>
<div className="detail_type">
<span>:</span>
<div className="materialType">{item.example.type || "无"}</div>
</div>
</div>
:
<div className="contour_detail" >
<div className="detail_logo">
{
item.example.logo ? <svg
width="100"
height="100"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
>
<path d={item.example.logo} fill="transparent" stroke="black" strokeWidth="1" />
</svg>
:
<Icon iconSize={40} icon="add-to-artifact" />
}
</div>
<div className="detail_name">
<span>:</span>
<div className="materialName">{item.example.name || "无"}</div>
</div>
</div>
}
<Button
text="选择"
onClick={() =>
{
this.props.selectStore.itemIndex = this.props.itemIndex;
this.props.display(Display.Block);
}}
/>
</div>
</li>
);
}
}

@ -13,6 +13,7 @@ import { Board } from '../../DatabaseServices/Entity/Board';
import { Curve } from '../../DatabaseServices/Entity/Curve';
import { MaterialTable } from '../../DatabaseServices/MaterialTable';
import { PhysicalMaterialRecord } from '../../DatabaseServices/PhysicalMaterialRecord';
import { BulkheadCeiling } from '../../DatabaseServices/Room/Entity/Ceiling/BulkheadCeiling';
import { Text } from '../../DatabaseServices/Text/Text';
import { CommandWrap } from '../../Editor/CommandMachine';
import { GenerateRaycaster, PointPick, Raycast } from '../../Editor/PointPick';
@ -134,6 +135,8 @@ export class MaterialExplorer extends React.Component<{ materialTable: MaterialT
{
if (IsMeshMaterialEntity(en))
en.SetMaterialAtSlot(material.Id, intersection.face.materialIndex);
else if (en instanceof BulkheadCeiling)
en.SetMaterial(material.Id, intersection);
if (en instanceof Board)
{

@ -0,0 +1,33 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import { AnyObject } from '../../Store/OptionInterface/IOptionInterface';
import { DataList } from '../Common/Datalist';
interface IDataListProps
{
dataList?: any[];
select?: (e, data) => void;
selectIds?: Set<string>;
showInfos?: boolean;
idKey?: string;
handleDbClick?: (data: AnyObject) => void;
}
@observer
export class OnlySelectDataList extends React.Component<IDataListProps, {}>
{
render()
{
return (
<DataList
dataList={this.props.dataList}
idKey={this.props.idKey}
select={this.props.select}
selectData={this.props.selectIds}
dbclickImg={this.props.handleDbClick}
hintClassName="hint editor-lint"
isSvg={this.props.idKey === "topline_id"} //需改动
/>
);
}
}

@ -0,0 +1,397 @@
import { Classes, HTMLSelect, ITreeNode, Tree } from "@blueprintjs/core";
import hotkeys from "hotkeys-js-ext";
import { action, observable } from "mobx";
import { observer } from "mobx-react";
import React from "react";
import { app } from "../../../ApplicationServices/Application";
import { DirUrl } from "../../../Common/HostUrl";
import { MouseKey } from "../../../Common/KeyEnum";
import { DirectoryId, PostJson, RequestStatus } from "../../../Common/Request";
import { EOrderType } from "../../../Editor/OrderType";
import { AnyObject } from "../../Store/OptionInterface/IOptionInterface";
import { OrderType } from "../SourceManage/CommonPanel";
import { CommonPanelStore, commonPanelStore } from "../SourceManage/CommonPanelStore";
import { Pagination } from "../SourceManage/Pagination";
import { OnlySelectPanelStore } from "./OnlySelectPanelStore";
import './SelectManagePanel.less';
export interface IDirectoryProps
{
id: DirectoryId | string;
path: string;
pathNum: number[];
}
interface ICommonPanelState
{
nodes: ITreeNode[]; //目录节点
defaultDirName: string; //新目录名字
updateNode: ITreeNode; //需要更新的目录节点
}
interface ICommonPanelProps
{
defaultDirId: string; //根目录ID
getUrl: string; //获取数据URL
dirNameFilter?: string[];
handleDbClick?: (data: AnyObject) => void; //双击dataImg事件
}
@observer
export class OnlySelectDataManage extends React.Component<ICommonPanelProps, ICommonPanelState>
{
constructor(props)
{
super(props);
this.state = {
nodes: [],
defaultDirName: "",
updateNode: undefined,
};
this._CurrentDir.id = this.props.defaultDirId;
switch (this.props.defaultDirId)
{
case DirectoryId.ImgDir:
this._IdKey = "image_id";
break;
case DirectoryId.HistoryDit:
case DirectoryId.FileDir:
this._IdKey = "file_id";
break;
case DirectoryId.MaterialDir:
this._IdKey = "material_id";
break;
case DirectoryId.ToplineDir:
case DirectoryId.Frame:
case DirectoryId.KnifePathDir:
case DirectoryId.CeilingContour:
this._IdKey = "topline_id";
break;
case DirectoryId.DrillingDir:
case DirectoryId.TemplateDir:
this._IdKey = "module_id";
break;
default:
console.warn("未知目录id");
this._IdKey = "";
}
}
_SelectStore: OnlySelectPanelStore = OnlySelectPanelStore.GetSingleInstance();
static CurrentDirCache: Map<string, IDirectoryProps> = new Map();
@observable _DataList = [];
@observable _CurrentDir: IDirectoryProps = {
id: DirectoryId.None,
path: "",
pathNum: []
};
@observable _PageData = { //分页数据
count: 0,
currentPage: 1,
pageCount: 15
};
@observable _SearchStr = "";//搜索材质名
_IdKey: string;
_Tree: HTMLElement; //目录数元素
_TimeId = null; //搜索时的定时器
get IsCtrlDown()
{
return app.Editor.ModalManage.IsCtrlDown || hotkeys.ctrl;
}
componentDidMount()
{
//监听右键其他位置
if (this._Tree)
{
this._Tree.addEventListener('mousedown', this._HandleTreeMouseDown);
}
this._LoadingData();
}
componentWillUnmount()
{
if (this._Tree)
{
this._Tree.removeEventListener('mousedown', this._HandleTreeMouseDown);
}
}
render()
{
return (
<>
<div className="searchMaterial">
<HTMLSelect
disabled={this.props.defaultDirId === DirectoryId.HistoryDit}
defaultValue={commonPanelStore.m_Option.orderType}
options={OrderType}
onChange={(e) =>
{
commonPanelStore.m_Option.orderType = e.target.value as EOrderType;
this._HandleGetData();
}}
/>
<input
disabled={this.props.defaultDirId === DirectoryId.HistoryDit}
className="bp3-input"
placeholder="搜索..."
type="search"
defaultValue={this._SearchStr}
onKeyDown={(e) => { e.stopPropagation(); }}
onChange={(e) => this._HandleSearch(e)}
/>
</div>
<div className="materialParams">
<div
className="bp3-card img-tree"
ref={c => this._Tree = c}
>
<Tree
contents={this.state.nodes}
onNodeClick={this._HandleNodeClick}
onNodeCollapse={(node) => this._HandleNodeCollapse(node, true)}
onNodeExpand={(node) => this._HandleNodeCollapse(node, false)}
className={Classes.ELEVATION_0}
/>
</div>
<div className="bp3-card img-lib" >
{
React.Children.map(this.props.children, child =>
child && React.cloneElement(child as React.DetailedReactHTMLElement<any, any>,
{
dataList: this._DataList,
currentDir: this._CurrentDir,
getData: this._HandleGetData,
select: this._HandleSelectData,
handleDbClick: this._HandleDbClick,
selectIds: this._SelectStore.currentSelectId,
idKey: this._IdKey,
})
)
}
{
this._PageData.count > this._PageData.pageCount &&
<Pagination
getImgListFun={this._HandleGetData}
pageData={this._PageData}
maxCount={3}
/>
}
</div>
</div>
</>
);
}
_HandleSelectData = (e: React.MouseEvent<HTMLInputElement>, data) =>
{
const id = data[this._IdKey];
this._SelectStore.Clear();
this._SelectStore.currentSelectId.add(id);
this._SelectStore.dataName = data.name;
};
//双击dataImg事件
_HandleDbClick = (data) =>
{
this.props.handleDbClick(data);
};
//选中目录亮显
_SelectNode = (nodeData: ITreeNode, isSelected?: boolean) =>
{
this._ForEachNode(this.state.nodes, n => (n.isSelected = false));
nodeData.isSelected = true;
this.setState(this.state);
};
//点击目录
_HandleNodeClick = async (nodeData: ITreeNode, _nodePath: number[]) =>
{
const isReLoad = nodeData.id !== this._CurrentDir.id;
this._GetCurrentDir(nodeData.id, _nodePath);
this._SelectStore.Clear();
if (isReLoad)
await this._HandleGetData();
this._SelectNode(nodeData);
};
//去掉其他节点被选择状态
_ForEachNode(nodes: ITreeNode[], callback: (node: ITreeNode) => void)
{
if (nodes == null)
{
return;
}
for (const node of nodes)
{
callback(node);
this._ForEachNode(node.childNodes, callback);
}
}
//获取当前目录路径
_GetCurrentDir = (dirId: React.ReactText, nodePath: number[]) =>
{
let path = "";
let childNodes: ITreeNode[];
for (let i of nodePath)
{
let node: ITreeNode;
if (!childNodes)
{
node = this.state.nodes[i];
}
else
{
node = childNodes[i];
}
path += node.label + "/";
childNodes = node.childNodes;
}
Object.assign(this._CurrentDir, { id: dirId, path, pathNum: nodePath });
OnlySelectDataManage.CurrentDirCache.set(this.props.defaultDirId, { id: dirId as DirectoryId, path, pathNum: nodePath });
};
//分析目录转换为ui节点数
_ParseNodes = (dirs: any) =>
{
let newNodes: ITreeNode[] = [];
for (let dir of dirs)
{
let node: ITreeNode = {
id: dir.dir_id,
label: dir.dir_name,
icon: "folder-close",
hasCaret: dir.childs.length > 0,
className: dir.dir_id, //为了识别这个元素的目录id
childNodes: this._ParseNodes(dir.childs),
};
newNodes.push(node);
}
return newNodes;
};
//展开折叠目录
_HandleNodeCollapse = (nodeData: ITreeNode, isCollapse: boolean) =>
{
nodeData.isExpanded = !isCollapse;
this.setState(this.state);
};
/**
*
* name-,curr_page-,dir_id:id
* desc
*/
@action
_HandleGetData = async (queryData?: { dir_id?: string, name?: string, curr_page?: number; }) =>
{
let query: any;
query = {
dir_id: this._CurrentDir.id,
curr_page: 1,
page_count: this._PageData.pageCount,
name: this._SearchStr,
};
query.order = CommonPanelStore.GetInstance().m_Option.orderType;
//若不传入数据,用默认查询数据.
if (queryData)
{
Object.assign(query, queryData);
}
this._PageData.currentPage = query.curr_page;
let data = await PostJson(this.props.getUrl, query);
if (data.err_code === RequestStatus.Ok && Number(data.count))
{
let dataList: any[];
dataList = data.images || data.materials || data.toplines || data.files || data.modules || [];
dataList.forEach(d => d.isChecked = false);
this._DataList = dataList;
}
else
this._DataList.length = 0;
this._PageData.count = parseInt(data.count);
this._SelectStore.Clear();
};
//点击节点容器的动作
_HandleTreeMouseDown = (e: MouseEvent) =>
{
this._SelectStore.Clear();
let el = e.target as HTMLElement;
if (e.button === MouseKey.Left)
{
if (el.classList.contains('bp3-tree'))
{
this._ForEachNode(this.state.nodes, n => (n.isSelected = false));
if (this._CurrentDir.id !== this.props.defaultDirId)
{
Object.assign(this._CurrentDir, { id: this.props.defaultDirId, path: "", pathNum: [] });
OnlySelectDataManage.CurrentDirCache.set(this.props.defaultDirId, { id: this.props.defaultDirId as DirectoryId, path: "", pathNum: [] });
this._HandleGetData();
}
}
}
};
_LoadingData = async () =>
{
let data = await PostJson(DirUrl.query, { dir_type: this.props.defaultDirId });
if (data.err_code === RequestStatus.Ok)
{
let dirs = data.dirs as { dir_name: string; }[];
if (this.props.dirNameFilter)
{
dirs = dirs.filter(dir =>
{
return this.props.dirNameFilter.some(s => dir.dir_name.includes(s));
});
}
this.setState({ nodes: this._ParseNodes(dirs) });
}
let currentDirProps = OnlySelectDataManage.CurrentDirCache.get(this.props.defaultDirId);
if (currentDirProps)
{
this._LoadCurrentNode(currentDirProps);
}
await this._HandleGetData();
};
_LoadCurrentNode = (currentDirProps: IDirectoryProps) =>
{
let node: ITreeNode;
for (let n of currentDirProps.pathNum)
{
if (!node)
{
node = this.state.nodes[n];
}
else
node = node.childNodes[n];
if (!node) return;
if (node.childNodes.length)
node.isExpanded = true;
}
if (node)
{
this._SelectNode(node, true);
Object.assign(this._CurrentDir, currentDirProps);
}
};
_HandleSearch = async (e: React.ChangeEvent<HTMLInputElement>) =>
{
this._SearchStr = e.currentTarget.value;
if (this._TimeId)
clearTimeout(this._TimeId);
this._TimeId = setTimeout(async () =>
{
await this._HandleGetData();
}, 500);
};
}

@ -0,0 +1,23 @@
import { observable } from "mobx";
//只用于 选择的面板Store
export class OnlySelectPanelStore
{
itemIndex: number = 0;
dataName: string = "";
@observable currentSelectId: Set<string> = new Set<string>(); //当前选择的信息
Clear()
{
this.dataName = "";
this.currentSelectId.clear();
}
private static _SingleInstance: OnlySelectPanelStore;
static GetSingleInstance(): OnlySelectPanelStore
{
if (this._SingleInstance) return this._SingleInstance;
this._SingleInstance = new OnlySelectPanelStore;
return this._SingleInstance;
}
}

@ -0,0 +1,93 @@
#SelectManagePanel {
position: absolute;
width: 1000px;
.bp3-dialog-body {
width: 100%;
height: 700px;
.searchMaterial {
margin: 2px 0 0 0;
height: 34px;
display: flex;
justify-content: flex-end;
input {
width: 31%;
}
.bp3-html-select{
margin-right: 10px;
select{
height: 30px;
}
.bp3-icon {
right: 2px;
top: 7.5px;
}
}
}
.materialParams {
display: flex;
height: 665px;
.img-tree {
width: 25%;
}
.img-lib {
width: 75%;
.editor-lint {
-webkit-user-drag: none;
}
img {
-webkit-user-drag: none;
}
.apply-img-hint:hover:before {
all: unset;
}
.hint:hover::before {
all: unset;
}
&>:nth-child(1) {
height: 600px;
}
.fileList {
text-align: center;
}
.fileList:hover {
cursor: pointer;
}
}
.pagination {
height : 40px;
display : flex;
padding : 10px 0;
align-items : center;
flex-direction : row;
justify-content: center;
.bp3-label {
line-height : 1.5rem;
margin-bottom: 0.5px;
span{
width: unset;
}
}
}
}
}
}

@ -0,0 +1,110 @@
import { Button, Classes, Icon } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { MaterialUrls, ToplineUrls } from '../../../Common/HostUrl';
import { DirectoryId } from '../../../Common/Request';
import { HeadCeilingInfoStore } from '../../Store/HeadCeilingStore/HeadCeilingInfoStore';
import { HeadCeilingProfileMaterialStore } from '../../Store/HeadCeilingStore/HeadCeilingMaterialStore';
import { Display } from '../HeadCeiling/HeadCeilingInfoConfigPanel';
import { ModalState } from '../Modal/ModalInterface';
import { OnlySelectDataList } from './OnlySelectDataList';
import { OnlySelectDataManage } from './OnlySelectManage';
import { OnlySelectPanelStore } from './OnlySelectPanelStore';
interface IDataListProps
{
directoryId: DirectoryId;
setDisplay: (display: Display) => void;
dataStore: HeadCeilingInfoStore | HeadCeilingProfileMaterialStore;
selectStore: OnlySelectPanelStore;
dispaly: Display;
return: (status: ModalState) => void;
}
@observer
export class OnlySelectManagePanel extends React.Component<IDataListProps, {}>
{
_Title: string = "";
_GetUrl: string = "";
constructor(prop)
{
//这边修改标题和数据地址
super(prop);
switch (this.props.directoryId)
{
case DirectoryId.ImgDir:
break;
case DirectoryId.HistoryDit:
case DirectoryId.FileDir:
break;
case DirectoryId.MaterialDir:
this._Title = "选择材质";
this._GetUrl = MaterialUrls.get;
break;
case DirectoryId.ToplineDir:
case DirectoryId.Frame:
case DirectoryId.KnifePathDir:
break;
case DirectoryId.CeilingContour:
this._Title = "选择吊顶轮廓";
this._GetUrl = ToplineUrls.get;
break;
case DirectoryId.DrillingDir:
case DirectoryId.TemplateDir:
break;
default:
console.warn("未知目录id");
}
}
render()
{
return (
<div id='SelectManagePanel' className={Classes.DIALOG_CONTAINER}>
<div className={Classes.DIALOG} tabIndex={1}>
<div className={Classes.DIALOG_HEADER} data-id="dragArea">
<Icon icon="plus" iconSize={18} />
<h4 className={Classes.HEADING}>{this._Title}</h4>
<Button
icon="cross"
aria-label="Close"
minimal={true}
onClick={() => { this._Return(ModalState.Cancel); }}
/>
</div>
<div className={Classes.DIALOG_BODY}>
<OnlySelectDataManage
defaultDirId={this.props.directoryId}
getUrl={this._GetUrl}
handleDbClick={() => { this._Return(ModalState.Ok); }}
>
<OnlySelectDataList />
</OnlySelectDataManage>
</div>
<div className={Classes.DIALOG_FOOTER} >
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button
className={Classes.INTENT_PRIMARY}
text="确定"
onClick={() => { this._Return(ModalState.Ok); }}
/>
<Button
className={Classes.INTENT_DANGER}
text="取消"
onClick={() => { this._Return(ModalState.Cancel); }}
/>
</div>
</div>
</div>
</div >
);
}
//选择完成或者取消
_Return = async (status: ModalState) =>
{
this.props.return(status);
this.props.setDisplay(Display.None);
};
}

@ -11,6 +11,7 @@ import { deflate, getPolylineSVG, toplineFileIn } from '../../../../Common/Seria
import { inflateBase64 } from "../../../../Common/inflate";
import { CADFiler } from '../../../../DatabaseServices/CADFiler';
import { Polyline } from '../../../../DatabaseServices/Entity/Polyline';
import { CommandWrap } from '../../../../Editor/CommandMachine';
import { CommandState } from '../../../../Editor/CommandState';
import { TempEditor } from '../../../../Editor/TempEditor';
import { IKnifeProps, IUiOption } from "../../../Store/OptionInterface/IOptionInterface";
@ -116,18 +117,21 @@ export class KnifeList extends React.Component<IKnifeListProps, { isContextMenuO
if (data.err_code === RequestStatus.Ok)
{
let file = inflateBase64(data.toplines.file);
let pl = toplineFileIn(file);
app.Database.ModelSpace.Append(pl);
app.Database.hm.lockIndex++;//禁止初始化动作被撤销
this._cameraFiler = new CADFiler;
app.Viewer.CameraCtrl.WriteFile(this._cameraFiler);
ZoomToEntity(pl);
AppToaster.show({
message: this.renderToasterMessage(),
intent: Intent.PRIMARY,
timeout: 0,
onDismiss: this.exitEditor
await CommandWrap(() =>
{
let file = inflateBase64(data.toplines.file);
let pl = toplineFileIn(file);
app.Database.ModelSpace.Append(pl);
app.Database.hm.lockIndex++;//禁止初始化动作被撤销
this._cameraFiler = new CADFiler;
app.Viewer.CameraCtrl.WriteFile(this._cameraFiler);
ZoomToEntity(pl);
AppToaster.show({
message: this.renderToasterMessage(),
intent: Intent.PRIMARY,
timeout: 0,
onDismiss: this.exitEditor
});
});
}
};

@ -199,6 +199,7 @@ export class CommonPanel extends React.Component<ICommonPanelProps, ICommonPanel
case DirectoryId.ToplineDir:
case DirectoryId.Frame:
case DirectoryId.KnifePathDir:
case DirectoryId.CeilingContour:
this.idKey = "topline_id";
break;
case DirectoryId.DrillingDir:
@ -1169,6 +1170,7 @@ export class CommonPanel extends React.Component<ICommonPanelProps, ICommonPanel
case DirectoryId.ToplineDir:
case DirectoryId.Frame:
case DirectoryId.KnifePathDir:
case DirectoryId.CeilingContour:
url = ToplineUrls.move;
query.topline_ids = dataIds;
break;
@ -1276,8 +1278,6 @@ export class CommonPanel extends React.Component<ICommonPanelProps, ICommonPanel
this.pageData.currentPage = query.curr_page;
this.pageData.pageCount = query.page_count;
if (query.name && query.dir_id === this.props.defaultDirId)
query.dir_id = DirectoryId.None;
//获取data
//资源库中的门窗

@ -48,20 +48,10 @@ export class MaterialList extends React.Component<IImgListProps, {}> {
isOpen: false,
};
private readyMtl: { material_id: string; name: string; };
private handleGetMtlJson = async (mat: { material_id: string; }): Promise<string | undefined> =>
{
let material_id = mat.material_id;
let data = await PostJson(MaterialUrls.detail, { material_id });
if (data.err_code === RequestStatus.Ok)
{
let file = data.materials.file;
return inflateBase64(file);
}
};
private editor = async (data: { material_id: string; }) =>
{
let json = await this.handleGetMtlJson(data);
let json = await GetMtlJson(data);
if (json)
{
let mtl = MaterialIn(JSON.parse(json));
@ -99,7 +89,7 @@ export class MaterialList extends React.Component<IImgListProps, {}> {
if (!mtl)
{
let json = await this.handleGetMtlJson(this.readyMtl);
let json = await GetMtlJson(this.readyMtl);
if (json)
{
let mtlOut = MaterialIn(JSON.parse(json) as Object[]) as PhysicalMaterialRecord;
@ -200,7 +190,7 @@ export class MaterialList extends React.Component<IImgListProps, {}> {
};
private renameMtl = async () =>
{
let json = await this.handleGetMtlJson(this.readyMtl);
let json = await GetMtlJson(this.readyMtl);
if (json)
{
MaterialInAndAppendAppData(JSON.parse(json));
@ -217,7 +207,7 @@ export class MaterialList extends React.Component<IImgListProps, {}> {
{
CommandWrap(async () =>
{
let json = await this.handleGetMtlJson(this.readyMtl);
let json = await GetMtlJson(this.readyMtl);
if (json)
{
let mtl = MaterialIn(JSON.parse(json)) as PhysicalMaterialRecord;
@ -245,7 +235,7 @@ export class MaterialList extends React.Component<IImgListProps, {}> {
let newMaterial = app.Database.MaterialTable.GetAt(data.name);
if (!newMaterial)
{
let json = await this.handleGetMtlJson(data);
let json = await GetMtlJson(data);
let material = MaterialIn(JSON.parse(json) as Object[]) as PhysicalMaterialRecord;
app.Database.WblockCloneObejcts(
[material],
@ -424,3 +414,15 @@ export class MaterialList extends React.Component<IImgListProps, {}> {
this.readyMtl = null;
};
}
export async function GetMtlJson(mat: { material_id: string; }): Promise<string | undefined>
{
let material_id = mat.material_id;
let data = await PostJson(MaterialUrls.detail, { material_id });
if (data.err_code === RequestStatus.Ok)
{
let file = data.materials.file;
return inflateBase64(file);
}
};

@ -25,7 +25,7 @@ export interface IToplineListProps
select?: (e: React.FormEvent<HTMLInputElement>, data: any) => void;
draw: (id: string) => void;
isRename: IObservableValue<boolean>;
updata: (name: string, dir: IDirectoryProps, call: Function) => void;
updata?: (name: string, dir: IDirectoryProps, call: Function) => void;
getData?: () => void;
info: { id: string, name: string; };
selectIds?: Set<string>;

@ -296,3 +296,14 @@
padding: 20px;
}
}
#OperRoomFlatTop{
.bp3-dialog-body{
height : 70px;
width : 200px;
display : flex;
align-items : center;
border-radius : 5px;
justify-content: space-evenly;
}
}

@ -4,6 +4,8 @@ import React, { Component } from 'react';
import { app } from '../../../../ApplicationServices/Application';
import { Entity } from '../../../../DatabaseServices/Entity/Entity';
import { EntityRef } from '../../../../DatabaseServices/Entity/EntityRef';
import { HardwareTopline } from '../../../../DatabaseServices/Hardware/HardwareTopline';
import { BulkheadCeiling } from '../../../../DatabaseServices/Room/Entity/Ceiling/BulkheadCeiling';
import { CommandWrap } from '../../../../Editor/CommandMachine';
import { AxisType } from '../../../../Editor/TranstrolControl/CoorAxes';
import { ZeroVec } from '../../../../Geometry/GeUtils';
@ -27,15 +29,21 @@ interface ModuleBaseParamsProps
@observer
export default class ModuleBaseParams extends Component<ModuleBaseParamsProps, {}>
{
_ShowParams = (ents: Entity[]) =>
{
if (ents.length < 1 || this.props.RoomBaseParamsStore._EntityIds.length > 0 || ents[0] instanceof BulkheadCeiling || ents[0] instanceof HardwareTopline)
return false;
else
return true;
};
render()
{
const { ModifyModelStore, RoomBaseParamsStore } = this.props;
const { ModifyModelStore } = this.props;
let ents = ModifyModelStore._EntityIds.map(id => app.Database.GetObjectId(id)?.Object).filter(en => en && !en.IsErase && en instanceof Entity) as Entity[];
return (
<div className='ModuleBaseParams'
style={{ display: ents.length < 1 || RoomBaseParamsStore._EntityIds.length > 0 ? 'none' : "block" }}
>
<div className='ModuleBaseParams' style={{ display: this._ShowParams(ents) ? "block" : "none" }} >
<div className='CollapseOpenTitle'
onClick={() => { ModifyModelStore.isCollapseOpened = !ModifyModelStore.isCollapseOpened; }}
>

@ -0,0 +1,563 @@
import { Button, Classes, Icon, Intent } from "@blueprintjs/core";
import { observer } from "mobx-react";
import React, { Component } from "react";
import { Matrix4, Vector3 } from "three";
import { SelectExtrudeContour, selectOutlinePosition } from "../../../../Add-on/DrawBoard/DrawSpecialShapeBoardTool";
import { ViewChange } from "../../../../Add-on/ViewChange";
import { app } from "../../../../ApplicationServices/Application";
import { MaterialUrls } from "../../../../Common/HostUrl";
import { NormalMatrix } from "../../../../Common/Matrix4Utils";
import { DirectoryId, PostJson, RequestStatus } from "../../../../Common/Request";
import { MaterialIn } from "../../../../Common/SerializeMaterial";
import { DuplicateRecordCloning } from "../../../../Common/Status";
import { inflateBase64 } from "../../../../Common/inflate";
import { CADFiler } from "../../../../DatabaseServices/CADFiler";
import { Contour } from "../../../../DatabaseServices/Contour";
import { Database } from "../../../../DatabaseServices/Database";
import { Curve } from "../../../../DatabaseServices/Entity/Curve";
import { Polyline } from "../../../../DatabaseServices/Entity/Polyline";
import { ObjectId } from "../../../../DatabaseServices/ObjectId";
import { PhysicalMaterialRecord } from "../../../../DatabaseServices/PhysicalMaterialRecord";
import { BulkheadCeiling, BulkheadCeiling_ContourType } from "../../../../DatabaseServices/Room/Entity/Ceiling/BulkheadCeiling";
import { BulkheadCeilingShapeTemplateExtendData } from "../../../../DatabaseServices/Room/Entity/Ceiling/BulkheadCeilingShapeExtendData";
import { RoomFlatTop } from "../../../../DatabaseServices/Room/Entity/Flat/RoomFlatTop";
import { CommandWrap } from "../../../../Editor/CommandMachine";
import { JigUtils } from "../../../../Editor/JigUtils";
import { SelectBox, SelectType } from "../../../../Editor/SelectBox";
import { AsVector2, ZAxisN } from "../../../../Geometry/GeUtils";
import { BulkheadCeilingContourData, GetCeilingContourDate, HeadCeilingInfoStore, NONE_CONTOUR_ID } from "../../../Store/HeadCeilingStore/HeadCeilingInfoStore";
import { NONE_MATERIAL_ID } from "../../../Store/HeadCeilingStore/HeadCeilingMaterialStore";
import { BoardModalType } from "../../Board/BoardModalType";
import { LastExtractBoardContour } from "../../Board/LastExtractBoardContour";
import { DialogUserConfig } from "../../Board/UserConfigComponent";
import { HeadCeilingInfoConfigPanel } from "../../HeadCeiling/HeadCeilingInfoConfigPanel";
import { ModalPosition } from "../../Modal/ModalInterface";
import { AppToaster } from "../../Toaster";
import './ModifyModel.less';
interface RoomBaseParamsProps
{
roomFlatTopEnt?: RoomFlatTop;
headCeilingEnt?: BulkheadCeiling;
}
interface ContourData
{
polyline: Polyline;
database: Database;
extData: BulkheadCeilingShapeTemplateExtendData;
}
@observer
export default class RoomFlatTopParams extends Component<RoomBaseParamsProps, {}>
{
//提取天花板轮廓
_PickUpModelingOnFlatTop = async () =>
{
let roomFlatTop = this.props.roomFlatTopEnt;
//载入UI配置
let store = HeadCeilingInfoStore.GetInstance();
let config = new DialogUserConfig(store, BoardModalType.BulkheadCeilingContour);
await config.LoadAndInitConfig();
await CommandWrap(async () =>
{
app.Editor.SelectCtrl.Cancel();
app.Editor.ModalManage.ToggleShow();
app.Editor.MaskManage.Clear();
//视角转为俯视图
await new ViewChange(ZAxisN, true).exec();
app.Viewer.CameraCtrl.ZoomExtentsBox3(roomFlatTop.BoundingBox.expandByScalar(8000));
//获取天花板轮廓并放置
let cu = JigUtils.Draw(roomFlatTop.Contour.Clone().ApplyMatrix(roomFlatTop.OCSNoClone));
NormalMatrix(cu.OCSNoClone);
LastExtractBoardContour.Contour = cu;
cu.Erase(false);
let moveCus = [cu];
let isOk = await selectOutlinePosition(moveCus);
app.Editor.ModalManage.ToggleShow();
if (isOk)
{
//配置吊顶轮廓编辑时呼出的UI的Items
let date: BulkheadCeilingContourData[] = [];
date.push({
contourType: BulkheadCeiling_ContourType.Land,
contourId: NONE_CONTOUR_ID,
color: 1
});
await store.InitEditorContourItems(date);
cu.ColorIndex = date[0].color;
app.Editor.ModalManage.RenderModeless(
HeadCeilingInfoConfigPanel,
{
store,
isNotUpdateStore: true,
directoryId: DirectoryId.CeilingContour,
configType: BoardModalType.BulkheadCeilingContour,
noClose: true
},
{ canMinimize: false, position: ModalPosition.RightMid }
);
app.Editor.MaskManage.Clear();
app.Editor.ModalManage.MinAll();
}
}, "提取天花板轮廓");
};
//应用轮廓生成吊顶
_ApplayContour = async () =>
{
app.Editor.SelectCtrl.Cancel();
app.Editor.ModalManage.ToggleShow();
app.Editor.MaskManage.Clear();
//视角转为俯视图
if (!app.Viewer.CameraCtrl.Direction.equals(new Vector3(0, 0, -1)))
await new ViewChange(ZAxisN, false).exec();
AppToaster.show({
message: "请选择该天花板提取出来的外围轮廓!",
timeout: 5000,
intent: Intent.PRIMARY,
}, "ApplayContour");
await CommandWrap(async () =>
{
//获取选择的轮廓范围内曲线
let { contour, group, useCurves } = await SelectExtrudeContour(true, true, true);
AppToaster.dismiss("ApplayContour");
if (!contour) return;
if (!(useCurves[0] as Curve)?.IsClose)
{
AppToaster.show({
message: "选择的外围轮廓错误或没有闭合!",
timeout: 5000,
intent: Intent.DANGER,
}, "ApplayContour");
return;
}
let box = contour.BoundingBox.applyMatrix4(app.Viewer.DCS);
box.expandByVector(new Vector3(1, 1, 1));
let selectBox = new SelectBox(app.Viewer, AsVector2(box.min), AsVector2(box.max), SelectType.C);
for (let g of group)
{
for (let curve of g)
{
if (selectBox.CheckSelectTraverse(curve.GetDrawObjectFromRenderType()))
{
if (!curve.IsClose)
{
AppToaster.show({
message: "内部曲线必须全是闭合多段线!",
timeout: 5000,
intent: Intent.DANGER,
});
return;
}
}
}
}
let objChace: Map<number, ContourData> = new Map();
//载入UI配置
let contourStore = HeadCeilingInfoStore.GetInstance();
let config = new DialogUserConfig(contourStore, BoardModalType.BulkheadCeilingContour);
await config.LoadAndInitConfig();
let bulkheadCeiling = new BulkheadCeiling();
let matrix = new Matrix4().setPosition(this.props.roomFlatTopEnt.BoundingBox.min.sub(useCurves[0].BoundingBox.min));
matrix.multiply(new Matrix4().setPosition(0, 0, -bulkheadCeiling.Height));
useCurves[0].ApplyMatrix(matrix);
//先根据最外圈曲线绘制吊顶
await this._BuildBulkheadCeiling(contourStore, useCurves[0] as Curve, objChace, bulkheadCeiling);
for (let c of useCurves)
c.Erase();
//再根据内部曲线绘制吊顶
for (let g of group)
{
for (let curve of g)
{
if (selectBox.CheckSelectTraverse(curve.GetDrawObjectFromRenderType()))
{
curve.ApplyMatrix(matrix);
await this._BuildBulkheadCeiling(contourStore, curve, objChace, bulkheadCeiling);
curve.Erase();
break;
}
}
}
//吊顶绑定天花板
bulkheadCeiling.RelativeRoomFlatTop = this.props.roomFlatTopEnt.objectId;
app.Database.ModelSpace.Append(bulkheadCeiling);
}, "绘制吊顶");
app.Editor.ModalManage.ToggleShow();
app.Editor.ModalManage.DestoryAll();
};
/**
* 线
*
* @param {HeadCeilingInfoStore} contourStore
* @param {Curve} curve
* @param {Map<number, ContourData>} objChace ID-ContourData
* @param {BulkheadCeiling} bulkheadCeiling
* @memberof RoomFlatTopParams
*/
async _BuildBulkheadCeiling(contourStore: HeadCeilingInfoStore, curve: Curve, objChace: Map<number, ContourData>, bulkheadCeiling: BulkheadCeiling)
{
let dbData: ContourData = {
polyline: undefined,
database: undefined,
extData: undefined
};
let materials: number[] = [];//服务端的mtl_id
let shapeMaterialSlotData: number[] = []; //轮廓顺序对应的材质序列
let contourShapeId = contourStore.m_Option.Item.find(item => item.color === curve.ColorIndex && item.example.id !== NONE_CONTOUR_ID)?.example.id;
//该曲线有使用吊顶截面轮廓
if (contourShapeId)
{
if (!objChace.has(contourShapeId))
{
//获取截面轮廓信息
let db = new Database(false, false, true);
let contourData = await GetCeilingContourDate(contourShapeId.toString());
if (!contourData) contourShapeId = undefined;
else
{
let json = inflateBase64(contourData.toplines.file);
let f = new CADFiler(JSON.parse(json));
db.FileRead(f);
dbData.polyline = Contour.Combine(db.ModelSpace.Entitys as Curve[], true) as Polyline;
if (dbData.polyline.Area2 > 0)
dbData.polyline.Reverse();
let tempF = new CADFiler(db.ExtendedData);
let extData = new BulkheadCeilingShapeTemplateExtendData;
extData.ReadFile(tempF);
dbData.database = db;
dbData.extData = extData;
objChace.set(contourShapeId, dbData);
}
}
else
dbData = objChace.get(contourShapeId);
//收集材质id
if (dbData.extData)
for (let [color, mtlId] of dbData.extData.Color_MaterialId)
materials.push(mtlId);
//材质槽分析
if (contourShapeId) // 该轮廓已从 轮廓管理中 被删除 continue
{
// for (let e of dbData.database.ModelSpace.Entitys)
// TestDraw(e.Clone());
for (let i = 0; i < dbData.polyline.EndParam; i++)
{
let param = i + 0.5;
let p = dbData.polyline.GetPointAtParam(param);
// let c = new Circle(p, 5);
// TestDraw(c, i + 1);
for (let ent of dbData.database.ModelSpace.Entitys)
{
if (ent instanceof Curve && ent.PtOnCurve(p))
{
let color = ent.ColorIndex;
let materialId = dbData.extData.Color_MaterialId.get(color);
if (materialId)
shapeMaterialSlotData.push(materials.findIndex((mtlId) => mtlId === materialId));
else
shapeMaterialSlotData.push(0);
break;
}
}
}
}
}
//将材质载入到当前图纸
let clonedMtls: ObjectId<PhysicalMaterialRecord>[] = [];
for (let mtlid of materials)
{
if (mtlid === NONE_MATERIAL_ID)
{
clonedMtls.push(undefined);
continue;
}
let mtlData = await PostJson(MaterialUrls.detail, { material_id: mtlid }, false);
if (mtlData.err_code !== RequestStatus.Ok)
{
AppToaster.show({
message: "截面轮廓内设置的材质已被删除或丢失,请检查!",
timeout: 3000,
intent: Intent.DANGER,
}, "mtl_err");
clonedMtls.push(undefined);
continue;
}
let material = MaterialIn(JSON.parse(inflateBase64(mtlData.materials.file)) as Object[]) as PhysicalMaterialRecord;
material.Name = mtlData.materials.name;
app.Database.WblockCloneObejcts(
[material],
app.Database.MaterialTable,
new Map(),
DuplicateRecordCloning.Ignore
);
clonedMtls.push(app.Database.MaterialTable.GetAt(material.Name).Id);
}
let example = contourStore.m_Option.Item.find((item) => curve.ColorIndex === item.color).example;
bulkheadCeiling.ContourData.push({
ContourType: example.contourType,
Path: curve.Clone(),
SweepShapeTempalteId: undefined,
Contour: dbData.polyline,
ContourId: example.id,
Materials: clonedMtls,
ShapeMaterialSlotData: shapeMaterialSlotData
});
}
//提取吊顶轮廓
_PickUpModelingOnHeadCeiling = async () =>
{
let bulkheadCeiling = this.props.headCeilingEnt;
//载入UI配置
let store = HeadCeilingInfoStore.GetInstance();
let config = new DialogUserConfig(store, BoardModalType.BulkheadCeilingContour);
await config.LoadAndInitConfig();
await CommandWrap(async () =>
{
app.Editor.SelectCtrl.Cancel();
app.Editor.ModalManage.ToggleShow();
app.Editor.MaskManage.Clear();
//视角使用俯视图
await new ViewChange(ZAxisN, true).exec();
app.Viewer.CameraCtrl.ZoomExtentsBox3(bulkheadCeiling.BoundingBox.expandByScalar(8000));
let cu: Polyline;
let paths: Curve[] = [];
let firstPath = bulkheadCeiling.ContourData[0].Path;
//获取吊顶轮廓信息
for (let data of bulkheadCeiling.ContourData)
{
let path = JigUtils.Draw(data.Path.Clone().ApplyMatrix(bulkheadCeiling.OCSNoClone));
paths.push(path);
}
let roomFlatTop = bulkheadCeiling.RelativeRoomFlatTop.Object as RoomFlatTop;
//天花板被拉伸,且面积大于吊顶最大轮廓 补填一个最外围的天花板轮廓
if (roomFlatTop.Area > firstPath.Area)
{
let contuor = roomFlatTop.Contour.Clone();
let mtl = new Matrix4().setPosition(roomFlatTop.BoundingBox.min.sub(contuor.BoundingBox.min));
mtl.multiply(new Matrix4().setPosition(0, 0, -bulkheadCeiling.Height));
cu = JigUtils.Draw(contuor.ApplyMatrix(mtl));
paths.push(cu);
}
let isOk = await selectOutlinePosition(paths);
app.Editor.ModalManage.ToggleShow();
if (isOk)
{
//根据吊顶轮廓信息配置Store
let data: BulkheadCeilingContourData[] = [];
for (let contourData of bulkheadCeiling.ContourData)
{
data.push({
contourType: contourData.ContourType,
contourId: contourData.ContourId,
color: 0
});
}
if (cu)
{
data.push({
contourType: BulkheadCeiling_ContourType.Hole,
contourId: NONE_CONTOUR_ID,
color: 0
});
}
await store.InitEditorContourItems(data);
for (let i = 0; i < paths.length; i++)
paths[i].ColorIndex = data[i].color;
app.Editor.ModalManage.RenderModeless(
HeadCeilingInfoConfigPanel,
{
store,
isNotUpdateStore: true,
directoryId: DirectoryId.CeilingContour,
configType: BoardModalType.BulkheadCeilingContour,
noClose: true
},
{ canMinimize: false, position: ModalPosition.RightMid }
);
app.Editor.MaskManage.Clear();
app.Editor.ModalManage.MinAll();
}
}, "提取吊顶轮廓");
};
//已有的吊顶使用新轮廓,重新生成新吊顶
_RedoContour = async () =>
{
app.Editor.SelectCtrl.Cancel();
app.Editor.ModalManage.ToggleShow();
app.Editor.MaskManage.Clear();
if (!app.Viewer.CameraCtrl.Direction.equals(new Vector3(0, 0, -1)))
await new ViewChange(ZAxisN, false).exec();
AppToaster.show({
message: "请选择该吊顶提取出来的外围轮廓!",
timeout: 5000,
intent: Intent.PRIMARY,
}, "RedoContour");
await CommandWrap(async () =>
{
//获取应用的轮廓
let { contour, group, useCurves } = await SelectExtrudeContour(true, true, true);
AppToaster.dismiss("RedoContour");
if (!contour) return;
if (!(useCurves[0] as Curve)?.IsClose)
{
AppToaster.show({
message: "选择的外围轮廓错误或没有闭合!",
timeout: 5000,
intent: Intent.DANGER,
}, "ApplayContour");
return;
}
let box = contour.BoundingBox.applyMatrix4(app.Viewer.DCS);
box.expandByVector(new Vector3(1, 1, 1));
let selectBox = new SelectBox(app.Viewer, AsVector2(box.min), AsVector2(box.max), SelectType.C);
for (let g of group)
{
for (let curve of g)
{
if (selectBox.CheckSelectTraverse(curve.GetDrawObjectFromRenderType()))
{
if (!curve.IsClose)
{
AppToaster.show({
message: "内部曲线必须全是闭合多段线!",
timeout: 5000,
intent: Intent.DANGER,
});
return;
}
}
}
}
//载入UI配置
let contourStore = HeadCeilingInfoStore.GetInstance();
let objChace: Map<number, ContourData> = new Map();
let config = new DialogUserConfig(contourStore, BoardModalType.BulkheadCeilingContour);
await config.LoadAndInitConfig();
let bulkheadCeiling = this.props.headCeilingEnt;
let roomFlatTop = bulkheadCeiling.RelativeRoomFlatTop.Object as RoomFlatTop;
bulkheadCeiling.ContourData = [];
let matrix = bulkheadCeiling.OCSInv;
matrix.multiply(new Matrix4().setPosition(roomFlatTop.BoundingBox.min.sub(useCurves[0].BoundingBox.min)));
matrix.multiply(new Matrix4().setPosition(0, 0, -bulkheadCeiling.Height));
useCurves[0].ApplyMatrix(matrix);
//先根据最外圈曲线绘制吊顶
await this._BuildBulkheadCeiling(contourStore, useCurves[0] as Curve, objChace, bulkheadCeiling);
for (let c of useCurves)
c.Erase();
//再根据内部曲线绘制吊顶
for (let g of group)
{
for (let curve of g)
{
if (selectBox.CheckSelectTraverse(curve.GetDrawObjectFromRenderType()))
{
curve.ApplyMatrix(matrix);
await this._BuildBulkheadCeiling(contourStore, curve, objChace, bulkheadCeiling);
curve.Erase();
break;
}
}
}
bulkheadCeiling.Update();
}, "重做吊顶");
app.Editor.ModalManage.ToggleShow();
app.Editor.ModalManage.DestoryAll();
};
render()
{
return (
<div id="OperRoomFlatTop" className={Classes.DIALOG_CONTAINER}>
<div className={Classes.DIALOG}>
<div className={Classes.DIALOG_HEADER} data-id="dragArea">
<Icon icon="exchange" iconSize={18} />
<h4 className={Classes.HEADING}></h4>
<Button
icon="cross"
aria-label="Close"
minimal={true}
onClick={() => { app.Editor.ModalManage.Destory(); }}
/>
</div>
<div>
<div className={Classes.DIALOG_BODY}>
<Button
text="轮廓提取"
onClick={this.props.headCeilingEnt ? this._PickUpModelingOnHeadCeiling : this._PickUpModelingOnFlatTop}
/>
<Button
text="轮廓应用"
onClick={this.props.headCeilingEnt ? this._RedoContour : this._ApplayContour}
/>
</div>
</div>
</div>
</div >
);
}
}

@ -36,6 +36,12 @@ export class RoomDesignPanel extends React.Component<{ store: ToolsBlockStore; }
execFun={execFun}
btnTitile="一键画窗"
/>
<ToolsBlock
blockId={"roomDesign"}
list={iconList.roomFlatTop}
execFun={execFun}
btnTitile="画吊顶"
/>
</div>
);
}

@ -27,6 +27,7 @@ export class ToolsBlockStore extends Singleton
roomHole: [],
roomWindow: [],
oneKeyDrawDoorWindow: [],
roomFlatTop: [],
};
//记录每个tab面板的工具块id和图标总数
blocksData: TopToolBarBlockData =

@ -219,6 +219,10 @@ export class TopToolBar extends React.Component<{}, {}>
{ svg: IconEnum.DrawPWindow, title: "飘窗", command: CommandNames.PC },
{ svg: IconEnum.DrawLPWindow, title: "转角飘窗", command: CommandNames.ZJPC },
];
store.iconList.roomFlatTop = [
{ svg: IconEnum.HeadCeilingContour, title: "吊顶轮廓管理", command: CommandNames.HeadCeilingContour },
{ svg: IconEnum.HeadCeilingInfoConfigPanel, title: "轮廓对照表", command: CommandNames.HeadCeilingInfoConfigPanel }
];
store.calcIconNumInTabs();
let topStore = DownPanelStore.GetInstance();

@ -27,6 +27,7 @@ export interface ToolsBlockOption
roomHole: ICommandIconInfo[];
roomWindow: ICommandIconInfo[];
oneKeyDrawDoorWindow: ICommandIconInfo[];
roomFlatTop: ICommandIconInfo[];
}
export interface TopToolBarBlockDataItem
{

@ -285,4 +285,10 @@
background-color: @verticalTab_selected_bg;
}
}
//吊顶材质面板
#BulkheadCeilingConfig {
.bp3-dialog-body {
background-color: @fileName_bg;
}
}
}

@ -229,4 +229,7 @@ export enum IconEnum
CurBoardMtl = "curBoardMtl.svg", //当前默认板材质图标
CurWallMtl = "curWallMtl.svg", //当前默认墙材质图标
CurFloorMtl = "curFloorMtl.svg",//当前默认
HeadCeilingContour = "HeadCeilingContour.svg",//吊顶轮廓管理
HeadCeilingMaterialPanel = "HeadCeilingMaterialPanel.svg",//吊顶截面轮廓材质面板
HeadCeilingInfoConfigPanel = "HeadCeilingInfoConfigPanel.svg",//吊顶轮廓面板
}

@ -0,0 +1,196 @@
import { Intent } from "@blueprintjs/core";
import { observable } from "mobx";
import { ToplineUrls } from "../../../Common/HostUrl";
import { IResponseData, PostJson, RequestStatus } from "../../../Common/Request";
import { CADFiler } from "../../../DatabaseServices/CADFiler";
import { BulkheadCeiling_ContourType } from "../../../DatabaseServices/Room/Entity/Ceiling/BulkheadCeiling";
import { AppToaster } from "../../Components/Toaster";
import { IConfigStore } from "../BoardStore";
import { BulkheadCeilingOption } from "../OptionInterface/BulkheadCeilingOption";
export const NONE_CONTOUR_ID = -1;
export interface BulkheadCeilingContourData
{
contourType: BulkheadCeiling_ContourType;
contourId: number;
color: number;
}
export class HeadCeilingInfoStore implements IConfigStore
{
@observable configName = "默认";
@observable configsNames: string[] = [];
@observable m_Option: BulkheadCeilingOption = { Item: [] };
InitOption()
{
this.InitModelingItems();
}
InitModelingItems()
{
this.m_Option.Item = [];
for (let i = 0; i < 15; i++)
{
{
this.m_Option.Item.push(
{
color: i + 1,
example: {
contourType: BulkheadCeiling_ContourType.Land, //默认是岛
id: NONE_CONTOUR_ID,
name: "",
logo: ""
}
},
);
}
}
}
/**
* UIItems
*
* NONE_CONTOUR_ID 0便
*/
async InitEditorContourItems(extendData: BulkheadCeilingContourData[])
{
let index = 0;
if (extendData.length > 0)
for (let data of extendData)
{
//标记下没有材质的轮廓
if (data.contourId === NONE_CONTOUR_ID) data.contourId = 0; //因为 等于NONE_CONTOUR_ID 的话 会被存在轮廓覆盖
//item中是否包含这个材质
let item = this.m_Option.Item.find(e => (
e.example.id === data.contourId &&
e.example.contourType === data.contourType
));
//有就不添加
if (item)
{
//设置对应表 data[0]是curve的颜色 {视图中curve改成item.color的颜色 调用extendData.Color_MaterialId.get(data[0])}
data.color = item.color;
continue;
}
//没有包含这个材质 添加
let contourData: IResponseData;
//不是空材质就获取材质信息
if (data.contourId !== 0)
contourData = await GetCeilingContourDate(data.contourId.toString());
//store的15个item 都有材质值了 往后添加第16 17....
if (index > this.m_Option.Item.length)
{
this.m_Option.Item.push(
{
color: index + 1,
example: {
contourType: data.contourType,
id: data.contourId,
name: contourData?.toplines?.name ?? "无",
logo: contourData?.toplines?.logo ?? "",
}
},
);
data.color = this.m_Option.Item[index].color;
index++;
}
else
{
for (; index < this.m_Option.Item.length; index++)
{
let example = this.m_Option.Item[index].example;
//没有材质的Item用上
if (example.id === NONE_CONTOUR_ID)
{
example.id = data.contourId;
example.contourType = data.contourType;
example.name = contourData?.toplines?.name ?? "无";
example.logo = contourData?.toplines?.logo ?? "";
data.color = this.m_Option.Item[index].color;
index++;
break;
}
}
}
}
}
SaveConfig()
{
return this.WriteFile() as any;
};
UpdateOption(confData: any[] | any)
{
if (!Array.isArray(confData))
confData = confData.CADFilerData as any[];
let file = new CADFiler(confData);
this.ReadFile(file);
}
ReadFile = (file: CADFiler) =>
{
let ver = file.Read();
let count = file.Read();
this.m_Option.Item = [];
for (let i = 0; i < count; i++)
{
this.m_Option.Item.push(
{
color: file.Read(),
example: {
contourType: file.Read(),
id: file.Read(),
name: file.Read(),
logo: file.Read(),
}
}
);
}
};
WriteFile(file: CADFiler = new CADFiler)
{
file.Write(1);
file.Write(this.m_Option.Item.length);
for (let item of this.m_Option.Item)
{
file.Write(item.color);
file.Write(item.example.contourType);
file.Write(item.example.id);
file.Write(item.example.name);
file.Write(item.example.logo);
}
return file.Data;
}
private static _SingleInstance: HeadCeilingInfoStore;
static GetInstance(): HeadCeilingInfoStore
{
if (this._SingleInstance) return this._SingleInstance;
this._SingleInstance = new HeadCeilingInfoStore;
return this._SingleInstance;
}
}
export async function GetCeilingContourDate(contour_id: string): Promise<IResponseData | undefined>
{
let data = await PostJson(ToplineUrls.detail, { topline_id: contour_id });
if (data.err_code === RequestStatus.Ok)
return data;
AppToaster.show({
message: "未找到该吊顶截面轮廓,可能已被删除!",
timeout: 5000,
intent: Intent.DANGER,
});
return undefined;
};

@ -0,0 +1,189 @@
import { observable } from "mobx";
import { MaterialUrls } from "../../../Common/HostUrl";
import { PostJson, RequestStatus } from "../../../Common/Request";
import { MaterialIn } from "../../../Common/SerializeMaterial";
import { inflateBase64 } from "../../../Common/inflate";
import { CADFiler } from "../../../DatabaseServices/CADFiler";
import { PhysicalMaterialRecord } from "../../../DatabaseServices/PhysicalMaterialRecord";
import { BulkheadCeilingShapeTemplateExtendData } from "../../../DatabaseServices/Room/Entity/Ceiling/BulkheadCeilingShapeExtendData";
import { IConfigStore } from "../BoardStore";
import { BulkheadCeilingProfileMaterialOption } from "../OptionInterface/BulkheadCeilingOption";
//空材质ID使用-1
export const NONE_MATERIAL_ID = -1;
export interface ICeilingProfileMaterialItem
{
readonly color: number;
example: {
id: number;
name: string,
type: string,
};
}
export class HeadCeilingProfileMaterialStore implements IConfigStore
{
@observable configName = "默认";
@observable configsNames: string[] = [];
@observable m_Option: BulkheadCeilingProfileMaterialOption = { Item: [] };
InitOption()
{
this.InitModelingItems();
}
InitModelingItems()
{
this.m_Option.Item = [];
for (let i = 0; i < 15; i++)
{
{
this.m_Option.Item.push(
{
color: i + 1,
example: {
id: NONE_MATERIAL_ID,
name: "",
type: "",
}
},
);
}
}
}
/**
* UIItems
*
* NONE_MATERIAL_ID 0便
*/
async InitEditorMaterialItems(extendData: BulkheadCeilingShapeTemplateExtendData)
{
let index = 0;
if (extendData.Color_MaterialId.size)
for (let data of extendData.Color_MaterialId)
{
//标记下空材质的data id
if (data[1] === NONE_MATERIAL_ID) data[1] = 0;
//item中是否包含这个材质
let item = this.m_Option.Item.find(e => e.example.id === data[1]);
//有就不添加
if (item)
{
//设置对应表 data[0]是curve的颜色 {视图中curve改成item.color的颜色 调用extendData.Color_MaterialId.get(data[0])}
extendData.Color_MaterialId.set(data[0], item.color);
continue;
}
//没有包含这个材质 添加
let material: PhysicalMaterialRecord;
//不是空材质就获取材质信息
if (data[1] !== 0)
{
let mtlData = await PostJson(MaterialUrls.detail, { material_id: data[1].toString() });
if (mtlData.err_code !== RequestStatus.Ok)
{
//材质不可用 设置为空材质
data[1] = 0;
}
else
{
material = MaterialIn(JSON.parse(inflateBase64(mtlData.materials.file)) as Object[]) as PhysicalMaterialRecord;
material.Name = mtlData.materials.name;
}
}
//store的15个item 都有材质值了 往后添加第16 17....
if (index > this.m_Option.Item.length)
{
this.m_Option.Item.push(
{
color: index + 1,
example: {
id: data[1],
name: material?.Name ?? "无",
type: material?.type ?? "无"
}
},
);
extendData.Color_MaterialId.set(data[0], this.m_Option.Item[index].color);
}
else
{
for (; index < this.m_Option.Item.length; index++)
{
let contour = this.m_Option.Item[index].example;
//没有材质的Item用上
if (this.m_Option.Item[index].example.id === NONE_MATERIAL_ID)
{
contour.id = data[1];
contour.name = material?.Name ?? "无";
contour.type = material?.type ?? "无";
extendData.Color_MaterialId.set(data[0], this.m_Option.Item[index].color);
index++;
break;
}
}
}
}
}
SaveConfig()
{
return this.WriteFile() as any;
};
UpdateOption(confData: any[] | any)
{
if (!Array.isArray(confData))
confData = confData.CADFilerData as any[];
let file = new CADFiler(confData);
this.ReadFile(file);
}
ReadFile = (file: CADFiler) =>
{
let ver = file.Read();
let count = file.Read();
this.m_Option.Item = [];
for (let i = 0; i < count; i++)
{
this.m_Option.Item.push(
{
color: file.Read(),
example: {
id: file.Read(),
name: file.Read(),
type: file.Read(),
}
}
);
}
};
WriteFile(file: CADFiler = new CADFiler)
{
file.Write(1);
file.Write(this.m_Option.Item.length);
for (let item of this.m_Option.Item)
{
file.Write(item.color);
file.Write(item.example.id);
file.Write(item.example.name);
file.Write(item.example.type);
}
return file.Data;
}
private static _SingleInstance: HeadCeilingProfileMaterialStore;
static GetInstance(): HeadCeilingProfileMaterialStore
{
if (this._SingleInstance) return this._SingleInstance;
this._SingleInstance = new HeadCeilingProfileMaterialStore;
return this._SingleInstance;
}
}

@ -0,0 +1,33 @@
import { BulkheadCeiling_ContourType } from "../../../DatabaseServices/Room/Entity/Ceiling/BulkheadCeiling";
import { IBaseOption } from "./IOptionInterface";
export interface ICeilingProfileMaterialItem
{
readonly color: number;
example: {
id: number;
name: string,
type: string,
};
}
export interface ICeilingItem
{
readonly color: number;
example: {
contourType: BulkheadCeiling_ContourType;//洞 还是岛
id: number; //吊顶轮廓id
name: string;
logo: string;
};
}
export interface BulkheadCeilingOption extends IBaseOption
{
Item: ICeilingItem[];
}
export interface BulkheadCeilingProfileMaterialOption extends IBaseOption
{
Item: ICeilingProfileMaterialItem[];
}
Loading…
Cancel
Save