/*
* hyp2mat - convert hyperlynx files to matlab scripts
* Copyright 2012 Koen De Vleeschauwer.
*
* This file is part of hyp2mat.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*
* Uses the Clipper library from Angus Johnson to do polygon arithmetic
*/
#include
#include
#include
#include "hypfile.h"
using namespace std;
using namespace HypFile;
/*
* Polygon constructor
*/
Polygon::Polygon()
{
vertex.clear();
polygon_type = POLYGON_TYPE_COPPER;
id = -1;
positive = true; /* is a polygon, not a hole */
width = 0.0;
plane_separation = -1.0; /* negative if not set */
left_plane_separation = -1.0; /* negative if not set */
net_name.clear();
layer_name.clear();
}
/*
* Draw an arc from (x1, y1) to (x2, y2) with center (xc, yc) and radius r using a polygonal approximation.
* Arc may be clockwise or counterclockwise.
*/
Polygon HypFile::Hyp::arc2poly(double x1, double y1, double x2, double y2, double xc, double yc, double r, bool clockwise)
{
Polygon arc_segment;
arc_segment.width = 0;
arc_segment.positive = true;
arc_segment.layer_name = "";
int poly_points = 0;
double alpha = atan2(y1-yc, x1-xc);
double beta = atan2(y2-yc, x2-xc);
if (clockwise) {
// draw arc clockwise from (x1,y1) to (x2,y2)
if (alpha < beta)
alpha = alpha + 2 * pi;
}
else {
// draw arc counterclockwise from (x1,y1) to (x2,y2)
if (beta < alpha)
beta = beta + 2 * pi;
// draw a circle if starting and end points are the same
if ((x1 == x2) && (y1 == y2))
beta = alpha + 2 * pi;
}
// Calculate number of segments needed for a full circle.
int segments = min_circle_segments;
if (arc_precision > 0) {
// Increase number of segments until difference between circular arc
// and polygonal approximation is less than max_arc_error
double arc_error;
do {
arc_error = r * (1 - cos(pi/segments));
if (arc_error > arc_precision) segments += 4;
}
while (arc_error > arc_precision);
}
else if (arc_precision < 0)
error("error: negative arc precision");
// A full circle is drawn using 'segments' segments; a 90 degree arc using segments/4.
poly_points = round(segments*abs(beta-alpha)/(2*pi));
// Sanity checks
if (poly_points < 1) poly_points = 1;
// first point
arc_segment.vertex.push_back(Point(x1, y1));
// intermediate points
for (int i = 1; i < poly_points ; i++) {
double angle = alpha + (beta - alpha) * i / poly_points;
double x = xc + r * cos(angle);
double y = yc + r * sin(angle);
arc_segment.vertex.push_back(Point(x, y));
}
// last point
arc_segment.vertex.push_back(Point(x2, y2));
return arc_segment;
}
/*
* Draws straight metal trace segment as a polygon.
*/
Polygon HypFile::Hyp::segment2poly(double x1, double y1, double x2, double y2, double width)
{
/*
* Note a hyperlynx line segment is drawn, ending in two half-circles:
* ------------------------------- ^
* / \ |
* | | width
* \ / |
* ------------------------------- V
*
* <----------- length ---------->
*/
Polygon line_segment;
line_segment.width = 0;
line_segment.positive = true;
line_segment.layer_name = "";
/* Sanity check */
double dx;
double dy;
if (( x1 == x2) && (y2 == y1)) {
dx = width/2;
dy = 0;
}
else {
double length = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
dx = width / 2 * (y2 - y1) / length;
dy = width / 2 * (x2 - x1) / length;
}
Polygon arc2 = arc2poly(x2 + dx, y2 - dy, x2 - dx, y2 + dy, x2, y2, width/2, false);
Polygon arc1 = arc2poly(x1 - dx, y1 + dy, x1 + dx, y1 - dy, x1, y1, width/2, false);
line_segment.vertex = arc2.vertex;
line_segment.vertex.insert(line_segment.vertex.end(), arc1.vertex.begin(), arc1.vertex.end());
return line_segment;
}
/*
* Convert a pad to a polygon
*/
Polygon HypFile::Hyp::pad2poly(double pad_x, double pad_y, Pad pad)
{
Polygon pad_poly;
pad_poly.width = 0;
pad_poly.layer_name = pad.layer_name;
pad_poly.positive = true;
if (pad.pad_type == PAD_TYPE_ANTIPAD) pad_poly.polygon_type = POLYGON_TYPE_ANTIPAD;
else pad_poly.polygon_type = POLYGON_TYPE_PAD;
double sx = pad.pad_sx;
double sy = pad.pad_sy;
double angle = pad.pad_angle;
if (fmod(angle, 180) == 0) angle = 0;
else if (fmod(angle, 90) == 0) {
double t;
t = sx;
sx = sy;
sy = t;
angle = 0;
}
/*
* Circular and oval pads
*/
if (pad.pad_shape == PAD_SHAPE_OVAL) {
/* calculate number of segments needed */
int segments = min_circle_segments;
if (arc_precision > 0) {
/* Increase number of segments until difference between oval
and polygonal approximation is less than max_arc_error */
double arc_error_x;
double arc_error_y;
do {
arc_error_x = sx / 2 * (1 - cos(pi/segments));
arc_error_y = sy / 2 * (1 - cos(pi/segments));
if ((arc_error_x > arc_precision) || (arc_error_y > arc_precision)) segments += 4;
}
while ((arc_error_x > arc_precision) || (arc_error_y > arc_precision));
}
/* calculate points */
for (int i = 0; i < segments; i++) {
double alpha = 2 * pi * i / segments;
double x = sx / 2 / cos(pi / segments) * cos(alpha);
double y = sy / 2 / cos(pi / segments) * sin(alpha);
pad_poly.vertex.push_back(Point(x, y));
}
}
/*
* Square and rectangular pads
*/
else if (pad.pad_shape == PAD_SHAPE_RECTANGULAR) {
pad_poly.vertex.push_back(Point( sx/2, sy/2));
pad_poly.vertex.push_back(Point( sx/2, -sy/2));
pad_poly.vertex.push_back(Point(-sx/2, -sy/2));
pad_poly.vertex.push_back(Point(-sx/2, sy/2));
}
/*
* Oblong pads
*/
else if (pad.pad_shape == PAD_SHAPE_OBLONG) {
if (sx > sy) {
/* horizontal oblong pad */
double x1 = (sx - sy)/2;
double y1 = -sy/2;
double x2 = x1;
double y2 = -y1;
double xc = (sx - sy)/2;
double yc = 0;
double r = sy/2;
Polygon arc_right = arc2poly(x1, y1, x2, y2, xc, yc, r, false);
Polygon arc_left = arc2poly(-x1, -y1, -x2, -y2, -xc, -yc, r, false);
pad_poly.vertex = arc_right.vertex;
pad_poly.vertex.insert(pad_poly.vertex.end(), arc_left.vertex.begin(), arc_left.vertex.end());
}
else {
/* vertical oblong pad */
double x1 = sx/2;
double y1 = (sy - sx)/2;
double x2 = -x1;
double y2 = y1;
double xc = 0;
double yc = (sy-sx)/2;
double r = sx/2;
Polygon arc_top = arc2poly(x1, y1, x2, y2, xc, yc, r, false);
Polygon arc_bottom = arc2poly(-x1, -y1, -x2, -y2, -xc, -yc, r, false);
pad_poly.vertex = arc_top.vertex;
pad_poly.vertex.insert(pad_poly.vertex.end(), arc_bottom.vertex.begin(), arc_bottom.vertex.end());
}
}
/*
* Unknown pad shape
*/
else error("unknown pad shape");
/* Rotate polygon over pad rotation angle */
if (angle != 0) {
double cosa = cos(angle * pi / 180);
double sina = sin(angle * pi / 180);
for (PointList::iterator i = pad_poly.vertex.begin(); i != pad_poly.vertex.end(); ++i) {
double x_rot = cosa * i->x - sina * i->y ;
double y_rot = sina * i->x + cosa * i->y ;
i->x = x_rot;
i->y = y_rot;
}
}
/* Translate polygon to pad center */
for (PointList::iterator i = pad_poly.vertex.begin(); i != pad_poly.vertex.end(); ++i) {
i->x += pad_x;
i->y += pad_y;
}
/* return pad converted to polygon */
return pad_poly;
}
/* not truncated */