123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- /*
- * *********************************************************************************************************************
- *
- * !!
- * .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, SNetUtil } from "@persagy-web/base";
- import {
- SCanvasPaintEngine,
- SCanvasView,
- SPainter,
- SPoint,
- SRect,
- SSvgPaintEngine
- } from "@persagy-web/draw";
- import { SGraphScene } from "./SGraphScene";
- import { SGraphItem } from "./SGraphItem";
- import { v1 as uuidv1 } from "uuid";
- import { SColor } from "@persagy-web/draw";
- /**
- * Graph 图形引擎视图类
- *
- * @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
- /** 背景色 */
- backgroundColor: SColor = SColor.Transparent;
- /** 旋转角度 */
- rotate: number = 0;
- /**
- * 构造函数
- *
- * @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()
- /**
- * 将场景中的 x, y 坐标转换成视图坐标。
- *
- * @param x 场景中的横坐标
- * @param y 场景中的纵坐标
- * @return 视图坐标
- */
- mapFromScene(x: number, y: number): SPoint;
- /**
- * 将场景中的 x, y 坐标转换成视图坐标。
- *
- * @param pos 场景中的坐标
- * @return 视图坐标
- */
- mapFromScene(pos: SPoint): SPoint;
- /**
- * 将场景中的 x, y 坐标转换成视图坐标(重载实现)。
- *
- * @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()
- /**
- * 将视图的 x, y 坐标转换成场景坐标。
- *
- * @param x 视图横坐标
- * @param y 视图纵坐标
- * @return 场景坐标
- */
- mapToScene(x: number, y: number): SPoint;
- /**
- * 将视图的 x, y 坐标转换成场景坐标。
- *
- * @param pos 视图坐标
- * @return 场景坐标
- */
- mapToScene(pos: SPoint): SPoint;
- /**
- * 将视图的 x, y 坐标转换成场景坐标。(不推荐在外部调用)
- *
- * @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 name 名称
- * @param type 图像类型
- * @param width 要保存图形的宽
- * @param height 要保存图形的高
- */
- saveImageSize(
- name: string,
- type: string,
- width: number,
- height: number
- ): void {
- const can = document.createElement("CANVAS") as HTMLCanvasElement;
- can.width = width;
- can.height = height;
- can.id = uuidv1();
- const body = document.getElementsByTagName("body")[0];
- body.appendChild(can);
- let engine = new SCanvasPaintEngine(
- can.getContext("2d") as CanvasRenderingContext2D
- );
- let painter = new SPainter(engine);
- let vi = new SGraphView(can.id);
- vi.scene = this.scene;
- vi.fitSceneToView();
- vi.backgroundColor = new SColor("#FFFFFF");
- vi.onDraw(painter);
- vi.saveImage(name, type);
- // @ts-ignore
- this.scene.view = this;
- can.remove();
- // @ts-ignore
- vi = null;
- } // Function saveImageSize()
- /**
- * 绘制视图
- *
- * @param 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);
- painter.rotate(this.rotate);
- this.scene.drawScene(painter, new SRect());
- painter.restore();
- // 绘制前景
- painter.save();
- this.drawForeground(painter);
- painter.restore();
- } // Function onDraw();
- /**
- * 绘制场景背景
- *
- * @param painter 绘制对象
- */
- protected drawBackground(painter: SPainter): void {
- painter.brush.color = this.backgroundColor;
- painter.pen.color = SColor.Transparent;
- painter.drawRect(0, 0, this.width, this.height);
- // DO NOTHING
- } // Function drawBackground()
- /**
- * 绘制场景前景
- *
- * @param 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 onDoubleClick()
- /**
- * 鼠标按下事件
- *
- * @param event 事件参数
- */
- protected onMouseDown(event: MouseEvent): void {
- super.onMouseDown(event);
- if (this.scene != null) {
- let ce = this.toSceneMotionEvent(event);
- this.scene.onMouseDown(ce);
- }
- } // Function onMouseDown()
- /**
- * 鼠标移动事件
- *
- * @param event 事件参数
- */
- protected onMouseMove(event: MouseEvent): void {
- super.onMouseMove(event);
- if (this.scene != null) {
- let ce = this.toSceneMotionEvent(event);
- this.scene.onMouseMove(ce);
- }
- } // Function onMouseMove()
- /**
- * 鼠标松开事件
- *
- * @param event 事件参数
- */
- protected onMouseUp(event: MouseEvent): void {
- super.onMouseUp(event);
- if (this.scene != null) {
- let ce = this.toSceneMotionEvent(event);
- this.scene.onMouseUp(ce);
- }
- } // Function onMouseUp()
- /**
- * 上下文菜单事件
- *
- * @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 事件参数
- * @return MouseEvent 事件转换成场景 SMouseEvent 事件
- */
- private toSceneMotionEvent(event: MouseEvent): SMouseEvent {
- let se = new SMouseEvent(event);
- se.matrix.translate(this.origin.x, this.origin.y);
- se.matrix.scale(this.scale, this.scale);
- se.matrix.rotate(this.rotate);
- const mp = new SPoint(event.offsetX, event.offsetY).matrixTransform(
- se.matrix.inversed()
- );
- se.x = mp.x;
- se.y = mp.y;
- return se;
- } // Function toSceneMotionEvent()
- } // Class SGraphView
|