Draw.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  1. import { dataService } from "../Service/DataService.js";
  2. import { stateService } from "../Service/StateService.js";
  3. import { coordinate } from "../Coordinate.js";
  4. import Style from "@/graphic/CanvasStyle/index.js";
  5. import VectorType from "../enum/VectorType.js";
  6. import { mathUtil } from "../Util/MathUtil.js";
  7. import ElementEvents from "../enum/ElementEvents.js";
  8. import { elementService } from "../Service/ElementService.js";
  9. import UIEvents from "@/graphic/enum/UIEvents.js";
  10. import VectorCategory from "@/graphic/enum/VectorCategory.js";
  11. import Settings from "@/graphic/Settings.js";
  12. import { Canvg } from 'canvg'
  13. import SVGIcons from "../CanvasStyle/ImageLabels/SVGIcons";
  14. const imgCache = {};
  15. const help = {
  16. getVectorStyle(vector, geoType = vector.geoType) {
  17. const geoId = vector?.vectorId;
  18. if (!geoId) {
  19. return [Style[geoType], undefined];
  20. }
  21. const itemsEntry = [
  22. [stateService.getSelectItem(), "Select"],
  23. [stateService.getDraggingItem(), "Dragging"],
  24. [stateService.getFocusItem(), "Focus"],
  25. ];
  26. let currentAttr;
  27. return [
  28. itemsEntry.reduce((prev, [item, attr]) => {
  29. if (!item) return prev;
  30. const selected =
  31. geoId === item.vectorId ||
  32. (item.parent && Object.keys(item.parent).some((id) => id === geoId));
  33. if (selected && Style[attr]) {
  34. const style = Style[attr][geoType] || Style[attr][vector.category];
  35. if (style) {
  36. currentAttr = attr;
  37. return style;
  38. }
  39. }
  40. return prev;
  41. }, Style[geoType]),
  42. currentAttr,
  43. ];
  44. },
  45. setStyle(ctx, styles) {
  46. for (const style in styles) {
  47. if (typeof styles[style] === "function") {
  48. styles[style](ctx, vector);
  49. } else {
  50. ctx[style] = styles[style];
  51. }
  52. }
  53. },
  54. setVectorStyle(ctx, vector, geoType = vector.geoType) {
  55. let styles, attr;
  56. if (Array.isArray(geoType)) {
  57. for (const type of geoType) {
  58. [styles, attr] = help.getVectorStyle(vector, type);
  59. if (styles) {
  60. break;
  61. }
  62. }
  63. } else {
  64. [styles, attr] = help.getVectorStyle(vector, geoType);
  65. }
  66. help.setStyle(ctx, styles);
  67. return [styles, attr];
  68. },
  69. transformCoves(lines) {
  70. return lines.map((line) =>
  71. line.map((line) => ({
  72. start: coordinate.getScreenXY(line.start),
  73. end: coordinate.getScreenXY(line.end),
  74. controls: line.controls.map(coordinate.getScreenXY.bind(coordinate)),
  75. }))
  76. );
  77. },
  78. drawCoves(ctx, coves) {
  79. for (const curve of coves) {
  80. ctx.beginPath();
  81. ctx.moveTo(curve.start.x, curve.start.y);
  82. if (curve.controls.length === 1) {
  83. ctx.quadraticCurveTo(
  84. curve.controls[0].x,
  85. curve.controls[0].y,
  86. curve.end.x,
  87. curve.end.y
  88. );
  89. } else {
  90. ctx.bezierCurveTo(
  91. curve.controls[0].x,
  92. curve.controls[0].y,
  93. curve.controls[1].x,
  94. curve.controls[1].y,
  95. curve.end.x,
  96. curve.end.y
  97. );
  98. }
  99. ctx.stroke();
  100. }
  101. },
  102. getReal(data) {
  103. return (data * coordinate.ratio * coordinate.zoom) / coordinate.defaultZoom;
  104. },
  105. getImage(src) {
  106. if (imgCache[src]) {
  107. return imgCache[src];
  108. }
  109. const img = new Image();
  110. img.src = src;
  111. return (imgCache[src] = new Promise((resolve) => {
  112. img.onload = () => {
  113. resolve(img);
  114. };
  115. }));
  116. },
  117. getTextCenter(ctx, txt) {
  118. const text = ctx.measureText(txt);
  119. const height = text.actualBoundingBoxAscent + text.actualBoundingBoxDescent;
  120. return {
  121. width: text.width,
  122. height,
  123. x: text.width / 2,
  124. y: -height / 2,
  125. };
  126. },
  127. // 绘制圆角矩形
  128. roundRect(ctx, x, y, width, height, radius) {
  129. ctx.beginPath();
  130. ctx.moveTo(x + radius, y);
  131. ctx.lineTo(x + width - radius, y);
  132. ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
  133. ctx.lineTo(x + width, y + height - radius);
  134. ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
  135. ctx.lineTo(x + radius, y + height);
  136. ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
  137. ctx.lineTo(x, y + radius);
  138. ctx.quadraticCurveTo(x, y, x + radius, y);
  139. ctx.closePath();
  140. },
  141. getRealDistance(p1, p2) {
  142. return (
  143. Math.round(mathUtil.getDistance(p1, p2) * coordinate.res * 100) / 100
  144. );
  145. },
  146. getPerpendicularPoint(p1, p2, p3, d) {
  147. if (p1.x === p2.x) {
  148. return { x: p3.x + d, y: p3.y };
  149. } else if (p1.y === p2.y) {
  150. return { x: p3.x, y: p3.y + d };
  151. }
  152. // 计算通过 p1 和 p2 的直线的斜率和截距
  153. const slope = (p2.y - p1.y) / (p2.x - p1.x);
  154. const intercept = p1.y - slope * p1.x;
  155. // 计算垂直线的斜率和截距
  156. const perpendicularSlope = -1 / slope;
  157. const perpendicularIntercept = p3.y - perpendicularSlope * p3.x;
  158. // 计算垂足点 p0
  159. const x =
  160. (perpendicularIntercept - intercept) / (slope - perpendicularSlope);
  161. const y = slope * x + intercept;
  162. const p0 = { x, y };
  163. // 计算点 p4
  164. const distance = d; // 指定距离
  165. const dx = distance / Math.sqrt(1 + perpendicularSlope ** 2);
  166. const dy = perpendicularSlope * dx;
  167. return { x: p0.x + dx, y: p0.y + dy };
  168. },
  169. drawLineText(ctx, start, end, text, style) {
  170. if (start.x > end.x) {
  171. [start, end] = [end, start];
  172. }
  173. const angle =
  174. (Math.atan2(end.y - start.y, end.x - start.x) * 180) / Math.PI;
  175. const center = mathUtil.lineCenter(start, end);
  176. ctx.save();
  177. ctx.translate(center.x, center.y);
  178. ctx.rotate((angle * Math.PI) / 180);
  179. ctx.font = `${(style.fontSize || 10) * coordinate.ratio}px Microsoft YaHei`;
  180. const textCenter = help.getTextCenter(ctx, text);
  181. const padding = style.padding;
  182. help.roundRect(
  183. ctx,
  184. -textCenter.x - padding,
  185. textCenter.y - padding,
  186. textCenter.width + 2 * padding,
  187. textCenter.height + 2 * padding,
  188. textCenter.height / 2 + padding
  189. );
  190. ctx.fillStyle = style.backColor;
  191. ctx.fill();
  192. ctx.fillStyle = style.fillColor;
  193. ctx.fillText(text, -textCenter.x, -textCenter.y);
  194. ctx.restore();
  195. },
  196. };
  197. export default class Draw {
  198. constructor() {
  199. this.canvas = null;
  200. this.context = null;
  201. }
  202. initContext(canvas) {
  203. if (canvas) {
  204. this.canvas = canvas;
  205. this.context = canvas.getContext("2d");
  206. } else {
  207. this.context = null;
  208. this.canvas = null;
  209. }
  210. }
  211. clear() {
  212. this.context.clearRect(
  213. 0,
  214. 0,
  215. this.context.canvas.width,
  216. this.context.canvas.height
  217. );
  218. }
  219. drawBackGroundImg(vector) {
  220. if (!vector.display) {
  221. return;
  222. }
  223. const img = vector.imageData;
  224. const width = help.getReal(img.width);
  225. const height = help.getReal(img.height);
  226. const center = coordinate.getScreenXY(vector.center);
  227. this.context.save();
  228. this.context.drawImage(
  229. img,
  230. center.x - width / 2,
  231. center.y - height / 2,
  232. width,
  233. height
  234. );
  235. this.context.restore();
  236. }
  237. drawGrid(startX, startY, w, h, step1, step2) {
  238. this.context.save();
  239. this.context.beginPath();
  240. for (var x = startX; x <= w; x += step1) {
  241. this.context.moveTo(x, 0);
  242. this.context.lineTo(x, h);
  243. }
  244. for (var y = startY; y <= h; y += step1) {
  245. this.context.moveTo(0, y);
  246. this.context.lineTo(w, y);
  247. }
  248. this.context.strokeStyle = "rgba(0,0,0,0.1)";
  249. this.context.lineWidth = 0.5 * coordinate.ratio;
  250. this.context.stroke();
  251. this.context.beginPath();
  252. for (var x = startX; x <= w; x += step2) {
  253. this.context.moveTo(x, 0);
  254. this.context.lineTo(x, h);
  255. }
  256. for (var y = startY; y <= h; y += step2) {
  257. this.context.moveTo(0, y);
  258. this.context.lineTo(w, y);
  259. }
  260. this.context.strokeStyle = "rgba(0,0,0,0.2)";
  261. this.context.lineWidth = 1 * coordinate.ratio;
  262. this.context.stroke();
  263. this.context.restore();
  264. }
  265. drawRoad(vector, isTemp) {
  266. if (!isTemp && vector.display && vector.way !== "oneWay") {
  267. const ctx = this.context;
  268. const draw = (midDivide) => {
  269. const startScreen = coordinate.getScreenXY(midDivide.start);
  270. const endScreen = coordinate.getScreenXY(midDivide.end);
  271. ctx.beginPath();
  272. ctx.moveTo(startScreen.x, startScreen.y);
  273. ctx.lineTo(endScreen.x, endScreen.y);
  274. ctx.stroke();
  275. };
  276. ctx.save();
  277. help.setVectorStyle(ctx, vector);
  278. vector.midDivide.leftMidDivide && draw(vector.midDivide.leftMidDivide);
  279. vector.midDivide.rightMidDivide && draw(vector.midDivide.rightMidDivide);
  280. ctx.restore();
  281. }
  282. if (import.meta.env.DEV && !isTemp) {
  283. const startReal = isTemp
  284. ? vector.start
  285. : dataService.getRoadPoint(vector.startId);
  286. const endReal = isTemp
  287. ? vector.end
  288. : dataService.getRoadPoint(vector.endId);
  289. this.drawTextByInfo(
  290. { x: (startReal.x + endReal.x) / 2, y: (startReal.y + endReal.y) / 2 },
  291. vector.vectorId
  292. );
  293. }
  294. this.drawRoadEdge(vector, isTemp);
  295. vector.leftLanes && vector.leftLanes.forEach(this.drawLan.bind(this));
  296. vector.rightLanes && vector.rightLanes.forEach(this.drawLan.bind(this));
  297. }
  298. drawLan(lan) {
  299. const ctx = this.context;
  300. const start = coordinate.getScreenXY(lan.start);
  301. const end = coordinate.getScreenXY(lan.end);
  302. ctx.save();
  303. ctx.beginPath();
  304. help.setVectorStyle(ctx, null, "Lane");
  305. ctx.setLineDash(Style.Lane.dash);
  306. ctx.moveTo(start.x, start.y);
  307. ctx.lineTo(end.x, end.y);
  308. ctx.stroke();
  309. ctx.restore();
  310. if (import.meta.env.DEV) {
  311. this.drawPoint(lan.start);
  312. this.drawPoint(lan.end);
  313. }
  314. }
  315. drawRoadEdge(vector, isTemp) {
  316. //判断是否与road方向一致。角度足够小,路足够宽,有可能向量方向不一致
  317. const start = isTemp
  318. ? vector.start
  319. : dataService.getRoadPoint(vector.startId);
  320. const end = isTemp ? vector.end : dataService.getRoadPoint(vector.endId);
  321. const drawRoadEdgeChild = (edgeVector) => {
  322. const flag = mathUtil.isSameDirForVector(
  323. start,
  324. end,
  325. edgeVector.start,
  326. edgeVector.end
  327. );
  328. if (flag) {
  329. ctx.beginPath();
  330. const point1 = coordinate.getScreenXY(edgeVector.start);
  331. const point2 = coordinate.getScreenXY(edgeVector.end);
  332. ctx.moveTo(point1.x, point1.y);
  333. ctx.lineTo(point2.x, point2.y);
  334. ctx.stroke();
  335. }
  336. this.drawTextByInfo(
  337. {
  338. x: (edgeVector.start.x + edgeVector.end.x) / 2,
  339. y: (edgeVector.start.y + edgeVector.end.y) / 2,
  340. },
  341. edgeVector.vectorId
  342. );
  343. };
  344. const leftEdge = isTemp
  345. ? vector.leftEdge
  346. : dataService.getRoadEdge(vector.leftEdgeId);
  347. const rightEdge = isTemp
  348. ? vector.rightEdge
  349. : dataService.getRoadEdge(vector.rightEdgeId);
  350. const ctx = this.context;
  351. ctx.save();
  352. isTemp && (ctx.globalAlpha = 0.3);
  353. help.setVectorStyle(ctx, leftEdge);
  354. drawRoadEdgeChild(leftEdge);
  355. help.setVectorStyle(ctx, rightEdge);
  356. drawRoadEdgeChild(rightEdge);
  357. ctx.restore();
  358. if (import.meta.env.DEV) {
  359. this.drawPoint(leftEdge.start);
  360. this.drawPoint(leftEdge.end);
  361. this.drawPoint(rightEdge.start);
  362. this.drawPoint(rightEdge.end);
  363. }
  364. }
  365. drawCrossPoint(vector) {
  366. const start = coordinate.getScreenXY(
  367. dataService
  368. .getRoadEdge(vector.edgeInfo1.id)
  369. .getPosition(vector.edgeInfo1.dir)
  370. );
  371. const end = coordinate.getScreenXY(
  372. dataService
  373. .getRoadEdge(vector.edgeInfo2.id)
  374. .getPosition(vector.edgeInfo2.dir)
  375. );
  376. const pt2 = mathUtil.twoOrderBezier(
  377. 0.5,
  378. start,
  379. coordinate.getScreenXY({ x: vector.x, y: vector.y }),
  380. end
  381. );
  382. const pt = mathUtil.twoOrderBezier2(0.5, start, pt2, end);
  383. const extremePoint = coordinate.getScreenXY(vector.extremePoint);
  384. const ctx = this.context;
  385. ctx.save();
  386. ctx.strokeStyle = "red";
  387. ctx.beginPath();
  388. ctx.arc(
  389. // pt.x,
  390. // pt.y,
  391. extremePoint.x,
  392. extremePoint.y,
  393. Style.CrossPoint.radius * coordinate.ratio,
  394. 0,
  395. Math.PI * 2,
  396. true
  397. );
  398. ctx.stroke();
  399. ctx.fill();
  400. ctx.restore();
  401. ctx.save();
  402. ctx.beginPath();
  403. help.setVectorStyle(ctx, null, "RoadEdge");
  404. //曲线
  405. // ctx.moveTo(start.x, start.y);
  406. // ctx.quadraticCurveTo(pt.x, pt.y, end.x, end.y);
  407. const [coves] = help.transformCoves([vector.curves]);
  408. help.drawCoves(ctx, coves);
  409. ctx.restore();
  410. }
  411. drawCurveRoad(vector) {
  412. if (vector.display && vector.midDivide) {
  413. const covesArray = help.transformCoves([
  414. vector.midDivide.leftMidDivideCurves,
  415. vector.midDivide.rightMidDivideCurves,
  416. ]);
  417. const ctx = this.context;
  418. ctx.save();
  419. help.setVectorStyle(ctx, vector);
  420. for (let coves of covesArray) {
  421. help.drawCoves(ctx, coves);
  422. }
  423. ctx.restore();
  424. }
  425. this.drawCurveRoadEdge(dataService.getCurveRoadEdge(vector.rightEdgeId));
  426. this.drawCurveRoadEdge(dataService.getCurveRoadEdge(vector.leftEdgeId));
  427. vector.leftLanesCurves &&
  428. vector.leftLanesCurves.forEach(this.drawCurveLan.bind(this));
  429. vector.rightLanesCurves &&
  430. vector.rightLanesCurves.forEach(this.drawCurveLan.bind(this));
  431. if (import.meta.env.DEV) {
  432. vector.points.forEach(this.drawPoint.bind(this));
  433. }
  434. }
  435. drawCurveRoadEdge(vector, isTemp) {
  436. const [coves] = help.transformCoves([vector.curves]);
  437. const ctx = this.context;
  438. ctx.save();
  439. help.setVectorStyle(ctx, vector);
  440. help.drawCoves(ctx, coves);
  441. ctx.restore();
  442. if (import.meta.env.DEV) {
  443. vector.points.forEach(this.drawPoint.bind(this));
  444. }
  445. }
  446. drawCurveLan(lines) {
  447. const [coves] = help.transformCoves([lines]);
  448. const ctx = this.context;
  449. ctx.save();
  450. help.setVectorStyle(ctx, null, "CurveLan");
  451. ctx.setLineDash(Style.Lane.dash);
  452. help.drawCoves(ctx, coves);
  453. ctx.restore();
  454. if (import.meta.env.DEV) {
  455. lines.map((line) => {
  456. this.drawPoint(line.start);
  457. this.drawPoint(line.end);
  458. });
  459. }
  460. }
  461. drawRoadPoint(vector) {
  462. this.drawPoint(vector);
  463. }
  464. drawArrow(vector) {
  465. const startReal = dataService.getPoint(vector.startId);
  466. const start = coordinate.getScreenXY(startReal);
  467. const endReal = dataService.getPoint(vector.endId);
  468. const end = coordinate.getScreenXY(endReal);
  469. const ctx = this.context;
  470. ctx.save();
  471. const [style] = help.setVectorStyle(this.context, vector);
  472. if (vector.color) {
  473. ctx.strokeStyle = vector.color;
  474. }
  475. const dires =
  476. vector.category === UIEvents.MeasureLine
  477. ? [
  478. [start, end],
  479. [end, start],
  480. ]
  481. : [[start, end]];
  482. for (let [start, end] of dires) {
  483. const lines = mathUtil.getArrow(start, end);
  484. ctx.moveTo(lines[0].x, lines[0].y);
  485. ctx.lineTo(lines[1].x, lines[1].y);
  486. ctx.lineTo(lines[2].x, lines[2].y);
  487. }
  488. ctx.stroke();
  489. ctx.restore();
  490. }
  491. drawMagnifier(vector) {
  492. const ctx = this.context;
  493. const [style] = help.setVectorStyle(ctx, vector);
  494. const radius = vector.radius || style.radius;
  495. this.drawPoint({
  496. ...vector,
  497. ...vector.position,
  498. radius,
  499. });
  500. const pt = coordinate.getScreenXY(vector.position);
  501. vector.setPopPosition();
  502. const target = {
  503. x: vector.popPosition.x,
  504. y: vector.popPosition.y,
  505. };
  506. const offset = radius / 2;
  507. const targetPts =
  508. style === Style.Focus.Magnifier
  509. ? [mathUtil.translate(pt, target, pt, radius), target]
  510. : null;
  511. ctx.save();
  512. ctx.beginPath();
  513. ctx.moveTo(pt.x - offset, pt.y);
  514. ctx.lineTo(pt.x + offset, pt.y);
  515. ctx.stroke();
  516. ctx.beginPath();
  517. ctx.moveTo(pt.x, pt.y - offset);
  518. ctx.lineTo(pt.x, pt.y + offset);
  519. ctx.stroke();
  520. if (targetPts) {
  521. ctx.beginPath();
  522. ctx.moveTo(targetPts[0].x, targetPts[0].y);
  523. ctx.lineTo(targetPts[1].x, targetPts[1].y);
  524. ctx.stroke();
  525. let img, imgBound;
  526. if (vector.photoImage) {
  527. img = vector.photoImage;
  528. imgBound = [0, 0, img.width, img.height];
  529. } else {
  530. const size = help.getReal(style.target.realRadius);
  531. const backImg = dataService.getBackgroundImg();
  532. img = backImg.imageData;
  533. const imgCenter = coordinate.getScreenXY(backImg.center);
  534. const start = {
  535. x: imgCenter.x - help.getReal(img.width) / 2,
  536. y: imgCenter.y - help.getReal(img.height) / 2,
  537. };
  538. const ro = img.width / help.getReal(img.width);
  539. imgBound = [
  540. (pt.x - start.x - size) * ro,
  541. (pt.y - start.y - size) * ro,
  542. size * 2 * ro,
  543. size * 2 * ro,
  544. ];
  545. }
  546. const size = style.target.radius;
  547. ctx.beginPath();
  548. ctx.arc(target.x, target.y, size, 0, 2 * Math.PI);
  549. ctx.clip();
  550. ctx.drawImage(
  551. img,
  552. ...imgBound,
  553. target.x - size,
  554. target.y - size,
  555. size * 2,
  556. size * 2
  557. );
  558. ctx.strokeStyle = style.target.strokeStyle;
  559. ctx.lineWidth = style.target.lineWidth;
  560. ctx.stroke();
  561. }
  562. ctx.restore();
  563. }
  564. drawCircle(element) {
  565. const [_, label] = help.getVectorStyle(element);
  566. this.drawPoint({
  567. ...element,
  568. // radius: help.getReal(element.radius),
  569. radius: (element.radius * coordinate.zoom) / coordinate.defaultZoom,
  570. geoType: VectorType.Circle,
  571. ...element.center,
  572. });
  573. console.log(label);
  574. label && element.points.forEach((point) => this.drawPoint(point));
  575. }
  576. drawPoint(vector) {
  577. if (vector.category === VectorCategory.Point.TestBasePoint) {
  578. return;
  579. }
  580. const pt = coordinate.getScreenXY({ x: vector.x, y: vector.y });
  581. const ctx = this.context;
  582. let [style, attr] = help.setVectorStyle(ctx, vector, [
  583. vector.category,
  584. vector.geoType,
  585. "Point",
  586. ]);
  587. if (vector.category === VectorCategory.Point.NormalPoint) {
  588. const lineid = Object.keys(vector.parent)[0];
  589. let line, style;
  590. if (!(lineid && (line = dataService.getLine(lineid)))) {
  591. return;
  592. }
  593. const [_, attr] = help.getVectorStyle(line);
  594. if (!attr) {
  595. return;
  596. }
  597. }
  598. if (vector.color) {
  599. ctx.strokeStyle = vector.color;
  600. }
  601. const draw = (style) => {
  602. const radius = vector.radius || style.radius;
  603. ctx.save();
  604. ctx.beginPath();
  605. ctx.arc(pt.x, pt.y, radius, 0, Math.PI * 2, true);
  606. help.setStyle(ctx, style);
  607. ctx.stroke();
  608. ctx.fill();
  609. ctx.restore();
  610. };
  611. if (Settings.selectBasePointId === vector.vectorId) {
  612. style = {
  613. ...style,
  614. fillStyle: "red",
  615. out: style.out && {
  616. ...style.out,
  617. strokeStyle: "red",
  618. },
  619. };
  620. }
  621. draw(style);
  622. if (style.out) {
  623. draw(style.out);
  624. }
  625. if (import.meta.env.DEV) {
  626. if (vector.vectorId) {
  627. this.drawTextByInfo(vector, vector.vectorId);
  628. }
  629. }
  630. }
  631. drawTextByInfo(position, txt, angle, setStyle = true) {
  632. const ctx = this.context;
  633. ctx.save();
  634. setStyle && help.setVectorStyle(ctx, null, "Text");
  635. const pt = coordinate.getScreenXY(position);
  636. const textCenter = help.getTextCenter(ctx, txt);
  637. // pt.x -= textCenter.x;
  638. // pt.y -= textCenter.y;
  639. if (angle) {
  640. ctx.translate(pt.x, pt.y);
  641. ctx.rotate(angle);
  642. ctx.translate(-textCenter.x, -textCenter.y);
  643. ctx.fillText(txt, 0, 0);
  644. } else {
  645. ctx.fillText(txt, pt.x, pt.y);
  646. }
  647. ctx.restore();
  648. }
  649. // 文字
  650. drawText(vector) {
  651. help.setVectorStyle(this.context, vector);
  652. this.context.fillStyle = vector.color;
  653. const oldFont = this.context.font;
  654. this.context.font = `${
  655. vector.fontSize * coordinate.ratio
  656. }px Microsoft YaHei`;
  657. this.drawTextByInfo(
  658. vector.center,
  659. vector.value,
  660. -(vector.angle || 0),
  661. false
  662. );
  663. const ctx = this.context;
  664. const pt = coordinate.getScreenXY(vector.center);
  665. const text = ctx.measureText(vector.value);
  666. pt.x -= text.width / 2;
  667. pt.y += (text.actualBoundingBoxAscent + text.actualBoundingBoxDescent) / 2;
  668. this.context.font = oldFont;
  669. }
  670. drawSVG(vector) {
  671. const points = vector.points.map(coordinate.getScreenXY.bind(coordinate))
  672. const svgWidth= 64
  673. const svgHidth= 64
  674. const width = mathUtil.getDistance(points[0], points[1])
  675. const height = mathUtil.getDistance(points[0], points[3])
  676. const dires = [points[0], {...points[0], x: 10000}, points[1]]
  677. let angle = mathUtil.Angle(...dires) * (Math.PI / 180)
  678. angle = mathUtil.isClockwise(dires) ? angle : -angle;
  679. this.context.save();
  680. this.context.translate(points[0].x, points[0].y)
  681. this.context.rotate(angle)
  682. this.context.scale(width / svgWidth, height / svgHidth)
  683. const [style] = help.setVectorStyle(this.context, vector)
  684. this.context.lineWidth = style.lineWidth / (width / svgWidth)
  685. SVGIcons.keche_plane.draw(this.context)
  686. if (import.meta.env.DEV) {
  687. this.context.restore();
  688. this.context.beginPath();
  689. this.context.moveTo(points[0].x, points[0].y);
  690. this.context.lineTo(points[1].x, points[1].y);
  691. this.context.lineTo(points[2].x, points[2].y);
  692. this.context.lineTo(points[3].x, points[3].y);
  693. this.context.closePath();
  694. this.context.stroke();
  695. this.context.restore();
  696. }
  697. }
  698. drawLineText(vector, style) {
  699. const startReal = dataService.getPoint(vector.startId);
  700. const endReal = dataService.getPoint(vector.endId);
  701. help.drawLineText(
  702. this.context,
  703. coordinate.getScreenXY(startReal),
  704. coordinate.getScreenXY(endReal),
  705. (vector.value
  706. ? Math.round(vector.value * 100) / 100
  707. : help.getRealDistance(startReal, endReal)) + "m",
  708. style
  709. );
  710. }
  711. drawBaseLineLabel(vector) {
  712. const startReal = dataService.getPoint(vector.startId);
  713. const start = coordinate.getScreenXY(startReal);
  714. const endReal = dataService.getPoint(vector.endId);
  715. const end = coordinate.getScreenXY(endReal);
  716. const point = mathUtil.translate(
  717. end,
  718. start,
  719. end,
  720. mathUtil.getDistance(start, end) / 3
  721. );
  722. const p4 = help.getPerpendicularPoint(
  723. start,
  724. end,
  725. point,
  726. 30 * coordinate.ratio
  727. );
  728. const ctx = this.context;
  729. ctx.beginPath();
  730. const [style] = help.setVectorStyle(
  731. this.context,
  732. vector,
  733. vector.category || vector.geoType
  734. );
  735. ctx.moveTo(point.x, point.y);
  736. ctx.lineTo(p4.x, p4.y);
  737. ctx.stroke();
  738. const p5 = help.getPerpendicularPoint(
  739. start,
  740. end,
  741. point,
  742. 35 * coordinate.ratio
  743. );
  744. this.context.font = `${12 * coordinate.ratio}px Microsoft YaHei`;
  745. help.drawLineText(
  746. this.context,
  747. help.getPerpendicularPoint(point, p5, p5, 10 * coordinate.ratio),
  748. help.getPerpendicularPoint(point, p5, p5, -10 * coordinate.ratio),
  749. "基准线",
  750. {
  751. padding: 6 * coordinate.ratio,
  752. backColor: "rgba(0,0,0,0)",
  753. fillColor: style.strokeStyle,
  754. }
  755. );
  756. }
  757. drawCurveLine(vector) {
  758. // points CurveLine
  759. console.log(vector)
  760. const ctx = this.context;
  761. ctx.save();
  762. help.setVectorStyle(ctx, vector);
  763. help.drawCoves(ctx, help.transformCoves([vector.points]));
  764. ctx.restore();
  765. if (import.meta.env.DEV) {
  766. vector.points.forEach(this.drawPoint.bind(this));
  767. }
  768. }
  769. drawLine(vector) {
  770. const startReal = dataService.getPoint(vector.startId);
  771. const start = coordinate.getScreenXY(startReal);
  772. const endReal = dataService.getPoint(vector.endId);
  773. const end = coordinate.getScreenXY(endReal);
  774. this.context.save();
  775. const [style, attr] = help.setVectorStyle(this.context, vector, [
  776. vector.category,
  777. vector.geoType,
  778. "BaseLine",
  779. ]);
  780. if (style.dash) {
  781. this.context.setLineDash(style.dash);
  782. }
  783. this.context.beginPath();
  784. this.context.moveTo(start.x, start.y);
  785. this.context.lineTo(end.x, end.y);
  786. this.context.stroke();
  787. this.context.restore();
  788. const drawPoints = () => {
  789. // if (attr) {
  790. // this.drawPoint(dataService.getPoint(vector.startId))
  791. // this.drawPoint(dataService.getPoint(vector.endId))
  792. // }
  793. };
  794. switch (vector.category) {
  795. case VectorCategory.Line.ArrowLine:
  796. this.drawArrow(vector);
  797. drawPoints();
  798. break;
  799. case VectorCategory.Line.BaseLine:
  800. this.drawBaseLineLabel(vector);
  801. drawPoints();
  802. break;
  803. case VectorCategory.Line.MeasureLine:
  804. case VectorCategory.Line.PositionLine:
  805. this.drawLineText(vector, style.text);
  806. break;
  807. }
  808. }
  809. drawElementLine(element) {
  810. let start = elementService.getPoint(element.startId);
  811. start = coordinate.getScreenXY(start);
  812. let end = elementService.getPoint(element.endId);
  813. end = coordinate.getScreenXY(end);
  814. this.context.save();
  815. const [style] = help.setVectorStyle(
  816. this.context,
  817. element,
  818. element.category || element.geoType
  819. );
  820. if (style.dash) {
  821. this.context.setLineDash(style.dash);
  822. }
  823. this.context.beginPath();
  824. this.context.moveTo(start.x, start.y);
  825. this.context.lineTo(end.x, end.y);
  826. this.context.stroke();
  827. this.context.restore();
  828. }
  829. }
  830. const draw = new Draw();
  831. export { draw };