void D_PAD::ImportSettingsFromMaster( const D_PAD& aMasterPad ) { SetShape( aMasterPad.GetShape() ); SetLayerSet( aMasterPad.GetLayerSet() ); SetAttribute( aMasterPad.GetAttribute() ); // The pad orientation, for historical reasons is the // pad rotation + parent rotation. // So we have to manage this parent rotation double pad_rot = aMasterPad.GetOrientation(); if( aMasterPad.GetParent() ) pad_rot -= aMasterPad.GetParent()->GetOrientation(); if( GetParent() ) pad_rot += GetParent()->GetOrientation(); SetOrientation( pad_rot ); SetSize( aMasterPad.GetSize() ); SetDelta( wxSize( 0, 0 ) ); SetOffset( aMasterPad.GetOffset() ); SetDrillSize( aMasterPad.GetDrillSize() ); SetDrillShape( aMasterPad.GetDrillShape() ); SetRoundRectRadiusRatio( aMasterPad.GetRoundRectRadiusRatio() ); switch( aMasterPad.GetShape() ) { case PAD_SHAPE_TRAPEZOID: SetDelta( aMasterPad.GetDelta() ); break; case PAD_SHAPE_CIRCLE: // ensure size.y == size.x SetSize( wxSize( GetSize().x, GetSize().x ) ); break; default: ; } switch( aMasterPad.GetAttribute() ) { case PAD_ATTRIB_SMD: case PAD_ATTRIB_CONN: // These pads do not have hole (they are expected to be only on one // external copper layer) SetDrillSize( wxSize( 0, 0 ) ); break; default: ; } // Add or remove custom pad shapes: SetPrimitives( aMasterPad.GetPrimitives() ); SetAnchorPadShape( aMasterPad.GetAnchorPadShape() ); MergePrimitivesAsPolygon(); }
/* 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; module = module->Next() ) { for( D_PAD *pad = module->Pads(); pad; pad = pad->Next() ) { D356_RECORD rk; rk.access = compute_pad_access_code( aPcb, pad->GetLayerSet() ); // 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_ATTRIB_SMD; rk.mechanical = (pad->GetAttribute() == PAD_ATTRIB_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_SHAPE_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->GetLayerSet()[F_Mask] ) rk.soldermask &= ~1; if( pad->GetLayerSet()[B_Mask] ) rk.soldermask &= ~2; aRecords.push_back( rk ); } } } }
unsigned MODULE::GetUniquePadCount( INCLUDE_NPTH_T aIncludeNPTH ) const { std::set<wxUint32> usedNames; // Create a set of used pad numbers for( D_PAD* pad = Pads(); pad; pad = pad->Next() ) { // Skip pads not on copper layers (used to build complex // solder paste shapes for instance) if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none() ) continue; // Skip pads with no name, because they are usually "mechanical" // pads, not "electrical" pads if( pad->GetPadName().IsEmpty() ) continue; if( !aIncludeNPTH ) { // skip NPTH if( pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED ) { continue; } } usedNames.insert( pad->GetPackedPadName() ); } return usedNames.size(); }
void MODULE::TransformPadsShapesWithClearanceToPolygon( PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aCornerBuffer, int aInflateValue, int aMaxError, bool aSkipNPTHPadsWihNoCopper ) const { D_PAD* pad = PadsList(); wxSize margin; for( ; pad != NULL; pad = pad->Next() ) { if( aLayer != UNDEFINED_LAYER && !pad->IsOnLayer(aLayer) ) continue; // NPTH pads are not drawn on layers if the shape size and pos is the same // as their hole: if( aSkipNPTHPadsWihNoCopper && pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED ) { if( pad->GetDrillSize() == pad->GetSize() && pad->GetOffset() == wxPoint( 0, 0 ) ) { switch( pad->GetShape() ) { case PAD_SHAPE_CIRCLE: if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE ) continue; break; case PAD_SHAPE_OVAL: if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE ) continue; break; default: break; } } } switch( aLayer ) { case F_Mask: case B_Mask: margin.x = margin.y = pad->GetSolderMaskMargin() + aInflateValue; break; case F_Paste: case B_Paste: margin = pad->GetSolderPasteMargin(); margin.x += aInflateValue; margin.y += aInflateValue; break; default: margin.x = margin.y = aInflateValue; break; } pad->BuildPadShapePolygon( aCornerBuffer, margin ); } }
/** * Helper function HasNonSMDPins * returns true if the given module has any non smd pins, such as through hole * and therefore cannot be placed automatically. */ static bool HasNonSMDPins( MODULE* aModule ) { D_PAD* pad; for( pad = aModule->Pads(); pad; pad = pad->Next() ) { if( pad->GetAttribute() != PAD_SMD ) return true; } return false; }
unsigned MODULE::GetPadCount( INCLUDE_NPTH_T aIncludeNPTH ) const { if( aIncludeNPTH ) return m_Pads.GetCount(); unsigned cnt = 0; for( D_PAD* pad = m_Pads; pad; pad = pad->Next() ) { if( pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED ) continue; cnt++; } return cnt; }
/* Add a new pad to aModule. */ void PCB_BASE_FRAME::AddPad( MODULE* aModule, bool draw ) { m_Pcb->m_Status_Pcb = 0; aModule->SetLastEditTime(); D_PAD* pad = new D_PAD( aModule ); // Add the new pad to end of the module pad list. aModule->Pads().PushBack( pad ); // Update the pad properties, // and keep NETINFO_LIST::ORPHANED as net info // which is the default when nets cannot be handled. Import_Pad_Settings( pad, false ); pad->SetPosition( GetCrossHairPosition() ); // Set the relative pad position // ( pad position for module orient, 0, and relative to the module position) wxPoint pos0 = pad->GetPosition() - aModule->GetPosition(); RotatePoint( &pos0, -aModule->GetOrientation() ); pad->SetPos0( pos0 ); /* NPTH pads take empty pad number (since they can't be connected), * other pads get incremented from the last one edited */ wxString padName; if( pad->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED ) { padName = GetNextPadName( GetDesignSettings() .m_Pad_Master.GetPadName() ); } pad->SetPadName( padName ); GetDesignSettings().m_Pad_Master.SetPadName( padName ); aModule->CalculateBoundingBox(); SetMsgPanel( pad ); if( draw ) m_canvas->RefreshDrawingRect( aModule->GetBoundingBox() ); }
/** * Function idf_export_module * retrieves information from all board modules, adds drill holes to * the DRILLED_HOLES or BOARD_OUTLINE section as appropriate, * compiles data for the PLACEMENT section and compiles data for * the library ELECTRICAL section. */ static void idf_export_module( BOARD* aPcb, MODULE* aModule, IDF3_BOARD& aIDFBoard ) { // Reference Designator std::string crefdes = TO_UTF8( aModule->GetReference() ); if( crefdes.empty() || !crefdes.compare( "~" ) ) { std::string cvalue = TO_UTF8( aModule->GetValue() ); // if both the RefDes and Value are empty or set to '~' the board owns the part, // otherwise associated parts of the module must be marked NOREFDES. if( cvalue.empty() || !cvalue.compare( "~" ) ) crefdes = "BOARD"; else crefdes = "NOREFDES"; } // TODO: If module cutouts are supported we must add code here // for( EDA_ITEM* item = aModule->GraphicalItems(); item != NULL; item = item->Next() ) // { // if( ( item->Type() != PCB_MODULE_EDGE_T ) // || (item->GetLayer() != Edge_Cuts ) ) continue; // code to export cutouts // } // Export pads double drill, x, y; double scale = aIDFBoard.GetUserScale(); IDF3::KEY_PLATING kplate; std::string pintype; std::string tstr; double dx, dy; aIDFBoard.GetUserOffset( dx, dy ); for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() ) { drill = (double) pad->GetDrillSize().x * scale; x = pad->GetPosition().x * scale + dx; y = -pad->GetPosition().y * scale + dy; // Export the hole on the edge layer if( drill > 0.0 ) { // plating if( pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED ) kplate = IDF3::NPTH; else kplate = IDF3::PTH; // hole type tstr = TO_UTF8( pad->GetPadName() ); if( tstr.empty() || !tstr.compare( "0" ) || !tstr.compare( "~" ) || ( kplate == IDF3::NPTH ) ||( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ) ) pintype = "MTG"; else pintype = "PIN"; // fields: // 1. hole dia. : float // 2. X coord : float // 3. Y coord : float // 4. plating : PTH | NPTH // 5. Assoc. part : BOARD | NOREFDES | PANEL | {"refdes"} // 6. type : PIN | VIA | MTG | TOOL | { "other" } // 7. owner : MCAD | ECAD | UNOWNED if( ( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ) && ( pad->GetDrillSize().x != pad->GetDrillSize().y ) ) { // NOTE: IDF does not have direct support for slots; // slots are implemented as a board cutout and we // cannot represent plating or reference designators double dlength = pad->GetDrillSize().y * scale; // NOTE: The orientation of modules and pads have // the opposite sense due to KiCad drawing on a // screen with a LH coordinate system double angle = pad->GetOrientation() / 10.0; // NOTE: Since this code assumes the scenario where // GetDrillSize().y is the length but idf_parser.cpp // assumes a length along the X axis, the orientation // must be shifted +90 deg when GetDrillSize().y is // the major axis. if( dlength < drill ) { std::swap( drill, dlength ); } else { angle += 90.0; } // NOTE: KiCad measures a slot's length from end to end // rather than between the centers of the arcs dlength -= drill; aIDFBoard.AddSlot( drill, dlength, angle, x, y ); } else { IDF_DRILL_DATA *dp = new IDF_DRILL_DATA( drill, x, y, kplate, crefdes, pintype, IDF3::ECAD ); if( !aIDFBoard.AddDrill( dp ) ) { delete dp; std::ostringstream ostr; ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__; ostr << "(): could not add drill"; throw std::runtime_error( ostr.str() ); } } } } // add any valid models to the library item list std::string refdes; IDF3_COMPONENT* comp = NULL; for( S3D_MASTER* modfile = aModule->Models(); modfile != 0; modfile = modfile->Next() ) { if( !modfile->Is3DType( S3D_MASTER::FILE3D_IDF ) || modfile->GetShape3DFullFilename().empty() ) continue; if( refdes.empty() ) { refdes = TO_UTF8( aModule->GetReference() ); // NOREFDES cannot be used or else the software gets confused // when writing out the placement data due to conflicting // placement and layer specifications; to work around this we // create a (hopefully) unique refdes for our exported part. if( refdes.empty() || !refdes.compare( "~" ) ) refdes = aIDFBoard.GetNewRefDes(); } IDF3_COMP_OUTLINE* outline; outline = aIDFBoard.GetComponentOutline( modfile->GetShape3DFullFilename() ); if( !outline ) throw( std::runtime_error( aIDFBoard.GetError() ) ); double rotz = aModule->GetOrientation()/10.0; double locx = modfile->m_MatPosition.x * 25.4; // part offsets are in inches double locy = modfile->m_MatPosition.y * 25.4; double locz = modfile->m_MatPosition.z * 25.4; double lrot = modfile->m_MatRotation.z; bool top = ( aModule->GetLayer() == B_Cu ) ? false : true; if( top ) { locy = -locy; RotatePoint( &locx, &locy, aModule->GetOrientation() ); locy = -locy; } if( !top ) { lrot = -lrot; RotatePoint( &locx, &locy, aModule->GetOrientation() ); locy = -locy; rotz = 180.0 - rotz; if( rotz >= 360.0 ) while( rotz >= 360.0 ) rotz -= 360.0; if( rotz <= -360.0 ) while( rotz <= -360.0 ) rotz += 360.0; } if( comp == NULL ) comp = aIDFBoard.FindComponent( refdes ); if( comp == NULL ) { comp = new IDF3_COMPONENT( &aIDFBoard ); if( comp == NULL ) throw( std::runtime_error( aIDFBoard.GetError() ) ); comp->SetRefDes( refdes ); if( top ) comp->SetPosition( aModule->GetPosition().x * scale + dx, -aModule->GetPosition().y * scale + dy, rotz, IDF3::LYR_TOP ); else comp->SetPosition( aModule->GetPosition().x * scale + dx, -aModule->GetPosition().y * scale + dy, rotz, IDF3::LYR_BOTTOM ); comp->SetPlacement( IDF3::PS_ECAD ); aIDFBoard.AddComponent( comp ); } else { double refX, refY, refA; IDF3::IDF_LAYER side; if( ! comp->GetPosition( refX, refY, refA, side ) ) { // place the item if( top ) comp->SetPosition( aModule->GetPosition().x * scale + dx, -aModule->GetPosition().y * scale + dy, rotz, IDF3::LYR_TOP ); else comp->SetPosition( aModule->GetPosition().x * scale + dx, -aModule->GetPosition().y * scale + dy, rotz, IDF3::LYR_BOTTOM ); comp->SetPlacement( IDF3::PS_ECAD ); } else { // check that the retrieved component matches this one refX = refX - ( aModule->GetPosition().x * scale + dx ); refY = refY - ( -aModule->GetPosition().y * scale + dy ); refA = refA - rotz; refA *= refA; refX *= refX; refY *= refY; refX += refY; // conditions: same side, X,Y coordinates within 10 microns, // angle within 0.01 degree if( ( top && side == IDF3::LYR_BOTTOM ) || ( !top && side == IDF3::LYR_TOP ) || ( refA > 0.0001 ) || ( refX > 0.0001 ) ) { comp->GetPosition( refX, refY, refA, side ); std::ostringstream ostr; ostr << "* " << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n"; ostr << "* conflicting Reference Designator '" << refdes << "'\n"; ostr << "* X loc: " << (aModule->GetPosition().x * scale + dx); ostr << " vs. " << refX << "\n"; ostr << "* Y loc: " << (-aModule->GetPosition().y * scale + dy); ostr << " vs. " << refY << "\n"; ostr << "* angle: " << rotz; ostr << " vs. " << refA << "\n"; if( top ) ostr << "* TOP vs. "; else ostr << "* BOTTOM vs. "; if( side == IDF3::LYR_TOP ) ostr << "TOP"; else ostr << "BOTTOM"; throw( std::runtime_error( ostr.str() ) ); } } } // create the local data ... IDF3_COMP_OUTLINE_DATA* data = new IDF3_COMP_OUTLINE_DATA( comp, outline ); data->SetOffsets( locx, locy, locz, lrot ); comp->AddOutlineData( data ); } return; }
void DIALOG_PAD_PROPERTIES::PadPropertiesAccept( wxCommandEvent& event ) { if( !padValuesOK() ) return; bool rastnestIsChanged = false; int isign = m_isFlipped ? -1 : 1; transferDataToPad( m_padMaster ); // m_padMaster is a pattern: ensure there is no net for this pad: m_padMaster->SetNetCode( NETINFO_LIST::UNCONNECTED ); 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->GetLayerSet() != m_padMaster->GetLayerSet() ) { rastnestIsChanged = true; m_currentPad->SetLayerSet( m_padMaster->GetLayerSet() ); } if( m_isFlipped ) m_currentPad->SetLayerSet( FlipLayerMask( m_currentPad->GetLayerSet() ) ); m_currentPad->SetPadName( m_padMaster->GetPadName() ); wxString padNetname; // For PAD_HOLE_NOT_PLATED, ensure there is no net name selected if( m_padMaster->GetAttribute() != PAD_HOLE_NOT_PLATED ) padNetname = m_PadNetNameCtrl->GetValue(); if( m_currentPad->GetNetname() != padNetname ) { const NETINFO_ITEM* netinfo = m_board->FindNet( padNetname ); if( !padNetname.IsEmpty() && netinfo == NULL ) { DisplayError( NULL, _( "Unknown netname, netname not changed" ) ); } else { rastnestIsChanged = true; m_currentPad->SetNetCode( netinfo->GetNet() ); } } 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" ) ); } LSET padlayers_mask = m_dummyPad->GetLayerSet(); if( padlayers_mask == 0 ) error_msgs.Add( _( "Error: pad has no layer" ) ); if( !padlayers_mask[F_Cu] && !padlayers_mask[B_Cu] ) { if( m_dummyPad->GetDrillSize().x || m_dummyPad->GetDrillSize().y ) { // Note: he message is shown in an HTML window msg = _( "Error: the pad is not on a copper layer and has a hole" ); if( m_dummyPad->GetAttribute() == PAD_HOLE_NOT_PLATED ) { msg += wxT("<br><br><i>"); msg += _( "For NPTH pad, set pad size value to pad drill value," " 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_HOLE_NOT_PLATED: // Not plated, but through hole, a hole is expected case PAD_STANDARD : // Pad through hole, a hole is also expected if( m_dummyPad->GetDrillSize().x <= 0 ) error_msgs.Add( _( "Error: Through hole pad: drill diameter set to 0" ) ); break; case PAD_CONN: // Connector pads are smd pads, just they do not have solder paste. if( padlayers_mask[B_Paste] || padlayers_mask[F_Paste] ) error_msgs.Add( _( "Error: Connector pads are not on the solder paste layer\n" "Use SMD pads instead" ) ); // Fall trough case PAD_SMD: // SMD and Connector pads (One external copper layer only) /* if( padlayers_mask[B_Cu] && padlayers_mask[F_Cu] ) error_msgs.Add( _( "Error: only one copper layer allowed for SMD or Connector pads" ) ); */ 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; // Disable pad net name wxTextCtrl if the caller is the footprint editor // because nets are living only in the board managed by the board editor m_canEditNetName = m_parent->IsType( FRAME_PCB ); // Setup layers names from board // Should be made first, before calling m_rbCopperLayersSel->SetSelection() m_rbCopperLayersSel->SetString( 0, m_board->GetLayerName( F_Cu ) ); m_rbCopperLayersSel->SetString( 1, m_board->GetLayerName( B_Cu ) ); m_PadLayerAdhCmp->SetLabel( m_board->GetLayerName( F_Adhes ) ); m_PadLayerAdhCu->SetLabel( m_board->GetLayerName( B_Adhes ) ); m_PadLayerPateCmp->SetLabel( m_board->GetLayerName( F_Paste ) ); m_PadLayerPateCu->SetLabel( m_board->GetLayerName( B_Paste ) ); m_PadLayerSilkCmp->SetLabel( m_board->GetLayerName( F_SilkS ) ); m_PadLayerSilkCu->SetLabel( m_board->GetLayerName( B_SilkS ) ); m_PadLayerMaskCmp->SetLabel( m_board->GetLayerName( F_Mask ) ); m_PadLayerMaskCu->SetLabel( m_board->GetLayerName( B_Mask ) ); m_PadLayerECO1->SetLabel( m_board->GetLayerName( Eco1_User ) ); m_PadLayerECO2->SetLabel( m_board->GetLayerName( Eco2_User ) ); m_PadLayerDraft->SetLabel( m_board->GetLayerName( Dwgs_User ) ); m_isFlipped = false; if( m_currentPad ) { MODULE* module = m_currentPad->GetParent(); if( module->GetLayer() == B_Cu ) { 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->SetLayerSet( FlipLayerMask( m_dummyPad->GetLayerSet() ) ); } 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->GetLayerSet() ); // 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 < DIM( code_type ); ii++ ) { if( code_type[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( m_canEditNetName && enable && m_currentPad != NULL ); m_LengthPadToDieCtrl->Enable( enable ); if( m_dummyPad->GetDrillShape() != PAD_DRILL_OBLONG ) m_DrillShapeCtrl->SetSelection( 0 ); else m_DrillShapeCtrl->SetSelection( 1 ); // Update some dialog widgets state (Enable/disable options): wxCommandEvent cmd_event; setPadLayersList( m_dummyPad->GetLayerSet() ); 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->GetLayerSet()[F_Cu] ) { color = m_board->GetVisibleElementColor( PAD_FR_VISIBLE ); } if( m_dummyPad->GetLayerSet()[B_Cu] ) { 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(); }
void EXCELLON_WRITER::BuildHolesList( int aFirstLayer, int aLastLayer, bool aExcludeThroughHoles, bool aGenerateNPTH_list, bool aMerge_PTH_NPTH ) { HOLE_INFO new_hole; int hole_value; m_holeListBuffer.clear(); m_toolListBuffer.clear(); if( (aFirstLayer >= 0) && (aLastLayer >= 0) ) { if( aFirstLayer > aLastLayer ) std::swap( aFirstLayer, aLastLayer ); } if ( aGenerateNPTH_list && aMerge_PTH_NPTH ) { return; } // build hole list for vias if( ! aGenerateNPTH_list ) // vias are always plated ! { for( VIA* via = GetFirstVia( m_pcb->m_Track ); via; via = GetFirstVia( via->Next() ) ) { hole_value = via->GetDrillValue(); if( hole_value == 0 ) // Should not occur. continue; new_hole.m_Tool_Reference = -1; // Flag value for Not initialized new_hole.m_Hole_Orient = 0; new_hole.m_Hole_Diameter = hole_value; new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter; new_hole.m_Hole_Shape = 0; // hole shape: round new_hole.m_Hole_Pos = via->GetStart(); via->LayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer ); // LayerPair return params with m_Hole_Bottom_Layer > m_Hole_Top_Layer // Remember: top layer = 0 and bottom layer = 31 for through hole vias // the via should be at least from aFirstLayer to aLastLayer if( (new_hole.m_Hole_Top_Layer > aFirstLayer) && (aFirstLayer >= 0) ) continue; // via above the first layer if( (new_hole.m_Hole_Bottom_Layer < aLastLayer) && (aLastLayer >= 0) ) continue; // via below the last layer if( aExcludeThroughHoles && (new_hole.m_Hole_Bottom_Layer == B_Cu) && (new_hole.m_Hole_Top_Layer == F_Cu) ) continue; m_holeListBuffer.push_back( new_hole ); } } // build hole list for pads (assumed always through holes) if( !aExcludeThroughHoles || aGenerateNPTH_list ) { for( MODULE* module = m_pcb->m_Modules; module; module = module->Next() ) { // Read and analyse pads for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) { if( ! aGenerateNPTH_list && pad->GetAttribute() == PAD_HOLE_NOT_PLATED && ! aMerge_PTH_NPTH ) continue; if( aGenerateNPTH_list && pad->GetAttribute() != PAD_HOLE_NOT_PLATED ) continue; if( pad->GetDrillSize().x == 0 ) continue; new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_HOLE_NOT_PLATED); new_hole.m_Tool_Reference = -1; // Flag is: Not initialized new_hole.m_Hole_Orient = pad->GetOrientation(); new_hole.m_Hole_Shape = 0; // hole shape: round new_hole.m_Hole_Diameter = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y ); new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter; if( pad->GetDrillShape() != PAD_DRILL_CIRCLE ) new_hole.m_Hole_Shape = 1; // oval flag set new_hole.m_Hole_Size = pad->GetDrillSize(); new_hole.m_Hole_Pos = pad->GetPosition(); // hole position new_hole.m_Hole_Bottom_Layer = B_Cu; new_hole.m_Hole_Top_Layer = F_Cu;// pad holes are through holes m_holeListBuffer.push_back( new_hole ); } } } // Sort holes per increasing diameter value sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), CmpHoleDiameterValue ); // build the tool list int LastHole = -1; /* Set to not initialized (this is a value not used * for m_holeListBuffer[ii].m_Hole_Diameter) */ DRILL_TOOL new_tool( 0 ); unsigned jj; for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ ) { if( m_holeListBuffer[ii].m_Hole_Diameter != LastHole ) { new_tool.m_Diameter = ( m_holeListBuffer[ii].m_Hole_Diameter ); m_toolListBuffer.push_back( new_tool ); LastHole = new_tool.m_Diameter; } jj = m_toolListBuffer.size(); if( jj == 0 ) continue; // Should not occurs m_holeListBuffer[ii].m_Tool_Reference = jj; // Tool value Initialized (value >= 1) m_toolListBuffer.back().m_TotalCount++; if( m_holeListBuffer[ii].m_Hole_Shape ) m_toolListBuffer.back().m_OvalCount++; } }
void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, REPORTER* aErrorMessages, REPORTER* aActivity ) { BOARD* pcb = GetBoard(); // If FL_RENDER_SHOW_HOLES_IN_ZONES is true, holes are correctly removed from copper zones areas. // If FL_RENDER_SHOW_HOLES_IN_ZONES is false, holes are not removed from copper zones areas, // but the calculation time is twice shorter. bool remove_Holes = isEnabled( FL_RENDER_SHOW_HOLES_IN_ZONES ); bool realistic_mode = isRealisticMode(); bool useTextures = isRealisticMode() && isEnabled( FL_RENDER_TEXTURES ); // Number of segments to convert a circle to polygon // We use 2 values: the first gives a good shape (for instanes rond pads) // the second is used to speed up calculations, when a poor approximation is acceptable (holes) const int segcountforcircle = 18; double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2.0) ); const int segcountLowQuality = 12; // segments to draw a circle with low quality // to reduce time calculations // for holes and items which do not need // a fine representation double correctionFactorLQ = 1.0 / cos( M_PI / (segcountLowQuality * 2.0) ); SHAPE_POLY_SET bufferPolys; // copper areas: tracks, pads and filled zones areas // when holes are removed from zones SHAPE_POLY_SET bufferPcbOutlines; // stores the board main outlines SHAPE_POLY_SET bufferZonesPolys; // copper filled zones areas // when holes are not removed from zones SHAPE_POLY_SET currLayerHoles; // Contains holes for the current layer SHAPE_POLY_SET allLayerHoles; // Contains holes for all layers // Build a polygon from edge cut items wxString msg; if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines, allLayerHoles, &msg ) ) { if( aErrorMessages ) { msg << wxT("\n") << _("Unable to calculate the board outlines.\n" "Therefore use the board boundary box.") << wxT("\n\n"); aErrorMessages->Report( msg, REPORTER::RPT_WARNING ); } } // Build board holes, with optimization of large holes shape. buildBoardThroughHolesPolygonList( allLayerHoles, segcountLowQuality, true ); LSET cu_set = LSET::AllCuMask( GetPrm3DVisu().m_CopperLayersCount ); glNewList( aBoardList, GL_COMPILE ); for( LSEQ cu = cu_set.CuStack(); cu; ++cu ) { LAYER_ID layer = *cu; // Skip non enabled layers in normal mode, // and internal layers in realistic mode if( !is3DLayerEnabled( layer ) ) continue; if( aActivity ) aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) ); bufferPolys.RemoveAllContours(); bufferZonesPolys.RemoveAllContours(); currLayerHoles.RemoveAllContours(); // Draw track shapes: for( TRACK* track = pcb->m_Track; track; track = track->Next() ) { if( !track->IsOnLayer( layer ) ) continue; track->TransformShapeWithClearanceToPolygon( bufferPolys, 0, segcountforcircle, correctionFactor ); // Add blind/buried via holes if( track->Type() == PCB_VIA_T ) { VIA *via = static_cast<VIA*>( track ); if( via->GetViaType() == VIA_THROUGH ) continue; // already done int holediameter = via->GetDrillValue(); int thickness = GetPrm3DVisu().GetCopperThicknessBIU(); int hole_outer_radius = (holediameter + thickness) / 2; TransformCircleToPolygon( currLayerHoles, via->GetStart(), hole_outer_radius, segcountLowQuality ); } } // draw pad shapes for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) { // Note: NPTH pads are not drawn on copper layers when the pad // has same shape as its hole module->TransformPadsShapesWithClearanceToPolygon( layer, bufferPolys, 0, segcountforcircle, correctionFactor, true ); // Micro-wave modules may have items on copper layers module->TransformGraphicShapesWithClearanceToPolygonSet( layer, bufferPolys, 0, segcountforcircle, correctionFactor ); // pad holes are already in list. } // Draw copper zones. Note: // * if the holes are removed from copper zones // the polygons are stored in bufferPolys (which contains all other polygons) // * if the holes are NOT removed from copper zones // the polygons are stored in bufferZonesPolys if( isEnabled( FL_ZONE ) ) { for( int ii = 0; ii < pcb->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = pcb->GetArea( ii ); LAYER_NUM zonelayer = zone->GetLayer(); if( zonelayer == layer ) { zone->TransformSolidAreasShapesToPolygonSet( remove_Holes ? bufferPolys : bufferZonesPolys, segcountLowQuality, correctionFactorLQ ); } } } // draw graphic items on copper layers (texts) for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() ) { if( !item->IsOnLayer( layer ) ) continue; switch( item->Type() ) { case PCB_LINE_T: // should not exist on copper layers ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( bufferPolys, 0, segcountforcircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( bufferPolys, 0, segcountLowQuality, correctionFactor ); break; default: break; } } // bufferPolys contains polygons to merge. Many overlaps . // Calculate merged polygons if( bufferPolys.IsEmpty() ) continue; // Use Clipper lib to subtract holes to copper areas if( currLayerHoles.OutlineCount() ) { currLayerHoles.Append(allLayerHoles); currLayerHoles.Simplify(); bufferPolys.BooleanSubtract( currLayerHoles ); } else bufferPolys.BooleanSubtract( allLayerHoles ); int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer ); int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer ); float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted) // If we are not using thickness, then the z-normal has to match the layer direction // because just one plane will be drawn if( !thickness ) zNormal = Get3DLayer_Z_Orientation( layer ); if( realistic_mode ) { setGLCopperColor(); } else { EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( layer ); SetGLColor( color ); } // If holes are removed from copper zones, bufferPolys contains all polygons // to draw (tracks+zones+texts). Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos, thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, zNormal ); // If holes are not removed from copper zones (for calculation time reasons, // the zone polygons are stored in bufferZonesPolys and have to be drawn now: if( !bufferZonesPolys.IsEmpty() ) { Draw3D_SolidHorizontalPolyPolygons( bufferZonesPolys, zpos, thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, zNormal ); } } if( aActivity ) aActivity->Report( _( "Build board body" ) ); // Draw plated vertical holes inside the board, but not always. They are drawn: // - if the board body is not shown, to show the holes. // - or if the copper thickness is shown if( !isEnabled( FL_SHOW_BOARD_BODY ) || isEnabled( FL_USE_COPPER_THICKNESS ) ) { // Draw vias holes (vertical cylinders) for( const TRACK* track = pcb->m_Track; track; track = track->Next() ) { if( track->Type() == PCB_VIA_T ) { const VIA *via = static_cast<const VIA*>(track); draw3DViaHole( via ); } } // Draw pads holes (vertical cylinders) for( const MODULE* module = pcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) if( pad->GetAttribute () != PAD_HOLE_NOT_PLATED ) draw3DPadHole( pad ); } } glEndList(); // Build the body board: glNewList( aBodyOnlyList, GL_COMPILE ); if( isRealisticMode() ) { setGLEpoxyColor( 1.00 ); } else { EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( Edge_Cuts ); SetGLColor( color, 0.7 ); } float copper_thickness = GetPrm3DVisu().GetCopperThicknessBIU(); // a small offset between substrate and external copper layer to avoid artifacts // when drawing copper items on board float epsilon = Millimeter2iu( 0.01 ); float zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ); float board_thickness = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu ) - GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ); // items on copper layers and having a thickness = copper_thickness // are drawn from zpos - copper_thickness/2 to zpos + copper_thickness // therefore substrate position is copper_thickness/2 to // substrate_height - copper_thickness/2 zpos += (copper_thickness + epsilon) / 2.0f; board_thickness -= copper_thickness + epsilon; bufferPcbOutlines.BooleanSubtract( allLayerHoles ); if( !bufferPcbOutlines.IsEmpty() ) { Draw3D_SolidHorizontalPolyPolygons( bufferPcbOutlines, zpos + board_thickness / 2.0, board_thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, 1.0f ); } glEndList(); }
void DIALOG_GENDRILL::InitDisplayParams() { wxString msg; m_Choice_Unit->SetSelection( m_UnitDrillIsInch ? 1 : 0 ); m_Choice_Zeros_Format->SetSelection( m_ZerosFormat ); UpdatePrecisionOptions(); m_Check_Minimal->SetValue( m_MinimalHeader ); if( m_DrillOriginIsAuxAxis ) m_Choice_Drill_Offset->SetSelection( 1 ); m_Check_Mirror->SetValue( m_Mirror ); m_Check_Merge_PTH_NPTH->SetValue( m_Merge_PTH_NPTH ); m_Choice_Drill_Map->SetSelection( m_mapFileType ); m_ViaDrillValue->SetLabel( _( "Use Netclass values" ) ); m_MicroViaDrillValue->SetLabel( _( "Use Netclass values" ) ); // See if we have some buried vias or/and microvias, and display // microvias drill value if so m_throughViasCount = 0; m_microViasCount = 0; m_blindOrBuriedViasCount = 0; for( TRACK* track = m_parent->GetBoard()->m_Track; track != NULL; track = track->Next() ) { const VIA *via = dynamic_cast<const VIA*>( track ); if( via ) { switch( via->GetViaType() ) { case VIA_THROUGH: m_throughViasCount++; break; case VIA_MICROVIA: m_microViasCount++; break; case VIA_BLIND_BURIED: m_blindOrBuriedViasCount++; break; default: break; } } } m_MicroViaDrillValue->Enable( m_microViasCount ); // Count plated pad holes and not plated pad holes: m_platedPadsHoleCount = 0; m_notplatedPadsHoleCount = 0; for( MODULE* module = m_parent->GetBoard()->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad != NULL; pad = pad->Next() ) { if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE ) { if( pad->GetDrillSize().x != 0 ) { if( pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED ) m_notplatedPadsHoleCount++; else m_platedPadsHoleCount++; } } else { if( pad->GetDrillSize().x != 0 && pad->GetDrillSize().y != 0 ) { if( pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED ) m_notplatedPadsHoleCount++; else m_platedPadsHoleCount++; } } } } // Display hole counts: msg = m_PlatedPadsCountInfoMsg->GetLabel(); msg << wxT( " " ) << m_platedPadsHoleCount; m_PlatedPadsCountInfoMsg->SetLabel( msg ); msg = m_NotPlatedPadsCountInfoMsg->GetLabel(); msg << wxT( " " ) << m_notplatedPadsHoleCount; m_NotPlatedPadsCountInfoMsg->SetLabel( msg ); msg = m_ThroughViasInfoMsg->GetLabel(); msg << wxT( " " ) << m_throughViasCount; m_ThroughViasInfoMsg->SetLabel( msg ); msg = m_MicroViasInfoMsg->GetLabel(); msg << wxT( " " ) << m_microViasCount; m_MicroViasInfoMsg->SetLabel( msg ); msg = m_BuriedViasInfoMsg->GetLabel(); msg << wxT( " " ) << m_blindOrBuriedViasCount; m_BuriedViasInfoMsg->SetLabel( msg ); // Output directory m_outputDirectoryName->SetValue( m_plotOpts.GetOutputDirectory() ); }
// 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; }
/** * 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 DIALOG_PAD_PROPERTIES::OnPaintShowPanel( wxPaintEvent& event ) { wxPaintDC dc( m_panelShowPad ); PAD_DRAWINFO drawInfo; EDA_COLOR_T color = BLACK; if( m_dummyPad->GetLayerSet()[F_Cu] ) { color = m_board->GetVisibleElementColor( PAD_FR_VISIBLE ); } if( m_dummyPad->GetLayerSet()[B_Cu] ) { 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 ); // Invalid x size. User could enter zero, or have deleted all text prior to // entering a new value; this is also treated as zero. If dim is left at // zero, the drawing scale is zero and we get a crash. if( dim == 0 ) { // If drill size has been set, use that. Otherwise default to 1mm. dim = m_dummyPad->GetDrillSize().x; if( dim == 0 ) dim = 1000000; } if( m_dummyPad->GetLocalClearance() > 0 ) dim += m_dummyPad->GetLocalClearance() * 2; double scale = (double) dc_size.x / dim; // If the pad is a circle, use the x size here instead. int ysize; if( m_dummyPad->GetShape() == PAD_CIRCLE ) ysize = m_dummyPad->GetSize().x; else ysize = m_dummyPad->GetSize().y; dim = ysize + std::abs( m_dummyPad->GetDelta().x ); // Invalid y size. See note about x size above. if( dim == 0 ) { dim = m_dummyPad->GetDrillSize().y; if( dim == 0 ) dim = 1000000; } 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(); }
/* 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(); }
void BuildUnconnectedThermalStubsPolygonList( CPOLYGONS_LIST& aCornerBuffer, BOARD* aPcb, ZONE_CONTAINER* aZone, double aArcCorrection, double aRoundPadThermalRotation ) { std::vector<wxPoint> corners_buffer; // a local polygon buffer to store one stub corners_buffer.reserve( 4 ); wxPoint ptTest[4]; int zone_clearance = aZone->GetZoneClearance(); EDA_RECT item_boundingbox; EDA_RECT zone_boundingbox = aZone->GetBoundingBox(); int biggest_clearance = aPcb->GetDesignSettings().GetBiggestClearanceValue(); biggest_clearance = std::max( biggest_clearance, zone_clearance ); zone_boundingbox.Inflate( biggest_clearance ); // half size of the pen used to draw/plot zones outlines int pen_radius = aZone->GetMinThickness() / 2; 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( aZone->GetPadConnection( pad ) == THT_THERMAL && pad->GetAttribute() != PAD_STANDARD ) continue; if( aZone->GetPadConnection( pad ) != THERMAL_PAD && aZone->GetPadConnection( pad ) != THT_THERMAL ) continue; // check if( !pad->IsOnLayer( aZone->GetLayer() ) ) continue; if( pad->GetNetCode() != aZone->GetNetCode() ) continue; // Calculate thermal bridge half width int thermalBridgeWidth = aZone->GetThermalReliefCopperBridge( pad ) - aZone->GetMinThickness(); if( thermalBridgeWidth <= 0 ) continue; // we need the thermal bridge half width // with a small extra size to be sure we create a stub // slightly larger than the actual stub thermalBridgeWidth = ( thermalBridgeWidth + 4 ) / 2; int thermalReliefGap = aZone->GetThermalReliefGap( pad ); item_boundingbox = pad->GetBoundingBox(); item_boundingbox.Inflate( thermalReliefGap ); if( !( item_boundingbox.Intersects( zone_boundingbox ) ) ) continue; // Thermal bridges are like a segment from a starting point inside the pad // to an ending point outside the pad // calculate the ending point of the thermal pad, outside the pad wxPoint endpoint; endpoint.x = ( pad->GetSize().x / 2 ) + thermalReliefGap; endpoint.y = ( pad->GetSize().y / 2 ) + thermalReliefGap; // Calculate the starting point of the thermal stub // inside the pad wxPoint startpoint; int copperThickness = aZone->GetThermalReliefCopperBridge( pad ) - aZone->GetMinThickness(); if( copperThickness < 0 ) copperThickness = 0; // Leave a small extra size to the copper area inside to pad copperThickness += KiROUND( IU_PER_MM * 0.04 ); startpoint.x = std::min( pad->GetSize().x, copperThickness ); startpoint.y = std::min( pad->GetSize().y, copperThickness ); startpoint.x /= 2; startpoint.y /= 2; // This is a CIRCLE pad tweak // for circle pads, the thermal stubs orientation is 45 deg double fAngle = pad->GetOrientation(); if( pad->GetShape() == PAD_CIRCLE ) { endpoint.x = KiROUND( endpoint.x * aArcCorrection ); endpoint.y = endpoint.x; fAngle = aRoundPadThermalRotation; } // contour line width has to be taken into calculation to avoid "thermal stub bleed" endpoint.x += pen_radius; endpoint.y += pen_radius; // compute north, south, west and east points for zone connection. ptTest[0] = wxPoint( 0, endpoint.y ); // lower point ptTest[1] = wxPoint( 0, -endpoint.y ); // upper point ptTest[2] = wxPoint( endpoint.x, 0 ); // right point ptTest[3] = wxPoint( -endpoint.x, 0 ); // left point // Test all sides for( int i = 0; i < 4; i++ ) { // rotate point RotatePoint( &ptTest[i], fAngle ); // translate point ptTest[i] += pad->ShapePos(); if( aZone->HitTestFilledArea( ptTest[i] ) ) continue; corners_buffer.clear(); // polygons are rectangles with width of copper bridge value switch( i ) { case 0: // lower stub corners_buffer.push_back( wxPoint( -thermalBridgeWidth, endpoint.y ) ); corners_buffer.push_back( wxPoint( +thermalBridgeWidth, endpoint.y ) ); corners_buffer.push_back( wxPoint( +thermalBridgeWidth, startpoint.y ) ); corners_buffer.push_back( wxPoint( -thermalBridgeWidth, startpoint.y ) ); break; case 1: // upper stub corners_buffer.push_back( wxPoint( -thermalBridgeWidth, -endpoint.y ) ); corners_buffer.push_back( wxPoint( +thermalBridgeWidth, -endpoint.y ) ); corners_buffer.push_back( wxPoint( +thermalBridgeWidth, -startpoint.y ) ); corners_buffer.push_back( wxPoint( -thermalBridgeWidth, -startpoint.y ) ); break; case 2: // right stub corners_buffer.push_back( wxPoint( endpoint.x, -thermalBridgeWidth ) ); corners_buffer.push_back( wxPoint( endpoint.x, thermalBridgeWidth ) ); corners_buffer.push_back( wxPoint( +startpoint.x, thermalBridgeWidth ) ); corners_buffer.push_back( wxPoint( +startpoint.x, -thermalBridgeWidth ) ); break; case 3: // left stub corners_buffer.push_back( wxPoint( -endpoint.x, -thermalBridgeWidth ) ); corners_buffer.push_back( wxPoint( -endpoint.x, thermalBridgeWidth ) ); corners_buffer.push_back( wxPoint( -startpoint.x, thermalBridgeWidth ) ); corners_buffer.push_back( wxPoint( -startpoint.x, -thermalBridgeWidth ) ); break; } // add computed polygon to list for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) { wxPoint cpos = corners_buffer[ic]; RotatePoint( &cpos, fAngle ); // Rotate according to module orientation cpos += pad->ShapePos(); // Shift origin to position CPolyPt corner; corner.x = cpos.x; corner.y = cpos.y; corner.end_contour = ( ic < (corners_buffer.size() - 1) ) ? false : true; aCornerBuffer.Append( corner ); } } } } }
/** * Function AddClearanceAreasPolygonsToPolysList * Supports a min thickness area constraint. * Add non copper areas polygons (pads and tracks with clearance) * to the filled copper area found * in BuildFilledPolysListData after calculating filled areas in a zone * Non filled copper areas are pads and track and their clearance areas * The filled copper area must be computed just before. * BuildFilledPolysListData() call this function just after creating the * filled copper area polygon (without clearance areas) * to do that this function: * 1 - Creates the main outline (zone outline) using a correction to shrink the resulting area * with m_ZoneMinThickness/2 value. * The result is areas with a margin of m_ZoneMinThickness/2 * When drawing outline with segments having a thickness of m_ZoneMinThickness, the * outlines will match exactly the initial outlines * 3 - Add all non filled areas (pads, tracks) in group B with a clearance of m_Clearance + * m_ZoneMinThickness/2 * in a buffer * - If Thermal shapes are wanted, add non filled area, in order to create these thermal shapes * 4 - calculates the polygon A - B * 5 - put resulting list of polygons (filled areas) in m_FilledPolysList * This zone contains pads with the same net. * 6 - Remove insulated copper islands * 7 - If Thermal shapes are wanted, remove unconnected stubs in thermal shapes: * creates a buffer of polygons corresponding to stubs to remove * sub them to the filled areas. * Remove new insulated copper islands */ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList( BOARD* aPcb ) { // Set the number of segments in arc approximations if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF ) s_CircleToSegmentsCount = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF; else s_CircleToSegmentsCount = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF; /* calculates the coeff to compensate radius reduction of holes clearance * due to the segment approx. * For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2) * s_Correction is 1 /cos( PI/s_CircleToSegmentsCount ) */ s_Correction = 1.0 / cos( M_PI / s_CircleToSegmentsCount ); // This KI_POLYGON_SET is the area(s) to fill, with m_ZoneMinThickness/2 KI_POLYGON_SET polyset_zone_solid_areas; int margin = m_ZoneMinThickness / 2; /* First, creates the main polygon (i.e. the filled area using only one outline) * to reserve a m_ZoneMinThickness/2 margin around the outlines and holes * this margin is the room to redraw outlines with segments having a width set to * m_ZoneMinThickness * so m_ZoneMinThickness is the min thickness of the filled zones areas * the main polygon is stored in polyset_zone_solid_areas */ CopyPolygonsFromFilledPolysListToKiPolygonList( polyset_zone_solid_areas ); polyset_zone_solid_areas -= margin; if( polyset_zone_solid_areas.size() == 0 ) return; /* Calculates the clearance value that meet DRC requirements * from m_ZoneClearance and clearance from the corresponding netclass * We have a "local" clearance in zones because most of time * clearance between a zone and others items is bigger than the netclass clearance * this is more true for small clearance values * Note also the "local" clearance is used for clearance between non copper items * or items like texts on copper layers */ int zone_clearance = std::max( m_ZoneClearance, GetClearance() ); zone_clearance += margin; /* store holes (i.e. tracks and pads areas as polygons outlines) * in a polygon list */ /* items ouside the zone bounding box are skipped * the bounding box is the zone bounding box + the biggest clearance found in Netclass list */ EDA_RECT item_boundingbox; EDA_RECT zone_boundingbox = GetBoundingBox(); int biggest_clearance = aPcb->GetDesignSettings().GetBiggestClearanceValue(); biggest_clearance = std::max( biggest_clearance, zone_clearance ); zone_boundingbox.Inflate( biggest_clearance ); /* * First : Add pads. Note: pads having the same net as zone are left in zone. * Thermal shapes will be created later if necessary */ int item_clearance; // static to avoid unnecessary memory allocation when filling many zones. static CPOLYGONS_LIST cornerBufferPolysToSubstract; cornerBufferPolysToSubstract.RemoveAllContours(); /* Use a dummy pad to calculate hole clerance when a pad is not on all copper layers * and this pad has a hole * This dummy pad has the size and shape of the hole * Therefore, this dummy pad is a circle or an oval. * A pad must have a parent because some functions expect a non null parent * to find the parent board, and some other data */ MODULE dummymodule( aPcb ); // Creates a dummy parent D_PAD dummypad( &dummymodule ); for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { D_PAD* nextpad; for( D_PAD* pad = module->Pads(); pad != NULL; pad = nextpad ) { nextpad = pad->Next(); // pad pointer can be modified by next code, so // calculate the next pad here if( !pad->IsOnLayer( GetLayer() ) ) { /* Test for pads that are on top or bottom only and have a hole. * There are curious pads but they can be used for some components that are * inside the board (in fact inside the hole. Some photo diodes and Leds are * like this) */ if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 ) continue; // Use a dummy pad to calculate a hole shape that have the same dimension as // the pad hole dummypad.SetSize( pad->GetDrillSize() ); dummypad.SetOrientation( pad->GetOrientation() ); dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_OBLONG ? PAD_OVAL : PAD_CIRCLE ); dummypad.SetPosition( pad->GetPosition() ); pad = &dummypad; } // Note: netcode <=0 means not connected item if( ( pad->GetNetCode() != GetNetCode() ) || ( pad->GetNetCode() <= 0 ) ) { item_clearance = pad->GetClearance() + margin; item_boundingbox = pad->GetBoundingBox(); item_boundingbox.Inflate( item_clearance ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { int clearance = std::max( zone_clearance, item_clearance ); pad->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, clearance, s_CircleToSegmentsCount, s_Correction ); } continue; } if( ( GetPadConnection( pad ) == PAD_NOT_IN_ZONE ) || ( pad->GetShape() == PAD_TRAPEZOID ) ) // PAD_TRAPEZOID shapes are not in zones because they are used in microwave apps // and i think it is good that shapes are not changed by thermal pads or others { int gap = zone_clearance; int thermalGap = GetThermalReliefGap( pad ); gap = std::max( gap, thermalGap ); item_boundingbox = pad->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { pad->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, gap, s_CircleToSegmentsCount, s_Correction ); } } } } /* Add holes (i.e. tracks and vias areas as polygons outlines) * in cornerBufferPolysToSubstract */ for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) { if( !track->IsOnLayer( GetLayer() ) ) continue; if( track->GetNetCode() == GetNetCode() && (GetNetCode() != 0) ) continue; item_clearance = track->GetClearance() + margin; item_boundingbox = track->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { int clearance = std::max( zone_clearance, item_clearance ); track->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, clearance, s_CircleToSegmentsCount, s_Correction ); } } /* Add module edge items that are on copper layers * Pcbnew allows these items to be on copper layers in microwave applictions * This is a bad thing, but must be handled here, until a better way is found */ for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( !item->IsOnLayer( GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) ) continue; if( item->Type() != PCB_MODULE_EDGE_T ) continue; item_boundingbox = item->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { ( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, zone_clearance, s_CircleToSegmentsCount, s_Correction ); } } } // Add graphic items (copper texts) and board edges for( BOARD_ITEM* item = aPcb->m_Drawings; item; item = item->Next() ) { if( item->GetLayer() != GetLayer() && item->GetLayer() != Edge_Cuts ) continue; switch( item->Type() ) { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, zone_clearance, s_CircleToSegmentsCount, s_Correction ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon( cornerBufferPolysToSubstract, zone_clearance ); break; default: break; } } // Add zones outlines having an higher priority and keepout for( int ii = 0; ii < GetBoard()->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = GetBoard()->GetArea( ii ); if( zone->GetLayer() != GetLayer() ) continue; if( !zone->GetIsKeepout() && zone->GetPriority() <= GetPriority() ) continue; if( zone->GetIsKeepout() && ! zone->GetDoNotAllowCopperPour() ) continue; // A highter priority zone or keepout area is found: remove its area item_boundingbox = zone->GetBoundingBox(); if( !item_boundingbox.Intersects( zone_boundingbox ) ) continue; // Add the zone outline area. // However if the zone has the same net as the current zone, // do not add clearance. // the zone will be connected to the current zone, but filled areas // will use different parameters (clearance, thermal shapes ) bool addclearance = GetNetCode() != zone->GetNetCode(); int clearance = zone_clearance; if( zone->GetIsKeepout() ) { addclearance = true; clearance = m_ZoneMinThickness / 2; } zone->TransformOutlinesShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, clearance, addclearance ); } // Remove thermal symbols for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad != NULL; pad = pad->Next() ) { // Rejects non-standard pads with tht-only thermal reliefs if( GetPadConnection( pad ) == THT_THERMAL && pad->GetAttribute() != PAD_STANDARD ) continue; if( GetPadConnection( pad ) != THERMAL_PAD && GetPadConnection( pad ) != THT_THERMAL ) continue; if( !pad->IsOnLayer( GetLayer() ) ) continue; if( pad->GetNetCode() != GetNetCode() ) continue; item_boundingbox = pad->GetBoundingBox(); int thermalGap = GetThermalReliefGap( pad ); item_boundingbox.Inflate( thermalGap, thermalGap ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { CreateThermalReliefPadPolygon( cornerBufferPolysToSubstract, *pad, thermalGap, GetThermalReliefCopperBridge( pad ), m_ZoneMinThickness, s_CircleToSegmentsCount, s_Correction, s_thermalRot ); } } } // cornerBufferPolysToSubstract contains polygons to substract. // polyset_zone_solid_areas contains the main filled area // Calculate now actual solid areas if( cornerBufferPolysToSubstract.GetCornersCount() > 0 ) { KI_POLYGON_SET polyset_holes; cornerBufferPolysToSubstract.ExportTo( polyset_holes ); // Remove holes from initial area.: polyset_zone_solid_areas -= polyset_holes; } // put solid areas in m_FilledPolysList: m_FilledPolysList.RemoveAllContours(); CopyPolygonsFromKiPolygonListToFilledPolysList( polyset_zone_solid_areas ); // Remove insulated islands: if( GetNetCode() > 0 ) TestForCopperIslandAndRemoveInsulatedIslands( aPcb ); // Now we remove all unused thermal stubs. cornerBufferPolysToSubstract.RemoveAllContours(); // Test thermal stubs connections and add polygons to remove unconnected stubs. // (this is a refinement for thermal relief shapes) if( GetNetCode() > 0 ) BuildUnconnectedThermalStubsPolygonList( cornerBufferPolysToSubstract, aPcb, this, s_Correction, s_thermalRot ); // remove copper areas corresponding to not connected stubs if( cornerBufferPolysToSubstract.GetCornersCount() ) { KI_POLYGON_SET polyset_holes; cornerBufferPolysToSubstract.ExportTo( polyset_holes ); // Remove unconnected stubs polyset_zone_solid_areas -= polyset_holes; // put these areas in m_FilledPolysList m_FilledPolysList.RemoveAllContours(); CopyPolygonsFromKiPolygonListToFilledPolysList( polyset_zone_solid_areas ); if( GetNetCode() > 0 ) TestForCopperIslandAndRemoveInsulatedIslands( aPcb ); } cornerBufferPolysToSubstract.RemoveAllContours(); }
/* * Function BuildHolesList * Create the list of holes and tools for a given board * The list is sorted by increasing drill values * Only holes from aFirstLayer to aLastLayer copper layers are listed (for vias, because pad holes are always through holes) * param aPcb : the given board * param aHoleListBuffer : the std::vector<HOLE_INFO> to fill with pcb holes info * param aToolListBuffer : the std::vector<DRILL_TOOL> to fill with tools to use * param aFirstLayer = first layer to consider. if < 0 aFirstLayer is ignored (used to creates report file) * param aLastLayer = last layer to consider. if < 0 aLastLayer is ignored * param aExcludeThroughHoles : if true, exclude through holes ( pads and vias through ) * param aGenerateNPTH_list : * true to create NPTH only list (with no plated holes) * false to created plated holes list (with no NPTH ) */ void Build_Holes_List( BOARD* aPcb, std::vector<HOLE_INFO>& aHoleListBuffer, std::vector<DRILL_TOOL>& aToolListBuffer, int aFirstLayer, int aLastLayer, bool aExcludeThroughHoles, bool aGenerateNPTH_list ) { HOLE_INFO new_hole; int hole_value; aHoleListBuffer.clear(); aToolListBuffer.clear(); if( (aFirstLayer >= 0) && (aLastLayer >= 0) ) { if( aFirstLayer > aLastLayer ) EXCHG( aFirstLayer, aLastLayer ); } /* build hole list for vias */ if( ! aGenerateNPTH_list ) // vias are always plated ! { for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) { if( track->Type() != PCB_VIA_T ) continue; SEGVIA* via = (SEGVIA*) track; hole_value = via->GetDrillValue(); if( hole_value == 0 ) continue; new_hole.m_Tool_Reference = -1; // Flag value for Not initialized new_hole.m_Hole_Orient = 0; new_hole.m_Hole_Diameter = hole_value; new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter; new_hole.m_Hole_Shape = 0; // hole shape: round new_hole.m_Hole_Pos = via->m_Start; via->ReturnLayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer ); // ReturnLayerPair return params with m_Hole_Bottom_Layer < m_Hole_Top_Layer if( (new_hole.m_Hole_Bottom_Layer > aFirstLayer) && (aFirstLayer >= 0) ) continue; if( (new_hole.m_Hole_Top_Layer < aLastLayer) && (aLastLayer >= 0) ) continue; if( aExcludeThroughHoles && (new_hole.m_Hole_Bottom_Layer == LAYER_N_BACK) && (new_hole.m_Hole_Top_Layer == LAYER_N_FRONT) ) continue; aHoleListBuffer.push_back( new_hole ); } } // build hole list for pads (assumed always through holes) if( !aExcludeThroughHoles || aGenerateNPTH_list ) { for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { // Read and analyse pads for( D_PAD* pad = module->m_Pads; pad; pad = pad->Next() ) { if( ! aGenerateNPTH_list && pad->GetAttribute() == PAD_HOLE_NOT_PLATED ) continue; if( aGenerateNPTH_list && pad->GetAttribute() != PAD_HOLE_NOT_PLATED ) continue; if( pad->GetDrillSize().x == 0 ) continue; new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_HOLE_NOT_PLATED); new_hole.m_Tool_Reference = -1; // Flag is: Not initialized new_hole.m_Hole_Orient = pad->GetOrientation(); new_hole.m_Hole_Shape = 0; // hole shape: round new_hole.m_Hole_Diameter = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y ); new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter; if( pad->GetDrillShape() != PAD_CIRCLE ) new_hole.m_Hole_Shape = 1; // oval flag set new_hole.m_Hole_Size = pad->GetDrillSize(); new_hole.m_Hole_Pos = pad->GetPosition(); // hole position new_hole.m_Hole_Bottom_Layer = LAYER_N_BACK; new_hole.m_Hole_Top_Layer = LAYER_N_FRONT;// pad holes are through holes aHoleListBuffer.push_back( new_hole ); } } } // Sort holes per increasing diameter value sort( aHoleListBuffer.begin(), aHoleListBuffer.end(), CmpHoleDiameterValue ); // build the tool list int LastHole = -1; /* Set to not initialised (this is a value not used * for aHoleListBuffer[ii].m_Hole_Diameter) */ DRILL_TOOL new_tool( 0 ); unsigned jj; for( unsigned ii = 0; ii < aHoleListBuffer.size(); ii++ ) { if( aHoleListBuffer[ii].m_Hole_Diameter != LastHole ) { new_tool.m_Diameter = ( aHoleListBuffer[ii].m_Hole_Diameter ); aToolListBuffer.push_back( new_tool ); LastHole = new_tool.m_Diameter; } jj = aToolListBuffer.size(); if( jj == 0 ) continue; // Should not occurs aHoleListBuffer[ii].m_Tool_Reference = jj; // Tool value Initialized (value >= 1) aToolListBuffer.back().m_TotalCount++; if( aHoleListBuffer[ii].m_Hole_Shape ) aToolListBuffer.back().m_OvalCount++; } }
void EDA_3D_CANVAS::buildBoardThroughHolesPolygonList( SHAPE_POLY_SET& allBoardHoles, int aSegCountPerCircle, bool aOptimizeLargeCircles ) { // hole diameter value to change seg count by circle: int small_hole_limit = Millimeter2iu( 1.0 ); int copper_thickness = GetPrm3DVisu().GetCopperThicknessBIU(); BOARD* pcb = GetBoard(); // Build holes of through vias: for( TRACK* track = pcb->m_Track; track; track = track->Next() ) { if( track->Type() != PCB_VIA_T ) continue; VIA *via = static_cast<VIA*>( track ); if( via->GetViaType() != VIA_THROUGH ) continue; int holediameter = via->GetDrillValue(); int hole_outer_radius = (holediameter + copper_thickness) / 2; TransformCircleToPolygon( allBoardHoles, via->GetStart(), hole_outer_radius, aSegCountPerCircle ); } // Build holes of through pads: for( MODULE* footprint = pcb->m_Modules; footprint; footprint = footprint->Next() ) { for( D_PAD* pad = footprint->Pads(); pad; pad = pad->Next() ) { // Calculate a factor to apply to segcount for large holes ( > 1 mm) // (bigger pad drill size -> more segments) because holes in pads can have // very different sizes and optimizing this segcount gives a better look // Mainly mounting holes have a size bigger than small_hole_limit wxSize padHole = pad->GetDrillSize(); if( ! padHole.x ) // Not drilled pad like SMD pad continue; // we use the hole diameter to calculate the seg count. // for round holes, padHole.x == padHole.y // for oblong holes, the diameter is the smaller of (padHole.x, padHole.y) int diam = std::min( padHole.x, padHole.y ); int segcount = aSegCountPerCircle; if( diam > small_hole_limit ) { double segFactor = (double)diam / small_hole_limit; segcount = (int)(aSegCountPerCircle * segFactor); // limit segcount to 48. For a circle this is a very good approx. if( segcount > 48 ) segcount = 48; } // The hole in the body is inflated by copper thickness. int inflate = copper_thickness; // If not plated, no copper. if( pad->GetAttribute () == PAD_HOLE_NOT_PLATED ) inflate = 0; pad->BuildPadDrillShapePolygon( allBoardHoles, inflate, segcount ); } } allBoardHoles.Simplify(); }
void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeatures ) { int segsPerCircle; double correctionFactor; // Set the number of segments in arc approximations if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF ) segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF; else segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF; /* calculates the coeff to compensate radius reduction of holes clearance * due to the segment approx. * For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2) * s_Correction is 1 /cos( PI/s_CircleToSegmentsCount ) */ correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle ); aFeatures.RemoveAllContours(); int outline_half_thickness = m_ZoneMinThickness / 2; int zone_clearance = std::max( m_ZoneClearance, GetClearance() ); zone_clearance += outline_half_thickness; /* store holes (i.e. tracks and pads areas as polygons outlines) * in a polygon list */ /* items ouside the zone bounding box are skipped * the bounding box is the zone bounding box + the biggest clearance found in Netclass list */ EDA_RECT item_boundingbox; EDA_RECT zone_boundingbox = GetBoundingBox(); int biggest_clearance = aPcb->GetDesignSettings().GetBiggestClearanceValue(); biggest_clearance = std::max( biggest_clearance, zone_clearance ); zone_boundingbox.Inflate( biggest_clearance ); /* * First : Add pads. Note: pads having the same net as zone are left in zone. * Thermal shapes will be created later if necessary */ int item_clearance; /* Use a dummy pad to calculate hole clerance when a pad is not on all copper layers * and this pad has a hole * This dummy pad has the size and shape of the hole * Therefore, this dummy pad is a circle or an oval. * A pad must have a parent because some functions expect a non null parent * to find the parent board, and some other data */ MODULE dummymodule( aPcb ); // Creates a dummy parent D_PAD dummypad( &dummymodule ); for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { D_PAD* nextpad; for( D_PAD* pad = module->Pads(); pad != NULL; pad = nextpad ) { nextpad = pad->Next(); // pad pointer can be modified by next code, so // calculate the next pad here if( !pad->IsOnLayer( GetLayer() ) ) { /* Test for pads that are on top or bottom only and have a hole. * There are curious pads but they can be used for some components that are * inside the board (in fact inside the hole. Some photo diodes and Leds are * like this) */ if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 ) continue; // Use a dummy pad to calculate a hole shape that have the same dimension as // the pad hole dummypad.SetSize( pad->GetDrillSize() ); dummypad.SetOrientation( pad->GetOrientation() ); dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ? PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE ); dummypad.SetPosition( pad->GetPosition() ); pad = &dummypad; } // Note: netcode <=0 means not connected item if( ( pad->GetNetCode() != GetNetCode() ) || ( pad->GetNetCode() <= 0 ) ) { item_clearance = pad->GetClearance() + outline_half_thickness; item_boundingbox = pad->GetBoundingBox(); item_boundingbox.Inflate( item_clearance ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { int clearance = std::max( zone_clearance, item_clearance ); pad->TransformShapeWithClearanceToPolygon( aFeatures, clearance, segsPerCircle, correctionFactor ); } continue; } // Pads are removed from zone if the setup is PAD_ZONE_CONN_NONE if( GetPadConnection( pad ) == PAD_ZONE_CONN_NONE ) { int gap = zone_clearance; int thermalGap = GetThermalReliefGap( pad ); gap = std::max( gap, thermalGap ); item_boundingbox = pad->GetBoundingBox(); item_boundingbox.Inflate( gap ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { pad->TransformShapeWithClearanceToPolygon( aFeatures, gap, segsPerCircle, correctionFactor ); } } } } /* Add holes (i.e. tracks and vias areas as polygons outlines) * in cornerBufferPolysToSubstract */ for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) { if( !track->IsOnLayer( GetLayer() ) ) continue; if( track->GetNetCode() == GetNetCode() && (GetNetCode() != 0) ) continue; item_clearance = track->GetClearance() + outline_half_thickness; item_boundingbox = track->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { int clearance = std::max( zone_clearance, item_clearance ); track->TransformShapeWithClearanceToPolygon( aFeatures, clearance, segsPerCircle, correctionFactor ); } } /* Add module edge items that are on copper layers * Pcbnew allows these items to be on copper layers in microwave applictions * This is a bad thing, but must be handled here, until a better way is found */ for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( !item->IsOnLayer( GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) ) continue; if( item->Type() != PCB_MODULE_EDGE_T ) continue; item_boundingbox = item->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { ( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon( aFeatures, zone_clearance, segsPerCircle, correctionFactor ); } } } // Add graphic items (copper texts) and board edges for( BOARD_ITEM* item = aPcb->m_Drawings; item; item = item->Next() ) { if( item->GetLayer() != GetLayer() && item->GetLayer() != Edge_Cuts ) continue; switch( item->Type() ) { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( aFeatures, zone_clearance, segsPerCircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon( aFeatures, zone_clearance ); break; default: break; } } // Add zones outlines having an higher priority and keepout for( int ii = 0; ii < GetBoard()->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = GetBoard()->GetArea( ii ); if( zone->GetLayer() != GetLayer() ) continue; if( !zone->GetIsKeepout() && zone->GetPriority() <= GetPriority() ) continue; if( zone->GetIsKeepout() && ! zone->GetDoNotAllowCopperPour() ) continue; // A highter priority zone or keepout area is found: remove this area item_boundingbox = zone->GetBoundingBox(); if( !item_boundingbox.Intersects( zone_boundingbox ) ) continue; // Add the zone outline area. // However if the zone has the same net as the current zone, // do not add any clearance. // the zone will be connected to the current zone, but filled areas // will use different parameters (clearance, thermal shapes ) bool same_net = GetNetCode() == zone->GetNetCode(); bool use_net_clearance = true; int min_clearance = zone_clearance; // Do not forget to make room to draw the thick outlines // of the hole created by the area of the zone to remove int holeclearance = zone->GetClearance() + outline_half_thickness; // The final clearance is obviously the max value of each zone clearance min_clearance = std::max( min_clearance, holeclearance ); if( zone->GetIsKeepout() || same_net ) { // Just take in account the fact the outline has a thickness, so // the actual area to substract is inflated to take in account this fact min_clearance = outline_half_thickness; use_net_clearance = false; } zone->TransformOutlinesShapeWithClearanceToPolygon( aFeatures, min_clearance, use_net_clearance ); } // Remove thermal symbols for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad != NULL; pad = pad->Next() ) { // Rejects non-standard pads with tht-only thermal reliefs if( GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL && pad->GetAttribute() != PAD_ATTRIB_STANDARD ) continue; if( GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL && GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL ) continue; if( !pad->IsOnLayer( GetLayer() ) ) continue; if( pad->GetNetCode() != GetNetCode() ) continue; item_boundingbox = pad->GetBoundingBox(); int thermalGap = GetThermalReliefGap( pad ); item_boundingbox.Inflate( thermalGap, thermalGap ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { CreateThermalReliefPadPolygon( aFeatures, *pad, thermalGap, GetThermalReliefCopperBridge( pad ), m_ZoneMinThickness, segsPerCircle, correctionFactor, s_thermalRot ); } } } }
void EXCELLON_WRITER::BuildHolesList( LAYER_PAIR aLayerPair, bool aGenerateNPTH_list ) { HOLE_INFO new_hole; m_holeListBuffer.clear(); m_toolListBuffer.clear(); wxASSERT( aLayerPair.first < aLayerPair.second ); // fix the caller // build hole list for vias if( ! aGenerateNPTH_list ) // vias are always plated ! { for( VIA* via = GetFirstVia( m_pcb->m_Track ); via; via = GetFirstVia( via->Next() ) ) { int hole_sz = via->GetDrillValue(); if( hole_sz == 0 ) // Should not occur. continue; new_hole.m_Tool_Reference = -1; // Flag value for Not initialized new_hole.m_Hole_Orient = 0; new_hole.m_Hole_Diameter = hole_sz; new_hole.m_Hole_NotPlated = false; new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter; new_hole.m_Hole_Shape = 0; // hole shape: round new_hole.m_Hole_Pos = via->GetStart(); via->LayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer ); // LayerPair() returns params with m_Hole_Bottom_Layer > m_Hole_Top_Layer // Remember: top layer = 0 and bottom layer = 31 for through hole vias // Any captured via should be from aLayerPair.first to aLayerPair.second exactly. if( new_hole.m_Hole_Top_Layer != aLayerPair.first || new_hole.m_Hole_Bottom_Layer != aLayerPair.second ) continue; m_holeListBuffer.push_back( new_hole ); } } if( aLayerPair == LAYER_PAIR( F_Cu, B_Cu ) ) { // add holes for thru hole pads for( MODULE* module = m_pcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) { if( !m_merge_PTH_NPTH ) { if( !aGenerateNPTH_list && pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED ) continue; if( aGenerateNPTH_list && pad->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED ) continue; } if( pad->GetDrillSize().x == 0 ) continue; new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED); new_hole.m_Tool_Reference = -1; // Flag is: Not initialized new_hole.m_Hole_Orient = pad->GetOrientation(); new_hole.m_Hole_Shape = 0; // hole shape: round new_hole.m_Hole_Diameter = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y ); new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter; if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE ) new_hole.m_Hole_Shape = 1; // oval flag set new_hole.m_Hole_Size = pad->GetDrillSize(); new_hole.m_Hole_Pos = pad->GetPosition(); // hole position new_hole.m_Hole_Bottom_Layer = B_Cu; new_hole.m_Hole_Top_Layer = F_Cu; // pad holes are through holes m_holeListBuffer.push_back( new_hole ); } } } // Sort holes per increasing diameter value sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), CmpHoleSettings ); // build the tool list int last_hole = -1; // Set to not initialized (this is a value not used // for m_holeListBuffer[ii].m_Hole_Diameter) bool last_notplated_opt = false; DRILL_TOOL new_tool( 0, false ); unsigned jj; for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ ) { if( m_holeListBuffer[ii].m_Hole_Diameter != last_hole || m_holeListBuffer[ii].m_Hole_NotPlated != last_notplated_opt ) { new_tool.m_Diameter = m_holeListBuffer[ii].m_Hole_Diameter; new_tool.m_Hole_NotPlated = m_holeListBuffer[ii].m_Hole_NotPlated; m_toolListBuffer.push_back( new_tool ); last_hole = new_tool.m_Diameter; last_notplated_opt = new_tool.m_Hole_NotPlated; } jj = m_toolListBuffer.size(); if( jj == 0 ) continue; // Should not occurs m_holeListBuffer[ii].m_Tool_Reference = jj; // Tool value Initialized (value >= 1) m_toolListBuffer.back().m_TotalCount++; if( m_holeListBuffer[ii].m_Hole_Shape ) m_toolListBuffer.back().m_OvalCount++; } }
/* * 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; double 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->GetFPID() != Module_Ref->GetFPID() ) continue; bool saveMe = false; for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) { // Filters changes prohibited. 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; 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->GetFPID() != Module_Ref->GetFPID() ) 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->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->GetLayerSet() != aPad->GetLayerSet() ) continue; else m_Pcb->m_Status_Pcb &= ~( LISTE_RATSNEST_ITEM_OK | CONNEXION_OK); } // Change characteristics: pad->SetAttribute( aPad->GetAttribute() ); pad->SetShape( aPad->GetShape() ); pad->SetLayerSet( aPad->GetLayerSet() ); 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(); }