[您有新的未分配科技点]计算几何入门(1):点,向量以及向量的简单应用
2017-08-07 06:38
363 查看
在打了一阵数据结构之后,老师表示“今天晚上让学长给你们讲一下计算几何”……然后就死了.jpg
昨天晚上一直在推数学的式子以及回顾讲课的笔记……计算几何特点就是多而杂,即使是入门部分也是如此……
首先,我们从二维的几何问题开始处理。
我们知道,高中解析几何计算几何的基础是向量(Vector)和点(Point),所以我们先来表示这两个概念:
在计算几何中,点和向量一般用结构体来存储,像这样:
由于向量和点表示方式是相同的,但是我们并不能给点加减。
所以我们给Point起一个别名,这样用的时候就不会引起歧义了。
接下来,我们考虑实现一些基本操作:向量求长度,加减,旋转,点积和叉积,两向量所成角。
向量加减可以通过重载来实现,点积和叉积记住公式直接算就可以,旋转可以通过计算三角函数展开现推……
所以其实都是板子。代码见下:
在有了这些基本的操作,尤其是叉积之后,我们就可以解决更多的问题了。
所以我们先展开了解一下叉积。
叉积,表示的是一种“有向面积”,其计算定义为(x1,y1)×(x2,y2)=x1*y2-x2*y1 ①
之所以称它为有向面积,是因为事实上叉积的结果是一个向量c,其长度有|c|=|a|*|b|*sinΘ
从①式我们容易发现,a×b和b×a似乎是不同的。
事实上,的确如此,对于向量叉积有a×b=-(b×a)这也正说明了叉积“有向”的特点:
因为把a旋转到b经过的角度和把b旋转到a经过的角度恰好是相反的,因此其结果相反。
有了这个“有向面积“,我们就可以解决不少问题了。
首先,我们先来考虑一系列求多边形面积的题目:
例1:给三点坐标,求三角形面积。
设三个点与原点连线的向量为a,b,c,那么有(b-a)×(c-a)=2*S三角形。计算即可。
大概长这样:
例2:给顶点坐标,求多边形面积。
最朴素的做法是把n边形剖分成n-2个三角形,但我们有更高效的做法。
在这之前,我们先要了解极角的概念。
极角表示一个向量与x轴正半轴所成的角,范围为[-π,π]。在C++中,求向量(x,y)的极角可以使用函数atan2
写法为atan2(y,x),需要调用cmath库
了解了极角之后,我们就可以讨论求多边形面积的问题了:
按照极角从小到大(逆时针顺序)给向量v1=(x1,y1)……vn=(xn,yn)排序,然后计算(v1×v2+v2×v3……vn×v1)/2即可
由于叉积有正有负,所以在计算一圈之后我们就正好得到了多边形的面积,这实际上还是一个三角剖分。
有兴趣的同学可以自己推一下式子。不过,在代码实现时我们一般以最左下角的点作为向量的起点。
下面给出一份简单的代码(设点已经按逆时针排序,编号0~n-1):
接下来,我们考虑一种求相交性的问题。
例3:给出四点A,B,C,D坐标,求AB,CD两直线交点O坐标。
首先,我们来考虑一条线段的“分点".(下面开始盗图)
上面这个式子感性理解一下就可以明白:如果λ1无限大,C就无限接近B,而上式也正好无限接近λ1B/λ1=B。
然后,我们就可以利用分点去求直线的交点了。
首先,我们要排除平行和重合的情况
我们思考一下,假设|AO|/|OB|=λ,那么就会有
|AO|/|OB|=S△ ADC/S△BDC=(AD×AC)/2 /(BC×BD)/2
这样,我们就可以用叉积算出分点的λ,然后用分点计算O点坐标即可。
特别注意,对于上图,上式中A与B叉积的顺序相反。
这一点不难看出其正确性,有兴趣的读者可以思考下为什么(提示:分类讨论,考虑分点的计算公式)
代码见下:
扩展:如果求两条线段的交点呢?
这时我们就要判断交点是否在AB,CD上了。我们可以通过判断OA、OB向量的点积,以及OC、OD向量的点积来确定:如果O在两线段上,那么两个点积都是负的。
例4:给出n边形的n个顶点(x1,y1)……(xn,yn),求一个给定点是否在多边形内。
一般来说,判断这个的方法有多种,比如引一条射线看与边的交点个数,算夹角之和是否等于360°,算叉积是否都为负,等等。
但是,我们还有另外一种logn求某个点是否在凸多边形(如果是凹多边形,这个方法就不再适用,较为推荐的是引线法)内部的方法,并且比较稳定
首先,我们把n边形分为n-2个三角形,那么如果某个点在多边形内部。
接着,我们通过叉积计算这个点到底在哪个区间。判断的方法很好想,不再赘述。
最后,我们得到了这个点可能处在的区间,然后我们判断这个点是否在多边形那条边的“左侧”(使用叉积判断)即可
代码大概长这样(所有点已经按照逆时针排好序)
关于向量部分的基础内容暂时就这么多,以后我还会带来其他计算几何的知识。希望看完博文的你有所收获:)
昨天晚上一直在推数学的式子以及回顾讲课的笔记……计算几何特点就是多而杂,即使是入门部分也是如此……
首先,我们从二维的几何问题开始处理。
我们知道,高中解析几何计算几何的基础是向量(Vector)和点(Point),所以我们先来表示这两个概念:
在计算几何中,点和向量一般用结构体来存储,像这样:
struct Point { double x,y,rad; Point(double xx=0,double yy=0){x=xx,y=yy;} }; typedef Point Vector;
由于向量和点表示方式是相同的,但是我们并不能给点加减。
所以我们给Point起一个别名,这样用的时候就不会引起歧义了。
接下来,我们考虑实现一些基本操作:向量求长度,加减,旋转,点积和叉积,两向量所成角。
向量加减可以通过重载来实现,点积和叉积记住公式直接算就可以,旋转可以通过计算三角函数展开现推……
所以其实都是板子。代码见下:
Vector operator + (Vector a,Vector b){return Vector(a.x+b.x,a.y+b.y);}//加 Vector operator - (Vector a,Vector b){return Vector(a.x-b.x,a.y-b.y);}//减 inline double Dot(Vector a,Vector b){return a.x*b.x+a.y*b.y;}//点积 inline double length(Vector a){return sqrt(Dot(a,a));}//长度 inline Vector Rotate(Vector a,double rad) {return Vector(a.x*cos(rad)-a.y*sin(rad),a.x*sin(rad)+a.y*cos(rad));}//旋转 inline double Cross(Vector a,Vector b){return a.x*b.y-a.y*b.x;}//叉积 inline double Angle(Vector a,Vector b){return acos(Dot(a,b)/length(a)/length(b));}//角度
在有了这些基本的操作,尤其是叉积之后,我们就可以解决更多的问题了。
所以我们先展开了解一下叉积。
叉积,表示的是一种“有向面积”,其计算定义为(x1,y1)×(x2,y2)=x1*y2-x2*y1 ①
之所以称它为有向面积,是因为事实上叉积的结果是一个向量c,其长度有|c|=|a|*|b|*sinΘ
从①式我们容易发现,a×b和b×a似乎是不同的。
事实上,的确如此,对于向量叉积有a×b=-(b×a)这也正说明了叉积“有向”的特点:
因为把a旋转到b经过的角度和把b旋转到a经过的角度恰好是相反的,因此其结果相反。
有了这个“有向面积“,我们就可以解决不少问题了。
首先,我们先来考虑一系列求多边形面积的题目:
例1:给三点坐标,求三角形面积。
设三个点与原点连线的向量为a,b,c,那么有(b-a)×(c-a)=2*S三角形。计算即可。
大概长这样:
inline double Area2(Point a,Point b,Point c){return Cross(b-a,c-a);}
例2:给顶点坐标,求多边形面积。
最朴素的做法是把n边形剖分成n-2个三角形,但我们有更高效的做法。
在这之前,我们先要了解极角的概念。
极角表示一个向量与x轴正半轴所成的角,范围为[-π,π]。在C++中,求向量(x,y)的极角可以使用函数atan2
写法为atan2(y,x),需要调用cmath库
了解了极角之后,我们就可以讨论求多边形面积的问题了:
按照极角从小到大(逆时针顺序)给向量v1=(x1,y1)……vn=(xn,yn)排序,然后计算(v1×v2+v2×v3……vn×v1)/2即可
由于叉积有正有负,所以在计算一圈之后我们就正好得到了多边形的面积,这实际上还是一个三角剖分。
有兴趣的同学可以自己推一下式子。不过,在代码实现时我们一般以最左下角的点作为向量的起点。
下面给出一份简单的代码(设点已经按逆时针排序,编号0~n-1):
double ans=0; for(int i=1;i<n-1;i++) ans+=Cross(p[i]-p[0],p[i+1]-p[0]); printf("%.2lf\n",ans/2);
接下来,我们考虑一种求相交性的问题。
例3:给出四点A,B,C,D坐标,求AB,CD两直线交点O坐标。
首先,我们来考虑一条线段的“分点".(下面开始盗图)
上面这个式子感性理解一下就可以明白:如果λ1无限大,C就无限接近B,而上式也正好无限接近λ1B/λ1=B。
然后,我们就可以利用分点去求直线的交点了。
首先,我们要排除平行和重合的情况
我们思考一下,假设|AO|/|OB|=λ,那么就会有
|AO|/|OB|=S△ ADC/S△BDC=(AD×AC)/2 /(BC×BD)/2
这样,我们就可以用叉积算出分点的λ,然后用分点计算O点坐标即可。
特别注意,对于上图,上式中A与B叉积的顺序相反。
这一点不难看出其正确性,有兴趣的读者可以思考下为什么(提示:分类讨论,考虑分点的计算公式)
代码见下:
1 Vector operator * (Vector a,double p){return Vector(a.x*p,a.y*p);} 2 Vector operator / (Vector a,double p){return Vector(a.x/p,a.y/p);} 3 inline Vector Divide_Point(Point a,Point b,double p1,double p2){return (a*p1+b*p2)/(p1+p2);}//A,B分点 4 inline Vector Cross_Point(Point a,Point b,Point c,Point d) 5 {return Divide_Point(a,b,Cross(d-a,c-a),Cross(c-b,d-b));}//直线交点
扩展:如果求两条线段的交点呢?
这时我们就要判断交点是否在AB,CD上了。我们可以通过判断OA、OB向量的点积,以及OC、OD向量的点积来确定:如果O在两线段上,那么两个点积都是负的。
例4:给出n边形的n个顶点(x1,y1)……(xn,yn),求一个给定点是否在多边形内。
一般来说,判断这个的方法有多种,比如引一条射线看与边的交点个数,算夹角之和是否等于360°,算叉积是否都为负,等等。
但是,我们还有另外一种logn求某个点是否在凸多边形(如果是凹多边形,这个方法就不再适用,较为推荐的是引线法)内部的方法,并且比较稳定
首先,我们把n边形分为n-2个三角形,那么如果某个点在多边形内部。
接着,我们通过叉积计算这个点到底在哪个区间。判断的方法很好想,不再赘述。
最后,我们得到了这个点可能处在的区间,然后我们判断这个点是否在多边形那条边的“左侧”(使用叉积判断)即可
代码大概长这样(所有点已经按照逆时针排好序)
inline bool judge(Point x) { p[n+1]=p[1];//编号1~n,处理边界 //判断多边形为线段时是否在线段上,视情况添加这段代码 /*if(n==2) { if(Dot(p[1]-x,p[2]-x)==-length(p[1]-x)*length(p[2]-x))return 1; else return 0; }*/ int l=2,r=n,ans=-1; while(l<=r) { int mi=(l+r)>>1; if(Cross(p[mi]-p[1],x-p[1])>eps)ans=mi,l=mi+1; else r=mi-1; } if(ans==-1)return 0; if(Cross(p[ans+1]-p[ans],x-p[ans])>eps)return 1; }
关于向量部分的基础内容暂时就这么多,以后我还会带来其他计算几何的知识。希望看完博文的你有所收获:)
相关文章推荐
- 计算几何入门题之点,线,面,形基本关系以及点积叉积的理解
- UVa 11437:Triangle Fun(计算几何综合应用,求直线交点,向量运算,求三角形面积)
- pick定理的应用——简单的计算几何问题Triangle
- 计算几何简单入门题列表
- 计算几何(一):叉积的简单应用
- 理解openstack中与虚拟机相关一些简单网络知识-3.计算节点的网络拓扑以及网络分配
- HDU1798 简单高中的计算几何题= =。正余弦定理的综合应用么。。。
- UVA - 10250 - The Other Two Trees (简单计算几何)
- 关于iOS应用管理之九宫格的坐标计算以及与UIScrollView的结合
- POJ 计算几何入门题目推荐
- _00019 Storm的体系结构介绍以及Storm入门案例(官网上的简单Java案例)
- Javascript 向量简单应用
- JQuery入门―编写一个简单的JQuery应用案例
- 简单的三层框架以及使用dbutils进行数据库操作(入门)
- Websphere 入门基础 第九课 IBM WebSphere Studio实验以及应用开发实验的设置指导
- LightOJ Circle in Square (简单计算几何)
- OpenGLES的简单应用绘图,以及照相机画面变色变形渲染(一)
- 【计算几何】向量表示
- POJ 1696 Space Ant 计算几何 叉积的应用
- OpenGLES的简单应用绘图,以及照相机画面变色变形渲染(七)