Draw.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. import { dataService } from "../Service/DataService.js";
  2. import { stateService } from "../Service/StateService.js";
  3. import { coordinate } from "../Coordinate.js";
  4. import Style from "@/graphic/CanvasStyle/index.js";
  5. import VectorType from "../enum/VectorType.js";
  6. import { mathUtil } from "../Util/MathUtil.js";
  7. import ElementEvents from "../enum/ElementEvents.js";
  8. import { elementService } from "../Service/ElementService.js";
  9. const help = {
  10. getVectorStyle(vector, geoType = vector.geoType) {
  11. const geoId = vector?.vectorId;
  12. if (!geoId) {
  13. return Style[geoType];
  14. }
  15. const itemsEntry = [
  16. [stateService.getSelectItem(), "Select"],
  17. [stateService.getDraggingItem(), "Dragging"],
  18. [stateService.getFocusItem(), "Focus"],
  19. ];
  20. return itemsEntry.reduce((prev, [item, attr]) => {
  21. if (
  22. item &&
  23. item.type === VectorType[geoType] &&
  24. geoId === item.vectorId
  25. ) {
  26. if (Style[attr] && Style[attr][geoType]) {
  27. return Style[attr][geoType];
  28. }
  29. }
  30. return prev;
  31. }, Style[geoType]);
  32. },
  33. setVectorStyle(ctx, vector, geoType = vector.geoType) {
  34. const styles = help.getVectorStyle(vector, geoType);
  35. for (const style in styles) {
  36. if (typeof styles[style] === "function") {
  37. styles[style](ctx, vector);
  38. } else {
  39. ctx[style] = styles[style];
  40. }
  41. }
  42. return styles;
  43. },
  44. transformCoves(lines) {
  45. return lines.map((line) =>
  46. line.map((line) => ({
  47. start: coordinate.getScreenXY(line.start),
  48. end: coordinate.getScreenXY(line.end),
  49. controls: line.controls.map(coordinate.getScreenXY.bind(coordinate)),
  50. }))
  51. );
  52. },
  53. drawCoves(ctx, coves) {
  54. for (const curve of coves) {
  55. ctx.beginPath();
  56. ctx.moveTo(curve.start.x, curve.start.y);
  57. if (curve.controls.length === 1) {
  58. ctx.quadraticCurveTo(
  59. curve.controls[0].x,
  60. curve.controls[0].y,
  61. curve.end.x,
  62. curve.end.y
  63. );
  64. } else {
  65. ctx.bezierCurveTo(
  66. curve.controls[0].x,
  67. curve.controls[0].y,
  68. curve.controls[1].x,
  69. curve.controls[1].y,
  70. curve.end.x,
  71. curve.end.y
  72. );
  73. }
  74. ctx.stroke();
  75. }
  76. },
  77. };
  78. export default class Draw {
  79. constructor() {
  80. this.context = null;
  81. }
  82. initContext(canvas) {
  83. if (canvas) {
  84. this.context = canvas.getContext("2d");
  85. } else {
  86. this.context = null;
  87. }
  88. }
  89. clear() {
  90. this.context.clearRect(
  91. 0,
  92. 0,
  93. this.context.canvas.width,
  94. this.context.canvas.height
  95. );
  96. }
  97. drawBackGroundImg(vector) {
  98. this.context.save();
  99. this.context.restore();
  100. }
  101. drawGrid(startX, startY, w, h, step1, step2) {
  102. this.context.save();
  103. this.context.beginPath();
  104. for (var x = startX; x <= w; x += step1) {
  105. this.context.moveTo(x, 0);
  106. this.context.lineTo(x, h);
  107. }
  108. for (var y = startY; y <= h; y += step1) {
  109. this.context.moveTo(0, y);
  110. this.context.lineTo(w, y);
  111. }
  112. this.context.strokeStyle = "rgba(0,0,0,0.1)";
  113. this.context.lineWidth = 0.5;
  114. this.context.stroke();
  115. this.context.beginPath();
  116. for (var x = startX; x <= w; x += step2) {
  117. this.context.moveTo(x, 0);
  118. this.context.lineTo(x, h);
  119. }
  120. for (var y = startY; y <= h; y += step2) {
  121. this.context.moveTo(0, y);
  122. this.context.lineTo(w, y);
  123. }
  124. this.context.strokeStyle = "rgba(0,0,0,0.2)";
  125. this.context.lineWidth = 1;
  126. this.context.stroke();
  127. this.context.restore();
  128. }
  129. drawRoad(vector, isTemp) {
  130. console.log(vector);
  131. if (!isTemp && vector.display && vector.way !== "oneWay") {
  132. const ctx = this.context;
  133. const draw = (midDivide) => {
  134. const startScreen = coordinate.getScreenXY(midDivide.start);
  135. const endScreen = coordinate.getScreenXY(midDivide.end);
  136. ctx.beginPath();
  137. ctx.moveTo(startScreen.x, startScreen.y);
  138. ctx.lineTo(endScreen.x, endScreen.y);
  139. ctx.stroke();
  140. };
  141. ctx.save();
  142. help.setVectorStyle(ctx, vector);
  143. vector.midDivide.leftMidDivide && draw(vector.midDivide.leftMidDivide);
  144. vector.midDivide.rightMidDivide && draw(vector.midDivide.rightMidDivide);
  145. ctx.restore();
  146. }
  147. if (import.meta.env.DEV && !isTemp) {
  148. const startReal = isTemp
  149. ? vector.start
  150. : dataService.getRoadPoint(vector.startId);
  151. const endReal = isTemp
  152. ? vector.end
  153. : dataService.getRoadPoint(vector.endId);
  154. this.drawText(
  155. { x: (startReal.x + endReal.x) / 2, y: (startReal.y + endReal.y) / 2 },
  156. vector.vectorId
  157. );
  158. }
  159. this.drawRoadEdge(vector, isTemp);
  160. vector.leftLanes && vector.leftLanes.forEach(this.drawLan.bind(this));
  161. vector.rightLanes && vector.rightLanes.forEach(this.drawLan.bind(this));
  162. }
  163. drawLan(lan) {
  164. const ctx = this.context;
  165. const start = coordinate.getScreenXY(lan.start);
  166. const end = coordinate.getScreenXY(lan.end);
  167. ctx.save();
  168. ctx.beginPath();
  169. help.setVectorStyle(ctx, null, "Lane");
  170. ctx.setLineDash(Style.Lane.dash);
  171. ctx.moveTo(start.x, start.y);
  172. ctx.lineTo(end.x, end.y);
  173. ctx.stroke();
  174. ctx.restore();
  175. if (import.meta.env.DEV) {
  176. this.drawPoint(lan.start);
  177. this.drawPoint(lan.end);
  178. }
  179. }
  180. drawRoadEdge(vector, isTemp) {
  181. //判断是否与road方向一致。角度足够小,路足够宽,有可能向量方向不一致
  182. const start = isTemp
  183. ? vector.start
  184. : dataService.getRoadPoint(vector.startId);
  185. const end = isTemp ? vector.end : dataService.getRoadPoint(vector.endId);
  186. const drawRoadEdgeChild = (edgeVector) => {
  187. const flag = mathUtil.isSameDirForVector(
  188. start,
  189. end,
  190. edgeVector.start,
  191. edgeVector.end
  192. );
  193. if (flag) {
  194. ctx.beginPath();
  195. const point1 = coordinate.getScreenXY(edgeVector.start);
  196. const point2 = coordinate.getScreenXY(edgeVector.end);
  197. ctx.moveTo(point1.x, point1.y);
  198. ctx.lineTo(point2.x, point2.y);
  199. ctx.stroke();
  200. }
  201. this.drawText(
  202. {
  203. x: (edgeVector.start.x + edgeVector.end.x) / 2,
  204. y: (edgeVector.start.y + edgeVector.end.y) / 2,
  205. },
  206. edgeVector.vectorId
  207. );
  208. };
  209. const leftEdge = isTemp
  210. ? vector.leftEdge
  211. : dataService.getRoadEdge(vector.leftEdgeId);
  212. const rightEdge = isTemp
  213. ? vector.rightEdge
  214. : dataService.getRoadEdge(vector.rightEdgeId);
  215. const ctx = this.context;
  216. ctx.save();
  217. isTemp && (ctx.globalAlpha = 0.3);
  218. help.setVectorStyle(ctx, leftEdge);
  219. drawRoadEdgeChild(leftEdge);
  220. help.setVectorStyle(ctx, rightEdge);
  221. drawRoadEdgeChild(rightEdge);
  222. ctx.restore();
  223. if (import.meta.env.DEV) {
  224. this.drawPoint(leftEdge.start);
  225. this.drawPoint(leftEdge.end);
  226. this.drawPoint(rightEdge.start);
  227. this.drawPoint(rightEdge.end);
  228. }
  229. }
  230. drawControlPoint(vector) {
  231. const start = coordinate.getScreenXY(
  232. dataService
  233. .getRoadEdge(vector.edgeInfo1.id)
  234. .getPosition(vector.edgeInfo1.dir)
  235. );
  236. const end = coordinate.getScreenXY(
  237. dataService
  238. .getRoadEdge(vector.edgeInfo2.id)
  239. .getPosition(vector.edgeInfo2.dir)
  240. );
  241. const pt2 = mathUtil.twoOrderBezier(
  242. 0.5,
  243. start,
  244. coordinate.getScreenXY({ x: vector.x, y: vector.y }),
  245. end
  246. );
  247. const pt = mathUtil.twoOrderBezier2(0.5, start, pt2, end);
  248. const ctx = this.context;
  249. ctx.save();
  250. ctx.beginPath();
  251. ctx.arc(
  252. pt.x,
  253. pt.y,
  254. Style.ControlPoint.radius * coordinate.ratio,
  255. 0,
  256. Math.PI * 2,
  257. true
  258. );
  259. ctx.stroke();
  260. ctx.fill();
  261. ctx.restore();
  262. ctx.save();
  263. ctx.beginPath();
  264. help.setVectorStyle(ctx, null, "RoadEdge");
  265. //曲线
  266. ctx.moveTo(start.x, start.y);
  267. ctx.quadraticCurveTo(pt.x, pt.y, end.x, end.y);
  268. ctx.stroke();
  269. ctx.restore();
  270. }
  271. drawCurveRoad(vector) {
  272. if (vector.display && vector.midDivide) {
  273. const covesArray = help.transformCoves([
  274. vector.midDivide.leftMidDivideCurves,
  275. vector.midDivide.rightMidDivideCurves,
  276. ]);
  277. const ctx = this.context;
  278. ctx.save();
  279. help.setVectorStyle(ctx, vector);
  280. for (let coves of covesArray) {
  281. help.drawCoves(ctx, coves);
  282. }
  283. ctx.restore();
  284. }
  285. this.drawCurveRoadEdge(dataService.getCurveRoadEdge(vector.rightEdgeId));
  286. this.drawCurveRoadEdge(dataService.getCurveRoadEdge(vector.leftEdgeId));
  287. vector.leftLanesCurves &&
  288. vector.leftLanesCurves.forEach(this.drawCurveLan.bind(this));
  289. vector.rightLanesCurves &&
  290. vector.rightLanesCurves.forEach(this.drawCurveLan.bind(this));
  291. if (import.meta.env.DEV) {
  292. vector.points.forEach(this.drawPoint.bind(this));
  293. }
  294. }
  295. drawCurveRoadEdge(vector, isTemp) {
  296. const [coves] = help.transformCoves([vector.curves]);
  297. const ctx = this.context;
  298. ctx.save();
  299. help.setVectorStyle(ctx, vector);
  300. help.drawCoves(ctx, coves);
  301. ctx.restore();
  302. if (import.meta.env.DEV) {
  303. vector.points.forEach(this.drawPoint.bind(this));
  304. }
  305. }
  306. drawCurveLan(lines) {
  307. const [coves] = help.transformCoves([lines]);
  308. const ctx = this.context;
  309. ctx.save();
  310. help.setVectorStyle(ctx, null, "CurveLan");
  311. ctx.setLineDash(Style.Lane.dash);
  312. help.drawCoves(ctx, coves);
  313. ctx.restore();
  314. if (import.meta.env.DEV) {
  315. lines.map((line) => {
  316. this.drawPoint(line.start);
  317. this.drawPoint(line.end);
  318. });
  319. }
  320. }
  321. drawRoadPoint(vector) {
  322. this.drawPoint(vector);
  323. }
  324. drawCircle(element) {
  325. this.drawPoint({
  326. ...element.center,
  327. radius: element.radius,
  328. geoType: element.geoType,
  329. });
  330. }
  331. drawPoint(vector) {
  332. const pt = coordinate.getScreenXY({ x: vector.x, y: vector.y });
  333. const ctx = this.context;
  334. const style = help.setVectorStyle(ctx, vector, vector.geoType || "Point");
  335. const radius = (vector.radius || style.radius) * coordinate.ratio;
  336. ctx.save();
  337. ctx.beginPath();
  338. ctx.arc(pt.x, pt.y, radius, 0, Math.PI * 2, true);
  339. ctx.stroke();
  340. ctx.fill();
  341. ctx.restore();
  342. if (import.meta.env.DEV) {
  343. if (vector.vectorId) {
  344. this.drawText(vector, vector.vectorId);
  345. }
  346. }
  347. }
  348. // 文字
  349. drawText(position, txt, angle) {
  350. const ctx = this.context;
  351. ctx.save();
  352. help.setVectorStyle(ctx, null, "Text");
  353. const pt = coordinate.getScreenXY(position);
  354. if (angle) {
  355. ctx.translate(pt.x, pt.y);
  356. ctx.rotate(angle);
  357. ctx.fillText(txt, 0, 0);
  358. } else {
  359. ctx.fillText(txt, pt.x, pt.y);
  360. }
  361. ctx.restore();
  362. }
  363. drawLine(vector) {
  364. let start = dataService.getPoint(vector.startId);
  365. start = coordinate.getScreenXY(start);
  366. let end = dataService.getPoint(vector.endId);
  367. end = coordinate.getScreenXY(end);
  368. this.context.save();
  369. const style = help.setVectorStyle(
  370. this.context,
  371. vector,
  372. vector.category || vector.geoType
  373. );
  374. if (style.dash) {
  375. this.context.setLineDash(style.dash);
  376. }
  377. this.context.beginPath();
  378. this.context.moveTo(start.x, start.y);
  379. this.context.lineTo(end.x, end.y);
  380. this.context.stroke();
  381. this.context.restore();
  382. }
  383. drawElementLine(element) {
  384. let start = elementService.getPoint(element.startId);
  385. start = coordinate.getScreenXY(start);
  386. let end = elementService.getPoint(element.endId);
  387. end = coordinate.getScreenXY(end);
  388. this.context.save();
  389. const style = help.setVectorStyle(
  390. this.context,
  391. element,
  392. element.category || element.geoType
  393. );
  394. if (style.dash) {
  395. this.context.setLineDash(style.dash);
  396. }
  397. this.context.beginPath();
  398. this.context.moveTo(start.x, start.y);
  399. this.context.lineTo(end.x, end.y);
  400. this.context.stroke();
  401. this.context.restore();
  402. }
  403. }
  404. const draw = new Draw();
  405. export { draw };