You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

352 lines
9.2 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2004-2012 KiCad Developers, see change_log.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /**
  25. * @file lib_circle.cpp
  26. * @brief LIB_CIRCLE class implementation.
  27. */
  28. #include <fctsys.h>
  29. #include <gr_basic.h>
  30. #include <macros.h>
  31. #include <class_drawpanel.h>
  32. #include <plot_common.h>
  33. #include <trigo.h>
  34. #include <wxstruct.h>
  35. #include <richio.h>
  36. #include <base_units.h>
  37. #include <msgpanel.h>
  38. #include <general.h>
  39. #include <lib_circle.h>
  40. #include <transform.h>
  41. LIB_CIRCLE::LIB_CIRCLE( LIB_COMPONENT* aParent ) :
  42. LIB_ITEM( LIB_CIRCLE_T, aParent )
  43. {
  44. m_Radius = 0;
  45. m_Width = 0;
  46. m_Fill = NO_FILL;
  47. m_isFillable = true;
  48. m_typeName = _( "Circle" );
  49. }
  50. bool LIB_CIRCLE::Save( OUTPUTFORMATTER& aFormatter )
  51. {
  52. aFormatter.Print( 0, "C %d %d %d %d %d %d %c\n", m_Pos.x, m_Pos.y,
  53. m_Radius, m_Unit, m_Convert, m_Width, fill_tab[m_Fill] );
  54. return true;
  55. }
  56. bool LIB_CIRCLE::Load( LINE_READER& aLineReader, wxString& aErrorMsg )
  57. {
  58. char tmp[256];
  59. char* line = (char*) aLineReader;
  60. int cnt = sscanf( line + 2, "%d %d %d %d %d %d %s", &m_Pos.x, &m_Pos.y,
  61. &m_Radius, &m_Unit, &m_Convert, &m_Width, tmp );
  62. if( cnt < 6 )
  63. {
  64. aErrorMsg.Printf( _( "Circle only had %d parameters of the required 6" ), cnt );
  65. return false;
  66. }
  67. if( tmp[0] == 'F' )
  68. m_Fill = FILLED_SHAPE;
  69. if( tmp[0] == 'f' )
  70. m_Fill = FILLED_WITH_BG_BODYCOLOR;
  71. return true;
  72. }
  73. bool LIB_CIRCLE::HitTest( const wxPoint& aPosRef )
  74. {
  75. int mindist = GetPenSize() / 2;
  76. // Have a minimal tolerance for hit test
  77. if( mindist < MINIMUM_SELECTION_DISTANCE )
  78. mindist = MINIMUM_SELECTION_DISTANCE;
  79. return HitTest( aPosRef, mindist, DefaultTransform );
  80. }
  81. bool LIB_CIRCLE::HitTest( wxPoint aPosRef, int aThreshold, const TRANSFORM& aTransform )
  82. {
  83. if( aThreshold < 0 )
  84. aThreshold = GetPenSize() / 2;
  85. int dist = KiROUND( GetLineLength( aPosRef, aTransform.TransformCoordinate( m_Pos ) ) );
  86. if( abs( dist - m_Radius ) <= aThreshold )
  87. return true;
  88. return false;
  89. }
  90. EDA_ITEM* LIB_CIRCLE::Clone() const
  91. {
  92. return new LIB_CIRCLE( *this );
  93. }
  94. int LIB_CIRCLE::compare( const LIB_ITEM& aOther ) const
  95. {
  96. wxASSERT( aOther.Type() == LIB_CIRCLE_T );
  97. const LIB_CIRCLE* tmp = ( LIB_CIRCLE* ) &aOther;
  98. if( m_Pos.x != tmp->m_Pos.x )
  99. return m_Pos.x - tmp->m_Pos.x;
  100. if( m_Pos.y != tmp->m_Pos.y )
  101. return m_Pos.y - tmp->m_Pos.y;
  102. if( m_Radius != tmp->m_Radius )
  103. return m_Radius - tmp->m_Radius;
  104. return 0;
  105. }
  106. void LIB_CIRCLE::SetOffset( const wxPoint& aOffset )
  107. {
  108. m_Pos += aOffset;
  109. }
  110. bool LIB_CIRCLE::Inside( EDA_RECT& aRect ) const
  111. {
  112. /*
  113. * FIXME: This fails to take into account the radius around the center
  114. * point.
  115. */
  116. return aRect.Contains( m_Pos.x, -m_Pos.y );
  117. }
  118. void LIB_CIRCLE::Move( const wxPoint& aPosition )
  119. {
  120. m_Pos = aPosition;
  121. }
  122. void LIB_CIRCLE::MirrorHorizontal( const wxPoint& aCenter )
  123. {
  124. m_Pos.x -= aCenter.x;
  125. m_Pos.x *= -1;
  126. m_Pos.x += aCenter.x;
  127. }
  128. void LIB_CIRCLE::MirrorVertical( const wxPoint& aCenter )
  129. {
  130. m_Pos.y -= aCenter.y;
  131. m_Pos.y *= -1;
  132. m_Pos.y += aCenter.y;
  133. }
  134. void LIB_CIRCLE::Rotate( const wxPoint& aCenter, bool aRotateCCW )
  135. {
  136. int rot_angle = aRotateCCW ? -900 : 900;
  137. RotatePoint( &m_Pos, aCenter, rot_angle );
  138. }
  139. void LIB_CIRCLE::Plot( PLOTTER* aPlotter, const wxPoint& aOffset, bool aFill,
  140. const TRANSFORM& aTransform )
  141. {
  142. wxPoint pos = aTransform.TransformCoordinate( m_Pos ) + aOffset;
  143. if( aFill && m_Fill == FILLED_WITH_BG_BODYCOLOR )
  144. {
  145. aPlotter->SetColor( GetLayerColor( LAYER_DEVICE_BACKGROUND ) );
  146. aPlotter->Circle( pos, m_Radius * 2, FILLED_SHAPE, 0 );
  147. }
  148. bool already_filled = m_Fill == FILLED_WITH_BG_BODYCOLOR;
  149. aPlotter->SetColor( GetLayerColor( LAYER_DEVICE ) );
  150. aPlotter->Circle( pos, m_Radius * 2, already_filled ? NO_FILL : m_Fill, GetPenSize() );
  151. }
  152. int LIB_CIRCLE::GetPenSize() const
  153. {
  154. return ( m_Width == 0 ) ? GetDefaultLineThickness() : m_Width;
  155. }
  156. void LIB_CIRCLE::drawGraphic( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aOffset,
  157. EDA_COLOR_T aColor, GR_DRAWMODE aDrawMode, void* aData,
  158. const TRANSFORM& aTransform )
  159. {
  160. wxPoint pos1;
  161. EDA_COLOR_T color = GetLayerColor( LAYER_DEVICE );
  162. if( aColor < 0 ) // Used normal color or selected color
  163. {
  164. if( IsSelected() )
  165. color = GetItemSelectedColor();
  166. }
  167. else
  168. {
  169. color = aColor;
  170. }
  171. pos1 = aTransform.TransformCoordinate( m_Pos ) + aOffset;
  172. GRSetDrawMode( aDC, aDrawMode );
  173. FILL_T fill = aData ? NO_FILL : m_Fill;
  174. if( aColor >= 0 )
  175. fill = NO_FILL;
  176. EDA_RECT* const clipbox = aPanel? aPanel->GetClipBox() : NULL;
  177. if( fill == FILLED_WITH_BG_BODYCOLOR )
  178. GRFilledCircle( clipbox, aDC, pos1.x, pos1.y, m_Radius, GetPenSize(),
  179. (m_Flags & IS_MOVED) ? color : GetLayerColor( LAYER_DEVICE_BACKGROUND ),
  180. GetLayerColor( LAYER_DEVICE_BACKGROUND ) );
  181. else if( fill == FILLED_SHAPE )
  182. GRFilledCircle( clipbox, aDC, pos1.x, pos1.y, m_Radius, 0, color, color );
  183. else
  184. GRCircle( clipbox, aDC, pos1.x, pos1.y, m_Radius, GetPenSize(), color );
  185. /* Set to one (1) to draw bounding box around circle to validate bounding
  186. * box calculation. */
  187. #if 0
  188. EDA_RECT bBox = GetBoundingBox();
  189. GRRect( clipbox, aDC, bBox.GetOrigin().x, bBox.GetOrigin().y,
  190. bBox.GetEnd().x, bBox.GetEnd().y, 0, LIGHTMAGENTA );
  191. #endif
  192. }
  193. const EDA_RECT LIB_CIRCLE::GetBoundingBox() const
  194. {
  195. EDA_RECT rect;
  196. rect.SetOrigin( m_Pos.x - m_Radius, ( m_Pos.y - m_Radius ) * -1 );
  197. rect.SetEnd( m_Pos.x + m_Radius, ( m_Pos.y + m_Radius ) * -1 );
  198. rect.Inflate( m_Width / 2, m_Width / 2 );
  199. return rect;
  200. }
  201. void LIB_CIRCLE::GetMsgPanelInfo( MSG_PANEL_ITEMS& aList )
  202. {
  203. wxString msg;
  204. EDA_RECT bBox = GetBoundingBox();
  205. LIB_ITEM::GetMsgPanelInfo( aList );
  206. msg = ReturnStringFromValue( g_UserUnit, m_Width, true );
  207. aList.push_back( MSG_PANEL_ITEM( _( "Line width" ), msg, BLUE ) );
  208. msg = ReturnStringFromValue( g_UserUnit, m_Radius, true );
  209. aList.push_back( MSG_PANEL_ITEM( _( "Radius" ), msg, RED ) );
  210. msg.Printf( wxT( "(%d, %d, %d, %d)" ), bBox.GetOrigin().x,
  211. bBox.GetOrigin().y, bBox.GetEnd().x, bBox.GetEnd().y );
  212. aList.push_back( MSG_PANEL_ITEM( _( "Bounding box" ), msg, BROWN ) );
  213. }
  214. wxString LIB_CIRCLE::GetSelectMenuText() const
  215. {
  216. return wxString::Format( _( "Circle center (%s, %s), radius %s" ),
  217. GetChars( CoordinateToString( m_Pos.x ) ),
  218. GetChars( CoordinateToString( m_Pos.y ) ),
  219. GetChars( CoordinateToString( m_Radius ) ) );
  220. }
  221. void LIB_CIRCLE::BeginEdit( STATUS_FLAGS aEditMode, const wxPoint aPosition )
  222. {
  223. wxCHECK_RET( ( aEditMode & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0,
  224. wxT( "Invalid edit mode for LIB_CIRCLE object." ) );
  225. if( aEditMode == IS_NEW )
  226. {
  227. m_Pos = m_initialPos = aPosition;
  228. }
  229. else if( aEditMode == IS_MOVED )
  230. {
  231. m_initialPos = m_Pos;
  232. m_initialCursorPos = aPosition;
  233. SetEraseLastDrawItem();
  234. }
  235. else if( aEditMode == IS_RESIZED )
  236. {
  237. SetEraseLastDrawItem();
  238. }
  239. m_Flags = aEditMode;
  240. }
  241. bool LIB_CIRCLE::ContinueEdit( const wxPoint aPosition )
  242. {
  243. wxCHECK_MSG( ( m_Flags & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0, false,
  244. wxT( "Bad call to ContinueEdit(). LIB_CIRCLE is not being edited." ) );
  245. return false;
  246. }
  247. void LIB_CIRCLE::EndEdit( const wxPoint& aPosition, bool aAbort )
  248. {
  249. wxCHECK_RET( ( m_Flags & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0,
  250. wxT( "Bad call to EndEdit(). LIB_CIRCLE is not being edited." ) );
  251. SetEraseLastDrawItem( false );
  252. m_Flags = 0;
  253. }
  254. void LIB_CIRCLE::calcEdit( const wxPoint& aPosition )
  255. {
  256. if( m_Flags == IS_NEW || m_Flags == IS_RESIZED )
  257. {
  258. if( m_Flags == IS_NEW )
  259. SetEraseLastDrawItem();
  260. m_Radius = KiROUND( GetLineLength( m_Pos, aPosition ) );
  261. }
  262. else
  263. {
  264. Move( m_initialPos + aPosition - m_initialCursorPos );
  265. }
  266. }