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.

1433 lines
41 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
8 years ago
8 years ago
8 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <bitmaps.h>
  26. #include <geometry/geometry_utils.h>
  27. #include <geometry/shape_null.h>
  28. #include <core/mirror.h>
  29. #include <advanced_config.h>
  30. #include <pcb_edit_frame.h>
  31. #include <pcb_screen.h>
  32. #include <board.h>
  33. #include <board_design_settings.h>
  34. #include <pad.h>
  35. #include <zone.h>
  36. #include <string_utils.h>
  37. #include <math_for_graphics.h>
  38. #include <settings/color_settings.h>
  39. #include <settings/settings_manager.h>
  40. #include <trigo.h>
  41. #include <i18n_utility.h>
  42. ZONE::ZONE( BOARD_ITEM_CONTAINER* aParent, bool aInFP ) :
  43. BOARD_CONNECTED_ITEM( aParent, aInFP ? PCB_FP_ZONE_T : PCB_ZONE_T ),
  44. m_area( 0.0 )
  45. {
  46. m_CornerSelection = nullptr; // no corner is selected
  47. m_isFilled = false; // fill status : true when the zone is filled
  48. m_fillMode = ZONE_FILL_MODE::POLYGONS;
  49. m_borderStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE;
  50. m_borderHatchPitch = GetDefaultHatchPitch();
  51. m_hv45 = false;
  52. m_hatchThickness = 0;
  53. m_hatchGap = 0;
  54. m_hatchOrientation = 0.0;
  55. m_hatchSmoothingLevel = 0; // Grid pattern smoothing type. 0 = no smoothing
  56. m_hatchSmoothingValue = 0.1; // Grid pattern chamfer value relative to the gap value
  57. // used only if m_hatchSmoothingLevel > 0
  58. m_hatchHoleMinArea = 0.3; // Min size before holes are dropped (ratio of hole size)
  59. m_hatchBorderAlgorithm = 1; // 0 = use zone min thickness; 1 = use hatch width
  60. m_priority = 0;
  61. m_cornerSmoothingType = ZONE_SETTINGS::SMOOTHING_NONE;
  62. SetIsRuleArea( aInFP ); // Zones living in footprints have the rule area option
  63. SetDoNotAllowCopperPour( false ); // has meaning only if m_isRuleArea == true
  64. SetDoNotAllowVias( true ); // has meaning only if m_isRuleArea == true
  65. SetDoNotAllowTracks( true ); // has meaning only if m_isRuleArea == true
  66. SetDoNotAllowPads( true ); // has meaning only if m_isRuleArea == true
  67. SetDoNotAllowFootprints( false ); // has meaning only if m_isRuleArea == true
  68. m_cornerRadius = 0;
  69. SetLocalFlags( 0 ); // flags temporary used in zone calculations
  70. m_Poly = new SHAPE_POLY_SET(); // Outlines
  71. m_fillVersion = 5; // set the "old" way to build filled polygon areas (< 6.0.x)
  72. m_islandRemovalMode = ISLAND_REMOVAL_MODE::ALWAYS;
  73. aParent->GetZoneSettings().ExportSetting( *this );
  74. m_needRefill = false; // True only after some edition.
  75. }
  76. ZONE::ZONE( const ZONE& aZone )
  77. : BOARD_CONNECTED_ITEM( aZone ),
  78. m_Poly( nullptr ),
  79. m_CornerSelection( nullptr )
  80. {
  81. InitDataFromSrcInCopyCtor( aZone );
  82. }
  83. ZONE& ZONE::operator=( const ZONE& aOther )
  84. {
  85. BOARD_CONNECTED_ITEM::operator=( aOther );
  86. InitDataFromSrcInCopyCtor( aOther );
  87. return *this;
  88. }
  89. ZONE::~ZONE()
  90. {
  91. delete m_Poly;
  92. delete m_CornerSelection;
  93. }
  94. void ZONE::InitDataFromSrcInCopyCtor( const ZONE& aZone )
  95. {
  96. // members are expected non initialize in this.
  97. // InitDataFromSrcInCopyCtor() is expected to be called
  98. // only from a copy constructor.
  99. // Copy only useful EDA_ITEM flags:
  100. m_flags = aZone.m_flags;
  101. m_forceVisible = aZone.m_forceVisible;
  102. // Replace the outlines for aZone outlines.
  103. delete m_Poly;
  104. m_Poly = new SHAPE_POLY_SET( *aZone.m_Poly );
  105. m_cornerSmoothingType = aZone.m_cornerSmoothingType;
  106. m_cornerRadius = aZone.m_cornerRadius;
  107. m_zoneName = aZone.m_zoneName;
  108. m_priority = aZone.m_priority;
  109. m_isRuleArea = aZone.m_isRuleArea;
  110. SetLayerSet( aZone.GetLayerSet() );
  111. m_doNotAllowCopperPour = aZone.m_doNotAllowCopperPour;
  112. m_doNotAllowVias = aZone.m_doNotAllowVias;
  113. m_doNotAllowTracks = aZone.m_doNotAllowTracks;
  114. m_doNotAllowPads = aZone.m_doNotAllowPads;
  115. m_doNotAllowFootprints = aZone.m_doNotAllowFootprints;
  116. m_PadConnection = aZone.m_PadConnection;
  117. m_ZoneClearance = aZone.m_ZoneClearance; // clearance value
  118. m_ZoneMinThickness = aZone.m_ZoneMinThickness;
  119. m_fillVersion = aZone.m_fillVersion;
  120. m_islandRemovalMode = aZone.m_islandRemovalMode;
  121. m_minIslandArea = aZone.m_minIslandArea;
  122. m_isFilled = aZone.m_isFilled;
  123. m_needRefill = aZone.m_needRefill;
  124. m_thermalReliefGap = aZone.m_thermalReliefGap;
  125. m_thermalReliefSpokeWidth = aZone.m_thermalReliefSpokeWidth;
  126. m_fillMode = aZone.m_fillMode; // solid vs. hatched
  127. m_hatchThickness = aZone.m_hatchThickness;
  128. m_hatchGap = aZone.m_hatchGap;
  129. m_hatchOrientation = aZone.m_hatchOrientation;
  130. m_hatchSmoothingLevel = aZone.m_hatchSmoothingLevel;
  131. m_hatchSmoothingValue = aZone.m_hatchSmoothingValue;
  132. m_hatchBorderAlgorithm = aZone.m_hatchBorderAlgorithm;
  133. m_hatchHoleMinArea = aZone.m_hatchHoleMinArea;
  134. // For corner moving, corner index to drag, or nullptr if no selection
  135. delete m_CornerSelection;
  136. m_CornerSelection = nullptr;
  137. for( PCB_LAYER_ID layer : aZone.GetLayerSet().Seq() )
  138. {
  139. m_FilledPolysList[layer] = aZone.m_FilledPolysList.at( layer );
  140. m_RawPolysList[layer] = aZone.m_RawPolysList.at( layer );
  141. m_filledPolysHash[layer] = aZone.m_filledPolysHash.at( layer );
  142. m_FillSegmList[layer] = aZone.m_FillSegmList.at( layer ); // vector <> copy
  143. m_insulatedIslands[layer] = aZone.m_insulatedIslands.at( layer );
  144. }
  145. m_borderStyle = aZone.m_borderStyle;
  146. m_borderHatchPitch = aZone.m_borderHatchPitch;
  147. m_borderHatchLines = aZone.m_borderHatchLines;
  148. SetLocalFlags( aZone.GetLocalFlags() );
  149. m_netinfo = aZone.m_netinfo;
  150. m_hv45 = aZone.m_hv45;
  151. m_area = aZone.m_area;
  152. }
  153. EDA_ITEM* ZONE::Clone() const
  154. {
  155. return new ZONE( *this );
  156. }
  157. bool ZONE::UnFill()
  158. {
  159. bool change = false;
  160. for( std::pair<const PCB_LAYER_ID, SHAPE_POLY_SET>& pair : m_FilledPolysList )
  161. {
  162. change |= !pair.second.IsEmpty();
  163. m_insulatedIslands[pair.first].clear();
  164. pair.second.RemoveAllContours();
  165. }
  166. for( std::pair<const PCB_LAYER_ID, std::vector<SEG> >& pair : m_FillSegmList )
  167. {
  168. change |= !pair.second.empty();
  169. pair.second.clear();
  170. }
  171. m_isFilled = false;
  172. m_fillFlags.clear();
  173. return change;
  174. }
  175. wxPoint ZONE::GetPosition() const
  176. {
  177. return (wxPoint) GetCornerPosition( 0 );
  178. }
  179. PCB_LAYER_ID ZONE::GetLayer() const
  180. {
  181. return BOARD_ITEM::GetLayer();
  182. }
  183. bool ZONE::IsOnCopperLayer() const
  184. {
  185. return ( m_layerSet & LSET::AllCuMask() ).count() > 0;
  186. }
  187. bool ZONE::CommonLayerExists( const LSET aLayerSet ) const
  188. {
  189. LSET common = GetLayerSet() & aLayerSet;
  190. return common.count() > 0;
  191. }
  192. void ZONE::SetLayer( PCB_LAYER_ID aLayer )
  193. {
  194. SetLayerSet( LSET( aLayer ) );
  195. m_layer = aLayer;
  196. }
  197. void ZONE::SetLayerSet( LSET aLayerSet )
  198. {
  199. if( GetIsRuleArea() )
  200. {
  201. // Rule areas can only exist on copper layers
  202. aLayerSet &= LSET::AllCuMask();
  203. }
  204. if( aLayerSet.count() == 0 )
  205. return;
  206. if( m_layerSet != aLayerSet )
  207. {
  208. SetNeedRefill( true );
  209. UnFill();
  210. m_FillSegmList.clear();
  211. m_FilledPolysList.clear();
  212. m_RawPolysList.clear();
  213. m_filledPolysHash.clear();
  214. m_insulatedIslands.clear();
  215. for( PCB_LAYER_ID layer : aLayerSet.Seq() )
  216. {
  217. m_FillSegmList[layer] = {};
  218. m_FilledPolysList[layer] = {};
  219. m_RawPolysList[layer] = {};
  220. m_filledPolysHash[layer] = {};
  221. m_insulatedIslands[layer] = {};
  222. }
  223. }
  224. m_layerSet = aLayerSet;
  225. // Set the single layer parameter. For zones that can be on many layers, this parameter
  226. // is arbitrary at best, but some code still uses it.
  227. // Priority is F_Cu then B_Cu then to the first selected layer
  228. m_layer = aLayerSet.Seq()[0];
  229. if( m_layer != F_Cu && aLayerSet[B_Cu] )
  230. m_layer = B_Cu;
  231. }
  232. LSET ZONE::GetLayerSet() const
  233. {
  234. return m_layerSet;
  235. }
  236. void ZONE::ViewGetLayers( int aLayers[], int& aCount ) const
  237. {
  238. LSEQ layers = m_layerSet.Seq();
  239. for( unsigned int idx = 0; idx < layers.size(); idx++ )
  240. aLayers[idx] = LAYER_ZONE_START + layers[idx];
  241. aCount = layers.size();
  242. }
  243. double ZONE::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
  244. {
  245. constexpr double HIDE = std::numeric_limits<double>::max();
  246. return aView->IsLayerVisible( LAYER_ZONES ) ? 0.0 : HIDE;
  247. }
  248. bool ZONE::IsOnLayer( PCB_LAYER_ID aLayer ) const
  249. {
  250. return m_layerSet.test( aLayer );
  251. }
  252. const EDA_RECT ZONE::GetBoundingBox() const
  253. {
  254. auto bb = m_Poly->BBox();
  255. EDA_RECT ret( (wxPoint) bb.GetOrigin(), wxSize( bb.GetWidth(), bb.GetHeight() ) );
  256. return ret;
  257. }
  258. int ZONE::GetThermalReliefGap( PAD* aPad, wxString* aSource ) const
  259. {
  260. if( aPad->GetEffectiveThermalGap() == 0 )
  261. {
  262. if( aSource )
  263. *aSource = _( "zone" );
  264. return m_thermalReliefGap;
  265. }
  266. return aPad->GetEffectiveThermalGap( aSource );
  267. }
  268. int ZONE::GetThermalReliefSpokeWidth( PAD* aPad, wxString* aSource ) const
  269. {
  270. if( aPad->GetEffectiveThermalSpokeWidth() == 0 )
  271. {
  272. if( aSource )
  273. *aSource = _( "zone" );
  274. return m_thermalReliefSpokeWidth;
  275. }
  276. return aPad->GetEffectiveThermalSpokeWidth( aSource );
  277. }
  278. void ZONE::SetCornerRadius( unsigned int aRadius )
  279. {
  280. if( m_cornerRadius != aRadius )
  281. SetNeedRefill( true );
  282. m_cornerRadius = aRadius;
  283. }
  284. bool ZONE::GetFilledPolysUseThickness( PCB_LAYER_ID aLayer ) const
  285. {
  286. if( ADVANCED_CFG::GetCfg().m_DebugZoneFiller && LSET::InternalCuMask().Contains( aLayer ) )
  287. return false;
  288. return GetFilledPolysUseThickness();
  289. }
  290. static SHAPE_POLY_SET g_nullPoly;
  291. MD5_HASH ZONE::GetHashValue( PCB_LAYER_ID aLayer )
  292. {
  293. if( !m_filledPolysHash.count( aLayer ) )
  294. return g_nullPoly.GetHash();
  295. else
  296. return m_filledPolysHash.at( aLayer );
  297. }
  298. void ZONE::BuildHashValue( PCB_LAYER_ID aLayer )
  299. {
  300. if( !m_FilledPolysList.count( aLayer ) )
  301. m_filledPolysHash[aLayer] = g_nullPoly.GetHash();
  302. else
  303. m_filledPolysHash[aLayer] = m_FilledPolysList.at( aLayer ).GetHash();
  304. }
  305. bool ZONE::HitTest( const wxPoint& aPosition, int aAccuracy ) const
  306. {
  307. // When looking for an "exact" hit aAccuracy will be 0 which works poorly for very thin
  308. // lines. Give it a floor.
  309. int accuracy = std::max( aAccuracy, Millimeter2iu( 0.1 ) );
  310. return HitTestForCorner( aPosition, accuracy * 2 ) || HitTestForEdge( aPosition, accuracy );
  311. }
  312. void ZONE::SetSelectedCorner( const wxPoint& aPosition, int aAccuracy )
  313. {
  314. SHAPE_POLY_SET::VERTEX_INDEX corner;
  315. // If there is some corner to be selected, assign it to m_CornerSelection
  316. if( HitTestForCorner( aPosition, aAccuracy * 2, corner )
  317. || HitTestForEdge( aPosition, aAccuracy, corner ) )
  318. {
  319. if( m_CornerSelection == nullptr )
  320. m_CornerSelection = new SHAPE_POLY_SET::VERTEX_INDEX;
  321. *m_CornerSelection = corner;
  322. }
  323. }
  324. bool ZONE::HitTestForCorner( const wxPoint& refPos, int aAccuracy,
  325. SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const
  326. {
  327. return m_Poly->CollideVertex( VECTOR2I( refPos ), aCornerHit, aAccuracy );
  328. }
  329. bool ZONE::HitTestForCorner( const wxPoint& refPos, int aAccuracy ) const
  330. {
  331. SHAPE_POLY_SET::VERTEX_INDEX dummy;
  332. return HitTestForCorner( refPos, aAccuracy, dummy );
  333. }
  334. bool ZONE::HitTestForEdge( const wxPoint& refPos, int aAccuracy,
  335. SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const
  336. {
  337. return m_Poly->CollideEdge( VECTOR2I( refPos ), aCornerHit, aAccuracy );
  338. }
  339. bool ZONE::HitTestForEdge( const wxPoint& refPos, int aAccuracy ) const
  340. {
  341. SHAPE_POLY_SET::VERTEX_INDEX dummy;
  342. return HitTestForEdge( refPos, aAccuracy, dummy );
  343. }
  344. bool ZONE::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
  345. {
  346. // Calculate bounding box for zone
  347. EDA_RECT bbox = GetBoundingBox();
  348. bbox.Normalize();
  349. EDA_RECT arect = aRect;
  350. arect.Normalize();
  351. arect.Inflate( aAccuracy );
  352. if( aContained )
  353. {
  354. return arect.Contains( bbox );
  355. }
  356. else
  357. {
  358. // Fast test: if aBox is outside the polygon bounding box, rectangles cannot intersect
  359. if( !arect.Intersects( bbox ) )
  360. return false;
  361. int count = m_Poly->TotalVertices();
  362. for( int ii = 0; ii < count; ii++ )
  363. {
  364. auto vertex = m_Poly->CVertex( ii );
  365. auto vertexNext = m_Poly->CVertex( ( ii + 1 ) % count );
  366. // Test if the point is within the rect
  367. if( arect.Contains( ( wxPoint ) vertex ) )
  368. return true;
  369. // Test if this edge intersects the rect
  370. if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) )
  371. return true;
  372. }
  373. return false;
  374. }
  375. }
  376. int ZONE::GetLocalClearance( wxString* aSource ) const
  377. {
  378. if( m_isRuleArea )
  379. return 0;
  380. if( aSource )
  381. *aSource = _( "zone" );
  382. return m_ZoneClearance;
  383. }
  384. bool ZONE::HitTestFilledArea( PCB_LAYER_ID aLayer, const wxPoint &aRefPos, int aAccuracy ) const
  385. {
  386. // Rule areas have no filled area, but it's generally nice to treat their interior as if it were
  387. // filled so that people don't have to select them by their outline (which is min-width)
  388. if( GetIsRuleArea() )
  389. return m_Poly->Contains( VECTOR2I( aRefPos.x, aRefPos.y ), -1, aAccuracy );
  390. if( !m_FilledPolysList.count( aLayer ) )
  391. return false;
  392. return m_FilledPolysList.at( aLayer ).Contains( VECTOR2I( aRefPos.x, aRefPos.y ), -1,
  393. aAccuracy );
  394. }
  395. bool ZONE::HitTestCutout( const VECTOR2I& aRefPos, int* aOutlineIdx, int* aHoleIdx ) const
  396. {
  397. // Iterate over each outline polygon in the zone and then iterate over
  398. // each hole it has to see if the point is in it.
  399. for( int i = 0; i < m_Poly->OutlineCount(); i++ )
  400. {
  401. for( int j = 0; j < m_Poly->HoleCount( i ); j++ )
  402. {
  403. if( m_Poly->Hole( i, j ).PointInside( aRefPos ) )
  404. {
  405. if( aOutlineIdx )
  406. *aOutlineIdx = i;
  407. if( aHoleIdx )
  408. *aHoleIdx = j;
  409. return true;
  410. }
  411. }
  412. }
  413. return false;
  414. }
  415. void ZONE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  416. {
  417. EDA_UNITS units = aFrame->GetUserUnits();
  418. wxString msg;
  419. if( GetIsRuleArea() )
  420. msg = _( "Rule Area" );
  421. else if( IsOnCopperLayer() )
  422. msg = _( "Copper Zone" );
  423. else
  424. msg = _( "Non-copper Zone" );
  425. // Display Cutout instead of Outline for holes inside a zone (i.e. when num contour !=0).
  426. // Check whether the selected corner is in a hole; i.e., in any contour but the first one.
  427. if( m_CornerSelection != nullptr && m_CornerSelection->m_contour > 0 )
  428. msg << wxT( " " ) << _( "Cutout" );
  429. aList.emplace_back( _( "Type" ), msg );
  430. if( GetIsRuleArea() )
  431. {
  432. msg.Empty();
  433. if( GetDoNotAllowVias() )
  434. AccumulateDescription( msg, _( "No vias" ) );
  435. if( GetDoNotAllowTracks() )
  436. AccumulateDescription( msg, _( "No tracks" ) );
  437. if( GetDoNotAllowPads() )
  438. AccumulateDescription( msg, _( "No pads" ) );
  439. if( GetDoNotAllowCopperPour() )
  440. AccumulateDescription( msg, _( "No copper zones" ) );
  441. if( GetDoNotAllowFootprints() )
  442. AccumulateDescription( msg, _( "No footprints" ) );
  443. if( !msg.IsEmpty() )
  444. aList.emplace_back( MSG_PANEL_ITEM( _( "Restrictions" ), msg ) );
  445. }
  446. else if( IsOnCopperLayer() )
  447. {
  448. aList.emplace_back( _( "Net" ), UnescapeString( GetNetname() ) );
  449. aList.emplace_back( _( "NetClass" ), UnescapeString( GetNetClass()->GetName() ) );
  450. // Display priority level
  451. aList.emplace_back( _( "Priority" ), wxString::Format( "%d", GetPriority() ) );
  452. }
  453. if( IsLocked() )
  454. aList.emplace_back( _( "Status" ), _( "Locked" ) );
  455. wxString layerDesc;
  456. int count = 0;
  457. for( PCB_LAYER_ID layer : m_layerSet.Seq() )
  458. {
  459. if( count == 0 )
  460. layerDesc = GetBoard()->GetLayerName( layer );
  461. count++;
  462. }
  463. if( count > 1 )
  464. layerDesc.Printf( _( "%s and %d more" ), layerDesc, count - 1 );
  465. aList.emplace_back( _( "Layer" ), layerDesc );
  466. if( !m_zoneName.empty() )
  467. aList.emplace_back( _( "Name" ), m_zoneName );
  468. switch( m_fillMode )
  469. {
  470. case ZONE_FILL_MODE::POLYGONS: msg = _( "Solid" ); break;
  471. case ZONE_FILL_MODE::HATCH_PATTERN: msg = _( "Hatched" ); break;
  472. default: msg = _( "Unknown" ); break;
  473. }
  474. aList.emplace_back( _( "Fill Mode" ), msg );
  475. msg = MessageTextFromValue( units, m_area, true, EDA_DATA_TYPE::AREA );
  476. aList.emplace_back( _( "Filled Area" ), msg );
  477. wxString source;
  478. int clearance = GetOwnClearance( GetLayer(), &source );
  479. aList.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
  480. MessageTextFromValue( units, clearance ) ),
  481. wxString::Format( _( "(from %s)" ), source ) );
  482. // Useful for statistics, especially when zones are complex the number of hatches
  483. // and filled polygons can explain the display and DRC calculation time:
  484. msg.Printf( wxT( "%d" ), (int) m_borderHatchLines.size() );
  485. aList.emplace_back( MSG_PANEL_ITEM( _( "HatchBorder Lines" ), msg ) );
  486. PCB_LAYER_ID layer = m_layer;
  487. // NOTE: This brings in dependence on PCB_EDIT_FRAME to the qa tests, which isn't ideal.
  488. // TODO: Figure out a way for items to know the active layer without the whole edit frame?
  489. #if 0
  490. if( PCB_EDIT_FRAME* pcbframe = dynamic_cast<PCB_EDIT_FRAME*>( aFrame ) )
  491. {
  492. if( m_FilledPolysList.count( pcbframe->GetActiveLayer() ) )
  493. layer = pcbframe->GetActiveLayer();
  494. }
  495. #endif
  496. if( !GetIsRuleArea() )
  497. {
  498. auto layer_it = m_FilledPolysList.find( layer );
  499. if( layer_it == m_FilledPolysList.end() )
  500. layer_it = m_FilledPolysList.begin();
  501. if( layer_it != m_FilledPolysList.end() )
  502. {
  503. msg.Printf( wxT( "%d" ), layer_it->second.TotalVertices() );
  504. aList.emplace_back( MSG_PANEL_ITEM( _( "Corner Count" ), msg ) );
  505. }
  506. }
  507. }
  508. void ZONE::Move( const wxPoint& offset )
  509. {
  510. /* move outlines */
  511. m_Poly->Move( offset );
  512. HatchBorder();
  513. for( std::pair<const PCB_LAYER_ID, SHAPE_POLY_SET>& pair : m_FilledPolysList )
  514. pair.second.Move( offset );
  515. for( std::pair<const PCB_LAYER_ID, std::vector<SEG> >& pair : m_FillSegmList )
  516. {
  517. for( SEG& seg : pair.second )
  518. {
  519. seg.A += VECTOR2I( offset );
  520. seg.B += VECTOR2I( offset );
  521. }
  522. }
  523. }
  524. void ZONE::MoveEdge( const wxPoint& offset, int aEdge )
  525. {
  526. int next_corner;
  527. if( m_Poly->GetNeighbourIndexes( aEdge, nullptr, &next_corner ) )
  528. {
  529. m_Poly->SetVertex( aEdge, m_Poly->CVertex( aEdge ) + VECTOR2I( offset ) );
  530. m_Poly->SetVertex( next_corner, m_Poly->CVertex( next_corner ) + VECTOR2I( offset ) );
  531. HatchBorder();
  532. SetNeedRefill( true );
  533. }
  534. }
  535. void ZONE::Rotate( const wxPoint& aCentre, double aAngle )
  536. {
  537. aAngle = -DECIDEG2RAD( aAngle );
  538. m_Poly->Rotate( aAngle, VECTOR2I( aCentre ) );
  539. HatchBorder();
  540. /* rotate filled areas: */
  541. for( std::pair<const PCB_LAYER_ID, SHAPE_POLY_SET>& pair : m_FilledPolysList )
  542. pair.second.Rotate( aAngle, VECTOR2I( aCentre ) );
  543. for( std::pair<const PCB_LAYER_ID, std::vector<SEG> >& pair : m_FillSegmList )
  544. {
  545. for( SEG& seg : pair.second )
  546. {
  547. wxPoint a( seg.A );
  548. RotatePoint( &a, aCentre, aAngle );
  549. seg.A = a;
  550. wxPoint b( seg.B );
  551. RotatePoint( &b, aCentre, aAngle );
  552. seg.B = a;
  553. }
  554. }
  555. }
  556. void ZONE::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
  557. {
  558. Mirror( aCentre, aFlipLeftRight );
  559. int copperLayerCount = GetBoard()->GetCopperLayerCount();
  560. if( GetIsRuleArea() )
  561. SetLayerSet( FlipLayerMask( GetLayerSet(), copperLayerCount ) );
  562. else
  563. SetLayer( FlipLayer( GetLayer(), copperLayerCount ) );
  564. }
  565. void ZONE::Mirror( const wxPoint& aMirrorRef, bool aMirrorLeftRight )
  566. {
  567. // ZONEs mirror about the x-axis (why?!?)
  568. m_Poly->Mirror( aMirrorLeftRight, !aMirrorLeftRight, VECTOR2I( aMirrorRef ) );
  569. HatchBorder();
  570. for( std::pair<const PCB_LAYER_ID, SHAPE_POLY_SET>& pair : m_FilledPolysList )
  571. pair.second.Mirror( aMirrorLeftRight, !aMirrorLeftRight, VECTOR2I( aMirrorRef ) );
  572. for( std::pair<const PCB_LAYER_ID, std::vector<SEG> >& pair : m_FillSegmList )
  573. {
  574. for( SEG& seg : pair.second )
  575. {
  576. if( aMirrorLeftRight )
  577. {
  578. MIRROR( seg.A.x, aMirrorRef.x );
  579. MIRROR( seg.B.x, aMirrorRef.x );
  580. }
  581. else
  582. {
  583. MIRROR( seg.A.y, aMirrorRef.y );
  584. MIRROR( seg.B.y, aMirrorRef.y );
  585. }
  586. }
  587. }
  588. }
  589. ZONE_CONNECTION ZONE::GetPadConnection( PAD* aPad, wxString* aSource ) const
  590. {
  591. if( aPad == nullptr || aPad->GetEffectiveZoneConnection() == ZONE_CONNECTION::INHERITED )
  592. {
  593. if( aSource )
  594. *aSource = _( "zone" );
  595. return m_PadConnection;
  596. }
  597. else
  598. {
  599. return aPad->GetEffectiveZoneConnection( aSource );
  600. }
  601. }
  602. void ZONE::RemoveCutout( int aOutlineIdx, int aHoleIdx )
  603. {
  604. // Ensure the requested cutout is valid
  605. if( m_Poly->OutlineCount() < aOutlineIdx || m_Poly->HoleCount( aOutlineIdx ) < aHoleIdx )
  606. return;
  607. SHAPE_POLY_SET cutPoly( m_Poly->Hole( aOutlineIdx, aHoleIdx ) );
  608. // Add the cutout back to the zone
  609. m_Poly->BooleanAdd( cutPoly, SHAPE_POLY_SET::PM_FAST );
  610. SetNeedRefill( true );
  611. }
  612. void ZONE::AddPolygon( const SHAPE_LINE_CHAIN& aPolygon )
  613. {
  614. wxASSERT( aPolygon.IsClosed() );
  615. // Add the outline as a new polygon in the polygon set
  616. if( m_Poly->OutlineCount() == 0 )
  617. m_Poly->AddOutline( aPolygon );
  618. else
  619. m_Poly->AddHole( aPolygon );
  620. SetNeedRefill( true );
  621. }
  622. void ZONE::AddPolygon( std::vector< wxPoint >& aPolygon )
  623. {
  624. if( aPolygon.empty() )
  625. return;
  626. SHAPE_LINE_CHAIN outline;
  627. // Create an outline and populate it with the points of aPolygon
  628. for( const wxPoint& pt : aPolygon)
  629. outline.Append( pt );
  630. outline.SetClosed( true );
  631. AddPolygon( outline );
  632. }
  633. bool ZONE::AppendCorner( wxPoint aPosition, int aHoleIdx, bool aAllowDuplication )
  634. {
  635. // Ensure the main outline exists:
  636. if( m_Poly->OutlineCount() == 0 )
  637. m_Poly->NewOutline();
  638. // If aHoleIdx >= 0, the corner musty be added to the hole, index aHoleIdx.
  639. // (remember: the index of the first hole is 0)
  640. // Return error if if does dot exist.
  641. if( aHoleIdx >= m_Poly->HoleCount( 0 ) )
  642. return false;
  643. m_Poly->Append( aPosition.x, aPosition.y, -1, aHoleIdx, aAllowDuplication );
  644. SetNeedRefill( true );
  645. return true;
  646. }
  647. wxString ZONE::GetSelectMenuText( EDA_UNITS aUnits ) const
  648. {
  649. wxString layerDesc;
  650. int count = 0;
  651. for( PCB_LAYER_ID layer : m_layerSet.Seq() )
  652. {
  653. if( count == 0 )
  654. layerDesc = GetBoard()->GetLayerName( layer );
  655. count++;
  656. }
  657. if( count > 1 )
  658. layerDesc.Printf( _( "%s and %d more" ), layerDesc, count - 1 );
  659. // Check whether the selected contour is a hole (contour index > 0)
  660. if( m_CornerSelection != nullptr && m_CornerSelection->m_contour > 0 )
  661. {
  662. if( GetIsRuleArea() )
  663. return wxString::Format( _( "Rule Area Cutout on %s" ), layerDesc );
  664. else
  665. return wxString::Format( _( "Zone Cutout on %s" ), layerDesc );
  666. }
  667. else
  668. {
  669. if( GetIsRuleArea() )
  670. return wxString::Format( _( "Rule Area on %s" ), layerDesc );
  671. else
  672. return wxString::Format( _( "Zone %s on %s" ), GetNetnameMsg(), layerDesc );
  673. }
  674. }
  675. int ZONE::GetBorderHatchPitch() const
  676. {
  677. return m_borderHatchPitch;
  678. }
  679. void ZONE::SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE aHatchStyle, int aHatchPitch,
  680. bool aRebuildHatch )
  681. {
  682. SetHatchPitch( aHatchPitch );
  683. m_borderStyle = aHatchStyle;
  684. if( aRebuildHatch )
  685. HatchBorder();
  686. }
  687. void ZONE::SetHatchPitch( int aPitch )
  688. {
  689. m_borderHatchPitch = aPitch;
  690. }
  691. void ZONE::UnHatchBorder()
  692. {
  693. m_borderHatchLines.clear();
  694. }
  695. // Creates hatch lines inside the outline of the complex polygon
  696. // sort function used in ::HatchBorder to sort points by descending wxPoint.x values
  697. bool sortEndsByDescendingX( const VECTOR2I& ref, const VECTOR2I& tst )
  698. {
  699. return tst.x < ref.x;
  700. }
  701. void ZONE::HatchBorder()
  702. {
  703. UnHatchBorder();
  704. if( m_borderStyle == ZONE_BORDER_DISPLAY_STYLE::NO_HATCH
  705. || m_borderHatchPitch == 0
  706. || m_Poly->IsEmpty() )
  707. {
  708. return;
  709. }
  710. // define range for hatch lines
  711. int min_x = m_Poly->CVertex( 0 ).x;
  712. int max_x = m_Poly->CVertex( 0 ).x;
  713. int min_y = m_Poly->CVertex( 0 ).y;
  714. int max_y = m_Poly->CVertex( 0 ).y;
  715. for( auto iterator = m_Poly->IterateWithHoles(); iterator; iterator++ )
  716. {
  717. if( iterator->x < min_x )
  718. min_x = iterator->x;
  719. if( iterator->x > max_x )
  720. max_x = iterator->x;
  721. if( iterator->y < min_y )
  722. min_y = iterator->y;
  723. if( iterator->y > max_y )
  724. max_y = iterator->y;
  725. }
  726. // Calculate spacing between 2 hatch lines
  727. int spacing;
  728. if( m_borderStyle == ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE )
  729. spacing = m_borderHatchPitch;
  730. else
  731. spacing = m_borderHatchPitch * 2;
  732. // set the "length" of hatch lines (the length on horizontal axis)
  733. int hatch_line_len = m_borderHatchPitch;
  734. // To have a better look, give a slope depending on the layer
  735. LAYER_NUM layer = GetLayer();
  736. int slope_flag = (layer & 1) ? 1 : -1; // 1 or -1
  737. double slope = 0.707106 * slope_flag; // 45 degrees slope
  738. int max_a, min_a;
  739. if( slope_flag == 1 )
  740. {
  741. max_a = KiROUND( max_y - slope * min_x );
  742. min_a = KiROUND( min_y - slope * max_x );
  743. }
  744. else
  745. {
  746. max_a = KiROUND( max_y - slope * max_x );
  747. min_a = KiROUND( min_y - slope * min_x );
  748. }
  749. min_a = (min_a / spacing) * spacing;
  750. // calculate an offset depending on layer number,
  751. // for a better look of hatches on a multilayer board
  752. int offset = (layer * 7) / 8;
  753. min_a += offset;
  754. // loop through hatch lines
  755. #define MAXPTS 200 // Usually we store only few values per one hatch line
  756. // depending on the complexity of the zone outline
  757. static std::vector<VECTOR2I> pointbuffer;
  758. pointbuffer.clear();
  759. pointbuffer.reserve( MAXPTS + 2 );
  760. for( int a = min_a; a < max_a; a += spacing )
  761. {
  762. // get intersection points for this hatch line
  763. // Note: because we should have an even number of intersections with the
  764. // current hatch line and the zone outline (a closed polygon,
  765. // or a set of closed polygons), if an odd count is found
  766. // we skip this line (should not occur)
  767. pointbuffer.clear();
  768. // Iterate through all vertices
  769. for( auto iterator = m_Poly->IterateSegmentsWithHoles(); iterator; iterator++ )
  770. {
  771. double x, y;
  772. bool ok;
  773. SEG segment = *iterator;
  774. ok = FindLineSegmentIntersection( a, slope, segment.A.x, segment.A.y, segment.B.x,
  775. segment.B.y, x, y );
  776. if( ok )
  777. {
  778. VECTOR2I point( KiROUND( x ), KiROUND( y ) );
  779. pointbuffer.push_back( point );
  780. }
  781. if( pointbuffer.size() >= MAXPTS ) // overflow
  782. {
  783. wxASSERT( 0 );
  784. break;
  785. }
  786. }
  787. // ensure we have found an even intersection points count
  788. // because intersections are the ends of segments
  789. // inside the polygon(s) and a segment has 2 ends.
  790. // if not, this is a strange case (a bug ?) so skip this hatch
  791. if( pointbuffer.size() % 2 != 0 )
  792. continue;
  793. // sort points in order of descending x (if more than 2) to
  794. // ensure the starting point and the ending point of the same segment
  795. // are stored one just after the other.
  796. if( pointbuffer.size() > 2 )
  797. sort( pointbuffer.begin(), pointbuffer.end(), sortEndsByDescendingX );
  798. // creates lines or short segments inside the complex polygon
  799. for( unsigned ip = 0; ip < pointbuffer.size(); ip += 2 )
  800. {
  801. int dx = pointbuffer[ip + 1].x - pointbuffer[ip].x;
  802. // Push only one line for diagonal hatch,
  803. // or for small lines < twice the line length
  804. // else push 2 small lines
  805. if( m_borderStyle == ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL
  806. || std::abs( dx ) < 2 * hatch_line_len )
  807. {
  808. m_borderHatchLines.emplace_back( SEG( pointbuffer[ip], pointbuffer[ ip + 1] ) );
  809. }
  810. else
  811. {
  812. double dy = pointbuffer[ip + 1].y - pointbuffer[ip].y;
  813. slope = dy / dx;
  814. if( dx > 0 )
  815. dx = hatch_line_len;
  816. else
  817. dx = -hatch_line_len;
  818. int x1 = KiROUND( pointbuffer[ip].x + dx );
  819. int x2 = KiROUND( pointbuffer[ip + 1].x - dx );
  820. int y1 = KiROUND( pointbuffer[ip].y + dx * slope );
  821. int y2 = KiROUND( pointbuffer[ip + 1].y - dx * slope );
  822. m_borderHatchLines.emplace_back( SEG( pointbuffer[ip].x, pointbuffer[ip].y,
  823. x1, y1 ) );
  824. m_borderHatchLines.emplace_back( SEG( pointbuffer[ip+1].x, pointbuffer[ip+1].y,
  825. x2, y2 ) );
  826. }
  827. }
  828. }
  829. }
  830. int ZONE::GetDefaultHatchPitch()
  831. {
  832. return Mils2iu( 20 );
  833. }
  834. BITMAPS ZONE::GetMenuImage() const
  835. {
  836. return BITMAPS::add_zone;
  837. }
  838. void ZONE::SwapData( BOARD_ITEM* aImage )
  839. {
  840. assert( aImage->Type() == PCB_ZONE_T || aImage->Type() == PCB_FP_ZONE_T );
  841. std::swap( *((ZONE*) this), *((ZONE*) aImage) );
  842. }
  843. void ZONE::CacheTriangulation( PCB_LAYER_ID aLayer )
  844. {
  845. if( aLayer == UNDEFINED_LAYER )
  846. {
  847. for( std::pair<const PCB_LAYER_ID, SHAPE_POLY_SET>& pair : m_FilledPolysList )
  848. pair.second.CacheTriangulation();
  849. }
  850. else
  851. {
  852. if( m_FilledPolysList.count( aLayer ) )
  853. m_FilledPolysList[ aLayer ].CacheTriangulation();
  854. }
  855. }
  856. bool ZONE::IsIsland( PCB_LAYER_ID aLayer, int aPolyIdx ) const
  857. {
  858. if( GetNetCode() < 1 )
  859. return true;
  860. if( !m_insulatedIslands.count( aLayer ) )
  861. return false;
  862. return m_insulatedIslands.at( aLayer ).count( aPolyIdx );
  863. }
  864. void ZONE::GetInteractingZones( PCB_LAYER_ID aLayer, std::vector<ZONE*>* aZones ) const
  865. {
  866. int epsilon = Millimeter2iu( 0.001 );
  867. for( ZONE* candidate : GetBoard()->Zones() )
  868. {
  869. if( candidate == this )
  870. continue;
  871. if( !candidate->GetLayerSet().test( aLayer ) )
  872. continue;
  873. if( candidate->GetIsRuleArea() )
  874. continue;
  875. if( candidate->GetNetCode() != GetNetCode() )
  876. continue;
  877. for( auto iter = m_Poly->CIterate(); iter; iter++ )
  878. {
  879. if( candidate->m_Poly->Collide( iter.Get(), epsilon ) )
  880. {
  881. aZones->push_back( candidate );
  882. break;
  883. }
  884. }
  885. }
  886. }
  887. bool ZONE::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER_ID aLayer,
  888. SHAPE_POLY_SET* aBoardOutline,
  889. SHAPE_POLY_SET* aSmoothedPolyWithApron ) const
  890. {
  891. if( GetNumCorners() <= 2 ) // malformed zone. polygon calculations will not like it ...
  892. return false;
  893. // Processing of arc shapes in zones is not yet supported because Clipper can't do boolean
  894. // operations on them. The poly outline must be flattened first.
  895. SHAPE_POLY_SET flattened = *m_Poly;
  896. flattened.ClearArcs();
  897. if( GetIsRuleArea() )
  898. {
  899. // We like keepouts just the way they are....
  900. aSmoothedPoly = flattened;
  901. return true;
  902. }
  903. const BOARD* board = GetBoard();
  904. int maxError = ARC_HIGH_DEF;
  905. bool keepExternalFillets = false;
  906. if( board )
  907. {
  908. BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
  909. maxError = bds.m_MaxError;
  910. keepExternalFillets = bds.m_ZoneKeepExternalFillets;
  911. }
  912. auto smooth = [&]( SHAPE_POLY_SET& aPoly )
  913. {
  914. switch( m_cornerSmoothingType )
  915. {
  916. case ZONE_SETTINGS::SMOOTHING_CHAMFER:
  917. aPoly = aPoly.Chamfer( (int) m_cornerRadius );
  918. break;
  919. case ZONE_SETTINGS::SMOOTHING_FILLET:
  920. {
  921. aPoly = aPoly.Fillet( (int) m_cornerRadius, maxError );
  922. break;
  923. }
  924. default:
  925. break;
  926. }
  927. };
  928. std::vector<ZONE*> interactingZones;
  929. GetInteractingZones( aLayer, &interactingZones );
  930. SHAPE_POLY_SET* maxExtents = &flattened;
  931. SHAPE_POLY_SET withFillets;
  932. aSmoothedPoly = flattened;
  933. // Should external fillets (that is, those applied to concave corners) be kept? While it
  934. // seems safer to never have copper extend outside the zone outline, 5.1.x and prior did
  935. // indeed fill them so we leave the mode available.
  936. if( keepExternalFillets )
  937. {
  938. withFillets = flattened;
  939. smooth( withFillets );
  940. withFillets.BooleanAdd( flattened, SHAPE_POLY_SET::PM_FAST );
  941. maxExtents = &withFillets;
  942. }
  943. for( ZONE* zone : interactingZones )
  944. {
  945. SHAPE_POLY_SET flattened_outline = *zone->Outline();
  946. flattened_outline.ClearArcs();
  947. aSmoothedPoly.BooleanAdd( flattened_outline, SHAPE_POLY_SET::PM_FAST );
  948. }
  949. if( aBoardOutline )
  950. {
  951. SHAPE_POLY_SET poly = *aBoardOutline;
  952. aSmoothedPoly.BooleanIntersection( poly, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  953. }
  954. smooth( aSmoothedPoly );
  955. if( aSmoothedPolyWithApron )
  956. {
  957. SHAPE_POLY_SET poly = *maxExtents;
  958. poly.Inflate( m_ZoneMinThickness, 64 );
  959. *aSmoothedPolyWithApron = aSmoothedPoly;
  960. aSmoothedPolyWithApron->BooleanIntersection( poly, SHAPE_POLY_SET::PM_FAST );
  961. }
  962. aSmoothedPoly.BooleanIntersection( *maxExtents, SHAPE_POLY_SET::PM_FAST );
  963. return true;
  964. }
  965. double ZONE::CalculateFilledArea()
  966. {
  967. m_area = 0.0;
  968. // Iterate over each outline polygon in the zone and then iterate over
  969. // each hole it has to compute the total area.
  970. for( std::pair<const PCB_LAYER_ID, SHAPE_POLY_SET>& pair : m_FilledPolysList )
  971. {
  972. SHAPE_POLY_SET& poly = pair.second;
  973. for( int i = 0; i < poly.OutlineCount(); i++ )
  974. {
  975. m_area += poly.Outline( i ).Area();
  976. for( int j = 0; j < poly.HoleCount( i ); j++ )
  977. m_area -= poly.Hole( i, j ).Area();
  978. }
  979. }
  980. return m_area;
  981. }
  982. void ZONE::TransformSmoothedOutlineToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aClearance,
  983. SHAPE_POLY_SET* aBoardOutline ) const
  984. {
  985. // Creates the zone outline polygon (with holes if any)
  986. SHAPE_POLY_SET polybuffer;
  987. BuildSmoothedPoly( polybuffer, GetLayer(), aBoardOutline );
  988. // Calculate the polygon with clearance
  989. // holes are linked to the main outline, so only one polygon is created.
  990. if( aClearance )
  991. {
  992. const BOARD* board = GetBoard();
  993. int maxError = ARC_HIGH_DEF;
  994. if( board )
  995. maxError = board->GetDesignSettings().m_MaxError;
  996. int segCount = GetArcToSegmentCount( aClearance, maxError, 360.0 );
  997. polybuffer.Inflate( aClearance, segCount );
  998. }
  999. polybuffer.Fracture( SHAPE_POLY_SET::PM_FAST );
  1000. aCornerBuffer.Append( polybuffer );
  1001. }
  1002. bool ZONE::IsKeepout() const
  1003. {
  1004. return m_doNotAllowCopperPour || m_doNotAllowVias || m_doNotAllowTracks || m_doNotAllowPads ||
  1005. m_doNotAllowFootprints;
  1006. }
  1007. bool ZONE::KeepoutAll() const
  1008. {
  1009. return m_doNotAllowCopperPour && m_doNotAllowVias && m_doNotAllowTracks && m_doNotAllowPads &&
  1010. m_doNotAllowFootprints;
  1011. }
  1012. FP_ZONE::FP_ZONE( BOARD_ITEM_CONTAINER* aParent ) :
  1013. ZONE( aParent, true )
  1014. {
  1015. // in a footprint, net classes are not managed.
  1016. // so set the net to NETINFO_LIST::ORPHANED_ITEM
  1017. SetNetCode( -1, true );
  1018. }
  1019. FP_ZONE::FP_ZONE( const FP_ZONE& aZone ) :
  1020. ZONE( aZone.GetParent(), true )
  1021. {
  1022. InitDataFromSrcInCopyCtor( aZone );
  1023. }
  1024. FP_ZONE& FP_ZONE::operator=( const FP_ZONE& aOther )
  1025. {
  1026. ZONE::operator=( aOther );
  1027. return *this;
  1028. }
  1029. EDA_ITEM* FP_ZONE::Clone() const
  1030. {
  1031. return new FP_ZONE( *this );
  1032. }
  1033. double FP_ZONE::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
  1034. {
  1035. constexpr double HIDE = (double)std::numeric_limits<double>::max();
  1036. if( !aView )
  1037. return 0;
  1038. if( !aView->IsLayerVisible( LAYER_ZONES ) )
  1039. return HIDE;
  1040. bool flipped = GetParent() && GetParent()->GetLayer() == B_Cu;
  1041. // Handle Render tab switches
  1042. if( !flipped && !aView->IsLayerVisible( LAYER_MOD_FR ) )
  1043. return HIDE;
  1044. if( flipped && !aView->IsLayerVisible( LAYER_MOD_BK ) )
  1045. return HIDE;
  1046. // Other layers are shown without any conditions
  1047. return 0.0;
  1048. }
  1049. std::shared_ptr<SHAPE> ZONE::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
  1050. {
  1051. std::shared_ptr<SHAPE> shape;
  1052. if( m_FilledPolysList.find( aLayer ) == m_FilledPolysList.end() )
  1053. {
  1054. shape = std::make_shared<SHAPE_NULL>();
  1055. }
  1056. else
  1057. {
  1058. shape.reset( m_FilledPolysList.at( aLayer ).Clone() );
  1059. }
  1060. return shape;
  1061. }
  1062. static struct ZONE_DESC
  1063. {
  1064. ZONE_DESC()
  1065. {
  1066. ENUM_MAP<ZONE_CONNECTION>::Instance()
  1067. .Map( ZONE_CONNECTION::INHERITED, _HKI( "Inherited" ) )
  1068. .Map( ZONE_CONNECTION::NONE, _HKI( "None" ) )
  1069. .Map( ZONE_CONNECTION::THERMAL, _HKI( "Thermal reliefs" ) )
  1070. .Map( ZONE_CONNECTION::FULL, _HKI( "Solid" ) )
  1071. .Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Reliefs for PTH" ) );
  1072. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1073. REGISTER_TYPE( ZONE );
  1074. propMgr.InheritsAfter( TYPE_HASH( ZONE ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
  1075. propMgr.AddProperty( new PROPERTY<ZONE, unsigned>( _HKI( "Priority" ),
  1076. &ZONE::SetPriority, &ZONE::GetPriority ) );
  1077. //propMgr.AddProperty( new PROPERTY<ZONE, bool>( "Filled",
  1078. //&ZONE::SetIsFilled, &ZONE::IsFilled ) );
  1079. propMgr.AddProperty( new PROPERTY<ZONE, wxString>( _HKI( "Name" ),
  1080. &ZONE::SetZoneName, &ZONE::GetZoneName ) );
  1081. propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Clearance Override" ),
  1082. &ZONE::SetLocalClearance, &ZONE::GetLocalClearance,
  1083. PROPERTY_DISPLAY::DISTANCE ) );
  1084. propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Min Width" ),
  1085. &ZONE::SetMinThickness, &ZONE::GetMinThickness,
  1086. PROPERTY_DISPLAY::DISTANCE ) );
  1087. propMgr.AddProperty( new PROPERTY_ENUM<ZONE, ZONE_CONNECTION>( _HKI( "Pad Connections" ),
  1088. &ZONE::SetPadConnection, &ZONE::GetPadConnection ) );
  1089. propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Thermal Relief Gap" ),
  1090. &ZONE::SetThermalReliefGap, &ZONE::GetThermalReliefGap,
  1091. PROPERTY_DISPLAY::DISTANCE ) );
  1092. propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Thermal Relief Width" ),
  1093. &ZONE::SetThermalReliefSpokeWidth, &ZONE::GetThermalReliefSpokeWidth,
  1094. PROPERTY_DISPLAY::DISTANCE ) );
  1095. }
  1096. } _ZONE_DESC;
  1097. ENUM_TO_WXANY( ZONE_CONNECTION );