You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现基于折线路径与宽度生成走廊路径的函数?

实现折线走廊路径生成函数 getCorridorPath

需求说明

我们需要编写一个getCorridorPath函数,输入一维折线坐标数组(每两个元素代表一个点的x、y值)和走廊宽度,输出一个闭合的走廊路径坐标数组,实现示例中的走廊效果。

示例输入:

var coords = [50, 50, 150, 50, 250, 100, 220, 200, 350, 100];
var width = 10;

调用方式:

var corridorPath = getCorridorPath(coords, width);

预期输出是一个闭合的一维坐标数组,类似:

[40, 50, 50, 40, 150, 40, 157, 41, 255, 90, 260, 105, 240, 170, 340, 90, 360, 90, 360, 110, 220, 215, 205, 205, 233, 107, 147, 62, 50, 60, 40, 50]

实现思路

  1. 坐标格式转换:把一维坐标数组转成带x、y属性的点对象数组,方便后续计算。
  2. 计算线段法向量:对每段折线,算出垂直方向的单位法向量,用来向两侧偏移生成走廊边界。
  3. 处理转折点:针对折线中间的转折点,计算相邻两段线段的角平分线方向,通过偏移得到走廊在拐角处的过渡点,避免出现尖锐的角。
  4. 拼接路径:把所有左侧边界点按顺序排列,再把右侧边界点逆序排列,最后首尾闭合,形成完整的走廊路径。

完整代码实现

function getCorridorPath(coords, width) {
    // 步骤1:将一维坐标数组转换为点对象数组
    const points = [];
    for (let i = 0; i < coords.length; i += 2) {
        points.push({ x: coords[i], y: coords[i + 1] });
    }
    const halfWidth = width / 2;
    const leftPoints = [];
    const rightPoints = [];

    // 步骤2:处理每一段线段,计算偏移点
    for (let i = 0; i < points.length; i++) {
        const current = points[i];
        const next = points[i + 1];
        
        // 如果是最后一个点,停止处理线段
        if (!next) break;

        // 计算线段的方向向量
        const dx = next.x - current.x;
        const dy = next.y - current.y;
        // 计算线段长度
        const length = Math.sqrt(dx * dx + dy * dy);
        // 计算单位法向量(左右两个方向)
        const normalLeft = { x: -dy / length, y: dx / length };
        const normalRight = { x: dy / length, y: -dx / length };

        // 计算当前线段的左右偏移点
        const left = {
            x: current.x + normalLeft.x * halfWidth,
            y: current.y + normalLeft.y * halfWidth
        };
        const right = {
            x: current.x + normalRight.x * halfWidth,
            y: current.y + normalRight.y * halfWidth
        };

        // 处理转折点(非第一个点)
        if (i > 0) {
            const prev = points[i - 1];
            // 计算前一段线段的方向向量
            const prevDx = current.x - prev.x;
            const prevDy = current.y - prev.y;
            const prevLength = Math.sqrt(prevDx * prevDx + prevDy * prevDy);
            const prevNormalLeft = { x: -prevDy / prevLength, y: prevDx / prevLength };
            const prevNormalRight = { x: prevDy / prevLength, y: -prevDx / prevLength };

            // 前一段的结束偏移点
            const prevLeftEnd = {
                x: current.x + prevNormalLeft.x * halfWidth,
                y: current.y + prevNormalLeft.y * halfWidth
            };
            const prevRightEnd = {
                x: current.x + prevNormalRight.x * halfWidth,
                y: current.y + prevNormalRight.y * halfWidth
            };

            // 计算两个左侧偏移线段的交点(转折点的左侧过渡点)
            const leftIntersection = getLineIntersection(prevLeftEnd, left, current, current);
            // 计算两个右侧偏移线段的交点(转折点的右侧过渡点)
            const rightIntersection = getLineIntersection(prevRightEnd, right, current, current);

            // 替换当前左侧点为交点
            leftPoints.pop();
            leftPoints.push(leftIntersection || left);
            // 替换当前右侧点为交点
            rightPoints.pop();
            rightPoints.push(rightIntersection || right);
        } else {
            // 第一个点直接加入
            leftPoints.push(left);
            rightPoints.push(right);
        }

        // 加入下一个点的偏移点(最后一段的结束点)
        if (i === points.length - 2) {
            leftPoints.push({
                x: next.x + normalLeft.x * halfWidth,
                y: next.y + normalLeft.y * halfWidth
            });
            rightPoints.push({
                x: next.x + normalRight.x * halfWidth,
                y: next.y + normalRight.y * halfWidth
            });
        }
    }

    // 步骤3:拼接路径:左侧点顺序 + 右侧点逆序 + 第一个左侧点闭合
    const path = [];
    leftPoints.forEach(p => path.push(p.x, p.y));
    // 逆序遍历右侧点,排除最后一个(避免重复)
    for (let i = rightPoints.length - 1; i >= 0; i--) {
        path.push(rightPoints[i].x, rightPoints[i].y);
    }
    // 闭合路径,加入第一个左侧点
    path.push(leftPoints[0].x, leftPoints[0].y);

    return path;
}

// 辅助函数:计算两条线段的交点
function getLineIntersection(p1, p2, p3, p4) {
    const denom = (p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y);
    if (denom === 0) return null; // 线段平行或重合

    const ua = ((p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x)) / denom;
    const ub = ((p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x)) / denom;

    if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) {
        return {
            x: p1.x + ua * (p2.x - p1.x),
            y: p1.y + ua * (p2.y - p1.y)
        };
    }
    return null;
}

代码说明

  • getLineIntersection辅助函数用来计算两条线段的交点,处理折线转折点处的边界过渡,确保走廊在拐角处平滑连接。
  • 函数把走廊宽度分成一半,分别向折线两侧偏移,生成左右边界。
  • 最终路径通过拼接左侧边界点、逆序的右侧边界点,并闭合路径,形成完整的走廊多边形。

内容的提问来源于stack exchange,提问作者rem99

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:00:33