SGraphView.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. import { SMouseEvent, SNetUtil } from "@saga-web/base/lib";
  2. import {
  3. SCanvasView,
  4. SPainter,
  5. SPoint,
  6. SRect,
  7. SSvgPaintEngine
  8. } from "@saga-web/draw/lib";
  9. import { SGraphScene } from "./SGraphScene";
  10. import { SGraphItem } from "./SGraphItem";
  11. /**
  12. * Graphy图形引擎视图类
  13. *
  14. * @author 庞利祥(sybotan@126.com)
  15. */
  16. export class SGraphView extends SCanvasView {
  17. /** 场景对象 */
  18. private _scene: SGraphScene | null = null;
  19. get scene(): SGraphScene | null {
  20. return this._scene;
  21. } // Get scene
  22. set scene(v: SGraphScene | null) {
  23. if (this._scene != null) {
  24. this._scene.view = null;
  25. }
  26. this._scene = v;
  27. if (this._scene != null) {
  28. this._scene.view = this;
  29. }
  30. this.update();
  31. } // Set scene
  32. /**
  33. * 构造函数
  34. *
  35. * @param id 画布对象ID
  36. */
  37. constructor(id: string) {
  38. super(id);
  39. } // Function constructor()
  40. /**
  41. * 保存场景SVG文件
  42. *
  43. * @param name 文件名
  44. * @param width svg宽度
  45. * @param height svg高度
  46. */
  47. saveSceneSvg(name: string, width: number, height: number): void {
  48. let url = URL.createObjectURL(
  49. new Blob([this.sceneSvgData(width, height)], {
  50. type: "text/xml,charset=UTF-8"
  51. })
  52. );
  53. SNetUtil.downLoad(name, url);
  54. } // Function saveSceneSvg()
  55. /**
  56. * 场景SVG图形的数据
  57. *
  58. * @param width svg宽度
  59. * @param height svg高度
  60. * @return URL地址
  61. */
  62. sceneSvgData(width: number, height: number): string {
  63. if (null == this.scene) {
  64. return "";
  65. }
  66. let engine = new SSvgPaintEngine(width, height);
  67. let painter = new SPainter(engine);
  68. // 保存视图缩放比例与原点位置
  69. let s0 = this.scale;
  70. let x0 = this.origin.x;
  71. let y0 = this.origin.y;
  72. // 场景中无对象
  73. let rect = this.scene.allItemRect();
  74. this.fitRectToSize(width, height, rect);
  75. this.onDraw(painter);
  76. // 恢复视图缩放比例与原点位置
  77. this.scale = s0;
  78. this.origin.x = x0;
  79. this.origin.y = y0;
  80. return engine.toSvg();
  81. } // Function saveSvg()
  82. /**
  83. * 适配视图到视图
  84. */
  85. fitSceneToView(): void {
  86. if (null == this.scene) {
  87. return;
  88. }
  89. // 场景中无对象
  90. let rect = this.scene.allItemRect();
  91. this.fitRectToSize(this.width, this.height, rect);
  92. } // Function FitView()
  93. /**
  94. * 适配选中的对象在视图中可见
  95. */
  96. fitSelectedToView(): void {
  97. if (null == this.scene) {
  98. return;
  99. }
  100. // 场景中无对象
  101. let rect = this.scene.selectedItemRect();
  102. this.fitRectToSize(this.width, this.height, rect);
  103. } // Function fitSelectedToSize()
  104. /**
  105. * 适配任意对象在视图中可见
  106. */
  107. fitItemToView(itemList: SGraphItem[]): void {
  108. if (null == this.scene) {
  109. return;
  110. }
  111. let rect: SRect | null = null;
  112. // 依次取item列中的所有item。将所有item的边界做并焦处理。
  113. for (let item of itemList) {
  114. if (rect == null) {
  115. rect = item.boundingRect().translated(item.pos.x, item.pos.y);
  116. } else {
  117. rect.union(
  118. item.boundingRect().translated(item.pos.x, item.pos.y)
  119. );
  120. }
  121. }
  122. // 场景中无对象
  123. this.fitRectToSize(this.width, this.height, rect);
  124. } // Function fitItemToView()
  125. /**
  126. * 将场景中的xy坐标转换成视图坐标。
  127. *
  128. * @param x 场景中的横坐标
  129. * @param y 场景中的纵坐标
  130. * @return 视图坐标
  131. */
  132. mapFromScene(x: number, y: number): SPoint;
  133. /**
  134. * 将场景中的xy坐标转换成视图坐标。
  135. *
  136. * @param pos 场景中的坐标
  137. * @return 视图坐标
  138. */
  139. mapFromScene(pos: SPoint): SPoint;
  140. /**
  141. * 将场景中的xy坐标转换成视图坐标(重载实现)。
  142. *
  143. * @param x 场景中的横坐标
  144. * @param y 场景中的纵坐标
  145. * @return 视图坐标
  146. */
  147. mapFromScene(x: number | SPoint, y?: number): SPoint {
  148. if (x instanceof SPoint) {
  149. // 如果传入的是SPoint对象
  150. return new SPoint(
  151. x.x * this.scale + this.origin.x,
  152. x.y * this.scale + this.origin.y
  153. );
  154. }
  155. // @ts-ignore
  156. return new SPoint(
  157. x * this.scale + this.origin.x,
  158. (y == undefined ? 0 : y) * this.scale + this.origin.y
  159. );
  160. } // Function mapFromScene()
  161. /**
  162. * 将i视图的xy坐标转换成场景坐标。
  163. *
  164. * @param x 视图横坐标
  165. * @param y 视图纵坐标
  166. * @return 场景坐标
  167. */
  168. mapToScene(x: number, y: number): SPoint;
  169. /**
  170. * 将i视图的xy坐标转换成场景坐标。
  171. *
  172. * @param pos 视图坐标
  173. * @return 场景坐标
  174. */
  175. mapToScene(pos: SPoint): SPoint;
  176. /**
  177. * 将i视图的xy坐标转换成场景坐标。(不推荐在外部调用)
  178. *
  179. * @param x 视图的横坐标/或SPoint对象
  180. * @param y 视图的纵坐标
  181. * @return 场景坐标
  182. */
  183. mapToScene(x: number | SPoint, y?: number): SPoint {
  184. if (x instanceof SPoint) {
  185. // 如果传入的是SPoint对象
  186. return new SPoint(
  187. (x.x - this.origin.x) / this.scale,
  188. (x.y - this.origin.y) / this.scale
  189. );
  190. }
  191. return new SPoint(
  192. (x - this.origin.x) / this.scale,
  193. ((y == undefined ? 0 : y) - this.origin.y) / this.scale
  194. );
  195. } // Function mapToScene()
  196. /**
  197. * 绘制视图
  198. *
  199. * @param painter painter对象
  200. */
  201. protected onDraw(painter: SPainter): void {
  202. painter.save();
  203. painter.clearRect(0, 0, this.width, this.height);
  204. painter.restore();
  205. // 如果未设备场景
  206. if (this.scene == null) {
  207. return;
  208. }
  209. // 绘制背景
  210. painter.save();
  211. this.drawBackground(painter);
  212. painter.restore();
  213. // 绘制场景
  214. painter.save();
  215. painter.translate(this.origin.x, this.origin.y);
  216. painter.scale(this.scale, this.scale);
  217. this.scene.drawScene(painter, new SRect());
  218. painter.restore();
  219. // 绘制前景
  220. painter.save();
  221. this.drawForeground(painter);
  222. painter.restore();
  223. } // Function onDraw();
  224. /**
  225. * 绘制场景背景
  226. *
  227. * @param painter painter对象
  228. */
  229. protected drawBackground(painter: SPainter): void {
  230. // DO NOTHING
  231. } // Function drawBackground()
  232. /**
  233. * 绘制场景前景
  234. *
  235. * @param painter painter对象
  236. */
  237. protected drawForeground(painter: SPainter): void {
  238. // DO NOTHING
  239. } // Function drawForeground()
  240. /**
  241. * 鼠标双击事件
  242. *
  243. * @param event 事件参数
  244. */
  245. protected onDoubleClick(event: MouseEvent): void {
  246. if (this.scene != null) {
  247. let ce = this.toSceneMotionEvent(event);
  248. this.scene.onDoubleClick(ce);
  249. }
  250. } // Function onClick()
  251. /**
  252. * 鼠标按下事件
  253. *
  254. * @param event 事件参数
  255. */
  256. protected onMouseDown(event: MouseEvent): void {
  257. super.onMouseDown(event);
  258. if (this.scene != null) {
  259. let ce = this.toSceneMotionEvent(event);
  260. this.scene.onMouseDown(ce);
  261. }
  262. } // Function onClick()
  263. /**
  264. * 鼠标移动事件
  265. *
  266. * @param event 事件参数
  267. */
  268. protected onMouseMove(event: MouseEvent): void {
  269. super.onMouseMove(event);
  270. if (this.scene != null) {
  271. let ce = this.toSceneMotionEvent(event);
  272. this.scene.onMouseMove(ce);
  273. }
  274. } // Function onClick()
  275. /**
  276. * 鼠标松开事件
  277. *
  278. * @param event 事件参数
  279. */
  280. protected onMouseUp(event: MouseEvent): void {
  281. super.onMouseUp(event);
  282. if (this.scene != null) {
  283. let ce = this.toSceneMotionEvent(event);
  284. this.scene.onMouseUp(ce);
  285. }
  286. } // Function onClick()
  287. /**
  288. * 上下文菜单事件
  289. *
  290. * @param event 事件参数
  291. */
  292. protected onContextMenu(event: MouseEvent): void {
  293. if (this.scene != null) {
  294. let ce = this.toSceneMotionEvent(event);
  295. this.scene.onContextMenu(ce);
  296. }
  297. } // Function onContextMenu()
  298. /**
  299. * 按键按下事件
  300. *
  301. * @param event 事件参数
  302. */
  303. protected onKeyDown(event: KeyboardEvent): void {
  304. if (this.scene != null) {
  305. this.scene.onKeyDown(event);
  306. }
  307. } // Function onKeyDown()
  308. /**
  309. * 按键松开事件
  310. *
  311. * @param event 事件参数
  312. */
  313. protected onKeyUp(event: KeyboardEvent): void {
  314. if (this.scene != null) {
  315. this.scene.onKeyUp(event);
  316. }
  317. } // Function onKeyUp()
  318. /**
  319. * 适配场景在视图中可见
  320. *
  321. * @param width 宽度
  322. * @param height 高度
  323. * @param rect 对象的矩阵大小
  324. */
  325. private fitRectToSize(
  326. width: number,
  327. height: number,
  328. rect: SRect | null
  329. ): void {
  330. // 未设置场景
  331. if (null == rect || !rect.isValid()) {
  332. return;
  333. }
  334. this.scale = Math.min(width / rect.width, height / rect.height) * 0.8;
  335. // 计算场景中心点
  336. let center = rect.center();
  337. this.origin.x = width / 2.0 - center.x * this.scale;
  338. this.origin.y = height / 2.0 - center.y * this.scale;
  339. } // Function fitRectToSize()
  340. /**
  341. * MouseEvent事件转换成场景SMouseEvent事件
  342. *
  343. * @param event 事件参数
  344. */
  345. private toSceneMotionEvent(event: MouseEvent): SMouseEvent {
  346. let se = new SMouseEvent(event);
  347. se.matrix.translateSelf(this.origin.x, this.origin.y);
  348. se.matrix.scaleSelf(this.scale, this.scale);
  349. const mp = new DOMPoint(event.offsetX, event.offsetY).matrixTransform(
  350. se.matrix.inverse()
  351. );
  352. se.x = mp.x;
  353. se.y = mp.y;
  354. return se;
  355. } // Function toSceneMotionEvent()
  356. } // Class SGraphyView