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.

351 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. if( fill == FILLED_WITH_BG_BODYCOLOR )
  177. GRFilledCircle( aPanel->GetClipBox(), aDC, pos1.x, pos1.y, m_Radius, GetPenSize(),
  178. (m_Flags & IS_MOVED) ? color : GetLayerColor( LAYER_DEVICE_BACKGROUND ),
  179. GetLayerColor( LAYER_DEVICE_BACKGROUND ) );
  180. else if( fill == FILLED_SHAPE )
  181. GRFilledCircle( aPanel->GetClipBox(), aDC, pos1.x, pos1.y, m_Radius, 0, color, color );
  182. else
  183. GRCircle( aPanel->GetClipBox(), aDC, pos1.x, pos1.y, m_Radius, GetPenSize(), color );
  184. /* Set to one (1) to draw bounding box around circle to validate bounding
  185. * box calculation. */
  186. #if 0
  187. EDA_RECT bBox = GetBoundingBox();
  188. GRRect( aPanel->GetClipBox(), aDC, bBox.GetOrigin().x, bBox.GetOrigin().y,
  189. bBox.GetEnd().x, bBox.GetEnd().y, 0, LIGHTMAGENTA );
  190. #endif
  191. }
  192. const EDA_RECT LIB_CIRCLE::GetBoundingBox() const
  193. {
  194. EDA_RECT rect;
  195. rect.SetOrigin( m_Pos.x - m_Radius, ( m_Pos.y - m_Radius ) * -1 );
  196. rect.SetEnd( m_Pos.x + m_Radius, ( m_Pos.y + m_Radius ) * -1 );
  197. rect.Inflate( m_Width / 2, m_Width / 2 );
  198. return rect;
  199. }
  200. void LIB_CIRCLE::GetMsgPanelInfo( MSG_PANEL_ITEMS& aList )
  201. {
  202. wxString msg;
  203. EDA_RECT bBox = GetBoundingBox();
  204. LIB_ITEM::GetMsgPanelInfo( aList );
  205. msg = ReturnStringFromValue( g_UserUnit, m_Width, true );
  206. aList.push_back( MSG_PANEL_ITEM( _( "Line width" ), msg, BLUE ) );
  207. msg = ReturnStringFromValue( g_UserUnit, m_Radius, true );
  208. aList.push_back( MSG_PANEL_ITEM( _( "Radius" ), msg, RED ) );
  209. msg.Printf( wxT( "(%d, %d, %d, %d)" ), bBox.GetOrigin().x,
  210. bBox.GetOrigin().y, bBox.GetEnd().x, bBox.GetEnd().y );
  211. aList.push_back( MSG_PANEL_ITEM( _( "Bounding box" ), msg, BROWN ) );
  212. }
  213. wxString LIB_CIRCLE::GetSelectMenuText() const
  214. {
  215. return wxString::Format( _( "Circle center (%s, %s), radius %s" ),
  216. GetChars( CoordinateToString( m_Pos.x ) ),
  217. GetChars( CoordinateToString( m_Pos.y ) ),
  218. GetChars( CoordinateToString( m_Radius ) ) );
  219. }
  220. void LIB_CIRCLE::BeginEdit( STATUS_FLAGS aEditMode, const wxPoint aPosition )
  221. {
  222. wxCHECK_RET( ( aEditMode & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0,
  223. wxT( "Invalid edit mode for LIB_CIRCLE object." ) );
  224. if( aEditMode == IS_NEW )
  225. {
  226. m_Pos = m_initialPos = aPosition;
  227. }
  228. else if( aEditMode == IS_MOVED )
  229. {
  230. m_initialPos = m_Pos;
  231. m_initialCursorPos = aPosition;
  232. SetEraseLastDrawItem();
  233. }
  234. else if( aEditMode == IS_RESIZED )
  235. {
  236. SetEraseLastDrawItem();
  237. }
  238. m_Flags = aEditMode;
  239. }
  240. bool LIB_CIRCLE::ContinueEdit( const wxPoint aPosition )
  241. {
  242. wxCHECK_MSG( ( m_Flags & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0, false,
  243. wxT( "Bad call to ContinueEdit(). LIB_CIRCLE is not being edited." ) );
  244. return false;
  245. }
  246. void LIB_CIRCLE::EndEdit( const wxPoint& aPosition, bool aAbort )
  247. {
  248. wxCHECK_RET( ( m_Flags & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0,
  249. wxT( "Bad call to EndEdit(). LIB_CIRCLE is not being edited." ) );
  250. SetEraseLastDrawItem( false );
  251. m_Flags = 0;
  252. }
  253. void LIB_CIRCLE::calcEdit( const wxPoint& aPosition )
  254. {
  255. if( m_Flags == IS_NEW || m_Flags == IS_RESIZED )
  256. {
  257. if( m_Flags == IS_NEW )
  258. SetEraseLastDrawItem();
  259. m_Radius = KiROUND( GetLineLength( m_Pos, aPosition ) );
  260. }
  261. else
  262. {
  263. Move( m_initialPos + aPosition - m_initialCursorPos );
  264. }
  265. }