SPainter.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  1. import { SObject } from "@saga-web/base/lib";
  2. import { SPaintEngine } from "./engines/SPaintEngine";
  3. import {
  4. SBrush,
  5. SCanvasPaintEngine,
  6. SCanvasView,
  7. SFont,
  8. SLine,
  9. SPath2D,
  10. SPen,
  11. SPoint,
  12. SRect,
  13. SSize
  14. } from "./";
  15. import { SCompositeType } from "./enums/SCompositeType";
  16. import { SArrow } from "./types/SArrow";
  17. import { SArrowStyleType } from "./enums/SArrowStyleType";
  18. /**
  19. * Painter
  20. *
  21. * @author 庞利祥(sybotan@126.com)
  22. */
  23. export class SPainter extends SObject {
  24. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  25. // 属性定义
  26. /** 绘制引擎 */
  27. private readonly engine: SPaintEngine;
  28. /** 画笔 */
  29. get pen(): SPen {
  30. return this.engine.state.pen;
  31. } // Get pen
  32. set pen(value: SPen) {
  33. this.engine.state.pen = value;
  34. } // Set pen
  35. /** 画刷 */
  36. get brush(): SBrush {
  37. return this.engine.state.brush;
  38. } // Get brush
  39. set brush(value: SBrush) {
  40. this.engine.state.brush = value;
  41. } // Set brush
  42. /** 字体属性 */
  43. get font(): SFont {
  44. return this.engine.state.font;
  45. } // Get font
  46. set font(value: SFont) {
  47. this.engine.state.font = value;
  48. } // Set font
  49. /** 融合属性 */
  50. get composite(): number {
  51. return this.engine.state._composite;
  52. } // Get composite
  53. set composite(value: number) {
  54. this.engine.state._composite = value;
  55. } // Set composite
  56. /** 变换矩阵 */
  57. get worldTransform(): DOMMatrix {
  58. return this.engine.state.matrix;
  59. } // Get worldTransform
  60. /**
  61. * 构造函数
  62. *
  63. * @param engine 绘制引擎
  64. */
  65. constructor(engine: SCanvasView | SPaintEngine) {
  66. super();
  67. if (engine instanceof SCanvasView) {
  68. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  69. this.engine = new SCanvasPaintEngine(engine.canvas!!);
  70. } else {
  71. this.engine = engine;
  72. }
  73. this.pen = new SPen();
  74. this.brush = new SBrush();
  75. this.font = new SFont();
  76. this.composite = SCompositeType.SourceOver;
  77. } // Constructor()
  78. /**
  79. * 保存painter状态
  80. */
  81. save(): void {
  82. this.engine.save();
  83. } // Function save()
  84. /**
  85. * 恢复painter状态
  86. */
  87. restore(): void {
  88. this.engine.restore();
  89. } // Function restore()
  90. // =================================================================================================================
  91. // 变换相关
  92. /**
  93. * 平移变换
  94. *
  95. * @param x X轴方向平移
  96. * @param y Y辆方向平移
  97. */
  98. translate(x: number, y: number): void {
  99. this.engine.translate(x, y);
  100. } // Function translate()
  101. /**
  102. * 缩放
  103. *
  104. * @param x X轴方向缩放
  105. * @param y Y辆方向缩放
  106. */
  107. scale(x: number, y: number): void {
  108. this.engine.scale(x, y);
  109. } // Function scale()
  110. /**
  111. * 旋转
  112. *
  113. * @param angle 旋转角度(单位弧度)
  114. */
  115. rotate(angle: number): void {
  116. this.engine.rotate(angle);
  117. } // Function rotate()
  118. /**
  119. * 将当前的变形矩阵乘上一个基于自身参数的矩阵
  120. *
  121. * @param m11 水平方向的缩放
  122. * @param m12 水平方向的倾斜偏移
  123. * @param m21 竖直方向的倾斜偏移
  124. * @param m22 竖直方向的缩放
  125. * @param dx 水平方向的移动
  126. * @param dy 竖直方向的移动
  127. */
  128. transform(
  129. m11: number,
  130. m12: number,
  131. m21: number,
  132. m22: number,
  133. dx: number,
  134. dy: number
  135. ): void {
  136. this.engine.transform(m11, m12, m21, m22, dx, dy);
  137. } // Function transform()
  138. /**
  139. * 将当前的变形矩阵重置为单位矩阵,再将当前的变形矩阵乘上一个基于自身参数的矩阵
  140. *
  141. * @param m11 水平方向的缩放
  142. * @param m12 水平方向的倾斜偏移
  143. * @param m21 竖直方向的倾斜偏移
  144. * @param m22 竖直方向的缩放
  145. * @param dx 水平方向的移动
  146. * @param dy 竖直方向的移动
  147. */
  148. setTransform(
  149. m11: number,
  150. m12: number,
  151. m21: number,
  152. m22: number,
  153. dx: number,
  154. dy: number
  155. ): void {
  156. this.engine.setTransform(m11, m12, m21, m22, dx, dy);
  157. } // Function transform()
  158. /**
  159. * 重置当前变形为单位矩阵。等价于调用setTransform(1, 0, 0, 1, 0, 0)
  160. */
  161. resetTransform(): void {
  162. this.engine.resetTransform();
  163. } // Function resetTransform()
  164. // =================================================================================================================
  165. // 绘制相关
  166. /**
  167. * 设置裁剪路径
  168. *
  169. * @param path 裁剪路径
  170. */
  171. setClip(path: Path2D): void {
  172. this.engine.setClip(path);
  173. } // Function setClip()
  174. /**
  175. * 清空矩形区域
  176. *
  177. * @param rect 矩形
  178. */
  179. clearRect(rect: SRect): void;
  180. /**
  181. * 清空矩形区域
  182. *
  183. * @param leftTop 左上角坐标
  184. * @param rightBottom 右下角坐标
  185. */
  186. clearRect(leftTop: SPoint, rightBottom: SPoint): void;
  187. /**
  188. * 清空矩形区域
  189. *
  190. * @param leftTop 左上角坐标
  191. * @param size 大小
  192. */
  193. clearRect(leftTop: SPoint, size: SSize): void;
  194. /**
  195. * 清空矩形区域
  196. *
  197. * @param x X坐标
  198. * @param y Y坐标
  199. * @param w 宽度
  200. * @param h 高度
  201. */
  202. clearRect(x: number, y: number, w: number, h: number): void;
  203. /**
  204. * 清空矩形区域(重载实现)
  205. *
  206. * @param x X坐标 | 左上角坐标 | 矩形
  207. * @param y Y坐标 | 右下角坐标 | 大小
  208. * @param w 宽度
  209. * @param h 高度
  210. */
  211. clearRect(
  212. x: number | SPoint | SRect,
  213. y?: number | SPoint | SSize,
  214. w?: number,
  215. h?: number
  216. ): void {
  217. if (x instanceof SRect) {
  218. this.engine.clearRect(x);
  219. } else if (x instanceof SPoint && y instanceof SPoint) {
  220. this.engine.clearRect(new SRect(x, y));
  221. } else if (x instanceof SPoint && y instanceof SSize) {
  222. this.engine.clearRect(new SRect(x, y));
  223. } else {
  224. this.engine.clearRect(
  225. new SRect(x as number, y as number, w as number, h as number)
  226. );
  227. }
  228. } // Function clearRect()
  229. /**
  230. * 绘制矩形
  231. *
  232. * @param rect 矩形
  233. */
  234. drawRect(rect: SRect): void;
  235. /**
  236. * 绘制矩形
  237. *
  238. * @param leftTop 左上角坐标
  239. * @param rightBottom 右下角坐标
  240. */
  241. drawRect(leftTop: SPoint, rightBottom: SPoint): void;
  242. /**
  243. * 绘制矩形
  244. *
  245. * @param leftTop 左上角坐标
  246. * @param size 大小
  247. */
  248. drawRect(leftTop: SPoint, size: SSize): void;
  249. /**
  250. * 绘制矩形
  251. *
  252. * @param x X坐标
  253. * @param y Y坐标
  254. * @param w 宽度
  255. * @param h 高度
  256. */
  257. drawRect(x: number, y: number, w: number, h: number): void;
  258. /**
  259. * 绘制矩形
  260. *
  261. * @param x X坐标 | 左上角坐标 | 矩形
  262. * @param y Y坐标 | 右下角坐标 | 大小
  263. * @param w 宽度
  264. * @param h 高度
  265. */
  266. drawRect(
  267. x: number | SPoint | SRect,
  268. y?: number | SPoint | SSize,
  269. w?: number,
  270. h?: number
  271. ): void {
  272. if (x instanceof SRect) {
  273. this.engine.drawRect(x);
  274. } else if (x instanceof SPoint && y instanceof SPoint) {
  275. this.engine.drawRect(new SRect(x, y));
  276. } else if (x instanceof SPoint && y instanceof SSize) {
  277. this.engine.drawRect(new SRect(x, y));
  278. } else {
  279. this.engine.drawRect(
  280. new SRect(x as number, y as number, w as number, h as number)
  281. );
  282. }
  283. } // Function drawRect()
  284. // /**
  285. // * 绘制带导角空心矩形
  286. // *
  287. // * @param x X坐标
  288. // * @param y Y坐标
  289. // * @param w 宽度
  290. // * @param h 高度
  291. // * @param r 导角半径
  292. // */
  293. // drawRoundedRect(
  294. // x: number,
  295. // y: number,
  296. // w: number,
  297. // h: number,
  298. // r: number
  299. // ): void {} // Function drawRoundedRect()
  300. /**
  301. * 绘制圆形
  302. *
  303. * @param cx 圆心X坐标
  304. * @param cy 圆心X坐标
  305. * @param r 圆半径
  306. */
  307. drawCircle(cx: number, cy: number, r: number): void {
  308. this.engine.drawCircle(cx, cy, r);
  309. } // Function drawCircle()
  310. /**
  311. * 绘制椭圆
  312. *
  313. * @param cx 圆点X坐标
  314. * @param cy 圆点Y坐标
  315. * @param rx 水平半径
  316. * @param ry 垂直半径
  317. */
  318. drawEllipse(cx: number, cy: number, rx: number, ry: number): void {
  319. this.engine.drawEllipse(cx, cy, rx, ry);
  320. } // Function drawEllipse()
  321. /**
  322. * 绘制椭圆弧
  323. *
  324. * @param rect 椭圆所在矩形
  325. * @param startAngle 开始角度(单位弧度)
  326. * @param endAngle 结束角度(单位弧度)
  327. */
  328. drawArc(rect: SRect, startAngle: number, endAngle: number): void;
  329. /**
  330. * 绘制椭圆弧
  331. *
  332. * @param x 椭圆所在矩形X坐标
  333. * @param y 椭圆所在矩形Y坐标
  334. * @param width 椭圆所在矩形宽度
  335. * @param height 椭圆所在矩形高度
  336. * @param startAngle 开始角度(单位弧度)
  337. * @param endAngle 结束角度(单位弧度)
  338. */
  339. drawArc(
  340. x: number,
  341. y: number,
  342. width: number,
  343. height: number,
  344. startAngle: number,
  345. endAngle: number
  346. ): void;
  347. /**
  348. * 绘制椭圆弧(重载实现)
  349. *
  350. * @param x 椭圆所在矩形X坐标
  351. * @param y 椭圆所在矩形Y坐标
  352. * @param width 椭圆所在矩形宽度
  353. * @param height 椭圆所在矩形高度
  354. * @param startAngle 开始角度(单位弧度)
  355. * @param endAngle 结束角度(单位弧度)
  356. */
  357. drawArc(
  358. x: SRect | number,
  359. y: number,
  360. width: number,
  361. height?: number,
  362. startAngle?: number,
  363. endAngle?: number
  364. ): void {
  365. if (x instanceof SRect) {
  366. this.engine.drawArc(x.x, x.y, x.width, x.height, y, width);
  367. } else {
  368. this.engine.drawArc(
  369. x,
  370. y,
  371. width,
  372. height as number,
  373. startAngle as number,
  374. endAngle as number
  375. );
  376. }
  377. } // Function drawArc()
  378. /**
  379. * 绘制椭圆弦弧
  380. *
  381. * @param rect 椭圆所在矩形
  382. * @param startAngle 开始角度(单位弧度)
  383. * @param endAngle 结束角度(单位弧度)
  384. */
  385. drawChord(rect: SRect, startAngle: number, endAngle: number): void;
  386. /**
  387. * 绘制椭圆弦弧
  388. *
  389. * @param x 椭圆所在矩形X坐标
  390. * @param y 椭圆所在矩形Y坐标
  391. * @param width 椭圆所在矩形宽度
  392. * @param height 椭圆所在矩形高度
  393. * @param startAngle 开始角度(单位弧度)
  394. * @param endAngle 结束角度(单位弧度)
  395. */
  396. drawChord(
  397. x: number,
  398. y: number,
  399. width: number,
  400. height: number,
  401. startAngle: number,
  402. endAngle: number
  403. ): void;
  404. /**
  405. * 绘制椭圆弦弧(重载实现)
  406. *
  407. * @param x 椭圆所在矩形X坐标
  408. * @param y 椭圆所在矩形Y坐标
  409. * @param width 椭圆所在矩形宽度
  410. * @param height 椭圆所在矩形高度
  411. * @param startAngle 开始角度(单位弧度)
  412. * @param endAngle 结束角度(单位弧度)
  413. */
  414. drawChord(
  415. x: SRect | number,
  416. y: number,
  417. width: number,
  418. height?: number,
  419. startAngle?: number,
  420. endAngle?: number
  421. ): void {
  422. if (x instanceof SRect) {
  423. this.engine.drawChord(x.x, x.y, x.width, x.height, y, width);
  424. } else {
  425. this.engine.drawChord(
  426. x,
  427. y,
  428. width,
  429. height as number,
  430. startAngle as number,
  431. endAngle as number
  432. );
  433. }
  434. } // Function drawChord()
  435. /**
  436. * 绘制椭圆饼
  437. *
  438. * @param rect 椭圆所在矩形
  439. * @param startAngle 开始角度(单位弧度)
  440. * @param endAngle 结束角度(单位弧度)
  441. */
  442. drawPie(rect: SRect, startAngle: number, endAngle: number): void;
  443. /**
  444. * 绘制椭圆饼
  445. *
  446. * @param x 椭圆所在矩形X坐标
  447. * @param y 椭圆所在矩形Y坐标
  448. * @param width 椭圆所在矩形宽度
  449. * @param height 椭圆所在矩形高度
  450. * @param startAngle 开始角度(单位弧度)
  451. * @param endAngle 结束角度(单位弧度)
  452. */
  453. drawPie(
  454. x: number,
  455. y: number,
  456. width: number,
  457. height: number,
  458. startAngle: number,
  459. endAngle: number
  460. ): void;
  461. /**
  462. * 绘制椭圆饼
  463. *
  464. * @param x 椭圆所在矩形X坐标
  465. * @param y 椭圆所在矩形Y坐标
  466. * @param width 椭圆所在矩形宽度
  467. * @param height 椭圆所在矩形高度
  468. * @param startAngle 开始角度(单位弧度)
  469. * @param endAngle 结束角度(单位弧度)
  470. */
  471. drawPie(
  472. x: SRect | number,
  473. y: number,
  474. width: number,
  475. height?: number,
  476. startAngle?: number,
  477. endAngle?: number
  478. ): void {
  479. if (x instanceof SRect) {
  480. this.engine.drawPie(x.x, x.y, x.width, x.height, y, width);
  481. } else {
  482. this.engine.drawPie(
  483. x,
  484. y,
  485. width,
  486. height as number,
  487. startAngle as number,
  488. endAngle as number
  489. );
  490. }
  491. } // Function drawPie()
  492. /**
  493. * 绘制一条线段
  494. *
  495. * @param line 线段
  496. */
  497. drawLine(line: SLine): void;
  498. /**
  499. * 绘制一条线段
  500. *
  501. * @param p1 启点坐标
  502. * @param p2 终点坐标
  503. */
  504. drawLine(p1: SPoint, p2: SPoint): void;
  505. /**
  506. * 绘制一条线段
  507. *
  508. * @param x1 启点X坐标
  509. * @param y1 启点Y坐标
  510. * @param x2 终点X坐标
  511. * @param y2 终点Y坐标
  512. */
  513. drawLine(x1: number, y1: number, x2: number, y2: number): void;
  514. /**
  515. * 绘制一条线段(重载实现)
  516. *
  517. * @param x1 启点X坐标 | 起点1坐标 | 线段 |
  518. * @param y1 启点Y坐标 | 终点坐标
  519. * @param x2 终点X坐标
  520. * @param y2 终点Y坐标
  521. */
  522. drawLine(
  523. x1: number | SPoint | SLine,
  524. y1?: number | SPoint,
  525. x2?: number,
  526. y2?: number
  527. ): void {
  528. if (x1 instanceof SLine) {
  529. this.engine.drawLine(x1);
  530. } else if (x1 instanceof SPoint && y1 instanceof SPoint) {
  531. this.engine.drawLine(new SLine(x1, y1));
  532. } else {
  533. this.engine.drawLine(
  534. new SLine(
  535. x1 as number,
  536. y1 as number,
  537. x2 as number,
  538. y2 as number
  539. )
  540. );
  541. }
  542. } // Function drawLine()
  543. /**
  544. * 绘制折线
  545. *
  546. * @param points 折线折点
  547. */
  548. drawPolyline(points: SPoint[]): void {
  549. this.engine.drawPolyline(points);
  550. } // Function drawPolyline()
  551. /**
  552. * 绘制多边形
  553. *
  554. * @param points 多边形顶点
  555. */
  556. drawPolygon(points: SPoint[]): void {
  557. this.engine.drawPolygon(points);
  558. } // Functin drawPolygon()
  559. /**
  560. * 绘制路径
  561. *
  562. * @param path 路径
  563. */
  564. drawPath(path: SPath2D): void {
  565. this.engine.drawPath(path);
  566. } // Function drawPath()
  567. /**
  568. * 绘制文本
  569. *
  570. * @param text 文本内容
  571. * @param x X坐标
  572. * @param y Y坐标
  573. * @param maxWidth 最大宽度
  574. */
  575. drawText(text: string, x: number, y: number, maxWidth?: number): void {
  576. this.engine.drawText(text, x, y, maxWidth);
  577. } // Function drawText()
  578. /**
  579. * 绘制图片
  580. *
  581. * @param img 图片
  582. * @param x X坐标
  583. * @param y Y坐标
  584. * @param width 宽度
  585. * @param height 高度
  586. */
  587. drawImage(
  588. img: CanvasImageSource,
  589. x: number,
  590. y: number,
  591. width?: number,
  592. height?: number
  593. ): void {
  594. this.engine.drawImage(img, x, y, width, height);
  595. } // Function drawImage()
  596. /**
  597. * painter转实现view象素
  598. *
  599. * @param p 绘制坐标
  600. */
  601. toPx(p: number): number {
  602. return p / this.engine.state.matrix.a;
  603. } // Function painterToView()
  604. /**
  605. * 绘制带箭头的线段
  606. *
  607. * @param line 线段
  608. * @param style 末端样式
  609. */
  610. drawArrowLine(line: SLine, style?: SArrow): void;
  611. /**
  612. * 绘制带箭头的线段
  613. *
  614. * @param p1 启点坐标
  615. * @param p2 终点坐标
  616. * @param style 末端样式
  617. */
  618. drawArrowLine(p1: SPoint, p2: SPoint, style?: SArrow): void;
  619. /**
  620. * 绘制带箭头的线段
  621. *
  622. * @param x1 启点X坐标
  623. * @param y1 启点Y坐标
  624. * @param x2 终点X坐标
  625. * @param y2 终点Y坐标
  626. * @param style 末端样式
  627. */
  628. drawArrowLine(
  629. x1: number,
  630. y1: number,
  631. x2: number,
  632. y2: number,
  633. style?: SArrow
  634. ): void;
  635. /**
  636. * 绘制带箭头的线段
  637. *
  638. * @param x1 启点X坐标
  639. * @param y1 启点Y坐标
  640. * @param x2 终点X坐标
  641. * @param y2 终点Y坐标
  642. * @param st 线段两端样式
  643. */
  644. drawArrowLine(
  645. x1: number | SPoint | SLine,
  646. y1?: number | SPoint | SArrow,
  647. x2?: number | SArrow,
  648. y2?: number,
  649. st?: SArrow
  650. ): void {
  651. let line: SLine, style: SArrow;
  652. if (x1 instanceof SLine) {
  653. line = x1;
  654. style = y1 as SArrow;
  655. } else if (x1 instanceof SPoint && y1 instanceof SPoint) {
  656. line = new SLine(x1, y1);
  657. style = x2 as SArrow;
  658. } else {
  659. line = new SLine(
  660. x1 as number,
  661. y1 as number,
  662. x2 as number,
  663. y2 as number
  664. );
  665. style = st as SArrow;
  666. }
  667. this.engine.drawLine(line);
  668. if (style) {
  669. if (style.begin) {
  670. if (style.begin == SArrowStyleType.Basic) {
  671. this.drawBasicArrow(line, false);
  672. } else if (style.begin == SArrowStyleType.Triangle) {
  673. this.drawTriangleArrow(line, false);
  674. } else if (style.begin == SArrowStyleType.Diamond) {
  675. this.drawDiamondArrow(line, false);
  676. } else if (style.begin == SArrowStyleType.Square) {
  677. this.drawSquareArrow(line, false);
  678. } else if (style.begin == SArrowStyleType.Circle) {
  679. this.drawCircleArrow(line, false);
  680. }
  681. }
  682. if (style.end) {
  683. if (style.end == SArrowStyleType.Basic) {
  684. this.drawBasicArrow(line, true);
  685. } else if (style.end == SArrowStyleType.Triangle) {
  686. this.drawTriangleArrow(line, true);
  687. } else if (style.end == SArrowStyleType.Diamond) {
  688. this.drawDiamondArrow(line, true);
  689. } else if (style.end == SArrowStyleType.Square) {
  690. this.drawSquareArrow(line, true);
  691. } else if (style.end == SArrowStyleType.Circle) {
  692. this.drawCircleArrow(line, true);
  693. }
  694. }
  695. }
  696. } // Function drawArrowLine()
  697. /**
  698. * 私有计算方法-绘制线段末端标准箭头
  699. *
  700. * @param line 要加末端样式的线段
  701. * @param isEnd 是否为结束位置
  702. * */
  703. private drawBasicArrow(line: SLine, isEnd: boolean = true): void {
  704. // 定义箭头长度
  705. const d = 5;
  706. // 箭头横坐标
  707. const x1 = d * Math.cos(Math.PI / 4);
  708. // 箭头纵坐标
  709. const y1 = d * Math.sin(Math.PI / 4);
  710. // 线段与x轴夹角
  711. const fo = Math.atan(line.dy / line.dx);
  712. const ang = line.dx >= 0 ? fo : fo + Math.PI;
  713. // 是否为终点画箭头
  714. if (isEnd) {
  715. this.save();
  716. this.translate(line.x2, line.y2);
  717. this.rotate(ang);
  718. this.engine.drawPolyline([
  719. new SPoint(-x1, y1),
  720. new SPoint(0, 0),
  721. new SPoint(-x1, -y1)
  722. ]);
  723. this.restore();
  724. } else {
  725. this.save();
  726. this.translate(line.x1, line.y1);
  727. this.rotate(ang);
  728. this.engine.drawPolyline([
  729. new SPoint(x1, y1),
  730. new SPoint(0, 0),
  731. new SPoint(x1, -y1)
  732. ]);
  733. this.restore();
  734. }
  735. } // Function drawArrow()
  736. /**
  737. * 私有计算方法-绘制线段末端三角形箭头
  738. *
  739. * @param line 要加末端样式的线段
  740. * @param isEnd 是否为结束位置
  741. * */
  742. private drawTriangleArrow(line: SLine, isEnd: boolean = true): void {
  743. // 定义箭头长度
  744. const d = 5;
  745. // 箭头横坐标
  746. const x1 = d * Math.cos(Math.PI / 12);
  747. // 箭头纵坐标
  748. const y1 = d * Math.sin(Math.PI / 12);
  749. // 线段与x轴夹角
  750. const fo = Math.atan(line.dy / line.dx);
  751. const ang = line.dx >= 0 ? fo : fo + Math.PI;
  752. // 是否为终点画箭头
  753. if (isEnd) {
  754. this.save();
  755. this.translate(line.x2, line.y2);
  756. this.rotate(ang);
  757. this.engine.drawPolygon([
  758. new SPoint(-x1, y1),
  759. new SPoint(0, 0),
  760. new SPoint(-x1, -y1)
  761. ]);
  762. this.restore();
  763. } else {
  764. this.save();
  765. this.translate(line.x1, line.y1);
  766. this.rotate(ang);
  767. this.engine.drawPolygon([
  768. new SPoint(x1, y1),
  769. new SPoint(0, 0),
  770. new SPoint(x1, -y1)
  771. ]);
  772. this.restore();
  773. }
  774. } // Function drawTriangleArrow()
  775. /**
  776. * 私有计算方法-绘制线段末端菱形箭头
  777. *
  778. * @param line 要加末端样式的线段
  779. * @param isEnd 是否为结束位置
  780. * */
  781. private drawDiamondArrow(line: SLine, isEnd: boolean = true): void {
  782. // 定义箭头长度
  783. const d = 5;
  784. // 箭头横坐标
  785. const x1 = d * Math.cos(Math.PI / 4);
  786. // 箭头纵坐标
  787. const y1 = d * Math.sin(Math.PI / 4);
  788. // 线段与x轴夹角
  789. const fo = Math.atan(line.dy / line.dx);
  790. const ang = line.dx >= 0 ? fo : fo + Math.PI;
  791. // 是否为终点画箭头
  792. if (isEnd) {
  793. this.save();
  794. this.translate(line.x2, line.y2);
  795. this.rotate(ang);
  796. this.engine.drawPolygon([
  797. new SPoint(-x1, y1),
  798. new SPoint(0, 0),
  799. new SPoint(-x1, -y1),
  800. new SPoint(-Math.sqrt(2) * d, 0)
  801. ]);
  802. this.restore();
  803. } else {
  804. this.save();
  805. this.translate(line.x1, line.y1);
  806. this.rotate(ang);
  807. this.engine.drawPolygon([
  808. new SPoint(x1, y1),
  809. new SPoint(0, 0),
  810. new SPoint(x1, -y1),
  811. new SPoint(Math.sqrt(2) * d, 0)
  812. ]);
  813. this.restore();
  814. }
  815. } // Function drawDiamondArrow()
  816. /**
  817. * 私有计算方法-绘制线段末端方形箭头
  818. *
  819. * @param line 要加末端样式的线段
  820. * @param isEnd 是否为结束位置
  821. * */
  822. private drawSquareArrow(line: SLine, isEnd: boolean = true): void {
  823. // 定义箭头长度
  824. const d = 5;
  825. // 线段与x轴夹角
  826. const fo = Math.atan(line.dy / line.dx);
  827. const ang = line.dx >= 0 ? fo : fo + Math.PI;
  828. // 是否为终点画箭头
  829. if (isEnd) {
  830. this.save();
  831. this.translate(line.x2, line.y2);
  832. this.rotate(ang);
  833. this.engine.drawPolygon([
  834. new SPoint(-d, d / 2),
  835. new SPoint(0, d / 2),
  836. new SPoint(0, -d / 2),
  837. new SPoint(-d, -d / 2)
  838. ]);
  839. this.restore();
  840. } else {
  841. this.save();
  842. this.translate(line.x1, line.y1);
  843. this.rotate(ang);
  844. this.engine.drawPolygon([
  845. new SPoint(0, d / 2),
  846. new SPoint(d, d / 2),
  847. new SPoint(d, -d / 2),
  848. new SPoint(0, -d / 2)
  849. ]);
  850. this.restore();
  851. }
  852. } // Function drawSquareArrow()
  853. /**
  854. * 私有计算方法-绘制线段末端圆形箭头
  855. *
  856. * @param line 要加末端样式的线段
  857. * @param isEnd 是否为结束位置
  858. * */
  859. private drawCircleArrow(line: SLine, isEnd: boolean = true): void {
  860. // 定义箭头长度
  861. const d = 5;
  862. // 线段与x轴夹角
  863. const fo = Math.atan(line.dy / line.dx);
  864. const ang = line.dx >= 0 ? fo : fo + Math.PI;
  865. // 是否为终点画箭头
  866. if (isEnd) {
  867. this.save();
  868. this.translate(line.x2, line.y2);
  869. this.rotate(ang);
  870. this.engine.drawCircle(-d / 2, 0, d / 2);
  871. this.restore();
  872. } else {
  873. this.save();
  874. this.translate(line.x1, line.y1);
  875. this.rotate(ang);
  876. this.engine.drawCircle(d / 2, 0, d / 2);
  877. this.restore();
  878. }
  879. } // Function drawCircleArrow()
  880. } // Class SPainter