简体   繁体   English

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

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

I'm trying to get the points of the smallest polygons that the lines create around the starting point of the chart, that is, the innermost polygon.我试图获取线条围绕图表起点创建的最小多边形的点,即最里面的多边形。 The lines vary depending on some parameters that can be changed.行因某些可以更改的参数而异。

Example of such a chart:此类图表的示例:

在此处输入图片说明

As is visible from the chart, the lines intersect many times and, thus, create multiple polygons.从图表中可以看出,这些线多次相交,因此创建了多个多边形。 However, I am interested in getting only the smallest polygon that is within the starting point (the center) of the chart.但是,我只想获取图表起点(中心)内的最小多边形。

I was thinking about drawing multiple parallels with x-axis from the y-axis to left and right from the top and the bottom (the smallest intercept on +y and -y axis) and seeing which line gets "hit" first as to get the lines which would enclose this polygon, then getting their intersections for vertices which can be used to drawing the polygon.我正在考虑从 y 轴到左和右从顶部和底部(+y 和 -y 轴上的最小截距)绘制多个与 x 轴的平行线,然后查看哪条线首先被“击中”以获得将包围此多边形的线,然后获取可用于绘制多边形的顶点的交点。 However, since it will take numerous points to check such lines with precision, I am wondering if there is perhaps a more elegant solution to the problem?但是,由于要精确检查这些线条需要很多点,我想知道是否有更优雅的解决方案?

I managed to solve this with the help of Stef and his algorithm.在 Stef 和他的算法的帮助下,我设法解决了这个问题。 Here is the code I used:这是我使用的代码:

 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; };

Essentially, first I get all the intersections of all the lines on the chart.本质上,首先我得到图表上所有线的所有交点。 Then I find the closest one to the chart's starting point (0,0) as the smallest polygon enclosing it should contain that vertex.然后我找到最接近图表起点 (0,0) 的一个,因为包围它的最小多边形应该包含该顶点。 After that, I begin moving along the two lines that make up the closest intersection.之后,我开始沿着构成最近交叉点的两条线移动。 Repeating the process for those two starting lines, I move clockwise along the line up to the next intersection, where I then move along the next line, continuing the process until I get a duplicate vertex in my result array, that is, until the polygon is closed.对这两条起始线重复这个过程,我沿着这条线顺时针移动到下一个交点,然后沿着下一条线移动,继续这个过程直到我在结果数组中得到一个重复的顶点,也就是说,直到多边形关闭了。 In the end, I compare the two polygons and return the smaller one.最后,我比较两个多边形并返回较小的一个。

There is most likely a more efficient way to do this, but this works for now!很可能有一种更有效的方法来做到这一点,但这现在有效!

End result:最终结果:

在此处输入图片说明

Here is a possible algorithm:这是一个可能的算法:

  • While a cycle was not found:虽然没有找到循环:
    • Start at some point (x,y) on some line L从某行L上的某个点(x,y)开始
    • Find next intersection point (x',y') on L in clockwise direction沿顺时针方向在L上找到下一个交点(x',y')
    • If the origin (0, 0) is on the right of this line:如果原点(0, 0)在这条线的右边:
      • x = x'
      • y = y'
      • L = that new line L =那个新行
  • If a cycle was found: this cycle is the polygon.如果找到一个循环:这个循环就是多边形。

算法图解

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

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