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 );
}
Example #3
0
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 );
}
Example #5
0
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 );
    }
}
Example #9
0
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;
        }
    }
}
Example #10
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();
}
Example #14
0
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;
}
Example #22
0
/* 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 );
        }
    }
}
Example #27
0
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();
    }
}