/* * ********************************************************************************************************************* * * !! * .F88X * X8888Y * .}888888N; * i888888N; .:! .I$WI: * R888888I .'N88~ i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8& * .R888888I .;N8888~ .X8' "8I.!,/8" !%NY8`"8I8~~8>,88I * +888888N; .8888888Y "&&8Y.}8, * ./888888N; .R888888Y .'}~ .>}'.`+> i}! "i' +/' .'i~ !11,.:">, .~]! .i}i * ~888888%: .I888888l .]88~`1/iY88Ii+1'.R$8$8]"888888888> Y8$ W8E X8E W8888'188Il}Y88$* * 18888888 E8888881 .]W%8$`R8X'&8%++N8i,8N%N8+l8%` .}8N:.R$RE%N88N%N$K$R 188,FE$8%~Y88I * .E888888I .i8888888' .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~ * 8888888I .,N888888~ ~88i"8W,!N8*.I88.}888%F,i$88"F88" 888:E8X.>88!i88>`888*.}Fl1]*}1YKi' * i888888N' I888Y ]88;/EX*IFKFK88X K8R .l8W 88Y ~88}'88E&%8W.X8N``]88!.$8K .:W8I * .i888888N; I8Y .&8$ .X88! i881.:%888>I88 ;88] +88+.';;;;:.Y88X 18N.,88l .+88/ * .:R888888I * .&888888I Copyright (c) 2009-2020. 博锐尚格科技股份有限公司 * ~8888' * .!88~ All rights reserved. * * ********************************************************************************************************************* */ import { SMouseEvent, SMatrix } from "@persagy-web/base"; import { SPainter, SPoint, SRect } from "@persagy-web/draw"; import { SGraphItem } from "./SGraphItem"; import { SGraphView } from "./SGraphView"; import { SGraphSelectContainer } from "./SGraphSelectContainer"; /** * Graph 图形引擎场景类 * * @author 庞利祥 */ export class SGraphScene { /** 展示场景的视图 */ view: SGraphView | null = null; /** 根节点 */ protected root: SGraphItem = new SGraphItem(); /** 当前捕获 Item */ grabItem: SGraphItem | null = null; /** 鼠标所在 Item */ hoverItem: SGraphItem | null = null; /** 选择器 */ selectContainer: SGraphSelectContainer = new SGraphSelectContainer(); /** * 构造函数 */ constructor() { this.root.scene = this; this.addItem(this.selectContainer); } // Constructor /** * 添加 item 对象到场景。 * * @param item 添加的对象 */ addItem(item: SGraphItem): void { item.parent = this.root; } // Function addItem() /** * 从场景中移除 Item。 * * @param item 被移除的对象 */ removeItem(item: SGraphItem): void { item.parent = null; } // Function removeItem() /** * 绘制场景 * * @param painter 绘制对象 * @param rect 更新绘制区域 */ drawScene(painter: SPainter, rect: SRect): void { this.root.onPaint(painter, rect); } // Function drawScene() /** * 绘制背景 * * @param painter 绘制对象 * @param rect 更新绘制区域 */ drawBackground(painter: SPainter, rect: SRect) { // DO NOTHING } // Function drawBackground() /** * 绘制前景 * * @param painter 绘制对象 * @param rect 更新绘制区域 */ drawForeground(painter: SPainter, rect: SRect) { // DO NOTHING } // Function drawForeground() /** * 所有 item 占用的矩形区域 * * @param flag 是否只对可见 item 处理 * @return 所有 item 占用的矩形区域 */ allItemRect(flag: boolean = true): SRect | null { let rect: SRect | null = null; // 依次取 item 列中的所有 item。将所有 item 的边界做并焦处理。 for (let item of this.root.children) { if ((flag && item.visible) || !flag) { if (rect == null) { rect = item .boundingRect() .translated(item.pos.x, item.pos.y); } else { rect.union( item.boundingRect().translated(item.pos.x, item.pos.y) ); } } } return rect; } // Function allItemRect() /** * 被选中 item 占用的矩形区域 * * @return 被选中 item 占用的矩形区域 */ selectedItemRect(): SRect | null { let rect: SRect | null = null; // 依次取 item 列中的所有 item。将所有 item 的边界做并焦处理。 for (let item of this.root.children) { // 如果 item 未被选中,则去选择下一个 item if (!item.selected) { continue; } if (rect == null) { rect = item.boundingRect().translated(item.pos.x, item.pos.y); } else { rect.union( item.boundingRect().translated(item.pos.x, item.pos.y) ); } } return rect; } // Function selectedItemRect() /** * 获得选中的对象列表 * * @return 选中对象列表 */ selectedItems(): SGraphItem[] { let itemList = Array(); for (let item of this.root.children) { // 如果 item 未被选中,则去选择下一个 item if (item.selected) { itemList.push(item); } } return itemList; } // Function selectedItems() // ================================================================================================================= // 事件 /** * 鼠标双击事件 * * @param event 保存事件参数 * @return 是否处理事件 */ onDoubleClick(event: SMouseEvent): boolean { if (this.grabItem != null) { return this.grabItem.onDoubleClick( this.toGrabItemMotionEvent(this.grabItem, event) ); } return this.root.onDoubleClick(event); } // Function onDoubleClick() /** * 鼠标按下事件 * * @param event 保存事件参数 * @return 是否处理事件 */ onMouseDown(event: SMouseEvent): boolean { if (this.grabItem != null) { return this.grabItem.onMouseDown( this.toGrabItemMotionEvent(this.grabItem, event) ); } const flag = this.root.onMouseDown(event); if (!flag) { this.selectContainer.clear(); } return flag; } // Function onMouseDown() /** * 鼠标移动事件 * * @param event 保存事件参数 * @return 是否处理事件 */ onMouseMove(event: SMouseEvent): boolean { if (this.grabItem != null) { return this.grabItem.onMouseMove( this.toGrabItemMotionEvent(this.grabItem, event) ); } return this.root.onMouseMove(event); } // Function onMouseMove() /** * 释放鼠标事件 * * @param event 保存事件参数 * @return 是否处理事件 */ onMouseUp(event: SMouseEvent): boolean { if (this.grabItem != null) { return this.grabItem.onMouseUp( this.toGrabItemMotionEvent(this.grabItem, event) ); } return this.root.onMouseUp(event); } // Function onMouseUp() /** * 上下文菜单事件 * * @param event 事件参数 * @return 是否处理事件 */ onContextMenu(event: SMouseEvent): boolean { if (this.grabItem != null) { return this.grabItem.onContextMenu( this.toGrabItemMotionEvent(this.grabItem, event) ); } return this.root.onContextMenu(event); } // Function onContextMenu() /** * 按键按下事件 * * @param event 事件参数 */ onKeyDown(event: KeyboardEvent): void { if (this.grabItem != null) { return this.grabItem.onKeyDown(event); } return this.root.onKeyDown(event); } // Function onKeyDown() /** * 按键松开事件 * * @param event 事件参数 */ onKeyUp(event: KeyboardEvent): void { if (this.grabItem != null) { return this.grabItem.onKeyUp(event); } return this.root.onKeyUp(event); } // Function onKeyUp() /** * 转换场景事件坐标到指定 Item 坐标事件 * * @param item 指定的 item 对象 * @param event 场景事件 * @return 转换场景事件坐标到指定 Item 坐标事件 */ private toGrabItemMotionEvent( item: SGraphItem, event: SMouseEvent ): SMouseEvent { let se = { ...event }; se.matrix = new SMatrix(); if (this.view) { se.matrix.translate(this.view.origin.x, this.view.origin.y); se.matrix.scale(this.view.scale, this.view.scale); se.matrix.rotate(this.view.rotate); } se.matrix.multiply(item.scene2itemMattrix()); let p = new SPoint(event.offsetX, event.offsetY).matrixTransform( se.matrix.inversed() ); se.x = p.x; se.y = p.y; return se; } // Function toGrabItemMotionEvent() } // Class SGraphScene