基于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
相关产品推荐
相关产品推荐

