import { SMouseButton, SMouseEvent, SObject } from "@saga-web/base/lib"; import { SPainter, SPoint, SRect } from "@saga-web/draw/lib"; import { SGraphScene } from "./SGraphScene"; /** * Graph图形引擎Item类 * * @author 庞利祥(sybotan@126.com) */ export class SGraphItem extends SObject { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 属性 /** 场景对象 */ private _scene: SGraphScene | null = null; get scene(): SGraphScene | null { if (null != this._parent) { return this._parent.scene; } else { return this._scene; } } // Get scene set scene(v: SGraphScene | null) { this._scene = v; this.update(); } // Set scene /** parent属性存值函数 */ private _parent: SGraphItem | null = null; get parent(): SGraphItem | null { return this._parent; } // Get parent set parent(v: SGraphItem | null) { // 如果parent未变更 if (this.parent == v) { return; } // 如果原parent不为空 if (this._parent != null) { // 将节点从原parent节点中摘除 let i = this._parent.children.indexOf(this); this._parent.children.splice(i, 1); } this._parent = v; this.update(); // 如果新parent不为空 if (this._parent != null) { // 将节点加入到新parent节点中 this._parent.children.push(this); this._parent.children.sort(SGraphItem.sortItemZOrder); } } // Set parent() /** 子节点 */ children: SGraphItem[] = []; /** Z轴顺序 */ private _zOrder: number = 0; get zOrder(): number { return this._zOrder; } // Get zOrder set zOrder(v: number) { this._zOrder = v; if (this._parent != null) { this._parent.children.sort(SGraphItem.sortItemZOrder); } this.update(); } // Set zOrder /** 位置 */ pos: SPoint = new SPoint(0, 0); /** X轴坐标 */ get x(): number { return this.pos.x; } // Get x set x(v: number) { if (this.pos.x == v) { return; } let old = this.pos.x; this.pos.x = v; this.update(); } // Set x /** Y轴坐标 */ get y(): number { return this.pos.y; } // Get y set y(v: number) { if (this.pos.y == v) { return; } let old = this.pos.y; this.pos.y = v; this.update(); } // Set y /** 缩放比例 */ scale: number = 1; /** 是否可见 */ _visible: boolean = true; get visible(): boolean { return this._visible; } // Get visible set visible(v: boolean) { this._visible = v; this.update(); } // Set visible /** 是否可以移动 */ moveable: boolean = false; /** 是否正在移动 */ private _isMoving = false; /** 是否可用 */ enabled: boolean = true; /** 是否可被选中 */ selectable = false; /** 是否被选中 */ private _selected = false; get selected(): boolean { return this._selected && this.selectable && this.enabled; } // Get selected set selected(value: boolean) { // 如果选择状态未变更 if (this.selected == value) { return; } this._selected = value; this.update(); } // Set selected /** 是否进行变形 */ isTransform = true; /** 鼠标按下时位置 */ private _mouseDownPos = new SPoint(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 函数 /** * 构造函数 * * @param parent 指向父对象 */ constructor(parent: SGraphItem | null = null) { super(); if (parent) { this.parent = parent; } } // Function constructor() /** * Item绘制框架 * * @param painter painter对象 * @param rect 绘制区域 */ onPaint(painter: SPainter, rect: SRect): void { this.onDraw(painter); for (let item of this.children) { // 如果item不可见 if (!item.visible) { continue; } // item所占矩阵与要刷新的矩阵相交则刷新,否则不刷 // if (item.boundingRect()--rect){ // // } // 保存画布状态 painter.save(); // item位移到指定位置绘制 painter.translate(item.x, item.y); // 如果不进行变形处理,则取消painter的变型操作 if (!item.isTransform) { let matrix = painter.worldTransform; let x0 = matrix.e; let y0 = matrix.f; painter.resetTransform(); painter.translate(x0, y0); } // 设置绘制区域 // canvas.clip(item.boundingRect()) // rect 调整 宽度高度取最小值 根据pos位移 // 绘制item item.onPaint(painter, rect); // 恢复画布状态 painter.restore(); } } // Function onPaint() /** * Item绘制操作 * * @param painter painter对象 */ onDraw(painter: SPainter): void {} // Function onDraw() /** * 隐藏对象 */ hide(): void { this.visible = false; } // Function hide() /** * 显示对象 */ show(): void { this.visible = true; } // Function show() /** * 更新Item */ update(): void { if (null != this.scene) { const view = this.scene.view; if (null != view) { view.update(); } } } // Function update() /** * Item对象边界区域 * * @return 对象边界区域 */ boundingRect(): SRect { return new SRect(0, 0, 10, 10); } // Function boundingRect() /** * 移动item到指定位置 * * @param x 新位置的x坐标 * @param y 新位置的y坐标 */ moveTo(x: number, y: number): void { this.x = x; this.y = y; } // moveTo() /** * 判断item是否包含点x,y * * @param x 横坐标(当前item) * @param y 纵坐标(当前item) * * @return boolean */ contains(x: number, y: number): boolean { return this.boundingRect().contains(x - this.x, y - this.y); } // Function contains() /** * 获得item的路径节点列表。(该节点被加载到场景中,如果未被加载到场景中,计算会出错) * * @return *[] */ itemPath(): SGraphItem[] { if (this.parent != null) { let list = this.parent.itemPath(); list.push(this); return list; } return [this]; } // Function itemPath() /** * 将场景中的xy坐标转换成item坐标。(该节点被加载到场景中,如果未被加载到场景中,计算会出错) * * @param x 场景中的横坐标 * @param y 场景中的纵坐标 * * @return 在item中的坐标 */ mapFromScene(x: number, y: number): SPoint { let list = this.itemPath(); let x0 = x; let y0 = y; for (let item of list) { x0 = (x0 - item.x) / item.scale; y0 = (y0 - item.y) / item.scale; } return new SPoint(x0, y0); } // Function mapFromScene() /** * 将item中的xy坐标转换成场景坐标。(该节点被加载到场景中,如果未被加载到场景中,计算会出错) * * @param x item中的横坐标 * @param y item中的纵坐标 * * @return 在场景中的坐标 */ mapToScene(x: number, y: number): SPoint { if (this.parent == null) { return new SPoint(x, y); } return this.parent.mapToScene( x * this.scale + this.x, y * this.scale + this.y ); } // Function mapToScene() // ================================================================================================================= // 事件 /** * 鼠标单击事件 * * @param event 保存事件参数 * @return boolean */ onClick(event: SMouseEvent): boolean { for (let i = this.children.length - 1; i >= 0; i--) { let item = this.children[i]; if (!this.acceptEvent() || !item.visible) { continue; } let ce = SGraphItem.toChildMouseEvent(item, event); if (item.contains(event.x, event.y) && item.onClick(ce)) { // 如果点在子项目上且子项目处理了事件 return true; } } return false; } // Function onClick() /** * 鼠标双击事件 * * @param event 保存事件参数 * @return boolean */ onDoubleClick(event: SMouseEvent): boolean { for (let i = this.children.length - 1; i >= 0; i--) { let item = this.children[i]; if (!this.acceptEvent() || !item.visible) { continue; } let ce = SGraphItem.toChildMouseEvent(item, event); if (item.contains(event.x, event.y) && item.onDoubleClick(ce)) { // 如果点在子项目上且子项目处理了事件 return true; } } return false; } // Function onDoubleClick() /** * 鼠标按下事件 * * @param event 保存事件参数 * @return boolean */ onMouseDown(event: SMouseEvent): boolean { for (let i = this.children.length - 1; i >= 0; i--) { let item = this.children[i]; if (!this.acceptEvent() || !item.visible) { continue; } let ce = SGraphItem.toChildMouseEvent(item, event); if (item.contains(event.x, event.y) && item.onMouseDown(ce)) { // 如果点在子项目上且子项目处理了事件 return true; } } if (this.selectable) { this.clickSelect(event); } if (this.moveable) { this._mouseDownPos = new SPoint(event.x, event.y); this._isMoving = true; this.grabItem(this); return true; } return false; } // Function onMouseDown() /** * 鼠标移动事件 * * @param event 保存事件参数 * @return boolean */ onMouseMove(event: SMouseEvent): boolean { for (let i = this.children.length - 1; i >= 0; i--) { let item = this.children[i]; if (!this.acceptEvent() || !item.visible) { continue; } let ce = SGraphItem.toChildMouseEvent(item, event); if (item.contains(event.x, event.y) && item.onMouseMove(ce)) { // 如果点在子项目上且子项目处理了事件 return true; } } if ( event.buttons & SMouseButton.LeftButton && this.moveable && this._isMoving ) { let old = new SPoint(this.pos.x, this.pos.y); this.moveTo( this.pos.x + event.x - this._mouseDownPos.x, this.pos.y + event.y - this._mouseDownPos.y ); this.$emit("onMove", old, this.pos); } // 处理hover const scene = this.scene; if (null != scene) { if (scene.hoverItem == null || scene.hoverItem !== this) { if (scene.hoverItem != null) { scene.hoverItem.onMouseLeave(event); } this.onMouseEnter(event); scene.hoverItem = this; } } return true; } // Function onMouseMove() /** * 释放鼠标事件 * * @param event 保存事件参数 * @return boolean */ onMouseUp(event: SMouseEvent): boolean { for (let i = this.children.length - 1; i >= 0; i--) { let item = this.children[i]; if (!this.acceptEvent() || !item.visible) { continue; } let ce = SGraphItem.toChildMouseEvent(item, event); if (item.contains(event.x, event.y) && item.onMouseUp(ce)) { // 如果点在子项目上且子项目处理了事件 return true; } } this._isMoving = false; this.releaseItem(); return false; } // Function onMouseUp() /** * 鼠标进入事件 * * @param event 保存事件参数 * @return boolean */ onMouseEnter(event: SMouseEvent): boolean { return false; } // Function onMouseEnter() /** * 鼠标离开事件 * * @param event 保存事件参数 * @return boolean */ onMouseLeave(event: SMouseEvent): boolean { return false; } // Function onMouseLeave() /** * 上下文菜单事件 * * @param event 事件参数 */ onContextMenu(event: SMouseEvent): boolean { for (let i = this.children.length - 1; i >= 0; i--) { let item = this.children[i]; if (!this.acceptEvent() || !item.visible) { continue; } let ce = SGraphItem.toChildMouseEvent(item, event); if (item.contains(event.x, event.y) && item.onContextMenu(ce)) { // 如果点在子项目上且子项目处理了事件 return true; } } return false; } // Function onContextMenu() /** * 按键按下事件 * * @param event 事件参数 */ onKeyDown(event: KeyboardEvent): void {} // Function onKeyDown() /** * 按键press事件 * * @param event 事件参数 */ onKeyPress(event: KeyboardEvent): void {} // Function onKeyPress() /** * 按键松开事件 * * @param event 事件参数 */ onKeyUp(event: KeyboardEvent): void {} // Function onKeyUp() /** * 取消操作item事件 * * */ cancelOperate(): void {} // Function cancelOperate() // ================================================================================================================= // 私有方法 /** * 按ZOrder排序 * * @param a 比较元素1 * @param b 比较元素2 * @return {number} */ private static sortItemZOrder(a: SGraphItem, b: SGraphItem): number { return a.zOrder - b.zOrder; } // Function sortItemZOrder() /** * 鼠标事件转子对象鼠标事件 * * @param child 子对象 * @param event 事件参数 * @return 子对象鼠标事件 */ private static toChildMouseEvent( child: SGraphItem, event: SMouseEvent ): SMouseEvent { let ce = new SMouseEvent(event); ce.x = (event.x - child.x) / child.scale; ce.y = (event.y - child.y) / child.scale; return ce; } // Function toChildMouseEvent() /** * 锁定item * * @param item 被锁定的item */ private grabItem(item: SGraphItem): void { if (this.scene != null) { this.scene.grabItem = item; } } // Function grabItem /** * 释放被锁定的item */ private releaseItem(): void { if (this.scene != null) { this.scene.grabItem = null; } } // Function grabItem /** * 判断是否处理事件 * * @return true: 处理事件,否则不处理 */ private acceptEvent(): boolean { return this.visible && this.enabled; } // Function acceptEvent() /** * 点选item对象 * * @param event 事件参数 */ private clickSelect(event: SMouseEvent): void { // 如果Item不可被选中,或没有按下鼠标左键,则直接返回 if (!this.selectable || event.buttons != SMouseButton.LeftButton) { return; } const scene = this.scene; if (scene == null) { return; } // 如果按下Ctrl键,只改变当前item的选择标志 if (event.ctrlKey) { scene.selectContainer.toggleItem(this); } else { scene.selectContainer.setItem(this); } } // Function clickSelect() } // Class SGraphItem