void PCB_EDIT_FRAME::Delete_Drawings_All_Layer( LAYER_ID aLayer ) { if( IsCopperLayer( aLayer ) ) { DisplayError( this, _( "Copper layer global delete not allowed!" ) ); return; } wxString msg = wxString::Format( _( "Delete everything on layer %s?" ), GetChars( GetBoard()->GetLayerName( aLayer ) ) ); if( !IsOK( this, msg ) ) return; PICKED_ITEMS_LIST pickList; ITEM_PICKER picker( NULL, UR_DELETED ); BOARD_ITEM* PtNext; for( BOARD_ITEM* item = GetBoard()->m_Drawings; item; item = PtNext ) { PtNext = item->Next(); switch( item->Type() ) { case PCB_LINE_T: case PCB_TEXT_T: case PCB_DIMENSION_T: case PCB_TARGET_T: if( item->GetLayer() == aLayer ) { item->UnLink(); picker.SetItem( item ); pickList.PushItem( picker ); } break; default: { wxString msg; msg.Printf( wxT("Delete_Drawings_All_Layer() error: unknown type %d"), item->Type() ); wxMessageBox( msg ); break; } } } if( pickList.GetCount() ) { OnModify(); SaveCopyInUndoList(pickList, UR_DELETED); } }
/** * Function idf_export_outline * retrieves line segment information from the edge layer and compiles * the data into a form which can be output as an IDFv3 compliant * BOARD_OUTLINE section. */ static void idf_export_outline( BOARD* aPcb, IDF3_BOARD& aIDFBoard ) { double scale = aIDFBoard.GetUserScale(); DRAWSEGMENT* graphic; // KiCad graphical item IDF_POINT sp, ep; // start and end points from KiCad item std::list< IDF_SEGMENT* > lines; // IDF intermediate form of KiCad graphical item IDF_OUTLINE* outline = NULL; // graphical items forming an outline or cutout // NOTE: IMPLEMENTATION // If/when component cutouts are allowed, we must implement them separately. Cutouts // must be added to the board outline section and not to the Other Outline section. // The module cutouts should be handled via the idf_export_module() routine. double offX, offY; aIDFBoard.GetUserOffset( offX, offY ); // Retrieve segments and arcs from the board for( BOARD_ITEM* item = aPcb->m_Drawings; item; item = item->Next() ) { if( item->Type() != PCB_LINE_T || item->GetLayer() != Edge_Cuts ) continue; graphic = (DRAWSEGMENT*) item; switch( graphic->GetShape() ) { case S_SEGMENT: { if( ( graphic->GetStart().x == graphic->GetEnd().x ) && ( graphic->GetStart().y == graphic->GetEnd().y ) ) break; sp.x = graphic->GetStart().x * scale + offX; sp.y = -graphic->GetStart().y * scale + offY; ep.x = graphic->GetEnd().x * scale + offX; ep.y = -graphic->GetEnd().y * scale + offY; IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep ); if( seg ) lines.push_back( seg ); } break; case S_ARC: { if( ( graphic->GetCenter().x == graphic->GetArcStart().x ) && ( graphic->GetCenter().y == graphic->GetArcStart().y ) ) break; sp.x = graphic->GetCenter().x * scale + offX; sp.y = -graphic->GetCenter().y * scale + offY; ep.x = graphic->GetArcStart().x * scale + offX; ep.y = -graphic->GetArcStart().y * scale + offY; IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep, -graphic->GetAngle() / 10.0, true ); if( seg ) lines.push_back( seg ); } break; case S_CIRCLE: { if( graphic->GetRadius() == 0 ) break; sp.x = graphic->GetCenter().x * scale + offX; sp.y = -graphic->GetCenter().y * scale + offY; ep.x = sp.x - graphic->GetRadius() * scale; ep.y = sp.y; // Circles must always have an angle of +360 deg. to appease // quirky MCAD implementations of IDF. IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep, 360.0, true ); if( seg ) lines.push_back( seg ); } break; default: break; } } // if there is no outline then use the bounding box if( lines.empty() ) { goto UseBoundingBox; } // get the board outline and write it out // note: we do not use a try/catch block here since we intend // to simply ignore unclosed loops and continue processing // until we're out of segments to process outline = new IDF_OUTLINE; IDF3::GetOutline( lines, *outline ); if( outline->empty() ) goto UseBoundingBox; aIDFBoard.AddBoardOutline( outline ); outline = NULL; // get all cutouts and write them out while( !lines.empty() ) { if( !outline ) outline = new IDF_OUTLINE; IDF3::GetOutline( lines, *outline ); if( outline->empty() ) { outline->Clear(); continue; } aIDFBoard.AddBoardOutline( outline ); outline = NULL; } return; UseBoundingBox: // clean up if necessary while( !lines.empty() ) { delete lines.front(); lines.pop_front(); } if( outline ) outline->Clear(); else outline = new IDF_OUTLINE; // fetch a rectangular bounding box for the board; // there is always some uncertainty in the board dimensions // computed via ComputeBoundingBox() since this depends on the // individual module entities. EDA_RECT bbbox = aPcb->ComputeBoundingBox( true ); // convert to mm and compensate for an assumed LINE_WIDTH line thickness double x = ( bbbox.GetOrigin().x + LINE_WIDTH / 2 ) * scale + offX; double y = ( bbbox.GetOrigin().y + LINE_WIDTH / 2 ) * scale + offY; double dx = ( bbbox.GetSize().x - LINE_WIDTH ) * scale; double dy = ( bbbox.GetSize().y - LINE_WIDTH ) * scale; double px[4], py[4]; px[0] = x; py[0] = y; px[1] = x; py[1] = y + dy; px[2] = x + dx; py[2] = y + dy; px[3] = x + dx; py[3] = y; IDF_POINT p1, p2; p1.x = px[3]; p1.y = py[3]; p2.x = px[0]; p2.y = py[0]; outline->push( new IDF_SEGMENT( p1, p2 ) ); for( int i = 1; i < 4; ++i ) { p1.x = px[i - 1]; p1.y = py[i - 1]; p2.x = px[i]; p2.y = py[i]; outline->push( new IDF_SEGMENT( p1, p2 ) ); } aIDFBoard.AddBoardOutline( outline ); }
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 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 ); } } } } }
// todo: explain the selection heuristics void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) const { std::set<BOARD_ITEM*> rejected; const double footprintAreaRatio = 0.2; const double modulePadMinCoverRatio = 0.45; const double padViaAreaRatio = 0.5; const double trackViaLengthRatio = 2.0; const double trackTrackLengthRatio = 0.3; const double textToFeatureMinRatio = 0.2; const double textToFootprintMinRatio = 0.4; LAYER_ID actLayer = m_frame->GetActiveLayer(); LSET silkLayers( 2, B_SilkS, F_SilkS ); if( silkLayers[actLayer] ) { std::set<BOARD_ITEM*> preferred; for( int i = 0; i < aCollector.GetCount(); ++i ) { BOARD_ITEM* item = aCollector[i]; if ( item->Type() == PCB_MODULE_TEXT_T || item->Type() == PCB_TEXT_T || item->Type() == PCB_LINE_T ) if ( silkLayers[item->GetLayer()] ) preferred.insert ( item ); } if( preferred.size() != 0 ) { aCollector.Empty(); BOOST_FOREACH( BOARD_ITEM* item, preferred ) aCollector.Append( item ); return; } } if( aCollector.CountType( PCB_MODULE_TEXT_T ) > 0 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) if( TEXTE_MODULE* txt = dyn_cast<TEXTE_MODULE*>( aCollector[i] ) ) { double textArea = calcArea( txt ); for( int j = 0; j < aCollector.GetCount(); ++j ) { BOARD_ITEM* item = aCollector[j]; double areaRatio = calcRatio( textArea, calcArea( item ) ); if( item->Type() == PCB_MODULE_T && areaRatio < textToFootprintMinRatio ) { //printf("rejectModuleN\n"); rejected.insert( item ); } switch( item->Type() ) { case PCB_TRACE_T: case PCB_PAD_T: case PCB_LINE_T: case PCB_VIA_T: case PCB_MODULE_T: if( areaRatio > textToFeatureMinRatio ) { //printf("t after moduleRejected\n"); rejected.insert( txt ); } break; default: break; } } } } if( aCollector.CountType( PCB_MODULE_T ) > 0 ) { double minArea = calcMinArea( aCollector, PCB_MODULE_T ); double maxArea = calcMaxArea( aCollector, PCB_MODULE_T ); if( calcRatio( minArea, maxArea ) <= footprintAreaRatio ) { for( int i = 0; i < aCollector.GetCount(); ++i ) if( MODULE* mod = dyn_cast<MODULE*>( aCollector[i] ) ) { double normalizedArea = calcRatio( calcArea(mod), maxArea ); if( normalizedArea > footprintAreaRatio ) { //printf("rejectModule1\n"); rejected.insert( mod ); } } } } if( aCollector.CountType ( PCB_PAD_T ) > 0 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if ( D_PAD* pad = dyn_cast<D_PAD*>( aCollector[i] ) ) { double ratio = pad->GetParent()->PadCoverageRatio(); if( ratio < modulePadMinCoverRatio ) rejected.insert( pad->GetParent() ); } } } if( aCollector.CountType( PCB_VIA_T ) > 0 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if( VIA* via = dyn_cast<VIA*>( aCollector[i] ) ) { double viaArea = calcArea( via ); for( int j = 0; j < aCollector.GetCount(); ++j ) { BOARD_ITEM* item = aCollector[j]; double areaRatio = calcRatio ( viaArea, calcArea( item ) ); if( item->Type() == PCB_MODULE_T && areaRatio < modulePadMinCoverRatio ) rejected.insert( item ); if( item->Type() == PCB_PAD_T && areaRatio < padViaAreaRatio ) rejected.insert( item ); if( TRACK* track = dyn_cast<TRACK*>( item ) ) { if( track->GetNetCode() != via->GetNetCode() ) continue; double lenRatio = (double) ( track->GetLength() + track->GetWidth() ) / (double) via->GetWidth(); if( lenRatio > trackViaLengthRatio ) rejected.insert( track ); } } } } } int nTracks = aCollector.CountType ( PCB_TRACE_T ); if( nTracks > 0 ) { double maxLength = 0.0; double minLength = std::numeric_limits<double>::max(); double maxArea = 0.0; for( int i = 0; i < aCollector.GetCount(); ++i ) if ( TRACK *track = dyn_cast<TRACK*> ( aCollector[i] ) ) { maxLength = std::max( track->GetLength(), maxLength ); maxLength = std::max( (double)track->GetWidth(), maxLength ); minLength = std::min( std::max ( track->GetLength(), (double)track->GetWidth() ), minLength ); double area = ( track->GetLength() + track->GetWidth() * track->GetWidth() ); maxArea = std::max(area, maxArea); } if( maxLength > 0.0 && minLength/maxLength < trackTrackLengthRatio && nTracks > 1 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if( TRACK* track = dyn_cast<TRACK*>( aCollector[i] ) ) { double ratio = std::max( (double) track->GetWidth(), track->GetLength()) / maxLength; if( ratio > trackTrackLengthRatio ) rejected.insert( track) ; } } } for( int j = 0; j < aCollector.GetCount(); ++j ) { if( MODULE* mod = dyn_cast<MODULE*>( aCollector[j] ) ) { double ratio = maxArea / mod->GetFootprintRect().GetArea(); if( ratio < modulePadMinCoverRatio ) { //printf("rejectModule\n"); rejected.insert( mod ); } } } } BOOST_FOREACH( BOARD_ITEM* item, rejected ) { aCollector.Remove( item ); }
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 ); } } } }
bool PCB_EDIT_FRAME::OnRightClick( const wxPoint& aMousePos, wxMenu* aPopMenu ) { wxString msg; int flags = 0; bool locate_track = false; bool blockActive = (GetScreen()->m_BlockLocate.m_Command != BLOCK_IDLE); wxClientDC dc( m_canvas ); BOARD_ITEM* item = GetCurItem(); m_canvas->SetCanStartBlock( -1 ); // Avoid to start a block coomand when clicking on menu // If a command or a block is in progress: // Put the Cancel command (if needed) and the End command if( blockActive ) { createPopUpBlockMenu( aPopMenu ); aPopMenu->AppendSeparator(); return true; } m_canvas->CrossHairOff( &dc ); if( GetToolId() != ID_NO_TOOL_SELECTED ) { if( item && item->GetFlags() ) { AddMenuItem( aPopMenu, ID_POPUP_CANCEL_CURRENT_COMMAND, _( "Cancel" ), KiBitmap( cancel_xpm ) ); } else { AddMenuItem( aPopMenu, ID_POPUP_CLOSE_CURRENT_TOOL, _( "End Tool" ), KiBitmap( cursor_xpm ) ); } aPopMenu->AppendSeparator(); } else { if( item && item->GetFlags() ) { AddMenuItem( aPopMenu, ID_POPUP_CANCEL_CURRENT_COMMAND, _( "Cancel" ), KiBitmap( cancel_xpm ) ); aPopMenu->AppendSeparator(); } } /* Select a proper item */ wxPoint cursorPos = GetScreen()->GetCrossHairPosition(); wxPoint selectPos = m_Collector->GetRefPos(); selectPos = GetScreen()->GetNearestGridPosition( selectPos ); /* We can reselect another item only if there are no item being edited * because ALL moving functions use GetCurItem(), therefore GetCurItem() * must return the same item during moving. We know an item is moving * if( item && (item->m_Flags != 0)) is true and after calling * PcbGeneralLocateAndDisplay(), GetCurItem() is any arbitrary BOARD_ITEM, * not the current item being edited. In such case we cannot call * PcbGeneralLocateAndDisplay(). */ if( !item || (item->GetFlags() == 0) ) { // show "item selector" menu only if no item now or selected item was not // previously picked at this position if( !item || cursorPos != selectPos ) { m_canvas->SetAbortRequest( false ); item = PcbGeneralLocateAndDisplay(); if( m_canvas->GetAbortRequest() ) { m_canvas->CrossHairOn( &dc ); return false; } } } item = GetCurItem(); if( item ) flags = item->GetFlags(); else flags = 0; if( item ) { switch( item->Type() ) { case PCB_MODULE_T: createPopUpMenuForFootprints( (MODULE*) item, aPopMenu ); if( m_mainToolBar->GetToolToggled( ID_TOOLBARH_PCB_MODE_MODULE ) ) { aPopMenu->AppendSeparator(); if( !( (MODULE*) item )->IsLocked() ) { msg = AddHotkeyName( _("Lock Module" ), g_Board_Editor_Hokeys_Descr, HK_LOCK_UNLOCK_FOOTPRINT ); AddMenuItem( aPopMenu, ID_POPUP_PCB_AUTOPLACE_FIXE_MODULE, msg, KiBitmap( locked_xpm ) ); } else { msg = AddHotkeyName( _( "Unlock Module" ), g_Board_Editor_Hokeys_Descr, HK_LOCK_UNLOCK_FOOTPRINT ); AddMenuItem( aPopMenu, ID_POPUP_PCB_AUTOPLACE_FREE_MODULE, msg, KiBitmap( unlocked_xpm ) ); } if( !flags ) aPopMenu->Append( ID_POPUP_PCB_AUTOPLACE_CURRENT_MODULE, _( "Auto Place Module" ) ); } if( m_mainToolBar->GetToolToggled( ID_TOOLBARH_PCB_MODE_TRACKS ) ) { if( !flags ) aPopMenu->Append( ID_POPUP_PCB_AUTOROUTE_MODULE, _( "Autoroute Module" ) ); } break; case PCB_PAD_T: createPopUpMenuForFpPads( (D_PAD*) item, aPopMenu ); break; case PCB_MODULE_TEXT_T: createPopUpMenuForFpTexts( (TEXTE_MODULE*) item, aPopMenu ); break; case PCB_LINE_T: // Some graphic items on technical layers if( (flags & IS_NEW) ) { AddMenuItem( aPopMenu, ID_POPUP_PCB_STOP_CURRENT_DRAWING, _( "End Drawing" ), KiBitmap( apply_xpm ) ); } if( !flags ) { msg = AddHotkeyName( _( "Move Drawing" ), g_Board_Editor_Hokeys_Descr, HK_MOVE_ITEM ); AddMenuItem( aPopMenu, ID_POPUP_PCB_MOVE_DRAWING_REQUEST, msg, KiBitmap( move_xpm ) ); AddMenuItem( aPopMenu, ID_POPUP_PCB_EDIT_DRAWING, _( "Edit Drawing" ), KiBitmap( edit_xpm ) ); AddMenuItem( aPopMenu, ID_POPUP_PCB_DELETE_DRAWING, _( "Delete Drawing" ), KiBitmap( delete_xpm ) ); if( item->GetLayer() > LAST_COPPER_LAYER ) AddMenuItem( aPopMenu, ID_POPUP_PCB_DELETE_DRAWING_LAYER, _( "Delete All Drawing on Layer" ), KiBitmap( delete_xpm ) ); } break; case PCB_ZONE_T: // Item used to fill a zone AddMenuItem( aPopMenu, ID_POPUP_PCB_DELETE_ZONE, _( "Delete Zone Filling" ), KiBitmap( delete_xpm ) ); break; case PCB_ZONE_AREA_T: // Item used to handle a zone area (outlines, holes ...) if( flags & IS_NEW ) { AddMenuItem( aPopMenu, ID_POPUP_PCB_STOP_CURRENT_EDGE_ZONE, _( "Close Zone Outline" ), KiBitmap( apply_xpm ) ); AddMenuItem( aPopMenu, ID_POPUP_PCB_DELETE_ZONE_LAST_CREATED_CORNER, _( "Delete Last Corner" ), KiBitmap( delete_xpm ) ); } else { createPopUpMenuForZones( (ZONE_CONTAINER*) item, aPopMenu ); } break; case PCB_TEXT_T: createPopUpMenuForTexts( (TEXTE_PCB*) item, aPopMenu ); break; case PCB_TRACE_T: case PCB_VIA_T: locate_track = true; createPopupMenuForTracks( (TRACK*) item, aPopMenu ); break; case PCB_MARKER_T: createPopUpMenuForMarkers( (MARKER_PCB*) item, aPopMenu ); break; case PCB_DIMENSION_T: if( !flags ) { msg = AddHotkeyName( _( "Edit Dimension" ), g_Board_Editor_Hokeys_Descr, HK_EDIT_ITEM ); AddMenuItem( aPopMenu, ID_POPUP_PCB_EDIT_DIMENSION, msg, KiBitmap( edit_xpm ) ); msg = AddHotkeyName( _( "Move Dimension Text" ), g_Board_Editor_Hokeys_Descr, HK_MOVE_ITEM ); AddMenuItem( aPopMenu, ID_POPUP_PCB_MOVE_TEXT_DIMENSION_REQUEST, msg, KiBitmap( move_text_xpm ) ); msg = AddHotkeyName( _( "Delete Dimension" ), g_Board_Editor_Hokeys_Descr, HK_DELETE ); AddMenuItem( aPopMenu, ID_POPUP_PCB_DELETE_DIMENSION, msg, KiBitmap( delete_xpm ) ); } break; case PCB_TARGET_T: if( !flags ) { msg = AddHotkeyName( _( "Move Target" ), g_Board_Editor_Hokeys_Descr, HK_MOVE_ITEM ); AddMenuItem( aPopMenu, ID_POPUP_PCB_MOVE_MIRE_REQUEST, msg, KiBitmap( move_xpm ) ); msg = AddHotkeyName( _( "Edit Target" ), g_Board_Editor_Hokeys_Descr, HK_EDIT_ITEM ); AddMenuItem( aPopMenu, ID_POPUP_PCB_EDIT_MIRE, msg, KiBitmap( edit_xpm ) ); msg = AddHotkeyName( _( "Delete Target" ), g_Board_Editor_Hokeys_Descr, HK_DELETE ); AddMenuItem( aPopMenu, ID_POPUP_PCB_DELETE_MIRE, msg, KiBitmap( delete_xpm ) ); } break; case PCB_MODULE_EDGE_T: case SCREEN_T: case TYPE_NOT_INIT: case PCB_T: msg.Printf( wxT( "PCB_EDIT_FRAME::OnRightClick() Error: unexpected DrawType %d" ), item->Type() ); wxMessageBox( msg ); SetCurItem( NULL ); break; default: msg.Printf( wxT( "PCB_EDIT_FRAME::OnRightClick() Error: unknown DrawType %d" ), item->Type() ); wxMessageBox( msg ); // Attempt to clear error (but should no occurs ) if( item->Type() >= MAX_STRUCT_TYPE_ID ) SetCurItem( NULL ); break; } aPopMenu->AppendSeparator(); } if( !flags ) { msg = AddHotkeyName( _( "Get and Move Footprint" ), g_Board_Editor_Hokeys_Descr, HK_GET_AND_MOVE_FOOTPRINT ); AddMenuItem( aPopMenu, ID_POPUP_PCB_GET_AND_MOVE_MODULE_REQUEST, msg, KiBitmap( move_module_xpm ) ); } /* Display context sensitive commands: */ switch( GetToolId() ) { case ID_PCB_ZONES_BUTT: if( GetBoard()->m_ZoneDescriptorList.size() > 0 ) { aPopMenu->AppendSeparator(); AddMenuItem( aPopMenu, ID_POPUP_PCB_FILL_ALL_ZONES, _( "Fill or Refill All Zones" ), KiBitmap( fill_zone_xpm ) ); AddMenuItem( aPopMenu, ID_POPUP_PCB_REMOVE_FILLED_AREAS_IN_ALL_ZONES, _( "Remove Filled Areas in All Zones" ), KiBitmap( zone_unfill_xpm ) ); aPopMenu->AppendSeparator(); } AddMenuItem( aPopMenu, ID_POPUP_PCB_SELECT_LAYER, _( "Select Working Layer" ), KiBitmap( select_w_layer_xpm ) ); aPopMenu->AppendSeparator(); break; case ID_TRACK_BUTT: if ( ! locate_track ) // This menu is already added when a track is located AddMenuItem( aPopMenu, Append_Track_Width_List( GetBoard() ), ID_POPUP_PCB_SELECT_WIDTH, _( "Select Track Width" ), KiBitmap( width_track_xpm ) ); AddMenuItem( aPopMenu, ID_POPUP_PCB_SELECT_CU_LAYER, _( "Select Working Layer" ), KiBitmap( select_w_layer_xpm ) ); AddMenuItem( aPopMenu, ID_POPUP_PCB_SELECT_LAYER_PAIR, _( "Select Layer Pair for Vias" ), KiBitmap( select_layer_pair_xpm ) ); aPopMenu->AppendSeparator(); break; case ID_PCB_CIRCLE_BUTT: case ID_PCB_ARC_BUTT: case ID_PCB_ADD_TEXT_BUTT: case ID_PCB_ADD_LINE_BUTT: case ID_PCB_DIMENSION_BUTT: AddMenuItem( aPopMenu, ID_POPUP_PCB_SELECT_NO_CU_LAYER, _( "Select Working Layer" ), KiBitmap( select_w_layer_xpm ) ); aPopMenu->AppendSeparator(); break; case ID_PCB_MODULE_BUTT: AddMenuItem( aPopMenu, ID_POPUP_PCB_DISPLAY_FOOTPRINT_DOC, _( "Footprint Documentation" ), KiBitmap( book_xpm ) ); aPopMenu->AppendSeparator(); break; case ID_NO_TOOL_SELECTED: if( m_mainToolBar->GetToolToggled( ID_TOOLBARH_PCB_MODE_MODULE ) ) { wxMenu* commands = new wxMenu; AddMenuItem( aPopMenu, commands, ID_POPUP_PCB_AUTOPLACE_COMMANDS, _( "Glob Move and Place" ), KiBitmap( move_xpm ) ); AddMenuItem( commands, ID_POPUP_PCB_AUTOPLACE_FREE_ALL_MODULES, _( "Unlock All Modules" ), KiBitmap( unlocked_xpm ) ); AddMenuItem( commands, ID_POPUP_PCB_AUTOPLACE_FIXE_ALL_MODULES, _( "Lock All Modules" ), KiBitmap( locked_xpm ) ); commands->AppendSeparator(); AddMenuItem( commands, ID_POPUP_PCB_AUTOMOVE_ALL_MODULES, _( "Move All Modules" ), KiBitmap( move_xpm ) ); commands->Append( ID_POPUP_PCB_AUTOMOVE_NEW_MODULES, _( "Move New Modules" ) ); commands->AppendSeparator(); commands->Append( ID_POPUP_PCB_AUTOPLACE_ALL_MODULES, _( "Autoplace All Modules" ) ); commands->Append( ID_POPUP_PCB_AUTOPLACE_NEW_MODULES, _( "Autoplace New Modules" ) ); commands->Append( ID_POPUP_PCB_AUTOPLACE_NEXT_MODULE, _( "Autoplace Next Module" ) ); commands->AppendSeparator(); AddMenuItem( commands, ID_POPUP_PCB_REORIENT_ALL_MODULES, _( "Orient All Modules" ), KiBitmap( rotate_module_pos_xpm ) ); aPopMenu->AppendSeparator(); } if( m_mainToolBar->GetToolToggled( ID_TOOLBARH_PCB_MODE_TRACKS ) ) { wxMenu* commands = new wxMenu; aPopMenu->Append( ID_POPUP_PCB_AUTOROUTE_COMMANDS, _( "Autoroute" ), commands ); AddMenuItem( commands, ID_POPUP_PCB_SELECT_LAYER_PAIR, _( "Select Layer Pair" ), KiBitmap( select_layer_pair_xpm ) ); commands->AppendSeparator(); commands->Append( ID_POPUP_PCB_AUTOROUTE_ALL_MODULES, _( "Autoroute All Modules" ) ); commands->AppendSeparator(); commands->Append( ID_POPUP_PCB_AUTOROUTE_RESET_UNROUTED, _( "Reset Unrouted" ) ); aPopMenu->AppendSeparator(); } if( locate_track ) AddMenuItem( aPopMenu, Append_Track_Width_List( GetBoard() ), ID_POPUP_PCB_SELECT_WIDTH, _( "Select Track Width" ), KiBitmap( width_track_xpm ) ); AddMenuItem( aPopMenu, ID_POPUP_PCB_SELECT_LAYER, _( "Select Working Layer" ), KiBitmap( select_w_layer_xpm ) ); aPopMenu->AppendSeparator(); break; } m_canvas->CrossHairOn( &dc ); return true; }
/* 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 ); }
/* 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 ); }
/** * 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(); }
void DIALOG_GLOBAL_DELETION::AcceptPcbDelete( ) { bool gen_rastnest = false; m_Parent->SetCurItem( NULL ); if( m_DelAlls->GetValue() ) { m_Parent->Clear_Pcb( true ); } else { if( !IsOK( this, _( "Are you sure you want to delete the selected items?" ) ) ) return; BOARD* pcb = m_Parent->GetBoard(); PICKED_ITEMS_LIST pickersList; ITEM_PICKER itemPicker( NULL, UR_DELETED ); BOARD_ITEM* item; BOARD_ITEM* nextitem; RN_DATA* ratsnest = pcb->GetRatsnest(); LSET layers_filter = LSET().set(); if( m_rbLayersOption->GetSelection() != 0 ) // Use current layer only layers_filter = LSET( ToLAYER_ID( m_currentLayer ) ); if( m_DelZones->GetValue() ) { int area_index = 0; item = pcb->GetArea( area_index ); while( item ) { if( layers_filter[item->GetLayer()] ) { itemPicker.SetItem( item ); pickersList.PushItem( itemPicker ); pcb->Remove( item ); item->ViewRelease(); ratsnest->Remove( item ); gen_rastnest = true; } else { area_index++; } item = pcb->GetArea( area_index ); } } if( m_DelDrawings->GetValue() || m_DelBoardEdges->GetValue() ) { LSET masque_layer; if( m_DelDrawings->GetValue() ) masque_layer = LSET::AllNonCuMask().set( Edge_Cuts, false ); if( m_DelBoardEdges->GetValue() ) masque_layer.set( Edge_Cuts ); masque_layer &= layers_filter; for( item = pcb->m_Drawings; item; item = nextitem ) { nextitem = item->Next(); if( item->Type() == PCB_LINE_T && masque_layer[item->GetLayer()] ) { itemPicker.SetItem( item ); pickersList.PushItem( itemPicker ); item->ViewRelease(); item->UnLink(); } } } if( m_DelTexts->GetValue() ) { LSET del_text_layers = layers_filter; for( item = pcb->m_Drawings; item; item = nextitem ) { nextitem = item->Next(); if( item->Type() == PCB_TEXT_T && del_text_layers[item->GetLayer()] ) { itemPicker.SetItem( item ); pickersList.PushItem( itemPicker ); item->ViewRelease(); item->UnLink(); } } } if( m_DelModules->GetValue() ) { for( item = pcb->m_Modules; item; item = nextitem ) { nextitem = item->Next(); if( layers_filter[item->GetLayer()] && ( ( m_ModuleFilterNormal->GetValue() && !item->IsLocked() ) || ( m_ModuleFilterLocked->GetValue() && item->IsLocked() ) ) ) { itemPicker.SetItem( item ); pickersList.PushItem( itemPicker ); static_cast<MODULE*>( item )->RunOnChildren( boost::bind( &KIGFX::VIEW_ITEM::ViewRelease, _1 ) ); ratsnest->Remove( item ); item->ViewRelease(); item->UnLink(); gen_rastnest = true; } } } if( m_DelTracks->GetValue() ) { STATUS_FLAGS track_mask_filter = 0; if( !m_TrackFilterLocked->GetValue() ) track_mask_filter |= TRACK_LOCKED; if( !m_TrackFilterAR->GetValue() ) track_mask_filter |= TRACK_AR; TRACK* nexttrack; for( TRACK *track = pcb->m_Track; track; track = nexttrack ) { nexttrack = track->Next(); if( ( track->GetState( TRACK_LOCKED | TRACK_AR ) & track_mask_filter ) != 0 ) continue; if( ( track->GetState( TRACK_LOCKED | TRACK_AR ) == 0 ) && !m_TrackFilterNormal->GetValue() ) continue; if( ( track->Type() == PCB_VIA_T ) && !m_TrackFilterVias->GetValue() ) continue; if( ( track->GetLayerSet() & layers_filter ) == 0 ) continue; itemPicker.SetItem( track ); pickersList.PushItem( itemPicker ); track->ViewRelease(); ratsnest->Remove( track ); track->UnLink(); gen_rastnest = true; } } if( pickersList.GetCount() ) m_Parent->SaveCopyInUndoList( pickersList, UR_DELETED ); if( m_DelMarkers->GetValue() ) pcb->DeleteMARKERs(); if( gen_rastnest ) m_Parent->Compile_Ratsnest( NULL, true ); if( m_Parent->IsGalCanvasActive() ) pcb->GetRatsnest()->Recalculate(); } m_Parent->GetCanvas()->Refresh(); m_Parent->OnModify(); EndModal( 1 ); }
bool PCB_EDIT_FRAME::OnRightClick( const wxPoint& aMousePos, wxMenu* aPopMenu ) { wxString msg; STATUS_FLAGS flags = 0; bool trackFound = false; // Flag set to true, // if a track is being the cursor, to avoid // to display menus relative to tracks twice bool blockActive = !GetScreen()->m_BlockLocate.IsIdle(); BOARD_ITEM* item = GetCurItem(); m_canvas->SetCanStartBlock( -1 ); // Avoid to start a block command when clicking on menu // If a command or a block is in progress: // Put the Cancel command (if needed) and the End command if( blockActive ) { createPopUpBlockMenu( aPopMenu ); aPopMenu->AppendSeparator(); return true; } if( GetToolId() != ID_NO_TOOL_SELECTED ) { if( item && item->GetFlags() ) { AddMenuItem( aPopMenu, ID_POPUP_CANCEL_CURRENT_COMMAND, _( "Cancel" ), KiBitmap( cancel_xpm ) ); } else { AddMenuItem( aPopMenu, ID_POPUP_CLOSE_CURRENT_TOOL, _( "End Tool" ), KiBitmap( cursor_xpm ) ); } aPopMenu->AppendSeparator(); } else { if( item && item->GetFlags() ) { AddMenuItem( aPopMenu, ID_POPUP_CANCEL_CURRENT_COMMAND, _( "Cancel" ), KiBitmap( cancel_xpm ) ); aPopMenu->AppendSeparator(); } } // Select a proper item wxPoint cursorPos = GetCrossHairPosition(); wxPoint selectPos = m_Collector->GetRefPos(); selectPos = GetNearestGridPosition( selectPos ); /* We can reselect another item only if there are no item being edited * because ALL moving functions use GetCurItem(), therefore GetCurItem() * must return the same item during moving. We know an item is moving * if( item && (item->m_Flags != 0)) is true and after calling * PcbGeneralLocateAndDisplay(), GetCurItem() is any arbitrary BOARD_ITEM, * not the current item being edited. In such case we cannot call * PcbGeneralLocateAndDisplay(). */ if( !item || (item->GetFlags() == 0) ) { // show the "item selector" menu if no item selected or // if there is a selected item but the mouse has moved // (therefore a new item is perhaps under the cursor) if( !item || cursorPos != selectPos ) { m_canvas->SetAbortRequest( false ); PcbGeneralLocateAndDisplay(); if( m_canvas->GetAbortRequest() ) { return false; } } } item = GetCurItem(); flags = item ? item->GetFlags() : 0; // Add the context menu, which depends on the picked item: if( item ) { switch( item->Type() ) { case PCB_MODULE_T: createPopUpMenuForFootprints( (MODULE*) item, aPopMenu ); if( m_mainToolBar->GetToolToggled( ID_TOOLBARH_PCB_MODE_MODULE ) ) { aPopMenu->AppendSeparator(); if( !( (MODULE*) item )->IsLocked() ) { msg = AddHotkeyName( _("Lock Footprint" ), g_Board_Editor_Hokeys_Descr, HK_LOCK_UNLOCK_FOOTPRINT ); AddMenuItem( aPopMenu, ID_POPUP_PCB_AUTOPLACE_FIXE_MODULE, msg, KiBitmap( locked_xpm ) ); } else { msg = AddHotkeyName( _( "Unlock Footprint" ), g_Board_Editor_Hokeys_Descr, HK_LOCK_UNLOCK_FOOTPRINT ); AddMenuItem( aPopMenu, ID_POPUP_PCB_AUTOPLACE_FREE_MODULE, msg, KiBitmap( unlocked_xpm ) ); } if( !flags ) aPopMenu->Append( ID_POPUP_PCB_AUTOPLACE_CURRENT_MODULE, _( "Automatically Place Footprint" ) ); } if( m_mainToolBar->GetToolToggled( ID_TOOLBARH_PCB_MODE_TRACKS ) ) { if( !flags ) aPopMenu->Append( ID_POPUP_PCB_AUTOROUTE_MODULE, _( "Automatically Route Footprint" ) ); } break; case PCB_PAD_T: createPopUpMenuForFpPads( static_cast<D_PAD*>( item ), aPopMenu ); break; case PCB_MODULE_TEXT_T: createPopUpMenuForFpTexts( static_cast<TEXTE_MODULE*>( item ), aPopMenu ); break; case PCB_LINE_T: // Some graphic items on technical layers if( (flags & IS_NEW) ) { AddMenuItem( aPopMenu, ID_POPUP_PCB_STOP_CURRENT_DRAWING, _( "End Drawing" ), KiBitmap( checked_ok_xpm ) ); } if( !flags ) { msg = AddHotkeyName( _( "Move Drawing" ), g_Board_Editor_Hokeys_Descr, HK_MOVE_ITEM ); AddMenuItem( aPopMenu, ID_POPUP_PCB_MOVE_DRAWING_REQUEST, msg, KiBitmap( move_xpm ) ); msg = AddHotkeyName( _( "Duplicate Drawing" ), g_Board_Editor_Hokeys_Descr, HK_DUPLICATE_ITEM ); AddMenuItem( aPopMenu, ID_POPUP_PCB_DUPLICATE_ITEM, msg, KiBitmap( duplicate_line_xpm ) ); msg = AddHotkeyName( _("Move Drawing Exactly" ), g_Board_Editor_Hokeys_Descr, HK_MOVE_ITEM_EXACT ); AddMenuItem( aPopMenu, ID_POPUP_PCB_MOVE_EXACT, msg, KiBitmap( move_line_xpm ) ); msg = AddHotkeyName( _("Create Drawing Array" ), g_Board_Editor_Hokeys_Descr, HK_CREATE_ARRAY ); AddMenuItem( aPopMenu, ID_POPUP_PCB_CREATE_ARRAY, msg, KiBitmap( array_line_xpm ) ); msg = AddHotkeyName( _( "Edit Drawing" ), g_Board_Editor_Hokeys_Descr, HK_EDIT_ITEM ); AddMenuItem( aPopMenu, ID_POPUP_PCB_EDIT_DRAWING, msg, KiBitmap( edit_xpm ) ); AddMenuItem( aPopMenu, ID_POPUP_PCB_DELETE_DRAWING, _( "Delete Drawing" ), KiBitmap( delete_xpm ) ); if( !IsCopperLayer( item->GetLayer() ) ) AddMenuItem( aPopMenu, ID_POPUP_PCB_DELETE_DRAWING_LAYER, _( "Delete All Drawings on Layer" ), KiBitmap( delete_xpm ) ); } break; case PCB_ZONE_T: // Item used to fill a zone AddMenuItem( aPopMenu, ID_POPUP_PCB_DELETE_ZONE, _( "Delete Zone Filling" ), KiBitmap( delete_xpm ) ); break; case PCB_ZONE_AREA_T: // Item used to handle a zone area (outlines, holes ...) if( flags & IS_NEW ) { AddMenuItem( aPopMenu, ID_POPUP_PCB_STOP_CURRENT_EDGE_ZONE, _( "Close Zone Outline" ), KiBitmap( checked_ok_xpm ) ); AddMenuItem( aPopMenu, ID_POPUP_PCB_DELETE_ZONE_LAST_CREATED_CORNER, _( "Delete Last Corner" ), KiBitmap( delete_xpm ) ); } else { createPopUpMenuForZones( (ZONE_CONTAINER*) item, aPopMenu ); } break; case PCB_TEXT_T: createPopUpMenuForTexts( (TEXTE_PCB*) item, aPopMenu ); break; case PCB_TRACE_T: case PCB_VIA_T: trackFound = true; createPopupMenuForTracks( (TRACK*) item, aPopMenu ); break; case PCB_MARKER_T: createPopUpMenuForMarkers( (MARKER_PCB*) item, aPopMenu ); break; case PCB_DIMENSION_T: if( !flags ) { msg = AddHotkeyName( _( "Edit Dimension" ), g_Board_Editor_Hokeys_Descr, HK_EDIT_ITEM ); AddMenuItem( aPopMenu, ID_POPUP_PCB_EDIT_DIMENSION, msg, KiBitmap( edit_xpm ) ); msg = AddHotkeyName( _( "Move Dimension Text" ), g_Board_Editor_Hokeys_Descr, HK_MOVE_ITEM ); AddMenuItem( aPopMenu, ID_POPUP_PCB_MOVE_TEXT_DIMENSION_REQUEST, msg, KiBitmap( move_text_xpm ) ); msg = AddHotkeyName( _( "Duplicate Dimension" ), g_Board_Editor_Hokeys_Descr, HK_DUPLICATE_ITEM ); AddMenuItem( aPopMenu, ID_POPUP_PCB_DUPLICATE_ITEM, msg, KiBitmap( duplicate_text_xpm ) ); msg = AddHotkeyName( _("Move Dimension Exactly" ), g_Board_Editor_Hokeys_Descr, HK_MOVE_ITEM_EXACT ); AddMenuItem( aPopMenu, ID_POPUP_PCB_MOVE_EXACT, msg, KiBitmap( move_text_xpm ) ); msg = AddHotkeyName( _( "Delete Dimension" ), g_Board_Editor_Hokeys_Descr, HK_DELETE ); AddMenuItem( aPopMenu, ID_POPUP_PCB_DELETE_DIMENSION, msg, KiBitmap( delete_xpm ) ); } break; case PCB_TARGET_T: if( !flags ) { msg = AddHotkeyName( _( "Move Target" ), g_Board_Editor_Hokeys_Descr, HK_MOVE_ITEM ); AddMenuItem( aPopMenu, ID_POPUP_PCB_MOVE_MIRE_REQUEST, msg, KiBitmap( move_target_xpm ) ); msg = AddHotkeyName( _("Move Target Exactly" ), g_Board_Editor_Hokeys_Descr, HK_MOVE_ITEM_EXACT ); AddMenuItem( aPopMenu, ID_POPUP_PCB_MOVE_EXACT, msg, KiBitmap( move_target_xpm ) ); msg = AddHotkeyName( _( "Duplicate Target" ), g_Board_Editor_Hokeys_Descr, HK_DUPLICATE_ITEM ); AddMenuItem( aPopMenu, ID_POPUP_PCB_DUPLICATE_ITEM, msg, KiBitmap( duplicate_target_xpm ) ); msg = AddHotkeyName( _("Create Target Array" ), g_Board_Editor_Hokeys_Descr, HK_CREATE_ARRAY ); AddMenuItem( aPopMenu, ID_POPUP_PCB_CREATE_ARRAY, msg, KiBitmap( array_target_xpm ) ); msg = AddHotkeyName( _( "Edit Target" ), g_Board_Editor_Hokeys_Descr, HK_EDIT_ITEM ); AddMenuItem( aPopMenu, ID_POPUP_PCB_EDIT_MIRE, msg, KiBitmap( edit_xpm ) ); msg = AddHotkeyName( _( "Delete Target" ), g_Board_Editor_Hokeys_Descr, HK_DELETE ); AddMenuItem( aPopMenu, ID_POPUP_PCB_DELETE_MIRE, msg, KiBitmap( delete_xpm ) ); } break; case PCB_MODULE_EDGE_T: case SCREEN_T: case TYPE_NOT_INIT: case PCB_T: msg.Printf( wxT( "PCB_EDIT_FRAME::OnRightClick() Error: unexpected DrawType %d" ), item->Type() ); wxMessageBox( msg ); SetCurItem( NULL ); break; default: msg.Printf( wxT( "PCB_EDIT_FRAME::OnRightClick() Error: unknown DrawType %d" ), item->Type() ); wxMessageBox( msg ); // Attempt to clear error (but should no occurs ) if( item->Type() >= MAX_STRUCT_TYPE_ID ) SetCurItem( NULL ); break; } aPopMenu->AppendSeparator(); } if( !flags ) { msg = AddHotkeyName( _( "Get and Move Footprint" ), g_Board_Editor_Hokeys_Descr, HK_GET_AND_MOVE_FOOTPRINT ); AddMenuItem( aPopMenu, ID_POPUP_PCB_GET_AND_MOVE_MODULE_REQUEST, msg, KiBitmap( move_module_xpm ) ); } // Display context sensitive commands: switch( GetToolId() ) { case ID_PCB_ZONES_BUTT: if( GetBoard()->m_ZoneDescriptorList.size() > 0 ) { aPopMenu->AppendSeparator(); msg = AddHotkeyName( _( "Fill or Refill All Zones" ), g_Board_Editor_Hokeys_Descr, HK_ZONE_FILL_OR_REFILL ); AddMenuItem( aPopMenu, ID_POPUP_PCB_FILL_ALL_ZONES, msg, KiBitmap( fill_zone_xpm ) ); msg = AddHotkeyName( _( "Remove Filled Areas in All Zones" ), g_Board_Editor_Hokeys_Descr, HK_ZONE_REMOVE_FILLED ); AddMenuItem( aPopMenu, ID_POPUP_PCB_REMOVE_FILLED_AREAS_IN_ALL_ZONES, msg, KiBitmap( zone_unfill_xpm ) ); aPopMenu->AppendSeparator(); } AddMenuItem( aPopMenu, ID_POPUP_PCB_SELECT_LAYER, _( "Select Working Layer" ), KiBitmap( select_w_layer_xpm ) ); aPopMenu->AppendSeparator(); break; case ID_PCB_KEEPOUT_AREA_BUTT: AddMenuItem( aPopMenu, ID_POPUP_PCB_SELECT_LAYER, _( "Select Working Layer" ), KiBitmap( select_w_layer_xpm ) ); aPopMenu->AppendSeparator(); break; case ID_TRACK_BUTT: if ( ! trackFound ) // This menu is already added when a track is located { aPopMenu->AppendSeparator(); msg = AddHotkeyName( _( "Begin Track" ), g_Board_Editor_Hokeys_Descr, HK_ADD_NEW_TRACK ); AddMenuItem( aPopMenu, ID_POPUP_PCB_BEGIN_TRACK, msg, KiBitmap( add_tracks_xpm ) ); AddMenuItem( aPopMenu, Append_Track_Width_List( GetBoard() ), ID_POPUP_PCB_SELECT_WIDTH, _( "Select Track Width" ), KiBitmap( width_track_xpm ) ); AddMenuItem( aPopMenu, ID_POPUP_PCB_SELECT_CU_LAYER, _( "Select Working Layer" ), KiBitmap( select_w_layer_xpm ) ); AddMenuItem( aPopMenu, ID_POPUP_PCB_SELECT_LAYER_PAIR, _( "Select Layer Pair for Vias" ), KiBitmap( select_layer_pair_xpm ) ); aPopMenu->AppendSeparator(); } break; case ID_PCB_CIRCLE_BUTT: case ID_PCB_ARC_BUTT: case ID_PCB_ADD_TEXT_BUTT: case ID_PCB_ADD_LINE_BUTT: case ID_PCB_DIMENSION_BUTT: AddMenuItem( aPopMenu, ID_POPUP_PCB_SELECT_NO_CU_LAYER, _( "Select Working Layer" ), KiBitmap( select_w_layer_xpm ) ); aPopMenu->AppendSeparator(); break; case ID_PCB_MODULE_BUTT: if( !flags ) { AddMenuItem( aPopMenu, ID_POPUP_PCB_DISPLAY_FOOTPRINT_DOC, _( "Footprint Documentation" ), KiBitmap( book_xpm ) ); aPopMenu->AppendSeparator(); } break; case ID_NO_TOOL_SELECTED: if( m_mainToolBar->GetToolToggled( ID_TOOLBARH_PCB_MODE_MODULE ) ) { wxMenu* commands = new wxMenu; AddMenuItem( aPopMenu, commands, ID_POPUP_PCB_AUTOPLACE_COMMANDS, _( "Global Spread and Place" ), KiBitmap( move_xpm ) ); AddMenuItem( commands, ID_POPUP_PCB_AUTOPLACE_FREE_ALL_MODULES, _( "Unlock All Footprints" ), KiBitmap( unlocked_xpm ) ); AddMenuItem( commands, ID_POPUP_PCB_AUTOPLACE_FIXE_ALL_MODULES, _( "Lock All Footprints" ), KiBitmap( locked_xpm ) ); commands->AppendSeparator(); AddMenuItem( commands, ID_POPUP_PCB_SPREAD_ALL_MODULES, _( "Spread out All Footprints" ), KiBitmap( move_xpm ) ); commands->Append( ID_POPUP_PCB_SPREAD_NEW_MODULES, _( "Spread out Footprints not Already on Board" ) ); commands->AppendSeparator(); commands->Append( ID_POPUP_PCB_AUTOPLACE_ALL_MODULES, _( "Automatically Place All Footprints" ) ); commands->Append( ID_POPUP_PCB_AUTOPLACE_NEW_MODULES, _( "Automatically Place New Footprints" ) ); commands->Append( ID_POPUP_PCB_AUTOPLACE_NEXT_MODULE, _( "Automatically Place Next Footprints" ) ); commands->AppendSeparator(); AddMenuItem( commands, ID_POPUP_PCB_REORIENT_ALL_MODULES, _( "Orient All Footprints" ), KiBitmap( rotate_module_cw_xpm ) ); aPopMenu->AppendSeparator(); } if( m_mainToolBar->GetToolToggled( ID_TOOLBARH_PCB_MODE_TRACKS ) ) { wxMenu* commands = new wxMenu; aPopMenu->Append( ID_POPUP_PCB_AUTOROUTE_COMMANDS, _( "Autoroute" ), commands ); AddMenuItem( commands, ID_POPUP_PCB_SELECT_LAYER_PAIR, _( "Select Layer Pair" ), KiBitmap( select_layer_pair_xpm ) ); commands->AppendSeparator(); commands->Append( ID_POPUP_PCB_AUTOROUTE_ALL_MODULES, _( "Automatically Route All Footprints" ) ); commands->AppendSeparator(); commands->Append( ID_POPUP_PCB_AUTOROUTE_RESET_UNROUTED, _( "Reset Unrouted" ) ); aPopMenu->AppendSeparator(); } if( !trackFound ) { msg = AddHotkeyName( _( "Begin Track" ), g_Board_Editor_Hokeys_Descr, HK_ADD_NEW_TRACK ); AddMenuItem( aPopMenu, ID_POPUP_PCB_BEGIN_TRACK, msg, KiBitmap( add_tracks_xpm ) ); AddMenuItem( aPopMenu, Append_Track_Width_List( GetBoard() ), ID_POPUP_PCB_SELECT_WIDTH, _( "Select Track Width" ), KiBitmap( width_track_xpm ) ); AddMenuItem( aPopMenu, ID_POPUP_PCB_SELECT_LAYER, _( "Select Working Layer" ), KiBitmap( select_w_layer_xpm ) ); aPopMenu->AppendSeparator(); } break; } return true; }
void DRC::testTexts() { std::vector<wxPoint> textShape; // a buffer to store the text shape (set of segments) std::vector<D_PAD*> padList = m_pcb->GetPads(); // Test text areas for vias, tracks and pads inside text areas for( BOARD_ITEM* item = m_pcb->m_Drawings; item; item = item->Next() ) { // Drc test only items on copper layers if( ! IsCopperLayer( item->GetLayer() ) ) continue; // only texts on copper layers are tested if( item->Type() != PCB_TEXT_T ) continue; textShape.clear(); // So far the bounding box makes up the text-area TEXTE_PCB* text = (TEXTE_PCB*) item; text->TransformTextShapeToSegmentList( textShape ); if( textShape.size() == 0 ) // Should not happen (empty text?) continue; for( TRACK* track = m_pcb->m_Track; track != NULL; track = track->Next() ) { if( ! track->IsOnLayer( item->GetLayer() ) ) continue; // Test the distance between each segment and the current track/via int min_dist = ( track->GetWidth() + text->GetThickness() ) /2 + track->GetClearance(NULL); if( track->Type() == PCB_TRACE_T ) { SEG segref( track->GetStart(), track->GetEnd() ); // Error condition: Distance between text segment and track segment is // smaller than the clearance of the segment for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { SEG segtest( textShape[jj], textShape[jj+1] ); int dist = segref.Distance( segtest ); if( dist < min_dist ) { addMarkerToPcb( fillMarker( track, text, DRCE_TRACK_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } else if( track->Type() == PCB_VIA_T ) { // Error condition: Distance between text segment and via is // smaller than the clearance of the via for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { SEG segtest( textShape[jj], textShape[jj+1] ); if( segtest.PointCloserThan( track->GetPosition(), min_dist ) ) { addMarkerToPcb( fillMarker( track, text, DRCE_VIA_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } } // Test pads for( unsigned ii = 0; ii < padList.size(); ii++ ) { D_PAD* pad = padList[ii]; if( ! pad->IsOnLayer( item->GetLayer() ) ) continue; wxPoint shape_pos = pad->ShapePos(); for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { /* In order to make some calculations more easier or faster, * pads and tracks coordinates will be made relative * to the segment origin */ wxPoint origin = textShape[jj]; // origin will be the origin of other coordinates m_segmEnd = textShape[jj+1] - origin; wxPoint delta = m_segmEnd; m_segmAngle = 0; // for a non horizontal or vertical segment Compute the segment angle // in tenths of degrees and its length if( delta.x || delta.y ) // delta.x == delta.y == 0 for vias { // Compute the segment angle in 0,1 degrees m_segmAngle = ArcTangente( delta.y, delta.x ); // Compute the segment length: we build an equivalent rotated segment, // this segment is horizontal, therefore dx = length RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0 } m_segmLength = delta.x; m_padToTestPos = shape_pos - origin; if( !checkClearanceSegmToPad( pad, text->GetThickness(), pad->GetClearance(NULL) ) ) { addMarkerToPcb( fillMarker( pad, text, DRCE_PAD_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } } }
/** * Function Inspect * is the examining function within the INSPECTOR which is passed to the * Iterate function. Searches and collects all the objects that the old * function PcbGeneralLocateAndDisplay() would find, except that it keeps all * that it finds and does not do any displaying. * * @param testItem An EDA_ITEM to examine. * @param testData The const void* testData, not used here. * @return SEARCH_RESULT - SEARCH_QUIT if the Iterator is to stop the scan, * else SCAN_CONTINUE; */ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, const void* testData ) { BOARD_ITEM* item = (BOARD_ITEM*) testItem; MODULE* module = NULL; D_PAD* pad = NULL; bool pad_through = false; SEGVIA* via = NULL; MARKER_PCB* marker = NULL; #if 0 // debugging static int breakhere = 0; switch( item->Type() ) { case PCB_PAD_T: { MODULE* m = (MODULE*) item->GetParent(); if( m->GetReference() == wxT( "Y2" ) ) { breakhere++; } } break; case PCB_VIA_T: breakhere++; break; case PCB_TRACE_T: breakhere++; break; case PCB_ZONE_T: breakhere++; break; case PCB_TEXT_T: breakhere++; break; case PCB_LINE_T: breakhere++; break; case PCB_DIMENSION_T: breakhere++; break; case PCB_MODULE_TEXT_T: { TEXTE_MODULE* tm = (TEXTE_MODULE*) item; if( tm->GetText() == wxT( "10uH" ) ) { breakhere++; } } break; case PCB_MODULE_T: { MODULE* m = (MODULE*) item; if( m->GetReference() == wxT( "C98" ) ) { breakhere++; } } break; case PCB_MARKER_T: breakhere++; break; default: breakhere++; break; } #endif switch( item->Type() ) { case PCB_PAD_T: // there are pad specific visibility controls. // Criterias to select a pad is: // for smd pads: the module parent must be seen, and pads on the corresponding // board side must be seen // if pad is a thru hole, then it can be visible when its parent module is not. // for through pads: pads on Front or Back board sides must be seen pad = (D_PAD*) item; if( (pad->GetAttribute() != PAD_SMD) && (pad->GetAttribute() != PAD_CONN) ) // a hole is present, so multiple layers { // proceed to the common tests below, but without the parent module test, // by leaving module==NULL, but having pad != null pad_through = true; } else // smd, so use pads test after module test { module = (MODULE*) item->GetParent(); } break; case PCB_VIA_T: // vias are on many layers, so layer test is specific via = (SEGVIA*) item; break; case PCB_TRACE_T: break; case PCB_ZONE_T: break; case PCB_ZONE_AREA_T: break; case PCB_TEXT_T: break; case PCB_LINE_T: break; case PCB_DIMENSION_T: break; case PCB_TARGET_T: break; case PCB_MODULE_TEXT_T: module = (MODULE*) item->GetParent(); if( m_Guide->IgnoreMTextsMarkedNoShow() && !( (TEXTE_MODULE*) item )->IsVisible() ) goto exit; if( module ) { if( m_Guide->IgnoreMTextsOnCopper() && module->GetLayer()==LAYER_N_BACK ) goto exit; if( m_Guide->IgnoreMTextsOnCmp() && module->GetLayer()==LAYER_N_FRONT ) goto exit; if( m_Guide->IgnoreModulesVals() && item == &module->Value() ) goto exit; if( m_Guide->IgnoreModulesRefs() && item == &module->Reference() ) goto exit; } break; case PCB_MODULE_T: module = (MODULE*) item; break; case PCB_MARKER_T: marker = (MARKER_PCB*) item; break; default: break; } // common tests: if( module ) // true from case PCB_PAD_T, PCB_MODULE_TEXT_T, or PCB_MODULE_T { if( m_Guide->IgnoreModulesOnCu() && module->GetLayer()==LAYER_N_BACK ) goto exit; if( m_Guide->IgnoreModulesOnCmp() && module->GetLayer()==LAYER_N_FRONT ) goto exit; } // Pads are not sensitive to the layer visibility controls. // They all have their own separate visibility controls // skip them if not visible if( pad ) { if( m_Guide->IgnorePads() ) goto exit; if( ! pad_through ) { if( m_Guide->IgnorePadsOnFront() && pad->IsOnLayer(LAYER_N_FRONT ) ) goto exit; if( m_Guide->IgnorePadsOnBack() && pad->IsOnLayer(LAYER_N_BACK ) ) goto exit; } } if( marker ) { // Markers are not sensitive to the layer if( marker->HitTest( m_RefPos ) ) Append( item ); goto exit; } if( item->IsOnLayer( m_Guide->GetPreferredLayer() ) || m_Guide->IgnorePreferredLayer() ) { LAYER_NUM layer = item->GetLayer(); // Modules and their subcomponents: text and pads are not sensitive to the layer // visibility controls. They all have their own separate visibility controls // for vias, GetLayer() has no meaning, but IsOnLayer() works fine if( via || module || pad || m_Guide->IsLayerVisible( layer ) || !m_Guide->IgnoreNonVisibleLayers() ) { if( !m_Guide->IsLayerLocked( layer ) || !m_Guide->IgnoreLockedLayers() ) { if( !item->IsLocked() || !m_Guide->IgnoreLockedItems() ) { if( item->HitTest( m_RefPos ) ) { Append( item ); goto exit; } } } } } if( m_Guide->IncludeSecondary() ) { // for now, "secondary" means "tolerate any layer". It has // no effect on other criteria, since there is a separate "ignore" control for // those in the COLLECTORS_GUIDE LAYER_NUM layer = item->GetLayer(); // Modules and their subcomponents: text and pads are not sensitive to the layer // visibility controls. They all have their own separate visibility controls if( via || module || pad || m_Guide->IsLayerVisible( layer ) || !m_Guide->IgnoreNonVisibleLayers() ) { if( !m_Guide->IsLayerLocked( layer ) || !m_Guide->IgnoreLockedLayers() ) { if( !item->IsLocked() || !m_Guide->IgnoreLockedItems() ) { if( item->HitTest( m_RefPos ) ) { Append2nd( item ); goto exit; } } } } } exit: return SEARCH_CONTINUE; // always when collecting }
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 ); } } } } }
// todo: explain the selection heuristics void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) const { std::set<BOARD_ITEM*> rejected; const double footprintAreaRatio = 0.2; const double modulePadMinCoverRatio = 0.45; const double padViaAreaRatio = 0.5; const double trackViaLengthRatio = 2.0; const double trackTrackLengthRatio = 0.3; const double textToFeatureMinRatio = 0.2; const double textToFootprintMinRatio = 0.4; // If the common area of two compared items is above the following threshold, they cannot // be rejected (it means they overlap and it might be hard to pick one by selecting // its unique area). const double commonAreaRatio = 0.6; LAYER_ID actLayer = m_frame->GetActiveLayer(); LSET silkLayers( 2, B_SilkS, F_SilkS ); if( silkLayers[actLayer] ) { std::set<BOARD_ITEM*> preferred; for( int i = 0; i < aCollector.GetCount(); ++i ) { BOARD_ITEM* item = aCollector[i]; KICAD_T type = item->Type(); if( ( type == PCB_MODULE_TEXT_T || type == PCB_TEXT_T || type == PCB_LINE_T ) && silkLayers[item->GetLayer()] ) { preferred.insert( item ); } } if( preferred.size() != 0 ) { aCollector.Empty(); for( BOARD_ITEM* item : preferred ) aCollector.Append( item ); return; } } if( aCollector.CountType( PCB_MODULE_TEXT_T ) > 0 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if( TEXTE_MODULE* txt = dyn_cast<TEXTE_MODULE*>( aCollector[i] ) ) { double textArea = calcArea( txt ); for( int j = 0; j < aCollector.GetCount(); ++j ) { if( i == j ) continue; BOARD_ITEM* item = aCollector[j]; double itemArea = calcArea( item ); double areaRatio = calcRatio( textArea, itemArea ); double commonArea = calcCommonArea( txt, item ); double itemCommonRatio = calcRatio( commonArea, itemArea ); double txtCommonRatio = calcRatio( commonArea, textArea ); if( item->Type() == PCB_MODULE_T && areaRatio < textToFootprintMinRatio && itemCommonRatio < commonAreaRatio ) rejected.insert( item ); switch( item->Type() ) { case PCB_TRACE_T: case PCB_PAD_T: case PCB_LINE_T: case PCB_VIA_T: case PCB_MODULE_T: if( areaRatio > textToFeatureMinRatio && txtCommonRatio < commonAreaRatio ) rejected.insert( txt ); break; default: break; } } } } } if( aCollector.CountType( PCB_MODULE_T ) > 0 ) { double minArea = calcMinArea( aCollector, PCB_MODULE_T ); double maxArea = calcMaxArea( aCollector, PCB_MODULE_T ); if( calcRatio( minArea, maxArea ) <= footprintAreaRatio ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if( MODULE* mod = dyn_cast<MODULE*>( aCollector[i] ) ) { double normalizedArea = calcRatio( calcArea( mod ), maxArea ); if( normalizedArea > footprintAreaRatio ) rejected.insert( mod ); } } } } if( aCollector.CountType( PCB_PAD_T ) > 0 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if( D_PAD* pad = dyn_cast<D_PAD*>( aCollector[i] ) ) { double ratio = pad->GetParent()->PadCoverageRatio(); if( ratio < modulePadMinCoverRatio ) rejected.insert( pad->GetParent() ); } } } if( aCollector.CountType( PCB_VIA_T ) > 0 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if( VIA* via = dyn_cast<VIA*>( aCollector[i] ) ) { double viaArea = calcArea( via ); for( int j = 0; j < aCollector.GetCount(); ++j ) { if( i == j ) continue; BOARD_ITEM* item = aCollector[j]; double areaRatio = calcRatio( viaArea, calcArea( item ) ); if( item->Type() == PCB_MODULE_T && areaRatio < modulePadMinCoverRatio ) rejected.insert( item ); if( item->Type() == PCB_PAD_T && areaRatio < padViaAreaRatio ) rejected.insert( item ); if( TRACK* track = dyn_cast<TRACK*>( item ) ) { if( track->GetNetCode() != via->GetNetCode() ) continue; double lenRatio = (double) ( track->GetLength() + track->GetWidth() ) / (double) via->GetWidth(); if( lenRatio > trackViaLengthRatio ) rejected.insert( track ); } } } } } int nTracks = aCollector.CountType( PCB_TRACE_T ); if( nTracks > 0 ) { double maxLength = 0.0; double minLength = std::numeric_limits<double>::max(); double maxArea = 0.0; const TRACK* maxTrack = nullptr; for( int i = 0; i < aCollector.GetCount(); ++i ) { if( TRACK* track = dyn_cast<TRACK*> ( aCollector[i] ) ) { maxLength = std::max( track->GetLength(), maxLength ); maxLength = std::max( (double) track->GetWidth(), maxLength ); minLength = std::min( std::max( track->GetLength(), (double) track->GetWidth() ), minLength ); double area = track->GetLength() * track->GetWidth(); if( area > maxArea ) { maxArea = area; maxTrack = track; } } } if( maxLength > 0.0 && minLength / maxLength < trackTrackLengthRatio && nTracks > 1 ) { for( int i = 0; i < aCollector.GetCount(); ++i ) { if( TRACK* track = dyn_cast<TRACK*>( aCollector[i] ) ) { double ratio = std::max( (double) track->GetWidth(), track->GetLength() ) / maxLength; if( ratio > trackTrackLengthRatio ) rejected.insert( track ); } } } for( int j = 0; j < aCollector.GetCount(); ++j ) { if( MODULE* mod = dyn_cast<MODULE*>( aCollector[j] ) ) { double ratio = calcRatio( maxArea, mod->GetFootprintRect().GetArea() ); if( ratio < modulePadMinCoverRatio && calcCommonArea( maxTrack, mod ) < commonAreaRatio ) rejected.insert( mod ); } } } if( (unsigned) aCollector.GetCount() > rejected.size() ) // do not remove everything { for( BOARD_ITEM* item : rejected ) { aCollector.Remove( item ); } } }