您的位置:首页 > 其它

PCB 线路铜皮面积(残铜率)计算的实现方法

2019-05-23 03:03 971 查看

     一个多月没更新博客园了,这里继续分享关于PCB工程相关一些知识,做过PCB工程都知道用使用genesis或incam是可以非常方便的计算得到铜皮面积这个参数【下图】,但实际这个软件是通过什么算法计算出铜面积的呢,这个我们不得而知,但接下来这里介绍一种可以将【线路残铜率(铜皮面积)】计算得出来的方法.

/// <summary>
/// 【丢失精度】计算铜面积
/// </summary>
/// <param name="gS_list"></param>
/// <returns></returns>
public double s_area(List<gS> gS_list)
{
double SurfaceArea = 0;
foreach (var gS_item in gS_list)
{
foreach (var Polyline in gS_item.sur_group)
{
var sur_list = s_2gSur_Point(Polyline.sur_list);
if (Polyline.is_hole)
SurfaceArea -= s_area(sur_list);
else
SurfaceArea += s_area(sur_list);
}
}
return SurfaceArea;
}
/// <summary>
/// 【丢失精度】计算铜面积
/// </summary>
/// <param name="gSur_Point_list"></param>
/// <returns></returns>
public double s_area(List<gSur_Point> gSur_Point_list)
{
int Point_Count = gSur_Point_list.Count() - 1;
if (Point_Count < 2) return 0;
double PolylineArea = 0;
double ArcArea = 0;
for (int i = 1; i <= Point_Count; i++)
{
PolylineArea += gSur_Point_list[i - 1].p.x * gSur_Point_list[i].p.y - gSur_Point_list[i - 1].p.y * gSur_Point_list[i].p.x;
}
PolylineArea = Math.Abs(PolylineArea * 0.5);
return PolylineArea;
}
/// <summary>
/// 【精度】计算铜面积
/// </summary>
/// <param name="gSur_Point_list"></param>
/// <returns></returns>
public double s_area2(List<gSur_Point> gSur_Point_list)
{
int Point_Count = gSur_Point_list.Count() - 1;
if (Point_Count < 2) return 0;
double PolylineArea = 0;
double ArcArea = 0;
bool isCCW = s_isCCW(gSur_Point_list);
for (int i = 1; i <= Point_Count; i++)
{
if (gSur_Point_list[i].type_point > 0)
{
double a_area = a_Area(gSur_Point_list[i - 1].p, gSur_Point_list[i].p, gSur_Point_list[i + 1].p, gSur_Point_list[i].type_point == 2);
if (isCCW)
{
if (gSur_Point_list[i].type_point == 2)
ArcArea += a_area;
else
ArcArea -= a_area;
}
else
{
if (gSur_Point_list[i].type_point == 2)
ArcArea -= a_area;
else
ArcArea += a_area;
}
}
PolylineArea += gSur_Point_list[i - 1].p.x * gSur_Point_list[i].p.y - gSur_Point_list[i - 1].p.y * gSur_Point_list[i].p.x;
}
PolylineArea = Math.Abs(PolylineArea * 0.5);
PolylineArea += ArcArea;
//var isCW = s_isCW(gSur_Point_list);
//PolylineArea += (isCCW ? -ArcArea : ArcArea);
return PolylineArea;
}
/// <summary>
/// 求弧Arc 扇形面积
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public double a_Area(gPoint ps, gPoint pc, gPoint pe, bool ccw, bool islg180deg = false)
{
double r_ = p2p_di(pc, ps);
return pi * r_ * r_ * a_Angle(ps, pc, pe, ccw, islg180deg) / 360;
}
/// <summary>
/// 求弧Arc圆心角   3点    //后续改进  用叉积 与3P求角度求解  验证哪个效率高
/// </summary>
/// <param name="ps"></param>
/// <param name="pc"></param>
/// <param name="pe"></param>
/// <param name="ccw"></param>
/// <returns></returns>
public double a_Angle(gPoint ps, gPoint pc, gPoint pe, bool ccw, bool islg180deg = false)
{
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 == 360) { angle_s = 0; }
if (angle_e >= angle_s)
{
angle_sum = 360 - (angle_e - angle_s);  //360 - Math.Abs(angle_s - angle_e);
}
else
{
angle_sum = angle_s - angle_e;//Math.Abs(angle_s - angle_e);
}
if (islg180deg && angle_sum > 180)
{
angle_sum = 360 - angle_sum;
}
return angle_sum;
}
/// <summary>
/// 检测 Surface是否逆时针
/// </summary>
/// <param name="gSur_Point_list"></param>
/// <returns></returns>
public bool s_isCCW(List<gSur_Point> gSur_Point_list)
{
double d = 0;
int n = gSur_Point_list.Count() - 1;
for (int i = 0; i < n; i++)
{
if (gSur_Point_list[i].type_point > 0) continue;
int NextI = i + 1 + (gSur_Point_list[i+ 1].type_point > 0 ? 1 : 0);
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 > 0;
}
/// <summary>
/// 将gSur_Point中含弧的节点转为线
/// </summary>
/// <param name="gSur_Point_list"></param>
/// <param name="val_">此数值表示:分段数值</param>
/// <param name="type_">代表值数值类型 【0】弧长 【1】角度  【2】弦长 </param>
/// <returns></returns>
public List<gSur_Point> s_2gSur_Point(List<gSur_Point> gSur_Point_list, double val_ = 1d, int type_ = 1)
{
List<gSur_Point> resultList = new List<gSur_Point>();
if (gSur_Point_list.Count > 2)
{
bool is_flag = false;
resultList.Add(gSur_Point_list[0]);
for (int j = 1; j < gSur_Point_list.Count; j++)
{
if (is_flag)
{
is_flag = false;
continue;
}
if (gSur_Point_list[j].type_point > 0)
{
var aData = new gA(gSur_Point_list[j - 1].p, gSur_Point_list[j].p, gSur_Point_list[j + 1].p, 100, gSur_Point_list[j].type_point == 2 ? true : false);
var PlistData = a_2Plist(aData, val_, type_, true);
resultList.AddRange(PlistData.Select(tt => new gSur_Point(tt.p, 0)).ToList());
is_flag = true;
}
else
{
resultList.Add(gSur_Point_list[j]);
}
}
}
return resultList;
}
/// <summary>
/// 弧Arc 转点P组集
/// </summary>
/// <param name="a"></param>
/// <param name="val_">此数值表示:分段数值</param>
/// <param name="type_">代表值数值类型 【0】弧长 【1】角度  【2】弦长 </param>
/// <param name="is_avg">是否平均分布 </param>
/// <returns></returns>
public List<gPP> a_2Plist(gA a, double val_ = 0.1d, int type_ = 0, bool is_avg = false)
{
List<gPP> list_point = new List<gPP>();
gPP tempP;
tempP.p = a.ps;
tempP.symbols = a.symbols;
tempP.width = a.width;
list_point.Add(tempP);

double avg_count;
double angle_val = 0;
double rad_ = p2p_di(a.pc, a.pe);
double sum_alge = a_Angle(a);
if (type_ == 1)  //    【1】角度
{
angle_val = val_;
avg_count = (int)(Math.Floor(sum_alge / angle_val));  //  总角度/单角度
}
else if (type_ == 2)  //【2】弦长
{
angle_val = Math.Asin(val_ / (rad_ * 2)) * 360 / pi;
avg_count = (int)(Math.Ceiling(sum_alge / angle_val) + eps) - 1;  //  总角度/单弦长
}
else  //                【0】弧长
{
angle_val = val_ * 180 / (pi * rad_);
avg_count = (int)(Math.Ceiling(sum_alge / angle_val) + eps) - 1;  //  总角度/单角度
//avg_count = (int)(Math.Ceiling(a_Lenght(a) / val_)) - 1;  //  或  总弧长/单弧长
}
if (is_avg)
angle_val = sum_alge / avg_count;
if (avg_count > 1)
{
gPP centerP = tempP;
centerP.p = a.pc;
double angle_s = p_ang(a.pc, a.ps);
if (a.ccw) { angle_val = 0 - angle_val; }
for (int i = 1; i < avg_count; i++)
{
tempP = p_val_ang(centerP, rad_, angle_s - angle_val * i);
list_point.Add(tempP);
}
}
// if (!(zero(a.ps.x - a.pe.x) && zero(a.ps.y - a.pe.y)))
// {
//     tempP.p = a.pe;
//     list_point.Add(tempP);
// }
tempP.p = a.pe;
list_point.Add(tempP);
return list_point;
}
/// <summary>
/// 返回两点之间欧氏距离
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <returns></returns>
public double p2p_di(gPoint p1, gPoint p2)
{
return Math.Sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
/// <summary>
/// 求弧Arc圆心角       //后续改进  用叉积 与3P求角度求解  验证哪个效率高
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public double a_Angle(gA a)
{
double angle_s, angle_e, angle_sum;
if (a.ccw)
{
angle_s = p_ang(a.pc, a.pe);
angle_e = p_ang(a.pc, a.ps);
}
else
{
angle_s = p_ang(a.pc, a.ps);
angle_e = p_ang(a.pc, a.pe);
}
if (angle_s == 360) { angle_s = 0; }
if (angle_e >= angle_s)
angle_sum = 360 - Math.Abs(angle_s - angle_e);
else
angle_sum = Math.Abs(angle_s - angle_e);
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 * 180;
//象限角  转方位角   计算所属象限   并求得方位角
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 + 180;
}
else if (!(pe.x >= ps.x) && !(pe.y >= ps.y))  //↙   第三象限
{
return a_ang + 180;
}
else if (pe.x >= ps.x && !(pe.y >= ps.y))  // ↘   第四象限
{
return a_ang + 360;
}
else
{
return a_ang;
}
}
/// <summary>
/// 求增量坐标
/// </summary>
/// <param name="ps">起点</param>
/// <param name="val">增量值</param>
/// <param name="ang_direction">角度</param>
/// <returns></returns>
public gPP p_val_ang(gPP ps, double val, double ang_direction)
{
gPP pe = ps;
pe.p.x = ps.p.x + val * Math.Cos(ang_direction * Math.PI / 180);
pe.p.y = ps.p.y + val * Math.Sin(ang_direction * Math.PI / 180);
return pe;
}
View Code    3.计算铜的多边形周长函数

/// <summary>
/// 求Surface  总周长
/// </summary>
/// <param name="gS_list"></param>
/// <returns></returns>
public double s_Length(List<gS> gS_list)
{
int Surface_Count = gS_list.Count();
double SurfaceArea = 0;
foreach (var gS_item in gS_list)
{
foreach (var Polyline in gS_item.sur_group)
{
SurfaceArea += s_Length(Polyline.sur_list);
}
}
return SurfaceArea;
}
/// <summary>
/// 求Surface  总周长
/// </summary>
/// <param name="gSur_Point_list"></param>
/// <returns></returns>
public double s_Length(List<gSur_Point> gSur_Point_list)
{
double sum_lenght = 0;
bool is_flag = false;
bool ccw = false;
for (int i = 1; i < gSur_Point_list.Count; i++)
{
if (is_flag)
{
is_flag = false;
continue;
}
if (gSur_Point_list[i].type_point > 0)
{
if (gSur_Point_list[i].type_point == 2)
ccw = true;
else
ccw = false;
sum_lenght += a_Length(gSur_Point_list[i - 1].p, gSur_Point_list[i].p, gSur_Point_list[i + 1].p, ccw);
is_flag = true;
}
else
{
sum_lenght += l_Length(gSur_Point_list[i - 1].p, gSur_Point_list[i].p);
}
}
return sum_lenght;
}
/// <summary>
/// 求弧Arc长度  3点
/// </summary>
/// <param name="ps"></param>
/// <param name="pc"></param>
/// <param name="pe"></param>
/// <returns></returns>
public double a_Length(gPoint ps, gPoint pc, gPoint pe, bool ccw = false)
{
return pi / 180 * p2p_di(pc, ps) * a_Angle(ps, pc, pe, ccw);
}
/// <summary>
/// 求线Line长度  2点
/// </summary>
/// <param name="ps"></param>
/// <param name="pe"></param>
/// <returns></returns>
public double l_Length(gPoint ps, gPoint pe)
{
return Math.Sqrt((ps.x - pe.x) * (ps.x - pe.x) + (ps.y - pe.y) * (ps.y - pe.y));
}
View Code

  4.数据结构

/// <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; } = 0;
/// <summary>
/// 值
/// </summary>
public double Value { get; set; } = 0;
}
/// <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;
}
}
View Code

 

四.【铜面的多边形面积】--实现效果

     经测试,发现程序计算出来铜面积与genesis铜面积计算存在少量的偏差(猜侧奥宝为了达到越大规模铜面积计算,采用丢失精度计算铜面积达到快速计算铜面积的目的)

    小结: 采用程序计算在小规模铜面积计算,不管是计算速度还是铜面积计算精度已超过genesis.

 

 下例:genesis计算铜面积存一定偏差,实际PAD尺寸为4X3mm 面积为:12平方毫米  而genesis计算得到面积为12.004平方毫米

 

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: