 // Dick Hollenbeck's KiROUND R&D
// This provides better project control over rounding to int from double
// than wxRound() did. This scheme provides better logging in Debug builds
// and it provides for compile time calculation of constants.
#include <stdio.h>
#include <assert.h>
#include <limits.h>
//-----<KiROUND KIT>------------------------------------------------------------
/**
* KiROUND
* rounds a floating point number to an int using
* "round halfway cases away from zero".
* In Debug build an assert fires if will not fit into an int.
*/
#if defined( DEBUG )
// DEBUG: a macro to capture line and file, then calls this inline
static inline int KiRound( double v, int line, const char* filename )
{
v = v < 0 ? v - 0.5 : v + 0.5;
if( v > INT_MAX + 0.5 )
{
printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v );
}
else if( v < INT_MIN - 0.5 )
{
printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v );
}
return int( v );
}
#define KiROUND( v ) KiRound( v, __LINE__, __FILE__ )
#else
// RELEASE: a macro so compile can pre-compute constants.
#define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 )
#endif
//-----</KiROUND KIT>-----------------------------------------------------------
// Only a macro is compile time calculated, an inline function causes a static constructor
// in a situation like this.
// Therefore the Release build is best done with a MACRO not an inline function.
int Computed = KiROUND( 14.3 * 8 );
int main( int argc, char** argv )
{
for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 )
{
int i = KiROUND( d );
printf( "t: %d %.16g\n", i, d );
}
return 0;
}
14 years ago  // Dick Hollenbeck's KiROUND R&D
// This provides better project control over rounding to int from double
// than wxRound() did. This scheme provides better logging in Debug builds
// and it provides for compile time calculation of constants.
#include <stdio.h>
#include <assert.h>
#include <limits.h>
//-----<KiROUND KIT>------------------------------------------------------------
/**
* KiROUND
* rounds a floating point number to an int using
* "round halfway cases away from zero".
* In Debug build an assert fires if will not fit into an int.
*/
#if defined( DEBUG )
// DEBUG: a macro to capture line and file, then calls this inline
static inline int KiRound( double v, int line, const char* filename )
{
v = v < 0 ? v - 0.5 : v + 0.5;
if( v > INT_MAX + 0.5 )
{
printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v );
}
else if( v < INT_MIN - 0.5 )
{
printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v );
}
return int( v );
}
#define KiROUND( v ) KiRound( v, __LINE__, __FILE__ )
#else
// RELEASE: a macro so compile can pre-compute constants.
#define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 )
#endif
//-----</KiROUND KIT>-----------------------------------------------------------
// Only a macro is compile time calculated, an inline function causes a static constructor
// in a situation like this.
// Therefore the Release build is best done with a MACRO not an inline function.
int Computed = KiROUND( 14.3 * 8 );
int main( int argc, char** argv )
{
for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 )
{
int i = KiROUND( d );
printf( "t: %d %.16g\n", i, d );
}
return 0;
}
14 years ago |
|
/*
* This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 1992-2010 <Jean-Pierre Charras> * Copyright (C) 1992-2010 KiCad Developers, see change_log.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
/**
* @file class_gerber_draw_item.cpp */
#include <fctsys.h>
#include <polygons_defs.h>
#include <gr_basic.h>
#include <common.h>
#include <trigo.h>
#include <class_drawpanel.h>
#include <macros.h>
#include <msgpanel.h>
#include <gerbview.h>
#include <gerbview_frame.h>
#include <class_gerber_draw_item.h>
#include <class_GERBER.h>
GERBER_DRAW_ITEM::GERBER_DRAW_ITEM( GBR_LAYOUT* aParent, GERBER_IMAGE* aGerberparams ) : EDA_ITEM( (EDA_ITEM*)aParent, TYPE_GERBER_DRAW_ITEM ) { m_imageParams = aGerberparams; m_Layer = 0; m_Shape = GBR_SEGMENT; m_Flashed = false; m_DCode = 0; m_UnitsMetric = false; m_LayerNegative = false; m_swapAxis = false; m_mirrorA = false; m_mirrorB = false; m_drawScale.x = m_drawScale.y = 1.0; m_lyrRotation = 0; if( m_imageParams ) SetLayerParameters(); }
// Copy constructor
GERBER_DRAW_ITEM::GERBER_DRAW_ITEM( const GERBER_DRAW_ITEM& aSource ) : EDA_ITEM( aSource ) { m_imageParams = aSource.m_imageParams; m_Shape = aSource.m_Shape;
m_Flags = aSource.m_Flags; SetTimeStamp( aSource.m_TimeStamp );
SetStatus( aSource.GetStatus() ); m_Start = aSource.m_Start; m_End = aSource.m_End; m_Size = aSource.m_Size; m_Layer = aSource.m_Layer; m_Shape = aSource.m_Shape; m_Flashed = aSource.m_Flashed; m_DCode = aSource.m_DCode; m_PolyCorners = aSource.m_PolyCorners; m_UnitsMetric = aSource.m_UnitsMetric; m_LayerNegative = aSource.m_LayerNegative; m_swapAxis = aSource.m_swapAxis; m_mirrorA = aSource.m_mirrorA; m_mirrorB = aSource.m_mirrorB; m_layerOffset = aSource.m_layerOffset; m_drawScale = aSource.m_drawScale; m_lyrRotation = aSource.m_lyrRotation; }
GERBER_DRAW_ITEM::~GERBER_DRAW_ITEM() { }
GERBER_DRAW_ITEM* GERBER_DRAW_ITEM::Copy() const { return new GERBER_DRAW_ITEM( *this ); }
wxPoint GERBER_DRAW_ITEM::GetABPosition( const wxPoint& aXYPosition ) const { /* Note: RS274Xrevd_e is obscure about the order of transforms:
* For instance: Rotation must be made after or before mirroring ? * Note: if something is changed here, GetYXPosition must reflect changes */ wxPoint abPos = aXYPosition + m_imageParams->m_ImageJustifyOffset;
if( m_swapAxis ) EXCHG( abPos.x, abPos.y );
abPos += m_layerOffset + m_imageParams->m_ImageOffset; abPos.x = KiROUND( abPos.x * m_drawScale.x ); abPos.y = KiROUND( abPos.y * m_drawScale.y ); double rotation = m_lyrRotation * 10 + m_imageParams->m_ImageRotation * 10;
if( rotation ) RotatePoint( &abPos, -rotation );
// Negate A axis if mirrored
if( m_mirrorA ) NEGATE( abPos.x );
// abPos.y must be negated when no mirror, because draw axis is top to bottom
if( !m_mirrorB ) NEGATE( abPos.y ); return abPos; }
wxPoint GERBER_DRAW_ITEM::GetXYPosition( const wxPoint& aABPosition ) const { // do the inverse transform made by GetABPosition
wxPoint xyPos = aABPosition;
if( m_mirrorA ) NEGATE( xyPos.x );
if( !m_mirrorB ) NEGATE( xyPos.y );
double rotation = m_lyrRotation * 10 + m_imageParams->m_ImageRotation * 10;
if( rotation ) RotatePoint( &xyPos, rotation );
xyPos.x = KiROUND( xyPos.x / m_drawScale.x ); xyPos.y = KiROUND( xyPos.y / m_drawScale.y ); xyPos -= m_layerOffset + m_imageParams->m_ImageOffset;
if( m_swapAxis ) EXCHG( xyPos.x, xyPos.y );
return xyPos - m_imageParams->m_ImageJustifyOffset; }
void GERBER_DRAW_ITEM::SetLayerParameters() { m_UnitsMetric = m_imageParams->m_GerbMetric; m_swapAxis = m_imageParams->m_SwapAxis; // false if A = X, B = Y;
// true if A =Y, B = Y
m_mirrorA = m_imageParams->m_MirrorA; // true: mirror / axe A
m_mirrorB = m_imageParams->m_MirrorB; // true: mirror / axe B
m_drawScale = m_imageParams->m_Scale; // A and B scaling factor
m_layerOffset = m_imageParams->m_Offset; // Offset from OF command
// Rotation from RO command:
m_lyrRotation = m_imageParams->m_LocalRotation; m_LayerNegative = m_imageParams->GetLayerParams().m_LayerNegative; }
wxString GERBER_DRAW_ITEM::ShowGBRShape() { switch( m_Shape ) { case GBR_SEGMENT: return _( "Line" );
case GBR_ARC: return _( "Arc" );
case GBR_CIRCLE: return _( "Circle" );
case GBR_SPOT_OVAL: return wxT( "spot_oval" );
case GBR_SPOT_CIRCLE: return wxT( "spot_circle" );
case GBR_SPOT_RECT: return wxT( "spot_rect" );
case GBR_SPOT_POLY: return wxT( "spot_poly" );
case GBR_POLYGON: return wxT( "polygon" );
case GBR_SPOT_MACRO: { wxString name = wxT( "apt_macro" ); D_CODE* dcode = GetDcodeDescr();
if( dcode && dcode->GetMacro() ) name << wxT(" ") << dcode->GetMacro()->name;
return name; }
default: return wxT( "??" ); } }
D_CODE* GERBER_DRAW_ITEM::GetDcodeDescr() { if( (m_DCode < FIRST_DCODE) || (m_DCode > LAST_DCODE) ) return NULL; GERBER_IMAGE* gerber = g_GERBER_List[m_Layer]; if( gerber == NULL ) return NULL;
D_CODE* d_code = gerber->GetDCODE( m_DCode, false );
return d_code; }
const EDA_RECT GERBER_DRAW_ITEM::GetBoundingBox() const { // return a rectangle which is (pos,dim) in nature. therefore the +1
EDA_RECT bbox( m_Start, wxSize( 1, 1 ) );
bbox.Inflate( m_Size.x / 2, m_Size.y / 2 );
bbox.SetOrigin( GetABPosition( bbox.GetOrigin() ) ); bbox.SetEnd( GetABPosition( bbox.GetEnd() ) ); return bbox; }
void GERBER_DRAW_ITEM::MoveAB( const wxPoint& aMoveVector ) { wxPoint xymove = GetXYPosition( aMoveVector );
m_Start += xymove; m_End += xymove; m_ArcCentre += xymove;
for( unsigned ii = 0; ii < m_PolyCorners.size(); ii++ ) m_PolyCorners[ii] += xymove; }
void GERBER_DRAW_ITEM::MoveXY( const wxPoint& aMoveVector ) { m_Start += aMoveVector; m_End += aMoveVector; m_ArcCentre += aMoveVector;
for( unsigned ii = 0; ii < m_PolyCorners.size(); ii++ ) m_PolyCorners[ii] += aMoveVector; }
bool GERBER_DRAW_ITEM::Save( FILE* aFile ) const { return true; }
bool GERBER_DRAW_ITEM::HasNegativeItems() { bool isClear = m_LayerNegative ^ m_imageParams->m_ImageNegative;
// if isClear is true, this item has negative shape
// but if isClear is true, and if this item use an aperture macro definition,
// we must see if this aperture macro uses a negative shape.
if( isClear ) return true;
// see for a macro def
D_CODE* dcodeDescr = GetDcodeDescr();
if( dcodeDescr == NULL ) return false;
if( m_Shape == GBR_SPOT_MACRO ) { APERTURE_MACRO* macro = dcodeDescr->GetMacro();
if( macro ) // macro == NULL should not occurs
return macro->HasNegativeItems( this ); }
return false; }
void GERBER_DRAW_ITEM::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDrawMode, const wxPoint& aOffset ) { // used when a D_CODE is not found. default D_CODE to draw a flashed item
static D_CODE dummyD_CODE( 0 ); EDA_COLOR_T color, alt_color; bool isFilled; int radius; int halfPenWidth; static bool show_err; D_CODE* d_codeDescr = GetDcodeDescr(); GERBVIEW_FRAME* gerbFrame = (GERBVIEW_FRAME*) aPanel->GetParent();
if( d_codeDescr == NULL ) d_codeDescr = &dummyD_CODE;
if( gerbFrame->IsLayerVisible( GetLayer() ) == false ) return;
color = gerbFrame->GetLayerColor( GetLayer() );
if( aDrawMode & GR_HIGHLIGHT ) ColorChangeHighlightFlag( &color, !(aDrawMode & GR_AND) );
ColorApplyHighlightFlag( &color );
alt_color = gerbFrame->GetNegativeItemsColor();
/* isDark is true if flash is positive and should use a drawing
* color other than the background color, else use the background color * when drawing so that an erasure happens. */ bool isDark = !(m_LayerNegative ^ m_imageParams->m_ImageNegative);
if( !isDark ) { // draw in background color ("negative" color)
EXCHG( color, alt_color ); }
GRSetDrawMode( aDC, aDrawMode );
isFilled = gerbFrame->DisplayLinesSolidMode();
switch( m_Shape ) { case GBR_POLYGON: isFilled = gerbFrame->DisplayPolygonsSolidMode();
if( !isDark ) isFilled = true;
DrawGbrPoly( aPanel->GetClipBox(), aDC, color, aOffset, isFilled ); break;
case GBR_CIRCLE: radius = KiROUND( GetLineLength( m_Start, m_End ) );
halfPenWidth = m_Size.x >> 1;
if( !isFilled ) { // draw the border of the pen's path using two circles, each as narrow as possible
GRCircle( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), radius - halfPenWidth, 0, color ); GRCircle( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), radius + halfPenWidth, 0, color ); } else // Filled mode
{ GRCircle( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), radius, m_Size.x, color ); } break;
case GBR_ARC: // Currently, arcs plotted with a rectangular aperture are not supported.
// a round pen only is expected.
#if 0 // for arc debug only
GRLine( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), GetABPosition( m_ArcCentre ), 0, color ); GRLine( aPanel->GetClipBox(), aDC, GetABPosition( m_End ), GetABPosition( m_ArcCentre ), 0, color ); #endif
if( !isFilled ) { GRArc1( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), GetABPosition( m_End ), GetABPosition( m_ArcCentre ), 0, color ); } else { GRArc1( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), GetABPosition( m_End ), GetABPosition( m_ArcCentre ), m_Size.x, color ); }
break;
case GBR_SPOT_CIRCLE: case GBR_SPOT_RECT: case GBR_SPOT_OVAL: case GBR_SPOT_POLY: case GBR_SPOT_MACRO: isFilled = gerbFrame->DisplayFlashedItemsSolidMode(); d_codeDescr->DrawFlashedShape( this, aPanel->GetClipBox(), aDC, color, alt_color, m_Start, isFilled ); break;
case GBR_SEGMENT: /* Plot a line from m_Start to m_End.
* Usually, a round pen is used, but some gerber files use a rectangular pen * In fact, any aperture can be used to plot a line. * currently: only a square pen is handled (I believe using a polygon gives a strange plot). */ if( d_codeDescr->m_Shape == APT_RECT ) { if( m_PolyCorners.size() == 0 ) ConvertSegmentToPolygon( );
DrawGbrPoly( aPanel->GetClipBox(), aDC, color, aOffset, isFilled ); } else { if( !isFilled ) { GRCSegm( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), GetABPosition( m_End ), m_Size.x, color ); } else { GRFilledSegment( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), GetABPosition( m_End ), m_Size.x, color ); } }
break;
default: if( !show_err ) { wxMessageBox( wxT( "Trace_Segment() type error" ) ); show_err = true; }
break; } }
void GERBER_DRAW_ITEM::ConvertSegmentToPolygon( ) { m_PolyCorners.clear(); m_PolyCorners.reserve(6);
wxPoint start = m_Start; wxPoint end = m_End;
// make calculations more easy if ensure start.x < end.x
// (only 2 quadrants to consider)
if( start.x > end.x ) EXCHG( start, end );
// calculate values relative to start point:
wxPoint delta = end - start;
// calculate corners for the first quadrant only (delta.x and delta.y > 0 )
// currently, delta.x already is > 0.
// make delta.y > 0
bool change = delta.y < 0;
if( change ) NEGATE( delta.y);
// Now create the full polygon.
// Due to previous changes, the shape is always something like
// 3 4
// 2 5
// 1 6
wxPoint corner; corner.x -= m_Size.x/2; corner.y -= m_Size.y/2; m_PolyCorners.push_back( corner ); // Lower left corner, start point (1)
corner.y += m_Size.y; m_PolyCorners.push_back( corner ); // upper left corner, start point (2)
if( delta.x || delta.y) { corner += delta; m_PolyCorners.push_back( corner ); // upper left corner, end point (3)
}
corner.x += m_Size.x; m_PolyCorners.push_back( corner ); // upper right corner, end point (4)
corner.y -= m_Size.y; m_PolyCorners.push_back( corner ); // lower right corner, end point (5)
if( delta.x || delta.y ) { corner -= delta; m_PolyCorners.push_back( corner ); // lower left corner, start point (6)
}
// Create final polygon:
for( unsigned ii = 0; ii < m_PolyCorners.size(); ii++ ) { if( change ) NEGATE( m_PolyCorners[ii].y);
m_PolyCorners[ii] += start; } }
void GERBER_DRAW_ITEM::DrawGbrPoly( EDA_RECT* aClipBox, wxDC* aDC, EDA_COLOR_T aColor, const wxPoint& aOffset, bool aFilledShape ) { std::vector<wxPoint> points;
points = m_PolyCorners; for( unsigned ii = 0; ii < points.size(); ii++ ) { points[ii] += aOffset; points[ii] = GetABPosition( points[ii] ); }
GRClosedPoly( aClipBox, aDC, points.size(), &points[0], aFilledShape, aColor, aColor ); }
void GERBER_DRAW_ITEM::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ) { wxString msg;
msg = ShowGBRShape(); aList.push_back( MSG_PANEL_ITEM( _( "Type" ), msg, DARKCYAN ) );
// Display D_Code value:
msg.Printf( wxT( "%d" ), m_DCode ); aList.push_back( MSG_PANEL_ITEM( _( "D Code" ), msg, RED ) );
// Display graphic layer number
msg.Printf( wxT( "%d" ), GetLayer() + 1 ); aList.push_back( MSG_PANEL_ITEM( _( "Graphic Layer" ), msg, BROWN ) );
// Display item rotation
// The full rotation is Image rotation + m_lyrRotation
// but m_lyrRotation is specific to this object
// so we display only this parameter
msg.Printf( wxT( "%f" ), m_lyrRotation ); aList.push_back( MSG_PANEL_ITEM( _( "Rotation" ), msg, BLUE ) );
// Display item polarity (item specific)
msg = m_LayerNegative ? _("Clear") : _("Dark"); aList.push_back( MSG_PANEL_ITEM( _( "Polarity" ), msg, BLUE ) );
// Display mirroring (item specific)
msg.Printf( wxT( "A:%s B:%s" ), m_mirrorA ? _("Yes") : _("No"), m_mirrorB ? _("Yes") : _("No")); aList.push_back( MSG_PANEL_ITEM( _( "Mirror" ), msg, DARKRED ) );
// Display AB axis swap (item specific)
msg = m_swapAxis ? wxT( "A=Y B=X" ) : wxT( "A=X B=Y" ); aList.push_back( MSG_PANEL_ITEM( _( "AB axis" ), msg, DARKRED ) ); }
bool GERBER_DRAW_ITEM::HitTest( const wxPoint& aRefPos ) const { // calculate aRefPos in XY gerber axis:
wxPoint ref_pos = GetXYPosition( aRefPos );
// TODO: a better analyze of the shape (perhaps create a D_CODE::HitTest for flashed items)
int radius = std::min( m_Size.x, m_Size.y ) >> 1;
if( m_Flashed ) return HitTestPoints( m_Start, ref_pos, radius ); else return TestSegmentHit( ref_pos, m_Start, m_End, radius ); }
bool GERBER_DRAW_ITEM::HitTest( const EDA_RECT& aRefArea ) const { wxPoint pos = GetABPosition( m_Start );
if( aRefArea.Contains( pos ) ) return true;
pos = GetABPosition( m_End );
if( aRefArea.Contains( pos ) ) return true;
return false; }
#if defined(DEBUG)
void GERBER_DRAW_ITEM::Show( int nestLevel, std::ostream& os ) const { NestedSpace( nestLevel, os ) << '<' << GetClass().Lower().mb_str() <<
" shape=\"" << m_Shape << '"' << " addr=\"" << std::hex << this << std::dec << '"' << " layer=\"" << m_Layer << '"' << " size=\"" << m_Size << '"' << " flags=\"" << m_Flags << '"' << " status=\"" << GetStatus() << '"' << "<start" << m_Start << "/>" << "<end" << m_End << "/>";
os << "</" << GetClass().Lower().mb_str() << ">\n"; }
#endif
|