我有以下多边形,它只是一组2D点,如下所示:-
poly0=[80 60
90 60
100 60
110 60
110 50
120 50
130 50
140 50
150 50
160 50
170 50
180 50
190 50
200 50
210 50
210 60
210 70
210 80
210 90
220 90
220 100
210 100
210 110
200 110
200 120
190 120
180 120
180 130
170 130
160 130
150 130
140 130
130 130
130 120
120 120
110 120
110 110
100 110
100 100
90 100
90 90
90 80
90 70
80 70
80 60];
现在我可以使用它了。
>> line(poly0(:,1), poly0(:,2),'Color','k','LineWidth',3,'LineStyle',':');
这清楚地表明了我的原始多边形点集是高度冗余的。基本上,位于同一直线上的多个点被枚举,不需要。我可以开始检查每对点,如果它们在同一条直线上,则可以将其删除。但这意味着要使用许多for循环。我无法提出一种智能的矢量化方法。
如何获得一组新的点,这些点的大小比以前的点短很多,但仍然代表完全相同的多边形?我应该只有多边形中的顶点数量一样多的点。那么换句话说,如何从上述数据集中快速找到顶点?
PS :此处的顶点角为90度,但是如果您提供解决方案,请不要尝试利用这一事实。我想要一个更一般的答案。
答案 0 :(得分:5)
两个现有的答案都有很大的缺点:
Durkee's method仅在后续点之间的距离完全相同时才有效。点必须具有完全可表示为浮点值的坐标,以便可以找到直线上后续点之间的距离相同。如果点不等距,则该方法不执行任何操作。另外,多边形的起点和终点不会一起检查,因此,如果在多边形的起点/终点形成一条直线,则将留下太多点。
ShadowMan's method更好,因为距离不必相同,并且正确处理了多边形起点/终点的线。但是,它也使用浮点相等比较,通常无法正常工作。只有使用整数坐标,此方法才能正常工作。此外,它使用vecnorm
(具有平方根)和除法(相对于此处显示的方法而言)都是比较昂贵的操作。
要查看三个点是否形成一条直线,可以使用简单的算术规则。假设我们有点p0
,p1
和p2
。从p0
到p1
的向量和从p0
到p2
的向量形成平行四边形的基础,平行四边形的面积可以由the cross product of the two vectors计算(在2D中,则叉积被理解为使用z=0
,结果向量具有x=0
和y=0
,只有z
值才有用;因此,我们假设2D叉积产生标量值)。可以计算如下:
v1 = p1 - p0;
v2 = p2 - p0;
x = v1(1)*v2(2) - v1(2)*v2(1);
如果两个向量平行,则叉积 x
将为零,这意味着三个点是共线的。但是,由于浮点算术不精确,因此对于等于0的相等性测试必须具有一定的容差。我在这里使用1e-6作为公差。使用比点之间的距离小几个数量级的值。
给出一组输入点p
,我们可以通过以下步骤找到角点:
p1 = p; % point 1
p0 = circshift(p1,1); % point 0
v1 = p1 - p0; % vector from point 0 to 1
v2 = circshift(p1,-1) - p0; % vector from point 0 to 2
x = v1(:,1).*v2(:,2) - v1(:,2).*v2(:,1); % cross product
idx = abs(x) > 1e-6; % comparison with tolerance
p = p(idx,:); % corner points
请注意,如果两个连续的点具有相同的坐标(即向量之一的长度为零),则叉积测试将失败。如果数据可能有重复的点,则需要进行额外的测试。
这是这三种方法的结果。我创建了一个具有非平凡坐标且顶点间距不相等的多边形。我还将开始/结束间隙放在一条直线边缘的中间。这些特性旨在说明其他两种方法的缺点。
这是我用来生成图形的代码:
% Make a polygon that will be difficult for the other two methods
p = [0,0 ; 0.5,0 ; 1,0 ; 1,1 ; 0.5,1 ; 0,1];
p = p + rand(size(p))/3;
p(end+1,:) = p(1,:);
q = [];
for ii = 1:size(p,1)-1
t = p(ii,:) + (p(ii+1,:) - p(ii,:)) .* [0;0.1;1/3;0.45;0.5897545;pi/4;exp(1)/3];
q = [q;t];
end
q = circshift(q,3,1);
figure
subplot(2,2,1)
plot(q(:,1),q(:,2),'bo-')
axis equal
title('input')
subplot(2,2,2)
res1 = method1(q);
plot(res1(:,1),res1(:,2),'ro-')
axis equal
title('Durkee''s method')
subplot(2,2,3)
res2 = method2(q);
plot(res2(:,1),res2(:,2),'ro-')
axis equal
title('ShadowMan''s method')
subplot(2,2,4)
res3 = method3(q);
plot(res3(:,1),res3(:,2),'go-')
axis equal
title('correct method')
% Durkee's method: https://stackoverflow.com/a/55603145/7328782
function P = method1(P)
a = logical([1 diff(P(:,1),2)' 1]);
b = logical([1 diff(P(:,2),2)' 1]);
idx = or(a,b);
P = P(idx,:);
end
% ShadowMan's method: https://stackoverflow.com/a/55603040/7328782
function corners = method2(poly0)
poly0Z = circshift(poly0,1);
poly0I = circshift(poly0,-1);
unitVectIn =(poly0 - poly0I)./vecnorm((poly0 - poly0I),2,2);
unitVectOut =(poly0Z - poly0)./vecnorm((poly0Z - poly0),2,2);
cornerIndices = sum(unitVectIn == unitVectOut,2)==0;
corners = poly0(cornerIndices,:);
end
% vecnorm is new to R2017b, I'm still running R2017a.
function p = vecnorm(p,n,d)
% n is always 2
p = sqrt(sum(p.^2,d));
end
function p = method3(p1)
p0 = circshift(p1,1);
v1 = p1 - p0;
v2 = circshift(p1,-1) - p0;
x = v1(:,1).*v2(:,2) - v1(:,2).*v2(:,1);
idx = abs(x) > 1e-6;
p = p1(idx,:);
end
答案 1 :(得分:1)
可以非常优雅地完成“向量”方式。我也尝试过for循环的方式,您可以用这种方式做同样的事情,但是您要求向量,所以这是我的方式。
我对数据所做的唯一更改是在启动此脚本之前删除了所有重复项。另外,提供的点应按顺时针或逆时针顺序排列。
poly0Z = circshift(poly0,1);
poly0I = circshift(poly0,-1);
unitVectIn =(poly0 - poly0I)./vecnorm((poly0 - poly0I),2,2);
unitVectOut =(poly0Z - poly0)./vecnorm((poly0Z - poly0),2,2) ;
cornerIndices = sum(unitVectIn == unitVectOut,2)==0
corners = poly0(cornerIndices,:)
line(poly0(:,1), poly0(:,2),'Color','k','LineWidth',2,'LineStyle',':');
hold on
scatter(corners(:,1), corners(:,2),'filled')
此方法的基础是转到每个点,计算进入的单位矢量,然后计算出的单位矢量。单位矢量输入与单位矢量输出不匹配的点是角。
答案 2 :(得分:1)
好的,我修改了它以处理非方形角。
考虑一个由点标识的三角形
P = [0 0; 1 0; 2 0; 1.5 1; 1 2; .5 1; 0 0];
如果我们按照注释中提到的问题定义2个导数向量,则这是一个7x2数组。
a = logical([1 diff(P(:,1),2)' 1]);
b = logical([1 diff(P(:,2),2)' 1]);
从那里,我们可以将两者结合起来以获得新的索引变量
idx = or(a,b);
最后,我们可以用它来绘制剧情
line(P(idx,1), P(idx,2),'Color','k','LineWidth',3,'LineStyle',':');
如果要绘制线图,我认为您需要将最后一个变量设置为false。
idx(end) = false;