Draw.js 23 KB

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