PNS_PCBNEW_RULE_RESOLVER::PNS_PCBNEW_RULE_RESOLVER( BOARD* aBoard, PNS::ROUTER* aRouter ) : m_router( aRouter ), m_board( aBoard ) { PNS::NODE* world = m_router->GetWorld(); PNS::TOPOLOGY topo( world ); m_netClearanceCache.resize( m_board->GetNetCount() ); // Build clearance cache for net classes for( unsigned int i = 0; i < m_board->GetNetCount(); i++ ) { NETINFO_ITEM* ni = m_board->FindNet( i ); if( ni == NULL ) continue; CLEARANCE_ENT ent; ent.coupledNet = DpCoupledNet( i ); wxString netClassName = ni->GetClassName(); NETCLASSPTR nc = m_board->GetDesignSettings().m_NetClasses.Find( netClassName ); int clearance = nc->GetClearance(); ent.clearance = clearance; m_netClearanceCache[i] = ent; wxLogTrace( "PNS", "Add net %u netclass %s clearance %d", i, netClassName.mb_str(), clearance ); } // Build clearance cache for pads for( MODULE* mod = m_board->m_Modules; mod ; mod = mod->Next() ) { auto moduleClearance = mod->GetLocalClearance(); for( D_PAD* pad = mod->PadsList(); pad; pad = pad->Next() ) { int padClearance = pad->GetLocalClearance(); if( padClearance > 0 ) m_localClearanceCache[ pad ] = padClearance; else if( moduleClearance > 0 ) m_localClearanceCache[ pad ] = moduleClearance; } } //printf("DefaultCL : %d\n", m_board->GetDesignSettings().m_NetClasses.Find ("Default clearance")->GetClearance()); m_overrideEnabled = false; m_defaultClearance = Millimeter2iu( 0.254 ); // m_board->m_NetClasses.Find ("Default clearance")->GetClearance(); m_overrideNetA = 0; m_overrideNetB = 0; m_overrideClearance = 0; m_useDpGap = false; }
void BOARD::DrawHighLight( EDA_DRAW_PANEL* am_canvas, wxDC* DC, int aNetCode ) { GR_DRAWMODE draw_mode; if( IsHighLightNetON() ) draw_mode = GR_HIGHLIGHT | GR_OR; else draw_mode = GR_AND | GR_HIGHLIGHT; // Redraw zones for( int ii = 0; ii < GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = GetArea( ii ); if( zone->GetNetCode() == aNetCode ) { zone->Draw( am_canvas, DC, draw_mode ); } } // Redraw any pads that have aNetCode for( MODULE* module = m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->PadsList(); pad; pad = pad->Next() ) { if( pad->GetNetCode() == aNetCode ) { pad->Draw( am_canvas, DC, draw_mode ); } } } // Redraw track and vias that have aNetCode for( TRACK* seg = m_Track; seg; seg = seg->Next() ) { if( seg->GetNetCode() == aNetCode ) { seg->Draw( am_canvas, DC, draw_mode ); } } }
MODULE* MWAVE::CreateMicrowaveInductor( INDUCTOR_PATTERN& inductorPattern, PCB_EDIT_FRAME* aPcbFrame, wxString& aErrorMessage ) { /* Build a microwave inductor footprint. * - Length Mself.lng * - Extremities Mself.m_Start and Mself.m_End * We must determine: * Mself.nbrin = number of segments perpendicular to the direction * (The coil nbrin will demicercles + 1 + 2 1 / 4 circle) * Mself.lbrin = length of a strand * Mself.radius = radius of rounded parts of the coil * Mself.delta = segments extremities connection between him and the coil even * * The equations are * Mself.m_Size.x = 2 * Mself.radius + Mself.lbrin * Mself.m_Size.y * Mself.delta = 2 + 2 * Mself.nbrin * Mself.radius * Mself.lng = 2 * Mself.delta / / connections to the coil + (Mself.nbrin-2) * Mself.lbrin / / length of the strands except 1st and last + (Mself.nbrin 1) * (PI * Mself.radius) / / length of rounded * Mself.lbrin + / 2 - Melf.radius * 2) / / length of 1st and last bit * * The constraints are: * Nbrin >= 2 * Mself.radius < Mself.m_Size.x * Mself.m_Size.y = Mself.radius * 4 + 2 * Mself.raccord * Mself.lbrin> Mself.radius * 2 * * The calculation is conducted in the following way: * Initially: * Nbrin = 2 * Radius = 4 * m_Size.x (arbitrarily fixed value) * Then: * Increasing the number of segments to the desired length * (Radius decreases if necessary) */ D_PAD* pad; int ll; wxString msg; auto pt = inductorPattern.m_End - inductorPattern.m_Start; int min_len = KiROUND( EuclideanNorm( pt ) ); inductorPattern.m_length = min_len; // Enter the desired length. msg = StringFromValue( g_UserUnit, inductorPattern.m_length ); wxTextEntryDialog dlg( nullptr, wxEmptyString, _( "Length of Trace:" ), msg ); if( dlg.ShowModal() != wxID_OK ) return nullptr; // canceled by user msg = dlg.GetValue(); inductorPattern.m_length = ValueFromString( g_UserUnit, msg ); // Control values (ii = minimum length) if( inductorPattern.m_length < min_len ) { aErrorMessage = _( "Requested length < minimum length" ); return nullptr; } // Calculate the elements. std::vector <wxPoint> buffer; ll = BuildCornersList_S_Shape( buffer, inductorPattern.m_Start, inductorPattern.m_End, inductorPattern.m_length, inductorPattern.m_Width ); if( !ll ) { aErrorMessage = _( "Requested length too large" ); return nullptr; } // Generate footprint. the value is also used as footprint name. msg = "L"; wxTextEntryDialog cmpdlg( nullptr, wxEmptyString, _( "Component Value:" ), msg ); cmpdlg.SetTextValidator( FILE_NAME_CHAR_VALIDATOR( &msg ) ); if( ( cmpdlg.ShowModal() != wxID_OK ) || msg.IsEmpty() ) return nullptr; // Aborted by user MODULE* module = aPcbFrame->CreateNewModule( msg ); // here the module is already in the BOARD, CreateNewModule() does that. module->SetFPID( LIB_ID( wxString( "mw_inductor" ) ) ); module->SetAttributes( MOD_VIRTUAL | MOD_CMS ); module->ClearFlags(); module->SetPosition( inductorPattern.m_End ); // Generate segments for( unsigned jj = 1; jj < buffer.size(); jj++ ) { EDGE_MODULE* PtSegm; PtSegm = new EDGE_MODULE( module ); PtSegm->SetStart( buffer[jj - 1] ); PtSegm->SetEnd( buffer[jj] ); PtSegm->SetWidth( inductorPattern.m_Width ); PtSegm->SetLayer( module->GetLayer() ); PtSegm->SetShape( S_SEGMENT ); PtSegm->SetStart0( PtSegm->GetStart() - module->GetPosition() ); PtSegm->SetEnd0( PtSegm->GetEnd() - module->GetPosition() ); module->GraphicalItemsList().PushBack( PtSegm ); } // Place a pad on each end of coil. pad = new D_PAD( module ); module->PadsList().PushFront( pad ); pad->SetName( "1" ); pad->SetPosition( inductorPattern.m_End ); pad->SetPos0( pad->GetPosition() - module->GetPosition() ); pad->SetSize( wxSize( inductorPattern.m_Width, inductorPattern.m_Width ) ); pad->SetLayerSet( LSET( module->GetLayer() ) ); pad->SetAttribute( PAD_ATTRIB_SMD ); pad->SetShape( PAD_SHAPE_CIRCLE ); D_PAD* newpad = new D_PAD( *pad ); module->PadsList().Insert( newpad, pad->Next() ); pad = newpad; pad->SetName( "2" ); pad->SetPosition( inductorPattern.m_Start ); pad->SetPos0( pad->GetPosition() - module->GetPosition() ); // Modify text positions. wxPoint refPos( ( inductorPattern.m_Start.x + inductorPattern.m_End.x ) / 2, ( inductorPattern.m_Start.y + inductorPattern.m_End.y ) / 2 ); wxPoint valPos = refPos; refPos.y -= module->Reference().GetTextSize().y; module->Reference().SetPosition( refPos ); valPos.y += module->Value().GetTextSize().y; module->Value().SetPosition( valPos ); module->CalculateBoundingBox(); return module; }
void PCB_BASE_EDIT_FRAME::SaveCopyInUndoList( const PICKED_ITEMS_LIST& aItemsList, UNDO_REDO_T aTypeCommand, const wxPoint& aTransformPoint ) { PICKED_ITEMS_LIST* commandToUndo = new PICKED_ITEMS_LIST(); commandToUndo->m_TransformPoint = aTransformPoint; // First, filter unnecessary stuff from the list (i.e. for multiple pads / labels modified), // take the first occurence of the module (we save copies of modules when one of its subitems // is changed). for( unsigned ii = 0; ii < aItemsList.GetCount(); ii++ ) { ITEM_PICKER curr_picker = aItemsList.GetItemWrapper(ii); BOARD_ITEM* item = (BOARD_ITEM*) aItemsList.GetPickedItem( ii ); // For items belonging to modules, we need to save state of the parent module if( item->Type() == PCB_MODULE_TEXT_T || item->Type() == PCB_MODULE_EDGE_T || item->Type() == PCB_PAD_T ) { // Item to be stored in the undo buffer is the parent module item = item->GetParent(); wxASSERT( item && item->Type() == PCB_MODULE_T ); if( item == NULL ) continue; // Check if the parent module has already been saved in another entry bool found = false; for( unsigned j = 0; j < commandToUndo->GetCount(); j++ ) { if( commandToUndo->GetPickedItem( j ) == item && commandToUndo->GetPickedItemStatus( j ) == UR_CHANGED ) { found = true; break; } } if( !found ) { // Create a clean copy of the parent module MODULE* orig = static_cast<MODULE*>( item ); MODULE* clone = new MODULE( *orig ); clone->SetParent( GetBoard() ); // Clear current flags (which can be temporary set by a current edit command) for( EDA_ITEM* loc_item = clone->GraphicalItemsList(); loc_item; loc_item = loc_item->Next() ) loc_item->ClearFlags(); for( D_PAD* pad = clone->PadsList(); pad; pad = pad->Next() ) pad->ClearFlags(); clone->Reference().ClearFlags(); clone->Value().ClearFlags(); ITEM_PICKER picker( item, UR_CHANGED ); picker.SetLink( clone ); commandToUndo->PushItem( picker ); orig->SetLastEditTime(); } else { continue; } } else { // Normal case: all other BOARD_ITEMs, are simply copied to the new list commandToUndo->PushItem( curr_picker ); } } for( unsigned ii = 0; ii < commandToUndo->GetCount(); ii++ ) { BOARD_ITEM* item = (BOARD_ITEM*) commandToUndo->GetPickedItem( ii ); UNDO_REDO_T command = commandToUndo->GetPickedItemStatus( ii ); if( command == UR_UNSPECIFIED ) { command = aTypeCommand; commandToUndo->SetPickedItemStatus( command, ii ); } wxASSERT( item ); switch( command ) { case UR_CHANGED: case UR_DRILLORIGIN: case UR_GRIDORIGIN: /* If needed, create a copy of item, and put in undo list * in the picker, as link * If this link is not null, the copy is already done */ if( commandToUndo->GetPickedItemLink( ii ) == NULL ) { EDA_ITEM* cloned = item->Clone(); commandToUndo->SetPickedItemLink( cloned, ii ); } break; case UR_MOVED: case UR_ROTATED: case UR_ROTATED_CLOCKWISE: case UR_FLIPPED: case UR_NEW: case UR_DELETED: break; default: { wxLogDebug( wxT( "SaveCopyInUndoList() error (unknown code %X)" ), command ); } break; } } if( commandToUndo->GetCount() ) { /* Save the copy in undo list */ GetScreen()->PushCommandToUndoList( commandToUndo ); /* Clear redo list, because after a new command one cannot redo a command */ GetScreen()->ClearUndoORRedoList( GetScreen()->m_RedoList ); } else { // Should not occur wxASSERT( false ); delete commandToUndo; } }
void ZONE_FILLER::buildZoneFeatureHoleList( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aFeatures ) const { int segsPerCircle; double correctionFactor; // Set the number of segments in arc approximations if( aZone->GetArcSegmentCount() == 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 = aZone->GetMinThickness() / 2; // When removing holes, the holes must be expanded by outline_half_thickness // to take in account the thickness of the zone outlines int zone_clearance = aZone->GetClearance() + outline_half_thickness; // When holes are created by non copper items (edge cut items), use only // the m_ZoneClearance parameter (zone clearance with no netclass clearance) int zone_to_edgecut_clearance = aZone->GetZoneClearance() + 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 = aZone->GetBoundingBox(); int biggest_clearance = m_board->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 */ /* Use a dummy pad to calculate hole clearance 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( m_board ); // Creates a dummy parent D_PAD dummypad( &dummymodule ); for( MODULE* module = m_board->m_Modules; module; module = module->Next() ) { D_PAD* nextpad; for( D_PAD* pad = module->PadsList(); pad != NULL; pad = nextpad ) { nextpad = pad->Next(); // pad pointer can be modified by next code, so // calculate the next pad here if( !pad->IsOnLayer( aZone->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() != aZone->GetNetCode() ) || ( pad->GetNetCode() <= 0 ) ) { int 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_SHAPE_CUSTOM can have a specific keepout, to avoid to break the shape if( pad->GetShape() == PAD_SHAPE_CUSTOM && pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) { // the pad shape in zone can be its convex hull or // the shape itself SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() ); outline.Inflate( KiROUND( clearance * correctionFactor ), segsPerCircle ); pad->CustomShapeAsPolygonToBoardPosition( &outline, pad->GetPosition(), pad->GetOrientation() ); if( pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) { std::vector<wxPoint> convex_hull; BuildConvexHull( convex_hull, outline ); aFeatures.NewOutline(); for( unsigned ii = 0; ii < convex_hull.size(); ++ii ) aFeatures.Append( convex_hull[ii] ); } else aFeatures.Append( outline ); } else pad->TransformShapeWithClearanceToPolygon( aFeatures, clearance, segsPerCircle, correctionFactor ); } continue; } // Pads are removed from zone if the setup is PAD_ZONE_CONN_NONE // or if they have a custom shape, because a thermal relief will break // the shape if( aZone->GetPadConnection( pad ) == PAD_ZONE_CONN_NONE || pad->GetShape() == PAD_SHAPE_CUSTOM ) { int gap = zone_clearance; int thermalGap = aZone->GetThermalReliefGap( pad ); gap = std::max( gap, thermalGap ); item_boundingbox = pad->GetBoundingBox(); item_boundingbox.Inflate( gap ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { // PAD_SHAPE_CUSTOM has a specific keepout, to avoid to break the shape // the pad shape in zone can be its convex hull or the shape itself if( pad->GetShape() == PAD_SHAPE_CUSTOM && pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) { // the pad shape in zone can be its convex hull or // the shape itself SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() ); outline.Inflate( KiROUND( gap * correctionFactor ), segsPerCircle ); pad->CustomShapeAsPolygonToBoardPosition( &outline, pad->GetPosition(), pad->GetOrientation() ); std::vector<wxPoint> convex_hull; BuildConvexHull( convex_hull, outline ); aFeatures.NewOutline(); for( unsigned ii = 0; ii < convex_hull.size(); ++ii ) aFeatures.Append( convex_hull[ii] ); } else pad->TransformShapeWithClearanceToPolygon( aFeatures, gap, segsPerCircle, correctionFactor ); } } } } /* Add holes (i.e. tracks and vias areas as polygons outlines) * in cornerBufferPolysToSubstract */ for( auto track : m_board->Tracks() ) { if( !track->IsOnLayer( aZone->GetLayer() ) ) continue; if( track->GetNetCode() == aZone->GetNetCode() && ( aZone->GetNetCode() != 0) ) continue; int 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( auto module : m_board->Modules() ) { for( auto item : module->GraphicalItems() ) { if( !item->IsOnLayer( aZone->GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) ) continue; if( item->Type() != PCB_MODULE_EDGE_T ) continue; item_boundingbox = item->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { int zclearance = zone_clearance; if( item->IsOnLayer( Edge_Cuts ) ) // use only the m_ZoneClearance, not the clearance using // the netclass value, because we do not have a copper item zclearance = zone_to_edgecut_clearance; ( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon( aFeatures, zclearance, segsPerCircle, correctionFactor ); } } } // Add graphic items (copper texts) and board edges // Currently copper texts have no net, so only the zone_clearance // is used. for( auto item : m_board->Drawings() ) { if( item->GetLayer() != aZone->GetLayer() && item->GetLayer() != Edge_Cuts ) continue; int zclearance = zone_clearance; if( item->GetLayer() == Edge_Cuts ) // use only the m_ZoneClearance, not the clearance using // the netclass value, because we do not have a copper item zclearance = zone_to_edgecut_clearance; switch( item->Type() ) { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( aFeatures, zclearance, segsPerCircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon( aFeatures, zclearance ); break; default: break; } } // Add zones outlines having an higher priority and keepout for( int ii = 0; ii < m_board->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = m_board->GetArea( ii ); // If the zones share no common layers if( !aZone->CommonLayerExists( zone->GetLayerSet() ) ) continue; if( !zone->GetIsKeepout() && zone->GetPriority() <= aZone->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 = aZone->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( auto module : m_board->Modules() ) { for( auto pad : module->Pads() ) { // Rejects non-standard pads with tht-only thermal reliefs if( aZone->GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL && pad->GetAttribute() != PAD_ATTRIB_STANDARD ) continue; if( aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL && aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL ) continue; if( !pad->IsOnLayer( aZone->GetLayer() ) ) continue; if( pad->GetNetCode() != aZone->GetNetCode() ) continue; item_boundingbox = pad->GetBoundingBox(); int thermalGap = aZone->GetThermalReliefGap( pad ); item_boundingbox.Inflate( thermalGap, thermalGap ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { CreateThermalReliefPadPolygon( aFeatures, *pad, thermalGap, aZone->GetThermalReliefCopperBridge( pad ), aZone->GetMinThickness(), segsPerCircle, correctionFactor, s_thermalRot ); } } } }
/* * Function DoPushPadProperties * Function to change pad properties for the given footprint or all identical footprints * aPad is the pattern. The given footprint is the parent of this pad * aSameFootprints: if true, make changes on all identical footprints * aPadShapeFilter: if true, make changes only on pads having the same shape as aPad * aPadOrientFilter: if true, make changes only on pads having the same orientation as aPad * aPadLayerFilter: if true, make changes only on pads having the same layers as aPad * aSaveForUndo: if true: create an entry in the Undo/Redo list * (usually: true in Schematic editor, false in Module editor) */ void PCB_BASE_FRAME::DoPushPadProperties( D_PAD* aPad, bool aSameFootprints, bool aPadShapeFilter, bool aPadOrientFilter, bool aPadLayerFilter, bool aSaveForUndo ) { MODULE* Module_Ref = aPad->GetParent(); double pad_orient = aPad->GetOrientation() - Module_Ref->GetOrientation(); // Prepare an undo list: if( aSaveForUndo ) { PICKED_ITEMS_LIST itemsList; if( aSameFootprints ) { for( MODULE* module = m_Pcb->m_Modules; module; module = module->Next() ) { if( module->GetFPID() == Module_Ref->GetFPID() ) { ITEM_PICKER itemWrapper( module, UR_CHANGED ); itemsList.PushItem( itemWrapper ); } } } else { ITEM_PICKER itemWrapper( Module_Ref, UR_CHANGED ); itemsList.PushItem( itemWrapper ); } SaveCopyInUndoList( itemsList, UR_CHANGED ); } // Update the current module and same others modules if requested. for( MODULE* module = m_Pcb->m_Modules; module; module = module->Next() ) { if( !aSameFootprints && (module != Module_Ref) ) continue; if( module->GetFPID() != Module_Ref->GetFPID() ) continue; // Erase module on screen module->SetFlags( DO_NOT_DRAW ); m_canvas->RefreshDrawingRect( module->GetBoundingBox() ); module->ClearFlags( DO_NOT_DRAW ); for( D_PAD* pad = module->PadsList(); pad; pad = pad->Next() ) { if( aPadShapeFilter && ( pad->GetShape() != aPad->GetShape() ) ) continue; double currpad_orient = pad->GetOrientation() - module->GetOrientation(); if( aPadOrientFilter && ( currpad_orient != pad_orient ) ) continue; if( aPadLayerFilter && ( pad->GetLayerSet() != aPad->GetLayerSet() ) ) continue; // Do not copy pad to itself, it can create issues with custom pad primitives. if( pad == aPad ) continue; pad->ImportSettingsFromMaster( *aPad ); } module->CalculateBoundingBox(); m_canvas->RefreshDrawingRect( module->GetBoundingBox() ); } OnModify(); }