import { SMouseEvent, SNetUtil } from "@saga-web/base/lib"; import { SCanvasView, SPainter, SPoint, SRect, SSvgPaintEngine } from "@saga-web/draw/lib"; import { SGraphScene } from "./SGraphScene"; import { SGraphItem } from "./SGraphItem"; /** * Graphy图形引擎视图类 * * @author 庞利祥(sybotan@126.com) */ export class SGraphView extends SCanvasView { /** 场景对象 */ private _scene: SGraphScene | null = null; get scene(): SGraphScene | null { return this._scene; } // Get scene set scene(v: SGraphScene | null) { if (this._scene != null) { this._scene.view = null; } this._scene = v; if (this._scene != null) { this._scene.view = this; } this.update(); } // Set scene /** * 构造函数 * * @param id 画布对象ID */ constructor(id: string) { super(id); } // Function constructor() /** * 保存场景SVG文件 * * @param name 文件名 * @param width svg宽度 * @param height svg高度 */ saveSceneSvg(name: string, width: number, height: number): void { let url = URL.createObjectURL( new Blob([this.sceneSvgData(width, height)], { type: "text/xml,charset=UTF-8" }) ); SNetUtil.downLoad(name, url); } // Function saveSceneSvg() /** * 场景SVG图形的数据 * * @param width svg宽度 * @param height svg高度 * @return URL地址 */ sceneSvgData(width: number, height: number): string { if (null == this.scene) { return ""; } let engine = new SSvgPaintEngine(width, height); let painter = new SPainter(engine); // 保存视图缩放比例与原点位置 let s0 = this.scale; let x0 = this.origin.x; let y0 = this.origin.y; // 场景中无对象 let rect = this.scene.allItemRect(); this.fitRectToSize(width, height, rect); this.onDraw(painter); // 恢复视图缩放比例与原点位置 this.scale = s0; this.origin.x = x0; this.origin.y = y0; return engine.toSvg(); } // Function saveSvg() /** * 适配视图到视图 */ fitSceneToView(): void { if (null == this.scene) { return; } // 场景中无对象 let rect = this.scene.allItemRect(); this.fitRectToSize(this.width, this.height, rect); } // Function FitView() /** * 适配选中的对象在视图中可见 */ fitSelectedToView(): void { if (null == this.scene) { return; } // 场景中无对象 let rect = this.scene.selectedItemRect(); this.fitRectToSize(this.width, this.height, rect); } // Function fitSelectedToSize() /** * 适配任意对象在视图中可见 */ fitItemToView(itemList: SGraphItem[]): void { if (null == this.scene) { return; } let rect: SRect | null = null; // 依次取item列中的所有item。将所有item的边界做并焦处理。 for (let item of itemList) { 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) ); } } // 场景中无对象 this.fitRectToSize(this.width, this.height, rect); } // Function fitItemToView() /** * 将场景中的xy坐标转换成视图坐标。 * * @param x 场景中的横坐标 * @param y 场景中的纵坐标 * @return 视图坐标 */ mapFromScene(x: number, y: number): SPoint; /** * 将场景中的xy坐标转换成视图坐标。 * * @param pos 场景中的坐标 * @return 视图坐标 */ mapFromScene(pos: SPoint): SPoint; /** * 将场景中的xy坐标转换成视图坐标(重载实现)。 * * @param x 场景中的横坐标 * @param y 场景中的纵坐标 * @return 视图坐标 */ mapFromScene(x: number | SPoint, y?: number): SPoint { if (x instanceof SPoint) { // 如果传入的是SPoint对象 return new SPoint( x.x * this.scale + this.origin.x, x.y * this.scale + this.origin.y ); } // @ts-ignore return new SPoint( x * this.scale + this.origin.x, (y == undefined ? 0 : y) * this.scale + this.origin.y ); } // Function mapFromScene() /** * 将i视图的xy坐标转换成场景坐标。 * * @param x 视图横坐标 * @param y 视图纵坐标 * @return 场景坐标 */ mapToScene(x: number, y: number): SPoint; /** * 将i视图的xy坐标转换成场景坐标。 * * @param pos 视图坐标 * @return 场景坐标 */ mapToScene(pos: SPoint): SPoint; /** * 将i视图的xy坐标转换成场景坐标。(不推荐在外部调用) * * @param x 视图的横坐标/或SPoint对象 * @param y 视图的纵坐标 * @return 场景坐标 */ mapToScene(x: number | SPoint, y?: number): SPoint { if (x instanceof SPoint) { // 如果传入的是SPoint对象 return new SPoint( (x.x - this.origin.x) / this.scale, (x.y - this.origin.y) / this.scale ); } return new SPoint( (x - this.origin.x) / this.scale, ((y == undefined ? 0 : y) - this.origin.y) / this.scale ); } // Function mapToScene() /** * 绘制视图 * * @param painter painter对象 */ protected onDraw(painter: SPainter): void { painter.save(); painter.clearRect(0, 0, this.width, this.height); painter.restore(); // 如果未设备场景 if (this.scene == null) { return; } // 绘制背景 painter.save(); this.drawBackground(painter); painter.restore(); // 绘制场景 painter.save(); painter.translate(this.origin.x, this.origin.y); painter.scale(this.scale, this.scale); this.scene.drawScene(painter, new SRect()); painter.restore(); // 绘制前景 painter.save(); this.drawForeground(painter); painter.restore(); } // Function onDraw(); /** * 绘制场景背景 * * @param painter painter对象 */ protected drawBackground(painter: SPainter): void { // DO NOTHING } // Function drawBackground() /** * 绘制场景前景 * * @param painter painter对象 */ protected drawForeground(painter: SPainter): void { // DO NOTHING } // Function drawForeground() /** * 鼠标双击事件 * * @param event 事件参数 */ protected onDoubleClick(event: MouseEvent): void { if (this.scene != null) { let ce = this.toSceneMotionEvent(event); this.scene.onDoubleClick(ce); } } // Function onClick() /** * 鼠标按下事件 * * @param event 事件参数 */ protected onMouseDown(event: MouseEvent): void { super.onMouseDown(event); if (this.scene != null) { let ce = this.toSceneMotionEvent(event); this.scene.onMouseDown(ce); } } // Function onClick() /** * 鼠标移动事件 * * @param event 事件参数 */ protected onMouseMove(event: MouseEvent): void { super.onMouseMove(event); if (this.scene != null) { let ce = this.toSceneMotionEvent(event); this.scene.onMouseMove(ce); } } // Function onClick() /** * 鼠标松开事件 * * @param event 事件参数 */ protected onMouseUp(event: MouseEvent): void { super.onMouseUp(event); if (this.scene != null) { let ce = this.toSceneMotionEvent(event); this.scene.onMouseUp(ce); } } // Function onClick() /** * 上下文菜单事件 * * @param event 事件参数 */ protected onContextMenu(event: MouseEvent): void { if (this.scene != null) { let ce = this.toSceneMotionEvent(event); this.scene.onContextMenu(ce); } } // Function onContextMenu() /** * 按键按下事件 * * @param event 事件参数 */ protected onKeyDown(event: KeyboardEvent): void { if (this.scene != null) { this.scene.onKeyDown(event); } } // Function onKeyDown() /** * 按键松开事件 * * @param event 事件参数 */ protected onKeyUp(event: KeyboardEvent): void { if (this.scene != null) { this.scene.onKeyUp(event); } } // Function onKeyUp() /** * 适配场景在视图中可见 * * @param width 宽度 * @param height 高度 * @param rect 对象的矩阵大小 */ private fitRectToSize( width: number, height: number, rect: SRect | null ): void { // 未设置场景 if (null == rect || !rect.isValid()) { return; } this.scale = Math.min(width / rect.width, height / rect.height) * 0.8; // 计算场景中心点 let center = rect.center(); this.origin.x = width / 2.0 - center.x * this.scale; this.origin.y = height / 2.0 - center.y * this.scale; } // Function fitRectToSize() /** * MouseEvent事件转换成场景SMouseEvent事件 * * @param event 事件参数 */ private toSceneMotionEvent(event: MouseEvent): SMouseEvent { let se = new SMouseEvent(event); se.matrix.translateSelf(this.origin.x, this.origin.y); se.matrix.scaleSelf(this.scale, this.scale); const mp = new DOMPoint(event.offsetX, event.offsetY).matrixTransform( se.matrix.inverse() ); se.x = mp.x; se.y = mp.y; return se; } // Function toSceneMotionEvent() } // Class SGraphyView