import { dataService } from "../Service/DataService.js"; import { stateService } from "../Service/StateService.js"; import { coordinate } from "../Coordinate.js"; import Style from "@/graphic/CanvasStyle/index.js"; import VectorType from "../enum/VectorType.js"; import { mathUtil } from "../Util/MathUtil.js"; import ElementEvents from "../enum/ElementEvents.js"; import { elementService } from "../Service/ElementService.js"; const help = { getVectorStyle(vector, geoType = vector.geoType) { const geoId = vector?.vectorId; if (!geoId) { return Style[geoType]; } const itemsEntry = [ [stateService.getSelectItem(), "Select"], [stateService.getDraggingItem(), "Dragging"], [stateService.getFocusItem(), "Focus"], ]; return itemsEntry.reduce((prev, [item, attr]) => { if ( item && item.type === VectorType[geoType] && geoId === item.vectorId ) { if (Style[attr] && Style[attr][geoType]) { return Style[attr][geoType]; } } return prev; }, Style[geoType]); }, setVectorStyle(ctx, vector, geoType = vector.geoType) { const styles = help.getVectorStyle(vector, geoType); for (const style in styles) { if (typeof styles[style] === "function") { styles[style](ctx, vector); } else { ctx[style] = styles[style]; } } return styles; }, transformCoves(lines) { return lines.map((line) => line.map((line) => ({ start: coordinate.getScreenXY(line.start), end: coordinate.getScreenXY(line.end), controls: line.controls.map(coordinate.getScreenXY.bind(coordinate)), })) ); }, drawCoves(ctx, coves) { for (const curve of coves) { ctx.beginPath(); ctx.moveTo(curve.start.x, curve.start.y); if (curve.controls.length === 1) { ctx.quadraticCurveTo( curve.controls[0].x, curve.controls[0].y, curve.end.x, curve.end.y ); } else { ctx.bezierCurveTo( curve.controls[0].x, curve.controls[0].y, curve.controls[1].x, curve.controls[1].y, curve.end.x, curve.end.y ); } ctx.stroke(); } }, }; export default class Draw { constructor() { this.context = null; } initContext(canvas) { if (canvas) { this.context = canvas.getContext("2d"); } else { this.context = null; } } clear() { this.context.clearRect( 0, 0, this.context.canvas.width, this.context.canvas.height ); } drawBackGroundImg(vector) { this.context.save(); this.context.restore(); } drawGrid(startX, startY, w, h, step1, step2) { this.context.save(); this.context.beginPath(); for (var x = startX; x <= w; x += step1) { this.context.moveTo(x, 0); this.context.lineTo(x, h); } for (var y = startY; y <= h; y += step1) { this.context.moveTo(0, y); this.context.lineTo(w, y); } this.context.strokeStyle = "rgba(0,0,0,0.1)"; this.context.lineWidth = 0.5; this.context.stroke(); this.context.beginPath(); for (var x = startX; x <= w; x += step2) { this.context.moveTo(x, 0); this.context.lineTo(x, h); } for (var y = startY; y <= h; y += step2) { this.context.moveTo(0, y); this.context.lineTo(w, y); } this.context.strokeStyle = "rgba(0,0,0,0.2)"; this.context.lineWidth = 1; this.context.stroke(); this.context.restore(); } drawRoad(vector, isTemp) { console.log(vector); if (!isTemp && vector.display && vector.way !== "oneWay") { const ctx = this.context; const draw = (midDivide) => { const startScreen = coordinate.getScreenXY(midDivide.start); const endScreen = coordinate.getScreenXY(midDivide.end); ctx.beginPath(); ctx.moveTo(startScreen.x, startScreen.y); ctx.lineTo(endScreen.x, endScreen.y); ctx.stroke(); }; ctx.save(); help.setVectorStyle(ctx, vector); vector.midDivide.leftMidDivide && draw(vector.midDivide.leftMidDivide); vector.midDivide.rightMidDivide && draw(vector.midDivide.rightMidDivide); ctx.restore(); } if (import.meta.env.DEV && !isTemp) { const startReal = isTemp ? vector.start : dataService.getRoadPoint(vector.startId); const endReal = isTemp ? vector.end : dataService.getRoadPoint(vector.endId); this.drawText( { x: (startReal.x + endReal.x) / 2, y: (startReal.y + endReal.y) / 2 }, vector.vectorId ); } this.drawRoadEdge(vector, isTemp); vector.leftLanes && vector.leftLanes.forEach(this.drawLan.bind(this)); vector.rightLanes && vector.rightLanes.forEach(this.drawLan.bind(this)); } drawLan(lan) { const ctx = this.context; const start = coordinate.getScreenXY(lan.start); const end = coordinate.getScreenXY(lan.end); ctx.save(); ctx.beginPath(); help.setVectorStyle(ctx, null, "Lane"); ctx.setLineDash(Style.Lane.dash); ctx.moveTo(start.x, start.y); ctx.lineTo(end.x, end.y); ctx.stroke(); ctx.restore(); if (import.meta.env.DEV) { this.drawPoint(lan.start); this.drawPoint(lan.end); } } drawRoadEdge(vector, isTemp) { //判断是否与road方向一致。角度足够小,路足够宽,有可能向量方向不一致 const start = isTemp ? vector.start : dataService.getRoadPoint(vector.startId); const end = isTemp ? vector.end : dataService.getRoadPoint(vector.endId); const drawRoadEdgeChild = (edgeVector) => { const flag = mathUtil.isSameDirForVector( start, end, edgeVector.start, edgeVector.end ); if (flag) { ctx.beginPath(); const point1 = coordinate.getScreenXY(edgeVector.start); const point2 = coordinate.getScreenXY(edgeVector.end); ctx.moveTo(point1.x, point1.y); ctx.lineTo(point2.x, point2.y); ctx.stroke(); } this.drawText( { x: (edgeVector.start.x + edgeVector.end.x) / 2, y: (edgeVector.start.y + edgeVector.end.y) / 2, }, edgeVector.vectorId ); }; const leftEdge = isTemp ? vector.leftEdge : dataService.getRoadEdge(vector.leftEdgeId); const rightEdge = isTemp ? vector.rightEdge : dataService.getRoadEdge(vector.rightEdgeId); const ctx = this.context; ctx.save(); isTemp && (ctx.globalAlpha = 0.3); help.setVectorStyle(ctx, leftEdge); drawRoadEdgeChild(leftEdge); help.setVectorStyle(ctx, rightEdge); drawRoadEdgeChild(rightEdge); ctx.restore(); if (import.meta.env.DEV) { this.drawPoint(leftEdge.start); this.drawPoint(leftEdge.end); this.drawPoint(rightEdge.start); this.drawPoint(rightEdge.end); } } drawControlPoint(vector) { const start = coordinate.getScreenXY( dataService .getRoadEdge(vector.edgeInfo1.id) .getPosition(vector.edgeInfo1.dir) ); const end = coordinate.getScreenXY( dataService .getRoadEdge(vector.edgeInfo2.id) .getPosition(vector.edgeInfo2.dir) ); const pt2 = mathUtil.twoOrderBezier( 0.5, start, coordinate.getScreenXY({ x: vector.x, y: vector.y }), end ); const pt = mathUtil.twoOrderBezier2(0.5, start, pt2, end); const ctx = this.context; ctx.save(); ctx.beginPath(); ctx.arc( pt.x, pt.y, Style.ControlPoint.radius * coordinate.ratio, 0, Math.PI * 2, true ); ctx.stroke(); ctx.fill(); ctx.restore(); ctx.save(); ctx.beginPath(); help.setVectorStyle(ctx, null, "RoadEdge"); //曲线 ctx.moveTo(start.x, start.y); ctx.quadraticCurveTo(pt.x, pt.y, end.x, end.y); ctx.stroke(); ctx.restore(); } drawCurveRoad(vector) { if (vector.display && vector.midDivide) { const covesArray = help.transformCoves([ vector.midDivide.leftMidDivideCurves, vector.midDivide.rightMidDivideCurves, ]); const ctx = this.context; ctx.save(); help.setVectorStyle(ctx, vector); for (let coves of covesArray) { help.drawCoves(ctx, coves); } ctx.restore(); } this.drawCurveRoadEdge(dataService.getCurveRoadEdge(vector.rightEdgeId)); this.drawCurveRoadEdge(dataService.getCurveRoadEdge(vector.leftEdgeId)); vector.leftLanesCurves && vector.leftLanesCurves.forEach(this.drawCurveLan.bind(this)); vector.rightLanesCurves && vector.rightLanesCurves.forEach(this.drawCurveLan.bind(this)); if (import.meta.env.DEV) { vector.points.forEach(this.drawPoint.bind(this)); } } drawCurveRoadEdge(vector, isTemp) { const [coves] = help.transformCoves([vector.curves]); const ctx = this.context; ctx.save(); help.setVectorStyle(ctx, vector); help.drawCoves(ctx, coves); ctx.restore(); if (import.meta.env.DEV) { vector.points.forEach(this.drawPoint.bind(this)); } } drawCurveLan(lines) { const [coves] = help.transformCoves([lines]); const ctx = this.context; ctx.save(); help.setVectorStyle(ctx, null, "CurveLan"); ctx.setLineDash(Style.Lane.dash); help.drawCoves(ctx, coves); ctx.restore(); if (import.meta.env.DEV) { lines.map((line) => { this.drawPoint(line.start); this.drawPoint(line.end); }); } } drawRoadPoint(vector) { this.drawPoint(vector); } drawCircle(element) { this.drawPoint({ ...element.center, radius: element.radius, geoType: element.geoType, }); } drawPoint(vector) { const pt = coordinate.getScreenXY({ x: vector.x, y: vector.y }); const ctx = this.context; const style = help.setVectorStyle(ctx, vector, vector.geoType || "Point"); const radius = (vector.radius || style.radius) * coordinate.ratio; ctx.save(); ctx.beginPath(); ctx.arc(pt.x, pt.y, radius, 0, Math.PI * 2, true); ctx.stroke(); ctx.fill(); ctx.restore(); if (import.meta.env.DEV) { if (vector.vectorId) { this.drawText(vector, vector.vectorId); } } } // 文字 drawText(position, txt, angle) { const ctx = this.context; ctx.save(); help.setVectorStyle(ctx, null, "Text"); const pt = coordinate.getScreenXY(position); if (angle) { ctx.translate(pt.x, pt.y); ctx.rotate(angle); ctx.fillText(txt, 0, 0); } else { ctx.fillText(txt, pt.x, pt.y); } ctx.restore(); } drawLine(vector) { let start = dataService.getPoint(vector.startId); start = coordinate.getScreenXY(start); let end = dataService.getPoint(vector.endId); end = coordinate.getScreenXY(end); this.context.save(); const style = help.setVectorStyle( this.context, vector, vector.category || vector.geoType ); if (style.dash) { this.context.setLineDash(style.dash); } this.context.beginPath(); this.context.moveTo(start.x, start.y); this.context.lineTo(end.x, end.y); this.context.stroke(); this.context.restore(); } drawElementLine(element) { let start = elementService.getPoint(element.startId); start = coordinate.getScreenXY(start); let end = elementService.getPoint(element.endId); end = coordinate.getScreenXY(end); this.context.save(); const style = help.setVectorStyle( this.context, element, element.category || element.geoType ); if (style.dash) { this.context.setLineDash(style.dash); } this.context.beginPath(); this.context.moveTo(start.x, start.y); this.context.lineTo(end.x, end.y); this.context.stroke(); this.context.restore(); } } const draw = new Draw(); export { draw };