void PCB_EDIT_FRAME::duplicateZone( wxDC* aDC, ZONE_CONTAINER* aZone ) { ZONE_CONTAINER* newZone = new ZONE_CONTAINER( GetBoard() ); newZone->Copy( aZone ); newZone->UnFill(); ZONE_SETTINGS zoneSettings; zoneSettings << *aZone; bool success; if( aZone->GetIsKeepout() ) success = InvokeKeepoutAreaEditor( this, &zoneSettings ); else if( aZone->IsOnCopperLayer() ) success = InvokeCopperZonesEditor( this, &zoneSettings ); else success = InvokeNonCopperZonesEditor( this, aZone, &zoneSettings ); if( success ) { zoneSettings.ExportSetting( *newZone ); newZone->m_Poly->Hatch(); s_AuxiliaryList.ClearListAndDeleteItems(); s_PickedList.ClearListAndDeleteItems(); SaveCopyOfZones( s_PickedList, GetBoard(), newZone->GetNet(), newZone->GetLayer() ); GetBoard()->Add( newZone ); ITEM_PICKER picker( newZone, UR_NEW ); s_PickedList.PushItem( picker ); GetScreen()->SetCurItem( NULL ); // This outline may be deleted when merging outlines // Combine zones if possible GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, newZone ); // Redraw zones GetBoard()->RedrawAreasOutlines( m_canvas, aDC, GR_OR, newZone->GetLayer() ); GetBoard()->RedrawFilledAreas( m_canvas, aDC, GR_OR, newZone->GetLayer() ); if( GetBoard()->GetAreaIndex( newZone ) >= 0 && GetBoard()->Test_Drc_Areas_Outlines_To_Areas_Outlines( newZone, true ) ) { DisplayError( this, _( "Duplicate Zone: The outline of the duplicated zone fails DRC check!" ) ); } UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() ); SaveCopyInUndoList( s_PickedList, UR_UNSPECIFIED ); s_PickedList.ClearItemsList(); OnModify(); } else delete newZone; }
void DRC::testKeepoutAreas() { // Test keepout areas for vias, tracks and pads inside keepout areas for( int ii = 0; ii < m_pcb->GetAreaCount(); ii++ ) { ZONE_CONTAINER* area = m_pcb->GetArea( ii ); if( !area->GetIsKeepout() ) continue; for( TRACK* segm = m_pcb->m_Track; segm != NULL; segm = segm->Next() ) { if( segm->Type() == PCB_TRACE_T ) { if( ! area->GetDoNotAllowTracks() ) continue; if( segm->GetLayer() != area->GetLayer() ) continue; if( area->Outline()->Distance( segm->GetStart(), segm->GetEnd(), segm->GetWidth() ) == 0 ) { m_currentMarker = fillMarker( segm, NULL, DRCE_TRACK_INSIDE_KEEPOUT, m_currentMarker ); m_pcb->Add( m_currentMarker ); m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker ); m_currentMarker = 0; } } else if( segm->Type() == PCB_VIA_T ) { if( ! area->GetDoNotAllowVias() ) continue; if( ! ((VIA*)segm)->IsOnLayer( area->GetLayer() ) ) continue; if( area->Outline()->Distance( segm->GetPosition() ) < segm->GetWidth()/2 ) { m_currentMarker = fillMarker( segm, NULL, DRCE_VIA_INSIDE_KEEPOUT, m_currentMarker ); m_pcb->Add( m_currentMarker ); m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker ); m_currentMarker = 0; } } } // Test pads: TODO } }
void DIALOG_NON_COPPER_ZONES_EDITOR::Init() { BOARD* board = m_parent->GetBoard(); SetReturnCode( ZONE_ABORT ); // Will be changed on button click AddUnitSymbol( *m_MinThicknessValueTitle, g_UserUnit ); wxString msg = StringFromValue( g_UserUnit, m_settings.m_ZoneMinThickness ); m_ZoneMinThicknessCtrl->SetValue( msg ); if( m_settings.m_Zone_45_Only ) m_OrientEdgesOpt->SetSelection( 1 ); switch( m_settings.m_Zone_HatchingStyle ) { case CPolyLine::NO_HATCH: m_OutlineAppearanceCtrl->SetSelection( 0 ); break; case CPolyLine::DIAGONAL_EDGE: m_OutlineAppearanceCtrl->SetSelection( 1 ); break; case CPolyLine::DIAGONAL_FULL: m_OutlineAppearanceCtrl->SetSelection( 2 ); break; } // Create one column in m_LayerSelectionCtrl wxListItem column0; column0.SetId( 0 ); m_LayerSelectionCtrl->InsertColumn( 0, column0 ); // Create an icon list: wxImageList* imageList = new wxImageList( LAYER_BITMAP_SIZE_X, LAYER_BITMAP_SIZE_Y ); m_LayerSelectionCtrl->AssignImageList( imageList, wxIMAGE_LIST_SMALL ); int ii = 0; int lyrSelect = ( (PCB_SCREEN*) m_parent->GetScreen() )->m_Active_Layer; if( m_zone ) lyrSelect = m_zone->GetLayer(); for( LSEQ seq = LSET::AllNonCuMask().Seq(); seq; ++seq, ++ii ) { LAYER_ID layer = *seq; EDA_COLOR_T layerColor = board->GetLayerColor( layer ); imageList->Add( makeLayerBitmap( layerColor ) ); wxString msg = board->GetLayerName( layer ); msg.Trim(); int itemIndex = m_LayerSelectionCtrl->InsertItem( m_LayerSelectionCtrl->GetItemCount(), msg, ii ); if(lyrSelect == layer ) m_LayerSelectionCtrl->Select( itemIndex ); } }
/** * Function SaveCopyOfZones * creates a copy of zones having a given netcode on a given layer, * and fill a pick list with pickers to handle these copies * the UndoRedo status is set to UR_CHANGED for all items in list * Later, UpdateCopyOfZonesList will change and update these pickers after a zone edition * @param aPickList = the pick list * @param aPcb = the Board * @param aNetCode = the reference netcode. if aNetCode < 0 all netcodes are used * @param aLayer = the layer of zones. if aLayer < 0, all layers are used * @return the count of saved copies */ int SaveCopyOfZones( PICKED_ITEMS_LIST& aPickList, BOARD* aPcb, int aNetCode, LAYER_NUM aLayer ) { int copyCount = 0; for( unsigned ii = 0; ; ii++ ) { ZONE_CONTAINER* zone = aPcb->GetArea( ii ); if( zone == NULL ) // End of list break; if( aNetCode >= 0 && aNetCode != zone->GetNetCode() ) continue; if( aLayer >= 0 && aLayer != zone->GetLayer() ) continue; ZONE_CONTAINER* zoneDup = new ZONE_CONTAINER( *zone ); zoneDup->SetParent( aPcb ); ITEM_PICKER picker( zone, UR_CHANGED ); picker.SetLink( zoneDup ); aPickList.PushItem( picker ); copyCount++; } return copyCount; }
/** * Function IsSame * test is 2 zones are equivalent: * 2 zones are equivalent if they have same parameters and same outlines * info relative to filling is not take in account * @param aZoneToCompare = zone to compare with "this" */ bool ZONE_CONTAINER::IsSame( const ZONE_CONTAINER& aZoneToCompare ) { // compare basic parameters: if( GetLayer() != aZoneToCompare.GetLayer() ) return false; if( GetNetCode() != aZoneToCompare.GetNetCode() ) return false; if( GetPriority() != aZoneToCompare.GetPriority() ) return false; // Compare zone specific parameters if( GetIsKeepout() != aZoneToCompare.GetIsKeepout() ) return false; if( GetIsKeepout() ) { if( GetDoNotAllowCopperPour() != aZoneToCompare.GetDoNotAllowCopperPour() ) return false; if( GetDoNotAllowVias() != aZoneToCompare.GetDoNotAllowVias() ) return false; if( GetDoNotAllowTracks() != aZoneToCompare.GetDoNotAllowTracks() ) return false; } if( m_ArcToSegmentsCount != aZoneToCompare.GetArcSegmentCount() ) return false; if( m_ZoneClearance != aZoneToCompare.m_ZoneClearance ) return false; if( m_ZoneMinThickness != aZoneToCompare.GetMinThickness() ) return false; if( m_FillMode != aZoneToCompare.GetFillMode() ) return false; if( m_PadConnection != aZoneToCompare.m_PadConnection ) return false; if( m_ThermalReliefGap != aZoneToCompare.m_ThermalReliefGap ) return false; if( m_ThermalReliefCopperBridge != aZoneToCompare.m_ThermalReliefCopperBridge ) return false; // Compare outlines wxASSERT( m_Poly ); // m_Poly == NULL Should never happen wxASSERT( aZoneToCompare.Outline() ); if( Outline()->m_CornersList.GetList() != aZoneToCompare.Outline()->m_CornersList.GetList() ) // Compare vector return false; return true; }
static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb ) { double scale = aModel.scale; double x, y; for( int ii = 0; ii < aPcb->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = aPcb->GetArea( ii ); VRML_LAYER* vl; if( !GetLayer( aModel, zone->GetLayer(), &vl ) ) continue; if( !zone->IsFilled() ) { zone->SetFillMode( 0 ); // use filled polygons zone->BuildFilledSolidAreasPolygons( aPcb ); } const CPOLYGONS_LIST& poly = zone->GetFilledPolysList(); int nvert = poly.GetCornersCount(); int i = 0; while( i < nvert ) { int seg = vl->NewContour(); bool first = true; if( seg < 0 ) break; while( i < nvert ) { x = poly.GetX(i) * scale; y = -(poly.GetY(i) * scale); if( poly.IsEndContour(i) ) break; if( !vl->AddVertex( seg, x, y ) ) throw( std::runtime_error( vl->GetError() ) ); ++i; } // KiCad ensures that the first polygon is the outline // and all others are holes vl->EnsureWinding( seg, first ? false : true ); if( first ) first = false; ++i; } } }
bool DRC::doTrackKeepoutDrc( TRACK* aRefSeg ) { // Test keepout areas for vias, tracks and pads inside keepout areas for( int ii = 0; ii < m_pcb->GetAreaCount(); ii++ ) { ZONE_CONTAINER* area = m_pcb->GetArea( ii ); if( !area->GetIsKeepout() ) continue; if( aRefSeg->Type() == PCB_TRACE_T ) { if( ! area->GetDoNotAllowTracks() ) continue; if( aRefSeg->GetLayer() != area->GetLayer() ) continue; if( area->Outline()->Distance( aRefSeg->GetStart(), aRefSeg->GetEnd(), aRefSeg->GetWidth() ) == 0 ) { m_currentMarker = fillMarker( aRefSeg, NULL, DRCE_TRACK_INSIDE_KEEPOUT, m_currentMarker ); return false; } } else if( aRefSeg->Type() == PCB_VIA_T ) { if( ! area->GetDoNotAllowVias() ) continue; if( ! ((VIA*)aRefSeg)->IsOnLayer( area->GetLayer() ) ) continue; if( area->Outline()->Distance( aRefSeg->GetPosition() ) < aRefSeg->GetWidth()/2 ) { m_currentMarker = fillMarker( aRefSeg, NULL, DRCE_VIA_INSIDE_KEEPOUT, m_currentMarker ); return false; } } } return true; }
int PCB_EDITOR_CONTROL::ZoneMerge( const TOOL_EVENT& aEvent ) { const SELECTION& selection = m_toolMgr->GetTool<SELECTION_TOOL>()->GetSelection(); BOARD* board = getModel<BOARD>(); BOARD_COMMIT commit( m_frame ); if( selection.Size() < 2 ) return 0; int netcode = -1; ZONE_CONTAINER* firstZone = nullptr; std::vector<ZONE_CONTAINER*> toMerge, merged; for( auto item : selection ) { auto curr_area = dynamic_cast<ZONE_CONTAINER*>( item ); if( !curr_area ) continue; if( !firstZone ) firstZone = curr_area; netcode = curr_area->GetNetCode(); if( firstZone->GetNetCode() != netcode ) continue; if( curr_area->GetPriority() != firstZone->GetPriority() ) continue; if( curr_area->GetIsKeepout() != firstZone->GetIsKeepout() ) continue; if( curr_area->GetLayer() != firstZone->GetLayer() ) continue; if( !board->TestAreaIntersection( curr_area, firstZone ) ) continue; toMerge.push_back( curr_area ); } m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true ); if( mergeZones( commit, toMerge, merged ) ) { commit.Push( _( "Merge zones" ) ); for( auto item : merged ) m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, item ); } return 0; }
void BOARD::ConvertBrdLayerToPolygonalContours( PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aOutlines ) { // convert tracks and vias: for( TRACK* track = m_Track; track != NULL; track = track->Next() ) { if( !track->IsOnLayer( aLayer ) ) continue; track->TransformShapeWithClearanceToPolygon( aOutlines, 0 ); } // convert pads for( MODULE* module = m_Modules; module != NULL; module = module->Next() ) { module->TransformPadsShapesWithClearanceToPolygon( aLayer, aOutlines, 0 ); // Micro-wave modules may have items on copper layers module->TransformGraphicShapesWithClearanceToPolygonSet( aLayer, aOutlines, 0 ); } // convert copper zones for( int ii = 0; ii < GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = GetArea( ii ); PCB_LAYER_ID zonelayer = zone->GetLayer(); if( zonelayer == aLayer ) zone->TransformSolidAreasShapesToPolygonSet( aOutlines ); } // convert graphic items on copper layers (texts) for( BOARD_ITEM* item = m_Drawings; item; item = item->Next() ) { if( !item->IsOnLayer( aLayer ) ) continue; switch( item->Type() ) { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( aOutlines, 0 ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( aOutlines, 0 ); break; default: break; } } }
bool PCB_EDIT_FRAME::End_Zone( wxDC* DC ) { ZONE_CONTAINER* zone = GetBoard()->m_CurrentZoneContour; if( !zone ) return true; // Validate the current outline: if( zone->GetNumCorners() <= 2 ) // An outline must have 3 corners or more { Abort_Zone_Create_Outline( m_canvas, DC ); return true; } // Remove the last corner if is is at the same location as the prevoius corner zone->m_Poly->RemoveNullSegments(); // Validate the current edge: int icorner = zone->GetNumCorners() - 1; if( zone->IsOnCopperLayer() ) { if( Drc_On && m_drc->Drc( zone, icorner - 1 ) == BAD_DRC ) // we can't validate last edge return false; if( Drc_On && m_drc->Drc( zone, icorner ) == BAD_DRC ) // we can't validate the closing edge { DisplayError( this, _( "DRC error: closing this area creates a drc error with an other area" ) ); m_canvas->MoveCursorToCrossHair(); return false; } } zone->ClearFlags(); zone->DrawWhileCreateOutline( m_canvas, DC, GR_XOR ); m_canvas->SetMouseCapture( NULL, NULL ); // Undraw old drawings, because they can have important changes int layer = zone->GetLayer(); GetBoard()->RedrawAreasOutlines( m_canvas, DC, GR_XOR, layer ); GetBoard()->RedrawFilledAreas( m_canvas, DC, GR_XOR, layer ); // Save initial zones configuration, for undo/redo, before adding new zone s_AuxiliaryList.ClearListAndDeleteItems(); s_PickedList.ClearListAndDeleteItems(); SaveCopyOfZones(s_PickedList, GetBoard(), zone->GetNet(), zone->GetLayer() ); // Put new zone in list if( !s_CurrentZone ) { zone->m_Poly->CloseLastContour(); // Close the current corner list GetBoard()->Add( zone ); GetBoard()->m_CurrentZoneContour = NULL; // Add this zone in picked list, as new item ITEM_PICKER picker( zone, UR_NEW ); s_PickedList.PushItem( picker ); } else // Append this outline as a cutout to an existing zone { for( int ii = 0; ii < zone->GetNumCorners(); ii++ ) { s_CurrentZone->AppendCorner( zone->GetCornerPosition( ii ) ); } s_CurrentZone->m_Poly->CloseLastContour(); // Close the current corner list zone->RemoveAllContours(); // All corners are copied in s_CurrentZone. Free corner list. zone = s_CurrentZone; } s_AddCutoutToCurrentZone = false; s_CurrentZone = NULL; GetScreen()->SetCurItem( NULL ); // This outline can be deleted when merging outlines // Combine zones if possible : GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, zone ); // Redraw the real edge zone : GetBoard()->RedrawAreasOutlines( m_canvas, DC, GR_OR, layer ); GetBoard()->RedrawFilledAreas( m_canvas, DC, GR_OR, layer ); int ii = GetBoard()->GetAreaIndex( zone ); // test if zone exists if( ii < 0 ) zone = NULL; // was removed by combining zones int error_count = GetBoard()->Test_Drc_Areas_Outlines_To_Areas_Outlines( zone, true ); if( error_count ) { DisplayError( this, _( "Area: DRC outline error" ) ); } UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() ); SaveCopyInUndoList(s_PickedList, UR_UNSPECIFIED); s_PickedList.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items OnModify(); return true; }
void PCB_EDIT_FRAME::Block_SelectItems() { LSET layerMask; bool selectOnlyComplete = GetScreen()->m_BlockLocate.GetWidth() > 0 ; GetScreen()->m_BlockLocate.Normalize(); PICKED_ITEMS_LIST* itemsList = &GetScreen()->m_BlockLocate.GetItems(); ITEM_PICKER picker( NULL, UR_UNSPECIFIED ); // Add modules if( blockIncludeModules ) { for( MODULE* module = m_Pcb->m_Modules; module; module = module->Next() ) { LAYER_ID layer = module->GetLayer(); if( module->HitTest( GetScreen()->m_BlockLocate, selectOnlyComplete ) && ( !module->IsLocked() || blockIncludeLockedModules ) ) { if( blockIncludeItemsOnInvisibleLayers || m_Pcb->IsModuleLayerVisible( layer ) ) { picker.SetItem ( module ); itemsList->PushItem( picker ); } } } } // Add tracks and vias if( blockIncludeTracks ) { for( TRACK* track = m_Pcb->m_Track; track != NULL; track = track->Next() ) { if( track->HitTest( GetScreen()->m_BlockLocate, selectOnlyComplete ) ) { if( blockIncludeItemsOnInvisibleLayers || m_Pcb->IsLayerVisible( track->GetLayer() ) ) { picker.SetItem( track ); itemsList->PushItem( picker ); } } } } // Add graphic items layerMask = LSET( Edge_Cuts ); if( blockIncludeItemsOnTechLayers ) layerMask.set(); if( !blockIncludeBoardOutlineLayer ) layerMask.set( Edge_Cuts, false ); for( BOARD_ITEM* PtStruct = m_Pcb->m_Drawings; PtStruct != NULL; PtStruct = PtStruct->Next() ) { if( !m_Pcb->IsLayerVisible( PtStruct->GetLayer() ) && ! blockIncludeItemsOnInvisibleLayers) continue; bool select_me = false; switch( PtStruct->Type() ) { case PCB_LINE_T: if( !layerMask[PtStruct->GetLayer()] ) break; if( !PtStruct->HitTest( GetScreen()->m_BlockLocate, selectOnlyComplete ) ) break; select_me = true; // This item is in bloc: select it break; case PCB_TEXT_T: if( !blockIncludePcbTexts ) break; if( !PtStruct->HitTest( GetScreen()->m_BlockLocate, selectOnlyComplete ) ) break; select_me = true; // This item is in bloc: select it break; case PCB_TARGET_T: if( !layerMask[PtStruct->GetLayer()] ) break; if( !PtStruct->HitTest( GetScreen()->m_BlockLocate, selectOnlyComplete ) ) break; select_me = true; // This item is in bloc: select it break; case PCB_DIMENSION_T: if( !layerMask[PtStruct->GetLayer()] ) break; if( !PtStruct->HitTest( GetScreen()->m_BlockLocate, selectOnlyComplete ) ) break; select_me = true; // This item is in bloc: select it break; default: break; } if( select_me ) { picker.SetItem ( PtStruct ); itemsList->PushItem( picker ); } } // Add zones if( blockIncludeZones ) { for( int ii = 0; ii < m_Pcb->GetAreaCount(); ii++ ) { ZONE_CONTAINER* area = m_Pcb->GetArea( ii ); if( area->HitTest( GetScreen()->m_BlockLocate, selectOnlyComplete ) ) { if( blockIncludeItemsOnInvisibleLayers || m_Pcb->IsLayerVisible( area->GetLayer() ) ) { BOARD_ITEM* zone_c = (BOARD_ITEM*) area; picker.SetItem ( zone_c ); itemsList->PushItem( picker ); } } } } }
/* Plot a solder mask layer. * Solder mask layers have a minimum thickness value and cannot be drawn like standard layers, * unless the minimum thickness is 0. * Currently the algo is: * 1 - build all pad shapes as polygons with a size inflated by * mask clearance + (min width solder mask /2) * 2 - Merge shapes * 3 - deflate result by (min width solder mask /2) * 4 - oring result by all pad shapes as polygons with a size inflated by * mask clearance only (because deflate sometimes creates shape artifacts) * 5 - draw result as polygons * * TODO: * make this calculation only for shapes with clearance near than (min width solder mask) * (using DRC algo) * plot all other shapes by flashing the basing shape * (shapes will be better, and calculations faster) */ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt, int aMinThickness ) { LAYER_ID layer = aLayerMask[B_Mask] ? B_Mask : F_Mask; int inflate = aMinThickness/2; BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); itemplotter.SetLayerSet( aLayerMask ); // Plot edge layer and graphic items // They do not have a solder Mask margin, because they are only graphic items // on this layer (like logos), not actually areas around pads. itemplotter.PlotBoardGraphicItems(); for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( layer != item->GetLayer() ) continue; switch( item->Type() ) { case PCB_MODULE_EDGE_T: itemplotter.Plot_1_EdgeModule( (EDGE_MODULE*) item ); break; default: break; } } } // Build polygons for each pad shape. // the size of the shape on solder mask should be: // size of pad + clearance around the pad. // clearance = solder mask clearance + extra margin // extra margin is half the min width for solder mask // This extra margin is used to merge too close shapes // (distance < aMinThickness), and will be removed when creating // the actual shapes SHAPE_POLY_SET areas; // Contains shapes to plot SHAPE_POLY_SET initialPolys; // Contains exact shapes to plot /* calculates the coeff to compensate radius reduction of holes clearance * due to the segment approx ( 1 /cos( PI/circleToSegmentsCount ) */ int circleToSegmentsCount = 32; double correction = 1.0 / cos( M_PI / circleToSegmentsCount ); // Plot pads for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { // add shapes with exact size module->TransformPadsShapesWithClearanceToPolygon( layer, initialPolys, 0, circleToSegmentsCount, correction ); // add shapes inflated by aMinThickness/2 module->TransformPadsShapesWithClearanceToPolygon( layer, areas, inflate, circleToSegmentsCount, correction ); } // Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true, if( aPlotOpt.GetPlotViaOnMaskLayer() ) { // The current layer is a solder mask, // use the global mask clearance for vias int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin; int via_margin = via_clearance + inflate; for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) { const VIA* via = dyn_cast<const VIA*>( track ); if( !via ) continue; // vias are plotted only if they are on the corresponding // external copper layer LSET via_set = via->GetLayerSet(); if( via_set[B_Cu] ) via_set.set( B_Mask ); if( via_set[F_Cu] ) via_set.set( F_Mask ); if( !( via_set & aLayerMask ).any() ) continue; via->TransformShapeWithClearanceToPolygon( areas, via_margin, circleToSegmentsCount, correction ); via->TransformShapeWithClearanceToPolygon( initialPolys, via_clearance, circleToSegmentsCount, correction ); } } // Add filled zone areas. #if 0 // Set to 1 if a solder mask margin must be applied to zones on solder mask int zone_margin = aBoard->GetDesignSettings().m_SolderMaskMargin; #else int zone_margin = 0; #endif for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = aBoard->GetArea( ii ); if( zone->GetLayer() != layer ) continue; zone->TransformOutlinesShapeWithClearanceToPolygon( areas, inflate+zone_margin, false ); zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys, zone_margin, false ); } // To avoid a lot of code, use a ZONE_CONTAINER // to handle and plot polygons, because our polygons look exactly like // filled areas in zones // Note, also this code is not optimized: it creates a lot of copy/duplicate data // However it is not complex, and fast enough for plot purposes (copy/convert data // is only a very small calculation time for these calculations) ZONE_CONTAINER zone( aBoard ); zone.SetArcSegmentCount( 32 ); zone.SetMinThickness( 0 ); // trace polygons only zone.SetLayer ( layer ); areas.BooleanAdd( initialPolys ); areas.Inflate( -inflate, circleToSegmentsCount ); // Combine the current areas to initial areas. This is mandatory because // inflate/deflate transform is not perfect, and we want the initial areas perfectly kept areas.BooleanAdd( initialPolys ); areas.Fracture(); zone.AddFilledPolysList( areas ); itemplotter.PlotFilledAreas( &zone ); }
/** * Function AddClearanceAreasPolygonsToPolysList * Supports a min thickness area constraint. * Add non copper areas polygons (pads and tracks with clearance) * to the filled copper area found * in BuildFilledPolysListData after calculating filled areas in a zone * Non filled copper areas are pads and track and their clearance areas * The filled copper area must be computed just before. * BuildFilledPolysListData() call this function just after creating the * filled copper area polygon (without clearance areas) * to do that this function: * 1 - Creates the main outline (zone outline) using a correction to shrink the resulting area * with m_ZoneMinThickness/2 value. * The result is areas with a margin of m_ZoneMinThickness/2 * When drawing outline with segments having a thickness of m_ZoneMinThickness, the * outlines will match exactly the initial outlines * 3 - Add all non filled areas (pads, tracks) in group B with a clearance of m_Clearance + * m_ZoneMinThickness/2 * in a buffer * - If Thermal shapes are wanted, add non filled area, in order to create these thermal shapes * 4 - calculates the polygon A - B * 5 - put resulting list of polygons (filled areas) in m_FilledPolysList * This zone contains pads with the same net. * 6 - Remove insulated copper islands * 7 - If Thermal shapes are wanted, remove unconnected stubs in thermal shapes: * creates a buffer of polygons corresponding to stubs to remove * sub them to the filled areas. * Remove new insulated copper islands */ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList( BOARD* aPcb ) { // Set the number of segments in arc approximations if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF ) s_CircleToSegmentsCount = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF; else s_CircleToSegmentsCount = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF; /* calculates the coeff to compensate radius reduction of holes clearance * due to the segment approx. * For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2) * s_Correction is 1 /cos( PI/s_CircleToSegmentsCount ) */ s_Correction = 1.0 / cos( M_PI / s_CircleToSegmentsCount ); // This KI_POLYGON_SET is the area(s) to fill, with m_ZoneMinThickness/2 KI_POLYGON_SET polyset_zone_solid_areas; int margin = m_ZoneMinThickness / 2; /* First, creates the main polygon (i.e. the filled area using only one outline) * to reserve a m_ZoneMinThickness/2 margin around the outlines and holes * this margin is the room to redraw outlines with segments having a width set to * m_ZoneMinThickness * so m_ZoneMinThickness is the min thickness of the filled zones areas * the main polygon is stored in polyset_zone_solid_areas */ CopyPolygonsFromFilledPolysListToKiPolygonList( polyset_zone_solid_areas ); polyset_zone_solid_areas -= margin; if( polyset_zone_solid_areas.size() == 0 ) return; /* Calculates the clearance value that meet DRC requirements * from m_ZoneClearance and clearance from the corresponding netclass * We have a "local" clearance in zones because most of time * clearance between a zone and others items is bigger than the netclass clearance * this is more true for small clearance values * Note also the "local" clearance is used for clearance between non copper items * or items like texts on copper layers */ int zone_clearance = std::max( m_ZoneClearance, GetClearance() ); zone_clearance += margin; /* store holes (i.e. tracks and pads areas as polygons outlines) * in a polygon list */ /* items ouside the zone bounding box are skipped * the bounding box is the zone bounding box + the biggest clearance found in Netclass list */ EDA_RECT item_boundingbox; EDA_RECT zone_boundingbox = GetBoundingBox(); int biggest_clearance = aPcb->GetDesignSettings().GetBiggestClearanceValue(); biggest_clearance = std::max( biggest_clearance, zone_clearance ); zone_boundingbox.Inflate( biggest_clearance ); /* * First : Add pads. Note: pads having the same net as zone are left in zone. * Thermal shapes will be created later if necessary */ int item_clearance; // static to avoid unnecessary memory allocation when filling many zones. static CPOLYGONS_LIST cornerBufferPolysToSubstract; cornerBufferPolysToSubstract.RemoveAllContours(); /* Use a dummy pad to calculate hole clerance when a pad is not on all copper layers * and this pad has a hole * This dummy pad has the size and shape of the hole * Therefore, this dummy pad is a circle or an oval. * A pad must have a parent because some functions expect a non null parent * to find the parent board, and some other data */ MODULE dummymodule( aPcb ); // Creates a dummy parent D_PAD dummypad( &dummymodule ); for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { D_PAD* nextpad; for( D_PAD* pad = module->Pads(); pad != NULL; pad = nextpad ) { nextpad = pad->Next(); // pad pointer can be modified by next code, so // calculate the next pad here if( !pad->IsOnLayer( GetLayer() ) ) { /* Test for pads that are on top or bottom only and have a hole. * There are curious pads but they can be used for some components that are * inside the board (in fact inside the hole. Some photo diodes and Leds are * like this) */ if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 ) continue; // Use a dummy pad to calculate a hole shape that have the same dimension as // the pad hole dummypad.SetSize( pad->GetDrillSize() ); dummypad.SetOrientation( pad->GetOrientation() ); dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_OBLONG ? PAD_OVAL : PAD_CIRCLE ); dummypad.SetPosition( pad->GetPosition() ); pad = &dummypad; } // Note: netcode <=0 means not connected item if( ( pad->GetNetCode() != GetNetCode() ) || ( pad->GetNetCode() <= 0 ) ) { item_clearance = pad->GetClearance() + margin; item_boundingbox = pad->GetBoundingBox(); item_boundingbox.Inflate( item_clearance ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { int clearance = std::max( zone_clearance, item_clearance ); pad->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, clearance, s_CircleToSegmentsCount, s_Correction ); } continue; } if( ( GetPadConnection( pad ) == PAD_NOT_IN_ZONE ) || ( pad->GetShape() == PAD_TRAPEZOID ) ) // PAD_TRAPEZOID shapes are not in zones because they are used in microwave apps // and i think it is good that shapes are not changed by thermal pads or others { int gap = zone_clearance; int thermalGap = GetThermalReliefGap( pad ); gap = std::max( gap, thermalGap ); item_boundingbox = pad->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { pad->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, gap, s_CircleToSegmentsCount, s_Correction ); } } } } /* Add holes (i.e. tracks and vias areas as polygons outlines) * in cornerBufferPolysToSubstract */ for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) { if( !track->IsOnLayer( GetLayer() ) ) continue; if( track->GetNetCode() == GetNetCode() && (GetNetCode() != 0) ) continue; item_clearance = track->GetClearance() + margin; item_boundingbox = track->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { int clearance = std::max( zone_clearance, item_clearance ); track->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, clearance, s_CircleToSegmentsCount, s_Correction ); } } /* Add module edge items that are on copper layers * Pcbnew allows these items to be on copper layers in microwave applictions * This is a bad thing, but must be handled here, until a better way is found */ for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( !item->IsOnLayer( GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) ) continue; if( item->Type() != PCB_MODULE_EDGE_T ) continue; item_boundingbox = item->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { ( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, zone_clearance, s_CircleToSegmentsCount, s_Correction ); } } } // Add graphic items (copper texts) and board edges for( BOARD_ITEM* item = aPcb->m_Drawings; item; item = item->Next() ) { if( item->GetLayer() != GetLayer() && item->GetLayer() != Edge_Cuts ) continue; switch( item->Type() ) { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, zone_clearance, s_CircleToSegmentsCount, s_Correction ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon( cornerBufferPolysToSubstract, zone_clearance ); break; default: break; } } // Add zones outlines having an higher priority and keepout for( int ii = 0; ii < GetBoard()->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = GetBoard()->GetArea( ii ); if( zone->GetLayer() != GetLayer() ) continue; if( !zone->GetIsKeepout() && zone->GetPriority() <= GetPriority() ) continue; if( zone->GetIsKeepout() && ! zone->GetDoNotAllowCopperPour() ) continue; // A highter priority zone or keepout area is found: remove its area item_boundingbox = zone->GetBoundingBox(); if( !item_boundingbox.Intersects( zone_boundingbox ) ) continue; // Add the zone outline area. // However if the zone has the same net as the current zone, // do not add clearance. // the zone will be connected to the current zone, but filled areas // will use different parameters (clearance, thermal shapes ) bool addclearance = GetNetCode() != zone->GetNetCode(); int clearance = zone_clearance; if( zone->GetIsKeepout() ) { addclearance = true; clearance = m_ZoneMinThickness / 2; } zone->TransformOutlinesShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, clearance, addclearance ); } // Remove thermal symbols for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad != NULL; pad = pad->Next() ) { // Rejects non-standard pads with tht-only thermal reliefs if( GetPadConnection( pad ) == THT_THERMAL && pad->GetAttribute() != PAD_STANDARD ) continue; if( GetPadConnection( pad ) != THERMAL_PAD && GetPadConnection( pad ) != THT_THERMAL ) continue; if( !pad->IsOnLayer( GetLayer() ) ) continue; if( pad->GetNetCode() != GetNetCode() ) continue; item_boundingbox = pad->GetBoundingBox(); int thermalGap = GetThermalReliefGap( pad ); item_boundingbox.Inflate( thermalGap, thermalGap ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { CreateThermalReliefPadPolygon( cornerBufferPolysToSubstract, *pad, thermalGap, GetThermalReliefCopperBridge( pad ), m_ZoneMinThickness, s_CircleToSegmentsCount, s_Correction, s_thermalRot ); } } } // cornerBufferPolysToSubstract contains polygons to substract. // polyset_zone_solid_areas contains the main filled area // Calculate now actual solid areas if( cornerBufferPolysToSubstract.GetCornersCount() > 0 ) { KI_POLYGON_SET polyset_holes; cornerBufferPolysToSubstract.ExportTo( polyset_holes ); // Remove holes from initial area.: polyset_zone_solid_areas -= polyset_holes; } // put solid areas in m_FilledPolysList: m_FilledPolysList.RemoveAllContours(); CopyPolygonsFromKiPolygonListToFilledPolysList( polyset_zone_solid_areas ); // Remove insulated islands: if( GetNetCode() > 0 ) TestForCopperIslandAndRemoveInsulatedIslands( aPcb ); // Now we remove all unused thermal stubs. cornerBufferPolysToSubstract.RemoveAllContours(); // Test thermal stubs connections and add polygons to remove unconnected stubs. // (this is a refinement for thermal relief shapes) if( GetNetCode() > 0 ) BuildUnconnectedThermalStubsPolygonList( cornerBufferPolysToSubstract, aPcb, this, s_Correction, s_thermalRot ); // remove copper areas corresponding to not connected stubs if( cornerBufferPolysToSubstract.GetCornersCount() ) { KI_POLYGON_SET polyset_holes; cornerBufferPolysToSubstract.ExportTo( polyset_holes ); // Remove unconnected stubs polyset_zone_solid_areas -= polyset_holes; // put these areas in m_FilledPolysList m_FilledPolysList.RemoveAllContours(); CopyPolygonsFromKiPolygonListToFilledPolysList( polyset_zone_solid_areas ); if( GetNetCode() > 0 ) TestForCopperIslandAndRemoveInsulatedIslands( aPcb ); } cornerBufferPolysToSubstract.RemoveAllContours(); }
/** * Function Test_Connection_To_Copper_Areas * init .m_ZoneSubnet parameter in tracks and pads according to the connections to areas found * @param aNetcode = netcode to analyse. if -1, analyse all nets */ void BOARD::Test_Connections_To_Copper_Areas( int aNetcode ) { // list of pads and tracks candidates on this layer and on this net. // It is static to avoid multiple memory realloc. static std::vector <BOARD_CONNECTED_ITEM*> candidates; // clear .m_ZoneSubnet parameter for pads for( MODULE* module = m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) if( aNetcode < 0 || aNetcode == pad->GetNetCode() ) pad->SetZoneSubNet( 0 ); } // clear .m_ZoneSubnet parameter for tracks and vias for( TRACK* track = m_Track; track; track = track->Next() ) { if( aNetcode < 0 || aNetcode == track->GetNetCode() ) track->SetZoneSubNet( 0 ); } // examine all zones, net by net: int subnet = 0; // Build zones candidates list std::vector<ZONE_CONTAINER*> zones_candidates; zones_candidates.reserve( GetAreaCount() ); for( int index = 0; index < GetAreaCount(); index++ ) { ZONE_CONTAINER* zone = GetArea( index ); if( !zone->IsOnCopperLayer() ) continue; if( aNetcode >= 0 && aNetcode != zone->GetNetCode() ) continue; if( zone->GetFilledPolysList().GetCornersCount() == 0 ) continue; zones_candidates.push_back( zone ); } // sort them by netcode then vertices count. // For a given net, examine the smaller zones first slightly speed up calculation // (25% faster) // this is only noticeable with very large boards and depends on board zones topology // This is due to the fact some items are connected by small zones ares, // before examining large zones areas and these items are not tested after a connection is found sort( zones_candidates.begin(), zones_candidates.end(), sort_areas ); int oldnetcode = -1; for( unsigned idx = 0; idx < zones_candidates.size(); idx++ ) { ZONE_CONTAINER* zone = zones_candidates[idx]; int netcode = zone->GetNetCode(); // Build a list of candidates connected to the net: // At this point, layers are not considered, because areas on different layers can // be connected by a via or a pad. // (because zones are sorted by netcode, there is made only once per net) NETINFO_ITEM* net = FindNet( netcode ); wxASSERT( net ); if( net == NULL ) continue; if( oldnetcode != netcode ) { oldnetcode = netcode; candidates.clear(); // Build the list of pads candidates connected to the net: candidates.reserve( net->m_PadInNetList.size() ); for( unsigned ii = 0; ii < net->m_PadInNetList.size(); ii++ ) candidates.push_back( net->m_PadInNetList[ii] ); // Build the list of track candidates connected to the net: TRACK* track = m_Track.GetFirst()->GetStartNetCode( netcode ); for( ; track; track = track->Next() ) { if( track->GetNetCode() != netcode ) break; candidates.push_back( track ); } } // test if a candidate is inside a filled area of this zone unsigned indexstart = 0, indexend; const CPOLYGONS_LIST& polysList = zone->GetFilledPolysList(); for( indexend = 0; indexend < polysList.GetCornersCount(); indexend++ ) { // end of a filled sub-area found if( polysList.IsEndContour( indexend ) ) { subnet++; EDA_RECT bbox = zone->CalculateSubAreaBoundaryBox( indexstart, indexend ); for( unsigned ic = 0; ic < candidates.size(); ic++ ) { // test if this area is connected to a board item: BOARD_CONNECTED_ITEM* item = candidates[ic]; if( item->GetZoneSubNet() == subnet ) // Already merged continue; if( !item->IsOnLayer( zone->GetLayer() ) ) continue; wxPoint pos1, pos2; if( item->Type() == PCB_PAD_T ) { // For pads we use the shape position instead of // the pad position, because the zones are connected // to the center of the shape, not the pad position // (this is important for pads with thermal relief) pos1 = pos2 = ( (D_PAD*) item )->ShapePos(); } else if( item->Type() == PCB_VIA_T ) { const VIA *via = static_cast<const VIA*>( item ); pos1 = via->GetStart(); pos2 = pos1; } else if( item->Type() == PCB_TRACE_T ) { const TRACK *trk = static_cast<const TRACK*>( item ); pos1 = trk->GetStart(); pos2 = trk->GetEnd(); } else { continue; } bool connected = false; if( bbox.Contains( pos1 ) ) { if( TestPointInsidePolygon( polysList, indexstart, indexend, pos1.x, pos1.y ) ) connected = true; } if( !connected && (pos1 != pos2 ) ) { if( bbox.Contains( pos2 ) ) { if( TestPointInsidePolygon( polysList, indexstart, indexend, pos2.x, pos2.y ) ) connected = true; } } if( connected ) { // Set ZoneSubnet to the current subnet value. // If the previous subnet is not 0, merge all items with old subnet // to the new one int old_subnet = item->GetZoneSubNet(); item->SetZoneSubNet( subnet ); // Merge previous subnet with the current if( (old_subnet > 0) && (old_subnet != subnet) ) { for( unsigned jj = 0; jj < candidates.size(); jj++ ) { BOARD_CONNECTED_ITEM* item_to_merge = candidates[jj]; if( old_subnet == item_to_merge->GetZoneSubNet() ) { item_to_merge->SetZoneSubNet( subnet ); } } } // End if ( old_subnet > 0 ) } // End if( connected ) } // End test candidates for the current filled area indexstart = indexend + 1; // prepare test next area, starting at indexend+1 // (if exists). End read one area in // zone->m_FilledPolysList } } // End read all segments in zone } // End read all zones candidates }
/** * Function ConvertBrdLayerToPolygonalContours * Build a set of polygons which are the outlines of copper items * (pads, tracks, texts, zones) * the holes in vias or pads are ignored * Usefull to export the shape of copper layers to dxf polygons * or 3D viewer * the polygons are not merged. * @param aLayer = A layer, like LAYER_N_BACK, etc. * @param aOutlines The CPOLYGONS_LIST to fill in with main outlines. * @return true if success, false if a contour is not valid */ void BOARD::ConvertBrdLayerToPolygonalContours( LAYER_NUM aLayer, CPOLYGONS_LIST& aOutlines ) { // Number of segments to convert a circle to a polygon const int segcountforcircle = 18; double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) ); // convert tracks and vias: for( TRACK* track = m_Track; track != NULL; track = track->Next() ) { if( !track->IsOnLayer( aLayer ) ) continue; track->TransformShapeWithClearanceToPolygon( aOutlines, 0, segcountforcircle, correctionFactor ); } // convert pads for( MODULE* module = m_Modules; module != NULL; module = module->Next() ) { module->TransformPadsShapesWithClearanceToPolygon( aLayer, aOutlines, 0, segcountforcircle, correctionFactor ); // Micro-wave modules may have items on copper layers module->TransformGraphicShapesWithClearanceToPolygonSet( aLayer, aOutlines, 0, segcountforcircle, correctionFactor ); } // convert copper zones for( int ii = 0; ii < GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = GetArea( ii ); LAYER_NUM zonelayer = zone->GetLayer(); if( zonelayer == aLayer ) zone->TransformSolidAreasShapesToPolygonSet( aOutlines, segcountforcircle, correctionFactor ); } // convert graphic items on copper layers (texts) for( BOARD_ITEM* item = m_Drawings; item; item = item->Next() ) { if( !item->IsOnLayer( aLayer ) ) continue; switch( item->Type() ) { case PCB_LINE_T: // should not exist on copper layers ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( aOutlines, 0, segcountforcircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( aOutlines, 0, segcountforcircle, correctionFactor ); break; default: break; } } }
void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, REPORTER* aErrorMessages, REPORTER* aActivity ) { BOARD* pcb = GetBoard(); // If FL_RENDER_SHOW_HOLES_IN_ZONES is true, holes are correctly removed from copper zones areas. // If FL_RENDER_SHOW_HOLES_IN_ZONES is false, holes are not removed from copper zones areas, // but the calculation time is twice shorter. bool remove_Holes = isEnabled( FL_RENDER_SHOW_HOLES_IN_ZONES ); bool realistic_mode = isRealisticMode(); bool useTextures = isRealisticMode() && isEnabled( FL_RENDER_TEXTURES ); // Number of segments to convert a circle to polygon // We use 2 values: the first gives a good shape (for instanes rond pads) // the second is used to speed up calculations, when a poor approximation is acceptable (holes) const int segcountforcircle = 18; double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2.0) ); const int segcountLowQuality = 12; // segments to draw a circle with low quality // to reduce time calculations // for holes and items which do not need // a fine representation double correctionFactorLQ = 1.0 / cos( M_PI / (segcountLowQuality * 2.0) ); SHAPE_POLY_SET bufferPolys; // copper areas: tracks, pads and filled zones areas // when holes are removed from zones SHAPE_POLY_SET bufferPcbOutlines; // stores the board main outlines SHAPE_POLY_SET bufferZonesPolys; // copper filled zones areas // when holes are not removed from zones SHAPE_POLY_SET currLayerHoles; // Contains holes for the current layer SHAPE_POLY_SET allLayerHoles; // Contains holes for all layers // Build a polygon from edge cut items wxString msg; if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines, allLayerHoles, &msg ) ) { if( aErrorMessages ) { msg << wxT("\n") << _("Unable to calculate the board outlines.\n" "Therefore use the board boundary box.") << wxT("\n\n"); aErrorMessages->Report( msg, REPORTER::RPT_WARNING ); } } // Build board holes, with optimization of large holes shape. buildBoardThroughHolesPolygonList( allLayerHoles, segcountLowQuality, true ); LSET cu_set = LSET::AllCuMask( GetPrm3DVisu().m_CopperLayersCount ); glNewList( aBoardList, GL_COMPILE ); for( LSEQ cu = cu_set.CuStack(); cu; ++cu ) { LAYER_ID layer = *cu; // Skip non enabled layers in normal mode, // and internal layers in realistic mode if( !is3DLayerEnabled( layer ) ) continue; if( aActivity ) aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) ); bufferPolys.RemoveAllContours(); bufferZonesPolys.RemoveAllContours(); currLayerHoles.RemoveAllContours(); // Draw track shapes: for( TRACK* track = pcb->m_Track; track; track = track->Next() ) { if( !track->IsOnLayer( layer ) ) continue; track->TransformShapeWithClearanceToPolygon( bufferPolys, 0, segcountforcircle, correctionFactor ); // Add blind/buried via holes if( track->Type() == PCB_VIA_T ) { VIA *via = static_cast<VIA*>( track ); if( via->GetViaType() == VIA_THROUGH ) continue; // already done int holediameter = via->GetDrillValue(); int thickness = GetPrm3DVisu().GetCopperThicknessBIU(); int hole_outer_radius = (holediameter + thickness) / 2; TransformCircleToPolygon( currLayerHoles, via->GetStart(), hole_outer_radius, segcountLowQuality ); } } // draw pad shapes for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) { // Note: NPTH pads are not drawn on copper layers when the pad // has same shape as its hole module->TransformPadsShapesWithClearanceToPolygon( layer, bufferPolys, 0, segcountforcircle, correctionFactor, true ); // Micro-wave modules may have items on copper layers module->TransformGraphicShapesWithClearanceToPolygonSet( layer, bufferPolys, 0, segcountforcircle, correctionFactor ); // pad holes are already in list. } // Draw copper zones. Note: // * if the holes are removed from copper zones // the polygons are stored in bufferPolys (which contains all other polygons) // * if the holes are NOT removed from copper zones // the polygons are stored in bufferZonesPolys if( isEnabled( FL_ZONE ) ) { for( int ii = 0; ii < pcb->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = pcb->GetArea( ii ); LAYER_NUM zonelayer = zone->GetLayer(); if( zonelayer == layer ) { zone->TransformSolidAreasShapesToPolygonSet( remove_Holes ? bufferPolys : bufferZonesPolys, segcountLowQuality, correctionFactorLQ ); } } } // draw graphic items on copper layers (texts) for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() ) { if( !item->IsOnLayer( layer ) ) continue; switch( item->Type() ) { case PCB_LINE_T: // should not exist on copper layers ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( bufferPolys, 0, segcountforcircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( bufferPolys, 0, segcountLowQuality, correctionFactor ); break; default: break; } } // bufferPolys contains polygons to merge. Many overlaps . // Calculate merged polygons if( bufferPolys.IsEmpty() ) continue; // Use Clipper lib to subtract holes to copper areas if( currLayerHoles.OutlineCount() ) { currLayerHoles.Append(allLayerHoles); currLayerHoles.Simplify(); bufferPolys.BooleanSubtract( currLayerHoles ); } else bufferPolys.BooleanSubtract( allLayerHoles ); int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer ); int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer ); float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted) // If we are not using thickness, then the z-normal has to match the layer direction // because just one plane will be drawn if( !thickness ) zNormal = Get3DLayer_Z_Orientation( layer ); if( realistic_mode ) { setGLCopperColor(); } else { EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( layer ); SetGLColor( color ); } // If holes are removed from copper zones, bufferPolys contains all polygons // to draw (tracks+zones+texts). Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos, thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, zNormal ); // If holes are not removed from copper zones (for calculation time reasons, // the zone polygons are stored in bufferZonesPolys and have to be drawn now: if( !bufferZonesPolys.IsEmpty() ) { Draw3D_SolidHorizontalPolyPolygons( bufferZonesPolys, zpos, thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, zNormal ); } } if( aActivity ) aActivity->Report( _( "Build board body" ) ); // Draw plated vertical holes inside the board, but not always. They are drawn: // - if the board body is not shown, to show the holes. // - or if the copper thickness is shown if( !isEnabled( FL_SHOW_BOARD_BODY ) || isEnabled( FL_USE_COPPER_THICKNESS ) ) { // Draw vias holes (vertical cylinders) for( const TRACK* track = pcb->m_Track; track; track = track->Next() ) { if( track->Type() == PCB_VIA_T ) { const VIA *via = static_cast<const VIA*>(track); draw3DViaHole( via ); } } // Draw pads holes (vertical cylinders) for( const MODULE* module = pcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) if( pad->GetAttribute () != PAD_HOLE_NOT_PLATED ) draw3DPadHole( pad ); } } glEndList(); // Build the body board: glNewList( aBodyOnlyList, GL_COMPILE ); if( isRealisticMode() ) { setGLEpoxyColor( 1.00 ); } else { EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( Edge_Cuts ); SetGLColor( color, 0.7 ); } float copper_thickness = GetPrm3DVisu().GetCopperThicknessBIU(); // a small offset between substrate and external copper layer to avoid artifacts // when drawing copper items on board float epsilon = Millimeter2iu( 0.01 ); float zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ); float board_thickness = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu ) - GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ); // items on copper layers and having a thickness = copper_thickness // are drawn from zpos - copper_thickness/2 to zpos + copper_thickness // therefore substrate position is copper_thickness/2 to // substrate_height - copper_thickness/2 zpos += (copper_thickness + epsilon) / 2.0f; board_thickness -= copper_thickness + epsilon; bufferPcbOutlines.BooleanSubtract( allLayerHoles ); if( !bufferPcbOutlines.IsEmpty() ) { Draw3D_SolidHorizontalPolyPolygons( bufferPcbOutlines, zpos + board_thickness / 2.0, board_thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, 1.0f ); } glEndList(); }
void PCB_EDIT_FRAME::Block_SelectItems() { int layerMask; GetScreen()->m_BlockLocate.Normalize(); PICKED_ITEMS_LIST* itemsList = &GetScreen()->m_BlockLocate.m_ItemsSelection; ITEM_PICKER picker( NULL, UR_UNSPECIFIED ); // Add modules if( blockIncludeModules ) { for( MODULE* module = m_Pcb->m_Modules; module != NULL; module = module->Next() ) { int layer = module->GetLayer(); if( module->HitTest( GetScreen()->m_BlockLocate ) && ( !module->IsLocked() || blockIncludeLockedModules ) ) { if( blockIncludeItemsOnInvisibleLayers || m_Pcb->IsModuleLayerVisible( layer ) ) { picker.SetItem ( module ); itemsList->PushItem( picker ); } } } } // Add tracks and vias if( blockIncludeTracks ) { for( TRACK* pt_segm = m_Pcb->m_Track; pt_segm != NULL; pt_segm = pt_segm->Next() ) { if( pt_segm->HitTest( GetScreen()->m_BlockLocate ) ) { if( blockIncludeItemsOnInvisibleLayers || m_Pcb->IsLayerVisible( pt_segm->GetLayer() ) ) { picker.SetItem ( pt_segm ); itemsList->PushItem( picker ); } } } } // Add graphic items layerMask = EDGE_LAYER; if( blockIncludeItemsOnTechLayers ) layerMask = ALL_LAYERS; if( !blockIncludeBoardOutlineLayer ) layerMask &= ~EDGE_LAYER; for( BOARD_ITEM* PtStruct = m_Pcb->m_Drawings; PtStruct != NULL; PtStruct = PtStruct->Next() ) { if( !m_Pcb->IsLayerVisible( PtStruct->GetLayer() ) && ! blockIncludeItemsOnInvisibleLayers) continue; bool select_me = false; switch( PtStruct->Type() ) { case PCB_LINE_T: if( (GetLayerMask( PtStruct->GetLayer() ) & layerMask) == 0 ) break; if( !PtStruct->HitTest( GetScreen()->m_BlockLocate ) ) break; select_me = true; // This item is in bloc: select it break; case PCB_TEXT_T: if( !blockIncludePcbTexts ) break; if( !PtStruct->HitTest( GetScreen()->m_BlockLocate ) ) break; select_me = true; // This item is in bloc: select it break; case PCB_TARGET_T: if( ( GetLayerMask( PtStruct->GetLayer() ) & layerMask ) == 0 ) break; if( !PtStruct->HitTest( GetScreen()->m_BlockLocate ) ) break; select_me = true; // This item is in bloc: select it break; case PCB_DIMENSION_T: if( ( GetLayerMask( PtStruct->GetLayer() ) & layerMask ) == 0 ) break; if( !PtStruct->HitTest( GetScreen()->m_BlockLocate ) ) break; select_me = true; // This item is in bloc: select it break; default: break; } if( select_me ) { picker.SetItem ( PtStruct ); itemsList->PushItem( picker ); } } // Add zones if( blockIncludeZones ) { for( int ii = 0; ii < m_Pcb->GetAreaCount(); ii++ ) { ZONE_CONTAINER* area = m_Pcb->GetArea( ii ); if( area->HitTest( GetScreen()->m_BlockLocate ) ) { if( blockIncludeItemsOnInvisibleLayers || m_Pcb->IsLayerVisible( area->GetLayer() ) ) { BOARD_ITEM* zone_c = (BOARD_ITEM*) area; picker.SetItem ( zone_c ); itemsList->PushItem( picker ); } } } } }
void PCB_EDIT_FRAME::PrintPage( wxDC* aDC, LSET aPrintMask, bool aPrintMirrorMode, void* aData) { const GR_DRAWMODE drawmode = (GR_DRAWMODE) 0; DISPLAY_OPTIONS save_opt; BOARD* Pcb = GetBoard(); int defaultPenSize = Millimeter2iu( 0.2 ); bool onePagePerLayer = false; PRINT_PARAMETERS* printParameters = (PRINT_PARAMETERS*) aData; // can be null DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)GetDisplayOptions(); if( printParameters && printParameters->m_OptionPrintPage == 0 ) onePagePerLayer = true; PRINT_PARAMETERS::DrillShapeOptT drillShapeOpt = PRINT_PARAMETERS::FULL_DRILL_SHAPE; if( printParameters ) { drillShapeOpt = printParameters->m_DrillShapeOpt; defaultPenSize = printParameters->m_PenDefaultSize; } save_opt = *displ_opts; LAYER_ID activeLayer = GetScreen()->m_Active_Layer; displ_opts->m_ContrastModeDisplay = false; displ_opts->m_DisplayPadFill = true; displ_opts->m_DisplayViaFill = true; if( !( aPrintMask & LSET::AllCuMask() ).any() ) { if( onePagePerLayer ) { // We can print mask layers (solder mask and solder paste) with the actual // pad sizes. To do that, we must set ContrastModeDisplay to true and set // the GetScreen()->m_Active_Layer to the current printed layer displ_opts->m_ContrastModeDisplay = true; displ_opts->m_DisplayPadFill = true; // Calculate the active layer number to print from its mask layer: GetScreen()->m_Active_Layer = B_Cu; for( LAYER_NUM id = LAYER_ID_COUNT-1; id >= 0; --id ) { if( aPrintMask[id] ) { GetScreen()->m_Active_Layer = LAYER_ID( id ); break; } } // pads on Silkscreen layer are usually plot in sketch mode: if( GetScreen()->m_Active_Layer == B_SilkS || GetScreen()->m_Active_Layer == F_SilkS ) { displ_opts->m_DisplayPadFill = false; } } else { displ_opts->m_DisplayPadFill = false; } } displ_opts->m_DisplayPadNum = false; bool nctmp = GetBoard()->IsElementVisible( NO_CONNECTS_VISIBLE ); GetBoard()->SetElementVisibility( NO_CONNECTS_VISIBLE, false ); bool anchorsTmp = GetBoard()->IsElementVisible( ANCHOR_VISIBLE ); GetBoard()->SetElementVisibility( ANCHOR_VISIBLE, false ); displ_opts->m_DisplayPadIsol = false; displ_opts->m_DisplayModEdgeFill = FILLED; displ_opts->m_DisplayModTextFill = FILLED; displ_opts->m_DisplayPcbTrackFill = true; displ_opts->m_ShowTrackClearanceMode = DO_NOT_SHOW_CLEARANCE; displ_opts->m_DisplayDrawItemsFill = FILLED; displ_opts->m_DisplayZonesMode = 0; displ_opts->m_DisplayNetNamesMode = 0; m_canvas->SetPrintMirrored( aPrintMirrorMode ); for( BOARD_ITEM* item = Pcb->m_Drawings; item; item = item->Next() ) { switch( item->Type() ) { case PCB_LINE_T: case PCB_DIMENSION_T: case PCB_TEXT_T: case PCB_TARGET_T: if( aPrintMask[item->GetLayer()] ) item->Draw( m_canvas, aDC, drawmode ); break; case PCB_MARKER_T: default: break; } } // Print tracks for( TRACK* track = Pcb->m_Track; track; track = track->Next() ) { if( !( aPrintMask & track->GetLayerSet() ).any() ) continue; if( track->Type() == PCB_VIA_T ) // VIA encountered. { int radius = track->GetWidth() / 2; const VIA* via = static_cast<const VIA*>( track ); EDA_COLOR_T color = g_ColorsSettings.GetItemColor( VIAS_VISIBLE + via->GetViaType() ); GRFilledCircle( m_canvas->GetClipBox(), aDC, via->GetStart().x, via->GetStart().y, radius, 0, color, color ); } else { track->Draw( m_canvas, aDC, drawmode ); } } // Outdated: only for compatibility to old boards for( TRACK* track = Pcb->m_Zone; track; track = track->Next() ) { if( !( aPrintMask & track->GetLayerSet() ).any() ) continue; track->Draw( m_canvas, aDC, drawmode ); } // Draw filled areas (i.e. zones) for( int ii = 0; ii < Pcb->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = Pcb->GetArea( ii ); if( aPrintMask[zone->GetLayer()] ) zone->DrawFilledArea( m_canvas, aDC, drawmode ); } // Draw footprints, this is done at last in order to print the pad holes in // white after the tracks and zones int tmp = D_PAD::m_PadSketchModePenSize; D_PAD::m_PadSketchModePenSize = defaultPenSize; for( MODULE* module = (MODULE*) Pcb->m_Modules; module; module = module->Next() ) { Print_Module( m_canvas, aDC, module, drawmode, aPrintMask, drillShapeOpt ); } D_PAD::m_PadSketchModePenSize = tmp; /* Print via holes in bg color: Not sure it is good for buried or blind * vias */ if( drillShapeOpt != PRINT_PARAMETERS::NO_DRILL_SHAPE ) { TRACK* track = Pcb->m_Track; EDA_COLOR_T color = WHITE; bool blackpenstate = GetGRForceBlackPenState(); GRForceBlackPen( false ); for( ; track; track = track->Next() ) { if( !( aPrintMask & track->GetLayerSet() ).any() ) continue; if( track->Type() == PCB_VIA_T ) // VIA encountered. { int diameter; const VIA *via = static_cast<const VIA*>( track ); if( drillShapeOpt == PRINT_PARAMETERS::SMALL_DRILL_SHAPE ) diameter = std::min( SMALL_DRILL, via->GetDrillValue() ); else diameter = via->GetDrillValue(); GRFilledCircle( m_canvas->GetClipBox(), aDC, track->GetStart().x, track->GetStart().y, diameter/2, 0, color, color ); } } GRForceBlackPen( blackpenstate ); } m_canvas->SetPrintMirrored( false ); *displ_opts = save_opt; GetScreen()->m_Active_Layer = activeLayer; GetBoard()->SetElementVisibility( NO_CONNECTS_VISIBLE, nctmp ); GetBoard()->SetElementVisibility( ANCHOR_VISIBLE, anchorsTmp ); }
void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeatures ) { int segsPerCircle; double correctionFactor; // Set the number of segments in arc approximations if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF ) segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF; else segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF; /* calculates the coeff to compensate radius reduction of holes clearance * due to the segment approx. * For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2) * s_Correction is 1 /cos( PI/s_CircleToSegmentsCount ) */ correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle ); aFeatures.RemoveAllContours(); int outline_half_thickness = m_ZoneMinThickness / 2; int zone_clearance = std::max( m_ZoneClearance, GetClearance() ); zone_clearance += outline_half_thickness; /* store holes (i.e. tracks and pads areas as polygons outlines) * in a polygon list */ /* items ouside the zone bounding box are skipped * the bounding box is the zone bounding box + the biggest clearance found in Netclass list */ EDA_RECT item_boundingbox; EDA_RECT zone_boundingbox = GetBoundingBox(); int biggest_clearance = aPcb->GetDesignSettings().GetBiggestClearanceValue(); biggest_clearance = std::max( biggest_clearance, zone_clearance ); zone_boundingbox.Inflate( biggest_clearance ); /* * First : Add pads. Note: pads having the same net as zone are left in zone. * Thermal shapes will be created later if necessary */ int item_clearance; /* Use a dummy pad to calculate hole clerance when a pad is not on all copper layers * and this pad has a hole * This dummy pad has the size and shape of the hole * Therefore, this dummy pad is a circle or an oval. * A pad must have a parent because some functions expect a non null parent * to find the parent board, and some other data */ MODULE dummymodule( aPcb ); // Creates a dummy parent D_PAD dummypad( &dummymodule ); for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { D_PAD* nextpad; for( D_PAD* pad = module->Pads(); pad != NULL; pad = nextpad ) { nextpad = pad->Next(); // pad pointer can be modified by next code, so // calculate the next pad here if( !pad->IsOnLayer( GetLayer() ) ) { /* Test for pads that are on top or bottom only and have a hole. * There are curious pads but they can be used for some components that are * inside the board (in fact inside the hole. Some photo diodes and Leds are * like this) */ if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 ) continue; // Use a dummy pad to calculate a hole shape that have the same dimension as // the pad hole dummypad.SetSize( pad->GetDrillSize() ); dummypad.SetOrientation( pad->GetOrientation() ); dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ? PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE ); dummypad.SetPosition( pad->GetPosition() ); pad = &dummypad; } // Note: netcode <=0 means not connected item if( ( pad->GetNetCode() != GetNetCode() ) || ( pad->GetNetCode() <= 0 ) ) { item_clearance = pad->GetClearance() + outline_half_thickness; item_boundingbox = pad->GetBoundingBox(); item_boundingbox.Inflate( item_clearance ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { int clearance = std::max( zone_clearance, item_clearance ); pad->TransformShapeWithClearanceToPolygon( aFeatures, clearance, segsPerCircle, correctionFactor ); } continue; } // Pads are removed from zone if the setup is PAD_ZONE_CONN_NONE if( GetPadConnection( pad ) == PAD_ZONE_CONN_NONE ) { int gap = zone_clearance; int thermalGap = GetThermalReliefGap( pad ); gap = std::max( gap, thermalGap ); item_boundingbox = pad->GetBoundingBox(); item_boundingbox.Inflate( gap ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { pad->TransformShapeWithClearanceToPolygon( aFeatures, gap, segsPerCircle, correctionFactor ); } } } } /* Add holes (i.e. tracks and vias areas as polygons outlines) * in cornerBufferPolysToSubstract */ for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) { if( !track->IsOnLayer( GetLayer() ) ) continue; if( track->GetNetCode() == GetNetCode() && (GetNetCode() != 0) ) continue; item_clearance = track->GetClearance() + outline_half_thickness; item_boundingbox = track->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { int clearance = std::max( zone_clearance, item_clearance ); track->TransformShapeWithClearanceToPolygon( aFeatures, clearance, segsPerCircle, correctionFactor ); } } /* Add module edge items that are on copper layers * Pcbnew allows these items to be on copper layers in microwave applictions * This is a bad thing, but must be handled here, until a better way is found */ for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( !item->IsOnLayer( GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) ) continue; if( item->Type() != PCB_MODULE_EDGE_T ) continue; item_boundingbox = item->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { ( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon( aFeatures, zone_clearance, segsPerCircle, correctionFactor ); } } } // Add graphic items (copper texts) and board edges for( BOARD_ITEM* item = aPcb->m_Drawings; item; item = item->Next() ) { if( item->GetLayer() != GetLayer() && item->GetLayer() != Edge_Cuts ) continue; switch( item->Type() ) { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( aFeatures, zone_clearance, segsPerCircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon( aFeatures, zone_clearance ); break; default: break; } } // Add zones outlines having an higher priority and keepout for( int ii = 0; ii < GetBoard()->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = GetBoard()->GetArea( ii ); if( zone->GetLayer() != GetLayer() ) continue; if( !zone->GetIsKeepout() && zone->GetPriority() <= GetPriority() ) continue; if( zone->GetIsKeepout() && ! zone->GetDoNotAllowCopperPour() ) continue; // A highter priority zone or keepout area is found: remove this area item_boundingbox = zone->GetBoundingBox(); if( !item_boundingbox.Intersects( zone_boundingbox ) ) continue; // Add the zone outline area. // However if the zone has the same net as the current zone, // do not add any clearance. // the zone will be connected to the current zone, but filled areas // will use different parameters (clearance, thermal shapes ) bool same_net = GetNetCode() == zone->GetNetCode(); bool use_net_clearance = true; int min_clearance = zone_clearance; // Do not forget to make room to draw the thick outlines // of the hole created by the area of the zone to remove int holeclearance = zone->GetClearance() + outline_half_thickness; // The final clearance is obviously the max value of each zone clearance min_clearance = std::max( min_clearance, holeclearance ); if( zone->GetIsKeepout() || same_net ) { // Just take in account the fact the outline has a thickness, so // the actual area to substract is inflated to take in account this fact min_clearance = outline_half_thickness; use_net_clearance = false; } zone->TransformOutlinesShapeWithClearanceToPolygon( aFeatures, min_clearance, use_net_clearance ); } // Remove thermal symbols for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad != NULL; pad = pad->Next() ) { // Rejects non-standard pads with tht-only thermal reliefs if( GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL && pad->GetAttribute() != PAD_ATTRIB_STANDARD ) continue; if( GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL && GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL ) continue; if( !pad->IsOnLayer( GetLayer() ) ) continue; if( pad->GetNetCode() != GetNetCode() ) continue; item_boundingbox = pad->GetBoundingBox(); int thermalGap = GetThermalReliefGap( pad ); item_boundingbox.Inflate( thermalGap, thermalGap ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { CreateThermalReliefPadPolygon( aFeatures, *pad, thermalGap, GetThermalReliefCopperBridge( pad ), m_ZoneMinThickness, segsPerCircle, correctionFactor, s_thermalRot ); } } } }
void DIALOG_NON_COPPER_ZONES_EDITOR::Init() { BOARD* board = m_parent->GetBoard(); SetReturnCode( ZONE_ABORT ); // Will be changed on button click AddUnitSymbol( *m_MinThicknessValueTitle, g_UserUnit ); wxString msg = StringFromValue( g_UserUnit, m_settings.m_ZoneMinThickness ); m_ZoneMinThicknessCtrl->SetValue( msg ); if( m_settings.m_Zone_45_Only ) m_OrientEdgesOpt->SetSelection( 1 ); switch( m_settings.m_Zone_HatchingStyle ) { case CPolyLine::NO_HATCH: m_OutlineAppearanceCtrl->SetSelection( 0 ); break; case CPolyLine::DIAGONAL_EDGE: m_OutlineAppearanceCtrl->SetSelection( 1 ); break; case CPolyLine::DIAGONAL_FULL: m_OutlineAppearanceCtrl->SetSelection( 2 ); break; } // Create one column in m_LayerSelectionCtrl wxListItem column0; column0.SetId( 0 ); m_LayerSelectionCtrl->InsertColumn( 0, column0 ); // Create an icon list: wxImageList* imageList = new wxImageList( LAYER_BITMAP_SIZE_X, LAYER_BITMAP_SIZE_Y ); m_LayerSelectionCtrl->AssignImageList( imageList, wxIMAGE_LIST_SMALL ); int lyrSelect = ( (PCB_SCREEN*) m_parent->GetScreen() )->m_Active_Layer; if( m_zone ) lyrSelect = m_zone->GetLayer(); int ctrlWidth = 0; // Min width for m_LayerSelectionCtrl to show the layers names int imgIdx = 0; for( LSEQ seq = LSET::AllNonCuMask().Seq(); seq; ++seq, ++imgIdx ) { LAYER_ID layer = *seq; EDA_COLOR_T layerColor = board->GetLayerColor( layer ); imageList->Add( makeLayerBitmap( layerColor ) ); wxString msg = board->GetLayerName( layer ); msg.Trim(); int itemIndex = m_LayerSelectionCtrl->InsertItem( m_LayerSelectionCtrl->GetItemCount(), msg, imgIdx ); if(lyrSelect == layer ) m_LayerSelectionCtrl->Select( itemIndex ); wxSize tsize( GetTextSize( msg, m_LayerSelectionCtrl ) ); ctrlWidth = std::max( ctrlWidth, tsize.x ); } // The most easy way to ensure the right size is to use wxLIST_AUTOSIZE // unfortunately this option does not work well both on // wxWidgets 2.8 ( column witdth too small), and // wxWidgets 2.9 ( column witdth too large) ctrlWidth += LAYER_BITMAP_SIZE_X + 25; // Add bitmap width + margin between bitmap and text m_LayerSelectionCtrl->SetColumnWidth( 0, ctrlWidth ); ctrlWidth += 25; // add small margin between text and window borders // and room for vertical scroll bar m_LayerSelectionCtrl->SetMinSize( wxSize( ctrlWidth, -1 ) ); }
int PCB_EDIT_FRAME::Begin_Zone( wxDC* DC ) { ZONE_SETTINGS zoneInfo = GetZoneSettings(); // verify if s_CurrentZone exists (could be deleted since last selection) : int ii; for( ii = 0; ii < GetBoard()->GetAreaCount(); ii++ ) { if( s_CurrentZone == GetBoard()->GetArea( ii ) ) break; } if( ii >= GetBoard()->GetAreaCount() ) // Not found: could be deleted since last selection { s_AddCutoutToCurrentZone = false; s_CurrentZone = NULL; } // If no zone contour in progress, a new zone is being created: if( !GetBoard()->m_CurrentZoneContour ) { if( GetToolId() == ID_PCB_KEEPOUT_AREA_BUTT && getActiveLayer() >= FIRST_NON_COPPER_LAYER ) { DisplayError( this, _( "Error: a keepout area is allowed only on copper layers" ) ); return 0; } else GetBoard()->m_CurrentZoneContour = new ZONE_CONTAINER( GetBoard() ); } ZONE_CONTAINER* zone = GetBoard()->m_CurrentZoneContour; if( zone->GetNumCorners() == 0 ) // Start a new contour: init zone params (net, layer ...) { if( !s_CurrentZone ) // A new outline is created, from scratch { ZONE_EDIT_T edited; // Init zone params to reasonable values zone->SetLayer( getActiveLayer() ); // Prompt user for parameters: m_canvas->SetIgnoreMouseEvents( true ); if( zone->IsOnCopperLayer() ) { // Put a zone on a copper layer if( GetBoard()->GetHighLightNetCode() > 0 ) { zoneInfo.m_NetcodeSelection = GetBoard()->GetHighLightNetCode(); zone->SetNet( zoneInfo.m_NetcodeSelection ); zone->SetNetNameFromNetCode( ); } double tmp = ZONE_THERMAL_RELIEF_GAP_MIL; wxGetApp().GetSettings()->Read( ZONE_THERMAL_RELIEF_GAP_STRING_KEY, &tmp ); zoneInfo.m_ThermalReliefGap = KiROUND( tmp * IU_PER_MILS); tmp = ZONE_THERMAL_RELIEF_COPPER_WIDTH_MIL; wxGetApp().GetSettings()->Read( ZONE_THERMAL_RELIEF_COPPER_WIDTH_STRING_KEY, &tmp ); zoneInfo.m_ThermalReliefCopperBridge = KiROUND( tmp * IU_PER_MILS ); tmp = ZONE_CLEARANCE_MIL; wxGetApp().GetSettings()->Read( ZONE_CLEARANCE_WIDTH_STRING_KEY, &tmp ); zoneInfo.m_ZoneClearance = KiROUND( tmp * IU_PER_MILS ); tmp = ZONE_THICKNESS_MIL; wxGetApp().GetSettings()->Read( ZONE_MIN_THICKNESS_WIDTH_STRING_KEY, &tmp ); zoneInfo.m_ZoneMinThickness = KiROUND( tmp * IU_PER_MILS ); zoneInfo.m_CurrentZone_Layer = zone->GetLayer(); if( GetToolId() == ID_PCB_KEEPOUT_AREA_BUTT ) { zoneInfo.SetIsKeepout( true ); edited = InvokeKeepoutAreaEditor( this, &zoneInfo ); } else { zoneInfo.SetIsKeepout( false ); edited = InvokeCopperZonesEditor( this, &zoneInfo ); } } else // Put a zone on a non copper layer (technical layer) { zoneInfo.SetIsKeepout( false ); zoneInfo.m_NetcodeSelection = 0; // No net for non copper zones edited = InvokeNonCopperZonesEditor( this, zone, &zoneInfo ); } m_canvas->MoveCursorToCrossHair(); m_canvas->SetIgnoreMouseEvents( false ); if( edited == ZONE_ABORT ) return 0; // Switch active layer to the selected zone layer setActiveLayer( zoneInfo.m_CurrentZone_Layer ); SetZoneSettings( zoneInfo ); } else { // Start a new contour: init zone params (net and layer) from an existing // zone (add cutout or similar zone) zoneInfo.m_CurrentZone_Layer = s_CurrentZone->GetLayer(); setActiveLayer( s_CurrentZone->GetLayer() ); zoneInfo << *s_CurrentZone; SetZoneSettings( zoneInfo ); } // Show the Net for zones on copper layers if( zoneInfo.m_CurrentZone_Layer < FIRST_NO_COPPER_LAYER && ! zoneInfo.GetIsKeepout() ) { if( s_CurrentZone ) { zoneInfo.m_NetcodeSelection = s_CurrentZone->GetNet(); GetBoard()->SetZoneSettings( zoneInfo ); } if( GetBoard()->IsHighLightNetON() ) { HighLight( DC ); // Remove old highlight selection } GetBoard()->SetHighLightNet( zoneInfo.m_NetcodeSelection ); HighLight( DC ); } if( !s_AddCutoutToCurrentZone ) s_CurrentZone = NULL; // the zone is used only once ("add similar zone" command) } // if first segment if( zone->GetNumCorners() == 0 ) { zone->SetFlags( IS_NEW ); zone->SetTimeStamp( GetNewTimeStamp() ); zoneInfo.ExportSetting( *zone ); zone->m_Poly->Start( zoneInfo.m_CurrentZone_Layer, GetScreen()->GetCrossHairPosition().x, GetScreen()->GetCrossHairPosition().y, zone->GetHatchStyle() ); zone->AppendCorner( GetScreen()->GetCrossHairPosition() ); if( Drc_On && (m_drc->Drc( zone, 0 ) == BAD_DRC) && zone->IsOnCopperLayer() ) { zone->ClearFlags(); zone->RemoveAllContours(); // use the form of SetCurItem() which does not write to the msg panel, // SCREEN::SetCurItem(), so the DRC error remains on screen. // PCB_EDIT_FRAME::SetCurItem() calls DisplayInfo(). GetScreen()->SetCurItem( NULL ); DisplayError( this, _( "DRC error: this start point is inside or too close an other area" ) ); return 0; } SetCurItem( zone ); m_canvas->SetMouseCapture( Show_New_Edge_While_Move_Mouse, Abort_Zone_Create_Outline ); } else // edge in progress: { ii = zone->GetNumCorners() - 1; // edge in progress : the current corner coordinate was set // by Show_New_Edge_While_Move_Mouse if( zone->GetCornerPosition( ii - 1 ) != zone->GetCornerPosition( ii ) ) { if( !Drc_On || !zone->IsOnCopperLayer() || ( m_drc->Drc( zone, ii - 1 ) == OK_DRC ) ) { // Ok, we can add a new corner if( m_canvas->IsMouseCaptured() ) m_canvas->CallMouseCapture( DC, wxPoint(0,0), false ); zone->AppendCorner( GetScreen()->GetCrossHairPosition() ); SetCurItem( zone ); // calls DisplayInfo(). if( m_canvas->IsMouseCaptured() ) m_canvas->CallMouseCapture( DC, wxPoint(0,0), false ); } } } return zone->GetNumCorners(); }
/* Plot a copper layer or mask. * Silk screen layers are not plotted here. */ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter, LAYER_MSK aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt ) { BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); itemplotter.SetLayerMask( aLayerMask ); EDA_DRAW_MODE_T plotMode = aPlotOpt.GetMode(); // Plot edge layer and graphic items itemplotter.PlotBoardGraphicItems(); // Draw footprint shapes without pads (pads will plotted later) // We plot here module texts, but they are usually on silkscreen layer, // so they are not plot here but plot by PlotSilkScreen() // Plot footprints fields (ref, value ...) for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { if( ! itemplotter.PlotAllTextsModule( module ) ) { wxLogMessage( _( "Your BOARD has a bad layer number for module %s" ), GetChars( module->GetReference() ) ); } } for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( ! (aLayerMask & GetLayerMask( item->GetLayer() ) ) ) continue; switch( item->Type() ) { case PCB_MODULE_EDGE_T: itemplotter.Plot_1_EdgeModule( (EDGE_MODULE*) item ); break; default: break; } } } // Plot footprint pads for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) { if( (pad->GetLayerMask() & aLayerMask) == 0 ) continue; wxSize margin; double width_adj = 0; if( aLayerMask & ALL_CU_LAYERS ) width_adj = itemplotter.getFineWidthAdj(); switch( aLayerMask & ( SOLDERMASK_LAYER_BACK | SOLDERMASK_LAYER_FRONT | SOLDERPASTE_LAYER_BACK | SOLDERPASTE_LAYER_FRONT ) ) { case SOLDERMASK_LAYER_FRONT: case SOLDERMASK_LAYER_BACK: margin.x = margin.y = pad->GetSolderMaskMargin(); break; case SOLDERPASTE_LAYER_FRONT: case SOLDERPASTE_LAYER_BACK: margin = pad->GetSolderPasteMargin(); break; default: break; } wxSize padPlotsSize; padPlotsSize.x = pad->GetSize().x + ( 2 * margin.x ) + width_adj; padPlotsSize.y = pad->GetSize().y + ( 2 * margin.y ) + width_adj; // Don't draw a null size item : if( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 ) continue; EDA_COLOR_T color = BLACK; if( (pad->GetLayerMask() & LAYER_BACK) ) color = aBoard->GetVisibleElementColor( PAD_BK_VISIBLE ); if((pad->GetLayerMask() & LAYER_FRONT ) ) color = ColorFromInt( color | aBoard->GetVisibleElementColor( PAD_FR_VISIBLE ) ); // Temporary set the pad size to the required plot size: wxSize tmppadsize = pad->GetSize(); pad->SetSize( padPlotsSize ); switch( pad->GetShape() ) { case PAD_CIRCLE: case PAD_OVAL: if( aPlotOpt.GetSkipPlotNPTH_Pads() && (pad->GetSize() == pad->GetDrillSize()) && (pad->GetAttribute() == PAD_HOLE_NOT_PLATED) ) break; // Fall through: case PAD_TRAPEZOID: case PAD_RECT: default: itemplotter.PlotPad( pad, color, plotMode ); break; } pad->SetSize( tmppadsize ); // Restore the pad size } } // Plot vias on copper layers, and if aPlotOpt.GetPlotViaOnMaskLayer() is true, // plot them on solder mask for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) { const VIA* Via = dynamic_cast<const VIA*>( track ); if( !Via ) continue; // vias are not plotted if not on selected layer, but if layer // is SOLDERMASK_LAYER_BACK or SOLDERMASK_LAYER_FRONT,vias are drawn, // only if they are on the corresponding external copper layer int via_mask_layer = Via->GetLayerMask(); if( aPlotOpt.GetPlotViaOnMaskLayer() ) { if( via_mask_layer & LAYER_BACK ) via_mask_layer |= SOLDERMASK_LAYER_BACK; if( via_mask_layer & LAYER_FRONT ) via_mask_layer |= SOLDERMASK_LAYER_FRONT; } if( ( via_mask_layer & aLayerMask ) == 0 ) continue; int via_margin = 0; double width_adj = 0; // If the current layer is a solder mask, use the global mask // clearance for vias if( ( aLayerMask & ( SOLDERMASK_LAYER_BACK | SOLDERMASK_LAYER_FRONT ) ) ) via_margin = aBoard->GetDesignSettings().m_SolderMaskMargin; if( aLayerMask & ALL_CU_LAYERS ) width_adj = itemplotter.getFineWidthAdj(); int diameter = Via->GetWidth() + 2 * via_margin + width_adj; // Don't draw a null size item : if( diameter <= 0 ) continue; EDA_COLOR_T color = aBoard->GetVisibleElementColor(VIAS_VISIBLE + Via->GetViaType()); // Set plot color (change WHITE to LIGHTGRAY because // the white items are not seen on a white paper or screen aPlotter->SetColor( color != WHITE ? color : LIGHTGRAY); aPlotter->FlashPadCircle( Via->GetStart(), diameter, plotMode ); } // Plot tracks (not vias) : for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) { if( track->Type() == PCB_VIA_T ) continue; if( (GetLayerMask( track->GetLayer() ) & aLayerMask) == 0 ) continue; int width = track->GetWidth() + itemplotter.getFineWidthAdj(); aPlotter->SetColor( itemplotter.getColor( track->GetLayer() ) ); aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, plotMode ); } // Plot zones (outdated, for old boards compatibility): for( TRACK* track = aBoard->m_Zone; track; track = track->Next() ) { if( (GetLayerMask( track->GetLayer() ) & aLayerMask) == 0 ) continue; int width = track->GetWidth() + itemplotter.getFineWidthAdj(); aPlotter->SetColor( itemplotter.getColor( track->GetLayer() ) ); aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, plotMode ); } // Plot filled ares for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = aBoard->GetArea( ii ); if( ( GetLayerMask(zone->GetLayer() ) & aLayerMask ) == 0 ) continue; itemplotter.PlotFilledAreas( zone ); } // Adding drill marks, if required and if the plotter is able to plot them: if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE ) itemplotter.PlotDrillMarks(); }
/* Plot a solder mask layer. * Solder mask layers have a minimum thickness value and cannot be drawn like standard layers, * unless the minimum thickness is 0. * Currently the algo is: * 1 - build all pad shapes as polygons with a size inflated by * mask clearance + (min width solder mask /2) * 2 - Merge shapes * 3 - deflate result by (min width solder mask /2) * 4 - oring result by all pad shapes as polygons with a size inflated by * mask clearance only (because deflate sometimes creates shape artifacts) * 5 - draw result as polygons * * TODO: * make this calculation only for shapes with clearance near than (min width solder mask) * (using DRC algo) * plot all other shapes by flashing the basing shape * (shapes will be better, and calculations faster) */ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LAYER_MSK aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt, int aMinThickness ) { LAYER_NUM layer = ( aLayerMask & SOLDERMASK_LAYER_BACK ) ? SOLDERMASK_N_BACK : SOLDERMASK_N_FRONT; int inflate = aMinThickness/2; BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); itemplotter.SetLayerMask( aLayerMask ); // Plot edge layer and graphic items itemplotter.PlotBoardGraphicItems(); for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( layer != item->GetLayer() ) continue; switch( item->Type() ) { case PCB_MODULE_EDGE_T: itemplotter.Plot_1_EdgeModule( (EDGE_MODULE*) item ); break; default: break; } } } // Build polygons for each pad shape. // the size of the shape on solder mask should be: // size of pad + clearance around the pad. // clearance = solder mask clearance + extra margin // extra margin is half the min width for solder mask // This extra margin is used to merge too close shapes // (distance < aMinThickness), and will be removed when creating // the actual shapes CPOLYGONS_LIST bufferPolys; // Contains shapes to plot CPOLYGONS_LIST initialPolys; // Contains exact shapes to plot /* calculates the coeff to compensate radius reduction of holes clearance * due to the segment approx ( 1 /cos( PI/circleToSegmentsCount ) */ int circleToSegmentsCount = 32; double correction = 1.0 / cos( M_PI / circleToSegmentsCount ); // Plot pads for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { // add shapes with exact size module->TransformPadsShapesWithClearanceToPolygon( layer, initialPolys, 0, circleToSegmentsCount, correction ); // add shapes inflated by aMinThickness/2 module->TransformPadsShapesWithClearanceToPolygon( layer, bufferPolys, inflate, circleToSegmentsCount, correction ); } // Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true, if( aPlotOpt.GetPlotViaOnMaskLayer() ) { // The current layer is a solder mask, // use the global mask clearance for vias int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin; int via_margin = via_clearance + inflate; for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) { const VIA* via = dynamic_cast<const VIA*>( track ); if( !via ) continue; // vias are plotted only if they are on the corresponding // external copper layer LAYER_MSK via_mask_layer = via->GetLayerMask(); if( via_mask_layer & LAYER_BACK ) via_mask_layer |= SOLDERMASK_LAYER_BACK; if( via_mask_layer & LAYER_FRONT ) via_mask_layer |= SOLDERMASK_LAYER_FRONT; if( ( via_mask_layer & aLayerMask ) == 0 ) continue; via->TransformShapeWithClearanceToPolygon( bufferPolys, via_margin, circleToSegmentsCount, correction ); via->TransformShapeWithClearanceToPolygon( initialPolys, via_clearance, circleToSegmentsCount, correction ); } } // Add filled zone areas for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = aBoard->GetArea( ii ); if( zone->GetLayer() != layer ) continue; zone->TransformOutlinesShapeWithClearanceToPolygon( bufferPolys, inflate, true ); } // Now: // 1 - merge areas which are intersecting, i.e. remove gaps // having a thickness < aMinThickness // 2 - deflate resulting areas by aMinThickness/2 KI_POLYGON_SET areasToMerge; bufferPolys.ExportTo( areasToMerge ); KI_POLYGON_SET initialAreas; initialPolys.ExportTo( initialAreas ); // Merge polygons: because each shape was created with an extra margin // = aMinThickness/2, shapes too close ( dist < aMinThickness ) // will be merged, because they are overlapping KI_POLYGON_SET areas; areas |= areasToMerge; // Deflate: remove the extra margin, to create the actual shapes // Here I am using polygon:resize, because this function creates better shapes // than deflate algo. // Use here deflate with arc creation and 18 segments per circle to create arcs // In boost polygon (at least v 1.54 and previous) in very rare cases resize crashes // with 16 segments (perhaps related to 45 degrees pads). So using 18 segments // is a workaround to try to avoid these crashes areas = resize( areas, -inflate , true, 18 ); // Resize slightly changes shapes. So *ensure* initial shapes are kept areas |= initialAreas; // To avoid a lot of code, use a ZONE_CONTAINER // to plot polygons, because they are exactly like // filled areas in zones ZONE_CONTAINER zone( aBoard ); zone.SetArcSegmentCount( 32 ); zone.SetMinThickness( 0 ); // trace polygons only zone.SetLayer ( layer ); zone.CopyPolygonsFromKiPolygonListToFilledPolysList( areas ); itemplotter.PlotFilledAreas( &zone ); }