SPolygonItem.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. /*
  2. * ********************************************************************************************************************
  3. *
  4. * :*$@@%$*: ;: ;; ;;
  5. * :@@%! :!@@%: %! ;%%@@%$ =@@@@@@@%; @%@@@%%%%@@@@@
  6. * :@%; :$= %%$$$%$$ ;$$ ;$@= !@$
  7. * =@! %! @ $=;% !@@@%: !$$$$$$$$$$$$$$=
  8. * =@* %! @ $= % %@= =%@! %=
  9. * *$%%! @@= ;=$%%%$*: %! @ $= % =%%%%%%@$ *%: =%
  10. * %@@!: !@@@%=$@@@@%! :*@@$: %! @ $= % $* ;@ @* :%*
  11. * ;@@! ;!!!;: ;@%: =======@%========* @ $$ % $%*****$@ :@$=*********=@$
  12. * $@* ;@@@%=!: *@*
  13. * =@$ ;;;!=%@@@@=! =@!
  14. * %@$: =@%: :*@@@* %@= Copyright (c) 2016-2019. 北京上格云技术有限公司
  15. * ;%@@$=$@@%* *@@@$=%@@%;
  16. * ::;:: ::;:: All rights reserved.
  17. *
  18. * ********************************************************************************************************************
  19. */
  20. import { SGraphItem, SGraphPointListInsert, SGraphPointListDelete, SGraphPointListUpdate } from "@saga-web/graph/lib";
  21. import { SUndoStack } from "@saga-web/base/";
  22. ;
  23. import { SColor, SLineCapStyle, SPoint, SRect, SLine, SPolygonUtil } from "@saga-web/draw";
  24. import { SMathUtil } from "@saga-web/big/lib/utils/SMathUtil";
  25. import { SItemStatus } from "@saga-web/big";
  26. /**
  27. * 编辑多边形
  28. *
  29. * @author 韩耀龙
  30. */
  31. export class SPolygonItem extends SGraphItem {
  32. /**
  33. * 构造函数
  34. *
  35. * @param parent 指向父对象
  36. * @param data PolygonData数据
  37. */
  38. constructor(parent) {
  39. super(parent);
  40. /** X坐标最小值 */
  41. this.minX = Number.MAX_SAFE_INTEGER;
  42. /** X坐标最大值 */
  43. this.maxX = Number.MIN_SAFE_INTEGER;
  44. /** Y坐标最小值 */
  45. this.minY = Number.MAX_SAFE_INTEGER;
  46. /** Y坐标最大值 */
  47. this.maxY = Number.MIN_SAFE_INTEGER;
  48. /** 轮廓线坐标 */
  49. this.pointList = [];
  50. /** 是否闭合 */
  51. this.closeFlag = false;
  52. // 当前状态
  53. this._status = SItemStatus.Normal;
  54. this.data = null;
  55. /** 边框颜色 */
  56. this._strokeColor = new SColor("#0091FF");
  57. /** 填充颜色 */
  58. this._fillColor = new SColor("#1EE887");
  59. /** 边框的宽 只可输入像素宽*/
  60. this._lineWidth = 4;
  61. /** 鼠标移动点 */
  62. this.lastPoint = null;
  63. /** 当前鼠标获取顶点对应索引 */
  64. this.curIndex = -1;
  65. /** 当前鼠标获取顶点对应坐标 */
  66. this.curPoint = null;
  67. /** 灵敏像素 */
  68. this.len = 10;
  69. /** 场景像素 内部将灵敏像素换算为场景实际距离 */
  70. this.scenceLen = 15;
  71. /** 场景像素 */
  72. this.isAlt = false;
  73. /** undoredo堆栈 */
  74. this.undoStack = new SUndoStack();
  75. }
  76. // 获取当前状态
  77. get getPointList() {
  78. return this.pointList;
  79. }
  80. // 编辑当前状态
  81. set setPointList(arr) {
  82. this.pointList = arr;
  83. this.update();
  84. }
  85. ;
  86. // 获取当前状态
  87. get status() {
  88. return this._status;
  89. }
  90. // 编辑当前状态
  91. set status(value) {
  92. this._status = value;
  93. // 如果状态为show则需清栈
  94. if (value == SItemStatus.Normal) {
  95. if (this.undoStack) {
  96. this.undoStack.clear();
  97. }
  98. }
  99. this.update();
  100. }
  101. ;
  102. /** 画笔颜色 */
  103. get strokeColor() {
  104. return this._strokeColor;
  105. }
  106. ;
  107. set strokeColor(v) {
  108. this._strokeColor = v;
  109. }
  110. get fillColor() {
  111. return this._fillColor;
  112. }
  113. ;
  114. set fillColor(v) {
  115. this._fillColor = v;
  116. }
  117. get lineWidth() {
  118. return this._lineWidth;
  119. }
  120. set lineWidth(v) {
  121. this._lineWidth = v;
  122. this.update();
  123. }
  124. ;
  125. //////////////////
  126. // 以下为对pointList 数组的操作方法
  127. /**
  128. * 储存新的多边形顶点
  129. *
  130. * @param x 点位得x坐标
  131. * @param y 点位得y坐标
  132. * @param i 储存所在索引
  133. * @return SPoint。添加的顶点
  134. */
  135. insertPoint(x, y, i = null) {
  136. const point = new SPoint(x, y);
  137. if (i == null) {
  138. this.pointList.push(point);
  139. }
  140. else {
  141. this.pointList.splice(i, 0, point);
  142. }
  143. this.update();
  144. return point;
  145. }
  146. /**
  147. * 删除点位
  148. *
  149. * @param i 删除点所在的索引
  150. * @return SPoint|null。索引不在数组范围则返回null
  151. */
  152. deletePoint(i = null) {
  153. let point = null;
  154. if (i != null) {
  155. if (i >= (this.pointList.length) || i < 0) {
  156. point = null;
  157. }
  158. else {
  159. point = new SPoint(this.pointList[i].x, this.pointList[i].y);
  160. this.pointList.splice(i, 1);
  161. }
  162. }
  163. else {
  164. if (this.pointList.length) {
  165. point = this.pointList[this.pointList.length - 1];
  166. this.pointList.pop();
  167. }
  168. else {
  169. point = null;
  170. }
  171. }
  172. this.update();
  173. return point;
  174. }
  175. /**
  176. * 多边形顶点的移动位置
  177. *
  178. * @param x 点位得x坐标
  179. * @param y 点位得y坐标
  180. * @param i 点位得i坐标
  181. * @return 移动对应得点。如果索引无法找到移动顶点,则返回null
  182. */
  183. movePoint(x, y, i) {
  184. let point = null;
  185. if (i >= (this.pointList.length) || i < 0) {
  186. return null;
  187. }
  188. if (this.pointList.length) {
  189. this.pointList[i].x = x;
  190. this.pointList[i].y = y;
  191. }
  192. point = this.pointList[i];
  193. return point;
  194. }
  195. /**
  196. * 打印出多边形数组
  197. *
  198. * @return 顶点数组
  199. */
  200. PrintPointList() {
  201. return this.pointList;
  202. }
  203. ////////////
  204. // 以下为三种状态下的绘制多边形方法
  205. /**
  206. * 展示状态 --绘制多边形数组
  207. *
  208. * @param painter 绘制类
  209. * @param pointList 绘制多边形数组
  210. */
  211. drawShowPolygon(painter, pointList) {
  212. painter.pen.lineCapStyle = SLineCapStyle.Square;
  213. painter.pen.color = this._strokeColor;
  214. painter.pen.lineWidth = painter.toPx(this._lineWidth);
  215. painter.brush.color = this._fillColor;
  216. painter.drawPolygon([...pointList]);
  217. }
  218. /**
  219. * 创建状态 --绘制多边形数组
  220. *
  221. * @param painter 绘制类
  222. * @param pointList 绘制多边形数组
  223. */
  224. drawCreatePolygon(painter, pointList) {
  225. painter.pen.lineCapStyle = SLineCapStyle.Square;
  226. painter.pen.color = this._strokeColor;
  227. painter.pen.lineWidth = painter.toPx(this._lineWidth);
  228. if (this.lastPoint && pointList.length) {
  229. painter.drawLine(pointList[pointList.length - 1].x, pointList[pointList.length - 1].y, this.lastPoint.x, this.lastPoint.y);
  230. }
  231. painter.drawPolyline(pointList);
  232. painter.pen.color = SColor.Transparent;
  233. painter.brush.color = this._fillColor;
  234. painter.pen.lineWidth = painter.toPx(this._lineWidth);
  235. if (this.lastPoint) {
  236. painter.drawPolygon([...pointList, this.lastPoint]);
  237. }
  238. else {
  239. painter.drawPolygon(pointList);
  240. }
  241. }
  242. /**
  243. *
  244. * 编辑状态 --绘制多边形数组
  245. *
  246. * @param painter 绘制类
  247. * @param pointList 绘制多边形数组
  248. */
  249. drawEditPolygon(painter, pointList) {
  250. // 展示多边形
  251. this.drawShowPolygon(painter, pointList);
  252. // 绘制顶点块
  253. painter.pen.color = SColor.Black;
  254. painter.brush.color = SColor.White;
  255. pointList.forEach((item, index) => {
  256. painter.drawCircle(item.x, item.y, painter.toPx(this.len / 2));
  257. });
  258. }
  259. /**
  260. * 编辑状态操作多边形数组
  261. *
  262. * @param event 鼠标事件
  263. *
  264. *
  265. */
  266. editPolygonPoint(event) {
  267. // 判断是否为删除状态 isAlt = true为删除状态
  268. if (this.isAlt) {
  269. // 1 判断是否点击在多边形顶点
  270. let lenIndex = -1; // 当前点击到的点位索引;
  271. let curenLen = this.scenceLen; // 当前的灵敏度
  272. this.pointList.forEach((item, index) => {
  273. let dis = SMathUtil.pointDistance(event.x, event.y, item.x, item.y);
  274. if (dis < curenLen) {
  275. curenLen = dis;
  276. lenIndex = index;
  277. }
  278. });
  279. // 若点击到,对该索引对应的点做删除
  280. if (lenIndex != -1) {
  281. if (this.pointList.length <= 3) {
  282. return;
  283. }
  284. const delePoint = new SPoint(this.pointList[lenIndex].x, this.pointList[lenIndex].y);
  285. this.deletePoint(lenIndex);
  286. // 记录顶点操作记录压入堆栈
  287. this.recordAction(SGraphPointListDelete, [this.pointList, delePoint, lenIndex]);
  288. }
  289. }
  290. else {
  291. // 1 判断是否点击在多边形顶点
  292. this.curIndex = -1;
  293. this.curPoint = null;
  294. let lenIndex = -1; // 当前点击到的点位索引;
  295. let curenLen = this.scenceLen; // 当前的灵敏度
  296. this.pointList.forEach((item, index) => {
  297. let dis = SMathUtil.pointDistance(event.x, event.y, item.x, item.y);
  298. if (dis < curenLen) {
  299. curenLen = dis;
  300. lenIndex = index;
  301. }
  302. });
  303. this.curIndex = lenIndex;
  304. // 2判断是否点击在多边形得边上
  305. if (-1 == lenIndex) {
  306. let len = SMathUtil.pointToLine(new SPoint(event.x, event.y), new SLine(this.pointList[0], this.pointList[1])), index = 0;
  307. if (this.pointList.length > 2) {
  308. for (let i = 1; i < this.pointList.length; i++) {
  309. let dis = SMathUtil.pointToLine(new SPoint(event.x, event.y), new SLine(this.pointList[i], this.pointList[i + 1]));
  310. if ((i + 1) == this.pointList.length) {
  311. dis = SMathUtil.pointToLine(new SPoint(event.x, event.y), new SLine(this.pointList[i], this.pointList[0]));
  312. }
  313. if (dis.MinDis < len.MinDis) {
  314. len = dis;
  315. index = i;
  316. }
  317. }
  318. }
  319. // 判断是否有点
  320. if (len.Point) {
  321. console.log(index, len.Point);
  322. // 点在了多边形的边上
  323. if (len.MinDis <= this.scenceLen) {
  324. this.pointList.splice(index + 1, 0, len.Point);
  325. // 记录新增顶点操作记录压入堆栈
  326. this.recordAction(SGraphPointListInsert, [this.pointList, len.Point, index + 1]);
  327. }
  328. else { //没点在多边形边上也没点在多边形顶点上
  329. super.onMouseDown(event);
  330. }
  331. }
  332. }
  333. else {
  334. // 当捕捉到顶点后 ,记录当前点的xy坐标,用于undo、redo操作
  335. this.curPoint = new SPoint(this.pointList[this.curIndex].x, this.pointList[this.curIndex].y);
  336. }
  337. // 刷新视图
  338. this.update();
  339. }
  340. }
  341. /////////////////////
  342. // undo、redo相关操作
  343. /**
  344. * 记录相关动作并推入栈中
  345. * @param SGraphCommand 相关命令类
  346. * @param any 对应传入参数
  347. */
  348. recordAction(SGraphCommand, any) {
  349. // 记录相关命令并推入堆栈中
  350. const sgraphcommand = new SGraphCommand(this, ...any);
  351. if (this.undoStack) {
  352. this.undoStack.push(sgraphcommand);
  353. }
  354. }
  355. /**
  356. * 执行取消操作执行
  357. */
  358. undo() {
  359. if (this.status == SItemStatus.Normal) {
  360. return;
  361. }
  362. if (this.undoStack) {
  363. this.undoStack.undo();
  364. }
  365. }
  366. /**
  367. * 执行重做操作执行
  368. */
  369. redo() {
  370. if (this.status == SItemStatus.Normal) {
  371. return;
  372. }
  373. if (this.undoStack) {
  374. this.undoStack.redo();
  375. }
  376. }
  377. ///////////////////////////////
  378. // 以下为鼠标事件
  379. /**
  380. * 鼠标双击事件
  381. *
  382. * @param event 事件参数
  383. * @return boolean
  384. */
  385. onDoubleClick(event) {
  386. // 如果位show状态 双击改对象则需改为编辑状态
  387. if (SItemStatus.Normal == this.status) {
  388. this.status = SItemStatus.Edit;
  389. this.grabItem(this);
  390. }
  391. else if (SItemStatus.Edit == this.status) {
  392. this.status = SItemStatus.Normal;
  393. this.releaseItem();
  394. if (this.undoStack) {
  395. this.undoStack.clear();
  396. }
  397. }
  398. this.update();
  399. return true;
  400. } // Function onDoubleClick()
  401. /**
  402. * 键盘事件
  403. *
  404. * @param event 事件参数
  405. * @return boolean
  406. */
  407. onKeyDown(event) {
  408. if (this.status == SItemStatus.Normal) {
  409. return false;
  410. }
  411. else if (this.status == SItemStatus.Create) {
  412. if (event.code == 'Enter') {
  413. // 当顶点大于二个时才又条件执行闭合操作并清空堆栈
  414. if (this.pointList.length > 2) {
  415. this.status = SItemStatus.Normal;
  416. //3 传递完成事件状态
  417. this.$emit('finishCreated');
  418. //1 grabItem 置为null
  419. this.releaseItem();
  420. //2 清空undo
  421. if (this.undoStack) {
  422. this.undoStack.clear();
  423. }
  424. }
  425. }
  426. }
  427. else if (this.status == SItemStatus.Edit) {
  428. if (event.key == 'Alt') {
  429. this.isAlt = true;
  430. }
  431. }
  432. this.update();
  433. return true;
  434. } // Function onKeyDown()
  435. /**
  436. * 键盘键抬起事件
  437. *
  438. * @param event 事件参数
  439. * @return boolean
  440. */
  441. onKeyUp(event) {
  442. if (this.status == SItemStatus.Edit) {
  443. if (event.key == 'Alt') {
  444. this.isAlt = false;
  445. }
  446. }
  447. this.update();
  448. } // Function onKeyUp()
  449. /**
  450. * 鼠标按下事件
  451. *
  452. * @param event 事件参数
  453. * @return boolean
  454. */
  455. onMouseDown(event) {
  456. // 如果状态为编辑状态则添加点
  457. if (this.status == SItemStatus.Create) {
  458. // 新增顶点
  459. this.insertPoint(event.x, event.y);
  460. // 记录新增顶点操作记录压入堆栈
  461. let pos = new SPoint(event.x, event.y);
  462. this.recordAction(SGraphPointListInsert, [this.pointList, pos]);
  463. }
  464. else if (this.status == SItemStatus.Edit) {
  465. // 对多边形数组做编辑操作
  466. this.editPolygonPoint(event);
  467. }
  468. else {
  469. return super.onMouseDown(event);
  470. }
  471. return true;
  472. } // Function onMouseDown()
  473. /**
  474. * 鼠标移入事件
  475. *
  476. * @param event 事件参数
  477. * @return boolean
  478. */
  479. onMouseEnter(event) {
  480. return true;
  481. } // Function onMouseEnter()
  482. /**
  483. * 鼠标移出事件
  484. *
  485. * @param event 事件参数
  486. * @return boolean
  487. */
  488. onMouseLeave(event) {
  489. return true;
  490. } // Function onMouseLeave()
  491. /**
  492. * 鼠标移动事件
  493. *
  494. * @param event 事件参数
  495. * @return boolean
  496. */
  497. onMouseMove(event) {
  498. if (this.status == SItemStatus.Create) {
  499. this.lastPoint = new SPoint;
  500. this.lastPoint.x = event.x;
  501. this.lastPoint.y = event.y;
  502. this.update();
  503. }
  504. else if (this.status == SItemStatus.Edit) {
  505. if (-1 != this.curIndex) {
  506. this.pointList[this.curIndex].x = event.x;
  507. this.pointList[this.curIndex].y = event.y;
  508. }
  509. this.update();
  510. }
  511. else {
  512. // return super.onMouseMove(event)
  513. }
  514. return true;
  515. } // Function onMouseMove()
  516. /**
  517. * 鼠标抬起事件
  518. *
  519. * @param event 事件参数
  520. * @return boolean
  521. */
  522. onMouseUp(event) {
  523. if (this.status == SItemStatus.Edit) {
  524. if (-1 != this.curIndex) {
  525. const pos = new SPoint(this.pointList[this.curIndex].x, this.pointList[this.curIndex].y);
  526. this.recordAction(SGraphPointListUpdate, [this.pointList, this.curPoint, pos, this.curIndex]);
  527. }
  528. this.curIndex = -1;
  529. this.curPoint = null;
  530. }
  531. else {
  532. // return super.onMouseUp(event)
  533. }
  534. return true;
  535. } // Function onMouseUp()
  536. /**
  537. * 适配事件
  538. *
  539. * @param event 事件参数
  540. * @return boolean
  541. */
  542. onResize(event) {
  543. return true;
  544. } // Function onResize()
  545. /**
  546. * 取消操作
  547. *
  548. * @param painter painter对象
  549. */
  550. cancelOperate() {
  551. // 当状态为展示状态
  552. if (this.status == SItemStatus.Create) {
  553. // 闭合多边形
  554. this.parent = null;
  555. }
  556. else if (this.status == SItemStatus.Edit) {
  557. // 编辑状态
  558. this.status = SItemStatus.Normal;
  559. }
  560. this.update();
  561. } // Function cancelOperate()
  562. /**
  563. * Item对象边界区域
  564. *
  565. * @return SRect
  566. */
  567. boundingRect() {
  568. if (this.pointList.length) {
  569. this.minX = this.pointList[0].x;
  570. this.maxX = this.pointList[0].x;
  571. this.minY = this.pointList[0].y;
  572. this.maxY = this.pointList[0].y;
  573. this.pointList.forEach(it => {
  574. let x = it.x, y = it.y;
  575. if (x < this.minX) {
  576. this.minX = x;
  577. }
  578. if (y < this.minY) {
  579. this.minY = y;
  580. }
  581. if (x > this.maxX) {
  582. this.maxX = x;
  583. }
  584. if (y > this.maxY) {
  585. this.maxY = y;
  586. }
  587. });
  588. }
  589. ;
  590. return new SRect(this.minX, this.minY, this.maxX - this.minX, this.maxY - this.minY);
  591. } // Function boundingRect()
  592. /**
  593. * 判断点是否在区域内
  594. *
  595. * @param x
  596. * @param y
  597. */
  598. contains(x, y) {
  599. let arr = this.pointList;
  600. if (arr.length < 3 || !SPolygonUtil.pointIn(x, y, arr)) {
  601. return false;
  602. }
  603. return true;
  604. } // Function contains()
  605. /**
  606. * Item绘制操作
  607. *
  608. * @param painter painter对象
  609. */
  610. onDraw(painter) {
  611. this.scenceLen = painter.toPx(this.len);
  612. // 当状态为展示状态
  613. if (this.status == SItemStatus.Normal) {
  614. // 闭合多边形
  615. this.drawShowPolygon(painter, this.pointList);
  616. }
  617. else if (this.status == SItemStatus.Create) {
  618. // 创建状态
  619. this.drawCreatePolygon(painter, this.pointList);
  620. }
  621. else {
  622. // 编辑状态
  623. this.drawEditPolygon(painter, this.pointList);
  624. }
  625. } // Function onDraw()
  626. }