diff --git a/debug.log b/debug.log new file mode 100644 index 0000000..ebbc1a4 --- /dev/null +++ b/debug.log @@ -0,0 +1 @@ +[1214/094922.722:ERROR:directory_reader_win.cc(43)] FindFirstFile: ϵͳҲָ· (0x3) diff --git a/package-lock.json b/package-lock.json index 596ffac..f36019f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10508,7 +10508,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -10529,12 +10530,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -10549,17 +10552,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -10676,7 +10682,8 @@ "inherits": { "version": "2.0.4", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -10688,6 +10695,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -10702,6 +10710,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -10709,12 +10718,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -10733,6 +10744,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -10822,7 +10834,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -10834,6 +10847,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -10919,7 +10933,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -10955,6 +10970,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -10974,6 +10990,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -11017,12 +11034,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -17551,7 +17570,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -17594,7 +17614,8 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", @@ -17605,7 +17626,8 @@ "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -17722,7 +17744,8 @@ "inherits": { "version": "2.0.4", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -17734,6 +17757,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -17763,6 +17787,7 @@ "version": "2.9.0", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -17781,6 +17806,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -17870,7 +17896,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -17882,6 +17909,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -17967,7 +17995,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -18003,6 +18032,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -18022,6 +18052,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -18065,12 +18096,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/src/app/canvas-share-data.service.ts b/src/app/canvas-share-data.service.ts index b068d3f..ac9a037 100644 --- a/src/app/canvas-share-data.service.ts +++ b/src/app/canvas-share-data.service.ts @@ -1,19 +1,35 @@ 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() { } + private _sendMessage: ReplaySubject = new ReplaySubject(1); + GameMode: any; isChange = false; // 数据 是否改动 - selectStorey: any = {area: '', details: ''}; // 选择当前 楼层 数据 selectTemplateData: any; // 选择当前 模板数据 + // 总平面图/建筑 楼层 + selectStorey: any = {area: '', details: ''}; // 选择当前 楼层 数据 originalcompanyBuildingData: any; // 单位/建筑 数据 originaleveryStoreyData: any; // 总平面图/楼层/区域 楼层数据 + // 总平面图/建筑 楼层 + + // 处置 节点 + allDisposalNode: any = []; // 所有 处置节点 + allNodeMarkers: any; // 灾情 标签信息 + selectPanelPoint: DisposalNodeData = new DisposalNodeData(); + selectPanelPointBaseData: any = {description: '', notes: '', weather: '', airTemperature: '', windDirection: '', windScale: ''}; // 当前 数据节点 所对应的 天气,详情 数据节点 + // 处置 节点 + /** + * 游戏模式 + */ + gameMode: GameMode = GameMode.BasicInformation; facilityAssetsName = new Map([ [ '消防水池', '消防水池'], @@ -46,6 +62,26 @@ export class CanvasShareDataService { [ '消防管网', '消防管网'], [ 'DCS控制室', 'DCS控制室'] ]); + + /** * 向其他组件发送信息 * + * @param message 需要发送的信息 * @returns {Observavle} */ + public sendMessage(message: any) { + this._sendMessage.next(message); + } + public getMessage(): Observable { + return this._sendMessage.asObservable(); + } + + // 处置节点 筛选出 匹配数据 匹配不到 return undefined + findDisposalNode(parentId: string= null, name: string= null) { + if (parentId && name) { // 匹配 父id, name + const returnData = this.allDisposalNode.find(item => item.parentId === parentId && item.name === name); + return returnData; + } else { // 匹配 id + const returnData = this.allDisposalNode.find(item => item.id === parentId); + return returnData; + } + } /** * 获取单位毗邻信息 */ @@ -208,6 +244,31 @@ export class CanvasShareDataService { }); return list; } + /** + * 反序列化对象 + * @param json 字符串 + */ + public deserialize(json: any): T { + const obj: T = JSON.parse( + json, + (_, val) => { + if (val === null) { return null; } + if (Array.isArray(val) || typeof val !== 'object') { + return val; + } + return Object.entries(val).reduce((a, [key, val]) => { + const count = key.length; + if (count > 1) { + a[key[0].toUpperCase() + key.substring(1, count)] = val; + } else { + a[key] = val; + } + return a; + }, {}); + } + ); + return obj; + } } /** @@ -334,3 +395,338 @@ export enum PropertyType { // 供给类型 SupplyType } +/** + * 处置节点 + */ +export class DisposalNode { + /** + * 编号 + */ + public Id: string; + /** + * 名称 + */ + public Name: string; + /** + * 等级 + */ + public Level: number; + /** + * 排序 + */ + public Order: number; + /** + * 详情 + */ + public Description: string; + /** + * 注意事项 + */ + public Notes: string; + /** + * 天气 + */ + public Weather: string; + /** + * 气温 + */ + public AirTemperature?: number; + /** + * 风向 + */ + public WindDirection: Direction; + /** + * 风力等级 + */ + public WindScale: WindScale; + /** + * 图片名称 + */ + public ImageNames: string[]; + /** + * 图片地址 + */ + public ImageUrls: string[]; + /** + * 父节点编号 + */ + public ParentId: string; + /** + * 灾情编号 + */ + public DisasterId: string; + /** + * 预案组件编号 + */ + public PlanComponentId: string; + /** + * 单位编号 + */ + public CompanyId: string; + /** + * 总平面图编号 + */ + public SitePlanId: string; + /** + * 建筑编号 + */ + public BuildingId: string; + /** + * 建筑区域编号 + */ + public BuildingAreaId: string; +} +/** + * 方向。 + */ +export enum Direction { + /** + * 东 + */ + East, + /** + * 西 + */ + West, + /** + * 南 + */ + South, + /** + * 北 + */ + North, + /** + * 东南 + */ + Southeast, + /** + * 西南 + */ + Southwest, + /** + * 东北 + */ + Northeast, + /** + * 西北 + */ + Northwest +} +/** + * 风力等级 + */ +export enum WindScale { + WS0, + WS1, + WS2, + WS3, + WS4, + WS5, + WS6, + WS7, + WS8, + WS9, + WS10, + WS11, + WS12, + WS13, + WS14, + WS15, + WS16, + WS17, + WS18 +} +/** + * 处置节点数据 + */ +export class DisposalNodeData { + /** + * 编号 + */ + public Id: string; + /** + * 数据 + */ + public Data: any; + /** + * 版本号 + */ + public Version: string; + /** + * 处置节点编号 + */ + public DisposalNodeId: string; + /** + * 预案组件编号 + */ + public PlanComponentId: string; +} +/** + * 楼层节点数据 + */ +export class FloorNodeData { + /** + * 存量 + */ + public Stock: Map = new Map(); + /** + * 增量 + */ + public Increment: Map = new Map(); + /** + * 用户定义的增量。 + */ + public DefinedIncrement: Map = new Map(); +} +/** + * 素材数据 + */ +export class AssetData { + /// + /// 模板编号 + /// + public TemplateId: string; + /// + /// 编号 + /// + public Id: string; + /// + /// 名称 + /// + public Name: string; + /// + /// 角度 + /// + public Angle: number; + /// + /// 颜色 + /// + public Color: string; + /// + /// 坐标 + /// + public Point: PIXI.Point; + /// + /// 宽度 + /// + public Width: number; + /// + /// 高度 + /// + public Height: number; + /// + /// 是否启用 + /// + public Enabled: boolean; + /// + /// 填充方式 + /// + public FillMode: FillMode; + /// + /// 图片地址 + /// + public ImageUrl: string; + /// + /// 是否固定大小 + /// + public FixedSize: boolean; + /// + /// 点路径 + /// + public MultiPoint: PIXI.Point[]; + /// + /// 建筑ID + /// + public BuildingId: string; + /// + /// 单位ID + /// + public CompanyId: string; + /// + /// 楼层编号 + /// + public FloorId: string; + /// + /// 楼层名称 + /// + public FloorName: string; + /// + /// 消防要素编号 + /// + public FireElementId: string; + /// + /// 属性列表 + /// + public PropertyInfos: PropertyInfo[]; + /// + /// 交互方式 + /// + public InteractiveMode: InteractiveMode; + /// + /// 是否来自建筑 + /// + public IsFromBuilding: boolean; + /// + /// 渲染方式。 + /// + public DrawMode: ImageType; + /// + /// 9宫格边框数值。 + /// + public Border: Border; + /// + /// 厚度。 + /// + public Thickness: number; + /// + /// 素材类型 + /// + public GameMode: GameMode; +} +/** + * 填充模式 + */ +export enum FillMode { + Color, + Image +} +/** + * 交互方式 + */ +export enum InteractiveMode { + /** + * 单点。 + */ + Single, + /** + * 多点不闭合。 + */ + Multiple, + /** + * 多点闭合。 + */ + MultipleClosed +} +/** + * 图片显示类型 + */ +export enum ImageType { + Simple = 0, + Sliced = 1, + Tiled = 2, + Filled = 3 +} +/** + * 边框 + */ +export class Border { + + public x: number; + + public y: number; + + public z: number; + + public w: number; +} diff --git a/src/app/ui/collection-tools/collection-tools.component.ts b/src/app/ui/collection-tools/collection-tools.component.ts index 32718a7..f2dce76 100644 --- a/src/app/ui/collection-tools/collection-tools.component.ts +++ b/src/app/ui/collection-tools/collection-tools.component.ts @@ -431,7 +431,7 @@ export class CollectionToolsComponent implements OnInit { let that = this window.setTimeout(()=>{ document.getElementById("functionalDomainContent").oncontextmenu = function (event) { - that.canvas.cancelPaint() + // that.canvas.cancelPaint() that.selectImageIndex = -1 event.preventDefault(); }; diff --git a/src/app/working-area/charm.js b/src/app/working-area/charm.js new file mode 100644 index 0000000..a87804d --- /dev/null +++ b/src/app/working-area/charm.js @@ -0,0 +1,836 @@ +export class Charm { + constructor(renderingEngine = PIXI) { + + if (renderingEngine === undefined) throw new Error("Please assign a rendering engine in the constructor before using charm.js"); + + //Find out which rendering engine is being used (the default is Pixi) + this.renderer = ""; + + //If the `renderingEngine` is Pixi, set up Pixi object aliases + if (renderingEngine.ParticleContainer && renderingEngine.Sprite) { + this.renderer = "pixi"; + } + + + //An array to store the global tweens + this.globalTweens = []; + + //An object that stores all the easing formulas + this.easingFormulas = { + + //Linear + linear(x) { + return x; + }, + + //Smoothstep + smoothstep(x) { + return x * x * (3 - 2 * x); + }, + smoothstepSquared(x) { + return Math.pow((x * x * (3 - 2 * x)), 2); + }, + smoothstepCubed(x) { + return Math.pow((x * x * (3 - 2 * x)), 3); + }, + + //Acceleration + acceleration(x) { + return x * x; + }, + accelerationCubed(x) { + return Math.pow(x * x, 3); + }, + + //Deceleration + deceleration(x) { + return 1 - Math.pow(1 - x, 2); + }, + decelerationCubed(x) { + return 1 - Math.pow(1 - x, 3); + }, + + //Sine + sine(x) { + return Math.sin(x * Math.PI / 2); + }, + sineSquared(x) { + return Math.pow(Math.sin(x * Math.PI / 2), 2); + }, + sineCubed(x) { + return Math.pow(Math.sin(x * Math.PI / 2), 2); + }, + inverseSine(x) { + return 1 - Math.sin((1 - x) * Math.PI / 2); + }, + inverseSineSquared(x) { + return 1 - Math.pow(Math.sin((1 - x) * Math.PI / 2), 2); + }, + inverseSineCubed(x) { + return 1 - Math.pow(Math.sin((1 - x) * Math.PI / 2), 3); + }, + + //Spline + spline(t, p0, p1, p2, p3) { + return 0.5 * ( + (2 * p1) + + (-p0 + p2) * t + + (2 * p0 - 5 * p1 + 4 * p2 - p3) * t * t + + (-p0 + 3 * p1 - 3 * p2 + p3) * t * t * t + ); + }, + + //Bezier curve + cubicBezier(t, a, b, c, d) { + let t2 = t * t; + let t3 = t2 * t; + return a + (-a * 3 + t * (3 * a - a * t)) * t + (3 * b + t * (-6 * b + b * 3 * t)) * t + (c * 3 - c * 3 * t) * t2 + d * t3; + } + }; + + //Add `scaleX` and `scaleY` properties to Pixi sprites + this._addScaleProperties = (sprite) => { + if (this.renderer === "pixi") { + if (!("scaleX" in sprite) && ("scale" in sprite) && ("x" in sprite.scale)) { + Object.defineProperty( + sprite, + "scaleX", { + get() { + return sprite.scale.x + }, + set(value) { + sprite.scale.x = value + } + } + ); + } + if (!("scaleY" in sprite) && ("scale" in sprite) && ("y" in sprite.scale)) { + Object.defineProperty( + sprite, + "scaleY", { + get() { + return sprite.scale.y + }, + set(value) { + sprite.scale.y = value + } + } + ); + } + } + }; + } + + //The low level `tweenProperty` function is used as the foundation + //for the the higher level tween methods. + tweenProperty( + sprite, //Sprite object + property, //String property + startValue, //Tween start value + endValue, //Tween end value + totalFrames, //Duration in frames + type = "smoothstep", //The easing type + yoyo = false, //Yoyo? + delayBeforeRepeat = 0 //Delay in frames before repeating + ) { + + //Create the tween object + let o = {}; + + //If the tween is a bounce type (a spline), set the + //start and end magnitude values + let typeArray = type.split(" "); + if (typeArray[0] === "bounce") { + o.startMagnitude = parseInt(typeArray[1]); + o.endMagnitude = parseInt(typeArray[2]); + } + + //Use `o.start` to make a new tween using the current + //end point values + o.start = (startValue, endValue) => { + + //Clone the start and end values so that any possible references to sprite + //properties are converted to ordinary numbers + o.startValue = JSON.parse(JSON.stringify(startValue)); + o.endValue = JSON.parse(JSON.stringify(endValue)); + o.playing = true; + o.totalFrames = totalFrames; + o.frameCounter = 0; + + //Add the tween to the global `tweens` array. The `tweens` array is + //updated on each frame + this.globalTweens.push(o); + }; + + //Call `o.start` to start the tween + o.start(startValue, endValue); + + //The `update` method will be called on each frame by the game loop. + //This is what makes the tween move + o.update = () => { + + let time, curvedTime; + + if (o.playing) { + + //If the elapsed frames are less than the total frames, + //use the tweening formulas to move the sprite + if (o.frameCounter < o.totalFrames) { + + //Find the normalized value + let normalizedTime = o.frameCounter / o.totalFrames; + + //Select the correct easing function from the + //`ease` object’s library of easing functions + + + //If it's not a spline, use one of the ordinary easing functions + if (typeArray[0] !== "bounce") { + curvedTime = this.easingFormulas[type](normalizedTime); + } + + //If it's a spline, use the `spline` function and apply the + //2 additional `type` array values as the spline's start and + //end points + else { + curvedTime = this.easingFormulas.spline(normalizedTime, o.startMagnitude, 0, 1, o.endMagnitude); + } + + //Interpolate the sprite's property based on the curve + sprite[property] = (o.endValue * curvedTime) + (o.startValue * (1 - curvedTime)); + + o.frameCounter += 1; + } + + //When the tween has finished playing, run the end tasks + else { + sprite[property] = o.endValue; + o.end(); + } + } + }; + + //The `end` method will be called when the tween is finished + o.end = () => { + + //Set `playing` to `false` + o.playing = false; + + //Call the tween's `onComplete` method, if it's been assigned + if (o.onComplete) o.onComplete(); + + //Remove the tween from the `tweens` array + this.globalTweens.splice(this.globalTweens.indexOf(o), 1); + + //If the tween's `yoyo` property is `true`, create a new tween + //using the same values, but use the current tween's `startValue` + //as the next tween's `endValue` + if (yoyo) { + this.wait(delayBeforeRepeat).then(() => { + o.start(o.endValue, o.startValue); + }); + } + }; + + //Pause and play methods + o.play = () => o.playing = true; + o.pause = () => o.playing = false; + + //Return the tween object + return o; + } + + //`makeTween` is a general low-level method for making complex tweens + //out of multiple `tweenProperty` functions. Its one argument, + //`tweensToAdd` is an array containing multiple `tweenProperty` calls + + makeTween(tweensToAdd) { + + //Create an object to manage the tweens + let o = {}; + + //Create a `tweens` array to store the new tweens + o.tweens = []; + + //Make a new tween for each array + tweensToAdd.forEach(tweenPropertyArguments => { + + //Use the tween property arguments to make a new tween + let newTween = this.tweenProperty(...tweenPropertyArguments); + + //Push the new tween into this object's internal `tweens` array + o.tweens.push(newTween); + }); + + //Add a counter to keep track of the + //number of tweens that have completed their actions + let completionCounter = 0; + + //`o.completed` will be called each time one of the tweens + //finishes + o.completed = () => { + + //Add 1 to the `completionCounter` + completionCounter += 1; + + //If all tweens have finished, call the user-defined `onComplete` + //method, if it's been assigned. Reset the `completionCounter` + if (completionCounter === o.tweens.length) { + if (o.onComplete) o.onComplete(); + completionCounter = 0; + } + }; + + //Add `onComplete` methods to all tweens + o.tweens.forEach(tween => { + tween.onComplete = () => o.completed(); + }); + + //Add pause and play methods to control all the tweens + o.pause = () => { + o.tweens.forEach(tween => { + tween.playing = false; + }); + }; + o.play = () => { + o.tweens.forEach(tween => { + tween.playing = true; + }); + }; + + //Return the tween object + return o; + } + + /* High level tween methods */ + + //1. Simple tweens + + //`fadeOut` + fadeOut(sprite, frames = 60) { + return this.tweenProperty( + sprite, "alpha", sprite.alpha, 0, frames, "sine" + ); + } + + //`fadeIn` + fadeIn(sprite, frames = 60) { + return this.tweenProperty( + sprite, "alpha", sprite.alpha, 1, frames, "sine" + ); + } + + //`pulse` + //Fades the sprite in and out at a steady rate. + //Set the `minAlpha` to something greater than 0 if you + //don't want the sprite to fade away completely + pulse(sprite, frames = 60, minAlpha = 0) { + return this.tweenProperty( + sprite, "alpha", sprite.alpha, minAlpha, frames, "smoothstep", true + ); + } + + //2. Complex tweens + + slide( + sprite, endX, endY, + frames = 60, type = "smoothstep", yoyo = false, delayBeforeRepeat = 0 + ) { + return this.makeTween([ + + //Create the x axis tween + [sprite, "x", sprite.x, endX, frames, type, yoyo, delayBeforeRepeat], + + //Create the y axis tween + [sprite, "y", sprite.y, endY, frames, type, yoyo, delayBeforeRepeat] + + ]); + } + + breathe( + sprite, endScaleX = 0.8, endScaleY = 0.8, + frames = 60, yoyo = true, delayBeforeRepeat = 0 + ) { + + //Add `scaleX` and `scaleY` properties to Pixi sprites + this._addScaleProperties(sprite); + + return this.makeTween([ + + //Create the scaleX tween + [ + sprite, "scaleX", sprite.scaleX, endScaleX, + frames, "smoothstepSquared", yoyo, delayBeforeRepeat + ], + + //Create the scaleY tween + [ + sprite, "scaleY", sprite.scaleY, endScaleY, + frames, "smoothstepSquared", yoyo, delayBeforeRepeat + ] + ]); + } + + scale(sprite, endScaleX = 0.5, endScaleY = 0.5, frames = 60) { + + //Add `scaleX` and `scaleY` properties to Pixi sprites + this._addScaleProperties(sprite); + + return this.makeTween([ + + //Create the scaleX tween + [ + sprite, "scaleX", sprite.scaleX, endScaleX, + frames, "smoothstep", false + ], + + //Create the scaleY tween + [ + sprite, "scaleY", sprite.scaleY, endScaleY, + frames, "smoothstep", false + ] + ]); + } + + strobe( + sprite, scaleFactor = 1.3, startMagnitude = 10, endMagnitude = 20, + frames = 10, yoyo = true, delayBeforeRepeat = 0 + ) { + + let bounce = "bounce " + startMagnitude + " " + endMagnitude; + + //Add `scaleX` and `scaleY` properties to Pixi sprites + this._addScaleProperties(sprite); + + return this.makeTween([ + + //Create the scaleX tween + [ + sprite, "scaleX", sprite.scaleX, scaleFactor, frames, + bounce, yoyo, delayBeforeRepeat + ], + + //Create the scaleY tween + [ + sprite, "scaleY", sprite.scaleY, scaleFactor, frames, + bounce, yoyo, delayBeforeRepeat + ] + ]); + } + + wobble( + sprite, + scaleFactorX = 1.2, + scaleFactorY = 1.2, + frames = 10, + xStartMagnitude = 10, + xEndMagnitude = 10, + yStartMagnitude = -10, + yEndMagnitude = -10, + friction = 0.98, + yoyo = true, + delayBeforeRepeat = 0 + ) { + + let bounceX = "bounce " + xStartMagnitude + " " + xEndMagnitude; + let bounceY = "bounce " + yStartMagnitude + " " + yEndMagnitude; + + //Add `scaleX` and `scaleY` properties to Pixi sprites + this._addScaleProperties(sprite); + + let o = this.makeTween([ + + //Create the scaleX tween + [ + sprite, "scaleX", sprite.scaleX, scaleFactorX, frames, + bounceX, yoyo, delayBeforeRepeat + ], + + //Create the scaleY tween + [ + sprite, "scaleY", sprite.scaleY, scaleFactorY, frames, + bounceY, yoyo, delayBeforeRepeat + ] + ]); + + //Add some friction to the `endValue` at the end of each tween + o.tweens.forEach(tween => { + tween.onComplete = () => { + + //Add friction if the `endValue` is greater than 1 + if (tween.endValue > 1) { + tween.endValue *= friction; + + //Set the `endValue` to 1 when the effect is finished and + //remove the tween from the global `tweens` array + if (tween.endValue <= 1) { + tween.endValue = 1; + this.removeTween(tween); + } + } + }; + }); + + return o; + } + + //3. Motion path tweens + + followCurve( + sprite, + pointsArray, + totalFrames, + type = "smoothstep", + yoyo = false, + delayBeforeRepeat = 0 + ) { + + //Create the tween object + let o = {}; + + //If the tween is a bounce type (a spline), set the + //start and end magnitude values + let typeArray = type.split(" "); + if (typeArray[0] === "bounce") { + o.startMagnitude = parseInt(typeArray[1]); + o.endMagnitude = parseInt(typeArray[2]); + } + + //Use `tween.start` to make a new tween using the current + //end point values + o.start = (pointsArray) => { + o.playing = true; + o.totalFrames = totalFrames; + o.frameCounter = 0; + + //Clone the points array + o.pointsArray = JSON.parse(JSON.stringify(pointsArray)); + + //Add the tween to the `globalTweens` array. The `globalTweens` array is + //updated on each frame + this.globalTweens.push(o); + }; + + //Call `tween.start` to start the first tween + o.start(pointsArray); + + //The `update` method will be called on each frame by the game loop. + //This is what makes the tween move + o.update = () => { + + let normalizedTime, curvedTime, + p = o.pointsArray; + + if (o.playing) { + + //If the elapsed frames are less than the total frames, + //use the tweening formulas to move the sprite + if (o.frameCounter < o.totalFrames) { + + //Find the normalized value + normalizedTime = o.frameCounter / o.totalFrames; + + //Select the correct easing function + + //If it's not a spline, use one of the ordinary tween + //functions + if (typeArray[0] !== "bounce") { + curvedTime = this.easingFormulas[type](normalizedTime); + } + + //If it's a spline, use the `spline` function and apply the + //2 additional `type` array values as the spline's start and + //end points + else { + //curve = tweenFunction.spline(n, type[1], 0, 1, type[2]); + curvedTime = this.easingFormulas.spline(normalizedTime, o.startMagnitude, 0, 1, o.endMagnitude); + } + + //Apply the Bezier curve to the sprite's position + sprite.x = this.easingFormulas.cubicBezier(curvedTime, p[0][0], p[1][0], p[2][0], p[3][0]); + sprite.y = this.easingFormulas.cubicBezier(curvedTime, p[0][1], p[1][1], p[2][1], p[3][1]); + + //Add one to the `elapsedFrames` + o.frameCounter += 1; + } + + //When the tween has finished playing, run the end tasks + else { + //sprite[property] = o.endValue; + o.end(); + } + } + }; + + //The `end` method will be called when the tween is finished + o.end = () => { + + //Set `playing` to `false` + o.playing = false; + + //Call the tween's `onComplete` method, if it's been + //assigned + if (o.onComplete) o.onComplete(); + + //Remove the tween from the global `tweens` array + this.globalTweens.splice(this.globalTweens.indexOf(o), 1); + + //If the tween's `yoyo` property is `true`, reverse the array and + //use it to create a new tween + if (yoyo) { + this.wait(delayBeforeRepeat).then(() => { + o.pointsArray = o.pointsArray.reverse(); + o.start(o.pointsArray); + }); + } + }; + + //Pause and play methods + o.pause = () => { + o.playing = false; + }; + o.play = () => { + o.playing = true; + }; + + //Return the tween object + return o; + } + + walkPath( + sprite, //The sprite + originalPathArray, //A 2D array of waypoints + totalFrames = 300, //The duration, in frames + type = "smoothstep", //The easing type + loop = false, //Should the animation loop? + yoyo = false, //Shoud the direction reverse? + delayBetweenSections = 0 //Delay, in milliseconds, between sections + ) { + + //Clone the path array so that any possible references to sprite + //properties are converted into ordinary numbers + let pathArray = JSON.parse(JSON.stringify(originalPathArray)); + + //Figure out the duration, in frames, of each path section by + //dividing the `totalFrames` by the length of the `pathArray` + let frames = totalFrames / pathArray.length; + + //Set the current point to 0, which will be the first waypoint + let currentPoint = 0; + + //The `makePath` function creates a single tween between two points and + //then schedules the next path to be made after it + let makePath = (currentPoint) => { + + //Use the `makeTween` function to tween the sprite's + //x and y position + let tween = this.makeTween([ + + //Create the x axis tween between the first x value in the + //current point and the x value in the following point + [ + sprite, + "x", + pathArray[currentPoint][0], + pathArray[currentPoint + 1][0], + frames, + type + ], + + //Create the y axis tween in the same way + [ + sprite, + "y", + pathArray[currentPoint][1], + pathArray[currentPoint + 1][1], + frames, + type + ] + ]); + + //When the tween is complete, advance the `currentPoint` by one. + //Add an optional delay between path segments, and then make the + //next connecting path + tween.onComplete = () => { + + //Advance to the next point + currentPoint += 1; + + //If the sprite hasn't reached the end of the + //path, tween the sprite to the next point + if (currentPoint < pathArray.length - 1) { + this.wait(delayBetweenSections).then(() => { + tween = makePath(currentPoint); + }); + } + + //If we've reached the end of the path, optionally + //loop and yoyo it + else { + + //Reverse the path if `loop` is `true` + if (loop) { + + //Reverse the array if `yoyo` is `true` + if (yoyo) pathArray.reverse(); + + //Optionally wait before restarting + this.wait(delayBetweenSections).then(() => { + + //Reset the `currentPoint` to 0 so that we can + //restart at the first point + currentPoint = 0; + + //Set the sprite to the first point + sprite.x = pathArray[0][0]; + sprite.y = pathArray[0][1]; + + //Make the first new path + tween = makePath(currentPoint); + + //... and so it continues! + }); + } + } + }; + + //Return the path tween to the main function + return tween; + }; + + //Make the first path using the internal `makePath` function (below) + let tween = makePath(currentPoint); + + //Pass the tween back to the main program + return tween; + } + + walkCurve( + sprite, //The sprite + pathArray, //2D array of Bezier curves + totalFrames = 300, //The duration, in frames + type = "smoothstep", //The easing type + loop = false, //Should the animation loop? + yoyo = false, //Should the direction reverse? + delayBeforeContinue = 0 //Delay, in milliseconds, between sections + ) { + + //Divide the `totalFrames` into sections for each part of the path + let frames = totalFrames / pathArray.length; + + //Set the current curve to 0, which will be the first one + let currentCurve = 0; + + //The `makePath` function + let makePath = (currentCurve) => { + + //Use the custom `followCurve` function to make + //a sprite follow a curve + let tween = this.followCurve( + sprite, + pathArray[currentCurve], + frames, + type + ); + + //When the tween is complete, advance the `currentCurve` by one. + //Add an optional delay between path segments, and then make the + //next path + tween.onComplete = () => { + currentCurve += 1; + if (currentCurve < pathArray.length) { + this.wait(delayBeforeContinue).then(() => { + tween = makePath(currentCurve); + }); + } + + //If we've reached the end of the path, optionally + //loop and reverse it + else { + if (loop) { + if (yoyo) { + + //Reverse order of the curves in the `pathArray` + pathArray.reverse(); + + //Reverse the order of the points in each curve + pathArray.forEach(curveArray => curveArray.reverse()); + } + + //After an optional delay, reset the sprite to the + //beginning of the path and make the next new path + this.wait(delayBeforeContinue).then(() => { + currentCurve = 0; + sprite.x = pathArray[0][0]; + sprite.y = pathArray[0][1]; + tween = makePath(currentCurve); + }); + } + } + }; + + //Return the path tween to the main function + return tween; + }; + + //Make the first path + let tween = makePath(currentCurve); + + //Pass the tween back to the main program + return tween; + } + + //4. Utilities + + /* + The `wait` method lets you set up a timed sequence of events + + wait(1000) + .then(() => console.log("One")) + .then(() => wait(1000)) + .then(() => console.log("Two")) + .then(() => wait(1000)) + .then(() => console.log("Three")) + + */ + + wait(duration = 0) { + return new Promise((resolve, reject) => { + setTimeout(resolve, duration); + }); + } + + //A utility to remove tweens from the game + removeTween(tweenObject) { + + //Remove the tween if `tweenObject` doesn't have any nested + //tween objects + if (!tweenObject.tweens) { + tweenObject.pause(); + + //array.splice(-1,1) will always remove last elemnt of array, so this + //extra check prevents that (Thank you, MCumic10! https://github.com/kittykatattack/charm/issues/5) + if (this.globalTweens.indexOf(tweenObject) != -1) { + this.globalTweens.splice(this.globalTweens.indexOf(tweenObject), 1); + } + + //Otherwise, remove the nested tween objects + } else { + tweenObject.pause(); + tweenObject.tweens.forEach(element => { + this.globalTweens.splice(this.globalTweens.indexOf(element), 1); + }); + } + } + + update() { + + //Update all the tween objects in the `globalTweens` array + if (this.globalTweens.length > 0) { + for (let i = this.globalTweens.length - 1; i >= 0; i--) { + let tween = this.globalTweens[i]; + if (tween) tween.update(); + } + } + } +} \ No newline at end of file diff --git a/src/app/working-area/model/PropertyInfo.ts b/src/app/working-area/model/PropertyInfo.ts new file mode 100644 index 0000000..4ae2c6a --- /dev/null +++ b/src/app/working-area/model/PropertyInfo.ts @@ -0,0 +1,62 @@ +/** + * 属性 + */ +export class PropertyInfo { + constructor(instanceData: any) { + this.Tag = instanceData.tag; + this.Order = instanceData.order; + this.Enabled = instanceData.enabled; + this.Visible = instanceData.visible; + this.Required = instanceData.required; + this.RuleName = instanceData.ruleName; + this.RuleValue = instanceData.ruleValue; + this.PhysicalUnit = instanceData.physicalUnit; + this.PropertyName = instanceData.propertyName; + this.PropertyType = instanceData.propertyType; + this.PropertyValue = instanceData.propertyValue; + } + /** + * 标记位,用于扩展 + */ + public Tag: string; + /** + * 属性排序 + */ + public Order: number; + /** + * 是否启用 + */ + public Enabled: boolean; + /** + * 是否可见 + */ + public Visible: boolean; + /** + * 必填 + */ + public Required: boolean; + /** + * 验证规则名称 + */ + public RuleName: string; + /** + * 验证规则值 + */ + public RuleValue: string; + /** + * 物理单位 + */ + public PhysicalUnit: string; + /** + * 属性名称 + */ + public PropertyName: string; + /** + * 属性类型 + */ + public PropertyType: number; + /** + * 属性值 + */ + public PropertyValue: string; +} diff --git a/src/app/working-area/model/arrows.ts b/src/app/working-area/model/arrows.ts new file mode 100644 index 0000000..a553771 --- /dev/null +++ b/src/app/working-area/model/arrows.ts @@ -0,0 +1,41 @@ +import { WorkingAreaComponent } from '../working-area.component'; +import * as PIXI from 'pixi.js'; + +/** + * 箭头 + * 创建一个只有2个点组成的箭头 + */ +export class Arrows extends PIXI.Container { + public line: PIXI.Graphics = new PIXI.Graphics(); + public ready = false; + constructor(public assetData: any, private workingArea: WorkingAreaComponent) { + super(); + this.workingArea.backgroundImage.addChild(this); + this.name = this.assetData.Id; + this.addChild(this.line); + this.refresh(); + this.interactive = true; + this.on('mousedown', event => { + if (!this.ready) { return; } + event.stopPropagation(); + this.workingArea.selection.selectOne(this); + }); + } + /** + * 刷新 + */ + public refresh() { + this.line.clear(); + this.line.lineStyle(5, 0xff0000, 1); + this.line.moveTo(this.assetData.pointA.x, this.assetData.pointA.y); + this.line.lineTo(this.assetData.pointB.x, this.assetData.pointB.y); + + const angle = Math.atan2((this.assetData.pointB.y - this.assetData.pointA.y), (this.assetData.pointB.x - this.assetData.pointA.x)) + * (180 / Math.PI) + 90; + + this.line.beginFill(0xff0000); + console.log(Math.PI / 180 / 1.6); + this.line.drawStar(this.assetData.pointB.x, this.assetData.pointB.y, 3, 10, 0, (Math.PI / 180 * angle)); + this.line.endFill(); + } +} diff --git a/src/app/working-area/model/axArrowConnector.ts b/src/app/working-area/model/axArrowConnector.ts new file mode 100644 index 0000000..65b511c --- /dev/null +++ b/src/app/working-area/model/axArrowConnector.ts @@ -0,0 +1,322 @@ +import { WorkingAreaComponent } from '../working-area.component'; +import * as PIXI from 'pixi.js'; +import { AxShape } from './axShape'; + +/** + * 墙面 + */ +export class AxArrowConnector extends AxShape { + + line: PIXI.Graphics; + text: PIXI.Text; + style = new PIXI.TextStyle({ + fontFamily: 'Arial', + fontSize: 18, + fontStyle: 'normal', + fontWeight: 'bold', + fill: ['#000000'], + stroke: '#ffffff', + strokeThickness: 3, + dropShadow: true, + dropShadowColor: '#000000', + dropShadowBlur: 3, + dropShadowAngle: Math.PI / 6, + dropShadowDistance: 1, + wordWrap: false, + wordWrapWidth: 100, + }); + pts: PIXI.Point[]; + + constructor(assetData: any, workingArea: WorkingAreaComponent) { + super(assetData, workingArea); + this.text = new PIXI.Text(this.assetData.Name + + '\r\n' + + this.assetData.PropertyInfos?.find((item: { PropertyName: string; }) => + item.PropertyName === '名称/编号')?.PropertyValue, this.style); + this.line = new PIXI.Graphics(); + this.addChild(this.text); + this.addChild(this.line); + this.workingArea.backgroundImage.addChild(this); + this.refresh(this.line, this.assetData.MultiPoint); + + } + + /** + * 刷新形状 + */ + public refresh(c: PIXI.Graphics, pts: PIXI.Point[]): void { + if (pts.length < 2) { + this.text.position = pts[0]; + return; + } + const strokeWidth = 1; + const startWidth = 30 + strokeWidth; + const endWidth = 30 + strokeWidth; + const edgeWidth = 10; + const openEnded = false; + const markerStart = false;// 起始箭头 + const markerEnd = false;// 结束箭头 + const spacing = (openEnded) ? 0 : 0 + strokeWidth / 2; + const startSize = 30 + strokeWidth; + const endSize = 30 + strokeWidth; + const isRounded = true; + + const pe = pts[pts.length - 1]; + + let i0 = 1; + + while (i0 < pts.length - 1 && pts[i0].x === pts[0].x && pts[i0].y === pts[0].y) { + i0++; + } + + const dx = pts[i0].x - pts[0].x; + const dy = pts[i0].y - pts[0].y; + const dist = Math.sqrt(dx * dx + dy * dy); + + if (dist === 0) { + return; + } + let nx = dx / dist; + let nx1 = nx; + let nx2 = nx; + let ny = dy / dist; + let ny2 = ny; + let ny1 = ny; + let orthx = edgeWidth * ny; + let orthy = -edgeWidth * nx; + + const fns = []; + + // if (isRounded) { + // // c.setLineJoin('round'); + // c.lineTextureStyle({ join: PIXI.LINE_JOIN.ROUND }); + // } else if (pts.length > 2) { + // // Only mitre if there are waypoints + // // c.setMiterLimit(1.42); + // c.lineTextureStyle({ miterLimit: 1.42 }); + // } + // c.lineStyle(1, 0x000000, 1); + c.clear(); + c.lineTextureStyle({ width: 1, color: 0x00000, join: PIXI.LINE_JOIN.ROUND }); + c.beginFill(0xffffff); + const startNx = nx; + const startNy = ny; + + if (markerStart && !openEnded) { + this.paintMarker(c, pts[0].x, pts[0].y, nx, ny, startSize, startWidth, edgeWidth, spacing, true); + } else { + const outStartX = pts[0].x + orthx / 2 + spacing * nx; + const outStartY = pts[0].y + orthy / 2 + spacing * ny; + const inEndX = pts[0].x - orthx / 2 + spacing * nx; + const inEndY = pts[0].y - orthy / 2 + spacing * ny; + + if (openEnded) { + c.moveTo(outStartX, outStartY); + fns.push( () => { + c.lineTo(inEndX, inEndY); + }); + } else { + c.moveTo(inEndX, inEndY); + c.lineTo(outStartX, outStartY); + } + } + let dx1 = 0; + let dy1 = 0; + let dist1 = 0; + + for (let i = 0; i < pts.length - 2; i++) { + const pos = this.relativeCcw(pts[i].x, pts[i].y, pts[i + 1].x, pts[i + 1].y, pts[i + 2].x, pts[i + 2].y); + + dx1 = pts[i + 2].x - pts[i + 1].x; + dy1 = pts[i + 2].y - pts[i + 1].y; + + dist1 = Math.sqrt(dx1 * dx1 + dy1 * dy1); + + if (dist1 !== 0) { + nx1 = dx1 / dist1; + ny1 = dy1 / dist1; + + const tmp1 = nx * nx1 + ny * ny1; + const tmp = Math.max(Math.sqrt((tmp1 + 1) / 2), 0.04); + + nx2 = (nx + nx1); + ny2 = (ny + ny1); + + const dist2 = Math.sqrt(nx2 * nx2 + ny2 * ny2); + + if (dist2 !== 0) { + nx2 = nx2 / dist2; + ny2 = ny2 / dist2; + + const strokeWidthFactor = Math.max(tmp, Math.min(1 / 200 + 0.04, 0.35)); + const angleFactor = (pos !== 0 && isRounded) ? Math.max(0.1, strokeWidthFactor) : Math.max(tmp, 0.06); + + const outX = pts[i + 1].x + ny2 * edgeWidth / 2 / angleFactor; + const outY = pts[i + 1].y - nx2 * edgeWidth / 2 / angleFactor; + const inX = pts[i + 1].x - ny2 * edgeWidth / 2 / angleFactor; + const inY = pts[i + 1].y + nx2 * edgeWidth / 2 / angleFactor; + + if (pos === 0 || !isRounded) { + c.lineTo(outX, outY); + + ((x, y) => { + fns.push(() => { + c.lineTo(x, y); + }); + })(inX, inY); + } else if (pos === -1) { + const c1x = inX + ny * edgeWidth; + const c1y = inY - nx * edgeWidth; + const c2x = inX + ny1 * edgeWidth; + const c2y = inY - nx1 * edgeWidth; + c.lineTo(c1x, c1y); + if (isRounded) { + c.quadraticCurveTo(outX, outY, c2x, c2y); // 圆角 + } else { + c.lineTo(outX, outY); + } + ((x, y) => { + fns.push(() => { + c.lineTo(x, y); + }); + })(inX, inY); + } else { + c.lineTo(outX, outY); + + ((x, y) => { + const c1x = outX - ny * edgeWidth; + const c1y = outY + nx * edgeWidth; + const c2x = outX - ny1 * edgeWidth; + const c2y = outY + nx1 * edgeWidth; + + fns.push(() => { + if (isRounded) { + c.quadraticCurveTo(x, y, c1x, c1y); + } else { + c.lineTo(x, y); + } + }); + fns.push(() => { + c.lineTo(c2x, c2y); + }); + })(inX, inY); + } + + nx = nx1; + ny = ny1; + } + } + } + orthx = edgeWidth * ny1; + orthy = - edgeWidth * nx1; + + if (markerEnd && !openEnded) { + this.paintMarker(c, pe.x, pe.y, -nx, -ny, endSize, endWidth, edgeWidth, spacing, false); + } else { + c.lineTo(pe.x - spacing * nx1 + orthx / 2, pe.y - spacing * ny1 + orthy / 2); + + const inStartX = pe.x - spacing * nx1 - orthx / 2; + const inStartY = pe.y - spacing * ny1 - orthy / 2; + + if (!openEnded) { + c.lineTo(inStartX, inStartY); + } else { + c.moveTo(inStartX, inStartY); + + fns.splice(0, 0, () => { + c.moveTo(inStartX, inStartY); + }); + } + } + + for (let i = fns.length - 1; i >= 0; i--) { + fns[i](); + } + c.closePath(); + c.endFill(); + // if (openEnded) + // { + // c.end(); + // c.stroke(); + // } + // else + // { + // c.close(); + // c.fillAndStroke(); + // } + + // c.setShadow(false); + + // c.setMiterLimit(4); + + // if (isRounded) + // { + // c.setLineJoin('flat'); + // } + + // if (pts.length > 2) + // { + // c.setMiterLimit(4); + // if (markerStart && !openEnded) + // { + // c.begin(); + // this.paintMarker(c, pts[0].x, pts[0].y, startNx, startNy, startSize, startWidth, edgeWidth, spacing, true); + // c.stroke(); + // c.end(); + // } + + // if (markerEnd && !openEnded) + // { + // c.begin(); + // this.paintMarker(c, pe.x, pe.y, -nx, -ny, endSize, endWidth, edgeWidth, spacing, true); + // c.stroke(); + // c.end(); + // } + // } + } + paintMarker(c: PIXI.Graphics, ptX: number, ptY: number, nx: number, ny: number, + size: number, arrowWidth: number, edgeWidth: number, spacing: number, initialMove: boolean) { + const widthArrowRatio = edgeWidth / arrowWidth; + const orthx = edgeWidth * ny / 2; + const orthy = -edgeWidth * nx / 2; + + const spaceX = (spacing + size) * nx; + const spaceY = (spacing + size) * ny; + + if (initialMove) { + c.moveTo(ptX - orthx + spaceX, ptY - orthy + spaceY); + } else { + c.lineTo(ptX - orthx + spaceX, ptY - orthy + spaceY); + } + c.lineTo(ptX - orthx / widthArrowRatio + spaceX, ptY - orthy / widthArrowRatio + spaceY); + c.lineTo(ptX + spacing * nx, ptY + spacing * ny); + c.lineTo(ptX + orthx / widthArrowRatio + spaceX, ptY + orthy / widthArrowRatio + spaceY); + c.lineTo(ptX + orthx + spaceX, ptY + orthy + spaceY); + } + relativeCcw(x1: number, y1: number, x2: number, y2: number, px: number, py: number) { + x2 -= x1; + y2 -= y1; + px -= x1; + py -= y1; + let ccw = px * y2 - py * x2; + + if (ccw === 0.0) { + ccw = px * x2 + py * y2; + + if (ccw > 0.0) { + px -= x2; + py -= y2; + ccw = px * x2 + py * y2; + + if (ccw < 0.0) { + ccw = 0.0; + } + } + } + return (ccw < 0.0) ? -1 : ((ccw > 0.0) ? 1 : 0); + } + + redraw(): void{ + this.refresh(this.line, this.assetData.MultiPoint); + } +} diff --git a/src/app/working-area/model/axImageShape.ts b/src/app/working-area/model/axImageShape.ts new file mode 100644 index 0000000..e76728e --- /dev/null +++ b/src/app/working-area/model/axImageShape.ts @@ -0,0 +1,443 @@ +import { WorkingAreaComponent } from '../working-area.component'; +import * as ObjectID from 'bson-objectid'; +import { GameMode } from './gameMode'; +import { Pipeline } from './pipeline'; +import { PaintMode } from './paintModel'; +import * as PIXI from 'pixi.js'; +import { PropertyInfo } from './PropertyInfo'; +import { AxShape } from './axShape'; +import { Sprite } from 'pixi.js'; + +/** + * 安信图片形状 + * AxImageShape + */ +export class AxImageShape extends AxShape { + style = new PIXI.TextStyle({ + fontFamily: 'Arial', + fontSize: 18, + fontStyle: 'normal', + fontWeight: 'bold', + fill: ['#000000'], + stroke: '#ffffff', + strokeThickness: 3, + dropShadow: true, + dropShadowColor: '#000000', + dropShadowBlur: 3, + dropShadowAngle: Math.PI / 6, + dropShadowDistance: 1, + wordWrap: false, + wordWrapWidth: 100, + }); + + text = new PIXI.Text(this.assetData.Name + + '\r\n' + + this.assetData.PropertyInfos?.find(item => item.PropertyName === '名称/编号')?.PropertyValue, this.style); + + /** + * 选中圆点 + */ + + image: PIXI.Sprite; + selectionBox = new PIXI.Graphics(); + connectPointTexture = PIXI.Texture.from('assets/images/handle-secondary.png'); + connectPoint: Sprite; + // 可移动的 + + // 可选中的 + + // up: PIXI.Sprite; + // down: PIXI.Sprite; + // left: PIXI.Sprite; + // right: PIXI.Sprite; + // upLeft: PIXI.Sprite; + // upRight: PIXI.Sprite; + // downLeft: PIXI.Sprite; + // downRight: PIXI.Sprite; + constructor(assetData: any, workingArea: WorkingAreaComponent) { + super(assetData, workingArea); + this.x = this.assetData.Point.x; + this.y = this.assetData.Point.y; + this.name = this.assetData.Id; + this.image = PIXI.Sprite.from(this.assetData.ImageUrl); + this.image.angle = this.assetData.Angle; + + this.image.x = 0; + this.image.y = 0; + this.image.width = this.assetData.Width; + this.image.height = this.assetData.Height; + this.image.alpha = 1; + this.image.anchor.set(0.5); + // this.image.interactive = true; + // this.image.buttonMode = true; + // this.image + // .on('mousedown', event => { + // event.stopPropagation(); + // this.workingArea.selection.selectOne(this); + // // this.paintingPipeline(this.x, this.y); + // // 如果链接对象不为空,禁止移动 + // if (this.workingArea.allowEdit && this.assetData.GameMode === this.workingArea.canvasData.gameMode) { + // event.currentTarget.parent.data = event.data; + // event.currentTarget.parent.alpha = 0.5; + // event.currentTarget.parent.dragging = true; + // } + // }) + // .on('mouseup', event => { + // if (event.currentTarget.parent.dragging) { + // event.currentTarget.parent.alpha = 1; + // event.currentTarget.parent.dragging = false; + // event.currentTarget.parent.data = null; + // } + // }) + // .on('mouseupoutside', event => { + // if (event.currentTarget.parent.dragging) { + // event.currentTarget.parent.alpha = 1; + // event.currentTarget.parent.dragging = false; + // event.currentTarget.parent.data = null; + // } + // }) + // .on('mousemove', event => { + // if (event.currentTarget.parent.dragging) { + // // // 如果拖动过程中发现父对象不是背景图 + // // if (this.parent !== this.workingArea.backgroundImage) { + // // this.setParent(this.workingArea.backgroundImage); + // // if (this.assetData.FixedSize) { + // // const scale = 1 / this.workingArea.backgroundImage.scale.x; + // // this.scale.set(scale); + // // } + // // } + // const newPosition = event.currentTarget.parent.data.getLocalPosition(event.currentTarget.parent.parent); + // event.currentTarget.parent.x = newPosition.x; + // event.currentTarget.parent.y = newPosition.y; + // this.assetData.Point = new PIXI.Point(this.x, this.y); + // this.workingArea.canvasData.isChange = true; + // } + // }) + // .on('rightclick', event => { + + // }) + // .on('mouseover', event => { + // event.stopPropagation(); + // if (this.workingArea.previewImage !== null + // && this.workingArea.getPaintMode() === PaintMode.singlePointIcon) { + // this.workingArea.previewImage.visible = false; + // } + // // if (this.assetData.CanConnect) { + // // this.setSelectionBox(true, this.image); + // // } + // }) + // .on('mouseout', event => { + // event.stopPropagation(); + // if (this.workingArea.previewImage !== null + // && this.workingArea.getPaintMode() === PaintMode.singlePointIcon) { + // this.workingArea.previewImage.visible = true; + // } + // // if (this.assetData.CanConnect) { + // // this.setSelectionBox(false); + // // } + // }); + this.text.x = this.image.x; + this.text.y = this.image.y - this.image.height / 2; + this.text.anchor.set(0.5, 1); + + if (this.assetData.GameMode === 2) { + this.text.visible = false; + } + this.addChild(this.text); + this.addChild(this.image); + this.addChild(this.selectionBox); + + if (this.assetData.CanConnect) { + // connectPoint + this.connectPoint = new PIXI.Sprite(this.connectPointTexture); + this.connectPoint.anchor.set(0.5); + this.connectPoint.x = this.image.x; + this.connectPoint.y = this.image.y; + this.addChild(this.connectPoint); + this.connectPoint.interactive = true; + this.connectPoint + .on('mousedown', event => { + event.stopPropagation(); + this.paintingPipeline(this.x, this.y); + }) + .on('mouseover', event => { + this.setSelectionBox(true, this.connectPoint); + }) + .on('mouseout', event => { + this.setSelectionBox(false); + }); + // // up + // this.up = new PIXI.Sprite(this.selectedPointTexture); + // this.up.anchor.set(0.5); + // this.up.x = this.image.x; + // this.up.y = this.image.y - (this.image.height / 2); + // this.addChild(this.up); + // this.up.interactive = true; + // this.up + // .on('mousedown', event => { + // event.stopPropagation(); + // const pt = this.toGlobal(new PIXI.Point(this.up.x, this.up.y)); + // const pt2 = this.workingArea.backgroundImage.toLocal(pt); + // this.paintingPipeline(pt2.x, pt2.y); + // }) + // .on('mouseover', event => { + // this.setSelectionBox(true, this.up); + // }) + // .on('mouseout', event => { + // this.setSelectionBox(false); + // }); + // // down + // this.down = new PIXI.Sprite(this.selectedPointTexture); + // this.down.anchor.set(0.5); + // this.down.x = this.image.x; + // this.down.y = this.image.y + (this.image.height / 2); + // this.addChild(this.down); + // this.down.interactive = true; + // this.down + // .on('mousedown', event => { + // event.stopPropagation(); + // const pt = this.toGlobal(new PIXI.Point(this.down.x, this.down.y)); + // const pt2 = this.workingArea.backgroundImage.toLocal(pt); + // this.paintingPipeline(pt2.x, pt2.y); + // }) + // .on('mouseover', event => { + // this.setSelectionBox(true, this.down); + // }) + // .on('mouseout', event => { + // this.setSelectionBox(false); + // }); + // // left + // this.left = new PIXI.Sprite(this.selectedPointTexture); + // this.left.anchor.set(0.5); + // this.left.x = this.image.x - (this.image.width / 2); + // this.left.y = this.image.y; + // this.addChild(this.left); + // this.left.interactive = true; + // this.left + // .on('mousedown', event => { + // event.stopPropagation(); + // const pt = this.toGlobal(new PIXI.Point(this.left.x, this.left.y)); + // const pt2 = this.workingArea.backgroundImage.toLocal(pt); + // this.paintingPipeline(pt2.x, pt2.y); + // }) + // .on('mouseover', event => { + // this.setSelectionBox(true, this.left); + // }) + // .on('mouseout', event => { + // this.setSelectionBox(false); + // }); + // // right + // this.right = new PIXI.Sprite(this.selectedPointTexture); + // this.right.anchor.set(0.5); + // this.right.x = this.image.x + (this.image.width / 2); + // this.right.y = this.image.y; + // this.addChild(this.right); + // this.right.interactive = true; + // this.right + // .on('mousedown', event => { + // event.stopPropagation(); + // const pt = this.toGlobal(new PIXI.Point(this.right.x, this.right.y)); + // const pt2 = this.workingArea.backgroundImage.toLocal(pt); + // this.paintingPipeline(pt2.x, pt2.y); + // }) + // .on('mouseover', event => { + // this.setSelectionBox(true, this.right); + // }) + // .on('mouseout', event => { + // this.setSelectionBox(false); + // }); + // // up-left + // this.upLeft = new PIXI.Sprite(this.selectedPointTexture); + // this.upLeft.anchor.set(0.5); + // this.upLeft.x = this.image.x - (this.image.width / 2); + // this.upLeft.y = this.image.y - (this.image.height / 2); + // this.addChild(this.upLeft); + // this.upLeft.interactive = true; + // this.upLeft + // .on('mousedown', event => { + // event.stopPropagation(); + // const pt = this.toGlobal(new PIXI.Point(this.upLeft.x, this.upLeft.y)); + // const pt2 = this.workingArea.backgroundImage.toLocal(pt); + // this.paintingPipeline(pt2.x, pt2.y); + // }) + // .on('mouseover', event => { + // this.setSelectionBox(true, this.upLeft); + // }) + // .on('mouseout', event => { + // this.setSelectionBox(false); + // }); + // // up-right + // this.upRight = new PIXI.Sprite(this.selectedPointTexture); + // this.upRight.anchor.set(0.5); + // this.upRight.x = this.image.x + (this.image.width / 2); + // this.upRight.y = this.image.y - (this.image.height / 2); + // this.addChild(this.upRight); + // this.upRight.interactive = true; + // this.upRight + // .on('mousedown', event => { + // event.stopPropagation(); + // const pt = this.toGlobal(new PIXI.Point(this.upRight.x, this.upRight.y)); + // const pt2 = this.workingArea.backgroundImage.toLocal(pt); + // this.paintingPipeline(pt2.x, pt2.y); + // }) + // .on('mouseover', event => { + // this.setSelectionBox(true, this.upRight); + // }) + // .on('mouseout', event => { + // this.setSelectionBox(false); + // }); + + // // down-left + // this.downLeft = new PIXI.Sprite(this.selectedPointTexture); + // this.downLeft.anchor.set(0.5); + // this.downLeft.x = this.image.x - (this.image.width / 2); + // this.downLeft.y = this.image.y + (this.image.height / 2); + // this.addChild(this.downLeft); + // this.downLeft.interactive = true; + // this.downLeft + // .on('mousedown', event => { + // event.stopPropagation(); + // const pt = this.toGlobal(new PIXI.Point(this.downLeft.x, this.downLeft.y)); + // const pt2 = this.workingArea.backgroundImage.toLocal(pt); + // this.paintingPipeline(pt2.x, pt2.y); + // }) + // .on('mouseover', event => { + // this.setSelectionBox(true, this.downLeft); + // }) + // .on('mouseout', event => { + // this.setSelectionBox(false); + // }); + // // down-right + // this.downRight = new PIXI.Sprite(this.selectedPointTexture); + // this.downRight.anchor.set(0.5); + // this.downRight.x = this.image.x + (this.image.width / 2); + // this.downRight.y = this.image.y + (this.image.height / 2); + // this.addChild(this.downRight); + // this.downRight.interactive = true; + // this.downRight + // .on('mousedown', event => { + // event.stopPropagation(); + // const pt = this.toGlobal(new PIXI.Point(this.downRight.x, this.downRight.y)); + // const pt2 = this.workingArea.backgroundImage.toLocal(pt); + // this.paintingPipeline(pt2.x, pt2.y); + // }) + // .on('mouseover', event => { + // this.setSelectionBox(true, this.downRight); + // }) + // .on('mouseout', event => { + // this.setSelectionBox(false); + // }); + + this.showConnectionPoint(false); + } + } + // 设置选择框 + public setSelectionBox(b: boolean, sprite?: PIXI.Sprite) { + if (b) { + this.selectionBox.lineStyle(2, 0x00EB00, 1); + this.selectionBox.position = sprite.position; + this.selectionBox.drawRect(- sprite.width / 2, - sprite.height / 2, sprite.width, sprite.height); + // const p0 = new PIXI.Point(- sprite.width / 2, - sprite.height / 2); + // const pe = new PIXI.Point(sprite.width / 2, sprite.height / 2); + // const pw = new PIXI.Point(p0.x + sprite.width, p0.y); + // const ph = new PIXI.Point(p0.x, p0.y + sprite.height); + // this.drawDashedLine(this.selectionBox, p0, pw, 0x1234ff); + // this.drawDashedLine(this.selectionBox, p0, ph, 0x1234ff); + // this.drawDashedLine(this.selectionBox, pe, pw, 0x1234ff); + // this.drawDashedLine(this.selectionBox, pe, ph, 0x1234ff); + } else { + this.selectionBox.clear(); + } + } + // 设置名称 + public setNameVisible(value: boolean, mode: GameMode) { + if (this.assetData.GameMode === mode) { + this.text.visible = value; + } + } + // 显示连接点 + public showConnectionPoint(b: boolean) { + this.connectPoint.visible = b; + // this.up.visible = b; + // this.down.visible = b; + // this.left.visible = b; + // this.right.visible = b; + // this.upLeft.visible = b; + // this.downLeft.visible = b; + // this.upRight.visible = b; + // this.downRight.visible = b; + } + paintingPipeline(x: number, y: number) { + if (this.assetData.CanConnect) { + if (this.workingArea.getPaintMode() === PaintMode.Pipeline) { + if (this.workingArea.paintingPipeline === null) { + this.workingArea.previewLineSegment.visible = true; + this.workingArea.currentClickPoint.position = + new PIXI.Point(this.workingArea.circleShadow.x, this.workingArea.circleShadow.y); + this.workingArea.paintPoints.push(new PIXI.Point(x, y)); + const json = JSON.parse(JSON.stringify(this.workingArea.canvasData.selectTemplateData.propertyInfos)); + const list = []; + json.forEach(element => { + const property = new PropertyInfo(element); + list.push(property); + }); + const tempData = { + TemplateId: this.workingArea.canvasData.selectTemplateData.id, + CanConnect: this.workingArea.canvasData.selectTemplateData.canConnect, + Pipelines: new Array(), + FloorId: this.workingArea.canvasData.selectStorey.id, + Angle: this.workingArea.canvasData.selectTemplateData.angle, + Color: this.workingArea.canvasData.selectTemplateData.color, + Enabled: this.workingArea.canvasData.selectTemplateData.enabled, + FillMode: this.workingArea.canvasData.selectTemplateData.fillMode, + FireElementId: this.workingArea.canvasData.selectTemplateData.fireElementId, + FixedSize: this.workingArea.canvasData.selectTemplateData.fixedSize, + Height : 32, + Width : 32, + Id: ObjectID.default.generate(), + ImageUrl: this.workingArea.canvasData.selectTemplateData.imageUrl, + InteractiveMode: this.workingArea.canvasData.selectTemplateData.interactiveMode, + MultiPoint : JSON.parse(JSON.stringify(this.workingArea.paintPoints)), + Point: new PIXI.Point(0, 0), + Name : this.workingArea.canvasData.selectTemplateData.name, + PropertyInfos: list, + Border : this.workingArea.canvasData.selectTemplateData.border, + DrawMode : this.workingArea.canvasData.selectTemplateData.drawMode, + Thickness : this.workingArea.canvasData.selectTemplateData.thickness, + IsFromBuilding : this.workingArea.canvasData.selectTemplateData.isFromBuilding, + GameMode: this.workingArea.canvasData.gameMode, + LinkedObjects: new Array(this.assetData.Id), + }; + this.workingArea.paintingPipeline = new Pipeline(tempData, this.workingArea); + this.assetData.Pipelines.push(this.workingArea.paintingPipeline.assetData.Id); + this.workingArea.emit('createIcon', this.workingArea.paintingPipeline); + } else { + this.workingArea.previewLineSegment.visible = false; + this.workingArea.currentClickPoint.position = + new PIXI.Point(this.workingArea.circleShadow.x, this.workingArea.circleShadow.y); + this.workingArea.paintPoints.push(new PIXI.Point(x, y)); + this.workingArea.paintingPipeline.assetData.MultiPoint = + JSON.parse(JSON.stringify(this.workingArea.paintPoints)); + this.workingArea.paintingPipeline.assetData.LinkedObjects.push(this.assetData.Id); + this.assetData.Pipelines.push(this.workingArea.paintingPipeline.assetData.Id); + this.workingArea.paintingPipeline.refresh(); + this.workingArea.initPipelineData(); + } + } + } + } + // 刷新 + public refresh() { + if (this.assetData.CanConnect) { + + } + this.image.width = this.assetData.Width; + this.image.height = this.assetData.Height; + this.image.angle = this.assetData.Angle; + this.text.text = this.assetData.Name + + '\r\n' + + this.assetData.PropertyInfos?.find(item => item.PropertyName === '名称/编号')?.PropertyValue; + this.text.x = this.image.x; + this.text.y = this.image.y - this.image.height / 2; + } +} diff --git a/src/app/working-area/model/axPreviewImageShape.ts b/src/app/working-area/model/axPreviewImageShape.ts new file mode 100644 index 0000000..623b2d7 --- /dev/null +++ b/src/app/working-area/model/axPreviewImageShape.ts @@ -0,0 +1,27 @@ +import { Sprite, Texture } from 'pixi.js'; +import { WorkingAreaComponent } from '../working-area.component'; +import { AxShape } from './axShape'; + +export class AxPreviewImageShape extends AxShape { + image: Sprite = null; + /** + * + */ + constructor(workingArea: WorkingAreaComponent) { + super(null, workingArea); + this.image = new Sprite(); + this.image.width = 32; + this.image.height = 32; + this.image.anchor.set(0.5); + this.interactive = false; + this.scale.set(1 / this.workingArea.backgroundImage.scale.x); + this.addChild(this.image); + } + /** + * 重新设置图片地址 + * @param url 图片路径 + */ + setImageUrl(url: string) { + this.image.texture = Texture.from(url); + } +} diff --git a/src/app/working-area/model/axShape.ts b/src/app/working-area/model/axShape.ts new file mode 100644 index 0000000..bd82842 --- /dev/null +++ b/src/app/working-area/model/axShape.ts @@ -0,0 +1,76 @@ +import { Constructor } from '@angular/material/core/common-behaviors/constructor'; +import * as PIXI from 'pixi.js'; +import { Point, Rectangle, Graphics, Container } from 'pixi.js'; +import { WorkingAreaComponent } from '../working-area.component'; + +/** + * 安信形状 + */ +export class AxShape extends Container { + assetData: any; + workingArea: WorkingAreaComponent; + // 可以被移动的 + moveable = true; + // 可以被选中的 + selectable = true; + + constructor(assetData: any, workingArea: WorkingAreaComponent) { + super(); + this.assetData = assetData; + this.workingArea = workingArea; + this.workingArea.backgroundImage.addChild(this); + this.interactive = true; + this.buttonMode = true; + this.on('mousedown', event => { + console.log(this.assetData); + event.stopPropagation(); + if (this.selectable) { + this.workingArea.selection.selectOne(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; + } + }) + .on('mouseup', event => { + if (event.currentTarget.dragging) { + event.currentTarget.alpha = 1; + event.currentTarget.dragging = false; + event.currentTarget.data = null; + } + }) + .on('mouseupoutside', event => { + if (event.currentTarget.dragging) { + event.currentTarget.alpha = 1; + event.currentTarget.dragging = false; + event.currentTarget.data = null; + } + }) + .on('mousemove', event => { + if (event.currentTarget.dragging) { + 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; + this.assetData.Point = new PIXI.Point(this.x, this.y); + this.workingArea.canvasData.isChange = true; + } + }) + .on('rightclick', event => { + + }) + .on('mouseover', event => { + event.stopPropagation(); + }) + .on('mouseout', event => { + event.stopPropagation(); + }); + } + redraw(): void { + + } +} diff --git a/src/app/working-area/model/gameMode.ts b/src/app/working-area/model/gameMode.ts new file mode 100644 index 0000000..feabdbb --- /dev/null +++ b/src/app/working-area/model/gameMode.ts @@ -0,0 +1,7 @@ +/** + * 游戏状态 + */ +export enum GameMode { + BasicInformation, + Assignment +} \ No newline at end of file diff --git a/src/app/working-area/model/multipointIcon.ts b/src/app/working-area/model/multipointIcon.ts new file mode 100644 index 0000000..df2da6f --- /dev/null +++ b/src/app/working-area/model/multipointIcon.ts @@ -0,0 +1,246 @@ +import { WorkingAreaComponent } from '../working-area.component'; +import { GameMode } from './gameMode'; +import * as PIXI from 'pixi.js'; + +/** + * 多点连线 + */ +export class MultipointIcon extends PIXI.Container { + public pointsData: PIXI.Point[]; + public pointsGraphics: PIXI.Graphics[] = []; + public iconsTilingSprite: PIXI.TilingSprite[] = []; + style = new PIXI.TextStyle({ + fontFamily: 'Arial', + fontSize: 18, + fontStyle: 'normal', + fontWeight: 'bold', + fill: ['#000000'], + stroke: '#ffffff', + strokeThickness: 3, + dropShadow: true, + dropShadowColor: '#000000', + dropShadowBlur: 3, + dropShadowAngle: Math.PI / 6, + dropShadowDistance: 1, + wordWrap: false, + wordWrapWidth: 100, + }); + + public text = new PIXI.Text(this.assetData.Name + + '\r\n' + + this.assetData.PropertyInfos?.find(item => item.PropertyName === '名称/编号')?.PropertyValue, this.style); + /** + * + * @param texture 图片素材 + * @param points 点集合 + */ + constructor(public assetData: any, private workingArea: WorkingAreaComponent) { + super(); + this.name = this.assetData.Id; + this.pointsData = this.assetData.MultiPoint; + this.x = this.assetData.Point.x; + this.y = this.assetData.Point.y; + this.workingArea.backgroundImage.addChild(this); + // 画线图标 + for (let i = 0, count = this.pointsData.length - 1; i < count; i++) { + const pointA = this.pointsData[i]; + const pointB = this.pointsData[i + 1]; + + const angle = Math.atan2((pointB.y - pointA.y), (pointB.x - pointA.x)) * (180 / Math.PI); + const a = pointB.x - pointA.x; + const b = pointB.y - pointA.y; + const distance = Math.sqrt(a * a + b * b); + + const icon = new PIXI.TilingSprite(PIXI.Texture.from(this.assetData.ImageUrl), distance, 64); + icon.anchor.set(0, 0.5); + icon.x = pointA.x; + icon.y = pointA.y; + icon.angle = angle; + icon.height = this.assetData.Thickness === 0 ? 32 : this.assetData.Thickness; + this.iconsTilingSprite.push(icon); + this.addChild(icon); + if (i === 0) { + this.text.anchor.set(0.5); + this.text.position = icon.position; + this.text.y -= this.assetData.Height; + this.addChild(this.text); + } + } + // 画点 + this.pointsData.forEach((item, index, array) => { + const iconPoint = new PIXI.Graphics(); + iconPoint.lineStyle(1, 0xFFBD01, 1); + iconPoint.beginFill(0xFFFFFF, 1); + iconPoint.drawCircle(0, 0, 15); + iconPoint.x = item.x; + iconPoint.y = item.y; + iconPoint.endFill(); + iconPoint.visible = false; + this.pointsGraphics.push(iconPoint); + this.addChild(iconPoint); + }); + // 添加圆点事件 + this.pointsGraphics.forEach((item, index, array) => { + item.interactive = true; + item.on('mousedown', event => { + event.stopPropagation(); + if (this.workingArea.allowEdit && this.assetData.GameMode === this.workingArea.canvasData.gameMode) { + event.currentTarget.data = event.data; + event.currentTarget.alpha = 0.5; + event.currentTarget.dragging = true; + } + }) + .on('mouseup', event => { + if (event.currentTarget.dragging) { + event.currentTarget.alpha = 1; + event.currentTarget.dragging = false; + event.currentTarget.data = null; + } + }) + .on('mouseupoutside', event => { + if (event.currentTarget.dragging) { + event.currentTarget.alpha = 1; + event.currentTarget.dragging = false; + event.currentTarget.data = null; + } + }) + .on('mousemove', event => { + if (event.currentTarget.dragging) { + const newPosition = event.currentTarget.data.getLocalPosition(event.currentTarget.parent); + event.currentTarget.x = newPosition.x; + event.currentTarget.y = newPosition.y; + + this.assetData.MultiPoint[index].x = newPosition.x; + this.assetData.MultiPoint[index].y = newPosition.y; + this.workingArea.canvasData.isChange = true; + + if (index === 0) {// 第一个点 + this.iconsTilingSprite[index].x = newPosition.x; + this.iconsTilingSprite[index].y = newPosition.y; + + const pointA = array[index]; + const pointB = array[index + 1]; + + const angle = Math.atan2((pointB.y - pointA.y), (pointB.x - pointA.x)) * (180 / Math.PI); + const a = pointB.x - pointA.x; + const b = pointB.y - pointA.y; + const distance = Math.sqrt(a * a + b * b); + this.iconsTilingSprite[index].angle = angle; + this.iconsTilingSprite[index].width = distance; + + this.text.position = this.iconsTilingSprite[index].position; + this.text.y -= this.assetData.Height; + } else if (index < array.length - 1) {// 不是第一个点,也不是最后一个点 + this.iconsTilingSprite[index].x = newPosition.x; + this.iconsTilingSprite[index].y = newPosition.y; + + const pointA = array[index]; // 当前点 + const pointB = array[index + 1]; // 后一个点 + const pointC = array[index - 1]; // 前一个点 + + const angle = Math.atan2((pointB.y - pointA.y), (pointB.x - pointA.x)) * (180 / Math.PI); + const a = pointB.x - pointA.x; + const b = pointB.y - pointA.y; + const distance = Math.sqrt(a * a + b * b); + this.iconsTilingSprite[index].angle = angle; + this.iconsTilingSprite[index].width = distance; + + const angleC = Math.atan2((pointA.y - pointC.y), (pointA.x - pointC.x)) * (180 / Math.PI); + const aC = pointA.x - pointC.x; + const bC = pointA.y - pointC.y; + const distanceC = Math.sqrt(aC * aC + bC * bC); + this.iconsTilingSprite[index - 1].angle = angleC; + this.iconsTilingSprite[index - 1].width = distanceC; + } else if (index === array.length - 1) { // 最后一个点 + const pointA = array[index]; // 当前点 + const pointC = array[index - 1]; // 前一个点 + + const angleC = Math.atan2((pointA.y - pointC.y), (pointA.x - pointC.x)) * (180 / Math.PI); + const aC = pointA.x - pointC.x; + const bC = pointA.y - pointC.y; + const distanceC = Math.sqrt(aC * aC + bC * bC); + this.iconsTilingSprite[index - 1].angle = angleC; + this.iconsTilingSprite[index - 1].width = distanceC; + } + } + }) + .on('rightclick', event => { + }); + }); + // // 缩放 + // this.workingArea.on('backgroundScale', data => { + // const scale = 1 / data; + // this.text.scale.set(scale); + // }); + // 添加选中事件 + this.iconsTilingSprite.forEach((item, index, array) => { + item.interactive = true; + item.buttonMode = true; + item.on('mousedown', event => { + event.stopPropagation(); + this.workingArea.selection.selectOne(this); + if (this.workingArea.allowEdit && this.assetData.GameMode === this.workingArea.canvasData.gameMode) { + event.currentTarget.parent.data = event.data; + event.currentTarget.parent.alpha = 0.5; + event.currentTarget.parent.dragging = true; + + event.currentTarget.parent.dragPoint = event.data.getLocalPosition(event.currentTarget.parent.parent); + event.currentTarget.parent.dragPoint.x -= event.currentTarget.parent.x; + event.currentTarget.parent.dragPoint.y -= event.currentTarget.parent.y; + } + }) + .on('mouseup', event => { + if (event.currentTarget.parent.dragging) { + event.currentTarget.parent.alpha = 1; + event.currentTarget.parent.dragging = false; + event.currentTarget.parent.data = null; + } + }) + .on('mouseupoutside', event => { + if (event.currentTarget.parent.dragging) { + event.currentTarget.parent.alpha = 1; + event.currentTarget.parent.dragging = false; + event.currentTarget.parent.data = null; + } + }) + .on('mousemove', event => { + if (event.currentTarget.parent.dragging) { + const newPosition = event.currentTarget.parent.data.getLocalPosition(event.currentTarget.parent.parent); + event.currentTarget.parent.x = newPosition.x - event.currentTarget.parent.dragPoint.x; + event.currentTarget.parent.y = newPosition.y - event.currentTarget.parent.dragPoint.y; + + this.assetData.Point = new PIXI.Point(this.x, this.y); + this.workingArea.canvasData.isChange = true; + } + }) + .on('rightclick', event => { + + }); + }); + } + /** + * 设置点显示状态 + * @param value 显示状态 + */ + public setPointVisiable(value: boolean) { + this.pointsGraphics.forEach((item) => { + item.visible = value; + }); + } + // 设置名称 + public setNameVisible(value: boolean, mode: GameMode) { + if (this.assetData.GameMode === mode) { + this.text.visible = value; + } + } + // 刷新数据 + public refresh() { + console.log(this.assetData); + this.iconsTilingSprite.forEach(element => { + element.height = this.assetData.Thickness === 0 ? 32 : this.assetData.Thickness; + }); + this.text.text = this.assetData.Name + + '\r\n' + + this.assetData.PropertyInfos.find(item => item.PropertyName === '名称/编号')?.PropertyValue; + } +} diff --git a/src/app/working-area/model/paintModel.ts b/src/app/working-area/model/paintModel.ts new file mode 100644 index 0000000..39a06ce --- /dev/null +++ b/src/app/working-area/model/paintModel.ts @@ -0,0 +1,33 @@ +/** + * 绘制模式 + */ +export enum PaintMode { + /** + * 单点图标 + */ + singlePointIcon, + /** + * 线段图标 + */ + lineIcon, + /** + * 自定义多边形 + */ + polygonIcon, + /** + * 水带多边形 + */ + Pipeline, + /** + * 结束绘制 + */ + endPaint, + /** + * 暂无 + */ + Arrows, + /** + * 暂无 + */ + Car, +} diff --git a/src/app/working-area/model/pipeline.ts b/src/app/working-area/model/pipeline.ts new file mode 100644 index 0000000..74045ad --- /dev/null +++ b/src/app/working-area/model/pipeline.ts @@ -0,0 +1,328 @@ +import { WorkingAreaComponent } from '../working-area.component'; +import * as PIXI from 'pixi.js'; +import { AxShape } from './axShape'; + +/** + * 管线 + */ +export class Pipeline extends AxShape { + public line: PIXI.Graphics = new PIXI.Graphics(); + constructor(assetData: any, workingArea: WorkingAreaComponent) { + super(assetData, workingArea); + this.name = this.assetData.Id; + this.moveable = false; + this.x = this.assetData.Point.x; + this.y = this.assetData.Point.y; + this.workingArea.backgroundImage.addChild(this); + this.addChild(this.line); + // 画线图标 + this.refresh(); + this.interactive = true; + this.on('mousedown', event => { + event.stopPropagation(); + this.workingArea.selection.selectOne(this); + }); + } + /** + * 刷新 + */ + public refresh() { + + const strokeWidth = 1; + const startWidth = 30 + strokeWidth; + const endWidth = 30 + strokeWidth; + const edgeWidth = 10; + const openEnded = false; + const markerStart = false; + const markerEnd = true; + const spacing = (openEnded) ? 0 : 0 + strokeWidth / 2; + const startSize = 30 + strokeWidth; + const endSize = 30 + strokeWidth; + const isRounded = true; + const pts = this.assetData.MultiPoint; + const c = this.line; + if (pts.length < 2) { return; } + // Base vector (between first points) + const pe = pts[pts.length - 1]; + + // Finds first non-overlapping point + let i0 = 1; + + while (i0 < pts.length - 1 && pts[i0].x === pts[0].x && pts[i0].y === pts[0].y) { + i0++; + } + + const dx = pts[i0].x - pts[0].x; + const dy = pts[i0].y - pts[0].y; + const dist = Math.sqrt(dx * dx + dy * dy); + + if (dist === 0) { + return; + } + + // Computes the norm and the inverse norm + let nx = dx / dist; + let nx1 = nx; + let nx2 = nx; + let ny = dy / dist; + let ny2 = ny; + let ny1 = ny; + let orthx = edgeWidth * ny; + let orthy = -edgeWidth * nx; + + // Stores the inbound function calls in reverse order in fns + const fns = []; + + // if (isRounded) { + // // c.setLineJoin('round'); + // c.lineTextureStyle({ join: PIXI.LINE_JOIN.ROUND }); + // } else if (pts.length > 2) { + // // Only mitre if there are waypoints + // // c.setMiterLimit(1.42); + // c.lineTextureStyle({ miterLimit: 1.42 }); + // } + // c.lineStyle(1, 0x000000, 1); + c.clear(); + c.lineTextureStyle({ width: 1, color: 0x00000, join: PIXI.LINE_JOIN.ROUND }); + // c.begin(); + c.beginFill(0xffffff); + const startNx = nx; + const startNy = ny; + + if (markerStart && !openEnded) { + this.paintMarker(c, pts[0].x, pts[0].y, nx, ny, startSize, startWidth, edgeWidth, spacing, true); + } else { + const outStartX = pts[0].x + orthx / 2 + spacing * nx; + const outStartY = pts[0].y + orthy / 2 + spacing * ny; + const inEndX = pts[0].x - orthx / 2 + spacing * nx; + const inEndY = pts[0].y - orthy / 2 + spacing * ny; + + if (openEnded) { + c.moveTo(outStartX, outStartY); + fns.push( () => { + c.lineTo(inEndX, inEndY); + }); + } else { + c.moveTo(inEndX, inEndY); + c.lineTo(outStartX, outStartY); + } + } + let dx1 = 0; + let dy1 = 0; + let dist1 = 0; + + for (let i = 0; i < pts.length - 2; i++) { + // Work out in which direction the line is bending + const pos = this.relativeCcw(pts[i].x, pts[i].y, pts[i + 1].x, pts[i + 1].y, pts[i + 2].x, pts[i + 2].y); + + dx1 = pts[i + 2].x - pts[i + 1].x; + dy1 = pts[i + 2].y - pts[i + 1].y; + + dist1 = Math.sqrt(dx1 * dx1 + dy1 * dy1); + + if (dist1 !== 0) { + nx1 = dx1 / dist1; + ny1 = dy1 / dist1; + + const tmp1 = nx * nx1 + ny * ny1; + const tmp = Math.max(Math.sqrt((tmp1 + 1) / 2), 0.04); + + // Work out the normal orthogonal to the line through the control point and the edge sides intersection + nx2 = (nx + nx1); + ny2 = (ny + ny1); + + const dist2 = Math.sqrt(nx2 * nx2 + ny2 * ny2); + + if (dist2 !== 0) { + nx2 = nx2 / dist2; + ny2 = ny2 / dist2; + + // Higher strokewidths require a larger minimum bend, 0.35 covers all but the most extreme cases + const strokeWidthFactor = Math.max(tmp, Math.min(1 / 200 + 0.04, 0.35)); + const angleFactor = (pos !== 0 && isRounded) ? Math.max(0.1, strokeWidthFactor) : Math.max(tmp, 0.06); + + const outX = pts[i + 1].x + ny2 * edgeWidth / 2 / angleFactor; + const outY = pts[i + 1].y - nx2 * edgeWidth / 2 / angleFactor; + const inX = pts[i + 1].x - ny2 * edgeWidth / 2 / angleFactor; + const inY = pts[i + 1].y + nx2 * edgeWidth / 2 / angleFactor; + + if (pos === 0 || !isRounded) { + // If the two segments are aligned, or if we're not drawing curved sections between segments + // just draw straight to the intersection point + c.lineTo(outX, outY); + + ((x, y) => { + fns.push(() => { + c.lineTo(x, y); + }); + })(inX, inY); + } else if (pos === -1) { + const c1x = inX + ny * edgeWidth; + const c1y = inY - nx * edgeWidth; + const c2x = inX + ny1 * edgeWidth; + const c2y = inY - nx1 * edgeWidth; + c.lineTo(c1x, c1y); + if (isRounded) { + c.quadraticCurveTo(outX, outY, c2x, c2y); // 圆角 + } else { + c.lineTo(outX, outY); + } + ((x, y) => { + fns.push(() => { + c.lineTo(x, y); + }); + })(inX, inY); + } else { + c.lineTo(outX, outY); + + ((x, y) => { + const c1x = outX - ny * edgeWidth; + const c1y = outY + nx * edgeWidth; + const c2x = outX - ny1 * edgeWidth; + const c2y = outY + nx1 * edgeWidth; + + fns.push(() => { + if (isRounded) { + c.quadraticCurveTo(x, y, c1x, c1y); + } else { + c.lineTo(x, y); + } + }); + fns.push(() => { + c.lineTo(c2x, c2y); + }); + })(inX, inY); + } + + nx = nx1; + ny = ny1; + } + } + } + orthx = edgeWidth * ny1; + orthy = - edgeWidth * nx1; + + if (markerEnd && !openEnded) { + this.paintMarker(c, pe.x, pe.y, -nx, -ny, endSize, endWidth, edgeWidth, spacing, false); + } else { + c.lineTo(pe.x - spacing * nx1 + orthx / 2, pe.y - spacing * ny1 + orthy / 2); + + const inStartX = pe.x - spacing * nx1 - orthx / 2; + const inStartY = pe.y - spacing * ny1 - orthy / 2; + + if (!openEnded) { + c.lineTo(inStartX, inStartY); + } else { + c.moveTo(inStartX, inStartY); + + fns.splice(0, 0, () => { + c.moveTo(inStartX, inStartY); + }); + } + } + + for (let i = fns.length - 1; i >= 0; i--) { + fns[i](); + } + c.closePath(); + c.endFill(); + // if (openEnded) + // { + // c.end(); + // c.stroke(); + // } + // else + // { + // c.close(); + // c.fillAndStroke(); + // } + + // Workaround for shadow on top of base arrow + // c.setShadow(false); + + // Need to redraw the markers without the low miter limit + // c.setMiterLimit(4); + + // if (isRounded) + // { + // c.setLineJoin('flat'); + // } + + // if (pts.length > 2) { + // // Only to repaint markers if no waypoints + // // Need to redraw the markers without the low miter limit + // // c.setMiterLimit(4); + // c.lineTextureStyle({ width: 1, color: 0x00000, miterLimit: 4 }); + // if (markerStart && !openEnded) { + // // c.begin(); + // this.paintMarker(c, pts[0].x, pts[0].y, startNx, startNy, startSize, startWidth, edgeWidth, spacing, true); + // // c.stroke(); + // // c.end(); + // // c.closePath(); + // } + + // if (markerEnd && !openEnded) { + // // c.begin(); + // this.paintMarker(c, pe.x, pe.y, -nx, -ny, endSize, endWidth, edgeWidth, spacing, true); + // // c.stroke(); + // // c.end(); + // // c.closePath(); + // } + // } + } + /** + * 画箭头 + * @param c + * @param ptX + * @param ptY + * @param nx + * @param ny + * @param size + * @param arrowWidth + * @param edgeWidth + * @param spacing + * @param initialMove + */ + paintMarker(c: PIXI.Graphics, ptX: number, ptY: number, nx: number, ny: number, + size: number, arrowWidth: number, edgeWidth: number, spacing: number, initialMove: boolean) { + const widthArrowRatio = edgeWidth / arrowWidth; + const orthx = edgeWidth * ny / 2; + const orthy = -edgeWidth * nx / 2; + + const spaceX = (spacing + size) * nx; + const spaceY = (spacing + size) * ny; + + if (initialMove) { + c.moveTo(ptX - orthx + spaceX, ptY - orthy + spaceY); + } else { + c.lineTo(ptX - orthx + spaceX, ptY - orthy + spaceY); + } + c.lineTo(ptX - orthx / widthArrowRatio + spaceX, ptY - orthy / widthArrowRatio + spaceY); + c.lineTo(ptX + spacing * nx, ptY + spacing * ny); + c.lineTo(ptX + orthx / widthArrowRatio + spaceX, ptY + orthy / widthArrowRatio + spaceY); + c.lineTo(ptX + orthx + spaceX, ptY + orthy + spaceY); + } + + relativeCcw(x1: number, y1: number, x2: number, y2: number, px: number, py: number) { + x2 -= x1; + y2 -= y1; + px -= x1; + py -= y1; + let ccw = px * y2 - py * x2; + + if (ccw === 0.0) { + ccw = px * x2 + py * y2; + + if (ccw > 0.0) { + px -= x2; + py -= y2; + ccw = px * x2 + py * y2; + + if (ccw < 0.0) { + ccw = 0.0; + } + } + } + return (ccw < 0.0) ? -1 : ((ccw > 0.0) ? 1 : 0); + } +} diff --git a/src/app/working-area/model/polygonIcon.ts b/src/app/working-area/model/polygonIcon.ts new file mode 100644 index 0000000..a7d0eb9 --- /dev/null +++ b/src/app/working-area/model/polygonIcon.ts @@ -0,0 +1,269 @@ +import { WorkingAreaComponent } from '../working-area.component'; +import { GameMode } from './gameMode'; +import * as PIXI from 'pixi.js'; +import { PaintMode } from './paintModel'; + +/** + * 多边形 + */ +export class PolygonIcon extends PIXI.Container { + public pointsData: PIXI.Point[]; + public pointsGraphics: PIXI.Graphics[] = []; + public polygonGraphics: PIXI.Graphics = new PIXI.Graphics(); + public polygonLineGraphics: PIXI.Graphics = new PIXI.Graphics(); + style = new PIXI.TextStyle({ + fontFamily: 'Arial', + fontSize: 18, + fontStyle: 'normal', + fontWeight: 'bold', + fill: ['#000000'], + stroke: '#ffffff', + strokeThickness: 3, + dropShadow: true, + dropShadowColor: '#000000', + dropShadowBlur: 3, + dropShadowAngle: Math.PI / 6, + dropShadowDistance: 1, + wordWrap: false, + wordWrapWidth: 100, + }); + + public text = new PIXI.Text(this.assetData.Name + + '\r\n' + + this.assetData.PropertyInfos.find(item => item.PropertyName === '名称/编号')?.PropertyValue, this.style); + /** + * + * @param points 点集合 + */ + constructor(public assetData: any, private workingArea: WorkingAreaComponent) { + super(); + this.name = this.assetData.Id; + this.x = this.assetData.Point.x; + this.y = this.assetData.Point.y; + this.pointsData = this.assetData.MultiPoint; + this.workingArea.backgroundImage.addChild(this); + this.sortableChildren = true; + // 画点 + this.pointsData.forEach((item, index, array) => { + const iconPoint = new PIXI.Graphics(); + iconPoint.lineStyle(1, 0xFFBD01, 1); + iconPoint.beginFill(0xFFFFFF, 1); + iconPoint.drawCircle(0, 0, 15); + iconPoint.x = item.x; + iconPoint.y = item.y; + iconPoint.endFill(); + iconPoint.visible = false; + this.pointsGraphics.push(iconPoint); + this.addChild(iconPoint); + }); + // 填充多边形 + + const color: number = this.assetData.Color.substring(0, 7).replace('#', '0x'); + const angle: number = parseInt(this.assetData.Color.substring(7), 16) / 255; + this.polygonGraphics.beginFill(color, angle); + this.polygonGraphics.drawPolygon(this.getPoints()); + this.polygonGraphics.endFill(); + this.addChild(this.polygonGraphics); + // 画多边形 + this.polygonLineGraphics.lineStyle(5, 0xFFBD01, 1); + this.polygonLineGraphics.drawPolygon(this.getPoints()); + this.polygonLineGraphics.closePath(); + this.addChild(this.polygonLineGraphics); + + this.text.anchor.set(0.5); + this.text.position = this.calculatePolygonGravityCenter(this.pointsData); + // console.log(this.calculatePolygonGravityCenter(this.pointsData)); + this.polygonGraphics.addChild(this.text); + // 添加圆点事件 + this.pointsGraphics.forEach((item, index, array) => { + item.interactive = true; + item.buttonMode = true; + item.zIndex = 1; + item.on('mousedown', event => { + event.stopPropagation(); + if (this.workingArea.allowEdit && this.assetData.GameMode === this.workingArea.canvasData.gameMode) { + event.currentTarget.data = event.data; + event.currentTarget.alpha = 0.5; + event.currentTarget.dragging = true; + } + }) + .on('mouseup', event => { + if (event.currentTarget.dragging) { + event.currentTarget.alpha = 1; + event.currentTarget.dragging = false; + event.currentTarget.data = null; + } + }) + .on('mouseupoutside', event => { + if (event.currentTarget.dragging) { + event.currentTarget.alpha = 1; + event.currentTarget.dragging = false; + event.currentTarget.data = null; + } + }) + .on('mousemove', event => { + if (event.currentTarget.dragging) { + const newPosition = event.currentTarget.data.getLocalPosition(event.currentTarget.parent); + event.currentTarget.x = newPosition.x; + event.currentTarget.y = newPosition.y; + + this.assetData.MultiPoint[index].x = newPosition.x; + this.assetData.MultiPoint[index].y = newPosition.y; + this.workingArea.canvasData.isChange = true; + // 填充多边形 + this.polygonGraphics.clear(); + this.polygonGraphics.beginFill(color, angle); + this.polygonGraphics.drawPolygon(this.getPoints()); + this.polygonGraphics.endFill(); + // 画多边形 + this.polygonLineGraphics.clear(); + this.polygonLineGraphics.lineStyle(5, 0xFFBD01, 1); + this.polygonLineGraphics.drawPolygon(this.getPoints()); + this.polygonLineGraphics.closePath(); + + this.text.position = this.calculatePolygonGravityCenter(this.pointsData); + } + }) + .on('rightclick', event => { + }) .on('mouseover', event => { + event.stopPropagation(); + if (this.workingArea.previewImage !== null + && this.workingArea.getPaintMode() === PaintMode.singlePointIcon) { + this.workingArea.previewImage.visible = false; + } + }) + .on('mouseout', event => { + event.stopPropagation(); + if (this.workingArea.previewImage !== null + && this.workingArea.getPaintMode() === PaintMode.singlePointIcon) { + this.workingArea.previewImage.visible = true; + } + }); + }); + // 添加选中事件 + this.polygonGraphics.interactive = true; + this.polygonGraphics.buttonMode = true; + this.polygonGraphics + .on('mousedown', event => { + event.stopPropagation(); + this.workingArea.selection.selectOne(this); + if (this.workingArea.allowEdit && this.assetData.GameMode === this.workingArea.canvasData.gameMode) { + event.currentTarget.parent.data = event.data; + event.currentTarget.parent.alpha = 0.5; + event.currentTarget.parent.dragging = true; + + event.currentTarget.parent.dragPoint = event.data.getLocalPosition(event.currentTarget.parent.parent); + event.currentTarget.parent.dragPoint.x -= event.currentTarget.parent.x; + event.currentTarget.parent.dragPoint.y -= event.currentTarget.parent.y; + } + }) + .on('mouseup', event => { + if (event.currentTarget.parent.dragging) { + event.currentTarget.parent.alpha = 1; + event.currentTarget.parent.dragging = false; + event.currentTarget.parent.data = null; + } + }) + .on('mouseupoutside', event => { + if (event.currentTarget.parent.dragging) { + event.currentTarget.parent.alpha = 1; + event.currentTarget.parent.dragging = false; + event.currentTarget.parent.data = null; + } + }) + .on('mousemove', event => { + if (event.currentTarget.parent.dragging) { + const newPosition = event.currentTarget.parent.data.getLocalPosition(event.currentTarget.parent.parent); + event.currentTarget.parent.x = newPosition.x - event.currentTarget.parent.dragPoint.x; + event.currentTarget.parent.y = newPosition.y - event.currentTarget.parent.dragPoint.y; + + this.assetData.Point = new PIXI.Point(this.x, this.y); + this.workingArea.canvasData.isChange = true; + } + }) + .on('rightclick', event => { + // this.workingArea.selection.deselectAll(); + }) + .on('mouseover', event => { + event.stopPropagation(); + if (this.workingArea.previewImage !== null + && this.workingArea.getPaintMode() === PaintMode.singlePointIcon) { + this.workingArea.previewImage.visible = false; + } + }) + .on('mouseout', event => { + event.stopPropagation(); + if (this.workingArea.previewImage !== null + && this.workingArea.getPaintMode() === PaintMode.singlePointIcon) { + this.workingArea.previewImage.visible = true; + } + }); + } + /** + * 设置点显示状态 + * @param value 显示状态 + */ + public setPointVisiable(value: boolean) { + this.pointsGraphics.forEach((item) => { + item.visible = value; + }); + } + + public calculatePolygonGravityCenter(points: PIXI.Point[]) { + let area = 0.0; // 多边形面积 + let gravityLat = 0.0; // 重心点 latitude + let gravityLng = 0.0; // 重心点 longitude + points.forEach((item, index) => { + // 1 + const lat = item.x; + const lng = item.y; + const nextLat = points[(index + 1) % points.length].x; + const nextLng = points[(index + 1) % points.length].y; + // 2 + const tempArea = (nextLat * lng - nextLng * lat) / 2.0; + // 3 + area += tempArea; + // 4 + gravityLat += tempArea * (lat + nextLat) / 3; + gravityLng += tempArea * (lng + nextLng) / 3; + }); + // 5 + gravityLat = gravityLat / area; + gravityLng = gravityLng / area; + + return new PIXI.Point(gravityLat, gravityLng); + } + /** + * 获取点集合 + */ + public getPoints(): PIXI.Point[] { + const points: PIXI.Point[] = []; + this.pointsGraphics.forEach(item => { + points.push(item.position); + }); + return points; + } + /** + * 设置名称显示 + * @param value true/false 显示/隐藏 + * @param mode BasicInformation = 0 基本信息 + * Assignment想定作业 = 1 想定作业 + */ + public setNameVisible(value: boolean, mode: GameMode) { + if (this.assetData.GameMode === mode) { + this.text.visible = value; + } + } + public refresh() { + this.text.text = this.assetData.Name + + '\r\n' + + this.assetData.PropertyInfos.find(item => item.PropertyName === '名称/编号')?.PropertyValue; + // 填充多边形 + const color: number = this.assetData.Color.substring(0, 7).replace('#', '0x'); + const angle: number = parseInt(this.assetData.Color.substring(7), 16) / 255; + this.polygonGraphics.clear(); + this.polygonGraphics.beginFill(color, angle); + this.polygonGraphics.drawPolygon(this.getPoints()); + this.polygonGraphics.endFill(); + } +} diff --git a/src/app/working-area/model/putCarArea.ts b/src/app/working-area/model/putCarArea.ts new file mode 100644 index 0000000..25480b2 --- /dev/null +++ b/src/app/working-area/model/putCarArea.ts @@ -0,0 +1,59 @@ +// import { OldFilmFilter } from 'pixi-filters'; +// import { WorkingAreaComponent } from '../working-area.component'; +// import { PaintMode } from './paintModel'; +// import { SinglePointIcon } from './axImageShape'; +// import * as PIXI from 'pixi.js'; + +// /** +// * 汽车放置区域 +// */ +// export class PutCarArea extends PIXI.Container { +// public polygonGraphics: PIXI.Graphics = new PIXI.Graphics(); +// constructor(public assetData: any, private workingArea: WorkingAreaComponent) { +// super(); +// this.name = this.assetData.Id; +// this.x = this.assetData.Point.x; +// this.y = this.assetData.Point.y; +// this.workingArea.backgroundImage.addChild(this); +// this.sortableChildren = true; + +// // 填充多边形 + +// const color: number = this.assetData.Color.substring(0, 7).replace('#', '0x'); +// const angle: number = parseInt(this.assetData.Color.substring(7), 16) / 255; +// this.polygonGraphics.beginFill(color, angle); +// this.polygonGraphics.drawPolygon(this.assetData.MultiPoint); +// this.polygonGraphics.endFill(); +// this.addChild(this.polygonGraphics); +// // 添加选中事件 +// this.polygonGraphics.interactive = true; +// this.polygonGraphics +// .on('pointerdown', (event) => { +// if (this.workingArea.getPaintMode() === PaintMode.Car) { +// this.workingArea.selectCar.Point = +// new PIXI.Point(this.workingArea.previewSinglePointIcon.x, this.workingArea.previewSinglePointIcon.y); +// this.workingArea.selectCar.Angle = this.assetData.Direction; +// const car = new SinglePointIcon(this.workingArea.selectCar, this.workingArea); +// this.workingArea.setPaintMode(PaintMode.endPaint); +// } +// }) +// .on('pointerup', (event) => { + +// }) +// .on('pointerupoutside', (event) => { + +// }) +// .on('pointerover', (event) => { +// this.workingArea.previewSinglePointIcon.filters = null; +// this.workingArea.previewSinglePointIcon.zIndex = this.zIndex + 1; +// // 设置车辆方向 +// this.workingArea.previewSinglePointIcon.angle = this.assetData.Direction; +// console.log(this.assetData.Name); +// }) +// .on('pointerout', (event) => { +// this.workingArea.previewSinglePointIcon.filters = [ +// new OldFilmFilter() +// ]; +// }); +// } +// } diff --git a/src/app/working-area/working-area.component.ts b/src/app/working-area/working-area.component.ts index 8083e6b..ab365cd 100644 --- a/src/app/working-area/working-area.component.ts +++ b/src/app/working-area/working-area.component.ts @@ -1,10 +1,21 @@ -import { Component, OnInit, ElementRef, ViewChild, AfterViewInit } from '@angular/core'; +import { Component, OnInit, ElementRef, ViewChild, AfterViewInit, Input } from '@angular/core'; import * as PIXI from 'pixi.js'; import { EventEmitter } from 'events'; import { EventManager } from '@angular/platform-browser'; import { OutlineFilter } from 'pixi-filters'; -import { CanvasShareDataService } from '../canvas-share-data.service'; +import { AssetData, CanvasShareDataService, DisposalNodeData, FloorNodeData } from '../canvas-share-data.service'; import * as ObjectID from 'bson-objectid'; +import { Charm } from './charm'; +import { AxImageShape } from './model/axImageShape'; +import { GameMode } from './model/gameMode'; +import { MultipointIcon } from './model/multipointIcon'; +import { PolygonIcon } from './model/polygonIcon'; +import { Pipeline } from './model/pipeline'; +import { PaintMode } from './model/paintModel'; +import { AxShape } from './model/axShape'; +import { PropertyInfo } from './model/PropertyInfo'; +import { AxPreviewImageShape } from './model/axPreviewImageShape'; +import { AxArrowConnector } from './model/axArrowConnector'; @Component({ @@ -12,7 +23,9 @@ import * as ObjectID from 'bson-objectid'; templateUrl: './working-area.component.html', styleUrls: ['./working-area.component.scss'] }) - +/** + * 工作区 + */ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterViewInit { constructor(private eventManager: EventManager, public canvasData: CanvasShareDataService) { @@ -21,6 +34,10 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV @ViewChild('content') content: ElementRef; + /** + * 父组件 + */ + @Input() init: any; /** * pixijs 程序 */ @@ -32,11 +49,11 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV /** * 背景图 */ - public backgroundImage: PIXI.Sprite; + public backgroundImage: PIXI.Sprite = null; /** - * 预览单点图标 + * 绘制图片形状时预览状态的图片 */ - public previewSinglePointIcon = new PIXI.Sprite(); + public previewImage: AxPreviewImageShape = null; /** * 预览线段 */ @@ -52,7 +69,7 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV /** * 绘画模式 */ - public paintMode: PaintMode; + private paintMode: PaintMode = PaintMode.endPaint; /** * 选择器 */ @@ -65,10 +82,18 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV * 绘制点集合 */ public paintPoints: PIXI.Point[] = []; + /** + * 绘制中的管线 + */ + public paintingPipeline: Pipeline = null; /** * 绘制中的多点图标 */ public paintingIcon: MultipointIcon; + /** + * 绘制中的图形 + */ + public paintingShape: AxShape = null; /** * 绘制中的连线 */ @@ -81,8 +106,10 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV * 拷贝素材数据 */ public copyData: any[] = []; - - private enterPaintEndButton = PIXI.Sprite.from('assets/images/enterPaintButton.jpg'); + /** + * 确认绘制按钮 + */ + private enterPaintEndButton = PIXI.Sprite.from('assets/images/enterPaintButton.png'); /** * 框选工具图形 */ @@ -95,9 +122,26 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV * 最终鼠标位置 */ private finalScreenMousePos: PIXI.Point = new PIXI.Point(); - // 根据ID 找到图标 - - // 根据ID 找到数据 + /** + * 允许编辑 + */ + public allowEdit = true; + /** + * 动画控制器 + */ + public animator; + public animation; + public animationIcon; + public animationTime; + // 车辆作业面 + public carAreas: PolygonIcon[]; + // 车辆数据 + public carData: Map = new Map(); + // 当前选择的车辆id + public selectCar: any = null; + /** + * 数据初始化 + */ ngOnInit(): void { this.eventManager.addGlobalEventListener('window', 'keydown', (event: any) => { if (event.keyCode === 17) { @@ -110,38 +154,79 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.rectToolGraphics.visible = false; this.rectToolGraphics.clear(); } + // 按Del键删除选中的图标 if (event.keyCode === 46) { this.selection.objects.forEach(item => { - // 删除 选中的数据 - if (item.assetData.IsFromBuilding) { - // console.log(this.canvasData.originaleveryStoreyData.data[item.assetData.Id]); - delete this.canvasData.originalcompanyBuildingData.data[item.assetData.Id]; - // console.log(this.canvasData.originalcompanyBuildingData.data[item.assetData.Id]); - } else { - // console.log(this.canvasData.originaleveryStoreyData.data[item.assetData.Id]); - delete this.canvasData.originaleveryStoreyData.data[item.assetData.Id]; - // console.log(this.canvasData.originaleveryStoreyData.data[item.assetData.Id]); + if (this.allowEdit + && this.canvasData.gameMode === item.assetData.GameMode) { + console.log(this.canvasData.gameMode); + switch (this.canvasData.gameMode) { + case 0: + delete this.canvasData.originaleveryStoreyData.data[item.assetData.Id]; + this.backgroundImage.removeChild(this.backgroundImage.getChildByName(item.assetData.Id)); + this.canvasData.isChange = true; + break; + case 1: + delete this.canvasData.selectPanelPoint.Data.DefinedIncrement[item.assetData.Id]; + delete this.canvasData.selectPanelPoint.Data.Increment[item.assetData.Id]; + delete this.canvasData.selectPanelPoint.Data.Stock[item.assetData.Id]; + this.backgroundImage.removeChild(this.backgroundImage.getChildByName(item.assetData.Id)); + this.canvasData.isChange = true; + break; } - // console.log(this.backgroundImage.getChildByName(item.assetData.Id)); - // 删除选中的图标 - this.backgroundImage.removeChild(this.backgroundImage.getChildByName(item.assetData.Id)); - // 标记 - this.canvasData.isChange = true; + } }); this.emit('deleteIcon'); } }); - // 测试代码 + // 打印当前工作区信息 this.eventManager.addGlobalEventListener('window', 'keypress', (event: any) => { + // console.log(event.keyCode); + if (event.keyCode === 32) { + switch (this.paintMode) { + case 0: + console.log(`当前的绘制模式是:单点图标`); + break; + case 1: + console.log(`当前的绘制模式是:线段图标`); + break; + case 2: + console.log(`当前的绘制模式是:自定义多边形`); + break; + case 3: + console.log(`当前的绘制模式是:水带多边形`); + break; + case 4: + console.log(`当前的绘制模式是:暂无`); + break; + case 5: + console.log(`当前的绘制模式是:暂无`); + break; + case 6: + console.log(`当前的绘制模式是:结束绘制`); + break; + default: + break; + } + console.log('当前楼层的数据:'); + console.log(this.canvasData.originaleveryStoreyData.data); + console.log('绘制中的管线:'); + console.log(this.paintingPipeline); + + console.log('处置预案数据:'); + console.log(this.canvasData.selectPanelPoint.Data); + } }); - setTimeout(() => { - this.createCanvas(); - }, 0); } - + /** + * 页面初始化 + */ ngAfterViewInit(): void { - + this.createCanvas(); + window.onresize = () => { + this.resetCanvas(); + }; } /** * @@ -183,6 +268,31 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV } this.emit('backgroundScale', this.backgroundImage.scale.x); } + /** + * + * @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); + } + } /** * 创建画布 */ @@ -196,40 +306,20 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV backgroundColor: 0xE9FAFF }); this.content.nativeElement.appendChild(this.app.view); - - this.createBackgroundImage(); - // this.createPreviewSinglePointIcon(); - // this.createPreviewLineSegment(); - // this.createCircleShadow(); - // this.createEnterPaintEndButton(); - // this.backgroundImage.addChild(this.paintingLine); - this.on('select', obj => { - if (obj instanceof MultipointIcon) { - obj.setPointVisiable(true); - } else if (obj instanceof PolygonIcon) { - obj.setPointVisiable(true); - } else { - obj.filters = [this.outlineFilterGreen]; - } - }); - this.on('deselect', obj => { - if (obj instanceof MultipointIcon) { - obj.setPointVisiable(false); - } else if (obj instanceof PolygonIcon) { - obj.setPointVisiable(false); - } else { - obj.filters = []; - } - }); + this.animator = new Charm(PIXI); this.app.ticker.add((delta) => { + this.animator.update(); this.mousePosition = this.app.renderer.plugins.interaction.mouse.global; + // 预览图片 + if (this.previewImage !== null) { + this.previewImage.position = this.backgroundImage.toLocal(this.mousePosition); + } - this.previewSinglePointIcon.position = this.backgroundImage.toLocal(this.mousePosition); - this.circleShadow.position = this.backgroundImage.toLocal(this.mousePosition); - - this.refreshPreviewLineSegment(this.currentClickPoint.position, this.circleShadow.position); - + if (this.backgroundImage !== null) { + this.circleShadow.position = this.backgroundImage.toLocal(this.mousePosition); + this.refreshPreviewLineSegment(this.currentClickPoint.position, this.circleShadow.position); + } if (this.rectToolGraphics.visible === true) { const init = this.initialScreenMousePos; @@ -238,20 +328,123 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.rectToolGraphics.clear(); this.rectToolGraphics.lineStyle(2, 0x00ff00, 1); this.rectToolGraphics.beginFill(0xccccf2, 0.25); - this.rectToolGraphics.drawRect(init.x, init.y, final.x - init.x, final.y - init.y); + 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.rectToolGraphics.closePath(); + // this.rectToolGraphics.closePath(); + } + // if (this.paintingArrows !== null) { + // this.paintingArrows.assetData.pointB = new PIXI.Point(this.circleShadow.position.x, this.circleShadow.position.y); + // this.paintingArrows.refresh(); + // } + }); + /** + * 选中事件 + */ + this.on('select', obj => { + // this.moveIconToScreenCenter(obj); + if (this.allowEdit) { + if (obj instanceof MultipointIcon) { + if (obj.assetData.GameMode === this.canvasData.gameMode) { + obj.setPointVisiable(true); + } else { + obj.filters = [this.outlineFilterGreen]; + } + } else if (obj instanceof PolygonIcon) { + if (obj.assetData.GameMode === this.canvasData.gameMode) { + obj.setPointVisiable(true); + } else { + obj.filters = [this.outlineFilterGreen]; + } + } else { + obj.filters = [this.outlineFilterGreen]; + } + } else { + obj.filters = [this.outlineFilterGreen]; + } + }); + /** + * 取消选中事件 + */ + this.on('deselect', obj => { + if (this.allowEdit) { + if (obj instanceof MultipointIcon) { + obj.setPointVisiable(false); + } else if (obj instanceof PolygonIcon) { + obj.filters = []; + obj.setPointVisiable(false); + } else { + obj.filters = []; + } + } else { + obj.filters = []; + } + }); + this.on('backgroundScale', scale => { + this.backgroundImage.children.forEach(item => { + if (item instanceof AxImageShape) { + if (item.assetData.FixedSize) { + const data = 1 / scale; + item.scale.set(data); + } else { + const data = 1 / scale; + item.text.scale.set(data); + } + } else if (item instanceof MultipointIcon) { + const data = 1 / scale; + item.text.scale.set(data); + } else if (item instanceof PolygonIcon) { + const data = 1 / scale; + item.text.scale.set(data); + item.pointsGraphics.forEach(point => { + point.scale.set(data); + }); + } else if (item instanceof AxPreviewImageShape) { + const data = 1 / scale; + item.scale.set(data); + } + }); + + }); + this.on('createIcon', obj => { + if (obj.assetData.GameMode === GameMode.BasicInformation) { + // if (obj.assetData.IsFromBuilding) { + // this.canvasData.originalcompanyBuildingData.data[obj.assetData.Id] = obj.assetData; + // } else { + this.canvasData.originaleveryStoreyData.data[obj.assetData.Id] = obj.assetData; + // } + } else { + // console.log(); + if (this.canvasData.selectPanelPoint.Data === undefined + || this.canvasData.selectPanelPoint.Data === null) { + this.canvasData.selectPanelPoint.Data = new FloorNodeData(); + } + this.canvasData.selectPanelPoint.Data.Stock[obj.assetData.Id] = obj.assetData; } + this.canvasData.isChange = true; }); } + /** + * 重置画布 + */ + public resetCanvas() { + this.app.renderer.resize(this.content.nativeElement.clientWidth, this.content.nativeElement.clientHeight); + } /** * 设置名称显示 * @param value true 显示 false 隐藏 * @param mode BasicInformation = 0 基本信息 Assignment想定作业 = 1 想定作业 */ public setNameVisible(value: boolean, mode: GameMode): void { - this.backgroundImage.children.forEach(item => { - if (item instanceof SinglePointIcon) { + this.backgroundImage?.children.forEach(item => { + if (item instanceof AxImageShape) { item.setNameVisible(value, mode); } else if (item instanceof MultipointIcon) { item.setNameVisible(value, mode); @@ -266,7 +459,7 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV */ public refreshIcon(id: string): void { const icon = this.backgroundImage.children.find(item => item.name === id); - if (icon instanceof SinglePointIcon) { + if (icon instanceof AxImageShape) { icon.refresh(); } else if (icon instanceof MultipointIcon) { icon.refresh(); @@ -280,8 +473,9 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV */ public setIconScale(value: number): void { this.backgroundImage.children.forEach(item => { - if (item instanceof SinglePointIcon) { - item.scale.set(value); + if (item instanceof AxImageShape) { + console.log(item.image.scale); + item.image.scale.set(value); } else if (item instanceof MultipointIcon) { } else if (item instanceof PolygonIcon) { @@ -295,52 +489,75 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV public setHighlight(ids: string[]): void { this.selection.deselectAll(); ids.forEach(item => { - const obj = this.backgroundImage.getChildByName(item); + let obj = this.backgroundImage.getChildByName(item); + if (obj === null) { + obj = this.app.stage.getChildByName(item); + } this.selection.select(obj); }); } /** - * 刷新 + * 刷新工作区 */ - public refresh(): void { + public async refresh() { + this.setPaintMode(PaintMode.endPaint); + this.resetCanvas(); this.destroyBackgroundImage(); - this.createBackgroundImage(); - this.refreshBackgroundImage(); - this.versionChecking(); - - + await this.createBackgroundImage(this.canvasData.selectStorey.imageUrl); + this.createFloorShape(); + if (this.canvasData.gameMode === GameMode.Assignment) { + this.createWorkNode(); + } + this.emit('backgroundScale', this.backgroundImage.scale.x); + } + /** + * 创建楼层图形 + */ + private createFloorShape() { const floorData = this.canvasData.originaleveryStoreyData.data; - const buildingData = this.canvasData.originalcompanyBuildingData.data; - const floor = this.canvasData.selectStorey; - // // key=>属性名 data[key]=>属性值 Object.keys(floorData).forEach((key) => { switch (floorData[key].InteractiveMode) { - case 0: - const singleIcon = new SinglePointIcon(floorData[key], this); + case 0: + const singleIcon = new AxImageShape(floorData[key], this); + singleIcon.moveable = this.allowEdit && this.canvasData.gameMode === singleIcon.assetData.GameMode; break; - case 1: + case 1: const icon = new MultipointIcon(floorData[key], this); break; - case 2: + case 2: const polygonIcon = new PolygonIcon(floorData[key], this); break; + case 3: + const pipeline = new Pipeline(floorData[key], this); + break; } }); - Object.keys(buildingData).forEach((key) => { - if (buildingData[key].FloorId === floor.id) { - switch (buildingData[key].InteractiveMode) { - case 0: - const singleIcon = new SinglePointIcon(buildingData[key], this); - break; - case 1: - const icon = new MultipointIcon(buildingData[key], this); - break; - case 2: - const polygonIcon = new PolygonIcon(buildingData[key], this); - break; + } + + private createWorkNode() { + // 加载处置节点数据 + const nodeData = this.canvasData.selectPanelPoint.Data; + if (nodeData !== undefined && nodeData !== null) { + Object.keys(nodeData).forEach((key) => { + Object.keys(nodeData[key]).forEach((tempKey) => { + 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; + break; + case 1: + const icon = new MultipointIcon(nodeData[key][tempKey], this); + break; + case 2: + const polygonIcon = new PolygonIcon(nodeData[key][tempKey], this); + break; + case 3: + const pipeline = new Pipeline(nodeData[key][tempKey], this); + break; } - } - }); + }); + }); + } } /** * @@ -352,31 +569,44 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.backgroundImage.getChildByName(item).visible = b; }); } - /** - * 版本检查 - */ - public versionChecking(): void { - const floorData = this.canvasData.originaleveryStoreyData; - const buildingData = this.canvasData.originalcompanyBuildingData; - if (floorData.version && floorData.version === '1.0') { - floorData.version = '2.0'; - Object.keys(floorData.data).forEach(item => { - floorData.data[item].Point.y *= -1; - floorData.data[item].MultiPoint?.forEach(element => { - element.y *= -1; - }); - }); - } - if (buildingData.version && buildingData.version === '1.0') { - buildingData.version = '2.0'; - Object.keys(buildingData.data).forEach(item => { - buildingData.data[item].Point.y *= -1; - buildingData.data[item].MultiPoint?.forEach(element => { - element.y *= -1; - }); - }); - } - } + // /** + // * 版本检查 + // */ + // public versionChecking(): void { + // const floorData = this.canvasData.originaleveryStoreyData; + // const buildingData = this.canvasData.originalcompanyBuildingData; + // const nodeData = this.canvasData.selectPanelPoint; + // if (floorData.version && floorData.version === '1.0') { + // floorData.version = '2.0'; + // Object.keys(floorData.data).forEach(item => { + // floorData.data[item].Point.y *= -1; + // floorData.data[item].MultiPoint?.forEach(element => { + // element.y *= -1; + // }); + // }); + // } + // if (buildingData.version && buildingData.version === '1.0') { + // buildingData.version = '2.0'; + // Object.keys(buildingData.data).forEach(item => { + // buildingData.data[item].Point.y *= -1; + // buildingData.data[item].MultiPoint?.forEach(element => { + // element.y *= -1; + // }); + // }); + // } + // if (nodeData.Version && nodeData.Version === '1.0') { + // nodeData.Version = '2.0'; + // console.log(this.canvasData.selectPanelPoint.Version); + // Object.keys(nodeData.Data).forEach((key) => { + // Object.keys(nodeData.Data[key]).forEach((tempKey) => { + // nodeData.Data[key][tempKey].Point.y *= -1; + // nodeData.Data[key][tempKey].MultiPoint?.forEach(element => { + // element.y *= -1; + // }); + // }); + // }); + // } + // } /** * 创建确认绘制结束按钮 */ @@ -390,31 +620,38 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.enterPaintEndButton .on('mousedown', event => { event.stopPropagation(); - this.emit('createIcon'); - this.enterPaintEndButton.visible = false; this.enterPaint(); }); this.backgroundImage.addChild(this.enterPaintEndButton); this.enterPaintEndButton.zIndex = this.backgroundImage.children.length; this.enterPaintEndButton.visible = false; } - // /** - // * 刷新确认按钮 - // */ - // private refreshEnterPaintEndButton() { - // this.enterPaintEndButton.position = this.circleShadow.position; - // this.enterPaintEndButton.visible = true; - // } /** * 创建背景图 */ - private createBackgroundImage(): void { - this.backgroundImage = PIXI.Sprite.from('assets/images/noImg.png'); + private async createBackgroundImage(imageUrl: string): Promise { + const image = await PIXI.Texture.fromURL(imageUrl); + this.backgroundImage = new PIXI.Sprite(image); this.backgroundImage.anchor.set(0.5); this.backgroundImage.x = this.app.view.width / 2; this.backgroundImage.y = this.app.view.height / 2; this.backgroundImage.interactive = true; this.backgroundImage.name = 'background'; + + // const left = this.init.element.nativeElement.querySelector('.functionalDomainLeft').clientWidth; + // const right = this.init.element.nativeElement.querySelector('.functionalDomainRight').clientWidth; + 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.sortableChildren = true; this.backgroundImage .on('mousedown', event => { @@ -435,8 +672,11 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV const property = new PropertyInfo(element); list.push(property); }); - const assetData = { + + const assetData = { TemplateId: this.canvasData.selectTemplateData.id, + CanConnect: this.canvasData.selectTemplateData.canConnect, + Pipelines: new Array(), FloorId: this.canvasData.selectStorey.id, Angle: this.canvasData.selectTemplateData.angle, Color: this.canvasData.selectTemplateData.color, @@ -450,24 +690,18 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV ImageUrl: this.canvasData.selectTemplateData.imageUrl, InteractiveMode: this.canvasData.selectTemplateData.interactiveMode, MultiPoint : null, - Point: new PIXI.Point(this.previewSinglePointIcon.x, this.previewSinglePointIcon.y), + Point: new PIXI.Point(this.previewImage.x, this.previewImage.y), Name : this.canvasData.selectTemplateData.name, PropertyInfos: list, Border : this.canvasData.selectTemplateData.border, DrawMode : this.canvasData.selectTemplateData.drawMode, Thickness : this.canvasData.selectTemplateData.thickness, IsFromBuilding : this.canvasData.selectTemplateData.isFromBuilding, - GameMode : GameMode.BasicInformation + GameMode : this.canvasData.gameMode }; - const singleIcon = new SinglePointIcon(assetData, this); - if (singleIcon.assetData.IsFromBuilding) { - this.canvasData.originalcompanyBuildingData.data[singleIcon.assetData.Id] = singleIcon.assetData; - } else { - this.canvasData.originaleveryStoreyData.data[singleIcon.assetData.Id] = singleIcon.assetData; - } - this.emit('createIcon'); + const singleIcon = new AxImageShape(assetData, this); + this.emit('createIcon', singleIcon); this.emit('backgroundScale', this.backgroundImage.scale.x); - this.canvasData.isChange = true; break; case PaintMode.lineIcon: this.previewLineSegment.visible = true; @@ -510,7 +744,7 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV DrawMode: this.canvasData.selectTemplateData.drawMode, Thickness: this.canvasData.selectTemplateData.thickness, IsFromBuilding: this.canvasData.selectTemplateData.isFromBuilding, - GameMode: GameMode.BasicInformation + GameMode: this.canvasData.gameMode }; // const assetData1 = { // ImageUrl: this.canvasData.selectTemplateData.imageUrl, @@ -549,6 +783,62 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV // } // this.paintingIcon = new PolygonIcon(this.paintPoints, this); break; + case PaintMode.Pipeline: + if (this.canvasData.selectTemplateData.name === '水带') { + if (this.paintingPipeline !== null) { + this.currentClickPoint.position = new PIXI.Point(this.circleShadow.x, this.circleShadow.y); + this.paintPoints.push(new PIXI.Point(this.circleShadow.x, this.circleShadow.y)); + this.paintingPipeline.assetData.MultiPoint = JSON.parse(JSON.stringify(this.paintPoints)); + this.paintingPipeline.refresh(); + } + } else { + this.previewLineSegment.visible = true; + this.currentClickPoint.position = new PIXI.Point(this.circleShadow.x, this.circleShadow.y); + this.paintPoints.push(new PIXI.Point(this.circleShadow.x, this.circleShadow.y)); + + if (this.paintPoints.length >= 2) { + this.enterPaintEndButton.position = this.circleShadow.position; + this.enterPaintEndButton.visible = true; + } + if (this.paintingShape === null) { + const jsonObject = JSON.parse(JSON.stringify(this.canvasData.selectTemplateData.propertyInfos)); + const propertyList = []; + jsonObject.forEach(element => { + const property = new PropertyInfo(element); + propertyList.push(property); + }); + const assetData2 = { + TemplateId: this.canvasData.selectTemplateData.id, + FloorId: this.canvasData.selectStorey.id, + Angle: this.canvasData.selectTemplateData.angle, + Color: this.canvasData.selectTemplateData.color, + Enabled: this.canvasData.selectTemplateData.enabled, + FillMode: this.canvasData.selectTemplateData.fillMode, + FireElementId: this.canvasData.selectTemplateData.fireElementId, + FixedSize: this.canvasData.selectTemplateData.fixedSize, + Height: 32, + Width: 32, + Id: ObjectID.default.generate(), + ImageUrl: this.canvasData.selectTemplateData.imageUrl, + InteractiveMode: this.canvasData.selectTemplateData.interactiveMode, + MultiPoint: JSON.parse(JSON.stringify(this.paintPoints)), + Point: new PIXI.Point(0, 0), + Name: this.canvasData.selectTemplateData.name, + PropertyInfos: propertyList, + Border: this.canvasData.selectTemplateData.border, + DrawMode: this.canvasData.selectTemplateData.drawMode, + Thickness: this.canvasData.selectTemplateData.thickness, + IsFromBuilding: this.canvasData.selectTemplateData.isFromBuilding, + GameMode: this.canvasData.gameMode + }; + this.paintingShape = new AxArrowConnector(assetData2, this); + } else { + this.paintingShape.assetData.MultiPoint = JSON.parse(JSON.stringify(this.paintPoints)); + this.paintingShape.redraw(); + } + } + this.emit('backgroundScale', this.backgroundImage.scale.x); + break; } } else if (!event.currentTarget.dragging && this.selection.isMultiselection === true) { this.rectToolGraphics.visible = true; @@ -564,10 +854,11 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV } if (this.rectToolGraphics.visible === true) { this.backgroundImage.children.forEach(item => { - if (item instanceof SinglePointIcon + if (item instanceof AxImageShape || item instanceof MultipointIcon || item instanceof PolygonIcon) { if (this.rectToolGraphics.getLocalBounds().contains(item.x, item.y)) { + console.log(item); this.selection.select(item); } } @@ -592,18 +883,31 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.finalScreenMousePos = this.backgroundImage.toLocal(this.mousePosition); } } - }).on('rightclick', event => { + }) + .on('rightclick', event => { event.stopPropagation(); - this.cancelPaint(); this.selection.deselectAll(); + this.setPaintMode(PaintMode.endPaint); + }) + .on('pointerover', (event) => { + if (this.previewImage !== null + && this.paintMode === PaintMode.singlePointIcon) { + this.previewImage.visible = true; + } + }) + .on('pointerout', (event) => { + if (this.previewImage !== null + && this.paintMode === PaintMode.singlePointIcon) { + this.previewImage.visible = false; + } }); this.app.stage.addChild(this.backgroundImage); - - this.createPreviewSinglePointIcon(); + this.createPreviewImage(); this.createPreviewLineSegment(); this.createCircleShadow(); this.createEnterPaintEndButton(); this.backgroundImage.addChild(this.paintingLine); + } /** @@ -623,7 +927,6 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV */ public destroyBackgroundImage(): void { this.app.stage.removeChild(this.backgroundImage); - this.removeAllListeners('backgroundScale'); } /** * 设置背景图缩放 @@ -643,22 +946,17 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV /** * 创建预览单点图标 */ - private createPreviewSinglePointIcon(): void { - this.previewSinglePointIcon = PIXI.Sprite.from('assets/images/noImg.png'); - this.previewSinglePointIcon.width = 32; - this.previewSinglePointIcon.height = 32; - this.previewSinglePointIcon.alpha = 0.5; - this.previewSinglePointIcon.anchor.set(0.5); - this.previewSinglePointIcon.visible = false; - this.backgroundImage.addChild(this.previewSinglePointIcon); - } - /** - * 改变预览单点图标 - * @param uri 图片地址 - */ - private changePreviewSinglePointIcon(uri: string): void { - this.previewSinglePointIcon.texture = PIXI.Texture.from(uri); - this.previewSinglePointIcon.visible = true; + private createPreviewImage(): void { + // if (this.previewSinglePointIcon === null) { + // this.previewSinglePointIcon = PIXI.Sprite.from(this.canvasData.selectTemplateData.imageUrl); + // this.previewSinglePointIcon.width = this.canvasData.selectTemplateData.width; + // this.previewSinglePointIcon.height = this.canvasData.selectTemplateData.height; + // this.previewSinglePointIcon.anchor.set(0.5); + // this.previewSinglePointIcon.interactive = false; + // this.backgroundImage.addChild(this.previewSinglePointIcon); + // this.previewSinglePointIcon.scale.set(1 / this.backgroundImage.scale.x); + // } + this.previewImage = new AxPreviewImageShape(this); } /** * 创建预览线段 @@ -669,6 +967,7 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.backgroundImage.addChild(this.previewLineSegment); this.backgroundImage.addChild(this.rectToolGraphics); + this.rectToolGraphics.visible = false; } /** * 刷新预览线段 @@ -677,7 +976,7 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV */ private refreshPreviewLineSegment(pointA: PIXI.Point, pointB: PIXI.Point) { this.previewLineSegment.clear(); - this.previewLineSegment.lineStyle(1, 0xffd900, 1); + this.previewLineSegment.lineStyle(5, 0x00ff00, 1); this.previewLineSegment.moveTo(pointA.x, pointA.y); this.previewLineSegment.lineTo(pointB.x, pointB.y ); } @@ -692,55 +991,45 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.circleShadow.visible = false; this.backgroundImage.addChild(this.circleShadow); } + showConnectionPoint(b: boolean) { + this.backgroundImage?.children.forEach(item => { + if (item instanceof AxImageShape) { + if (item.assetData.CanConnect) { + item.showConnectionPoint(b); + } + } + }); + } /** * 开始绘制 */ public beginPaint() { - switch (this.canvasData.selectTemplateData.interactiveMode) { - case 0: - this.beginPaintSinglePointIcon(); - break; - case 1: - this.beginPaintLineIcon(); - break; - case 2: - this.beginPaintPolygonIcon(); - break; - } + this.setPaintMode(PaintMode.endPaint); + this.setPaintMode(this.canvasData.selectTemplateData.interactiveMode); } /** - * 开始绘画单点图标 + * 初始化管线数据 */ - private beginPaintSinglePointIcon(): void { - this.cancelPaint(); - this.paintMode = PaintMode.singlePointIcon; - this.changePreviewSinglePointIcon(this.canvasData.selectTemplateData.imageUrl); + public initPipelineData(): void { + this.paintPoints = []; + this.paintingPipeline = null; } - /** - * 开始绘画多边形 - */ - private beginPaintPolygonIcon(): void { - this.cancelPaint(); - this.paintMode = PaintMode.polygonIcon; - this.circleShadow.visible = true; + public beginPaintingArrows(): void { + this.paintMode = PaintMode.Arrows; } /** - * 开始绘制多点图标 + * 设置绘制状态 + * @param mode 状态 */ - private beginPaintLineIcon(): void { - this.cancelPaint(); - this.paintMode = PaintMode.lineIcon; - this.previewSinglePointIcon.texture = PIXI.Texture.from(this.canvasData.selectTemplateData.imageUrl); - this.circleShadow.visible = true; - } - /** - * 取消绘画 - */ - public cancelPaint(): void { + public setPaintMode(mode: PaintMode) { + if (this.paintMode === mode) { + return; + } + this.paintMode = mode; switch (this.paintMode) { case PaintMode.singlePointIcon: - this.previewSinglePointIcon.visible = false; - this.paintMode = PaintMode.endPaint; + this.previewImage.visible = true; + this.previewImage.setImageUrl(this.canvasData.selectTemplateData.imageUrl); break; case PaintMode.lineIcon: this.circleShadow.visible = false; @@ -749,7 +1038,8 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV if (this.paintingIcon !== null) { this.backgroundImage.removeChild(this.paintingIcon); } - this.paintMode = PaintMode.endPaint; + this.previewImage.setImageUrl(this.canvasData.selectTemplateData.imageUrl); + this.circleShadow.visible = true; break; case PaintMode.polygonIcon: this.circleShadow.visible = false; @@ -757,25 +1047,62 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV this.paintingIcon = null; this.paintPoints.splice(0, this.paintPoints.length); this.paintingLine.clear(); - this.paintMode = PaintMode.endPaint; + this.circleShadow.visible = true; + break; + case PaintMode.Pipeline: + if (this.canvasData.selectTemplateData.name==='水带') { + this.showConnectionPoint(true); + } else { + + } + break; + case PaintMode.endPaint: + this.showConnectionPoint(false); + if (this.previewImage !== null) { + this.previewImage.visible = false; + } + // 重置组件状态 + if ( this.paintingIcon !== undefined + && this.paintingIcon !== null) { + this.backgroundImage.removeChild(this.paintingIcon); + } + + if (this.paintingPipeline !== undefined + && this.paintingPipeline !== null) { + this.backgroundImage.removeChild(this.paintingPipeline); + } + this.paintingLine.clear(); + this.resetData(); + break; + default: break; } } + /** + * 获取绘制状态 + */ + public getPaintMode(): PaintMode { + return this.paintMode; + } + /** + * 重置 + */ + public resetData() { + this.initPipelineData(); + // + this.circleShadow.visible = false; + this.previewLineSegment.visible = false; + } /** * 确认绘制 */ private enterPaint(): void { this.previewLineSegment.visible = false; + this.enterPaintEndButton.visible = false; switch (this.paintMode) { case PaintMode.lineIcon: if (this.paintPoints.length >= 2) { - - if (this.paintingIcon.assetData.IsFromBuilding) { - this.canvasData.originalcompanyBuildingData.data[this.paintingIcon.assetData.Id] = this.paintingIcon.assetData; - } else { - this.canvasData.originaleveryStoreyData.data[this.paintingIcon.assetData.Id] = this.paintingIcon.assetData; - } - this.canvasData.isChange = true; + this.emit('createIcon', this.paintingIcon); this.paintingIcon = null; } break; @@ -810,16 +1137,13 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV DrawMode: this.canvasData.selectTemplateData.drawMode, Thickness: this.canvasData.selectTemplateData.thickness, IsFromBuilding: this.canvasData.selectTemplateData.isFromBuilding, - GameMode: GameMode.BasicInformation + GameMode: this.canvasData.gameMode }; const polygonIcon = new PolygonIcon(assetData, this); - if (polygonIcon.assetData.IsFromBuilding) { - this.canvasData.originalcompanyBuildingData.data[polygonIcon.assetData.Id] = polygonIcon.assetData; - } else { - this.canvasData.originaleveryStoreyData.data[polygonIcon.assetData.Id] = polygonIcon.assetData; - } - this.canvasData.isChange = true; + this.emit('createIcon', polygonIcon); } + case PaintMode.Pipeline: + this.paintingShape = null; break; } this.paintPoints.splice(0, this.paintPoints.length); @@ -847,14 +1171,14 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV newData.BuildingId = buildingId; newData.FloorId = floorId; newData.Point = new PIXI.Point(item.Point.x + 5, item.Point.y + 5); - if (newData.IsFromBuilding) { - this.canvasData.originalcompanyBuildingData.data[newData.Id] = newData; - } else { - this.canvasData.originaleveryStoreyData.data[newData.Id] = newData; - } + // if (newData.IsFromBuilding) { + // this.canvasData.originalcompanyBuildingData.data[newData.Id] = newData; + // } else { + this.canvasData.originaleveryStoreyData.data[newData.Id] = newData; + // } switch (item.InteractiveMode) { case PaintMode.singlePointIcon: - const singleIcon = new SinglePointIcon(newData, this); + const singleIcon = new AxImageShape(newData, this); break; case PaintMode.lineIcon: const lineIcon = new MultipointIcon(newData, this); @@ -868,596 +1192,7 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV } } -/** - * 绘制模式 - */ -enum PaintMode { - singlePointIcon, - lineIcon, - polygonIcon, - beginPainting, - endPaint, -} -/** - * 单点图标 - */ -export class SinglePointIcon extends PIXI.Container { - style = new PIXI.TextStyle({ - fontFamily: 'Arial', - fontSize: 18, - fontStyle: 'normal', - fontWeight: 'bold', - fill: ['#000000'], - stroke: '#ffffff', - strokeThickness: 3, - dropShadow: true, - dropShadowColor: '#000000', - dropShadowBlur: 3, - dropShadowAngle: Math.PI / 6, - dropShadowDistance: 1, - wordWrap: false, - wordWrapWidth: 100, - }); - - private text = new PIXI.Text(this.assetData.Name - + '\r\n' - + this.assetData.PropertyInfos.find(item => item.PropertyName === '名称/编号')?.PropertyValue, this.style); - private image = PIXI.Sprite.from(this.assetData.ImageUrl); - constructor(public assetData: any, private workingArea: WorkingAreaComponent) { - super(); - this.workingArea.backgroundImage.addChild(this); - this.x = this.assetData.Point.x; - this.y = this.assetData.Point.y; - this.name = this.assetData.Id; - - this.image.angle = this.assetData.Angle; - - this.image.x = 0; - this.image.y = 0; - this.image.width = this.assetData.Width; - this.image.height = this.assetData.Height; - - this.image.alpha = 1; - this.image.anchor.set(0.5); - this.image.interactive = true; - this.image - .on('mousedown', event => { - event.stopPropagation(); - console.log(this.assetData); - event.currentTarget.parent.data = event.data; - event.currentTarget.parent.alpha = 0.5; - event.currentTarget.parent.dragging = true; - this.workingArea.selection.selectOne(this); - }) - .on('mouseup', event => { - event.currentTarget.parent.alpha = 1; - event.currentTarget.parent.dragging = false; - event.currentTarget.parent.data = null; - }) - .on('mouseupoutside', event => { - event.currentTarget.parent.alpha = 1; - event.currentTarget.parent.dragging = false; - event.currentTarget.parent.data = null; - }) - .on('mousemove', event => { - if (event.currentTarget.parent.dragging) { - const newPosition = event.currentTarget.parent.data.getLocalPosition(event.currentTarget.parent.parent); - event.currentTarget.parent.x = newPosition.x; - event.currentTarget.parent.y = newPosition.y; - - // this.text.x = newPosition.x; - // this.text.y = newPosition.y - 32; - - this.assetData.Point = new PIXI.Point(this.x, this.y); - this.workingArea.canvasData.isChange = true; - // console.log(this.workingArea.canvasData.originaleveryStoreyData); - // console.log(this.workingArea.canvasData.originaleveryStoreyData.data[this.assetData.Id].Point); - } - }) - .on('rightclick', event => { - - }) - .on('mouseover', event => { - - }); - - this.workingArea.on('backgroundScale', data => { - if (this.assetData.FixedSize) { - const scale = 1 / data; - this.scale.set(scale); - } - }); - - this.text.x = this.image.x; - this.text.y = this.image.y - this.image.height / 2; - this.text.anchor.set(0.5, 1); - - this.addChild(this.text); - this.addChild(this.image); - } - // 设置名称 - public setNameVisible(value: boolean, mode: GameMode) { - if (this.assetData.GameMode === mode) { - this.text.visible = value; - } - } - // 刷新 - public refresh() { - this.image.width = this.assetData.Width; - this.image.height = this.assetData.Height; - this.image.angle = this.assetData.Angle; - this.text.text = this.assetData.Name - + '\r\n' - + this.assetData.PropertyInfos.find(item => item.PropertyName === '名称/编号')?.PropertyValue; - this.text.x = this.image.x; - this.text.y = this.image.y - this.image.height / 2; - } -} -/** - * 多点连线 - */ -export class MultipointIcon extends PIXI.Container { - public pointsData: PIXI.Point[]; - public pointsGraphics: PIXI.Graphics[] = []; - public iconsTilingSprite: PIXI.TilingSprite[] = []; - style = new PIXI.TextStyle({ - fontFamily: 'Arial', - fontSize: 18, - fontStyle: 'normal', - fontWeight: 'bold', - fill: ['#000000'], - stroke: '#ffffff', - strokeThickness: 3, - dropShadow: true, - dropShadowColor: '#000000', - dropShadowBlur: 3, - dropShadowAngle: Math.PI / 6, - dropShadowDistance: 1, - wordWrap: false, - wordWrapWidth: 100, - }); - - private text = new PIXI.Text(this.assetData.Name - + '\r\n' - + this.assetData.PropertyInfos.find(item => item.PropertyName === '名称/编号')?.PropertyValue, this.style); - /** - * - * @param texture 图片素材 - * @param points 点集合 - */ - constructor(public assetData: any, private workingArea: WorkingAreaComponent) { - super(); - this.name = this.assetData.Id; - this.pointsData = this.assetData.MultiPoint; - this.x = this.assetData.Point.x; - this.y = this.assetData.Point.y; - this.workingArea.backgroundImage.addChild(this); - // 画线图标 - for (let i = 0, count = this.pointsData.length - 1; i < count; i++) { - const pointA = this.pointsData[i]; - const pointB = this.pointsData[i + 1]; - - const angle = Math.atan2((pointB.y - pointA.y), (pointB.x - pointA.x)) * (180 / Math.PI); - const a = pointB.x - pointA.x; - const b = pointB.y - pointA.y; - const distance = Math.sqrt(a * a + b * b); - - const icon = new PIXI.TilingSprite(PIXI.Texture.from(this.assetData.ImageUrl), distance, 64); - icon.anchor.set(0, 0.5); - icon.x = pointA.x; - icon.y = pointA.y; - icon.angle = angle; - this.iconsTilingSprite.push(icon); - this.addChild(icon); - if (i === 0) { - this.text.anchor.set(0.5); - this.text.position = icon.position; - this.text.y -= this.assetData.Height; - this.addChild(this.text); - } - } - // 画点 - this.pointsData.forEach((item, index, array) => { - const iconPoint = new PIXI.Graphics(); - iconPoint.lineStyle(1, 0xFFBD01, 1); - iconPoint.beginFill(0xFFFFFF, 1); - iconPoint.drawCircle(0, 0, 15); - iconPoint.x = item.x; - iconPoint.y = item.y; - iconPoint.endFill(); - iconPoint.visible = false; - this.pointsGraphics.push(iconPoint); - this.addChild(iconPoint); - }); - // 添加圆点事件 - this.pointsGraphics.forEach((item, index, array) => { - item.interactive = true; - item.on('mousedown', event => { - event.stopPropagation(); - event.currentTarget.data = event.data; - event.currentTarget.alpha = 0.5; - event.currentTarget.dragging = true; - }) - .on('mouseup', event => { - event.currentTarget.alpha = 1; - event.currentTarget.dragging = false; - event.currentTarget.data = null; - }) - .on('mouseupoutside', event => { - event.currentTarget.alpha = 1; - event.currentTarget.dragging = false; - event.currentTarget.data = null; - }) - .on('mousemove', event => { - if (event.currentTarget.dragging) { - const newPosition = event.currentTarget.data.getLocalPosition(event.currentTarget.parent); - event.currentTarget.x = newPosition.x; - event.currentTarget.y = newPosition.y; - - this.assetData.MultiPoint[index].x = newPosition.x; - this.assetData.MultiPoint[index].y = newPosition.y; - this.workingArea.canvasData.isChange = true; - - if (index === 0) {// 第一个点 - this.iconsTilingSprite[index].x = newPosition.x; - this.iconsTilingSprite[index].y = newPosition.y; - - const pointA = array[index]; - const pointB = array[index + 1]; - - const angle = Math.atan2((pointB.y - pointA.y), (pointB.x - pointA.x)) * (180 / Math.PI); - const a = pointB.x - pointA.x; - const b = pointB.y - pointA.y; - const distance = Math.sqrt(a * a + b * b); - this.iconsTilingSprite[index].angle = angle; - this.iconsTilingSprite[index].width = distance; - - this.text.position = this.iconsTilingSprite[index].position; - this.text.y -= this.assetData.Height; - } else if (index < array.length - 1) {// 不是第一个点,也不是最后一个点 - this.iconsTilingSprite[index].x = newPosition.x; - this.iconsTilingSprite[index].y = newPosition.y; - - const pointA = array[index]; // 当前点 - const pointB = array[index + 1]; // 后一个点 - const pointC = array[index - 1]; // 前一个点 - - const angle = Math.atan2((pointB.y - pointA.y), (pointB.x - pointA.x)) * (180 / Math.PI); - const a = pointB.x - pointA.x; - const b = pointB.y - pointA.y; - const distance = Math.sqrt(a * a + b * b); - this.iconsTilingSprite[index].angle = angle; - this.iconsTilingSprite[index].width = distance; - - const angleC = Math.atan2((pointA.y - pointC.y), (pointA.x - pointC.x)) * (180 / Math.PI); - const aC = pointA.x - pointC.x; - const bC = pointA.y - pointC.y; - const distanceC = Math.sqrt(aC * aC + bC * bC); - this.iconsTilingSprite[index - 1].angle = angleC; - this.iconsTilingSprite[index - 1].width = distanceC; - } else if (index === array.length - 1) { // 最后一个点 - const pointA = array[index]; // 当前点 - const pointC = array[index - 1]; // 前一个点 - - const angleC = Math.atan2((pointA.y - pointC.y), (pointA.x - pointC.x)) * (180 / Math.PI); - const aC = pointA.x - pointC.x; - const bC = pointA.y - pointC.y; - const distanceC = Math.sqrt(aC * aC + bC * bC); - this.iconsTilingSprite[index - 1].angle = angleC; - this.iconsTilingSprite[index - 1].width = distanceC; - } - } - }) - .on('rightclick', event => { - }) - .on('mouseover', event => { - - }); - }); - // 缩放 - this.workingArea.on('backgroundScale', data => { - const scale = 1 / data; - this.text.scale.set(scale); - }); - // 添加选中事件 - this.iconsTilingSprite.forEach((item, index, array) => { - item.interactive = true; - item.on('mousedown', event => { - event.stopPropagation(); - event.currentTarget.parent.data = event.data; - event.currentTarget.parent.alpha = 0.5; - event.currentTarget.parent.dragging = true; - - event.currentTarget.parent.dragPoint = event.data.getLocalPosition(event.currentTarget.parent.parent); - event.currentTarget.parent.dragPoint.x -= event.currentTarget.parent.x; - event.currentTarget.parent.dragPoint.y -= event.currentTarget.parent.y; - - this.workingArea.selection.selectOne(this); - }) - .on('mouseup', event => { - event.currentTarget.parent.alpha = 1; - event.currentTarget.parent.dragging = false; - event.currentTarget.parent.data = null; - }) - .on('mouseupoutside', event => { - event.currentTarget.parent.alpha = 1; - event.currentTarget.parent.dragging = false; - event.currentTarget.parent.data = null; - }) - .on('mousemove', event => { - if (event.currentTarget.parent.dragging) { - const newPosition = event.currentTarget.parent.data.getLocalPosition(event.currentTarget.parent.parent); - event.currentTarget.parent.x = newPosition.x - event.currentTarget.parent.dragPoint.x; - event.currentTarget.parent.y = newPosition.y - event.currentTarget.parent.dragPoint.y; - - this.assetData.Point = new PIXI.Point(this.x, this.y); - this.workingArea.canvasData.isChange = true; - // console.log(this.workingArea.canvasData.originaleveryStoreyData.data[this.assetData.Id].Point); - } - }) - .on('rightclick', event => { - // this.workingArea.selection.deselectAll(); - }); - }); - } - /** - * 设置点显示状态 - * @param value 显示状态 - */ - public setPointVisiable(value: boolean) { - this.pointsGraphics.forEach((item) => { - item.visible = value; - }); - } - // 设置名称 - public setNameVisible(value: boolean, mode: GameMode) { - if (this.assetData.GameMode === mode) { - this.text.visible = value; - } - } - // - public refresh() { - this.text.text = this.assetData.Name - + '\r\n' - + this.assetData.PropertyInfos.find(item => item.PropertyName === '名称/编号')?.PropertyValue; - } -} -/** - * 多边形 - */ -export class PolygonIcon extends PIXI.Container { - public pointsData: PIXI.Point[]; - public pointsGraphics: PIXI.Graphics[] = []; - public polygonGraphics: PIXI.Graphics = new PIXI.Graphics(); - public polygonLineGraphics: PIXI.Graphics = new PIXI.Graphics(); - style = new PIXI.TextStyle({ - fontFamily: 'Arial', - fontSize: 18, - fontStyle: 'normal', - fontWeight: 'bold', - fill: ['#000000'], - stroke: '#ffffff', - strokeThickness: 3, - dropShadow: true, - dropShadowColor: '#000000', - dropShadowBlur: 3, - dropShadowAngle: Math.PI / 6, - dropShadowDistance: 1, - wordWrap: false, - wordWrapWidth: 100, - }); - - private text = new PIXI.Text(this.assetData.Name - + '\r\n' - + this.assetData.PropertyInfos.find(item => item.PropertyName === '名称/编号')?.PropertyValue, this.style); - /** - * - * @param points 点集合 - */ - constructor(public assetData: any, private workingArea: WorkingAreaComponent) { - super(); - this.name = this.assetData.Id; - this.x = this.assetData.Point.x; - this.y = this.assetData.Point.y; - this.pointsData = this.assetData.MultiPoint; - this.workingArea.backgroundImage.addChild(this); - this.sortableChildren = true; - // 画点 - this.pointsData.forEach((item, index, array) => { - const iconPoint = new PIXI.Graphics(); - iconPoint.lineStyle(1, 0xFFBD01, 1); - iconPoint.beginFill(0xFFFFFF, 1); - iconPoint.drawCircle(0, 0, 15); - iconPoint.x = item.x; - iconPoint.y = item.y; - iconPoint.endFill(); - iconPoint.visible = false; - this.pointsGraphics.push(iconPoint); - this.addChild(iconPoint); - }); - // 填充多边形 - - const color: number = this.assetData.Color.substring(0, 7).replace('#', '0x'); - const angle: number = parseInt(this.assetData.Color.substring(7), 16) / 255; - this.polygonGraphics.beginFill(color, angle); - this.polygonGraphics.drawPolygon(this.getPoints()); - this.polygonGraphics.endFill(); - this.addChild(this.polygonGraphics); - // 画多边形 - this.polygonLineGraphics.lineStyle(5, 0xFFBD01, 1); - this.polygonLineGraphics.drawPolygon(this.getPoints()); - this.polygonLineGraphics.closePath(); - this.addChild(this.polygonLineGraphics); - - this.text.anchor.set(0.5); - this.text.position = this.calculatePolygonGravityCenter(this.pointsData); - // console.log(this.calculatePolygonGravityCenter(this.pointsData)); - this.polygonGraphics.addChild(this.text); - // 添加圆点事件 - this.pointsGraphics.forEach((item, index, array) => { - item.interactive = true; - item.zIndex = 1; - item.on('mousedown', event => { - event.stopPropagation(); - event.currentTarget.data = event.data; - event.currentTarget.alpha = 0.5; - event.currentTarget.dragging = true; - - // console.log(item.zIndex); - // console.log(this.polygonLineGraphics.zIndex); - }) - .on('mouseup', event => { - event.currentTarget.alpha = 1; - event.currentTarget.dragging = false; - event.currentTarget.data = null; - }) - .on('mouseupoutside', event => { - event.currentTarget.alpha = 1; - event.currentTarget.dragging = false; - event.currentTarget.data = null; - }) - .on('mousemove', event => { - if (event.currentTarget.dragging) { - const newPosition = event.currentTarget.data.getLocalPosition(event.currentTarget.parent); - event.currentTarget.x = newPosition.x; - event.currentTarget.y = newPosition.y; - - this.assetData.MultiPoint[index].x = newPosition.x; - this.assetData.MultiPoint[index].y = newPosition.y; - this.workingArea.canvasData.isChange = true; - // 填充多边形 - this.polygonGraphics.clear(); - this.polygonGraphics.beginFill(color, angle); - this.polygonGraphics.drawPolygon(this.getPoints()); - this.polygonGraphics.endFill(); - // 画多边形 - this.polygonLineGraphics.clear(); - this.polygonLineGraphics.lineStyle(5, 0xFFBD01, 1); - this.polygonLineGraphics.drawPolygon(this.getPoints()); - this.polygonLineGraphics.closePath(); - - this.text.position = this.calculatePolygonGravityCenter(this.pointsData); - } - }) - .on('rightclick', event => { - }); - }); - // 添加选中事件 - this.polygonGraphics.interactive = true; - this.polygonGraphics.on('mousedown', event => { - event.stopPropagation(); - event.currentTarget.parent.data = event.data; - event.currentTarget.parent.alpha = 0.5; - event.currentTarget.parent.dragging = true; - - event.currentTarget.parent.dragPoint = event.data.getLocalPosition(event.currentTarget.parent.parent); - event.currentTarget.parent.dragPoint.x -= event.currentTarget.parent.x; - event.currentTarget.parent.dragPoint.y -= event.currentTarget.parent.y; - - this.workingArea.selection.selectOne(this); - }) - .on('mouseup', event => { - event.currentTarget.parent.alpha = 1; - event.currentTarget.parent.dragging = false; - event.currentTarget.parent.data = null; - }) - .on('mouseupoutside', event => { - event.currentTarget.parent.alpha = 1; - event.currentTarget.parent.dragging = false; - event.currentTarget.parent.data = null; - }) - .on('mousemove', event => { - if (event.currentTarget.parent.dragging) { - const newPosition = event.currentTarget.parent.data.getLocalPosition(event.currentTarget.parent.parent); - event.currentTarget.parent.x = newPosition.x - event.currentTarget.parent.dragPoint.x; - event.currentTarget.parent.y = newPosition.y - event.currentTarget.parent.dragPoint.y; - - this.assetData.Point = new PIXI.Point(this.x, this.y); - this.workingArea.canvasData.isChange = true; - } - }) - .on('rightclick', event => { - // this.workingArea.selection.deselectAll(); - }); - // 缩放 - this.workingArea.on('backgroundScale', data => { - const scale = 1 / data; - this.text.scale.set(scale); - }); - } - /** - * 设置点显示状态 - * @param value 显示状态 - */ - public setPointVisiable(value: boolean) { - this.pointsGraphics.forEach((item) => { - item.visible = value; - }); - } - - public calculatePolygonGravityCenter(points: PIXI.Point[]) { - let area = 0.0; // 多边形面积 - let gravityLat = 0.0; // 重心点 latitude - let gravityLng = 0.0; // 重心点 longitude - points.forEach((item, index) => { - // 1 - const lat = item.x; - const lng = item.y; - const nextLat = points[(index + 1) % points.length].x; - const nextLng = points[(index + 1) % points.length].y; - // 2 - const tempArea = (nextLat * lng - nextLng * lat) / 2.0; - // 3 - area += tempArea; - // 4 - gravityLat += tempArea * (lat + nextLat) / 3; - gravityLng += tempArea * (lng + nextLng) / 3; - }); - // 5 - gravityLat = gravityLat / area; - gravityLng = gravityLng / area; - - return new PIXI.Point(gravityLat, gravityLng); -} - /** - * 获取点集合 - */ - public getPoints(): PIXI.Point[] { - const points: PIXI.Point[] = []; - this.pointsGraphics.forEach(item => { - points.push(item.position); - }); - return points; - } - /** - * 设置名称显示 - * @param value true/false 显示/隐藏 - * @param mode BasicInformation = 0 基本信息 - * Assignment想定作业 = 1 想定作业 - */ - public setNameVisible(value: boolean, mode: GameMode) { - if (this.assetData.GameMode === mode) { - this.text.visible = value; - } - } - public refresh() { - this.text.text = this.assetData.Name - + '\r\n' - + this.assetData.PropertyInfos.find(item => item.PropertyName === '名称/编号')?.PropertyValue; - // 填充多边形 - const color: number = this.assetData.Color.substring(0, 7).replace('#', '0x'); - const angle: number = parseInt(this.assetData.Color.substring(7), 16) / 255; - this.polygonGraphics.clear(); - this.polygonGraphics.beginFill(color, angle); - this.polygonGraphics.drawPolygon(this.getPoints()); - this.polygonGraphics.endFill(); - } -} - -export class Line extends PIXI.Container { - -} /** * 选择器 */ @@ -1535,72 +1270,15 @@ export class Selection { }); } } + + /** - * 游戏状态 - */ -enum GameMode { - BasicInformation, - Assignment -} -/** - * 属性 + * 车辆类型 */ -export class PropertyInfo { - constructor(instanceData: any) { - this.Tag = instanceData.tag; - this.Order = instanceData.order; - this.Enabled = instanceData.enabled; - this.Visible = instanceData.visible; - this.Required = instanceData.required; - this.RuleName = instanceData.ruleName; - this.RuleValue = instanceData.ruleValue; - this.PhysicalUnit = instanceData.physicalUnit; - this.PropertyName = instanceData.propertyName; - this.PropertyType = instanceData.propertyType; - this.PropertyValue = instanceData.propertyValue; - } - /** - * 标记位,用于扩展 - */ - public Tag: string; - /** - * 属性排序 - */ - public Order: number; - /** - * 是否启用 - */ - public Enabled: boolean; - /** - * 是否可见 - */ - public Visible: boolean; - /** - * 必填 - */ - public Required: boolean; - /** - * 验证规则名称 - */ - public RuleName: string; - /** - * 验证规则值 - */ - public RuleValue: string; - /** - * 物理单位 - */ - public PhysicalUnit: string; - /** - * 属性名称 - */ - public PropertyName: string; - /** - * 属性类型 - */ - public PropertyType: number; - /** - * 属性值 - */ - public PropertyValue: string; +export enum Type { + 水源 = 0, + 举高喷射消防车 = 1, + 泡沫消防车 = 2, + 水罐消防车 = 3, + 压缩空气泡沫消防车 = 4 } diff --git a/src/assets/images/enterPaintButton.jpg b/src/assets/images/enterPaintButton.jpg deleted file mode 100644 index 44b5faf..0000000 Binary files a/src/assets/images/enterPaintButton.jpg and /dev/null differ diff --git a/src/assets/images/enterPaintButton.png b/src/assets/images/enterPaintButton.png new file mode 100644 index 0000000..e6b99b7 Binary files /dev/null and b/src/assets/images/enterPaintButton.png differ diff --git a/src/assets/images/testBackground.jpg b/src/assets/images/testBackground.jpg new file mode 100644 index 0000000..c65e9c5 Binary files /dev/null and b/src/assets/images/testBackground.jpg differ