4
* This program source code file is part of KiCad, a free EDA CAD application.
6
* Copyright (C) 2013-2014 Cirilo Bernardo
8
* This program is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU General Public License
10
* as published by the Free Software Foundation; either version 2
11
* of the License, or (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, you may find one here:
20
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21
* or you may search the http://www.gnu.org website for the version 2 license,
22
* or you may write to the Free Software Foundation, Inc.,
23
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
32
#include <idf_common.h>
33
#include <build_version.h>
36
void IDF3::PrintSeg( IDF_SEGMENT* aSegment )
38
if( aSegment->IsCircle() )
40
fprintf(stdout, "printSeg(): CIRCLE: C(%.3f, %.3f) P(%.3f, %.3f) rad. %.3f\n",
41
aSegment->startPoint.x, aSegment->startPoint.y,
42
aSegment->endPoint.x, aSegment->endPoint.y,
47
if( aSegment->angle < -MIN_ANG || aSegment->angle > MIN_ANG )
49
fprintf(stdout, "printSeg(): ARC: p1(%.3f, %.3f) p2(%.3f, %.3f) ang. %.3f\n",
50
aSegment->startPoint.x, aSegment->startPoint.y,
51
aSegment->endPoint.x, aSegment->endPoint.y,
56
fprintf(stdout, "printSeg(): LINE: p1(%.3f, %.3f) p2(%.3f, %.3f)\n",
57
aSegment->startPoint.x, aSegment->startPoint.y,
58
aSegment->endPoint.x, aSegment->endPoint.y );
65
bool IDF_POINT::Matches( const IDF_POINT& aPoint, double aRadius )
67
double dx = x - aPoint.x;
68
double dy = y - aPoint.y;
70
double d2 = dx * dx + dy * dy;
72
if( d2 <= aRadius * aRadius )
79
double IDF_POINT::CalcDistance( const IDF_POINT& aPoint ) const
81
double dx = aPoint.x - x;
82
double dy = aPoint.y - y;
83
double dist = sqrt( dx * dx + dy * dy );
89
double IDF3::CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
91
return atan2( aEndPoint.y - aStartPoint.y, aEndPoint.x - aStartPoint.x );
95
double IDF3::CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
97
double ang = CalcAngleRad( aStartPoint, aEndPoint );
99
// round to thousandths of a degree
100
int iang = int (ang / M_PI * 1800000.0);
102
ang = iang / 10000.0;
108
void IDF3::GetOutline( std::list<IDF_SEGMENT*>& aLines,
109
IDF_OUTLINE& aOutline )
113
// NOTE: To tell if the point order is CCW or CW,
114
// sum all: (endPoint.X[n] - startPoint.X[n])*(endPoint[n] + startPoint.Y[n])
115
// If the result is >0, the direction is CW, otherwise
116
// it is CCW. Note that the result cannot be 0 unless
117
// we have a bounded area of 0.
119
// First we find the segment with the leftmost point
120
std::list<IDF_SEGMENT*>::iterator bl = aLines.begin();
121
std::list<IDF_SEGMENT*>::iterator el = aLines.end();
122
std::list<IDF_SEGMENT*>::iterator idx = bl++; // iterator for the object with minX
124
double minx = (*idx)->GetMinX();
129
curx = (*bl)->GetMinX();
140
aOutline.push( *idx );
146
// If the item is a circle then we're done
147
if( aOutline.front()->IsCircle() )
151
bool complete = false; // set if loop is complete
152
bool matched; // set if a segment's end point was matched
160
while( bl != el && !matched )
162
if( (*bl)->MatchesStart( aOutline.back()->endPoint ) )
164
if( (*bl)->IsCircle() )
166
// a circle on the perimeter is pathological but we just ignore it
175
aOutline.push( *bl );
187
// attempt to match the end points
191
while( bl != el && !matched )
193
if( (*bl)->MatchesEnd( aOutline.back()->endPoint ) )
195
if( (*bl)->IsCircle() )
197
// a circle on the perimeter is pathological but we just ignore it
207
aOutline.push( *bl );
220
// still no match - attempt to close the loop
221
if( (aOutline.size() > 1) || ( aOutline.front()->angle < -MIN_ANG )
222
|| ( aOutline.front()->angle > MIN_ANG ) )
225
IDF_SEGMENT* seg = new IDF_SEGMENT( aOutline.back()->endPoint,
226
aOutline.front()->startPoint );
234
aOutline.push( seg );
239
// the outline is bad; drop the segments
245
// check if the loop is complete
246
if( aOutline.front()->MatchesStart( aOutline.back()->endPoint ) )
255
IDF_SEGMENT::IDF_SEGMENT()
263
IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
268
startPoint = aStartPoint;
269
endPoint = aEndPoint;
273
IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint,
274
const IDF_POINT& aEndPoint,
278
double diff = abs( aAngle ) - 360.0;
281
&& diff > -MIN_ANG ) || ( aAngle < MIN_ANG && aAngle > -MIN_ANG ) || (!aFromKicad) )
284
startPoint = aStartPoint;
285
endPoint = aEndPoint;
287
if( diff < MIN_ANG && diff > -MIN_ANG )
290
center = aStartPoint;
292
radius = aStartPoint.CalcDistance( aEndPoint );
294
else if( aAngle < MIN_ANG && aAngle > -MIN_ANG )
296
CalcCenterAndRadius();
302
// we need to convert from the KiCad arc convention
305
center = aStartPoint;
307
offsetAngle = IDF3::CalcAngleDeg( aStartPoint, aEndPoint );
309
radius = aStartPoint.CalcDistance( aEndPoint );
311
startPoint = aEndPoint;
313
double ang = offsetAngle + aAngle;
314
ang = (ang / 180.0) * M_PI;
316
endPoint.x = ( radius * cos( ang ) ) + center.x;
317
endPoint.y = ( radius * sin( ang ) ) + center.y;
321
bool IDF_SEGMENT::MatchesStart( const IDF_POINT& aPoint, double aRadius )
323
return startPoint.Matches( aPoint, aRadius );
327
bool IDF_SEGMENT::MatchesEnd( const IDF_POINT& aPoint, double aRadius )
329
return endPoint.Matches( aPoint, aRadius );
333
void IDF_SEGMENT::CalcCenterAndRadius( void )
335
// NOTE: this routine does not check if the points are the same
336
// or too close to be sensible in a production setting.
338
double offAng = IDF3::CalcAngleRad( startPoint, endPoint );
339
double d = startPoint.CalcDistance( endPoint ) / 2.0;
340
double xm = ( startPoint.x + endPoint.x ) * 0.5;
341
double ym = ( startPoint.y + endPoint.y ) * 0.5;
343
radius = d / sin( angle * M_PI / 180.0 );
350
// calculate the height of the triangle with base d and hypotenuse r
351
double dh2 = radius * radius - d * d;
355
// this should only ever happen due to rounding errors when r == d
359
double h = sqrt( dh2 );
366
if( ( angle > M_PI ) || ( angle < -M_PI ) )
369
center.x = h * cos( offAng ) + xm;
370
center.y = h * sin( offAng ) + ym;
372
offsetAngle = IDF3::CalcAngleDeg( center, startPoint );
376
bool IDF_SEGMENT::IsCircle( void )
378
double diff = abs( angle ) - 360.0;
380
if( ( diff < MIN_ANG ) && ( diff > -MIN_ANG ) )
387
double IDF_SEGMENT::GetMinX( void )
390
return std::min( startPoint.x, endPoint.x );
392
// Calculate the leftmost point of the circle or arc
396
// if only everything were this easy
397
return center.x - radius;
401
// 1. CCW arc: if offset + included angle >= 180 deg then
402
// MinX = center.x - radius, otherwise MinX is the
403
// same as for the case of a line.
404
// 2. CW arc: if offset + included angle <= -180 deg then
405
// MinX = center.x - radius, otherwise MinX is the
406
// same as for the case of a line.
411
if( ( offsetAngle + angle ) >= 180.0 )
413
return center.x - radius;
417
return std::min( startPoint.x, endPoint.x );
422
if( ( offsetAngle + angle ) <= -180.0 )
424
return center.x - radius;
427
return std::min( startPoint.x, endPoint.x );
431
void IDF_SEGMENT::SwapEnds( void )
435
// reverse the direction
440
IDF_POINT tmp = startPoint;
441
startPoint = endPoint;
444
if( ( angle < MIN_ANG ) && ( angle > -MIN_ANG ) )
445
return; // nothing more to do
447
// change the direction of the arc
449
// calculate the new offset angle
450
offsetAngle = IDF3::CalcAngleDeg( center, startPoint );
454
void IDF_OUTLINE::push( IDF_SEGMENT* item )
456
if( !outline.empty() )
458
if( item->IsCircle() )
461
wxString msg = wxT( "INVALID GEOMETRY: a circle is being added to a non-empty outline" );
462
THROW_IO_ERROR( msg );
466
if( outline.back()->IsCircle() )
468
// we can't add lines to a circle
469
wxString msg = wxT( "INVALID GEOMETRY: a line is being added to a circular outline" );
470
THROW_IO_ERROR( msg );
472
else if( !item->MatchesStart( outline.back()->endPoint ) )
474
// startPoint[N] != endPoint[N -1]
475
wxString msg = wxT( "INVALID GEOMETRY: disjoint segments" );
476
THROW_IO_ERROR( msg );
481
outline.push_back( item );
482
dir += ( outline.back()->endPoint.x - outline.back()->startPoint.x )
483
* ( outline.back()->endPoint.y + outline.back()->startPoint.y );