当前位置: 首页 > news >正文

c# 线向量生成多边形_C#多边形求角——实例说

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

看左下图,这是一个多环的多边形,一个外环(内部为多边形内部区域),一个内环(外部为多边形内部区域),同时多边形中任意一个角不等于零角(等于 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;

}

///

/// 计算三点角度,p1-p2-p3为沿环方向的三个连续顶点,其中p2为角点。计算结果范围 0° - 180°,-1为无效值

///

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

//零向量,存在共点

&& y1 == ) ;

&& y2 == ) ;

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))

{

) ;

;

}

else

{

) ;

) ;

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点的 B'点(称为面内面外判断点),其中 BB'的距离很小很小(若直接以 M 点作为面内面外判断点,由于存在多环的情况,会出现问题)。若 B' 在多边形内,则待求角为劣角,内积计算夹角即为结果,若 B' 在多边形外,即出现 ∠DEF 这种情况(此时 B' 是 E'),则需要求内积计算夹角的组角作为计算结果。

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

///

/// 求取面内面外判断点,p1-p2-p3为沿环方向的三个连续顶点,其中p2为角点。

///

private static CxPoint CalculationJudgePoint(CxPoint p1, CxPoint p2, CxPoint p3, double SmallDis = 0.01)

{

;

;

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;

}

///

/// 判断点在线的左方还是右方,在左为 true,在线上或在右为 false

///

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 类。

///

/// 调用示例: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);

///

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;

}

///

/// 角度计算主方法,p1-p2-p3为沿环方向的三个连续顶点,其中p2为角点。

///

/// p1-p2-p3所在环方向,顺时针为 true,逆时针为 false

public static double Analysis(CxPoint p1, CxPoint p2, CxPoint p3, bool IsClockwise)

{

double Angle = CalculationAngle(p1, p2, p3);

) return Angle;

CxPoint JudgePoint = CalculationJudgePoint(p1, p2, p3);

CxLine ReferenceLine = new CxLine(p2, p3);

bool IsLeft = JudgAbout(ReferenceLine, JudgePoint);

- Angle;

return Angle;

}

///

/// 计算三点角度,p1-p2-p3为沿环方向的三个连续顶点,其中p2为角点。计算结果范围 0° - 180°,-1为无效值

///

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

//零向量,存在共点

&& y1 == ) ;

&& y2 == ) ;

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))

{

) ;

;

}

else

{

) ;

) ;

else return Angle;

}

}

///

/// 求取面内面外判断点,p1-p2-p3为沿环方向的三个连续顶点,其中p2为角点。

///

private static CxPoint CalculationJudgePoint(CxPoint p1, CxPoint p2, CxPoint p3, double SmallDis = 0.01)

{

;

;

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);

}

///

/// 判断点在线的左方还是右方,在左为 true,在线上或在右为 false

///

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;

}

}

}

参考代码

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

相关文章:

  • 《疯狂的程序员》序
  • 多元线性回归算法python实现_机器学习算法python实现-线性回归
  • 正确的座机号码格式_简历里的手机号及座机号的标准写法是什么?正确书写才更可能求职成功!...
  • 《走出软件作坊》序
  • python dict key顺序_python的dict中dict.keys()和dict.values()的提取是否遵守某种固定顺序?...
  • find linux 目录深度_linux find 命令查找文件和文件夹
  • 国际商务英语学习[十五]
  • redis统计用户日活量_使用redis统计用户日活、月活(实践版)
  • SerialPort类连接串口
  • labiview ni python_高效全能架构大前端精品教程python学习网站 百度云 百度网盘下载...
  • 更趋实用的Amazon Web Services
  • python iphone 爬虫_Python爬虫实战之抓取京东苹果手机评价!
  • 串口数据通信程序实现(SerialPort类方法)
  • python的win32模块_python模块:win32com用法详解
  • Compile syslog-win32
  • 【Linux系统编程】快速查找errno错误码信息
  • 07.Android之多媒体问题
  • C++类的相互关联
  • CSS实用技巧
  • gf框架之分页模块(五) - 自定义分页
  • GitUp, 你不可错过的秀外慧中的git工具
  • IP路由与转发
  • MySQL的数据类型
  • Object.assign方法不能实现深复制
  • Otto开发初探——微服务依赖管理新利器
  • 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  • 服务器之间,相同帐号,实现免密钥登录
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 解决iview多表头动态更改列元素发生的错误
  • 京东美团研发面经
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 人脸识别最新开发经验demo
  • 收藏好这篇,别再只说“数据劫持”了
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 以太坊客户端Geth命令参数详解
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • Spring Batch JSON 支持
  • 选择阿里云数据库HBase版十大理由
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • (C语言)二分查找 超详细
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (转)Sublime Text3配置Lua运行环境
  • (转)VC++中ondraw在什么时候调用的
  • (转)用.Net的File控件上传文件的解决方案
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • * 论文笔记 【Wide Deep Learning for Recommender Systems】
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .net开发引用程序集提示没有强名称的解决办法
  • .NET值类型变量“活”在哪?
  • @Data注解的作用