Commit e126042b authored by raburton's avatar raburton

set eol-style native on new files

parent 5eda8a52
// PolyLine.cpp ... implementation of CPolyLine class
// from FreePCB.
// Adaptation for kicad
//
using namespace std;
#include <math.h>
#include <vector>
#include "PolyLine.h"
#define to_int(x) (int)round((x))
/* Stuff to compile PolyLine.cpp, used in std::vector as CArray. does not work. must be redesigned, only for test */
#define SetSize reserve
#define pi 3.14159265359
#define DENOM 10 // to use mils for php clipping
//#define DENOM 1 // to use internal units for php clipping
// dl is a pointer to CDisplayList for drawing graphic elements
// if dl = NULL, doesn't draw anything but can still hold data
//
CPolyLine::CPolyLine( CDisplayList * dl )
{
m_dlist = dl;
m_HatchStyle = 0;
m_sel_box = 0;
m_gpc_poly = new gpc_polygon;
m_gpc_poly->num_contours = 0;
m_php_poly = new polygon;
}
CPolyLine::CPolyLine()
{
m_dlist = NULL;
m_HatchStyle = 0;
m_sel_box = 0;
m_gpc_poly = new gpc_polygon;
m_gpc_poly->num_contours = 0;
m_php_poly = new polygon;
}
// destructor, removes display elements
//
CPolyLine::~CPolyLine()
{
Undraw();
FreeGpcPoly();
delete m_gpc_poly;
delete m_php_poly;
}
// Use the General Polygon Clipping Library to clip contours
// If this results in new polygons, return them as std::vector p
// If bRetainArcs == TRUE, try to retain arcs in polys
// Returns number of external contours, or -1 if error
//
int CPolyLine::NormalizeWithGpc( std::vector<CPolyLine*> * pa, BOOL bRetainArcs )
{
std::vector<CArc> arc_array;
if( bRetainArcs )
MakeGpcPoly( -1, &arc_array );
else
MakeGpcPoly( -1, NULL );
Undraw();
// now, recreate poly
// first, find outside contours and create new CPolyLines if necessary
int n_ext_cont = 0;
for( int ic=0; ic<m_gpc_poly->num_contours; ic++ )
{
if( !(m_gpc_poly->hole)[ic] )
{
if( n_ext_cont == 0 )
{
// first external contour, replace this poly
corner.clear();
side_style.clear();
for( int i=0; i<m_gpc_poly->contour[ic].num_vertices; i++ )
{
int x = to_int(((m_gpc_poly->contour)[ic].vertex)[i].x);
int y = to_int(((m_gpc_poly->contour)[ic].vertex)[i].y);
if( i==0 )
Start( m_layer, m_Width, m_sel_box, x, y, m_HatchStyle );
else
AppendCorner( x, y, STRAIGHT, FALSE );
}
Close();
n_ext_cont++;
}
else if( pa )
{
// next external contour, create new poly
CPolyLine * poly = new CPolyLine;
pa->SetSize(n_ext_cont); // put in array
(*pa)[n_ext_cont-1] = poly;
for( int i=0; i<m_gpc_poly->contour[ic].num_vertices; i++ )
{
int x = to_int(((m_gpc_poly->contour)[ic].vertex)[i].x);
int y = to_int(((m_gpc_poly->contour)[ic].vertex)[i].y);
if( i==0 )
poly->Start( m_layer, m_Width, m_sel_box, x, y, m_HatchStyle );
else
poly->AppendCorner( x, y, STRAIGHT, FALSE );
}
poly->Close( STRAIGHT, FALSE );
n_ext_cont++;
}
}
}
// now add cutouts to the CPolyLine(s)
for( int ic=0; ic<m_gpc_poly->num_contours; ic++ )
{
if( (m_gpc_poly->hole)[ic] )
{
CPolyLine * ext_poly = NULL;
if( n_ext_cont == 1 )
{
ext_poly = this;
}
else
{
// find the polygon that contains this hole
for( int i=0; i<m_gpc_poly->contour[ic].num_vertices; i++ )
{
int x = to_int(((m_gpc_poly->contour)[ic].vertex)[i].x);
int y = to_int(((m_gpc_poly->contour)[ic].vertex)[i].y);
if( TestPointInside( x, y ) )
ext_poly = this;
else
{
for( int ext_ic=0; ext_ic<n_ext_cont-1; ext_ic++ )
{
if( (*pa)[ext_ic]->TestPointInside( x, y ) )
{
ext_poly = (*pa)[ext_ic];
break;
}
}
}
if( ext_poly )
break;
}
}
if( !ext_poly )
ASSERT(0);
for( int i=0; i<m_gpc_poly->contour[ic].num_vertices; i++ )
{
int x = to_int(((m_gpc_poly->contour)[ic].vertex)[i].x);
int y = to_int(((m_gpc_poly->contour)[ic].vertex)[i].y);
ext_poly->AppendCorner( x, y, STRAIGHT, FALSE );
}
ext_poly->Close( STRAIGHT, FALSE );
}
}
if( bRetainArcs )
RestoreArcs( &arc_array, pa );
FreeGpcPoly();
return n_ext_cont;
}
// make a php_polygon from first contour
int CPolyLine::MakePhpPoly()
{
FreePhpPoly();
polygon test_poly;
int nv = GetContourEnd(0);
for( int iv=0; iv<=nv; iv++ )
{
int x = GetX(iv)/DENOM;
int y = GetY(iv)/DENOM;
m_php_poly->addv( x, y );
}
return 0;
}
void CPolyLine::FreePhpPoly()
{
// delete all vertices
while( m_php_poly->m_cnt > 1 )
{
vertex * fv = m_php_poly->getFirst();
m_php_poly->del( fv->m_nextV );
}
delete m_php_poly->m_first;
m_php_poly->m_first = NULL;
m_php_poly->m_cnt = 0;
}
// Use the php clipping lib to clip this poly against poly
//
void CPolyLine::ClipPhpPolygon( int php_op, CPolyLine * poly )
{
Undraw();
poly->MakePhpPoly();
MakePhpPoly();
polygon * p = m_php_poly->boolean( poly->m_php_poly, php_op );
poly->FreePhpPoly();
FreePhpPoly();
if( p )
{
// now screw with the PolyLine
corner.clear();
side_style.clear();
do
{
vertex * v = p->getFirst();
Start( m_layer, m_Width, m_sel_box,
to_int(v->X()*DENOM),
to_int(v->Y()*DENOM),
m_HatchStyle );
do
{
vertex * n = v->Next();
AppendCorner( to_int(v->X()*DENOM), to_int((v->Y()*DENOM )) );
v = n;
}
while( v->id() != p->getFirst()->id() );
Close();
// p = p->NextPoly();
delete p;
p = NULL;
}
while( p );
}
Draw();
}
// make a gpc_polygon for a closed polyline contour
// approximates arcs with multiple straight-line segments
// if icontour = -1, make polygon with all contours,
// combining intersecting contours if possible
// returns data on arcs in arc_array
//
int CPolyLine::MakeGpcPoly( int icontour, std::vector<CArc> * arc_array )
{
if( m_gpc_poly->num_contours )
FreeGpcPoly();
if( !GetClosed() && (icontour == (GetNumContours()-1) || icontour == -1))
return 1; // error
// initialize m_gpc_poly
m_gpc_poly->num_contours = 0;
m_gpc_poly->hole = NULL;
m_gpc_poly->contour = NULL;
int n_arcs = 0;
int first_contour = icontour;
int last_contour = icontour;
if( icontour == -1 )
{
first_contour = 0;
last_contour = GetNumContours() - 1;
}
if( arc_array )
arc_array->SetSize(0);
int iarc = 0;
for( int icont=first_contour; icont<=last_contour; icont++ )
{
// make gpc_polygon for this contour
gpc_polygon * gpc = new gpc_polygon;
gpc->num_contours = 0;
gpc->hole = NULL;
gpc->contour = NULL;
// first, calculate number of vertices in contour
int n_vertices = 0;
int ic_st = GetContourStart(icont);
int ic_end = GetContourEnd(icont);
for( int ic=ic_st; ic<=ic_end; ic++ )
{
int style = side_style[ic];
int x1 = corner[ic].x;
int y1 = corner[ic].y;
int x2, y2;
if( ic < ic_end )
{
x2 = corner[ic+1].x;
y2 = corner[ic+1].y;
}
else
{
x2 = corner[ic_st].x;
y2 = corner[ic_st].y;
}
if( style == STRAIGHT )
n_vertices++;
else
{
// style is ARC_CW or ARC_CCW
int n; // number of steps for arcs
n = (abs(x2-x1)+abs(y2-y1))/(CArc::MAX_STEP);
n = max( n, CArc::MIN_STEPS ); // or at most 5 degrees of arc
n_vertices += n;
n_arcs++;
}
}
// now create gcp_vertex_list for this contour
gpc_vertex_list * g_v_list = new gpc_vertex_list;
g_v_list->vertex = (gpc_vertex*)calloc( sizeof(gpc_vertex), n_vertices );
g_v_list->num_vertices = n_vertices;
int ivtx = 0;
for( int ic=ic_st; ic<=ic_end; ic++ )
{
int style = side_style[ic];
int x1 = corner[ic].x;
int y1 = corner[ic].y;
int x2, y2;
if( ic < ic_end )
{
x2 = corner[ic+1].x;
y2 = corner[ic+1].y;
}
else
{
x2 = corner[ic_st].x;
y2 = corner[ic_st].y;
}
if( style == STRAIGHT )
{
g_v_list->vertex[ivtx].x = x1;
g_v_list->vertex[ivtx].y = y1;
ivtx++;
}
else
{
// style is arc_cw or arc_ccw
int n; // number of steps for arcs
n = (abs(x2-x1)+abs(y2-y1))/(CArc::MAX_STEP);
n = max( n, CArc::MIN_STEPS ); // or at most 5 degrees of arc
double xo, yo, theta1, theta2, a, b;
a = fabs( (double)(x1 - x2) );
b = fabs( (double)(y1 - y2) );
if( style == CPolyLine::ARC_CW )
{
// clockwise arc (ie.quadrant of ellipse)
if( x2 > x1 && y2 > y1 )
{
// first quadrant, draw second quadrant of ellipse
xo = x2;
yo = y1;
theta1 = pi;
theta2 = pi/2.0;
}
else if( x2 < x1 && y2 > y1 )
{
// second quadrant, draw third quadrant of ellipse
xo = x1;
yo = y2;
theta1 = 3.0*pi/2.0;
theta2 = pi;
}
else if( x2 < x1 && y2 < y1 )
{
// third quadrant, draw fourth quadrant of ellipse
xo = x2;
yo = y1;
theta1 = 2.0*pi;
theta2 = 3.0*pi/2.0;
}
else
{
xo = x1; // fourth quadrant, draw first quadrant of ellipse
yo = y2;
theta1 = pi/2.0;
theta2 = 0.0;
}
}
else
{
// counter-clockwise arc
if( x2 > x1 && y2 > y1 )
{
xo = x1; // first quadrant, draw fourth quadrant of ellipse
yo = y2;
theta1 = 3.0*pi/2.0;
theta2 = 2.0*pi;
}
else if( x2 < x1 && y2 > y1 )
{
xo = x2; // second quadrant
yo = y1;
theta1 = 0.0;
theta2 = pi/2.0;
}
else if( x2 < x1 && y2 < y1 )
{
xo = x1; // third quadrant
yo = y2;
theta1 = pi/2.0;
theta2 = pi;
}
else
{
xo = x2; // fourth quadrant
yo = y1;
theta1 = pi;
theta2 = 3.0*pi/2.0;
}
}
// now write steps for arc
if( arc_array )
{
arc_array->SetSize(iarc+1);
(*arc_array)[iarc].style = style;
(*arc_array)[iarc].n_steps = n;
(*arc_array)[iarc].xi = x1;
(*arc_array)[iarc].yi = y1;
(*arc_array)[iarc].xf = x2;
(*arc_array)[iarc].yf = y2;
iarc++;
}
for( int is=0; is<n; is++ )
{
double theta = theta1 + ((theta2-theta1)*(double)is)/n;
double x = xo + a*cos(theta);
double y = yo + b*sin(theta);
if( is == 0 )
{
x = x1;
y = y1;
}
g_v_list->vertex[ivtx].x = x;
g_v_list->vertex[ivtx].y = y;
ivtx++;
}
}
}
if( n_vertices != ivtx )
ASSERT(0);
// add vertex_list to gpc
gpc_add_contour( gpc, g_v_list, 0 );
// now clip m_gpc_poly with gpc, put new poly into result
gpc_polygon * result = new gpc_polygon;
if( icontour == -1 && icont != 0 )
gpc_polygon_clip( GPC_DIFF, m_gpc_poly, gpc, result ); // hole
else
gpc_polygon_clip( GPC_UNION, m_gpc_poly, gpc, result ); // outside
// now copy result to m_gpc_poly
gpc_free_polygon( m_gpc_poly );
delete m_gpc_poly;
m_gpc_poly = result;
gpc_free_polygon( gpc );
delete gpc;
free( g_v_list->vertex );
free( g_v_list );
}
return 0;
}
int CPolyLine::FreeGpcPoly()
{
if( m_gpc_poly->num_contours )
{
delete m_gpc_poly->contour->vertex;
delete m_gpc_poly->contour;
delete m_gpc_poly->hole;
}
m_gpc_poly->num_contours = 0;
return 0;
}
// Restore arcs to a polygon where they were replaced with steps
// If pa != NULL, also use polygons in pa array
//
int CPolyLine::RestoreArcs( std::vector<CArc> * arc_array, std::vector<CPolyLine*> * pa )
{
// get poly info
int n_polys = 1;
if( pa )
n_polys += pa->size();
CPolyLine * poly;
// undraw polys and clear utility flag for all corners
for( int ip=0; ip<n_polys; ip++ )
{
if( ip == 0 )
poly = this;
else
poly = (*pa)[ip-1];
poly->Undraw();
for( int ic=0; ic<poly->GetNumCorners(); ic++ )
poly->SetUtility( ic, 0 ); // clear utility flag
}
// find arcs and replace them
BOOL bFound;
int arc_start;
int arc_end;
for( unsigned iarc=0; iarc<arc_array->size(); iarc++ )
{
int arc_xi = (*arc_array)[iarc].xi;
int arc_yi = (*arc_array)[iarc].yi;
int arc_xf = (*arc_array)[iarc].xf;
int arc_yf = (*arc_array)[iarc].yf;
int n_steps = (*arc_array)[iarc].n_steps;
int style = (*arc_array)[iarc].style;
bFound = FALSE;
// loop through polys
for( int ip=0; ip<n_polys; ip++ )
{
if( ip == 0 )
poly = this;
else
poly = (*pa)[ip-1];
for( int icont=0; icont<poly->GetNumContours(); icont++ )
{
int ic_start = poly->GetContourStart(icont);
int ic_end = poly->GetContourEnd(icont);
if( (ic_end-ic_start) > n_steps )
{
for( int ic=ic_start; ic<=ic_end; ic++ )
{
int ic_next = ic+1;
if( ic_next > ic_end )
ic_next = ic_start;
int xi = poly->GetX(ic);
int yi = poly->GetY(ic);
if( xi == arc_xi && yi == arc_yi )
{
// test for forward arc
int ic2 = ic + n_steps;
if( ic2 > ic_end )
ic2 = ic2 - ic_end + ic_start - 1;
int xf = poly->GetX(ic2);
int yf = poly->GetY(ic2);
if( xf == arc_xf && yf == arc_yf )
{
// arc from ic to ic2
bFound = TRUE;
arc_start = ic;
arc_end = ic2;
}
else
{
// try reverse arc
ic2 = ic - n_steps;
if( ic2 < ic_start )
ic2 = ic2 - ic_start + ic_end + 1;
xf = poly->GetX(ic2);
yf = poly->GetY(ic2);
if( xf == arc_xf && yf == arc_yf )
{
// arc from ic2 to ic
bFound = TRUE;
arc_start = ic2;
arc_end = ic;
style = 3 - style;
}
}
if( bFound )
{
poly->side_style[arc_start] = style;
// mark corners for deletion from arc_start+1 to arc_end-1
for( int i=arc_start+1; i!=arc_end; )
{
if( i > ic_end )
i = ic_start;
poly->SetUtility( i, 1 );
if( i == ic_end )
i = ic_start;
else
i++;
}
break;
}
}
if( bFound )
break;
}
}
if( bFound )
break;
}
}
if( bFound )
(*arc_array)[iarc].bFound = TRUE;
}
// now delete all marked corners
for( int ip=0; ip<n_polys; ip++ )
{
if( ip == 0 )
poly = this;
else
poly = (*pa)[ip-1];
for( int ic=poly->GetNumCorners()-1; ic>=0; ic-- )
{
if( poly->GetUtility(ic) )
poly->DeleteCorner( ic, FALSE );
}
}
return 0;
}
// initialize new polyline
// set layer, width, selection box size, starting point, id and pointer
//
// if sel_box = 0, don't create selection elements at all
//
// if polyline is board outline, enter with:
// id.type = ID_BOARD
// id.st = ID_BOARD_OUTLINE
// id.i = 0
// ptr = NULL
//
// if polyline is copper area, enter with:
// id.type = ID_NET;
// id.st = ID_AREA
// id.i = index to area
// ptr = pointer to net
//
void CPolyLine::Start( int layer, int w, int sel_box, int x, int y,
int hatch )
{
m_layer = layer;
m_Width = w;
m_sel_box = sel_box;
m_HatchStyle = hatch;
CPolyPt poly_pt( x, y );
poly_pt.end_contour = FALSE;
corner.push_back(poly_pt);
side_style.push_back(0);
}
// add a corner to unclosed polyline
//
void CPolyLine::AppendCorner( int x, int y, int style, BOOL bDraw )
{
Undraw();
CPolyPt poly_pt( x, y );
poly_pt.end_contour = FALSE;
// add entries for new corner and side
corner.push_back(poly_pt);
side_style.push_back(style);
if( corner.size() > 0 && !corner[corner.size()-1].end_contour )
side_style[corner.size()-1] = style;
int dl_type;
if( style == CPolyLine::STRAIGHT )
dl_type = DL_LINE;
else if( style == CPolyLine::ARC_CW )
dl_type = DL_ARC_CW;
else if( style == CPolyLine::ARC_CCW )
dl_type = DL_ARC_CCW;
else
ASSERT(0);
if( bDraw )
Draw();
}
// close last polyline contour
//
void CPolyLine::Close( int style, BOOL bDraw )
{
if( GetClosed() )
ASSERT(0);
Undraw();
side_style[corner.size()-1] = style;
corner[corner.size()-1].end_contour = TRUE;
if( bDraw )
Draw();
}
// move corner of polyline
//
void CPolyLine::MoveCorner( int ic, int x, int y )
{
Undraw();
corner[ic].x = x;
corner[ic].y = y;
Draw();
}
// delete corner and adjust arrays
//
void CPolyLine::DeleteCorner( int ic, BOOL bDraw )
{
Undraw();
int icont = GetContour( ic );
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
BOOL bClosed = icont < GetNumContours()-1 || GetClosed();
if( !bClosed )
{
// open contour, must be last contour
corner.erase( corner.begin() + ic );
if( ic != istart )
side_style.erase( side_style.begin() + ic-1 );
}
else
{
// closed contour
corner.erase( corner.begin() + ic );
side_style.erase( side_style.begin() + ic );
if( ic == iend )
corner[ic-1].end_contour = TRUE;
}
if( bClosed && GetContourSize(icont) < 3 )
{
// delete the entire contour
RemoveContour( icont );
}
if( bDraw )
Draw();
}
void CPolyLine::RemoveContour( int icont )
{
Undraw();
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
if( icont == 0 && GetNumContours() == 1 )
{
// remove the only contour
ASSERT(0);
}
else if( icont == GetNumContours()-1 )
{
// remove last contour
corner.erase( corner.begin() + icont, corner.end() );
side_style.erase( side_style.begin() + icont, side_style.end() );
}
else
{
// remove closed contour
for( int ic=iend; ic>=istart; ic-- )
{
corner.erase( corner.begin() + ic );
side_style.erase( side_style.begin() + ic );
}
}
Draw();
}
/** Function InsertCorner
* insert a new corner between two existing corners
* @param ic = index for the insertion point: the corner is inserted AFTER ic
* @param x, y = coordinates corner to insert
*/
void CPolyLine::InsertCorner( int ic, int x, int y )
{
Undraw();
if ( (unsigned)(ic) >= corner.size() )
{
corner.push_back( CPolyPt(x,y) );
side_style.push_back( STRAIGHT );
}
else
{
corner.insert( corner.begin() + ic + 1, CPolyPt(x,y) );
side_style.insert( side_style.begin() + ic + 1, STRAIGHT );
}
if( (unsigned)(ic+1) < corner.size() )
{
if( corner[ic].end_contour )
{
corner[ic+1].end_contour = TRUE;
corner[ic].end_contour = FALSE;
}
}
Draw();
}
// undraw polyline by removing all graphic elements from display list
//
void CPolyLine::Undraw()
{
if( m_dlist && bDrawn )
{
// remove display elements, if present
for( unsigned i=0; i<dl_side.size(); i++ )
m_dlist->Remove( dl_side[i] );
for( unsigned i=0; i<dl_side_sel.size(); i++ )
m_dlist->Remove( dl_side_sel[i] );
for( unsigned i=0; i<dl_corner_sel.size(); i++ )
m_dlist->Remove( dl_corner_sel[i] );
// remove pointers
dl_side.clear();
dl_side_sel.clear();
dl_corner_sel.clear();
}
m_HatchLines.clear();
bDrawn = FALSE;
}
// draw polyline by adding all graphics to display list
// if side style is ARC_CW or ARC_CCW but endpoints are not angled,
// convert to STRAIGHT
//
void CPolyLine::Draw( CDisplayList * dl )
{
// first, undraw if necessary
if( bDrawn )
Undraw();
// use new display list if provided
if( dl )
m_dlist = dl;
#if 0
int i_start_contour = 0;
if( m_dlist )
{
// set up std::vectors
dl_side.SetSize( corner.size() );
if( m_sel_box )
{
dl_side_sel.SetSize( corner.size() );
dl_corner_sel.SetSize( corner.size() );
}
else
{
dl_side_sel.clear();
dl_corner_sel.clear();
}
// now draw elements
for( int ic=0; ic<corner.size(); ic++ )
{
m_id.ii = ic;
int xi = corner[ic].x;
int yi = corner[ic].y;
int xf, yf;
if( corner[ic].end_contour == FALSE && ic < corner.size()-1 )
{
xf = corner[ic+1].x;
yf = corner[ic+1].y;
}
else
{
xf = corner[i_start_contour].x;
yf = corner[i_start_contour].y;
i_start_contour = ic+1;
}
// draw
if( m_sel_box )
{
m_id.sst = ID_SEL_CORNER;
dl_corner_sel[ic] = m_dlist->AddSelector( m_id, m_ptr, m_layer, DL_HOLLOW_RECT,
1, 0, 0, xi-m_sel_box, yi-m_sel_box,
xi+m_sel_box, yi+m_sel_box, 0, 0 );
}
if( ic<(corner.size()-1) || corner[ic].end_contour )
{
// draw side
if( xi == xf || yi == yf )
{
// if endpoints not angled, make side STRAIGHT
side_style[ic] = STRAIGHT;
}
int g_type = DL_LINE;
if( side_style[ic] == STRAIGHT )
g_type = DL_LINE;
else if( side_style[ic] == ARC_CW )
g_type = DL_ARC_CW;
else if( side_style[ic] == ARC_CCW )
g_type = DL_ARC_CCW;
m_id.sst = ID_SIDE;
dl_side[ic] = m_dlist->Add( m_id, m_ptr, m_layer, g_type,
1, m_w, 0, xi, yi, xf, yf, 0, 0 );
if( m_sel_box )
{
m_id.sst = ID_SEL_SIDE;
dl_side_sel[ic] = m_dlist->AddSelector( m_id, m_ptr, m_layer, g_type,
1, m_w, 0, xi, yi, xf, yf, 0, 0 );
}
}
}
// if( m_HatchStyle )
// Hatch();
}
#endif
Hatch();
bDrawn = TRUE;
}
// start dragging new corner to be inserted into side, make side and hatching invisible
//
void CPolyLine::StartDraggingToInsertCorner( CDC * pDC, int ic, int x, int y )
{
if( !m_dlist )
ASSERT(0);
int icont = GetContour( ic );
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
int post_c;
if( ic == iend )
post_c = istart;
else
post_c = ic + 1;
int xi = corner[ic].x;
int yi = corner[ic].y;
int xf = corner[post_c].x;
int yf = corner[post_c].y;
m_dlist->StartDraggingLineVertex( pDC, x, y, xi, yi, xf, yf,
LAY_SELECTION, LAY_SELECTION, 1, 1, DSS_STRAIGHT, DSS_STRAIGHT,
0, 0, 0, 0, 1 );
m_dlist->CancelHighLight();
m_dlist->Set_visible( dl_side[ic], 0 );
/* for( int ih=0; ih<m_nhatch; ih++ )
m_dlist->Set_visible( dl_hatch[ih], 0 );
*/
}
// cancel dragging inserted corner, make side and hatching visible again
//
void CPolyLine::CancelDraggingToInsertCorner( int ic )
{
if( !m_dlist )
ASSERT(0);
int post_c;
if( ic == (int)(corner.size()-1) )
post_c = 0;
else
post_c = ic + 1;
m_dlist->StopDragging();
/* m_dlist->Set_visible( dl_side[ic], 1 );
for( int ih=0; ih<m_nhatch; ih++ )
m_dlist->Set_visible( dl_hatch[ih], 1 );
*/
}
// start dragging corner to new position, make adjacent sides and hatching invisible
//
void CPolyLine::StartDraggingToMoveCorner( CDC * pDC, int ic, int x, int y )
{
if( !m_dlist )
ASSERT(0);
// see if corner is the first or last corner of an open contour
int icont = GetContour( ic );
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
if( !GetClosed()
&& icont == GetNumContours() - 1
&& (ic == istart || ic == iend) )
{
// yes
int style, xi, yi, iside;
if( ic == istart )
{
// first corner
iside = ic;
xi = GetX( ic+1 );
yi = GetY( ic+1 );
style = GetSideStyle( iside );
// reverse arc since we are drawing from corner 1 to 0
if( style == CPolyLine::ARC_CW )
style = CPolyLine::ARC_CCW;
else if( style == CPolyLine::ARC_CCW )
style = CPolyLine::ARC_CW;
}
else
{
// last corner
iside = ic - 1;
xi = GetX( ic-1 );
yi = GetY( ic-1);
style = GetSideStyle( iside );
}
m_dlist->StartDraggingArc( pDC, style, GetX(ic), GetY(ic), xi, yi, LAY_SELECTION, 1, 1 );
m_dlist->CancelHighLight();
m_dlist->Set_visible( dl_side[iside], 0 );
/* for( int ih=0; ih<m_nhatch; ih++ )
m_dlist->Set_visible( dl_hatch[ih], 0 );
*/
}
else
{
// no
// get indexes for preceding and following corners
int pre_c, post_c;
int poly_side_style1, poly_side_style2;
int style1, style2;
if( ic == istart )
{
pre_c = iend;
post_c = istart+1;
poly_side_style1 = side_style[iend];
poly_side_style2 = side_style[istart];
}
else if( ic == iend )
{
// last side
pre_c = ic-1;
post_c = istart;
poly_side_style1 = side_style[ic-1];
poly_side_style2 = side_style[ic];
}
else
{
pre_c = ic-1;
post_c = ic+1;
poly_side_style1 = side_style[ic-1];
poly_side_style2 = side_style[ic];
}
if( poly_side_style1 == STRAIGHT )
style1 = DSS_STRAIGHT;
else if( poly_side_style1 == ARC_CW )
style1 = DSS_ARC_CW;
else if( poly_side_style1 == ARC_CCW )
style1 = DSS_ARC_CCW;
if( poly_side_style2 == STRAIGHT )
style2 = DSS_STRAIGHT;
else if( poly_side_style2 == ARC_CW )
style2 = DSS_ARC_CW;
else if( poly_side_style2 == ARC_CCW )
style2 = DSS_ARC_CCW;
int xi = corner[pre_c].x;
int yi = corner[pre_c].y;
int xf = corner[post_c].x;
int yf = corner[post_c].y;
m_dlist->StartDraggingLineVertex( pDC, x, y, xi, yi, xf, yf,
LAY_SELECTION, LAY_SELECTION, 1, 1, style1, style2,
0, 0, 0, 0, 1 );
m_dlist->CancelHighLight();
m_dlist->Set_visible( dl_side[pre_c], 0 );
m_dlist->Set_visible( dl_side[ic], 0 );
/* for( int ih=0; ih<m_nhatch; ih++ )
m_dlist->Set_visible( dl_hatch[ih], 0 );
*/ }
}
// cancel dragging corner to new position, make sides and hatching visible again
//
// highlight side by drawing line over it
//
void CPolyLine::HighlightSide( int is )
{
if( !m_dlist )
ASSERT(0);
if( GetClosed() && is >= (int)corner.size() )
return;
if( !GetClosed() && is >= (int)(corner.size()-1) )
return;
int style;
if( side_style[is] == CPolyLine::STRAIGHT )
style = DL_LINE;
else if( side_style[is] == CPolyLine::ARC_CW )
style = DL_ARC_CW;
else if( side_style[is] == CPolyLine::ARC_CCW )
style = DL_ARC_CCW;
m_dlist->HighLight( style,
m_dlist->Get_x( dl_side_sel[is] ),
m_dlist->Get_y( dl_side_sel[is] ),
m_dlist->Get_xf( dl_side_sel[is] ),
m_dlist->Get_yf( dl_side_sel[is] ),
m_dlist->Get_w( dl_side_sel[is]) );
}
int CPolyLine::GetX( int ic )
{
return corner[ic].x;
}
int CPolyLine::GetY( int ic )
{
return corner[ic].y;
}
int CPolyLine::GetEndContour( int ic )
{
return corner[ic].end_contour;
}
CRect CPolyLine::GetBounds()
{
CRect r = GetCornerBounds();
r.left -= m_Width/2;
r.right += m_Width/2;
r.bottom -= m_Width/2;
r.top += m_Width/2;
return r;
}
CRect CPolyLine::GetCornerBounds()
{
CRect r;
r.left = r.bottom = INT_MAX;
r.right = r.top = INT_MIN;
for( unsigned i=0; i<corner.size(); i++ )
{
r.left = min( r.left, corner[i].x );
r.right = max( r.right, corner[i].x );
r.bottom = min( r.bottom, corner[i].y );
r.top = max( r.top, corner[i].y );
}
return r;
}
CRect CPolyLine::GetCornerBounds( int icont )
{
CRect r;
r.left = r.bottom = INT_MAX;
r.right = r.top = INT_MIN;
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
for( int i=istart; i<=iend; i++ )
{
r.left = min( r.left, corner[i].x );
r.right = max( r.right, corner[i].x );
r.bottom = min( r.bottom, corner[i].y );
r.top = max( r.top, corner[i].y );
}
return r;
}
int CPolyLine::GetNumCorners()
{
return corner.size();
}
int CPolyLine::GetNumSides()
{
if( GetClosed() )
return corner.size();
else
return corner.size()-1;
}
int CPolyLine::GetW()
{
return m_Width;
}
int CPolyLine::GetSelBoxSize()
{
return m_sel_box;
}
int CPolyLine::GetNumContours()
{
int ncont = 0;
if( !corner.size() )
return 0;
for( unsigned ic=0; ic<corner.size(); ic++ )
if( corner[ic].end_contour )
ncont++;
if( !corner[corner.size()-1].end_contour )
ncont++;
return ncont;
}
int CPolyLine::GetContour( int ic )
{
int ncont = 0;
for( int i=0; i<ic; i++ )
{
if( corner[i].end_contour )
ncont++;
}
return ncont;
}
int CPolyLine::GetContourStart( int icont )
{
if( icont == 0 )
return 0;
int ncont = 0;
for( unsigned i=0; i<corner.size(); i++ )
{
if( corner[i].end_contour )
{
ncont++;
if( ncont == icont )
return i+1;
}
}
ASSERT(0);
return 0;
}
int CPolyLine::GetContourEnd( int icont )
{
if( icont < 0 )
return 0;
if( icont == GetNumContours()-1 )
return corner.size()-1;
int ncont = 0;
for( unsigned i=0; i<corner.size(); i++ )
{
if( corner[i].end_contour )
{
if( ncont == icont )
return i;
ncont++;
}
}
ASSERT(0);
return 0;
}
int CPolyLine::GetContourSize( int icont )
{
return GetContourEnd(icont) - GetContourStart(icont) + 1;
}
void CPolyLine::SetSideStyle( int is, int style )
{
Undraw();
CPoint p1, p2;
if( is == (int)(corner.size()-1) )
{
p1.x = corner[corner.size()-1].x;
p1.y = corner[corner.size()-1].y;
p2.x = corner[0].x;
p2.y = corner[0].y;
}
else
{
p1.x = corner[is].x;
p1.y = corner[is].y;
p2.x = corner[is+1].x;
p2.y = corner[is+1].y;
}
if( p1.x == p2.x || p1.y == p2.y )
side_style[is] = STRAIGHT;
else
side_style[is] = style;
Draw();
}
int CPolyLine::GetSideStyle( int is )
{
return side_style[is];
}
int CPolyLine::GetClosed()
{
if( corner.size() == 0 )
return 0;
else
return corner[corner.size()-1].end_contour;
}
// draw hatch lines
//
void CPolyLine::Hatch()
{
m_HatchLines.clear();
if( m_HatchStyle == NO_HATCH )
{
return;
}
int layer = m_layer;
// if( /*m_dlist && */GetClosed() )
{
enum {
MAXPTS = 100,
MAXLINES = 1000
};
int xx[MAXPTS], yy[MAXPTS];
// define range for hatch lines
int min_x = corner[0].x;
int max_x = corner[0].x;
int min_y = corner[0].y;
int max_y = corner[0].y;
for( unsigned ic = 1; ic < corner.size(); ic++ )
{
if( corner[ic].x < min_x )
min_x = corner[ic].x;
if( corner[ic].x > max_x )
max_x = corner[ic].x;
if( corner[ic].y < min_y )
min_y = corner[ic].y;
if( corner[ic].y > max_y )
max_y = corner[ic].y;
}
int slope_flag = (layer & 1) ? 1 : -1; // 1 or -1
double slope = 0.707106*slope_flag;
int spacing;
if( m_HatchStyle == DIAGONAL_EDGE )
spacing = 10*PCBU_PER_MIL;
else
spacing = 50*PCBU_PER_MIL;
int max_a, min_a;
if( slope_flag == 1 )
{
max_a = (int)(max_y - slope*min_x);
min_a = (int)(min_y - slope*max_x);
}
else
{
max_a = (int)(max_y - slope*max_x);
min_a = (int)(min_y - slope*min_x);
}
min_a = (min_a/spacing)*spacing;
int offset;
if( layer < (LAY_TOP_COPPER+2) )
offset = 0;
else if( layer < (LAY_TOP_COPPER+4) )
offset = spacing/2;
else if( layer < (LAY_TOP_COPPER+6) )
offset = spacing/4;
else if( layer < (LAY_TOP_COPPER+8) )
offset = 3*spacing/4;
else if( layer < (LAY_TOP_COPPER+10) )
offset = 1*spacing/8;
else if( layer < (LAY_TOP_COPPER+12) )
offset = 3*spacing/8;
else if( layer < (LAY_TOP_COPPER+14) )
offset = 5*spacing/8;
else if( layer < (LAY_TOP_COPPER+16) )
offset = 7*spacing/8;
else
ASSERT(0);
min_a += offset;
// now calculate and draw hatch lines
int nc = corner.size();
// loop through hatch lines
for( int a=min_a; a<max_a; a+=spacing )
{
// get intersection points for this hatch line
int nloops = 0;
int npts;
// make this a loop in case my homebrew hatching algorithm screws up
do
{
npts = 0;
int i_start_contour = 0;
for( int ic=0; ic<nc; ic++ )
{
double x, y, x2, y2;
int ok;
if( corner[ic].end_contour )
{
ok = FindLineSegmentIntersection( a, slope,
corner[ic].x, corner[ic].y,
corner[i_start_contour].x, corner[i_start_contour].y,
side_style[ic],
&x, &y, &x2, &y2 );
i_start_contour = ic + 1;
}
else
{
ok = FindLineSegmentIntersection( a, slope,
corner[ic].x, corner[ic].y,
corner[ic+1].x, corner[ic+1].y,
side_style[ic],
&x, &y, &x2, &y2 );
}
if( ok )
{
xx[npts] = (int)x;
yy[npts] = (int)y;
npts++;
ASSERT( npts<MAXPTS ); // overflow
}
if( ok == 2 )
{
xx[npts] = (int)x2;
yy[npts] = (int)y2;
npts++;
ASSERT( npts<MAXPTS ); // overflow
}
}
nloops++;
a += PCBU_PER_MIL/100;
} while( npts%2 != 0 && nloops < 3 );
ASSERT( npts%2==0 ); // odd number of intersection points, error
// sort points in order of descending x (if more than 2)
if( npts>2 )
{
for( int istart=0; istart<(npts-1); istart++ )
{
int max_x = INT_MIN;
int imax;
for( int i=istart; i<npts; i++ )
{
if( xx[i] > max_x )
{
max_x = xx[i];
imax = i;
}
}
int temp = xx[istart];
xx[istart] = xx[imax];
xx[imax] = temp;
temp = yy[istart];
yy[istart] = yy[imax];
yy[imax] = temp;
}
}
// draw lines
for( int ip=0; ip<npts; ip+=2 )
{
double dx = xx[ip+1] - xx[ip];
if( m_HatchStyle == DIAGONAL_FULL || fabs(dx) < 40*NM_PER_MIL )
{
m_HatchLines.push_back(CSegment(xx[ip], yy[ip], xx[ip+1], yy[ip+1]) );
}
else
{
double dy = yy[ip+1] - yy[ip];
double slope = dy/dx;
if( dx > 0 )
dx = 20*NM_PER_MIL;
else
dx = -20*NM_PER_MIL;
double x1 = xx[ip] + dx;
double x2 = xx[ip+1] - dx;
double y1 = yy[ip] + dx*slope;
double y2 = yy[ip+1] - dx*slope;
m_HatchLines.push_back(CSegment(xx[ip], yy[ip], to_int(x1), to_int(y1)) );
m_HatchLines.push_back(CSegment(xx[ip+1], yy[ip+1], to_int(x2), to_int(y2)) );
}
}
} // end for
}
}
// test to see if a point is inside polyline
//
BOOL CPolyLine::TestPointInside( int x, int y )
{
enum { MAXPTS = 100 };
if( !GetClosed() )
ASSERT(0);
// define line passing through (x,y), with slope = 2/3;
// get intersection points
double xx[MAXPTS], yy[MAXPTS];
double slope = (double)2.0/3.0;
double a = y - slope*x;
int nloops = 0;
int npts;
// make this a loop so if my homebrew algorithm screws up, we try it again
do
{
// now find all intersection points of line with polyline sides
npts = 0;
for( int icont=0; icont<GetNumContours(); icont++ )
{
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
for( int ic=istart; ic<=iend; ic++ )
{
double x, y, x2, y2;
int ok;
if( ic == istart )
ok = FindLineSegmentIntersection( a, slope,
corner[iend].x, corner[iend].y,
corner[istart].x, corner[istart].y,
side_style[corner.size()-1],
&x, &y, &x2, &y2 );
else
ok = FindLineSegmentIntersection( a, slope,
corner[ic-1].x, corner[ic-1].y,
corner[ic].x, corner[ic].y,
side_style[ic-1],
&x, &y, &x2, &y2 );
if( ok )
{
xx[npts] = (int)x;
yy[npts] = (int)y;
npts++;
ASSERT( npts<MAXPTS ); // overflow
}
if( ok == 2 )
{
xx[npts] = (int)x2;
yy[npts] = (int)y2;
npts++;
ASSERT( npts<MAXPTS ); // overflow
}
}
}
nloops++;
a += PCBU_PER_MIL/100;
} while( npts%2 != 0 && nloops < 3 );
ASSERT( npts%2==0 ); // odd number of intersection points, error
// count intersection points to right of (x,y), if odd (x,y) is inside polyline
int ncount = 0;
for( int ip=0; ip<npts; ip++ )
{
if( xx[ip] == x && yy[ip] == y )
return FALSE; // (x,y) is on a side, call it outside
else if( xx[ip] > x )
ncount++;
}
if( ncount%2 )
return TRUE;
else
return FALSE;
}
// test to see if a point is inside polyline contour
//
BOOL CPolyLine::TestPointInsideContour( int icont, int x, int y )
{
if( icont >= GetNumContours() )
return FALSE;
enum { MAXPTS = 100 };
if( !GetClosed() )
ASSERT(0);
// define line passing through (x,y), with slope = 2/3;
// get intersection points
double xx[MAXPTS], yy[MAXPTS];
double slope = (double)2.0/3.0;
double a = y - slope*x;
int nloops = 0;
int npts;
// make this a loop so if my homebrew algorithm screws up, we try it again
do
{
// now find all intersection points of line with polyline sides
npts = 0;
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
for( int ic=istart; ic<=iend; ic++ )
{
double x, y, x2, y2;
int ok;
if( ic == istart )
ok = FindLineSegmentIntersection( a, slope,
corner[iend].x, corner[iend].y,
corner[istart].x, corner[istart].y,
side_style[corner.size()-1],
&x, &y, &x2, &y2 );
else
ok = FindLineSegmentIntersection( a, slope,
corner[ic-1].x, corner[ic-1].y,
corner[ic].x, corner[ic].y,
side_style[ic-1],
&x, &y, &x2, &y2 );
if( ok )
{
xx[npts] = (int)x;
yy[npts] = (int)y;
npts++;
ASSERT( npts<MAXPTS ); // overflow
}
if( ok == 2 )
{
xx[npts] = (int)x2;
yy[npts] = (int)y2;
npts++;
ASSERT( npts<MAXPTS ); // overflow
}
}
nloops++;
a += PCBU_PER_MIL/100;
} while( npts%2 != 0 && nloops < 3 );
ASSERT( npts%2==0 ); // odd number of intersection points, error
// count intersection points to right of (x,y), if odd (x,y) is inside polyline
int ncount = 0;
for( int ip=0; ip<npts; ip++ )
{
if( xx[ip] == x && yy[ip] == y )
return FALSE; // (x,y) is on a side, call it outside
else if( xx[ip] > x )
ncount++;
}
if( ncount%2 )
return TRUE;
else
return FALSE;
}
// Test for intersection of sides
//
int CPolyLine::TestIntersection( CPolyLine * poly )
{
if( !GetClosed() )
ASSERT(0);
if( !poly->GetClosed() )
ASSERT(0);
for( int ic=0; ic<GetNumContours(); ic++ )
{
int istart = GetContourStart(ic);
int iend = GetContourEnd(ic);
for( int is=istart; is<=iend; is++ )
{
int xf, yf;
if( is < GetContourEnd(ic) )
{
xf = GetX(is+1);
yf = GetY(is+1);
}
else
{
xf = GetX(istart);
yf = GetY(istart);
}
for( int ic2=0; ic2<poly->GetNumContours(); ic2++ )
{
int istart2 = poly->GetContourStart(ic2);
int iend2 = poly->GetContourEnd(ic2);
for( int is2=istart2; is2<=iend2; is2++ )
{
int xf2, yf2;
if( is2 < poly->GetContourEnd(ic2) )
{
xf2 = poly->GetX(is2+1);
yf2 = poly->GetY(is2+1);
}
else
{
xf2 = poly->GetX(istart2);
yf2 = poly->GetY(istart2);
}
// test for intersection between side and side2
}
}
}
}
return 0;
}
// set selection box size
//
void CPolyLine::SetSelBoxSize( int sel_box )
{
// Undraw();
m_sel_box = sel_box;
// Draw();
}
// set pointer to display list, and draw into display list
//
void CPolyLine::SetDisplayList( CDisplayList * dl )
{
if( m_dlist )
Undraw();
m_dlist = dl;
if( m_dlist )
Draw();
}
// copy data from another poly, but don't draw it
//
void CPolyLine::Copy( CPolyLine * src )
{
Undraw();
m_dlist = src->m_dlist;
m_sel_box = src->m_sel_box;
// copy corners
for( unsigned i=0; i< src->corner.size(); i++ )
corner.push_back(src->corner[i]);
// copy side styles
int nsides = src->GetNumSides();
side_style.SetSize(nsides);
for( int i=0; i<nsides; i++ )
side_style[i] = src->side_style[i];
// don't copy the Gpc_poly, just clear the old one
FreeGpcPoly();
}
void CPolyLine::MoveOrigin( int x_off, int y_off )
{
Undraw();
for( int ic=0; ic < GetNumCorners(); ic++ )
{
SetX( ic, GetX(ic) + x_off );
SetY( ic, GetY(ic) + y_off );
}
Draw();
}
// Set various parameters:
// the calling function should Undraw() before calling them,
// and Draw() after
//
void CPolyLine::SetX( int ic, int x ) { corner[ic].x = x; }
void CPolyLine::SetY( int ic, int y ) { corner[ic].y = y; }
void CPolyLine::SetEndContour( int ic, BOOL end_contour ) { corner[ic].end_contour = end_contour; }
// Create CPolyLine for a pad
//
CPolyLine * CPolyLine::MakePolylineForPad( int type, int x, int y, int w, int l, int r, int angle )
{
CPolyLine * poly = new CPolyLine;
int dx = l/2;
int dy = w/2;
if( angle%180 == 90 )
{
dx = w/2;
dy = l/2;
}
if( type == PAD_ROUND )
{
poly->Start( 0, 0, 0, x-dx, y, 0 );
poly->AppendCorner( x, y+dy, ARC_CW, 0 );
poly->AppendCorner( x+dx, y, ARC_CW, 0 );
poly->AppendCorner( x, y-dy, ARC_CW, 0 );
poly->Close( ARC_CW );
}
return poly;
}
// Add cutout for a pad
// Convert arcs to multiple straight lines
// Do NOT draw or undraw
//
void CPolyLine::AddContourForPadClearance( int type, int x, int y, int w,
int l, int r, int angle, int fill_clearance,
int hole_w, int hole_clearance, BOOL bThermal, int spoke_w )
{
int dx = l/2;
int dy = w/2;
if( angle%180 == 90 )
{
dx = w/2;
dy = l/2;
}
int x_clearance = max( fill_clearance, hole_clearance+hole_w/2-dx);
int y_clearance = max( fill_clearance, hole_clearance+hole_w/2-dy);
dx += x_clearance;
dy += y_clearance;
if( !bThermal )
{
// normal clearance
if( type == PAD_ROUND || (type == PAD_NONE && hole_w > 0) )
{
AppendCorner( x-dx, y, ARC_CW, 0 );
AppendCorner( x, y+dy, ARC_CW, 0 );
AppendCorner( x+dx, y, ARC_CW, 0 );
AppendCorner( x, y-dy, ARC_CW, 0 );
Close( ARC_CW );
}
else if( type == PAD_SQUARE || type == PAD_RECT
|| type == PAD_RRECT || type == PAD_OVAL )
{
AppendCorner( x-dx, y-dy, STRAIGHT, 0 );
AppendCorner( x+dx, y-dy, STRAIGHT, 0 );
AppendCorner( x+dx, y+dy, STRAIGHT, 0 );
AppendCorner( x-dx, y+dy, STRAIGHT, 0 );
Close( STRAIGHT );
}
}
else
{
// thermal relief
if( type == PAD_ROUND || (type == PAD_NONE && hole_w > 0) )
{
// draw 4 "wedges"
double r = max(w/2 + fill_clearance, hole_w/2 + hole_clearance);
double start_angle = asin( spoke_w/(2.0*r) );
double th1, th2, corner_x, corner_y;
for( int i=0; i<4; i++ )
{
if( i == 0 )
{
corner_x = spoke_w/2;
corner_y = spoke_w/2;
th1 = start_angle;
th2 = pi/2.0 - start_angle;
}
else if( i == 1 )
{
corner_x = -spoke_w/2;
corner_y = spoke_w/2;
th1 = pi/2.0 + start_angle;
th2 = pi - start_angle;
}
else if( i == 2 )
{
corner_x = -spoke_w/2;
corner_y = -spoke_w/2;
th1 = -pi + start_angle;
th2 = -pi/2.0 - start_angle;
}
else if( i == 3 )
{
corner_x = spoke_w/2;
corner_y = -spoke_w/2;
th1 = -pi/2.0 + start_angle;
th2 = -start_angle;
}
AppendCorner( to_int(x+corner_x), to_int(y+corner_y), STRAIGHT, 0 );
AppendCorner( to_int(x+r*cos(th1)), to_int(y+r*sin(th1)), STRAIGHT, 0 );
AppendCorner( to_int(x+r*cos(th2)), to_int(y+r*sin(th2)), ARC_CCW, 0 );
Close( STRAIGHT );
}
}
else if( type == PAD_SQUARE || type == PAD_RECT
|| type == PAD_RRECT || type == PAD_OVAL )
{
// draw 4 rectangles
int xL = x - dx;
int xR = x - spoke_w/2;
int yB = y - dy;
int yT = y - spoke_w/2;
AppendCorner( xL, yB, STRAIGHT, 0 );
AppendCorner( xR, yB, STRAIGHT, 0 );
AppendCorner( xR, yT, STRAIGHT, 0 );
AppendCorner( xL, yT, STRAIGHT, 0 );
Close( STRAIGHT );
xL = x + spoke_w/2;
xR = x + dx;
AppendCorner( xL, yB, STRAIGHT, 0 );
AppendCorner( xR, yB, STRAIGHT, 0 );
AppendCorner( xR, yT, STRAIGHT, 0 );
AppendCorner( xL, yT, STRAIGHT, 0 );
Close( STRAIGHT );
xL = x - dx;
xR = x - spoke_w/2;
yB = y + spoke_w/2;
yT = y + dy;
AppendCorner( xL, yB, STRAIGHT, 0 );
AppendCorner( xR, yB, STRAIGHT, 0 );
AppendCorner( xR, yT, STRAIGHT, 0 );
AppendCorner( xL, yT, STRAIGHT, 0 );
Close( STRAIGHT );
xL = x + spoke_w/2;
xR = x + dx;
AppendCorner( xL, yB, STRAIGHT, 0 );
AppendCorner( xR, yB, STRAIGHT, 0 );
AppendCorner( xR, yT, STRAIGHT, 0 );
AppendCorner( xL, yT, STRAIGHT, 0 );
Close( STRAIGHT );
}
}
return;
}
void CPolyLine::AppendArc( int xi, int yi, int xf, int yf, int xc, int yc, int num )
{
// get radius
double r = sqrt( (double)(xi-xc)*(xi-xc) + (double)(yi-yc)*(yi-yc) );
// get angles of start and finish
double th_i = atan2( (double)yi-yc, (double)xi-xc );
double th_f = atan2( (double)yf-yc, (double)xf-xc );
double th_d = (th_f - th_i)/(num-1);
double theta = th_i;
// generate arc
for( int ic=0; ic<num; ic++ )
{
int x = to_int(xc + r*cos(theta));
int y = to_int(yc + r*sin(theta));
AppendCorner( x, y, STRAIGHT, 0 );
theta += th_d;
}
Close( STRAIGHT );
}
void CPolyLine::ClipGpcPolygon( gpc_op op, CPolyLine * clip_poly )
{
gpc_polygon * result = new gpc_polygon;
gpc_polygon_clip( op, m_gpc_poly, clip_poly->GetGpcPoly(), result );
gpc_free_polygon( m_gpc_poly );
delete m_gpc_poly;
m_gpc_poly = result;
}
// PolyLine.cpp ... implementation of CPolyLine class
// from FreePCB.
// Adaptation for kicad
//
using namespace std;
#include <math.h>
#include <vector>
#include "PolyLine.h"
#define to_int(x) (int)round((x))
/* Stuff to compile PolyLine.cpp, used in std::vector as CArray. does not work. must be redesigned, only for test */
#define SetSize reserve
#define pi 3.14159265359
#define DENOM 10 // to use mils for php clipping
//#define DENOM 1 // to use internal units for php clipping
// dl is a pointer to CDisplayList for drawing graphic elements
// if dl = NULL, doesn't draw anything but can still hold data
//
CPolyLine::CPolyLine( CDisplayList * dl )
{
m_dlist = dl;
m_HatchStyle = 0;
m_sel_box = 0;
m_gpc_poly = new gpc_polygon;
m_gpc_poly->num_contours = 0;
m_php_poly = new polygon;
}
CPolyLine::CPolyLine()
{
m_dlist = NULL;
m_HatchStyle = 0;
m_sel_box = 0;
m_gpc_poly = new gpc_polygon;
m_gpc_poly->num_contours = 0;
m_php_poly = new polygon;
}
// destructor, removes display elements
//
CPolyLine::~CPolyLine()
{
Undraw();
FreeGpcPoly();
delete m_gpc_poly;
delete m_php_poly;
}
// Use the General Polygon Clipping Library to clip contours
// If this results in new polygons, return them as std::vector p
// If bRetainArcs == TRUE, try to retain arcs in polys
// Returns number of external contours, or -1 if error
//
int CPolyLine::NormalizeWithGpc( std::vector<CPolyLine*> * pa, BOOL bRetainArcs )
{
std::vector<CArc> arc_array;
if( bRetainArcs )
MakeGpcPoly( -1, &arc_array );
else
MakeGpcPoly( -1, NULL );
Undraw();
// now, recreate poly
// first, find outside contours and create new CPolyLines if necessary
int n_ext_cont = 0;
for( int ic=0; ic<m_gpc_poly->num_contours; ic++ )
{
if( !(m_gpc_poly->hole)[ic] )
{
if( n_ext_cont == 0 )
{
// first external contour, replace this poly
corner.clear();
side_style.clear();
for( int i=0; i<m_gpc_poly->contour[ic].num_vertices; i++ )
{
int x = to_int(((m_gpc_poly->contour)[ic].vertex)[i].x);
int y = to_int(((m_gpc_poly->contour)[ic].vertex)[i].y);
if( i==0 )
Start( m_layer, m_Width, m_sel_box, x, y, m_HatchStyle );
else
AppendCorner( x, y, STRAIGHT, FALSE );
}
Close();
n_ext_cont++;
}
else if( pa )
{
// next external contour, create new poly
CPolyLine * poly = new CPolyLine;
pa->SetSize(n_ext_cont); // put in array
(*pa)[n_ext_cont-1] = poly;
for( int i=0; i<m_gpc_poly->contour[ic].num_vertices; i++ )
{
int x = to_int(((m_gpc_poly->contour)[ic].vertex)[i].x);
int y = to_int(((m_gpc_poly->contour)[ic].vertex)[i].y);
if( i==0 )
poly->Start( m_layer, m_Width, m_sel_box, x, y, m_HatchStyle );
else
poly->AppendCorner( x, y, STRAIGHT, FALSE );
}
poly->Close( STRAIGHT, FALSE );
n_ext_cont++;
}
}
}
// now add cutouts to the CPolyLine(s)
for( int ic=0; ic<m_gpc_poly->num_contours; ic++ )
{
if( (m_gpc_poly->hole)[ic] )
{
CPolyLine * ext_poly = NULL;
if( n_ext_cont == 1 )
{
ext_poly = this;
}
else
{
// find the polygon that contains this hole
for( int i=0; i<m_gpc_poly->contour[ic].num_vertices; i++ )
{
int x = to_int(((m_gpc_poly->contour)[ic].vertex)[i].x);
int y = to_int(((m_gpc_poly->contour)[ic].vertex)[i].y);
if( TestPointInside( x, y ) )
ext_poly = this;
else
{
for( int ext_ic=0; ext_ic<n_ext_cont-1; ext_ic++ )
{
if( (*pa)[ext_ic]->TestPointInside( x, y ) )
{
ext_poly = (*pa)[ext_ic];
break;
}
}
}
if( ext_poly )
break;
}
}
if( !ext_poly )
ASSERT(0);
for( int i=0; i<m_gpc_poly->contour[ic].num_vertices; i++ )
{
int x = to_int(((m_gpc_poly->contour)[ic].vertex)[i].x);
int y = to_int(((m_gpc_poly->contour)[ic].vertex)[i].y);
ext_poly->AppendCorner( x, y, STRAIGHT, FALSE );
}
ext_poly->Close( STRAIGHT, FALSE );
}
}
if( bRetainArcs )
RestoreArcs( &arc_array, pa );
FreeGpcPoly();
return n_ext_cont;
}
// make a php_polygon from first contour
int CPolyLine::MakePhpPoly()
{
FreePhpPoly();
polygon test_poly;
int nv = GetContourEnd(0);
for( int iv=0; iv<=nv; iv++ )
{
int x = GetX(iv)/DENOM;
int y = GetY(iv)/DENOM;
m_php_poly->addv( x, y );
}
return 0;
}
void CPolyLine::FreePhpPoly()
{
// delete all vertices
while( m_php_poly->m_cnt > 1 )
{
vertex * fv = m_php_poly->getFirst();
m_php_poly->del( fv->m_nextV );
}
delete m_php_poly->m_first;
m_php_poly->m_first = NULL;
m_php_poly->m_cnt = 0;
}
// Use the php clipping lib to clip this poly against poly
//
void CPolyLine::ClipPhpPolygon( int php_op, CPolyLine * poly )
{
Undraw();
poly->MakePhpPoly();
MakePhpPoly();
polygon * p = m_php_poly->boolean( poly->m_php_poly, php_op );
poly->FreePhpPoly();
FreePhpPoly();
if( p )
{
// now screw with the PolyLine
corner.clear();
side_style.clear();
do
{
vertex * v = p->getFirst();
Start( m_layer, m_Width, m_sel_box,
to_int(v->X()*DENOM),
to_int(v->Y()*DENOM),
m_HatchStyle );
do
{
vertex * n = v->Next();
AppendCorner( to_int(v->X()*DENOM), to_int((v->Y()*DENOM )) );
v = n;
}
while( v->id() != p->getFirst()->id() );
Close();
// p = p->NextPoly();
delete p;
p = NULL;
}
while( p );
}
Draw();
}
// make a gpc_polygon for a closed polyline contour
// approximates arcs with multiple straight-line segments
// if icontour = -1, make polygon with all contours,
// combining intersecting contours if possible
// returns data on arcs in arc_array
//
int CPolyLine::MakeGpcPoly( int icontour, std::vector<CArc> * arc_array )
{
if( m_gpc_poly->num_contours )
FreeGpcPoly();
if( !GetClosed() && (icontour == (GetNumContours()-1) || icontour == -1))
return 1; // error
// initialize m_gpc_poly
m_gpc_poly->num_contours = 0;
m_gpc_poly->hole = NULL;
m_gpc_poly->contour = NULL;
int n_arcs = 0;
int first_contour = icontour;
int last_contour = icontour;
if( icontour == -1 )
{
first_contour = 0;
last_contour = GetNumContours() - 1;
}
if( arc_array )
arc_array->SetSize(0);
int iarc = 0;
for( int icont=first_contour; icont<=last_contour; icont++ )
{
// make gpc_polygon for this contour
gpc_polygon * gpc = new gpc_polygon;
gpc->num_contours = 0;
gpc->hole = NULL;
gpc->contour = NULL;
// first, calculate number of vertices in contour
int n_vertices = 0;
int ic_st = GetContourStart(icont);
int ic_end = GetContourEnd(icont);
for( int ic=ic_st; ic<=ic_end; ic++ )
{
int style = side_style[ic];
int x1 = corner[ic].x;
int y1 = corner[ic].y;
int x2, y2;
if( ic < ic_end )
{
x2 = corner[ic+1].x;
y2 = corner[ic+1].y;
}
else
{
x2 = corner[ic_st].x;
y2 = corner[ic_st].y;
}
if( style == STRAIGHT )
n_vertices++;
else
{
// style is ARC_CW or ARC_CCW
int n; // number of steps for arcs
n = (abs(x2-x1)+abs(y2-y1))/(CArc::MAX_STEP);
n = max( n, CArc::MIN_STEPS ); // or at most 5 degrees of arc
n_vertices += n;
n_arcs++;
}
}
// now create gcp_vertex_list for this contour
gpc_vertex_list * g_v_list = new gpc_vertex_list;
g_v_list->vertex = (gpc_vertex*)calloc( sizeof(gpc_vertex), n_vertices );
g_v_list->num_vertices = n_vertices;
int ivtx = 0;
for( int ic=ic_st; ic<=ic_end; ic++ )
{
int style = side_style[ic];
int x1 = corner[ic].x;
int y1 = corner[ic].y;
int x2, y2;
if( ic < ic_end )
{
x2 = corner[ic+1].x;
y2 = corner[ic+1].y;
}
else
{
x2 = corner[ic_st].x;
y2 = corner[ic_st].y;
}
if( style == STRAIGHT )
{
g_v_list->vertex[ivtx].x = x1;
g_v_list->vertex[ivtx].y = y1;
ivtx++;
}
else
{
// style is arc_cw or arc_ccw
int n; // number of steps for arcs
n = (abs(x2-x1)+abs(y2-y1))/(CArc::MAX_STEP);
n = max( n, CArc::MIN_STEPS ); // or at most 5 degrees of arc
double xo, yo, theta1, theta2, a, b;
a = fabs( (double)(x1 - x2) );
b = fabs( (double)(y1 - y2) );
if( style == CPolyLine::ARC_CW )
{
// clockwise arc (ie.quadrant of ellipse)
if( x2 > x1 && y2 > y1 )
{
// first quadrant, draw second quadrant of ellipse
xo = x2;
yo = y1;
theta1 = pi;
theta2 = pi/2.0;
}
else if( x2 < x1 && y2 > y1 )
{
// second quadrant, draw third quadrant of ellipse
xo = x1;
yo = y2;
theta1 = 3.0*pi/2.0;
theta2 = pi;
}
else if( x2 < x1 && y2 < y1 )
{
// third quadrant, draw fourth quadrant of ellipse
xo = x2;
yo = y1;
theta1 = 2.0*pi;
theta2 = 3.0*pi/2.0;
}
else
{
xo = x1; // fourth quadrant, draw first quadrant of ellipse
yo = y2;
theta1 = pi/2.0;
theta2 = 0.0;
}
}
else
{
// counter-clockwise arc
if( x2 > x1 && y2 > y1 )
{
xo = x1; // first quadrant, draw fourth quadrant of ellipse
yo = y2;
theta1 = 3.0*pi/2.0;
theta2 = 2.0*pi;
}
else if( x2 < x1 && y2 > y1 )
{
xo = x2; // second quadrant
yo = y1;
theta1 = 0.0;
theta2 = pi/2.0;
}
else if( x2 < x1 && y2 < y1 )
{
xo = x1; // third quadrant
yo = y2;
theta1 = pi/2.0;
theta2 = pi;
}
else
{
xo = x2; // fourth quadrant
yo = y1;
theta1 = pi;
theta2 = 3.0*pi/2.0;
}
}
// now write steps for arc
if( arc_array )
{
arc_array->SetSize(iarc+1);
(*arc_array)[iarc].style = style;
(*arc_array)[iarc].n_steps = n;
(*arc_array)[iarc].xi = x1;
(*arc_array)[iarc].yi = y1;
(*arc_array)[iarc].xf = x2;
(*arc_array)[iarc].yf = y2;
iarc++;
}
for( int is=0; is<n; is++ )
{
double theta = theta1 + ((theta2-theta1)*(double)is)/n;
double x = xo + a*cos(theta);
double y = yo + b*sin(theta);
if( is == 0 )
{
x = x1;
y = y1;
}
g_v_list->vertex[ivtx].x = x;
g_v_list->vertex[ivtx].y = y;
ivtx++;
}
}
}
if( n_vertices != ivtx )
ASSERT(0);
// add vertex_list to gpc
gpc_add_contour( gpc, g_v_list, 0 );
// now clip m_gpc_poly with gpc, put new poly into result
gpc_polygon * result = new gpc_polygon;
if( icontour == -1 && icont != 0 )
gpc_polygon_clip( GPC_DIFF, m_gpc_poly, gpc, result ); // hole
else
gpc_polygon_clip( GPC_UNION, m_gpc_poly, gpc, result ); // outside
// now copy result to m_gpc_poly
gpc_free_polygon( m_gpc_poly );
delete m_gpc_poly;
m_gpc_poly = result;
gpc_free_polygon( gpc );
delete gpc;
free( g_v_list->vertex );
free( g_v_list );
}
return 0;
}
int CPolyLine::FreeGpcPoly()
{
if( m_gpc_poly->num_contours )
{
delete m_gpc_poly->contour->vertex;
delete m_gpc_poly->contour;
delete m_gpc_poly->hole;
}
m_gpc_poly->num_contours = 0;
return 0;
}
// Restore arcs to a polygon where they were replaced with steps
// If pa != NULL, also use polygons in pa array
//
int CPolyLine::RestoreArcs( std::vector<CArc> * arc_array, std::vector<CPolyLine*> * pa )
{
// get poly info
int n_polys = 1;
if( pa )
n_polys += pa->size();
CPolyLine * poly;
// undraw polys and clear utility flag for all corners
for( int ip=0; ip<n_polys; ip++ )
{
if( ip == 0 )
poly = this;
else
poly = (*pa)[ip-1];
poly->Undraw();
for( int ic=0; ic<poly->GetNumCorners(); ic++ )
poly->SetUtility( ic, 0 ); // clear utility flag
}
// find arcs and replace them
BOOL bFound;
int arc_start;
int arc_end;
for( unsigned iarc=0; iarc<arc_array->size(); iarc++ )
{
int arc_xi = (*arc_array)[iarc].xi;
int arc_yi = (*arc_array)[iarc].yi;
int arc_xf = (*arc_array)[iarc].xf;
int arc_yf = (*arc_array)[iarc].yf;
int n_steps = (*arc_array)[iarc].n_steps;
int style = (*arc_array)[iarc].style;
bFound = FALSE;
// loop through polys
for( int ip=0; ip<n_polys; ip++ )
{
if( ip == 0 )
poly = this;
else
poly = (*pa)[ip-1];
for( int icont=0; icont<poly->GetNumContours(); icont++ )
{
int ic_start = poly->GetContourStart(icont);
int ic_end = poly->GetContourEnd(icont);
if( (ic_end-ic_start) > n_steps )
{
for( int ic=ic_start; ic<=ic_end; ic++ )
{
int ic_next = ic+1;
if( ic_next > ic_end )
ic_next = ic_start;
int xi = poly->GetX(ic);
int yi = poly->GetY(ic);
if( xi == arc_xi && yi == arc_yi )
{
// test for forward arc
int ic2 = ic + n_steps;
if( ic2 > ic_end )
ic2 = ic2 - ic_end + ic_start - 1;
int xf = poly->GetX(ic2);
int yf = poly->GetY(ic2);
if( xf == arc_xf && yf == arc_yf )
{
// arc from ic to ic2
bFound = TRUE;
arc_start = ic;
arc_end = ic2;
}
else
{
// try reverse arc
ic2 = ic - n_steps;
if( ic2 < ic_start )
ic2 = ic2 - ic_start + ic_end + 1;
xf = poly->GetX(ic2);
yf = poly->GetY(ic2);
if( xf == arc_xf && yf == arc_yf )
{
// arc from ic2 to ic
bFound = TRUE;
arc_start = ic2;
arc_end = ic;
style = 3 - style;
}
}
if( bFound )
{
poly->side_style[arc_start] = style;
// mark corners for deletion from arc_start+1 to arc_end-1
for( int i=arc_start+1; i!=arc_end; )
{
if( i > ic_end )
i = ic_start;
poly->SetUtility( i, 1 );
if( i == ic_end )
i = ic_start;
else
i++;
}
break;
}
}
if( bFound )
break;
}
}
if( bFound )
break;
}
}
if( bFound )
(*arc_array)[iarc].bFound = TRUE;
}
// now delete all marked corners
for( int ip=0; ip<n_polys; ip++ )
{
if( ip == 0 )
poly = this;
else
poly = (*pa)[ip-1];
for( int ic=poly->GetNumCorners()-1; ic>=0; ic-- )
{
if( poly->GetUtility(ic) )
poly->DeleteCorner( ic, FALSE );
}
}
return 0;
}
// initialize new polyline
// set layer, width, selection box size, starting point, id and pointer
//
// if sel_box = 0, don't create selection elements at all
//
// if polyline is board outline, enter with:
// id.type = ID_BOARD
// id.st = ID_BOARD_OUTLINE
// id.i = 0
// ptr = NULL
//
// if polyline is copper area, enter with:
// id.type = ID_NET;
// id.st = ID_AREA
// id.i = index to area
// ptr = pointer to net
//
void CPolyLine::Start( int layer, int w, int sel_box, int x, int y,
int hatch )
{
m_layer = layer;
m_Width = w;
m_sel_box = sel_box;
m_HatchStyle = hatch;
CPolyPt poly_pt( x, y );
poly_pt.end_contour = FALSE;
corner.push_back(poly_pt);
side_style.push_back(0);
}
// add a corner to unclosed polyline
//
void CPolyLine::AppendCorner( int x, int y, int style, BOOL bDraw )
{
Undraw();
CPolyPt poly_pt( x, y );
poly_pt.end_contour = FALSE;
// add entries for new corner and side
corner.push_back(poly_pt);
side_style.push_back(style);
if( corner.size() > 0 && !corner[corner.size()-1].end_contour )
side_style[corner.size()-1] = style;
int dl_type;
if( style == CPolyLine::STRAIGHT )
dl_type = DL_LINE;
else if( style == CPolyLine::ARC_CW )
dl_type = DL_ARC_CW;
else if( style == CPolyLine::ARC_CCW )
dl_type = DL_ARC_CCW;
else
ASSERT(0);
if( bDraw )
Draw();
}
// close last polyline contour
//
void CPolyLine::Close( int style, BOOL bDraw )
{
if( GetClosed() )
ASSERT(0);
Undraw();
side_style[corner.size()-1] = style;
corner[corner.size()-1].end_contour = TRUE;
if( bDraw )
Draw();
}
// move corner of polyline
//
void CPolyLine::MoveCorner( int ic, int x, int y )
{
Undraw();
corner[ic].x = x;
corner[ic].y = y;
Draw();
}
// delete corner and adjust arrays
//
void CPolyLine::DeleteCorner( int ic, BOOL bDraw )
{
Undraw();
int icont = GetContour( ic );
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
BOOL bClosed = icont < GetNumContours()-1 || GetClosed();
if( !bClosed )
{
// open contour, must be last contour
corner.erase( corner.begin() + ic );
if( ic != istart )
side_style.erase( side_style.begin() + ic-1 );
}
else
{
// closed contour
corner.erase( corner.begin() + ic );
side_style.erase( side_style.begin() + ic );
if( ic == iend )
corner[ic-1].end_contour = TRUE;
}
if( bClosed && GetContourSize(icont) < 3 )
{
// delete the entire contour
RemoveContour( icont );
}
if( bDraw )
Draw();
}
void CPolyLine::RemoveContour( int icont )
{
Undraw();
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
if( icont == 0 && GetNumContours() == 1 )
{
// remove the only contour
ASSERT(0);
}
else if( icont == GetNumContours()-1 )
{
// remove last contour
corner.erase( corner.begin() + icont, corner.end() );
side_style.erase( side_style.begin() + icont, side_style.end() );
}
else
{
// remove closed contour
for( int ic=iend; ic>=istart; ic-- )
{
corner.erase( corner.begin() + ic );
side_style.erase( side_style.begin() + ic );
}
}
Draw();
}
/** Function InsertCorner
* insert a new corner between two existing corners
* @param ic = index for the insertion point: the corner is inserted AFTER ic
* @param x, y = coordinates corner to insert
*/
void CPolyLine::InsertCorner( int ic, int x, int y )
{
Undraw();
if ( (unsigned)(ic) >= corner.size() )
{
corner.push_back( CPolyPt(x,y) );
side_style.push_back( STRAIGHT );
}
else
{
corner.insert( corner.begin() + ic + 1, CPolyPt(x,y) );
side_style.insert( side_style.begin() + ic + 1, STRAIGHT );
}
if( (unsigned)(ic+1) < corner.size() )
{
if( corner[ic].end_contour )
{
corner[ic+1].end_contour = TRUE;
corner[ic].end_contour = FALSE;
}
}
Draw();
}
// undraw polyline by removing all graphic elements from display list
//
void CPolyLine::Undraw()
{
if( m_dlist && bDrawn )
{
// remove display elements, if present
for( unsigned i=0; i<dl_side.size(); i++ )
m_dlist->Remove( dl_side[i] );
for( unsigned i=0; i<dl_side_sel.size(); i++ )
m_dlist->Remove( dl_side_sel[i] );
for( unsigned i=0; i<dl_corner_sel.size(); i++ )
m_dlist->Remove( dl_corner_sel[i] );
// remove pointers
dl_side.clear();
dl_side_sel.clear();
dl_corner_sel.clear();
}
m_HatchLines.clear();
bDrawn = FALSE;
}
// draw polyline by adding all graphics to display list
// if side style is ARC_CW or ARC_CCW but endpoints are not angled,
// convert to STRAIGHT
//
void CPolyLine::Draw( CDisplayList * dl )
{
// first, undraw if necessary
if( bDrawn )
Undraw();
// use new display list if provided
if( dl )
m_dlist = dl;
#if 0
int i_start_contour = 0;
if( m_dlist )
{
// set up std::vectors
dl_side.SetSize( corner.size() );
if( m_sel_box )
{
dl_side_sel.SetSize( corner.size() );
dl_corner_sel.SetSize( corner.size() );
}
else
{
dl_side_sel.clear();
dl_corner_sel.clear();
}
// now draw elements
for( int ic=0; ic<corner.size(); ic++ )
{
m_id.ii = ic;
int xi = corner[ic].x;
int yi = corner[ic].y;
int xf, yf;
if( corner[ic].end_contour == FALSE && ic < corner.size()-1 )
{
xf = corner[ic+1].x;
yf = corner[ic+1].y;
}
else
{
xf = corner[i_start_contour].x;
yf = corner[i_start_contour].y;
i_start_contour = ic+1;
}
// draw
if( m_sel_box )
{
m_id.sst = ID_SEL_CORNER;
dl_corner_sel[ic] = m_dlist->AddSelector( m_id, m_ptr, m_layer, DL_HOLLOW_RECT,
1, 0, 0, xi-m_sel_box, yi-m_sel_box,
xi+m_sel_box, yi+m_sel_box, 0, 0 );
}
if( ic<(corner.size()-1) || corner[ic].end_contour )
{
// draw side
if( xi == xf || yi == yf )
{
// if endpoints not angled, make side STRAIGHT
side_style[ic] = STRAIGHT;
}
int g_type = DL_LINE;
if( side_style[ic] == STRAIGHT )
g_type = DL_LINE;
else if( side_style[ic] == ARC_CW )
g_type = DL_ARC_CW;
else if( side_style[ic] == ARC_CCW )
g_type = DL_ARC_CCW;
m_id.sst = ID_SIDE;
dl_side[ic] = m_dlist->Add( m_id, m_ptr, m_layer, g_type,
1, m_w, 0, xi, yi, xf, yf, 0, 0 );
if( m_sel_box )
{
m_id.sst = ID_SEL_SIDE;
dl_side_sel[ic] = m_dlist->AddSelector( m_id, m_ptr, m_layer, g_type,
1, m_w, 0, xi, yi, xf, yf, 0, 0 );
}
}
}
// if( m_HatchStyle )
// Hatch();
}
#endif
Hatch();
bDrawn = TRUE;
}
// start dragging new corner to be inserted into side, make side and hatching invisible
//
void CPolyLine::StartDraggingToInsertCorner( CDC * pDC, int ic, int x, int y )
{
if( !m_dlist )
ASSERT(0);
int icont = GetContour( ic );
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
int post_c;
if( ic == iend )
post_c = istart;
else
post_c = ic + 1;
int xi = corner[ic].x;
int yi = corner[ic].y;
int xf = corner[post_c].x;
int yf = corner[post_c].y;
m_dlist->StartDraggingLineVertex( pDC, x, y, xi, yi, xf, yf,
LAY_SELECTION, LAY_SELECTION, 1, 1, DSS_STRAIGHT, DSS_STRAIGHT,
0, 0, 0, 0, 1 );
m_dlist->CancelHighLight();
m_dlist->Set_visible( dl_side[ic], 0 );
/* for( int ih=0; ih<m_nhatch; ih++ )
m_dlist->Set_visible( dl_hatch[ih], 0 );
*/
}
// cancel dragging inserted corner, make side and hatching visible again
//
void CPolyLine::CancelDraggingToInsertCorner( int ic )
{
if( !m_dlist )
ASSERT(0);
int post_c;
if( ic == (int)(corner.size()-1) )
post_c = 0;
else
post_c = ic + 1;
m_dlist->StopDragging();
/* m_dlist->Set_visible( dl_side[ic], 1 );
for( int ih=0; ih<m_nhatch; ih++ )
m_dlist->Set_visible( dl_hatch[ih], 1 );
*/
}
// start dragging corner to new position, make adjacent sides and hatching invisible
//
void CPolyLine::StartDraggingToMoveCorner( CDC * pDC, int ic, int x, int y )
{
if( !m_dlist )
ASSERT(0);
// see if corner is the first or last corner of an open contour
int icont = GetContour( ic );
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
if( !GetClosed()
&& icont == GetNumContours() - 1
&& (ic == istart || ic == iend) )
{
// yes
int style, xi, yi, iside;
if( ic == istart )
{
// first corner
iside = ic;
xi = GetX( ic+1 );
yi = GetY( ic+1 );
style = GetSideStyle( iside );
// reverse arc since we are drawing from corner 1 to 0
if( style == CPolyLine::ARC_CW )
style = CPolyLine::ARC_CCW;
else if( style == CPolyLine::ARC_CCW )
style = CPolyLine::ARC_CW;
}
else
{
// last corner
iside = ic - 1;
xi = GetX( ic-1 );
yi = GetY( ic-1);
style = GetSideStyle( iside );
}
m_dlist->StartDraggingArc( pDC, style, GetX(ic), GetY(ic), xi, yi, LAY_SELECTION, 1, 1 );
m_dlist->CancelHighLight();
m_dlist->Set_visible( dl_side[iside], 0 );
/* for( int ih=0; ih<m_nhatch; ih++ )
m_dlist->Set_visible( dl_hatch[ih], 0 );
*/
}
else
{
// no
// get indexes for preceding and following corners
int pre_c, post_c;
int poly_side_style1, poly_side_style2;
int style1, style2;
if( ic == istart )
{
pre_c = iend;
post_c = istart+1;
poly_side_style1 = side_style[iend];
poly_side_style2 = side_style[istart];
}
else if( ic == iend )
{
// last side
pre_c = ic-1;
post_c = istart;
poly_side_style1 = side_style[ic-1];
poly_side_style2 = side_style[ic];
}
else
{
pre_c = ic-1;
post_c = ic+1;
poly_side_style1 = side_style[ic-1];
poly_side_style2 = side_style[ic];
}
if( poly_side_style1 == STRAIGHT )
style1 = DSS_STRAIGHT;
else if( poly_side_style1 == ARC_CW )
style1 = DSS_ARC_CW;
else if( poly_side_style1 == ARC_CCW )
style1 = DSS_ARC_CCW;
if( poly_side_style2 == STRAIGHT )
style2 = DSS_STRAIGHT;
else if( poly_side_style2 == ARC_CW )
style2 = DSS_ARC_CW;
else if( poly_side_style2 == ARC_CCW )
style2 = DSS_ARC_CCW;
int xi = corner[pre_c].x;
int yi = corner[pre_c].y;
int xf = corner[post_c].x;
int yf = corner[post_c].y;
m_dlist->StartDraggingLineVertex( pDC, x, y, xi, yi, xf, yf,
LAY_SELECTION, LAY_SELECTION, 1, 1, style1, style2,
0, 0, 0, 0, 1 );
m_dlist->CancelHighLight();
m_dlist->Set_visible( dl_side[pre_c], 0 );
m_dlist->Set_visible( dl_side[ic], 0 );
/* for( int ih=0; ih<m_nhatch; ih++ )
m_dlist->Set_visible( dl_hatch[ih], 0 );
*/ }
}
// cancel dragging corner to new position, make sides and hatching visible again
//
// highlight side by drawing line over it
//
void CPolyLine::HighlightSide( int is )
{
if( !m_dlist )
ASSERT(0);
if( GetClosed() && is >= (int)corner.size() )
return;
if( !GetClosed() && is >= (int)(corner.size()-1) )
return;
int style;
if( side_style[is] == CPolyLine::STRAIGHT )
style = DL_LINE;
else if( side_style[is] == CPolyLine::ARC_CW )
style = DL_ARC_CW;
else if( side_style[is] == CPolyLine::ARC_CCW )
style = DL_ARC_CCW;
m_dlist->HighLight( style,
m_dlist->Get_x( dl_side_sel[is] ),
m_dlist->Get_y( dl_side_sel[is] ),
m_dlist->Get_xf( dl_side_sel[is] ),
m_dlist->Get_yf( dl_side_sel[is] ),
m_dlist->Get_w( dl_side_sel[is]) );
}
int CPolyLine::GetX( int ic )
{
return corner[ic].x;
}
int CPolyLine::GetY( int ic )
{
return corner[ic].y;
}
int CPolyLine::GetEndContour( int ic )
{
return corner[ic].end_contour;
}
CRect CPolyLine::GetBounds()
{
CRect r = GetCornerBounds();
r.left -= m_Width/2;
r.right += m_Width/2;
r.bottom -= m_Width/2;
r.top += m_Width/2;
return r;
}
CRect CPolyLine::GetCornerBounds()
{
CRect r;
r.left = r.bottom = INT_MAX;
r.right = r.top = INT_MIN;
for( unsigned i=0; i<corner.size(); i++ )
{
r.left = min( r.left, corner[i].x );
r.right = max( r.right, corner[i].x );
r.bottom = min( r.bottom, corner[i].y );
r.top = max( r.top, corner[i].y );
}
return r;
}
CRect CPolyLine::GetCornerBounds( int icont )
{
CRect r;
r.left = r.bottom = INT_MAX;
r.right = r.top = INT_MIN;
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
for( int i=istart; i<=iend; i++ )
{
r.left = min( r.left, corner[i].x );
r.right = max( r.right, corner[i].x );
r.bottom = min( r.bottom, corner[i].y );
r.top = max( r.top, corner[i].y );
}
return r;
}
int CPolyLine::GetNumCorners()
{
return corner.size();
}
int CPolyLine::GetNumSides()
{
if( GetClosed() )
return corner.size();
else
return corner.size()-1;
}
int CPolyLine::GetW()
{
return m_Width;
}
int CPolyLine::GetSelBoxSize()
{
return m_sel_box;
}
int CPolyLine::GetNumContours()
{
int ncont = 0;
if( !corner.size() )
return 0;
for( unsigned ic=0; ic<corner.size(); ic++ )
if( corner[ic].end_contour )
ncont++;
if( !corner[corner.size()-1].end_contour )
ncont++;
return ncont;
}
int CPolyLine::GetContour( int ic )
{
int ncont = 0;
for( int i=0; i<ic; i++ )
{
if( corner[i].end_contour )
ncont++;
}
return ncont;
}
int CPolyLine::GetContourStart( int icont )
{
if( icont == 0 )
return 0;
int ncont = 0;
for( unsigned i=0; i<corner.size(); i++ )
{
if( corner[i].end_contour )
{
ncont++;
if( ncont == icont )
return i+1;
}
}
ASSERT(0);
return 0;
}
int CPolyLine::GetContourEnd( int icont )
{
if( icont < 0 )
return 0;
if( icont == GetNumContours()-1 )
return corner.size()-1;
int ncont = 0;
for( unsigned i=0; i<corner.size(); i++ )
{
if( corner[i].end_contour )
{
if( ncont == icont )
return i;
ncont++;
}
}
ASSERT(0);
return 0;
}
int CPolyLine::GetContourSize( int icont )
{
return GetContourEnd(icont) - GetContourStart(icont) + 1;
}
void CPolyLine::SetSideStyle( int is, int style )
{
Undraw();
CPoint p1, p2;
if( is == (int)(corner.size()-1) )
{
p1.x = corner[corner.size()-1].x;
p1.y = corner[corner.size()-1].y;
p2.x = corner[0].x;
p2.y = corner[0].y;
}
else
{
p1.x = corner[is].x;
p1.y = corner[is].y;
p2.x = corner[is+1].x;
p2.y = corner[is+1].y;
}
if( p1.x == p2.x || p1.y == p2.y )
side_style[is] = STRAIGHT;
else
side_style[is] = style;
Draw();
}
int CPolyLine::GetSideStyle( int is )
{
return side_style[is];
}
int CPolyLine::GetClosed()
{
if( corner.size() == 0 )
return 0;
else
return corner[corner.size()-1].end_contour;
}
// draw hatch lines
//
void CPolyLine::Hatch()
{
m_HatchLines.clear();
if( m_HatchStyle == NO_HATCH )
{
return;
}
int layer = m_layer;
// if( /*m_dlist && */GetClosed() )
{
enum {
MAXPTS = 100,
MAXLINES = 1000
};
int xx[MAXPTS], yy[MAXPTS];
// define range for hatch lines
int min_x = corner[0].x;
int max_x = corner[0].x;
int min_y = corner[0].y;
int max_y = corner[0].y;
for( unsigned ic = 1; ic < corner.size(); ic++ )
{
if( corner[ic].x < min_x )
min_x = corner[ic].x;
if( corner[ic].x > max_x )
max_x = corner[ic].x;
if( corner[ic].y < min_y )
min_y = corner[ic].y;
if( corner[ic].y > max_y )
max_y = corner[ic].y;
}
int slope_flag = (layer & 1) ? 1 : -1; // 1 or -1
double slope = 0.707106*slope_flag;
int spacing;
if( m_HatchStyle == DIAGONAL_EDGE )
spacing = 10*PCBU_PER_MIL;
else
spacing = 50*PCBU_PER_MIL;
int max_a, min_a;
if( slope_flag == 1 )
{
max_a = (int)(max_y - slope*min_x);
min_a = (int)(min_y - slope*max_x);
}
else
{
max_a = (int)(max_y - slope*max_x);
min_a = (int)(min_y - slope*min_x);
}
min_a = (min_a/spacing)*spacing;
int offset;
if( layer < (LAY_TOP_COPPER+2) )
offset = 0;
else if( layer < (LAY_TOP_COPPER+4) )
offset = spacing/2;
else if( layer < (LAY_TOP_COPPER+6) )
offset = spacing/4;
else if( layer < (LAY_TOP_COPPER+8) )
offset = 3*spacing/4;
else if( layer < (LAY_TOP_COPPER+10) )
offset = 1*spacing/8;
else if( layer < (LAY_TOP_COPPER+12) )
offset = 3*spacing/8;
else if( layer < (LAY_TOP_COPPER+14) )
offset = 5*spacing/8;
else if( layer < (LAY_TOP_COPPER+16) )
offset = 7*spacing/8;
else
ASSERT(0);
min_a += offset;
// now calculate and draw hatch lines
int nc = corner.size();
// loop through hatch lines
for( int a=min_a; a<max_a; a+=spacing )
{
// get intersection points for this hatch line
int nloops = 0;
int npts;
// make this a loop in case my homebrew hatching algorithm screws up
do
{
npts = 0;
int i_start_contour = 0;
for( int ic=0; ic<nc; ic++ )
{
double x, y, x2, y2;
int ok;
if( corner[ic].end_contour )
{
ok = FindLineSegmentIntersection( a, slope,
corner[ic].x, corner[ic].y,
corner[i_start_contour].x, corner[i_start_contour].y,
side_style[ic],
&x, &y, &x2, &y2 );
i_start_contour = ic + 1;
}
else
{
ok = FindLineSegmentIntersection( a, slope,
corner[ic].x, corner[ic].y,
corner[ic+1].x, corner[ic+1].y,
side_style[ic],
&x, &y, &x2, &y2 );
}
if( ok )
{
xx[npts] = (int)x;
yy[npts] = (int)y;
npts++;
ASSERT( npts<MAXPTS ); // overflow
}
if( ok == 2 )
{
xx[npts] = (int)x2;
yy[npts] = (int)y2;
npts++;
ASSERT( npts<MAXPTS ); // overflow
}
}
nloops++;
a += PCBU_PER_MIL/100;
} while( npts%2 != 0 && nloops < 3 );
ASSERT( npts%2==0 ); // odd number of intersection points, error
// sort points in order of descending x (if more than 2)
if( npts>2 )
{
for( int istart=0; istart<(npts-1); istart++ )
{
int max_x = INT_MIN;
int imax;
for( int i=istart; i<npts; i++ )
{
if( xx[i] > max_x )
{
max_x = xx[i];
imax = i;
}
}
int temp = xx[istart];
xx[istart] = xx[imax];
xx[imax] = temp;
temp = yy[istart];
yy[istart] = yy[imax];
yy[imax] = temp;
}
}
// draw lines
for( int ip=0; ip<npts; ip+=2 )
{
double dx = xx[ip+1] - xx[ip];
if( m_HatchStyle == DIAGONAL_FULL || fabs(dx) < 40*NM_PER_MIL )
{
m_HatchLines.push_back(CSegment(xx[ip], yy[ip], xx[ip+1], yy[ip+1]) );
}
else
{
double dy = yy[ip+1] - yy[ip];
double slope = dy/dx;
if( dx > 0 )
dx = 20*NM_PER_MIL;
else
dx = -20*NM_PER_MIL;
double x1 = xx[ip] + dx;
double x2 = xx[ip+1] - dx;
double y1 = yy[ip] + dx*slope;
double y2 = yy[ip+1] - dx*slope;
m_HatchLines.push_back(CSegment(xx[ip], yy[ip], to_int(x1), to_int(y1)) );
m_HatchLines.push_back(CSegment(xx[ip+1], yy[ip+1], to_int(x2), to_int(y2)) );
}
}
} // end for
}
}
// test to see if a point is inside polyline
//
BOOL CPolyLine::TestPointInside( int x, int y )
{
enum { MAXPTS = 100 };
if( !GetClosed() )
ASSERT(0);
// define line passing through (x,y), with slope = 2/3;
// get intersection points
double xx[MAXPTS], yy[MAXPTS];
double slope = (double)2.0/3.0;
double a = y - slope*x;
int nloops = 0;
int npts;
// make this a loop so if my homebrew algorithm screws up, we try it again
do
{
// now find all intersection points of line with polyline sides
npts = 0;
for( int icont=0; icont<GetNumContours(); icont++ )
{
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
for( int ic=istart; ic<=iend; ic++ )
{
double x, y, x2, y2;
int ok;
if( ic == istart )
ok = FindLineSegmentIntersection( a, slope,
corner[iend].x, corner[iend].y,
corner[istart].x, corner[istart].y,
side_style[corner.size()-1],
&x, &y, &x2, &y2 );
else
ok = FindLineSegmentIntersection( a, slope,
corner[ic-1].x, corner[ic-1].y,
corner[ic].x, corner[ic].y,
side_style[ic-1],
&x, &y, &x2, &y2 );
if( ok )
{
xx[npts] = (int)x;
yy[npts] = (int)y;
npts++;
ASSERT( npts<MAXPTS ); // overflow
}
if( ok == 2 )
{
xx[npts] = (int)x2;
yy[npts] = (int)y2;
npts++;
ASSERT( npts<MAXPTS ); // overflow
}
}
}
nloops++;
a += PCBU_PER_MIL/100;
} while( npts%2 != 0 && nloops < 3 );
ASSERT( npts%2==0 ); // odd number of intersection points, error
// count intersection points to right of (x,y), if odd (x,y) is inside polyline
int ncount = 0;
for( int ip=0; ip<npts; ip++ )
{
if( xx[ip] == x && yy[ip] == y )
return FALSE; // (x,y) is on a side, call it outside
else if( xx[ip] > x )
ncount++;
}
if( ncount%2 )
return TRUE;
else
return FALSE;
}
// test to see if a point is inside polyline contour
//
BOOL CPolyLine::TestPointInsideContour( int icont, int x, int y )
{
if( icont >= GetNumContours() )
return FALSE;
enum { MAXPTS = 100 };
if( !GetClosed() )
ASSERT(0);
// define line passing through (x,y), with slope = 2/3;
// get intersection points
double xx[MAXPTS], yy[MAXPTS];
double slope = (double)2.0/3.0;
double a = y - slope*x;
int nloops = 0;
int npts;
// make this a loop so if my homebrew algorithm screws up, we try it again
do
{
// now find all intersection points of line with polyline sides
npts = 0;
int istart = GetContourStart( icont );
int iend = GetContourEnd( icont );
for( int ic=istart; ic<=iend; ic++ )
{
double x, y, x2, y2;
int ok;
if( ic == istart )
ok = FindLineSegmentIntersection( a, slope,
corner[iend].x, corner[iend].y,
corner[istart].x, corner[istart].y,
side_style[corner.size()-1],
&x, &y, &x2, &y2 );
else
ok = FindLineSegmentIntersection( a, slope,
corner[ic-1].x, corner[ic-1].y,
corner[ic].x, corner[ic].y,
side_style[ic-1],
&x, &y, &x2, &y2 );
if( ok )
{
xx[npts] = (int)x;
yy[npts] = (int)y;
npts++;
ASSERT( npts<MAXPTS ); // overflow
}
if( ok == 2 )
{
xx[npts] = (int)x2;
yy[npts] = (int)y2;
npts++;
ASSERT( npts<MAXPTS ); // overflow
}
}
nloops++;
a += PCBU_PER_MIL/100;
} while( npts%2 != 0 && nloops < 3 );
ASSERT( npts%2==0 ); // odd number of intersection points, error
// count intersection points to right of (x,y), if odd (x,y) is inside polyline
int ncount = 0;
for( int ip=0; ip<npts; ip++ )
{
if( xx[ip] == x && yy[ip] == y )
return FALSE; // (x,y) is on a side, call it outside
else if( xx[ip] > x )
ncount++;
}
if( ncount%2 )
return TRUE;
else
return FALSE;
}
// Test for intersection of sides
//
int CPolyLine::TestIntersection( CPolyLine * poly )
{
if( !GetClosed() )
ASSERT(0);
if( !poly->GetClosed() )
ASSERT(0);
for( int ic=0; ic<GetNumContours(); ic++ )
{
int istart = GetContourStart(ic);
int iend = GetContourEnd(ic);
for( int is=istart; is<=iend; is++ )
{
int xf, yf;
if( is < GetContourEnd(ic) )
{
xf = GetX(is+1);
yf = GetY(is+1);
}
else
{
xf = GetX(istart);
yf = GetY(istart);
}
for( int ic2=0; ic2<poly->GetNumContours(); ic2++ )
{
int istart2 = poly->GetContourStart(ic2);
int iend2 = poly->GetContourEnd(ic2);
for( int is2=istart2; is2<=iend2; is2++ )
{
int xf2, yf2;
if( is2 < poly->GetContourEnd(ic2) )
{
xf2 = poly->GetX(is2+1);
yf2 = poly->GetY(is2+1);
}
else
{
xf2 = poly->GetX(istart2);
yf2 = poly->GetY(istart2);
}
// test for intersection between side and side2
}
}
}
}
return 0;
}
// set selection box size
//
void CPolyLine::SetSelBoxSize( int sel_box )
{
// Undraw();
m_sel_box = sel_box;
// Draw();
}
// set pointer to display list, and draw into display list
//
void CPolyLine::SetDisplayList( CDisplayList * dl )
{
if( m_dlist )
Undraw();
m_dlist = dl;
if( m_dlist )
Draw();
}
// copy data from another poly, but don't draw it
//
void CPolyLine::Copy( CPolyLine * src )
{
Undraw();
m_dlist = src->m_dlist;
m_sel_box = src->m_sel_box;
// copy corners
for( unsigned i=0; i< src->corner.size(); i++ )
corner.push_back(src->corner[i]);
// copy side styles
int nsides = src->GetNumSides();
side_style.SetSize(nsides);
for( int i=0; i<nsides; i++ )
side_style[i] = src->side_style[i];
// don't copy the Gpc_poly, just clear the old one
FreeGpcPoly();
}
void CPolyLine::MoveOrigin( int x_off, int y_off )
{
Undraw();
for( int ic=0; ic < GetNumCorners(); ic++ )
{
SetX( ic, GetX(ic) + x_off );
SetY( ic, GetY(ic) + y_off );
}
Draw();
}
// Set various parameters:
// the calling function should Undraw() before calling them,
// and Draw() after
//
void CPolyLine::SetX( int ic, int x ) { corner[ic].x = x; }
void CPolyLine::SetY( int ic, int y ) { corner[ic].y = y; }
void CPolyLine::SetEndContour( int ic, BOOL end_contour ) { corner[ic].end_contour = end_contour; }
// Create CPolyLine for a pad
//
CPolyLine * CPolyLine::MakePolylineForPad( int type, int x, int y, int w, int l, int r, int angle )
{
CPolyLine * poly = new CPolyLine;
int dx = l/2;
int dy = w/2;
if( angle%180 == 90 )
{
dx = w/2;
dy = l/2;
}
if( type == PAD_ROUND )
{
poly->Start( 0, 0, 0, x-dx, y, 0 );
poly->AppendCorner( x, y+dy, ARC_CW, 0 );
poly->AppendCorner( x+dx, y, ARC_CW, 0 );
poly->AppendCorner( x, y-dy, ARC_CW, 0 );
poly->Close( ARC_CW );
}
return poly;
}
// Add cutout for a pad
// Convert arcs to multiple straight lines
// Do NOT draw or undraw
//
void CPolyLine::AddContourForPadClearance( int type, int x, int y, int w,
int l, int r, int angle, int fill_clearance,
int hole_w, int hole_clearance, BOOL bThermal, int spoke_w )
{
int dx = l/2;
int dy = w/2;
if( angle%180 == 90 )
{
dx = w/2;
dy = l/2;
}
int x_clearance = max( fill_clearance, hole_clearance+hole_w/2-dx);
int y_clearance = max( fill_clearance, hole_clearance+hole_w/2-dy);
dx += x_clearance;
dy += y_clearance;
if( !bThermal )
{
// normal clearance
if( type == PAD_ROUND || (type == PAD_NONE && hole_w > 0) )
{
AppendCorner( x-dx, y, ARC_CW, 0 );
AppendCorner( x, y+dy, ARC_CW, 0 );
AppendCorner( x+dx, y, ARC_CW, 0 );
AppendCorner( x, y-dy, ARC_CW, 0 );
Close( ARC_CW );
}
else if( type == PAD_SQUARE || type == PAD_RECT
|| type == PAD_RRECT || type == PAD_OVAL )
{
AppendCorner( x-dx, y-dy, STRAIGHT, 0 );
AppendCorner( x+dx, y-dy, STRAIGHT, 0 );
AppendCorner( x+dx, y+dy, STRAIGHT, 0 );
AppendCorner( x-dx, y+dy, STRAIGHT, 0 );
Close( STRAIGHT );
}
}
else
{
// thermal relief
if( type == PAD_ROUND || (type == PAD_NONE && hole_w > 0) )
{
// draw 4 "wedges"
double r = max(w/2 + fill_clearance, hole_w/2 + hole_clearance);
double start_angle = asin( spoke_w/(2.0*r) );
double th1, th2, corner_x, corner_y;
for( int i=0; i<4; i++ )
{
if( i == 0 )
{
corner_x = spoke_w/2;
corner_y = spoke_w/2;
th1 = start_angle;
th2 = pi/2.0 - start_angle;
}
else if( i == 1 )
{
corner_x = -spoke_w/2;
corner_y = spoke_w/2;
th1 = pi/2.0 + start_angle;
th2 = pi - start_angle;
}
else if( i == 2 )
{
corner_x = -spoke_w/2;
corner_y = -spoke_w/2;
th1 = -pi + start_angle;
th2 = -pi/2.0 - start_angle;
}
else if( i == 3 )
{
corner_x = spoke_w/2;
corner_y = -spoke_w/2;
th1 = -pi/2.0 + start_angle;
th2 = -start_angle;
}
AppendCorner( to_int(x+corner_x), to_int(y+corner_y), STRAIGHT, 0 );
AppendCorner( to_int(x+r*cos(th1)), to_int(y+r*sin(th1)), STRAIGHT, 0 );
AppendCorner( to_int(x+r*cos(th2)), to_int(y+r*sin(th2)), ARC_CCW, 0 );
Close( STRAIGHT );
}
}
else if( type == PAD_SQUARE || type == PAD_RECT
|| type == PAD_RRECT || type == PAD_OVAL )
{
// draw 4 rectangles
int xL = x - dx;
int xR = x - spoke_w/2;
int yB = y - dy;
int yT = y - spoke_w/2;
AppendCorner( xL, yB, STRAIGHT, 0 );
AppendCorner( xR, yB, STRAIGHT, 0 );
AppendCorner( xR, yT, STRAIGHT, 0 );
AppendCorner( xL, yT, STRAIGHT, 0 );
Close( STRAIGHT );
xL = x + spoke_w/2;
xR = x + dx;
AppendCorner( xL, yB, STRAIGHT, 0 );
AppendCorner( xR, yB, STRAIGHT, 0 );
AppendCorner( xR, yT, STRAIGHT, 0 );
AppendCorner( xL, yT, STRAIGHT, 0 );
Close( STRAIGHT );
xL = x - dx;
xR = x - spoke_w/2;
yB = y + spoke_w/2;
yT = y + dy;
AppendCorner( xL, yB, STRAIGHT, 0 );
AppendCorner( xR, yB, STRAIGHT, 0 );
AppendCorner( xR, yT, STRAIGHT, 0 );
AppendCorner( xL, yT, STRAIGHT, 0 );
Close( STRAIGHT );
xL = x + spoke_w/2;
xR = x + dx;
AppendCorner( xL, yB, STRAIGHT, 0 );
AppendCorner( xR, yB, STRAIGHT, 0 );
AppendCorner( xR, yT, STRAIGHT, 0 );
AppendCorner( xL, yT, STRAIGHT, 0 );
Close( STRAIGHT );
}
}
return;
}
void CPolyLine::AppendArc( int xi, int yi, int xf, int yf, int xc, int yc, int num )
{
// get radius
double r = sqrt( (double)(xi-xc)*(xi-xc) + (double)(yi-yc)*(yi-yc) );
// get angles of start and finish
double th_i = atan2( (double)yi-yc, (double)xi-xc );
double th_f = atan2( (double)yf-yc, (double)xf-xc );
double th_d = (th_f - th_i)/(num-1);
double theta = th_i;
// generate arc
for( int ic=0; ic<num; ic++ )
{
int x = to_int(xc + r*cos(theta));
int y = to_int(yc + r*sin(theta));
AppendCorner( x, y, STRAIGHT, 0 );
theta += th_d;
}
Close( STRAIGHT );
}
void CPolyLine::ClipGpcPolygon( gpc_op op, CPolyLine * clip_poly )
{
gpc_polygon * result = new gpc_polygon;
gpc_polygon_clip( op, m_gpc_poly, clip_poly->GetGpcPoly(), result );
gpc_free_polygon( m_gpc_poly );
delete m_gpc_poly;
m_gpc_poly = result;
}
// PolyLine.h ... definition of CPolyLine class
//
// A polyline contains one or more contours, where each contour
// is defined by a list of corners and side-styles
// There may be multiple contours in a polyline.
// The last contour may be open or closed, any others must be closed.
// All of the corners and side-styles are concatenated into 2 arrays,
// separated by setting the end_contour flag of the last corner of
// each contour.
//
// When used for copper areas, the first contour is the outer edge
// of the area, subsequent ones are "holes" in the copper.
//
// If a CDisplayList pointer is provided, the polyline can draw itself
#ifndef POLYLINE_H
#define POLYLINE_H
#include <vector>
#include "defs-macros.h"
#include "GenericPolygonClipperLibrary.h"
#include "php_polygon.h"
#include "php_polygon_vertex.h"
#include "PolyLine2Kicad.h"
#include "freepcb_ids.h"
#include "freepcbDisplayList.h"
#include "math_for_graphics.h"
class CSegment {
public:
int xi, yi, xf, yf;
CSegment() {};
CSegment(int x0, int y0, int x1, int y1) {
xi = x0; yi = y0; xf = x1; yf = y1; }
};
class CArc {
public:
enum{ MAX_STEP = 50*25400 }; // max step is 20 mils
enum{ MIN_STEPS = 18 }; // min step is 5 degrees
int style;
int xi, yi, xf, yf;
int n_steps; // number of straight-line segments in gpc_poly
BOOL bFound;
};
class CPolyPt
{
public:
CPolyPt( int qx=0, int qy=0, BOOL qf=FALSE )
{ x=qx; y=qy; end_contour=qf; utility = 0; };
int x;
int y;
BOOL end_contour;
int utility;
};
class CPolyLine
{
public:
enum { STRAIGHT, ARC_CW, ARC_CCW }; // side styles
enum { NO_HATCH, DIAGONAL_FULL, DIAGONAL_EDGE }; // hatch styles
enum { DEF_SIZE = 50, DEF_ADD = 50 }; // number of array elements to add at a time
// constructors/destructor
CPolyLine( CDisplayList * dl );
CPolyLine();
~CPolyLine();
// functions for modifying polyline
void Start( int layer, int w, int sel_box, int x, int y,
int hatch );
void AppendCorner( int x, int y, int style = STRAIGHT, BOOL bDraw=TRUE );
void InsertCorner( int ic, int x, int y );
void DeleteCorner( int ic, BOOL bDraw=TRUE );
void MoveCorner( int ic, int x, int y );
void Close( int style = STRAIGHT, BOOL bDraw=TRUE );
void RemoveContour( int icont );
// drawing functions
void HighlightSide( int is );
void HighlightCorner( int ic );
void StartDraggingToInsertCorner( CDC * pDC, int ic, int x, int y);
void StartDraggingToMoveCorner( CDC * pDC, int ic, int x, int y);
void CancelDraggingToInsertCorner( int ic );
void CancelDraggingToMoveCorner( int ic );
void Undraw();
void Draw( CDisplayList * dl = NULL );
void Hatch();
void MakeVisible( BOOL visible = TRUE );
void MoveOrigin( int x_off, int y_off );
// misc. functions
CRect GetBounds();
CRect GetCornerBounds();
CRect GetCornerBounds( int icont );
void Copy( CPolyLine * src );
BOOL TestPointInside( int x, int y );
BOOL TestPointInsideContour( int icont, int x, int y );
int TestIntersection( CPolyLine * poly );
void AppendArc( int xi, int yi, int xf, int yf, int xc, int yc, int num );
// access functions
int GetNumCorners();
int GetNumSides();
int GetClosed();
int GetNumContours();
int GetContour( int ic );
int GetContourStart( int icont );
int GetContourEnd( int icont );
int GetContourSize( int icont );
int GetX( int ic );
int GetY( int ic );
int GetEndContour( int ic );
int GetUtility( int ic ){ return corner[ic].utility; };
void SetUtility( int ic, int utility ){ corner[ic].utility = utility; };
int GetW();
int GetSideStyle( int is );
id GetId();
int GetSelBoxSize();
CDisplayList * GetDisplayList(){ return m_dlist; };
int GetHatch(){ return m_HatchStyle; }
void SetHatch( int hatch ){ Undraw(); m_HatchStyle = hatch; Draw(); };
void SetX( int ic, int x );
void SetY( int ic, int y );
void SetEndContour( int ic, BOOL end_contour );
// void SetLayer( int layer );
void SetW( int w );
void SetSideStyle( int is, int style );
void SetSelBoxSize( int sel_box );
void SetDisplayList( CDisplayList * dl );
// GPC functions
int MakeGpcPoly( int icontour=0, std::vector<CArc> * arc_array=NULL );
int FreeGpcPoly();
gpc_polygon * GetGpcPoly(){ return m_gpc_poly; };
int NormalizeWithGpc( std::vector<CPolyLine*> * pa=NULL, BOOL bRetainArcs=FALSE );
int RestoreArcs( std::vector<CArc> * arc_array, std::vector<CPolyLine*> * pa=NULL );
CPolyLine * MakePolylineForPad( int type, int x, int y, int w, int l, int r, int angle );
void AddContourForPadClearance( int type, int x, int y, int w,
int l, int r, int angle, int fill_clearance,
int hole_w, int hole_clearance, BOOL bThermal=FALSE, int spoke_w=0 );
void ClipGpcPolygon( gpc_op op, CPolyLine * poly );
// PHP functions
int MakePhpPoly();
void FreePhpPoly();
void ClipPhpPolygon( int php_op, CPolyLine * poly );
private:
CDisplayList * m_dlist; // display list
int m_layer; // layer to draw on
int m_Width; // line width
int m_sel_box; // corner selection box width/2
public:
std::vector <CPolyPt> corner; // array of points for corners
std::vector <int> side_style; // array of styles for sides
private:
std::vector <dl_element*> dl_side; // graphic elements
std::vector <dl_element*> dl_side_sel;
std::vector <dl_element*> dl_corner_sel;
public:
int m_HatchStyle; // hatch style, see enum above
std::vector <CSegment> m_HatchLines; // hatch lines
private:
gpc_polygon * m_gpc_poly; // polygon in gpc format
polygon * m_php_poly;
BOOL bDrawn;
};
#endif // #ifndef POLYLINE_H
// PolyLine.h ... definition of CPolyLine class
//
// A polyline contains one or more contours, where each contour
// is defined by a list of corners and side-styles
// There may be multiple contours in a polyline.
// The last contour may be open or closed, any others must be closed.
// All of the corners and side-styles are concatenated into 2 arrays,
// separated by setting the end_contour flag of the last corner of
// each contour.
//
// When used for copper areas, the first contour is the outer edge
// of the area, subsequent ones are "holes" in the copper.
//
// If a CDisplayList pointer is provided, the polyline can draw itself
#ifndef POLYLINE_H
#define POLYLINE_H
#include <vector>
#include "defs-macros.h"
#include "GenericPolygonClipperLibrary.h"
#include "php_polygon.h"
#include "php_polygon_vertex.h"
#include "PolyLine2Kicad.h"
#include "freepcb_ids.h"
#include "freepcbDisplayList.h"
#include "math_for_graphics.h"
class CSegment {
public:
int xi, yi, xf, yf;
CSegment() {};
CSegment(int x0, int y0, int x1, int y1) {
xi = x0; yi = y0; xf = x1; yf = y1; }
};
class CArc {
public:
enum{ MAX_STEP = 50*25400 }; // max step is 20 mils
enum{ MIN_STEPS = 18 }; // min step is 5 degrees
int style;
int xi, yi, xf, yf;
int n_steps; // number of straight-line segments in gpc_poly
BOOL bFound;
};
class CPolyPt
{
public:
CPolyPt( int qx=0, int qy=0, BOOL qf=FALSE )
{ x=qx; y=qy; end_contour=qf; utility = 0; };
int x;
int y;
BOOL end_contour;
int utility;
};
class CPolyLine
{
public:
enum { STRAIGHT, ARC_CW, ARC_CCW }; // side styles
enum { NO_HATCH, DIAGONAL_FULL, DIAGONAL_EDGE }; // hatch styles
enum { DEF_SIZE = 50, DEF_ADD = 50 }; // number of array elements to add at a time
// constructors/destructor
CPolyLine( CDisplayList * dl );
CPolyLine();
~CPolyLine();
// functions for modifying polyline
void Start( int layer, int w, int sel_box, int x, int y,
int hatch );
void AppendCorner( int x, int y, int style = STRAIGHT, BOOL bDraw=TRUE );
void InsertCorner( int ic, int x, int y );
void DeleteCorner( int ic, BOOL bDraw=TRUE );
void MoveCorner( int ic, int x, int y );
void Close( int style = STRAIGHT, BOOL bDraw=TRUE );
void RemoveContour( int icont );
// drawing functions
void HighlightSide( int is );
void HighlightCorner( int ic );
void StartDraggingToInsertCorner( CDC * pDC, int ic, int x, int y);
void StartDraggingToMoveCorner( CDC * pDC, int ic, int x, int y);
void CancelDraggingToInsertCorner( int ic );
void CancelDraggingToMoveCorner( int ic );
void Undraw();
void Draw( CDisplayList * dl = NULL );
void Hatch();
void MakeVisible( BOOL visible = TRUE );
void MoveOrigin( int x_off, int y_off );
// misc. functions
CRect GetBounds();
CRect GetCornerBounds();
CRect GetCornerBounds( int icont );
void Copy( CPolyLine * src );
BOOL TestPointInside( int x, int y );
BOOL TestPointInsideContour( int icont, int x, int y );
int TestIntersection( CPolyLine * poly );
void AppendArc( int xi, int yi, int xf, int yf, int xc, int yc, int num );
// access functions
int GetNumCorners();
int GetNumSides();
int GetClosed();
int GetNumContours();
int GetContour( int ic );
int GetContourStart( int icont );
int GetContourEnd( int icont );
int GetContourSize( int icont );
int GetX( int ic );
int GetY( int ic );
int GetEndContour( int ic );
int GetUtility( int ic ){ return corner[ic].utility; };
void SetUtility( int ic, int utility ){ corner[ic].utility = utility; };
int GetW();
int GetSideStyle( int is );
id GetId();
int GetSelBoxSize();
CDisplayList * GetDisplayList(){ return m_dlist; };
int GetHatch(){ return m_HatchStyle; }
void SetHatch( int hatch ){ Undraw(); m_HatchStyle = hatch; Draw(); };
void SetX( int ic, int x );
void SetY( int ic, int y );
void SetEndContour( int ic, BOOL end_contour );
// void SetLayer( int layer );
void SetW( int w );
void SetSideStyle( int is, int style );
void SetSelBoxSize( int sel_box );
void SetDisplayList( CDisplayList * dl );
// GPC functions
int MakeGpcPoly( int icontour=0, std::vector<CArc> * arc_array=NULL );
int FreeGpcPoly();
gpc_polygon * GetGpcPoly(){ return m_gpc_poly; };
int NormalizeWithGpc( std::vector<CPolyLine*> * pa=NULL, BOOL bRetainArcs=FALSE );
int RestoreArcs( std::vector<CArc> * arc_array, std::vector<CPolyLine*> * pa=NULL );
CPolyLine * MakePolylineForPad( int type, int x, int y, int w, int l, int r, int angle );
void AddContourForPadClearance( int type, int x, int y, int w,
int l, int r, int angle, int fill_clearance,
int hole_w, int hole_clearance, BOOL bThermal=FALSE, int spoke_w=0 );
void ClipGpcPolygon( gpc_op op, CPolyLine * poly );
// PHP functions
int MakePhpPoly();
void FreePhpPoly();
void ClipPhpPolygon( int php_op, CPolyLine * poly );
private:
CDisplayList * m_dlist; // display list
int m_layer; // layer to draw on
int m_Width; // line width
int m_sel_box; // corner selection box width/2
public:
std::vector <CPolyPt> corner; // array of points for corners
std::vector <int> side_style; // array of styles for sides
private:
std::vector <dl_element*> dl_side; // graphic elements
std::vector <dl_element*> dl_side_sel;
std::vector <dl_element*> dl_corner_sel;
public:
int m_HatchStyle; // hatch style, see enum above
std::vector <CSegment> m_HatchLines; // hatch lines
private:
gpc_polygon * m_gpc_poly; // polygon in gpc format
polygon * m_php_poly;
BOOL bDrawn;
};
#endif // #ifndef POLYLINE_H
// PolyLine.h ... definition of CPolyLine class
//
// A polyline contains one or more contours, where each contour
// is defined by a list of corners and side-styles
// There may be multiple contours in a polyline.
// The last contour may be open or closed, any others must be closed.
// All of the corners and side-styles are concatenated into 2 arrays,
// separated by setting the end_contour flag of the last corner of
// each contour.
//
// When used for copper areas, the first contour is the outer edge
// of the area, subsequent ones are "holes" in the copper.
//
// If a CDisplayList pointer is provided, the polyline can draw itself
#ifndef POLYLINE2KICAD_H
#define POLYLINE2KICAD_H
#define PCBU_PER_MIL 10
#define MAX_LAYERS 32
#define NM_PER_MIL 10 // 25400
// pad shapes
enum
{
PAD_NONE = 0,
PAD_ROUND,
PAD_SQUARE,
PAD_RECT,
PAD_RRECT,
PAD_OVAL,
PAD_OCTAGON
};
/*
enum
{
// visible layers
LAY_SELECTION = 0,
LAY_BACKGND,
LAY_VISIBLE_GRID,
LAY_HILITE,
LAY_DRC_ERROR,
LAY_BOARD_OUTLINE,
LAY_RAT_LINE,
LAY_SILK_TOP,
LAY_SILK_BOTTOM,
LAY_SM_TOP,
LAY_SM_BOTTOM,
LAY_PAD_THRU,
LAY_TOP_COPPER,
LAY_BOTTOM_COPPER,
// invisible layers
LAY_MASK_TOP = -100,
LAY_MASK_BOTTOM = -101,
LAY_PASTE_TOP = -102,
LAY_PASTE_BOTTOM = -103
};
*/
#define LAY_SELECTION 0
#define LAY_TOP_COPPER 0
#define CDC wxDC
class wxDC;
#if 0
class dl_element;
class CDisplayList {
public:
void Set_visible(void*, int) {};
int Get_x(void) { return 0;};
int Get_y(void) { return 0;};
void StopDragging(void) {};
void CancelHighLight(void) {};
void StartDraggingLineVertex(...) {};
void Add() {};
};
#endif
class CRect {
public:
int left, right, top, bottom;
};
class CPoint {
public:
int x, y;
public:
CPoint(void) { x = y = 0;};
CPoint(int i, int j) { x = i; y = j;};
};
#endif // #ifndef POLYLINE2KICAD_H
// PolyLine.h ... definition of CPolyLine class
//
// A polyline contains one or more contours, where each contour
// is defined by a list of corners and side-styles
// There may be multiple contours in a polyline.
// The last contour may be open or closed, any others must be closed.
// All of the corners and side-styles are concatenated into 2 arrays,
// separated by setting the end_contour flag of the last corner of
// each contour.
//
// When used for copper areas, the first contour is the outer edge
// of the area, subsequent ones are "holes" in the copper.
//
// If a CDisplayList pointer is provided, the polyline can draw itself
#ifndef POLYLINE2KICAD_H
#define POLYLINE2KICAD_H
#define PCBU_PER_MIL 10
#define MAX_LAYERS 32
#define NM_PER_MIL 10 // 25400
// pad shapes
enum
{
PAD_NONE = 0,
PAD_ROUND,
PAD_SQUARE,
PAD_RECT,
PAD_RRECT,
PAD_OVAL,
PAD_OCTAGON
};
/*
enum
{
// visible layers
LAY_SELECTION = 0,
LAY_BACKGND,
LAY_VISIBLE_GRID,
LAY_HILITE,
LAY_DRC_ERROR,
LAY_BOARD_OUTLINE,
LAY_RAT_LINE,
LAY_SILK_TOP,
LAY_SILK_BOTTOM,
LAY_SM_TOP,
LAY_SM_BOTTOM,
LAY_PAD_THRU,
LAY_TOP_COPPER,
LAY_BOTTOM_COPPER,
// invisible layers
LAY_MASK_TOP = -100,
LAY_MASK_BOTTOM = -101,
LAY_PASTE_TOP = -102,
LAY_PASTE_BOTTOM = -103
};
*/
#define LAY_SELECTION 0
#define LAY_TOP_COPPER 0
#define CDC wxDC
class wxDC;
#if 0
class dl_element;
class CDisplayList {
public:
void Set_visible(void*, int) {};
int Get_x(void) { return 0;};
int Get_y(void) { return 0;};
void StopDragging(void) {};
void CancelHighLight(void) {};
void StartDraggingLineVertex(...) {};
void Add() {};
};
#endif
class CRect {
public:
int left, right, top, bottom;
};
class CPoint {
public:
int x, y;
public:
CPoint(void) { x = y = 0;};
CPoint(int i, int j) { x = i; y = j;};
};
#endif // #ifndef POLYLINE2KICAD_H
/* stuff for class CDisplayList */
#include "PolyLine.h"
dl_element * CDisplayList::Add( id id, void * ptr, int glayer, int gtype, int visible,
int w, int holew, int x, int y, int xf, int yf, int xo, int yo,
int radius, int orig_layer )
{
return NULL;
}
dl_element * CDisplayList::AddSelector( id id, void * ptr, int glayer, int gtype, int visible,
int w, int holew, int x, int y, int xf, int yf, int xo, int yo, int radius )
{
return NULL;
}
void CDisplayList::Set_visible( dl_element * el, int visible )
{
}
int CDisplayList::StopDragging()
{
return 0;
}
int CDisplayList::CancelHighLight()
{
return 0;
}
void CDisplayList::Set_id( dl_element * el, id * id )
{
}
id CDisplayList::Remove( dl_element * element )
{
return 0;
}
int CDisplayList::Get_w( dl_element * el )
{
return 0;
}
int CDisplayList::Get_x( dl_element * el )
{
return 0;
}
int CDisplayList::Get_y( dl_element * el )
{
return 0;
}
int CDisplayList::Get_xf( dl_element * el )
{
return 0;
}
int CDisplayList::Get_yf( dl_element * el )
{
return 0;
}
int CDisplayList::HighLight( int gtype, int x, int y, int xf, int yf, int w, int orig_layer )
{
return 0;
}
int CDisplayList::StartDraggingLineVertex( CDC * pDC, int x, int y, int xi, int yi,
int xf, int yf,
int layer1, int layer2, int w1, int w2,
int style1, int style2,
int layer_no_via, int via_w, int via_holew, int dir,
int crosshair )
{
return 0;
}
int CDisplayList::StartDraggingArc( CDC * pDC, int style, int x, int y, int xi, int yi,
int layer, int w, int crosshair )
{
return 0;
}
/* stuff for class CDisplayList */
#include "PolyLine.h"
dl_element * CDisplayList::Add( id id, void * ptr, int glayer, int gtype, int visible,
int w, int holew, int x, int y, int xf, int yf, int xo, int yo,
int radius, int orig_layer )
{
return NULL;
}
dl_element * CDisplayList::AddSelector( id id, void * ptr, int glayer, int gtype, int visible,
int w, int holew, int x, int y, int xf, int yf, int xo, int yo, int radius )
{
return NULL;
}
void CDisplayList::Set_visible( dl_element * el, int visible )
{
}
int CDisplayList::StopDragging()
{
return 0;
}
int CDisplayList::CancelHighLight()
{
return 0;
}
void CDisplayList::Set_id( dl_element * el, id * id )
{
}
id CDisplayList::Remove( dl_element * element )
{
return 0;
}
int CDisplayList::Get_w( dl_element * el )
{
return 0;
}
int CDisplayList::Get_x( dl_element * el )
{
return 0;
}
int CDisplayList::Get_y( dl_element * el )
{
return 0;
}
int CDisplayList::Get_xf( dl_element * el )
{
return 0;
}
int CDisplayList::Get_yf( dl_element * el )
{
return 0;
}
int CDisplayList::HighLight( int gtype, int x, int y, int xf, int yf, int w, int orig_layer )
{
return 0;
}
int CDisplayList::StartDraggingLineVertex( CDC * pDC, int x, int y, int xi, int yi,
int xf, int yf,
int layer1, int layer2, int w1, int w2,
int style1, int style2,
int layer_no_via, int via_w, int via_holew, int dir,
int crosshair )
{
return 0;
}
int CDisplayList::StartDraggingArc( CDC * pDC, int style, int x, int y, int xi, int yi,
int layer, int w, int crosshair )
{
return 0;
}
// DisplayList.h : header file for CDisplayList class
//
#ifndef FP_DISPLAY_LIST_H
#define FP_DISPLAY_LIST_H
//#define DL_MAX_LAYERS 32
#define DL_MAGIC 2674
#define PCBU_PER_WU 25400 // conversion from PCB units to world units
// graphics element types
enum
{
DL_NONE = 0,
DL_LINE, // line segment with round end-caps
DL_CIRC, // filled circle
DL_HOLLOW_CIRC, // circle outline
DL_DONUT, // annulus
DL_SQUARE, // filled square
DL_RECT, // filled rectangle
DL_RRECT, // filled rounded rectangle
DL_OVAL, // filled oval
DL_OCTAGON, // filled octagon
DL_HOLE, // hole, shown as circle
DL_HOLLOW_RECT, // rectangle outline
DL_RECT_X, // rectangle outline with X
DL_POINT, // shape to highlight a point
DL_ARC_CW, // arc with clockwise curve
DL_ARC_CCW, // arc with counter-clockwise curve
DL_X // X
};
// dragging line shapes
enum
{
DS_NONE = 0,
DS_LINE_VERTEX, // vertex between two lines
DS_LINE, // line
DS_ARC_STRAIGHT, // straight line (used when drawing polylines)
DS_ARC_CW, // clockwise arc (used when drawing polylines)
DS_ARC_CCW // counterclockwise arc (used when drawing polylines)
};
// styles of line segment when dragging line or line vertex
enum
{
DSS_STRAIGHT = 100, // straight line
DSS_ARC_CW, // clockwise arc
DSS_ARC_CCW // counterclockwise arc
};
// inflection modes for DS_LINE and DS_LINE_VERTEX
enum
{
IM_NONE = 0,
IM_90_45,
IM_45_90,
IM_90
};
class CDisplayList;
// this structure contains an element of the display list
class dl_element
{
friend class CDisplayList;
public:
CDisplayList * dlist;
int magic;
dl_element * prev;
dl_element * next;
id m_id; // identifier (see ids.h)
void * ptr; // pointer to object drawing this element
int gtype; // type of primitive
int visible; // 0 to hide
//private:
int sel_vert; // for selection rectangles, 1 if part is vertical
int w; // width (for round or square shapes)
int holew; // hole width (for round holes)
int x_org, y_org; // x origin (for rotation, reflection, etc.)
int x, y; // starting or center position of element
int xf, yf; // opposite corner (for rectangle or line)
int radius; // radius of corners for DL_RRECT
int layer; // layer to draw on
int orig_layer; // for elements on highlight layer,
// the original layer, the highlight will
// only be drawn if this layer is visible
};
class CDisplayList
{
private:
// display-list parameters for each layer
dl_element m_start[MAX_LAYERS], m_end[MAX_LAYERS];
int m_rgb[MAX_LAYERS][3]; // layer colors
BOOL m_vis[MAX_LAYERS]; // layer visibility flags
int m_layer_in_order[MAX_LAYERS]; // array of layers in draw order
int m_order_for_layer[MAX_LAYERS]; // draw order for each layer
// window parameters
int m_pcbu_per_wu; // i.e. nm per world unit
CRect m_client_r; // client rect (pixels)
CRect m_screen_r; // client rect (screen coords)
int m_pane_org_x; // left border of drawing pane (pixels)
int m_pane_org_y; // bottom border of drawing pane (pixels)
int m_bottom_pane_h; // height of bottom pane
CDC * memDC; // pointer to memory DC
double m_scale; // world units per pixel
int m_org_x; // world x-coord of left side of screen (world units)
int m_org_y; // world y-coord of bottom of screen (world units)
int m_max_x; // world x_coord of right side of screen (world units)
int m_max_y; // world y_coord of top of screen (world units)
int w_ext_x, w_ext_y; // window extents (world units)
int v_ext_x, v_ext_y; // viewport extents (pixels)
double m_wu_per_pixel_x; // ratio w_ext_x/v_ext_x (world units per pixel)
double m_wu_per_pixel_y; // ratio w_ext_y/v_ext_y (world units per pixel)
double m_pcbu_per_pixel_x;
double m_pcbu_per_pixel_y;
// general dragging parameters
int m_drag_angle; // angle of rotation of selection rectangle (starts at 0)
int m_drag_side; // 0 = no change, 1 = switch to opposite
int m_drag_vert; // 1 if item being dragged is a vertical part
// parameters for dragging polyline sides and trace segments
// that can be modified while dragging
int m_drag_flag; // 1 if dragging something
int m_drag_shape; // shape
int m_last_drag_shape; // last shape drawn
int m_drag_x; // last cursor position for dragged shape
int m_drag_y;
int m_drag_xi; // start of rubberband drag line
int m_drag_yi;
int m_drag_xf; // end of rubberband drag line
int m_drag_yf;
int m_drag_layer_1; // line layer
int m_drag_w1; // line width
int m_drag_style1; // line style
int m_inflection_mode; // inflection mode
int m_last_inflection_mode; // last mode drawn
// extra parameters when dragging vertex between 2 line segments
int m_drag_style2;
int m_drag_layer_2;
int m_drag_w2;
// parameters used to draw leading via if necessary
int m_drag_layer_no_via;
int m_drag_via_w;
int m_drag_via_holew;
int m_drag_via_drawn;
// arrays of lines and ratlines being dragged
// these can be rotated and flipped while being dragged
int m_drag_layer; // layer
int m_drag_max_lines; // max size of array for line segments
int m_drag_num_lines; // number of line segments to drag
CPoint * m_drag_line_pt; // array of relative coords for line endpoints
int m_drag_max_ratlines; // max size of ratline array
int m_drag_num_ratlines; // number of ratlines to drag
CPoint * m_drag_ratline_start_pt; // absolute coords for ratline start points
CPoint * m_drag_ratline_end_pt; // relative coords for ratline endpoints
int m_drag_ratline_width;
// cursor parameters
int m_cross_hairs; // 0 = none, 1 = cross-hairs, 2 = diagonals
CPoint m_cross_left, m_cross_right, m_cross_top, m_cross_bottom; // end-points
CPoint m_cross_topleft, m_cross_topright, m_cross_botleft, m_cross_botright;
// grid
int m_visual_grid_on;
double m_visual_grid_spacing; // in world units
public:
CDisplayList( int pcbu_per_wu );
~CDisplayList();
void SetVisibleGrid( BOOL on, double grid );
void SetMapping( CRect *client_r, CRect *screen_r, int pane_org_x, int pane_bottom_h, double scale, int org_x, int org_y );
void SetDCToWorldCoords( CDC * pDC, CDC * mDC, int pcbu_org_x, int pcbu_org_y );
void SetLayerRGB( int layer, int r, int g, int b );
void SetLayerVisible( int layer, BOOL vis );
void SetLayerDrawOrder( int layer, int order )
{ m_layer_in_order[order] = layer; m_order_for_layer[layer] = order; };
dl_element * Add( id id, void * ptr, int glayer, int gtype, int visible,
int w, int holew, int x, int y, int xf, int yf, int xo, int yo,
int radius=0, int orig_layer=LAY_SELECTION );
dl_element * AddSelector( id id, void * ptr, int glayer, int gtype, int visible,
int w, int holew, int x, int y, int xf, int yf, int xo, int yo, int radius=0 );
void RemoveAll();
void RemoveAllFromLayer( int layer );
id Remove( dl_element * element );
void Draw( CDC * pDC );
int HighLight( int gtype, int x, int y, int xf, int yf, int w, int orig_layer=LAY_SELECTION );
int CancelHighLight();
void * TestSelect( int x, int y, id * sel_id, int * layer,
id * exclude_id = NULL, void * exclude_ptr = NULL, id * include_id = NULL,
int n_include_ids=1 );
int StartDraggingArray( CDC * pDC, int x, int y, int vert, int layer, int crosshair = 1 );
int StartDraggingRatLine( CDC * pDC, int x, int y, int xf, int yf, int layer,
int w, int crosshair = 1 );
int StartDraggingRectangle( CDC * pDC, int x, int y, int xi, int yi,
int xf, int yf, int vert, int layer );
int StartDraggingLineVertex( CDC * pDC, int x, int y, int xi, int yi,
int xf, int yf,
int layer1, int layer2, int w1, int w2,
int style1, int style2,
int layer_no_via, int via_w, int via_holew, int dir,
int crosshair );
int StartDraggingLine( CDC * pDC, int x, int y, int xi, int yi, int layer1, int w,
int layer_no_via, int via_w, int via_holew,
int crosshair, int style, int inflection_mode );
int StartDraggingArc( CDC * pDC, int style, int x, int y, int xi, int yi,
int layer, int w, int crosshair );
void SetDragArcStyle( int style );
void Drag( CDC * pDC, int x, int y );
int StopDragging();
void ChangeRoutingLayer( CDC * pDC, int layer1, int layer2, int w );
void IncrementDragAngle( CDC * pDC );
int MakeDragLineArray( int num_lines );
int MakeDragRatlineArray( int num_ratlines, int width );
int AddDragLine( CPoint pi, CPoint pf );
int AddDragRatline( CPoint pi, CPoint pf );
int GetDragAngle();
void FlipDragSide( CDC * pDC );
int GetDragSide();
void SetUpCrosshairs( int type, int x, int y );
void SetInflectionMode( int mode ){ m_inflection_mode = mode; };
CPoint ScreenToPCB( CPoint point );
CPoint PCBToScreen( CPoint point );
CPoint WindowToPCB( CPoint point );
// set element parameters
void Set_gtype( dl_element * el, int gtype );
void Set_visible( dl_element * el, int visible );
void Set_sel_vert( dl_element * el, int sel_vert );
void Set_w( dl_element * el, int w );
void Set_holew( dl_element * el, int holew );
void Set_x_org( dl_element * el, int x_org );
void Set_y_org( dl_element * el, int y_org );
void Set_x( dl_element * el, int x );
void Set_y( dl_element * el, int y );
void Set_xf( dl_element * el, int xf );
void Set_yf( dl_element * el, int yf );
void Set_id( dl_element * el, id * id );
void Set_layer( dl_element * el, int layer );
void Set_radius( dl_element * el, int radius );
void Move( dl_element * el, int dx, int dy );
// get element parameters
void * Get_ptr( dl_element * el );
int Get_gtype( dl_element * el );
int Get_visible( dl_element * el );
int Get_sel_vert( dl_element * el );
int Get_w( dl_element * el );
int Get_holew( dl_element * el );
int Get_x_org( dl_element * el );
int Get_y_org( dl_element * el );
int Get_x( dl_element * el );
int Get_y( dl_element * el );
int Get_xf( dl_element * el );
int Get_yf( dl_element * el );
int Get_radius( dl_element * el );
int Get_layer( dl_element * el );
id Get_id( dl_element * el );
};
#endif // #ifndef FP_DISPLAY_LIST_H
// DisplayList.h : header file for CDisplayList class
//
#ifndef FP_DISPLAY_LIST_H
#define FP_DISPLAY_LIST_H
//#define DL_MAX_LAYERS 32
#define DL_MAGIC 2674
#define PCBU_PER_WU 25400 // conversion from PCB units to world units
// graphics element types
enum
{
DL_NONE = 0,
DL_LINE, // line segment with round end-caps
DL_CIRC, // filled circle
DL_HOLLOW_CIRC, // circle outline
DL_DONUT, // annulus
DL_SQUARE, // filled square
DL_RECT, // filled rectangle
DL_RRECT, // filled rounded rectangle
DL_OVAL, // filled oval
DL_OCTAGON, // filled octagon
DL_HOLE, // hole, shown as circle
DL_HOLLOW_RECT, // rectangle outline
DL_RECT_X, // rectangle outline with X
DL_POINT, // shape to highlight a point
DL_ARC_CW, // arc with clockwise curve
DL_ARC_CCW, // arc with counter-clockwise curve
DL_X // X
};
// dragging line shapes
enum
{
DS_NONE = 0,
DS_LINE_VERTEX, // vertex between two lines
DS_LINE, // line
DS_ARC_STRAIGHT, // straight line (used when drawing polylines)
DS_ARC_CW, // clockwise arc (used when drawing polylines)
DS_ARC_CCW // counterclockwise arc (used when drawing polylines)
};
// styles of line segment when dragging line or line vertex
enum
{
DSS_STRAIGHT = 100, // straight line
DSS_ARC_CW, // clockwise arc
DSS_ARC_CCW // counterclockwise arc
};
// inflection modes for DS_LINE and DS_LINE_VERTEX
enum
{
IM_NONE = 0,
IM_90_45,
IM_45_90,
IM_90
};
class CDisplayList;
// this structure contains an element of the display list
class dl_element
{
friend class CDisplayList;
public:
CDisplayList * dlist;
int magic;
dl_element * prev;
dl_element * next;
id m_id; // identifier (see ids.h)
void * ptr; // pointer to object drawing this element
int gtype; // type of primitive
int visible; // 0 to hide
//private:
int sel_vert; // for selection rectangles, 1 if part is vertical
int w; // width (for round or square shapes)
int holew; // hole width (for round holes)
int x_org, y_org; // x origin (for rotation, reflection, etc.)
int x, y; // starting or center position of element
int xf, yf; // opposite corner (for rectangle or line)
int radius; // radius of corners for DL_RRECT
int layer; // layer to draw on
int orig_layer; // for elements on highlight layer,
// the original layer, the highlight will
// only be drawn if this layer is visible
};
class CDisplayList
{
private:
// display-list parameters for each layer
dl_element m_start[MAX_LAYERS], m_end[MAX_LAYERS];
int m_rgb[MAX_LAYERS][3]; // layer colors
BOOL m_vis[MAX_LAYERS]; // layer visibility flags
int m_layer_in_order[MAX_LAYERS]; // array of layers in draw order
int m_order_for_layer[MAX_LAYERS]; // draw order for each layer
// window parameters
int m_pcbu_per_wu; // i.e. nm per world unit
CRect m_client_r; // client rect (pixels)
CRect m_screen_r; // client rect (screen coords)
int m_pane_org_x; // left border of drawing pane (pixels)
int m_pane_org_y; // bottom border of drawing pane (pixels)
int m_bottom_pane_h; // height of bottom pane
CDC * memDC; // pointer to memory DC
double m_scale; // world units per pixel
int m_org_x; // world x-coord of left side of screen (world units)
int m_org_y; // world y-coord of bottom of screen (world units)
int m_max_x; // world x_coord of right side of screen (world units)
int m_max_y; // world y_coord of top of screen (world units)
int w_ext_x, w_ext_y; // window extents (world units)
int v_ext_x, v_ext_y; // viewport extents (pixels)
double m_wu_per_pixel_x; // ratio w_ext_x/v_ext_x (world units per pixel)
double m_wu_per_pixel_y; // ratio w_ext_y/v_ext_y (world units per pixel)
double m_pcbu_per_pixel_x;
double m_pcbu_per_pixel_y;
// general dragging parameters
int m_drag_angle; // angle of rotation of selection rectangle (starts at 0)
int m_drag_side; // 0 = no change, 1 = switch to opposite
int m_drag_vert; // 1 if item being dragged is a vertical part
// parameters for dragging polyline sides and trace segments
// that can be modified while dragging
int m_drag_flag; // 1 if dragging something
int m_drag_shape; // shape
int m_last_drag_shape; // last shape drawn
int m_drag_x; // last cursor position for dragged shape
int m_drag_y;
int m_drag_xi; // start of rubberband drag line
int m_drag_yi;
int m_drag_xf; // end of rubberband drag line
int m_drag_yf;
int m_drag_layer_1; // line layer
int m_drag_w1; // line width
int m_drag_style1; // line style
int m_inflection_mode; // inflection mode
int m_last_inflection_mode; // last mode drawn
// extra parameters when dragging vertex between 2 line segments
int m_drag_style2;
int m_drag_layer_2;
int m_drag_w2;
// parameters used to draw leading via if necessary
int m_drag_layer_no_via;
int m_drag_via_w;
int m_drag_via_holew;
int m_drag_via_drawn;
// arrays of lines and ratlines being dragged
// these can be rotated and flipped while being dragged
int m_drag_layer; // layer
int m_drag_max_lines; // max size of array for line segments
int m_drag_num_lines; // number of line segments to drag
CPoint * m_drag_line_pt; // array of relative coords for line endpoints
int m_drag_max_ratlines; // max size of ratline array
int m_drag_num_ratlines; // number of ratlines to drag
CPoint * m_drag_ratline_start_pt; // absolute coords for ratline start points
CPoint * m_drag_ratline_end_pt; // relative coords for ratline endpoints
int m_drag_ratline_width;
// cursor parameters
int m_cross_hairs; // 0 = none, 1 = cross-hairs, 2 = diagonals
CPoint m_cross_left, m_cross_right, m_cross_top, m_cross_bottom; // end-points
CPoint m_cross_topleft, m_cross_topright, m_cross_botleft, m_cross_botright;
// grid
int m_visual_grid_on;
double m_visual_grid_spacing; // in world units
public:
CDisplayList( int pcbu_per_wu );
~CDisplayList();
void SetVisibleGrid( BOOL on, double grid );
void SetMapping( CRect *client_r, CRect *screen_r, int pane_org_x, int pane_bottom_h, double scale, int org_x, int org_y );
void SetDCToWorldCoords( CDC * pDC, CDC * mDC, int pcbu_org_x, int pcbu_org_y );
void SetLayerRGB( int layer, int r, int g, int b );
void SetLayerVisible( int layer, BOOL vis );
void SetLayerDrawOrder( int layer, int order )
{ m_layer_in_order[order] = layer; m_order_for_layer[layer] = order; };
dl_element * Add( id id, void * ptr, int glayer, int gtype, int visible,
int w, int holew, int x, int y, int xf, int yf, int xo, int yo,
int radius=0, int orig_layer=LAY_SELECTION );
dl_element * AddSelector( id id, void * ptr, int glayer, int gtype, int visible,
int w, int holew, int x, int y, int xf, int yf, int xo, int yo, int radius=0 );
void RemoveAll();
void RemoveAllFromLayer( int layer );
id Remove( dl_element * element );
void Draw( CDC * pDC );
int HighLight( int gtype, int x, int y, int xf, int yf, int w, int orig_layer=LAY_SELECTION );
int CancelHighLight();
void * TestSelect( int x, int y, id * sel_id, int * layer,
id * exclude_id = NULL, void * exclude_ptr = NULL, id * include_id = NULL,
int n_include_ids=1 );
int StartDraggingArray( CDC * pDC, int x, int y, int vert, int layer, int crosshair = 1 );
int StartDraggingRatLine( CDC * pDC, int x, int y, int xf, int yf, int layer,
int w, int crosshair = 1 );
int StartDraggingRectangle( CDC * pDC, int x, int y, int xi, int yi,
int xf, int yf, int vert, int layer );
int StartDraggingLineVertex( CDC * pDC, int x, int y, int xi, int yi,
int xf, int yf,
int layer1, int layer2, int w1, int w2,
int style1, int style2,
int layer_no_via, int via_w, int via_holew, int dir,
int crosshair );
int StartDraggingLine( CDC * pDC, int x, int y, int xi, int yi, int layer1, int w,
int layer_no_via, int via_w, int via_holew,
int crosshair, int style, int inflection_mode );
int StartDraggingArc( CDC * pDC, int style, int x, int y, int xi, int yi,
int layer, int w, int crosshair );
void SetDragArcStyle( int style );
void Drag( CDC * pDC, int x, int y );
int StopDragging();
void ChangeRoutingLayer( CDC * pDC, int layer1, int layer2, int w );
void IncrementDragAngle( CDC * pDC );
int MakeDragLineArray( int num_lines );
int MakeDragRatlineArray( int num_ratlines, int width );
int AddDragLine( CPoint pi, CPoint pf );
int AddDragRatline( CPoint pi, CPoint pf );
int GetDragAngle();
void FlipDragSide( CDC * pDC );
int GetDragSide();
void SetUpCrosshairs( int type, int x, int y );
void SetInflectionMode( int mode ){ m_inflection_mode = mode; };
CPoint ScreenToPCB( CPoint point );
CPoint PCBToScreen( CPoint point );
CPoint WindowToPCB( CPoint point );
// set element parameters
void Set_gtype( dl_element * el, int gtype );
void Set_visible( dl_element * el, int visible );
void Set_sel_vert( dl_element * el, int sel_vert );
void Set_w( dl_element * el, int w );
void Set_holew( dl_element * el, int holew );
void Set_x_org( dl_element * el, int x_org );
void Set_y_org( dl_element * el, int y_org );
void Set_x( dl_element * el, int x );
void Set_y( dl_element * el, int y );
void Set_xf( dl_element * el, int xf );
void Set_yf( dl_element * el, int yf );
void Set_id( dl_element * el, id * id );
void Set_layer( dl_element * el, int layer );
void Set_radius( dl_element * el, int radius );
void Move( dl_element * el, int dx, int dy );
// get element parameters
void * Get_ptr( dl_element * el );
int Get_gtype( dl_element * el );
int Get_visible( dl_element * el );
int Get_sel_vert( dl_element * el );
int Get_w( dl_element * el );
int Get_holew( dl_element * el );
int Get_x_org( dl_element * el );
int Get_y_org( dl_element * el );
int Get_x( dl_element * el );
int Get_y( dl_element * el );
int Get_xf( dl_element * el );
int Get_yf( dl_element * el );
int Get_radius( dl_element * el );
int Get_layer( dl_element * el );
id Get_id( dl_element * el );
};
#endif // #ifndef FP_DISPLAY_LIST_H
// definition of ID structure used by FreePCB
//
#pragma once
// struct id : this structure is used to identify PCB design elements
// such as instances of parts or nets, and their subelements
// Each element will have its own id.
// An id is attached to each item of the Display List so that it can
// be linked back to the PCB design element which drew it.
// These are mainly used to identify items selected by clicking the mouse
//
// In general:
// id.type = type of PCB element (e.g. part, net, text)
// id.st = subelement type (e.g. part pad, net connection)
// id.i = subelement index (zero-based)
// id.sst = subelement of subelement (e.g. net connection segment)
// id.ii = subsubelement index (zero-based)
//
// For example, the id for segment 0 of connection 4 of net 12 would be
// id = { ID_NET, 12, ID_CONNECT, 4, ID_SEG, 0 };
//
//
class id {
public:
// constructor
id( int qt=0, int qst=0, int qis=0, int qsst=0, int qiis=0 )
{ type=qt; st=qst; i=qis; sst=qsst; ii=qiis; }
// operators
friend int operator ==(id id1, id id2)
{ return (id1.type==id2.type
&& id1.st==id2.st
&& id1.sst==id2.sst
&& id1.i==id2.i
&& id1.ii==id2.ii );
}
// member functions
void Clear()
{ type=0; st=0; i=0; sst=0; ii=0; }
void Set( int qt, int qst=0, int qis=0, int qsst=0, int qiis=0 )
{ type=qt; st=qst; i=qis; sst=qsst; ii=qiis; }
// member variables
unsigned int type; // type of element
unsigned int st; // type of subelement
unsigned int i; // index of subelement
unsigned int sst; // type of subsubelement
unsigned int ii; // index of subsubelement
};
// these are constants used in ids
// root types
enum {
ID_NONE = 0, // an undefined type or st (or an error)
ID_BOARD, // board outline
ID_PART, // part
ID_NET, // net
ID_TEXT, // free-standing text
ID_DRC, // DRC error
ID_SM_CUTOUT, // cutout for solder mask
ID_MULTI // if multiple selections
};
// subtypes of ID_PART
enum {
ID_PAD = 1, // pad_stack in a part
ID_SEL_PAD, // selection rectangle for pad_stack in a part
ID_OUTLINE, // part outline
ID_REF_TXT, // text showing ref num for part
ID_ORIG, // part origin
ID_SEL_RECT, // selection rectangle for part
ID_SEL_REF_TXT // selection rectangle for ref text
};
// subtypes of ID_TEXT
enum {
ID_SEL_TXT = 1, // selection rectangle
ID_STROKE // stroke for text
};
// subtypes of ID_NET
enum {
ID_ENTIRE_NET = 0,
ID_CONNECT, // connection
ID_AREA // copper area
};
// subtypes of ID_BOARD
enum {
ID_BOARD_OUTLINE = 1,
};
// subsubtypes of ID_NET.ID_CONNECT
enum {
ID_ENTIRE_CONNECT = 0,
ID_SEG,
ID_SEL_SEG,
ID_VERTEX,
ID_SEL_VERTEX,
ID_VIA
};
// subsubtypes of ID_NET.ID_AREA, ID_BOARD.ID_BOARD_OUTLINE, ID_SM_CUTOUT
enum {
ID_SIDE = 1,
ID_SEL_SIDE,
ID_SEL_CORNER,
ID_HATCH,
ID_PIN_X, // only used by ID_AREA
ID_STUB_X // only used by ID_AREA
};
// subtypes of ID_DRC
// for subsubtypes, use types in DesignRules.h
enum {
ID_DRE = 1,
ID_SEL_DRE
};
// definition of ID structure used by FreePCB
//
#pragma once
// struct id : this structure is used to identify PCB design elements
// such as instances of parts or nets, and their subelements
// Each element will have its own id.
// An id is attached to each item of the Display List so that it can
// be linked back to the PCB design element which drew it.
// These are mainly used to identify items selected by clicking the mouse
//
// In general:
// id.type = type of PCB element (e.g. part, net, text)
// id.st = subelement type (e.g. part pad, net connection)
// id.i = subelement index (zero-based)
// id.sst = subelement of subelement (e.g. net connection segment)
// id.ii = subsubelement index (zero-based)
//
// For example, the id for segment 0 of connection 4 of net 12 would be
// id = { ID_NET, 12, ID_CONNECT, 4, ID_SEG, 0 };
//
//
class id {
public:
// constructor
id( int qt=0, int qst=0, int qis=0, int qsst=0, int qiis=0 )
{ type=qt; st=qst; i=qis; sst=qsst; ii=qiis; }
// operators
friend int operator ==(id id1, id id2)
{ return (id1.type==id2.type
&& id1.st==id2.st
&& id1.sst==id2.sst
&& id1.i==id2.i
&& id1.ii==id2.ii );
}
// member functions
void Clear()
{ type=0; st=0; i=0; sst=0; ii=0; }
void Set( int qt, int qst=0, int qis=0, int qsst=0, int qiis=0 )
{ type=qt; st=qst; i=qis; sst=qsst; ii=qiis; }
// member variables
unsigned int type; // type of element
unsigned int st; // type of subelement
unsigned int i; // index of subelement
unsigned int sst; // type of subsubelement
unsigned int ii; // index of subsubelement
};
// these are constants used in ids
// root types
enum {
ID_NONE = 0, // an undefined type or st (or an error)
ID_BOARD, // board outline
ID_PART, // part
ID_NET, // net
ID_TEXT, // free-standing text
ID_DRC, // DRC error
ID_SM_CUTOUT, // cutout for solder mask
ID_MULTI // if multiple selections
};
// subtypes of ID_PART
enum {
ID_PAD = 1, // pad_stack in a part
ID_SEL_PAD, // selection rectangle for pad_stack in a part
ID_OUTLINE, // part outline
ID_REF_TXT, // text showing ref num for part
ID_ORIG, // part origin
ID_SEL_RECT, // selection rectangle for part
ID_SEL_REF_TXT // selection rectangle for ref text
};
// subtypes of ID_TEXT
enum {
ID_SEL_TXT = 1, // selection rectangle
ID_STROKE // stroke for text
};
// subtypes of ID_NET
enum {
ID_ENTIRE_NET = 0,
ID_CONNECT, // connection
ID_AREA // copper area
};
// subtypes of ID_BOARD
enum {
ID_BOARD_OUTLINE = 1,
};
// subsubtypes of ID_NET.ID_CONNECT
enum {
ID_ENTIRE_CONNECT = 0,
ID_SEG,
ID_SEL_SEG,
ID_VERTEX,
ID_SEL_VERTEX,
ID_VIA
};
// subsubtypes of ID_NET.ID_AREA, ID_BOARD.ID_BOARD_OUTLINE, ID_SM_CUTOUT
enum {
ID_SIDE = 1,
ID_SEL_SIDE,
ID_SEL_CORNER,
ID_HATCH,
ID_PIN_X, // only used by ID_AREA
ID_STUB_X // only used by ID_AREA
};
// subtypes of ID_DRC
// for subsubtypes, use types in DesignRules.h
enum {
ID_DRE = 1,
ID_SEL_DRE
};
// math for graphics utility routines, from FreePCB
using namespace std;
#include <vector>
#include <math.h>
#include <float.h>
#include <limits.h>
#include "defs-macros.h"
#include "PolyLine2Kicad.h"
#include "freepcb_ids.h"
#include "PolyLine.h"
// function to find inflection-pont to create a "dogleg" of two straight-line segments
// where one segment is vertical or horizontal and the other is at 45 degrees or 90 degrees
// enter with:
// pi = start point
// pf = end point
// mode = IM_90_45 or IM_45_90 or IM_90
//
CPoint GetInflectionPoint( CPoint pi, CPoint pf, int mode )
{
CPoint p = pi;
if( mode == IM_NONE )
return p;
int dx = pf.x - pi.x;
int dy = pf.y - pi.y;
if( dx == 0 || dy == 0 || abs(dx) == abs(dy) )
{
// only one segment needed
}
else
{
if( abs(dy) > abs(dx) )
{
// vertical > horizontal
if( mode == IM_90 )
{
p.x = pi.x;
p.y = pf.y;
}
else if( mode == IM_45_90 || mode == IM_90_45 )
{
int vert; // length of vertical line needed
if( dy > 0 )
vert = dy - abs(dx); // positive
else
vert = dy + abs(dx); // negative
if( mode == IM_90_45 )
p.y = pi.y + vert;
else if( mode == IM_45_90 )
{
p.y = pf.y - vert;
p.x = pf.x;
}
}
else
ASSERT(0);
}
else
{
// horizontal > vertical
if( mode == IM_90 )
{
p.x = pf.x;
p.y = pi.y;
}
else if( mode == IM_45_90 || mode == IM_90_45 )
{
int hor; // length of horizontal line needed
if( dx > 0 )
hor = dx - abs(dy); // positive
else
hor = dx + abs(dy); // negative
if( mode == IM_90_45 )
p.x = pi.x + hor;
else if( mode == IM_45_90 )
{
p.x = pf.x - hor;
p.y = pf.y;
}
}
else
ASSERT(0);
}
}
return p;
}
//
// function to rotate a point clockwise about another point
// currently, angle must be 0, 90, 180 or 270
//
void RotatePoint( CPoint *p, int angle, CPoint org )
{
if( angle == 90 )
{
int tempy = org.y + (org.x - p->x);
p->x = org.x + (p->y - org.y);
p->y = tempy;
}
else if( angle > 90 )
{
for( int i=0; i<(angle/90); i++ )
RotatePoint( p, 90, org );
}
}
// function to rotate a rectangle clockwise about a point
// angle must be 0, 90, 180 or 270
// on exit, r->top > r.bottom, r.right > r.left
//
void RotateRect( CRect *r, int angle, CPoint org )
{
CRect tr;
if( angle == 90 )
{
tr.left = org.x + (r->bottom - org.y);
tr.right = org.x + (r->top - org.y);
tr.top = org.y + (org.x - r->right);
tr.bottom = org.y + (org.x - r->left);
if( tr.left > tr.right )
{
int temp = tr.right;
tr.left = tr.right;
tr.left = temp;
}
if( tr.left > tr.right )
{
int temp = tr.right;
tr.left = tr.right;
tr.left = temp;
}
if( tr.bottom > tr.top )
{
int temp = tr.bottom;
tr.bottom = tr.top;
tr.top = temp;
}
}
else if( angle > 90 )
{
tr = *r;
for( int i=0; i<(angle/90); i++ )
RotateRect( &tr, 90, org );
}
*r = tr;
}
// test for hit on line segment
// i.e. cursor within a given distance from segment
// enter with: x,y = cursor coords
// (xi,yi) and (xf,yf) are the end-points of the line segment
// dist = maximum distance for hit
//
int TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist )
{
double dd;
// test for vertical or horizontal segment
if( xf==xi )
{
// vertical segment
dd = fabs( (double)(x-xi) );
if( dd<dist && ( (yf>yi && y<yf && y>yi) || (yf<yi && y>yf && y<yi) ) )
return 1;
}
else if( yf==yi )
{
// horizontal segment
dd = fabs( (double)(y-yi) );
if( dd<dist && ( (xf>xi && x<xf && x>xi) || (xf<xi && x>xf && x<xi) ) )
return 1;
}
else
{
// oblique segment
// find a,b such that (xi,yi) and (xf,yf) lie on y = a + bx
double b = (double)(yf-yi)/(xf-xi);
double a = (double)yi-b*xi;
// find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
double d = -1.0/b;
double c = (double)y-d*x;
// find nearest point to (x,y) on line segment (xi,yi) to (xf,yf)
double xp = (a-c)/(d-b);
double yp = a + b*xp;
// find distance
dd = sqrt((x-xp)*(x-xp)+(y-yp)*(y-yp));
if( fabs(b)>0.7 )
{
// line segment more vertical than horizontal
if( dd<dist && ( (yf>yi && yp<yf && yp>yi) || (yf<yi && yp>yf && yp<yi) ) )
return 1;
}
else
{
// line segment more horizontal than vertical
if( dd<dist && ( (xf>xi && xp<xf && xp>xi) || (xf<xi && xp>xf && xp<xi) ) )
return 1;
}
}
return 0; // no hit
}
// find intersection between y = a + bx and y = c + dx;
//
int FindLineIntersection( double a, double b, double c, double d, double * x, double * y )
{
*x = (c-a)/(b-d);
*y = a + b*(*x);
return 0;
}
// set EllipseKH struct to describe the ellipse for an arc
//
int MakeEllipseFromArc( int xi, int yi, int xf, int yf, int style, EllipseKH * el )
{
// arc (quadrant of ellipse)
// convert to clockwise arc
int xxi, xxf, yyi, yyf;
if( style == CPolyLine::ARC_CCW )
{
xxi = xf;
xxf = xi;
yyi = yf;
yyf = yi;
}
else
{
xxi = xi;
xxf = xf;
yyi = yi;
yyf = yf;
}
// find center and radii of ellipse
double xo, yo;
if( xxf > xxi && yyf > yyi )
{
xo = xxf;
yo = yyi;
el->theta1 = M_PI;
el->theta2 = M_PI/2.0;
}
else if( xxf < xxi && yyf > yyi )
{
xo = xxi;
yo = yyf;
el->theta1 = -M_PI/2.0;
el->theta2 = -M_PI;
}
else if( xxf < xxi && yyf < yyi )
{
xo = xxf;
yo = yyi;
el->theta1 = 0.0;
el->theta2 = -M_PI/2.0;
}
else if( xxf > xxi && yyf < yyi )
{
xo = xxi;
yo = yyf;
el->theta1 = M_PI/2.0;
el->theta2 = 0.0;
}
el->Center.X = xo;
el->Center.Y = yo;
el->xrad = abs(xf-xi);
el->yrad = abs(yf-yi);
#if 0
el->Phi = 0.0;
el->MaxRad = el->xrad;
el->MinRad = el->yrad;
if( el->MaxRad < el->MinRad )
{
el->MaxRad = el->yrad;
el->MinRad = el->xrad;
el->Phi = M_PI/2.0;
}
#endif
return 0;
}
// find intersections between line segment (xi,yi) to (xf,yf)
// and line segment (xi2,yi2) to (xf2,yf2)
// the line segments may be arcs (i.e. quadrant of an ellipse) or straight
// returns number of intersections found (max of 2)
// returns coords of intersections in arrays x[2], y[2]
//
int FindSegmentIntersections( int xi, int yi, int xf, int yf, int style,
int xi2, int yi2, int xf2, int yf2, int style2,
double x[], double y[] )
{
double xr[12], yr[12];
int iret = 0;
if( max(xi,xf) < min(xi2,xf2)
|| min(xi,xf) > max(xi2,xf2)
|| max(yi,yf) < min(yi2,yf2)
|| min(yi,yf) > max(yi2,yf2) )
return 0;
if( style != CPolyLine::STRAIGHT && style2 != CPolyLine::STRAIGHT )
{
// two identical arcs intersect
if( style == style2 && xi == xi2 && yi == yi2 && xf == xf2 && yf == yf2 )
{
if( x && y )
{
x[0] = xi;
y[0] = yi;
}
return 1;
}
else if( style != style2 && xi == xf2 && yi == yf2 && xf == xi2 && yf == yi2 )
{
if( x && y )
{
x[0] = xi;
y[0] = yi;
}
return 1;
}
}
if( style == CPolyLine::STRAIGHT && style2 == CPolyLine::STRAIGHT )
{
// both straight-line segments
int x, y;
bool bYes = TestForIntersectionOfStraightLineSegments( xi, yi, xf, yf, xi2, yi2, xf2, yf2, &x, &y );
if( !bYes )
return 0;
xr[0] = x;
yr[0] = y;
iret = 1;
}
else if( style == CPolyLine::STRAIGHT )
{
// first segment is straight, second segment is an arc
int ret;
double x1r, y1r, x2r, y2r;
if( xf == xi )
{
// vertical first segment
double a = xi;
double b = DBL_MAX/2.0;
ret = FindLineSegmentIntersection( a, b, xi2, yi2, xf2, yf2, style2,
&x1r, &y1r, &x2r, &y2r );
}
else
{
double b = (double)(yf-yi)/(double)(xf-xi);
double a = yf - b*xf;
ret = FindLineSegmentIntersection( a, b, xi2, yi2, xf2, yf2, style2,
&x1r, &y1r, &x2r, &y2r );
}
if( ret == 0 )
return 0;
if( InRange( x1r, xi, xf ) && InRange( y1r, yi, yf ) )
{
xr[iret] = x1r;
yr[iret] = y1r;
iret++;
}
if( ret == 2 )
{
if( InRange( x2r, xi, xf ) && InRange( y2r, yi, yf ) )
{
xr[iret] = x2r;
yr[iret] = y2r;
iret++;
}
}
}
else if( style2 == CPolyLine::STRAIGHT )
{
// first segment is an arc, second segment is straight
int ret;
double x1r, y1r, x2r, y2r;
if( xf2 == xi2 )
{
// vertical second segment
double a = xi2;
double b = DBL_MAX/2.0;
ret = FindLineSegmentIntersection( a, b, xi, yi, xf, yf, style,
&x1r, &y1r, &x2r, &y2r );
}
else
{
double b = (double)(yf2-yi2)/(double)(xf2-xi2);
double a = yf2 - b*xf2;
ret = FindLineSegmentIntersection( a, b, xi, yi, xf, yf, style,
&x1r, &y1r, &x2r, &y2r );
}
if( ret == 0 )
return 0;
if( InRange( x1r, xi2, xf2 ) && InRange( y1r, yi2, yf2 ) )
{
xr[iret] = x1r;
yr[iret] = y1r;
iret++;
}
if( ret == 2 )
{
if( InRange( x2r, xi2, xf2 ) && InRange( y2r, yi2, yf2 ) )
{
xr[iret] = x2r;
yr[iret] = y2r;
iret++;
}
}
}
else
{
// both segments are arcs
EllipseKH el1;
EllipseKH el2;
MakeEllipseFromArc( xi, yi, xf, yf, style, &el1 );
MakeEllipseFromArc( xi2, yi2, xf2, yf2, style2, &el2 );
int n;
if( el1.xrad+el1.yrad > el2.xrad+el2.yrad )
n = GetArcIntersections( &el1, &el2 );
else
n = GetArcIntersections( &el2, &el1 );
iret = n;
}
if( x && y )
{
for( int i=0; i<iret; i++ )
{
x[i] = xr[i];
y[i] = yr[i];
}
}
return iret;
}
// find intersection between line y = a + bx and line segment (xi,yi) to (xf,yf)
// if b > DBL_MAX/10, assume vertical line at x = a
// the line segment may be an arc (i.e. quadrant of an ellipse)
// return 0 if no intersection
// returns 1 or 2 if intersections found
// sets coords of intersections in *x1, *y1, *x2, *y2
// if no intersection, returns min distance in dist
//
int FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf, int style,
double * x1, double * y1, double * x2, double * y2,
double * dist )
{
double xx, yy;
bool bVert = false;
if( b > DBL_MAX/10.0 )
bVert = true;
if( xf != xi )
{
// non-vertical segment, get intersection
if( style == CPolyLine::STRAIGHT || yf == yi )
{
// horizontal or oblique straight segment
// put into form y = c + dx;
double d = (double)(yf-yi)/(double)(xf-xi);
double c = yf - d*xf;
if( bVert )
{
// if vertical line, easy
if( InRange( a, xi, xf ) )
{
*x1 = a;
*y1 = c + d*a;
return 1;
}
else
{
if( dist )
*dist = min( abs(a-xi), abs(a-xf) );
return 0;
}
}
if( fabs(b-d) < 1E-12 )
{
// parallel lines
if( dist )
{
*dist = GetPointToLineDistance( a, b, xi, xf );
}
return 0; // lines parallel
}
// calculate intersection
xx = (c-a)/(b-d);
yy = a + b*(xx);
// see if intersection is within the line segment
if( yf == yi )
{
// horizontal line
if( (xx>=xi && xx>xf) || (xx<=xi && xx<xf) )
return 0;
}
else
{
// oblique line
if( (xx>=xi && xx>xf) || (xx<=xi && xx<xf)
|| (yy>yi && yy>yf) || (yy<yi && yy<yf) )
return 0;
}
}
else if( style == CPolyLine::ARC_CW || style == CPolyLine::ARC_CCW )
{
// arc (quadrant of ellipse)
// convert to clockwise arc
int xxi, xxf, yyi, yyf;
if( style == CPolyLine::ARC_CCW )
{
xxi = xf;
xxf = xi;
yyi = yf;
yyf = yi;
}
else
{
xxi = xi;
xxf = xf;
yyi = yi;
yyf = yf;
}
// find center and radii of ellipse
double xo, yo, rx, ry;
if( xxf > xxi && yyf > yyi )
{
xo = xxf;
yo = yyi;
}
else if( xxf < xxi && yyf > yyi )
{
xo = xxi;
yo = yyf;
}
else if( xxf < xxi && yyf < yyi )
{
xo = xxf;
yo = yyi;
}
else if( xxf > xxi && yyf < yyi )
{
xo = xxi;
yo = yyf;
}
rx = fabs( (double)(xxi-xxf) );
ry = fabs( (double)(yyi-yyf) );
bool test;
double xx1, xx2, yy1, yy2, aa;
if( bVert )
{
// shift vertical line to coordinate system of ellipse
aa = a - xo;
test = FindVerticalLineEllipseIntersections( rx, ry, aa, &yy1, &yy2 );
if( !test )
return 0;
// shift back to PCB coordinates
yy1 += yo;
yy2 += yo;
xx1 = a;
xx2 = a;
}
else
{
// shift line to coordinate system of ellipse
aa = a + b*xo - yo;
test = FindLineEllipseIntersections( rx, ry, aa, b, &xx1, &xx2 );
if( !test )
return 0;
// shift back to PCB coordinates
yy1 = aa + b*xx1;
xx1 += xo;
yy1 += yo;
yy2 = aa + b*xx2;
xx2 += xo;
yy2 += yo;
}
int npts = 0;
if( (xxf>xxi && xx1<xxf && xx1>xxi) || (xxf<xxi && xx1<xxi && xx1>xxf) )
{
if( (yyf>yyi && yy1<yyf && yy1>yyi) || (yyf<yyi && yy1<yyi && yy1>yyf) )
{
*x1 = xx1;
*y1 = yy1;
npts = 1;
}
}
if( (xxf>xxi && xx2<xxf && xx2>xxi) || (xxf<xxi && xx2<xxi && xx2>xxf) )
{
if( (yyf>yyi && yy2<yyf && yy2>yyi) || (yyf<yyi && yy2<yyi && yy2>yyf) )
{
if( npts == 0 )
{
*x1 = xx2;
*y1 = yy2;
npts = 1;
}
else
{
*x2 = xx2;
*y2 = yy2;
npts = 2;
}
}
}
return npts;
}
else
ASSERT(0);
}
else
{
// vertical line segment
if( bVert )
return 0;
xx = xi;
yy = a + b*xx;
if( (yy>=yi && yy>yf) || (yy<=yi && yy<yf) )
return 0;
}
*x1 = xx;
*y1 = yy;
return 1;
}
// Test for intersection of line segments
// If lines are parallel, returns false
// If true, returns intersection coords in x, y
// if false, returns min. distance in dist (may be 0.0 if parallel)
// and coords on nearest point in one of the segments in (x,y)
//
bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f,
int x2i, int y2i, int x2f, int y2f,
int * x, int * y, double * d )
{
double a, b, dist;
// first, test for intersection
if( x1i == x1f && x2i == x2f )
{
// both segments are vertical, can't intersect
}
else if( y1i == y1f && y2i == y2f )
{
// both segments are horizontal, can't intersect
}
else if( x1i == x1f && y2i == y2f )
{
// first seg. vertical, second horizontal, see if they cross
if( InRange( x1i, x2i, x2f )
&& InRange( y2i, y1i, y1f ) )
{
if( x )
*x = x1i;
if( y )
*y = y2i;
if( d )
*d = 0.0;
return true;
}
}
else if( y1i == y1f && x2i == x2f )
{
// first seg. horizontal, second vertical, see if they cross
if( InRange( y1i, y2i, y2f )
&& InRange( x2i, x1i, x1f ) )
{
if( x )
*x = x2i;
if( y )
*y = y1i;
if( d )
*d = 0.0;
return true;
}
}
else if( x1i == x1f )
{
// first segment vertical, second oblique
// get a and b for second line segment, so that y = a + bx;
b = (double)(y2f-y2i)/(x2f-x2i);
a = (double)y2i - b*x2i;
double x1, y1, x2, y2;
int test = FindLineSegmentIntersection( a, b, x1i, y1i, x1f, y1f, CPolyLine::STRAIGHT,
&x1, &y1, &x2, &y2 );
if( test )
{
if( InRange( y1, y1i, y1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) )
{
if( x )
*x = x1;
if( y )
*y = y1;
if( d )
*d = 0.0;
return true;
}
}
}
else if( y1i == y1f )
{
// first segment horizontal, second oblique
// get a and b for second line segment, so that y = a + bx;
b = (double)(y2f-y2i)/(x2f-x2i);
a = (double)y2i - b*x2i;
double x1, y1, x2, y2;
int test = FindLineSegmentIntersection( a, b, x1i, y1i, x1f, y1f, CPolyLine::STRAIGHT,
&x1, &y1, &x2, &y2 );
if( test )
{
if( InRange( x1, x1i, x1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) )
{
if( x )
*x = x1;
if( y )
*y = y1;
if( d )
*d = 0.0;
return true;
}
}
}
else if( x2i == x2f )
{
// second segment vertical, first oblique
// get a and b for first line segment, so that y = a + bx;
b = (double)(y1f-y1i)/(x1f-x1i);
a = (double)y1i - b*x1i;
double x1, y1, x2, y2;
int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f, CPolyLine::STRAIGHT,
&x1, &y1, &x2, &y2 );
if( test )
{
if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) && InRange( y1, y2i, y2f ) )
{
if( x )
*x = x1;
if( y )
*y = y1;
if( d )
*d = 0.0;
return true;
}
}
}
else if( y2i == y2f )
{
// second segment horizontal, first oblique
// get a and b for second line segment, so that y = a + bx;
b = (double)(y1f-y1i)/(x1f-x1i);
a = (double)y1i - b*x1i;
double x1, y1, x2, y2;
int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f, CPolyLine::STRAIGHT,
&x1, &y1, &x2, &y2 );
if( test )
{
if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) )
{
if( x )
*x = x1;
if( y )
*y = y1;
if( d )
*d = 0.0;
return true;
}
}
}
else
{
// both segments oblique
if( (long)(y1f-y1i)*(x2f-x2i) != (long)(y2f-y2i)*(x1f-x1i) )
{
// not parallel, get a and b for first line segment, so that y = a + bx;
b = (double)(y1f-y1i)/(x1f-x1i);
a = (double)y1i - b*x1i;
double x1, y1, x2, y2;
int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f, CPolyLine::STRAIGHT,
&x1, &y1, &x2, &y2 );
// both segments oblique
if( test )
{
if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) )
{
if( x )
*x = x1;
if( y )
*y = y1;
if( d )
*d = 0.0;
return true;
}
}
}
}
// don't intersect, get shortest distance between each endpoint and the other line segment
dist = GetPointToLineSegmentDistance( x1i, y1i, x2i, y2i, x2f, y2f );
double xx = x1i;
double yy = y1i;
double dd = GetPointToLineSegmentDistance( x1f, y1f, x2i, y2i, x2f, y2f );
if( dd < dist )
{
dist = dd;
xx = x1f;
yy = y1f;
}
dd = GetPointToLineSegmentDistance( x2i, y2i, x1i, y1i, x1f, y1f );
if( dd < dist )
{
dist = dd;
xx = x2i;
yy = y2i;
}
dd = GetPointToLineSegmentDistance( x2f, y2f, x1i, y1i, x1f, y1f );
if( dd < dist )
{
dist = dd;
xx = x2f;
yy = y2f;
}
if( x )
*x = xx;
if( y )
*y = yy;
if( d )
*d = dist;
return false;
}
// quicksort algorithm
// sorts array numbers[], also moves elements of another array index[]
//
#define Q3WAY
void quickSort(int numbers[], int index[], int array_size)
{
#ifdef Q3WAY
q_sort_3way(numbers, index, 0, array_size - 1);
#else
q_sort(numbers, index, 0, array_size - 1);
#endif
}
// standard quicksort
//
void q_sort(int numbers[], int index[], int left, int right)
{
int pivot, pivot_index, l_hold, r_hold;
l_hold = left;
r_hold = right;
pivot = numbers[left];
pivot_index = index[left];
while (left < right)
{
while ((numbers[right] >= pivot) && (left < right))
right--;
if (left != right)
{
numbers[left] = numbers[right];
index[left] = index[right];
left++;
}
while ((numbers[left] <= pivot) && (left < right))
left++;
if (left != right)
{
numbers[right] = numbers[left];
index[right] = index[left];
right--;
}
}
numbers[left] = pivot;
index[left] = pivot_index;
pivot = left;
left = l_hold;
right = r_hold;
if (left < pivot)
q_sort(numbers, index, left, pivot-1);
if (right > pivot)
q_sort(numbers, index, pivot+1, right);
}
// 3-way quicksort...useful where there are duplicate values
//
void q_sort_3way( int a[], int b[], int l, int r )
{
#define EXCH(i,j) {int temp=a[i]; a[i]=a[j]; a[j]=temp; temp=b[i]; b[i]=b[j]; b[j]=temp;}
int i = l - 1;
int j = r;
int p = l - 1;
int q = r;
int v = a[r];
if( r <= l )
return;
for(;;)
{
while( a[++i] < v );
while( v < a[--j] )
if( j == 1 )
break;
if( i >= j )
break;
EXCH( i, j );
if( a[i] == v )
{
p++;
EXCH( p, i );
}
if( v == a[j] )
{
q--;
EXCH( j, q );
}
}
EXCH( i, r );
j = i - 1;
i = i + 1;
for( int k=l; k<p; k++, j-- )
EXCH( k, j );
for( int k=r-1; k>q; k--, i++ )
EXCH( i, k );
q_sort_3way( a, b, l, j );
q_sort_3way( a, b, i, r );
}
// solves quadratic equation
// i.e. ax**2 + bx + c = 0
// returns true if solution exist, with solutions in x1 and x2
// else returns false
//
bool Quadratic( double a, double b, double c, double *x1, double *x2 )
{
double root = b*b - 4.0*a*c;
if( root < 0.0 )
return false;
root = sqrt( root );
*x1 = (-b+root)/(2.0*a);
*x2 = (-b-root)/(2.0*a);
return true;
}
// finds intersections of vertical line at x
// with ellipse defined by (x^2)/(a^2) + (y^2)/(b^2) = 1;
// returns true if solution exist, with solutions in y1 and y2
// else returns false
//
bool FindVerticalLineEllipseIntersections( double a, double b, double x, double *y1, double *y2 )
{
double y_sqr = (1.0-(x*x)/(a*a))*b*b;
if( y_sqr < 0.0 )
return false;
*y1 = sqrt(y_sqr);
*y2 = -*y1;
return true;
}
// finds intersections of straight line y = c + dx
// with ellipse defined by (x^2)/(a^2) + (y^2)/(b^2) = 1;
// returns true if solution exist, with solutions in x1 and x2
// else returns false
//
bool FindLineEllipseIntersections( double a, double b, double c, double d, double *x1, double *x2 )
{
// quadratic terms
double A = d*d+b*b/(a*a);
double B = 2.0*c*d;
double C = c*c-b*b;
return Quadratic( A, B, C, x1, x2 );
}
#if 0
// draw a straight line or an arc between xi,yi and xf,yf
//
void DrawArc( CDC * pDC, int shape, int xxi, int yyi, int xxf, int yyf, bool bMeta )
{
int xi, yi, xf, yf;
if( shape == DL_LINE || xxi == xxf || yyi == yyf )
{
// draw straight line
pDC->MoveTo( xxi, yyi );
pDC->LineTo( xxf, yyf );
}
else if( shape == DL_ARC_CCW || shape == DL_ARC_CW )
{
// set endpoints so we can always draw counter-clockwise arc
if( shape == DL_ARC_CW )
{
xi = xxf;
yi = yyf;
xf = xxi;
yf = yyi;
}
else
{
xi = xxi;
yi = yyi;
xf = xxf;
yf = yyf;
}
pDC->MoveTo( xi, yi );
if( xf > xi && yf > yi )
{
// quadrant 1
int w = (xf-xi)*2;
int h = (yf-yi)*2;
if( !bMeta )
pDC->Arc( xf-w, yi+h, xf, yi,
xi, yi, xf, yf );
else
pDC->Arc( xf-w, yi, xf, yi+h,
xf, yf, xi, yi );
}
else if( xf < xi && yf > yi )
{
// quadrant 2
int w = -(xf-xi)*2;
int h = (yf-yi)*2;
if( !bMeta )
pDC->Arc( xi-w, yf, xi, yf-h,
xi, yi, xf, yf );
else
pDC->Arc( xi-w, yf-h, xi, yf,
xf, yf, xi, yi );
}
else if( xf < xi && yf < yi )
{
// quadrant 3
int w = -(xf-xi)*2;
int h = -(yf-yi)*2;
if( !bMeta )
pDC->Arc( xf, yi, xf+w, yi-h,
xi, yi, xf, yf );
else
pDC->Arc( xf, yi-h, xf+w, yi,
xf, yf, xi, yi );
}
else if( xf > xi && yf < yi )
{
// quadrant 4
int w = (xf-xi)*2;
int h = -(yf-yi)*2;
if( !bMeta )
pDC->Arc( xi, yf+h, xi+w, yf,
xi, yi, xf, yf );
else
pDC->Arc( xi, yf, xi+w, yf+h,
xf, yf, xi, yi );
}
pDC->MoveTo( xxf, yyf );
}
else
ASSERT(0); // oops
}
#endif
// Get arrays of circles, rects and line segments to represent pad
// for purposes of drawing pad or calculating clearances
// margins of circles and line segments represent pad outline
// circles and rects are used to find points inside pad
//
void GetPadElements( int type, int x, int y, int wid, int len, int radius, int angle,
int * nr, my_rect r[], int * nc, my_circle c[], int * ns, my_seg s[] )
{
*nc = 0;
*nr = 0;
*ns = 0;
if( type == PAD_ROUND )
{
*nc = 1;
c[0] = my_circle(x,y,wid/2);
return;
}
if( type == PAD_SQUARE )
{
*nr = 1;
r[0] = my_rect(x-wid/2, y-wid/2,x+wid/2, y+wid/2);
*ns = 4;
s[0] = my_seg(x-wid/2, y+wid/2,x+wid/2, y+wid/2); // top
s[1] = my_seg(x-wid/2, y-wid/2,x+wid/2, y-wid/2); // bottom
s[2] = my_seg(x-wid/2, y-wid/2,x-wid/2, y+wid/2); // left
s[3] = my_seg(x+wid/2, y-wid/2,x+wid/2, y+wid/2); // right
return;
}
if( type == PAD_OCTAGON )
{
const double pi = 3.14159265359;
*nc = 1; // circle represents inside of polygon
c[0] = my_circle(x, y, wid/2);
*ns = 8; // now create sides of polygon
double theta = pi/8.0;
double radius = 0.5*(double)wid/cos(theta);
double last_x = x + radius*cos(theta);
double last_y = y + radius*sin(theta);
for( int is=0; is<8; is++ )
{
theta += pi/4.0;
double dx = x + radius*cos(theta);
double dy = y + radius*sin(theta);
s[is] = my_seg(last_x, last_y, x, y);
last_x = dx;
last_y = dy;
}
return;
}
//
int h;
int v;
if( angle == 90 || angle == 270 )
{
h = wid;
v = len;
}
else
{
v = wid;
h = len;
}
if( type == PAD_RECT )
{
*nr = 1;
r[0] = my_rect(x-h/2, y-v/2, x+h/2, y+v/2);
*ns = 4;
s[0] = my_seg(x-h/2, y+v/2,x+h/2, y+v/2); // top
s[1] = my_seg(x-h/2, y-v/2,x+h/2, y-v/2); // bottom
s[2] = my_seg(x-h/2, y-v/2,x-h/2, y+v/2); // left
s[3] = my_seg(x+h/2, y-v/2,x+h/2, y+v/2); // right
return;
}
if( type == PAD_RRECT )
{
*nc = 4;
c[0] = my_circle(x-h/2+radius, y-v/2+radius, radius); // bottom left circle
c[1] = my_circle(x+h/2-radius, y-v/2+radius, radius); // bottom right circle
c[2] = my_circle(x-h/2+radius, y+v/2-radius, radius); // top left circle
c[3] = my_circle(x+h/2-radius, y+v/2-radius, radius); // top right circle
*ns = 4;
s[0] = my_seg(x-h/2+radius, y+v/2, x+h/2-radius, y+v/2); // top
s[1] = my_seg(x-h/2+radius, y-v/2, x+h/2-radius, y+v/2); // bottom
s[2] = my_seg(x-h/2, y-v/2+radius, x-h/2, y+v/2-radius); // left
s[3] = my_seg(x+h/2, y-v/2+radius, x+h/2, y+v/2-radius); // right
return;
}
if( type == PAD_OVAL )
{
if( h > v )
{
// horizontal
*nc = 2;
c[0] = my_circle(x-h/2+v/2, y, v/2); // left circle
c[1] = my_circle(x+h/2-v/2, y, v/2); // right circle
*nr = 1;
r[0] = my_rect(x-h/2+v/2, y-v/2, x+h/2-v/2, y+v/2);
*ns = 2;
s[0] = my_seg(x-h/2+v/2, y+v/2, x+h/2-v/2, y+v/2); // top
s[1] = my_seg(x-h/2+v/2, y-v/2, x+h/2-v/2, y-v/2); // bottom
}
else
{
// vertical
*nc = 2;
c[0] = my_circle(x, y+v/2-h/2, h/2); // top circle
c[1] = my_circle(x, y-v/2+h/2, h/2); // bottom circle
*nr = 1;
r[0] = my_rect(x-h/2, y-v/2+h/2, x+h/2, y+v/2-h/2);
*ns = 2;
s[0] = my_seg(x-h/2, y-v/2+h/2, x-h/2, y+v/2-h/2); // left
s[1] = my_seg(x+h/2, y-v/2+h/2, x+h/2, y+v/2-h/2); // left
}
return;
}
ASSERT(0);
}
// Find distance from a staright line segment to a pad
//
int GetClearanceBetweenSegmentAndPad( int x1, int y1, int x2, int y2, int w,
int type, int x, int y, int wid, int len, int radius, int angle )
{
if( type == PAD_NONE )
return INT_MAX;
else
{
int nc, nr, ns;
my_circle c[4];
my_rect r[2];
my_seg s[8];
GetPadElements( type, x, y, wid, len, radius, angle,
&nr, r, &nc, c, &ns, s );
// first test for endpoints of line segment in rectangle
for( int ir=0; ir<nr; ir++ )
{
if( x1 >= r[ir].xlo && x1 <= r[ir].xhi && y1 >= r[ir].ylo && y1 <= r[ir].yhi )
return 0;
if( x2 >= r[ir].xlo && x2 <= r[ir].xhi && y2 >= r[ir].ylo && y2 <= r[ir].yhi )
return 0;
}
// now get distance from elements of pad outline
int dist = INT_MAX;
for( int ic=0; ic<nc; ic++ )
{
int d = GetPointToLineSegmentDistance( c[ic].x, c[ic].y, x1, y1, x2, y2 ) - c[ic].r - w/2;
dist = min(dist,d);
}
for( int is=0; is<ns; is++ )
{
double d;
TestForIntersectionOfStraightLineSegments( s[is].xi, s[is].yi, s[is].xf, s[is].yf,
x1, y1, x2, y2, NULL, NULL, &d );
d -= w/2;
dist = min(dist,d);
}
return max(0,dist);
}
}
// Get clearance between 2 segments
// Returns point in segment closest to other segment in x, y
// in clearance > max_cl, just returns max_cl and doesn't return x,y
//
int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int style1, int w1,
int x2i, int y2i, int x2f, int y2f, int style2, int w2,
int max_cl, int * x, int * y )
{
// check clearance between bounding rectangles
int test = max_cl + w1/2 + w2/2;
if( min(x1i,x1f)-max(x2i,x2f) > test )
return max_cl;
if( min(x2i,x2f)-max(x1i,x1f) > test )
return max_cl;
if( min(y1i,y1f)-max(y2i,y2f) > test )
return max_cl;
if( min(y2i,y2f)-max(y1i,y1f) > test )
return max_cl;
if( style1 == CPolyLine::STRAIGHT && style1 == CPolyLine::STRAIGHT )
{
// both segments are straight lines
int xx, yy;
double dd;
TestForIntersectionOfStraightLineSegments( x1i, y1i, x1f, y1f,
x2i, y2i, x2f, y2f, &xx, &yy, &dd );
int d = max( 0, dd - w1/2 - w2/2 );
if( x )
*x = xx;
if( y )
*y = yy;
return d;
}
// not both straight-line segments
// see if segments intersect
double xr[2];
double yr[2];
test = FindSegmentIntersections( x1i, y1i, x1f, y1f, style1, x2i, y2i, x2f, y2f, style2, xr, yr );
if( test )
{
if( x )
*x = xr[0];
if( y )
*y = yr[0];
return 0.0;
}
// at least one segment is an arc
EllipseKH el1;
EllipseKH el2;
bool bArcs;
int xi, yi, xf, yf;
if( style2 == CPolyLine::STRAIGHT )
{
// style1 = arc, style2 = straight
MakeEllipseFromArc( x1i, y1i, x1f, y1f, style1, &el1 );
xi = x2i;
yi = y2i;
xf = x2f;
yf = y2f;
bArcs = false;
}
else if( style1 == CPolyLine::STRAIGHT )
{
// style2 = arc, style1 = straight
xi = x1i;
yi = y1i;
xf = x1f;
yf = y1f;
MakeEllipseFromArc( x2i, y2i, x2f, y2f, style2, &el1 );
bArcs = false;
}
else
{
// style1 = arc, style2 = arc
MakeEllipseFromArc( x1i, y1i, x1f, y1f, style1, &el1 );
MakeEllipseFromArc( x2i, y2i, x2f, y2f, style2, &el2 );
bArcs = true;
}
const int NSTEPS = 32;
if( el1.theta2 > el1.theta1 )
ASSERT(0);
if( bArcs && el2.theta2 > el2.theta1 )
ASSERT(0);
// test multiple points in both segments
double th1;
double th2;
double len2;
if( bArcs )
{
th1 = el2.theta1;
th2 = el2.theta2;
len2 = max(el2.xrad, el2.yrad);
}
else
{
th1 = 1.0;
th2 = 0.0;
len2 = abs(xf-xi)+abs(yf-yi);
}
double s_start = el1.theta1;
double s_end = el1.theta2;
double s_start2 = th1;
double s_end2 = th2;
double dmin = DBL_MAX;
double xmin, ymin, smin, smin2;
int nsteps = NSTEPS;
int nsteps2 = NSTEPS;
double step = (s_start-s_end)/(nsteps-1);
double step2 = (s_start2-s_end2)/(nsteps2-1);
while( (step * max(el1.xrad, el1.yrad)) > 0.1*NM_PER_MIL
&& (step2 * len2) > 0.1*NM_PER_MIL )
{
step = (s_start-s_end)/(nsteps-1);
for( int i=0; i<nsteps; i++ )
{
double s;
if( i < nsteps-1 )
s = s_start - i*step;
else
s = s_end;
double x = el1.Center.X + el1.xrad*cos(s);
double y = el1.Center.Y + el1.yrad*sin(s);
// if not an arc, use s2 as fractional distance along line
step2 = (s_start2-s_end2)/(nsteps2-1);
for( int i2=0; i2<nsteps2; i2++ )
{
double s2;
if( i2 < nsteps2-1 )
s2 = s_start2 - i2*step2;
else
s2 = s_end2;
double x2, y2;
if( !bArcs )
{
x2 = xi + (xf-xi)*s2;
y2 = yi + (yf-yi)*s2;
}
else
{
x2 = el2.Center.X + el2.xrad*cos(s2);
y2 = el2.Center.Y + el2.yrad*sin(s2);
}
double d = Distance( x, y, x2, y2 );
if( d < dmin )
{
dmin = d;
xmin = x;
ymin = y;
smin = s;
smin2 = s2;
}
}
}
if( step > step2 )
{
s_start = min(el1.theta1, smin + step);
s_end = max(el1.theta2, smin - step);
step = (s_start - s_end)/nsteps;
}
else
{
s_start2 = min(th1, smin2 + step2);
s_end2 = max(th2, smin2 - step2);
step2 = (s_start2 - s_end2)/nsteps2;
}
}
if( x )
*x = xmin;
if( y )
*y = ymin;
return max(0,dmin-w1/2-w2/2); // allow for widths
}
// Find clearance between pads
// For each pad:
// type = PAD_ROUND, PAD_SQUARE, etc.
// x, y = center position
// w, l = width and length
// r = corner radius
// angle = 0 or 90 (if 0, pad length is along x-axis)
//
int GetClearanceBetweenPads( int type1, int x1, int y1, int w1, int l1, int r1, int angle1,
int type2, int x2, int y2, int w2, int l2, int r2, int angle2 )
{
if( type1 == PAD_NONE )
return INT_MAX;
if( type2 == PAD_NONE )
return INT_MAX;
int dist = INT_MAX;
int nr, nc, ns, nrr, ncc, nss;
my_rect r[2], rr[2];
my_circle c[4], cc[4];
my_seg s[8], ss[8];
GetPadElements( type1, x1, y1, w1, l1, r1, angle1,
&nr, r, &nc, c, &ns, s );
GetPadElements( type2, x2, y2, w2, l2, r2, angle2,
&nrr, rr, &ncc, cc, &nss, ss );
// now find distance from every element of pad1 to every element of pad2
for( int ic=0; ic<nc; ic++ )
{
for( int icc=0; icc<ncc; icc++ )
{
int d = Distance( c[ic].x, c[ic].y, cc[icc].x, cc[icc].y )
- c[ic].r - cc[icc].r;
dist = min(dist,d);
}
for( int iss=0; iss<nss; iss++ )
{
int d = GetPointToLineSegmentDistance( c[ic].x, c[ic].y,
ss[iss].xi, ss[iss].yi, ss[iss].xf, ss[iss].yf ) - c[ic].r;
dist = min(dist,d);
}
}
for( int is=0; is<ns; is++ )
{
for( int icc=0; icc<ncc; icc++ )
{
int d = GetPointToLineSegmentDistance( cc[icc].x, cc[icc].y,
s[is].xi, s[is].yi, s[is].xf, s[is].yf ) - cc[icc].r;
dist = min(dist,d);
}
for( int iss=0; iss<nss; iss++ )
{
double d;
TestForIntersectionOfStraightLineSegments( s[is].xi, s[is].yi, s[is].xf, s[is].yf,
ss[iss].xi, ss[iss].yi, ss[iss].xf, ss[iss].yf, NULL, NULL, &d );
dist = min(dist,d);
}
}
return max(dist,0);
}
// Get min. distance from (x,y) to line y = a + bx
// if b > DBL_MAX/10, assume vertical line at x = a
// returns closest point on line in xp, yp
//
double GetPointToLineDistance( double a, double b, int x, int y, double * xpp, double * ypp )
{
if( b > DBL_MAX/10 )
{
// vertical line
if( xpp && ypp )
{
*xpp = a;
*ypp = y;
}
return abs(a-x);
}
// find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
double d = -1.0/b;
double c = (double)y-d*x;
// find nearest point to (x,y) on line through (xi,yi) to (xf,yf)
double xp = (a-c)/(d-b);
double yp = a + b*xp;
if( xpp && ypp )
{
*xpp = xp;
*ypp = yp;
}
// find distance
return Distance( x, y, xp, yp );
}
/***********************************************************************************/
double GetPointToLineSegmentDistance( int x, int y, int xi, int yi, int xf, int yf )
/***********************************************************************************/
/** Function GetPointToLineSegmentDistance
* Get distance between line segment and point
* @param x,y = point
* @param xi,yi and xf,yf = the end-points of the line segment
* @return the distance
*/
{
// test for vertical or horizontal segment
if( xf==xi )
{
// vertical line segment
if( InRange( y, yi, yf ) )
return abs( x - xi );
else
return min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
}
else if( yf==yi )
{
// horizontal line segment
if( InRange( x, xi, xf ) )
return abs( y - yi );
else
return min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
}
else
{
// oblique segment
// find a,b such that (xi,yi) and (xf,yf) lie on y = a + bx
double b = (double)(yf-yi)/(xf-xi);
double a = (double)yi-b*xi;
// find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
double d = -1.0/b;
double c = (double)y-d*x;
// find nearest point to (x,y) on line through (xi,yi) to (xf,yf)
double xp = (a-c)/(d-b);
double yp = a + b*xp;
// find distance
if( InRange( xp, xi, xf ) && InRange( yp, yi, yf ) )
return Distance( x, y, xp, yp );
else
return min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
}
}
// test for value within range
//
bool InRange( double x, double xi, double xf )
{
if( xf>xi )
{
if( x >= xi && x <= xf )
return true;
}
else
{
if( x >= xf && x <= xi )
return true;
}
return false;
}
// Get distance between 2 points
//
double Distance( int x1, int y1, int x2, int y2 )
{
double d;
d = sqrt( (double)(x1-x2)*(x1-x2) + (double)(y1-y2)*(y1-y2) );
if( d > INT_MAX || d < INT_MIN )
ASSERT(0);
return (int)d;
}
// this finds approximate solutions
// note: this works best if el2 is smaller than el1
//
int GetArcIntersections( EllipseKH * el1, EllipseKH * el2,
double * x1, double * y1, double * x2, double * y2 )
{
if( el1->theta2 > el1->theta1 )
ASSERT(0);
if( el2->theta2 > el2->theta1 )
ASSERT(0);
const int NSTEPS = 32;
double xret[2], yret[2];
double xscale = 1.0/el1->xrad;
double yscale = 1.0/el1->yrad;
// now transform params of second ellipse into reference frame
// with origin at center if first ellipse,
// scaled so the first ellipse is a circle of radius = 1.0
double xo = (el2->Center.X - el1->Center.X)*xscale;
double yo = (el2->Center.Y - el1->Center.Y)*yscale;
double xr = el2->xrad*xscale;
double yr = el2->yrad*yscale;
// now test NSTEPS positions in arc, moving clockwise (ie. decreasing theta)
double step = M_PI/((NSTEPS-1)*2.0);
double d_prev, th_prev;
double th_interp;
double th1;
int n = 0;
for( int i=0; i<NSTEPS; i++ )
{
double theta;
if( i < NSTEPS-1 )
theta = el2->theta1 - i*step;
else
theta = el2->theta2;
double x = xo + xr*cos(theta);
double y = yo + yr*sin(theta);
double d = 1.0 - sqrt(x*x + y*y);
if( i>0 )
{
bool bInt = false;
if( d >= 0.0 && d_prev <= 0.0 )
{
th_interp = theta + (step*(-d_prev))/(d-d_prev);
bInt = true;
}
else if( d <= 0.0 && d_prev >= 0.0 )
{
th_interp = theta + (step*d_prev)/(d_prev-d);
bInt = true;
}
if( bInt )
{
x = xo + xr*cos(th_interp);
y = yo + yr*sin(th_interp);
th1 = atan2( y, x );
if( th1 <= el1->theta1 && th1 >= el1->theta2 )
{
xret[n] = x*el1->xrad + el1->Center.X;
yret[n] = y*el1->yrad + el1->Center.Y;
n++;
if( n > 2 )
ASSERT(0);
}
}
}
d_prev = d;
th_prev = theta;
}
if( x1 )
*x1 = xret[0];
if( y1 )
*y1 = yret[0];
if( x2 )
*x2 = xret[1];
if( y2 )
*y2 = yret[1];
return n;
}
// this finds approximate solution
//
//double GetSegmentClearance( EllipseKH * el1, EllipseKH * el2,
double GetArcClearance( EllipseKH * el1, EllipseKH * el2,
double * x1, double * y1 )
{
const int NSTEPS = 32;
if( el1->theta2 > el1->theta1 )
ASSERT(0);
if( el2->theta2 > el2->theta1 )
ASSERT(0);
// test multiple positions in both arcs, moving clockwise (ie. decreasing theta)
double th_start = el1->theta1;
double th_end = el1->theta2;
double th_start2 = el2->theta1;
double th_end2 = el2->theta2;
double dmin = DBL_MAX;
double xmin, ymin, thmin, thmin2;
int nsteps = NSTEPS;
int nsteps2 = NSTEPS;
double step = (th_start-th_end)/(nsteps-1);
double step2 = (th_start2-th_end2)/(nsteps2-1);
while( (step * max(el1->xrad, el1->yrad)) > 1.0*NM_PER_MIL
&& (step2 * max(el2->xrad, el2->yrad)) > 1.0*NM_PER_MIL )
{
step = (th_start-th_end)/(nsteps-1);
for( int i=0; i<nsteps; i++ )
{
double theta;
if( i < nsteps-1 )
theta = th_start - i*step;
else
theta = th_end;
double x = el1->Center.X + el1->xrad*cos(theta);
double y = el1->Center.Y + el1->yrad*sin(theta);
step2 = (th_start2-th_end2)/(nsteps2-1);
for( int i2=0; i2<nsteps2; i2++ )
{
double theta2;
if( i2 < nsteps2-1 )
theta2 = th_start2 - i2*step2;
else
theta2 = th_end2;
double x2 = el2->Center.X + el2->xrad*cos(theta2);
double y2 = el2->Center.Y + el2->yrad*sin(theta2);
double d = Distance( x, y, x2, y2 );
if( d < dmin )
{
dmin = d;
xmin = x;
ymin = y;
thmin = theta;
thmin2 = theta2;
}
}
}
if( step > step2 )
{
th_start = min(el1->theta1, thmin + step);
th_end = max(el1->theta2, thmin - step);
step = (th_start - th_end)/nsteps;
}
else
{
th_start2 = min(el2->theta1, thmin2 + step2);
th_end2 = max(el2->theta2, thmin2 - step2);
step2 = (th_start2 - th_end2)/nsteps2;
}
}
if( x1 )
*x1 = xmin;
if( y1 )
*y1 = ymin;
return dmin;
}
// math for graphics utility routines, from FreePCB
using namespace std;
#include <vector>
#include <math.h>
#include <float.h>
#include <limits.h>
#include "defs-macros.h"
#include "PolyLine2Kicad.h"
#include "freepcb_ids.h"
#include "PolyLine.h"
// function to find inflection-pont to create a "dogleg" of two straight-line segments
// where one segment is vertical or horizontal and the other is at 45 degrees or 90 degrees
// enter with:
// pi = start point
// pf = end point
// mode = IM_90_45 or IM_45_90 or IM_90
//
CPoint GetInflectionPoint( CPoint pi, CPoint pf, int mode )
{
CPoint p = pi;
if( mode == IM_NONE )
return p;
int dx = pf.x - pi.x;
int dy = pf.y - pi.y;
if( dx == 0 || dy == 0 || abs(dx) == abs(dy) )
{
// only one segment needed
}
else
{
if( abs(dy) > abs(dx) )
{
// vertical > horizontal
if( mode == IM_90 )
{
p.x = pi.x;
p.y = pf.y;
}
else if( mode == IM_45_90 || mode == IM_90_45 )
{
int vert; // length of vertical line needed
if( dy > 0 )
vert = dy - abs(dx); // positive
else
vert = dy + abs(dx); // negative
if( mode == IM_90_45 )
p.y = pi.y + vert;
else if( mode == IM_45_90 )
{
p.y = pf.y - vert;
p.x = pf.x;
}
}
else
ASSERT(0);
}
else
{
// horizontal > vertical
if( mode == IM_90 )
{
p.x = pf.x;
p.y = pi.y;
}
else if( mode == IM_45_90 || mode == IM_90_45 )
{
int hor; // length of horizontal line needed
if( dx > 0 )
hor = dx - abs(dy); // positive
else
hor = dx + abs(dy); // negative
if( mode == IM_90_45 )
p.x = pi.x + hor;
else if( mode == IM_45_90 )
{
p.x = pf.x - hor;
p.y = pf.y;
}
}
else
ASSERT(0);
}
}
return p;
}
//
// function to rotate a point clockwise about another point
// currently, angle must be 0, 90, 180 or 270
//
void RotatePoint( CPoint *p, int angle, CPoint org )
{
if( angle == 90 )
{
int tempy = org.y + (org.x - p->x);
p->x = org.x + (p->y - org.y);
p->y = tempy;
}
else if( angle > 90 )
{
for( int i=0; i<(angle/90); i++ )
RotatePoint( p, 90, org );
}
}
// function to rotate a rectangle clockwise about a point
// angle must be 0, 90, 180 or 270
// on exit, r->top > r.bottom, r.right > r.left
//
void RotateRect( CRect *r, int angle, CPoint org )
{
CRect tr;
if( angle == 90 )
{
tr.left = org.x + (r->bottom - org.y);
tr.right = org.x + (r->top - org.y);
tr.top = org.y + (org.x - r->right);
tr.bottom = org.y + (org.x - r->left);
if( tr.left > tr.right )
{
int temp = tr.right;
tr.left = tr.right;
tr.left = temp;
}
if( tr.left > tr.right )
{
int temp = tr.right;
tr.left = tr.right;
tr.left = temp;
}
if( tr.bottom > tr.top )
{
int temp = tr.bottom;
tr.bottom = tr.top;
tr.top = temp;
}
}
else if( angle > 90 )
{
tr = *r;
for( int i=0; i<(angle/90); i++ )
RotateRect( &tr, 90, org );
}
*r = tr;
}
// test for hit on line segment
// i.e. cursor within a given distance from segment
// enter with: x,y = cursor coords
// (xi,yi) and (xf,yf) are the end-points of the line segment
// dist = maximum distance for hit
//
int TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist )
{
double dd;
// test for vertical or horizontal segment
if( xf==xi )
{
// vertical segment
dd = fabs( (double)(x-xi) );
if( dd<dist && ( (yf>yi && y<yf && y>yi) || (yf<yi && y>yf && y<yi) ) )
return 1;
}
else if( yf==yi )
{
// horizontal segment
dd = fabs( (double)(y-yi) );
if( dd<dist && ( (xf>xi && x<xf && x>xi) || (xf<xi && x>xf && x<xi) ) )
return 1;
}
else
{
// oblique segment
// find a,b such that (xi,yi) and (xf,yf) lie on y = a + bx
double b = (double)(yf-yi)/(xf-xi);
double a = (double)yi-b*xi;
// find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
double d = -1.0/b;
double c = (double)y-d*x;
// find nearest point to (x,y) on line segment (xi,yi) to (xf,yf)
double xp = (a-c)/(d-b);
double yp = a + b*xp;
// find distance
dd = sqrt((x-xp)*(x-xp)+(y-yp)*(y-yp));
if( fabs(b)>0.7 )
{
// line segment more vertical than horizontal
if( dd<dist && ( (yf>yi && yp<yf && yp>yi) || (yf<yi && yp>yf && yp<yi) ) )
return 1;
}
else
{
// line segment more horizontal than vertical
if( dd<dist && ( (xf>xi && xp<xf && xp>xi) || (xf<xi && xp>xf && xp<xi) ) )
return 1;
}
}
return 0; // no hit
}
// find intersection between y = a + bx and y = c + dx;
//
int FindLineIntersection( double a, double b, double c, double d, double * x, double * y )
{
*x = (c-a)/(b-d);
*y = a + b*(*x);
return 0;
}
// set EllipseKH struct to describe the ellipse for an arc
//
int MakeEllipseFromArc( int xi, int yi, int xf, int yf, int style, EllipseKH * el )
{
// arc (quadrant of ellipse)
// convert to clockwise arc
int xxi, xxf, yyi, yyf;
if( style == CPolyLine::ARC_CCW )
{
xxi = xf;
xxf = xi;
yyi = yf;
yyf = yi;
}
else
{
xxi = xi;
xxf = xf;
yyi = yi;
yyf = yf;
}
// find center and radii of ellipse
double xo, yo;
if( xxf > xxi && yyf > yyi )
{
xo = xxf;
yo = yyi;
el->theta1 = M_PI;
el->theta2 = M_PI/2.0;
}
else if( xxf < xxi && yyf > yyi )
{
xo = xxi;
yo = yyf;
el->theta1 = -M_PI/2.0;
el->theta2 = -M_PI;
}
else if( xxf < xxi && yyf < yyi )
{
xo = xxf;
yo = yyi;
el->theta1 = 0.0;
el->theta2 = -M_PI/2.0;
}
else if( xxf > xxi && yyf < yyi )
{
xo = xxi;
yo = yyf;
el->theta1 = M_PI/2.0;
el->theta2 = 0.0;
}
el->Center.X = xo;
el->Center.Y = yo;
el->xrad = abs(xf-xi);
el->yrad = abs(yf-yi);
#if 0
el->Phi = 0.0;
el->MaxRad = el->xrad;
el->MinRad = el->yrad;
if( el->MaxRad < el->MinRad )
{
el->MaxRad = el->yrad;
el->MinRad = el->xrad;
el->Phi = M_PI/2.0;
}
#endif
return 0;
}
// find intersections between line segment (xi,yi) to (xf,yf)
// and line segment (xi2,yi2) to (xf2,yf2)
// the line segments may be arcs (i.e. quadrant of an ellipse) or straight
// returns number of intersections found (max of 2)
// returns coords of intersections in arrays x[2], y[2]
//
int FindSegmentIntersections( int xi, int yi, int xf, int yf, int style,
int xi2, int yi2, int xf2, int yf2, int style2,
double x[], double y[] )
{
double xr[12], yr[12];
int iret = 0;
if( max(xi,xf) < min(xi2,xf2)
|| min(xi,xf) > max(xi2,xf2)
|| max(yi,yf) < min(yi2,yf2)
|| min(yi,yf) > max(yi2,yf2) )
return 0;
if( style != CPolyLine::STRAIGHT && style2 != CPolyLine::STRAIGHT )
{
// two identical arcs intersect
if( style == style2 && xi == xi2 && yi == yi2 && xf == xf2 && yf == yf2 )
{
if( x && y )
{
x[0] = xi;
y[0] = yi;
}
return 1;
}
else if( style != style2 && xi == xf2 && yi == yf2 && xf == xi2 && yf == yi2 )
{
if( x && y )
{
x[0] = xi;
y[0] = yi;
}
return 1;
}
}
if( style == CPolyLine::STRAIGHT && style2 == CPolyLine::STRAIGHT )
{
// both straight-line segments
int x, y;
bool bYes = TestForIntersectionOfStraightLineSegments( xi, yi, xf, yf, xi2, yi2, xf2, yf2, &x, &y );
if( !bYes )
return 0;
xr[0] = x;
yr[0] = y;
iret = 1;
}
else if( style == CPolyLine::STRAIGHT )
{
// first segment is straight, second segment is an arc
int ret;
double x1r, y1r, x2r, y2r;
if( xf == xi )
{
// vertical first segment
double a = xi;
double b = DBL_MAX/2.0;
ret = FindLineSegmentIntersection( a, b, xi2, yi2, xf2, yf2, style2,
&x1r, &y1r, &x2r, &y2r );
}
else
{
double b = (double)(yf-yi)/(double)(xf-xi);
double a = yf - b*xf;
ret = FindLineSegmentIntersection( a, b, xi2, yi2, xf2, yf2, style2,
&x1r, &y1r, &x2r, &y2r );
}
if( ret == 0 )
return 0;
if( InRange( x1r, xi, xf ) && InRange( y1r, yi, yf ) )
{
xr[iret] = x1r;
yr[iret] = y1r;
iret++;
}
if( ret == 2 )
{
if( InRange( x2r, xi, xf ) && InRange( y2r, yi, yf ) )
{
xr[iret] = x2r;
yr[iret] = y2r;
iret++;
}
}
}
else if( style2 == CPolyLine::STRAIGHT )
{
// first segment is an arc, second segment is straight
int ret;
double x1r, y1r, x2r, y2r;
if( xf2 == xi2 )
{
// vertical second segment
double a = xi2;
double b = DBL_MAX/2.0;
ret = FindLineSegmentIntersection( a, b, xi, yi, xf, yf, style,
&x1r, &y1r, &x2r, &y2r );
}
else
{
double b = (double)(yf2-yi2)/(double)(xf2-xi2);
double a = yf2 - b*xf2;
ret = FindLineSegmentIntersection( a, b, xi, yi, xf, yf, style,
&x1r, &y1r, &x2r, &y2r );
}
if( ret == 0 )
return 0;
if( InRange( x1r, xi2, xf2 ) && InRange( y1r, yi2, yf2 ) )
{
xr[iret] = x1r;
yr[iret] = y1r;
iret++;
}
if( ret == 2 )
{
if( InRange( x2r, xi2, xf2 ) && InRange( y2r, yi2, yf2 ) )
{
xr[iret] = x2r;
yr[iret] = y2r;
iret++;
}
}
}
else
{
// both segments are arcs
EllipseKH el1;
EllipseKH el2;
MakeEllipseFromArc( xi, yi, xf, yf, style, &el1 );
MakeEllipseFromArc( xi2, yi2, xf2, yf2, style2, &el2 );
int n;
if( el1.xrad+el1.yrad > el2.xrad+el2.yrad )
n = GetArcIntersections( &el1, &el2 );
else
n = GetArcIntersections( &el2, &el1 );
iret = n;
}
if( x && y )
{
for( int i=0; i<iret; i++ )
{
x[i] = xr[i];
y[i] = yr[i];
}
}
return iret;
}
// find intersection between line y = a + bx and line segment (xi,yi) to (xf,yf)
// if b > DBL_MAX/10, assume vertical line at x = a
// the line segment may be an arc (i.e. quadrant of an ellipse)
// return 0 if no intersection
// returns 1 or 2 if intersections found
// sets coords of intersections in *x1, *y1, *x2, *y2
// if no intersection, returns min distance in dist
//
int FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf, int style,
double * x1, double * y1, double * x2, double * y2,
double * dist )
{
double xx, yy;
bool bVert = false;
if( b > DBL_MAX/10.0 )
bVert = true;
if( xf != xi )
{
// non-vertical segment, get intersection
if( style == CPolyLine::STRAIGHT || yf == yi )
{
// horizontal or oblique straight segment
// put into form y = c + dx;
double d = (double)(yf-yi)/(double)(xf-xi);
double c = yf - d*xf;
if( bVert )
{
// if vertical line, easy
if( InRange( a, xi, xf ) )
{
*x1 = a;
*y1 = c + d*a;
return 1;
}
else
{
if( dist )
*dist = min( abs(a-xi), abs(a-xf) );
return 0;
}
}
if( fabs(b-d) < 1E-12 )
{
// parallel lines
if( dist )
{
*dist = GetPointToLineDistance( a, b, xi, xf );
}
return 0; // lines parallel
}
// calculate intersection
xx = (c-a)/(b-d);
yy = a + b*(xx);
// see if intersection is within the line segment
if( yf == yi )
{
// horizontal line
if( (xx>=xi && xx>xf) || (xx<=xi && xx<xf) )
return 0;
}
else
{
// oblique line
if( (xx>=xi && xx>xf) || (xx<=xi && xx<xf)
|| (yy>yi && yy>yf) || (yy<yi && yy<yf) )
return 0;
}
}
else if( style == CPolyLine::ARC_CW || style == CPolyLine::ARC_CCW )
{
// arc (quadrant of ellipse)
// convert to clockwise arc
int xxi, xxf, yyi, yyf;
if( style == CPolyLine::ARC_CCW )
{
xxi = xf;
xxf = xi;
yyi = yf;
yyf = yi;
}
else
{
xxi = xi;
xxf = xf;
yyi = yi;
yyf = yf;
}
// find center and radii of ellipse
double xo, yo, rx, ry;
if( xxf > xxi && yyf > yyi )
{
xo = xxf;
yo = yyi;
}
else if( xxf < xxi && yyf > yyi )
{
xo = xxi;
yo = yyf;
}
else if( xxf < xxi && yyf < yyi )
{
xo = xxf;
yo = yyi;
}
else if( xxf > xxi && yyf < yyi )
{
xo = xxi;
yo = yyf;
}
rx = fabs( (double)(xxi-xxf) );
ry = fabs( (double)(yyi-yyf) );
bool test;
double xx1, xx2, yy1, yy2, aa;
if( bVert )
{
// shift vertical line to coordinate system of ellipse
aa = a - xo;
test = FindVerticalLineEllipseIntersections( rx, ry, aa, &yy1, &yy2 );
if( !test )
return 0;
// shift back to PCB coordinates
yy1 += yo;
yy2 += yo;
xx1 = a;
xx2 = a;
}
else
{
// shift line to coordinate system of ellipse
aa = a + b*xo - yo;
test = FindLineEllipseIntersections( rx, ry, aa, b, &xx1, &xx2 );
if( !test )
return 0;
// shift back to PCB coordinates
yy1 = aa + b*xx1;
xx1 += xo;
yy1 += yo;
yy2 = aa + b*xx2;
xx2 += xo;
yy2 += yo;
}
int npts = 0;
if( (xxf>xxi && xx1<xxf && xx1>xxi) || (xxf<xxi && xx1<xxi && xx1>xxf) )
{
if( (yyf>yyi && yy1<yyf && yy1>yyi) || (yyf<yyi && yy1<yyi && yy1>yyf) )
{
*x1 = xx1;
*y1 = yy1;
npts = 1;
}
}
if( (xxf>xxi && xx2<xxf && xx2>xxi) || (xxf<xxi && xx2<xxi && xx2>xxf) )
{
if( (yyf>yyi && yy2<yyf && yy2>yyi) || (yyf<yyi && yy2<yyi && yy2>yyf) )
{
if( npts == 0 )
{
*x1 = xx2;
*y1 = yy2;
npts = 1;
}
else
{
*x2 = xx2;
*y2 = yy2;
npts = 2;
}
}
}
return npts;
}
else
ASSERT(0);
}
else
{
// vertical line segment
if( bVert )
return 0;
xx = xi;
yy = a + b*xx;
if( (yy>=yi && yy>yf) || (yy<=yi && yy<yf) )
return 0;
}
*x1 = xx;
*y1 = yy;
return 1;
}
// Test for intersection of line segments
// If lines are parallel, returns false
// If true, returns intersection coords in x, y
// if false, returns min. distance in dist (may be 0.0 if parallel)
// and coords on nearest point in one of the segments in (x,y)
//
bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f,
int x2i, int y2i, int x2f, int y2f,
int * x, int * y, double * d )
{
double a, b, dist;
// first, test for intersection
if( x1i == x1f && x2i == x2f )
{
// both segments are vertical, can't intersect
}
else if( y1i == y1f && y2i == y2f )
{
// both segments are horizontal, can't intersect
}
else if( x1i == x1f && y2i == y2f )
{
// first seg. vertical, second horizontal, see if they cross
if( InRange( x1i, x2i, x2f )
&& InRange( y2i, y1i, y1f ) )
{
if( x )
*x = x1i;
if( y )
*y = y2i;
if( d )
*d = 0.0;
return true;
}
}
else if( y1i == y1f && x2i == x2f )
{
// first seg. horizontal, second vertical, see if they cross
if( InRange( y1i, y2i, y2f )
&& InRange( x2i, x1i, x1f ) )
{
if( x )
*x = x2i;
if( y )
*y = y1i;
if( d )
*d = 0.0;
return true;
}
}
else if( x1i == x1f )
{
// first segment vertical, second oblique
// get a and b for second line segment, so that y = a + bx;
b = (double)(y2f-y2i)/(x2f-x2i);
a = (double)y2i - b*x2i;
double x1, y1, x2, y2;
int test = FindLineSegmentIntersection( a, b, x1i, y1i, x1f, y1f, CPolyLine::STRAIGHT,
&x1, &y1, &x2, &y2 );
if( test )
{
if( InRange( y1, y1i, y1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) )
{
if( x )
*x = x1;
if( y )
*y = y1;
if( d )
*d = 0.0;
return true;
}
}
}
else if( y1i == y1f )
{
// first segment horizontal, second oblique
// get a and b for second line segment, so that y = a + bx;
b = (double)(y2f-y2i)/(x2f-x2i);
a = (double)y2i - b*x2i;
double x1, y1, x2, y2;
int test = FindLineSegmentIntersection( a, b, x1i, y1i, x1f, y1f, CPolyLine::STRAIGHT,
&x1, &y1, &x2, &y2 );
if( test )
{
if( InRange( x1, x1i, x1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) )
{
if( x )
*x = x1;
if( y )
*y = y1;
if( d )
*d = 0.0;
return true;
}
}
}
else if( x2i == x2f )
{
// second segment vertical, first oblique
// get a and b for first line segment, so that y = a + bx;
b = (double)(y1f-y1i)/(x1f-x1i);
a = (double)y1i - b*x1i;
double x1, y1, x2, y2;
int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f, CPolyLine::STRAIGHT,
&x1, &y1, &x2, &y2 );
if( test )
{
if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) && InRange( y1, y2i, y2f ) )
{
if( x )
*x = x1;
if( y )
*y = y1;
if( d )
*d = 0.0;
return true;
}
}
}
else if( y2i == y2f )
{
// second segment horizontal, first oblique
// get a and b for second line segment, so that y = a + bx;
b = (double)(y1f-y1i)/(x1f-x1i);
a = (double)y1i - b*x1i;
double x1, y1, x2, y2;
int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f, CPolyLine::STRAIGHT,
&x1, &y1, &x2, &y2 );
if( test )
{
if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) )
{
if( x )
*x = x1;
if( y )
*y = y1;
if( d )
*d = 0.0;
return true;
}
}
}
else
{
// both segments oblique
if( (long)(y1f-y1i)*(x2f-x2i) != (long)(y2f-y2i)*(x1f-x1i) )
{
// not parallel, get a and b for first line segment, so that y = a + bx;
b = (double)(y1f-y1i)/(x1f-x1i);
a = (double)y1i - b*x1i;
double x1, y1, x2, y2;
int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f, CPolyLine::STRAIGHT,
&x1, &y1, &x2, &y2 );
// both segments oblique
if( test )
{
if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) )
{
if( x )
*x = x1;
if( y )
*y = y1;
if( d )
*d = 0.0;
return true;
}
}
}
}
// don't intersect, get shortest distance between each endpoint and the other line segment
dist = GetPointToLineSegmentDistance( x1i, y1i, x2i, y2i, x2f, y2f );
double xx = x1i;
double yy = y1i;
double dd = GetPointToLineSegmentDistance( x1f, y1f, x2i, y2i, x2f, y2f );
if( dd < dist )
{
dist = dd;
xx = x1f;
yy = y1f;
}
dd = GetPointToLineSegmentDistance( x2i, y2i, x1i, y1i, x1f, y1f );
if( dd < dist )
{
dist = dd;
xx = x2i;
yy = y2i;
}
dd = GetPointToLineSegmentDistance( x2f, y2f, x1i, y1i, x1f, y1f );
if( dd < dist )
{
dist = dd;
xx = x2f;
yy = y2f;
}
if( x )
*x = xx;
if( y )
*y = yy;
if( d )
*d = dist;
return false;
}
// quicksort algorithm
// sorts array numbers[], also moves elements of another array index[]
//
#define Q3WAY
void quickSort(int numbers[], int index[], int array_size)
{
#ifdef Q3WAY
q_sort_3way(numbers, index, 0, array_size - 1);
#else
q_sort(numbers, index, 0, array_size - 1);
#endif
}
// standard quicksort
//
void q_sort(int numbers[], int index[], int left, int right)
{
int pivot, pivot_index, l_hold, r_hold;
l_hold = left;
r_hold = right;
pivot = numbers[left];
pivot_index = index[left];
while (left < right)
{
while ((numbers[right] >= pivot) && (left < right))
right--;
if (left != right)
{
numbers[left] = numbers[right];
index[left] = index[right];
left++;
}
while ((numbers[left] <= pivot) && (left < right))
left++;
if (left != right)
{
numbers[right] = numbers[left];
index[right] = index[left];
right--;
}
}
numbers[left] = pivot;
index[left] = pivot_index;
pivot = left;
left = l_hold;
right = r_hold;
if (left < pivot)
q_sort(numbers, index, left, pivot-1);
if (right > pivot)
q_sort(numbers, index, pivot+1, right);
}
// 3-way quicksort...useful where there are duplicate values
//
void q_sort_3way( int a[], int b[], int l, int r )
{
#define EXCH(i,j) {int temp=a[i]; a[i]=a[j]; a[j]=temp; temp=b[i]; b[i]=b[j]; b[j]=temp;}
int i = l - 1;
int j = r;
int p = l - 1;
int q = r;
int v = a[r];
if( r <= l )
return;
for(;;)
{
while( a[++i] < v );
while( v < a[--j] )
if( j == 1 )
break;
if( i >= j )
break;
EXCH( i, j );
if( a[i] == v )
{
p++;
EXCH( p, i );
}
if( v == a[j] )
{
q--;
EXCH( j, q );
}
}
EXCH( i, r );
j = i - 1;
i = i + 1;
for( int k=l; k<p; k++, j-- )
EXCH( k, j );
for( int k=r-1; k>q; k--, i++ )
EXCH( i, k );
q_sort_3way( a, b, l, j );
q_sort_3way( a, b, i, r );
}
// solves quadratic equation
// i.e. ax**2 + bx + c = 0
// returns true if solution exist, with solutions in x1 and x2
// else returns false
//
bool Quadratic( double a, double b, double c, double *x1, double *x2 )
{
double root = b*b - 4.0*a*c;
if( root < 0.0 )
return false;
root = sqrt( root );
*x1 = (-b+root)/(2.0*a);
*x2 = (-b-root)/(2.0*a);
return true;
}
// finds intersections of vertical line at x
// with ellipse defined by (x^2)/(a^2) + (y^2)/(b^2) = 1;
// returns true if solution exist, with solutions in y1 and y2
// else returns false
//
bool FindVerticalLineEllipseIntersections( double a, double b, double x, double *y1, double *y2 )
{
double y_sqr = (1.0-(x*x)/(a*a))*b*b;
if( y_sqr < 0.0 )
return false;
*y1 = sqrt(y_sqr);
*y2 = -*y1;
return true;
}
// finds intersections of straight line y = c + dx
// with ellipse defined by (x^2)/(a^2) + (y^2)/(b^2) = 1;
// returns true if solution exist, with solutions in x1 and x2
// else returns false
//
bool FindLineEllipseIntersections( double a, double b, double c, double d, double *x1, double *x2 )
{
// quadratic terms
double A = d*d+b*b/(a*a);
double B = 2.0*c*d;
double C = c*c-b*b;
return Quadratic( A, B, C, x1, x2 );
}
#if 0
// draw a straight line or an arc between xi,yi and xf,yf
//
void DrawArc( CDC * pDC, int shape, int xxi, int yyi, int xxf, int yyf, bool bMeta )
{
int xi, yi, xf, yf;
if( shape == DL_LINE || xxi == xxf || yyi == yyf )
{
// draw straight line
pDC->MoveTo( xxi, yyi );
pDC->LineTo( xxf, yyf );
}
else if( shape == DL_ARC_CCW || shape == DL_ARC_CW )
{
// set endpoints so we can always draw counter-clockwise arc
if( shape == DL_ARC_CW )
{
xi = xxf;
yi = yyf;
xf = xxi;
yf = yyi;
}
else
{
xi = xxi;
yi = yyi;
xf = xxf;
yf = yyf;
}
pDC->MoveTo( xi, yi );
if( xf > xi && yf > yi )
{
// quadrant 1
int w = (xf-xi)*2;
int h = (yf-yi)*2;
if( !bMeta )
pDC->Arc( xf-w, yi+h, xf, yi,
xi, yi, xf, yf );
else
pDC->Arc( xf-w, yi, xf, yi+h,
xf, yf, xi, yi );
}
else if( xf < xi && yf > yi )
{
// quadrant 2
int w = -(xf-xi)*2;
int h = (yf-yi)*2;
if( !bMeta )
pDC->Arc( xi-w, yf, xi, yf-h,
xi, yi, xf, yf );
else
pDC->Arc( xi-w, yf-h, xi, yf,
xf, yf, xi, yi );
}
else if( xf < xi && yf < yi )
{
// quadrant 3
int w = -(xf-xi)*2;
int h = -(yf-yi)*2;
if( !bMeta )
pDC->Arc( xf, yi, xf+w, yi-h,
xi, yi, xf, yf );
else
pDC->Arc( xf, yi-h, xf+w, yi,
xf, yf, xi, yi );
}
else if( xf > xi && yf < yi )
{
// quadrant 4
int w = (xf-xi)*2;
int h = -(yf-yi)*2;
if( !bMeta )
pDC->Arc( xi, yf+h, xi+w, yf,
xi, yi, xf, yf );
else
pDC->Arc( xi, yf, xi+w, yf+h,
xf, yf, xi, yi );
}
pDC->MoveTo( xxf, yyf );
}
else
ASSERT(0); // oops
}
#endif
// Get arrays of circles, rects and line segments to represent pad
// for purposes of drawing pad or calculating clearances
// margins of circles and line segments represent pad outline
// circles and rects are used to find points inside pad
//
void GetPadElements( int type, int x, int y, int wid, int len, int radius, int angle,
int * nr, my_rect r[], int * nc, my_circle c[], int * ns, my_seg s[] )
{
*nc = 0;
*nr = 0;
*ns = 0;
if( type == PAD_ROUND )
{
*nc = 1;
c[0] = my_circle(x,y,wid/2);
return;
}
if( type == PAD_SQUARE )
{
*nr = 1;
r[0] = my_rect(x-wid/2, y-wid/2,x+wid/2, y+wid/2);
*ns = 4;
s[0] = my_seg(x-wid/2, y+wid/2,x+wid/2, y+wid/2); // top
s[1] = my_seg(x-wid/2, y-wid/2,x+wid/2, y-wid/2); // bottom
s[2] = my_seg(x-wid/2, y-wid/2,x-wid/2, y+wid/2); // left
s[3] = my_seg(x+wid/2, y-wid/2,x+wid/2, y+wid/2); // right
return;
}
if( type == PAD_OCTAGON )
{
const double pi = 3.14159265359;
*nc = 1; // circle represents inside of polygon
c[0] = my_circle(x, y, wid/2);
*ns = 8; // now create sides of polygon
double theta = pi/8.0;
double radius = 0.5*(double)wid/cos(theta);
double last_x = x + radius*cos(theta);
double last_y = y + radius*sin(theta);
for( int is=0; is<8; is++ )
{
theta += pi/4.0;
double dx = x + radius*cos(theta);
double dy = y + radius*sin(theta);
s[is] = my_seg(last_x, last_y, x, y);
last_x = dx;
last_y = dy;
}
return;
}
//
int h;
int v;
if( angle == 90 || angle == 270 )
{
h = wid;
v = len;
}
else
{
v = wid;
h = len;
}
if( type == PAD_RECT )
{
*nr = 1;
r[0] = my_rect(x-h/2, y-v/2, x+h/2, y+v/2);
*ns = 4;
s[0] = my_seg(x-h/2, y+v/2,x+h/2, y+v/2); // top
s[1] = my_seg(x-h/2, y-v/2,x+h/2, y-v/2); // bottom
s[2] = my_seg(x-h/2, y-v/2,x-h/2, y+v/2); // left
s[3] = my_seg(x+h/2, y-v/2,x+h/2, y+v/2); // right
return;
}
if( type == PAD_RRECT )
{
*nc = 4;
c[0] = my_circle(x-h/2+radius, y-v/2+radius, radius); // bottom left circle
c[1] = my_circle(x+h/2-radius, y-v/2+radius, radius); // bottom right circle
c[2] = my_circle(x-h/2+radius, y+v/2-radius, radius); // top left circle
c[3] = my_circle(x+h/2-radius, y+v/2-radius, radius); // top right circle
*ns = 4;
s[0] = my_seg(x-h/2+radius, y+v/2, x+h/2-radius, y+v/2); // top
s[1] = my_seg(x-h/2+radius, y-v/2, x+h/2-radius, y+v/2); // bottom
s[2] = my_seg(x-h/2, y-v/2+radius, x-h/2, y+v/2-radius); // left
s[3] = my_seg(x+h/2, y-v/2+radius, x+h/2, y+v/2-radius); // right
return;
}
if( type == PAD_OVAL )
{
if( h > v )
{
// horizontal
*nc = 2;
c[0] = my_circle(x-h/2+v/2, y, v/2); // left circle
c[1] = my_circle(x+h/2-v/2, y, v/2); // right circle
*nr = 1;
r[0] = my_rect(x-h/2+v/2, y-v/2, x+h/2-v/2, y+v/2);
*ns = 2;
s[0] = my_seg(x-h/2+v/2, y+v/2, x+h/2-v/2, y+v/2); // top
s[1] = my_seg(x-h/2+v/2, y-v/2, x+h/2-v/2, y-v/2); // bottom
}
else
{
// vertical
*nc = 2;
c[0] = my_circle(x, y+v/2-h/2, h/2); // top circle
c[1] = my_circle(x, y-v/2+h/2, h/2); // bottom circle
*nr = 1;
r[0] = my_rect(x-h/2, y-v/2+h/2, x+h/2, y+v/2-h/2);
*ns = 2;
s[0] = my_seg(x-h/2, y-v/2+h/2, x-h/2, y+v/2-h/2); // left
s[1] = my_seg(x+h/2, y-v/2+h/2, x+h/2, y+v/2-h/2); // left
}
return;
}
ASSERT(0);
}
// Find distance from a staright line segment to a pad
//
int GetClearanceBetweenSegmentAndPad( int x1, int y1, int x2, int y2, int w,
int type, int x, int y, int wid, int len, int radius, int angle )
{
if( type == PAD_NONE )
return INT_MAX;
else
{
int nc, nr, ns;
my_circle c[4];
my_rect r[2];
my_seg s[8];
GetPadElements( type, x, y, wid, len, radius, angle,
&nr, r, &nc, c, &ns, s );
// first test for endpoints of line segment in rectangle
for( int ir=0; ir<nr; ir++ )
{
if( x1 >= r[ir].xlo && x1 <= r[ir].xhi && y1 >= r[ir].ylo && y1 <= r[ir].yhi )
return 0;
if( x2 >= r[ir].xlo && x2 <= r[ir].xhi && y2 >= r[ir].ylo && y2 <= r[ir].yhi )
return 0;
}
// now get distance from elements of pad outline
int dist = INT_MAX;
for( int ic=0; ic<nc; ic++ )
{
int d = GetPointToLineSegmentDistance( c[ic].x, c[ic].y, x1, y1, x2, y2 ) - c[ic].r - w/2;
dist = min(dist,d);
}
for( int is=0; is<ns; is++ )
{
double d;
TestForIntersectionOfStraightLineSegments( s[is].xi, s[is].yi, s[is].xf, s[is].yf,
x1, y1, x2, y2, NULL, NULL, &d );
d -= w/2;
dist = min(dist,d);
}
return max(0,dist);
}
}
// Get clearance between 2 segments
// Returns point in segment closest to other segment in x, y
// in clearance > max_cl, just returns max_cl and doesn't return x,y
//
int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int style1, int w1,
int x2i, int y2i, int x2f, int y2f, int style2, int w2,
int max_cl, int * x, int * y )
{
// check clearance between bounding rectangles
int test = max_cl + w1/2 + w2/2;
if( min(x1i,x1f)-max(x2i,x2f) > test )
return max_cl;
if( min(x2i,x2f)-max(x1i,x1f) > test )
return max_cl;
if( min(y1i,y1f)-max(y2i,y2f) > test )
return max_cl;
if( min(y2i,y2f)-max(y1i,y1f) > test )
return max_cl;
if( style1 == CPolyLine::STRAIGHT && style1 == CPolyLine::STRAIGHT )
{
// both segments are straight lines
int xx, yy;
double dd;
TestForIntersectionOfStraightLineSegments( x1i, y1i, x1f, y1f,
x2i, y2i, x2f, y2f, &xx, &yy, &dd );
int d = max( 0, dd - w1/2 - w2/2 );
if( x )
*x = xx;
if( y )
*y = yy;
return d;
}
// not both straight-line segments
// see if segments intersect
double xr[2];
double yr[2];
test = FindSegmentIntersections( x1i, y1i, x1f, y1f, style1, x2i, y2i, x2f, y2f, style2, xr, yr );
if( test )
{
if( x )
*x = xr[0];
if( y )
*y = yr[0];
return 0.0;
}
// at least one segment is an arc
EllipseKH el1;
EllipseKH el2;
bool bArcs;
int xi, yi, xf, yf;
if( style2 == CPolyLine::STRAIGHT )
{
// style1 = arc, style2 = straight
MakeEllipseFromArc( x1i, y1i, x1f, y1f, style1, &el1 );
xi = x2i;
yi = y2i;
xf = x2f;
yf = y2f;
bArcs = false;
}
else if( style1 == CPolyLine::STRAIGHT )
{
// style2 = arc, style1 = straight
xi = x1i;
yi = y1i;
xf = x1f;
yf = y1f;
MakeEllipseFromArc( x2i, y2i, x2f, y2f, style2, &el1 );
bArcs = false;
}
else
{
// style1 = arc, style2 = arc
MakeEllipseFromArc( x1i, y1i, x1f, y1f, style1, &el1 );
MakeEllipseFromArc( x2i, y2i, x2f, y2f, style2, &el2 );
bArcs = true;
}
const int NSTEPS = 32;
if( el1.theta2 > el1.theta1 )
ASSERT(0);
if( bArcs && el2.theta2 > el2.theta1 )
ASSERT(0);
// test multiple points in both segments
double th1;
double th2;
double len2;
if( bArcs )
{
th1 = el2.theta1;
th2 = el2.theta2;
len2 = max(el2.xrad, el2.yrad);
}
else
{
th1 = 1.0;
th2 = 0.0;
len2 = abs(xf-xi)+abs(yf-yi);
}
double s_start = el1.theta1;
double s_end = el1.theta2;
double s_start2 = th1;
double s_end2 = th2;
double dmin = DBL_MAX;
double xmin, ymin, smin, smin2;
int nsteps = NSTEPS;
int nsteps2 = NSTEPS;
double step = (s_start-s_end)/(nsteps-1);
double step2 = (s_start2-s_end2)/(nsteps2-1);
while( (step * max(el1.xrad, el1.yrad)) > 0.1*NM_PER_MIL
&& (step2 * len2) > 0.1*NM_PER_MIL )
{
step = (s_start-s_end)/(nsteps-1);
for( int i=0; i<nsteps; i++ )
{
double s;
if( i < nsteps-1 )
s = s_start - i*step;
else
s = s_end;
double x = el1.Center.X + el1.xrad*cos(s);
double y = el1.Center.Y + el1.yrad*sin(s);
// if not an arc, use s2 as fractional distance along line
step2 = (s_start2-s_end2)/(nsteps2-1);
for( int i2=0; i2<nsteps2; i2++ )
{
double s2;
if( i2 < nsteps2-1 )
s2 = s_start2 - i2*step2;
else
s2 = s_end2;
double x2, y2;
if( !bArcs )
{
x2 = xi + (xf-xi)*s2;
y2 = yi + (yf-yi)*s2;
}
else
{
x2 = el2.Center.X + el2.xrad*cos(s2);
y2 = el2.Center.Y + el2.yrad*sin(s2);
}
double d = Distance( x, y, x2, y2 );
if( d < dmin )
{
dmin = d;
xmin = x;
ymin = y;
smin = s;
smin2 = s2;
}
}
}
if( step > step2 )
{
s_start = min(el1.theta1, smin + step);
s_end = max(el1.theta2, smin - step);
step = (s_start - s_end)/nsteps;
}
else
{
s_start2 = min(th1, smin2 + step2);
s_end2 = max(th2, smin2 - step2);
step2 = (s_start2 - s_end2)/nsteps2;
}
}
if( x )
*x = xmin;
if( y )
*y = ymin;
return max(0,dmin-w1/2-w2/2); // allow for widths
}
// Find clearance between pads
// For each pad:
// type = PAD_ROUND, PAD_SQUARE, etc.
// x, y = center position
// w, l = width and length
// r = corner radius
// angle = 0 or 90 (if 0, pad length is along x-axis)
//
int GetClearanceBetweenPads( int type1, int x1, int y1, int w1, int l1, int r1, int angle1,
int type2, int x2, int y2, int w2, int l2, int r2, int angle2 )
{
if( type1 == PAD_NONE )
return INT_MAX;
if( type2 == PAD_NONE )
return INT_MAX;
int dist = INT_MAX;
int nr, nc, ns, nrr, ncc, nss;
my_rect r[2], rr[2];
my_circle c[4], cc[4];
my_seg s[8], ss[8];
GetPadElements( type1, x1, y1, w1, l1, r1, angle1,
&nr, r, &nc, c, &ns, s );
GetPadElements( type2, x2, y2, w2, l2, r2, angle2,
&nrr, rr, &ncc, cc, &nss, ss );
// now find distance from every element of pad1 to every element of pad2
for( int ic=0; ic<nc; ic++ )
{
for( int icc=0; icc<ncc; icc++ )
{
int d = Distance( c[ic].x, c[ic].y, cc[icc].x, cc[icc].y )
- c[ic].r - cc[icc].r;
dist = min(dist,d);
}
for( int iss=0; iss<nss; iss++ )
{
int d = GetPointToLineSegmentDistance( c[ic].x, c[ic].y,
ss[iss].xi, ss[iss].yi, ss[iss].xf, ss[iss].yf ) - c[ic].r;
dist = min(dist,d);
}
}
for( int is=0; is<ns; is++ )
{
for( int icc=0; icc<ncc; icc++ )
{
int d = GetPointToLineSegmentDistance( cc[icc].x, cc[icc].y,
s[is].xi, s[is].yi, s[is].xf, s[is].yf ) - cc[icc].r;
dist = min(dist,d);
}
for( int iss=0; iss<nss; iss++ )
{
double d;
TestForIntersectionOfStraightLineSegments( s[is].xi, s[is].yi, s[is].xf, s[is].yf,
ss[iss].xi, ss[iss].yi, ss[iss].xf, ss[iss].yf, NULL, NULL, &d );
dist = min(dist,d);
}
}
return max(dist,0);
}
// Get min. distance from (x,y) to line y = a + bx
// if b > DBL_MAX/10, assume vertical line at x = a
// returns closest point on line in xp, yp
//
double GetPointToLineDistance( double a, double b, int x, int y, double * xpp, double * ypp )
{
if( b > DBL_MAX/10 )
{
// vertical line
if( xpp && ypp )
{
*xpp = a;
*ypp = y;
}
return abs(a-x);
}
// find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
double d = -1.0/b;
double c = (double)y-d*x;
// find nearest point to (x,y) on line through (xi,yi) to (xf,yf)
double xp = (a-c)/(d-b);
double yp = a + b*xp;
if( xpp && ypp )
{
*xpp = xp;
*ypp = yp;
}
// find distance
return Distance( x, y, xp, yp );
}
/***********************************************************************************/
double GetPointToLineSegmentDistance( int x, int y, int xi, int yi, int xf, int yf )
/***********************************************************************************/
/** Function GetPointToLineSegmentDistance
* Get distance between line segment and point
* @param x,y = point
* @param xi,yi and xf,yf = the end-points of the line segment
* @return the distance
*/
{
// test for vertical or horizontal segment
if( xf==xi )
{
// vertical line segment
if( InRange( y, yi, yf ) )
return abs( x - xi );
else
return min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
}
else if( yf==yi )
{
// horizontal line segment
if( InRange( x, xi, xf ) )
return abs( y - yi );
else
return min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
}
else
{
// oblique segment
// find a,b such that (xi,yi) and (xf,yf) lie on y = a + bx
double b = (double)(yf-yi)/(xf-xi);
double a = (double)yi-b*xi;
// find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
double d = -1.0/b;
double c = (double)y-d*x;
// find nearest point to (x,y) on line through (xi,yi) to (xf,yf)
double xp = (a-c)/(d-b);
double yp = a + b*xp;
// find distance
if( InRange( xp, xi, xf ) && InRange( yp, yi, yf ) )
return Distance( x, y, xp, yp );
else
return min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
}
}
// test for value within range
//
bool InRange( double x, double xi, double xf )
{
if( xf>xi )
{
if( x >= xi && x <= xf )
return true;
}
else
{
if( x >= xf && x <= xi )
return true;
}
return false;
}
// Get distance between 2 points
//
double Distance( int x1, int y1, int x2, int y2 )
{
double d;
d = sqrt( (double)(x1-x2)*(x1-x2) + (double)(y1-y2)*(y1-y2) );
if( d > INT_MAX || d < INT_MIN )
ASSERT(0);
return (int)d;
}
// this finds approximate solutions
// note: this works best if el2 is smaller than el1
//
int GetArcIntersections( EllipseKH * el1, EllipseKH * el2,
double * x1, double * y1, double * x2, double * y2 )
{
if( el1->theta2 > el1->theta1 )
ASSERT(0);
if( el2->theta2 > el2->theta1 )
ASSERT(0);
const int NSTEPS = 32;
double xret[2], yret[2];
double xscale = 1.0/el1->xrad;
double yscale = 1.0/el1->yrad;
// now transform params of second ellipse into reference frame
// with origin at center if first ellipse,
// scaled so the first ellipse is a circle of radius = 1.0
double xo = (el2->Center.X - el1->Center.X)*xscale;
double yo = (el2->Center.Y - el1->Center.Y)*yscale;
double xr = el2->xrad*xscale;
double yr = el2->yrad*yscale;
// now test NSTEPS positions in arc, moving clockwise (ie. decreasing theta)
double step = M_PI/((NSTEPS-1)*2.0);
double d_prev, th_prev;
double th_interp;
double th1;
int n = 0;
for( int i=0; i<NSTEPS; i++ )
{
double theta;
if( i < NSTEPS-1 )
theta = el2->theta1 - i*step;
else
theta = el2->theta2;
double x = xo + xr*cos(theta);
double y = yo + yr*sin(theta);
double d = 1.0 - sqrt(x*x + y*y);
if( i>0 )
{
bool bInt = false;
if( d >= 0.0 && d_prev <= 0.0 )
{
th_interp = theta + (step*(-d_prev))/(d-d_prev);
bInt = true;
}
else if( d <= 0.0 && d_prev >= 0.0 )
{
th_interp = theta + (step*d_prev)/(d_prev-d);
bInt = true;
}
if( bInt )
{
x = xo + xr*cos(th_interp);
y = yo + yr*sin(th_interp);
th1 = atan2( y, x );
if( th1 <= el1->theta1 && th1 >= el1->theta2 )
{
xret[n] = x*el1->xrad + el1->Center.X;
yret[n] = y*el1->yrad + el1->Center.Y;
n++;
if( n > 2 )
ASSERT(0);
}
}
}
d_prev = d;
th_prev = theta;
}
if( x1 )
*x1 = xret[0];
if( y1 )
*y1 = yret[0];
if( x2 )
*x2 = xret[1];
if( y2 )
*y2 = yret[1];
return n;
}
// this finds approximate solution
//
//double GetSegmentClearance( EllipseKH * el1, EllipseKH * el2,
double GetArcClearance( EllipseKH * el1, EllipseKH * el2,
double * x1, double * y1 )
{
const int NSTEPS = 32;
if( el1->theta2 > el1->theta1 )
ASSERT(0);
if( el2->theta2 > el2->theta1 )
ASSERT(0);
// test multiple positions in both arcs, moving clockwise (ie. decreasing theta)
double th_start = el1->theta1;
double th_end = el1->theta2;
double th_start2 = el2->theta1;
double th_end2 = el2->theta2;
double dmin = DBL_MAX;
double xmin, ymin, thmin, thmin2;
int nsteps = NSTEPS;
int nsteps2 = NSTEPS;
double step = (th_start-th_end)/(nsteps-1);
double step2 = (th_start2-th_end2)/(nsteps2-1);
while( (step * max(el1->xrad, el1->yrad)) > 1.0*NM_PER_MIL
&& (step2 * max(el2->xrad, el2->yrad)) > 1.0*NM_PER_MIL )
{
step = (th_start-th_end)/(nsteps-1);
for( int i=0; i<nsteps; i++ )
{
double theta;
if( i < nsteps-1 )
theta = th_start - i*step;
else
theta = th_end;
double x = el1->Center.X + el1->xrad*cos(theta);
double y = el1->Center.Y + el1->yrad*sin(theta);
step2 = (th_start2-th_end2)/(nsteps2-1);
for( int i2=0; i2<nsteps2; i2++ )
{
double theta2;
if( i2 < nsteps2-1 )
theta2 = th_start2 - i2*step2;
else
theta2 = th_end2;
double x2 = el2->Center.X + el2->xrad*cos(theta2);
double y2 = el2->Center.Y + el2->yrad*sin(theta2);
double d = Distance( x, y, x2, y2 );
if( d < dmin )
{
dmin = d;
xmin = x;
ymin = y;
thmin = theta;
thmin2 = theta2;
}
}
}
if( step > step2 )
{
th_start = min(el1->theta1, thmin + step);
th_end = max(el1->theta2, thmin - step);
step = (th_start - th_end)/nsteps;
}
else
{
th_start2 = min(el2->theta1, thmin2 + step2);
th_end2 = max(el2->theta2, thmin2 - step2);
step2 = (th_start2 - th_end2)/nsteps2;
}
}
if( x1 )
*x1 = xmin;
if( y1 )
*y1 = ymin;
return dmin;
}
// math stuff for graphics, from FreePCB
typedef struct PointTag
{
double X,Y;
} Point;
typedef struct EllipseTag
{
Point Center; /* ellipse center */
// double MaxRad,MinRad; /* major and minor axis */
// double Phi; /* major axis rotation */
double xrad, yrad; // radii on x and y
double theta1, theta2; // start and end angle for arc
} EllipseKH;
const CPoint zero(0,0);
class my_circle {
public:
my_circle(){};
my_circle( int xx, int yy, int rr )
{
x = xx;
y = yy;
r = rr;
};
int x, y, r;
};
class my_rect {
public:
my_rect(){};
my_rect( int xi, int yi, int xf, int yf )
{
xlo = min(xi,xf);
xhi = max(xi,xf);
ylo = min(yi,yf);
yhi = max(yi,yf);
};
int xlo, ylo, xhi, yhi;
};
class my_seg {
public:
my_seg(){};
my_seg( int xxi, int yyi, int xxf, int yyf )
{
xi = xxi;
yi = yyi;
xf = xxf;
yf = yyf;
};
int xi, yi, xf, yf;
};
// math stuff for graphics
BOOL Quadratic( double a, double b, double c, double *x1, double *x2 );
void DrawArc( CDC * pDC, int shape, int xxi, int yyi, int xxf, int yyf, BOOL bMeta=FALSE );
void RotatePoint( CPoint *p, int angle, CPoint org );
void RotateRect( CRect *r, int angle, CPoint org );
int TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist );
int FindLineIntersection( double a, double b, double c, double d, double * x, double * y );
int FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf, int style,
double * x1, double * y1, double * x2, double * y2, double * dist=NULL );
int FindSegmentIntersections( int xi, int yi, int xf, int yf, int style,
int xi2, int yi2, int xf2, int yf2, int style2,
double x[]=NULL, double y[]=NULL );
BOOL FindLineEllipseIntersections( double a, double b, double c, double d, double *x1, double *x2 );
BOOL FindVerticalLineEllipseIntersections( double a, double b, double x, double *y1, double *y2 );
BOOL TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f,
int x2i, int y2i, int x2f, int y2f,
int * x=NULL, int * y=NULL, double * dist=NULL );
void GetPadElements( int type, int x, int y, int wid, int len, int radius, int angle,
int * nr, my_rect r[], int * nc, my_circle c[], int * ns, my_seg s[] );
int GetClearanceBetweenPads( int type1, int x1, int y1, int w1, int l1, int r1, int angle1,
int type2, int x2, int y2, int w2, int l2, int r2, int angle2 );
int GetClearanceBetweenSegmentAndPad( int x1, int y1, int x2, int y2, int w,
int type, int x, int y, int wid, int len,
int radius, int angle );
int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int style1, int w1,
int x2i, int y2i, int x2f, int y2f, int style2, int w2,
int max_cl, int * x, int * y );
/** Function GetPointToLineSegmentDistance
* Get distance between line segment and point
* @param x,y = point
* @param xi,yi and xf,yf = the end-points of the line segment
* @return the distance
*/
double GetPointToLineSegmentDistance( int x, int y, int xi, int yi, int xf, int yf );
double GetPointToLineDistance( double a, double b, int x, int y, double * xp=NULL, double * yp=NULL );
BOOL InRange( double x, double xi, double xf );
double Distance( int x1, int y1, int x2, int y2 );
int GetArcIntersections( EllipseKH * el1, EllipseKH * el2,
double * x1=NULL, double * y1=NULL,
double * x2=NULL, double * y2=NULL );
CPoint GetInflectionPoint( CPoint pi, CPoint pf, int mode );
// quicksort (2-way or 3-way)
void quickSort(int numbers[], int index[], int array_size);
void q_sort(int numbers[], int index[], int left, int right);
void q_sort_3way( int a[], int b[], int left, int right );
// math stuff for graphics, from FreePCB
typedef struct PointTag
{
double X,Y;
} Point;
typedef struct EllipseTag
{
Point Center; /* ellipse center */
// double MaxRad,MinRad; /* major and minor axis */
// double Phi; /* major axis rotation */
double xrad, yrad; // radii on x and y
double theta1, theta2; // start and end angle for arc
} EllipseKH;
const CPoint zero(0,0);
class my_circle {
public:
my_circle(){};
my_circle( int xx, int yy, int rr )
{
x = xx;
y = yy;
r = rr;
};
int x, y, r;
};
class my_rect {
public:
my_rect(){};
my_rect( int xi, int yi, int xf, int yf )
{
xlo = min(xi,xf);
xhi = max(xi,xf);
ylo = min(yi,yf);
yhi = max(yi,yf);
};
int xlo, ylo, xhi, yhi;
};
class my_seg {
public:
my_seg(){};
my_seg( int xxi, int yyi, int xxf, int yyf )
{
xi = xxi;
yi = yyi;
xf = xxf;
yf = yyf;
};
int xi, yi, xf, yf;
};
// math stuff for graphics
BOOL Quadratic( double a, double b, double c, double *x1, double *x2 );
void DrawArc( CDC * pDC, int shape, int xxi, int yyi, int xxf, int yyf, BOOL bMeta=FALSE );
void RotatePoint( CPoint *p, int angle, CPoint org );
void RotateRect( CRect *r, int angle, CPoint org );
int TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist );
int FindLineIntersection( double a, double b, double c, double d, double * x, double * y );
int FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf, int style,
double * x1, double * y1, double * x2, double * y2, double * dist=NULL );
int FindSegmentIntersections( int xi, int yi, int xf, int yf, int style,
int xi2, int yi2, int xf2, int yf2, int style2,
double x[]=NULL, double y[]=NULL );
BOOL FindLineEllipseIntersections( double a, double b, double c, double d, double *x1, double *x2 );
BOOL FindVerticalLineEllipseIntersections( double a, double b, double x, double *y1, double *y2 );
BOOL TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f,
int x2i, int y2i, int x2f, int y2f,
int * x=NULL, int * y=NULL, double * dist=NULL );
void GetPadElements( int type, int x, int y, int wid, int len, int radius, int angle,
int * nr, my_rect r[], int * nc, my_circle c[], int * ns, my_seg s[] );
int GetClearanceBetweenPads( int type1, int x1, int y1, int w1, int l1, int r1, int angle1,
int type2, int x2, int y2, int w2, int l2, int r2, int angle2 );
int GetClearanceBetweenSegmentAndPad( int x1, int y1, int x2, int y2, int w,
int type, int x, int y, int wid, int len,
int radius, int angle );
int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int style1, int w1,
int x2i, int y2i, int x2f, int y2f, int style2, int w2,
int max_cl, int * x, int * y );
/** Function GetPointToLineSegmentDistance
* Get distance between line segment and point
* @param x,y = point
* @param xi,yi and xf,yf = the end-points of the line segment
* @return the distance
*/
double GetPointToLineSegmentDistance( int x, int y, int xi, int yi, int xf, int yf );
double GetPointToLineDistance( double a, double b, int x, int y, double * xp=NULL, double * yp=NULL );
BOOL InRange( double x, double xi, double xf );
double Distance( int x1, int y1, int x2, int y2 );
int GetArcIntersections( EllipseKH * el1, EllipseKH * el2,
double * x1=NULL, double * y1=NULL,
double * x2=NULL, double * y2=NULL );
CPoint GetInflectionPoint( CPoint pi, CPoint pf, int mode );
// quicksort (2-way or 3-way)
void quickSort(int numbers[], int index[], int array_size);
void q_sort(int numbers[], int index[], int left, int right);
void q_sort_3way( int a[], int b[], int left, int right );
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment