diff --git a/package-lock.json b/package-lock.json index f36019f..747223c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12845,6 +12845,11 @@ "sha.js": "^2.4.8" } }, + "penner": { + "version": "0.1.3", + "resolved": "https://registry.npm.taobao.org/penner/download/penner-0.1.3.tgz", + "integrity": "sha1-C4tILU6bOa8vPXw3WSIpuKzClwU=" + }, "perfect-scrollbar": { "version": "1.5.0", "resolved": "https://registry.npm.taobao.org/perfect-scrollbar/download/perfect-scrollbar-1.5.0.tgz", @@ -12922,6 +12927,14 @@ "@pixi/filter-zoom-blur": "3.1.1" } }, + "pixi-viewport": { + "version": "4.18.1", + "resolved": "https://registry.npm.taobao.org/pixi-viewport/download/pixi-viewport-4.18.1.tgz", + "integrity": "sha1-EP1/72igFjwcKhxvI83KVvzbRvM=", + "requires": { + "penner": "^0.1.3" + } + }, "pixi.js": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-5.3.2.tgz", @@ -15860,6 +15873,11 @@ } } }, + "three": { + "version": "0.125.2", + "resolved": "https://registry.npm.taobao.org/three/download/three-0.125.2.tgz", + "integrity": "sha1-3LoSdJoutBUi4VISuRnNP79ymxI=" + }, "through": { "version": "2.3.8", "resolved": "https://registry.npm.taobao.org/through/download/through-2.3.8.tgz", diff --git a/package.json b/package.json index 4110484..fcce8f8 100644 --- a/package.json +++ b/package.json @@ -37,9 +37,11 @@ "ngx-echarts": "^4.2.2", "ngx-perfect-scrollbar": "^8.0.0", "pixi-filters": "^3.1.1", + "pixi-viewport": "^4.18.1", "pixi.js": "^5.3.2", "rxjs": "~6.5.4", "swiper": "^5.3.6", + "three": "^0.125.2", "tslib": "^1.10.0", "viewerjs": "^1.6.2", "zone.js": "~0.10.2" diff --git a/src/app/canvas-share-data.service.ts b/src/app/canvas-share-data.service.ts index adf46a6..b489243 100644 --- a/src/app/canvas-share-data.service.ts +++ b/src/app/canvas-share-data.service.ts @@ -1,42 +1,41 @@ +import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import {ReplaySubject} from 'rxjs'; import { Observable } from 'rxjs'; import { GameMode } from './working-area/model/gameMode'; + @Injectable({ providedIn: 'root' }) export class CanvasShareDataService { - constructor() { } + constructor(private http:HttpClient) { } private _sendMessage: ReplaySubject = new ReplaySubject(1); examDisposalNodesData; // 考生进入时获取当前试卷的处置节点 - examFacilityAssetsData; // 考生进入时获取当前试卷要考察的消防设施 + examFacilityAssetsData; // 考生进入时获取当前试卷要考察的消防设施 + examOriginaleveryStoreyData: any; // 考生答卷 总平面图/楼层/区域 楼层数据 + hiddenBasicInfoFacilities: any = []; // 考生答卷 当前楼层需要隐藏的基本信息素材 + - isChange = false; // 数据 是否改动 - selectTemplateData: any; // 选择当前 模板数据 + isChange:boolean = false; // 数据 是否改动 + + selectTemplateData:any; // 选择当前 模板数据 // 总平面图/建筑 楼层 selectStorey: any = {area: '', details: ''}; // 选择当前 楼层 数据 - originalcompanyBuildingData: any; // 单位/建筑 数据 - originaleveryStoreyData: any; // 总平面图/楼层/区域 楼层数据 - - - examOriginaleveryStoreyData: any; // 考生答卷 总平面图/楼层/区域 楼层数据 - - hiddenBasicInfoFacilities: any = []; // 考生答卷 当前楼层需要隐藏的基本信息素材 // 总平面图/建筑 楼层 // 处置 节点 allDisposalNode: any = []; // 所有 处置节点 - allNodeMarkers: any; // 灾情 标签信息 - - selectPanelPoint: DisposalNodeData = new DisposalNodeData(); - - selectPanelPointBaseData: any = {description: '', notes: '', weather: '', airTemperature: '', windDirection: '', windScale: ''}; // 当前 数据节点 所对应的 天气,详情 数据节点 + allNodeMarkers: any = { highlightMarkers:{}, markers:{} }; // 灾情 标签信息 + selectPanelPoint: DisposalNodeData = new DisposalNodeData(); // 当前数据节点 + selectPanelPointBaseData: any = {description: '', notes: '', weather: '', airTemperature: '', windDirection: '', windScale: ''}; // 当前 数据节点 对应 父级节点 + customizeDisposalNode:any; // 新建 自定义数据节点 底图+名称 // 处置 节点 + /** * 游戏模式 */ @@ -83,6 +82,49 @@ export class CanvasShareDataService { return this._sendMessage.asObservable(); } + //分段上传 + sectionUpload (companyId:string,file) { + let data = {filename: file.name} + return new Promise ((resolve, reject)=>{ + this.http.post(`/api/NewMultipartUpload/PlanPlatform/${companyId}/DisposalNode`,{},{params:data}).subscribe(async (data:any)=>{ //初始化分段上传 + let objectName = data.objectName + let uploadId = data.uploadId + let PartNumberETag = []; //每次返回需要保存的信息 + //分块 处理 + let fileSize = file.size || null //上传文件的总大小 + let shardSize = 5 * 1024 * 1024 //5MB一个分片 + let allSlice = Math.ceil(fileSize / shardSize) //总文件/5MB===共分多少段 + + for (let i = 0;i < allSlice;i++) { //循环分段上传 + let start = i * shardSize //切割文件开始位置 + let end = Math.min(fileSize, start + shardSize); //切割文件结束位置 + let formData = new FormData() + formData.append("file",file.slice(start, end)) + + //同步写法实现异步调用 + let result = await new Promise((resolve, reject) => { + // await 需要后面返回一个 promise 对象 + this.http.post(`/api/MultipartUpload/PlanPlatform/${objectName}?uploadId=${uploadId}&partNumber=${i+1}`,formData).subscribe((data:any)=>{ + let msg = { "partNumber":data.partNumber || null, "eTag": data.eTag || null } + resolve(msg) // 调用 promise 内置方法处理成功 + }) + }); + PartNumberETag.push(result) + + if (PartNumberETag.length === allSlice) { //分块上传完成 + let data = PartNumberETag + let paramsData = {uploadId:uploadId} + this.http.post(`/api/CompleteMultipartUpload/PlanPlatform/${objectName}`,data,{params:paramsData}).subscribe(data=>{ + resolve(objectName) + }) + } + }//for循环 + + //分块 处理 + }) + }) + } + // 处置节点 筛选出 匹配数据 匹配不到 return undefined findDisposalNode(parentId: string= null, name: string= null) { if (parentId && name) { // 匹配 父id, name @@ -588,6 +630,14 @@ export class DisposalNodeData { * 版本号 */ public Version: string; + /** + * 图片地址 + */ + public BackgroundImageUrl: string; + /** + * 图片地址 + */ + public BackgroundImageAngle: number; /** * 处置节点编号 */ diff --git a/src/app/working-area/model/axGrid.ts b/src/app/working-area/model/axGrid.ts index 348c709..4e178ad 100644 --- a/src/app/working-area/model/axGrid.ts +++ b/src/app/working-area/model/axGrid.ts @@ -8,58 +8,38 @@ const DEFAULT_LINE_STYLE = { native: true, }; -/** - * @description 在屏幕上绘制网格的实用程序类。 - * @extends PIXI.Graphics - */ export class AxGrid extends PIXI.Graphics { - private _cellSize: number; - private _correctedWidth: number; - private _gridWidth: number; - private _useCorrectedWidth: boolean; - private _drawBoundaries: any; - private _amtLines: any; - /** - * @param {number} cellSize 默认值:网格边长的平方根 - */ + private _cellSize: number; + private _correctedWidth: number; + private _gridWidth: number; + private _useCorrectedWidth: boolean; + private _drawBoundaries: any; + private _amtLines: any; + set cellSize(cellSize) { this._cellSize = cellSize || Math.sqrt(this._correctedWidth); } - get cellSize() { return this._cellSize; } - - /** - * 网格边等距线的数量 - */ get amtLines() { return Math.floor(this.gridWidth / this.cellSize); } - - /** - * 由' width '构造函数参数给出的网格的请求宽度。 - */ get originalWidth() { return this._gridWidth; } - /** - * 修正后的网格宽度,即大于最小平方根的数 - * 修正后的宽度。 + * 修正后的网格宽度,大于最小平方根的数 */ get correctedWidth() { return this._correctedWidth; } - get useCorrectedWidth() { return this._useCorrectedWidth; } /** * 网格中每个角落的坐标。 - * @returns {{ x1: number, y1: number, x2: number, y2: number}} - * 最左边(**x1**),最上面(**y1**),最右边(**x2**)和最下面(**y2**)的坐标。 */ get bounds() { return { @@ -78,50 +58,11 @@ export class AxGrid extends PIXI.Graphics { return this._drawBoundaries; } - /** - * 网格的实际宽度。 - * 当' cellSize '不是默认值时,网格的宽度将为 - * 在' width '构造函数中给出的宽度。否则,就是修改后的宽度。 - */ get gridWidth() { if (!this.useCorrectedWidth) { return this._gridWidth; } return Math.abs(this.cellSize - Math.sqrt(this._correctedWidth)) <= 1e-6 ? this._correctedWidth : this._gridWidth; } - /** - * - * @param {number} width number. Required. - * - * The target sidelength of the grid. It is best for `width` to be a perfect square (i.e., 2, 4, 9, 16, 25, etc.). If - * not and the parameter `useCorrectedWidth` is set to **false**, then the grid will use a corrected width, - * which is the smallest perfect square greater than `width`. - * - * @param {number} cellSize number, null. Optional, default: square root of corrected width - * - * The size of each cell in the grid. - * If the value is **null**, the grid will use the default value. - * - * @param {{ width: number, color: number, alpha: number, alignment: number, native: boolean }}. Object. Optional. - * - * default: - * **{ - * width: 1, - * color: 0xffffff, - * alpha: 1, - * alignment: 0.5, - * native: true - * }** - * - * Configuration for the line style on the object. See documentation on `PIXI.Graphics` for more on the `LineStyle` class. - * - * @param {boolean} useCorrectedWidth boolean. Optional. default: **true** - * If **true**, the grid will use the smallest perfect square greater than `width`. - * Otherwise, the grid will use the exact value given by `width`. - * - * @param {boolean} drawBoundaries boolean. Optional. default: **true** - * If **true**, the grid will draw its boundaries. - * Otherwise, the grid will not draw its boundaries. Mouse pointer detection is not affected. - */ constructor( width, cellSize= null, @@ -151,12 +92,9 @@ export class AxGrid extends PIXI.Graphics { lConfig.alignment, lConfig.native ); - - // handle mouse move this.interactive = true; this.on('mousemove', (evt) => { const mouseCoords = evt.data.getLocalPosition(evt.currentTarget.parent); - // 检查鼠标是否在此网格的范围内。如果不是,那就什么都不做。 if ( mouseCoords.x >= this.bounds.x1 && mouseCoords.x <= this.bounds.x2 && @@ -192,11 +130,6 @@ export class AxGrid extends PIXI.Graphics { /** * 清除网格 - * - * @param {boolean} retainLineStyle 可选,默认:true - * - * 当**true**时,线条样式对象的配置将被保留。 - * 否则,对象的行样式将恢复为' PIXI '指定的默认值。图形的对象。 */ clearGrid(retainLineStyle = true) { const { width, alignment, color, alpha, native } = this.line; @@ -207,14 +140,10 @@ export class AxGrid extends PIXI.Graphics { return this; } - /** - * Transforms global coordinates to grid coordinates. - * @param {number} x - * The global X coordinate. - * - * @param {number} y - * The global Y coordinate. + * 返回网格的坐标 + * @param x 坐标x + * @param y 坐标y */ getCellCoordinates(x, y) { return { @@ -222,22 +151,15 @@ export class AxGrid extends PIXI.Graphics { y: Math.floor((y - this.bounds.y1) / this.cellSize), }; } - /** - * 检测到mousemove事件后触发的回调。 - * - * @param {PIXI.InteractionData} evt - * 'PIXI.InteractionData '事件 - * - * @param {{x: number, y: number}} gridCoords - * 网格坐标 + * 鼠标移动事件 + * @param evt 鼠标事件 + * @param gridCoords 鼠标所在网格坐标 */ onMousemove(evt, gridCoords) { } - - // 计算修正后的宽度。如果`useCorrectedWidth`构造函数参数设置为**false**, - // 然后,它简单地保持“width”的给定值作为修正后的宽度。 + // 默认宽度 _correctWidth() { if (!this._useCorrectedWidth) { this._correctedWidth = this._gridWidth; @@ -245,9 +167,7 @@ export class AxGrid extends PIXI.Graphics { this._correctedWidth = Math.ceil(Math.sqrt(this._gridWidth)) ** 2; } - - // 计算修正后的宽度。如果`useCorrectedWidth`构造函数参数设置为**false**, - // 然后,它简单地保持“width”的给定值作为修正后的宽度。 + // 自定义宽度 correctWidth(width: number) { if (!this._useCorrectedWidth) { this._correctedWidth = width; diff --git a/src/app/working-area/model/axImageShapeTest.ts b/src/app/working-area/model/axImageShapeTest.ts deleted file mode 100644 index 81c141c..0000000 --- a/src/app/working-area/model/axImageShapeTest.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { AxRectangleShape } from "./axRectangleShape"; - -export class AxImageShapeTest extends AxRectangleShape{ - /** - * - */ - constructor(x:number,y:number,width:number,height:number) { - super(x,y,width,height); - - } -} \ No newline at end of file diff --git a/src/app/working-area/model/axLegend.ts b/src/app/working-area/model/axLegend.ts index a886fd3..f2158ac 100644 --- a/src/app/working-area/model/axLegend.ts +++ b/src/app/working-area/model/axLegend.ts @@ -429,7 +429,6 @@ export class AxLegend extends AxShape { */ public drawBorder(scale: number) { const visible = this.upLeft.visible; - console.log(visible); this.setPointVisiable(false); super.drawBorder(scale); diff --git a/src/app/working-area/model/axMessageSystem.ts b/src/app/working-area/model/axMessageSystem.ts new file mode 100644 index 0000000..1451f9c --- /dev/null +++ b/src/app/working-area/model/axMessageSystem.ts @@ -0,0 +1,90 @@ +/** + * 事件系统 + */ +export class AxMessageSystem { + /** 监听数组 */ + private static listeners = {}; + + /** + * 注册事件 + * @param name 事件名称 + * @param callback 回调函数 + * @param context 上下文 + */ + public static addListener(name: string, callback: () => void, context: any) { + const observers: Observer[] = AxMessageSystem.listeners[name]; + if (!observers) { + AxMessageSystem.listeners[name] = []; + } + AxMessageSystem.listeners[name].push(new Observer(callback, context)); + } + + /** + * 移除事件 + * @param name 事件名称 + * @param callback 回调函数 + * @param context 上下文 + */ + public static removeListener(name: string, callback: () => void, context: any) { + const observers: Observer[] = AxMessageSystem.listeners[name]; + if (!observers) { return; } + const length = observers.length; + for (let i = 0; i < length; i++) { + const observer = observers[i]; + if (observer.compar(context)) { + observers.splice(i, 1); + break; + } + } + if (observers.length === 0) { + delete AxMessageSystem.listeners[name]; + } + } + + /** + * 发送事件 + * @param name 事件名称 + */ + public static send(name: string, ...args: any[]) { + const observers: Observer[] = AxMessageSystem.listeners[name]; + if (!observers) { return; } + const length = observers.length; + for (let i = 0; i < length; i++) { + const observer = observers[i]; + observer.notify(name, ...args); + } + } +} + +/** + * 观察者 + */ +class Observer { + /** 回调函数 */ + private callback: () => void; + /** 上下文 */ + private context: any = null; + + constructor(callback: () => void, context: any) { + const self = this; + self.callback = callback; + self.context = context; + } + + /** + * 发送通知 + * @param args 不定参数 + */ + notify(...args: any[]): void { + const self = this; + self.callback.call(self.context, ...args); + } + + /** + * 上下文比较 + * @param context 上下文 + */ + compar(context: any): boolean { + return context === this.context; + } +} diff --git a/src/app/working-area/model/axRectangleShape.ts b/src/app/working-area/model/axRectangleShape.ts index 297ffdb..cf61f6c 100644 --- a/src/app/working-area/model/axRectangleShape.ts +++ b/src/app/working-area/model/axRectangleShape.ts @@ -1,21 +1,21 @@ -import { Sprite } from "pixi.js"; -import { Graphics } from "pixi.js"; -import { WorkingAreaComponent } from "../working-area.component"; -import { AxShape } from "./axShape"; +import { Sprite } from 'pixi.js'; +import { Graphics } from 'pixi.js'; +import { WorkingAreaComponent } from '../working-area.component'; +import { AxShape } from './axShape'; -export class AxRectangleShape extends AxShape{ +export class AxRectangleShape extends AxShape { /** * */ - constructor(x:number,y:number,width:number,height:number,assetData: any, workingArea: WorkingAreaComponent) { - super(assetData,workingArea); - this.beginFill(0x0000ff,1); - this.lineStyle(1, 0xff0000,1); + constructor(x: number, y: number, width: number, height: number, assetData: any, workingArea: WorkingAreaComponent) { + super(assetData, workingArea); + this.beginFill(0x0000ff, 1); + this.lineStyle(1, 0xff0000, 1); this.drawRect(x, y, width, height); this.endFill(); - - + + } - + } diff --git a/src/app/working-area/model/axSelection.ts b/src/app/working-area/model/axSelection.ts new file mode 100644 index 0000000..f075523 --- /dev/null +++ b/src/app/working-area/model/axSelection.ts @@ -0,0 +1,65 @@ +import { allowedNodeEnvironmentFlags } from "process"; + +/** + * 选择器 + */ +export class AxSelection { + constructor() { + } + private objects: Set = new Set(); + // 获得第一个对象 + public first(): any { + if (this.objects.size > 0) { + return [...this.objects][0]; + } else { + return null; + } + } + // 是否已经选择了对象 + public has(obj: any): boolean { + return this.objects.has(obj); + } + // 是否所有选择对象都允许编辑 + public allowEdit(): boolean { + let allowEdit = true; + for (const item of this.objects) { + if (!item.allowEdit) { + allowEdit = false; + break; + } + } + return allowEdit; + } + // 获得所有对象 + public all() { + return [...this.objects]; + } + // 获取集合长度 + public size(): number { + return this.objects.size; + } + // 添加对象 + public add(obj: any) { + this.objects.add(obj); + } + // 添加集合 + public addArray(array: any[]) { + array.forEach(item => { + this.objects.add(item); + }); + } + // 移除对象 + public delete(obj: any) { + this.objects.delete(obj); + } + // 移除集合 + public deleteArray(array: any[]) { + array.forEach(item => { + this.objects.delete(item); + }); + } + // 清空所有对象 + public clear() { + this.objects.clear(); + } +} diff --git a/src/app/working-area/model/axShape.ts b/src/app/working-area/model/axShape.ts index 27c1773..9409a21 100644 --- a/src/app/working-area/model/axShape.ts +++ b/src/app/working-area/model/axShape.ts @@ -8,19 +8,21 @@ import { WorkingAreaComponent } from '../working-area.component'; */ export class AxShape extends Graphics { assetData: any; - pointTexture: PIXI.Texture = PIXI.Texture.from('assets/images/handle-main.png') + pointTexture: PIXI.Texture = PIXI.Texture.from('assets/images/handle-main.png'); workingArea: WorkingAreaComponent; - // 可以被移动的 - moveable = true; - // 可以被选中的 - selectable = true; + // 允许选择 + allowSelect = true; // 允许编辑 allowEdit = true; // 是否显示名称 showName = true; // 边框 border: PIXI.Graphics = new PIXI.Graphics(); - + // 鼠标位置 + mousePosition: PIXI.Point; + // 鼠标拖动 + mouseDragging: boolean; + constructor(assetData: any, workingArea: WorkingAreaComponent) { super(); this.border.visible = false; @@ -34,51 +36,31 @@ export class AxShape extends Graphics { this .on('pointerdown', event => { event.stopPropagation(); - if (this.selectable) { - this.workingArea.selection.selectOne(this); + if (this.allowSelect) { + this.workingArea.select(this); } - if (this.moveable) { - event.currentTarget.data = event.data; - event.currentTarget.alpha = 0.5; - event.currentTarget.dragging = true; - - event.currentTarget.dragPoint = event.data.getLocalPosition(event.currentTarget.parent); - event.currentTarget.dragPoint.x -= event.currentTarget.x; - event.currentTarget.dragPoint.y -= event.currentTarget.y; + if (this.allowEdit) { + this.mouseDragging = true; + this.mousePosition = new PIXI.Point(event.data.global.x, event.data.global.y); } }) .on('pointerup', event => { - if (event.currentTarget.dragging) { - event.currentTarget.alpha = 1; - event.currentTarget.dragging = false; - event.currentTarget.data = null; - } + this.mouseDragging = false; }) .on('pointerupoutside', event => { - if (event.currentTarget.dragging) { - event.currentTarget.alpha = 1; - event.currentTarget.dragging = false; - event.currentTarget.data = null; - } + this.mouseDragging = false; }) .on('pointermove', event => { - if (event.currentTarget.dragging) { - const newPosition = event.currentTarget.data.getLocalPosition(event.currentTarget.parent); - - // const offsetX = newPosition.x - event.currentTarget.dragPoint.x; - // const offsetY = newPosition.y - event.currentTarget.dragPoint.y; - // const offset = this.workingArea.backgroundImage.toLocal(new Point(offsetX, offsetY)); - // event.currentTarget.position += offset; - // // this.workingArea.selection.objects.forEach(shpae => { - // // shpae.x = newPosition.x - event.currentTarget.dragPoint.x; - // // shpae.y = newPosition.y - event.currentTarget.dragPoint.y; - // // shpae.assetData.Point = new PIXI.Point(this.x, this.y); - // // this.workingArea.canvasData.isChange = true; - // // }) - event.currentTarget.x = newPosition.x - event.currentTarget.dragPoint.x; - event.currentTarget.y = newPosition.y - event.currentTarget.dragPoint.y; - this.assetData.Point = new PIXI.Point(this.x, this.y); - this.workingArea.canvasData.isChange = true; + if (this.mouseDragging) { + this.workingArea.selection.all().forEach(item => { + const x = event.data.global.x - this.mousePosition.x; + const y = event.data.global.y - this.mousePosition.y; + item.x += x * (1 / this.workingArea.camera2D.scale.x); + item.y += y * (1 / this.workingArea.camera2D.scale.y); + item.assetData.Point = new PIXI.Point(item.x, item.y); + this.workingArea.canvasData.isChange = true; + }); + this.mousePosition = new PIXI.Point(event.data.global.x, event.data.global.y); } }) .on('rightclick', event => { @@ -86,14 +68,14 @@ export class AxShape extends Graphics { }); } redraw(): void { - + } - refresh(): void{ - + refresh(): void { + } public setItemScale(scale: number) { - + } public showBorder() { @@ -110,10 +92,10 @@ export class AxShape extends Graphics { * @param value 显示状态 */ public setPointVisiable(value: boolean) { - + } /** - * + * * @param rect 画边框 */ public drawBorder(scale: number) { @@ -127,14 +109,14 @@ export class AxShape extends Graphics { this.border.lineStyle(scale * 1, 0x00a8ff); - var spaceLength = scale * 1; - var lineLenght = rect.width + 0.5 + 0.5; - var dashLength = scale*( lineLenght +spaceLength - Math.floor((rect.width + rect.height)/2 / 4.1))/Math.floor((rect.width + rect.height)/2 / 4.1); - this.drawDash(this.border, p1.x -0.5*scale, p1.y, p2.x + 0.5*scale, p2.y,dashLength,spaceLength); - this.drawDash(this.border, p2.x, p2.y -0.5*scale, p3.x, p3.y + 0.5*scale, dashLength, spaceLength); - this.drawDash(this.border, p3.x+0.5*scale, p3.y, p4.x - 0.5*scale, p4.y, dashLength, spaceLength); - this.drawDash(this.border, p4.x, p4.y + 0.5*scale, p1.x, p1.y - 0.5*scale, dashLength, spaceLength); - + const spaceLength = scale * 1; + const lineLenght = rect.width + 0.5 + 0.5; + const dashLength = scale * ( lineLenght + spaceLength - Math.floor((rect.width + rect.height) / 2 / 4.1)) / Math.floor((rect.width + rect.height) / 2 / 4.1); + this.drawDash(this.border, p1.x - 0.5 * scale, p1.y, p2.x + 0.5 * scale, p2.y, dashLength, spaceLength); + this.drawDash(this.border, p2.x, p2.y - 0.5 * scale, p3.x, p3.y + 0.5 * scale, dashLength, spaceLength); + this.drawDash(this.border, p3.x + 0.5 * scale, p3.y, p4.x - 0.5 * scale, p4.y, dashLength, spaceLength); + this.drawDash(this.border, p4.x, p4.y + 0.5 * scale, p1.x, p1.y - 0.5 * scale, dashLength, spaceLength); + this.border.lineStyle(0, 0x0000ff); // this.border.beginFill(0x00ff00,0.1); this.border.moveTo(p1.x, p1.y); @@ -145,19 +127,19 @@ export class AxShape extends Graphics { // this.border.endFill(); } // 画虚线 - drawDash(target, x1, y1, x2, y2,dashLength = 5, spaceLength = 1) { - let x = x2 - x1; - let y = y2 - y1; + drawDash(target, x1, y1, x2, y2, dashLength = 5, spaceLength = 1) { + const x = x2 - x1; + const y = y2 - y1; let hyp = Math.sqrt((x) * (x) + (y) * (y)); - let units = hyp / (dashLength + spaceLength); - let dashSpaceRatio = dashLength / (dashLength + spaceLength); - let dashX = (x / units) * dashSpaceRatio; - let spaceX = (x / units) - dashX; - let dashY = (y / units) * dashSpaceRatio; - let spaceY = (y / units) - dashY; + const units = hyp / (dashLength + spaceLength); + const dashSpaceRatio = dashLength / (dashLength + spaceLength); + const dashX = (x / units) * dashSpaceRatio; + const spaceX = (x / units) - dashX; + const dashY = (y / units) * dashSpaceRatio; + const spaceY = (y / units) - dashY; target.moveTo(x1, y1); - + while (hyp > 0) { x1 += dashX; y1 += dashY; @@ -200,7 +182,7 @@ export class AxShape extends Graphics { return new PIXI.Point(gravityLat, gravityLng); } // 计算线段中点坐标 - public getLineCenter(point1:PIXI.Point,point2:PIXI.Point) { - return new PIXI.Point((point1.x+point2.x)/2,(point1.y+point2.y)/2) + public getLineCenter(point1: PIXI.Point, point2: PIXI.Point) { + return new PIXI.Point((point1.x + point2.x) / 2, (point1.y + point2.y) / 2); } } diff --git a/src/app/working-area/model/configuration.ts b/src/app/working-area/model/configuration.ts new file mode 100644 index 0000000..bf0507a --- /dev/null +++ b/src/app/working-area/model/configuration.ts @@ -0,0 +1,164 @@ +import { EventDispatcher } from 'three'; +import { EVENT_CHANGED } from './events'; + + +// GENERAL: +/** The dimensioning unit for 2D floorplan measurements. */ +export var configDimUnit = 'dimUnit'; +// WALL: +/** The initial wall height in cm. */ +export const configWallHeight = 'wallHeight'; +/** The initial wall thickness in cm. */ +export const configWallThickness = 'wallThickness'; + +export const configSystemUI = 'systemUI'; + +export const scale = 'scale'; + +export const gridSpacing = 'gridSpacing'; +export const snapToGrid = 'snapToGrid'; +export const directionalDrag = 'directionalDrag'; +export const dragOnlyX = 'dragOnlyX'; +export const dragOnlyY = 'dragOnlyY'; +export const snapTolerance = 'snapTolerance'; //In CMS +export const boundsX = 'boundsX'; //In CMS +export const boundsY = 'boundsY'; //In CMS +export const viewBounds = 'viewBounds';//In CMS + +export const dimInch = 'inch'; + +/** Dimensioning in Inch. */ +export const dimFeetAndInch = 'feetAndInch'; + +/** Dimensioning in Meter. */ +export const dimMeter = 'm'; + +/** Dimensioning in Centi Meter. */ +export const dimCentiMeter = 'cm'; + +/** Dimensioning in Milli Meter. */ +export const dimMilliMeter = 'mm'; + +export const VIEW_TOP = 'topview'; +export const VIEW_FRONT = 'frontview'; +export const VIEW_RIGHT = 'rightview'; +export const VIEW_LEFT = 'leftview'; +export const VIEW_ISOMETRY = 'isometryview'; + +export enum WallTypes{ + STRAIGHT, + CURVED +} + +export const TEXTURE_DEFAULT_REPEAT = 300; +export const defaultWallTexture = +{ + color: '#FFFFFF', repeat: TEXTURE_DEFAULT_REPEAT, normalmap: 'textures/Wall/Brick_Wall_017_SD/Brick_Wall_017_normal.jpg', roughnessmap: 'textures/Wall/Brick_Wall_017_SD/Brick_Wall_017_roughness.jpg', colormap: 'textures/Wall/Brick_Wall_017_SD/Brick_Wall_017_basecolor.jpg', ambientmap: 'textures/Wall/Brick_Wall_017_SD/Brick_Wall_017_ambientOcclusion.jpg', bumpmap: 'textures/Wall/Brick_Wall_017_SD/Brick_Wall_017_height.png' +}; +export const defaultFloorTexture = +{ + color: '#FFFFFF', emissive: '#181818', repeat: TEXTURE_DEFAULT_REPEAT, ambientmap: 'textures/Floor/Marble_Tiles_001/Marble_Tiles_001_ambientOcclusion.jpg', colormap: 'textures/Floor/Marble_Tiles_001/Marble_Tiles_001_basecolor.jpg', roughnessmap: 'textures/Floor/Marble_Tiles_001/Marble_Tiles_001_roughness.jpg', normalmap: 'textures/Floor/Marble_Tiles_001/Marble_Tiles_001_normal.jpg' +}; + +export const TEXTURE_PROPERTY_COLOR = 'color'; +export const TEXTURE_NO_PREVIEW = 'textures/NoPreview.jpg'; + +export var config = { + dimUnit: dimCentiMeter, + wallHeight: 250, + wallThickness: 20, + systemUI: false, + scale: 1, + snapToGrid: true, + dragOnlyX: false, + dragOnlyY: false, + snapTolerance: 50, + gridSpacing: 20, // 50, + directionalDrag: true, + boundsX: 500, + boundsY: 500, + viewBounds: 20000 }; + +export var wallInformation = { exterior: false, interior: false, midline: true, labels: true, exteriorlabel: 'e:', interiorlabel: 'i:', midlinelabel: 'm:' }; + + +/** + * The tolerance in cms between corners, otherwise below this tolerance they will snap together as one corner*/ +export const cornerTolerance = 20; + +/** Global configuration to customize the whole system. + * This is a singleton instance; + */ +export class Configuration extends EventDispatcher { + private static instance = new Configuration(); + constructor() { + /** Configuration data loaded from/stored to extern. */ + // this.data = {dimUnit: dimCentiMeter, wallHeight: 250, wallThickness: 10}; + super(); + } + + static getInstance() { + if (this.instance === undefined + || this.instance === null) { + this.instance = new Configuration(); + } + return this.instance; + } + + static getData() { + // return {dimUnit: dimCentiMeter,wallHeight: 250, wallThickness: 10}; + return config; + } + + /** Set a configuration parameter. */ + static setValue(key, value) { + // this.data[key] = value; + config[key] = value; + // if(key !== viewBounds){ + Configuration.getInstance().dispatchEvent({ type: EVENT_CHANGED, item: Configuration.getInstance(), 'key': key, 'value': value }); + // } + } + + /** Get a string configuration parameter. */ + static getStringValue(key) { + switch (key) { + case configDimUnit: + // return String(this.data[key]); + return String(Configuration.getData()[key]); + default: + throw new Error('Invalid string configuration parameter: ' + key); + } + } + + /** Get a numeric configuration parameter. */ + static getNumericValue(key) { + switch (key) { + case configSystemUI: + case configWallHeight: + case configWallThickness: + case scale: + case snapTolerance: + case gridSpacing: + case boundsX: + case boundsY: + case viewBounds: + // return Number(this.data[key]); + return Number(Configuration.getData()[key]); + default: + throw new Error('Invalid numeric configuration parameter: ' + key); + } + } + + /** Get a numeric configuration parameter. */ + static getBooleanValue(key) { + switch (key) { + case snapToGrid: + case directionalDrag: + case dragOnlyX: + case dragOnlyY: + return Boolean(Configuration.getData()[key]); + default: + throw new Error('Invalid Boolean configuration parameter: ' + key); + } + } +} \ No newline at end of file diff --git a/src/app/working-area/model/dimensioning.ts b/src/app/working-area/model/dimensioning.ts new file mode 100644 index 0000000..f7a3fc4 --- /dev/null +++ b/src/app/working-area/model/dimensioning.ts @@ -0,0 +1,145 @@ +import { Vector2, Vector3 } from 'three'; +import { Configuration, configDimUnit,dimInch, dimFeetAndInch, dimMeter, dimCentiMeter, dimMilliMeter } from './configuration'; + +export const decimals = 1000; + +export const cmPerFoot = 30.48; +export const pixelsPerFoot = 5.0; + +export const pixelsPerCm = 1; // 0.5; +export const cmPerPixel = (1.0 / pixelsPerCm); + + +export const dimensioningOptions = [dimInch, dimFeetAndInch, dimMeter, dimCentiMeter, dimMilliMeter]; + + +/** Dimensioning functions. */ +export class Dimensioning { + static cmToPixelVector2D(cmV2d) { + let pixelV2d = new Vector2(Dimensioning.cmToPixel(cmV2d.x), Dimensioning.cmToPixel(cmV2d.y)); + return pixelV2d; + } + + static cmToPixelVector3D(cmV3d) { + let pixelV2d = new Vector3(Dimensioning.cmToPixel(cmV3d.x), Dimensioning.cmToPixel(cmV3d.y), Dimensioning.cmToPixel(cmV3d.z)); + return pixelV2d; + } + + static pixelToCmVector2D(pixelV2d) { + let cmV2d = new Vector2(Dimensioning.cmToPixel(pixelV2d.x), Dimensioning.cmToPixel(pixelV2d.y)); + return cmV2d; + } + + static pixelToCmVector3D(pixel3d) { + let cmV2d = new Vector3(Dimensioning.cmToPixel(pixel3d.x), Dimensioning.cmToPixel(pixel3d.y), Dimensioning.cmToPixel(pixel3d.z)); + return cmV2d; + } + + static cmToPixel(cm, apply_scale = true) { + if (apply_scale) { + return cm * pixelsPerCm * Configuration.getNumericValue('scale'); + } + return cm * pixelsPerCm; + } + + static pixelToCm(pixel, apply_scale = true) { + if (apply_scale) { + return pixel * cmPerPixel * (1.0 / Configuration.getNumericValue('scale')); + } + return pixel * cmPerPixel; + } + + static roundOff(value, decimals) { + return Math.round(decimals * value) / decimals; + } + /** Converts cm to dimensioning number. + * @param cm Centi meter value to be converted. + * @returns Number representation. + */ + static cmFromMeasureRaw(measure) { + switch (Configuration.getStringValue(configDimUnit)) { + case dimFeetAndInch: + return Math.round(decimals * (measure * 30.480016459203095991)) / decimals; + case dimInch: + return Math.round(decimals * (measure * 2.5400013716002578512)) / decimals; + case dimMilliMeter: + return Math.round(decimals * (measure * 0.10000005400001014955)) / decimals; + case dimCentiMeter: + return measure; + case dimMeter: + default: + return Math.round(decimals * 100 * measure) / decimals; + } + } + + /** Converts cm to dimensioning string. + * @param cm Centi meter value to be converted. + * @returns String representation. + */ + static cmFromMeasure(measure) { + switch (Configuration.getStringValue(configDimUnit)) { + case dimFeetAndInch: + return Math.round(decimals * (measure * 30.480016459203095991)) / decimals + 'cm'; + case dimInch: + return Math.round(decimals * (measure * 2.5400013716002578512)) / decimals + 'cm'; + case dimMilliMeter: + return Math.round(decimals * (measure * 0.10000005400001014955)) / decimals + 'cm'; + case dimCentiMeter: + return measure; + case dimMeter: + default: + return Math.round(decimals * 100 * measure) / decimals + 'cm'; + } + } + + /** Converts cm to dimensioning string. + * @param cm Centi meter value to be converted. + * @returns String representation. + */ + static cmToMeasureRaw(cm, power = 1) { + switch (Configuration.getStringValue(configDimUnit)) { + case dimFeetAndInch: // dimFeetAndInch returns only the feet + var allInFeet = (cm * Math.pow(0.032808416666669996953, power)); + return allInFeet; + case dimInch: + var inches = Math.round(decimals * (cm * Math.pow(0.393700, power))) / decimals; + return inches; + case dimMilliMeter: + var mm = Math.round(decimals * (cm * Math.pow(10, power))) / decimals; + return mm; + case dimCentiMeter: + return Math.round(decimals * cm) / decimals; + case dimMeter: + default: + var m = Math.round(decimals * (cm * Math.pow(0.01, power))) / decimals; + return m; + } + } + + /** Converts cm to dimensioning string. + * @param cm Centi meter value to be converted. + * @returns String representation. + */ + static cmToMeasure(cm, power = 1) { + switch (Configuration.getStringValue(configDimUnit)) { + case dimFeetAndInch: + var allInFeet = (cm * Math.pow(0.032808416666669996953, power)); + var floorFeet = Math.floor(allInFeet); + var remainingFeet = allInFeet - floorFeet; + var remainingInches = Math.round(remainingFeet * 12); + return floorFeet + '\'' + remainingInches + ''; + case dimInch: + var inches = Math.round(decimals * (cm * Math.pow(0.393700, power))) / decimals; + return inches + '\''; + case dimMilliMeter: + var mm = Math.round(decimals * (cm * Math.pow(10, power))) / decimals; + return '' + mm + 'mm'; + case dimCentiMeter: + return '' + Math.round(decimals * cm) / decimals + 'cm'; + case dimMeter: + default: + var m = Math.round(decimals * (cm * Math.pow(0.01, power))) / decimals; + return '' + m + 'm'; + } + } +} \ No newline at end of file diff --git a/src/app/working-area/model/events.ts b/src/app/working-area/model/events.ts new file mode 100644 index 0000000..2cd6cf5 --- /dev/null +++ b/src/app/working-area/model/events.ts @@ -0,0 +1,67 @@ +export const EVENT_ACTION = 'ACTION_EVENT'; +export const EVENT_DELETED = 'DELETED_EVENT'; +export const EVENT_MOVED = 'MOVED_EVENT'; +export const EVENT_REDRAW = 'REDRAW_EVENT'; +export const EVENT_NEW = 'NEW_EVENT'; +export const EVENT_LOADED = 'LOADED_EVENT'; +export const EVENT_LOADING = 'LOADING_EVENT'; +export const EVENT_UPDATED = 'UPDATED_EVENT'; +export const EVENT_SAVED = 'SAVED_EVENT'; +export const EVENT_CHANGED = 'CHANGED_EVENT'; +export const EVENT_GLTF_READY = 'GLTF_READY_EVENT'; + +export const EVENT_EXTERNAL_FLOORPLAN_LOADED = 'EXTERNAL_FLOORPLAN_LOADED_EVENT'; + +export const EVENT_NEW_PARAMETRIC_ITEM = 'NEW_PARAMETRIC_ITEM_EVENT'; +export const EVENT_NEW_ITEM = 'NEW_ITEM_EVENT'; +export const EVENT_ITEM_LOADING = 'ITEM_LOADING_EVENT'; +export const EVENT_ITEM_LOADED = 'ITEM_LOADED_EVENT'; +export const EVENT_ITEM_REMOVED = 'ITEM_REMOVED_EVENT'; + +export const EVENT_ITEM_SELECTED = 'ITEM_SELECTED_EVENT'; +export const EVENT_ITEM_MOVE = 'ITEM_MOVED_EVENT'; +export const EVENT_ITEM_MOVE_FINISH = 'ITEM_MOVED_FINISH_EVENT'; +export const EVENT_ITEM_HOVERON = 'ITEM_HOVERON_EVENT'; +export const EVENT_ITEM_HOVEROFF = 'ITEM_HOVEROFF_EVENT'; +export const EVENT_NO_ITEM_SELECTED = 'ITEM_NO_SELECTED_EVENT'; + +export const EVENT_MODE_RESET = 'MODE_RESET_EVENT'; +export const EVENT_CAMERA_MOVED = 'CAMERA_MOVED_EVENT'; +export const EVENT_CAMERA_ACTIVE_STATUS = 'CAMERA_ACTIVE_STATUS_EVENT'; +export const EVENT_CAMERA_VIEW_CHANGE = 'CAMERA_VIEW_CHANGE_EVENT'; +export const EVENT_FPS_EXIT = 'CAMERA_FPS_EXIT_EVENT'; + +export const EVENT_WALL_CLICKED = 'WALL_CLICKED_EVENT'; +export const EVENT_ROOM_CLICKED = 'ROOM_CLICKED_EVENT'; +export const EVENT_FLOOR_CLICKED = 'FLOOR_CLICKED_EVENT'; +export const EVENT_NOTHING_CLICKED = 'NOTHING_CLICKED_EVENT'; + +export const EVENT_ROOM_NAME_CHANGED = 'CHANGED_ROOM_NAME_EVENT'; +export const EVENT_NEW_ROOMS_ADDED = 'ADDED_NEW_ROOMS_EVENT'; + +export const EVENT_CORNER_ATTRIBUTES_CHANGED = 'CORNER_ATTRIBUTES_CHANGED_EVENT'; +export const EVENT_WALL_ATTRIBUTES_CHANGED = 'WALL_ATTRIBUTES_CHANGED_EVENT'; +export const EVENT_ROOM_ATTRIBUTES_CHANGED = 'ROOM_ATTRIBUTES_CHANGED_EVENT'; + +export const EVENT_CORNER_2D_CLICKED = 'CORNER_CLICKED_2D_EVENT'; +export const EVENT_WALL_2D_CLICKED = 'WALL_CLICKED_2D_EVENT'; +export const EVENT_ROOM_2D_CLICKED = 'ROOM_CLICKED_2D_EVENT'; +export const EVENT_2D_UNSELECTED = 'UNSELECTED_2D_EVENT'; +export const EVENT_2D_SELECTED = 'SELECTED_2D_EVENT'; +export const EVENT_NOTHING_2D_SELECTED = 'NOTHING_2D_SELECTED_EVENT'; + +export const EVENT_CORNER_2D_DOUBLE_CLICKED = 'CORNER_DOUBLE_CLICKED_2D_EVENT'; +export const EVENT_WALL_2D_DOUBLE_CLICKED = 'WALL_DOUBLE_CLICKED_2D_EVENT'; +export const EVENT_ROOM_2D_DOUBLE_CLICKED = 'ROOM_DOUBLE_CLICKED_2D_EVENT'; + +export const EVENT_CORNER_2D_HOVER = 'CORNER_HOVER_2D_EVENT'; +export const EVENT_WALL_2D_HOVER = 'WALL_HOVER_2D_EVENT'; +export const EVENT_ROOM_2D_HOVER = 'ROOM_HOVER_2D_EVENT'; + +export const EVENT_KEY_PRESSED = 'KEY_PRESSED_EVENT'; +export const EVENT_KEY_RELEASED = 'KEY_RELEASED_EVENT'; + +export const EVENT_UPDATE_TEXTURES = 'UPDATE_TEXTURES_EVENT'; +export const EVENT_MODIFY_TEXTURE_ATTRIBUTE = 'MODIFY_TEXTURE_ATTRIBUTE_EVENT'; + +export const EVENT_PARAMETRIC_GEOMETRY_UPATED = 'PARAMETRIC_GEOMETRY_UPATED_EVENT'; diff --git a/src/app/working-area/model/grid2D.ts b/src/app/working-area/model/grid2D.ts new file mode 100644 index 0000000..9683a49 --- /dev/null +++ b/src/app/working-area/model/grid2D.ts @@ -0,0 +1,87 @@ +import { Configuration, gridSpacing, viewBounds } from './configuration'; +import { EVENT_CHANGED } from './events'; +import { Graphics } from 'pixi.js'; +import { Vector2 } from 'three'; +import { Dimensioning } from './dimensioning'; + +const GRID_SIZE = 10000; + +export class Grid2D extends Graphics { + + canvas; + options; + size; + gridScale; + constructor(canvas, options) { + super(); + // this.drawRect(0, 0, GRID_SIZE, GRID_SIZE); + this.canvas = canvas; + this.options = options; + this.size = new Vector2(GRID_SIZE, GRID_SIZE); + this.gridScale = 1.0; + this.width = this.size.x; + this.height = this.size.y; + this.drawRect(0, 0, GRID_SIZE, GRID_SIZE); + this.pivot.x = this.pivot.y = 0.5; + Configuration.getInstance().addEventListener(EVENT_CHANGED, (evt) => this.updateGrid()); + this.updateGrid(); + } + + updateGrid() { + let gridSize = Dimensioning.cmToPixel(Configuration.getNumericValue(viewBounds) * 1); + let spacingCMS = Configuration.getNumericValue(gridSpacing); + let spacing = Dimensioning.cmToPixel(spacingCMS); + let totalLines = gridSize / spacing; + let halfSize = gridSize * 0.5; + let linewidth = Math.max(1.0 / this.gridScale, 1.0) * 1/this.canvas.scale.x;// 增加缩放系数 + let highlightLineWidth = Math.max(1 / this.gridScale, 1.0) * 1/this.canvas.scale.x;// 增加缩放系数 + let normalColor = 0xE0E0E0; + let highlightColor = 0xD0D0D0; + const cellSize = 5; + this.clear(); + for (let i = 0; i <= totalLines; i++) { + let co = (i * spacing) - halfSize; + if (i % cellSize === 0) { + this.lineStyle(highlightLineWidth, highlightColor).moveTo(-halfSize, co).lineTo(halfSize, co); + this.lineStyle(highlightLineWidth, highlightColor).moveTo(co, -halfSize).lineTo(co, halfSize); + } else { + this.lineStyle(linewidth, normalColor).moveTo(-halfSize, co).lineTo(halfSize, co); + this.lineStyle(linewidth, normalColor).moveTo(co, -halfSize).lineTo(co, halfSize); + } + } + this.endFill(); + + this.beginFill(0xFF0000, 1.0); + this.drawCircle(-halfSize, -halfSize,5); + this.drawCircle(halfSize, -halfSize,5); + this.drawCircle(halfSize, halfSize,5); + this.drawCircle(-halfSize, halfSize,5); + this.drawCircle(0, 0, 5); + this.endFill(); + } + + getGridScale() { + return this.gridScale; + } + + setGridScale(value) { + this.gridScale = value; + this.updateGrid(); + } + + configurationUpdate(evt) { + if (evt.key === gridSpacing) { + this.updateGrid(); + } + } + getCellCoordinates(x, y) { + let gridSize = Dimensioning.cmToPixel(Configuration.getNumericValue(viewBounds) * 1); + let spacingCMS = Configuration.getNumericValue(gridSpacing); + let spacing = Dimensioning.cmToPixel(spacingCMS); + let halfSize = gridSize * 0.5; + return { + x: Math.floor((x - -halfSize) / spacing), + y: Math.floor((y - -halfSize) / spacing), + }; + } +} diff --git a/src/app/working-area/model/messageSystem.ts b/src/app/working-area/model/messageSystem.ts deleted file mode 100644 index 322eef4..0000000 --- a/src/app/working-area/model/messageSystem.ts +++ /dev/null @@ -1,37 +0,0 @@ -class MyEvent extends CustomEvent { - public static readonly CMD: string = "EVENT_NAME"; - public constructor($type: string , $data: T ) { - super( $type , { detail: $data, bubbles: true, cancelable: true, composed: true }); - } -} - -class MyDispatch extends EventTarget { - private static _instance: MyDispatch; - public static get Instance(): MyDispatch { - if (!MyDispatch._instance) MyDispatch._instance = new MyDispatch(); - return MyDispatch._instance; - } - public send($data: T, $type: string = MyEvent.CMD): void { - const $event: CustomEvent = new MyEvent($type, $data); - this.dispatchEvent($event); - } -} - -class Test { - - public constructor() { - MyDispatch.Instance.addEventListener(MyEvent.CMD, this.onEvent as EventListener); - } - private onEvent($e: MyEvent): void { - console.log(`target ${$e.target}`); - console.log(`name: ${$e.detail._name} , occupation: ${$e.detail._occupation}`); - } -} - -interface ITest { - _name: string; - _occupation: string; -} - -let $test: Test = new Test(); -MyDispatch.Instance.send({ _name: `Aonaufly`, _occupation: `it` }); diff --git a/src/app/working-area/working-area.component.html b/src/app/working-area/working-area.component.html index ee66290..736e057 100644 --- a/src/app/working-area/working-area.component.html +++ b/src/app/working-area/working-area.component.html @@ -1,2 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/src/app/working-area/working-area.component.ts b/src/app/working-area/working-area.component.ts index 04d2c17..127abb7 100644 --- a/src/app/working-area/working-area.component.ts +++ b/src/app/working-area/working-area.component.ts @@ -16,9 +16,11 @@ import { PropertyInfo } from './model/PropertyInfo'; import { AxPreviewImageShape } from './model/axPreviewImageShape'; import { AxArrowConnector } from './model/axArrowConnector'; import { AxLegend, Legend } from './model/axLegend'; -import { NullTemplateVisitor } from '@angular/compiler'; -import { AxRectangleShape } from './model/axRectangleShape'; import { AxGrid } from './model/axGrid'; +import { AxSelection } from './model/axSelection'; +import { AxMessageSystem } from './model/axMessageSystem'; +import { Grid2D } from './model/grid2D'; +import { Viewport } from 'pixi-viewport'; @Component({ @@ -44,7 +46,15 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV /** * pixijs 程序 */ - public app: PIXI.Application; + public app: PIXI.Application = null; + /** + * 相机 + */ + public camera2D: Viewport = null; + /** + * 网格 + */ + public grid2D: Grid2D = null; /** * 资源加载器 */ @@ -76,7 +86,7 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV /** * 选择器 */ - public selection: Selection = new Selection(this); + public readonly selection: AxSelection = new AxSelection(); /** * 当前鼠标的点 */ @@ -109,11 +119,11 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV * 确认绘制按钮 */ private enterPaintEndButton = PIXI.Sprite.from('assets/images/enterPaintButton.png'); - /** * 编辑点图片 */ editorPointTexture: PIXI.Texture = PIXI.Texture.from('assets/images/handle-main.png'); + backgroundTexture: PIXI.Texture = PIXI.Texture.from('assets/images/noImg.png'); /** * 框选工具图形 */ @@ -137,10 +147,8 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV public animation; public animationIcon; public animationTime; - /** - * 网格 - */ - public grid: AxGrid = null; + // 是否按下Ctrl键 + isCtrlKeyClicked = false; /** * 本软件版本号由四部分组成:<主版本号><次版本号><修订版本号><日期加希腊字母版本号> 例如:1.0.0.20210105_beta * Alpha版: 此版本表示该软件在此阶段主要是以实现软件功能为主,通常只在软件开发者内部交流,一般而言,该版本软件的Bug较多,需要继续修改。 @@ -148,7 +156,7 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV * RC版: 该版本已经相当成熟了,基本上不存在导致错误的BUG,与即将发行的正式版相差无几。 * Release版: 该版本意味“最终版本”,在前面版本的一系列测试版之后,终归会有一个正式版本,是最终交付用户使用的一个版本。该版本有时也称为标准版。一般情况下,Release不会以单词形式出现在软件封面上,取而代之的是符号®。 */ - public VERSION = '1.0.10.20210118_beta'; + public VERSION = '1.0.14.20210202_beta'; /** * 数据初始化 */ @@ -156,38 +164,75 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV PIXI.utils.skipHello(); this.sayHello(); this.eventManager.addGlobalEventListener('window', 'keydown', (event: any) => { - if (event.keyCode === 17) { - this.selection.isMultiselection = true; - } + // event.stopPropagation(); + // if (event.keyCode === 17) { + // this.isCtrlKeyClicked = true; + // } }); this.eventManager.addGlobalEventListener('window', 'keyup', (event: any) => { - if (event.keyCode === 17) { - this.selection.isMultiselection = false; - this.rectToolGraphics.visible = false; - this.rectToolGraphics.clear(); - } + // event.stopPropagation(); + // if (event.keyCode === 17) { + // this.isCtrlKeyClicked = false; + // this.rectToolGraphics.visible = false; + // this.rectToolGraphics.clear(); + // } // 按Del键删除选中的图标 if (event.keyCode === 46) { this.deleteSelectedShape(); } }); } + + public setMulitSelect(b: boolean) { + if (b) { + this.isCtrlKeyClicked = true; + this.camera2D.plugins.pause('drag'); + } else { + this.isCtrlKeyClicked = false; + this.camera2D.drag(); + } + } /** * 删除选中的图标 */ public deleteSelectedShape() { - this.selection.objects.forEach(item => { - this.deleteShape(item); - }); - this.selection.deselectAll(); - } - /** - * - * @param obj 删除一个形状 - */ - public deleteShape(shape) { - if (this.allowEdit && this.canvasData.gameMode === shape.assetData.GameMode) { - this.emit('deleteIcon', shape); + if (this.selection.size() > 0) { + this.selection.all().forEach(axShape => { + if (this.allowEdit && this.canvasData.gameMode === axShape.assetData.GameMode) { + // 删除图例对象 + const temp = this.backgroundImage.getChildByName('图例') as AxLegend; + if ( temp !== undefined + && temp !== null + && axShape.assetData.Name !== '图例') { + const itemLegend = new Legend(axShape.assetData.Name, axShape.assetData.ImageUrl, 1); + temp.deleteItem(itemLegend); + } + if (axShape.assetData.GameMode === GameMode.BasicInformation) { // 基本信息 + // 删除楼层数据 + delete this.canvasData.originaleveryStoreyData.data[axShape.assetData.Id]; + // 删除建筑数据 + delete this.canvasData.originalcompanyBuildingData.data[axShape.assetData.Id]; + } else if (axShape.assetData.GameMode === GameMode.Assignment) { // 处置预案 + delete this.canvasData.selectPanelPoint.Data.DefinedIncrement[axShape.assetData.Id]; + delete this.canvasData.selectPanelPoint.Data.Increment[axShape.assetData.Id]; + delete this.canvasData.selectPanelPoint.Data.Stock[axShape.assetData.Id]; + } else if (axShape.assetData.GameMode === GameMode.Examinee) { // 考生考试 + if (axShape.assetData.Tag === 1) { + // 删除楼层数据 + delete this.canvasData.examOriginaleveryStoreyData.data[axShape.assetData.Id]; + } else { + delete this.canvasData.selectPanelPoint.Data.DefinedIncrement[axShape.assetData.Id]; + delete this.canvasData.selectPanelPoint.Data.Increment[axShape.assetData.Id]; + delete this.canvasData.selectPanelPoint.Data.Stock[axShape.assetData.Id]; + } + } + this.backgroundImage.removeChild(axShape); + } + }); + this.selection.clear(); + this.emit('canvasDataChanged'); + this.canvasData.isChange = true; + AxMessageSystem.send(CanvasAction.selectionChanged); } } /** @@ -218,44 +263,44 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV } /** * - * @param event 鼠标滑动事件 - */ - public mouseWheelHandel(event) { - const delX = this.mousePosition.x - this.backgroundImage.position.x; - const delY = this.mousePosition.y - this.backgroundImage.position.y; - const pivot = this.backgroundImage.toLocal(this.mousePosition); - const delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail))); - if (delta > 0) { - if (this.backgroundImage.scale.x >= 32) { - this.backgroundImage.scale.x = 32; - this.backgroundImage.scale.y = 32; - this.resizeItem(1 / this.backgroundImage.scale.x); - return; - } - this.backgroundImage.pivot.set(pivot.x, pivot.y); + * @param event 鼠标滑动事件,改用ViewPort控制 + */ + // public mouseWheelHandel(event) { + // const delX = this.mousePosition.x - this.backgroundImage.position.x; + // const delY = this.mousePosition.y - this.backgroundImage.position.y; + // const pivot = this.backgroundImage.toLocal(this.mousePosition); + // const delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail))); + // if (delta > 0) { + // if (this.backgroundImage.scale.x >= 32) { + // this.backgroundImage.scale.x = 32; + // this.backgroundImage.scale.y = 32; + // this.resizeItem(1 / this.backgroundImage.scale.x); + // return; + // } + // this.backgroundImage.pivot.set(pivot.x, pivot.y); - this.backgroundImage.scale.x += this.backgroundImage.scale.x * 0.1; - this.backgroundImage.scale.y += this.backgroundImage.scale.y * 0.1; + // this.backgroundImage.scale.x += this.backgroundImage.scale.x * 0.1; + // this.backgroundImage.scale.y += this.backgroundImage.scale.y * 0.1; - this.backgroundImage.position.x += delX; - this.backgroundImage.position.y += delY; - } else if (delta < 0) { - if (this.backgroundImage.scale.x <= 0.1) { - this.backgroundImage.scale.x = 0.1; - this.backgroundImage.scale.y = 0.1; - this.resizeItem(1 / this.backgroundImage.scale.x); - return; - } - this.backgroundImage.pivot.set(pivot.x, pivot.y); + // this.backgroundImage.position.x += delX; + // this.backgroundImage.position.y += delY; + // } else if (delta < 0) { + // if (this.backgroundImage.scale.x <= 0.1) { + // this.backgroundImage.scale.x = 0.1; + // this.backgroundImage.scale.y = 0.1; + // this.resizeItem(1 / this.backgroundImage.scale.x); + // return; + // } + // this.backgroundImage.pivot.set(pivot.x, pivot.y); - this.backgroundImage.scale.x -= this.backgroundImage.scale.x * 0.1; - this.backgroundImage.scale.y -= this.backgroundImage.scale.y * 0.1; + // this.backgroundImage.scale.x -= this.backgroundImage.scale.x * 0.1; + // this.backgroundImage.scale.y -= this.backgroundImage.scale.y * 0.1; - this.backgroundImage.position.x += delX; - this.backgroundImage.position.y += delY; - } - this.resizeItem(1 / this.backgroundImage.scale.x); - } + // this.backgroundImage.position.x += delX; + // this.backgroundImage.position.y += delY; + // } + // this.resizeItem(1 / this.backgroundImage.scale.x); + // } // 重置图形缩放 public resizeItem(size: number) { this.backgroundImage.children.forEach(item => { @@ -267,28 +312,149 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV } /** * - * @param icon 移动到选中车辆到屏幕中心点 - */ - public moveIconToScreenCenter(icon) { - if (icon.parent === this.backgroundImage && ( - icon.assetData.Type === 1 || - icon.assetData.Type === 2 || - icon.assetData.Type === 3 || - icon.assetData.Type === 4 - )) { - console.log(this.backgroundImage.position); - this.backgroundImage.pivot.set(icon.x, icon.y); - this.backgroundImage.position.set(771, 404); - clearTimeout(this.animationTime); - this.animation?.pause(); - this.animationIcon?.scale.set(1); - this.animation = this.animator.breathe(icon, 10, 10, 30, true, 0); - this.animationIcon = icon; - this.animationTime = setTimeout(() => { - this.animation?.pause(); - this.animationIcon?.scale.set(1); - }, 5000); - } + * @param icon 移动到选中车辆到屏幕中心点,改用Viewport控制 + */ + // public moveIconToScreenCenter(icon) { + // if (icon.parent === this.backgroundImage && ( + // icon.assetData.Type === 1 || + // icon.assetData.Type === 2 || + // icon.assetData.Type === 3 || + // icon.assetData.Type === 4 + // )) { + // this.backgroundImage.pivot.set(icon.x, icon.y); + // this.backgroundImage.position.set(771, 404); + // clearTimeout(this.animationTime); + // this.animation?.pause(); + // this.animationIcon?.scale.set(1); + // this.animation = this.animator.breathe(icon, 10, 10, 30, true, 0); + // this.animationIcon = icon; + // this.animationTime = setTimeout(() => { + // this.animation?.pause(); + // this.animationIcon?.scale.set(1); + // }, 5000); + // } + // } + + + /** + * 创建2D相机 + */ + private createViewport(): void { + this.camera2D = new Viewport({ + screenWidth: this.app.view.width, + screenHeight: this.app.view.height, + worldWidth: 20000, + worldHeight: 20000, + interaction: this.app.renderer.plugins.interaction, + }); + + this.app.stage.addChild(this.camera2D); + + this.camera2D + .clamp({ + left: -10000, + right: 10000, + top: -10000, + bottom: 10000, + }) + .drag() + .pinch() + .wheel() + .clampZoom({ + minScale: 0.12, + maxScale: 16, + }) + .decelerate(); + + this.camera2D.on('wheel', event => { + this.updateCamera2D(); + }); + + this.camera2D.on('pointerdown', event => { + if (this.isCtrlKeyClicked === true) { + this.rectToolGraphics.visible = true; + this.initialScreenMousePos = this.backgroundImage.toLocal(this.mousePosition); + this.finalScreenMousePos = this.backgroundImage.toLocal(this.mousePosition); + } + }); + this.camera2D.on('pointerup', event => { + if (this.isCtrlKeyClicked === true) { + this.rectToolGraphics.visible = false; + const shapes: AxShape[] = []; + this.backgroundImage.children.forEach(item => { + if ( item instanceof AxShape + && item instanceof AxPreviewImageShape === false) { + // 判断2个矩形是否相交 + const rect1 = this.rectToolGraphics.getBounds(); + const rect2 = item.getBounds(); + if (this.isOverlap(rect1, rect2)) { + shapes.push(item); + } + } + }); + this.rectToolGraphics.clear(); + this.selectAll(shapes); + } + }); + this.camera2D.on('pointerupoutside', event => { + if (this.isCtrlKeyClicked === true) { + this.rectToolGraphics.visible = false; + const shapes: AxShape[] = []; + this.backgroundImage.children.forEach(item => { + if ( item instanceof AxShape + && item instanceof AxPreviewImageShape === false) { + // 判断2个矩形是否相交 + const rect1 = this.rectToolGraphics.getBounds(); + const rect2 = item.getBounds(); + if (this.isOverlap(rect1, rect2)) { + shapes.push(item); + } + } + }); + this.rectToolGraphics.clear(); + this.selectAll(shapes); + } + }); + this.camera2D.on('pointermove', event => { + if (this.isCtrlKeyClicked === true + && this.rectToolGraphics.visible === true) { + this.finalScreenMousePos = this.backgroundImage.toLocal(this.mousePosition); + + const init = this.initialScreenMousePos; + const final = this.finalScreenMousePos; + + this.rectToolGraphics.clear(); + this.rectToolGraphics.lineStyle(2, 0x00ff00, 1); + this.rectToolGraphics.beginFill(0xccccf2, 0.25); + if (final.x > init.x && final.y > init.y) { + this.rectToolGraphics.drawRect(init.x, init.y, final.x - init.x, final.y - init.y); + } else if (final.x > init.x && final.y < init.y) { + this.rectToolGraphics.drawRect(init.x, final.y, final.x - init.x, init.y - final.y); + } else if (final.x < init.x && final.y > init.y) { + this.rectToolGraphics.drawRect(final.x, init.y, init.x - final.x, final.y - init.y); + } else if (final.x < init.x && final.y < init.y) { + this.rectToolGraphics.drawRect(final.x, final.y, init.x - final.x, init.y - final.y); + } + this.rectToolGraphics.endFill(); + } + }); + + } + /** + * 更新2D相机 + */ + private updateCamera2D() { + this.grid2D.updateGrid(); + this.resizeItem(1 / this.camera2D.scale.x); + } + + /** + * 创建2D网格 + */ + private createGrid2D(): void { + this.grid2D = new Grid2D(this.camera2D, null); + + this.camera2D.addChild(this.grid2D); } /** * 创建画布 @@ -305,20 +471,11 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.content.nativeElement.appendChild(this.app.view); this.app.view.style.border = '1px dashed blue'; this.animator = new Charm(PIXI); - // 创建网格 - this.grid = new AxGrid(this.app.view.width, null, { color: 0xffffff }, true, true); - // this.grid.x = (this.app.view.width / 2) - (this.grid.gridWidth / 2); - // this.grid.y = (this.app.view.height / 2) - (this.grid.gridWidth / 2); - // this.grid.pivot.set(0.5); - this.grid.x = this.app.stage.x; - this.grid.y = this.app.stage.y; - this.app.stage.addChild(this.grid); - this.grid.drawGrid(); - this.grid.onMousemove = (evt, gridCoord) => { - console.log(gridCoord); - }; + this.createViewport(); + this.createGrid2D(); this.createBackgroundImage(); + this.app.ticker.add((delta) => { this.animator.update(); this.mousePosition = this.app.renderer.plugins.interaction.mouse.global; @@ -332,51 +489,29 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.refreshPreviewLineSegment(this.currentClickPoint.position, this.circleShadow.position); this.refreshPreviewPoint(); } - /** - * 显示框选 - */ - if (this.rectToolGraphics.visible === true) { + // /** + // * 显示框选 + // */ + // if (this.rectToolGraphics.visible === true) { - const init = this.initialScreenMousePos; - const final = this.finalScreenMousePos; + // const init = this.initialScreenMousePos; + // const final = this.finalScreenMousePos; - this.rectToolGraphics.clear(); - this.rectToolGraphics.lineStyle(2, 0x00ff00, 1); - this.rectToolGraphics.beginFill(0xccccf2, 0.25); - if (final.x > init.x && final.y > init.y) { - this.rectToolGraphics.drawRect(init.x, init.y, final.x - init.x, final.y - init.y); - } else if (final.x > init.x && final.y < init.y) { - this.rectToolGraphics.drawRect(init.x, final.y, final.x - init.x, init.y - final.y); - } else if (final.x < init.x && final.y > init.y) { - this.rectToolGraphics.drawRect(final.x, init.y, init.x - final.x, final.y - init.y); - } else if (final.x < init.x && final.y < init.y) { - this.rectToolGraphics.drawRect(final.x, final.y, init.x - final.x, init.y - final.y); - } - this.rectToolGraphics.endFill(); - } - }); - /** - * 选中事件 - */ - this.on('select', (axShape: AxShape) => { - // if (axShape instanceof AxRectangleShape) { - // let upLeft: PIXI.Sprite= new PIXI.Sprite(this.editorPointTexture); - // let upRight: PIXI.Sprite= new PIXI.Sprite(this.editorPointTexture); - // let downLeft: PIXI.Sprite= new PIXI.Sprite(this.editorPointTexture); - // let downRight: PIXI.Sprite = new PIXI.Sprite(this.editorPointTexture); - // } else { - axShape.showBorder(); - axShape.drawBorder(1 / this.backgroundImage.scale.x); - axShape.setPointVisiable(this.allowEdit); + // this.rectToolGraphics.clear(); + // this.rectToolGraphics.lineStyle(2, 0x00ff00, 1); + // this.rectToolGraphics.beginFill(0xccccf2, 0.25); + // if (final.x > init.x && final.y > init.y) { + // this.rectToolGraphics.drawRect(init.x, init.y, final.x - init.x, final.y - init.y); + // } else if (final.x > init.x && final.y < init.y) { + // this.rectToolGraphics.drawRect(init.x, final.y, final.x - init.x, init.y - final.y); + // } else if (final.x < init.x && final.y > init.y) { + // this.rectToolGraphics.drawRect(final.x, init.y, init.x - final.x, final.y - init.y); + // } else if (final.x < init.x && final.y < init.y) { + // this.rectToolGraphics.drawRect(final.x, final.y, init.x - final.x, init.y - final.y); + // } + // this.rectToolGraphics.endFill(); // } }); - /** - * 取消选中事件 - */ - this.on('deselect', (axShape: AxShape) => { - axShape.hideBorder(); - axShape.setPointVisiable(false); - }); /** * 创建图标事件(数据处理) */ @@ -415,45 +550,27 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.emit('canvasDataChanged'); this.canvasData.isChange = true; }); - /** - * 删除图标事件(数据处理) - */ - this.on('deleteIcon', (axShape: AxShape) => { - // 删除图例对象 - const temp = this.backgroundImage.getChildByName('图例') as AxLegend; - if ( temp !== undefined - && temp !== null - && axShape.assetData.Name !== '图例') { - const itemLegend = new Legend(axShape.assetData.Name, axShape.assetData.ImageUrl, 1); - temp.deleteItem(itemLegend); - } - - - if (axShape.assetData.GameMode === GameMode.BasicInformation) { // 基本信息 - // 删除楼层数据 - delete this.canvasData.originaleveryStoreyData.data[axShape.assetData.Id]; - // 删除建筑数据 - delete this.canvasData.originalcompanyBuildingData.data[axShape.assetData.Id]; - } else if (axShape.assetData.GameMode === GameMode.Assignment) { // 处置预案 - delete this.canvasData.selectPanelPoint.Data.DefinedIncrement[axShape.assetData.Id]; - delete this.canvasData.selectPanelPoint.Data.Increment[axShape.assetData.Id]; - delete this.canvasData.selectPanelPoint.Data.Stock[axShape.assetData.Id]; - } else if (axShape.assetData.GameMode === GameMode.Examinee) { // 考生考试 - if (axShape.assetData.Tag === 1) { - // 删除楼层数据 - delete this.canvasData.examOriginaleveryStoreyData.data[axShape.assetData.Id]; - } else { - delete this.canvasData.selectPanelPoint.Data.DefinedIncrement[axShape.assetData.Id]; - delete this.canvasData.selectPanelPoint.Data.Increment[axShape.assetData.Id]; - delete this.canvasData.selectPanelPoint.Data.Stock[axShape.assetData.Id]; - } - - } - this.backgroundImage.removeChild(axShape); - this.emit('canvasDataChanged'); - this.canvasData.isChange = true; - }); + // /// + // this.app.renderer.plugins.interaction.on('pointerdown', (event) => { + // if (event.data.button !== 2) { return }; + // this.dragFlag = true; + // this.startPoint = { x: event.data.global.x, y: event.data.global.y }; + // }); + // this.app.renderer.plugins.interaction.on('pointermove', (event) => { + // if (this.dragFlag) { + // const dx = event.data.global.x - this.startPoint.x; + // const dy = event.data.global.y - this.startPoint.y; + // this.app.stage.position.x += dx; + // this.app.stage.position.y += dy; + // this.startPoint = { x: event.data.global.x, y: event.data.global.y }; + // } + // }); + // this.app.renderer.plugins.interaction.on('pointerup', (event) => { + // this.dragFlag = false; + // }); } + // dragFlag; + // startPoint; /** * 重置画布 */ @@ -484,7 +601,7 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV */ public refreshIcon(id: string): void { const icon = this.backgroundImage.children.find(item => item.name === id); - console.log(icon); + // console.log(icon); if (icon instanceof AxImageShape) { icon.refresh(); } else if (icon instanceof MultipointIcon) { @@ -504,7 +621,6 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV public setIconScale(value: number): void { this.backgroundImage.children.forEach(item => { if (item instanceof AxImageShape) { - console.log(item.image.scale); item.image.scale.set(value); } else if (item instanceof MultipointIcon) { @@ -513,19 +629,6 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV } }); } - /** - * 设置高亮 - */ - public setHighlight(ids: string[]): void { - this.selection.deselectAll(); - ids.forEach(item => { - let obj = this.backgroundImage.getChildByName(item); - if (obj === null) { - obj = this.app.stage.getChildByName(item); - } - this.selection.select(obj); - }); - } /** * 创建楼层图形 */ @@ -535,21 +638,26 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV switch (floorData[key].InteractiveMode) { case 0: const singleIcon = new AxImageShape(floorData[key], this); - singleIcon.moveable = this.allowEdit && this.canvasData.gameMode === singleIcon.assetData.GameMode; + singleIcon.allowEdit = this.allowEdit && this.canvasData.gameMode === singleIcon.assetData.GameMode; break; case 1: const icon = new MultipointIcon(floorData[key], this); + icon.allowEdit = this.allowEdit && this.canvasData.gameMode === icon.assetData.GameMode; break; case 2: const polygonIcon = new PolygonIcon(floorData[key], this); + polygonIcon.allowEdit = this.allowEdit && this.canvasData.gameMode === polygonIcon.assetData.GameMode; break; case 3: if (floorData[key].Name === '水带') { - const distance = new AxArrowConnector(floorData[key], this, false, true); + const waterLine = new AxArrowConnector(floorData[key], this, false, true); + waterLine.allowEdit = this.allowEdit && this.canvasData.gameMode === waterLine.assetData.GameMode; } else if (floorData[key].Name === '距离') { const distance = new AxArrowConnector(floorData[key], this, true, true); + distance.allowEdit = this.allowEdit && this.canvasData.gameMode === distance.assetData.GameMode; } else if (floorData[key].Name === '普通墙' || floorData[key].Name === '承重墙') { const wall = new AxArrowConnector(floorData[key], this, false, false); + wall.allowEdit = this.allowEdit && this.canvasData.gameMode === wall.assetData.GameMode; } break; } @@ -567,22 +675,24 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV switch (nodeData[key][tempKey].InteractiveMode) { case 0: const singleIcon = new AxImageShape(nodeData[key][tempKey], this); - singleIcon.moveable = this.allowEdit && this.canvasData.gameMode === singleIcon.assetData.GameMode; + singleIcon.allowEdit = this.allowEdit && this.canvasData.gameMode === singleIcon.assetData.GameMode; break; case 1: - const icon = new MultipointIcon(nodeData[key][tempKey], this); - break; + const icon = new MultipointIcon(nodeData[key][tempKey], this); + icon.allowEdit = this.allowEdit && this.canvasData.gameMode === icon.assetData.GameMode; + break; case 2: const polygonIcon = new PolygonIcon(nodeData[key][tempKey], this); + polygonIcon.allowEdit = this.allowEdit && this.canvasData.gameMode === polygonIcon.assetData.GameMode; break; case 3: const pipeline = new AxArrowConnector(nodeData[key][tempKey], this, false, true); + pipeline.allowEdit = this.allowEdit && this.canvasData.gameMode === pipeline.assetData.GameMode; break; } }); }); } - // this.emit('backgroundScale', this.backgroundImage.scale.x); } /** * 创建确认绘制结束按钮 @@ -630,14 +740,8 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.backgroundImage .on('pointerdown', event => { if (event.data.button !== 0) { return; } - console.log(this.backgroundImage.toLocal(this.mousePosition)); - if (!event.currentTarget.dragging && this.selection.isMultiselection === false) { - this.selection.deselectAll(); - event.currentTarget.data = event.data; - event.currentTarget.dragging = true; - event.currentTarget.dragPoint = event.data.getLocalPosition(event.currentTarget.parent); - event.currentTarget.dragPoint.x -= event.currentTarget.x; - event.currentTarget.dragPoint.y -= event.currentTarget.y; + // console.log(this.backgroundImage.toLocal(this.mousePosition)); + if (this.isCtrlKeyClicked === false) { switch (this.paintMode) { case PaintMode.endPaint: break; @@ -828,56 +932,53 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV // this.emit('backgroundScale', this.backgroundImage.scale.x); break; } - } else if (!event.currentTarget.dragging && this.selection.isMultiselection === true) { - this.rectToolGraphics.visible = true; - event.currentTarget.dragging = true; - this.initialScreenMousePos = this.backgroundImage.toLocal(this.mousePosition); - this.finalScreenMousePos = this.backgroundImage.toLocal(this.mousePosition); } + // else if (this.isCtrlKeyClicked === true) { + // this.rectToolGraphics.visible = true; + // this.initialScreenMousePos = this.backgroundImage.toLocal(this.mousePosition); + // this.finalScreenMousePos = this.backgroundImage.toLocal(this.mousePosition); + // } }) .on('pointerup', event => { - if (event.currentTarget.dragging) { - event.currentTarget.dragging = false; - event.currentTarget.data = null; - } - if (this.rectToolGraphics.visible === true) { - this.backgroundImage.children.forEach(item => { - if ( item instanceof AxImageShape - || item instanceof MultipointIcon - || item instanceof PolygonIcon - || item instanceof AxArrowConnector) { - // 判断2个矩形是否相交 - const rect1 = this.rectToolGraphics.getBounds(); - const rect2 = item.getBounds(); - if (this.isOverlap(rect1, rect2)) { - this.selection.select(item); - } - } - }); - this.rectToolGraphics.clear(); - this.rectToolGraphics.visible = false; - } + // event.currentTarget.data = null; + + // if (this.rectToolGraphics.visible === true) { + // const shapes: AxShape[] = []; + // this.backgroundImage.children.forEach(item => { + // if ( item instanceof AxShape + // && item instanceof AxPreviewImageShape === false) { + // // 判断2个矩形是否相交 + // const rect1 = this.rectToolGraphics.getBounds(); + // const rect2 = item.getBounds(); + // if (this.isOverlap(rect1, rect2)) { + // shapes.push(item); + // } + // } + // }); + // this.rectToolGraphics.clear(); + // this.rectToolGraphics.visible = false; + // this.selectAll(shapes); + // } }) .on('pointerupoutside', event => { - if (event.currentTarget.dragging) { - event.currentTarget.dragging = false; - event.currentTarget.data = null; - } + // if (this.isMove) { + // event.currentTarget.data = null; + // } }) .on('pointermove', event => { - if (event.currentTarget.dragging && this.selection.isMultiselection === false) { - const newPosition = event.currentTarget.data.getLocalPosition(event.currentTarget.parent); - event.currentTarget.x = newPosition.x - event.currentTarget.dragPoint.x; - event.currentTarget.y = newPosition.y - event.currentTarget.dragPoint.y; - } else if (event.currentTarget.dragging && this.selection.isMultiselection === true) { - if (this.rectToolGraphics.visible === true) { - this.finalScreenMousePos = this.backgroundImage.toLocal(this.mousePosition); - } - } + // if (this.isCtrlKeyClicked === false) { + // // const newPosition = event.currentTarget.data.getLocalPosition(event.currentTarget.parent); + // // event.currentTarget.x = newPosition.x - event.currentTarget.dragPoint.x; + // // event.currentTarget.y = newPosition.y - event.currentTarget.dragPoint.y; + // } else if (this.isCtrlKeyClicked === true) { + // if (this.rectToolGraphics.visible === true) { + // this.finalScreenMousePos = this.backgroundImage.toLocal(this.mousePosition); + // } + // } }) .on('rightclick', event => { event.stopPropagation(); - this.selection.deselectAll(); + this.deselectAll(); this.setPaintMode(PaintMode.endPaint); }) .on('pointerover', (event) => { @@ -892,7 +993,7 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.previewImage.visible = false; } }); - this.app.stage.addChild(this.backgroundImage); + this.camera2D.addChild(this.backgroundImage); this.createPreviewImage(); this.createPreviewLineSegment(); this.createCircleShadow(); @@ -918,44 +1019,58 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV * 刷新背景图 */ public async refreshBackgroundImage(imageUrl: string = this.canvasData.selectStorey.imageUrl, imageAngle: number = this.canvasData.selectStorey.imageAngle): Promise { + if (imageAngle === undefined || imageAngle === null) { + imageAngle = 0; + } + this.backgroundImage.scale.set(1); + this.backgroundImage.pivot.set(0); + // this.backgroundImage.x = this.app.view.width / 2; + // this.backgroundImage.y = this.app.view.height / 2; if (imageUrl === undefined || imageUrl === null || imageUrl === '') { - this.backgroundImage.visible = false; + this.backgroundImage.texture = this.backgroundTexture; } else { - this.backgroundImage.visible = false; - this.backgroundImage.scale.set(1); - this.backgroundImage.pivot.set(0); - this.backgroundImage.x = this.app.view.width / 2; - this.backgroundImage.y = this.app.view.height / 2; this.backgroundImage.texture = await PIXI.Texture.fromURL(imageUrl); - this.backgroundImage.angle = imageAngle; - // 等待图片加载完成 - const imageWidth = this.backgroundImage.texture.width; - const imageHeight = this.backgroundImage.texture.height; - const appWidth = this.app.view.width - 470; - const appHeight = this.app.view.height; - const wScale = appWidth / imageWidth; - const hScale = appHeight / imageHeight; - const scale = wScale < hScale ? wScale : hScale; - // 设置图片缩放 - this.backgroundImage.scale.set(scale); - this.backgroundImage.visible = true; - this.backgroundImage.children.forEach((item) => { - if (item instanceof AxShape) { - item.refresh(); - } - }); + console.log(imageUrl); + } + this.backgroundImage.x = this.backgroundImage.width/2; + this.backgroundImage.y = this.backgroundImage.height/2; + this.backgroundImage.angle = imageAngle; + // 等待图片加载完成 + const imageWidth = this.backgroundImage.texture.width; + const imageHeight = this.backgroundImage.texture.height; + const appWidth = this.app.view.width - 470; + const appHeight = this.app.view.height; + const wScale = appWidth / imageWidth; + const hScale = appHeight / imageHeight; + let scale = wScale < hScale ? wScale : hScale; + + if (scale < 0.12) { + scale = 0.12; + } + if (scale > 16) { + scale = 16; } + this.camera2D.scale.set(scale); + this.camera2D.x = 235; + this.camera2D.y = 0; + // 设置图片缩放 + // this.backgroundImage.scale.set(scale); + // this.backgroundImage.visible = true; + this.backgroundImage.children.forEach((item) => { + if (item instanceof AxShape) { + item.refresh(); + } + }); } /** * 刷新 * @param imageUrl * @param imageAngle */ - public async refresh(imageUrl: string = this.canvasData.selectStorey.imageUrl, imageAngle: number = this.canvasData.selectStorey.imageAngle): Promise { - await this.refreshBackgroundImage(); - + public async refresh(): Promise { + await this.refreshBackgroundImage(this.canvasData.selectStorey.imageUrl, this.canvasData.selectStorey.imageAngle); // 清空所有图形 - this.selection.deselectAll(); + this.deselectAll(); const itemList = []; this.backgroundImage.children.forEach(item => { if (item instanceof AxShape && item instanceof AxPreviewImageShape === false) { @@ -973,6 +1088,31 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV // 创建处置预案图形 this.createNodeShape(this.canvasData.selectPanelPoint.Data); this.createAxLegend(); + + this.updateCamera2D(); + } + /** + * 加载无关联信息处置预案 + * @data 处置预案数据 + */ + public async loadNoRelevantInformationDisposalPlan(data: DisposalNodeData): Promise { + await this.refreshBackgroundImage(data.BackgroundImageUrl, data.BackgroundImageAngle); + // 清空所有图形 + this.deselectAll(); + const itemList = []; + this.backgroundImage.children.forEach(item => { + if (item instanceof AxShape && item instanceof AxPreviewImageShape === false) { + itemList.push(item.name); + } + }); + + itemList.forEach(item => { + this.backgroundImage.getChildByName(item).destroy(); + }); + // 创建处置预案图形 + this.createNodeShape(this.canvasData.selectPanelPoint.Data); + + this.updateCamera2D(); } /** * 创建安信图例 @@ -1121,7 +1261,7 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV */ public beginPaint() { console.log(this.canvasData.selectTemplateData); - this.selection.deselectAll(); + this.deselectAll(); this.setPaintMode(PaintMode.endPaint); this.setPaintMode(this.canvasData.selectTemplateData.interactiveMode); } @@ -1270,49 +1410,53 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV * 复制 */ public copy(): void { - this.copyData = []; - this.selection.objects.forEach(item => { - const newData = JSON.parse(JSON.stringify(item.assetData)); - this.copyData.push(newData); - }); + this.copyData = []; + this.selection.all().forEach(item => { + const newData = JSON.parse(JSON.stringify(item.assetData)); + this.copyData.push(newData); + }); } /** * 粘贴 */ public paste(companyId: string, buildingId: string, floorId: string): void { - this.copyData.forEach(item => { - item.Point = new PIXI.Point(item.Point.x + 5, item.Point.y + 5); - const newData = JSON.parse(JSON.stringify(item)); - newData.Id = ObjectID.default.generate(), - newData.CompanyId = companyId; - newData.BuildingId = buildingId; - newData.FloorId = floorId; - newData.Point = new PIXI.Point(item.Point.x + 5, item.Point.y + 5); - switch (item.InteractiveMode) { - case PaintMode.singlePointIcon: - const singleIcon = new AxImageShape(newData, this); - this.emit('createIcon', singleIcon); - break; - case PaintMode.lineIcon: - const lineIcon = new MultipointIcon(newData, this); - this.emit('createIcon', lineIcon); - break; - case PaintMode.polygonIcon: - const polygonIcon = new PolygonIcon(newData, this); - this.emit('createIcon', polygonIcon); - break; - case PaintMode.Pipeline: - if (item.Name === '距离') { - const wall = new AxArrowConnector(newData, this, true, true); - this.emit('createIcon', wall); - } else if (item.Name === '普通墙' || item.Name === '承重墙') { - const wall = new AxArrowConnector(newData, this, false, false); - this.emit('createIcon', wall); - } - break; - } - this.selection.select(this.backgroundImage.getChildByName(newData.Id)); - }); + const ids: string[] = []; + if (this.copyData.length > 0) { + this.copyData.forEach(item => { + item.Point = new PIXI.Point(item.Point.x + 5, item.Point.y + 5); + const newData = JSON.parse(JSON.stringify(item)); + newData.Id = ObjectID.default.generate(), + newData.CompanyId = companyId; + newData.BuildingId = buildingId; + newData.FloorId = floorId; + newData.Point = new PIXI.Point(item.Point.x + 5, item.Point.y + 5); + switch (item.InteractiveMode) { + case PaintMode.singlePointIcon: + const singleIcon = new AxImageShape(newData, this); + this.emit('createIcon', singleIcon); + break; + case PaintMode.lineIcon: + const lineIcon = new MultipointIcon(newData, this); + this.emit('createIcon', lineIcon); + break; + case PaintMode.polygonIcon: + const polygonIcon = new PolygonIcon(newData, this); + this.emit('createIcon', polygonIcon); + break; + case PaintMode.Pipeline: + if (item.Name === '距离') { + const wall = new AxArrowConnector(newData, this, true, true); + this.emit('createIcon', wall); + } else if (item.Name === '普通墙' || item.Name === '承重墙') { + const wall = new AxArrowConnector(newData, this, false, false); + this.emit('createIcon', wall); + } + break; + } + ids.push(newData.Id); + }); + this.setHighlight(ids); + } } //////////////////////////////////////////////////////////////////////// 通用///////////////////////////////////////////////////////////////////////////// /** @@ -1334,9 +1478,9 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV * 考生点击楼层 */ public async onExamineeClickFloor() { - await this.refreshBackgroundImage(); + await this.refreshBackgroundImage(this.canvasData.selectStorey.imageUrl,this.canvasData.selectStorey.imageAngle); // 清空所有图形 - this.selection.deselectAll(); + this.deselectAll(); const itemList = []; this.backgroundImage.children.forEach(item => { if (item instanceof AxShape && item instanceof AxPreviewImageShape === false) { @@ -1355,15 +1499,17 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.createNodeShape(this.canvasData.selectPanelPoint.Data); // 隐藏图标 this.setIconVisible(this.canvasData.hiddenBasicInfoFacilities, false); + + this.updateCamera2D(); } /** * 考官点击楼层-阅卷 */ public async onExaminerClickFloor() { - await this.refreshBackgroundImage(); + await this.refreshBackgroundImage(this.canvasData.selectStorey.imageUrl,this.canvasData.selectStorey.imageAngle); // 清空所有图形 - this.selection.deselectAll(); + this.deselectAll(); const itemList = []; this.backgroundImage.children.forEach(item => { if (item instanceof AxShape && item instanceof AxPreviewImageShape === false) { @@ -1382,14 +1528,16 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.createNodeShape(this.canvasData.selectPanelPoint.Data); // 隐藏图标 this.setIconVisible(this.canvasData.hiddenBasicInfoFacilities, false); + + this.updateCamera2D(); } /** * 考官点击楼层-创建试卷 */ public async onExaminerClickFloor_CreateTestpaper() { - await this.refreshBackgroundImage(); + await this.refreshBackgroundImage(this.canvasData.selectStorey.imageUrl,this.canvasData.selectStorey.imageAngle); // 清空所有图形 - this.selection.deselectAll(); + this.deselectAll(); const itemList = []; this.backgroundImage.children.forEach(item => { if (item instanceof AxShape && item instanceof AxPreviewImageShape === false) { @@ -1406,95 +1554,125 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.createNodeShape(this.canvasData.selectPanelPoint.Data); // 隐藏图标 this.setNameVisible(false, 0); - } -} -/** - * 选择器 - */ -export class Selection { - constructor(private workingArea: WorkingAreaComponent) {} - public objects: any[] = []; - public isMultiselection = false; + this.updateCamera2D(); + } + //////////////////////////////////////////////////////////////////// 选择逻辑 /** - * 返回选择器中是否包含对象 - * @param obj 对象 + * 清空选择,选择单个形状 + * @param shape 形状 */ - public contains(obj: any): boolean { - return this.objects.includes(obj); + public selectSingle(shape: AxShape) { + if (this.selection.first() !== null) { + this.selection.all().forEach(item => { + this.clearSelectEffect(item); + }); + this.selection.clear(); + } + this.selection.add(shape); + this.setSelectEffect(shape); + AxMessageSystem.send(CanvasAction.selectionChanged); } /** - * 选定对象 - * @param obj 对象 + * 选择 + * @param shape 形状 */ - public select(obj: any) { - if (!this.contains(obj)) { - this.workingArea.emit('select', obj); - this.objects.push(obj); + public select(shape: AxShape) { + if (this.selection.first() !== null + && !this.isCtrlKeyClicked + && !this.selection.has(shape)) { + this.selection.all().forEach(item => { + this.clearSelectEffect(item); + }); + this.selection.clear(); } + this.selection.add(shape); + this.setSelectEffect(shape); + AxMessageSystem.send(CanvasAction.selectionChanged); } /** - * 取消选定对象 - * @param obj 对象 + * 选择集合中的形状 + * @param shape 形状集合 */ - public deselect(obj: any) { - if (this.contains(obj)) { - this.workingArea.emit('deselect', obj); - const idx = this.objects.findIndex(x => x === obj); - this.objects.splice(idx, 1); - } + public selectAll(shape: AxShape[]) { + this.selection.addArray(shape); + this.selection.all().forEach(item => { + this.setSelectEffect(item); + }); + AxMessageSystem.send(CanvasAction.selectionChanged); } /** - * 选定或取消选定对象 - * @param obj 对象 + * 先清空再选择全部 + * @param shape 形状集合 */ - public selectOrDeselect(obj: any) { - if (this.contains(obj)) { - this.deselect(obj); - } else { - this.select(obj); + public selectAllWithClear(shape: AxShape[]) { + if (this.selection.first() !== null) { + this.selection.all().forEach(item => { + this.clearSelectEffect(item); + }); + this.selection.clear(); } + this.selection.addArray(shape); + this.selection.all().forEach(item => { + this.setSelectEffect(item); + }); + AxMessageSystem.send(CanvasAction.selectionChanged); } /** - * 取消选定所有已选定对象 + * 选择集合中所有id的形状 + * @param ids 形状id集合 */ - public deselectAll() { - this.objects.forEach(item => { - this.workingArea.emit('deselect', item); + public setHighlight(ids: string[]): void { + const shapes: AxShape[] = []; + // 重新选择 + ids.forEach(item => { + const obj = this.backgroundImage.getChildByName(item); + shapes.push(obj as AxShape); }); - this.objects.splice(0, this.objects.length); + this.selectAllWithClear(shapes); } /** - * 取消选定所有对象后选定一个对象 - * @param obj 对象 + * 取消所有选择 */ - public selectOne(obj: any) { - if (this.isMultiselection) { - this.selectOrDeselect(obj); - } else { - this.deselectAll(); - this.select(obj); + public deselectAll() { + if (this.selection.first() !== null) { + this.selection.all().forEach(item => { + this.clearSelectEffect(item); + }); + this.selection.clear(); + AxMessageSystem.send(CanvasAction.selectionChanged); } } /** - * 选定对象集合中所有对象 - * @param objects 对象集合 + * 设置选中效果 + * @param shape 形状 */ - public selectAll(objects: any[]) { - this.objects.forEach(item => { - this.select(item); - }); + public setSelectEffect(shape: AxShape) { + shape.hideBorder(); + shape.setPointVisiable(false); + shape.showBorder(); + shape.drawBorder(1 / this.backgroundImage.scale.x); + shape.setPointVisiable(this.allowEdit); + } + /** + * 设置形状选中效果 + * @param shape 形状 + */ + public clearSelectEffect(shape: AxShape) { + shape.hideBorder(); + shape.setPointVisiable(false); + } + ////////////////////////////////////////////////////////////////////////////////////////////////图例 + /** + * 设置图例显示隐藏 + * @param b true 显示,false隐藏 + */ + public setLegendVisible(b: boolean): void { + const legend = this.backgroundImage.getChildByName('图例') as AxLegend; + legend.visible = b; } } - - -/** - * 车辆类型 - */ -export enum Type { - 水源 = 0, - 举高喷射消防车 = 1, - 泡沫消防车 = 2, - 水罐消防车 = 3, - 压缩空气泡沫消防车 = 4 +enum CanvasAction { + selectionChanged = 'selectionChanged', + copyDataChanged = 'copyDataChanged' }