[基础]计算几何

计算几何学的基本概念和算法 以上代码只是计算几何学中一个简单算法的C语言实现示例,计算几何学涵盖的内容丰富多样,不同的算法和应用场景需要根据具体情况进一步扩展和优化代码逻辑以及相关的数据结构等内容。两个成员变量,用于表示点的坐标,方便后续对几何图形(如线段,由端点组成)进行操作和计算。实现了计算两个向量叉积的功能,按照向量叉积的计算公式。用于判断两条线段是否相交,采用基于向量叉积的方法。定义了二维平面上的点结构体,包含。 阅读详情

一、引言

  计算机的出现使得很多原本十分繁琐的工作得以大幅度简化,但是也有一些在人们直观看来很容易的问题却需要拿出一套并不简单的通用解决方案,比如几何问题。作为计算机科学的一个分支,计算几何主要研究解决几何问题的算法。在现代工程和数学领域,计算几何在图形学、机器人技术、超大规模集成电路设计和统计等诸多领域有着十分重要的应用。在本文中,我们将对计算几何常用的基本算法做一个全面的介绍,希望对您了解并应用计算几何的知识解决问题起到帮助。

二、目录

(本文整理的计算几何基本概念和常用算法包括如下内容:)

1. 矢量的概念

2. 矢量加减法

3. 矢量叉积

4. 折线段的拐向判断

5. 判断点是否在线段上

6. 判断两线段是否相交

7. 判断线段和直线是否相交

8. 判断矩形是否包含点

9. 判断线段、折线、多边形是否在矩形中

10. 判断矩形是否在矩形中

11. 判断圆是否在矩形中

12. 判断点是否在多边形中

13. 判断线段是否在多边形内

14. 判断折线是否在多边形内

15. 判断多边形是否在多边形内

16. 判断矩形是否在多边形内

17. 判断圆是否在多边形内

18. 判断点是否在圆内

19. 判断线段、折线、矩形、多边形是否在圆内

20. 判断圆是否在圆内

21. 计算点到线段的最近点

22. 计算点到折线、矩形、多边形的最近点

23. 计算点到圆的最近距离及交点坐标

24. 计算两条共线的线段的交点

25. 计算线段或直线与线段的交点

26. 求线段或直线与折线、矩形、多边形的交点

27. 求线段或直线与圆的交点

28. 凸包的概念

29. 凸包的求法


三、算法介绍

1.  矢量的概念:

  如果一条线段的端点是有次序之分的,我们把这种线段成为有向线段(directed segment)。如果有向线段p1p2的起点p1在坐标原点,我们可以把它称为矢量(vector)p2。


2.  矢量加减法:

  设二维矢量P = ( x1, y1 ),Q = ( x2 , y2 ),则矢量加法定义为: P + Q = ( x1 + x2 , y1 + y2 ),同样的,矢量减法定义为: P - Q = ( x1 - x2 , y1 - y2 )。显然有性质 P + Q = Q + P,P - Q = - ( Q - P )。


3.  矢量叉积:

  计算矢量叉积是与直线和线段相关算法的核心部分。设矢量P = ( x1, y1 ),Q = ( x2, y2 ),则矢量叉积定义为由(0,0)、p1、p2和p1+p2所组成的平行四边形的带符号的面积,即:P × Q = x1*y2 - x2*y1,其结果是一个标量。显然有性质 P × Q = - ( Q × P ) 和 P × ( - Q ) = - ( P × Q )。一般在不加说明的情况下,本文下述算法中所有的点都看作矢量,两点的加减法就是矢量相加减,而点的乘法则看作矢量叉积。

  叉积的一个非常重要性质是可以通过它的符号判断两矢量相互之间的顺逆时针关系:

  若 P × Q > 0 , 则P在Q的顺时针方向。
  若 P × Q < 0 , 则P在Q的逆时针方向。
  若 P × Q = 0 , 则P与Q共线,但可能同向也可能反向。


4.  折线段的拐向判断:

  折线段的拐向判断方法可以直接由矢量叉积的性质推出。对于有公共端点的线段p0p1和p1p2,通过计算(p2 - p0) × (p1 - p0)的符号便可以确定折线段的拐向:

  若(p2 - p0) × (p1 - p0) > 0,则p0p1在p1点拐向右侧后得到p1p2。

  若(p2 - p0) × (p1 - p0) < 0,则p0p1在p1点拐向左侧后得到p1p2。

  若(p2 - p0) × (p1 - p0) = 0,则p0、p1、p2三点共线。
具体情况可参考下图:


5.  判断点是否在线段上:

  设点为Q,线段为P1P2 ,判断点Q在该线段上的依据是:( Q - P1 ) × ( P2 - P1 ) = 0 且 Q 在以 P1,P2为对角顶点的矩形内。前者保证Q点在直线P1P2上,后者是保证Q点不在线段P1P2的延长线或反向延长线上,对于这一步骤的判断可以用以下过程实现:

  ON-SEGMENT(pi,pj,pk)

  if min(xi,xj) <= xk <= max(xi,xj) and min(yi,yj) <= yk <= max(yi,yj)

  then return true;

  else return false;

  特别要注意的是,由于需要考虑水平线段和垂直线段两种特殊情况,min(xi,xj)<=xk<=max(xi,xj)和min(yi,yj)<=yk<=max(yi,yj)两个条件必须同时满足才能返回真值。


6.  判断两线段是否相交:

  我们分两步确定两条线段是否相交:

  (1)快速排斥试验

    设以线段 P1P2 为对角线的矩形为R, 设以线段 Q1Q2 为对角线的矩形为T,如果R和T不相交,显然两线段不会相交。

  (2)跨立试验

    如果两线段相交,则两线段必然相互跨立对方。若P1P2跨立Q1Q2 ,则矢量 ( P1 - Q1 ) 和( P2 - Q1 )位于矢量( Q2 - Q1 ) 的两侧,即( P1 - Q1 ) × ( Q2 - Q1 ) * ( P2 - Q1 ) × ( Q2 - Q1 ) < 0。上式可改写成( P1 - Q1 ) × ( Q2 - Q1 ) * ( Q2 - Q1 ) × ( P2 - Q1 ) > 0。当 ( P1 - Q1 ) × ( Q2 - Q1 ) = 0 时,说明 ( P1 - Q1 ) 和 ( Q2 - Q1 )共线,但是因为已经通过快速排斥试验,所以 P1 一定在线段 Q1Q2上;同理,( Q2 - Q1 ) ×(P2 - Q1 ) = 0 说明 P2 一定在线段 Q1Q2上。所以判断P1P2跨立Q1Q2的依据是:( P1 - Q1 ) × ( Q2 - Q1 ) * ( Q2 - Q1 ) × ( P2 - Q1 ) >= 0。同理判断Q1Q2跨立P1P2的依据是:( Q1 - P1 ) × ( P2 - P1 ) * ( P2 - P1 ) × ( Q2 - P1 ) >= 0。具体情况如下图所示:

 

  在相同的原理下,对此算法的具体的实现细节可能会与此有所不同,除了这种过程外,大家也可以参考《算法导论》上的实现。


7.  判断线段和直线是否相交:

  有了上面的基础,这个算法就很容易了。如果线段P1P2和直线Q1Q2相交,则P1P2跨立Q1Q2,即:( P1 - Q1 ) × ( Q2 - Q1 ) * ( Q2 - Q1 ) × ( P2 - Q1 ) >= 0。


8.  判断矩形是否包含点:

  只要判断该点的横坐标和纵坐标是否夹在矩形的左右边和上下边之间。


9.  判断线段、折线、多边形是否在矩形中:

  因为矩形是个凸集,所以只要判断所有端点是否都在矩形中就可以了。


10.  判断矩形是否在矩形中:

  只要比较左右边界和上下边界就可以了。
11.  判断圆是否在矩形中:

  很容易证明,圆在矩形中的充要条件是:圆心在矩形中且圆的半径小于等于圆心到矩形四边的距离的最小值。


12.  判断点是否在多边形中:

  判断点P是否在多边形中是计算几何中一个非常基本但是十分重要的算法。以点P为端点,向左方作射线L,由于多边形是有界的,所以射线L的左端一定在多边形外,考虑沿着L从无穷远处开始自左向右移动,遇到和多边形的第一个交点的时候,进入到了多边形的内部,遇到第二个交点的时候,离开了多边形,……所以很容易看出当L和多边形的交点数目C是奇数的时候,P在多边形内,是偶数的话P在多边形外。

  但是有些特殊情况要加以考虑。如图下图 (a)(b)(c)(d)所示。在图(a)中,L和多边形的顶点相交,这时候交点只能计算一个;在图(b)中,L和多边形顶点的交点不应被计算;在图 (c)和(d) 中,L和多边形的一条边重合,这条边应该被忽略不计。如果L和多边形的一条边重合,这条边应该被忽略不计。

 

  为了统一起见,我们在计算射线L和多边形的交点的时候,1。对于多边形的水平边不作考虑;2。对于多边形的顶点和L相交的情况,如果该顶点是其所属的边上纵坐标较大的顶点,则计数,否则忽略;3。对于P在多边形边上的情形,直接可判断P属于多边行。由此得出算法的伪代码如下:

count ← 0;
以P为端点,作从右向左的射线L;
for 多边形的每条边s
do if P在边s上
then return true;
if s不是水平的
then if s的一个端点在L上
if 该端点是s两端点中纵坐标较大的端点
then count ← count+1
else if s和L相交
then count ← count+1;
if count mod 2 = 1
then return true;
else return false;


  其中做射线L的方法是:设P'的纵坐标和P相同,横坐标为正无穷大(很大的一个正数),则P和P'就确定了射线L。

  判断点是否在多边形中的这个算法的时间复杂度为O(n)。

  另外还有一种算法是用带符号的三角形面积之和与多边形面积进行比较,这种算法由于使用浮点数运算所以会带来一定误差,不推荐大家使用。


13.  判断线段是否在多边形内:

  线段在多边形内的一个必要条件是线段的两个端点都在多边形内,但由于多边形可能为凹,所以这不能成为判断的充分条件。如果线段和多边形的某条边内交(两线段内交是指两线段相交且交点不在两线段的端点),因为多边形的边的左右两侧分属多边形内外不同部分,所以线段一定会有一部分在多边形外(见图a)。于是我们得到线段在多边形内的第二个必要条件:线段和多边形的所有边都不内交。

  线段和多边形交于线段的两端点并不会影响线段是否在多边形内;但是如果多边形的某个顶点和线段相交,还必须判断两相邻交点之间的线段是否包含于多边形内部(反例见图b)。

 

  因此我们可以先求出所有和线段相交的多边形的顶点,然后按照X-Y坐标排序(X坐标小的排在前面,对于X坐标相同的点,Y坐标小的排在前面,这种排序准则也是为了保证水平和垂直情况的判断正确),这样相邻的两个点就是在线段上相邻的两交点,如果任意相邻两点的中点也在多边形内,则该线段一定在多边形内。

  证明如下:
 

  命题1:
    如果线段和多边形的两相邻交点P1 ,P2的中点P' 也在多边形内,则P1, P2之间的所有点都在多边形内。


  证明:
    假设P1,P2之间含有不在多边形内的点,不妨设该点为Q,在P1, P'之间,因为多边形是闭合曲线,所以其内外部之间有界,而P1属于多边行内部,Q属于多边性外部,P'属于多边性内部,P1-Q-P'完全连续,所以 P1Q和QP'一定跨越多边形的边界,因此在P1,P'之间至少还有两个该线段和多边形的交点,这和P1P2是相邻两交点矛盾,故命题成立。证毕。

  由命题1直接可得出推论:
  推论2:
    设多边形和线段PQ的交点依次为P1,P2,……Pn,其中Pi和Pi+1是相邻两交点,线段PQ在多边形内的充要条件是:P,Q在多边形内且对于i =1, 2,……, n-1,Pi ,Pi+1的中点也在多边形内。

  在实际编程中,没有必要计算所有的交点,首先应判断线段和多边形的边是否内交,倘若线段和多边形的某条边内交则线段一定在多边形外;如果线段和多边形的每一条边都不内交,则线段和多边形的交点一定是线段的端点或者多边形的顶点,只要判断点是否在线段上就可以了。

  至此我们得出算法如下:

if 线端PQ的端点不都在多边形内
then return false;
点集pointSet初始化为空;
for 多边形的每条边s
do if 线段的某个端点在s上
then 将该端点加入pointSet;
else if s的某个端点在线段PQ上
then 将该端点加入pointSet;
else if s和线段PQ相交 // 这时候已经可以肯定是内交了
then return false;
将pointSet中的点按照X-Y坐标排序;
for pointSet中每两个相邻点 pointSet[i] , pointSet[ i+1]
do if pointSet[i] , pointSet[ i+1] 的中点不在多边形中
then return false;
return true;

  这个过程中的排序因为交点数目肯定远小于多边形的顶点数目n,所以最多是常数级的复杂度,几乎可以忽略不计。因此算法的时间复杂度也是O(n)。


14.  判断折线是否在多边形内:

  只要判断折线的每条线段是否都在多边形内即可。设折线有m条线段,多边形有n个顶点,则该算法的时间复杂度为O(m*n)。


15.  判断多边形是否在多边形内:

  只要判断多边形的每条边是否都在多边形内即可。判断一个有m个顶点的多边形是否在一个有n个顶点的多边形内复杂度为O(m*n)。
16.  判断矩形是否在多边形内:

  将矩形转化为多边形,然后再判断是否在多边形内。


17  判断圆是否在多边形内:

  只要计算圆心到多边形的每条边的最短距离,如果该距离大于等于圆半径则该圆在多边形内。计算圆心到多边形每条边最短距离的算法在后文阐述。


18.  判断点是否在圆内:

  计算圆心到该点的距离,如果小于等于半径则该点在圆内。


19.  判断线段、折线、矩形、多边形是否在圆内:

  因为圆是凸集,所以只要判断是否每个顶点都在圆内即可。


20.  判断圆是否在圆内:

  设两圆为O1,O2,半径分别为r1, r2,要判断O2是否在O1内。先比较r1,r2的大小,如果r1<r2则O2不可能在O1内;否则如果两圆心的距离大于r1 - r2 ,则O2不在O1内;否则O2在O1内。


21.  计算点到线段的最近点:

  如果该线段平行于X轴(Y轴),则过点point作该线段所在直线的垂线,垂足很容易求得,然后计算出垂足,如果垂足在线段上则返回垂足,否则返回离垂足近的端点;如果该线段不平行于X轴也不平行于Y轴,则斜率存在且不为0。设线段的两端点为pt1和pt2,斜率为:k = ( pt2.y - pt1. y ) / (pt2.x - pt1.x );该直线方程为:y = k* ( x - pt1.x) + pt1.y。其垂线的斜率为 - 1 / k,垂线方程为:y = (-1/k) * (x - point.x) + point.y 。

  联立两直线方程解得:x = ( k^2 * pt1.x + k * (point.y - pt1.y ) + point.x ) / ( k^2 + 1) ,y = k * ( x - pt1.x) + pt1.y;然后再判断垂足是否在线段上,如果在线段上则返回垂足;如果不在则计算两端点到垂足的距离,选择距离垂足较近的端点返回。


22.  计算点到折线、矩形、多边形的最近点:

  只要分别计算点到每条线段的最近点,记录最近距离,取其中最近距离最小的点即可。


23.  计算点到圆的最近距离及交点坐标:

  如果该点在圆心,因为圆心到圆周任一点的距离相等,返回UNDEFINED。

  连接点P和圆心O,如果PO平行于X轴,则根据P在O的左边还是右边计算出最近点的横坐标为centerPoint.x - radius 或 centerPoint.x + radius。如果PO平行于Y轴,则根据P在O的上边还是下边计算出最近点的纵坐标为 centerPoint.y -+radius或 centerPoint.y - radius。如果PO不平行于X轴和Y轴,则PO的斜率存在且不为0,这时直线PO斜率为k = ( P.y - O.y )/ ( P.x - O.x )。直线PO的方程为:y = k * ( x - P.x) + P.y。设圆方程为:(x - O.x ) ^2 + ( y - O.y ) ^2 = r ^2,联立两方程组可以解出直线PO和圆的交点,取其中离P点较近的交点即可。


24.  计算两条共线的线段的交点:

  对于两条共线的线段,它们之间的位置关系有下图所示的几种情况。图(a)中两条线段没有交点;图 (b) 和 (d) 中两条线段有无穷焦点;图 (c) 中两条线段有一个交点。设line1是两条线段中较长的一条,line2是较短的一条,如果line1包含了line2的两个端点,则是图(d)的情况,两线段有无穷交点;如果line1只包含line2的一个端点,那么如果line1的某个端点等于被line1包含的line2的那个端点,则是图(c) 的情况,这时两线段只有一个交点,否则就是图(b)的情况,两线段也是有无穷的交点;如果line1不包含line2的任何端点,则是图(a)的情况,这时两线段没有交点。

 

25.  计算线段或直线与线段的交点:

  设一条线段为L0 = P1P2,另一条线段或直线为L1 = Q1Q2 ,要计算的就是L0和L1的交点。

 1. 首先判断L0和L1是否相交(方法已在前文讨论过),如果不相交则没有交点,否则说明L0和L1一定有交点,下面就将L0和L1都看作直线来考虑。

 2. 如果P1和P2横坐标相同,即L0平行于Y轴

  a) 若L1也平行于Y轴,

    i. 若P1的纵坐标和Q1的纵坐标相同,说明L0和L1共线,假如L1是直线的话他们有无穷的交点,假如L1是线段的话可用"计算两条共线线段的交点"的算法求他们的交点(该方法在前文已讨论过);
    ii. 否则说明L0和L1平行,他们没有交点;

  b) 若L1不平行于Y轴,则交点横坐标为P1的横坐标,代入到L1的直线方程中可以计算出交点纵坐标;

 3. 如果P1和P2横坐标不同,但是Q1和Q2横坐标相同,即L1平行于Y轴,则交点横坐标为Q1的横坐标,代入到L0的直线方程中可以计算出交点纵坐标;

 4. 如果P1和P2纵坐标相同,即L0平行于X轴

  a) 若L1也平行于X轴,

    i. 若P1的横坐标和Q1的横坐标相同,说明L0和L1共线,假如L1是直线的话他们有无穷的交点,假如L1是线段的话可用"计算两条共线线段的交点"的算法求他们的交点(该方法在前文已讨论过);
    ii. 否则说明L0和L1平行,他们没有交点;

  b) 若L1不平行于X轴,则交点纵坐标为P1的纵坐标,代入到L1的直线方程中可以计算出交点横坐标;

 5. 如果P1和P2纵坐标不同,但是Q1和Q2纵坐标相同,即L1平行于X轴,则交点纵坐标为Q1的纵坐标,代入到L0的直线方程中可以计算出交点横坐标;

 6. 剩下的情况就是L1和L0的斜率均存在且不为0的情况

  a) 计算出L0的斜率K0,L1的斜率K1 ;

  b) 如果K1 = K2

    i. 如果Q1在L0上,则说明L0和L1共线,假如L1是直线的话有无穷交点,假如L1是线段的话可用"计算两条共线线段的交点"的算法求他们的交点(该方法在前文已讨论过);
    ii. 如果Q1不在L0上,则说明L0和L1平行,他们没有交点。

  c) 联立两直线的方程组可以解出交点来

  这个算法并不复杂,但是要分情况讨论清楚,尤其是当两条线段共线的情况需要单独考虑,所以在前文将求两条共线线段的算法单独写出来。另外,一开始就先利用矢量叉乘判断线段与线段(或直线)是否相交,如果结果是相交,那么在后面就可以将线段全部看作直线来考虑。需要注意的是,我们可以将直线或线段方程改写为ax+by+c=0的形式,这样一来上述过程的部分步骤可以合并,缩短了代码长度,但是由于先要求出参数,这种算法将花费更多的时间。


26.  求线段或直线与折线、矩形、多边形的交点:

  分别求与每条边的交点即可。


27.  求线段或直线与圆的交点:

  设圆心为O,圆半径为r,直线(或线段)L上的两点为P1,P2。

  1. 如果L是线段且P1,P2都包含在圆O内,则没有交点;否则进行下一步。

  2. 如果L平行于Y轴,

   a) 计算圆心到L的距离dis;
   b) 如果dis > r 则L和圆没有交点;
   c) 利用勾股定理,可以求出两交点坐标,但要注意考虑L和圆的相切情况。

  3. 如果L平行于X轴,做法与L平行于Y轴的情况类似;

  4. 如果L既不平行X轴也不平行Y轴,可以求出L的斜率K,然后列出L的点斜式方程,和圆方程联立即可求解出L和圆的两个交点;

  5. 如果L是线段,对于2,3,4中求出的交点还要分别判断是否属于该线段的范围内。


28.  凸包的概念:

  点集Q的凸包(convex hull)是指一个最小凸多边形,满足Q中的点或者在多边形边上或者在其内。下图中由红色线段表示的多边形就是点集Q={p0,p1,...p12}的凸包。

 

29.  凸包的求法:

  现在已经证明了凸包算法的时间复杂度下界是O(n*logn),但是当凸包的顶点数h也被考虑进去的话,Krikpatrick和Seidel的剪枝搜索算法可以达到O(n*logh),在渐进意义下达到最优。最常用的凸包算法是Graham扫描法和Jarvis步进法。本文只简单介绍一下Graham 扫描法,其正确性的证明和Jarvis步进法的过程大家可以参考《算法导论》。

  对于一个有三个或以上点的点集Q,Graham扫描法的过程如下:

  令p0为Q中Y-X坐标排序下最小的点
  设<p1,p2,...pm>为对其余点按以p0为中心的极角逆时针排序所得的点集(如果有多个点有相同的极角,除了距p0最远的点外全部移除
  压p0进栈S
  压p1进栈S
  压p2进栈S
for i ← 3 to m
do while 由S的栈顶元素的下一个元素、S的栈顶元素以及pi构成的折线段不拐向左侧
对S弹栈
压pi进栈S
return S;
 

  此过程执行后,栈S由底至顶的元素就是Q的凸包顶点按逆时针排列的点序列。需要注意的是,我们对点按极角逆时针排序时,并不需要真正求出极角,只需要求出任意两点的次序就可以了。而这个步骤可以用前述的矢量叉积性质实现。


四、结语

  尽管人类对几何学的研究从古代起便没有中断过,但是具体到借助计算机来解决几何问题的研究,还只是停留在一个初级阶段,无论从应用领域还是发展前景来看,计算几何学都值得我们认真学习、加以运用,希望这篇文章能带你走进这个丰富多彩的世界。

 


目录


㈠ 点的基本运算
1. 平面上两点之间距离 1
2. 判断两点是否重合 1
3. 矢量叉乘 1
4. 矢量点乘 2
5. 判断点是否在线段上 2
6. 求一点饶某点旋转后的坐标 2
7. 求矢量夹角 2

㈡ 线段及直线的基本运算
1. 点与线段的关系 3
2. 求点到线段所在直线垂线的垂足 4
3. 点到线段的最近点 4
4. 点到线段所在直线的距离 4
5. 点到折线集的最近距离 4
6. 判断圆是否在多边形内 5
7. 求矢量夹角余弦 5
8. 求线段之间的夹角 5
9. 判断线段是否相交 6
10.判断线段是否相交但不交在端点处 6
11.求线段所在直线的方程 6
12.求直线的斜率 7
13.求直线的倾斜角 7
14.求点关于某直线的对称点 7
15.判断两条直线是否相交及求直线交点 7
16.判断线段是否相交,如果相交返回交点 7

㈢ 多边形常用算法模块
1. 判断多边形是否简单多边形 8
2. 检查多边形顶点的凸凹性 9
3. 判断多边形是否凸多边形 9
4. 求多边形面积 9
5. 判断多边形顶点的排列方向,方法一 10
6. 判断多边形顶点的排列方向,方法二 10
7. 射线法判断点是否在多边形内 10
8. 判断点是否在凸多边形内 11
9. 寻找点集的graham算法 12
10.寻找点集凸包的卷包裹法 13
11.判断线段是否在多边形内 14
12.求简单多边形的重心 15
13.求凸多边形的重心 17
14.求肯定在给定多边形内的一个点 17
15.求从多边形外一点出发到该多边形的切线 18
16.判断多边形的核是否存在 19

㈣ 圆的基本运算
1 .点是否在圆内 20
2 .求不共线的三点所确定的圆 21

㈤ 矩形的基本运算
1.已知矩形三点坐标,求第4点坐标 22

㈥ 常用算法的描述 22

㈦ 补充
1.两圆关系: 24
2.判断圆是否在矩形内: 24
3.点到平面的距离: 25
4.点是否在直线同侧: 25
5.镜面反射线: 25
6.矩形包含: 26
7.两圆交点: 27
8.两圆公共面积: 28
9. 圆和直线关系: 29
10. 内切圆: 30
11. 求切点: 31
12. 线段的左右旋: 31
13.公式: 32

 

/* 需要包含的头文件 */
#include <cmath >

/* 常用的常量定义 */
const double INF = 1E200
const double EP = 1E-10
const int MAXV = 300
const double PI = 3.14159265

/* 基本几何结构 */
struct POINT
{
    double x;
    double y; POINT(double a=0, double b=0) { x=a; y=b;} //constructor
};
struct LINESEG
{
    POINT s;
    POINT e; LINESEG(POINT a, POINT b) { s=a; e=b;}
    LINESEG() { }
};
struct LINE // 直线的解析方程 a*x+b*y+c=0 为统一表示,约定 a >= 0
{
    double a;
    double b;
    double c; LINE(double d1=1, double d2=-1, double d3=0) {a=d1; b=d2; c=d3;}
};

/********************/
* *
* 点的基本运算 *
* *
/********************/


double dist(POINT p1,POINT p2) // 返回两点之间欧氏距离
{
    return( sqrt( (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y) ) );
}
bool equal_point(POINT p1,POINT p2) // 判断两个点是否重合
{
    return ( (abs(p1.x-p2.x)<EP)&&(abs(p1.y-p2.y)<EP) );
}

/******************************************************************************
r=multiply(sp,ep,op),得到(sp-op)*(ep-op)的叉积
r>0:ep在矢量opsp的逆时针方向;
r=0:opspep三点共线;
r<0:ep在矢量opsp的顺时针方向
*******************************************************************************/


double multiply(POINT sp,POINT ep,POINT op)
{
    return((sp.x-op.x)*(ep.y-op.y)-(ep.x-op.x)*(sp.y-op.y));
}

/*******************************************************************************
r=dotmultiply(p1,p2,op),得到矢量(p1-op)和(p2-op)的点积,如果两个矢量都非零矢量
r<0:两矢量夹角为锐角;r=0:两矢量夹角为直角;r>0:两矢量夹角为钝角
*******************************************************************************/

double dotmultiply(POINT p1,POINT p2,POINT p0)
{
    return ((p1.x-p0.x)*(p2.x-p0.x)+(p1.y-p0.y)*(p2.y-p0.y));
}

/* 判断点p是否在线段l上,条件:(p在线段l所在的直线上)&& (点p在以线段l为对角线的矩形内) */
bool online(LINESEG l,POINT p)
{
    return((multiply(l.e,p,l.s)==0)
        &&( ( (p.x-l.s.x)*(p.x-l.e.x)<=0 )&&( (p.y-l.s.y)*(p.y-l.e.y)<=0 ) ) );
}

// 返回点p以点o为圆心逆时针旋转alpha(单位:弧度)后所在的位置
POINT rotate(POINT o,double alpha,POINT p)
{
    POINT tp;
    p.x-=o.x;
    p.y-=o.y;
    tp.x=p.x*cos(alpha)-p.y*sin(alpha)+o.x;
    tp.y=p.y*cos(alpha)+p.x*sin(alpha)+o.y;
    return tp;
}

/* 返回顶角在o点,起始边为os,终止边为oe的夹角(单位:弧度)
角度小于pi,返回正值
角度大于pi,返回负值
可以用于求线段之间的夹角
*/

double angle(POINT o,POINT s,POINT e)
{
    double cosfi,fi,norm;
    double dsx = s.x - o.x;
    double dsy = s.y - o.y;
    double dex = e.x - o.x;
    double dey = e.y - o.y;
   
    cosfi=dsx*dex+dsy*dey;
    norm=(dsx*dsx+dey*dey)*(dex*dex+dey*dey);
    cosfi /= sqrt( norm );
   
    if (cosfi >= 1.0 ) return 0;
    if (cosfi <= -1.0 ) return -3.1415926;
   
    fi=acos(cosfi);
    if (dsx*dey-dsy*dex>0) return fi; // 说明矢量os 在矢量 oe的顺时针方向
    return -fi;
}





/*****************************/
* *
* 线段及直线的基本运算 *
* *
/*****************************/


/* 判断点与线段的关系,用途很广泛
本函数是根据下面的公式写的,P是点C到线段AB所在直线的垂足

  AC dot AB
  r = ---------
  ||AB||^2
  (Cx-Ax)(Bx-Ax) + (Cy-Ay)(By-Ay)
  = -------------------------------
  L^2
 
    r has the following meaning:
   
      r=0 P = A
      r=1 P = B
      r<0 P is on the backward extension of AB
      r>1 P is on the forward extension of AB
      0<r<1 P is interior to AB
*/

double relation(POINT p,LINESEG l)
{
    LINESEG tl;
    tl.s=l.s;
    tl.e=p;
    return dotmultiply(tl.e,l.e,l.s)/(dist(l.s,l.e)*dist(l.s,l.e));
}

// 求点C到线段AB所在直线的垂足 P
POINT perpendicular(POINT p,LINESEG l)
{
    double r=relation(p,l);
    POINT tp;
    tp.x=l.s.x+r*(l.e.x-l.s.x);
    tp.y=l.s.y+r*(l.e.y-l.s.y);
    return tp;
}
/* 求点p到线段l的最短距离,并返回线段上距该点最近的点np
注意:np是线段l上到点p最近的点,不一定是垂足 */

double ptolinesegdist(POINT p,LINESEG l,POINT &np)
{
    double r=relation(p,l);
    if(r<0)
    {
        np=l.s;
        return dist(p,l.s);
    }
    if(r>1)
    {
        np=l.e;
        return dist(p,l.e);
    }
    np=perpendicular(p,l);
    return dist(p,np);
}

// 求点p到线段l所在直线的距离,请注意本函数与上个函数的区别
double ptoldist(POINT p,LINESEG l)
{
    return abs(multiply(p,l.e,l.s))/dist(l.s,l.e);
}

/* 计算点到折线集的最近距离,并返回最近点.
注意:调用的是ptolineseg()函数 */

double ptopointset(int vcount,POINT pointset[],POINT p,POINT &q)
{
    int i;
    double cd=double(INF),td;
    LINESEG l;
    POINT tq,cq;
   
    for(i=0;i<vcount-1;i++)
    {
        l.s=pointset[i];
        l.e=pointset[i+1];
        td=ptolinesegdist(p,l,tq);
        if(td<cd)
        {
            cd=td;
            cq=tq;
        }
    }
    q=cq;
    return cd;
}
/* 判断圆是否在多边形内.ptolineseg()函数的应用2 */
bool CircleInsidePolygon(int vcount,POINT center,double radius,POINT polygon[])
{
    POINT q;
    double d;
    q.x=0;
    q.y=0;
    d=ptopointset(vcount,polygon,center,q);
    if(d<radius||fabs(d-radius)<EP)
        return true;
    else
        return false;
}

/* 返回两个矢量l1和l2的夹角的余弦(-1 --- 1)注意:如果想从余弦求夹角的话,注意反余弦函数的定义域是从 0到pi */
double cosine(LINESEG l1,LINESEG l2)
{
    return (((l1.e.x-l1.s.x)*(l2.e.x-l2.s.x) +
        (l1.e.y-l1.s.y)*(l2.e.y-l2.s.y))/(dist(l1.e,l1.s)*dist(l2.e,l2.s))) );
}
// 返回线段l1与l2之间的夹角 单位:弧度 范围(-pi,pi)
double lsangle(LINESEG l1,LINESEG l2)
{
    POINT o,s,e;
    o.x=o.y=0;
    s.x=l1.e.x-l1.s.x;
    s.y=l1.e.y-l1.s.y;
    e.x=l2.e.x-l2.s.x;
    e.y=l2.e.y-l2.s.y;
    return angle(o,s,e);
}
// 如果线段u和v相交(包括相交在端点处)时,返回true
bool intersect(LINESEG u,LINESEG v)
{
    return( (max(u.s.x,u.e.x)>=min(v.s.x,v.e.x))&& //排斥实验
        (max(v.s.x,v.e.x)>=min(u.s.x,u.e.x))&&
        (max(u.s.y,u.e.y)>=min(v.s.y,v.e.y))&&
        (max(v.s.y,v.e.y)>=min(u.s.y,u.e.y))&&
        (multiply(v.s,u.e,u.s)*multiply(u.e,v.e,u.s)>=0)&& //跨立实验
        (multiply(u.s,v.e,v.s)*multiply(v.e,u.e,v.s)>=0));
}


// (线段u和v相交)&&(交点不是双方的端点) 时返回true
bool intersect_A(LINESEG u,LINESEG v)
{
    return((intersect(u,v))&&
        (!online(u,v.s))&&
        (!online(u,v.e))&&
        (!online(v,u.e))&&
        (!online(v,u.s)));
}


// 线段v所在直线与线段u相交时返回true;方法:判断线段u是否跨立线段v
bool intersect_l(LINESEG u,LINESEG v)
{
    return multiply(u.s,v.e,v.s)*multiply(v.e,u.e,v.s)>=0;
}


// 根据已知两点坐标,求过这两点的直线解析方程: a*x+b*y+c = 0 (a >= 0)
LINE makeline(POINT p1,POINT p2)
{
    LINE tl;
    int sign = 1;
    tl.a=p2.y-p1.y;
    if(tl.a<0)
    {
        sign = -1;
        tl.a=sign*tl.a;
    }
    tl.b=sign*(p1.x-p2.x);
    tl.c=sign*(p1.y*p2.x-p1.x*p2.y);
    return tl;
}

// 根据直线解析方程返回直线的斜率k,水平线返回 0,竖直线返回 1e200
double slope(LINE l)
{
    if(abs(l.a) < 1e-20)return 0;
    if(abs(l.b) < 1e-20)return INF;
    return -(l.a/l.b);
}

// 返回直线的倾斜角alpha ( 0 - pi)
double alpha(LINE l)
{
    if(abs(l.a)< EP)return 0;
    if(abs(l.b)< EP)return PI/2;
    double k=slope(l);
    if(k>0)
        return atan(k);
    else
        return PI+atan(k);
}

// 求点p关于直线l的对称点
POINT symmetry(LINE l,POINT p)
{
    POINT tp;
    tp.x=((l.b*l.b-l.a*l.a)*p.x-2*l.a*l.b*p.y-2*l.a*l.c)/(l.a*l.a+l.b*l.b);
    tp.y=((l.a*l.a-l.b*l.b)*p.y-2*l.a*l.b*p.x-2*l.b*l.c)/(l.a*l.a+l.b*l.b);
    return tp;
}

// 如果两条直线 l1(a1*x+b1*y+c1 = 0), l2(a2*x+b2*y+c2 = 0)相交,返回true,且返回交点p
bool lineintersect(LINE l1,LINE l2,POINT &p) // 是 L1,L2
{
    double d=l1.a*l2.b-l2.a*l1.b;
    if(abs(d)<EP) // 不相交
        return false;
    p.x = (l2.c*l1.b-l1.c*l2.b)/d;
    p.y = (l2.a*l1.c-l1.a*l2.c)/d;
    return true;
}

// 如果线段l1和l2相交,返回true且交点由(inter)返回,否则返回false
bool intersection(LINESEG l1,LINESEG l2,POINT &inter)
{
    LINE ll1,ll2;
    ll1=makeline(l1.s,l1.e);
    ll2=makeline(l2.s,l2.e);
    if(lineintersect(ll1,ll2,inter))
    {
        return online(l1,inter);
    }
    else
        return false;
}

 

算法篇---- 几何算法 几何算法 1)知道两个点,求直线方程。 (2)求点到直线的距离 3)点到直线的投影 (4)点关于直线的对称点 (5)两条直线的位置关系 (6)两个线段是否会相交 (7)知识点 (8)两个线段的交点 (9)点到线段之间的距离 (10)两条线段之间的距离 (11)多边形面积 (12)凸边形(判断) (13)点是否在多边形内部 (14)求凸包 (15)求凸包的直径(平面最远点对) (16)三角形 阅读详情

相关推荐

【几何专题】计算几何基础

整理的算法模板合集: ACM模板 目录AcWing 2984. 线段 / POJ3304(计算几何、常用技巧转换)AcWing 2983. 玩具 / POJ 2318.toys(计算几何基础、二分、判断点和直线的位置关系) AcWing 2984. 线段 / POJ3304(计算几何、常用技巧转换) 常用技巧转换,寻找一个直线旋转 我们发现如果可以找到一个直线与所有的线段都相交,那么我们做一个垂直这个直线的垂线,所有的线段的投影一定都交于这个直线与垂线的垂足处,我们可以很轻松地画图验证这一定理。 所

繁凡さん的博客 970

几何算法——介绍

介绍几何算法相关的。

luolei188的博客 3005

计算几何算法概览

计算几何算法概览原创:怒火之袍 2003年5月17日 计算几何常用算法概览本站原创:怒火之袍一、引言    计算机的出现使得很多原本十分繁琐的工作得以大幅度简化,但是也有一些在人们直观看来很容易的问题却需要拿出一套并不简单的通用解决方案,比如几何问题。作为计算机科学的一个分支,计算几何主要研究解决几何问题的算法。在现代工程和数学领域,

lithe的Blog 8314

常用几何算法

常用几何算法算法目录算法的介绍 引自 实用计算机几何算法 算法目录 矢量的概念 矢量加减法 矢量叉积 折线段的拐向判断 判断点是否在线段上 判断两线段是否相交 判断线段和直线是否相交 判断矩形是否包含点 判断线段、折线、多边形是否在矩形中 判断矩形是否在矩形中 判断圆是否在矩形中 判断点是否在多边形中 判断线段是否在多边形内 判断折线是否在多边形内 判断多边形是否在多边形内 判断矩形是否在多边形...

m0_46798553的博客 2310

常用几何关系算法

几何关系算法集: 目录 ㈠ 点的基本运算 1. 平面上两点之间距离 1 2. 判断两点是否重合 1 3. 矢量叉乘 1 4. 矢量点乘 2 5. 判断点是否在线段上 2 6. 求一点饶某点旋转后的坐标 2 7. 求矢量夹角 2 ㈡ 线段及直线的基本运算 1. 点与线段的关系 3 2. 求点到线段所在直线垂线的垂足 4 3. 点到线段的最近点 4 4. 点到线段所在直线的距离 4 5. 点到折线集的最近距离 4 6. 判断圆是否在多边形内 5 7. 求矢量夹角余弦 5 8. 求线段之间的夹角 5 9. 判断线段是否相交

VASPKIT绘制平面平均电荷差分密度

平面平均密度差(planar-averaged electron density difference)是密度差的衍生。密度差是个三维空间函数,经常通过绘制平面图、等值面图方式图形化考察。虽然这两种图很直观,但平面图会受到截面选取的影响,而等值面图会受到等值面数值(isovalue)选取的影响,因此都有一定任意性,也不容易定量讨论。对于电子转移有比较明确方向的情况,可以绘制平面平均密度差曲线,由此可以清楚地考察垂直于选取的方向上每个截面上的密度差积分值,在定量讨论、对比上比较便利。

weixin_43948934的博客 1万+

计算几何

OI中少有的以高中知识为主的版块。 1.高中计算几何基础知识 2.深刻的认识到计算几何用向量而不用解析几何。 3.图形的记录 (1):点,向量。这两个是差不多的。 (2):线:直线上一点和直线的方向向量。 (3):线段:只需要记录左右端点即可。。。。 4.正弦定理: aSinA=bSinB=cSinC=2R\frac {a}{Sin A} = \frac {b}{SinB}=\frac c{Sin...

Freopen的博客 1万+

YOLOv5 改进点总览:轻量化、高精度的背后有哪些魔改?

YOLOv5是由Ultralytics团队开发的高效目标检测模型,其核心改进包括:主干网络采用CSPDarknet53提升梯度传播效率;特征融合使用PANet增强小目标识别;输出结构采用解耦头设计优化定位和分类任务。此外,YOLOv5通过CIoU损失函数、Mosaic数据增强和SimOTA标签分配机制进一步提升性能。模型支持多种推理格式(ONNX/TensorRT等),并具备自动锚框匹配和多尺度训练功能,使其在轻量化(yolov5s仅7MB)与高性能(最高51.2% mAP)间取得平衡,但缺乏正式论文支撑是

guoguozgw的博客 1806

计算几何总结

计算几何总结 一、精度控制 计算几何经常牵扯到浮点数的运算,所以就会产生精度误差,因此我们需要设置一个eps(偏差值),一般取1e-7到1e-10之间,并用下面的函数控制精度。 const double eps=1e-8; int dcmp(double x) { if (fabs(x)<eps) return 0; else return x<0?-1:1; }二、向量

clover_hxy的博客 1万+

Qt实战:用QTreeView+QStandardItemModel实现多级多列文件浏览器(附完整代码)

本文深入探讨了使用Qt的QTreeView与QStandardItemModel构建多级多列文件浏览器的完整实践。通过分析模型/视图框架的优势,详细演示了从基础框架搭建、模拟数据填充,到对接真实文件系统实现懒加载的全过程。文章还涵盖了自定义委托、多列排序、右键菜单等高级定制技巧,并提供了性能优化建议和完整的代码示例,帮助开发者打造功能强大且高效的工业级文件管理界面。

weixin_29021291的博客 424

GlobalPlatform Technology Secure Channel Protocol ‘03‘ 标准 - 安全通道协议简介

按照GlobalPlatform Technology Secure Channel Protocol '03' (SCP03)标准,描述了如何利用S-MAC,S-RMAC,S-ENC进行安全通信的过程。

mmmx777的专栏 6092

ACM 计算几何基础模板

在ACM计算几何中,我们常常重复用到许多方法,例如求点距,求直线方程,等等。不妨利用模板总结之。这些模板为我从网上学习后加入自己的理解糅合得来qwq,看了很多的博客,查了很多书籍,其中印象最深的是林夕林夕关于计算几何模板的总结,大家也可以去参考他的应该比我的更详细qwq(不过大佬最近好像出没比较少了) 所以我这个主要还是留着自己参考吧,能帮到他人再好不过了 为了不产生歧义,方便理解,本模板使用笛卡尔坐标系,封装的多是数学中常见的函数。 前置知识:高中数学 文章目录基础声明点向量直线 基础声明 首先,打acm

qq_39561693的博客 1507

别再混淆了!深入浅出图解四元数姿态控制中的‘误差四元数’(附四种定义对比与仿真)

本文深入解析四元数姿态控制中的误差四元数定义差异,对比四种常见定义(如Q₀∘Q⁻¹与Q⁻¹∘Q₀)及其对PD控制性能的影响。通过刚体动力学仿真验证,揭示定义二(Q⁻¹∘Q₀)在无人机和航天器控制中的最优性能,提供工程实践建议与参数整定技巧,帮助开发者避免常见陷阱并提升控制效率。

weixin_42528191的博客 277

专题十三 基础计算几何

文章目录专题十三 基础计算几何POJ 2318 TOYSPOJ 2398 Toy StoragePOJ 3304 SegmentsPOJ 1269 Intersecting LinesPOJ 1556 The DoorsPOJ 2653 Pick-up sticksPOJ 1066 Treasure HuntPOJ 1410 IntersectionPOJ 1696 Space AntPOJ 3347 Kadj SquaresPOJ 2826 An Easy Problem?!POJ 1039 PipePO

m0_49959202的博客 312

PCL 利用RANSAC改进的ICP算法

本文介绍了一种基于RANSAC改进的ICP点云配准算法。算法原理部分阐述了在ICP迭代过程中使用RANSAC剔除错误匹配点对的方法。

点云侠的博客 196

【Bug已解决】Codex capacity errors 自动重试与意图保留 解决方案

原始报错:Codex Desktop: capacity errors should preserve intent and auto-retry instead of handing model routing back to the user场景:向模型服务发请求时遇到容量错误(服务端繁忙 / 限流 / 容量不足),客户端不做任何重试,直接把"请求失败,请选择另一个模型或重试"的锅甩给用户,让用户手动去点。用户本来只是想完成一次任务,却被中途打断去当"调度员"。

博客 279

计算几何系列——XCPC中计算几何一些题型杂谈(上)

这样一画,那么这题的模型就出来了,大致的一个思路就是把点的一个圆周顺序排列出来,这里需要一个排序操作,然后建立点与点之间的一个距离关系,这些距离关系构成了一个边集,然后我们对这个边集跑一遍最短路即可。那么难点在哪里呢?

Zz_0913的博客 1635

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

计算几何基础入门详解

我把计算几何分为了六大块 一、计算几何基础(点,线,面) 二、计算几何凸包 三、计算几何圆形 四、计算几何旋转卡壳 五、计算几何半平面交 六、三维计算几何(本博客内容不予介绍) 一、计算几何基础 1、最开始要提一下的还是精度问题,这在计算几何中可以说非常关键!!! const double eps=1e-8; //我一般都用eps表示,控制在1e-8左右,当然有些题会有它的要求 int sgn(d...

slass-mot-odet 2694

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

计算几何基础题目

大爱计算几何,做这些题目,感觉又回到了以前没日没夜的解数学题的时候一样 不过毕竟很多事情都过去了,尽量多刷吧 转载来自:http://blog.sina.com.cn/s/blog_49c5866c0100f3om.html 其实也谈不上推荐,只是自己做过的题目而已,甚至有的题目尚未AC,让在挣扎中。之所以推荐计算几何题,是因为,本人感觉ACM各种算法中计算几何算是比较实际

free斩 1705

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

计算几何系列——XCPC中计算几何一些题型杂谈(中)

在XCPC中有一种题叫计算几何,这类题在大多数时候都作为一类金牌题甚至防AK题的难度出现,但是在前段时间我写了许多学习计算几何的博客,会发现计算几何的知识体系十分独立且庞大,而且板子很长,就是一个特点:毒瘤。 所以,在这里,我作为一个主修计算几何的ACMer,做了一点点题后对这类毒瘤题做出了一定的总结,希望对大家能有所帮助。 首先,计算几何的题目并非全是难题,它也有一个难度梯度,我这边将其分为差不多三类题: 1.一般的平面几何知识的运用,一般这类题难度较低,需要掌握基本的几何知

Zz_0913的博客 1366

计算几何基础

文章目录计算几何基础1.算法原理1.1 基本符号与公式1.1.1 基础符号表示1.1.1.1 PI的表示1.1.2 基本公式1.1.2.1 正弦定理1.1.2.2 余弦定理1.1.2.3 角度与弧度转化1.2 向量1.2.1 向量加法1.2.2 向量减法1.2.3 向量模长1.2.4 向量数乘1.2.5 向量内积(点积)1.2.6 向量外积(叉积)1.2.7 向量旋转1.3 直线与线段1.3.1 线段相交问题1.3.2 点到直线的距离1.3.3 两直线交点1.4 多边形1.4.1 点和多边形的位置关系1.4

m0_49959202的博客 648

【计算几何】【计算几何模板】【持续更新】

必须学计算几何了…… muba

退役狗的专栏 961

计算几何入门详解

我把计算几何分为了六大块 一、计算几何基础(点,线,面) 二、计算几何凸包 三、计算几何圆形(本博主不予介绍) 四、计算几何旋转卡壳(本博主不予介绍) 五、计算几何半平面交 六、三维计算几何(本博主不予介绍) ...

slass-mot-odet 718

计算几何算法大全

/* 计算几何 目录 ㈠ 点的基本运算 平面上两点之间距离 1 判断两点是否重合 1 矢量叉乘 1 矢量点乘 2 判断点是否在线段上 2 求一点饶某点旋转后的坐标 2 求矢量夹角 2 ㈡ 线段及直线的基本运算 点与线段的关系 3 求点到线段所在直线垂线的垂足 4 点到线段的最近点 4 点到线段所在直线的距离 4 点到折线集的最近距离 4 判断圆是否在多边形内 5 求矢量夹角余弦 5 求线段之...

hayaqi0504的博客 3194
上一篇: 第8课 对话框
下一篇: ZOJ-1004-Anagrams by Stack
immortality
博客等级 码龄21年 67粉丝 68原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值