Draw.js 42 KB

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