PCB 铜皮(Surface)折线多边形扩大缩小实现(第一节)
继续铜皮多边形的相关的算法, 如何用代码实现多边形的扩大与缩小,这部份内容准备分为四节内容来讲解,
第一节,折线多边形的扩大缩小(不包含圆弧) 此篇讲第一节
第二节,带圆弧的多边形的扩大缩小
第三节,多边形扩大缩小----尖角处理
第四节,多边形扩大缩小----自相交处理
1.偏移点计算方法: (具体见贴的代码)
1.求出ABC三角形的角度【1】,即可求出BDP三角形角度【2】
2.通过偏移距离L与BDP角度【2】求出:点B到点D距离
3.求出ABC方位角位角
4.以点B为基准点,ABC方位角,点B到点D距离求出点D坐标
2.算法步骤:
1.获取多边形点数组 List<gSur_Point_list>
2.先检测多边形是顺时针,还是逆时针,这步必不可少,决定后面多边形计算是向内偏移还是向外偏移
这好比锣(铣)带的偏移算法,锣外形必须向外偏移,如果锣带是逆时针,那么它是Right补偿,那如果是顺时针那么它就是Left补偿;
3.遍历List<gSur_Point_list>,依次求出3点偏移后的的相交点(按上图偏移点计算方法实现)。
1.调用代码:
//获取层名为3的Surface数据
gLayer workLayerInfo = g.getFEATURES("");
var PolyListUp= calc2.s_offset(workLayerInfo.Slist, );
addCOM.line_poly(PolyListUp, );
var PolyListDown = calc2.s_offset(workLayerInfo.Slist, );
addCOM.line_poly(PolyListDown, ); //获取Profile数据
var Profile = g.getFEATURES_Profile();
var ProfileUp = calc2.s_offset(Profile.sur_list, );
addCOM.line_poly(ProfileUp, );
var ProfileDown = calc2.s_offset(Profile.sur_list, -);
addCOM.line_poly(ProfileDown, );
2. 折线多边形扩大缩小实现函数
/// <summary>
/// Surface偏移(扩大或缩小)
/// </summary>
/// <param name="gSur_Point_list"></param>
/// <param name="offset_val">偏移数值(正值加大 负值缩小)</param>
/// <returns></returns>
public List<gSur_Point> s_offset(List<gSur_Point> gSur_Point_list, double offset_val)
{
bool isCCW = s_isCCW(gSur_Point_list);
int count = gSur_Point_list.Count();
List<gSur_Point> Point_list = new List<gSur_Point>();
Point_list.Add(gSur_Point_list[]);
gPoint CurrentP = new gPoint();
for (int i = ; i < count; i++)
{
int NextIndex = (count == i + ) ? : i + ;
CurrentP = l2l_OffsetIntersect(gSur_Point_list[i - ].p, gSur_Point_list[i].p, gSur_Point_list[NextIndex].p, isCCW, offset_val);
Point_list.Add(new gSur_Point(CurrentP, gSur_Point_list[i].type_point));
}
gSur_Point_list[].p = CurrentP;
return Point_list;
}
/// <summary>
/// Surface偏移(扩大或缩小)
/// </summary>
/// <param name="gS"></param>
/// <param name="offset_val"></param>
/// <returns></returns>
public gS s_offset(gS gS, double offset_val)
{
gS SurfacePolyline = new gS();
SurfacePolyline.negative = gS.negative;
SurfacePolyline.attribut = gS.attribut;
foreach (var Polyline in gS.sur_group)
{
gSur_list sur_list = new gSur_list();
sur_list.is_ccw = Polyline.is_ccw;
sur_list.is_hole = Polyline.is_hole;
if (sur_list.is_hole)
sur_list.sur_list = s_offset(Polyline.sur_list, -offset_val);
else
sur_list.sur_list = s_offset(Polyline.sur_list, offset_val);
SurfacePolyline.sur_group.Add(sur_list);
}
return SurfacePolyline;
}
/// <summary>
/// Surface偏移(扩大或缩小)
/// </summary>
/// <param name="gS_list"></param>
/// <param name="offset_val"></param>
/// <returns></returns>
public List<gS> s_offset(List<gS> gS_list, double offset_val)
{
List<gS> surface_list = new List<gS>();
foreach (var item in gS_list)
{
surface_list.Add(s_offset(item, offset_val));
}
return surface_list;
}
/// <summary>
/// 检测 Surface是否逆时针
/// </summary>
/// <param name="gSur_Point_list"></param>
/// <returns></returns>
public bool s_isCCW(List<gSur_Point> gSur_Point_list)
{
double d = ;
int n = gSur_Point_list.Count() - ;
for (int i = ; i < n; i++)
{
if (gSur_Point_list[i].type_point > ) continue;
int NextI = i + + (gSur_Point_list[i + ].type_point > ? : );
d += -0.5 * (gSur_Point_list[NextI].p.y + gSur_Point_list[i].p.y) * (gSur_Point_list[NextI].p.x - gSur_Point_list[i].p.x);
}
return d > ;
}
/// <summary>
/// /线段与线段偏移 求交点
/// </summary>
/// <param name="ps"></param>
/// <param name="pc"></param>
/// <param name="pe"></param>
/// <param name="ccw"></param>
/// <param name="OffsetVal"></param>
/// <returns></returns>
public gPoint l2l_OffsetIntersect(gPoint ps, gPoint pc, gPoint pe, bool ccw, double OffsetVal)
{
double center_dirdction = ;
bool islg180deg = false;
double pcAng = a_Angle(ps, pc, pe, ccw, ref center_dirdction, ref islg180deg);//交点圆心角
double pcSinVal = OffsetVal / (Math.Sin(pcAng * 0.5 * Math.PI / )); //交点增量
var IntersectP = p_val_ang(pc, pcSinVal, center_dirdction);
return IntersectP;
}
/// <summary>
/// 求弧Arc圆心角 3点 //后续改进 用叉积 与3P求角度求解 验证哪个效率高
/// </summary>
/// <param name="ps"></param>
/// <param name="pc"></param>
/// <param name="pe"></param>
/// <param name="ccw"></param>
/// <param name="center_dirdction">中心方位角</param>
/// <param name="islg180deg">3点组成的内角不超180度,超出计算按反方位角计算 当值为true时 ccw值则失效了 返回值确认与P1,P2关系</param>
/// <returns></returns>
public double a_Angle(gPoint ps, gPoint pc, gPoint pe, bool ccw, ref double center_dirdction, ref bool islg180deg)
{
double angle_s, angle_e, angle_sum;
if (ccw)
{
angle_s = p_ang(pc, pe);
angle_e = p_ang(pc, ps);
}
else
{
angle_s = p_ang(pc, ps);
angle_e = p_ang(pc, pe);
}
if (angle_s == ) { angle_s = ; }
if (angle_e >= angle_s)
{
angle_sum = - (angle_e - angle_s);
center_dirdction = (angle_s + angle_e) * 0.5 + ;
}
else
{
angle_sum = angle_s - angle_e;
center_dirdction = (angle_s + angle_e) * 0.5;
}
if (islg180deg) //
{
if (angle_sum > )
{
angle_sum = - angle_sum;
center_dirdction = p_ang_invert(center_dirdction);
if (angle_e >= angle_s)
islg180deg = !(angle_e >= angle_s);
else
islg180deg = (angle_e >= angle_s);
}
else
{
//islg180deg = (angle_e >= angle_s); //例1 PS 30 PE 330 true 例2 PS 80 PE 30 false
if (angle_e >= angle_s)
islg180deg = (angle_e >= angle_s);
else
islg180deg = !(angle_e >= angle_s);
}
}
else
{
if (center_dirdction > ) { center_dirdction = center_dirdction - ; } }
return angle_sum;
}
/// <summary>
/// 求方位角
/// </summary>
/// <param name="ps"></param>
/// <param name="pe"></param>
/// <returns></returns>
public double p_ang(gPoint ps, gPoint pe)
{
double a_ang = Math.Atan((pe.y - ps.y) / (pe.x - ps.x)) / Math.PI * ;
//象限角 转方位角 计算所属象限 并求得方位角
if (pe.x >= ps.x && pe.y >= ps.y) //↗ 第一象限
{
return a_ang;
}
else if (!(pe.x >= ps.x) && pe.y >= ps.y) // ↖ 第二象限
{
return a_ang + ;
}
else if (!(pe.x >= ps.x) && !(pe.y >= ps.y)) //↙ 第三象限
{
return a_ang + ;
}
else if (pe.x >= ps.x && !(pe.y >= ps.y)) // ↘ 第四象限
{
return a_ang + ;
}
else
{
return a_ang;
}
}
/// <summary>
/// 求反方位角
/// </summary>
/// <param name="ang_direction"></param>
/// <returns></returns>
public double p_ang_invert(double ang_direction)//求反方位角
{
if (ang_direction >= )
return ang_direction - ;
else
return ang_direction + ;
}
/// <summary>
/// 求增量坐标
/// </summary>
/// <param name="ps">起点</param>
/// <param name="val">增量值</param>
/// <param name="ang_direction">角度</param>
/// <returns></returns>
public gPoint p_val_ang(gPoint ps, double val, double ang_direction)
{
gPoint pe;
pe.x = ps.x + val * Math.Cos(ang_direction * Math.PI / );
pe.y = ps.y + val * Math.Sin(ang_direction * Math.PI / );
return pe;
}
3.数据结构
/// <summary>
/// Surface 坐标泛型集类1
/// </summary>
public class gSur_Point
{
public gSur_Point()
{ }
public gSur_Point(double x_val, double y_val, byte type_point_)
{
this.p.x = x_val;
this.p.y = y_val;
this.type_point = type_point_;
}
public gSur_Point(gPoint p, byte type_point_)
{
this.p = p;
this.type_point = type_point_;
}
public gPoint p;
/// <summary>
/// 0为折点 1为顺时针 2为逆时针
/// </summary>
public byte type_point { get; set; } = ;
/// <summary>
/// 值
/// </summary>
public double Value { get; set; } = ;
}
/// <summary>
/// Surface 坐标泛型集类2
/// </summary>
public class gSur_list
{
public List<gSur_Point> sur_list = new List<gSur_Point>();
/// <summary>
/// 是否为空洞
/// </summary>
public bool is_hole { get; set; }
/// <summary>
/// 是否逆时针
/// </summary>
public bool is_ccw { get; set; }
}
/// <summary>
/// Surface 坐标泛型集类3
/// </summary>
public class gS
{
public List<gSur_list> sur_group = new List<gSur_list>();
/// <summary>
/// 是否为负 polarity-- P N
/// </summary>
public bool negative { get; set; }
public string attribut { get; set; }
}
/// <summary>
/// 点 数据类型 (XY)
/// </summary>
public struct gPoint
{
public gPoint(gPoint p_)
{
this.x = p_.x;
this.y = p_.y;
}
public gPoint(double x_val, double y_val)
{
this.x = x_val;
this.y = y_val;
}
public double x;
public double y;
public static gPoint operator +(gPoint p1, gPoint p2)
{
p1.x += p2.x;
p1.y += p2.y;
return p1;
}
public static gPoint operator -(gPoint p1, gPoint p2)
{
p1.x -= p2.x;
p1.y -= p2.y;
return p1;
}
}
/// <summary>
/// ARC 数据类型
/// </summary>
public struct gA
{
public gA(double ps_x, double ps_y, double pc_x, double pc_y, double pe_x, double pe_y, double width_, bool ccw_)
{
this.ps = new gPoint(ps_x, ps_y);
this.pc = new gPoint(pc_x, pc_y);
this.pe = new gPoint(pe_x, pe_y);
this.negative = false;
this.ccw = ccw_;
this.symbols = "r" + width_.ToString();
this.attribut = string.Empty;
this.width = width_;
}
public gA(gPoint ps_, gPoint pc_, gPoint pe_, double width_, bool ccw_ = false)
{
this.ps = ps_;
this.pc = pc_;
this.pe = pe_;
this.negative = false;
this.ccw = ccw_;
this.symbols = "r" + width_.ToString();
this.attribut = string.Empty;
this.width = width_;
}
public gPoint ps;
public gPoint pe;
public gPoint pc;
public bool negative;//polarity-- positive negative
public bool ccw; //direction-- cw ccw
public string symbols;
public string attribut;
public double width;
public static gA operator +(gA arc1, gPoint move_p)
{
arc1.ps += move_p;
arc1.pe += move_p;
arc1.pc += move_p;
return arc1;
}
public static gA operator +(gA arc1, gPP move_p)
{
arc1.ps += move_p.p;
arc1.pe += move_p.p;
arc1.pc += move_p.p;
return arc1;
}
public static gA operator +(gA arc1, gP move_p)
{
arc1.ps += move_p.p;
arc1.pe += move_p.p;
arc1.pc += move_p.p;
return arc1;
}
public static gA operator -(gA arc1, gPoint move_p)
{
arc1.ps -= move_p;
arc1.pe -= move_p;
arc1.pc -= move_p;
return arc1;
}
public static gA operator -(gA arc1, gPP move_p)
{
arc1.ps -= move_p.p;
arc1.pe -= move_p.p;
arc1.pc -= move_p.p;
return arc1;
}
public static gA operator -(gA arc1, gP move_p)
{
arc1.ps -= move_p.p;
arc1.pe -= move_p.p;
arc1.pc -= move_p.p;
return arc1;
}
}
PCB 铜皮(Surface)折线多边形扩大缩小实现(第一节)的更多相关文章
- 【高德地图API】从零开始学高德JS API(三)覆盖物——标注|折线|多边形|信息窗口|聚合marker|麻点图|图片覆盖物
原文:[高德地图API]从零开始学高德JS API(三)覆盖物——标注|折线|多边形|信息窗口|聚合marker|麻点图|图片覆盖物 摘要:覆盖物,是一张地图的灵魂.有覆盖物的地图,才是完整的地图.在 ...
- 【高德地图API】从头德国高中生JS API(三)覆盖物——大喊|折线|多边形|信息表|聚合marker|点蚀图|照片覆盖
覆盖物,是一张地图的灵魂.有覆盖物的地图.才是完整的地图.在一张地图上,除了底层的底图(瓦片图,矢量图),控件(有功能可操作的工具).最重要最必不可少的就是覆盖物了.覆盖物有多种.包含.标注.折线.多 ...
- PCB 线路铜皮面积(残铜率)计算的实现方法
一个多月没更新博客园了,这里继续分享关于PCB工程相关一些知识,做过PCB工程都知道用使用genesis或incam是可以非常方便的计算得到铜皮面积这个参数[下图],但实际这个软件是通过什么算法计算出 ...
- 用Altium Designer16 绘制STM32开发板PCB 笔记
第一部分 Altium designer 软件概括 一.安装:要安装英文版,只安装pcb design和importers/exporters 二.设置:dxp-preferences我们关心的是sy ...
- Protel99se教程九:protel99se中PCB设计的高级应用
在上一节我们PCB资源网的protel99se教程当中,我们给大家讲解了在protel99se进行原理图设计中的一些高级应用技巧,在这一节protel99se教程当中,我们将给大家讲解的是,在prot ...
- 「Android」 Surface分析
本篇针对Surface模块进行分析,从Java层的Activity创建开始,到ViewRoot.WindowsManagerService,再到JNI层和Native层. 首先推荐一个Android源 ...
- [深入理解Android卷一全文-第八章]深入理解Surface系统
由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版.而知识的传播不应该由于纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. ...
- Android4.0 Surface机制分析
1. java层面的Surface 对于Surface我们的认识主要是android的类Surface, android的文档描述Surface是"Handle onto a raw ...
- imageView图片放大缩小及旋转
imageView图片放大缩小及旋转 一.简介 二.方法 1)设置图片放大缩小效果 第一步:将<ImageView>标签中的android:scaleType设置为"fitCen ...
随机推荐
- Gearman 初窥【转载】
Gearman是一个分发任务的程序框架,可以用在各种场合,与Hadoop相 比,Gearman更偏向于任务分发功能.它的任务分布非常简单,简单得可以只需要用脚本即可完成.Gearman最初用于Live ...
- python学习之-- redis模块基本介绍
数据缓存系统: 1:mongodb:是直接持久化,直接存储于硬盘的缓存系统 2:redis: 半持久化,存储于内存和硬盘 3:memcache:数据只能存储在内存里的缓存系统关于memcache 学习 ...
- CSY版最大团,速度快一倍
#include <bits/stdc++.h> using namespace std; #define REP(i, n) for(int i(0); i < (n); ++i) ...
- spring boot + redis 实现session共享
这次带来的是spring boot + redis 实现session共享的教程. 在spring boot的文档中,告诉我们添加@EnableRedisHttpSession来开启spring se ...
- JavaOne Online Hands-on Labs
http://www.oracle.com/technetwork/java/index-156938.html
- JavaScript 中 for 循环
在ECMAScript5(简称 ES5)中,有三种 for 循环,分别是: 简单for循环 for-in forEach 在2015年6月份发布的ECMAScript6(简称 ES6)中,新增了一种循 ...
- IntelliJ IDEA 基本配置入门
前言:今天下载安装IntelliJ IDEA.随手创建了一个项目,运行Build提示错误. 与大多数用于开发JAVA的IDE类似,不做不论什么配置.编译是不会成功的.因此我尝试对IDEA的配置进行了一 ...
- [RxJS] Chain RxJS Operators Together with a Custom `pipe` Function using Array.reduce
Instead of writing complex operators, it's usually best to write simple, single-purpose operators th ...
- js 判断对象中所有属性是否为空
测试: var obj = {a:"123",b:""}; for(var key in obj){ if(!obj[key]) return; } 函数封装: ...
- Jenkins系列之-—03 修改Jenkins用户的密码
一.Jenkins修改用户密码 Jenkins用户的数据存放在JENKINS_HOME/users目录. 1. 打开忘记密码的用户文件夹,里面就一个文件config.xml.打开并找到<pass ...