繁体   English   中英

数学 - 获得线条围绕图表起点闭合的最小多边形

[英]Math - get the smallest polygon that the lines close around chart starting point

我试图获取线条围绕图表起点创建的最小多边形的点,即最里面的多边形。 行因某些可以更改的参数而异。

此类图表的示例:

在此处输入图片说明

从图表中可以看出,这些线多次相交,因此创建了多个多边形。 但是,我只想获取图表起点(中心)内的最小多边形。

我正在考虑从 y 轴到左和右从顶部和底部(+y 和 -y 轴上的最小截距)绘制多个与 x 轴的平行线,然后查看哪条线首先被“击中”以获得将包围此多边形的线,然后获取可用于绘制多边形的顶点的交点。 但是,由于要精确检查这些线条需要很多点,我想知道是否有更优雅的解决方案?

在 Stef 和他的算法的帮助下,我设法解决了这个问题。 这是我使用的代码:

 const getIntersections = async ( lines: IPoint[][] ): Promise<IIntersectLine[]> => { let lineIntersects: IIntersectLine[] = []; lines.forEach((line) => { let lineIntersect: IIntersectLine = { line: line, intersects: [], }; let x1 = line[1].x; let y1 = line[1].y; let x2 = line[2].x; let y2 = line[2].y; for (let i = 0; i < lines.length; i++) { let x3 = lines[i][1].x; let y3 = lines[i][1].y; let x4 = lines[i][2].x; let y4 = lines[i][2].y; if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) continue; let denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); if (denominator === 0) continue; let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator; let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator; if (ua < 0 || ua > 1 || ub < 0 || ub > 1) continue; let x = x1 + ua * (x2 - x1); let y = y1 + ua * (y2 - y1); lineIntersect.intersects.push({ x: +x.toFixed(4), y: +y.toFixed(4), }); } lineIntersect.intersects.sort((a, b) => { return ax - bx; }); lineIntersects.push(lineIntersect); }); return lineIntersects; }; const getStartingPoint = async (intersects: IPoint[]) => { let result: IPoint = intersects[0]; let distance = result.x * result.x + result.y * result.y; intersects.forEach((i) => { let newDistance = ix * ix + iy * iy; if (newDistance < distance) { distance = newDistance; result = i; } }); return result; }; const calcPolygonArea = async (polygon: IPoint[]) => { let total = 0; for (let i = 0, l = polygon.length; i < l; i++) { let addX = polygon[i].x; let addY = polygon[i == polygon.length - 1 ? 0 : i + 1].y; let subX = polygon[i == polygon.length - 1 ? 0 : i + 1].x; let subY = polygon[i].y; total += addX * addY * 0.5; total -= subX * subY * 0.5; } return Math.abs(total); }; export const getPolygonVertices = async (lines: IPoint[][]) => { let result: IPoint[] = []; let intersections = await getIntersections(lines); let intersectionVertices = intersections.map((x) => x.intersects).flat(); let startingPoint = await getStartingPoint(intersectionVertices); let crossedLines = intersections.filter((x) => x.intersects.some( (p) => px === startingPoint.x && py === startingPoint.y ) ); let newPoints: IPoint[] = []; const x0 = 0; const y0 = 0; crossedLines.forEach((line) => { let x1 = startingPoint.x; let y1 = startingPoint.y; let pointIndex = line.intersects.findIndex( (p) => px === startingPoint.x && py === startingPoint.y ); let d; if (line.intersects[pointIndex - 1]) { let x2 = line.intersects[pointIndex - 1].x; let y2 = line.intersects[pointIndex - 1].y; d = (x0 - x1) * (y2 - y1) - (y0 - y1) * (x2 - x1); if (d > 0) newPoints.push({ x: x2, y: y2 }); } if (line.intersects[pointIndex + 1]) { let x2 = line.intersects[pointIndex + 1].x; let y2 = line.intersects[pointIndex + 1].y; d = (x0 - x1) * (y2 - y1) - (y0 - y1) * (x2 - x1); if (d > 0) newPoints.push({ x: x2, y: y2 }); } }); let result1: IPoint[] = []; let result2: IPoint[] = []; for (let i = 0; i < newPoints.length; i++) { let tempResult: IPoint[] = []; tempResult.push(startingPoint, newPoints[i]); for (let j = 0; j < 50; j++) { const uniqueValues = new Set(tempResult.map((v) => vx)); if (uniqueValues.size < tempResult.length) { if (i === 0) result1 = tempResult; else result2 = tempResult; break; } let newCrossedLines = intersections.filter((x) => x.intersects.some( (p) => px === tempResult[tempResult.length - 1].x && py === tempResult[tempResult.length - 1].y ) ); let newLine = newCrossedLines.filter((l) => l.intersects.every( (p) => px !== tempResult[tempResult.length - 2].x && py !== tempResult[tempResult.length - 2].y ) )[0]; let x1 = tempResult[tempResult.length - 1].x; let y1 = tempResult[tempResult.length - 1].y; let pointIndex = newLine.intersects.findIndex( (p) => px === tempResult[tempResult.length - 1].x && py === tempResult[tempResult.length - 1].y ); let d; if (newLine.intersects[pointIndex - 1]) { let x2 = newLine.intersects[pointIndex - 1].x; let y2 = newLine.intersects[pointIndex - 1].y; d = (x0 - x1) * (y2 - y1) - (y0 - y1) * (x2 - x1); if (d > 0) tempResult.push({ x: x2, y: y2 }); } if (newLine.intersects[pointIndex + 1]) { let x2 = newLine.intersects[pointIndex + 1].x; let y2 = newLine.intersects[pointIndex + 1].y; d = (x0 - x1) * (y2 - y1) - (y0 - y1) * (x2 - x1); if (d > 0) tempResult.push({ x: x2, y: y2 }); } } } const area1 = await calcPolygonArea(result1); const area2 = await calcPolygonArea(result2); area1 < area2 ? (result = result1) : (result = result2); return result; };

本质上,首先我得到图表上所有线的所有交点。 然后我找到最接近图表起点 (0,0) 的一个,因为包围它的最小多边形应该包含该顶点。 之后,我开始沿着构成最近交叉点的两条线移动。 对这两条起始线重复这个过程,我沿着这条线顺时针移动到下一个交点,然后沿着下一条线移动,继续这个过程直到我在结果数组中得到一个重复的顶点,也就是说,直到多边形关闭了。 最后,我比较两个多边形并返回较小的一个。

很可能有一种更有效的方法来做到这一点,但这现在有效!

最终结果:

在此处输入图片说明

这是一个可能的算法:

  • 虽然没有找到循环:
    • 从某行L上的某个点(x,y)开始
    • 沿顺时针方向在L上找到下一个交点(x',y')
    • 如果原点(0, 0)在这条线的右边:
      • x = x'
      • y = y'
      • L =那个新行
  • 如果找到一个循环:这个循环就是多边形。

算法图解

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM