SPainter.ts 25 KB

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