SLineItem.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. import { SColor, SLine, SPainter, SPoint, SRect } from "@saga-web/draw/lib";
  2. import { SMouseButton, SMouseEvent, SUndoStack } from "@saga-web/base";
  3. import { SMathUtil } from "../utils/SMathUtil";
  4. import { SItemStatus } from "..";
  5. import {
  6. SGraphItem,
  7. SGraphPointListInsert,
  8. SGraphPointListUpdate,
  9. SLineStyle
  10. } from "@saga-web/graph/lib";
  11. /**
  12. * 直线item
  13. *
  14. * @author 张宇(taohuzy@163.com)
  15. * */
  16. export class SLineItem extends SGraphItem {
  17. /** X坐标最小值 */
  18. private minX = Number.MAX_SAFE_INTEGER;
  19. /** X坐标最大值 */
  20. private maxX = Number.MIN_SAFE_INTEGER;
  21. /** Y坐标最小值 */
  22. private minY = Number.MAX_SAFE_INTEGER;
  23. /** Y坐标最大值 */
  24. private maxY = Number.MIN_SAFE_INTEGER;
  25. /** 线段 */
  26. private _line: SPoint[] = [];
  27. get line(): SPoint[] {
  28. return this._line;
  29. }
  30. set line(arr: SPoint[]) {
  31. this._line = arr;
  32. this.update();
  33. }
  34. /** 是否完成绘制 */
  35. protected _status: SItemStatus = SItemStatus.Normal;
  36. get status(): SItemStatus {
  37. return this._status;
  38. }
  39. set status(v: SItemStatus) {
  40. this._status = v;
  41. this.update();
  42. }
  43. /** 线条颜色 */
  44. private _strokeColor: SColor = SColor.Black;
  45. get strokeColor(): SColor {
  46. return this._strokeColor;
  47. }
  48. set strokeColor(v: SColor) {
  49. this._strokeColor = v;
  50. this.update();
  51. }
  52. /** 线条样式 */
  53. private _lineStyle: SLineStyle = SLineStyle.Soild;
  54. get lineStyle(): SLineStyle {
  55. return this._lineStyle;
  56. }
  57. set lineStyle(v: SLineStyle) {
  58. this._lineStyle = v;
  59. this.update();
  60. }
  61. /** 端点填充色 */
  62. private _fillColor: SColor = SColor.White;
  63. get fillColor(): SColor {
  64. return this._fillColor;
  65. }
  66. set fillColor(v: SColor) {
  67. this._fillColor = v;
  68. this.update();
  69. }
  70. /** 选中端点填充色 */
  71. private _activeFillColor: SColor = new SColor("#2196f3");
  72. get activeFillColor(): SColor {
  73. return this._activeFillColor;
  74. }
  75. set activeFillColor(v: SColor) {
  76. this._activeFillColor = v;
  77. this.update();
  78. }
  79. /** 线条宽度 */
  80. private _lineWidth: number = 1;
  81. get lineWidth(): number {
  82. return this._lineWidth;
  83. }
  84. set lineWidth(v: number) {
  85. this._lineWidth = v;
  86. this.update();
  87. }
  88. /** 拖动灵敏度 */
  89. dis: number = 5;
  90. /** 拖动灵敏度 */
  91. private sceneDis: number = 5;
  92. /** 当前点索引 */
  93. curIndex: number = -1;
  94. /** 当前点坐标 */
  95. private curPoint: SPoint | null = null;
  96. /** undo/redo堆栈 */
  97. private undoStack: SUndoStack | null = new SUndoStack();
  98. /**
  99. * 构造函数
  100. *
  101. * @param parent 父级
  102. * */
  103. constructor(parent: SGraphItem | null);
  104. /**
  105. * 构造函数
  106. *
  107. * @param parent 父级
  108. * @param line 坐标集合
  109. * */
  110. constructor(parent: SGraphItem | null, line: SPoint[]);
  111. /**
  112. * 构造函数
  113. *
  114. * @param parent 父级
  115. * @param point 第一个点坐标
  116. * */
  117. constructor(parent: SGraphItem | null, point: SPoint);
  118. /**
  119. * 构造函数
  120. *
  121. * @param parent 父级
  122. * @param l 坐标集合|第一个点坐标
  123. * */
  124. constructor(parent: SGraphItem | null, l?: SPoint | SPoint[]) {
  125. super(parent);
  126. if (l) {
  127. if (l instanceof SPoint) {
  128. this.line.push(l);
  129. } else {
  130. this.line = l;
  131. }
  132. } else {
  133. this.line = [];
  134. }
  135. }
  136. /**
  137. * 添加点至数组中
  138. *
  139. * @param p 添加的点
  140. * */
  141. private addPoint(p: SPoint): void {
  142. if (this.line.length < 2) {
  143. this.line.push(p);
  144. this.recordAction(SGraphPointListInsert, [this.line, p]);
  145. } else {
  146. this.line[1] = p;
  147. this.recordAction(SGraphPointListInsert, [this.line, p, 1]);
  148. this.status = SItemStatus.Normal;
  149. this.releaseItem();
  150. this.$emit("finishCreated");
  151. }
  152. this.update();
  153. } // Function addPoint()
  154. /**
  155. * 鼠标双击事件
  156. *
  157. * @param event 事件参数
  158. * @return boolean
  159. */
  160. onDoubleClick(event: SMouseEvent): boolean {
  161. if (this.status == SItemStatus.Normal) {
  162. this.status = SItemStatus.Edit;
  163. this.grabItem(this);
  164. } else if (this.status == SItemStatus.Edit) {
  165. this.status = SItemStatus.Normal;
  166. this.releaseItem();
  167. }
  168. this.update();
  169. return true;
  170. } // Function onDoubleClick()
  171. /**
  172. * 鼠标按下事件
  173. *
  174. * @param event 鼠标事件
  175. * @return 是否处理事件
  176. * */
  177. onMouseDown(event: SMouseEvent): boolean {
  178. this.curIndex = -1;
  179. this.curPoint = null;
  180. if (event.buttons == SMouseButton.LeftButton) {
  181. if (this.status == SItemStatus.Normal) {
  182. return super.onMouseDown(event);
  183. } else if (this.status == SItemStatus.Edit) {
  184. // 判断是否点击到端点上(获取端点索引值)
  185. this.findNearestPoint(new SPoint(event.x, event.y));
  186. } else if (this.status == SItemStatus.Create) {
  187. this.addPoint(new SPoint(event.x, event.y));
  188. return true;
  189. }
  190. }
  191. return true;
  192. } // Function onMouseDown()
  193. /**
  194. * 鼠标抬起事件
  195. *
  196. * @param event 事件参数
  197. * @return boolean
  198. */
  199. onMouseUp(event: SMouseEvent): boolean {
  200. if (this.status == SItemStatus.Edit) {
  201. if (this.curIndex > -1) {
  202. const p = new SPoint(
  203. this.line[this.curIndex].x,
  204. this.line[this.curIndex].y
  205. );
  206. this.recordAction(SGraphPointListUpdate, [
  207. this.line,
  208. this.curPoint,
  209. p,
  210. this.curIndex
  211. ]);
  212. }
  213. } else if (this.status == SItemStatus.Normal) {
  214. this.moveToOrigin(this.x, this.y);
  215. return super.onMouseUp(event);
  216. }
  217. this.curIndex = -1;
  218. this.curPoint = null;
  219. return true;
  220. } // Function onMouseUp()
  221. /**
  222. * 鼠标移动事件
  223. *
  224. * @param event 鼠标事件
  225. * @return 是否处理事件
  226. * */
  227. onMouseMove(event: SMouseEvent): boolean {
  228. if (this.status == SItemStatus.Create) {
  229. if (this.line[0] instanceof SPoint) {
  230. this.line[1] = new SPoint(event.x, event.y);
  231. }
  232. } else if (this.status == SItemStatus.Edit) {
  233. if (-1 != this.curIndex) {
  234. this.line[this.curIndex].x = event.x;
  235. this.line[this.curIndex].y = event.y;
  236. }
  237. } else {
  238. return super.onMouseMove(event);
  239. }
  240. this.update();
  241. return true;
  242. } // Function onMouseMove()
  243. /**
  244. * 获取点击点与Point[]中的点距离最近点
  245. *
  246. * @param p 鼠标点击点
  247. * */
  248. findNearestPoint(p: SPoint): void {
  249. let len = this.sceneDis;
  250. for (let i = 0; i < this.line.length; i++) {
  251. let dis = SMathUtil.pointDistance(
  252. p.x,
  253. p.y,
  254. this.line[i].x,
  255. this.line[i].y
  256. );
  257. if (dis < len) {
  258. len = dis;
  259. this.curIndex = i;
  260. this.curPoint = this.line[this.curIndex];
  261. }
  262. }
  263. } // Function findNearestPoint()
  264. /**
  265. * 记录相关动作并推入栈中
  266. *
  267. * @param SGraphCommand 相关命令类
  268. * @param any 对应传入参数
  269. */
  270. protected recordAction(SGraphCommand: any, any: any[]): void {
  271. // 记录相关命令并推入堆栈中
  272. const command = new SGraphCommand(this, ...any);
  273. if (this.undoStack) {
  274. this.undoStack.push(command);
  275. }
  276. } // Function recordAction()
  277. /**
  278. * 移动后处理所有坐标,并肩原点置为场景原点
  279. *
  280. * @param x x坐标
  281. * @param y y坐标
  282. * */
  283. moveToOrigin(x: number, y: number): void {
  284. super.moveToOrigin(x, y);
  285. this.line = this.line.map(t => {
  286. t.x = t.x + x;
  287. t.y = t.y + y;
  288. return t;
  289. });
  290. this.x = 0;
  291. this.y = 0;
  292. } // Function moveToOrigin()
  293. /**
  294. * 判断点是否在区域内
  295. *
  296. * @param x
  297. * @param y
  298. * @return true-是
  299. */
  300. contains(x: number, y: number): boolean {
  301. if (this.line.length == 2) {
  302. let p = new SPoint(x, y);
  303. if (
  304. SMathUtil.pointToLine(p, new SLine(this.line[0], this.line[1]))
  305. .MinDis < this.dis
  306. ) {
  307. return true;
  308. }
  309. }
  310. return false;
  311. } // Function contains()
  312. /**
  313. * 撤销操作
  314. *
  315. */
  316. undo(): void {
  317. if (this.status != SItemStatus.Normal) {
  318. if (this.undoStack) {
  319. this.undoStack.undo();
  320. }
  321. }
  322. } // Function undo()
  323. /**
  324. * 重做操作
  325. *
  326. */
  327. redo(): void {
  328. if (this.status != SItemStatus.Normal) {
  329. if (this.undoStack) {
  330. this.undoStack.redo();
  331. }
  332. }
  333. } // Function redo()
  334. /**
  335. * 取消操作item事件
  336. *
  337. * */
  338. cancelOperate(): void {
  339. if (this.status == SItemStatus.Create) {
  340. this.parent = null;
  341. this.releaseItem();
  342. } else if (this.status == SItemStatus.Edit) {
  343. this.status = SItemStatus.Normal;
  344. this.releaseItem();
  345. }
  346. } // Function cancelOperate()
  347. /**
  348. * Item对象边界区域
  349. *
  350. * @return SRect
  351. */
  352. boundingRect(): SRect {
  353. if (this.line.length) {
  354. this.minX = this.line[0].x;
  355. this.maxX = this.line[0].x;
  356. this.minY = this.line[0].y;
  357. this.maxY = this.line[0].y;
  358. this.line.forEach(it => {
  359. let x = it.x,
  360. y = it.y;
  361. if (x < this.minX) {
  362. this.minX = x;
  363. }
  364. if (y < this.minY) {
  365. this.minY = y;
  366. }
  367. if (x > this.maxX) {
  368. this.maxX = x;
  369. }
  370. if (y > this.maxY) {
  371. this.maxY = y;
  372. }
  373. });
  374. }
  375. return new SRect(
  376. this.minX,
  377. this.minY,
  378. this.maxX - this.minX,
  379. this.maxY - this.minY
  380. );
  381. } // Function boundingRect()
  382. /**
  383. * Item绘制操作
  384. *
  385. * @param painter painter对象
  386. */
  387. onDraw(painter: SPainter): void {
  388. this.sceneDis = painter.toPx(this.dis);
  389. painter.pen.lineWidth = painter.toPx(this.lineWidth);
  390. painter.pen.color = this.strokeColor;
  391. if (this.line.length == 2) {
  392. // 绘制直线
  393. painter.pen.color = new SColor(this.strokeColor);
  394. if (this.lineStyle == SLineStyle.Dashed) {
  395. painter.pen.lineDash = [
  396. painter.toPx(this.lineWidth * 3),
  397. painter.toPx(this.lineWidth * 7)
  398. ];
  399. } else if (this.lineStyle == SLineStyle.Dotted) {
  400. painter.pen.lineDash = [
  401. painter.toPx(this.lineWidth),
  402. painter.toPx(this.lineWidth)
  403. ];
  404. }
  405. if (this.selected && this.status == SItemStatus.Normal) {
  406. painter.pen.lineWidth = painter.toPx(this.lineWidth * 2);
  407. painter.shadow.shadowBlur = 10;
  408. painter.shadow.shadowColor = new SColor(`#00000033`);
  409. painter.shadow.shadowOffsetX = 5;
  410. painter.shadow.shadowOffsetY = 5;
  411. }
  412. painter.drawLine(this.line[0], this.line[1]);
  413. if (
  414. this.status == SItemStatus.Edit ||
  415. this.status == SItemStatus.Create
  416. ) {
  417. // 绘制端点
  418. this.line.forEach((p, i): void => {
  419. painter.brush.color = this.fillColor;
  420. if (i == this.curIndex) {
  421. painter.brush.color = this.activeFillColor;
  422. }
  423. painter.drawCircle(p.x, p.y, painter.toPx(5));
  424. });
  425. }
  426. } else if (this.line.length == 1) {
  427. if (
  428. this.status == SItemStatus.Edit ||
  429. this.status == SItemStatus.Create
  430. ) {
  431. // 绘制端点
  432. painter.brush.color = this.fillColor;
  433. painter.drawCircle(
  434. this.line[0].x,
  435. this.line[0].y,
  436. painter.toPx(5)
  437. );
  438. }
  439. }
  440. } // Function onDraw()
  441. } // Class SLineItem