SOURCE

/**
 * 点对象
 * @typedef {Object} Point
 * @property {number} x - X坐标
 * @property {number} y - Y坐标
 */

/**
 * 生成航线点
 * @param {Point[]} polygon - 多边形顶点数组,按顺时针或逆时针顺序
 * @param {number} spacing - 航线间隔距离
 * @param {number} [angle=0] - 航线角度(弧度),默认为0(水平航线)
 * @returns {Point[]} 生成的航线点数组
 */
function generateFlightPath(polygon, spacing, angle = 0) {
    console.log('ops',polygon,spacing,angle)
    // 1. 计算多边形的边界框(考虑旋转后的坐标系)
    const bounds = getRotatedBounds(polygon, angle);
    
    // 2. 在旋转后的坐标系中生成扫描线
    const lines = generateScanLines(bounds, spacing);
    
    // 3. 对每条扫描线与多边形求交
    const rotatedPoints = [];
    const cosAngle = Math.cos(-angle);
    const sinAngle = Math.sin(-angle);
    
    lines.forEach(y => {
        const intersections = getPolygonIntersections(polygon, y, angle);
        
        // 对交点按x坐标排序
        intersections.sort((a, b) => a.x - b.x);
        
        // 生成航线点(每隔一个交点取一个点)
        for (let i = 0; i < intersections.length; i += 2) {
            if (i + 1 < intersections.length) {
                const start = intersections[i];
                const end = intersections[i + 1];
                
                // 在起点和终点之间按间距生成点
                const distance = Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2));
                const pointsCount = Math.floor(distance / spacing);
                
                for (let j = 0; j <= pointsCount; j++) {
                    const t = j / pointsCount;
                    const x = start.x + t * (end.x - start.x);
                    const y = start.y + t * (end.y - start.y);
                    rotatedPoints.push({x, y});
                }
            }
        }
    });
    
    // 4. 将点旋转回原始坐标系
    return rotatedPoints.map(p => ({
        x: p.x * cosAngle - p.y * sinAngle,
        y: p.x * sinAngle + p.y * cosAngle
    }));
}

/**
 * 获取多边形在旋转后的边界框
 * @param {Point[]} polygon - 多边形顶点数组
 * @param {number} angle - 旋转角度(弧度)
 * @returns {Object} 边界框 {minX, maxX, minY, maxY}
 */
function getRotatedBounds(polygon, angle) {
    const cosAngle = Math.cos(angle);
    const sinAngle = Math.sin(angle);
    
    let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
    
    polygon.forEach(p => {
        // 旋转点
        const x = p.x * cosAngle + p.y * sinAngle;
        const y = -p.x * sinAngle + p.y * cosAngle;
        
        minX = Math.min(minX, x);
        maxX = Math.max(maxX, x);
        minY = Math.min(minY, y);
        maxY = Math.max(maxY, y);
    });
    
    return {minX, maxX, minY, maxY};
}

/**
 * 生成扫描线
 * @param {Object} bounds - 边界框 {minX, maxX, minY, maxY}
 * @param {number} spacing - 线间距
 * @returns {number[]} 扫描线的y坐标数组
 */
function generateScanLines(bounds, spacing) {
    const lines = [];
    // 从minY开始,每隔spacing生成一条线,直到maxY
    for (let y = bounds.minY; y <= bounds.maxY; y += spacing) {
        lines.push(y);
    }
    return lines;
}

/**
 * 获取多边形与水平扫描线的交点
 * @param {Point[]} polygon - 多边形顶点数组
 * @param {number} y - 扫描线y坐标(在旋转后的坐标系中)
 * @param {number} angle - 旋转角度(弧度)
 * @returns {Point[]} 交点数组
 */
function getPolygonIntersections(polygon, y, angle) {
    const intersections = [];
    const cosAngle = Math.cos(angle);
    const sinAngle = Math.sin(angle);
    
    // 将多边形旋转到水平位置
    const rotatedPolygon = polygon.map(p => ({
        x: p.x * cosAngle + p.y * sinAngle,
        y: -p.x * sinAngle + p.y * cosAngle
    }));
    
    // 检查每条边与扫描线的交点
    for (let i = 0; i < rotatedPolygon.length; i++) {
        const p1 = rotatedPolygon[i];
        const p2 = rotatedPolygon[(i + 1) % rotatedPolygon.length];
        
        // 忽略水平边
        if (Math.abs(p1.y - p2.y) < 1e-6) continue;
        
        // 检查边是否跨越扫描线
        if ((p1.y <= y && p2.y > y) || (p2.y <= y && p1.y > y)) {
            // 计算交点
            const t = (y - p1.y) / (p2.y - p1.y);
            const x = p1.x + t * (p2.x - p1.x);
            
            // 旋转交点回原始坐标系
            intersections.push({
                x: x * cosAngle - y * sinAngle,
                y: x * sinAngle + y * cosAngle
            });
        }
    }
    
    return intersections;
}

// 示例用法
const polygon = [
    {x: 0, y: 0},
    {x: 100, y: 0},
    {x: 100, y: 50},
    {x: 50, y: 100},
    {x: 0, y: 50}
];

const spacing = 10;
const angle = Math.PI / 4; // 45度

const flightPath = generateFlightPath(polygon, spacing, angle);
console.log(flightPath);
console 命令行工具 X clear

                    
>
console