Draw.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440
  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 { mathUtil } from "../Util/MathUtil.js";
  6. import { elementService } from "../Service/ElementService.js";
  7. import UIEvents from "@/graphic/enum/UIEvents.js";
  8. import VectorCategory from "@/graphic/enum/VectorCategory.js";
  9. import Settings from "@/graphic/Settings.js";
  10. import SVGIcons from "../CanvasStyle/ImageLabels/SVGIcons";
  11. import VectorStyle from "@/graphic/enum/VectorStyle.js";
  12. import VectorWeight from "@/graphic/enum/VectorWeight.js";
  13. const imgCache = {};
  14. export const help = {
  15. getVectorStyle(vector, geoType = vector.geoType) {
  16. const geoId = vector?.vectorId;
  17. if (!geoId || Settings.screenMode) {
  18. return [Style[geoType], undefined];
  19. }
  20. const itemsEntry = [
  21. [stateService.getSelectItem(), "Select"],
  22. [stateService.getDraggingItem(), "Dragging"],
  23. [stateService.getFocusItem(), "Focus"],
  24. ];
  25. console.log(itemsEntry);
  26. let currentAttr;
  27. //console.log(itemsEntry)
  28. return [
  29. itemsEntry.reduce((prev, [item, attr]) => {
  30. if (!item) return prev;
  31. const selected =
  32. geoId === item.vectorId ||
  33. (item.parent && Object.keys(item.parent).some((id) => id === geoId));
  34. if (selected && Style[attr]) {
  35. const style = Style[attr][geoType] || Style[attr][vector.category];
  36. if (style) {
  37. currentAttr = attr;
  38. return style;
  39. }
  40. }
  41. return prev;
  42. }, Style[geoType]),
  43. currentAttr,
  44. ];
  45. },
  46. setStyle(ctx, styles) {
  47. for (const style in styles) {
  48. if (typeof styles[style] === "function") {
  49. styles[style](ctx, vector);
  50. } else {
  51. ctx[style] = styles[style];
  52. }
  53. }
  54. },
  55. setVectorStyle(ctx, vector, geoType = vector.geoType) {
  56. let styles, attr;
  57. if (Array.isArray(geoType)) {
  58. for (const type of geoType) {
  59. [styles, attr] = help.getVectorStyle(vector, type);
  60. if (styles) {
  61. break;
  62. }
  63. }
  64. } else {
  65. [styles, attr] = help.getVectorStyle(vector, geoType);
  66. }
  67. help.setStyle(ctx, styles);
  68. return [styles, attr];
  69. },
  70. transformCoves(lines) {
  71. return lines.map((line) =>
  72. line.map((line) => ({
  73. start: coordinate.getScreenXY(line.start),
  74. end: coordinate.getScreenXY(line.end),
  75. controls: line.controls.map(coordinate.getScreenXY.bind(coordinate)),
  76. }))
  77. );
  78. },
  79. drawCove(ctx, curve) {
  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. },
  98. drawCoves(ctx, coves) {
  99. for (const curve of coves) {
  100. ctx.beginPath();
  101. ctx.moveTo(curve.start.x, curve.start.y);
  102. help.drawCove(ctx, curve);
  103. ctx.stroke();
  104. }
  105. },
  106. getReal(data) {
  107. return (data * coordinate.ratio * coordinate.zoom) / coordinate.defaultZoom;
  108. },
  109. getImage(src) {
  110. if (imgCache[src]) {
  111. return imgCache[src];
  112. }
  113. const img = new Image();
  114. img.src = src;
  115. return (imgCache[src] = new Promise((resolve) => {
  116. img.onload = () => {
  117. resolve(img);
  118. };
  119. }));
  120. },
  121. getTextCenter(ctx, txt) {
  122. const text = ctx.measureText(txt);
  123. const height = text.actualBoundingBoxAscent + text.actualBoundingBoxDescent;
  124. return {
  125. width: text.width,
  126. height,
  127. x: text.width / 2,
  128. y: -height / 2,
  129. };
  130. },
  131. // 绘制圆角矩形
  132. roundRect(ctx, x, y, width, height, radius) {
  133. ctx.beginPath();
  134. ctx.moveTo(x + radius, y);
  135. ctx.lineTo(x + width - radius, y);
  136. ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
  137. ctx.lineTo(x + width, y + height - radius);
  138. ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
  139. ctx.lineTo(x + radius, y + height);
  140. ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
  141. ctx.lineTo(x, y + radius);
  142. ctx.quadraticCurveTo(x, y, x + radius, y);
  143. ctx.closePath();
  144. },
  145. getRealDistance(p1, p2) {
  146. return (
  147. Math.round(
  148. (mathUtil.getDistance(p1, p2) * coordinate.res * 100) / coordinate.ratio
  149. ) / 100
  150. );
  151. },
  152. getPerpendicularPoint(p1, p2, p3, d) {
  153. if (p1.x === p2.x) {
  154. return { x: p3.x + d, y: p3.y };
  155. } else if (p1.y === p2.y) {
  156. return { x: p3.x, y: p3.y + d };
  157. }
  158. // 计算通过 p1 和 p2 的直线的斜率和截距
  159. const slope = (p2.y - p1.y) / (p2.x - p1.x);
  160. const intercept = p1.y - slope * p1.x;
  161. // 计算垂直线的斜率和截距
  162. const perpendicularSlope = -1 / slope;
  163. const perpendicularIntercept = p3.y - perpendicularSlope * p3.x;
  164. // 计算垂足点 p0
  165. const x =
  166. (perpendicularIntercept - intercept) / (slope - perpendicularSlope);
  167. const y = slope * x + intercept;
  168. const p0 = { x, y };
  169. // 计算点 p4
  170. const distance = d; // 指定距离
  171. const dx = distance / Math.sqrt(1 + perpendicularSlope ** 2);
  172. const dy = perpendicularSlope * dx;
  173. return { x: p0.x + dx, y: p0.y + dy };
  174. },
  175. drawLineText(ctx, start, end, text, style) {
  176. if (start.x > end.x) {
  177. [start, end] = [end, start];
  178. }
  179. const angle =
  180. (Math.atan2(end.y - start.y, end.x - start.x) * 180) / Math.PI;
  181. const center = mathUtil.lineCenter(start, end);
  182. ctx.save();
  183. ctx.translate(center.x, center.y);
  184. ctx.rotate((angle * Math.PI) / 180);
  185. ctx.font = `${(style.fontSize || 10) * coordinate.ratio}px Microsoft YaHei`;
  186. const textCenter = help.getTextCenter(ctx, text);
  187. const padding = style.padding;
  188. help.roundRect(
  189. ctx,
  190. -textCenter.x - padding,
  191. textCenter.y - padding,
  192. textCenter.width + 2 * padding,
  193. textCenter.height + 2 * padding,
  194. textCenter.height / 2 + padding
  195. );
  196. ctx.fillStyle = style.backColor;
  197. ctx.fill();
  198. ctx.fillStyle = style.fillColor;
  199. ctx.fillText(text, -textCenter.x, -textCenter.y);
  200. ctx.restore();
  201. },
  202. isTriangleClockwise(p1, p2, p3) {
  203. const crossProduct =
  204. (p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y);
  205. return crossProduct < 0;
  206. },
  207. drawStyleLine(
  208. ctx,
  209. line,
  210. style = VectorStyle.SingleSolidLine,
  211. weight = VectorStyle.Thinning
  212. ) {
  213. ctx.save();
  214. style = style || VectorStyle.SingleSolidLine;
  215. ctx.beginPath();
  216. const lineWidth =
  217. Settings.lineWidth *
  218. (ctx.lineWidth || 1) *
  219. (weight === VectorWeight.Bold ? 2 : 1);
  220. const funStyle = [
  221. VectorStyle.PointDrawLine,
  222. VectorStyle.SingleDashedLine,
  223. VectorStyle.SingleSolidLine,
  224. ];
  225. if (typeof line === "function" && !funStyle.includes(style)) {
  226. style = VectorStyle.SingleSolidLine;
  227. }
  228. switch (style) {
  229. case VectorStyle.PointDrawLine:
  230. case VectorStyle.SingleDashedLine:
  231. case VectorStyle.SingleSolidLine:
  232. ctx.lineWidth = lineWidth;
  233. if (style === VectorStyle.SingleDashedLine) {
  234. ctx.setLineDash([8 * coordinate.ratio, 8 * coordinate.ratio]);
  235. } else if (style === VectorStyle.PointDrawLine) {
  236. ctx.setLineDash([
  237. 6 * coordinate.ratio,
  238. 6 * coordinate.ratio,
  239. 2 * coordinate.ratio,
  240. ]);
  241. }
  242. if (typeof line === "function") {
  243. line();
  244. } else {
  245. ctx.moveTo(line[0].x, line[0].y);
  246. ctx.lineTo(line[1].x, line[1].y);
  247. }
  248. break;
  249. // 单实线
  250. case VectorStyle.DoubleDashedLine:
  251. case VectorStyle.DoubleSolidLine:
  252. ctx.lineWidth = lineWidth;
  253. if (style === VectorStyle.DoubleDashedLine) {
  254. ctx.setLineDash([8 * coordinate.ratio, 8 * coordinate.ratio]);
  255. }
  256. const pd1 = help.getPerpendicularPoint(
  257. line[0],
  258. line[1],
  259. line[0],
  260. 4 * coordinate.ratio
  261. );
  262. const pd2 = help.getPerpendicularPoint(
  263. line[0],
  264. line[1],
  265. line[1],
  266. 4 * coordinate.ratio
  267. );
  268. const pd3 = help.getPerpendicularPoint(
  269. line[0],
  270. line[1],
  271. line[0],
  272. -4 * coordinate.ratio
  273. );
  274. const pd4 = help.getPerpendicularPoint(
  275. line[0],
  276. line[1],
  277. line[1],
  278. -4 * coordinate.ratio
  279. );
  280. ctx.moveTo(pd1.x, pd1.y);
  281. ctx.lineTo(pd2.x, pd2.y);
  282. ctx.stroke();
  283. ctx.moveTo(pd3.x, pd3.y);
  284. ctx.lineTo(pd4.x, pd4.y);
  285. break;
  286. case VectorStyle.BrokenLine:
  287. const ldis = 5 * coordinate.ratio;
  288. if (mathUtil.getDistance(...line) < ldis * 2) {
  289. ctx.moveTo(line[0].x, line[0].y);
  290. ctx.lineTo(line[1].x, line[1].y);
  291. } else {
  292. const start = mathUtil.translate(line[0], line[1], line[0], ldis);
  293. const end = mathUtil.translate(line[0], line[1], line[1], -ldis);
  294. const lineDis = mathUtil.getDistance(start, end);
  295. const len = Math.ceil(lineDis / (6 * coordinate.ratio));
  296. const split = lineDis / len;
  297. const points = [start];
  298. let temp = start;
  299. for (let i = 0; i < len; i++) {
  300. temp = mathUtil.translate(temp, line[1], temp, split);
  301. points.push(temp);
  302. }
  303. ctx.moveTo(line[0].x, line[0].y);
  304. ctx.lineTo(start.x, start.y);
  305. for (let i = 0; i < points.length - 1; i++) {
  306. const vTop = help.getPerpendicularPoint(
  307. points[i],
  308. points[i + 1],
  309. mathUtil.lineCenter(points[i], points[i + 1]),
  310. (split * (i % 2 ? -1 : 1)) / 2
  311. );
  312. ctx.lineTo(vTop.x, vTop.y);
  313. }
  314. ctx.lineTo(end.x, end.y);
  315. ctx.lineTo(line[1].x, line[1].y);
  316. }
  317. ctx.lineWidth = lineWidth;
  318. break;
  319. case VectorStyle.Greenbelt:
  320. const dis = 4 * coordinate.ratio;
  321. const size = 8 * coordinate.ratio;
  322. const p1 = help.getPerpendicularPoint(line[0], line[1], line[0], dis);
  323. const p2 = help.getPerpendicularPoint(line[0], line[1], line[1], dis);
  324. const p3 = help.getPerpendicularPoint(p1, p2, p2, size);
  325. const p4 = help.getPerpendicularPoint(p1, p2, p1, size);
  326. ctx.beginPath();
  327. ctx.lineWidth = lineWidth;
  328. ctx.moveTo(line[0].x, line[0].y);
  329. ctx.lineTo(line[1].x, line[1].y);
  330. ctx.stroke();
  331. ctx.beginPath();
  332. ctx.moveTo(p4.x, p4.y);
  333. ctx.lineTo(p1.x, p1.y);
  334. ctx.lineTo(p2.x, p2.y);
  335. ctx.lineTo(p3.x, p3.y);
  336. ctx.stroke();
  337. const rdis = 6 * coordinate.ratio;
  338. const lineDis = mathUtil.getDistance(p3, p4);
  339. const len = Math.ceil(lineDis / rdis);
  340. const split = lineDis / len;
  341. const points = [p3];
  342. const geo = [p4, { ...p4, x: 999 }, p3];
  343. let angle = (mathUtil.Angle1(...geo) / 180) * Math.PI;
  344. const isClock = help.isTriangleClockwise(...geo) || angle === 0;
  345. angle = isClock ? -angle : angle;
  346. let temp = p3;
  347. for (let i = 0; i < len; i++) {
  348. temp = mathUtil.translate(temp, p4, temp, split);
  349. points.push(temp);
  350. }
  351. for (let i = 0; i < points.length - 1; i++) {
  352. const center = mathUtil.lineCenter(points[i], points[i + 1]);
  353. ctx.beginPath();
  354. ctx.arc(
  355. center.x,
  356. center.y,
  357. split / 2,
  358. angle,
  359. angle + Math.PI,
  360. !isClock
  361. );
  362. ctx.stroke();
  363. }
  364. ctx.lineWidth = lineWidth;
  365. break;
  366. }
  367. ctx.stroke();
  368. ctx.restore();
  369. },
  370. };
  371. export default class Draw {
  372. constructor() {
  373. this.canvas = null;
  374. this.context = null;
  375. }
  376. initContext(canvas) {
  377. if (canvas) {
  378. this.canvas = canvas;
  379. this.context = canvas.getContext("2d");
  380. const saveRaw = this.context.save.bind(this.context);
  381. const restoreRaw = this.context.restore.bind(this.context);
  382. let index = 0;
  383. this.context.save = saveRaw;
  384. // this.context.save = () => {
  385. // index++;
  386. // console.error("save", index);
  387. // saveRaw();
  388. // };
  389. this.context.restore = restoreRaw;
  390. // this.context.restore = () => {
  391. // index--;
  392. // console.log("restore", index);
  393. // restoreRaw();
  394. // };
  395. } else {
  396. this.context = null;
  397. this.canvas = null;
  398. }
  399. }
  400. clear() {
  401. this.context.clearRect(
  402. 0,
  403. 0,
  404. this.context.canvas.width,
  405. this.context.canvas.height
  406. );
  407. }
  408. drawBackGroundImg(vector) {
  409. if (!vector.display) {
  410. return;
  411. }
  412. const img = vector.imageData;
  413. const width = help.getReal(img.width);
  414. const height = help.getReal(img.height);
  415. const center = coordinate.getScreenXY(vector.center);
  416. this.context.save();
  417. if (vector.scale) {
  418. this.context.translate(center.x, center.y);
  419. this.context.scale(vector.scale, vector.scale);
  420. this.context.translate(-center.x, -center.y);
  421. }
  422. this.context.drawImage(
  423. img,
  424. center.x - width / 2,
  425. center.y - height / 2,
  426. width,
  427. height
  428. );
  429. this.context.restore();
  430. }
  431. drawGrid(startX, startY, w, h, step1, step2) {
  432. this.context.save();
  433. this.context.beginPath();
  434. for (var x = startX; x <= w; x += step1) {
  435. this.context.moveTo(x, 0);
  436. this.context.lineTo(x, h);
  437. }
  438. for (var y = startY; y <= h; y += step1) {
  439. this.context.moveTo(0, y);
  440. this.context.lineTo(w, y);
  441. }
  442. this.context.strokeStyle = "rgba(0,0,0,0.1)";
  443. this.context.lineWidth = 0.5 * coordinate.ratio;
  444. this.context.stroke();
  445. this.context.beginPath();
  446. for (var x = startX; x <= w; x += step2) {
  447. this.context.moveTo(x, 0);
  448. this.context.lineTo(x, h);
  449. }
  450. for (var y = startY; y <= h; y += step2) {
  451. this.context.moveTo(0, y);
  452. this.context.lineTo(w, y);
  453. }
  454. this.context.strokeStyle = "rgba(0,0,0,0.2)";
  455. this.context.lineWidth = 1 * coordinate.ratio;
  456. this.context.stroke();
  457. this.context.restore();
  458. }
  459. drawRoad(vector, isTemp) {
  460. const [styles, label] = help.getVectorStyle(vector);
  461. if (!isTemp && vector.display && vector.way !== "oneWay") {
  462. const ctx = this.context;
  463. const draw = (midDivide) => {
  464. const startScreen = coordinate.getScreenXY(midDivide.start);
  465. const endScreen = coordinate.getScreenXY(midDivide.end);
  466. ctx.beginPath();
  467. if (label) {
  468. help.setStyle(ctx, Style.Focus.Road);
  469. }
  470. ctx.moveTo(startScreen.x, startScreen.y);
  471. ctx.lineTo(endScreen.x, endScreen.y);
  472. ctx.stroke();
  473. };
  474. ctx.save();
  475. help.setStyle(ctx, styles);
  476. vector.midDivide.leftMidDivide && draw(vector.midDivide.leftMidDivide);
  477. vector.midDivide.rightMidDivide && draw(vector.midDivide.rightMidDivide);
  478. ctx.restore();
  479. }
  480. if (import.meta.env.DEV && !isTemp) {
  481. const startReal = isTemp
  482. ? vector.start
  483. : dataService.getRoadPoint(vector.startId);
  484. const endReal = isTemp
  485. ? vector.end
  486. : dataService.getRoadPoint(vector.endId);
  487. this.drawTextByInfo(
  488. { x: (startReal.x + endReal.x) / 2, y: (startReal.y + endReal.y) / 2 },
  489. vector.vectorId
  490. );
  491. }
  492. this.drawRoadEdge(vector, isTemp);
  493. if (vector.way === "oneWay") {
  494. vector.singleLanes &&
  495. vector.singleLanes.forEach((g) => this.drawLan(g, !!label));
  496. } else {
  497. vector.leftLanes &&
  498. vector.leftLanes.forEach((g) => this.drawLan(g, !!label));
  499. vector.rightLanes &&
  500. vector.rightLanes.forEach((g) => this.drawLan(g, !!label));
  501. }
  502. }
  503. drawLan(lan, focus) {
  504. const ctx = this.context;
  505. const start = coordinate.getScreenXY(lan.start);
  506. const end = coordinate.getScreenXY(lan.end);
  507. ctx.save();
  508. ctx.beginPath();
  509. help.setVectorStyle(ctx, null, "Lane");
  510. if (focus) {
  511. ctx.strokeStyle = "rgba(255, 143, 40, 1)";
  512. }
  513. ctx.lineWidth *= Settings.lineWidth;
  514. ctx.setLineDash(Style.Lane.dash);
  515. ctx.moveTo(start.x, start.y);
  516. ctx.lineTo(end.x, end.y);
  517. ctx.stroke();
  518. ctx.restore();
  519. if (import.meta.env.DEV) {
  520. // this.drawPoint(lan.start);
  521. // this.drawPoint(lan.end);
  522. }
  523. }
  524. drawRoadEdge(vector, isTemp) {
  525. //判断是否与road方向一致。角度足够小,路足够宽,有可能向量方向不一致
  526. const start = isTemp
  527. ? vector.start
  528. : dataService.getRoadPoint(vector.startId);
  529. const end = isTemp ? vector.end : dataService.getRoadPoint(vector.endId);
  530. const drawRoadEdgeChild = (edgeVector) => {
  531. const flag = mathUtil.isSameDirForVector(
  532. start,
  533. end,
  534. edgeVector.start,
  535. edgeVector.end
  536. );
  537. if (flag) {
  538. const point1 = coordinate.getScreenXY(edgeVector.start);
  539. const point2 = coordinate.getScreenXY(edgeVector.end);
  540. help.drawStyleLine(
  541. ctx,
  542. [point1, point2],
  543. edgeVector.style,
  544. edgeVector.weight
  545. );
  546. }
  547. if (import.meta.env.DEV) {
  548. this.drawTextByInfo(
  549. {
  550. x: (edgeVector.start.x + edgeVector.end.x) / 2,
  551. y: (edgeVector.start.y + edgeVector.end.y) / 2,
  552. },
  553. edgeVector.vectorId
  554. );
  555. }
  556. };
  557. const leftEdge = isTemp
  558. ? vector.leftEdge
  559. : dataService.getRoadEdge(vector.leftEdgeId);
  560. const rightEdge = isTemp
  561. ? vector.rightEdge
  562. : dataService.getRoadEdge(vector.rightEdgeId);
  563. const ctx = this.context;
  564. ctx.save();
  565. isTemp && (ctx.globalAlpha = 0.3);
  566. help.setVectorStyle(ctx, leftEdge);
  567. let [style, fo] = help.getVectorStyle(vector);
  568. fo && help.setStyle(ctx, style);
  569. drawRoadEdgeChild(leftEdge);
  570. help.setVectorStyle(ctx, rightEdge);
  571. fo && help.setStyle(ctx, style);
  572. drawRoadEdgeChild(rightEdge);
  573. ctx.restore();
  574. if (fo) {
  575. ctx.save();
  576. const p1 = coordinate.getScreenXY(leftEdge.start);
  577. const p2 = coordinate.getScreenXY(rightEdge.start);
  578. const p3 = coordinate.getScreenXY(leftEdge.end);
  579. const p4 = coordinate.getScreenXY(rightEdge.end);
  580. ctx.lineWidth = 1 * coordinate.ratio;
  581. ctx.setLineDash([5 * coordinate.ratio, 5 * coordinate.ratio]);
  582. ctx.strokeStyle = Style.Road.strokeStyle;
  583. // ctx.beginPath();
  584. // ctx.moveTo(p1.x, p1.y);
  585. // ctx.lineTo(p2.x, p2.y);
  586. // ctx.stroke();
  587. // ctx.beginPath();
  588. // ctx.moveTo(p3.x, p3.y);
  589. // ctx.lineTo(p4.x, p4.y);
  590. // ctx.stroke();
  591. ctx.fillStyle = "rgba(255, 153, 0, 0.30)";
  592. ctx.moveTo(p1.x, p1.y);
  593. ctx.lineTo(p2.x, p2.y);
  594. ctx.lineTo(p4.x, p4.y);
  595. ctx.lineTo(p3.x, p3.y);
  596. ctx.fill();
  597. ctx.restore();
  598. }
  599. if (import.meta.env.DEV) {
  600. // this.drawPoint(leftEdge.start);
  601. // this.drawPoint(leftEdge.end);
  602. // this.drawPoint(rightEdge.start);
  603. // this.drawPoint(rightEdge.end);
  604. }
  605. }
  606. drawCrossPoint(vector) {
  607. const start = coordinate.getScreenXY(
  608. dataService
  609. .getRoadEdge(vector.edgeInfo1.id)
  610. .getPosition(vector.edgeInfo1.dir)
  611. );
  612. const end = coordinate.getScreenXY(
  613. dataService
  614. .getRoadEdge(vector.edgeInfo2.id)
  615. .getPosition(vector.edgeInfo2.dir)
  616. );
  617. const pt2 = mathUtil.twoOrderBezier(
  618. 0.5,
  619. start,
  620. coordinate.getScreenXY({ x: vector.x, y: vector.y }),
  621. end
  622. );
  623. const pt = mathUtil.twoOrderBezier2(0.5, start, pt2, end);
  624. const extremePoint = coordinate.getScreenXY(vector.extremePoint);
  625. const ctx = this.context;
  626. ctx.save();
  627. help.setVectorStyle(ctx, vector);
  628. ctx.beginPath();
  629. if (!Settings.screenMode) {
  630. ctx.arc(
  631. extremePoint.x,
  632. extremePoint.y,
  633. Style.CrossPoint.radius * coordinate.ratio,
  634. 0,
  635. Math.PI * 2,
  636. true
  637. );
  638. }
  639. ctx.stroke();
  640. ctx.fill();
  641. ctx.restore();
  642. ctx.save();
  643. ctx.beginPath();
  644. help.setVectorStyle(ctx, null, "RoadEdge");
  645. //曲线
  646. // ctx.moveTo(start.x, start.y);
  647. // ctx.quadraticCurveTo(pt.x, pt.y, end.x, end.y);
  648. help.drawStyleLine(
  649. ctx,
  650. () => {
  651. const [coves] = help.transformCoves([vector.curves]);
  652. help.drawCoves(ctx, coves);
  653. },
  654. vector.style,
  655. vector.weight
  656. );
  657. ctx.restore();
  658. }
  659. drawCurveRoad(vector) {
  660. const ctx = this.context;
  661. ctx.save();
  662. let midCovesArray;
  663. const [_, foo] = help.setVectorStyle(ctx, vector);
  664. if (vector.display && vector.midDivide) {
  665. midCovesArray = help.transformCoves([
  666. vector.midDivide.leftMidDivideCurves,
  667. // vector.midDivide.rightMidDivideCurves,
  668. ]);
  669. ctx.setLineDash([8 * coordinate.ratio, 8 * coordinate.ratio]);
  670. ctx.lineWidth *= Settings.lineWidth;
  671. for (let coves of midCovesArray) {
  672. help.drawCoves(ctx, coves);
  673. }
  674. // midCovesArray = help.transformCoves([
  675. // vector.curves,
  676. // ]);
  677. // ctx.strokeStyle = 'red'
  678. // for (let coves of midCovesArray) {
  679. // help.drawCoves(ctx, coves);
  680. // }
  681. }
  682. ctx.restore();
  683. this.drawCurveRoadEdge(
  684. dataService.getCurveRoadEdge(vector.rightEdgeId),
  685. vector
  686. );
  687. this.drawCurveRoadEdge(
  688. dataService.getCurveRoadEdge(vector.leftEdgeId),
  689. vector
  690. );
  691. vector.leftLanesCurves &&
  692. vector.leftLanesCurves.forEach((data) => this.drawCurveLan(data, foo));
  693. vector.rightLanesCurves &&
  694. vector.rightLanesCurves.forEach((data) => this.drawCurveLan(data, foo));
  695. if (foo) {
  696. const leftEdge = dataService.getCurveRoadEdge(vector.leftEdgeId);
  697. const rightEdge = dataService.getCurveRoadEdge(vector.rightEdgeId);
  698. // const p1 = coordinate.getScreenXY(leftEdge.start);
  699. // const p2 = coordinate.getScreenXY(rightEdge.start);
  700. // const p3 = coordinate.getScreenXY(leftEdge.end);
  701. // const p4 = coordinate.getScreenXY(rightEdge.end);
  702. // ctx.save();
  703. // ctx.setLineDash([5 * coordinate.ratio, 5 * coordinate.ratio]);
  704. // ctx.lineWidth = 1 * coordinate.ratio;
  705. // ctx.strokeStyle = Style.Lane.strokeStyle;
  706. // ctx.beginPath();
  707. // ctx.moveTo(p1.x, p1.y);
  708. // ctx.lineTo(p2.x, p2.y);
  709. // ctx.stroke();
  710. // ctx.beginPath();
  711. // ctx.moveTo(p3.x, p3.y);
  712. // ctx.lineTo(p4.x, p4.y);
  713. // ctx.stroke();
  714. if (midCovesArray) {
  715. const edgeCurves = help.transformCoves([
  716. leftEdge.curves,
  717. rightEdge.curves,
  718. ]);
  719. edgeCurves[1] = edgeCurves[1].reverse().map((curve) => ({
  720. start: curve.end,
  721. end: curve.start,
  722. controls: curve.controls.reverse(),
  723. }));
  724. ctx.beginPath();
  725. ctx.setLineDash([]);
  726. ctx.moveTo(edgeCurves[0][0].start.x, edgeCurves[0][0].start.y);
  727. edgeCurves[0].forEach((cuve) => help.drawCove(ctx, cuve));
  728. ctx.lineTo(edgeCurves[1][0].start.x, edgeCurves[1][0].start.y);
  729. edgeCurves[1].forEach((cuve) => help.drawCove(ctx, cuve));
  730. ctx.closePath();
  731. ctx.fillStyle = "rgba(255, 153, 0, 0.30)";
  732. ctx.fill();
  733. }
  734. ctx.restore();
  735. }
  736. // if (import.meta.env.DEV) {
  737. vector.points.forEach(this.drawPoint.bind(this));
  738. // }
  739. }
  740. drawCurveRoadEdge(vector, roadVector) {
  741. const [coves] = help.transformCoves([vector.curves]);
  742. const ctx = this.context;
  743. const [style, select] = help.getVectorStyle(roadVector);
  744. ctx.save();
  745. help.setVectorStyle(ctx, vector);
  746. select && help.setStyle(ctx, style);
  747. // ctx.lineWidth *= Settings.lineWidth;
  748. help.drawStyleLine(
  749. this.context,
  750. () => {
  751. help.drawCoves(ctx, coves);
  752. },
  753. vector.style,
  754. vector.weight
  755. );
  756. // help.drawCoves(ctx, coves);
  757. ctx.restore();
  758. if (import.meta.env.DEV) {
  759. // vector.points.forEach(this.drawPoint.bind(this));
  760. }
  761. }
  762. drawCurveLan(lines, focus) {
  763. const [coves] = help.transformCoves([lines]);
  764. const ctx = this.context;
  765. ctx.save();
  766. help.setVectorStyle(ctx, null, "CurveLan");
  767. ctx.lineWidth *= Settings.lineWidth;
  768. if (focus) {
  769. ctx.strokeStyle = "rgba(255, 153, 0, 1)";
  770. ctx.lineWidth *= 2;
  771. }
  772. ctx.setLineDash(Style.Lane.dash);
  773. help.drawCoves(ctx, coves);
  774. ctx.restore();
  775. // if (import.meta.env.DEV) {
  776. // lines.map((line) => {
  777. // this.drawPoint(line.start);
  778. // this.drawPoint(line.end);
  779. // });
  780. // }
  781. }
  782. drawRoadPoint(vector) {
  783. this.drawPoint(vector);
  784. }
  785. drawLineArrow(line, doubleArrow = false) {
  786. const ctx = this.context;
  787. const [start, end] = line;
  788. const dires = doubleArrow
  789. ? [
  790. [start, end],
  791. [end, start],
  792. ]
  793. : [[start, end]];
  794. ctx.save();
  795. for (let [start, end] of dires) {
  796. const lines = mathUtil.getArrow(start, end);
  797. ctx.moveTo(lines[0].x, lines[0].y);
  798. ctx.lineTo(lines[1].x, lines[1].y);
  799. ctx.lineTo(lines[2].x, lines[2].y);
  800. }
  801. ctx.stroke();
  802. ctx.restore();
  803. }
  804. drawArrow(vector) {
  805. const startReal = dataService.getPoint(vector.startId);
  806. const start = coordinate.getScreenXY(startReal);
  807. const endReal = dataService.getPoint(vector.endId);
  808. const end = coordinate.getScreenXY(endReal);
  809. const ctx = this.context;
  810. ctx.save();
  811. help.setVectorStyle(this.context, vector);
  812. if (vector.color) {
  813. ctx.strokeStyle = vector.color;
  814. }
  815. this.drawLineArrow([start, end], vector.category === UIEvents.DoubleArrow);
  816. }
  817. drawMagnifier(vector) {
  818. const ctx = this.context;
  819. ctx.save();
  820. const [style] = help.setVectorStyle(ctx, vector);
  821. const radius = vector.radius || style.radius;
  822. this.drawPoint({
  823. ...vector,
  824. ...vector.position,
  825. radius,
  826. });
  827. console.log(vector);
  828. const pt = coordinate.getScreenXY(vector.position);
  829. // vector.setPopPosition();
  830. const target = {
  831. x: vector.popPosition.x,
  832. y: vector.popPosition.y,
  833. };
  834. const offset = radius / 2;
  835. const targetPts = [mathUtil.translate(pt, target, pt, radius), target];
  836. ctx.beginPath();
  837. ctx.moveTo(pt.x - offset, pt.y);
  838. ctx.lineTo(pt.x + offset, pt.y);
  839. ctx.stroke();
  840. ctx.beginPath();
  841. ctx.moveTo(pt.x, pt.y - offset);
  842. ctx.lineTo(pt.x, pt.y + offset);
  843. ctx.stroke();
  844. if (targetPts) {
  845. ctx.beginPath();
  846. ctx.moveTo(targetPts[0].x, targetPts[0].y);
  847. ctx.lineTo(targetPts[1].x, targetPts[1].y);
  848. ctx.stroke();
  849. let img, imgBound;
  850. if (vector.photoImage) {
  851. img = vector.photoImage;
  852. let top = 0,
  853. left = 0,
  854. size = 0;
  855. if (img.width > img.height) {
  856. size = img.height;
  857. left = (img.width - size) / 2;
  858. } else {
  859. size = img.width;
  860. top = (img.height - size) / 2;
  861. }
  862. imgBound = [left, top, size, size];
  863. } else {
  864. const backImg = dataService.getBackgroundImg();
  865. const size = help.getReal(style.target.realRadius);
  866. img = backImg.imageData;
  867. const imgCenter = coordinate.getScreenXY(backImg.center);
  868. const width = img.width * backImg.scale;
  869. const height = img.height * backImg.scale;
  870. const start = {
  871. x: imgCenter.x - help.getReal(width) / 2,
  872. y: imgCenter.y - help.getReal(height) / 2,
  873. };
  874. const pts = pt;
  875. const ro = width / help.getReal(width);
  876. imgBound = [
  877. ((pts.x - start.x - size) * ro) / backImg.scale,
  878. ((pts.y - start.y - size) * ro) / backImg.scale,
  879. (size * 2 * ro) / backImg.scale,
  880. (size * 2 * ro) / backImg.scale,
  881. ];
  882. }
  883. const size = style.target.radius;
  884. ctx.beginPath();
  885. ctx.arc(target.x, target.y, size, 0, 2 * Math.PI);
  886. ctx.clip();
  887. ctx.drawImage(
  888. img,
  889. ...imgBound,
  890. target.x - size,
  891. target.y - size,
  892. size * 2,
  893. size * 2
  894. );
  895. ctx.strokeStyle = style.target.strokeStyle;
  896. ctx.lineWidth = style.target.lineWidth;
  897. ctx.stroke();
  898. }
  899. ctx.restore();
  900. }
  901. drawElliptic(element, radiusX = element.radiusX, radiusY = element.radiusY) {
  902. function drawEllipse(context, x, y, a, b) {
  903. const step = a > b ? 1 / a : 1 / b;
  904. context.beginPath();
  905. context.moveTo(x + a, y);
  906. for (let i = 0; i < 2 * Math.PI; i += step) {
  907. context.lineTo(x + a * Math.cos(i), y + b * Math.sin(i));
  908. }
  909. context.closePath();
  910. }
  911. const pt = coordinate.getScreenXY({
  912. x: element.center.x,
  913. y: element.center.y,
  914. });
  915. const ctx = this.context;
  916. ctx.save();
  917. const [_, label] = help.setVectorStyle(ctx, element);
  918. ctx.strokeStyle = element.color;
  919. drawEllipse(
  920. ctx,
  921. pt.x,
  922. pt.y,
  923. (radiusX * coordinate.zoom) / coordinate.defaultZoom,
  924. (radiusY * coordinate.zoom) / coordinate.defaultZoom
  925. );
  926. ctx.stroke();
  927. ctx.fill();
  928. ctx.restore();
  929. }
  930. drawCircle(element) {
  931. this.context.save();
  932. const geo = [
  933. element.center,
  934. element.points[1],
  935. { ...element.center, x: 999 },
  936. ];
  937. let angle = mathUtil.Angle(...geo);
  938. angle = help.isTriangleClockwise(...geo) ? -angle : angle;
  939. const center = coordinate.getScreenXY(element.center);
  940. this.context.translate(center.x, center.y);
  941. this.context.rotate((angle / 180) * Math.PI);
  942. this.context.translate(-center.x, -center.y);
  943. this.drawElliptic(element, element.radiusX, element.radiusY);
  944. this.context.restore();
  945. const [_, label] = help.getVectorStyle(element);
  946. label && element.points.forEach((point) => this.drawPoint(point));
  947. }
  948. drawPoint(vector, screenSave) {
  949. const screenNotDrawTypes = [VectorCategory.Point.NormalPoint];
  950. if (!screenSave) {
  951. if (
  952. (Settings.screenMode &&
  953. (!vector.category || screenNotDrawTypes.includes(vector.category))) ||
  954. vector.category === VectorCategory.Point.TestBasePoint
  955. ) {
  956. return;
  957. }
  958. }
  959. const pt = coordinate.getScreenXY({ x: vector.x, y: vector.y });
  960. const ctx = this.context;
  961. ctx.save();
  962. let [style, attr] = help.setVectorStyle(ctx, vector, [
  963. vector.category,
  964. vector.geoType,
  965. "Point",
  966. ]);
  967. if (vector.category === VectorCategory.Point.NormalPoint) {
  968. const lineid = Object.keys(vector.parent)[0];
  969. let line;
  970. if (!(lineid && (line = dataService.getLine(lineid)))) {
  971. ctx.restore();
  972. return;
  973. }
  974. const [stylea, attr] = help.getVectorStyle(line, line.category);
  975. style = {
  976. ...style,
  977. ...stylea,
  978. fillStyle: attr ? "#fff" : stylea.strokeStyle,
  979. };
  980. } else if (vector.category === VectorCategory.Point.FixPoint) {
  981. const text = dataService.getText(vector?.linkedTextId);
  982. if (text) {
  983. style = {
  984. ...style,
  985. fillStyle: text.color,
  986. strokeStyle: text.color,
  987. };
  988. }
  989. }
  990. if (vector.color) {
  991. ctx.strokeStyle = vector.color;
  992. style = {
  993. ...style,
  994. strokeStyle: vector.color,
  995. };
  996. }
  997. if (vector.fillColor) {
  998. style = {
  999. ...style,
  1000. fillStyle: vector.fillColor,
  1001. };
  1002. }
  1003. const draw = (style) => {
  1004. const radius = vector.radius || style.radius;
  1005. ctx.save();
  1006. ctx.beginPath();
  1007. ctx.arc(pt.x, pt.y, radius, 0, Math.PI * 2, true);
  1008. help.setStyle(ctx, style);
  1009. ctx.stroke();
  1010. ctx.fill();
  1011. ctx.restore();
  1012. };
  1013. if (Settings.selectBasePointId === vector.vectorId) {
  1014. style = {
  1015. ...style,
  1016. fillStyle: "rgba(255, 143, 40, 1)",
  1017. strokeStyle: "rgba(255,255,255,1)",
  1018. out: style.out && {
  1019. ...style.out,
  1020. strokeStyle: "rgba(255, 143, 40, 1)",
  1021. },
  1022. };
  1023. }
  1024. //console.log(vector, style, Settings.selectBasePointId)
  1025. draw(style);
  1026. if (style.out) {
  1027. draw(style.out);
  1028. }
  1029. if (vector.category === "BasePoint") {
  1030. ctx.font = `${12 * coordinate.ratio}px Microsoft YaHei`;
  1031. const bound = help.getTextCenter(ctx, "基准点");
  1032. const screen = coordinate.getScreenXY(vector);
  1033. const textPt = coordinate.getXYFromScreenNotRatio({
  1034. y: screen.y + bound.height + style.radius + 4 * coordinate.ratio,
  1035. x: screen.x - bound.width / 2,
  1036. });
  1037. ctx.fillStyle = style.fillStyle;
  1038. this.drawTextByInfo(textPt, "基准点", 0, false);
  1039. } else {
  1040. if (import.meta.env.DEV) {
  1041. if (vector.vectorId) {
  1042. // this.drawTextByInfo(vector, vector.vectorId);
  1043. }
  1044. }
  1045. }
  1046. ctx.restore();
  1047. }
  1048. drawTextByInfo(position, txt, angle, setStyle = true) {
  1049. const ctx = this.context;
  1050. ctx.save();
  1051. setStyle && help.setVectorStyle(ctx, null, "Text");
  1052. const pt = coordinate.getScreenXY(position);
  1053. const textCenter = help.getTextCenter(ctx, txt);
  1054. // pt.x -= textCenter.x;
  1055. // pt.y -= textCenter.y;
  1056. if (angle) {
  1057. ctx.translate(pt.x, pt.y);
  1058. ctx.rotate(angle);
  1059. ctx.translate(-textCenter.x, -textCenter.y);
  1060. ctx.fillText(txt, 0, 0);
  1061. } else {
  1062. ctx.fillText(txt, pt.x, pt.y);
  1063. }
  1064. ctx.restore();
  1065. }
  1066. // 文字
  1067. drawText(vector) {
  1068. this.context.save();
  1069. const [_, foo] = help.setVectorStyle(this.context, vector);
  1070. this.context.fillStyle = vector.color;
  1071. this.context.textBaseline = "bottom";
  1072. this.context.font = `${
  1073. vector.fontSize * coordinate.ratio
  1074. }px Microsoft YaHei`;
  1075. const bound = vector
  1076. .getBound(this.context)
  1077. .map(coordinate.getScreenXY.bind(coordinate));
  1078. this.context.fillText(vector.value, bound[3].x, bound[3].y);
  1079. let select = false;
  1080. const geo = stateService.getFocusItem();
  1081. if (geo) {
  1082. const realVector = dataService.getGeo(geo.type, geo.vectorId);
  1083. select = realVector && realVector.linkedTextId === vector.vectorId;
  1084. }
  1085. if (select || foo) {
  1086. this.context.beginPath();
  1087. const padding = 2 * coordinate.ratio;
  1088. this.context.moveTo(bound[0].x - padding, bound[0].y - padding);
  1089. this.context.lineTo(bound[1].x + padding, bound[1].y - padding);
  1090. this.context.lineTo(bound[2].x + padding, bound[2].y + padding);
  1091. this.context.lineTo(bound[3].x - padding, bound[3].y + padding);
  1092. this.context.strokeStyle = "rgba(255, 153, 0, 1)";
  1093. this.context.fillStyle = "rgba(255, 153, 0, 0.30)";
  1094. this.context.lineWidth = 2 * coordinate.ratio;
  1095. this.context.setLineDash([6 * coordinate.ratio, 2 * coordinate.ratio]);
  1096. this.context.closePath();
  1097. this.context.stroke();
  1098. this.context.fill();
  1099. }
  1100. this.context.restore();
  1101. vector.displayPoint &&
  1102. this.drawPoint(
  1103. { ...vector.center, color: vector.color, fillColor: vector.color },
  1104. true
  1105. );
  1106. // const bound = help.getTextCenter(this.context, vector.value);
  1107. // // console.log(vector)
  1108. // const screen = coordinate.getScreenXY(vector.center);
  1109. // this.drawTextByInfo(
  1110. // // vector.center,
  1111. // coordinate.getXYFromScreenNotRatio({
  1112. // // y: screen.y + (bound.height + Style.Point.radius),
  1113. // y: screen.y + (bound.height + Style.Point.radius),
  1114. // x: screen.x - bound.width / 2,
  1115. // }),
  1116. // vector.value,
  1117. // -(vector.angle || 0),
  1118. // false
  1119. // );
  1120. // this.context.restore();
  1121. // vector.displayPoint &&
  1122. // this.drawPoint({ ...vector.center, color: vector.color }, true);
  1123. // vector.getBound(this.context).forEach(this.drawPoint.bind(this));
  1124. }
  1125. drawSVG(vector) {
  1126. const points = vector.points.map(coordinate.getScreenXY.bind(coordinate));
  1127. const svgWidth = 64;
  1128. const svgHidth = 64;
  1129. const width = mathUtil.getDistance(points[0], points[1]);
  1130. const height = mathUtil.getDistance(points[0], points[3]);
  1131. const dires = [points[0], { ...points[0], x: 10000 }, points[1]];
  1132. let angle = mathUtil.Angle(...dires) * (Math.PI / 180);
  1133. angle = mathUtil.isClockwise(dires) ? angle : -angle;
  1134. this.context.save();
  1135. this.context.translate(points[0].x, points[0].y);
  1136. this.context.rotate(angle);
  1137. this.context.scale(width / svgWidth, height / svgHidth);
  1138. const [style, label] = help.getVectorStyle(vector);
  1139. this.context.lineWidth = style.lineWidth / (width / svgWidth);
  1140. this.context.fillStyle = "rgba(0,0,0,0)";
  1141. this.context.strokeStyle = "rgba(0,0,0,0)";
  1142. SVGIcons[vector.type].draw(
  1143. this.context,
  1144. style.fillStyle || "rgba(0,0,0,0)",
  1145. style.strokeStyle || "rgba(0,0,0,0)"
  1146. );
  1147. this.context.restore();
  1148. if (label) {
  1149. this.context.save();
  1150. this.context.beginPath();
  1151. this.context.moveTo(points[0].x, points[0].y);
  1152. this.context.lineTo(points[1].x, points[1].y);
  1153. this.context.lineTo(points[2].x, points[2].y);
  1154. this.context.lineTo(points[3].x, points[3].y);
  1155. this.context.strokeStyle = style.strokeStyle;
  1156. this.context.fillStyle = "rgba(255, 153, 0, 0.30)";
  1157. this.context.lineWidth = 2 * coordinate.ratio;
  1158. this.context.setLineDash([6 * coordinate.ratio, 2 * coordinate.ratio]);
  1159. this.context.closePath();
  1160. this.context.stroke();
  1161. this.context.fill();
  1162. this.context.restore();
  1163. vector.points.forEach((point) =>
  1164. this.drawPoint({
  1165. ...point,
  1166. fillColor: "#fff",
  1167. color: style.strokeStyle,
  1168. radius: 5,
  1169. })
  1170. );
  1171. }
  1172. }
  1173. drawLineText(vector, style) {
  1174. const startReal = dataService.getPoint(vector.startId);
  1175. const endReal = dataService.getPoint(vector.endId);
  1176. help.drawLineText(
  1177. this.context,
  1178. coordinate.getScreenXY(startReal),
  1179. coordinate.getScreenXY(endReal),
  1180. (vector.value
  1181. ? Math.round(vector.value * 100) / 100
  1182. : help.getRealDistance(startReal, endReal)) + "m",
  1183. style
  1184. );
  1185. }
  1186. drawBaseLineLabel(vector) {
  1187. const startReal = dataService.getPoint(vector.startId);
  1188. const start = coordinate.getScreenXY(startReal);
  1189. const endReal = dataService.getPoint(vector.endId);
  1190. const end = coordinate.getScreenXY(endReal);
  1191. const point = mathUtil.translate(
  1192. end,
  1193. start,
  1194. end,
  1195. mathUtil.getDistance(start, end) / 3
  1196. );
  1197. const p4 = help.getPerpendicularPoint(
  1198. start,
  1199. end,
  1200. point,
  1201. 30 * coordinate.ratio
  1202. );
  1203. const ctx = this.context;
  1204. ctx.save();
  1205. ctx.beginPath();
  1206. const [style] = help.setVectorStyle(
  1207. this.context,
  1208. vector,
  1209. vector.category || vector.geoType
  1210. );
  1211. ctx.moveTo(point.x, point.y);
  1212. ctx.lineTo(p4.x, p4.y);
  1213. ctx.stroke();
  1214. const p5 = help.getPerpendicularPoint(
  1215. start,
  1216. end,
  1217. point,
  1218. 35 * coordinate.ratio
  1219. );
  1220. this.context.font = `${12 * coordinate.ratio}px Microsoft YaHei`;
  1221. help.drawLineText(
  1222. this.context,
  1223. help.getPerpendicularPoint(point, p5, p5, 10 * coordinate.ratio),
  1224. help.getPerpendicularPoint(point, p5, p5, -10 * coordinate.ratio),
  1225. "基准线",
  1226. {
  1227. padding: 6 * coordinate.ratio,
  1228. backColor: "rgba(0,0,0,0)",
  1229. fillColor: style.strokeStyle,
  1230. }
  1231. );
  1232. ctx.restore();
  1233. }
  1234. drawCurveLine(vector) {
  1235. // points CurveLine
  1236. const ctx = this.context;
  1237. ctx.save();
  1238. help.setVectorStyle(ctx, vector);
  1239. help.drawStyleLine(
  1240. this.context,
  1241. () => {
  1242. help.transformCoves([vector.curves]).forEach((coves) => {
  1243. help.drawCoves(ctx, coves);
  1244. });
  1245. },
  1246. vector.style,
  1247. vector.weight
  1248. );
  1249. ctx.restore();
  1250. // if (import.meta.env.DEV) {
  1251. vector.points.forEach(this.drawPoint.bind(this));
  1252. // }
  1253. }
  1254. drawLine(vector) {
  1255. const startReal = dataService.getPoint(vector.startId);
  1256. const start = coordinate.getScreenXY(startReal);
  1257. const endReal = dataService.getPoint(vector.endId);
  1258. const end = coordinate.getScreenXY(endReal);
  1259. this.context.save();
  1260. const [style, attr] = help.setVectorStyle(this.context, vector, [
  1261. vector.category,
  1262. vector.geoType,
  1263. "BaseLine",
  1264. ]);
  1265. if (style.dash) {
  1266. this.context.setLineDash(style.dash);
  1267. }
  1268. help.drawStyleLine(this.context, [start, end], vector.style, vector.weight);
  1269. // vector.category = VectorCategory.Line.LocationLineByFixPoint;
  1270. switch (vector.category) {
  1271. case VectorCategory.Line.SingleArrowLine:
  1272. this.drawArrow(vector);
  1273. break;
  1274. case VectorCategory.Line.DoubleArrowLine:
  1275. this.drawArrow(vector);
  1276. break;
  1277. case VectorCategory.Line.BaseLine:
  1278. this.drawBaseLineLabel(vector);
  1279. break;
  1280. case VectorCategory.Line.LocationLineByFixPoint:
  1281. case VectorCategory.Line.LocationLineByBasePoint:
  1282. case VectorCategory.Line.FreeMeasureLine:
  1283. case VectorCategory.Line.MeasureLine:
  1284. case VectorCategory.Line.PositionLine:
  1285. this.drawLineText(vector, style.text);
  1286. if (
  1287. [
  1288. VectorCategory.Line.LocationLineByFixPoint,
  1289. VectorCategory.Line.LocationLineByBasePoint,
  1290. ].includes(vector.category)
  1291. ) {
  1292. this.drawLineArrow([start, end], true);
  1293. }
  1294. break;
  1295. }
  1296. this.context.restore();
  1297. }
  1298. drawElementLine(element) {
  1299. let start = elementService.getPoint(element.startId);
  1300. start = coordinate.getScreenXY(start);
  1301. let end = elementService.getPoint(element.endId);
  1302. end = coordinate.getScreenXY(end);
  1303. this.context.save();
  1304. const [style] = help.setVectorStyle(
  1305. this.context,
  1306. element,
  1307. element.category || element.geoType
  1308. );
  1309. if (style.dash) {
  1310. this.context.setLineDash(style.dash);
  1311. }
  1312. this.context.beginPath();
  1313. this.context.moveTo(start.x, start.y);
  1314. this.context.lineTo(end.x, end.y);
  1315. this.context.stroke();
  1316. this.context.restore();
  1317. }
  1318. }
  1319. const draw = new Draw();
  1320. export { draw };