/* Extract the D356 record from the modules (pads) */ static void build_pad_testpoints( BOARD *aPcb, std::vector <D356_RECORD>& aRecords ) { wxPoint origin = aPcb->GetAuxOrigin(); for( MODULE *module = aPcb->m_Modules; module != NULL; module = module->Next() ) { for( D_PAD *pad = module->Pads(); pad != NULL; pad = pad->Next() ) { D356_RECORD rk; rk.access = compute_pad_access_code( aPcb, pad->GetLayerMask() ); // It could be a mask only pad, we only handle pads with copper here if( rk.access != -1 ) { rk.netname = pad->GetNetname(); rk.refdes = module->GetReference(); pad->StringPadName( rk.pin ); rk.midpoint = false; // XXX MAYBE need to be computed (how?) const wxSize& drill = pad->GetDrillSize(); rk.drill = std::min( drill.x, drill.y ); rk.hole = (rk.drill != 0); rk.smd = pad->GetAttribute() == PAD_SMD; rk.mechanical = (pad->GetAttribute() == PAD_HOLE_NOT_PLATED); rk.x_location = pad->GetPosition().x - origin.x; rk.y_location = origin.y - pad->GetPosition().y; rk.x_size = pad->GetSize().x; // Rule: round pads have y = 0 if( pad->GetShape() == PAD_CIRCLE ) rk.y_size = 0; else rk.y_size = pad->GetSize().y; rk.rotation = -KiROUND( pad->GetOrientation() ) / 10; if( rk.rotation < 0 ) rk.rotation += 360; // the value indicates which sides are *not* accessible rk.soldermask = 3; if( pad->GetLayerMask() & SOLDERMASK_LAYER_FRONT) rk.soldermask &= ~1; if( pad->GetLayerMask() & SOLDERMASK_LAYER_BACK) rk.soldermask &= ~2; aRecords.push_back( rk ); } } } }
D_PAD* MODULE::GetPad( const wxPoint& aPosition, LAYER_MSK aLayerMask ) { for( D_PAD* pad = m_Pads; pad; pad = pad->Next() ) { // ... and on the correct layer. if( ( pad->GetLayerMask() & aLayerMask ) == 0 ) continue; if( pad->HitTest( aPosition ) ) return pad; } return NULL; }
/* Trace the pads of a module in sketch mode. * Used to display pads when when the module visibility is set to not visible * and we want to see pad through. * The pads must appear on the layers selected in LayerMask */ static void Trace_Pads_Only( EDA_DRAW_PANEL* panel, wxDC* DC, MODULE* aModule, int ox, int oy, LAYER_MSK aLayerMask, GR_DRAWMODE draw_mode ) { PCB_BASE_FRAME* frame = (PCB_BASE_FRAME*) panel->GetParent(); int tmp = frame->m_DisplayPadFill; frame->m_DisplayPadFill = false; // Draw pads. for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() ) { if( (pad->GetLayerMask() & aLayerMask) == 0 ) continue; pad->Draw( panel, DC, draw_mode, wxPoint( ox, oy ) ); } frame->m_DisplayPadFill = tmp; }
bool TRACKS_CLEANER::clean_vias() { TRACK* next_track; bool modified = false; for( TRACK* track = m_Brd->m_Track; track; track = track->Next() ) { // Correct via m_End defects (if any) if( track->Type() == PCB_VIA_T ) { if( track->GetStart() != track->GetEnd() ) track->SetEnd( track->GetStart() ); } if( track->GetShape() != VIA_THROUGH ) continue; // Search and delete others vias at same location TRACK* alt_track = track->Next(); for( ; alt_track != NULL; alt_track = next_track ) { next_track = alt_track->Next(); if( alt_track->GetShape() != VIA_THROUGH ) continue; if( alt_track->GetStart() != track->GetStart() ) continue; // delete via alt_track->UnLink(); delete alt_track; modified = true; } } // Delete Via on pads at same location for( TRACK* track = m_Brd->m_Track; track != NULL; track = next_track ) { next_track = track->Next(); if( track->GetShape() != VIA_THROUGH ) continue; // Examine the list of connected pads: // if one pad through is found, the via can be removed for( unsigned ii = 0; ii < track->m_PadsConnected.size(); ii++ ) { D_PAD * pad = track->m_PadsConnected[ii]; if( (pad->GetLayerMask() & ALL_CU_LAYERS) == ALL_CU_LAYERS ) { // redundant: via delete it track->UnLink(); delete track; modified = true; break; } } } return modified; }
bool DRC::doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_limit ) { LAYER_MSK layerMask = aRefPad->GetLayerMask() & ALL_CU_LAYERS; /* used to test DRC pad to holes: this dummy pad has the size and shape of the hole * to test pad to pad hole DRC, using the pad to pad DRC test function. * 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_pcb ); // Creates a dummy parent D_PAD dummypad( &dummymodule ); // Ensure the hole is on all copper layers dummypad.SetLayerMask( ALL_CU_LAYERS | dummypad.GetLayerMask() ); // Use the minimal local clearance value for the dummy pad. // The clearance of the active pad will be used as minimum distance to a hole // (a value = 0 means use netclass value) dummypad.SetLocalClearance( 1 ); for( D_PAD** pad_list = aStart; pad_list<aEnd; ++pad_list ) { D_PAD* pad = *pad_list; if( pad == aRefPad ) continue; // We can stop the test when pad->GetPosition().x > x_limit // because the list is sorted by X values if( pad->GetPosition().x > x_limit ) break; // No problem if pads are on different copper layers, // but their hole (if any ) can create DRC error because they are on all // copper layers, so we test them if( ( pad->GetLayerMask() & layerMask ) == 0 ) { // if holes are in the same location and have the same size and shape, // this can be accepted if( pad->GetPosition() == aRefPad->GetPosition() && pad->GetDrillSize() == aRefPad->GetDrillSize() && pad->GetDrillShape() == aRefPad->GetDrillShape() ) { if( aRefPad->GetDrillShape() == PAD_DRILL_CIRCLE ) continue; // for oval holes: must also have the same orientation if( pad->GetOrientation() == aRefPad->GetOrientation() ) continue; } /* Here, we must test clearance between holes and pads * dummy pad size and shape is adjusted to pad drill size and shape */ if( pad->GetDrillSize().x ) { // pad under testing has a hole, test this hole against pad reference dummypad.SetPosition( pad->GetPosition() ); dummypad.SetSize( pad->GetDrillSize() ); dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_OBLONG ? PAD_OVAL : PAD_CIRCLE ); dummypad.SetOrientation( pad->GetOrientation() ); if( !checkClearancePadToPad( aRefPad, &dummypad ) ) { // here we have a drc error on pad! m_currentMarker = fillMarker( pad, aRefPad, DRCE_HOLE_NEAR_PAD, m_currentMarker ); return false; } } if( aRefPad->GetDrillSize().x ) // pad reference has a hole { dummypad.SetPosition( aRefPad->GetPosition() ); dummypad.SetSize( aRefPad->GetDrillSize() ); dummypad.SetShape( aRefPad->GetDrillShape() == PAD_DRILL_OBLONG ? PAD_OVAL : PAD_CIRCLE ); dummypad.SetOrientation( aRefPad->GetOrientation() ); if( !checkClearancePadToPad( pad, &dummypad ) ) { // here we have a drc error on aRefPad! m_currentMarker = fillMarker( aRefPad, pad, DRCE_HOLE_NEAR_PAD, m_currentMarker ); return false; } } continue; } // The pad must be in a net (i.e pt_pad->GetNet() != 0 ), // But no problem if pads have the same netcode (same net) if( pad->GetNetCode() && ( aRefPad->GetNetCode() == pad->GetNetCode() ) ) continue; // if pads are from the same footprint if( pad->GetParent() == aRefPad->GetParent() ) { // and have the same pad number ( equivalent pads ) // one can argue that this 2nd test is not necessary, that any // two pads from a single module are acceptable. This 2nd test // should eventually be a configuration option. if( pad->PadNameEqual( aRefPad ) ) continue; } if( !checkClearancePadToPad( aRefPad, pad ) ) { // here we have a drc error! m_currentMarker = fillMarker( aRefPad, pad, DRCE_PAD_NEAR_PAD1, m_currentMarker ); return false; } } return true; }
int getOptimalModulePlacement( PCB_EDIT_FRAME* aFrame, MODULE* aModule, wxDC* aDC ) { int error = 1; wxPoint LastPosOK; double min_cost, curr_cost, Score; bool TstOtherSide; bool showRats = g_Show_Module_Ratsnest; BOARD* brd = aFrame->GetBoard(); aModule->CalculateBoundingBox(); g_Show_Module_Ratsnest = false; brd->m_Status_Pcb &= ~RATSNEST_ITEM_LOCAL_OK; aFrame->SetMsgPanel( aModule ); LastPosOK = RoutingMatrix.m_BrdBox.GetOrigin(); wxPoint mod_pos = aModule->GetPosition(); EDA_RECT fpBBox = aModule->GetFootprintRect(); // Move fpBBox to have the footprint position at (0,0) fpBBox.Move( -mod_pos ); wxPoint fpBBoxOrg = fpBBox.GetOrigin(); // Calculate the limit of the footprint position, relative // to the routing matrix area wxPoint xylimit = RoutingMatrix.m_BrdBox.GetEnd() - fpBBox.GetEnd(); wxPoint initialPos = RoutingMatrix.m_BrdBox.GetOrigin() - fpBBoxOrg; // Stay on grid. initialPos.x -= initialPos.x % RoutingMatrix.m_GridRouting; initialPos.y -= initialPos.y % RoutingMatrix.m_GridRouting; CurrPosition = initialPos; // Undraw the current footprint g_Offset_Module = wxPoint( 0, 0 ); DrawModuleOutlines( aFrame->GetCanvas(), aDC, aModule ); g_Offset_Module = mod_pos - CurrPosition; /* Examine pads, and set TstOtherSide to true if a footprint * has at least 1 pad through. */ TstOtherSide = false; if( RoutingMatrix.m_RoutingLayersCount > 1 ) { D_PAD* Pad; int otherLayerMask = LAYER_BACK; if( aModule->GetLayer() == LAYER_N_BACK ) otherLayerMask = LAYER_FRONT; for( Pad = aModule->Pads(); Pad != NULL; Pad = Pad->Next() ) { if( ( Pad->GetLayerMask() & otherLayerMask ) == 0 ) continue; TstOtherSide = true; break; } } // Draw the initial bounding box position EDA_COLOR_T color = BROWN; fpBBox.SetOrigin( fpBBoxOrg + CurrPosition ); draw_FootprintRect(aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, color); min_cost = -1.0; aFrame->SetStatusText( wxT( "Score ??, pos ??" ) ); for( ; CurrPosition.x < xylimit.x; CurrPosition.x += RoutingMatrix.m_GridRouting ) { wxYield(); if( aFrame->GetCanvas()->GetAbortRequest() ) { if( IsOK( aFrame, _( "OK to abort?" ) ) ) return ESC; else aFrame->GetCanvas()->SetAbortRequest( false ); } CurrPosition.y = initialPos.y; for( ; CurrPosition.y < xylimit.y; CurrPosition.y += RoutingMatrix.m_GridRouting ) { // Erase traces. draw_FootprintRect( aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, color ); fpBBox.SetOrigin( fpBBoxOrg + CurrPosition ); g_Offset_Module = mod_pos - CurrPosition; int keepOutCost = TstModuleOnBoard( brd, aModule, TstOtherSide ); // Draw at new place color = keepOutCost >= 0 ? BROWN : RED; draw_FootprintRect( aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, color ); if( keepOutCost >= 0 ) // i.e. if the module can be put here { error = 0; aFrame->build_ratsnest_module( aModule ); curr_cost = compute_Ratsnest_PlaceModule( brd ); Score = curr_cost + keepOutCost; if( (min_cost >= Score ) || (min_cost < 0 ) ) { LastPosOK = CurrPosition; min_cost = Score; wxString msg; msg.Printf( wxT( "Score %g, pos %s, %s" ), min_cost, GetChars( ::CoordinateToString( LastPosOK.x ) ), GetChars( ::CoordinateToString( LastPosOK.y ) ) ); aFrame->SetStatusText( msg ); } } } } // erasing the last traces GRRect( aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, 0, BROWN ); g_Show_Module_Ratsnest = showRats; // Regeneration of the modified variable. CurrPosition = LastPosOK; brd->m_Status_Pcb &= ~( RATSNEST_ITEM_LOCAL_OK | LISTE_PAD_OK ); MinCout = min_cost; return error; }
void DIALOG_PAD_PROPERTIES::PadPropertiesAccept( wxCommandEvent& event ) { if( !padValuesOK() ) return; bool rastnestIsChanged = false; int isign = m_isFlipped ? -1 : 1; transferDataToPad( &m_padMaster ); if( m_currentPad ) // Set current Pad parameters { wxSize size; MODULE* module = m_currentPad->GetParent(); m_parent->SaveCopyInUndoList( module, UR_CHANGED ); module->SetLastEditTime(); // redraw the area where the pad was, without pad (delete pad on screen) m_currentPad->SetFlags( DO_NOT_DRAW ); m_parent->GetCanvas()->RefreshDrawingRect( m_currentPad->GetBoundingBox() ); m_currentPad->ClearFlags( DO_NOT_DRAW ); // Update values m_currentPad->SetShape( m_padMaster.GetShape() ); m_currentPad->SetAttribute( m_padMaster.GetAttribute() ); if( m_currentPad->GetPosition() != m_padMaster.GetPosition() ) { m_currentPad->SetPosition( m_padMaster.GetPosition() ); rastnestIsChanged = true; } // compute the pos 0 value, i.e. pad position for module with orientation = 0 // i.e. relative to module origin (module position) wxPoint pt = m_currentPad->GetPosition() - module->GetPosition(); RotatePoint( &pt, -module->GetOrientation() ); m_currentPad->SetPos0( pt ); m_currentPad->SetOrientation( m_padMaster.GetOrientation() * isign + module->GetOrientation() ); m_currentPad->SetSize( m_padMaster.GetSize() ); size = m_padMaster.GetDelta(); size.y *= isign; m_currentPad->SetDelta( size ); m_currentPad->SetDrillSize( m_padMaster.GetDrillSize() ); m_currentPad->SetDrillShape( m_padMaster.GetDrillShape() ); wxPoint offset = m_padMaster.GetOffset(); offset.y *= isign; m_currentPad->SetOffset( offset ); m_currentPad->SetPadToDieLength( m_padMaster.GetPadToDieLength() ); if( m_currentPad->GetLayerMask() != m_padMaster.GetLayerMask() ) { rastnestIsChanged = true; m_currentPad->SetLayerMask( m_padMaster.GetLayerMask() ); } if( m_isFlipped ) m_currentPad->SetLayerMask( FlipLayerMask( m_currentPad->GetLayerMask() ) ); m_currentPad->SetPadName( m_padMaster.GetPadName() ); if( m_currentPad->GetNetname() != m_padMaster.GetNetname() ) { if( m_padMaster.GetNetname().IsEmpty() ) { rastnestIsChanged = true; m_currentPad->SetNet( 0 ); m_currentPad->SetNetname( wxEmptyString ); } else { const NETINFO_ITEM* net = m_board->FindNet( m_padMaster.GetNetname() ); if( net ) { rastnestIsChanged = true; m_currentPad->SetNetname( m_padMaster.GetNetname() ); m_currentPad->SetNet( net->GetNet() ); } else DisplayError( NULL, _( "Unknown netname, netname not changed" ) ); } } m_currentPad->SetLocalClearance( m_padMaster.GetLocalClearance() ); m_currentPad->SetLocalSolderMaskMargin( m_padMaster.GetLocalSolderMaskMargin() ); m_currentPad->SetLocalSolderPasteMargin( m_padMaster.GetLocalSolderPasteMargin() ); m_currentPad->SetLocalSolderPasteMarginRatio( m_padMaster.GetLocalSolderPasteMarginRatio() ); m_currentPad->SetZoneConnection( m_padMaster.GetZoneConnection() ); m_currentPad->SetThermalWidth( m_padMaster.GetThermalWidth() ); m_currentPad->SetThermalGap( m_padMaster.GetThermalGap() ); module->CalculateBoundingBox(); m_parent->SetMsgPanel( m_currentPad ); // redraw the area where the pad was m_parent->GetCanvas()->RefreshDrawingRect( m_currentPad->GetBoundingBox() ); m_parent->OnModify(); } EndModal( wxID_OK ); if( rastnestIsChanged ) // The net ratsnest must be recalculated m_board->m_Status_Pcb = 0; }
// test if all values are acceptable for the pad bool DIALOG_PAD_PROPERTIES::padValuesOK() { bool error = transferDataToPad( m_dummyPad ); wxArrayString error_msgs; wxString msg; // Test for incorrect values if( (m_dummyPad->GetSize().x < m_dummyPad->GetDrillSize().x) || (m_dummyPad->GetSize().y < m_dummyPad->GetDrillSize().y) ) { error_msgs.Add( _( "Incorrect value for pad drill: pad drill bigger than pad size" ) ); } LAYER_MSK padlayers_mask = m_dummyPad->GetLayerMask(); if( ( padlayers_mask == 0 ) && ( m_dummyPad->GetAttribute() != PAD_HOLE_NOT_PLATED ) ) error_msgs.Add( _( "Error: pad has no layer and is not a mechanical pad" ) ); padlayers_mask &= (LAYER_BACK | LAYER_FRONT); if( padlayers_mask == 0 ) { if( m_dummyPad->GetDrillSize().x || m_dummyPad->GetDrillSize().y ) { msg = _( "Error: pad is not on a copper layer and has a hole" ); if( m_dummyPad->GetAttribute() == PAD_HOLE_NOT_PLATED ) { msg += wxT("\n"); msg += _( "For NPTH pad, set pad drill value to pad size value,\n" "if you do not want this pad plotted in gerber files" ); } error_msgs.Add( msg ); } } wxPoint max_size; max_size.x = std::abs( m_dummyPad->GetOffset().x ); max_size.y = std::abs( m_dummyPad->GetOffset().y ); max_size.x += m_dummyPad->GetDrillSize().x / 2; max_size.y += m_dummyPad->GetDrillSize().y / 2; if( ( m_dummyPad->GetSize().x / 2 < max_size.x ) || ( m_dummyPad->GetSize().y / 2 < max_size.y ) ) { error_msgs.Add( _( "Incorrect value for pad offset" ) ); } if( error ) { error_msgs.Add( _( "Too large value for pad delta size" ) ); } switch( m_dummyPad->GetAttribute() ) { case PAD_STANDARD : // Pad through hole, a hole is expected if( m_dummyPad->GetDrillSize().x <= 0 ) error_msgs.Add( _( "Incorrect value for pad drill (too small value)" ) ); break; case PAD_SMD: // SMD and Connector pads (One external copper layer only) if( (padlayers_mask & LAYER_BACK) && (padlayers_mask & LAYER_FRONT) ) error_msgs.Add( _( "Error: only one copper layer allowed for this pad" ) ); break; case PAD_CONN: // connectors can have pads on "All" Cu layers. break; case PAD_HOLE_NOT_PLATED: // Not plated break; } if( error_msgs.GetCount() ) { HTML_MESSAGE_BOX dlg( this, _("Pad setup errors list" ) ); dlg.ListSet( error_msgs ); dlg.ShowModal(); } return error_msgs.GetCount() == 0; }
void DIALOG_PAD_PROPERTIES::initValues() { wxString msg; double angle; // Setup layers names from board // Should be made first, before calling m_rbCopperLayersSel->SetSelection() m_rbCopperLayersSel->SetString( 0, m_board->GetLayerName( LAYER_N_FRONT ) ); m_rbCopperLayersSel->SetString( 1, m_board->GetLayerName( LAYER_N_BACK ) ); m_PadLayerAdhCmp->SetLabel( m_board->GetLayerName( ADHESIVE_N_FRONT ) ); m_PadLayerAdhCu->SetLabel( m_board->GetLayerName( ADHESIVE_N_BACK ) ); m_PadLayerPateCmp->SetLabel( m_board->GetLayerName( SOLDERPASTE_N_FRONT ) ); m_PadLayerPateCu->SetLabel( m_board->GetLayerName( SOLDERPASTE_N_BACK ) ); m_PadLayerSilkCmp->SetLabel( m_board->GetLayerName( SILKSCREEN_N_FRONT ) ); m_PadLayerSilkCu->SetLabel( m_board->GetLayerName( SILKSCREEN_N_BACK ) ); m_PadLayerMaskCmp->SetLabel( m_board->GetLayerName( SOLDERMASK_N_FRONT ) ); m_PadLayerMaskCu->SetLabel( m_board->GetLayerName( SOLDERMASK_N_BACK ) ); m_PadLayerECO1->SetLabel( m_board->GetLayerName( ECO1_N ) ); m_PadLayerECO2->SetLabel( m_board->GetLayerName( ECO2_N ) ); m_PadLayerDraft->SetLabel( m_board->GetLayerName( DRAW_N ) ); m_isFlipped = false; if( m_currentPad ) { MODULE* module = m_currentPad->GetParent(); if( module->GetLayer() == LAYER_N_BACK ) { m_isFlipped = true; m_staticModuleSideValue->SetLabel( _( "Back side (footprint is mirrored)" ) ); } msg.Printf( wxT( "%.1f" ), module->GetOrientation() / 10.0 ); m_staticModuleRotValue->SetLabel( msg ); } if( m_isFlipped ) { wxPoint pt = m_dummyPad->GetOffset(); NEGATE( pt.y ); m_dummyPad->SetOffset( pt ); wxSize sz = m_dummyPad->GetDelta(); NEGATE( sz.y ); m_dummyPad->SetDelta( sz ); // flip pad's layers m_dummyPad->SetLayerMask( FlipLayerMask( m_dummyPad->GetLayerMask() ) ); } m_staticTextWarningPadFlipped->Show(m_isFlipped); m_PadNumCtrl->SetValue( m_dummyPad->GetPadName() ); m_PadNetNameCtrl->SetValue( m_dummyPad->GetNetname() ); // Display current unit name in dialog: m_PadPosX_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); m_PadPosY_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); m_PadDrill_X_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); m_PadDrill_Y_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); m_PadShapeSizeX_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); m_PadShapeSizeY_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); m_PadShapeOffsetX_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); m_PadShapeOffsetY_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); m_PadShapeDelta_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); m_PadLengthDie_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); // Display current pad masks clearances units m_NetClearanceUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); m_SolderMaskMarginUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); m_SolderPasteMarginUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); m_ThermalWidthUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); m_ThermalGapUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) ); // Display current pad parameters units: PutValueInLocalUnits( *m_PadPosition_X_Ctrl, m_dummyPad->GetPosition().x ); PutValueInLocalUnits( *m_PadPosition_Y_Ctrl, m_dummyPad->GetPosition().y ); PutValueInLocalUnits( *m_PadDrill_X_Ctrl, m_dummyPad->GetDrillSize().x ); PutValueInLocalUnits( *m_PadDrill_Y_Ctrl, m_dummyPad->GetDrillSize().y ); PutValueInLocalUnits( *m_ShapeSize_X_Ctrl, m_dummyPad->GetSize().x ); PutValueInLocalUnits( *m_ShapeSize_Y_Ctrl, m_dummyPad->GetSize().y ); PutValueInLocalUnits( *m_ShapeOffset_X_Ctrl, m_dummyPad->GetOffset().x ); PutValueInLocalUnits( *m_ShapeOffset_Y_Ctrl, m_dummyPad->GetOffset().y ); if( m_dummyPad->GetDelta().x ) { PutValueInLocalUnits( *m_ShapeDelta_Ctrl, m_dummyPad->GetDelta().x ); m_trapDeltaDirChoice->SetSelection( 0 ); } else { PutValueInLocalUnits( *m_ShapeDelta_Ctrl, m_dummyPad->GetDelta().y ); m_trapDeltaDirChoice->SetSelection( 1 ); } PutValueInLocalUnits( *m_LengthPadToDieCtrl, m_dummyPad->GetPadToDieLength() ); PutValueInLocalUnits( *m_NetClearanceValueCtrl, m_dummyPad->GetLocalClearance() ); PutValueInLocalUnits( *m_SolderMaskMarginCtrl, m_dummyPad->GetLocalSolderMaskMargin() ); PutValueInLocalUnits( *m_ThermalWidthCtrl, m_dummyPad->GetThermalWidth() ); PutValueInLocalUnits( *m_ThermalGapCtrl, m_dummyPad->GetThermalGap() ); // These 2 parameters are usually < 0, so prepare entering a negative value, if current is 0 PutValueInLocalUnits( *m_SolderPasteMarginCtrl, m_dummyPad->GetLocalSolderPasteMargin() ); if( m_dummyPad->GetLocalSolderPasteMargin() == 0 ) m_SolderPasteMarginCtrl->SetValue( wxT( "-" ) + m_SolderPasteMarginCtrl->GetValue() ); msg.Printf( wxT( "%f" ), m_dummyPad->GetLocalSolderPasteMarginRatio() * 100.0 ); if( m_dummyPad->GetLocalSolderPasteMarginRatio() == 0.0 && msg[0] == '0' ) // Sometimes Printf adds a sign if the value is small m_SolderPasteMarginRatioCtrl->SetValue( wxT( "-" ) + msg ); else m_SolderPasteMarginRatioCtrl->SetValue( msg ); switch( m_dummyPad->GetZoneConnection() ) { default: case UNDEFINED_CONNECTION: m_ZoneConnectionChoice->SetSelection( 0 ); break; case PAD_IN_ZONE: m_ZoneConnectionChoice->SetSelection( 1 ); break; case THERMAL_PAD: m_ZoneConnectionChoice->SetSelection( 2 ); break; case PAD_NOT_IN_ZONE: m_ZoneConnectionChoice->SetSelection( 3 ); break; } if( m_currentPad ) { MODULE* module = m_currentPad->GetParent(); angle = m_currentPad->GetOrientation() - module->GetOrientation(); if( m_isFlipped ) NEGATE( angle ); m_dummyPad->SetOrientation( angle ); } angle = m_dummyPad->GetOrientation(); NORMALIZE_ANGLE_180( angle ); // ? normalizing is in D_PAD::SetOrientation() // Set layers used by this pad: : setPadLayersList( m_dummyPad->GetLayerMask() ); // Pad Orient switch( int( angle ) ) { case 0: m_PadOrient->SetSelection( 0 ); break; case 900: m_PadOrient->SetSelection( 1 ); break; case -900: m_PadOrient->SetSelection( 2 ); break; case 1800: case -1800: m_PadOrient->SetSelection( 3 ); break; default: m_PadOrient->SetSelection( 4 ); break; } switch( m_dummyPad->GetShape() ) { default: case PAD_CIRCLE: m_PadShape->SetSelection( 0 ); break; case PAD_OVAL: m_PadShape->SetSelection( 1 ); break; case PAD_RECT: m_PadShape->SetSelection( 2 ); break; case PAD_TRAPEZOID: m_PadShape->SetSelection( 3 ); break; } msg.Printf( wxT( "%g" ), angle ); m_PadOrientCtrl->SetValue( msg ); // Type of pad selection m_PadType->SetSelection( 0 ); for( unsigned ii = 0; ii < NBTYPES; ii++ ) { if( CodeType[ii] == m_dummyPad->GetAttribute() ) { m_PadType->SetSelection( ii ); break; } } // Enable/disable Pad name,and pad length die // (disable for NPTH pads (mechanical pads) bool enable = m_dummyPad->GetAttribute() != PAD_HOLE_NOT_PLATED; m_PadNumCtrl->Enable( enable ); m_PadNetNameCtrl->Enable( enable ); m_LengthPadToDieCtrl->Enable( enable ); if( m_dummyPad->GetDrillShape() != PAD_OVAL ) m_DrillShapeCtrl->SetSelection( 0 ); else m_DrillShapeCtrl->SetSelection( 1 ); // Update some dialog widgets state (Enable/disable options): wxCommandEvent cmd_event; setPadLayersList( m_dummyPad->GetLayerMask() ); OnDrillShapeSelected( cmd_event ); OnPadShapeSelection( cmd_event ); }
void DIALOG_PAD_PROPERTIES::OnPaintShowPanel( wxPaintEvent& event ) { wxPaintDC dc( m_panelShowPad ); PAD_DRAWINFO drawInfo; EDA_COLOR_T color = BLACK; if( m_dummyPad->GetLayerMask() & LAYER_FRONT ) { color = m_board->GetVisibleElementColor( PAD_FR_VISIBLE ); } if( m_dummyPad->GetLayerMask() & LAYER_BACK ) { color = ColorMix( color, m_board->GetVisibleElementColor( PAD_BK_VISIBLE ) ); } // What could happen: the pad color is *actually* black, or no // copper was selected if( color == BLACK ) color = LIGHTGRAY; drawInfo.m_Color = color; drawInfo.m_HoleColor = DARKGRAY; drawInfo.m_Offset = m_dummyPad->GetPosition(); drawInfo.m_Display_padnum = true; drawInfo.m_Display_netname = true; if( m_dummyPad->GetAttribute() == PAD_HOLE_NOT_PLATED ) drawInfo.m_ShowNotPlatedHole = true; // Shows the local pad clearance drawInfo.m_PadClearance = m_dummyPad->GetLocalClearance(); wxSize dc_size = dc.GetSize(); dc.SetDeviceOrigin( dc_size.x / 2, dc_size.y / 2 ); // Calculate a suitable scale to fit the available draw area int dim = m_dummyPad->GetSize().x + std::abs( m_dummyPad->GetDelta().y); if( m_dummyPad->GetLocalClearance() > 0 ) dim += m_dummyPad->GetLocalClearance() * 2; double scale = (double) dc_size.x / dim; dim = m_dummyPad->GetSize().y + std::abs( m_dummyPad->GetDelta().x); if( m_dummyPad->GetLocalClearance() > 0 ) dim += m_dummyPad->GetLocalClearance() * 2; double altscale = (double) dc_size.y / dim; scale = std::min( scale, altscale ); // Give a margin scale *= 0.7; dc.SetUserScale( scale, scale ); GRResetPenAndBrush( &dc ); m_dummyPad->DrawShape( NULL, &dc, drawInfo ); // Draw X and Y axis. // this is particularly useful to show the reference position of pads // with offset and no hole GRLine( NULL, &dc, -dim, 0, dim, 0, 0, BLUE ); // X axis GRLine( NULL, &dc, 0, -dim, 0, dim, 0, BLUE ); // Y axis event.Skip(); }
/* Creates the plot for silkscreen layers * Silkscreen layers have specific requirement for pads (not filled) and texts * (with option to remove them from some copper areas (pads...) */ void PlotSilkScreen( BOARD *aBoard, PLOTTER* aPlotter, LAYER_MSK aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt ) { BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); itemplotter.SetLayerMask( aLayerMask ); // Plot edge layer and graphic items itemplotter.PlotBoardGraphicItems(); // Plot footprint outlines : itemplotter.Plot_Edges_Modules(); // Plot pads (creates pads outlines, for pads on silkscreen layers) int layersmask_plotpads = aLayerMask; // Calculate the mask layers of allowed layers for pads if( !aPlotOpt.GetPlotPadsOnSilkLayer() ) // Do not plot pads on silk screen layers layersmask_plotpads &= ~(SILKSCREEN_LAYER_BACK | SILKSCREEN_LAYER_FRONT ); if( layersmask_plotpads ) { for( MODULE* Module = aBoard->m_Modules; Module; Module = Module->Next() ) { for( D_PAD * pad = Module->Pads(); pad != NULL; pad = pad->Next() ) { // See if the pad is on this layer LAYER_MSK masklayer = pad->GetLayerMask(); if( (masklayer & layersmask_plotpads) == 0 ) continue; EDA_COLOR_T color = ColorFromInt(0); if( (layersmask_plotpads & SILKSCREEN_LAYER_BACK) ) color = aBoard->GetLayerColor( SILKSCREEN_N_BACK ); if((layersmask_plotpads & SILKSCREEN_LAYER_FRONT ) ) color = ColorFromInt( color | aBoard->GetLayerColor( SILKSCREEN_N_FRONT ) ); itemplotter.PlotPad( pad, color, LINE ); } } } // 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() ) ); } } // Plot filled areas for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ ) { ZONE_CONTAINER* edge_zone = aBoard->GetArea( ii ); if( ( GetLayerMask( edge_zone->GetLayer() ) & aLayerMask ) == 0 ) continue; itemplotter.PlotFilledAreas( edge_zone ); } // Plot segments used to fill zone areas (outdated, but here for old boards // compatibility): for( SEGZONE* seg = aBoard->m_Zone; seg != NULL; seg = seg->Next() ) { if( ( GetLayerMask( seg->GetLayer() ) & aLayerMask ) == 0 ) continue; aPlotter->ThickSegment( seg->GetStart(), seg->GetEnd(), seg->GetWidth(), itemplotter.GetMode() ); } }
/* 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(); }
/* * Function GlobalChange_PadSettings * Function to change pad caracteristics for the given footprint * or alls footprints which look like the given footprint * 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 * aRedraw: if true: redraws the footprint * aSaveForUndo: if true: create an entry in the Undo/Redo list * (usually: true in Schematic editor, false in Module editor) */ void PCB_BASE_FRAME::GlobalChange_PadSettings( D_PAD* aPad, bool aSameFootprints, bool aPadShapeFilter, bool aPadOrientFilter, bool aPadLayerFilter, bool aRedraw, bool aSaveForUndo ) { if( aPad == NULL ) aPad = &GetDesignSettings().m_Pad_Master; MODULE* module = aPad->GetParent(); if( module == NULL ) { DisplayError( this, wxT( "Global_Import_Pad_Settings() Error: NULL module" ) ); return; } // Search and copy the name of library reference. MODULE* Module_Ref = module; int pad_orient = aPad->GetOrientation() - Module_Ref->GetOrientation(); // Prepare an undo list: if( aSaveForUndo ) { PICKED_ITEMS_LIST itemsList; for( module = m_Pcb->m_Modules; module; module = module->Next() ) { if( !aSameFootprints && (module != Module_Ref) ) continue; if( module->m_LibRef != Module_Ref->m_LibRef ) continue; bool saveMe = false; for( D_PAD* pad = module->m_Pads; pad; pad = pad->Next() ) { // Filters changes prohibited. if( aPadShapeFilter && ( pad->GetShape() != aPad->GetShape() ) ) continue; int currpad_orient = pad->GetOrientation() - module->GetOrientation(); if( aPadOrientFilter && ( currpad_orient != pad_orient ) ) continue; if( aPadLayerFilter && pad->GetLayerMask() != aPad->GetLayerMask() ) continue; saveMe = true; } if( saveMe ) { ITEM_PICKER itemWrapper( module, UR_CHANGED ); itemsList.PushItem( itemWrapper ); } } SaveCopyInUndoList( itemsList, UR_CHANGED ); } // Update the current module and same others modules if requested. for( module = m_Pcb->m_Modules; module; module = module->Next() ) { if( !aSameFootprints && (module != Module_Ref) ) continue; if( module->m_LibRef != Module_Ref->m_LibRef ) continue; // Erase module on screen if( aRedraw ) { module->SetFlags( DO_NOT_DRAW ); m_canvas->RefreshDrawingRect( module->GetBoundingBox() ); module->ClearFlags( DO_NOT_DRAW ); } for( D_PAD* pad = module->m_Pads; pad; pad = pad->Next() ) { // Filters changes prohibited. if( aPadShapeFilter && ( pad->GetShape() != aPad->GetShape() ) ) continue; if( aPadOrientFilter && (pad->GetOrientation() - module->GetOrientation()) != pad_orient ) continue; if( aPadLayerFilter ) { if( pad->GetLayerMask() != aPad->GetLayerMask() ) continue; else m_Pcb->m_Status_Pcb &= ~( LISTE_RATSNEST_ITEM_OK | CONNEXION_OK); } // Change characteristics: pad->SetAttribute( aPad->GetAttribute() ); pad->SetShape( aPad->GetShape() ); pad->SetLayerMask( aPad->GetLayerMask() ); pad->SetSize( aPad->GetSize() ); pad->SetDelta( aPad->GetDelta() ); pad->SetOffset( aPad->GetOffset() ); pad->SetDrillSize( aPad->GetDrillSize() ); pad->SetDrillShape( aPad->GetDrillShape() ); pad->SetOrientation( pad_orient + module->GetOrientation() ); // copy also local mask margins, because these parameters usually depend on // pad sizes and layers pad->SetLocalSolderMaskMargin( aPad->GetLocalSolderMaskMargin() ); pad->SetLocalSolderPasteMargin( aPad->GetLocalSolderPasteMargin() ); pad->SetLocalSolderPasteMarginRatio( aPad->GetLocalSolderPasteMarginRatio() ); if( pad->GetShape() != PAD_TRAPEZOID ) { pad->SetDelta( wxSize( 0, 0 ) ); } if( pad->GetShape() == PAD_CIRCLE ) { // Ensure pad size.y = pad size.x int size = pad->GetSize().x; pad->SetSize( wxSize( size, size ) ); } switch( pad->GetAttribute() ) { case PAD_SMD: case PAD_CONN: pad->SetDrillSize( wxSize( 0, 0 ) ); pad->SetOffset( wxPoint( 0, 0 ) ); break; default: break; } } module->CalculateBoundingBox(); if( aRedraw ) m_canvas->RefreshDrawingRect( module->GetBoundingBox() ); } OnModify(); }
bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads ) { TRACK* track; wxPoint delta; // lenght on X and Y axis of segments LAYER_MSK layerMask; int net_code_ref; wxPoint shape_pos; NETCLASS* netclass = aRefSeg->GetNetClass(); /* In order to make some calculations more easier or faster, * pads and tracks coordinates will be made relative to the reference segment origin */ wxPoint origin = aRefSeg->GetStart(); // origin will be the origin of other coordinates m_segmEnd = delta = aRefSeg->GetEnd() - origin; m_segmAngle = 0; layerMask = aRefSeg->GetLayerMask(); net_code_ref = aRefSeg->GetNetCode(); // Phase 0 : Test vias if( aRefSeg->Type() == PCB_VIA_T ) { // test if the via size is smaller than minimum if( aRefSeg->GetShape() == VIA_MICROVIA ) { if( aRefSeg->GetWidth() < netclass->GetuViaMinDiameter() ) { m_currentMarker = fillMarker( aRefSeg, NULL, DRCE_TOO_SMALL_MICROVIA, m_currentMarker ); return false; } } else { if( aRefSeg->GetWidth() < netclass->GetViaMinDiameter() ) { m_currentMarker = fillMarker( aRefSeg, NULL, DRCE_TOO_SMALL_VIA, m_currentMarker ); return false; } } // test if via's hole is bigger than its diameter // This test is necessary since the via hole size and width can be modified // and a default via hole can be bigger than some vias sizes if( aRefSeg->GetDrillValue() > aRefSeg->GetWidth() ) { m_currentMarker = fillMarker( aRefSeg, NULL, DRCE_VIA_HOLE_BIGGER, m_currentMarker ); return false; } // For microvias: test if they are blind vias and only between 2 layers // because they are used for very small drill size and are drill by laser // and **only one layer** can be drilled if( aRefSeg->GetShape() == VIA_MICROVIA ) { LAYER_NUM layer1, layer2; bool err = true; ( (SEGVIA*) aRefSeg )->LayerPair( &layer1, &layer2 ); if( layer1 > layer2 ) EXCHG( layer1, layer2 ); // test: if( layer1 == LAYER_N_BACK && layer2 == LAYER_N_2 ) err = false; if( layer1 == (m_pcb->GetDesignSettings().GetCopperLayerCount() - 2 ) && layer2 == LAYER_N_FRONT ) err = false; if( err ) { m_currentMarker = fillMarker( aRefSeg, NULL, DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR, m_currentMarker ); return false; } } } else // This is a track segment { if( aRefSeg->GetWidth() < netclass->GetTrackMinWidth() ) { m_currentMarker = fillMarker( aRefSeg, NULL, DRCE_TOO_SMALL_TRACK_WIDTH, m_currentMarker ); return false; } } // for a non horizontal or vertical segment Compute the segment angle // in tenths of degrees and its length if( delta.x || delta.y ) { // 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; /******************************************/ /* Phase 1 : test DRC track to pads : */ /******************************************/ /* Use a dummy pad to test DRC tracks versus holes, for pads not on all copper layers * but having a hole * This dummy pad has the size and shape of the hole * to test tracks to pad hole DRC, using checkClearanceSegmToPad test function. * 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_pcb ); // Creates a dummy parent D_PAD dummypad( &dummymodule ); dummypad.SetLayerMask( ALL_CU_LAYERS ); // Ensure the hole is on all layers // Compute the min distance to pads if( testPads ) { for( unsigned ii = 0; ii<m_pcb->GetPadCount(); ++ii ) { D_PAD* pad = m_pcb->GetPad( ii ); /* No problem if pads are on an other layer, * But if a drill hole exists (a pad on a single layer can have a hole!) * we must test the hole */ if( (pad->GetLayerMask() & layerMask ) == 0 ) { /* We must test the pad hole. In order to use the function * checkClearanceSegmToPad(),a pseudo pad is used, with a shape and a * size like the hole */ if( pad->GetDrillSize().x == 0 ) continue; dummypad.SetSize( pad->GetDrillSize() ); dummypad.SetPosition( pad->GetPosition() ); dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_OBLONG ? PAD_OVAL : PAD_CIRCLE ); dummypad.SetOrientation( pad->GetOrientation() ); m_padToTestPos = dummypad.GetPosition() - origin; if( !checkClearanceSegmToPad( &dummypad, aRefSeg->GetWidth(), netclass->GetClearance() ) ) { m_currentMarker = fillMarker( aRefSeg, pad, DRCE_TRACK_NEAR_THROUGH_HOLE, m_currentMarker ); return false; } continue; } // The pad must be in a net (i.e pt_pad->GetNet() != 0 ) // but no problem if the pad netcode is the current netcode (same net) if( pad->GetNetCode() // the pad must be connected && net_code_ref == pad->GetNetCode() ) // the pad net is the same as current net -> Ok continue; // DRC for the pad shape_pos = pad->ShapePos(); m_padToTestPos = shape_pos - origin; if( !checkClearanceSegmToPad( pad, aRefSeg->GetWidth(), aRefSeg->GetClearance( pad ) ) ) { m_currentMarker = fillMarker( aRefSeg, pad, DRCE_TRACK_NEAR_PAD, m_currentMarker ); return false; } } } /***********************************************/ /* Phase 2: test DRC with other track segments */ /***********************************************/ // At this point the reference segment is the X axis // Test the reference segment with other track segments wxPoint segStartPoint; wxPoint segEndPoint; for( track = aStart; track; track = track->Next() ) { // No problem if segments have the same net code: if( net_code_ref == track->GetNetCode() ) continue; // No problem if segment are on different layers : if( ( layerMask & track->GetLayerMask() ) == 0 ) continue; // the minimum distance = clearance plus half the reference track // width plus half the other track's width int w_dist = aRefSeg->GetClearance( track ); w_dist += (aRefSeg->GetWidth() + track->GetWidth()) / 2; // If the reference segment is a via, we test it here if( aRefSeg->Type() == PCB_VIA_T ) { delta = track->GetEnd() - track->GetStart(); segStartPoint = aRefSeg->GetStart() - track->GetStart(); if( track->Type() == PCB_VIA_T ) { // Test distance between two vias, i.e. two circles, trivial case if( EuclideanNorm( segStartPoint ) < w_dist ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_VIA_NEAR_VIA, m_currentMarker ); return false; } } else // test via to segment { // Compute l'angle du segment a tester; double angle = ArcTangente( delta.y, delta.x ); // Compute new coordinates ( the segment become horizontal) RotatePoint( &delta, angle ); RotatePoint( &segStartPoint, angle ); if( !checkMarginToCircle( segStartPoint, w_dist, delta.x ) ) { m_currentMarker = fillMarker( track, aRefSeg, DRCE_VIA_NEAR_TRACK, m_currentMarker ); return false; } } continue; } /* We compute segStartPoint, segEndPoint = starting and ending point coordinates for * the segment to test in the new axis : the new X axis is the * reference segment. We must translate and rotate the segment to test */ segStartPoint = track->GetStart() - origin; segEndPoint = track->GetEnd() - origin; RotatePoint( &segStartPoint, m_segmAngle ); RotatePoint( &segEndPoint, m_segmAngle ); if( track->Type() == PCB_VIA_T ) { if( checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) ) continue; m_currentMarker = fillMarker( aRefSeg, track, DRCE_TRACK_NEAR_VIA, m_currentMarker ); return false; } /* We have changed axis: * the reference segment is Horizontal. * 3 cases : the segment to test can be parallel, perpendicular or have an other direction */ if( segStartPoint.y == segEndPoint.y ) // parallel segments { if( abs( segStartPoint.y ) >= w_dist ) continue; // Ensure segStartPoint.x <= segEndPoint.x if( segStartPoint.x > segEndPoint.x ) EXCHG( segStartPoint.x, segEndPoint.x ); if( segStartPoint.x > (-w_dist) && segStartPoint.x < (m_segmLength + w_dist) ) /* possible error drc */ { // the start point is inside the reference range // X........ // O--REF--+ // Fine test : we consider the rounded shape of each end of the track segment: if( segStartPoint.x >= 0 && segStartPoint.x <= m_segmLength ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_TRACK_ENDS1, m_currentMarker ); return false; } if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_TRACK_ENDS2, m_currentMarker ); return false; } } if( segEndPoint.x > (-w_dist) && segEndPoint.x < (m_segmLength + w_dist) ) { // the end point is inside the reference range // .....X // O--REF--+ // Fine test : we consider the rounded shape of the ends if( segEndPoint.x >= 0 && segEndPoint.x <= m_segmLength ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_TRACK_ENDS3, m_currentMarker ); return false; } if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_TRACK_ENDS4, m_currentMarker ); return false; } } if( segStartPoint.x <=0 && segEndPoint.x >= 0 ) { // the segment straddles the reference range (this actually only // checks if it straddles the origin, because the other cases where already // handled) // X.............X // O--REF--+ m_currentMarker = fillMarker( aRefSeg, track, DRCE_TRACK_SEGMENTS_TOO_CLOSE, m_currentMarker ); return false; } } else if( segStartPoint.x == segEndPoint.x ) // perpendicular segments { if( ( segStartPoint.x <= (-w_dist) ) || ( segStartPoint.x >= (m_segmLength + w_dist) ) ) continue; // Test if segments are crossing if( segStartPoint.y > segEndPoint.y ) EXCHG( segStartPoint.y, segEndPoint.y ); if( (segStartPoint.y < 0) && (segEndPoint.y > 0) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_TRACKS_CROSSING, m_currentMarker ); return false; } // At this point the drc error is due to an end near a reference segm end if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_ENDS_PROBLEM1, m_currentMarker ); return false; } if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_ENDS_PROBLEM2, m_currentMarker ); return false; } } else // segments quelconques entre eux { // calcul de la "surface de securite du segment de reference // First rought 'and fast) test : the track segment is like a rectangle m_xcliplo = m_ycliplo = -w_dist; m_xcliphi = m_segmLength + w_dist; m_ycliphi = w_dist; // A fine test is needed because a serment is not exactly a // rectangle, it has rounded ends if( !checkLine( segStartPoint, segEndPoint ) ) { /* 2eme passe : the track has rounded ends. * we must a fine test for each rounded end and the * rectangular zone */ m_xcliplo = 0; m_xcliphi = m_segmLength; if( !checkLine( segStartPoint, segEndPoint ) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_ENDS_PROBLEM3, m_currentMarker ); return false; } else // The drc error is due to the starting or the ending point of the reference segment { // Test the starting and the ending point segStartPoint = track->GetStart(); segEndPoint = track->GetEnd(); delta = segEndPoint - segStartPoint; // Compute the segment orientation (angle) en 0,1 degre double angle = ArcTangente( delta.y, delta.x ); // Compute the segment lenght: delta.x = lenght after rotation RotatePoint( &delta, angle ); /* Comute the reference segment coordinates relatives to a * X axis = current tested segment */ wxPoint relStartPos = aRefSeg->GetStart() - segStartPoint; wxPoint relEndPos = aRefSeg->GetEnd() - segStartPoint; RotatePoint( &relStartPos, angle ); RotatePoint( &relEndPos, angle ); if( !checkMarginToCircle( relStartPos, w_dist, delta.x ) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_ENDS_PROBLEM4, m_currentMarker ); return false; } if( !checkMarginToCircle( relEndPos, w_dist, delta.x ) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_ENDS_PROBLEM5, m_currentMarker ); return false; } } } } } return true; }