SBaseLineEdit.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. /*
  2. * *********************************************************************************************************************
  3. *
  4. * !!
  5. * .F88X
  6. * X8888Y
  7. * .}888888N;
  8. * i888888N; .:! .I$WI:
  9. * R888888I .'N88~ i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
  10. * .R888888I .;N8888~ .X8' "8I.!,/8" !%NY8`"8I8~~8>,88I
  11. * +888888N; .8888888Y "&&8Y.}8,
  12. * ./888888N; .R888888Y .'}~ .>}'.`+> i}! "i' +/' .'i~ !11,.:">, .~]! .i}i
  13. * ~888888%: .I888888l .]88~`1/iY88Ii+1'.R$8$8]"888888888> Y8$ W8E X8E W8888'188Il}Y88$*
  14. * 18888888 E8888881 .]W%8$`R8X'&8%++N8i,8N%N8+l8%` .}8N:.R$RE%N88N%N$K$R 188,FE$8%~Y88I
  15. * .E888888I .i8888888' .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
  16. * 8888888I .,N888888~ ~88i"8W,!N8*.I88.}888%F,i$88"F88" 888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
  17. * i888888N' I888Y ]88;/EX*IFKFK88X K8R .l8W 88Y ~88}'88E&%8W.X8N``]88!.$8K .:W8I
  18. * .i888888N; I8Y .&8$ .X88! i881.:%888>I88 ;88] +88+.';;;;:.Y88X 18N.,88l .+88/
  19. * .:R888888I
  20. * .&888888I Copyright (c) 2016-2020. 博锐尚格科技股份有限公司
  21. * ~8888'
  22. * .!88~ All rights reserved.
  23. *
  24. * *********************************************************************************************************************
  25. */
  26. import { SColor, SLine, SPainter, SPoint, SRect } from "@persagy-web/draw/";
  27. import { SMouseButton, SMouseEvent, SUndoStack } from "@persagy-web/base";
  28. import { SMathUtil } from "@persagy-web/big/lib/utils/SMathUtil";
  29. import { SItemStatus } from "@persagy-web/big";
  30. import {
  31. SGraphPointListInsert,
  32. SGraphPointListUpdate,
  33. SLineStyle,
  34. SGraphItem
  35. } from "@persagy-web/graph/";
  36. import { SGraphEdit } from ".."
  37. import { Marker } from "../type/Marker";
  38. /**
  39. * 直线编辑类
  40. *
  41. * @author 韩耀龙 <han_yao_long@163.com>
  42. */
  43. export class SBaseLineEdit extends SGraphEdit {
  44. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  45. //属性
  46. /** 编辑相关操作的数据 */
  47. data: Marker
  48. /** 起始锚点 */
  49. startItem: SGraphItem | null = null;
  50. /** 结束锚点 */
  51. endItem: SGraphItem | null = null;
  52. /** X 坐标最小值 */
  53. private minX = Number.MAX_SAFE_INTEGER;
  54. /** X 坐标最大值 */
  55. private maxX = Number.MIN_SAFE_INTEGER;
  56. /** Y 坐标最小值 */
  57. private minY = Number.MAX_SAFE_INTEGER;
  58. /** Y 坐标最大值 */
  59. private maxY = Number.MIN_SAFE_INTEGER;
  60. /** 线段 */
  61. private _line: SPoint[] = [];
  62. get line(): SPoint[] {
  63. return this._line;
  64. } // Get line
  65. set line(arr: SPoint[]) {
  66. this._line = arr;
  67. this.update();
  68. } // Set line
  69. /** 是否垂直水平绘制 */
  70. private _verAndLeve: Boolean = false;
  71. get verAndLeve(): Boolean {
  72. return this._verAndLeve;
  73. } // Get verAndLeve
  74. set verAndLeve(bool: Boolean) {
  75. this._verAndLeve = bool;
  76. this.update();
  77. } // Set verAndLeve
  78. /** 是否完成绘制 */
  79. protected _status: SItemStatus = SItemStatus.Normal;
  80. get status(): SItemStatus {
  81. return this._status;
  82. } // Get status
  83. set status(v: SItemStatus) {
  84. this._status = v;
  85. this.undoStack.clear();
  86. this.update();
  87. } // Set status
  88. /** 线条颜色 */
  89. private _strokeColor: SColor = SColor.Black;
  90. get strokeColor(): SColor {
  91. return this._strokeColor;
  92. } // Get strokeColor
  93. set strokeColor(v: SColor) {
  94. this._strokeColor = v;
  95. this.update();
  96. }// Get strokeColor
  97. /** 线条样式 */
  98. private _lineStyle: SLineStyle = SLineStyle.Solid;
  99. get lineStyle(): SLineStyle {
  100. return this._lineStyle;
  101. } // Get lineStyle
  102. set lineStyle(v: SLineStyle) {
  103. this._lineStyle = v;
  104. this.update();
  105. } // Set lineStyle
  106. /** 端点填充色 */
  107. private _fillColor: SColor = SColor.White;
  108. get fillColor(): SColor {
  109. return this._fillColor;
  110. } // Get fillColor
  111. set fillColor(v: SColor) {
  112. this._fillColor = v;
  113. this.update();
  114. } //Set fillColor
  115. /** 选中端点填充色 */
  116. private _activeFillColor: SColor = new SColor("#2196f3");
  117. get activeFillColor(): SColor {
  118. return this._activeFillColor;
  119. } //Get activeFillColor
  120. set activeFillColor(v: SColor) {
  121. this._activeFillColor = v;
  122. this.update();
  123. } //Set activeFillColor
  124. /** 线条宽度 */
  125. private _lineWidth: number = 1;
  126. get lineWidth(): number {
  127. return this._lineWidth;
  128. } //Get lineWidth
  129. set lineWidth(v: number) {
  130. this._lineWidth = v;
  131. this.update();
  132. } //Set lineWidth
  133. /** 拖动灵敏度 */
  134. dis: number = 5;
  135. /** 拖动灵敏度 */
  136. protected sceneDis: number = 5;
  137. /** 当前点索引 */
  138. curIndex: number = -1;
  139. /** 当前点坐标 */
  140. private curPoint: SPoint | null = null;
  141. /** undo/redo 堆栈 */
  142. private undoStack: SUndoStack = new SUndoStack();
  143. /**
  144. * 构造函数
  145. *
  146. * @param parent 父级
  147. * @param data 坐标集合|第一个点坐标
  148. */
  149. constructor(parent: SGraphItem | null, data: Marker) {
  150. super(parent);
  151. this.showSelect = false;
  152. this.data = data;
  153. if (data.style && data.style.default) {
  154. // 关于顶点
  155. if (data.style.default.line) {
  156. let setPointList: SPoint[];
  157. setPointList = data.style.default.line.map((i: { x: number; y: number; }) => {
  158. return new SPoint(i.x, i.y)
  159. });
  160. this.line = setPointList;
  161. }
  162. // 颜色
  163. if (data.style.default.strokeColor) {
  164. this.strokeColor = new SColor(data.style.default.strokeColor)
  165. }
  166. // 颜色
  167. if (data.style.default.fillColor) {
  168. this.fillColor = new SColor(data.style.default.fillColor)
  169. }
  170. // 线宽
  171. if (data.style.default.lineWidth) {
  172. this.lineWidth = data.style.default.lineWidth
  173. }
  174. // 线样式
  175. if (data.style.default.lineStyle) {
  176. this.lineStyle = data.style.default.lineStyle
  177. }
  178. }
  179. }
  180. /**
  181. * 添加点至数组中
  182. *
  183. * @param p 添加的点
  184. */
  185. private addPoint(p: SPoint): void {
  186. if (this.line.length < 2) {
  187. this.line.push(p);
  188. this.recordAction(SGraphPointListInsert, [this.line, p]);
  189. } else {
  190. this.line[1] = p;
  191. this.recordAction(SGraphPointListInsert, [this.line, p, 1]);
  192. this.status = SItemStatus.Normal;
  193. this.releaseItem();
  194. this.$emit("finishCreated");
  195. this.pointChange();
  196. }
  197. this.update();
  198. } // Function addPoint()
  199. /**
  200. * 鼠标双击事件
  201. *
  202. * @param event 事件参数
  203. * @return 该事件是否被处理
  204. */
  205. onDoubleClick(event: SMouseEvent): boolean {
  206. if (this.status == SItemStatus.Normal) {
  207. this.status = SItemStatus.Edit;
  208. this.grabItem(this);
  209. } else if (this.status == SItemStatus.Edit) {
  210. this.status = SItemStatus.Normal;
  211. this.releaseItem();
  212. }
  213. this.update();
  214. return true;
  215. } // Function onDoubleClick()
  216. /**
  217. * 鼠标按下事件
  218. *
  219. * @param event 鼠标事件
  220. * @return 是否处理事件
  221. */
  222. onMouseDown(event: SMouseEvent): boolean {
  223. this.curIndex = -1;
  224. this.curPoint = null;
  225. if (event.shiftKey || this._verAndLeve) {
  226. event = this.compare(event);
  227. }
  228. if (event.buttons == SMouseButton.LeftButton) {
  229. if (this.status == SItemStatus.Normal) {
  230. // return super.onMouseDown(event);
  231. super.onMouseDown(event);
  232. return true;
  233. } else if (this.status == SItemStatus.Edit) {
  234. // 判断是否点击到端点上(获取端点索引值)
  235. this.findNearestPoint(new SPoint(event.x, event.y));
  236. } else if (this.status == SItemStatus.Create) {
  237. this.addPoint(new SPoint(event.x, event.y));
  238. return true;
  239. }
  240. }
  241. return true;
  242. } // Function onMouseDown()
  243. /**
  244. * 鼠标抬起事件
  245. *
  246. * @param event 事件参数
  247. * @return 是否处理该事件
  248. */
  249. onMouseUp(event: SMouseEvent): boolean {
  250. if (this.status == SItemStatus.Edit) {
  251. if (this.curIndex > -1) {
  252. const p = new SPoint(
  253. this.line[this.curIndex].x,
  254. this.line[this.curIndex].y
  255. );
  256. this.recordAction(SGraphPointListUpdate, [
  257. this.line,
  258. this.curPoint,
  259. p,
  260. this.curIndex
  261. ]);
  262. }
  263. } else if (this.status == SItemStatus.Normal) {
  264. super.onMouseUp(event);
  265. return true
  266. }
  267. this.curIndex = -1;
  268. this.curPoint = null;
  269. return true;
  270. } // Function onMouseUp()
  271. /**
  272. * 鼠标抬起事件
  273. *
  274. * @param event 事件参数
  275. * @return 是否处理该事件
  276. */
  277. onKeyUp(event: KeyboardEvent): void {
  278. // 当 ESC 时
  279. if (27 == event.keyCode) {
  280. if (this.status == SItemStatus.Edit) {
  281. this.status = SItemStatus.Normal;
  282. this.releaseItem();
  283. } else if (this.status == SItemStatus.Create) {
  284. this.update()
  285. this.releaseItem();
  286. this.parent = null;
  287. }
  288. }
  289. } // Function onKeyUp()
  290. /**
  291. * 鼠标移动事件
  292. *
  293. * @param event 鼠标事件
  294. * @return 是否处理事件
  295. */
  296. onMouseMove(event: SMouseEvent): boolean {
  297. if (event.shiftKey || this._verAndLeve) {
  298. event = this.compare(event);
  299. }
  300. if (this.status == SItemStatus.Create) {
  301. if (this.line[0] instanceof SPoint) {
  302. this.line[1] = new SPoint(event.x, event.y);
  303. this.pointChange();
  304. }
  305. } else if (this.status == SItemStatus.Edit) {
  306. if (-1 != this.curIndex) {
  307. this.line[this.curIndex].x = event.x;
  308. this.line[this.curIndex].y = event.y;
  309. this.pointChange();
  310. }
  311. } else {
  312. return super.onMouseMove(event);
  313. }
  314. this.update();
  315. return true;
  316. } // Function onMouseMove()
  317. /**
  318. * 点发生变化
  319. */
  320. protected pointChange(): void {
  321. // do nothing
  322. } // Function pointChange()
  323. /**
  324. * 获取点击点与 Point[] 中的点距离最近点
  325. *
  326. * @param p 鼠标点击点
  327. */
  328. findNearestPoint(p: SPoint): void {
  329. let len = this.sceneDis;
  330. for (let i = 0; i < this.line.length; i++) {
  331. let dis = SMathUtil.pointDistance(
  332. p.x,
  333. p.y,
  334. this.line[i].x,
  335. this.line[i].y
  336. );
  337. if (dis < len) {
  338. len = dis;
  339. this.curIndex = i;
  340. this.curPoint = new SPoint(this.line[this.curIndex]);
  341. }
  342. }
  343. } // Function findNearestPoint()
  344. /**
  345. * 记录相关动作并推入栈中
  346. *
  347. * @param SGraphCommand 相关命令类
  348. * @param any 对应传入参数
  349. */
  350. protected recordAction(SGraphCommand: any, any: any[]): void {
  351. // 记录相关命令并推入堆栈中
  352. const command = new SGraphCommand(this.scene, this, ...any);
  353. this.undoStack.push(command);
  354. } // Function recordAction()
  355. /**
  356. * 移动后处理所有坐标,并肩原点置为场景原点
  357. *
  358. * @param x x 坐标
  359. * @param y y 坐标
  360. */
  361. moveToOrigin(): void {
  362. this.line = this.line.map(t => {
  363. t.x = t.x + this.x;
  364. t.y = t.y + this.y;
  365. return t;
  366. });
  367. this.x = 0;
  368. this.y = 0;
  369. } // Function moveToOrigin()
  370. /**
  371. * shift 垂直水平创建或编辑
  372. *
  373. * @param event 事件
  374. * @return 鼠标事件
  375. */
  376. compare(event: SMouseEvent): SMouseEvent {
  377. if (this.line.length) {
  378. let last = new SPoint(event.x, event.y);
  379. if (this.status == SItemStatus.Create) {
  380. last = this.line[0];
  381. } else if (this.status == SItemStatus.Edit) {
  382. if (this.curIndex == 1) {
  383. last = this.line[0];
  384. } else if (this.curIndex == 0) {
  385. last = this.line[1];
  386. }
  387. }
  388. const dx = Math.abs(event.x - last.x);
  389. const dy = Math.abs(event.y - last.y);
  390. if (dy > dx) {
  391. event.x = last.x;
  392. } else {
  393. event.y = last.y;
  394. }
  395. }
  396. return event;
  397. } // Function compare()
  398. /**
  399. * 判断点是否在区域内
  400. *
  401. * @param x x 坐标
  402. * @param y y 坐标
  403. * @return 是否在区域内
  404. */
  405. contains(x: number, y: number): boolean {
  406. if (this.line.length == 2) {
  407. let p = new SPoint(x, y);
  408. if (
  409. SMathUtil.pointToLine(p, new SLine(this.line[0], this.line[1]))
  410. .MinDis < this.sceneDis
  411. ) {
  412. return true;
  413. }
  414. }
  415. return false;
  416. } // Function contains()
  417. /**
  418. * 撤销操作
  419. */
  420. undo(): void {
  421. if (this.status != SItemStatus.Normal) {
  422. this.undoStack.undo();
  423. }
  424. } // Function undo()
  425. /**
  426. * 重做操作
  427. */
  428. redo(): void {
  429. if (this.status != SItemStatus.Normal) {
  430. this.undoStack.redo();
  431. }
  432. } // Function redo()
  433. /**
  434. * 取消操作 item 事件
  435. */
  436. cancelOperate(): void {
  437. if (this.status == SItemStatus.Create) {
  438. this.parent = null;
  439. this.releaseItem();
  440. } else if (this.status == SItemStatus.Edit) {
  441. this.status = SItemStatus.Normal;
  442. this.releaseItem();
  443. }
  444. } // Function cancelOperate()
  445. /**
  446. * Item 对象边界区域
  447. *
  448. * @return 边界区域
  449. */
  450. boundingRect(): SRect {
  451. if (this.line.length) {
  452. this.minX = this.line[0].x;
  453. this.maxX = this.line[0].x;
  454. this.minY = this.line[0].y;
  455. this.maxY = this.line[0].y;
  456. this.line.forEach(it => {
  457. let x = it.x,
  458. y = it.y;
  459. if (x < this.minX) {
  460. this.minX = x;
  461. }
  462. if (y < this.minY) {
  463. this.minY = y;
  464. }
  465. if (x > this.maxX) {
  466. this.maxX = x;
  467. }
  468. if (y > this.maxY) {
  469. this.maxY = y;
  470. }
  471. });
  472. }
  473. return new SRect(
  474. this.minX,
  475. this.minY,
  476. this.maxX - this.minX,
  477. this.maxY - this.minY
  478. );
  479. } // Function boundingRect()
  480. /**
  481. * 返回对象储存的相关数据
  482. *
  483. * @return 对象数据
  484. */
  485. toData(): any {
  486. this.moveToOrigin()
  487. const Line = [{ x: this.line[0].x, y: this.line[0].y }, { x: this.line[1].x, y: this.line[1].y }];
  488. this.data.style.default.line = Line;
  489. this.data.style.default.lineWidth = this.lineWidth;
  490. this.data.style.default.lineStyle = this.lineStyle;
  491. this.data.style.default.strokeColor = this.strokeColor.value;
  492. this.data.style.default.fillColor = this.fillColor.value;
  493. return this.data
  494. } // Function toData()
  495. /**
  496. * Item 绘制操作
  497. *
  498. * @param painter 绘制对象
  499. */
  500. onDraw(painter: SPainter): void {
  501. this.sceneDis = painter.toPx(this.dis);
  502. painter.pen.lineWidth = painter.toPx(this.lineWidth);
  503. painter.pen.color = this.strokeColor;
  504. if (this.line.length == 2) {
  505. // 绘制直线
  506. painter.pen.color = this.strokeColor;
  507. if (this.lineStyle == SLineStyle.Dashed) {
  508. painter.pen.lineDash = [
  509. painter.toPx(this.lineWidth * 3),
  510. painter.toPx(this.lineWidth * 7)
  511. ];
  512. } else if (this.lineStyle == SLineStyle.Dotted) {
  513. painter.pen.lineDash = [
  514. painter.toPx(2 * this.lineWidth),
  515. painter.toPx(2 * this.lineWidth)
  516. ];
  517. }
  518. if (this.selected && this.status == SItemStatus.Normal) {
  519. painter.pen.lineWidth = painter.toPx(this.lineWidth * 2);
  520. painter.shadow.shadowBlur = 10;
  521. painter.shadow.shadowColor = new SColor(`#00000033`);
  522. painter.shadow.shadowOffsetX = 5;
  523. painter.shadow.shadowOffsetY = 5;
  524. }
  525. painter.drawLine(this.line[0], this.line[1]);
  526. if (
  527. this.status == SItemStatus.Edit ||
  528. this.status == SItemStatus.Create
  529. ) {
  530. // 绘制端点
  531. this.line.forEach((p, i): void => {
  532. painter.brush.color = this.fillColor;
  533. if (i == this.curIndex) {
  534. painter.brush.color = this.activeFillColor;
  535. }
  536. painter.drawCircle(p.x, p.y, painter.toPx(5));
  537. });
  538. }
  539. } else if (this.line.length == 1) {
  540. if (
  541. this.status == SItemStatus.Edit ||
  542. this.status == SItemStatus.Create
  543. ) {
  544. // 绘制端点
  545. painter.brush.color = this.fillColor;
  546. painter.drawCircle(
  547. this.line[0].x,
  548. this.line[0].y,
  549. painter.toPx(5)
  550. );
  551. }
  552. }
  553. } // Function onDraw()
  554. } // Class SBaseLineEdit