mirror of https://gitee.com/cf-fz/WebCAD.git
!2773 功能: 图层
parent
d49806f684
commit
70fd7372a0
@ -0,0 +1,79 @@
|
||||
|
||||
//#region 避免无法进入测试 依赖错误导致的测试失败 ref file.test.ts
|
||||
import { HostApplicationServices } from '../../src/ApplicationServices/HostApplicationServices';
|
||||
import { DuplicateRecordCloning } from '../../src/Common/Status';
|
||||
import { AllObjectData } from '../../src/DatabaseServices/AllObjectData';
|
||||
AllObjectData;
|
||||
//#endregion
|
||||
|
||||
import { Database } from '../../src/DatabaseServices/Database';
|
||||
import { Entity } from '../../src/DatabaseServices/Entity/Entity';
|
||||
import { Line } from '../../src/DatabaseServices/Entity/Line';
|
||||
import { LayerTableRecord } from '../../src/DatabaseServices/LayerTableRecord';
|
||||
|
||||
|
||||
/**
|
||||
* 1.新绘制的对象必须在当前层上
|
||||
* 2.能够被序列化
|
||||
*/
|
||||
test('绘制的对象默认图层', () =>
|
||||
{
|
||||
let db = new Database(true, true, true);
|
||||
|
||||
let line = new Line;
|
||||
|
||||
db.ModelSpace.Append(line);
|
||||
|
||||
expect(line.Layer === db.DefaultLayer.Id).toBeTruthy();//图层等于默认图层
|
||||
|
||||
let layer = new LayerTableRecord();
|
||||
layer.Name = "新图层1";
|
||||
db.LayerTable.Add(layer);
|
||||
|
||||
db.LayerTable.Current = layer.Id;
|
||||
|
||||
line = new Line;
|
||||
db.ModelSpace.Append(line);
|
||||
expect(line.Layer === layer.Id).toBeTruthy();//图层应该等于当前图层
|
||||
|
||||
let id = line.Id.Index;
|
||||
|
||||
let db2 = new Database(false, false, true);
|
||||
db2.FileRead(db.FileWrite());
|
||||
|
||||
let idnew = db2.GetObjectId(id);
|
||||
|
||||
(idnew.Object as Entity).Layer.Index;//?
|
||||
|
||||
expect((idnew.Object as Entity).Layer.Object.Name).toBe(layer.Name);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* 1.对于模块内的实体,由于之前没有图层,新绘入后,需要设置到当前图层
|
||||
*/
|
||||
test('模版文件的图层', () =>
|
||||
{
|
||||
let db1 = new Database(true, true, true);
|
||||
HostApplicationServices.Database = db1;
|
||||
|
||||
let db2 = new Database(false, false, true);
|
||||
|
||||
let line = new Line;
|
||||
db2.ModelSpace.Append(line);
|
||||
line.Layer;
|
||||
line.HasLayer;//?
|
||||
|
||||
db1.WblockCloneObejcts([line], db1.ModelSpace, new Map, DuplicateRecordCloning.Ignore);
|
||||
|
||||
db1.ModelSpace.Entitys[0].Layer.Object.Name;//?
|
||||
|
||||
let layer = new LayerTableRecord();
|
||||
layer.Name = "新图层1";
|
||||
db1.LayerTable.Add(layer);
|
||||
db1.LayerTable.Current = layer.Id;
|
||||
|
||||
db1.WblockCloneObejcts([line], db1.ModelSpace, new Map, DuplicateRecordCloning.Ignore);
|
||||
|
||||
expect(db1.ModelSpace.Entitys[1].Layer.Object.Name).toBe(layer.Name);
|
||||
});
|
@ -0,0 +1,58 @@
|
||||
import { AutoRecord } from "./AutoRecord";
|
||||
import { Factory } from "./CADFactory";
|
||||
import { CADFiler } from "./CADFiler";
|
||||
import { CADObject } from "./CADObject";
|
||||
import { SymbolTableRecord } from "./SymbolTableRecord";
|
||||
|
||||
@Factory
|
||||
export class LayerTableRecord extends SymbolTableRecord
|
||||
{
|
||||
@AutoRecord ColorIndex: number = 7;
|
||||
|
||||
// @AutoRecord LineWeight: number = 0;
|
||||
|
||||
@AutoRecord IsOff = false;//关
|
||||
|
||||
@AutoRecord IsLocked = false;//锁定
|
||||
|
||||
// @AutoRecord IsFrozen = false;//冻结
|
||||
|
||||
// @AutoRecord IsHidden = false;//隐藏
|
||||
|
||||
// @AutoRecord Description = "";//描述
|
||||
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
//#region -------------------------File-------------------------
|
||||
//对象从文件中读取数据,初始化自身
|
||||
override ReadFile(file: CADFiler)
|
||||
{
|
||||
let ver = file.Read();
|
||||
super.ReadFile(file);
|
||||
|
||||
this.ColorIndex = file.Read();
|
||||
this.IsOff = file.Read();
|
||||
this.IsLocked = file.Read();
|
||||
}
|
||||
|
||||
//对象将自身数据写入到文件.
|
||||
override WriteFile(file: CADFiler)
|
||||
{
|
||||
file.Write(1);
|
||||
super.WriteFile(file);
|
||||
|
||||
file.Write(this.ColorIndex);
|
||||
file.Write(this.IsOff);
|
||||
file.Write(this.IsLocked);
|
||||
}
|
||||
|
||||
//局部撤销
|
||||
override ApplyPartialUndo(undoData: CADObject)
|
||||
{
|
||||
super.ApplyPartialUndo(undoData);
|
||||
}
|
||||
//#endregion
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
import { HostApplicationServices } from "../../ApplicationServices/HostApplicationServices";
|
||||
import { Factory } from "../CADFactory";
|
||||
import { CADFiler } from "../CADFiler";
|
||||
import { CADObject } from "../CADObject";
|
||||
import { HistorycRecord } from "../HistorycRecord";
|
||||
import { ISerialize } from "../ISerialize";
|
||||
import { LayerTableRecord } from "../LayerTableRecord";
|
||||
import { ObjectId } from "../ObjectId";
|
||||
import { SymbolTable } from "./SymbolTable";
|
||||
|
||||
@Factory
|
||||
export class LayerTable extends SymbolTable
|
||||
{
|
||||
protected _Current: ObjectId<LayerTableRecord>;//默认当前图层
|
||||
|
||||
public get Current(): ObjectId<LayerTableRecord>
|
||||
{
|
||||
return this._Current;
|
||||
}
|
||||
|
||||
public set Current(id: ObjectId<LayerTableRecord>)//设置当前层
|
||||
{
|
||||
if (this._Current === id) return;
|
||||
|
||||
let undoData = this.UndoRecord();
|
||||
if (undoData)
|
||||
{
|
||||
let hr = new HistorycRecord();
|
||||
hr.undoData = new ObjectIdData(this._Current);
|
||||
hr.redoData = new ObjectIdData(id);
|
||||
undoData.WriteObjectHistoryPath(this, hr);
|
||||
}
|
||||
|
||||
this._Current = id;
|
||||
HostApplicationServices.CurrentLayer = id;
|
||||
}
|
||||
|
||||
/** 用于描述图层结构的根节点 */
|
||||
private _Root = new LayerNode("根节点", true);
|
||||
|
||||
public get Root(): LayerNode
|
||||
{
|
||||
return this._Root;
|
||||
}
|
||||
|
||||
public set Root(root: LayerNode)
|
||||
{
|
||||
if (this._Root === root) return;
|
||||
|
||||
let undoData = this.UndoRecord();
|
||||
if (undoData)
|
||||
{
|
||||
let hr = new HistorycRecord();
|
||||
hr.undoData = new ObjectTreeData(this._Root);
|
||||
hr.redoData = new ObjectTreeData(root);
|
||||
undoData.WriteObjectHistoryPath(this, hr);
|
||||
}
|
||||
|
||||
this._Root = root;
|
||||
}
|
||||
|
||||
//#region -------------------------File-------------------------
|
||||
//对象从文件中读取数据,初始化自身
|
||||
override ReadFile(file: CADFiler)
|
||||
{
|
||||
let ver = file.Read();
|
||||
super.ReadFile(file);
|
||||
|
||||
this._Current = file.ReadObjectId() as ObjectId<LayerTableRecord>;
|
||||
this._Root = file.ReadObject();
|
||||
}
|
||||
//对象将自身数据写入到文件.
|
||||
override WriteFile(file: CADFiler)
|
||||
{
|
||||
file.Write(1);
|
||||
super.WriteFile(file);
|
||||
|
||||
file.WriteObjectId(this._Current);
|
||||
file.WriteObject(this._Root);
|
||||
}
|
||||
//局部撤销
|
||||
override ApplyPartialUndo(undoData: CADObject)
|
||||
{
|
||||
if (undoData instanceof ObjectIdData)
|
||||
{
|
||||
this._Current = undoData.objectId as any;
|
||||
HostApplicationServices.CurrentLayer = this._Current;
|
||||
}
|
||||
else if (undoData instanceof ObjectTreeData)
|
||||
this._Root = undoData.data;
|
||||
else
|
||||
super.ApplyPartialUndo(undoData);
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
|
||||
/** 用于描述图层结构的节点 */
|
||||
@Factory
|
||||
export class LayerNode implements ISerialize
|
||||
{
|
||||
children: LayerNode[];
|
||||
|
||||
constructor(public name: string, isHasChildren = false)
|
||||
{
|
||||
if (isHasChildren)
|
||||
this.children = [];
|
||||
}
|
||||
|
||||
ReadFile(f: CADFiler)
|
||||
{
|
||||
this.name = f.ReadString();
|
||||
const count = f.Read();
|
||||
if (count !== -1)
|
||||
{
|
||||
this.children = [];
|
||||
for (let i = 0; i < count; i++)
|
||||
{
|
||||
const child = new LayerNode("");
|
||||
child.ReadFile(f);
|
||||
this.children.push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WriteFile(f: CADFiler)
|
||||
{
|
||||
f.WriteString(this.name);
|
||||
if (!this.children)
|
||||
f.Write(-1);
|
||||
else
|
||||
{
|
||||
f.Write(this.children.length);
|
||||
for (const child of this.children)
|
||||
child.WriteFile(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录当前id的序列化数据
|
||||
*/
|
||||
@Factory
|
||||
export class ObjectIdData implements ISerialize
|
||||
{
|
||||
ReadFile(file: CADFiler): this
|
||||
{
|
||||
this.objectId = file.ReadObjectId();
|
||||
return this;
|
||||
}
|
||||
WriteFile(file: CADFiler): this
|
||||
{
|
||||
file.WriteObjectId(this.objectId);
|
||||
return this;
|
||||
}
|
||||
constructor(public objectId: ObjectId) { }
|
||||
}
|
||||
|
||||
/** 记录树状结构的序列化数据*/
|
||||
@Factory
|
||||
export class ObjectTreeData implements ISerialize
|
||||
{
|
||||
ReadFile(file: CADFiler): this
|
||||
{
|
||||
this.data = file.ReadObject();
|
||||
return this;
|
||||
}
|
||||
WriteFile(file: CADFiler): this
|
||||
{
|
||||
file.WriteObject(this.data);
|
||||
return this;
|
||||
}
|
||||
constructor(public data: LayerNode) { }
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
import { end } from "xaop";
|
||||
import { app } from "../ApplicationServices/Application";
|
||||
import { CommandNames } from "../Common/CommandNames";
|
||||
import { CommandHistoryRecord } from "../DatabaseServices/CommandHistoryRecord";
|
||||
import { LayerTableRecord } from "../DatabaseServices/LayerTableRecord";
|
||||
import { LayerTable } from "../DatabaseServices/Tables/LayerTable";
|
||||
import { LayerTopStore } from "../UI/Components/ToolBar/Layer/LayerPanel";
|
||||
import { LayerStore } from "../UI/Components/ToolBar/Layer/State/Store";
|
||||
import { DataToViewForLayer } from "../UI/Components/ToolBar/Layer/State/Transform";
|
||||
|
||||
|
||||
/** 图层反应器 */
|
||||
export class LayerReactor
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
//解析撤销重做
|
||||
const ParseFunction = (isUndo = true) =>
|
||||
{
|
||||
return (cmdName: string, historyRec: CommandHistoryRecord) =>
|
||||
{
|
||||
if (!app.Database.LayerTable || !app.Database.LayerTable.Current) return;
|
||||
// 图层UI随图层表更新
|
||||
for (const [k, v] of historyRec.HistoryList)
|
||||
{
|
||||
const o = k.Object;
|
||||
if (o instanceof LayerTableRecord || o instanceof LayerTable)
|
||||
{
|
||||
DataToViewForLayer.UpdateAll();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const ParseUndo = ParseFunction(true);
|
||||
const ParseRedo = ParseFunction(false);
|
||||
|
||||
end(app.Database.hm, app.Database.hm.RedoEvent, ParseRedo);
|
||||
end(app.Database.hm, app.Database.hm.UndoEvent, ParseUndo);
|
||||
|
||||
// 新建图纸时初始化图层UI
|
||||
const InitLayer = () =>
|
||||
{
|
||||
const layerStore = LayerStore.GetInstance();
|
||||
const layerTopStore = LayerTopStore.GetInstance();
|
||||
DataToViewForLayer.UpdateAll();
|
||||
layerTopStore.setCurrentLayerValue(layerStore.currentLayer.name);
|
||||
};
|
||||
end(app, app.CreateDocument, () => InitLayer());
|
||||
|
||||
// 打开图纸时加载图层UI
|
||||
end(app, app.OpenFile, () => InitLayer());
|
||||
|
||||
// 插入图纸/插入模板时更新图层UI
|
||||
app.CommandReactor.OnCommandEnd((cmdName, changeObjects, createObjects) =>
|
||||
{
|
||||
if (cmdName === CommandNames.Insert || cmdName === CommandNames.InsertModule || cmdName === CommandNames.EditTemplate)
|
||||
{
|
||||
DataToViewForLayer.UpdateRootByTable();
|
||||
DataToViewForLayer.UpdateAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
.colorHoverActive {
|
||||
border-color: #fff !important;
|
||||
}
|
||||
|
||||
.colorHover:hover {
|
||||
border-color: #fff !important;
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
import { Button, Classes } from "@blueprintjs/core";
|
||||
import { observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { app } from "../../../../../ApplicationServices/Application";
|
||||
import { ColorPalette } from "../../../../../Common/ColorPalette";
|
||||
import { ModalContainer, ModalHeader } from "../../../Modal/ModalContainer";
|
||||
import "./Color.css";
|
||||
import { ColorAPI } from "./ColorAPI";
|
||||
|
||||
/** 颜色选择的弹窗 */
|
||||
@observer
|
||||
export class ColorDialog extends React.Component<{ colorIndex: number, onConfirm?: (colorIndex?: number) => void; }> {
|
||||
constructor(props)
|
||||
{
|
||||
super(props);
|
||||
}
|
||||
|
||||
/** 图层名称输入框的DOM */
|
||||
_ColorManagerRef = React.createRef<ColorManager>();
|
||||
|
||||
// 视图渲染(这是主要部分,上面做的都是一些参数的初始化)
|
||||
public render()
|
||||
{
|
||||
const { colorIndex, onConfirm } = this.props;
|
||||
return (
|
||||
<ModalContainer className="headCeilingContourManage">
|
||||
<ModalHeader
|
||||
title={"颜色选择器"}
|
||||
icon="bold"
|
||||
close={() => app.Editor.ModalManage.Destory()}
|
||||
/>
|
||||
<div className={Classes.DIALOG_BODY} style={{ width: "500px", height: "auto" }}>
|
||||
<ColorManager ref={this._ColorManagerRef} colorIndex={colorIndex} />
|
||||
</div>
|
||||
<div className={Classes.DIALOG_FOOTER} >
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button
|
||||
className={Classes.INTENT_PRIMARY}
|
||||
text="确定"
|
||||
onClick={() =>
|
||||
{
|
||||
if (onConfirm) onConfirm(this._ColorManagerRef.current.colorIndex);
|
||||
app.Editor.ModalManage.Destory();
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.INTENT_DANGER}
|
||||
text="取消"
|
||||
onClick={() => app.Editor.ModalManage.Destory()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ModalContainer >
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** 颜色选择器 */
|
||||
@observer
|
||||
class ColorManager extends React.Component<{ colorIndex: number; }> {
|
||||
constructor(props)
|
||||
{
|
||||
super(props);
|
||||
}
|
||||
|
||||
/** 当前选择的颜色索引 */
|
||||
@observable
|
||||
colorIndex = this.props.colorIndex;
|
||||
|
||||
// 视图渲染(这是主要部分,上面做的都是一些参数的初始化)
|
||||
public render()
|
||||
{
|
||||
return (
|
||||
<div style={{ width: "100%", borderRadius: "3px", overflow: "hidden", display: "flex", flexWrap: "wrap" }}>
|
||||
{ColorPalette.slice(0, 256).map((color, index) => <div className={"colorHover" + (this.colorIndex === index ? " colorHoverActive" : "")} style={{
|
||||
width: "25px", height: "25px", border: "1px solid #000", display: "flex", justifyContent: "center", alignItems: "center", cursor: "pointer",
|
||||
background: ColorAPI.GetColor(index), color: ColorAPI.GetGrayColor(index)
|
||||
}} title={ColorAPI.GetColor(index)} onClick={() =>
|
||||
{
|
||||
this.colorIndex = index;
|
||||
}}>{index}</div>)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** 颜色块 */
|
||||
export const ColorBlock = (props: { colorIndex: number; }) => <div style={{ width: "15px", height: "15px", background: ColorAPI.GetColor(props.colorIndex), border: "1px solid #000", marginRight: "5px" }}></div>;
|
@ -0,0 +1,20 @@
|
||||
import { ColorPalette } from "../../../../../Common/ColorPalette";
|
||||
|
||||
export class ColorAPI
|
||||
{
|
||||
/** 获取WebCAD中的颜色 */
|
||||
static GetColor = (colorIndex: number) => `rgb(${ColorPalette[colorIndex][0]}, ${ColorPalette[colorIndex][1]}, ${ColorPalette[colorIndex][2]})`;
|
||||
|
||||
/** 获取WebCAD中的颜色索引(头尾相连) */
|
||||
static GetColorIndex = (index: number) => index % 7 + 1;
|
||||
|
||||
/** RGB转黑白色 */
|
||||
static Rgb2Gray = (r: number, g: number, b: number) => (r * 0.299) + (g * 0.587) + (b * 0.114) > 128 ? 255 : 0;
|
||||
|
||||
/** 获取WebCAD中的颜色对应的反向黑白色 */
|
||||
static GetGrayColor(colorIndex: number)
|
||||
{
|
||||
const value = Math.abs(255 - ColorAPI.Rgb2Gray(ColorPalette[colorIndex][0], ColorPalette[colorIndex][1], ColorPalette[colorIndex][2]));
|
||||
return `rgb(${value},${value},${value})`;
|
||||
};
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
import React from "react";
|
||||
|
||||
/** 创建一个SVG图标 */
|
||||
export const IconSVG = (props: { name: string, size?: number, color?: string; }) =>
|
||||
{
|
||||
const { name, size = 40, color = "#000000" } = props;
|
||||
if (name === "图层特性")
|
||||
return <svg viewBox="0 0 1024 1024" p-id="7511" width={`${size}`} height={`${size}`}>
|
||||
<path d="M83.914105 781.473684h485.052632l323.368421-363.789473h-485.052632l-323.368421 363.789473z" fill="#E7E7E7" p-id="7512"></path>
|
||||
<path d="M53.894737 794.947368L401.219368 404.210526h521.108211L575.002947 794.947368H53.894737zM862.315789 431.157895H413.318737L113.906526 768h448.997053L862.315789 431.157895z" fill="#131418" p-id="7513"></path>
|
||||
<path d="M83.914105 619.789474h485.052632l323.368421-363.789474h-485.052632l-323.368421 363.789474z" fill="#E7E7E7" p-id="7514"></path>
|
||||
<path d="M53.894737 633.263158L401.219368 242.526316h521.108211L575.002947 633.263158H53.894737zM862.315789 269.473684H413.318737L113.906526 606.315789h448.997053L862.315789 269.473684z" fill="#131418" p-id="7515"></path>
|
||||
<path d="M83.914105 458.105263h485.052632l323.368421-363.789474h-485.052632l-323.368421 363.789474z" fill="#E7E7E7" p-id="7516"></path>
|
||||
<path d="M53.894737 471.578947h521.10821L922.327579 80.842105H401.219368L53.894737 471.578947zM862.315789 107.789474L562.903579 444.631579H113.906526L413.318737 107.789474H862.315789z" fill="#131418" p-id="7517"></path>
|
||||
<path d="M622.861474 471.578947h323.368421v458.105264h-323.368421v-458.105264z" fill="#FFFFFF" p-id="7518"></path>
|
||||
<path d="M959.703579 943.157895h-350.31579V458.105263h350.31579v485.052632z m-26.947368-26.947369V485.052632h-296.421053v431.157894h296.421053z" fill="#131418" p-id="7519"></path>
|
||||
<path d="M676.756211 525.473684h215.578947v161.684211h-215.578947v-161.684211z" fill="#50A5FF" p-id="7520"></path>
|
||||
<path d="M905.808842 700.631579h-242.526316v-188.631579h242.526316v188.631579z m-26.947368-26.947368v-134.736843h-188.631579v134.736843h188.631579z" fill="#131418" p-id="7521"></path>
|
||||
<path d="M690.229895 768h188.631579v26.947368h-188.631579v-26.947368zM690.229895 848.842105h188.631579v26.947369h-188.631579v-26.947369z" fill="#767676" p-id="7522"></path>
|
||||
<path d="M744.124632 741.052632v80.842105h-26.947369v-80.842105h26.947369zM838.440421 821.894737v80.842105h-26.947368v-80.842105h26.947368z" fill="#767676" p-id="7523"></path>
|
||||
</svg>;
|
||||
if (name === "锁定选定对象图层")
|
||||
return <svg viewBox="0 0 1024 1024" p-id="4707" width={`${size}`} height={`${size}`}>
|
||||
<path d="M932.3776 0a38.4 38.4 0 0 1 33.3696 57.3952l-378.7008 665.6A38.4 38.4 0 0 1 553.664 742.4H40.4224a38.4 38.4 0 0 1-33.3696-57.3952l378.7008-665.6A38.4 38.4 0 0 1 419.136 0h513.2416z m-88.064 89.6H448.9088L128.4608 652.8h395.4176L844.3136 89.6zM924.7744 748.9664v-78.848c0-66.0224-65.1392-119.7184-131.1616-119.7184-66.0224 0-131.2128 53.696-131.2128 119.7184v78.848h-56.832a29.568 29.568 0 0 0-29.568 29.568v215.8592A29.568 29.568 0 0 0 605.568 1024h376.0896A29.5424 29.5424 0 0 0 1011.2 994.3936V778.5344a29.568 29.568 0 0 0-29.568-29.568h-56.8576zM810.9312 943.488c0 2.0096-1.8176 3.712-4.0448 3.712h-26.5216c-2.2528 0-4.0704-1.7024-4.0704-3.712v-45.056C763.8272 892.6208 755.2 880.896 755.2 867.2c0-19.4176 17.2032-35.2 38.3872-35.2C814.7968 832 832 847.7824 832 867.2c0.0256 13.696-8.6016 25.408-21.056 31.232v45.056zM716.8 755.2v-78.3744C716.8 635.3536 760.32 601.6 800.0256 601.6S883.2 635.3664 883.2 676.8256V755.2H716.8z" fill={`${color}`} p-id="4708"></path>
|
||||
</svg>;
|
||||
if (name === "解锁选定对象图层")
|
||||
return <svg viewBox="0 0 1024 1024" p-id="5919" width={`${size}`} height={`${size}`}>
|
||||
<path d="M932.307677 0a38.39712 38.39712 0 0 1 33.367097 57.390896l-378.672399 665.550083A38.39712 38.39712 0 0 1 553.622478 742.344324H40.419369a38.39712 38.39712 0 0 1-33.367098-57.390896l378.6724-665.550083A38.39712 38.39712 0 0 1 419.104567 0h513.20311z m-88.057396 89.593281H448.875134L128.451166 652.751044h395.387946L844.250281 89.593281zM721.123516 746.120041H891.581131v-2.150239h45.922956v2.150239h56.853336a29.693773 29.693773 0 0 1 29.501788 27.978702L1023.923206 994.024648c0 15.896408-12.210284 28.861835-27.671525 29.834563l-1.86866 0.063995H618.321626a29.693773 29.693773 0 0 1-29.501787-28.0043L588.755843 994.024648V775.980201c0-15.87081 12.235882-28.823438 27.697123-29.808964l58.696398-0.051196v-126.032148c0-65.633477 63.137665-119.19746 128.067195-120.886933L806.339525 499.162563c64.967927 0 129.078319 52.52726 131.100567 117.763968l0.051196 3.161362v42.697598c-3.263755 15.46124-10.636002 23.191861-22.116741 23.191861s-19.416144-7.73062-23.806215-23.191861v-40.713746c0-40.227383-42.697598-73.146514-82.6562-74.490414l-2.547009-0.051196c-39.805015 0-83.398545 32.074394-85.190411 71.981802l-0.063995 126.608104zM806.339525 819.138565C785.144314 819.138565 767.942404 836.67325 767.942404 858.239632c0 14.386121 7.692223 26.775592 19.044972 33.571882l2.047846 1.151914v50.044246c0 1.958253 1.382296 3.647726 3.250957 4.031698l0.819138 0.089593h26.519611a4.095693 4.095693 0 0 0 3.967703-3.302152l0.076794-0.819139v-50.044246A38.960278 38.960278 0 0 0 844.736645 858.226833C844.736645 836.67325 827.534735 819.138565 806.326725 819.138565z" fill={`${color}`} p-id="5920"></path>
|
||||
</svg>;
|
||||
if (name === "置为当前图层")
|
||||
return <svg viewBox="0 0 1024 1024" p-id="2842" width={`${size}`} height={`${size}`}>
|
||||
<path d="M942.933333 311.466667l-384-192c-25.6-17.066667-55.466667-17.066667-81.066666 0l-384 192c0 4.266667-4.266667 8.533333-8.533334 8.533333-4.266667 17.066667 0 34.133333 12.8 38.4L234.666667 426.666667l-115.2 59.733333c-12.8 8.533333-21.333333 21.333333-21.333334 34.133333 0 12.8 8.533333 29.866667 21.333334 34.133334l98.133333 51.2-98.133333 51.2c-12.8 8.533333-21.333333 21.333333-21.333334 34.133333 0 12.8 8.533333 29.866667 21.333334 34.133333l358.4 179.2c12.8 8.533333 25.6 12.8 38.4 12.8 12.8 0 29.866667-4.266667 42.666666-12.8l358.4-179.2c12.8-8.533333 21.333333-21.333333 21.333334-34.133333 0-12.8-8.533333-29.866667-21.333334-34.133333l-98.133333-51.2 98.133333-51.2c12.8-8.533333 21.333333-21.333333 21.333334-34.133334 0-12.8-8.533333-29.866667-21.333334-34.133333L806.4 426.666667l136.533333-68.266667c4.266667-4.266667 8.533333-4.266667 8.533334-8.533333 4.266667-17.066667 0-34.133333-8.533334-38.4z m-81.066666 379.733333l-328.533334 166.4c-8.533333 4.266667-17.066667 4.266667-25.6 0l-328.533333-166.4 106.666667-55.466667 196.266666 98.133334c12.8 8.533333 25.6 12.8 38.4 12.8 12.8 0 29.866667-4.266667 42.666667-12.8l196.266667-98.133334 102.4 55.466667z m0-170.666667l-106.666667 55.466667-64 29.866667-162.133333 81.066666c-8.533333 4.266667-17.066667 4.266667-25.6 0L341.333333 605.866667l-64-29.866667L170.666667 520.533333l128-64 183.466666 93.866667c25.6 17.066667 55.466667 17.066667 81.066667 0l183.466667-93.866667 115.2 64z" p-id="2843"></path>
|
||||
</svg>;
|
||||
if (name === "特性匹配")
|
||||
return <svg viewBox="0 0 1094 1024" p-id="1559" width={`${size}`} height={`${size}`}>
|
||||
<path d="M161.684211 80.842105h502.218105v697.532632H161.684211V80.842105z" fill="#E7E7E7" p-id="1560"></path>
|
||||
<path d="M677.376 67.368421H148.210526v724.48h529.165474V67.368421zM175.157895 764.901053V94.315789h475.270737v670.585264H175.157895z" fill="#131418" p-id="1561"></path>
|
||||
<path d="M245.382737 162.896842h334.821052v246.191158H245.382737V162.896842z" fill="#50A5FF" p-id="1562"></path>
|
||||
<path d="M593.677474 149.423158H231.909053v273.138526h361.768421V149.423158zM258.856421 395.614316V176.370526h307.873684v219.24379H258.856421z" fill="#131418" p-id="1563"></path>
|
||||
<path d="M266.320842 518.386526h292.97179v26.947369H266.320842v-26.947369zM266.320842 641.482105h292.97179v26.947369H266.320842v-26.947369z" fill="#767676" p-id="1564"></path>
|
||||
<path d="M335.117474 463.252211v123.095578h-26.947369v-123.095578h26.947369zM481.603368 586.374737v123.068631h-26.947368V586.374737h26.947368z" fill="#767676" p-id="1565"></path>
|
||||
<path d="M779.502579 530.19464q-19.236049 0.329671-31.830599 13.844086l-64.639793 58.563144-24.159292-20.798617q-14.315861-12.324455-32.751-12.016437-20.26782 0.330366-33.190037 15.340577l-55.556745 64.533697q-32.515594 29.21977-79.822816 47.163363l-42.345993 16.063216 274.370322 236.204072q22.117087 19.040492 49.796246 19.187939 31.864668 0.159407 51.080118-24.473884 26.2643-33.605864 40.709116-83.716328l34.547153-40.12934q12.904636-14.989789 10.256069-35.084319-2.436203-18.276053-16.772486-30.61809l-24.13887-20.781036 48.284566-72.607721q11.488919-14.501805 8.955056-33.573087-2.436203-18.276053-16.772486-30.61809l-63.247107-54.449126q-14.336283-12.342036-32.771422-12.034019z m-96.039387 108.335947l83.288987-75.434543c6.680874-7.760381 19.217515-7.990525 27.958155-0.465751l63.247107 54.449126c8.74064 7.524774 10.376906 19.956305 3.696032 27.716686l-62.213425 93.578392 42.171546 36.305278c8.74064 7.524774 10.374066 19.918302 3.675609 27.699105l-38.45019 44.663036c-8.92788 33.169791-21.585486 59.644028-37.611976 80.160049-15.258597 19.541444-43.278872 17.642468-62.067163 1.46772L467.096607 722.00211c9.530526-3.63592 18.713877-7.570721 27.506367-11.877569a268.611368 268.611368 0 0 0 50.985578-32.129057 236.058947 236.058947 0 0 0 20.134005-18.2244l-0.878148-0.755993 48.489083-56.324031c6.698456-7.780803 19.197093-8.008107 27.937733-0.483332l42.171545 36.305278z" fill="#131418" p-id="1566"></path>
|
||||
<path d="M684.504717 639.427231l83.452363-75.293894c6.680874-7.760381 19.299203-7.9202 28.162376-0.289938l64.125255 55.20512c8.883594 7.647843 10.601549 20.149699 3.920675 27.91008l-62.050049 93.719041 42.763786 36.815135c8.84275 7.612681 10.598708 20.111695 3.900252 27.892498L800.290292 861.709303 565.089474 659.226065l48.489083-56.32403c6.698456-7.780803 19.299203-7.9202 28.162375-0.289938l42.763785 36.815134z" fill="#FFFFFF" p-id="1567"></path>
|
||||
<path d="M591.245762 628.265156l222.075951 207.113938c-8.424421 45.017227-22.335978 72.865423-41.415168 93.95358-16.58319 18.436667-45.07939 16.305757-63.867682 0.131008L466.73185 721.723651c53.780597-20.300056 95.614828-51.133585 124.534334-93.440914z" fill="#FAD97F" p-id="1568"></path>
|
||||
</svg>;
|
||||
};
|
@ -0,0 +1,47 @@
|
||||
.layer-select {
|
||||
position: relative;
|
||||
width: 150px;
|
||||
font-size: 14px;
|
||||
z-index: 29;
|
||||
}
|
||||
|
||||
/* 当前选择项 */
|
||||
.layer-select-current {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 5px;
|
||||
width: 100%;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.layer-select-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
/* 下拉选项 */
|
||||
.layer-select-options {
|
||||
overflow-y: auto;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 0;
|
||||
padding: 5px;
|
||||
width: 100%;
|
||||
max-height: 400px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.layer-select-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.layer-select-option:hover {
|
||||
background: rgba(200, 210, 220, 0.5);
|
||||
}
|
@ -0,0 +1,278 @@
|
||||
import { app } from "../../../../ApplicationServices/Application";
|
||||
import { CommandNames } from "../../../../Common/CommandNames";
|
||||
import { Log } from "../../../../Common/Log";
|
||||
import { Entity } from "../../../../DatabaseServices/Entity/Entity";
|
||||
import { LayerTableRecord } from "../../../../DatabaseServices/LayerTableRecord";
|
||||
import { AmbientLight } from "../../../../DatabaseServices/Lights/AmbientLight";
|
||||
import { DirectionalLight } from "../../../../DatabaseServices/Lights/DirectionalLight";
|
||||
import { HemisphereLight } from "../../../../DatabaseServices/Lights/HemisphereLight";
|
||||
import { ObjectId } from "../../../../DatabaseServices/ObjectId";
|
||||
import { ViewportEntity } from "../../../../DatabaseServices/ViewportEntity";
|
||||
import { CommandWrap } from "../../../../Editor/CommandMachine";
|
||||
import { LayerAPI } from "./layerAPI";
|
||||
import { LayerTopStore } from "./LayerPanel";
|
||||
import { LayerAction, TreeAction } from "./State/Action";
|
||||
import { LayerStore } from "./State/Store";
|
||||
import { ViewToDataForLayer } from "./State/Transform";
|
||||
import type { ILayerNode } from "./State/Type";
|
||||
|
||||
/** 图层命令 */
|
||||
export class LayerCMD
|
||||
{
|
||||
/** 创建图层 */
|
||||
static AppendLayer = () =>
|
||||
{
|
||||
const layerAPI = new LayerAPI(LayerStore.GetInstance());
|
||||
const layerTopStore = LayerTopStore.GetInstance();
|
||||
// 更新View层数据
|
||||
const node = layerAPI.AppendFile();
|
||||
layerTopStore.featureMatchOptions[0].colorIndex = node.colorIndex;
|
||||
// 更新Data层数据
|
||||
CommandWrap(() =>
|
||||
{
|
||||
if (node)
|
||||
ViewToDataForLayer.AppendLayer(node);
|
||||
}, CommandNames.AppendLayer);
|
||||
};
|
||||
|
||||
/** 创建文件夹 */
|
||||
static AppendLayerFolder = () =>
|
||||
{
|
||||
const layerAPI = new LayerAPI(LayerStore.GetInstance());
|
||||
// 更新View层数据
|
||||
const node = layerAPI.AppendFolder();
|
||||
// 更新Data层数据
|
||||
CommandWrap(() =>
|
||||
{
|
||||
if (node)
|
||||
ViewToDataForLayer.AppendLayer(node);
|
||||
}, CommandNames.AppendLayerFolder);
|
||||
};
|
||||
|
||||
/** 删除图层 */
|
||||
static RemoveLayer = (node: ILayerNode, moveWhere?: string) =>
|
||||
{
|
||||
const layerAPI = new LayerAPI(LayerStore.GetInstance());
|
||||
const layerTopStore = LayerTopStore.GetInstance();
|
||||
// 更新View层数据
|
||||
if (!node.children)
|
||||
{
|
||||
layerAPI.RemoveFile(node);
|
||||
if (node.isCurrent)
|
||||
{
|
||||
const layerAction = new LayerAction(LayerStore.GetInstance());
|
||||
const normal = layerAction.FindNodeByName("默认");
|
||||
layerTopStore.featureMatchOptions[0].colorIndex = normal.colorIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
layerAPI.RemoveFolder(node);
|
||||
// 更新Data层数据
|
||||
CommandWrap(() =>
|
||||
{
|
||||
ViewToDataForLayer.RemoveLayer(node, moveWhere);
|
||||
}, !node.children ? CommandNames.RemoveLayer : CommandNames.RemoveLayerFolder);
|
||||
};
|
||||
|
||||
/** 置为当前图层 */
|
||||
static SetCurrentLayer = (node: ILayerNode) =>
|
||||
{
|
||||
const layerAPI = new LayerAPI(LayerStore.GetInstance());
|
||||
const layerTopStore = LayerTopStore.GetInstance();
|
||||
if (node.isCurrent)
|
||||
{
|
||||
Log("已经是当前图层");
|
||||
return;
|
||||
}
|
||||
layerAPI.SetCurrentFile(node);
|
||||
layerTopStore.featureMatchOptions[0].colorIndex = node.colorIndex;
|
||||
CommandWrap(() =>
|
||||
{
|
||||
ViewToDataForLayer.SetCurrentLayer();
|
||||
}, CommandNames.SetCurrentLayer);
|
||||
};
|
||||
|
||||
/** 修改实体所在的图层 */
|
||||
static PutEntitysInLayer = (ens: Entity[], node: ILayerNode) =>
|
||||
{
|
||||
const layerTable = app.Database.LayerTable;
|
||||
const layerTopStore = LayerTopStore.GetInstance();
|
||||
if (!layerTable.Has(node.name))
|
||||
return;
|
||||
const layer = layerTable.GetAt(node.name) as LayerTableRecord;
|
||||
layerTopStore.setColorIndex(node.colorIndex, 0);
|
||||
CommandWrap(() =>
|
||||
{
|
||||
for (const en of ens)
|
||||
en.Layer = layer.Id;
|
||||
}, CommandNames.PutEntitysInLayer);
|
||||
};
|
||||
|
||||
/** 设置是否显示 */
|
||||
static UpdateShow = (node: ILayerNode) =>
|
||||
{
|
||||
const layerAPI = new LayerAPI(LayerStore.GetInstance());
|
||||
if (!node.children)
|
||||
layerAPI.UpdateShowFile(node, !node.isOff);
|
||||
else
|
||||
layerAPI.UpdateShowFolder(node, !node.isOff);
|
||||
CommandWrap(() =>
|
||||
{
|
||||
ViewToDataForLayer.UpdateShow(node);
|
||||
}, CommandNames.UpdateShow);
|
||||
};
|
||||
|
||||
/** 设置是否锁定 */
|
||||
static UpdateLock = (node: ILayerNode, isLock: boolean) =>
|
||||
{
|
||||
const layerAPI = new LayerAPI(LayerStore.GetInstance());
|
||||
if (!node.children)
|
||||
layerAPI.UpdateLockFile(node, isLock);
|
||||
else
|
||||
layerAPI.UpdateLockFolder(node, isLock);
|
||||
CommandWrap(() =>
|
||||
{
|
||||
ViewToDataForLayer.UpdateLock(node);
|
||||
}, CommandNames.UpdateLock);
|
||||
};
|
||||
|
||||
/** 设置是否视口冻结 */
|
||||
static UpdateViewPoint = (node: ILayerNode) =>
|
||||
{
|
||||
const layerAPI = new LayerAPI(LayerStore.GetInstance());
|
||||
if (!node.children)
|
||||
layerAPI.UpdateViewportOffFile(node, !node.isViewportOff);
|
||||
else
|
||||
layerAPI.UpdateViewportOffFolder(node, !node.isViewportOff);
|
||||
CommandWrap(() =>
|
||||
{
|
||||
ViewToDataForLayer.UpdateViewPoint(node);
|
||||
}, CommandNames.UpdateViewPoint);
|
||||
};
|
||||
|
||||
/** 设置颜色 */
|
||||
static UpdateColor = (node: ILayerNode, colorIndex: number) =>
|
||||
{
|
||||
const layerAPI = new LayerAPI(LayerStore.GetInstance());
|
||||
const layerTopStore = LayerTopStore.GetInstance();
|
||||
layerAPI.UpdateColorFile(node, colorIndex);
|
||||
if (node.isCurrent)
|
||||
layerTopStore.featureMatchOptions[0].colorIndex = node.colorIndex;
|
||||
CommandWrap(() =>
|
||||
{
|
||||
ViewToDataForLayer.UpdateColor(node);
|
||||
}, CommandNames.UpdateColor);
|
||||
};
|
||||
|
||||
/** 重命名图层 */
|
||||
static RenameLayer = (node: ILayerNode, name: string) =>
|
||||
{
|
||||
const layerAPI = new LayerAPI(LayerStore.GetInstance());
|
||||
const layerTopStore = LayerTopStore.GetInstance();
|
||||
const oldName = node.name;
|
||||
layerAPI.RenameNode(node, name);
|
||||
if (layerTopStore.currentLayerValue === node.name)
|
||||
layerTopStore.currentLayerValue = name;
|
||||
CommandWrap(() =>
|
||||
{
|
||||
ViewToDataForLayer.RenameLayer(oldName, name);
|
||||
}, !node.children ? CommandNames.RenameLayer : CommandNames.RenameLayerFolder);
|
||||
};
|
||||
|
||||
/** 移动图层 */
|
||||
static MoveLayer = (node: ILayerNode, moveWhere: string, isFolder) =>
|
||||
{
|
||||
if (moveWhere === node.name)
|
||||
return;
|
||||
const layerStore = LayerStore.GetInstance();
|
||||
TreeAction.RemoveNode(layerStore.data, node.name);
|
||||
if (moveWhere === "--LAST")
|
||||
layerStore.data.push(node);
|
||||
else if (isFolder)
|
||||
TreeAction.FindNode(layerStore.data, (node: ILayerNode) => node.name === moveWhere).children.push(node);
|
||||
else
|
||||
{
|
||||
const indexs = TreeAction.FindIndexsByName(layerStore.data, moveWhere);
|
||||
TreeAction.InsertNode(layerStore.data, node, indexs);
|
||||
}
|
||||
CommandWrap(() =>
|
||||
{
|
||||
ViewToDataForLayer.MoveLayer(node);
|
||||
}, !node.children ? CommandNames.MoveLayer : CommandNames.MoveLayerFolder);
|
||||
};
|
||||
}
|
||||
|
||||
/** 选中图层中的实体 */
|
||||
export const SelectLayerEntitys = (data: ILayerNode) =>
|
||||
{
|
||||
const layerTable = app.Database.LayerTable;
|
||||
const nodeSet: Set<ObjectId<LayerTableRecord>> = new Set();
|
||||
// 获取ID
|
||||
if (data.children && data.children.length > 0)
|
||||
{
|
||||
TreeAction.UpdateNode(data.children, (node: ILayerNode) =>
|
||||
{
|
||||
if (!layerTable.Has(node.name))
|
||||
return;
|
||||
const layer = layerTable.GetAt(node.name) as LayerTableRecord;
|
||||
nodeSet.add(layer.Id);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!layerTable.Has(data.name))
|
||||
return;
|
||||
const layer = layerTable.GetAt(data.name) as LayerTableRecord;
|
||||
nodeSet.add(layer.Id);
|
||||
}
|
||||
|
||||
// 开始选择
|
||||
const ens: Entity[] = [];
|
||||
if (app.Viewer.isLayout)
|
||||
{
|
||||
const CurrentViewport = app.Viewer.CurrentViewport;
|
||||
if (CurrentViewport)
|
||||
ens.push(...app.Viewer.CurrentViewport.ShowObjects.map((id: ObjectId<Entity>) => id.Object).filter(en => nodeSet.has(en.Layer)));
|
||||
else
|
||||
ens.push(...app.Database.LayoutSpace.Entitys.filter(viewPoint => nodeSet.has(viewPoint.Layer) && viewPoint.IsVisible));
|
||||
}
|
||||
else
|
||||
{
|
||||
const isNormalLight = (en: Entity) => en instanceof AmbientLight || en instanceof DirectionalLight || en instanceof HemisphereLight;
|
||||
ens.push(...app.Database.ModelSpace.Entitys.filter(en => nodeSet.has(en.Layer) && en.IsVisible));
|
||||
ens.push(...app.Database.Lights.Entitys.filter(en => !isNormalLight(en) && nodeSet.has(en.Layer) && en.IsVisible));
|
||||
}
|
||||
app.Editor.SetSelection(ens);
|
||||
};
|
||||
|
||||
/** 选中视口时更新图层状态 */
|
||||
export const UpdateLayerByViewPoint = (viewPoint: ViewportEntity) =>
|
||||
{
|
||||
const layerStore = LayerStore.GetInstance();
|
||||
layerStore.isShowViewportOff = true;
|
||||
const UpdateNode = (nodes: ILayerNode[], Callback: (node: ILayerNode) => void, Predicate = (node: ILayerNode) => true) =>
|
||||
{
|
||||
nodes.map(node =>
|
||||
{
|
||||
if (Predicate(node))
|
||||
Callback(node);
|
||||
if (node.children && node.children.length > 0)
|
||||
UpdateNode(node.children, Callback, Predicate);
|
||||
});
|
||||
};
|
||||
UpdateNode(layerStore.data, (data) =>
|
||||
{
|
||||
const layerTable = app.Database.LayerTable;
|
||||
if (!layerTable.Has(data.name))
|
||||
return;
|
||||
const layer = layerTable.GetAt(data.name) as LayerTableRecord;
|
||||
data.isViewportOff = viewPoint.HasFreezeLayer(layer.Id);
|
||||
});
|
||||
};
|
||||
|
||||
/** 取消视口时更新图层状态 */
|
||||
export const UpdateLayerByNoViewPoint = () =>
|
||||
{
|
||||
const layerStore = LayerStore.GetInstance();
|
||||
layerStore.isShowViewportOff = false;
|
||||
};
|
@ -0,0 +1,74 @@
|
||||
/* 搜索框 */
|
||||
.layer-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
/* 操作栏 */
|
||||
.layer-operation {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.layer-operation-btn {
|
||||
margin: 0 5px;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
/* 图层列表-表头 */
|
||||
.layer-header {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.layer-header-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 30px;
|
||||
height: 100%;
|
||||
border-bottom: 1px solid #ccc;
|
||||
border-top: 1px solid #ccc;
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
/* 图层列表-表项 */
|
||||
.layer-body {
|
||||
width: 100%;
|
||||
height: calc(100% - 100px);
|
||||
}
|
||||
|
||||
.layer-body-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 100px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.layer-body-line {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
background: rgba(100, 150, 200, 0.5);
|
||||
}
|
||||
|
||||
/* 图层列表 */
|
||||
.layer-list {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.layer-list-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 30px;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/* 图层特性 */
|
||||
.layer-feature {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 5px;
|
||||
padding-right: 20px;
|
||||
height: 70px;
|
||||
font-size: 14px;
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.layer-feature-current {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 20px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.layer-feature-logo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 20px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.layer-feature-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 20px;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.layer-feature-btn:hover {
|
||||
background: rgba(200, 210, 220, 0.5);
|
||||
}
|
||||
|
||||
/* 特性匹配 */
|
||||
.feature-match {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 5px;
|
||||
height: 70px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.feature-match-logo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 20px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.feature-match-color {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 20px;
|
||||
height: 100%;
|
||||
}
|
@ -0,0 +1,360 @@
|
||||
import { Icon, Intent } from "@blueprintjs/core";
|
||||
import { action, observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from 'react';
|
||||
import { app } from "../../../../ApplicationServices/Application";
|
||||
import { HostApplicationServices } from "../../../../ApplicationServices/HostApplicationServices";
|
||||
import { Singleton } from "../../../../Common/Singleton";
|
||||
import { CommandWrap } from "../../../../Editor/CommandMachine";
|
||||
import { AppToaster } from "../../Toaster";
|
||||
import { PropertiesStore } from "../PropertiesStore";
|
||||
import { ColorBlock, ColorDialog } from "./Component/Color";
|
||||
import { ColorAPI } from "./Component/ColorAPI";
|
||||
import { IconSVG } from "./Component/Icon";
|
||||
import { ISelectOption, SelectInLayer, TSelectValue } from "./Component/Select";
|
||||
import { LayerCMD } from "./LayerCMD";
|
||||
import "./LayerPanel.css";
|
||||
import { LayerAction, TreeAction } from "./State/Action";
|
||||
import { LayerPropertieStore, LayerStore } from "./State/Store";
|
||||
import type { ILayerNode } from "./State/Type";
|
||||
|
||||
/** 图层状态机 */
|
||||
const layerStore = LayerStore.GetInstance();
|
||||
const layerAction = new LayerAction(layerStore);
|
||||
|
||||
interface IFeatureMatchOption
|
||||
{
|
||||
colorIndex?: number;
|
||||
name: string;
|
||||
value?: number;
|
||||
onClick?: (currentItem?: ISelectOption, HandleChange?: (currentItem?: ISelectOption) => void) => void;
|
||||
}
|
||||
|
||||
export class LayerTopStore extends Singleton
|
||||
{
|
||||
/** 当前图层key */
|
||||
@observable
|
||||
currentLayerValue = layerStore.currentLayer.name;
|
||||
|
||||
/** 颜色key */
|
||||
@observable
|
||||
colorValue: TSelectValue = 7;
|
||||
|
||||
/** 默认颜色key */
|
||||
@observable
|
||||
normalColorValue: TSelectValue = 7;
|
||||
|
||||
/** 是否使用默认颜色 */
|
||||
@observable
|
||||
isUseNormal = false;
|
||||
|
||||
/** 特性匹配里的颜色选项 */
|
||||
@observable
|
||||
featureMatchOptions: IFeatureMatchOption[] = [
|
||||
{ colorIndex: layerStore.currentLayer.colorIndex, name: "跟随图层", value: 0 },
|
||||
{ colorIndex: 1, name: "红色", value: 1 },
|
||||
{ colorIndex: 2, name: "黄色", value: 2 },
|
||||
{ colorIndex: 3, name: "绿色", value: 3 },
|
||||
{ colorIndex: 4, name: "青色", value: 4 },
|
||||
{ colorIndex: 5, name: "蓝色", value: 5 },
|
||||
{ colorIndex: 6, name: "紫色", value: 6 },
|
||||
{ colorIndex: 7, name: "白色", value: 7 },
|
||||
{ colorIndex: 8, name: "更多颜色", value: 8, onClick: this.OpenMoreColor },
|
||||
];
|
||||
|
||||
/** 点击更多颜色 */
|
||||
OpenMoreColor(currentItem: ISelectOption, HandleChange: Function)
|
||||
{
|
||||
app.Editor.ModalManage.RenderModal(ColorDialog, {
|
||||
colorIndex: layerTopStore.featureMatchOptions[8].colorIndex,
|
||||
onConfirm: (colorIndex: number) =>
|
||||
{
|
||||
layerTopStore.featureMatchOptions[8].colorIndex = colorIndex;
|
||||
currentItem.data.colorIndex = colorIndex;
|
||||
const ens = app.Editor.SelectCtrl.SelectSet.SelectEntityList;
|
||||
// 没有选中实体,设置默认颜色
|
||||
if (ens.length === 0)
|
||||
{
|
||||
HostApplicationServices.CurrentColorindex = colorIndex;
|
||||
layerTopStore.normalColorValue = colorIndex;
|
||||
layerTopStore.isUseNormal = true;
|
||||
}
|
||||
// 选中实体,设置实体颜色
|
||||
else
|
||||
{
|
||||
CommandWrap(() =>
|
||||
{
|
||||
ens.forEach(en => en.ColorIndex = colorIndex);
|
||||
layerTopStore.setColorValue(colorIndex);
|
||||
layerTopStore.isUseNormal = false;
|
||||
}, "设置颜色");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
setColorValue(value: TSelectValue)
|
||||
{
|
||||
this.colorValue = value;
|
||||
}
|
||||
|
||||
@action
|
||||
setIsUseNormal(value: boolean)
|
||||
{
|
||||
this.isUseNormal = value;
|
||||
}
|
||||
|
||||
@action
|
||||
setColorIndex(colorIndex: number, index = 8)
|
||||
{
|
||||
this.featureMatchOptions[index].colorIndex = colorIndex;
|
||||
}
|
||||
|
||||
@action
|
||||
setCurrentLayerValue(value: string)
|
||||
{
|
||||
this.currentLayerValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
/** 颜色状态机 */
|
||||
const layerTopStore = LayerTopStore.GetInstance();
|
||||
|
||||
/** 图层特性面板 */
|
||||
@observer
|
||||
export class LayerPanel extends React.Component<{}, {}>
|
||||
{
|
||||
constructor(props: {})
|
||||
{
|
||||
super(props);
|
||||
LayerPropertieStore.isShow = true;
|
||||
}
|
||||
render()
|
||||
{
|
||||
const propertiesStore = PropertiesStore.GetInstance();
|
||||
/** 图层颜色 */
|
||||
let layerColorIndex = layerStore.currentLayer.colorIndex;
|
||||
const ens = propertiesStore.GetEntitys();
|
||||
const en = ens[0];
|
||||
// 获取到正确的颜色
|
||||
let colorIndex = 0;
|
||||
const colorSet = new Set<number>();
|
||||
for (const en of ens)
|
||||
colorSet.add(en.ColorIndex);
|
||||
if (colorSet.size > 1)
|
||||
colorIndex = 0;
|
||||
else if (colorSet.size === 1)
|
||||
for (const color of colorSet)
|
||||
colorIndex = color;
|
||||
/** 是否为多种颜色 */
|
||||
const isMoreColor = colorIndex === 0;
|
||||
// 获取实体的图层
|
||||
const currentSet: Set<string> = new Set();
|
||||
ens.forEach(en => currentSet.add(en.Layer.Object.Name));
|
||||
if (currentSet.size > 1)
|
||||
layerTopStore.setCurrentLayerValue("多种");
|
||||
else
|
||||
{
|
||||
if (en)
|
||||
{
|
||||
const data = layerAction.FindNodeByName(en.Layer.Object.Name);
|
||||
if (data)
|
||||
{
|
||||
layerColorIndex = data.colorIndex;
|
||||
layerTopStore.setCurrentLayerValue(data.name);
|
||||
colorIndex = en.ColorIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
layerTopStore.setCurrentLayerValue(layerStore.currentLayer.name);
|
||||
}
|
||||
// 获取实体的颜色
|
||||
if (en)
|
||||
{
|
||||
layerTopStore.setIsUseNormal(false);
|
||||
if (isMoreColor)
|
||||
layerTopStore.setColorValue("多种");
|
||||
else if (colorIndex === 256)
|
||||
{
|
||||
if (layerTopStore.colorValue !== 0)
|
||||
layerTopStore.setColorValue(0);
|
||||
layerTopStore.setColorIndex(layerColorIndex, 0);
|
||||
}
|
||||
else if (colorIndex < 8)
|
||||
{
|
||||
if (layerTopStore.colorValue !== colorIndex)
|
||||
layerTopStore.setColorValue(colorIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (layerTopStore.colorValue !== colorIndex)
|
||||
{
|
||||
layerTopStore.setColorValue(colorIndex);
|
||||
layerTopStore.setColorIndex(colorIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
layerTopStore.setIsUseNormal(true);
|
||||
|
||||
return (
|
||||
<div className="tool-block" >
|
||||
{/* 图层特性 */}
|
||||
<LayerFeature />
|
||||
{/* 特性匹配 */}
|
||||
<FeatureMatch />
|
||||
</div >
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取图层选项(一维展开) */
|
||||
export const getLayerOptions = (layerDatas: ILayerNode[]) => TreeAction.FindFlatNodes(layerDatas, (node: ILayerNode) => !node.children).map((data: ILayerNode) =>
|
||||
({
|
||||
label: (<>
|
||||
<div style={{ width: "30px", height: "100%", display: "flex", justifyContent: "center", alignItems: "center", cursor: "pointer" }}>
|
||||
{data.isOff ? <Icon icon="eye-off" size={20} /> : <Icon icon="eye-open" size={20} />}
|
||||
</div>
|
||||
<div style={{ width: "30px", height: "100%", display: "flex", justifyContent: "center", alignItems: "center", cursor: "pointer" }}>
|
||||
{data.isLock ? <Icon icon="lock" size={20} /> : <Icon icon="unlock" size={20} />}
|
||||
</div>
|
||||
<div style={{ width: "15px", height: "15px", border: "1px solid #000", marginRight: "5px", background: ColorAPI.GetColor(data.colorIndex) }}></div>
|
||||
<div title={data.name} style={{ flex: 1, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{data.name}</div>
|
||||
</>),
|
||||
value: data.name
|
||||
}));
|
||||
|
||||
/** 图层特性 */
|
||||
@observer
|
||||
class LayerFeature extends React.Component<{}>
|
||||
{
|
||||
/** 创建当前图层的选择器 */
|
||||
private CreateLayerSelect = (layerDatas: ILayerNode[]) =>
|
||||
{
|
||||
const layerFeatureOptions = getLayerOptions(layerDatas);
|
||||
return (
|
||||
<div className="layer-feature-current">
|
||||
<div style={{ marginRight: "10px" }}>当前图层</div>
|
||||
<SelectInLayer style={{ width: "250px" }} options={layerFeatureOptions} value={layerTopStore.currentLayerValue}
|
||||
onChange={(item: ISelectOption) =>
|
||||
{
|
||||
const value = item.value as string;
|
||||
layerTopStore.currentLayerValue = value;
|
||||
const data = layerAction.FindNodeByName(value);
|
||||
const ens = app.Editor.SelectCtrl.SelectSet.SelectEntityList;
|
||||
if (ens.length === 0)
|
||||
LayerCMD.SetCurrentLayer(data);
|
||||
else
|
||||
LayerCMD.PutEntitysInLayer(ens, data);
|
||||
}} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
render()
|
||||
{
|
||||
return (
|
||||
< div className="layer-feature">
|
||||
<div className="layer-feature-logo">
|
||||
<IconSVG name="图层特性" size={30} />
|
||||
</div>
|
||||
{this.CreateLayerSelect(layerStore.data)}
|
||||
<div className="layer-feature-btn"
|
||||
onClick={() =>
|
||||
{
|
||||
const layerAction = new LayerAction(layerStore);
|
||||
const currentLayer = layerAction.FindNodeByName(layerTopStore.currentLayerValue);
|
||||
if (currentLayer)
|
||||
LayerCMD.UpdateLock(currentLayer, false);
|
||||
}}>
|
||||
<IconSVG name="解锁选定对象图层" size={30} />
|
||||
<div>解锁选定对象图层</div>
|
||||
</div>
|
||||
<div className="layer-feature-btn"
|
||||
onClick={() =>
|
||||
{
|
||||
const layerAction = new LayerAction(layerStore);
|
||||
const currentLayer = layerAction.FindNodeByName(layerTopStore.currentLayerValue);
|
||||
if (currentLayer)
|
||||
LayerCMD.UpdateLock(currentLayer, true);
|
||||
}}>
|
||||
<IconSVG name="锁定选定对象图层" size={30} />
|
||||
<div>锁定选定对象图层</div>
|
||||
</div>
|
||||
<div className="layer-feature-btn"
|
||||
onClick={() =>
|
||||
{
|
||||
const en = app.Editor.SelectCtrl.SelectSet.SelectEntityList[0];
|
||||
if (en)
|
||||
{
|
||||
const data = layerAction.FindNodeByName(en.Layer.Object.Name);
|
||||
if (data)
|
||||
LayerCMD.SetCurrentLayer(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "请先选中实体",
|
||||
timeout: 2000,
|
||||
intent: Intent.DANGER
|
||||
}, 'openfile');
|
||||
}
|
||||
}}>
|
||||
<IconSVG name="置为当前图层" size={30} />
|
||||
<div>置为当前图层</div>
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
}
|
||||
/** 特性匹配 */
|
||||
@observer
|
||||
class FeatureMatch extends React.Component<{}>
|
||||
{
|
||||
render()
|
||||
{
|
||||
const options = layerTopStore.featureMatchOptions.map(data => ({
|
||||
label: (<>{data.colorIndex && <ColorBlock colorIndex={data.colorIndex} />}{data.name}</>),
|
||||
value: data.value,
|
||||
data: { colorIndex: data.colorIndex },
|
||||
onClick: data.onClick
|
||||
}));
|
||||
return (
|
||||
< div className="feature-match">
|
||||
<div className="feature-match-logo">
|
||||
<IconSVG name="特性匹配" size={30} />
|
||||
<div>特性匹配</div>
|
||||
</div>
|
||||
<div className="feature-match-color">
|
||||
<div style={{ marginRight: "10px" }}>颜色</div>
|
||||
<SelectInLayer value={layerTopStore.isUseNormal ? layerTopStore.normalColorValue : layerTopStore.colorValue} options={options} isColorByValue onChange={(currentItem: ISelectOption) =>
|
||||
{
|
||||
const value = currentItem.value as number;
|
||||
// 点击更多颜色
|
||||
if (value === 8)
|
||||
return;
|
||||
const colorIndex = value === 0 ? 256 : currentItem.data.colorIndex as number;
|
||||
const ens = app.Editor.SelectCtrl.SelectSet.SelectEntityList;
|
||||
// 没选中实体,设置默认颜色
|
||||
if (ens.length === 0)
|
||||
{
|
||||
HostApplicationServices.CurrentColorindex = colorIndex;
|
||||
layerTopStore.normalColorValue = value;
|
||||
layerTopStore.isUseNormal = true;
|
||||
}
|
||||
// 选中实体,设置实体颜色
|
||||
else
|
||||
{
|
||||
CommandWrap(() =>
|
||||
{
|
||||
ens.forEach(en => en.ColorIndex = colorIndex);
|
||||
layerTopStore.setColorValue(value);
|
||||
layerTopStore.isUseNormal = false;
|
||||
}, "设置颜色");
|
||||
}
|
||||
}} />
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import { observable } from "mobx";
|
||||
import { Singleton } from "../../../../../Common/Singleton";
|
||||
import type { ILayerNode } from "./Type";
|
||||
|
||||
/** 图层的状态单例 */
|
||||
export class LayerStore extends Singleton
|
||||
{
|
||||
/** 自增ID */
|
||||
ID = { file: 0, folder: 0 };
|
||||
|
||||
/** 图层数据 */
|
||||
@observable
|
||||
data: ILayerNode[] = [
|
||||
{ name: "默认", isCurrent: true, isOff: false, isLock: false, colorIndex: 7, visible: true, selected: false, changeable: false },
|
||||
];
|
||||
|
||||
/** 当前图层 */
|
||||
@observable
|
||||
currentLayer = this.data[0];
|
||||
|
||||
/** 是否显示视口冻结 */
|
||||
@observable
|
||||
isShowViewportOff = false;
|
||||
|
||||
/** 初始化 */
|
||||
Init()
|
||||
{
|
||||
this.data = [
|
||||
{ name: "默认", isCurrent: true, isOff: false, isLock: false, colorIndex: 7, visible: true, selected: false, changeable: false },
|
||||
];
|
||||
this.currentLayer = this.data[0];
|
||||
this.ID = { file: 0, folder: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
/** 图层特性 */
|
||||
export const LayerPropertieStore = {
|
||||
/** 是否显示 */
|
||||
isShow: false
|
||||
};
|
@ -0,0 +1,35 @@
|
||||
/** 树的基本数据类型 */
|
||||
export interface INode
|
||||
{
|
||||
/** 名称 */
|
||||
name: string;
|
||||
/** 子节点 */
|
||||
children?: INode[];
|
||||
}
|
||||
|
||||
/** 图层的数据类型 */
|
||||
export interface ILayerNode extends INode
|
||||
{
|
||||
children?: ILayerNode[];
|
||||
/** 是否为当前图层 */
|
||||
isCurrent?: boolean;
|
||||
/** 是否隐藏 */
|
||||
isOff: boolean;
|
||||
/** 是否锁定 */
|
||||
isLock: boolean;
|
||||
/** 是否视口隐藏(仅在视口模式下有效) */
|
||||
isViewportOff?: boolean;
|
||||
/** 图层颜色索引 */
|
||||
colorIndex?: number;
|
||||
|
||||
/* ---- 以下仅用于UI ---- */
|
||||
|
||||
/** 是否显示(用于搜索过滤) */
|
||||
visible: boolean;
|
||||
/** 是否选中 */
|
||||
selected: boolean;
|
||||
/** 是否可输入(用于重命名) */
|
||||
changeable: boolean;
|
||||
/** 是否展开 */
|
||||
expanded?: boolean;
|
||||
}
|
Loading…
Reference in new issue