数学 - 获取线围绕图表起点闭合的最小多边形
Posted
技术标签:
【中文标题】数学 - 获取线围绕图表起点闭合的最小多边形【英文标题】:Math - get the smallest polygon that the lines close around chart starting point 【发布时间】:2021-01-17 20:58:43 【问题描述】:我正在尝试获取线条在图表起点周围创建的最小多边形的点,即最里面的多边形。线条因某些可以更改的参数而异。
此类图表的示例:
从图表中可以看出,这些线多次相交,因此创建了多个多边形。但是,我有兴趣只获取图表起点(中心)内的最小多边形。
我正在考虑从 y 轴到从顶部和底部向左和向右绘制多个与 x 轴的平行线(+y 和 -y 轴上的最小截距)并查看哪条线首先被“击中”为了得到将包围这个多边形的线,然后得到它们的交点以获得可用于绘制多边形的顶点。但是,由于要精确检查这些线需要很多点,我想知道是否有更优雅的解决方案?
【问题讨论】:
拜托,你能简化你的问题吗,别人很难理解你要解决的问题。不要让人们猜测问题可能是什么。 啊,抱歉。会尽量让它更清楚。 嗨!你熟悉线性规划吗?这个问题看起来非常类似于线性规划中遇到的概念。 【参考方案1】:在 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 a.x - b.x;
);
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 = i.x * i.x + i.y * i.y;
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) => p.x === startingPoint.x && p.y === 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) => p.x === startingPoint.x && p.y === 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) => v.x));
if (uniqueValues.size < tempResult.length)
if (i === 0) result1 = tempResult;
else result2 = tempResult;
break;
let newCrossedLines = intersections.filter((x) =>
x.intersects.some(
(p) =>
p.x === tempResult[tempResult.length - 1].x &&
p.y === tempResult[tempResult.length - 1].y
)
);
let newLine = newCrossedLines.filter((l) =>
l.intersects.every(
(p) =>
p.x !== tempResult[tempResult.length - 2].x &&
p.y !== 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) =>
p.x === tempResult[tempResult.length - 1].x &&
p.y === 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) 的多边形,因为包围它的最小多边形应该包含该顶点。之后,我开始沿着构成最近交叉点的两条线移动。重复这两条起始线的过程,我沿着这条线顺时针移动到下一个交叉点,然后沿着下一条线移动,继续这个过程,直到我在结果数组中得到一个重复的顶点,也就是说,直到多边形已经关闭。最后,我比较两个多边形并返回较小的那个。
很可能有一种更有效的方法可以做到这一点,但目前可行!
最终结果:
【讨论】:
【参考方案2】:这是一个可能的算法:
未找到循环时: 从某点开始(x,y)
某行L
在L
上顺时针方向查找下一个交点(x',y')
如果原点(0, 0)
在此行的右侧:
x = x'
y = y'
L =
那条新线
如果找到一个环:这个环就是多边形。
【讨论】:
对不起,迟到的评论。我试图实现这个解决方案以及一个不同的解决方案,我最终得到了一些可行的东西。感谢您的帮助!以上是关于数学 - 获取线围绕图表起点闭合的最小多边形的主要内容,如果未能解决你的问题,请参考以下文章
用于查找从点到多边形的最小距离的 Javascript 代码(由 html 区域定义)