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.

1015 lines
29 KiB

// 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
6 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 1992-2017 <Jean-Pierre Charras>
  5. * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.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. #include <fctsys.h>
  25. #include <gr_basic.h>
  26. #include <common.h>
  27. #include <trigo.h>
  28. #include <bitmaps.h>
  29. #include <msgpanel.h>
  30. #include <gerbview_frame.h>
  31. #include <convert_basic_shapes_to_polygon.h>
  32. #include <gerber_draw_item.h>
  33. #include <gerber_file_image.h>
  34. #include <gerber_file_image_list.h>
  35. #include <kicad_string.h>
  36. #include <geometry/shape_arc.h>
  37. #include <math/util.h> // for KiROUND
  38. GERBER_DRAW_ITEM::GERBER_DRAW_ITEM( GERBER_FILE_IMAGE* aGerberImageFile ) :
  39. EDA_ITEM( (EDA_ITEM*)NULL, GERBER_DRAW_ITEM_T )
  40. {
  41. m_GerberImageFile = aGerberImageFile;
  42. m_Shape = GBR_SEGMENT;
  43. m_Flashed = false;
  44. m_DCode = 0;
  45. m_UnitsMetric = false;
  46. m_LayerNegative = false;
  47. m_swapAxis = false;
  48. m_mirrorA = false;
  49. m_mirrorB = false;
  50. m_drawScale.x = m_drawScale.y = 1.0;
  51. m_lyrRotation = 0;
  52. if( m_GerberImageFile )
  53. SetLayerParameters();
  54. }
  55. GERBER_DRAW_ITEM::~GERBER_DRAW_ITEM()
  56. {
  57. }
  58. void GERBER_DRAW_ITEM::SetNetAttributes( const GBR_NETLIST_METADATA& aNetAttributes )
  59. {
  60. m_netAttributes = aNetAttributes;
  61. if( ( m_netAttributes.m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_CMP ) ||
  62. ( m_netAttributes.m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_PAD ) )
  63. m_GerberImageFile->m_ComponentsList.insert( std::make_pair( m_netAttributes.m_Cmpref, 0 ) );
  64. if( ( m_netAttributes.m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_NET ) )
  65. m_GerberImageFile->m_NetnamesList.insert( std::make_pair( m_netAttributes.m_Netname, 0 ) );
  66. }
  67. int GERBER_DRAW_ITEM::GetLayer() const
  68. {
  69. // returns the layer this item is on, or 0 if the m_GerberImageFile is NULL.
  70. return m_GerberImageFile ? m_GerberImageFile->m_GraphicLayer : 0;
  71. }
  72. bool GERBER_DRAW_ITEM::GetTextD_CodePrms( int& aSize, wxPoint& aPos, double& aOrientation )
  73. {
  74. // calculate the best size and orientation of the D_Code text
  75. if( m_DCode <= 0 )
  76. return false; // No D_Code for this item
  77. if( m_Flashed || m_Shape == GBR_ARC )
  78. {
  79. aPos = m_Start;
  80. }
  81. else // it is a line:
  82. {
  83. aPos = ( m_Start + m_End) / 2;
  84. }
  85. aPos = GetABPosition( aPos );
  86. int size; // the best size for the text
  87. if( GetDcodeDescr() )
  88. size = GetDcodeDescr()->GetShapeDim( this );
  89. else
  90. size = std::min( m_Size.x, m_Size.y );
  91. aOrientation = TEXT_ANGLE_HORIZ;
  92. if( m_Flashed )
  93. {
  94. // A reasonable size for text is min_dim/3 because most of time this text has 3 chars.
  95. aSize = size / 3;
  96. }
  97. else // this item is a line
  98. {
  99. wxPoint delta = m_Start - m_End;
  100. aOrientation = RAD2DECIDEG( atan2( (double)delta.y, (double)delta.x ) );
  101. NORMALIZE_ANGLE_90( aOrientation );
  102. // A reasonable size for text is size/2 because text needs margin below and above it.
  103. // a margin = size/4 seems good, expecting the line len is large enough to show 3 chars,
  104. // that is the case most of time.
  105. aSize = size / 2;
  106. }
  107. return true;
  108. }
  109. bool GERBER_DRAW_ITEM::GetTextD_CodePrms( double& aSize, VECTOR2D& aPos, double& aOrientation )
  110. {
  111. // aOrientation is returned in radians
  112. int size;
  113. wxPoint pos;
  114. if( ! GetTextD_CodePrms( size, pos, aOrientation ) )
  115. return false;
  116. aPos = pos;
  117. aSize = (double) size;
  118. aOrientation = DECIDEG2RAD( aOrientation );
  119. return true;
  120. }
  121. wxPoint GERBER_DRAW_ITEM::GetABPosition( const wxPoint& aXYPosition ) const
  122. {
  123. /* Note: RS274Xrevd_e is obscure about the order of transforms:
  124. * For instance: Rotation must be made after or before mirroring ?
  125. * Note: if something is changed here, GetYXPosition must reflect changes
  126. */
  127. wxPoint abPos = aXYPosition + m_GerberImageFile->m_ImageJustifyOffset;
  128. if( m_swapAxis )
  129. std::swap( abPos.x, abPos.y );
  130. abPos += m_layerOffset + m_GerberImageFile->m_ImageOffset;
  131. abPos.x = KiROUND( abPos.x * m_drawScale.x );
  132. abPos.y = KiROUND( abPos.y * m_drawScale.y );
  133. double rotation = m_lyrRotation * 10 + m_GerberImageFile->m_ImageRotation * 10;
  134. if( rotation )
  135. RotatePoint( &abPos, -rotation );
  136. // Negate A axis if mirrored
  137. if( m_mirrorA )
  138. abPos.x = -abPos.x;
  139. // abPos.y must be negated when no mirror, because draw axis is top to bottom
  140. if( !m_mirrorB )
  141. abPos.y = -abPos.y;
  142. return abPos;
  143. }
  144. wxPoint GERBER_DRAW_ITEM::GetXYPosition( const wxPoint& aABPosition ) const
  145. {
  146. // do the inverse transform made by GetABPosition
  147. wxPoint xyPos = aABPosition;
  148. if( m_mirrorA )
  149. xyPos.x = -xyPos.x;
  150. if( !m_mirrorB )
  151. xyPos.y = -xyPos.y;
  152. double rotation = m_lyrRotation * 10 + m_GerberImageFile->m_ImageRotation * 10;
  153. if( rotation )
  154. RotatePoint( &xyPos, rotation );
  155. xyPos.x = KiROUND( xyPos.x / m_drawScale.x );
  156. xyPos.y = KiROUND( xyPos.y / m_drawScale.y );
  157. xyPos -= m_layerOffset + m_GerberImageFile->m_ImageOffset;
  158. if( m_swapAxis )
  159. std::swap( xyPos.x, xyPos.y );
  160. return xyPos - m_GerberImageFile->m_ImageJustifyOffset;
  161. }
  162. void GERBER_DRAW_ITEM::SetLayerParameters()
  163. {
  164. m_UnitsMetric = m_GerberImageFile->m_GerbMetric;
  165. m_swapAxis = m_GerberImageFile->m_SwapAxis; // false if A = X, B = Y;
  166. // true if A =Y, B = Y
  167. m_mirrorA = m_GerberImageFile->m_MirrorA; // true: mirror / axe A
  168. m_mirrorB = m_GerberImageFile->m_MirrorB; // true: mirror / axe B
  169. m_drawScale = m_GerberImageFile->m_Scale; // A and B scaling factor
  170. m_layerOffset = m_GerberImageFile->m_Offset; // Offset from OF command
  171. // Rotation from RO command:
  172. m_lyrRotation = m_GerberImageFile->m_LocalRotation;
  173. m_LayerNegative = m_GerberImageFile->GetLayerParams().m_LayerNegative;
  174. }
  175. wxString GERBER_DRAW_ITEM::ShowGBRShape() const
  176. {
  177. switch( m_Shape )
  178. {
  179. case GBR_SEGMENT:
  180. return _( "Line" );
  181. case GBR_ARC:
  182. return _( "Arc" );
  183. case GBR_CIRCLE:
  184. return _( "Circle" );
  185. case GBR_SPOT_OVAL:
  186. return wxT( "spot_oval" );
  187. case GBR_SPOT_CIRCLE:
  188. return wxT( "spot_circle" );
  189. case GBR_SPOT_RECT:
  190. return wxT( "spot_rect" );
  191. case GBR_SPOT_POLY:
  192. return wxT( "spot_poly" );
  193. case GBR_POLYGON:
  194. return wxT( "polygon" );
  195. case GBR_SPOT_MACRO:
  196. {
  197. wxString name = wxT( "apt_macro" );
  198. D_CODE* dcode = GetDcodeDescr();
  199. if( dcode && dcode->GetMacro() )
  200. name << wxT(" ") << dcode->GetMacro()->name;
  201. return name;
  202. }
  203. default:
  204. return wxT( "??" );
  205. }
  206. }
  207. D_CODE* GERBER_DRAW_ITEM::GetDcodeDescr() const
  208. {
  209. if( (m_DCode < FIRST_DCODE) || (m_DCode > LAST_DCODE) )
  210. return NULL;
  211. if( m_GerberImageFile == NULL )
  212. return NULL;
  213. return m_GerberImageFile->GetDCODE( m_DCode );
  214. }
  215. const EDA_RECT GERBER_DRAW_ITEM::GetBoundingBox() const
  216. {
  217. // return a rectangle which is (pos,dim) in nature. therefore the +1
  218. EDA_RECT bbox( m_Start, wxSize( 1, 1 ) );
  219. D_CODE* code = GetDcodeDescr();
  220. // TODO(JE) GERBER_DRAW_ITEM maybe should actually be a number of subclasses.
  221. // Until/unless that is changed, we need to do different things depending on
  222. // what is actually being represented by this GERBER_DRAW_ITEM.
  223. switch( m_Shape )
  224. {
  225. case GBR_POLYGON:
  226. {
  227. auto bb = m_Polygon.BBox();
  228. bbox.Inflate( bb.GetWidth() / 2, bb.GetHeight() / 2 );
  229. bbox.SetOrigin( bb.GetOrigin().x, bb.GetOrigin().y );
  230. break;
  231. }
  232. case GBR_CIRCLE:
  233. {
  234. double radius = GetLineLength( m_Start, m_End );
  235. bbox.Inflate( radius, radius );
  236. break;
  237. }
  238. case GBR_ARC:
  239. {
  240. double arc_angle =
  241. atan2( double( m_End.y - m_ArcCentre.y ), double( m_End.x - m_ArcCentre.x ) )
  242. - atan2( double( m_Start.y - m_ArcCentre.y ), double( m_Start.x - m_ArcCentre.x ) );
  243. arc_angle *= 180.0 / M_PI;
  244. if( arc_angle < 0.0 )
  245. arc_angle += 360.0;
  246. if( m_End == m_Start ) // Arc with the end point = start point is expected to be a circle
  247. arc_angle = 360.0;
  248. SHAPE_ARC arc( m_ArcCentre, m_Start, arc_angle );
  249. BOX2I arc_bbox = arc.BBox( m_Size.x / 2 ); // m_Size.x is the line thickness
  250. bbox.SetOrigin( arc_bbox.GetX(), arc_bbox.GetY() );
  251. bbox.SetWidth( arc_bbox.GetWidth() );
  252. bbox.SetHeight( arc_bbox.GetHeight() );
  253. break;
  254. }
  255. case GBR_SPOT_CIRCLE:
  256. {
  257. if( code )
  258. {
  259. int radius = code->m_Size.x >> 1;
  260. bbox.Inflate( radius, radius );
  261. }
  262. break;
  263. }
  264. case GBR_SPOT_RECT:
  265. {
  266. if( code )
  267. bbox.Inflate( code->m_Size.x / 2, code->m_Size.y / 2 );
  268. break;
  269. }
  270. case GBR_SPOT_OVAL:
  271. {
  272. if( code )
  273. bbox.Inflate( code->m_Size.x /2, code->m_Size.y / 2 );
  274. break;
  275. }
  276. case GBR_SPOT_POLY:
  277. {
  278. if( code )
  279. {
  280. if( code->m_Polygon.OutlineCount() == 0 )
  281. code->ConvertShapeToPolygon();
  282. bbox.Inflate( code->m_Polygon.BBox().GetWidth() / 2,
  283. code->m_Polygon.BBox().GetHeight() / 2 );
  284. }
  285. break;
  286. }
  287. case GBR_SPOT_MACRO:
  288. {
  289. if( code )
  290. {
  291. // Update the shape drawings and the bounding box coordiantes:
  292. code->GetMacro()->GetApertureMacroShape( this, m_Start );
  293. // now the bounding box is valid:
  294. bbox = code->GetMacro()->GetBoundingBox();
  295. }
  296. break;
  297. }
  298. case GBR_SEGMENT:
  299. {
  300. if( code && code->m_Shape == APT_RECT )
  301. {
  302. if( m_Polygon.OutlineCount() > 0 )
  303. {
  304. auto bb = m_Polygon.BBox();
  305. bbox.Inflate( bb.GetWidth() / 2, bb.GetHeight() / 2 );
  306. bbox.SetOrigin( bb.GetOrigin().x, bb.GetOrigin().y );
  307. }
  308. }
  309. else
  310. {
  311. int radius = ( m_Size.x + 1 ) / 2;
  312. int ymax = std::max( m_Start.y, m_End.y ) + radius;
  313. int xmax = std::max( m_Start.x, m_End.x ) + radius;
  314. int ymin = std::min( m_Start.y, m_End.y ) - radius;
  315. int xmin = std::min( m_Start.x, m_End.x ) - radius;
  316. bbox = EDA_RECT( wxPoint( xmin, ymin ), wxSize( xmax - xmin + 1, ymax - ymin + 1 ) );
  317. }
  318. break;
  319. }
  320. default:
  321. wxASSERT_MSG( false, wxT( "GERBER_DRAW_ITEM shape is unknown!" ) );
  322. break;
  323. }
  324. // calculate the corners coordinates in current gerber axis orientations
  325. wxPoint org = GetABPosition( bbox.GetOrigin() );
  326. wxPoint end = GetABPosition( bbox.GetEnd() );
  327. // Set the corners position:
  328. bbox.SetOrigin( org );
  329. bbox.SetEnd( end );
  330. bbox.Normalize();
  331. return bbox;
  332. }
  333. void GERBER_DRAW_ITEM::MoveAB( const wxPoint& aMoveVector )
  334. {
  335. wxPoint xymove = GetXYPosition( aMoveVector );
  336. m_Start += xymove;
  337. m_End += xymove;
  338. m_ArcCentre += xymove;
  339. m_Polygon.Move( VECTOR2I( xymove ) );
  340. }
  341. void GERBER_DRAW_ITEM::MoveXY( const wxPoint& aMoveVector )
  342. {
  343. m_Start += aMoveVector;
  344. m_End += aMoveVector;
  345. m_ArcCentre += aMoveVector;
  346. m_Polygon.Move( VECTOR2I( aMoveVector ) );
  347. }
  348. bool GERBER_DRAW_ITEM::HasNegativeItems()
  349. {
  350. bool isClear = m_LayerNegative ^ m_GerberImageFile->m_ImageNegative;
  351. // if isClear is true, this item has negative shape
  352. return isClear;
  353. }
  354. void GERBER_DRAW_ITEM::Print( wxDC* aDC, const wxPoint& aOffset, GBR_DISPLAY_OPTIONS* aOptions )
  355. {
  356. // used when a D_CODE is not found. default D_CODE to draw a flashed item
  357. static D_CODE dummyD_CODE( 0 );
  358. bool isFilled;
  359. int radius;
  360. int halfPenWidth;
  361. static bool show_err;
  362. D_CODE* d_codeDescr = GetDcodeDescr();
  363. if( d_codeDescr == NULL )
  364. d_codeDescr = &dummyD_CODE;
  365. COLOR4D color = m_GerberImageFile->GetPositiveDrawColor();
  366. /* isDark is true if flash is positive and should use a drawing
  367. * color other than the background color, else use the background color
  368. * when drawing so that an erasure happens.
  369. */
  370. bool isDark = !(m_LayerNegative ^ m_GerberImageFile->m_ImageNegative);
  371. if( !isDark )
  372. {
  373. // draw in background color ("negative" color)
  374. color = aOptions->m_NegativeDrawColor;
  375. }
  376. isFilled = aOptions->m_DisplayLinesFill;
  377. switch( m_Shape )
  378. {
  379. case GBR_POLYGON:
  380. isFilled = aOptions->m_DisplayPolygonsFill;
  381. if( !isDark )
  382. isFilled = true;
  383. PrintGerberPoly( aDC, color, aOffset, isFilled );
  384. break;
  385. case GBR_CIRCLE:
  386. radius = KiROUND( GetLineLength( m_Start, m_End ) );
  387. halfPenWidth = m_Size.x >> 1;
  388. if( !isFilled )
  389. {
  390. // draw the border of the pen's path using two circles, each as narrow as possible
  391. GRCircle( nullptr, aDC, GetABPosition( m_Start ), radius - halfPenWidth, 0, color );
  392. GRCircle( nullptr, aDC, GetABPosition( m_Start ), radius + halfPenWidth, 0, color );
  393. }
  394. else // Filled mode
  395. {
  396. GRCircle( nullptr, aDC, GetABPosition( m_Start ), radius, m_Size.x, color );
  397. }
  398. break;
  399. case GBR_ARC:
  400. // Currently, arcs plotted with a rectangular aperture are not supported.
  401. // a round pen only is expected.
  402. if( !isFilled )
  403. {
  404. GRArc1( nullptr, aDC, GetABPosition( m_Start ), GetABPosition( m_End ),
  405. GetABPosition( m_ArcCentre ), 0, color );
  406. }
  407. else
  408. {
  409. GRArc1( nullptr, aDC, GetABPosition( m_Start ), GetABPosition( m_End ),
  410. GetABPosition( m_ArcCentre ), m_Size.x, color );
  411. }
  412. break;
  413. case GBR_SPOT_CIRCLE:
  414. case GBR_SPOT_RECT:
  415. case GBR_SPOT_OVAL:
  416. case GBR_SPOT_POLY:
  417. case GBR_SPOT_MACRO:
  418. isFilled = aOptions->m_DisplayFlashedItemsFill;
  419. d_codeDescr->DrawFlashedShape( this, nullptr, aDC, color, m_Start, isFilled );
  420. break;
  421. case GBR_SEGMENT:
  422. /* Plot a line from m_Start to m_End.
  423. * Usually, a round pen is used, but some gerber files use a rectangular pen
  424. * In fact, any aperture can be used to plot a line.
  425. * currently: only a square pen is handled (I believe using a polygon gives a strange plot).
  426. */
  427. if( d_codeDescr->m_Shape == APT_RECT )
  428. {
  429. if( m_Polygon.OutlineCount() == 0 )
  430. ConvertSegmentToPolygon();
  431. PrintGerberPoly( aDC, color, aOffset, isFilled );
  432. }
  433. else
  434. {
  435. if( !isFilled )
  436. {
  437. GRCSegm( nullptr, aDC, GetABPosition( m_Start ), GetABPosition( m_End ),
  438. m_Size.x, color );
  439. }
  440. else
  441. {
  442. GRFilledSegment( nullptr, aDC, GetABPosition( m_Start ), GetABPosition( m_End ),
  443. m_Size.x, color );
  444. }
  445. }
  446. break;
  447. default:
  448. if( !show_err )
  449. {
  450. wxMessageBox( wxT( "Trace_Segment() type error" ) );
  451. show_err = true;
  452. }
  453. break;
  454. }
  455. }
  456. void GERBER_DRAW_ITEM::ConvertSegmentToPolygon()
  457. {
  458. m_Polygon.RemoveAllContours();
  459. m_Polygon.NewOutline();
  460. wxPoint start = m_Start;
  461. wxPoint end = m_End;
  462. // make calculations more easy if ensure start.x < end.x
  463. // (only 2 quadrants to consider)
  464. if( start.x > end.x )
  465. std::swap( start, end );
  466. // calculate values relative to start point:
  467. wxPoint delta = end - start;
  468. // calculate corners for the first quadrant only (delta.x and delta.y > 0 )
  469. // currently, delta.x already is > 0.
  470. // make delta.y > 0
  471. bool change = delta.y < 0;
  472. if( change )
  473. delta.y = -delta.y;
  474. // Now create the full polygon.
  475. // Due to previous changes, the shape is always something like
  476. // 3 4
  477. // 2 5
  478. // 1 6
  479. wxPoint corner;
  480. corner.x -= m_Size.x/2;
  481. corner.y -= m_Size.y/2;
  482. wxPoint close = corner;
  483. m_Polygon.Append( VECTOR2I( corner ) ); // Lower left corner, start point (1)
  484. corner.y += m_Size.y;
  485. m_Polygon.Append( VECTOR2I( corner ) ); // upper left corner, start point (2)
  486. if( delta.x || delta.y)
  487. {
  488. corner += delta;
  489. m_Polygon.Append( VECTOR2I( corner ) ); // upper left corner, end point (3)
  490. }
  491. corner.x += m_Size.x;
  492. m_Polygon.Append( VECTOR2I( corner ) ); // upper right corner, end point (4)
  493. corner.y -= m_Size.y;
  494. m_Polygon.Append( VECTOR2I( corner ) ); // lower right corner, end point (5)
  495. if( delta.x || delta.y )
  496. {
  497. corner -= delta;
  498. m_Polygon.Append( VECTOR2I( corner ) ); // lower left corner, start point (6)
  499. }
  500. m_Polygon.Append( VECTOR2I( close ) ); // close the shape
  501. // Create final polygon:
  502. if( change )
  503. m_Polygon.Mirror( false, true );
  504. m_Polygon.Move( VECTOR2I( start ) );
  505. }
  506. void GERBER_DRAW_ITEM::PrintGerberPoly( wxDC* aDC, COLOR4D aColor, const wxPoint& aOffset,
  507. bool aFilledShape )
  508. {
  509. std::vector<wxPoint> points;
  510. SHAPE_LINE_CHAIN& poly = m_Polygon.Outline( 0 );
  511. int pointCount = poly.PointCount() - 1;
  512. points.reserve( pointCount );
  513. for( int ii = 0; ii < pointCount; ii++ )
  514. {
  515. wxPoint p( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
  516. points[ii] = p + aOffset;
  517. points[ii] = GetABPosition( points[ii] );
  518. }
  519. GRClosedPoly( nullptr, aDC, pointCount, &points[0], aFilledShape, aColor, aColor );
  520. }
  521. void GERBER_DRAW_ITEM::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  522. {
  523. wxString msg;
  524. wxString text;
  525. msg = ShowGBRShape();
  526. aList.emplace_back( _( "Type" ), msg, DARKCYAN );
  527. // Display D_Code value with its attributes for items using a DCode:
  528. if( m_Shape == GBR_POLYGON ) // Has no DCode, but can have an attribute
  529. {
  530. msg = _( "Attribute" );
  531. if( m_AperFunction.IsEmpty() )
  532. text = _( "No attribute" );
  533. else
  534. text = m_AperFunction;
  535. }
  536. else
  537. {
  538. msg.Printf( _( "D Code %d" ), m_DCode );
  539. D_CODE* apertDescr = GetDcodeDescr();
  540. if( !apertDescr || apertDescr->m_AperFunction.IsEmpty() )
  541. text = _( "No attribute" );
  542. else
  543. text = apertDescr->m_AperFunction;
  544. }
  545. aList.emplace_back( msg, text, RED );
  546. // Display graphic layer name
  547. msg = GERBER_FILE_IMAGE_LIST::GetImagesList().GetDisplayName( GetLayer(), true );
  548. aList.emplace_back( _( "Graphic Layer" ), msg, DARKGREEN );
  549. // Display item rotation
  550. // The full rotation is Image rotation + m_lyrRotation
  551. // but m_lyrRotation is specific to this object
  552. // so we display only this parameter
  553. msg.Printf( wxT( "%f" ), m_lyrRotation );
  554. aList.emplace_back( _( "Rotation" ), msg, BLUE );
  555. // Display item polarity (item specific)
  556. msg = m_LayerNegative ? _("Clear") : _("Dark");
  557. aList.emplace_back( _( "Polarity" ), msg, BLUE );
  558. // Display mirroring (item specific)
  559. msg.Printf( wxT( "A:%s B:%s" ),
  560. m_mirrorA ? _("Yes") : _("No"),
  561. m_mirrorB ? _("Yes") : _("No"));
  562. aList.emplace_back( _( "Mirror" ), msg, DARKRED );
  563. // Display AB axis swap (item specific)
  564. msg = m_swapAxis ? wxT( "A=Y B=X" ) : wxT( "A=X B=Y" );
  565. aList.emplace_back( _( "AB axis" ), msg, DARKRED );
  566. // Display net info, if exists
  567. if( m_netAttributes.m_NetAttribType == GBR_NETLIST_METADATA::GBR_NETINFO_UNSPECIFIED )
  568. return;
  569. // Build full net info:
  570. wxString net_msg;
  571. wxString cmp_pad_msg;
  572. if( ( m_netAttributes.m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_NET ) )
  573. {
  574. net_msg = _( "Net:" );
  575. net_msg << " ";
  576. if( m_netAttributes.m_Netname.IsEmpty() )
  577. net_msg << "<no net>";
  578. else
  579. net_msg << UnescapeString( m_netAttributes.m_Netname );
  580. }
  581. if( ( m_netAttributes.m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_PAD ) )
  582. {
  583. if( m_netAttributes.m_PadPinFunction.IsEmpty() )
  584. cmp_pad_msg.Printf( _( "Cmp: %s Pad: %s" ),
  585. m_netAttributes.m_Cmpref,
  586. m_netAttributes.m_Padname.GetValue() );
  587. else
  588. cmp_pad_msg.Printf( _( "Cmp: %s Pad: %s Fct %s" ),
  589. m_netAttributes.m_Cmpref,
  590. m_netAttributes.m_Padname.GetValue(),
  591. m_netAttributes.m_PadPinFunction.GetValue() );
  592. }
  593. else if( ( m_netAttributes.m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_CMP ) )
  594. {
  595. cmp_pad_msg = _( "Cmp:" );
  596. cmp_pad_msg << " " << m_netAttributes.m_Cmpref;
  597. }
  598. aList.emplace_back( net_msg, cmp_pad_msg, DARKCYAN );
  599. }
  600. BITMAP_DEF GERBER_DRAW_ITEM::GetMenuImage() const
  601. {
  602. if( m_Flashed )
  603. return pad_xpm;
  604. switch( m_Shape )
  605. {
  606. case GBR_SEGMENT:
  607. case GBR_ARC:
  608. case GBR_CIRCLE:
  609. return add_line_xpm;
  610. case GBR_SPOT_OVAL:
  611. case GBR_SPOT_CIRCLE:
  612. case GBR_SPOT_RECT:
  613. case GBR_SPOT_POLY:
  614. case GBR_SPOT_MACRO:
  615. // should be handles by m_Flashed == true
  616. return pad_xpm;
  617. case GBR_POLYGON:
  618. return add_graphical_polygon_xpm;
  619. }
  620. return info_xpm;
  621. }
  622. bool GERBER_DRAW_ITEM::HitTest( const wxPoint& aRefPos, int aAccuracy ) const
  623. {
  624. // In case the item has a very tiny width defined, allow it to be selected
  625. const int MIN_HIT_TEST_RADIUS = Millimeter2iu( 0.01 );
  626. // calculate aRefPos in XY gerber axis:
  627. wxPoint ref_pos = GetXYPosition( aRefPos );
  628. SHAPE_POLY_SET poly;
  629. switch( m_Shape )
  630. {
  631. case GBR_POLYGON:
  632. poly = m_Polygon;
  633. return poly.Contains( VECTOR2I( ref_pos ), 0, aAccuracy );
  634. case GBR_SPOT_POLY:
  635. poly = GetDcodeDescr()->m_Polygon;
  636. poly.Move( VECTOR2I( m_Start ) );
  637. return poly.Contains( VECTOR2I( ref_pos ), 0, aAccuracy );
  638. case GBR_SPOT_RECT:
  639. return GetBoundingBox().Contains( aRefPos );
  640. case GBR_SPOT_OVAL:
  641. {
  642. EDA_RECT bbox = GetBoundingBox();
  643. if( ! bbox.Contains( aRefPos ) )
  644. return false;
  645. // This is similar to a segment with thickness = min( m_Size.x, m_Size.y )
  646. int radius = std::min( m_Size.x, m_Size.y )/2;
  647. wxPoint start, end;
  648. if( m_Size.x > m_Size.y ) // Horizontal oval
  649. {
  650. int len = m_Size.y - m_Size.x;
  651. start.x = -len/2;
  652. end.x = len/2;
  653. }
  654. else // Vertical oval
  655. {
  656. int len = m_Size.x - m_Size.y;
  657. start.y = -len/2;
  658. end.y = len/2;
  659. }
  660. start += bbox.Centre();
  661. end += bbox.Centre();
  662. if( radius < MIN_HIT_TEST_RADIUS )
  663. radius = MIN_HIT_TEST_RADIUS;
  664. return TestSegmentHit( aRefPos, start, end, radius );
  665. }
  666. case GBR_ARC:
  667. {
  668. double radius = GetLineLength( m_Start, m_ArcCentre );
  669. VECTOR2D test_radius = VECTOR2D( ref_pos ) - VECTOR2D( m_ArcCentre );
  670. int size = ( ( m_Size.x < MIN_HIT_TEST_RADIUS ) ? MIN_HIT_TEST_RADIUS
  671. : m_Size.x );
  672. // Are we close enough to the radius?
  673. bool radius_hit = ( std::fabs( test_radius.EuclideanNorm() - radius) < size );
  674. if( radius_hit )
  675. {
  676. // Now check that we are within the arc angle
  677. VECTOR2D start = VECTOR2D( m_Start ) - VECTOR2D( m_ArcCentre );
  678. VECTOR2D end = VECTOR2D( m_End ) - VECTOR2D( m_ArcCentre );
  679. double start_angle = NormalizeAngleRadiansPos( start.Angle() );
  680. double end_angle = NormalizeAngleRadiansPos( end.Angle() );
  681. if( m_Start == m_End )
  682. {
  683. start_angle = 0;
  684. end_angle = 2 * M_PI;
  685. }
  686. else if( end_angle < start_angle )
  687. {
  688. end_angle += 2 * M_PI;
  689. }
  690. double test_angle = NormalizeAngleRadiansPos( test_radius.Angle() );
  691. return ( test_angle > start_angle && test_angle < end_angle );
  692. }
  693. return false;
  694. }
  695. case GBR_SPOT_MACRO:
  696. // Aperture macro polygons are already in absolute coordinates
  697. auto p = GetDcodeDescr()->GetMacro()->GetApertureMacroShape( this, m_Start );
  698. return p->Contains( VECTOR2I( aRefPos ), -1, aAccuracy );
  699. }
  700. // TODO: a better analyze of the shape (perhaps create a D_CODE::HitTest for flashed items)
  701. int radius = std::min( m_Size.x, m_Size.y ) >> 1;
  702. if( radius < MIN_HIT_TEST_RADIUS )
  703. radius = MIN_HIT_TEST_RADIUS;
  704. if( m_Flashed )
  705. return HitTestPoints( m_Start, ref_pos, radius );
  706. else
  707. return TestSegmentHit( ref_pos, m_Start, m_End, radius );
  708. }
  709. bool GERBER_DRAW_ITEM::HitTest( const EDA_RECT& aRefArea, bool aContained, int aAccuracy ) const
  710. {
  711. wxPoint pos = GetABPosition( m_Start );
  712. if( aRefArea.Contains( pos ) )
  713. return true;
  714. pos = GetABPosition( m_End );
  715. if( aRefArea.Contains( pos ) )
  716. return true;
  717. return false;
  718. }
  719. #if defined(DEBUG)
  720. void GERBER_DRAW_ITEM::Show( int nestLevel, std::ostream& os ) const
  721. {
  722. NestedSpace( nestLevel, os ) << '<' << GetClass().Lower().mb_str() <<
  723. " shape=\"" << m_Shape << '"' <<
  724. " addr=\"" << std::hex << this << std::dec << '"' <<
  725. " layer=\"" << GetLayer() << '"' <<
  726. " size=\"" << m_Size << '"' <<
  727. " flags=\"" << m_Flags << '"' <<
  728. " status=\"" << GetStatus() << '"' <<
  729. "<start" << m_Start << "/>" <<
  730. "<end" << m_End << "/>";
  731. os << "</" << GetClass().Lower().mb_str() << ">\n";
  732. }
  733. #endif
  734. void GERBER_DRAW_ITEM::ViewGetLayers( int aLayers[], int& aCount ) const
  735. {
  736. aCount = 2;
  737. aLayers[0] = GERBER_DRAW_LAYER( GetLayer() );
  738. aLayers[1] = GERBER_DCODE_LAYER( aLayers[0] );
  739. }
  740. const BOX2I GERBER_DRAW_ITEM::ViewBBox() const
  741. {
  742. EDA_RECT bbox = GetBoundingBox();
  743. return BOX2I( VECTOR2I( bbox.GetOrigin() ),
  744. VECTOR2I( bbox.GetSize() ) );
  745. }
  746. unsigned int GERBER_DRAW_ITEM::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
  747. {
  748. // DCodes will be shown only if zoom is appropriate:
  749. // Returns the level of detail of the item.
  750. // A level of detail (LOD) is the minimal VIEW scale that
  751. // is sufficient for an item to be shown on a given layer.
  752. if( IsDCodeLayer( aLayer ) )
  753. {
  754. int size = 0;
  755. switch( m_Shape )
  756. {
  757. case GBR_SPOT_MACRO:
  758. size = GetDcodeDescr()->GetMacro()->GetBoundingBox().GetWidth();
  759. break;
  760. case GBR_ARC:
  761. size = GetLineLength( m_Start, m_ArcCentre );
  762. break;
  763. default:
  764. size = m_Size.x;
  765. }
  766. // the level of details is chosen experimentally, to show
  767. // only a readable text:
  768. const int level = Millimeter2iu( 4 );
  769. return ( level / ( size + 1 ) );
  770. }
  771. // Other layers are shown without any conditions
  772. return 0;
  773. }
  774. SEARCH_RESULT GERBER_DRAW_ITEM::Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] )
  775. {
  776. KICAD_T stype = *scanTypes;
  777. // If caller wants to inspect my type
  778. if( stype == Type() )
  779. {
  780. if( SEARCH_RESULT::QUIT == inspector( this, testData ) )
  781. return SEARCH_RESULT::QUIT;
  782. }
  783. return SEARCH_RESULT::CONTINUE;
  784. }
  785. wxString GERBER_DRAW_ITEM::GetSelectMenuText( EDA_UNITS aUnits ) const
  786. {
  787. wxString layerName;
  788. layerName = GERBER_FILE_IMAGE_LIST::GetImagesList().GetDisplayName( GetLayer(), true );
  789. return wxString::Format( _( "%s (D%d) on layer %d: %s" ),
  790. ShowGBRShape(),
  791. m_DCode,
  792. GetLayer() + 1,
  793. layerName );
  794. }