Draw.js 24 KB

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