Draw.js 48 KB

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