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.

1026 lines
29 KiB

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