计算几何中的编程练习

本文参考了Dsp Tian的博客, 主要通过MATLAB程序解决了以下几个小问题:

  • 构造简单多边形
  • 射线法判断点与多边形关系
  • 最小包围矩形/Ritter’s最小包围圆
  • 多线段交点

构造简单多边形

输入 n n n边形顶点组成的矩阵( n × 2 n\times 2 n×2).

输出:排序后的顶点坐标并绘制相应的多边形.

步骤1 计算出所有顶点的中心位置;

步骤2 求每个顶点与中心点的极角;

步骤3 按极角对顶点坐标进行排序;

步骤4 将排序后的顶点顺次连接.

function p=creatPolygon(V)
% 绘制简单多边形
% 输入数据V是顶点组成的矩阵
n=size(V,1); % 顶点个数
center=mean(V);
angle=atan2(V(:,1)-center(1),V(:,2)-center(2)); % 每个点到坐标中心极角

V=[V,angle];
V=sortrows(V,3); % 按极角排序

p=V(:,1:2);
% p(n+1,:)=p(1,:); % 将第一个点添加到最后一个点后面, 方便连线
% plot(p(:,1),p(:,2))
end
n=4;
V=rand(n,2)
p=creatPolygon(V);
V =
    0.8147    0.6324
    0.9058    0.0975
    0.1270    0.2785
    0.9134    0.5469
p =

0.3922    0.0318   -2.7431
0.1712    0.0462   -2.1721
0.7431    0.7060    0.5204
0.6555    0.2769    1.5001
0.3922    0.0318   -2.7431

简单四边形

射线法判断点与多边形的关系

**基本原理:**从待判断点引出一条射线, 射线与多边形相交, 若交点为偶数, 则点不在多边形内; 若交点为奇数, 则点在多边形内.

输入:多边形各顶点组成的 n × 2 n\times 2 n×2的矩阵及 n u m num num个数据点 ( 2 × n u m ) (2\times num) (2×num).

输出:多边形内部包含的数据点.

步骤1 遍历所有的数据点, 为每个点生成一个计数器, 从当前点向做竖直的射线;

步骤2 计算多边形各边对应的直线方程;

步骤3 判断射线与多边形各边的相交情况, 每有一边相交, 计数器增加一次;

步骤4 根据计数情况, 将交点个数为偶数的点判定为外部点, 用红点表示; 将交点个数为奇数的点判定为内部点, 用绿色星号表示, 并记录下来.

function [poly,Internal]=Raymethod(points,V)

polyn=length(V); % 顶点个数n
poly=creatPolygon(V);

%将第一个点再添加到末尾, 方便判断
polyn=polyn+1;
poly(polyn,:)=poly(1,:);

% 采样点/矢量个数
num=size(points,2);

figure;
plot(poly(:,1),poly(:,2),'b');
hold on
Internal=[];
for i=1:num
    flag=0;
    for j=2:polyn
        x1=poly(j-1,1);  % 多边形前后两个点
        y1=poly(j-1,2);
        x2=poly(j,1);
        y2=poly(j,2);
        
        % 多边形一条边直线
        k=(y1-y2)/(x1-x2);
        b=y1-k*x1;
        x=points(1,i); 
        y=k*x+b; % 从当前点做竖直向上的射线看与多边形交点
        
        if min([x1 x2])<= x && x<=max([x1 x2]) && ...
                min([y1 y2])<= y && y<=max([y1 y2]) && y>=points(2,i)
            flag=flag+1;
        end
    end
    
    if mod(flag,2)==0 % 偶数则在外部
        plot(points(1,i),points(2,i),'r.');
    else % 奇数在内部
        plot(points(1,i),points(2,i),'g*');
        Internal=[Internal,points(:,i)];
    end
end

end 
V=rand(4,2);
points=rand(500,2)';
[poly,Internal]=Raymethod(points,V)

射线法判断点与多边形位置关系

最小包围矩形

输入:离散数据点.

输出:最小包围矩形的四个顶点坐标.

步骤1 求数据点的凸包;

(:可调用MATLAB的凸包计算函数 k = c o v h u l l ( X , Y ) k=covhull(X,Y) k=covhull(X,Y)返回凸包上数据点的索引)

步骤2 将凸包中两相邻的点连线作为矩形的一条边;

步骤3 寻找凸包上距离已知边最远的点, 过该点作平行线, 得到矩形的另一条平行边;

步骤4 将凸包上的点向已求得的边投影, 求得相距最远的两个投影点, 过该两点作垂直方向的直线作为矩形另外两条边;

步骤5 遍历凸包所有相邻两点, 重复步骤2-4, 将面积最小的矩形作为最终结果, 输出其四个点的坐标. 通常情况下, 最小包围矩形会过5个数据点.

function pbar=minBar(points)
% 最小包围矩形
% points输入数据点
% 输出最小包围矩形的四个顶点
% 调用凸包计算函数k=convhull(X,Y), 返回凸包上数据点的索引

n=length(points);

k=convhull(points(:,1),points(:,2));
K=length(k);

hull=points(k,:); %凸包上数据点索引

area=inf;

for i=2:K
    p1=hull(i-1,:); % 凸包上两个点
    p2=hull(i,:);
    
    k1=(p1(2)-p2(2))/(p1(1)-p2(1)); % 连接两个点的直线, 作为矩形的一条边
    b1=p1(2)-k1*p1(1);
    d=abs(hull(:,1)*k1-hull(:,2)+b1)/sqrt(k1^2+1); % 所有凸包上的点到k1,b1直线的距离

    [h,ind]=max(d); % 得到距离最大的点的索引ind,距离即为宽

    b2=hull(ind,2)-k1*hull(ind,1); % 与k1,b1直线平行的另一条边k1,b2

    k2=-1/k1; % 已求直线的垂线斜率


    b=hull(:,2)-k2*hull(:,1); % 过凸包所有点构成的k2,b直线系
    x1=-(b1-b)/(k1-k2); % 凸包上所有点在已求的第一条边上的投影
    y1=-(-b*k1+b1*k2)/(k1-k2);

    x2=-(b2-b)/(k1-k2);
    y2=-(-b*k1+b2*k2)/(k1-k2);

    [~,indmax1]=max(x1);  % 投影在第一条边上x方向最大与最小值
    [~,indmin1]=min(x1);


    [~,indmax2]=max(x2);  % 投影在第二条边上x方向最大与最小值
    [~,indmin2]=min(x2);

    w=sqrt((x1(indmax1)-x1(indmin1))^2+(y1(indmax1)-y1(indmin1))^2); % 矩形的长

    if area>=h*w
        area=h*w;
        pbar=[x1(indmax1) y1(indmax1);
              x2(indmax2) y2(indmax2);
              x2(indmin2) y2(indmin2);
              x1(indmin1) y1(indmin1)]; % 矩形四个角点
    end
end
    pbar(5,:)=pbar(1,:);

    plot(points(:,1),points(:,2),'.');
    hold on
    plot(pbar(:,1),pbar(:,2),'r');
    axis equal;
    
end

最小包围矩形

Ritter’s最小包围圆

输入:离散数据点.

输出:最小包围圆的圆心 ( x 0 , y 0 ) (x_0,y_0) (x0,y0)及半径 r r r.

步骤1 从离散数据点中随机选出两个点作为直径作初始圆;

步骤2 判断下一个点 p p p是否在圆中, 如果在则继续判断, 否则进入步骤3;

步骤3 将点 p p p作为新圆的一个边界点, 另一个边界点为距离点 p p p最远的旧圆上的点, 使用这两个点作为直径构造新圆;

步骤4 返回步骤2, 直至遍历完所有的数据点.

function [x0,y0,r]=Ritter(p)
n=size(p,1);
p1=p(1,:);
p2=p(2,:);
r=sqrt((p1(1)-p2(1))^2+(p1(2)-p2(2))^2)/2;
cenp=(p1+p2)/2;

for i=3:n

    newp=p(i,:);
    d=sqrt((cenp(1)-newp(1))^2+(cenp(2)-newp(2))^2);
    if d>r
        r=(r+d)/2;
        cenp=cenp+(d-r)/d*(newp-cenp);
    end
end
figure;
plot(p(:,1),p(:,2),'r.');
hold on
x0=cenp(1);
y0=cenp(2);
theta=0:0.01:2*pi;
x=x0+r*cos(theta);
y=y0+r*sin(theta);
plot(x,y,'b-',x0,y0,'g*');
axis equal

最小包围圆

多线段交点

基本思路:根据线段的端点求两条直线的交点, 再判断直线的交点是否在两条线段上.

function Mlsip(p)

% p是n*4的矩阵每一行表示线段左右端点的坐标(x1,y1,x2,y2)

n=size(p,1);
figure;
for i=1:n
    pbar=p(i,:);
    pbar=reshape(pbar,[2 2]);
    line(pbar(1,:),pbar(2,:));
end
hold on
for i=1:n-1
    p1=p(i,:);
    k1=(p1(2)-p1(4))/(p1(1)-p1(3));
    b1=p1(2)-k1*p1(1);
    for j=i+1:n
        p2=p(j,:);
	k2=(p2(2)-p2(4))/(p2(1)-p2(3));
	b2=p2(2)-k2*p2(1);
	
	% 求两直线交点
	x=-(b1-b2)/(k1-k2); 
	y=-(-b2*k1+b1*k2)/(k1-k2);
	
	% 判断交点是否在两线段上
	if min(p1(1),p1(3))<=x && x<= max(p1(1),p1(3)) && ...
	   min(p1(2),p1(4))<=y && y<= max(p1(2),p1(4)) && ...
	   min(p2(1),p2(3))<=x && y<= max(p2(1),p2(3)) && ...
	   min(p2(2),p2(4))<=y && y<=max(p2(2),p2(4))
		plot(x,y,'r*')
	end

    end
end

多线段交点

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值