void DIALOG_PAD_PROPERTIES::redraw() { if( m_parent->IsGalCanvasActive() ) { m_dummyPad->ViewUpdate(); BOX2I bbox = m_dummyPad->ViewBBox(); if( bbox.GetSize().x > 0 && bbox.GetSize().y > 0 ) { // Autozoom m_panelShowPadGal->GetView()->SetViewport( BOX2D( bbox.GetOrigin(), bbox.GetSize() ) ); // Add a margin m_panelShowPadGal->GetView()->SetScale( m_panelShowPadGal->GetView()->GetScale() * 0.7 ); m_panelShowPadGal->Refresh(); } } else { m_panelShowPad->Refresh(); } }
/* Delete marked items */ void DeleteMarkedItems( MODULE* module ) { if( module == NULL ) return; D_PAD* next_pad; BOARD* board = module->GetBoard(); for( D_PAD* pad = module->Pads(); pad; pad = next_pad ) { next_pad = pad->Next(); if( !pad->IsSelected() ) continue; if( board ) board->PadDelete( pad ); else pad->DeleteStructure(); } BOARD_ITEM* next_item; for( BOARD_ITEM* item = module->GraphicalItems(); item; item = next_item ) { next_item = item->Next(); if( !item->IsSelected() ) continue; item->DeleteStructure(); } // Ref and value can be flagged, but cannot be deleted ClearMarkItems( module ); }
void TRACKS_CLEANER::buildTrackConnectionInfo() { BuildTracksCandidatesList( m_Brd->m_Track, NULL); // clear flags and variables used in cleanup for( TRACK * track = m_Brd->m_Track; track; track = track->Next() ) { track->start = NULL; track->end = NULL; track->m_PadsConnected.clear(); track->SetState( START_ON_PAD|END_ON_PAD|BUSY, false ); } // Build connections info tracks to pads SearchTracksConnectedToPads(); for( TRACK * track = m_Brd->m_Track; track; track = track->Next() ) { // Mark track if connected to pads for( unsigned jj = 0; jj < track->m_PadsConnected.size(); jj++ ) { D_PAD * pad = track->m_PadsConnected[jj]; if( pad->HitTest( track->GetStart() ) ) { track->start = pad; track->SetState( START_ON_PAD, true ); } if( pad->HitTest( track->GetEnd() ) ) { track->end = pad; track->SetState( END_ON_PAD, true ); } } } }
/* Copy marked items, at new position = old position + offset */ void CopyMarkedItems( MODULE* module, wxPoint offset, bool aIncrement ) { if( module == NULL ) return; // Reference and value cannot be copied, they are unique. // Ensure they are not selected module->Reference().ClearFlags(); module->Value().ClearFlags(); for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) { if( !pad->IsSelected() ) continue; pad->ClearFlags( SELECTED ); D_PAD* NewPad = new D_PAD( *pad ); NewPad->SetParent( module ); NewPad->SetFlags( SELECTED ); module->Pads().PushFront( NewPad ); if( aIncrement ) NewPad->IncrementPadName( true, true ); } BOARD_ITEM* newItem; for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( !item->IsSelected() ) continue; item->ClearFlags( SELECTED ); newItem = (BOARD_ITEM*)item->Clone(); newItem->SetParent( module ); newItem->SetFlags( SELECTED ); module->GraphicalItems().PushFront( newItem ); } MoveMarkedItems( module, offset ); }
MODULE* PCB_EDIT_FRAME::CreateMuWaveBaseFootprint( const wxString& aValue, int aTextSize, int aPadCount ) { MODULE* module = CreateNewModule( aValue ); if( aTextSize > 0 ) { module->Reference().SetSize( wxSize( aTextSize, aTextSize ) ); module->Reference().SetThickness( aTextSize/5 ); module->Value().SetSize( wxSize( aTextSize, aTextSize ) ); module->Value().SetThickness( aTextSize/5 ); } // Create 2 pads used in gaps and stubs. The gap is between these 2 pads // the stub is the pad 2 wxString Line; int pad_num = 1; while( aPadCount-- ) { D_PAD* pad = new D_PAD( module ); module->Pads().PushFront( pad ); int tw = GetDesignSettings().GetCurrentTrackWidth(); pad->SetSize( wxSize( tw, tw ) ); pad->SetPosition( module->GetPosition() ); pad->SetShape( PAD_SHAPE_RECT ); pad->SetAttribute( PAD_ATTRIB_SMD ); pad->SetLayerSet( F_Cu ); Line.Printf( wxT( "%d" ), pad_num ); pad->SetPadName( Line ); pad_num++; } return module; }
/* 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() ); }
void MODULE::SetOrientation( double newangle ) { double angleChange = newangle - m_Orient; // change in rotation wxPoint pt; NORMALIZE_ANGLE_POS( newangle ); m_Orient = newangle; for( D_PAD* pad = m_Pads; pad; pad = pad->Next() ) { pt = pad->GetPos0(); pad->SetOrientation( pad->GetOrientation() + angleChange ); RotatePoint( &pt, m_Orient ); pad->SetPosition( GetPosition() + pt ); } // Update of the reference and value. m_Reference->SetDrawCoord(); m_Value->SetDrawCoord(); // Displace contours and text of the footprint. for( BOARD_ITEM* item = m_Drawings; item; item = item->Next() ) { if( item->Type() == PCB_MODULE_EDGE_T ) { EDGE_MODULE* edge = (EDGE_MODULE*) item; edge->SetDrawCoord(); } else if( item->Type() == PCB_MODULE_TEXT_T ) { TEXTE_MODULE* text = (TEXTE_MODULE*) item; text->SetDrawCoord(); } } CalculateBoundingBox(); }
/* generate pads shapes on layer aLayer as polygons, * and adds these polygons to aCornerBuffer * aCornerBuffer = the buffer to store polygons * aInflateValue = an additionnal size to add to pad shapes * aCircleToSegmentsCount = number of segments to approximate a circle * aCorrectionFactor = the correction to apply to a circle radius * to generate the polygon. * if aCorrectionFactor = 1.0, the polygon is inside the circle * the radius of circle approximated by segments is * initial radius * aCorrectionFactor */ void MODULE::TransformPadsShapesWithClearanceToPolygon( LAYER_NUM aLayer, CPOLYGONS_LIST& aCornerBuffer, int aInflateValue, int aCircleToSegmentsCount, double aCorrectionFactor ) { D_PAD* pad = Pads(); wxSize margin; for( ; pad != NULL; pad = pad->Next() ) { if( !pad->IsOnLayer(aLayer) ) continue; switch( aLayer ) { case SOLDERMASK_N_FRONT: case SOLDERMASK_N_BACK: margin.x = margin.y = pad->GetSolderMaskMargin() + aInflateValue; break; case SOLDERPASTE_N_FRONT: case SOLDERPASTE_N_BACK: margin = pad->GetSolderPasteMargin(); margin.x += aInflateValue; margin.y += aInflateValue; break; default: margin.x = margin.y = aInflateValue; break; } pad->BuildPadShapePolygon( aCornerBuffer, margin, aCircleToSegmentsCount, aCorrectionFactor ); } }
void DRC::testPad2Pad() { std::vector<D_PAD*> sortedPads; m_pcb->GetSortedPadListByXthenYCoord( sortedPads ); // find the max size of the pads (used to stop the test) int max_size = 0; for( unsigned i = 0; i < sortedPads.size(); ++i ) { D_PAD* pad = sortedPads[i]; // GetBoundingRadius() is the radius of the minimum sized circle fully containing the pad int radius = pad->GetBoundingRadius(); if( radius > max_size ) max_size = radius; } // Test the pads D_PAD** listEnd = &sortedPads[ sortedPads.size() ]; for( unsigned i = 0; i< sortedPads.size(); ++i ) { D_PAD* pad = sortedPads[i]; int x_limit = max_size + pad->GetClearance() + pad->GetBoundingRadius() + pad->GetPosition().x; if( !doPadToPadsDrc( pad, &sortedPads[i], listEnd, x_limit ) ) { wxASSERT( m_currentMarker ); m_pcb->Add( m_currentMarker ); m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker ); m_currentMarker = 0; } } }
MODULE* MWAVE::CreateMicrowaveInductor( INDUCTOR_PATTERN& inductorPattern, PCB_EDIT_FRAME* aPcbFrame, wxString& aErrorMessage ) { /* Build a microwave inductor footprint. * - Length Mself.lng * - Extremities Mself.m_Start and Mself.m_End * We must determine: * Mself.nbrin = number of segments perpendicular to the direction * (The coil nbrin will demicercles + 1 + 2 1 / 4 circle) * Mself.lbrin = length of a strand * Mself.radius = radius of rounded parts of the coil * Mself.delta = segments extremities connection between him and the coil even * * The equations are * Mself.m_Size.x = 2 * Mself.radius + Mself.lbrin * Mself.m_Size.y * Mself.delta = 2 + 2 * Mself.nbrin * Mself.radius * Mself.lng = 2 * Mself.delta / / connections to the coil + (Mself.nbrin-2) * Mself.lbrin / / length of the strands except 1st and last + (Mself.nbrin 1) * (PI * Mself.radius) / / length of rounded * Mself.lbrin + / 2 - Melf.radius * 2) / / length of 1st and last bit * * The constraints are: * Nbrin >= 2 * Mself.radius < Mself.m_Size.x * Mself.m_Size.y = Mself.radius * 4 + 2 * Mself.raccord * Mself.lbrin> Mself.radius * 2 * * The calculation is conducted in the following way: * Initially: * Nbrin = 2 * Radius = 4 * m_Size.x (arbitrarily fixed value) * Then: * Increasing the number of segments to the desired length * (Radius decreases if necessary) */ D_PAD* pad; int ll; wxString msg; auto pt = inductorPattern.m_End - inductorPattern.m_Start; int min_len = KiROUND( EuclideanNorm( pt ) ); inductorPattern.m_length = min_len; // Enter the desired length. msg = StringFromValue( g_UserUnit, inductorPattern.m_length ); wxTextEntryDialog dlg( nullptr, wxEmptyString, _( "Length of Trace:" ), msg ); if( dlg.ShowModal() != wxID_OK ) return nullptr; // canceled by user msg = dlg.GetValue(); inductorPattern.m_length = ValueFromString( g_UserUnit, msg ); // Control values (ii = minimum length) if( inductorPattern.m_length < min_len ) { aErrorMessage = _( "Requested length < minimum length" ); return nullptr; } // Calculate the elements. std::vector <wxPoint> buffer; ll = BuildCornersList_S_Shape( buffer, inductorPattern.m_Start, inductorPattern.m_End, inductorPattern.m_length, inductorPattern.m_Width ); if( !ll ) { aErrorMessage = _( "Requested length too large" ); return nullptr; } // Generate footprint. the value is also used as footprint name. msg = "L"; wxTextEntryDialog cmpdlg( nullptr, wxEmptyString, _( "Component Value:" ), msg ); cmpdlg.SetTextValidator( FILE_NAME_CHAR_VALIDATOR( &msg ) ); if( ( cmpdlg.ShowModal() != wxID_OK ) || msg.IsEmpty() ) return nullptr; // Aborted by user MODULE* module = aPcbFrame->CreateNewModule( msg ); // here the module is already in the BOARD, CreateNewModule() does that. module->SetFPID( LIB_ID( wxString( "mw_inductor" ) ) ); module->SetAttributes( MOD_VIRTUAL | MOD_CMS ); module->ClearFlags(); module->SetPosition( inductorPattern.m_End ); // Generate segments for( unsigned jj = 1; jj < buffer.size(); jj++ ) { EDGE_MODULE* PtSegm; PtSegm = new EDGE_MODULE( module ); PtSegm->SetStart( buffer[jj - 1] ); PtSegm->SetEnd( buffer[jj] ); PtSegm->SetWidth( inductorPattern.m_Width ); PtSegm->SetLayer( module->GetLayer() ); PtSegm->SetShape( S_SEGMENT ); PtSegm->SetStart0( PtSegm->GetStart() - module->GetPosition() ); PtSegm->SetEnd0( PtSegm->GetEnd() - module->GetPosition() ); module->GraphicalItemsList().PushBack( PtSegm ); } // Place a pad on each end of coil. pad = new D_PAD( module ); module->PadsList().PushFront( pad ); pad->SetName( "1" ); pad->SetPosition( inductorPattern.m_End ); pad->SetPos0( pad->GetPosition() - module->GetPosition() ); pad->SetSize( wxSize( inductorPattern.m_Width, inductorPattern.m_Width ) ); pad->SetLayerSet( LSET( module->GetLayer() ) ); pad->SetAttribute( PAD_ATTRIB_SMD ); pad->SetShape( PAD_SHAPE_CIRCLE ); D_PAD* newpad = new D_PAD( *pad ); module->PadsList().Insert( newpad, pad->Next() ); pad = newpad; pad->SetName( "2" ); pad->SetPosition( inductorPattern.m_Start ); pad->SetPos0( pad->GetPosition() - module->GetPosition() ); // Modify text positions. wxPoint refPos( ( inductorPattern.m_Start.x + inductorPattern.m_End.x ) / 2, ( inductorPattern.m_Start.y + inductorPattern.m_End.y ) / 2 ); wxPoint valPos = refPos; refPos.y -= module->Reference().GetTextSize().y; module->Reference().SetPosition( refPos ); valPos.y += module->Value().GetTextSize().y; module->Value().SetPosition( valPos ); module->CalculateBoundingBox(); return module; }
MODULE& MODULE::operator=( const MODULE& aOther ) { BOARD_ITEM::operator=( aOther ); m_Pos = aOther.m_Pos; m_fpid = aOther.m_fpid; m_Attributs = aOther.m_Attributs; m_ModuleStatus = aOther.m_ModuleStatus; m_Orient = aOther.m_Orient; m_BoundaryBox = aOther.m_BoundaryBox; m_CntRot90 = aOther.m_CntRot90; m_CntRot180 = aOther.m_CntRot180; m_LastEditTime = aOther.m_LastEditTime; m_Link = aOther.m_Link; m_Path = aOther.m_Path; //is this correct behavior? m_LocalClearance = aOther.m_LocalClearance; m_LocalSolderMaskMargin = aOther.m_LocalSolderMaskMargin; m_LocalSolderPasteMargin = aOther.m_LocalSolderPasteMargin; m_LocalSolderPasteMarginRatio = aOther.m_LocalSolderPasteMarginRatio; m_ZoneConnection = aOther.m_ZoneConnection; m_ThermalWidth = aOther.m_ThermalWidth; m_ThermalGap = aOther.m_ThermalGap; // Copy reference and value *m_Reference = *aOther.m_Reference; m_Reference->SetParent( this ); *m_Value = *aOther.m_Value; m_Value->SetParent( this ); // Copy auxiliary data: Pads m_Pads.DeleteAll(); for( D_PAD* pad = aOther.m_Pads; pad; pad = pad->Next() ) { Add( new D_PAD( *pad ) ); } // Copy auxiliary data: Drawings m_Drawings.DeleteAll(); for( BOARD_ITEM* item = aOther.m_Drawings; item; item = item->Next() ) { switch( item->Type() ) { case PCB_MODULE_TEXT_T: case PCB_MODULE_EDGE_T: Add( static_cast<BOARD_ITEM*>( item->Clone() ) ); break; default: wxLogMessage( wxT( "MODULE::operator=() internal error: unknown type" ) ); break; } } // Copy auxiliary data: 3D_Drawings info m_3D_Drawings.clear(); m_3D_Drawings = aOther.m_3D_Drawings; m_Doc = aOther.m_Doc; m_KeyWord = aOther.m_KeyWord; // Ensure auxiliary data is up to date CalculateBoundingBox(); return *this; }
void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* aActivity ) { BOARD* pcb = GetBoard(); bool useTextures = isRealisticMode() && isEnabled( FL_RENDER_TEXTURES ); // Number of segments to draw a circle using segments const int segcountforcircle = 18; double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) ); 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) ); // segments to draw a circle to build texts. Is is used only to build // the shape of each segment of the stroke font, therefore no need to have // many segments per circle. const int segcountInStrokeFont = 8; SHAPE_POLY_SET bufferPolys; SHAPE_POLY_SET allLayerHoles; // Contains through holes, calculated only once SHAPE_POLY_SET bufferPcbOutlines; // stores the board main outlines // 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 no optimization of large holes shape. buildBoardThroughHolesPolygonList( allLayerHoles, segcountLowQuality, false ); // draw graphic items, on technical layers static const LAYER_ID teckLayerList[] = { B_Adhes, F_Adhes, B_Paste, F_Paste, B_SilkS, F_SilkS, B_Mask, F_Mask, }; // User layers are not drawn here, only technical layers for( LSEQ seq = LSET::AllTechMask().Seq( teckLayerList, DIM( teckLayerList ) ); seq; ++seq ) { LAYER_ID layer = *seq; if( !is3DLayerEnabled( layer ) ) continue; if( layer == Edge_Cuts && isEnabled( FL_SHOW_BOARD_BODY ) ) continue; if( aActivity ) aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) ); bufferPolys.RemoveAllContours(); for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() ) { if( !item->IsOnLayer( layer ) ) continue; switch( item->Type() ) { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( bufferPolys, 0, segcountforcircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( bufferPolys, 0, segcountLowQuality, 1.0 ); break; default: break; } } for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) { if( layer == F_SilkS || layer == B_SilkS ) { // On silk screen layers, the pad shape is only the pad outline // never a filled shape D_PAD* pad = module->Pads(); int linewidth = g_DrawDefaultLineThickness; for( ; pad; pad = pad->Next() ) { if( !pad->IsOnLayer( layer ) ) continue; buildPadShapeThickOutlineAsPolygon( pad, bufferPolys, linewidth, segcountforcircle, correctionFactor ); } } else module->TransformPadsShapesWithClearanceToPolygon( layer, bufferPolys, 0, segcountforcircle, correctionFactor ); // On tech layers, use a poor circle approximation, only for texts (stroke font) module->TransformGraphicShapesWithClearanceToPolygonSet( layer, bufferPolys, 0, segcountforcircle, correctionFactor, segcountInStrokeFont ); } // Draw non copper zones if( isEnabled( FL_ZONE ) ) { for( int ii = 0; ii < pcb->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = pcb->GetArea( ii ); if( !zone->IsOnLayer( layer ) ) continue; zone->TransformSolidAreasShapesToPolygonSet( bufferPolys, segcountLowQuality, correctionFactorLQ ); } } // bufferPolys contains polygons to merge. Many overlaps . // Calculate merged polygons and remove pads and vias holes if( bufferPolys.IsEmpty() ) continue; // Solder mask layers are "negative" layers. // Shapes should be removed from the full board area. if( layer == B_Mask || layer == F_Mask ) { SHAPE_POLY_SET cuts = bufferPolys; bufferPolys = bufferPcbOutlines; cuts.Append(allLayerHoles); cuts.Simplify(); bufferPolys.BooleanSubtract( cuts ); } // Remove holes from Solder paste layers and silkscreen else if( layer == B_Paste || layer == F_Paste || layer == B_SilkS || layer == F_SilkS ) { bufferPolys.BooleanSubtract( allLayerHoles ); } int thickness = 0; if( layer != B_Mask && layer != F_Mask ) thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer ); int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer ); if( layer == Edge_Cuts ) { thickness = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu ) - GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ); zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ) + (thickness / 2); } else { // for Draw3D_SolidHorizontalPolyPolygons, zpos it the middle between bottom and top // sides. // However for top layers, zpos should be the bottom layer pos, // and for bottom layers, zpos should be the top layer pos. if( Get3DLayer_Z_Orientation( layer ) > 0 ) zpos += thickness/2; else zpos -= thickness/2 ; } 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 znormal must face the layer direction // because it will draw just one plane if( !thickness ) zNormal = Get3DLayer_Z_Orientation( layer ); setGLTechLayersColor( layer ); Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos, thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, zNormal ); } }
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 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(); }
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(); }
int getOptimalModulePlacement( PCB_EDIT_FRAME* aFrame, MODULE* aModule, wxDC* aDC ) { int error = 1; wxPoint LastPosOK; double min_cost, curr_cost, Score; bool TstOtherSide; DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)aFrame->GetDisplayOptions(); BOARD* brd = aFrame->GetBoard(); aModule->CalculateBoundingBox(); bool showRats = displ_opts->m_Show_Module_Ratsnest; displ_opts->m_Show_Module_Ratsnest = false; brd->m_Status_Pcb &= ~RATSNEST_ITEM_LOCAL_OK; aFrame->SetMsgPanel( aModule ); LastPosOK = RoutingMatrix.m_BrdBox.GetOrigin(); wxPoint mod_pos = aModule->GetPosition(); EDA_RECT fpBBox = aModule->GetFootprintRect(); // Move fpBBox to have the footprint position at (0,0) fpBBox.Move( -mod_pos ); wxPoint fpBBoxOrg = fpBBox.GetOrigin(); // Calculate the limit of the footprint position, relative // to the routing matrix area wxPoint xylimit = RoutingMatrix.m_BrdBox.GetEnd() - fpBBox.GetEnd(); wxPoint initialPos = RoutingMatrix.m_BrdBox.GetOrigin() - fpBBoxOrg; // Stay on grid. initialPos.x -= initialPos.x % RoutingMatrix.m_GridRouting; initialPos.y -= initialPos.y % RoutingMatrix.m_GridRouting; CurrPosition = initialPos; // Undraw the current footprint aModule->DrawOutlinesWhenMoving( aFrame->GetCanvas(), aDC, wxPoint( 0, 0 ) ); g_Offset_Module = mod_pos - CurrPosition; /* Examine pads, and set TstOtherSide to true if a footprint * has at least 1 pad through. */ TstOtherSide = false; if( RoutingMatrix.m_RoutingLayersCount > 1 ) { LSET other( aModule->GetLayer() == B_Cu ? F_Cu : B_Cu ); for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() ) { if( !( pad->GetLayerSet() & other ).any() ) continue; TstOtherSide = true; break; } } // Draw the initial bounding box position EDA_COLOR_T color = BROWN; fpBBox.SetOrigin( fpBBoxOrg + CurrPosition ); draw_FootprintRect(aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, color); min_cost = -1.0; aFrame->SetStatusText( wxT( "Score ??, pos ??" ) ); for( ; CurrPosition.x < xylimit.x; CurrPosition.x += RoutingMatrix.m_GridRouting ) { wxYield(); if( aFrame->GetCanvas()->GetAbortRequest() ) { if( IsOK( aFrame, _( "OK to abort?" ) ) ) { displ_opts->m_Show_Module_Ratsnest = showRats; return ESC; } else aFrame->GetCanvas()->SetAbortRequest( false ); } CurrPosition.y = initialPos.y; for( ; CurrPosition.y < xylimit.y; CurrPosition.y += RoutingMatrix.m_GridRouting ) { // Erase traces. draw_FootprintRect( aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, color ); fpBBox.SetOrigin( fpBBoxOrg + CurrPosition ); g_Offset_Module = mod_pos - CurrPosition; int keepOutCost = TstModuleOnBoard( brd, aModule, TstOtherSide ); // Draw at new place color = keepOutCost >= 0 ? BROWN : RED; draw_FootprintRect( aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, color ); if( keepOutCost >= 0 ) // i.e. if the module can be put here { error = 0; aFrame->build_ratsnest_module( aModule ); curr_cost = compute_Ratsnest_PlaceModule( brd ); Score = curr_cost + keepOutCost; if( (min_cost >= Score ) || (min_cost < 0 ) ) { LastPosOK = CurrPosition; min_cost = Score; wxString msg; msg.Printf( wxT( "Score %g, pos %s, %s" ), min_cost, GetChars( ::CoordinateToString( LastPosOK.x ) ), GetChars( ::CoordinateToString( LastPosOK.y ) ) ); aFrame->SetStatusText( msg ); } } } } // erasing the last traces GRRect( aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, 0, BROWN ); displ_opts->m_Show_Module_Ratsnest = showRats; // Regeneration of the modified variable. CurrPosition = LastPosOK; brd->m_Status_Pcb &= ~( RATSNEST_ITEM_LOCAL_OK | LISTE_PAD_OK ); MinCout = min_cost; return error; }
// 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::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; }
void DRC::testTexts() { std::vector<wxPoint> textShape; // a buffer to store the text shape (set of segments) std::vector<D_PAD*> padList = m_pcb->GetPads(); // Test text areas for vias, tracks and pads inside text areas for( BOARD_ITEM* item = m_pcb->m_Drawings; item; item = item->Next() ) { // Drc test only items on copper layers if( ! IsCopperLayer( item->GetLayer() ) ) continue; // only texts on copper layers are tested if( item->Type() != PCB_TEXT_T ) continue; textShape.clear(); // So far the bounding box makes up the text-area TEXTE_PCB* text = (TEXTE_PCB*) item; text->TransformTextShapeToSegmentList( textShape ); if( textShape.size() == 0 ) // Should not happen (empty text?) continue; for( TRACK* track = m_pcb->m_Track; track != NULL; track = track->Next() ) { if( ! track->IsOnLayer( item->GetLayer() ) ) continue; // Test the distance between each segment and the current track/via int min_dist = ( track->GetWidth() + text->GetThickness() ) /2 + track->GetClearance(NULL); if( track->Type() == PCB_TRACE_T ) { SEG segref( track->GetStart(), track->GetEnd() ); // Error condition: Distance between text segment and track segment is // smaller than the clearance of the segment for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { SEG segtest( textShape[jj], textShape[jj+1] ); int dist = segref.Distance( segtest ); if( dist < min_dist ) { addMarkerToPcb( fillMarker( track, text, DRCE_TRACK_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } else if( track->Type() == PCB_VIA_T ) { // Error condition: Distance between text segment and via is // smaller than the clearance of the via for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { SEG segtest( textShape[jj], textShape[jj+1] ); if( segtest.PointCloserThan( track->GetPosition(), min_dist ) ) { addMarkerToPcb( fillMarker( track, text, DRCE_VIA_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } } // Test pads for( unsigned ii = 0; ii < padList.size(); ii++ ) { D_PAD* pad = padList[ii]; if( ! pad->IsOnLayer( item->GetLayer() ) ) continue; wxPoint shape_pos = pad->ShapePos(); for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { /* In order to make some calculations more easier or faster, * pads and tracks coordinates will be made relative * to the segment origin */ wxPoint origin = textShape[jj]; // origin will be the origin of other coordinates m_segmEnd = textShape[jj+1] - origin; wxPoint delta = m_segmEnd; m_segmAngle = 0; // for a non horizontal or vertical segment Compute the segment angle // in tenths of degrees and its length if( delta.x || delta.y ) // delta.x == delta.y == 0 for vias { // Compute the segment angle in 0,1 degrees m_segmAngle = ArcTangente( delta.y, delta.x ); // Compute the segment length: we build an equivalent rotated segment, // this segment is horizontal, therefore dx = length RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0 } m_segmLength = delta.x; m_padToTestPos = shape_pos - origin; if( !checkClearanceSegmToPad( pad, text->GetThickness(), pad->GetClearance(NULL) ) ) { addMarkerToPcb( fillMarker( pad, text, DRCE_PAD_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } } }
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++; } }
/** * 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; }
/* init board, route traces*/ void PCB_EDIT_FRAME::Autoroute( wxDC* DC, int mode ) { int start, stop; MODULE* Module = NULL; D_PAD* Pad = NULL; int autoroute_net_code = -1; wxString msg; if( GetBoard()->GetCopperLayerCount() > 1 ) { g_Route_Layer_TOP = GetScreen()->m_Route_Layer_TOP; g_Route_Layer_BOTTOM = GetScreen()->m_Route_Layer_BOTTOM; } else { g_Route_Layer_TOP = g_Route_Layer_BOTTOM = LAYER_N_BACK; } switch( mode ) { case ROUTE_NET: if( GetScreen()->GetCurItem() ) { switch( GetScreen()->GetCurItem()->Type() ) { case PCB_PAD_T: Pad = (D_PAD*) GetScreen()->GetCurItem(); autoroute_net_code = Pad->GetNetCode(); break; default: break; } } if( autoroute_net_code <= 0 ) { wxMessageBox( _( "Net not selected" ) ); return; } break; case ROUTE_MODULE: Module = (MODULE*) GetScreen()->GetCurItem(); if( (Module == NULL) || (Module->Type() != PCB_MODULE_T) ) { wxMessageBox( _( "Module not selected" ) ); return; } break; case ROUTE_PAD: Pad = (D_PAD*) GetScreen()->GetCurItem(); if( (Pad == NULL) || (Pad->Type() != PCB_PAD_T) ) { wxMessageBox( _( "Pad not selected" ) ); return; } break; } if( (GetBoard()->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK ) == 0 ) Compile_Ratsnest( DC, true ); /* Set the flag on the ratsnest to CH_ROUTE_REQ. */ for( unsigned ii = 0; ii < GetBoard()->GetRatsnestsCount(); ii++ ) { RATSNEST_ITEM* ptmp = &GetBoard()->m_FullRatsnest[ii]; ptmp->m_Status &= ~CH_ROUTE_REQ; switch( mode ) { case ROUTE_ALL: ptmp->m_Status |= CH_ROUTE_REQ; break; case ROUTE_NET: if( autoroute_net_code == ptmp->GetNet() ) ptmp->m_Status |= CH_ROUTE_REQ; break; case ROUTE_MODULE: { D_PAD* pt_pad = (D_PAD*) Module->Pads(); for( ; pt_pad != NULL; pt_pad = pt_pad->Next() ) { if( ptmp->m_PadStart == pt_pad ) ptmp->m_Status |= CH_ROUTE_REQ; if( ptmp->m_PadEnd == pt_pad ) ptmp->m_Status |= CH_ROUTE_REQ; } break; } case ROUTE_PAD: if( ( ptmp->m_PadStart == Pad ) || ( ptmp->m_PadEnd == Pad ) ) ptmp->m_Status |= CH_ROUTE_REQ; break; } } start = time( NULL ); /* Calculation of no fixed routing to 5 mils and more. */ RoutingMatrix.m_GridRouting = (int)GetScreen()->GetGridSize().x; if( RoutingMatrix.m_GridRouting < (5*IU_PER_MILS) ) RoutingMatrix.m_GridRouting = 5*IU_PER_MILS; /* Calculated ncol and nrow, matrix size for routing. */ RoutingMatrix.ComputeMatrixSize( GetBoard() ); m_messagePanel->EraseMsgBox(); /* Map the board */ RoutingMatrix.m_RoutingLayersCount = 1; if( g_Route_Layer_TOP != g_Route_Layer_BOTTOM ) RoutingMatrix.m_RoutingLayersCount = 2; if( RoutingMatrix.InitRoutingMatrix() < 0 ) { wxMessageBox( _( "No memory for autorouting" ) ); RoutingMatrix.UnInitRoutingMatrix(); /* Free memory. */ return; } SetStatusText( _( "Place Cells" ) ); PlaceCells( GetBoard(), -1, FORCE_PADS ); /* Construction of the track list for router. */ RoutingMatrix.m_RouteCount = Build_Work( GetBoard() ); // DisplayRoutingMatrix( m_canvas, DC ); Solve( DC, RoutingMatrix.m_RoutingLayersCount ); /* Free memory. */ FreeQueue(); InitWork(); /* Free memory for the list of router connections. */ RoutingMatrix.UnInitRoutingMatrix(); stop = time( NULL ) - start; msg.Printf( wxT( "time = %d second%s" ), stop, ( stop == 1 ) ? wxT( "" ) : wxT( "s" ) ); SetStatusText( msg ); }
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++; } }
MODULE::MODULE( const MODULE& aModule ) : BOARD_ITEM_CONTAINER( aModule ) { m_Pos = aModule.m_Pos; m_fpid = aModule.m_fpid; m_Attributs = aModule.m_Attributs; m_ModuleStatus = aModule.m_ModuleStatus; m_Orient = aModule.m_Orient; m_BoundaryBox = aModule.m_BoundaryBox; m_CntRot90 = aModule.m_CntRot90; m_CntRot180 = aModule.m_CntRot180; m_LastEditTime = aModule.m_LastEditTime; m_Link = aModule.m_Link; m_Path = aModule.m_Path; //is this correct behavior? m_LocalClearance = aModule.m_LocalClearance; m_LocalSolderMaskMargin = aModule.m_LocalSolderMaskMargin; m_LocalSolderPasteMargin = aModule.m_LocalSolderPasteMargin; m_LocalSolderPasteMarginRatio = aModule.m_LocalSolderPasteMarginRatio; m_ZoneConnection = aModule.m_ZoneConnection; m_ThermalWidth = aModule.m_ThermalWidth; m_ThermalGap = aModule.m_ThermalGap; // Copy reference and value. m_Reference = new TEXTE_MODULE( *aModule.m_Reference ); m_Reference->SetParent( this ); m_Value = new TEXTE_MODULE( *aModule.m_Value ); m_Value->SetParent( this ); // Copy auxiliary data: Pads for( D_PAD* pad = aModule.m_Pads; pad; pad = pad->Next() ) { Add( new D_PAD( *pad ) ); } // Copy auxiliary data: Drawings for( BOARD_ITEM* item = aModule.m_Drawings; item; item = item->Next() ) { switch( item->Type() ) { case PCB_MODULE_TEXT_T: case PCB_MODULE_EDGE_T: Add( static_cast<BOARD_ITEM*>( item->Clone() ) ); break; default: wxLogMessage( wxT( "Class MODULE copy constructor internal error: unknown type" ) ); break; } } // Copy auxiliary data: 3D_Drawings info m_3D_Drawings = aModule.m_3D_Drawings; m_Doc = aModule.m_Doc; m_KeyWord = aModule.m_KeyWord; m_arflag = 0; // Ensure auxiliary data is up to date CalculateBoundingBox(); m_initial_comments = aModule.m_initial_comments ? new wxArrayString( *aModule.m_initial_comments ) : 0; }
bool DRC::doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_limit ) { const static LSET all_cu = LSET::AllCuMask(); LSET layerMask = aRefPad->GetLayerSet() & all_cu; /* used to test DRC pad to holes: this dummy pad has the size and shape of the hole * to test pad to pad hole DRC, using the pad to pad DRC test function. * Therefore, this dummy pad is a circle or an oval. * A pad must have a parent because some functions expect a non null parent * to find the parent board, and some other data */ MODULE dummymodule( m_pcb ); // Creates a dummy parent D_PAD dummypad( &dummymodule ); // Ensure the hole is on all copper layers dummypad.SetLayerSet( all_cu | dummypad.GetLayerSet() ); // Use the minimal local clearance value for the dummy pad. // The clearance of the active pad will be used as minimum distance to a hole // (a value = 0 means use netclass value) dummypad.SetLocalClearance( 1 ); for( D_PAD** pad_list = aStart; pad_list<aEnd; ++pad_list ) { D_PAD* pad = *pad_list; if( pad == aRefPad ) continue; // We can stop the test when pad->GetPosition().x > x_limit // because the list is sorted by X values if( pad->GetPosition().x > x_limit ) break; // No problem if pads which are on copper layers are on different copper layers, // (pads can be only on a technical layer, to build complex pads) // but their hole (if any ) can create DRC error because they are on all // copper layers, so we test them if( ( pad->GetLayerSet() & layerMask ) == 0 && ( pad->GetLayerSet() & all_cu ) != 0 && ( aRefPad->GetLayerSet() & all_cu ) != 0 ) { // if holes are in the same location and have the same size and shape, // this can be accepted if( pad->GetPosition() == aRefPad->GetPosition() && pad->GetDrillSize() == aRefPad->GetDrillSize() && pad->GetDrillShape() == aRefPad->GetDrillShape() ) { if( aRefPad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE ) continue; // for oval holes: must also have the same orientation if( pad->GetOrientation() == aRefPad->GetOrientation() ) continue; } /* Here, we must test clearance between holes and pads * dummy pad size and shape is adjusted to pad drill size and shape */ if( pad->GetDrillSize().x ) { // pad under testing has a hole, test this hole against pad reference dummypad.SetPosition( pad->GetPosition() ); dummypad.SetSize( pad->GetDrillSize() ); dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ? PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE ); dummypad.SetOrientation( pad->GetOrientation() ); if( !checkClearancePadToPad( aRefPad, &dummypad ) ) { // here we have a drc error on pad! m_currentMarker = fillMarker( pad, aRefPad, DRCE_HOLE_NEAR_PAD, m_currentMarker ); return false; } } if( aRefPad->GetDrillSize().x ) // pad reference has a hole { dummypad.SetPosition( aRefPad->GetPosition() ); dummypad.SetSize( aRefPad->GetDrillSize() ); dummypad.SetShape( aRefPad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ? PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE ); dummypad.SetOrientation( aRefPad->GetOrientation() ); if( !checkClearancePadToPad( pad, &dummypad ) ) { // here we have a drc error on aRefPad! m_currentMarker = fillMarker( aRefPad, pad, DRCE_HOLE_NEAR_PAD, m_currentMarker ); return false; } } continue; } // The pad must be in a net (i.e pt_pad->GetNet() != 0 ), // But no problem if pads have the same netcode (same net) if( pad->GetNetCode() && ( aRefPad->GetNetCode() == pad->GetNetCode() ) ) continue; // if pads are from the same footprint if( pad->GetParent() == aRefPad->GetParent() ) { // and have the same pad number ( equivalent pads ) // one can argue that this 2nd test is not necessary, that any // two pads from a single module are acceptable. This 2nd test // should eventually be a configuration option. if( pad->PadNameEqual( aRefPad ) ) continue; } // if either pad has no drill and is only on technical layers, not a clearance violation if( ( ( pad->GetLayerSet() & layerMask ) == 0 && !pad->GetDrillSize().x ) || ( ( aRefPad->GetLayerSet() & layerMask ) == 0 && !aRefPad->GetDrillSize().x ) ) { continue; } if( !checkClearancePadToPad( aRefPad, pad ) ) { // here we have a drc error! m_currentMarker = fillMarker( aRefPad, pad, DRCE_PAD_NEAR_PAD1, m_currentMarker ); return false; } } return true; }
/* Traces the outline of the search block structures * The entire block follows the cursor */ static void DrawMovingBlockOutlines( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aPosition, bool aErase ) { BASE_SCREEN* screen = aPanel->GetScreen(); FOOTPRINT_EDIT_FRAME* moduleEditFrame = FOOTPRINT_EDIT_FRAME::GetActiveFootprintEditor( aPanel->GetParent() ); wxASSERT( moduleEditFrame ); MODULE* currentModule = moduleEditFrame->GetBoard()->m_Modules; BLOCK_SELECTOR* block = &screen->m_BlockLocate; GRSetDrawMode( aDC, g_XorMode ); if( aErase ) { block->Draw( aPanel, aDC, block->GetMoveVector(), g_XorMode, block->GetColor() ); if( currentModule ) { wxPoint move_offset = -block->GetMoveVector(); BOARD_ITEM* item = currentModule->GraphicalItems(); for( ; item != NULL; item = item->Next() ) { if( !item->IsSelected() ) continue; switch( item->Type() ) { case PCB_MODULE_TEXT_T: case PCB_MODULE_EDGE_T: item->Draw( aPanel, aDC, g_XorMode, move_offset ); break; default: break; } } D_PAD* pad = currentModule->Pads(); for( ; pad != NULL; pad = pad->Next() ) { if( !pad->IsSelected() ) continue; pad->Draw( aPanel, aDC, g_XorMode, move_offset ); } } } // Repaint new view. block->SetMoveVector( moduleEditFrame->GetCrossHairPosition() - block->GetLastCursorPosition() ); block->Draw( aPanel, aDC, block->GetMoveVector(), g_XorMode, block->GetColor() ); if( currentModule ) { BOARD_ITEM* item = currentModule->GraphicalItems(); wxPoint move_offset = - block->GetMoveVector(); for( ; item != NULL; item = item->Next() ) { if( !item->IsSelected() ) continue; switch( item->Type() ) { case PCB_MODULE_TEXT_T: case PCB_MODULE_EDGE_T: item->Draw( aPanel, aDC, g_XorMode, move_offset ); break; default: break; } } D_PAD* pad = currentModule->Pads(); for( ; pad != NULL; pad = pad->Next() ) { if( !pad->IsSelected() ) continue; pad->Draw( aPanel, aDC, g_XorMode, move_offset ); } } }
bool TRACKS_CLEANER::clean_vias() { TRACK* next_track; bool modified = false; for( TRACK* track = m_Brd->m_Track; track; track = track->Next() ) { // Correct via m_End defects (if any) if( track->Type() == PCB_VIA_T ) { if( track->GetStart() != track->GetEnd() ) track->SetEnd( track->GetStart() ); } if( track->GetShape() != VIA_THROUGH ) continue; // Search and delete others vias at same location TRACK* alt_track = track->Next(); for( ; alt_track != NULL; alt_track = next_track ) { next_track = alt_track->Next(); if( alt_track->GetShape() != VIA_THROUGH ) continue; if( alt_track->GetStart() != track->GetStart() ) continue; // delete via alt_track->UnLink(); delete alt_track; modified = true; } } // Delete Via on pads at same location for( TRACK* track = m_Brd->m_Track; track != NULL; track = next_track ) { next_track = track->Next(); if( track->GetShape() != VIA_THROUGH ) continue; // Examine the list of connected pads: // if one pad through is found, the via can be removed for( unsigned ii = 0; ii < track->m_PadsConnected.size(); ii++ ) { D_PAD * pad = track->m_PadsConnected[ii]; if( (pad->GetLayerMask() & ALL_CU_LAYERS) == ALL_CU_LAYERS ) { // redundant: via delete it track->UnLink(); delete track; modified = true; break; } } } return modified; }
/** Mirror marked items, refer to a Vertical axis at position offset * Note: because this function is used in global transform, * if force_all is true, all items will be mirrored */ void MirrorMarkedItems( MODULE* module, wxPoint offset, bool force_all ) { #define SETMIRROR( z ) (z) -= offset.x; (z) = -(z); (z) += offset.x; wxPoint tmp; wxSize tmpz; if( module == NULL ) return; for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) { // Skip pads not selected, i.e. not inside the block to mirror: if( !pad->IsSelected() && !force_all ) continue; tmp = pad->GetPosition(); SETMIRROR( tmp.x ); pad->SetPosition( tmp ); pad->SetX0( pad->GetPosition().x ); tmp = pad->GetOffset(); NEGATE( tmp.x ); pad->SetOffset( tmp ); tmpz = pad->GetDelta(); NEGATE( tmpz.x ); pad->SetDelta( tmpz ); pad->SetOrientation( 1800 - pad->GetOrientation() ); } for( EDA_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { // Skip items not selected, i.e. not inside the block to mirror: if( !item->IsSelected() && !force_all ) continue; switch( item->Type() ) { case PCB_MODULE_EDGE_T: { EDGE_MODULE* em = (EDGE_MODULE*) item; tmp = em->GetStart0(); SETMIRROR( tmp.x ); em->SetStart0( tmp ); em->SetStartX( tmp.x ); tmp = em->GetEnd0(); SETMIRROR( tmp.x ); em->SetEnd0( tmp ); em->SetEndX( tmp.x ); em->SetAngle( -em->GetAngle() ); } break; case PCB_MODULE_TEXT_T: { TEXTE_MODULE* tm = (TEXTE_MODULE*) item; tmp = tm->GetTextPosition(); SETMIRROR( tmp.x ); tm->SetTextPosition( tmp ); tmp.y = tm->GetPos0().y; tm->SetPos0( tmp ); } break; default: break; } item->ClearFlags(); } }
static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule, std::ofstream& aOutputFile, double aVRMLModelsToBiu, bool aExport3DFiles, const wxString& a3D_Subdir ) { // Reference and value if( aModule->Reference().IsVisible() ) export_vrml_text_module( &aModule->Reference() ); if( aModule->Value().IsVisible() ) export_vrml_text_module( &aModule->Value() ); // Export module edges for( EDA_ITEM* item = aModule->GraphicalItems(); item; item = item->Next() ) { switch( item->Type() ) { case PCB_MODULE_TEXT_T: export_vrml_text_module( static_cast<TEXTE_MODULE*>( item ) ); break; case PCB_MODULE_EDGE_T: export_vrml_edge_module( aModel, static_cast<EDGE_MODULE*>( item ), aModule->GetOrientation() ); break; default: break; } } // Export pads for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() ) export_vrml_pad( aModel, aPcb, pad ); bool isFlipped = aModule->GetLayer() == B_Cu; // Export the object VRML model(s) for( S3D_MASTER* vrmlm = aModule->Models(); vrmlm; vrmlm = vrmlm->Next() ) { if( !vrmlm->Is3DType( S3D_MASTER::FILE3D_VRML ) ) continue; wxString fname = vrmlm->GetShape3DFullFilename(); fname.Replace( wxT( "\\" ), wxT( "/" ) ); wxString source_fname = fname; if( aExport3DFiles ) { // Change illegal characters in filenames ChangeIllegalCharacters( fname, true ); fname = a3D_Subdir + wxT( "/" ) + fname; if( !wxFileExists( fname ) ) wxCopyFile( source_fname, fname ); } /* Calculate 3D shape rotation: * this is the rotation parameters, with an additional 180 deg rotation * for footprints that are flipped * When flipped, axis rotation is the horizontal axis (X axis) */ double rotx = -vrmlm->m_MatRotation.x; double roty = -vrmlm->m_MatRotation.y; double rotz = -vrmlm->m_MatRotation.z; if( isFlipped ) { rotx += 180.0; NEGATE( roty ); NEGATE( rotz ); } // Do some quaternion munching double q1[4], q2[4], rot[4]; build_quat( 1, 0, 0, DEG2RAD( rotx ), q1 ); build_quat( 0, 1, 0, DEG2RAD( roty ), q2 ); compose_quat( q1, q2, q1 ); build_quat( 0, 0, 1, DEG2RAD( rotz ), q2 ); compose_quat( q1, q2, q1 ); // Note here aModule->GetOrientation() is in 0.1 degrees, // so module rotation has to be converted to radians build_quat( 0, 0, 1, DECIDEG2RAD( aModule->GetOrientation() ), q2 ); compose_quat( q1, q2, q1 ); from_quat( q1, rot ); aOutputFile << "Transform {\n"; // A null rotation would fail the acos! if( rot[3] != 0.0 ) { aOutputFile << " rotation " << std::setprecision( 3 ); aOutputFile << rot[0] << " " << rot[1] << " " << rot[2] << " " << rot[3] << "\n"; } // adjust 3D shape local offset position // they are given in inch, so they are converted in board IU. double offsetx = vrmlm->m_MatPosition.x * IU_PER_MILS * 1000.0; double offsety = vrmlm->m_MatPosition.y * IU_PER_MILS * 1000.0; double offsetz = vrmlm->m_MatPosition.z * IU_PER_MILS * 1000.0; if( isFlipped ) NEGATE( offsetz ); else // In normal mode, Y axis is reversed in Pcbnew. NEGATE( offsety ); RotatePoint( &offsetx, &offsety, aModule->GetOrientation() ); aOutputFile << " translation " << std::setprecision( aModel.precision ); aOutputFile << (( offsetx + aModule->GetPosition().x) * aModel.scale + aModel.tx ) << " "; aOutputFile << ( -(offsety + aModule->GetPosition().y) * aModel.scale - aModel.ty ) << " "; aOutputFile << ( (offsetz * aModel.scale ) + aModel.GetLayerZ( aModule->GetLayer() ) ) << "\n"; aOutputFile << " scale "; aOutputFile << ( vrmlm->m_MatScale.x * aVRMLModelsToBiu ) << " "; aOutputFile << ( vrmlm->m_MatScale.y * aVRMLModelsToBiu ) << " "; aOutputFile << ( vrmlm->m_MatScale.z * aVRMLModelsToBiu ) << "\n"; if( fname.EndsWith( wxT( "x3d" ) ) ) { X3D_MODEL_PARSER* parser = new X3D_MODEL_PARSER( vrmlm ); if( parser ) { // embed x3d model in vrml format double vrml_to_x3d = aVRMLModelsToBiu; parser->Load( fname, vrml_to_x3d ); try { aOutputFile << " children [\n "; aOutputFile << TO_UTF8( parser->VRML2_representation() ) << " ]\n"; aOutputFile << " }\n"; } catch( const std::exception& e ) { delete parser; throw; } } } else { aOutputFile << " children [\n Inline {\n url \""; aOutputFile << TO_UTF8( fname ) << "\"\n } ]\n"; aOutputFile << " }\n"; } } }
/** Rotate marked items, refer to a rotation point at position offset * Note: because this function is used in global transform, * if force_all is true, all items will be rotated */ void RotateMarkedItems( MODULE* module, wxPoint offset, bool force_all ) { #define ROTATE( z ) RotatePoint( (&z), offset, 900 ) if( module == NULL ) return; for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) { if( !pad->IsSelected() && !force_all ) continue; wxPoint pos = pad->GetPosition(); ROTATE( pos ); pad->SetPosition( pos ); pad->SetPos0( pad->GetPosition() ); pad->SetOrientation( pad->GetOrientation() + 900 ); } for( EDA_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( !item->IsSelected() && !force_all) continue; switch( item->Type() ) { case PCB_MODULE_EDGE_T: { EDGE_MODULE* em = (EDGE_MODULE*) item; wxPoint tmp = em->GetStart(); ROTATE( tmp ); em->SetStart( tmp ); em->SetStart0( tmp ); tmp = em->GetEnd(); ROTATE( tmp ); em->SetEnd( tmp ); em->SetEnd0( tmp ); } break; case PCB_MODULE_TEXT_T: { TEXTE_MODULE* tm = (TEXTE_MODULE*) item; wxPoint pos = tm->GetTextPosition(); ROTATE( pos ); tm->SetTextPosition( pos ); tm->SetPos0( tm->GetTextPosition() ); tm->SetOrientation( tm->GetOrientation() + 900 ); } break; default: ; } item->ClearFlags(); } }