canvas绘制红绿灯路口

无图不欢,先上图
canvas绘制红绿灯路口_第1张图片
使用方法(以vue3为例)

<template>
    <canvas class="lane" ref="laneCanvas"></canvas>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import Lane from '@/utils/lane.js'

let laneCanvas = ref(null)
/**
 * 车道方向,进口方向
 * 1 - 北,2 - 东北,3 - 东,4 - 东南,
 * 5 - 南,6 - 西南,7 - 西,8 - 西北
 * 
 * 直行放行 nThrough 0不放行 1放行
 * 左转放行 nTurnLeft 0不放行 1放行
 * 右转放行 nTurnRight 0不放行 1放行
 * 调头 nTurnAround 0不放行 1放行
 * 
 * 通道相位 nChannelNumberPhase 1-红灯 2绿灯 3黄灯
 */
const randData = () => {
    let data = []
    let cdireCtions = ['北', '东北', '东', '东南', '南', '西南', '西', '西北']
    cdireCtions.forEach((item, index) => {
        if (rand(0, 1)) {
            let lanes = []
            let lanesLength = rand(1, 6)
            if (lanesLength === 1) {
                lanes = [{
                    nThrough: 1,
                    nTurnLeft: rand(0, 1),
                    nTurnRight: rand(0, 1),
                    nTurnAround: rand(0, 1),
                    nChannelNumberPhase: rand(1, 3)
                }]
            } else if (lanesLength === 2) {
                lanes = [{
                    nThrough: 1,
                    nTurnLeft: rand(0, 1),
                    nTurnRight: 0,
                    nTurnAround: rand(0, 1),
                    nChannelNumberPhase: rand(1, 3)
                }, {
                    nThrough: 1,
                    nTurnLeft: 0,
                    nTurnRight: rand(0, 1),
                    nTurnAround: 0,
                    nChannelNumberPhase: rand(1, 3)
                }]
            } else {
                for (let i = 0; i < lanesLength; i++) {
                    let nThrough = 0
                    let nTurnLeft = 0
                    let nTurnRight = 0
                    let nTurnAround = 0
                    if (i === 0) {
                        nThrough = rand(0, 1)
                        nTurnLeft = 1
                        nTurnAround = rand(0, 1)
                    }
                    if (i === lanesLength - 1) {
                        nThrough = rand(0, 1)
                        nTurnRight = 1
                        nTurnAround = 0
                    }
                    if (i > 0 && i < lanesLength - 1) {
                        nThrough = 1
                        if (lanes[i - 1].nTurnLeft) {
                            nTurnLeft = rand(0, 1)
                            nTurnRight = 0
                        } else if (lanes[i - 1].nTurnRight) {
                            nTurnLeft = 0
                            nTurnRight = 1
                        } else {
                            nTurnLeft = 0
                            nTurnRight = rand(0, 1)
                        }
                        nTurnAround = 0
                    }
                    lanes.push({
                        nThrough,
                        nTurnLeft,
                        nTurnRight,
                        nTurnAround,
                        nChannelNumberPhase: rand(1, 3)
                    })
                }
            }
            data.push({
                nApproachDirection: index + 1,
                cdireCtion: cdireCtions[index],
                lanes
            })
        }
    })
    if (data.length < 2) {
        data = randData()
    }
    return data
}
const dataTest = [{
    nApproachDirection: 1,
    cdireCtion: '北',
    lanes: [{
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 1,
        nChannelNumberPhase: 3
    }, {
        nThrough: 1,
        nTurnLeft: 1,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 2
    }, {
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 1,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }]
}, {
    nApproachDirection: 2,
    cdireCtion: '东北',
    lanes: [{
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 1,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 1,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 1,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }]
}, {
    nApproachDirection: 3,
    cdireCtion: '东',
    lanes: [{
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 1,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 1,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 1,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }]
}, {
    nApproachDirection: 4,
    cdireCtion: '东南',
    lanes: [{
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 1,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 1,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 1,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }]
}, {
    nApproachDirection: 5,
    cdireCtion: '南',
    lanes: [{
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 1,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 1,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 1,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }]
}, {
    nApproachDirection: 6,
    cdireCtion: '西南',
    lanes: [{
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 1,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 1,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 1,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }]
}, {
    nApproachDirection: 7,
    cdireCtion: '西',
    lanes: [{
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 1,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 1,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 1,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }]
}, {
    nApproachDirection: 8,
    cdireCtion: '西北',
    lanes: [{
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 1,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 1,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 1,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }]
}]
const dataTest2 = [{
    nApproachDirection: 1,
    cdireCtion: '东',
    lanes: [{
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 1,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 1,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 1,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }]
}, {
    nApproachDirection: 2,
    cdireCtion: '南',
    lanes: [{
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 1,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 1,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 1,
        nTurnLeft: 0,
        nTurnRight: 0,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }, {
        nThrough: 0,
        nTurnLeft: 0,
        nTurnRight: 1,
        nTurnAround: 0,
        nChannelNumberPhase: 1
    }]
}]
const rand = (n, m) => {
    var c = m - n + 1
    return Math.floor(Math.random() * c + n)
}
onMounted(() => {
    let data = randData()
    console.log(data)
    let laneC = new Lane({
        canvas: laneCanvas.value,
        data: [...data]
    })

    setInterval(() => {
        data.forEach(dataItem => {
            dataItem.lanes.forEach(lane => {
                lane.nChannelNumberPhase = rand(1, 3)
            })
        })
        laneC.setData(data)
    }, 5000)
})

</script>
  
<style scoped lang="scss">
.lane {
    width: 100%;
    height: 100%;
    background-color: #325e76;
}
</style>

lane.js源码

class Lane {
    constructor(opt) {
        this.dpr = window.devicePixelRatio || 1
        this.canvas = opt.canvas
        this.w = null
        this.h = null
        this.ctx = null

        this.data = opt.data
        // 车道范围坐标
        this.region = []
        // 车道线坐标
        this.dataXY = []

        // 路中心空白区域占canvas宽高最小值的比,用来计算车道宽度。占比越大,中心空白区域越大,车道越宽,线路越短。取值范围0-1,不允许取0,1
        this.laneCenterProportion = 0.6 || opt.laneCenterProportion
        // 车道样式
        this.laneStyle = opt.laneStyle

        this.init()
    }
    init() {
        if (!this.canvas) {
            return
        }

        if (this.canvas.width !== Math.floor(this.canvas.offsetWidth * this.dpr) || this.canvas.height !== Math.floor(this.canvas.offsetHeight * this.dpr)) {
            this.w = this.canvas.width = Math.floor(this.canvas.offsetWidth * this.dpr)
            this.h = this.canvas.height = Math.floor(this.canvas.offsetHeight * this.dpr)
        }
        this.ctx = this.canvas.getContext('2d')

        this.getLaneStyle()
        this.formatDataXY()
        this.getRegion()
        this.draw()
    }
    // 获取车道样式
    getLaneStyle() {
        let laneStyle = {
            // 车道范围
            region: {
                width: 2 * this.dpr,
                color: '#fff',
                type: 'solid',
                CurveType: 'quadratic', // normal: 插值曲线, quadratic: 二次贝塞尔, arc: 圆弧线。arc有问题,请勿使用
                background: '#1f2748'
            },
            // 车道左侧车道线
            innerLeft: {
                width: 1 * this.dpr,
                color: '#999',
                type: [10 * this.dpr, 10 * this.dpr],
            },
            // 车道右侧车道线
            innerRight: {
                width: 1 * this.dpr,
                color: '#eee',
                type: [10 * this.dpr, 10 * this.dpr],
            },
            // 车道分割线
            innerDivider: {
                width: 2 * this.dpr,
                color: '#f0bf0a',
                type: 'solid'
            },
            // 车道标识
            direction: {
                widthProportion: 0.1, // 占车道比例,建议小于0.2
                HeightWidthProportion: 10, // 高宽比,建议大于5
                maxWidth: 20 * this.dpr,
                arrowWidth: 2, // 箭头/方向线的比例, 建议大于1小于2
                background: '#ddd'
            },
            // 斑马线
            zebraCrossing: {
                widthProportion: 0.05, // 单个斑马线宽占车道比例,建议小于0.2
                widthHeightProportion: 0.2, // 单个斑马线宽高比,建议小于0.5
                color: '#ddd'
            },
            // 红绿灯
            trafficLight: {
                rProportion: 0.3, // 单个红绿灯半径占车道比例,建议小于0.5,
                colors: ['#FF0033', '#33CC00', '#FFFF33'],
            }
        }
        if (this.laneStyle) {
            this.laneStyle = Object.assign(laneStyle, this.laneStyle)
        } else {
            this.laneStyle = laneStyle
        }

        let laneMaxNum = this.getLaneMaxNum()
        let sideLength = this.getSideLength()
        // 车道宽度 / 2 表示双向
        this.laneStyle.width = sideLength / 2 / laneMaxNum
        // 方向表示线宽高
        this.laneStyle.direction.width = this.laneStyle.width * this.laneStyle.direction.widthProportion
        if (this.laneStyle.direction.width > this.laneStyle.direction.maxWidth) {
            this.laneStyle.direction.width = this.laneStyle.direction.maxWidth
        }
        this.laneStyle.direction.height = this.laneStyle.direction.width * this.laneStyle.direction.HeightWidthProportion
        // 斑马线宽高
        this.laneStyle.zebraCrossing.width = this.laneStyle.width * this.laneStyle.zebraCrossing.widthProportion
        this.laneStyle.zebraCrossing.height = this.laneStyle.zebraCrossing.width / this.laneStyle.zebraCrossing.widthHeightProportion
        this.laneStyle.zebraCrossing.type = [this.laneStyle.zebraCrossing.width, this.laneStyle.zebraCrossing.width * 4]
        // 红绿灯半径
        this.laneStyle.trafficLight.r = this.laneStyle.width * this.laneStyle.trafficLight.rProportion
    }
    // 获取最大车道数
    getLaneMaxNum() {
        let laneMaxNum = 0
        this.data.forEach(item => {
            if (item.lanes.length > laneMaxNum) {
                laneMaxNum = item.lanes.length
            }
        })
        if(laneMaxNum === 1){
            laneMaxNum = 2 
        }
        return laneMaxNum
    }
    // 获取中心路口八边形边长
    getSideLength() {
        let minW = this.w > this.h ? this.h : this.w
        let sideLength = minW * this.laneCenterProportion / (Math.sqrt(2) + 1)
        return sideLength
    }
    // 计算车道坐标
    formatDataXY() {
        let dataXY = []
        // this.laneStyle
        // 车道起始中心位置
        let centerX = this.w / 2
        let centerY = this.h - this.h * (1 - this.laneCenterProportion) / 2
        // 车道长度
        let laneLength = Math.sqrt(Math.pow(this.w, 2) * Math.pow(this.h, 2))
        this.data.forEach(dataItem => {
            let dataXYItem = {
                nApproachDirection: dataItem.nApproachDirection,
            }
            // 起始x
            let startX = centerX - this.laneStyle.width * dataItem.lanes.length
            // 起始y
            let startY = centerY + this.laneStyle.zebraCrossing.height * 2
            // 结束Y
            let endY = startY + laneLength
            // 线
            let lines = []
            // 单向车道分割线数量
            let innerLines = dataItem.lanes.length - 1

            // 车道左边线
            lines.push({
                x0: startX,
                y0: startY - this.laneStyle.zebraCrossing.height * 2,
                x1: startX,
                y1: endY,
                type: 'outer'
            })
            // 车道左侧分割线
            for (let i = 0; i < innerLines; i++) {
                let x = startX + (i + 1) * this.laneStyle.width
                lines.push({
                    x0: x,
                    y0: startY,
                    x1: x,
                    y1: endY,
                    style: { ...this.laneStyle.innerLeft }
                })
            }
            // 左右车道分割线
            let dividerX = startX + (innerLines + 1) * this.laneStyle.width
            lines.push({
                x0: dividerX,
                y0: startY,
                x1: dividerX,
                y1: endY,
                style: { ...this.laneStyle.innerDivider }
            })
            // 车道右侧分割线
            for (let i = 0; i < innerLines; i++) {
                let x = startX + (innerLines + i + 2) * this.laneStyle.width
                lines.push({
                    x0: x,
                    y0: startY,
                    x1: x,
                    y1: endY,
                    style: { ...this.laneStyle.innerRight }
                })
            }
            // 车道右边线
            let outerRightx = startX + (innerLines + 1) * 2 * this.laneStyle.width
            lines.push({
                x0: outerRightx,
                y0: startY - this.laneStyle.zebraCrossing.height * 2,
                x1: outerRightx,
                y1: endY,
                type: 'outer'
            })
            dataXYItem.lines = lines

            // 方向标识
            let directionIdentifyings = []
            for (let i = 0; i < dataItem.lanes.length; i++) {
                let laneItem = dataItem.lanes[i]
                let key = [laneItem.nThrough, laneItem.nTurnLeft, laneItem.nTurnRight, laneItem.nTurnAround].join('')
                let line = lines[innerLines + i + 1]
                directionIdentifyings.push(this.getDirectionIdentifyings(key, this.laneStyle.direction.width, this.laneStyle.direction.height, {
                    x: line.x0 + this.laneStyle.width / 2,
                    y: line.y0 + this.laneStyle.direction.height / 2 + this.laneStyle.trafficLight.r * 4
                }))
            }
            dataXYItem.directionIdentifyings = directionIdentifyings

            // 斑马线
            dataXYItem.zebraCrossing = [{
                x: lines[0].x0,
                y: startY - this.laneStyle.zebraCrossing.height
            }, {
                x: lines[lines.length - 1].x0,
                y: startY - this.laneStyle.zebraCrossing.height
            }]

            // 红绿灯
            let trafficLights = []
            for (let i = 0; i < dataItem.lanes.length; i++) {
                let laneItem = dataItem.lanes[i]
                let line = lines[innerLines + i + 1]
                trafficLights.push({
                    x: line.x0 + this.laneStyle.width / 2,
                    y: line.y0 + this.laneStyle.trafficLight.r * 2,
                    r: this.laneStyle.trafficLight.r,
                    color: this.laneStyle.trafficLight.colors[laneItem.nChannelNumberPhase - 1]
                })
            }
            dataXYItem.trafficLights = trafficLights

            dataXY.push(dataXYItem)
        })

        this.dataXYByRotate(dataXY)

        this.dataXY = dataXY
    }
    // 获取方向标识坐标
    getDirectionIdentifyings(key, w, h, centerXY) {
        // 标识边界
        let topY = centerXY.y - h / 2
        let bottomY = centerXY.y + h / 2
        let leftX = centerXY.x - w / 2 * 3
        let rightX = centerXY.x + w / 2 * 3
        // 直行线中心位置
        let cX = centerXY.x + w
        let cY = centerXY.y
        // 箭头宽高
        let arrowW = w * this.laneStyle.direction.arrowWidth
        let arrowH = arrowW * Math.sin(Math.PI / 3)
        // 线坐标
        let points = []
        // 三角形坐标
        let arrowPoints = []
        switch (key) {
            case '0001':
                // 调头
                arrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: leftX + w / 2, y: bottomY - arrowH },
                    { x: leftX + w / 2, y: topY + w / 2 },
                    { x: leftX + w / 2 * 5, y: topY + w / 2 },
                    { x: leftX + w / 2 * 5, y: bottomY },
                ])
                break;
            case '0100':
                // 左转
                arrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[0][0].x + arrowPoints[0][2].x) / 2, y: (arrowPoints[0][0].y + arrowPoints[0][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                break;
            case '1000':
                // 直行
                leftX = centerXY.x - w / 2
                rightX = centerXY.x + w / 2
                cX = centerXY.x
                arrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: cX, y: topY + arrowH },
                    { x: cX, y: bottomY }
                ])
                break;
            case '0010':
                // 右转
                cX = centerXY.x - w
                arrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[0][0].x + arrowPoints[0][2].x) / 2, y: (arrowPoints[0][0].y + arrowPoints[0][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                break;
            case '0101':
                // 调头左转
                arrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: leftX + w / 2, y: bottomY - arrowH },
                    { x: leftX + w / 2, y: cY + w / 2 },
                    { x: leftX + w / 2 * 5, y: cY + w / 2 },
                    { x: leftX + w / 2 * 5, y: bottomY },
                ])
                arrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[1][0].x + arrowPoints[1][2].x) / 2, y: (arrowPoints[1][0].y + arrowPoints[1][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                break;
            case '1001':
                // 调头直行
                arrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: leftX + w / 2, y: bottomY - arrowH },
                    { x: leftX + w / 2, y: cY + w / 2 },
                    { x: leftX + w / 2 * 5, y: cY + w / 2 },
                    { x: leftX + w / 2 * 5, y: bottomY },
                ])
                arrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: cX, y: topY + arrowH },
                    { x: cX, y: bottomY }
                ])
                break;
            case '0011':
                // 调头右转
                leftX = centerXY.x - w / 2 * 5
                rightX = centerXY.x + w / 2 * 5
                arrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: leftX + w / 2, y: bottomY - arrowH },
                    { x: leftX + w / 2, y: cY + w / 2 },
                    { x: leftX + w / 2 * 5, y: cY + w / 2 },
                    { x: leftX + w / 2 * 5, y: bottomY },
                ])
                cX = centerXY.x
                arrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[1][0].x + arrowPoints[1][2].x) / 2, y: (arrowPoints[1][0].y + arrowPoints[1][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                break;
            case '1100':
                // 左转直行
                arrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[0][0].x + arrowPoints[0][2].x) / 2, y: (arrowPoints[0][0].y + arrowPoints[0][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                arrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: cX, y: topY + arrowH },
                    { x: cX, y: bottomY }
                ])
                break;
            case '0110':
                // 左转右转
                leftX = centerXY.x - w / 2 * 5
                rightX = centerXY.x + w / 2 * 5
                cX = centerXY.x
                arrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[0][0].x + arrowPoints[0][2].x) / 2, y: (arrowPoints[0][0].y + arrowPoints[0][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                arrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[1][0].x + arrowPoints[1][2].x) / 2, y: (arrowPoints[1][0].y + arrowPoints[1][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                break;
            case '1010':
                // 直行右转
                cX = centerXY.x - w
                arrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: cX, y: topY + arrowH },
                    { x: cX, y: bottomY }
                ])
                arrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[1][0].x + arrowPoints[1][2].x) / 2, y: (arrowPoints[1][0].y + arrowPoints[1][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                break;
            case '1101':
                // 调头左转直行
                arrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: leftX + w / 2, y: bottomY - arrowH },
                    { x: leftX + w / 2, y: cY + w / 2 },
                    { x: leftX + w / 2 * 5, y: cY + w / 2 },
                    { x: leftX + w / 2 * 5, y: bottomY },
                ])
                arrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[1][0].x + arrowPoints[1][2].x) / 2, y: (arrowPoints[1][0].y + arrowPoints[1][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                arrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: cX, y: topY + arrowH },
                    { x: cX, y: bottomY }
                ])
                break;
            case '1011':
                // 调头直行右转
                leftX = centerXY.x - w / 2 * 5
                rightX = centerXY.x + w / 2 * 5
                cX = centerXY.x
                arrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: leftX + w / 2, y: bottomY - arrowH },
                    { x: leftX + w / 2, y: cY + w / 2 },
                    { x: leftX + w / 2 * 5, y: cY + w / 2 },
                    { x: leftX + w / 2 * 5, y: bottomY },
                ])
                arrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: cX, y: topY + arrowH },
                    { x: cX, y: bottomY }
                ])
                arrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[2][0].x + arrowPoints[2][2].x) / 2, y: (arrowPoints[2][0].y + arrowPoints[2][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                break;
            case '0111':
                // 调头左转右转
                leftX = centerXY.x - w / 2 * 5
                rightX = centerXY.x + w / 2 * 5
                cX = centerXY.x
                arrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: leftX + w / 2, y: bottomY - arrowH },
                    { x: leftX + w / 2, y: cY + w / 2 },
                    { x: leftX + w / 2 * 5, y: cY + w / 2 },
                    { x: leftX + w / 2 * 5, y: bottomY },
                ])
                arrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[1][0].x + arrowPoints[1][2].x) / 2, y: (arrowPoints[1][0].y + arrowPoints[1][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                arrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[2][0].x + arrowPoints[2][2].x) / 2, y: (arrowPoints[2][0].y + arrowPoints[2][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                break;
            case '1110':
                // 左转直行右转
                leftX = centerXY.x - w / 2 * 5
                rightX = centerXY.x + w / 2 * 5
                cX = centerXY.x
                arrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[0][0].x + arrowPoints[0][2].x) / 2, y: (arrowPoints[0][0].y + arrowPoints[0][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                arrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: cX, y: topY + arrowH },
                    { x: cX, y: bottomY }
                ])
                arrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[2][0].x + arrowPoints[2][2].x) / 2, y: (arrowPoints[2][0].y + arrowPoints[2][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                break;
            case '1111':
                // 调头左转直行右转
                leftX = centerXY.x - w / 2 * 5
                rightX = centerXY.x + w / 2 * 5
                cX = centerXY.x
                arrowPoints.push(this.getArrow(1, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: leftX + w / 2, y: bottomY - arrowH },
                    { x: leftX + w / 2, y: cY + w / 2 },
                    { x: leftX + w / 2 * 5, y: cY + w / 2 },
                    { x: leftX + w / 2 * 5, y: bottomY },
                ])
                arrowPoints.push(this.getArrow(2, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[1][0].x + arrowPoints[1][2].x) / 2, y: (arrowPoints[1][0].y + arrowPoints[1][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                arrowPoints.push(this.getArrow(3, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: cX, y: topY + arrowH },
                    { x: cX, y: bottomY }
                ])
                arrowPoints.push(this.getArrow(4, arrowW, arrowH, w, h, topY, bottomY, leftX, rightX, cX, cY))
                points.push([
                    { x: (arrowPoints[3][0].x + arrowPoints[3][2].x) / 2, y: (arrowPoints[3][0].y + arrowPoints[3][2].y) / 2 },
                    { x: cX, y: cY },
                    { x: cX, y: bottomY }
                ])
                break;
        }

        return { arrowPoints, points }
    }
    getArrow(key, w, h, w2, h2, topY, bottomY, leftX, rightX, cX, cY) {
        let point = []
        let wd = (w - w2) / 2 // 三角形边长与线宽的差值的一半

        let rotateDeg = 30// 左转右转旋转角度
        let hv = h2 / Math.cos(Math.PI / 180 * rotateDeg) // 计算左转右转虚拟线长
        let topYv = topY - (hv - h2) / 2 // 虚拟起始高度

        switch (key) {
            case 1:
                // 调头
                point = [
                    { x: leftX - wd, y: bottomY - h },
                    { x: leftX + w2 / 2, y: bottomY },
                    { x: leftX + w2 + wd, y: bottomY - h }
                ]
                break;
            case 2:
                // 左转
                point = [
                    { x: cX + w / 2, y: topYv + h },
                    { x: cX, y: topYv },
                    { x: cX - w / 2, y: topYv + h }
                ]
                point.forEach(item => {
                    let newXY = this.computePosition(item.x, item.y, -rotateDeg, cX, cY)
                    item.x = newXY.x
                    item.y = newXY.y
                })
                break;
            case 3:
                // 直行
                point = [
                    { x: cX + w / 2, y: topY + h },
                    { x: cX, y: topY },
                    { x: cX - w / 2, y: topY + h }
                ]
                break;
            case 4:
                // 右转
                point = [
                    { x: cX + w / 2, y: topYv + h },
                    { x: cX, y: topYv },
                    { x: cX - w / 2, y: topYv + h }
                ]
                point.forEach(item => {
                    let newXY = this.computePosition(item.x, item.y, rotateDeg, cX, cY)
                    item.x = newXY.x
                    item.y = newXY.y
                })
                break;
        }
        return point
    }
    // 计算旋转坐标
    dataXYByRotate(dataXY) {
        let centerX = this.w / 2
        let centerY = this.h / 2
        dataXY.forEach(dataXYItem => {
            // 八边形,一个边占45度
            let rotateReg = -180 + (dataXYItem.nApproachDirection - 1) * 45
            dataXYItem.lines.forEach(line => {
                let xy0 = this.computePosition(line.x0, line.y0, rotateReg, centerX, centerY)
                line.x0 = xy0.x
                line.y0 = xy0.y

                let xy1 = this.computePosition(line.x1, line.y1, rotateReg, centerX, centerY)
                line.x1 = xy1.x
                line.y1 = xy1.y
            })
            dataXYItem.directionIdentifyings.forEach(directionIdentifying => {
                directionIdentifying.points.forEach(point => {
                    point.forEach(item => {
                        let { x, y } = this.computePosition(item.x, item.y, rotateReg, centerX, centerY)
                        item.x = x
                        item.y = y
                    })
                })
                directionIdentifying.arrowPoints.forEach(arrowPoint => {
                    arrowPoint.forEach(item => {
                        let { x, y } = this.computePosition(item.x, item.y, rotateReg, centerX, centerY)
                        item.x = x
                        item.y = y
                    })
                })
            })
            dataXYItem.zebraCrossing.forEach(zebraCrossing => {
                let { x, y } = this.computePosition(zebraCrossing.x, zebraCrossing.y, rotateReg, centerX, centerY)
                zebraCrossing.x = x
                zebraCrossing.y = y
            })
            dataXYItem.trafficLights.forEach(trafficLight => {
                let { x, y } = this.computePosition(trafficLight.x, trafficLight.y, rotateReg, centerX, centerY)
                trafficLight.x = x
                trafficLight.y = y
            })
        })
    }
    // 旋转计算
    computePosition(x, y, angle, centerX, centerY) {
        // 圆心
        let a = centerX;
        let b = centerY;
        // 计算
        let c = Math.PI / 180 * angle;
        let rx = (x - a) * Math.cos(c) - (y - b) * Math.sin(c) + a;
        let ry = (y - b) * Math.cos(c) + (x - a) * Math.sin(c) + b;
        return { x: rx, y: ry };
    }
    // 获取车道范围
    getRegion() {
        let region = []
        for (let i = 0; i < this.dataXY.length; i++) {
            let dataXYItem = this.dataXY[i]
            let linesLength = dataXYItem.lines.length
            if (i !== 0) {
                // 衔接上一车道
                let prevDataXYItem = this.dataXY[i - 1]
                let data = {
                    prevNApproachDirection: prevDataXYItem.nApproachDirection,
                    nApproachDirection: dataXYItem.nApproachDirection,
                    type: 'connect'
                }
                let diffNApproachDirection = dataXYItem.nApproachDirection - prevDataXYItem.nApproachDirection
                if(diffNApproachDirection > 4){
                    diffNApproachDirection = (prevDataXYItem.nApproachDirection + 8) - dataXYItem.nApproachDirection
                }
                if (diffNApproachDirection === 4) {
                    // 车道正对,直线即可
                    data.point = [
                        { x: prevDataXYItem.lines[0].x0, y: prevDataXYItem.lines[0].y0 },
                        { x: dataXYItem.lines[linesLength - 1].x0, y: dataXYItem.lines[linesLength - 1].y0 },
                    ]
                    region.push(data)
                } else {
                    if (this.laneStyle.region.CurveType === 'arc') {
                        let angle = 45 * diffNApproachDirection
                        let startAngle = 45 * (prevDataXYItem.nApproachDirection - 5)
                        data.OR = this.findCircleCenter(
                            prevDataXYItem.lines[0].x0, prevDataXYItem.lines[0].y0,
                            dataXYItem.lines[linesLength - 1].x0, dataXYItem.lines[linesLength - 1].y0,
                            angle,
                            true
                        )
                        data.OR.startAngle = startAngle
                        data.OR.endAngle = startAngle - angle
                        data.OR.anticlockwise = true
                    } else {
                        // 曲线
                        let laneXY0 = this.calculateIntersection([
                            [prevDataXYItem.lines[0].x0, prevDataXYItem.lines[0].y0],
                            [prevDataXYItem.lines[0].x1, prevDataXYItem.lines[0].y1],
                        ], [
                            [dataXYItem.lines[linesLength - 1].x0, dataXYItem.lines[linesLength - 1].y0],
                            [dataXYItem.lines[linesLength - 1].x1, dataXYItem.lines[linesLength - 1].y1],
                        ])
                        let laneXY1 = [(prevDataXYItem.lines[0].x0 + dataXYItem.lines[linesLength - 1].x0) / 2, (prevDataXYItem.lines[0].y0 + dataXYItem.lines[linesLength - 1].y0) / 2]
                        let originPoints = [
                            { x: prevDataXYItem.lines[0].x0, y: prevDataXYItem.lines[0].y0 },
                            { x: laneXY1[0] - (laneXY1[0] - laneXY0[0]) * diffNApproachDirection / 4, y: laneXY1[1] - (laneXY1[1] - laneXY0[1]) * diffNApproachDirection / 4 },
                            { x: dataXYItem.lines[linesLength - 1].x0, y: dataXYItem.lines[linesLength - 1].y0 },
                        ]
                        if (this.laneStyle.region.CurveType === 'normal') {
                            let point = this.getCurveVertex(originPoints)
                            data.point = point
                        } else {
                            data.point = originPoints
                        }
                    }
                    region.push(data)
                }
            }
            // 车道范围
            region.push({
                nApproachDirection: dataXYItem.nApproachDirection,
                x0: dataXYItem.lines[linesLength - 1].x0,
                y0: dataXYItem.lines[linesLength - 1].y0,
                x1: dataXYItem.lines[linesLength - 1].x1,
                y1: dataXYItem.lines[linesLength - 1].y1,
                x2: dataXYItem.lines[0].x1,
                y2: dataXYItem.lines[0].y1,
                x3: dataXYItem.lines[0].x0,
                y3: dataXYItem.lines[0].y0,
                type: 'lane'
            })
            if (i === this.dataXY.length - 1) {
                // 衔接起始车道
                let startDataXYItem = this.dataXY[0]
                let startLinesLength = startDataXYItem.lines.length
                let data = {
                    startNApproachDirection: startDataXYItem.nApproachDirection,
                    nApproachDirection: dataXYItem.nApproachDirection,
                    type: 'connect'
                }
                let diffNApproachDirection = startDataXYItem.nApproachDirection + 8 - dataXYItem.nApproachDirection
                if(diffNApproachDirection > 4){
                    diffNApproachDirection = dataXYItem.nApproachDirection - startDataXYItem.nApproachDirection
                }
                if (diffNApproachDirection === 4) {
                    // 车道正对,直线即可
                    data.point = [
                        { x: dataXYItem.lines[0].x0, y: dataXYItem.lines[0].y0 },
                        { x: startDataXYItem.lines[startLinesLength - 1].x0, y: startDataXYItem.lines[startLinesLength - 1].y0 },
                    ]
                    region.push(data)
                } else {
                    if (this.laneStyle.region.CurveType === 'arc') {
                        let angle = 45 * diffNApproachDirection
                        let startAngle = 45 * (dataXYItem.nApproachDirection - 1)
                        data.OR = this.findCircleCenter(
                            dataXYItem.lines[0].x0, dataXYItem.lines[0].y0,
                            startDataXYItem.lines[linesLength - 1].x0, startDataXYItem.lines[linesLength - 1].y0,
                            angle,
                            true
                        )
                        data.OR.endAngle = startAngle + angle
                        data.OR.startAngle = startAngle
                        data.OR.anticlockwise = false
                    } else {
                        // 曲线
                        let laneXY0 = this.calculateIntersection([
                            [dataXYItem.lines[0].x0, dataXYItem.lines[0].y0],
                            [dataXYItem.lines[0].x1, dataXYItem.lines[0].y1],
                        ], [
                            [startDataXYItem.lines[startLinesLength - 1].x0, startDataXYItem.lines[startLinesLength - 1].y0],
                            [startDataXYItem.lines[startLinesLength - 1].x1, startDataXYItem.lines[startLinesLength - 1].y1],
                        ])
                        let laneXY1 = [(startDataXYItem.lines[startLinesLength - 1].x0 + dataXYItem.lines[0].x0) / 2, (startDataXYItem.lines[startLinesLength - 1].y0 + dataXYItem.lines[0].y0) / 2]
                        let originPoints = [
                            { x: dataXYItem.lines[0].x0, y: dataXYItem.lines[0].y0 },
                            { x: laneXY1[0] - (laneXY1[0] - laneXY0[0]) * diffNApproachDirection / 4, y: laneXY1[1] - (laneXY1[1] - laneXY0[1]) * diffNApproachDirection / 4 },
                            { x: startDataXYItem.lines[startLinesLength - 1].x0, y: startDataXYItem.lines[startLinesLength - 1].y0 },
                        ]
                        if (this.laneStyle.region.CurveType === 'normal') {
                            let point = this.getCurveVertex(originPoints)
                            data.point = point
                        } else {
                            data.point = originPoints
                        }
                    }
                    region.push(data)
                }
            }
        }
        this.region = region
    }
    // 获取两条直线的交点
    calculateIntersection(line1, line2) {
        // 解方程组
        const x1 = line1[0][0];
        const y1 = line1[0][1];
        const x2 = line1[1][0];
        const y2 = line1[1][1];

        const x3 = line2[0][0];
        const y3 = line2[0][1];
        const x4 = line2[1][0];
        const y4 = line2[1][1];

        const denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);

        if (denominator === 0) {
            // 直线平行,没有交点
            return null;
        }

        const intersectionX = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / denominator;
        const intersectionY = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / denominator;

        return [intersectionX, intersectionY];
    }
    // 以下四个方法获取曲线
    getCurveVertex(vertex, pointsPow = 0.4) {
        let length = 0
        for (let i = 0; i < vertex.length - 1; i++) {
            length += Math.sqrt(Math.pow(vertex[i].x - vertex[i + 1].x, 2) + Math.pow(vertex[i].y - vertex[i + 1].y, 2))
        }
        length = Math.ceil(length)
        return this.getNewData(vertex, length * pointsPow)
    }
    // 曲线 插值
    getNewData(pointsOrigin, pointsPow) {
        const points = []
        const divisions = (pointsOrigin.length - 1) * pointsPow
        for (let i = 0; i < divisions; i++) {
            points.push(this.getPoint(i, divisions, pointsOrigin, pointsPow))
        }
        return points
    }
    getPoint(i, divisions, pointsOrigin, pointsPow) {
        const isRealI = (i * divisions) % pointsPow
        const p = ((pointsOrigin.length - 1) * i) / divisions
        const intPoint = Math.floor(p)
        const weight = p - intPoint
        const p0 = pointsOrigin[intPoint === 0 ? intPoint : intPoint - 1]
        const p1 = pointsOrigin[intPoint]
        const p2 = pointsOrigin[intPoint > pointsOrigin.length - 2 ? pointsOrigin.length - 1 : intPoint + 1]
        const p3 = pointsOrigin[intPoint > pointsOrigin.length - 3 ? pointsOrigin.length - 1 : intPoint + 2]
        return {
            isReal: isRealI === 0,
            x: this.catmullRom(weight, p0.x, p1.x, p2.x, p3.x),
            y: this.catmullRom(weight, p0.y, p1.y, p2.y, p3.y)
        }
    }
    catmullRom(t, p0, p1, p2, p3) {
        const v0 = (p2 - p0) * 0.5
        const v1 = (p3 - p1) * 0.5
        const t2 = t * t
        const t3 = t * t2
        return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1
    }
    // 根据圆上两点以及夹角角度 求 圆心
    findCircleCenter(x1, y1, x2, y2, theta, isNeg) {
        let cx = 0;
        let cy = 0;
        let dDistance = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
        let dRadius = dDistance * 0.5 / Math.sin(Math.PI / 180 * theta * 0.5);
        if (dDistance == 0.0) {
            // cout << "\n输入了相同的点!\n";
            return false;
        }
        if ((2 * dRadius) < dDistance) {
            // cout << "\n两点间距离大于直径!\n";
            return false;
        }
        let k_verticle = 0.0;
        let mid_x = 0.0
        let mid_y = 0.0;
        let a = 1.0;
        let b = 1.0;
        let c = 1.0;
        let k = (y2 - y1) / (x2 - x1);
        let cx1, cy1, cx2, cy2;
        if (k == 0) {
            cx1 = (x1 + x2) / 2.0;
            cx2 = (x1 + x2) / 2.0;
            cy1 = y1 + Math.sqrt(dRadius * dRadius - (x1 - x2) * (x1 - x2) / 4.0);
            cy2 = y2 - Math.sqrt(dRadius * dRadius - (x1 - x2) * (x1 - x2) / 4.0);
        }
        else {
            k_verticle = -1.0 / k;
            mid_x = (x1 + x2) / 2.0;
            mid_y = (y1 + y2) / 2.0;
            a = 1.0 + k_verticle * k_verticle;
            b = -2 * mid_x - k_verticle * k_verticle * (x1 + x2);
            c = mid_x * mid_x + k_verticle * k_verticle * (x1 + x2) * (x1 + x2) / 4.0 -
                (dRadius * dRadius - ((mid_x - x1) * (mid_x - x1) + (mid_y - y1) * (mid_y - y1)));
    
            cx1 = (-1.0 * b + Math.sqrt(b * b - 4 * a * c)) / (2 * a);
            cx2 = (-1.0 * b - Math.sqrt(b * b - 4 * a * c)) / (2 * a);
            cy1 = this.y_Coordinates(mid_x, mid_y, k_verticle, cx1);
            cy2 = this.y_Coordinates(mid_x, mid_y, k_verticle, cx2);
        }
        //cx2,cy2为顺时针圆心坐标,cx1,cy1为逆时针圆心坐标
    
        if (isNeg) {
            cx = cx1;
            cy = cy1;
        }
        else {
            cx = cx2;
            cy = cy2;
        }
    
        return {x: cx, y: cy, r: Math.sqrt(Math.pow(cx - x1, 2) + Math.pow(cy - y1, 2))};
    }
    y_Coordinates(x, y, k, x0) {
        return k * x0 - k * x + y;
    }
    // 设置新的红绿灯数据
    setData(data){
        this.dataXY.forEach((dataXYItem, dataXYIndex) => {
            dataXYItem.trafficLights.forEach((trafficLight, trafficLightIndex) => {
                trafficLight.color = this.laneStyle.trafficLight.colors[data[dataXYIndex].lanes[trafficLightIndex].nChannelNumberPhase - 1]
            })
        })
        this.ctx.clearRect(0,0,this.w,this.h)
        this.draw()
    }
    // 绘制
    draw() {
        this.drawRegion()
        this.drawLines()
        this.drawDirectionIdentifyings()
        this.drawZebraCrossing()
        this.drawTrafficLight()
        // this.drawHelper()
    }
    drawRegion() {
        this.ctx.save()
        // 缩放
        // this.ctx.translate(this.w / 2, this.h / 2)
        // this.ctx.scale(this.scaleC, this.scaleC)
        // this.ctx.translate(-this.w / 2, -this.h / 2)

        this.ctx.beginPath()
        this.ctx.fillStyle = this.laneStyle.region.background
        this.ctx.lineWidth = this.laneStyle.region.width
        this.ctx.strokeStyle = this.laneStyle.region.color
        this.ctx.lineJoin = 'round';
        for (let i = 0; i < this.region.length; i++) {
            let regionItem = this.region[i]
            if (regionItem.type === 'connect') {
                if (regionItem?.point?.length === 2 && this.laneStyle.region.CurveType !== 'arc') {
                    // 直线
                    regionItem.point.forEach(item => {
                        this.ctx.lineTo(item.x, item.y)
                    })
                } else if (this.laneStyle.region.CurveType === 'arc') {
                    // 圆
                    if(regionItem.OR)
                    this.ctx.arc(regionItem.OR.x, regionItem.OR.y, regionItem.OR.r, regionItem.OR.startAngle * Math.PI / 180, regionItem.OR.endAngle * Math.PI / 180, regionItem.OR.anticlockwise)
                } else if (this.laneStyle.region.CurveType === 'normal') {
                    // 插值
                    regionItem.point.forEach(item => {
                        this.ctx.lineTo(item.x, item.y)
                    })
                } else {
                    // 二次贝塞尔
                    this.ctx.lineTo(regionItem.point[0].x, regionItem.point[0].y)
                    this.ctx.quadraticCurveTo(regionItem.point[1].x, regionItem.point[1].y, regionItem.point[2].x, regionItem.point[2].y)
                }
            } else {
                this.ctx.lineTo(regionItem.x0, regionItem.y0)
                this.ctx.lineTo(regionItem.x1, regionItem.y1)
                this.ctx.lineTo(regionItem.x2, regionItem.y2)
                this.ctx.lineTo(regionItem.x3, regionItem.y3)
            }
        }
        this.ctx.fill()
        this.ctx.stroke()
        this.ctx.closePath()

        this.ctx.restore()
    }
    drawLines() {
        this.dataXY.forEach((dataXYItem, dataXYIndex) => {
            dataXYItem.lines.forEach(lineItem => {
                if (lineItem.type !== 'outer') {
                    this.ctx.save()
                    // 缩放
                    // this.ctx.translate(this.w / 2, this.h / 2)
                    // this.ctx.scale(this.scaleC, this.scaleC)
                    // this.ctx.translate(-this.w / 2, -this.h / 2)

                    this.ctx.beginPath()
                    this.ctx.lineWidth = lineItem.style.width
                    this.ctx.strokeStyle = lineItem.style.color
                    if (lineItem.style.type !== 'solid') {
                        this.ctx.setLineDash(lineItem.style.type);
                    }
                    this.ctx.lineTo(lineItem.x0, lineItem.y0)
                    this.ctx.lineTo(lineItem.x1, lineItem.y1)
                    this.ctx.stroke()
                    this.ctx.closePath()
                    this.ctx.restore()
                }
            })
        })
    }
    drawDirectionIdentifyings() {
        this.dataXY.forEach((dataXYItem, dataXYIndex) => {
            dataXYItem.directionIdentifyings.forEach(directionIdentifying => {
                this.ctx.save()
                // 缩放
                // this.ctx.translate(this.w / 2, this.h / 2)
                // this.ctx.scale(this.scaleC, this.scaleC)
                // this.ctx.translate(-this.w / 2, -this.h / 2)

                directionIdentifying.points.forEach(pointItem => {
                    this.ctx.beginPath()
                    this.ctx.lineWidth = directionIdentifying.w
                    this.ctx.strokeStyle = this.laneStyle.direction.background
                    pointItem.forEach(item => {
                        this.ctx.lineTo(item.x, item.y)
                    })
                    this.ctx.stroke()
                    this.ctx.closePath()
                })

                directionIdentifying.arrowPoints.forEach(arrowPoint => {
                    this.ctx.beginPath()
                    this.ctx.fillStyle = this.laneStyle.direction.background
                    arrowPoint.forEach(item => {
                        this.ctx.lineTo(item.x, item.y)
                    })
                    this.ctx.fill()
                    this.ctx.closePath()
                })

                this.ctx.restore()
            })
        })
    }
    drawTrafficLight() {
        this.dataXY.forEach((dataXYItem, dataXYIndex) => {
            dataXYItem.trafficLights.forEach(trafficLight => {
                this.ctx.save()
                // 缩放
                // this.ctx.translate(this.w / 2, this.h / 2)
                // this.ctx.scale(this.scaleC, this.scaleC)
                // this.ctx.translate(-this.w / 2, -this.h / 2)

                this.ctx.beginPath()
                this.ctx.fillStyle = trafficLight.color
                this.ctx.arc(trafficLight.x, trafficLight.y, trafficLight.r, 0, Math.PI * 2)
                this.ctx.fill()
                this.ctx.closePath()

                this.ctx.restore()
            })
        })
    }
    drawZebraCrossing() {
        this.dataXY.forEach((dataXYItem, dataXYIndex) => {
            this.ctx.save()
            // 缩放
            // this.ctx.translate(this.w / 2, this.h / 2)
            // this.ctx.scale(this.scaleC, this.scaleC)
            // this.ctx.translate(-this.w / 2, -this.h / 2)

            this.ctx.beginPath()
            this.ctx.lineWidth = this.laneStyle.zebraCrossing.height
            this.ctx.strokeStyle = this.laneStyle.zebraCrossing.color
            this.ctx.setLineDash(this.laneStyle.zebraCrossing.type);
            dataXYItem.zebraCrossing.forEach(zebraCrossing => {
                this.ctx.lineTo(zebraCrossing.x, zebraCrossing.y)
            })
            this.ctx.stroke()
            this.ctx.closePath()
            this.ctx.restore()
        })
    }
    drawHelper() {
        // 绘制车道方向数字,用来查看车道是否正确
        this.ctx.beginPath()
        this.ctx.fillStyle = '#fff'
        this.ctx.font = 20 * this.dpr + 'px Arial'
        for (let i = 0; i < this.region.length; i++) {
            let regionItem = this.region[i]
            this.ctx.fillText(regionItem.nApproachDirection, regionItem.x0, regionItem.y0)
        }
        this.ctx.closePath()

        // 绘制坐标线
        this.ctx.save()
        this.ctx.lineWidth = 2 * this.dpr
        this.ctx.strokeStyle = 'red'

        this.ctx.beginPath()
        this.ctx.lineTo(this.w / 2, 0)
        this.ctx.lineTo(this.w / 2, this.h)
        this.ctx.stroke()
        this.ctx.closePath()

        this.ctx.beginPath()
        this.ctx.lineTo(0, this.h / 2)
        this.ctx.lineTo(this.w, this.h / 2)
        this.ctx.stroke()
        this.ctx.closePath()
        this.ctx.restore()

        // 绘制方向标识
        let testKeys = ['0001', '0100', '1000', '0010', '0101', '1001', '0011', '1100', '0110', '1010', '1101', '1011', '0111', '1110', '1111']
        let width = this.w / testKeys.length
        let w = width * this.laneStyle.direction.widthProportion
        let h = w * this.laneStyle.direction.HeightWidthProportion
        testKeys.forEach((key, index) => {
            let { arrowPoints, points } = this.getDirectionIdentifyings(key, w, h, { x: width * index + width / 2, y: this.h / 2 })

            this.ctx.save()
            // 缩放
            // this.ctx.translate(this.w / 2, this.h / 2)
            // this.ctx.scale(this.scaleC, this.scaleC)
            // this.ctx.translate(-this.w / 2, -this.h / 2)
            points.forEach(pointItem => {
                this.ctx.beginPath()
                this.ctx.lineWidth = w
                this.ctx.strokeStyle = '#fff'
                pointItem.forEach(item => {
                    this.ctx.lineTo(item.x, item.y)
                })
                this.ctx.stroke()
                this.ctx.closePath()
            })

            arrowPoints.forEach(arrowPoint => {
                this.ctx.beginPath()
                this.ctx.fillStyle = '#fff'
                arrowPoint.forEach(item => {
                    this.ctx.lineTo(item.x, item.y)
                })
                this.ctx.fill()
                this.ctx.closePath()
            })

            this.ctx.restore()
        })
    }
}

export default Lane

你可能感兴趣的:(canvas,canvas,canva可画,前端)