Draw.js 47 KB

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