前段时间有写过一个计算多边形角度的代码,这里给它整理整理,留给自己也送给萌新。

看左下图,这是一个多环的多边形,一个外环(内部为多边形内部区域),一个内环(外部为多边形内部区域),同时多边形中任意一个角不等于零角(等于 0° 的角)或周角(等于 360° 的角)。注意:本文下文所讨论的多边形求角度不包含零角和周角。

现在我们要求 ∠ABC ∠DEF 的大小。那咋算唻?


1. 内积计算夹角

给它加上坐标系(坐标是自己配的,计算出的角度值不一定准确,但不影响角度大小的关系), 如右上图。角度采用向量的内积来求。

以上面的 ∠ABC 为例,数学计算公式如下。

于是乎,有:

角度计算代码如下:

public struct CxPoint
{
public CxPoint(double x, double y)
{
X = x;
Y = y;
} public double X;
public double Y;
} /// <summary>
/// 计算三点角度,p1-p2-p3为沿环方向的三个连续顶点,其中p2为角点。计算结果范围 0° - 180°,-1为无效值
/// </summary>
private static double CalculationAngle(CxPoint p1, CxPoint p2, CxPoint p3)
{
//Cos(Angle) = a•b/(|a|*|b|)
double x1 = p1.X - p2.X, y1 = p1.Y - p2.Y; //向量 a
double x2 = p3.X - p2.X, y2 = p3.Y - p2.Y; //向量 b //零向量,存在共点
if (x1 == && y1 == ) return -;
if (x2 == && y2 == ) return -; double v = x1 * x2 + y1 * y2; //向量内积 a•b
double val = Math.Sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2)); //a,b模长乘积 |a|*|b|
double CosAngle = v / val; //求出来的值可能略小于 -1 或者略大于 1,此时 Angle 等于 NaN
double Angle = Math.Acos(CosAngle) * 180.0 / 3.14159265358979; //两向量夹角,0-180 if (System.Double.IsNaN(Angle))
{
if (v > ) return ;
else return ;
}
else
{
if (Angle > ) return ;
else if (Angle < ) return ;
else return Angle;
}
}

参考代码

用上述代码我们能够计算得出 ∠ABC = 124.63°,∠DEF = 101.57°。细心的朋友会发现,∠DEF 很明显是个优角(大于 180° 小于 360° 的角),为什么求出来是个劣角的值(大于 0° 小于 180° 的角)呢?原来反余弦函数的值域为 [ 0,π ],故采用向量内积计算出来的夹角总是在 [ 0°,180° ] 之间。


2. 外积判断互组

针对像 ∠DEF 这种优角,我们如何计算其结果呢?原来,内积计算的夹角与正确结果必定互为组角(相加等于 360° 的两个角互为组角),如此 ∠DEF 的正确结果为 360° - 101.57° = 258.43°。故在内积计算夹角后,问题转换为判别待求角是优角还是劣角,优角则求其组角,劣角则直接是结果。

以 ∠ABC 为例 ,A → B → C 为环方向,取AC中点M,再取 BM 上靠近 点的 B' 点(称为面内面外判断点),其中 BB' 距离很小很小(若直接以 M 点作为面内面外判断点,由于存在多环的情况,会出现问题)。若 B' 在多边形内,则待求角为劣角,内积计算夹角即为结果,若 B' 在多边形外,即出现 ∠DEF 这种情况(此时 B' 是 E'),则需要求内积计算夹角的组角作为计算结果。

面内面外判断点求取代码如下:

/// <summary>
/// 求取面内面外判断点,p1-p2-p3为沿环方向的三个连续顶点,其中p2为角点。
/// </summary>
private static CxPoint CalculationJudgePoint(CxPoint p1, CxPoint p2, CxPoint p3, double SmallDis = 0.01)
{
double TempX = (p1.X + p3.X) / ;
double TempY = (p1.Y + p3.Y) / ; double DisX = TempX - p2.X;
double DisY = TempY - p2.Y;
double val = Math.Sqrt(DisX * DisX + DisY * DisY); double Scale = SmallDis / val; return new CxPoint(p2.X + Scale * DisX, p2.Y + Scale * DisY);
}

参考代码

假设,沿着环的方向,多边形的内部总在环的右侧区域,所以在上图中,∠ABC 所在的环为顺时针方向,DEF 所在的环为逆时针方向。有了这个假设,我们就能够用向量外积来判断 B' (或者是 E')点是否在面内了。具体做法为计算 ( 待求角角点,沿环方向角点下一顶点 ) 与 ( 待求角角点,面内面外判断点 ) 的外积(在本文图中为和 ):结果若大于 0,则面内面外判断点在环的左侧和多边形外部,待求角为优角,求内积计算夹角的组角作为结果;结果若小于等于 0,则面内面外判断点在环的右侧和多边形内部或边界上,待求角为劣角或平角,内积计算夹角直接作为结果。

以判断 B' BC 的哪一侧为例,数学计算公式如下。

左右侧判断代码如下:

public struct CxLine
{
public CxLine(CxPoint fromPoint, CxPoint toPoint)
{
FromPoint = fromPoint;
ToPoint = toPoint;
} public CxPoint FromPoint;
public CxPoint ToPoint;
} /// <summary>
/// 判断点在线的左方还是右方,在左为 true,在线上或在右为 false
/// </summary>
public static bool JudgAbout(CxLine pLine, CxPoint pPoint)
{
double ax = pLine.ToPoint.X - pLine.FromPoint.X;
double ay = pLine.ToPoint.Y - pLine.FromPoint.Y;
double bx = pPoint.X - pLine.FromPoint.X;
double by = pPoint.Y - pLine.FromPoint.Y;
double judge = ax * by - ay * bx; if (judge > 0.0)
return true;
else
return false;
}

参考代码


3. 求角源码整理

通过上述分析,将所有代码整理成一个 cs 类。

/// <summary>
/// 调用示例:AngleCalculation.CxPoint p1 = new AngleCalculation.CxPoint(-112, -12);
/// AngleCalculation.CxPoint p2 = new AngleCalculation.CxPoint(-68, -51);
/// AngleCalculation.CxPoint p3 = new AngleCalculation.CxPoint(0, 0);
/// double angle = AngleCalculation.Analysis(p1, p2, p3, true);
/// </summary>
public sealed class AngleCalculation
{
public struct CxPoint
{
public CxPoint(double x, double y)
{
X = x;
Y = y;
} public double X;
public double Y;
} public struct CxLine
{
public CxLine(CxPoint fromPoint, CxPoint toPoint)
{
FromPoint = fromPoint;
ToPoint = toPoint;
} public CxPoint FromPoint;
public CxPoint ToPoint;
} /// <summary>
/// 角度计算主方法,p1-p2-p3为沿环方向的三个连续顶点,其中p2为角点。
/// </summary>
/// <param name="IsClockwise">p1-p2-p3所在环方向,顺时针为 true,逆时针为 false</param>
public static double Analysis(CxPoint p1, CxPoint p2, CxPoint p3, bool IsClockwise)
{
double Angle = CalculationAngle(p1, p2, p3);
if (Angle == -) return Angle; CxPoint JudgePoint = CalculationJudgePoint(p1, p2, p3);
CxLine ReferenceLine = new CxLine(p2, p3); bool IsLeft = JudgAbout(ReferenceLine, JudgePoint); if (IsClockwise == IsLeft) Angle = - Angle; return Angle;
} /// <summary>
/// 计算三点角度,p1-p2-p3为沿环方向的三个连续顶点,其中p2为角点。计算结果范围 0° - 180°,-1为无效值
/// </summary>
private static double CalculationAngle(CxPoint p1, CxPoint p2, CxPoint p3)
{
//Cos(Angle) = a•b/(|a|*|b|)
double x1 = p1.X - p2.X, y1 = p1.Y - p2.Y; //向量 a
double x2 = p3.X - p2.X, y2 = p3.Y - p2.Y; //向量 b //零向量,存在共点
if (x1 == && y1 == ) return -;
if (x2 == && y2 == ) return -; double v = x1 * x2 + y1 * y2; //向量内积 a•b
double val = Math.Sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2)); //a,b模长乘积 |a|*|b|
double CosAngle = v / val; //求出来的值可能略小于 -1 或者略大于 1,此时 Angle 等于 NaN
double Angle = Math.Acos(CosAngle) * 180.0 / 3.14159265358979; //两向量夹角,0-180 if (System.Double.IsNaN(Angle))
{
if (v > ) return ;
else return ;
}
else
{
if (Angle > ) return ;
else if (Angle < ) return ;
else return Angle;
}
} /// <summary>
/// 求取面内面外判断点,p1-p2-p3为沿环方向的三个连续顶点,其中p2为角点。
/// </summary>
private static CxPoint CalculationJudgePoint(CxPoint p1, CxPoint p2, CxPoint p3, double SmallDis = 0.01)
{
double TempX = (p1.X + p3.X) / ;
double TempY = (p1.Y + p3.Y) / ; double DisX = TempX - p2.X;
double DisY = TempY - p2.Y;
double val = Math.Sqrt(DisX * DisX + DisY * DisY); double Scale = SmallDis / val; return new CxPoint(p2.X + Scale * DisX, p2.Y + Scale * DisY);
} /// <summary>
/// 判断点在线的左方还是右方,在左为 true,在线上或在右为 false
/// </summary>
private static bool JudgAbout(CxLine pLine, CxPoint pPoint)
{
double ax = pLine.ToPoint.X - pLine.FromPoint.X;
double ay = pLine.ToPoint.Y - pLine.FromPoint.Y;
double bx = pPoint.X - pLine.FromPoint.X;
double by = pPoint.Y - pLine.FromPoint.Y;
double judge = ax * by - ay * bx; if (judge > 0.0)
return true;
else
return false;
}
}
}

参考代码

 

作者:喵...鱼...喵

出处:https://www.cnblogs.com/bwuwj/

本文为作者原创,版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。如本文有误,欢迎批评指正。

C#多边形求角——实例说的更多相关文章

  1. [NetTopologySuite](2)任意多边形求交

    任意多边形求交: private void btnPolygon_Click(object sender, EventArgs e) { , , , , , , , , , , , , , }; , ...

  2. POJ 1279 Art Gallery 半平面交/多边形求核

    http://poj.org/problem?id=1279 顺时针给你一个多边形...求能看到所有点的面积...用半平面对所有边取交即可,模版题 这里的半平面交是O(n^2)的算法...比较逗比.. ...

  3. HDU - 3982:Harry Potter and J.K.Rowling(半平面交+圆与多边形求交)(WA ing)

    pro:给定一枚蛋糕,蛋糕上某个位置有个草莓,寿星在上面切了N刀,最后寿星会吃含有草莓的那一块蛋糕,问他的蛋糕占总蛋糕的面积比. sol:显然需要半平面交求含有蛋糕的那一块,然后有圆弧,不太方便求交. ...

  4. Area---poj1265(皮克定理+多边形求面积)

    题目链接:http://poj.org/problem?id=1265 题意是:有一个机器人在矩形网格中行走,起始点是(0,0),每次移动(dx,dy)的偏移量,已知,机器人走的图形是一个多边形,求这 ...

  5. 多边形求重心 HDU1115

    http://acm.hdu.edu.cn/showproblem.php?pid=1115 引用博客:https://blog.csdn.net/ysc504/article/details/881 ...

  6. hdu1115【多边形求重心模板】

    1.质量集中在顶点上.n个顶点坐标为(xi,yi),质量为mi,则重心(∑( xi×mi ) / ∑mi, ∑( yi×mi ) / ∑mi) 2.质量分布均匀.这个题就是这一类型,算法和上面的不同. ...

  7. Vivado cordic IP求模求角教程

    前言 当需要对复数求模的时候,用FPGA怎么求呢?怎么开根号? 方法1:先求幅值平方和,再使用cordic IP开根号.(蠢办法) 方法2:直接用cordic求取模值. 此处只介绍方法2,资源占用更少 ...

  8. zoj 1010 (线段相交判断+多边形求面积)

    链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=10 Area Time Limit: 2 Seconds      Mem ...

  9. poj 1654 Area (多边形求面积)

    链接:http://poj.org/problem?id=1654 Area Time Limit: 1000MS   Memory Limit: 10000K Total Submissions:  ...

随机推荐

  1. SmallLocks

    folly/SmallLocks.h This module is currently x64 only. This header defines two very small mutex types ...

  2. virtual box 桥接模式(bridge adapter)下无法获取ip(determine ip failed)的解决方法

    google出来的解决方案:创建静态ip的方法,既然虚拟机桥接模式下无法通过主机网卡获取ip桥接到网络,那么我们就创建最大网络地址,然后reboot,这样虚拟机就可以获取ip联网了. Static I ...

  3. leetcode116

    class Solution { public: void connect(TreeLinkNode *root) { if (root != NULL) { queue<TreeLinkNod ...

  4. GitHub中README.md文件的编辑和使用

    最近对它的README.md文件颇为感兴趣.便写下这贴,帮助更多的还不会编写README文件的同学们. README文件后缀名为md.md是markdown的缩写,markdown是一种编辑博客的语言 ...

  5. 有单例模式 Singleton 涉及的一些防止类被继承的东西

    c#中 : ------------------------------- 当对一个类应用 sealed 修饰符时,此修饰符会阻止其他类从该类继承. java中: ------------------ ...

  6. tomcat与jboss 01

    1. Tomcat是Apache鼎力支持的Java Web应用服务器(注:servlet容器),由于它优秀的稳定性以及丰富的文档资料,广泛的使用人群,从而在开源领域受到最广泛的青睐. 2. Jboss ...

  7. 若a与m互质,则a不影响m的完全剩余组

    [若a与m互质,则a不影响m的完全剩余组] 设t通过m的完全剩余组,若at不通过m的完全剩余组, 则会有at1=at2(mod m),即a(t1-t2)|m. 因为(a,m)=1,所以(t1-t2)| ...

  8. win bat命令后台运行控制台应用

    @echo off if "%1"=="h" goto begin start mshta vbscript:createobject()(window.clo ...

  9. oracle返回最大值一条记录

    虽然网上例子很多,但是试过多种办法都无效,原代码出处: https://blog.csdn.net/qyshibb/article/details/73332075 这个例子简单有效 select p ...

  10. ArcGIS GP服务的发布及调用

    参考https://www.jianshu.com/p/5331fa708fe5