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

基于D3.js:如何在多边形内生成SVG座位/方块的坐标?

问题描述

已知多边形区域、SVG座位/方块的总数量或行列数,如何生成用于插入SVG座位/方块的坐标?
示例:在多边形内添加10行,每行5个SVG座位/方块。

需求细节

  • 如何编写可返回SVG座位/方块插入轴点的函数?
  • 是否存在插值方法,传入多边形即可返回SVG座位/方块的插入点?

现有基础

已实现一个基础网格生成函数,需扩展为支持传入多边形、行列数、排列方向(左到右/上到下)、对齐方式(居中/上/下/左/右),并返回包含x、y坐标点的数据集。

现有网格函数代码:

function gridData() {
    var data = new Array();
    var xpos = 1; //starting xpos and ypos at 1 so the stroke will show when we make the grid below
    var ypos = 1;
    var width = 50;
    var height = 50;
    
    // iterate for rows 
    for (var row = 0; row < 10; row++) {
        data.push( new Array() );
        
        // iterate for cells/columns inside rows
        for (var column = 0; column < 10; column++) {
            data[row].push({
                x: xpos,
                y: ypos,
                width: width,
                height: height
            })
            // increment the x position. I.e. move it over by 50 (width variable)
            xpos += width;
        }
        // reset the x position after a row is complete
        xpos = 1;
        // increment the y position for the next row. Move it down 50 (height variable)
        ypos += height; 
    }
    return data;
}
解决方案

1. 编写返回SVG座位插入轴点的函数

实现这类函数需要四个核心步骤:

  • 计算多边形边界框:提取多边形顶点的最小/最大x、y值,得到能容纳多边形的矩形区域,作为网格布局的基础范围;
  • 计算座位布局参数:用边界框宽度除以列数得到横向间距,高度除以行数得到纵向间距(直接用座位自身宽高作为间距即可保证不重叠);
  • 处理排列与对齐逻辑:
    • 排列方向:左到右模式下先遍历列再换行,上到下模式下先遍历行再换列;
    • 对齐方式:比如居中对齐时,需要计算网格整体相对于边界框的偏移量,再调整每个座位的起始坐标;
  • 筛选多边形内的点:生成网格点后,通过点-in-多边形算法(如射线法)判断点是否在多边形内部,只保留符合要求的坐标。

2. 基于多边形的插值方法

没有通用的直接插值方法,但可以通过以下实用思路实现:

  • 先在多边形的边界框内生成规则网格点;
  • 用射线法或环绕数算法筛选出位于多边形内部的点;
  • 若需要更贴合不规则多边形的分布,可以在生成网格后对超出边界的点进行微调,或者采用重心Voronoi划分,但规则网格+点-in-多边形筛选已能满足大部分场景需求。

扩展后的完整函数示例

function generateSeatPositions(polygon, rows, cols, seatWidth, seatHeight, direction = "left-to-right", alignment = "center") {
    // 计算多边形的边界框
    const minX = Math.min(...polygon.map(p => p.x));
    const maxX = Math.max(...polygon.map(p => p.x));
    const minY = Math.min(...polygon.map(p => p.y));
    const maxY = Math.max(...polygon.map(p => p.y));
    
    // 根据对齐方式计算网格起始坐标
    const gridTotalWidth = cols * seatWidth;
    const gridTotalHeight = rows * seatHeight;
    let startX = minX;
    let startY = minY;

    switch (alignment) {
        case "center":
            startX += (maxX - minX - gridTotalWidth) / 2;
            startY += (maxY - minY - gridTotalHeight) / 2;
            break;
        case "right":
            startX = maxX - gridTotalWidth;
            break;
        case "bottom":
            startY = maxY - gridTotalHeight;
            break;
        // 左/上对齐使用默认起始值
    }

    const positions = [];
    if (direction === "left-to-right") {
        // 按行优先生成座位
        for (let row = 0; row < rows; row++) {
            const rowSeats = [];
            for (let col = 0; col < cols; col++) {
                const x = startX + col * seatWidth;
                const y = startY + row * seatHeight;
                // 检查点是否在多边形内部
                if (isPointInPolygon({ x, y }, polygon)) {
                    rowSeats.push({ x, y, width: seatWidth, height: seatHeight });
                }
            }
            if (rowSeats.length > 0) positions.push(rowSeats);
        }
    } else {
        // 按列优先生成座位
        for (let col = 0; col < cols; col++) {
            const colSeats = [];
            for (let row = 0; row < rows; row++) {
                const x = startX + col * seatWidth;
                const y = startY + row * seatHeight;
                if (isPointInPolygon({ x, y }, polygon)) {
                    colSeats.push({ x, y, width: seatWidth, height: seatHeight });
                }
            }
            if (colSeats.length > 0) positions.push(colSeats);
        }
    }

    return positions;
}

// 射线法实现点-in-多边形检测
function isPointInPolygon(point, polygon) {
    let inside = false;
    for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
        const [xi, yi] = [polygon[i].x, polygon[i].y];
        const [xj, yj] = [polygon[j].x, polygon[j].y];
        // 判断点是否与多边形边相交
        const intersect = ((yi > point.y) !== (yj > point.y)) &&
            (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi);
        if (intersect) inside = !inside;
    }
    return inside;
}

函数说明

  • 传入参数:多边形顶点数组(每个顶点含x、y)、行列数、座位宽高、排列方向(可选,默认左到右)、对齐方式(可选,默认居中);
  • 核心逻辑:先计算边界框并调整起始位置,再按指定方向生成网格点,最后筛选出多边形内的座位坐标;
  • 返回值:嵌套数组,外层对应行/列,内层为每个座位的坐标与尺寸信息。

内容的提问来源于stack exchange,提问作者B L Praveen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 11:54:51