double LIB_EDIT_FRAME::BestZoom()
{
    /* Please, note: wxMSW before version 2.9 seems have
     * problems with zoom values < 1 ( i.e. userscale > 1) and needs to be patched:
     * edit file <wxWidgets>/src/msw/dc.cpp
     * search for line static const int VIEWPORT_EXTENT = 1000;
     * and replace by static const int VIEWPORT_EXTENT = 10000;
     */
    int      dx, dy;
    wxSize   size;
    EDA_RECT BoundaryBox;

    if( m_component )
    {
        BoundaryBox = m_component->GetBoundingBox( m_unit, m_convert );
        dx = BoundaryBox.GetWidth();
        dy = BoundaryBox.GetHeight();
        SetScrollCenterPosition( wxPoint( 0, 0 ) );
    }
    else
    {
        const PAGE_INFO& pageInfo = GetScreen()->GetPageSettings();

        dx = pageInfo.GetSizeIU().x;
        dy = pageInfo.GetSizeIU().y;

        SetScrollCenterPosition( wxPoint( 0, 0 ) );
    }

    size = m_canvas->GetClientSize();

    // Reserve a 10% margin around component bounding box.
    double margin_scale_factor = 0.8;
    double zx =(double) dx / ( margin_scale_factor * (double)size.x );
    double zy = (double) dy / ( margin_scale_factor * (double)size.y );

    double bestzoom = std::max( zx, zy );

    // keep it >= minimal existing zoom (can happen for very small components
    // for instance when starting a new component
    if( bestzoom  < GetScreen()->m_ZoomList[0] )
        bestzoom  = GetScreen()->m_ZoomList[0];

    return bestzoom;
}
double LIB_VIEW_FRAME::BestZoom()
{
    /* Please, note: wxMSW before version 2.9 seems have
     * problems with zoom values < 1 ( i.e. userscale > 1) and needs to be patched:
     * edit file <wxWidgets>/src/msw/dc.cpp
     * search for line static const int VIEWPORT_EXTENT = 1000;
     * and replace by static const int VIEWPORT_EXTENT = 10000;
     */

    LIB_PART*   part = NULL;
    double      bestzoom = 16.0;      // default value for bestzoom
    PART_LIB*   lib = Prj().SchLibs()->FindLibrary( m_libraryName );

    if( lib  )
        part = lib->FindPart( m_entryName );

    if( !part )
    {
        SetScrollCenterPosition( wxPoint( 0, 0 ) );
        return bestzoom;
    }

    wxSize size = m_canvas->GetClientSize();

    EDA_RECT boundingBox = part->GetBoundingBox( m_unit, m_convert );

    // Reserve a 10% margin around component bounding box.
    double margin_scale_factor = 0.8;
    double zx =(double) boundingBox.GetWidth() /
               ( margin_scale_factor * (double)size.x );
    double zy = (double) boundingBox.GetHeight() /
                ( margin_scale_factor * (double)size.y);

    // Calculates the best zoom
    bestzoom = std::max( zx, zy );

    // keep it >= minimal existing zoom (can happen for very small components
    // like small power symbols
    if( bestzoom  < GetScreen()->m_ZoomList[0] )
        bestzoom  = GetScreen()->m_ZoomList[0];

    SetScrollCenterPosition( boundingBox.Centre() );

    return bestzoom;
}
void EDA_DRAW_PANEL::RefreshDrawingRect( const EDA_RECT& aRect, bool aEraseBackground )
{
    INSTALL_UNBUFFERED_DC( dc, this );

    wxRect rect = aRect;

    rect.x = dc.LogicalToDeviceX( rect.x );
    rect.y = dc.LogicalToDeviceY( rect.y );
    rect.width = dc.LogicalToDeviceXRel( rect.width );
    rect.height = dc.LogicalToDeviceYRel( rect.height );

    wxLogTrace( kicadTraceCoords,
                wxT( "Refresh area: drawing (%d, %d, %d, %d), device (%d, %d, %d, %d)" ),
                aRect.GetX(), aRect.GetY(), aRect.GetWidth(), aRect.GetHeight(),
                rect.x, rect.y, rect.width, rect.height );

    RefreshRect( rect, aEraseBackground );
}
Example #4
0
double PCB_BASE_FRAME::BestZoom()
{
    if( m_Pcb == NULL )
        return 1.0;

    EDA_RECT    ibbbox  = GetBoardBoundingBox();
    DSIZE       clientz = m_canvas->GetClientSize();
    DSIZE       boardz( ibbbox.GetWidth(), ibbbox.GetHeight() );

    double iu_per_du_X = clientz.x ? boardz.x / clientz.x : 1.0;
    double iu_per_du_Y = clientz.y ? boardz.y / clientz.y : 1.0;

    double bestzoom = std::max( iu_per_du_X, iu_per_du_Y );

    GetScreen()->SetScrollCenterPosition( ibbbox.Centre() );

    return bestzoom;
}
double GERBVIEW_FRAME::BestZoom()
{
    GERBER_DRAW_ITEM* item = GetGerberLayout()->m_Drawings;

    // gives a minimal value to zoom, if no item in list
    if( item == NULL  )
        return ZOOM_FACTOR( 350.0 );

    EDA_RECT bbox = GetGerberLayout()->ComputeBoundingBox();

    wxSize  size = m_canvas->GetClientSize();

    double  x   = (double) bbox.GetWidth() / (double) size.x;
    double  y   = (double) bbox.GetHeight() / (double) size.y;
    SetScrollCenterPosition( bbox.Centre() );

    double  best_zoom = std::max( x, y );
    return best_zoom;
}
Example #6
0
double LIB_VIEW_FRAME::BestZoom()
{
    LIB_PART*   part = NULL;
    double      defaultLibraryZoom = 7.33;

    if( m_libraryName.IsEmpty() || m_entryName.IsEmpty() )
    {
        SetScrollCenterPosition( wxPoint( 0, 0 ) );
        return defaultLibraryZoom;
    }

    LIB_ALIAS* alias = nullptr;

    try
    {
        alias = Prj().SchSymbolLibTable()->LoadSymbol( m_libraryName, m_entryName );
    }
    catch( ... )
    {
    }

    if( alias )
        part = alias->GetPart();

    if( !part )
    {
        SetScrollCenterPosition( wxPoint( 0, 0 ) );
        return defaultLibraryZoom;
    }

    EDA_RECT boundingBox = part->GetUnitBoundingBox( m_unit, m_convert );

    double  sizeX  = (double) boundingBox.GetWidth();
    double  sizeY  = (double) boundingBox.GetHeight();
    wxPoint centre = boundingBox.Centre();

    // Reserve a 20% margin around component bounding box.
    double  margin_scale_factor = 1.2;

    return bestZoom( sizeX, sizeY, margin_scale_factor, centre );
}
void WORKSHEET_DATAITEM_TEXT::SetConstrainedTextSize()
{
    m_ConstrainedTextSize = m_TextSize;

    if( m_ConstrainedTextSize.x == 0  )
        m_ConstrainedTextSize.x = m_DefaultTextSize.x;

    if( m_ConstrainedTextSize.y == 0 )
        m_ConstrainedTextSize.y = m_DefaultTextSize.y;

    if( m_BoundingBoxSize.x || m_BoundingBoxSize.y )
    {
        int linewidth = 0;
        // to know the X and Y size of the line, we should use
        // EDA_TEXT::GetTextBox()
        // but this function uses integers
        // So, to avoid truncations with our unit in mm, use microns.
        wxSize size_micron;
        size_micron.x = KiROUND( m_ConstrainedTextSize.x * 1000.0 );
        size_micron.y = KiROUND( m_ConstrainedTextSize.y * 1000.0 );
        WS_DRAW_ITEM_TEXT dummy( WS_DRAW_ITEM_TEXT( this, this->m_FullText,
                                               wxPoint(0,0),
                                               size_micron,
                                               linewidth, BLACK,
                                               IsItalic(), IsBold() ) );
        dummy.SetMultilineAllowed( true );
        TransfertSetupToGraphicText( &dummy );
        EDA_RECT rect = dummy.GetTextBox();
        DSIZE size;
        size.x = rect.GetWidth() / 1000.0;
        size.y = rect.GetHeight() / 1000.0;

        if( m_BoundingBoxSize.x && size.x > m_BoundingBoxSize.x )
            m_ConstrainedTextSize.x *= m_BoundingBoxSize.x / size.x;

        if( m_BoundingBoxSize.y &&  size.y > m_BoundingBoxSize.y )
            m_ConstrainedTextSize.y *= m_BoundingBoxSize.y / size.y;
    }
}
double LIB_EDIT_FRAME::BestZoom()
{
    LIB_PART*  part = GetCurPart();
    double     defaultLibraryZoom = 7.33;

    if( !part )
    {
        SetScrollCenterPosition( wxPoint( 0, 0 ) );
        return defaultLibraryZoom;
    }

    EDA_RECT boundingBox = part->GetUnitBoundingBox( m_unit, m_convert );

    double  sizeX  = (double) boundingBox.GetWidth();
    double  sizeY  = (double) boundingBox.GetHeight();
    wxPoint centre = boundingBox.Centre();

    // Reserve a 20% margin around component bounding box.
    double margin_scale_factor = 1.2;

    return bestZoom( sizeX, sizeY, margin_scale_factor, centre);
}
/*
 * Draw (on m_panelShowPin) the pin currently edited
 * accroding to current settings in dialog
 */
void DIALOG_LIB_EDIT_PIN::OnPaintShowPanel( wxPaintEvent& event )
{
    wxPaintDC    dc( m_panelShowPin );
    wxSize dc_size = dc.GetSize();
    dc.SetDeviceOrigin( dc_size.x / 2, dc_size.y / 2 );

    // Give a parent to m_dummyPin only from draw purpose.
    // In fact m_dummyPin should not have a parent, but draw functions need a parent
    // to know some options, about pin texts
    LIB_EDIT_FRAME* libframe = (LIB_EDIT_FRAME*) GetParent();
    m_dummyPin->SetParent( libframe->GetCurPart() );

    // Calculate a suitable scale to fit the available draw area
    EDA_RECT bBox = m_dummyPin->GetBoundingBox();
    double xscale    = (double) dc_size.x / bBox.GetWidth();
    double yscale = (double) dc_size.y / bBox.GetHeight();
    double scale = std::min( xscale, yscale );

    // Give a 10% margin
    scale *= 0.9;
    dc.SetUserScale( scale, scale );

    wxPoint offset = -bBox.Centre();

    GRResetPenAndBrush( &dc );

    // This is a flag for m_dummyPin->Draw
    uintptr_t flags = uintptr_t( PIN_DRAW_TEXTS | PIN_DRAW_DANGLING );

    m_dummyPin->Draw( NULL, &dc, offset, COLOR4D::UNSPECIFIED, GR_COPY,
                      (void*)flags, DefaultTransform );

    m_dummyPin->SetParent(NULL);

    event.Skip();
}
Example #10
0
EDA_RECT PCB_BASE_FRAME::GetBoardBoundingBox( bool aBoardEdgesOnly ) const
{
    wxASSERT( m_Pcb );

    EDA_RECT area = m_Pcb->ComputeBoundingBox( aBoardEdgesOnly );

    if( area.GetWidth() == 0 && area.GetHeight() == 0 )
    {
        wxSize pageSize = GetPageSizeIU();

        if( m_showBorderAndTitleBlock )
        {
            area.SetOrigin( 0, 0 );
            area.SetEnd( pageSize.x, pageSize.y );
        }
        else
        {
            area.SetOrigin( -pageSize.x / 2, -pageSize.y / 2 );
            area.SetEnd( pageSize.x / 2, pageSize.y / 2 );
        }
    }

    return area;
}
int genPlacementRoutingMatrix( BOARD* aBrd, EDA_MSG_PANEL* messagePanel )
{
    wxString msg;

    RoutingMatrix.UnInitRoutingMatrix();

    EDA_RECT bbox = aBrd->ComputeBoundingBox( true );

    if( bbox.GetWidth() == 0 || bbox.GetHeight() == 0 )
    {
        DisplayError( NULL, _( "No PCB edge found, unknown board size!" ) );
        return 0;
    }

    RoutingMatrix.ComputeMatrixSize( aBrd, true );
    int nbCells = RoutingMatrix.m_Ncols * RoutingMatrix.m_Nrows;

    messagePanel->EraseMsgBox();
    msg.Printf( wxT( "%d" ), RoutingMatrix.m_Ncols );
    messagePanel->SetMessage( 1, _( "Cols" ), msg, GREEN );
    msg.Printf( wxT( "%d" ), RoutingMatrix.m_Nrows );
    messagePanel->SetMessage( 7, _( "Lines" ), msg, GREEN );
    msg.Printf( wxT( "%d" ), nbCells );
    messagePanel->SetMessage( 14, _( "Cells." ), msg, YELLOW );

    // Choose the number of board sides.
    RoutingMatrix.m_RoutingLayersCount = 2;

    RoutingMatrix.InitRoutingMatrix();

    // Display memory usage.
    msg.Printf( wxT( "%d" ), RoutingMatrix.m_MemSize / 1024 );
    messagePanel->SetMessage( 24, wxT( "Mem(Kb)" ), msg, CYAN );

    g_Route_Layer_BOTTOM = F_Cu;

    if( RoutingMatrix.m_RoutingLayersCount > 1 )
        g_Route_Layer_BOTTOM = B_Cu;

    g_Route_Layer_TOP = F_Cu;

    // Place the edge layer segments
    TRACK TmpSegm( NULL );

    TmpSegm.SetLayer( UNDEFINED_LAYER );
    TmpSegm.SetNetCode( -1 );
    TmpSegm.SetWidth( RoutingMatrix.m_GridRouting / 2 );

    EDA_ITEM* PtStruct = aBrd->m_Drawings;

    for( ; PtStruct != NULL; PtStruct = PtStruct->Next() )
    {
        DRAWSEGMENT* DrawSegm;

        switch( PtStruct->Type() )
        {
        case PCB_LINE_T:
            DrawSegm = (DRAWSEGMENT*) PtStruct;

            if( DrawSegm->GetLayer() != Edge_Cuts )
                break;

            TraceSegmentPcb( DrawSegm, HOLE | CELL_is_EDGE,
                             RoutingMatrix.m_GridRouting, WRITE_CELL );
            break;

        case PCB_TEXT_T:
        default:
            break;
        }
    }

    // Mark cells of the routing matrix to CELL_is_ZONE
    // (i.e. availlable cell to place a module )
    // Init a starting point of attachment to the area.
    RoutingMatrix.OrCell( RoutingMatrix.m_Nrows / 2, RoutingMatrix.m_Ncols / 2,
                          BOTTOM, CELL_is_ZONE );

    // find and mark all other availlable cells:
    for( int ii = 1; ii != 0; )
        ii = propagate();

    // Initialize top layer. to the same value as the bottom layer
    if( RoutingMatrix.m_BoardSide[TOP] )
        memcpy( RoutingMatrix.m_BoardSide[TOP], RoutingMatrix.m_BoardSide[BOTTOM],
                nbCells * sizeof(MATRIX_CELL) );

    return 1;
}
Example #12
0
void BOARD_PRINTOUT_CONTROLLER::DrawPage()
{
    wxPoint       offset;
    double        userscale;
    EDA_RECT      boardBoundingBox;
    EDA_RECT      drawRect;
    wxDC*         dc = GetDC();
    BASE_SCREEN*  screen = m_Parent->GetScreen();
    bool          printMirror = m_PrintParams.m_PrintMirror;
    wxSize        pageSizeIU = m_Parent->GetPageSizeIU();

    wxBusyCursor  dummy;

#if defined (PCBNEW)
    BOARD * brd = ((PCB_BASE_FRAME*) m_Parent)->GetBoard();
    boardBoundingBox = brd->ComputeBoundingBox();
    wxString titleblockFilename = brd->GetFileName();
#elif defined (GERBVIEW)
    boardBoundingBox = ((GERBVIEW_FRAME*) m_Parent)->GetGerberLayoutBoundingBox();
    wxString titleblockFilename;    // TODO see if we uses the gerber file name
#else
    #error BOARD_PRINTOUT_CONTROLLER::DrawPage() works only for PCBNEW or GERBVIEW
#endif

    // Use the page size as the drawing area when the board is shown or the user scale
    // is less than 1.
    if( m_PrintParams.PrintBorderAndTitleBlock() )
        boardBoundingBox = EDA_RECT( wxPoint( 0, 0 ), pageSizeIU );

    wxLogTrace( tracePrinting, wxT( "Drawing bounding box:                 x=%d, y=%d, w=%d, h=%d" ),
                boardBoundingBox.GetX(), boardBoundingBox.GetY(),
                boardBoundingBox.GetWidth(), boardBoundingBox.GetHeight() );

    // Compute the PCB size in internal units
    userscale = m_PrintParams.m_PrintScale;

    if( m_PrintParams.m_PrintScale == 0 )   //  fit in page option
    {
        if(boardBoundingBox.GetWidth() && boardBoundingBox.GetHeight())
        {
            int margin = Millimeter2iu( 10.0 ); // add a margin around the drawings
            double scaleX = (double)(pageSizeIU.x - (2 * margin)) /
                            boardBoundingBox.GetWidth();
            double scaleY = (double)(pageSizeIU.y - (2 * margin)) /
                            boardBoundingBox.GetHeight();
            userscale = (scaleX < scaleY) ? scaleX : scaleY;
        }
        else
            userscale = 1.0;
    }

    wxSize scaledPageSize = pageSizeIU;
    drawRect.SetSize( scaledPageSize );
    scaledPageSize.x = wxRound( scaledPageSize.x / userscale );
    scaledPageSize.y = wxRound( scaledPageSize.y / userscale );


    if( m_PrintParams.m_PageSetupData )
    {
        wxLogTrace( tracePrinting, wxT( "Fit size to page margins:         x=%d, y=%d" ),
                    scaledPageSize.x, scaledPageSize.y );

        // Always scale to the size of the paper.
        FitThisSizeToPageMargins( scaledPageSize, *m_PrintParams.m_PageSetupData );
    }

    // Compute Accurate scale 1
    if( m_PrintParams.m_PrintScale == 1.0 )
    {
        // We want a 1:1 scale, regardless the page setup
        // like page size, margin ...
        MapScreenSizeToPaper(); // set best scale and offset (scale is not used)
        int w, h;
        GetPPIPrinter( &w, &h );
        double accurate_Xscale = (double) w / (IU_PER_MILS*1000);
        double accurate_Yscale = (double) h / (IU_PER_MILS*1000);

        if( IsPreview() )  // Scale must take in account the DC size in Preview
        {
            // Get the size of the DC in pixels
            wxSize       PlotAreaSize;
            dc->GetSize( &PlotAreaSize.x, &PlotAreaSize.y );
            GetPageSizePixels( &w, &h );
            accurate_Xscale *= (double)PlotAreaSize.x / w;
            accurate_Yscale *= (double)PlotAreaSize.y / h;
        }
        // Fine scale adjust
        accurate_Xscale *= m_PrintParams.m_XScaleAdjust;
        accurate_Yscale *= m_PrintParams.m_YScaleAdjust;

        // Set print scale for 1:1 exact scale
        dc->SetUserScale( accurate_Xscale, accurate_Yscale );
    }

    // Get the final size of the DC in pixels
    wxSize       PlotAreaSizeInPixels;
    dc->GetSize( &PlotAreaSizeInPixels.x, &PlotAreaSizeInPixels.y );
    wxLogTrace( tracePrinting, wxT( "Plot area in pixels:              x=%d, y=%d" ),
                PlotAreaSizeInPixels.x, PlotAreaSizeInPixels.y );
    double scalex, scaley;
    dc->GetUserScale( &scalex, &scaley );
    wxLogTrace( tracePrinting, wxT( "DC user scale:                    x=%g, y=%g" ),
                scalex, scaley );

    wxSize PlotAreaSizeInUserUnits;
    PlotAreaSizeInUserUnits.x = KiROUND( PlotAreaSizeInPixels.x / scalex );
    PlotAreaSizeInUserUnits.y = KiROUND( PlotAreaSizeInPixels.y / scaley );
    wxLogTrace( tracePrinting, wxT( "Scaled plot area in user units:   x=%d, y=%d" ),
                PlotAreaSizeInUserUnits.x, PlotAreaSizeInUserUnits.y );

    // In module editor, the module is located at 0,0 but for printing
    // it is moved to pageSizeIU.x/2, pageSizeIU.y/2.
    // So the equivalent board must be moved to the center of the page:
    if( m_Parent->IsType( MODULE_EDITOR_FRAME_TYPE ) )
    {
        boardBoundingBox.Move( wxPoint( pageSizeIU.x/2, pageSizeIU.y/2 ) );
    }

    // In some cases the plot origin is the centre of the board outline rather than the center
    // of the selected paper size.
    if( m_PrintParams.CenterOnBoardOutline() )
    {
        // Here we are only drawing the board and it's contents.
        drawRect = boardBoundingBox;
        offset.x += wxRound( (double) -scaledPageSize.x / 2.0 );
        offset.y += wxRound( (double) -scaledPageSize.y / 2.0 );

        wxPoint center = boardBoundingBox.Centre();

        if( printMirror )
        {
            // Calculate the mirrored center of the board.
            center.x = m_Parent->GetPageSizeIU().x - boardBoundingBox.Centre().x;
        }

        offset += center;
    }

    GRResetPenAndBrush( dc );

    EDA_DRAW_PANEL* panel = m_Parent->GetCanvas();
    EDA_RECT        tmp   = *panel->GetClipBox();

    // Set clip box to the max size
    #define MAX_VALUE (INT_MAX/2)   // MAX_VALUE is the max we can use in an integer
                                    // and that allows calculations without overflow
    panel->SetClipBox( EDA_RECT( wxPoint( 0, 0 ), wxSize( MAX_VALUE, MAX_VALUE ) ) );

    screen->m_IsPrinting = true;
    EDA_COLOR_T bg_color = g_DrawBgColor;

    // Print frame reference, if reqquested, before
    if( m_PrintParams.m_Print_Black_and_White )
        GRForceBlackPen( true );

    if( m_PrintParams.PrintBorderAndTitleBlock() )
        m_Parent->DrawWorkSheet( dc, screen, m_PrintParams.m_PenDefaultSize,
                                  IU_PER_MILS, titleblockFilename );

    if( printMirror )
    {
        // To plot mirror, we reverse the x axis, and modify the plot x origin
        dc->SetAxisOrientation( false, false);

        /* Plot offset x is moved by the x plot area size in order to have
         * the old draw area in the new draw area, because the draw origin has not moved
         * (this is the upper left corner) but the X axis is reversed, therefore the plotting area
         * is the x coordinate values from  - PlotAreaSize.x to 0 */
        int x_dc_offset = PlotAreaSizeInPixels.x;
        x_dc_offset = KiROUND( x_dc_offset  * userscale );
        dc->SetDeviceOrigin( x_dc_offset, 0 );

        wxLogTrace( tracePrinting, wxT( "Device origin:                    x=%d, y=%d" ),
                    x_dc_offset, 0 );

        panel->SetClipBox( EDA_RECT( wxPoint( -MAX_VALUE/2, -MAX_VALUE/2 ),
                                     panel->GetClipBox()->GetSize() ) );
    }

    // screen->m_DrawOrg = offset;
    dc->SetLogicalOrigin( offset.x, offset.y );

    wxLogTrace( tracePrinting, wxT( "Logical origin:                   x=%d, y=%d" ),
                offset.x, offset.y );

#if defined(wxUSE_LOG_TRACE) && defined( DEBUG )
    wxRect paperRect = GetPaperRectPixels();
    wxLogTrace( tracePrinting, wxT( "Paper rectangle:                  left=%d, top=%d, "
                                    "right=%d, bottom=%d" ),
                paperRect.GetLeft(), paperRect.GetTop(), paperRect.GetRight(),
                paperRect.GetBottom() );

    int devLeft = dc->LogicalToDeviceX( drawRect.GetX() );
    int devTop = dc->LogicalToDeviceY( drawRect.GetY() );
    int devRight = dc->LogicalToDeviceX( drawRect.GetRight() );
    int devBottom = dc->LogicalToDeviceY( drawRect.GetBottom() );
    wxLogTrace( tracePrinting, wxT( "Final device rectangle:           left=%d, top=%d, "
                                    "right=%d, bottom=%d\n" ),
                devLeft, devTop, devRight, devBottom );
#endif

    g_DrawBgColor = WHITE;

    /* when printing in color mode, we use the graphic OR mode that gives the same look as
     * the screen but because the background is white when printing, we must use a trick:
     * In order to plot on a white background in OR mode we must:
     * 1 - Plot all items in black, this creates a local black background
     * 2 - Plot in OR mode on black "local" background
     */
    if( !m_PrintParams.m_Print_Black_and_White )
    {
        // Creates a "local" black background
        GRForceBlackPen( true );
        m_Parent->PrintPage( dc, m_PrintParams.m_PrintMaskLayer,
                             printMirror, &m_PrintParams );
        GRForceBlackPen( false );
    }
    else
        GRForceBlackPen( true );


#if defined (GERBVIEW)
    // In B&W mode, do not force black pen for Gerbview
    // because negative objects need a white pen, not a black pen
    // B&W mode is handled in print page
    GRForceBlackPen( false );
#endif
    m_Parent->PrintPage( dc, m_PrintParams.m_PrintMaskLayer, printMirror,
                         &m_PrintParams );

    g_DrawBgColor = bg_color;
    screen->m_IsPrinting = false;
    panel->SetClipBox( tmp );
    GRForceBlackPen( false );
}
Example #13
0
/*
 * This is the real print function: print the active screen
 */
void BOARD_PRINTOUT_CONTROLER::DrawPage()
{
    int          tmpzoom;
    wxPoint      tmp_startvisu;
    wxPoint      old_org;
    wxPoint      DrawOffset;    // Offset de trace
    double       userscale;
    double       DrawZoom = 1;
    wxDC*        dc = GetDC();
    PCB_SCREEN*  screen = (PCB_SCREEN*) m_Parent->GetScreen();
    bool         printMirror = m_PrintParams.m_PrintMirror;

    wxBusyCursor dummy;

    // Save old draw scale and draw offset
    tmp_startvisu = screen->m_StartVisu;
    tmpzoom = screen->GetZoom();
    old_org = screen->m_DrawOrg;

    // Change draw scale and offset to draw the whole page
    screen->SetScalingFactor( DrawZoom );
    screen->m_DrawOrg.x   = screen->m_DrawOrg.y = 0;
    screen->m_StartVisu.x = screen->m_StartVisu.y = 0;

    PCB_BASE_FRAME* pcbframe = (PCB_BASE_FRAME*) m_Parent;

    wxSize  pageSizeIU = pcbframe->GetPageSizeIU();  // internal units
    EDA_RECT bbbox     = pcbframe->GetBoard()->ComputeBoundingBox();

    // In module editor, the module is located at 0,0 but for printing
    // it is moved to pageSizeIU.x/2, pageSizeIU.y/2.
    // So the equivalent board must be moved:
    if( m_Parent->IsType( MODULE_EDITOR_FRAME ) )
    {
        bbbox.Move( wxPoint( pageSizeIU.x/2, pageSizeIU.y/2 ) );
    }

    // Compute the PCB size in internal units
    userscale = m_PrintParams.m_PrintScale;

    if( userscale == 0 )                //  fit in page
    {
        // Margin = 0.4 inch
#if defined(KICAD_NANOMETRE)
        int extra_margin = int( 0.4 * 25400 );      // nanometers
#else
        int extra_margin = int( 0.4 * 1000 );       // deci-mils
#endif

        pageSizeIU.x = bbbox.GetWidth()  + extra_margin * 2;
        pageSizeIU.y = bbbox.GetHeight() + extra_margin * 2;

        userscale   = 0.99;
    }

    if( (m_PrintParams.m_PrintScale > 1.0)          //  scale > 1 -> Recadrage
       || (m_PrintParams.m_PrintScale == 0) )       //  fit in page
    {
        DrawOffset += bbbox.Centre();
    }

    if( m_PrintParams.m_PageSetupData )
    {
        wxSize pagesize;

        pagesize.x = int( pageSizeIU.x / userscale );
        pagesize.y = int( pageSizeIU.y / userscale );

        FitThisSizeToPageMargins( pagesize, *m_PrintParams.m_PageSetupData );
    }

    // Compute Accurate scale 1
    if( userscale == 1.0 )
    {
        // We want a 1:1 scale and margins for printing
        MapScreenSizeToPaper();
        int w, h;
        GetPPIPrinter( &w, &h );
        double accurate_Xscale = ( (double) ( DrawZoom * w ) ) / (double) PCB_INTERNAL_UNIT;
        double accurate_Yscale = ( (double) ( DrawZoom * h ) ) / (double) PCB_INTERNAL_UNIT;

        if( IsPreview() )  // Scale must take in account the DC size in Preview
        {
            // Get the size of the DC in pixels
            wxSize       PlotAreaSize;
            dc->GetSize( &PlotAreaSize.x, &PlotAreaSize.y );
            GetPageSizePixels( &w, &h );
            accurate_Xscale *= PlotAreaSize.x;
            accurate_Xscale /= (double) w;
            accurate_Yscale *= PlotAreaSize.y;
            accurate_Yscale /= (double) h;
        }
        accurate_Xscale *= m_PrintParams.m_XScaleAdjust;
        accurate_Yscale *= m_PrintParams.m_YScaleAdjust;
        // Fine scale adjust
        dc->SetUserScale( accurate_Xscale, accurate_Yscale );
    }

    // Get the final size of the DC in pixels
    wxSize       PlotAreaSizeInPixels;
    dc->GetSize( &PlotAreaSizeInPixels.x, &PlotAreaSizeInPixels.y );
    double scalex, scaley;
    dc->GetUserScale(&scalex, &scaley);
    wxSize PlotAreaSizeInUserUnits;
    PlotAreaSizeInUserUnits.x = (int) (PlotAreaSizeInPixels.x/scalex);
    PlotAreaSizeInUserUnits.y = (int) (PlotAreaSizeInPixels.y/scaley);

    /* In some cases the plot origin is the centre of the page
     *  when:
     *  - Asked
     *  - scale > 1
     *  - fit in page
     */
    if( m_PrintParams.m_ForceCentered
       || (m_PrintParams.m_PrintScale > 1.0)        //  scale > 1
       || (m_PrintParams.m_PrintScale == 0) )       //  fit in page
    {
        DrawOffset.x -= PlotAreaSizeInUserUnits.x / 2;
        DrawOffset.y -= PlotAreaSizeInUserUnits.y / 2;
    }

    screen->m_DrawOrg = DrawOffset;

    GRResetPenAndBrush( dc );

    if( m_PrintParams.m_Print_Black_and_White )
        GRForceBlackPen( true );

    EDA_DRAW_PANEL* panel = m_Parent->GetCanvas();
    EDA_RECT        tmp   = *panel->GetClipBox();

    // Set clip box to the max size
    #define MAX_VALUE (INT_MAX/2)   // MAX_VALUE is the max we can use in an integer
                                    // and that allows calculations without overflow
    panel->SetClipBox( EDA_RECT( wxPoint( 0, 0 ), wxSize( MAX_VALUE, MAX_VALUE ) ) );

    m_Parent->GetScreen()->m_IsPrinting = true;
    int bg_color = g_DrawBgColor;

    if( m_PrintParams.m_Print_Sheet_Ref )
        m_Parent->TraceWorkSheet( dc, screen, m_PrintParams.m_PenDefaultSize );

    if( printMirror )
    {
        // To plot mirror, we reverse the y axis, and modify the plot y origin
        dc->SetAxisOrientation( true, true );

        if( userscale < 1.0 )
            scaley /= userscale;

        /* Plot offset y is moved by the y plot area size in order to have
         * the old draw area in the new draw area, because the draw origin has not moved
         * (this is the upper left corner) but the Y axis is reversed, therefore the plotting area
         * is the y coordinate values from  - PlotAreaSize.y to 0 */
        int y_dc_offset = PlotAreaSizeInPixels.y;
        y_dc_offset = (int) ( ( double ) y_dc_offset * userscale );
        dc->SetDeviceOrigin( 0, y_dc_offset );
        int ysize = (int) ( PlotAreaSizeInPixels.y / scaley );
        DrawOffset.y += ysize;

        /* in order to keep the board position in the sheet
         * (when user scale <= 1) the y offset in moved by the distance between
         * the middle of the page and the middle of the board
         * This is equivalent to put the mirror axis to the board centre
         * for scales > 1, the DrawOffset was already computed to have the board centre
         * to the middle of the page.
         */
        wxPoint pcb_centre = bbbox.Centre();

        if( userscale <= 1.0 )
            DrawOffset.y += pcb_centre.y - (ysize / 2);

        dc->SetLogicalOrigin( screen->m_DrawOrg.x, screen->m_DrawOrg.y );
        panel->SetClipBox( EDA_RECT( wxPoint( -MAX_VALUE/2, -MAX_VALUE/2 ),
                                     panel->GetClipBox()->GetSize() ) );
    }

    g_DrawBgColor = WHITE;

    /* when printing in color mode, we use the graphic OR mode that gives the same look as the screen
     * But because the background is white when printing, we must use a trick:
     * In order to plot on a white background in OR mode we must:
     * 1 - Plot all items in black, this creates a local black backgroud
     * 2 - Plot in OR mode on black "local" background
     */
    if( !m_PrintParams.m_Print_Black_and_White )
    {   // Creates a "local" black background
        GRForceBlackPen( true );
        m_Parent->PrintPage( dc, m_PrintParams.m_PrintMaskLayer, printMirror, &m_PrintParams );
        GRForceBlackPen( false );
    }

    m_Parent->PrintPage( dc, m_PrintParams.m_PrintMaskLayer, printMirror, &m_PrintParams );

    g_DrawBgColor = bg_color;
    m_Parent->GetScreen()->m_IsPrinting = false;
    panel->SetClipBox( tmp );

    GRForceBlackPen( false );

    screen->m_StartVisu = tmp_startvisu;
    screen->m_DrawOrg   = old_org;
    screen->SetZoom( tmpzoom );
}
Example #14
0
bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
{
    EDA_RECT arect = aRect;
    arect.Normalize();
    arect.Inflate( aAccuracy );

    wxPoint shapePos = ShapePos();

    EDA_RECT shapeRect;

    int r;

    EDA_RECT bb = GetBoundingBox();

    wxPoint endCenter;
    int radius;

    if( !arect.Intersects( bb ) )
        return false;

    // This covers total containment for all test cases
    if( arect.Contains( bb ) )
        return true;

    switch( GetShape() )
    {
    case PAD_SHAPE_CIRCLE:
        return arect.IntersectsCircle( GetPosition(), GetBoundingRadius() );
    case PAD_SHAPE_RECT:
        shapeRect.SetOrigin( shapePos );
        shapeRect.Inflate( m_Size.x / 2, m_Size.y / 2 );
        return arect.Intersects( shapeRect, m_Orient );
    case PAD_SHAPE_OVAL:

        // Circlular test if dimensions are equal
        if( m_Size.x == m_Size.y )
            return arect.IntersectsCircle( shapePos, GetBoundingRadius() );

        shapeRect.SetOrigin( shapePos );

        // Horizontal dimension is greater
        if( m_Size.x > m_Size.y )
        {
            radius = m_Size.y / 2;

            shapeRect.Inflate( m_Size.x / 2 - radius, radius );

            endCenter = wxPoint( m_Size.x / 2 - radius, 0 );
            RotatePoint( &endCenter, m_Orient );

            // Test circular ends
            if( arect.IntersectsCircle( shapePos + endCenter, radius ) ||
                arect.IntersectsCircle( shapePos - endCenter, radius ) )
            {
                return true;
            }
        }
        else
        {
            radius = m_Size.x / 2;

            shapeRect.Inflate( radius, m_Size.y / 2 - radius );

            endCenter = wxPoint( 0, m_Size.y / 2 - radius );
            RotatePoint( &endCenter, m_Orient );

            // Test circular ends
            if( arect.IntersectsCircle( shapePos + endCenter, radius ) ||
                arect.IntersectsCircle( shapePos - endCenter, radius ) )
            {
                return true;
            }
        }

        // Test rectangular portion between rounded ends
        if( arect.Intersects( shapeRect, m_Orient ) )
        {
            return true;
        }

        break;
    case PAD_SHAPE_TRAPEZOID:
        /* Trapezoid intersection tests:
         * A) Any points of rect inside trapezoid
         * B) Any points of trapezoid inside rect
         * C) Any sides of trapezoid cross rect
         */
        {

        wxPoint poly[4];
        BuildPadPolygon( poly, wxSize( 0, 0 ), 0 );

        wxPoint corners[4];

        corners[0] = wxPoint( arect.GetLeft(),  arect.GetTop() );
        corners[1] = wxPoint( arect.GetRight(), arect.GetTop() );
        corners[2] = wxPoint( arect.GetRight(), arect.GetBottom() );
        corners[3] = wxPoint( arect.GetLeft(),  arect.GetBottom() );

        for( int i=0; i<4; i++ )
        {
            RotatePoint( &poly[i], m_Orient );
            poly[i] += shapePos;
        }

        for( int ii=0; ii<4; ii++ )
        {
            if( TestPointInsidePolygon( poly, 4, corners[ii] ) )
            {
                return true;
            }

            if( arect.Contains( poly[ii] ) )
            {
                return true;
            }

            if( arect.Intersects( poly[ii], poly[(ii+1) % 4] ) )
            {
                return true;
            }
        }

        return false;

        }
    case PAD_SHAPE_ROUNDRECT:
        /* RoundRect intersection can be broken up into simple tests:
         * a) Test intersection of horizontal rect
         * b) Test intersection of vertical rect
         * c) Test intersection of each corner
         */


        r = GetRoundRectCornerRadius();

        /* Test A - intersection of horizontal rect */
        shapeRect.SetSize( 0, 0 );
        shapeRect.SetOrigin( shapePos );
        shapeRect.Inflate( m_Size.x / 2, m_Size.y / 2 - r );

        // Short-circuit test for zero width or height
        if( shapeRect.GetWidth() > 0 && shapeRect.GetHeight() > 0 &&
            arect.Intersects( shapeRect, m_Orient ) )
        {
            return true;
        }

        /* Test B - intersection of vertical rect */
        shapeRect.SetSize( 0, 0 );
        shapeRect.SetOrigin( shapePos );
        shapeRect.Inflate( m_Size.x / 2 - r, m_Size.y / 2 );

        // Short-circuit test for zero width or height
        if( shapeRect.GetWidth() > 0 && shapeRect.GetHeight() > 0 &&
            arect.Intersects( shapeRect, m_Orient ) )
        {
            return true;
        }

        /* Test C - intersection of each corner */

        endCenter = wxPoint( m_Size.x / 2 - r, m_Size.y / 2 - r );
        RotatePoint( &endCenter, m_Orient );

        if( arect.IntersectsCircle( shapePos + endCenter, r ) ||
            arect.IntersectsCircle( shapePos - endCenter, r ) )
        {
            return true;
        }

        endCenter = wxPoint( m_Size.x / 2 - r, -m_Size.y / 2 + r );
        RotatePoint( &endCenter, m_Orient );

        if( arect.IntersectsCircle( shapePos + endCenter, r ) ||
            arect.IntersectsCircle( shapePos - endCenter, r ) )
        {
            return true;
        }

        break;
    default:
        break;
    }

    return false;
}
Example #15
0
void CINFO3D_VISU::InitSettings( REPORTER *aStatusTextReporter )
{
    wxLogTrace( m_logTrace, wxT( "CINFO3D_VISU::InitSettings" ) );

    // Calculates the board bounding box
    // First, use only the board outlines
    EDA_RECT bbbox = m_board->ComputeBoundingBox( true );

    // If no outlines, use the board with items
    if( ( bbbox.GetWidth() == 0 ) && ( bbbox.GetHeight() == 0 ) )
        bbbox = m_board->ComputeBoundingBox( false );

    // Gives a non null size to avoid issues in zoom / scale calculations
    if( ( bbbox.GetWidth() == 0 ) && ( bbbox.GetHeight() == 0 ) )
        bbbox.Inflate( Millimeter2iu( 10 ) );

    m_boardSize = bbbox.GetSize();
    m_boardPos  = bbbox.Centre();

    wxASSERT( (m_boardSize.x > 0) && (m_boardSize.y > 0) );

    m_boardPos.y = -m_boardPos.y; // The y coord is inverted in 3D viewer

    m_copperLayersCount = m_board->GetCopperLayerCount();

    // Ensure the board has 2 sides for 3D views, because it is hard to find
    // a *really* single side board in the true life...
    if( m_copperLayersCount < 2 )
        m_copperLayersCount = 2;

    // Calculate the convertion to apply to all positions.
    m_biuTo3Dunits = RANGE_SCALE_3D / std::max( m_boardSize.x, m_boardSize.y );

    // Calculate factors for cicle segment approximation
    m_calc_seg_min_factor3DU = (float)( SEG_MIN_FACTOR_BIU * m_biuTo3Dunits );
    m_calc_seg_max_factor3DU = (float)( SEG_MAX_FACTOR_BIU * m_biuTo3Dunits );

    m_epoxyThickness3DU = m_board->GetDesignSettings().GetBoardThickness() *
                          m_biuTo3Dunits;

    // !TODO: use value defined by user (currently use default values by ctor
    m_copperThickness3DU         = COPPER_THICKNESS     * m_biuTo3Dunits;
    m_nonCopperLayerThickness3DU = TECH_LAYER_THICKNESS * m_biuTo3Dunits;

    // Init  Z position of each layer
    // calculate z position for each copper layer
    // Zstart = -m_epoxyThickness / 2.0 is the z position of the back (bottom layer) (layer id = 31)
    // Zstart = +m_epoxyThickness / 2.0 is the z position of the front (top layer) (layer id = 0)
    // all unused copper layer z position are set to 0

    //  ____==__________==________==______ <- Bottom = +m_epoxyThickness / 2.0,
    // |                                  |   Top = Bottom + m_copperThickness
    // |__________________________________|
    //   ==         ==         ==     ==   <- Bottom = -m_epoxyThickness / 2.0,
    //                                        Top = Bottom - m_copperThickness

    unsigned int layer;

    for( layer = 0; layer < m_copperLayersCount; ++layer )
    {
        m_layerZcoordBottom[layer] = m_epoxyThickness3DU / 2.0f -
                                     (m_epoxyThickness3DU * layer / (m_copperLayersCount - 1) );

        if( layer < (m_copperLayersCount / 2) )
            m_layerZcoordTop[layer] = m_layerZcoordBottom[layer] + m_copperThickness3DU;
        else
            m_layerZcoordTop[layer] = m_layerZcoordBottom[layer] - m_copperThickness3DU;
    }

    #define layerThicknessMargin 1.1
    const float zpos_offset = m_nonCopperLayerThickness3DU * layerThicknessMargin;

    // Fill remaining unused copper layers and back layer zpos
    // with -m_epoxyThickness / 2.0
    for( ; layer < MAX_CU_LAYERS; layer++ )
    {
        m_layerZcoordBottom[layer] = -(m_epoxyThickness3DU / 2.0f);
        m_layerZcoordTop[layer]    = -(m_epoxyThickness3DU / 2.0f) - m_copperThickness3DU;
    }

    // This is the top of the copper layer thickness.
    const float zpos_copperTop_back  = m_layerZcoordTop[B_Cu];
    const float zpos_copperTop_front = m_layerZcoordTop[F_Cu];

    // calculate z position for each non copper layer
    // Solder mask and Solder paste have the same Z position
    for( int layer_id = MAX_CU_LAYERS; layer_id < LAYER_ID_COUNT; ++layer_id )
    {
        float zposTop;
        float zposBottom;

        switch( layer_id )
        {
        case B_Adhes:
            zposBottom = zpos_copperTop_back - 2.0f * zpos_offset;
            zposTop    = zposBottom - m_nonCopperLayerThickness3DU;
            break;

        case F_Adhes:
            zposBottom = zpos_copperTop_front + 2.0f * zpos_offset;
            zposTop    = zposBottom + m_nonCopperLayerThickness3DU;
            break;

        case B_Mask:
        case B_Paste:
            zposBottom = zpos_copperTop_back;
            zposTop    = zpos_copperTop_back - m_nonCopperLayerThickness3DU;
            break;

        case F_Mask:
        case F_Paste:
            zposTop    = zpos_copperTop_front + m_nonCopperLayerThickness3DU;
            zposBottom = zpos_copperTop_front;
            break;

        case B_SilkS:
            zposBottom = zpos_copperTop_back - 1.0f * zpos_offset;
            zposTop    = zposBottom - m_nonCopperLayerThickness3DU;
            break;

        case F_SilkS:
            zposBottom = zpos_copperTop_front + 1.0f * zpos_offset;
            zposTop    = zposBottom + m_nonCopperLayerThickness3DU;
            break;

        // !TODO: review
        default:
            zposTop = zpos_copperTop_front + (layer_id - MAX_CU_LAYERS + 3.0f) * zpos_offset;
            zposBottom = zposTop - m_nonCopperLayerThickness3DU;
            break;
        }

        m_layerZcoordTop[layer_id]    = zposTop;
        m_layerZcoordBottom[layer_id] = zposBottom;
    }

    m_boardCenter = SFVEC3F( m_boardPos.x * m_biuTo3Dunits,
                             m_boardPos.y * m_biuTo3Dunits,
                             0.0f );

    SFVEC3F boardSize = SFVEC3F( m_boardSize.x * m_biuTo3Dunits,
                                 m_boardSize.y * m_biuTo3Dunits,
                                 0.0f );
            boardSize /= 2.0f;

    SFVEC3F boardMin = (m_boardCenter - boardSize);
    SFVEC3F boardMax = (m_boardCenter + boardSize);

    boardMin.z = m_layerZcoordTop[B_Adhes];
    boardMax.z = m_layerZcoordTop[F_Adhes];

    m_boardBoudingBox = CBBOX( boardMin, boardMax );

#ifdef PRINT_STATISTICS_3D_VIEWER
    unsigned stats_startCreateBoardPolyTime = GetRunningMicroSecs();
#endif

    if( aStatusTextReporter )
        aStatusTextReporter->Report( _( "Build board body" ) );

    createBoardPolygon();

#ifdef PRINT_STATISTICS_3D_VIEWER
    unsigned stats_stopCreateBoardPolyTime = GetRunningMicroSecs();
    unsigned stats_startCreateLayersTime = stats_stopCreateBoardPolyTime;
#endif

    if( aStatusTextReporter )
        aStatusTextReporter->Report( _( "Create layers" ) );

    createLayers( aStatusTextReporter );

#ifdef PRINT_STATISTICS_3D_VIEWER
    unsigned stats_stopCreateLayersTime = GetRunningMicroSecs();

    printf( "CINFO3D_VISU::InitSettings times\n" );
    printf( "  CreateBoardPoly:          %.3f ms\n",
            (float)( stats_stopCreateBoardPolyTime  - stats_startCreateBoardPolyTime  ) / 1e3 );
    printf( "  CreateLayers and holes:   %.3f ms\n",
            (float)( stats_stopCreateLayersTime     - stats_startCreateLayersTime     ) / 1e3 );
    printf( "\n" );
#endif
}
Example #16
0
bool GENDRILL_WRITER_BASE::genDrillMapFile( const wxString& aFullFileName,
                                            PlotFormat aFormat )
{
    // Remark:
    // Hole list must be created before calling this function, by buildHolesList(),
    // for the right holes set (PTH, NPTH, buried/blind vias ...)

    double          scale = 1.0;
    wxPoint         offset;
    PLOTTER*        plotter = NULL;
    PAGE_INFO dummy( PAGE_INFO::A4, false );

    PCB_PLOT_PARAMS plot_opts;  // starts plotting with default options

    LOCALE_IO       toggle;     // use standard C notation for float numbers

    const PAGE_INFO& page_info =  m_pageInfo ? *m_pageInfo : dummy;

    // Calculate dimensions and center of PCB
    EDA_RECT        bbbox = m_pcb->GetBoardEdgesBoundingBox();

    // Calculate the scale for the format type, scale 1 in HPGL, drawing on
    // an A4 sheet in PS, + text description of symbols
    switch( aFormat )
    {
    case PLOT_FORMAT_GERBER:
        offset  = GetOffset();
        plotter = new GERBER_PLOTTER();
        plotter->SetViewport( offset, IU_PER_MILS/10, scale, false );
        plotter->SetGerberCoordinatesFormat( 5 );   // format x.5 unit = mm
        break;

    case PLOT_FORMAT_HPGL:    // Scale for HPGL format.
    {
        HPGL_PLOTTER* hpgl_plotter = new HPGL_PLOTTER;
        plotter = hpgl_plotter;
        hpgl_plotter->SetPenNumber( plot_opts.GetHPGLPenNum() );
        hpgl_plotter->SetPenSpeed( plot_opts.GetHPGLPenSpeed() );
        plotter->SetPageSettings( page_info );
        plotter->SetViewport( offset, IU_PER_MILS/10, scale, false );
    }
        break;


    default:
        wxASSERT( false );
        // fall through
    case PLOT_FORMAT_PDF:
    case PLOT_FORMAT_POST:
    {
        PAGE_INFO   pageA4( wxT( "A4" ) );
        wxSize      pageSizeIU = pageA4.GetSizeIU();

        // Reserve a margin around the page.
        int         margin = KiROUND( 20 * IU_PER_MM );

        // Calculate a scaling factor to print the board on the sheet
        double      Xscale = double( pageSizeIU.x - ( 2 * margin ) ) / bbbox.GetWidth();

        // We should print the list of drill sizes, so reserve room for it
        // 60% height for board 40% height for list
        int     ypagesize_for_board = KiROUND( pageSizeIU.y * 0.6 );
        double  Yscale = double( ypagesize_for_board - margin ) / bbbox.GetHeight();

        scale = std::min( Xscale, Yscale );

        // Experience shows the scale should not to large, because texts
        // create problem (can be to big or too small).
        // So the scale is clipped at 3.0;
        scale = std::min( scale, 3.0 );

        offset.x    = KiROUND( double( bbbox.Centre().x ) -
                               ( pageSizeIU.x / 2.0 ) / scale );
        offset.y    = KiROUND( double( bbbox.Centre().y ) -
                               ( ypagesize_for_board / 2.0 ) / scale );

        if( aFormat == PLOT_FORMAT_PDF )
            plotter = new PDF_PLOTTER;
        else
            plotter = new PS_PLOTTER;

        plotter->SetPageSettings( pageA4 );
        plotter->SetViewport( offset, IU_PER_MILS/10, scale, false );
    }
        break;

    case PLOT_FORMAT_DXF:
    {
        DXF_PLOTTER* dxf_plotter = new DXF_PLOTTER;
        plotter = dxf_plotter;
        plotter->SetPageSettings( page_info );
        plotter->SetViewport( offset, IU_PER_MILS/10, scale, false );
    }
        break;

    case PLOT_FORMAT_SVG:
    {
        SVG_PLOTTER* svg_plotter = new SVG_PLOTTER;
        plotter = svg_plotter;
        plotter->SetPageSettings( page_info );
        plotter->SetViewport( offset, IU_PER_MILS/10, scale, false );
    }
        break;
    }

    plotter->SetCreator( wxT( "PCBNEW" ) );
    plotter->SetDefaultLineWidth( 5 * IU_PER_MILS );
    plotter->SetColorMode( false );

    if( ! plotter->OpenFile( aFullFileName ) )
    {
        delete plotter;
        return false;
    }

    plotter->StartPlot();

    // Draw items on edge layer (not all, only items useful for drill map
    BRDITEMS_PLOTTER itemplotter( plotter, m_pcb, plot_opts );
    itemplotter.SetLayerSet( Edge_Cuts );

    for( auto PtStruct : m_pcb->Drawings() )
    {
        switch( PtStruct->Type() )
        {
        case PCB_LINE_T:
            itemplotter.PlotDrawSegment( (DRAWSEGMENT*) PtStruct );
            break;

        case PCB_TEXT_T:
            itemplotter.PlotTextePcb( (TEXTE_PCB*) PtStruct );
            break;

        case PCB_DIMENSION_T:
        case PCB_TARGET_T:
        case PCB_MARKER_T:     // do not draw
        default:
            break;
        }
    }

    int         x, y;
    int         plotX, plotY, TextWidth;
    int         intervalle = 0;
    char        line[1024];
    wxString    msg;
    int         textmarginaftersymbol = KiROUND( 2 * IU_PER_MM );

    // Set Drill Symbols width
    plotter->SetDefaultLineWidth( 0.2 * IU_PER_MM / scale );
    plotter->SetCurrentLineWidth( -1 );

    // Plot board outlines and drill map
    plotDrillMarks( plotter );

    // Print a list of symbols used.
    int     charSize    = 3 * IU_PER_MM;                    // text size in IUs
    double  charScale   = 1.0 / scale;                      // real scale will be 1/scale,
                                                            // because the global plot scale is scale
    TextWidth   = KiROUND( (charSize * charScale) / 10.0 );    // Set text width (thickness)
    intervalle  = KiROUND( charSize * charScale ) + TextWidth;

    // Trace information.
    plotX   = KiROUND( bbbox.GetX() + textmarginaftersymbol * charScale );
    plotY   = bbbox.GetBottom() + intervalle;

    // Plot title  "Info"
    wxString Text = wxT( "Drill Map:" );
    plotter->Text( wxPoint( plotX, plotY ), COLOR4D::UNSPECIFIED, Text, 0,
                   wxSize( KiROUND( charSize * charScale ),
                           KiROUND( charSize * charScale ) ),
                   GR_TEXT_HJUSTIFY_LEFT, GR_TEXT_VJUSTIFY_CENTER,
                   TextWidth, false, false );

    for( unsigned ii = 0; ii < m_toolListBuffer.size(); ii++ )
    {
        DRILL_TOOL& tool = m_toolListBuffer[ii];

        if( tool.m_TotalCount == 0 )
            continue;

        plotY += intervalle;

        int plot_diam = KiROUND( tool.m_Diameter );
        x = KiROUND( plotX - textmarginaftersymbol * charScale - plot_diam / 2.0 );
        y = KiROUND( plotY + charSize * charScale );
        plotter->Marker( wxPoint( x, y ), plot_diam, ii );

        // List the diameter of each drill in mm and inches.
        sprintf( line, "%2.2fmm / %2.3f\" ",
                 diameter_in_mm( tool.m_Diameter ),
                 diameter_in_inches( tool.m_Diameter ) );

        msg = FROM_UTF8( line );

        // Now list how many holes and ovals are associated with each drill.
        if( ( tool.m_TotalCount == 1 )
            && ( tool.m_OvalCount == 0 ) )
            sprintf( line, "(1 hole)" );
        else if( tool.m_TotalCount == 1 ) // && ( toolm_OvalCount == 1 )
            sprintf( line, "(1 slot)" );
        else if( tool.m_OvalCount == 0 )
            sprintf( line, "(%d holes)", tool.m_TotalCount );
        else if( tool.m_OvalCount == 1 )
            sprintf( line, "(%d holes + 1 slot)", tool.m_TotalCount - 1 );
        else // if ( toolm_OvalCount > 1 )
            sprintf( line, "(%d holes + %d slots)",
                     tool.m_TotalCount - tool.m_OvalCount,
                     tool.m_OvalCount );

        msg += FROM_UTF8( line );

        if( tool.m_Hole_NotPlated )
            msg += wxT( " (not plated)" );

        plotter->Text( wxPoint( plotX, y ), COLOR4D::UNSPECIFIED, msg, 0,
                       wxSize( KiROUND( charSize * charScale ),
                               KiROUND( charSize * charScale ) ),
                       GR_TEXT_HJUSTIFY_LEFT, GR_TEXT_VJUSTIFY_CENTER,
                       TextWidth, false, false );

        intervalle = KiROUND( ( ( charSize * charScale ) + TextWidth ) * 1.2 );

        if( intervalle < ( plot_diam + ( 1 * IU_PER_MM / scale ) + TextWidth ) )
            intervalle = plot_diam + ( 1 * IU_PER_MM / scale ) + TextWidth;
    }

    plotter->EndPlot();
    delete plotter;

    return true;
}
/*
 * Note 1: polygons are drawm using outlines witk a thickness = aMinThicknessValue
 * so shapes must take in account this outline thickness
 *
 * Note 2:
 *      Trapezoidal pads are not considered here because they are very special case
 *      and are used in microwave applications and they *DO NOT* have a thermal relief that
 *      change the shape by creating stubs and destroy their properties.
 */
void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer,
                                    const D_PAD&    aPad,
                                    int             aThermalGap,
                                    int             aCopperThickness,
                                    int             aMinThicknessValue,
                                    int             aError,
                                    double          aThermalRot )
{
    wxPoint corner, corner_end;
    wxSize  copper_thickness;
    wxPoint padShapePos = aPad.ShapePos();      // Note: for pad having a shape offset,
                                                // the pad position is NOT the shape position

    /* Keep in account the polygon outline thickness
     * aThermalGap must be increased by aMinThicknessValue/2 because drawing external outline
     * with a thickness of aMinThicknessValue will reduce gap by aMinThicknessValue/2
     */
    aThermalGap += aMinThicknessValue / 2;

    /* Keep in account the polygon outline thickness
     * copper_thickness must be decreased by aMinThicknessValue because drawing outlines
     * with a thickness of aMinThicknessValue will increase real thickness by aMinThicknessValue
     */
    int     dx = aPad.GetSize().x / 2;
    int     dy = aPad.GetSize().y / 2;

    copper_thickness.x = std::min( aPad.GetSize().x, aCopperThickness ) - aMinThicknessValue;
    copper_thickness.y = std::min( aPad.GetSize().y, aCopperThickness ) - aMinThicknessValue;

    if( copper_thickness.x < 0 )
        copper_thickness.x = 0;

    if( copper_thickness.y < 0 )
        copper_thickness.y = 0;

    switch( aPad.GetShape() )
    {
    case PAD_SHAPE_CIRCLE:    // Add 4 similar holes
        {
            /* we create 4 copper holes and put them in position 1, 2, 3 and 4
             * here is the area of the rectangular pad + its thermal gap
             * the 4 copper holes remove the copper in order to create the thermal gap
             * 4 ------ 1
             * |        |
             * |        |
             * |        |
             * |        |
             * 3 ------ 2
             * holes 2, 3, 4 are the same as hole 1, rotated 90, 180, 270 deg
             */

            // Build the hole pattern, for the hole in the X >0, Y > 0 plane:
            // The pattern roughtly is a 90 deg arc pie
            std::vector <wxPoint> corners_buffer;

            int    numSegs = std::max( GetArcToSegmentCount( dx + aThermalGap, aError, 360.0 ), 6 );
            double correction = GetCircletoPolyCorrectionFactor( numSegs );
            double delta = 3600.0 / numSegs;

            // Radius of outer arcs of the shape corrected for arc approximation by lines
            int outer_radius = KiROUND( ( dx + aThermalGap ) * correction );

            // Crosspoint of thermal spoke sides, the first point of polygon buffer
            corners_buffer.push_back( wxPoint( copper_thickness.x / 2, copper_thickness.y / 2 ) );

            // Add an intermediate point on spoke sides, to allow a > 90 deg angle between side
            // and first seg of arc approx
            corner.x = copper_thickness.x / 2;
            int y = outer_radius - (aThermalGap / 4);
            corner.y = KiROUND( sqrt( ( (double) y * y  - (double) corner.x * corner.x ) ) );

            if( aThermalRot != 0 )
                corners_buffer.push_back( corner );

            // calculate the starting point of the outter arc
            corner.x = copper_thickness.x / 2;

            corner.y = KiROUND( sqrt( ( (double) outer_radius * outer_radius ) -
                                      ( (double) corner.x * corner.x ) ) );
            RotatePoint( &corner, 90 ); // 9 degrees is the spoke fillet size

            // calculate the ending point of the outer arc
            corner_end.x = corner.y;
            corner_end.y = corner.x;

            // calculate intermediate points (y coordinate from corner.y to corner_end.y
            while( (corner.y > corner_end.y)  && (corner.x < corner_end.x) )
            {
                corners_buffer.push_back( corner );
                RotatePoint( &corner, delta );
            }

            corners_buffer.push_back( corner_end );

            /* add an intermediate point, to avoid angles < 90 deg between last arc approx line
             * and radius line
             */
            corner.x = corners_buffer[1].y;
            corner.y = corners_buffer[1].x;
            corners_buffer.push_back( corner );

            // Now, add the 4 holes ( each is the pattern, rotated by 0, 90, 180 and 270  deg
            // aThermalRot = 450 (45.0 degrees orientation) work fine.
            double angle_pad = aPad.GetOrientation();              // Pad orientation
            double th_angle  = aThermalRot;

            for( unsigned ihole = 0; ihole < 4; ihole++ )
            {
                aCornerBuffer.NewOutline();

                for( unsigned ii = 0; ii < corners_buffer.size(); ii++ )
                {
                    corner = corners_buffer[ii];
                    RotatePoint( &corner, th_angle + angle_pad );          // Rotate by segment angle and pad orientation
                    corner += padShapePos;
                    aCornerBuffer.Append( corner.x, corner.y );
                }

                th_angle += 900;       // Note: th_angle in in 0.1 deg.
            }
        }
        break;

    case PAD_SHAPE_OVAL:
        {
            // Oval pad support along the lines of round and rectangular pads
            std::vector <wxPoint> corners_buffer;               // Polygon buffer as vector

            dx = (aPad.GetSize().x / 2) + aThermalGap;     // Cutout radius x
            dy = (aPad.GetSize().y / 2) + aThermalGap;     // Cutout radius y

            wxPoint shape_offset;

            // We want to calculate an oval shape with dx > dy.
            // if this is not the case, exchange dx and dy, and rotate the shape 90 deg.
            int supp_angle = 0;

            if( dx < dy )
            {
                std::swap( dx, dy );
                supp_angle = 900;
                std::swap( copper_thickness.x, copper_thickness.y );
            }

            int deltasize = dx - dy;        // = distance between shape position and the 2 demi-circle ends centre
            // here we have dx > dy
            // Radius of outer arcs of the shape:
            int outer_radius = dy;     // The radius of the outer arc is radius end + aThermalGap


            int    numSegs = std::max( GetArcToSegmentCount( outer_radius, aError, 360.0 ), 6 );
            double delta = 3600.0 / numSegs;

            // Some coordinate fiddling, depending on the shape offset direction
            shape_offset = wxPoint( deltasize, 0 );

            // Crosspoint of thermal spoke sides, the first point of polygon buffer
            corner.x = copper_thickness.x / 2;
            corner.y = copper_thickness.y / 2;
            corners_buffer.push_back( corner );

            // Arc start point calculation, the intersecting point of cutout arc and thermal spoke edge
            // If copper thickness is more than shape offset, we need to calculate arc intercept point.
            if( copper_thickness.x > deltasize )
            {
                corner.x = copper_thickness.x / 2;
                corner.y = KiROUND( sqrt( ( (double) outer_radius * outer_radius ) -
                                        ( (double) ( corner.x - delta ) * ( corner.x - deltasize ) ) ) );
                corner.x -= deltasize;

                /* creates an intermediate point, to have a > 90 deg angle
                 * between the side and the first segment of arc approximation
                 */
                wxPoint intpoint = corner;
                intpoint.y -= aThermalGap / 4;
                corners_buffer.push_back( intpoint + shape_offset );
                RotatePoint( &corner, 90 ); // 9 degrees of thermal fillet
            }
            else
            {
                corner.x = copper_thickness.x / 2;
                corner.y = outer_radius;
                corners_buffer.push_back( corner );
            }

            // Add an intermediate point on spoke sides, to allow a > 90 deg angle between side
            // and first seg of arc approx
            wxPoint last_corner;
            last_corner.y = copper_thickness.y / 2;
            int     px = outer_radius - (aThermalGap / 4);
            last_corner.x =
                KiROUND( sqrt( ( ( (double) px * px ) - (double) last_corner.y * last_corner.y ) ) );

            // Arc stop point calculation, the intersecting point of cutout arc and thermal spoke edge
            corner_end.y = copper_thickness.y / 2;
            corner_end.x =
                KiROUND( sqrt( ( (double) outer_radius *
                             outer_radius ) - ( (double) corner_end.y * corner_end.y ) ) );
            RotatePoint( &corner_end, -90 ); // 9 degrees of thermal fillet

            // calculate intermediate arc points till limit is reached
            while( (corner.y > corner_end.y)  && (corner.x < corner_end.x) )
            {
                corners_buffer.push_back( corner + shape_offset );
                RotatePoint( &corner, delta );
            }

            //corners_buffer.push_back(corner + shape_offset);      // TODO: about one mil geometry error forms somewhere.
            corners_buffer.push_back( corner_end + shape_offset );
            corners_buffer.push_back( last_corner + shape_offset );         // Enabling the line above shows intersection point.

            /* Create 2 holes, rotated by pad rotation.
             */
            double angle = aPad.GetOrientation() + supp_angle;

            for( int irect = 0; irect < 2; irect++ )
            {
                aCornerBuffer.NewOutline();
                for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
                {
                    wxPoint cpos = corners_buffer[ic];
                    RotatePoint( &cpos, angle );
                    cpos += padShapePos;
                    aCornerBuffer.Append( cpos.x, cpos.y );
                }

                angle = AddAngles( angle, 1800 ); // this is calculate hole 3
            }

            // Create holes, that are the mirrored from the previous holes
            for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
            {
                wxPoint swap = corners_buffer[ic];
                swap.x = -swap.x;
                corners_buffer[ic] = swap;
            }

            // Now add corner 4 and 2 (2 is the corner 4 rotated by 180 deg
            angle = aPad.GetOrientation() + supp_angle;

            for( int irect = 0; irect < 2; irect++ )
            {
                aCornerBuffer.NewOutline();

                for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
                {
                    wxPoint cpos = corners_buffer[ic];
                    RotatePoint( &cpos, angle );
                    cpos += padShapePos;
                    aCornerBuffer.Append( cpos.x, cpos.y );
                }

                angle = AddAngles( angle, 1800 );
            }
        }
        break;

    case PAD_SHAPE_CHAMFERED_RECT:
    case PAD_SHAPE_ROUNDRECT:   // thermal shape is the same for rectangular shapes.
    case PAD_SHAPE_RECT:
        {
            /* we create 4 copper holes and put them in position 1, 2, 3 and 4
             * here is the area of the rectangular pad + its thermal gap
             * the 4 copper holes remove the copper in order to create the thermal gap
             * 1 ------ 4
             * |        |
             * |        |
             * |        |
             * |        |
             * 2 ------ 3
             * hole 3 is the same as hole 1, rotated 180 deg
             * hole 4 is the same as hole 2, rotated 180 deg and is the same as hole 1, mirrored
             */

            // First, create a rectangular hole for position 1 :
            // 2 ------- 3
            //  |        |
            //  |        |
            //  |        |
            // 1  -------4

            // Modified rectangles with one corner rounded. TODO: merging with oval thermals
            // and possibly round too.

            std::vector <wxPoint> corners_buffer;               // Polygon buffer as vector

            dx = (aPad.GetSize().x / 2) + aThermalGap;         // Cutout radius x
            dy = (aPad.GetSize().y / 2) + aThermalGap;         // Cutout radius y

            // calculation is optimized for pad shape with dy >= dx (vertical rectangle).
            // if it is not the case, just rotate this shape 90 degrees:
            double angle = aPad.GetOrientation();
            wxPoint corner_origin_pos( -aPad.GetSize().x / 2, -aPad.GetSize().y / 2 );

            if( dy < dx )
            {
                std::swap( dx, dy );
                std::swap( copper_thickness.x, copper_thickness.y );
                std::swap( corner_origin_pos.x, corner_origin_pos.y );
                angle += 900.0;
            }
            // Now calculate the hole pattern in position 1 ( top left pad corner )

            // The first point of polygon buffer is left lower corner, second the crosspoint of
            // thermal spoke sides, the third is upper right corner and the rest are rounding
            // vertices going anticlockwise. Note the inverted Y-axis in corners_buffer y coordinates.
            wxPoint arc_end_point( -dx, -(aThermalGap / 4 + copper_thickness.y / 2) );
            corners_buffer.push_back( arc_end_point );          // Adds small miters to zone
            corners_buffer.push_back( wxPoint( -(dx - aThermalGap / 4), -copper_thickness.y / 2 ) );    // fill and spoke corner
            corners_buffer.push_back( wxPoint( -copper_thickness.x / 2, -copper_thickness.y / 2 ) );
            corners_buffer.push_back( wxPoint( -copper_thickness.x / 2, -(dy - aThermalGap / 4) ) );
            // The first point to build the rounded corner:
            wxPoint arc_start_point( -(aThermalGap / 4 + copper_thickness.x / 2) , -dy );
            corners_buffer.push_back( arc_start_point );

            int    numSegs = std::max( GetArcToSegmentCount( aThermalGap, aError, 360.0 ), 6 );
            double correction = GetCircletoPolyCorrectionFactor( numSegs );
            int    rounding_radius = KiROUND( aThermalGap * correction ); // Corner rounding radius

            // Calculate arc angle parameters.
            // the start angle id near 900 decidegrees, the final angle is near 1800.0 decidegrees.
            double arc_increment = 3600.0 / numSegs;

            // the arc_angle_start is 900.0 or slighly more, depending on the actual arc starting point
            double arc_angle_start = atan2( -arc_start_point.y -corner_origin_pos.y, arc_start_point.x - corner_origin_pos.x ) * 1800/M_PI;
            if( arc_angle_start < 900.0 )
                arc_angle_start = 900.0;

            bool first_point = true;
            for( double curr_angle = arc_angle_start; ; curr_angle += arc_increment )
            {
                wxPoint corner_position = wxPoint( rounding_radius, 0 );
                RotatePoint( &corner_position, curr_angle );        // Rounding vector rotation
                corner_position += corner_origin_pos;               // Rounding vector + Pad corner offset

                // The arc angle is <= 90 degrees, therefore the arc is finished if the x coordinate
                // decrease or the y coordinate is smaller than the y end point
                if( !first_point &&
                    ( corner_position.x >= corners_buffer.back().x || corner_position.y > arc_end_point.y ) )
                    break;

                first_point = false;

                // Note: for hole in position 1, arc x coordinate is always < x starting point
                // and arc y coordinate is always <= y ending point
                if( corner_position != corners_buffer.back()    // avoid duplicate corners.
                    && corner_position.x <= arc_start_point.x )   // skip current point at the right of the starting point
                    corners_buffer.push_back( corner_position );
            }

            for( int irect = 0; irect < 2; irect++ )
            {
                aCornerBuffer.NewOutline();

                for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
                {
                    wxPoint cpos = corners_buffer[ic];
                    RotatePoint( &cpos, angle );            // Rotate according to module orientation
                    cpos += padShapePos;                    // Shift origin to position
                    aCornerBuffer.Append( cpos.x, cpos.y );
                }

                angle = AddAngles( angle, 1800 );       // this is calculate hole 3
            }

            // Create holes, that are the mirrored from the previous holes
            for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
            {
                wxPoint swap = corners_buffer[ic];
                swap.x = -swap.x;
                corners_buffer[ic] = swap;
            }

            // Now add corner 4 and 2 (2 is the corner 4 rotated by 180 deg
            for( int irect = 0; irect < 2; irect++ )
            {
                aCornerBuffer.NewOutline();

                for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
                {
                    wxPoint cpos = corners_buffer[ic];
                    RotatePoint( &cpos, angle );
                    cpos += padShapePos;
                    aCornerBuffer.Append( cpos.x, cpos.y );
                }

                angle = AddAngles( angle, 1800 );
            }
        }
        break;

    case PAD_SHAPE_TRAPEZOID:
        {
        SHAPE_POLY_SET antipad;       // The full antipad area

        // We need a length to build the stubs of the thermal reliefs
        // the value is not very important. The pad bounding box gives a reasonable value
        EDA_RECT bbox = aPad.GetBoundingBox();
        int stub_len = std::max( bbox.GetWidth(), bbox.GetHeight() );

        aPad.TransformShapeWithClearanceToPolygon( antipad, aThermalGap );

        SHAPE_POLY_SET stub;          // A basic stub ( a rectangle)
        SHAPE_POLY_SET stubs;        // the full stubs shape


        // We now substract the stubs (connections to the copper zone)
        //ClipperLib::Clipper clip_engine;
        // Prepare a clipping transform
        //clip_engine.AddPath( antipad, ClipperLib::ptSubject, true );

        // Create stubs and add them to clipper engine
        wxPoint stubBuffer[4];
        stubBuffer[0].x = stub_len;
        stubBuffer[0].y = copper_thickness.y/2;
        stubBuffer[1] = stubBuffer[0];
        stubBuffer[1].y = -copper_thickness.y/2;
        stubBuffer[2] = stubBuffer[1];
        stubBuffer[2].x = -stub_len;
        stubBuffer[3] = stubBuffer[2];
        stubBuffer[3].y = copper_thickness.y/2;

        stub.NewOutline();

        for( unsigned ii = 0; ii < arrayDim( stubBuffer ); ii++ )
        {
            wxPoint cpos = stubBuffer[ii];
            RotatePoint( &cpos, aPad.GetOrientation() );
            cpos += padShapePos;
            stub.Append( cpos.x, cpos.y );
        }

        stubs.Append( stub );

        stubBuffer[0].y = stub_len;
        stubBuffer[0].x = copper_thickness.x/2;
        stubBuffer[1] = stubBuffer[0];
        stubBuffer[1].x = -copper_thickness.x/2;
        stubBuffer[2] = stubBuffer[1];
        stubBuffer[2].y = -stub_len;
        stubBuffer[3] = stubBuffer[2];
        stubBuffer[3].x = copper_thickness.x/2;

        stub.RemoveAllContours();
        stub.NewOutline();

        for( unsigned ii = 0; ii < arrayDim( stubBuffer ); ii++ )
        {
            wxPoint cpos = stubBuffer[ii];
            RotatePoint( &cpos, aPad.GetOrientation() );
            cpos += padShapePos;
            stub.Append( cpos.x, cpos.y );
        }

        stubs.Append( stub );
        stubs.Simplify( SHAPE_POLY_SET::PM_FAST );

        antipad.BooleanSubtract( stubs, SHAPE_POLY_SET::PM_FAST );
        aCornerBuffer.Append( antipad );

        break;
        }

    default:
        ;
    }
}
Example #18
0
/**
 * Function PlaceCells
 * Initialize the matrix routing by setting obstacles for each occupied cell
 * a cell set to HOLE is an obstacle for tracks and vias
 * a cell set to VIA_IMPOSSIBLE is an obstacle for vias only.
 * a cell set to CELL_is_EDGE is a frontier.
 * Tracks and vias having the same net code as net_code are skipped
 * (htey do not are obstacles)
 *
 * For single-sided Routing 1:
 * BOTTOM side is used, and Route_Layer_BOTTOM = Route_Layer_TOP
 *
 * If flag == FORCE_PADS: all pads will be put in matrix as obstacles.
 */
void PlaceCells( BOARD* aPcb, int net_code, int flag )
{
    int       ux0 = 0, uy0 = 0, ux1, uy1, dx, dy;
    int       marge, via_marge;
    LAYER_MSK layerMask;

    // use the default NETCLASS?
    NETCLASS* nc = aPcb->m_NetClasses.GetDefault();

    int       trackWidth = nc->GetTrackWidth();
    int       clearance  = nc->GetClearance();
    int       viaSize    = nc->GetViaDiameter();

    marge     = clearance + (trackWidth / 2);
    via_marge = clearance + (viaSize / 2);

    // Place PADS on matrix routing:
    for( unsigned i = 0; i < aPcb->GetPadCount(); ++i )
    {
        D_PAD* pad = aPcb->GetPad( i );

        if( net_code != pad->GetNet() || (flag & FORCE_PADS) )
        {
            ::PlacePad( pad, HOLE, marge, WRITE_CELL );
        }

        ::PlacePad( pad, VIA_IMPOSSIBLE, via_marge, WRITE_OR_CELL );
    }

    // Place outlines of modules on matrix routing, if they are on a copper layer
    // or on the edge layer
    TRACK tmpSegm( NULL );  // A dummy track used to create segments.

    for( MODULE* module = aPcb->m_Modules; module; module = module->Next() )
    {
        for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() )
        {
            switch( item->Type() )
            {
            case PCB_MODULE_EDGE_T:
            {
                EDGE_MODULE* edge = (EDGE_MODULE*) item;

                tmpSegm.SetLayer( edge->GetLayer() );

                if( tmpSegm.GetLayer() == EDGE_N )
                    tmpSegm.SetLayer( UNDEFINED_LAYER );

                tmpSegm.SetStart( edge->GetStart() );
                tmpSegm.SetEnd(   edge->GetEnd() );
                tmpSegm.SetShape( edge->GetShape() );
                tmpSegm.SetWidth( edge->GetWidth() );
                tmpSegm.m_Param = edge->GetAngle();
                tmpSegm.SetNet( -1 );

                TraceSegmentPcb( &tmpSegm, HOLE, marge, WRITE_CELL );
                TraceSegmentPcb( &tmpSegm, VIA_IMPOSSIBLE, via_marge, WRITE_OR_CELL );
            }
            break;

            default:
                break;
            }
        }
    }

    // Place board outlines and texts on copper layers:
    for( BOARD_ITEM* item = aPcb->m_Drawings; item; item = item->Next() )
    {
        switch( item->Type() )
        {
        case PCB_LINE_T:
        {
            DRAWSEGMENT* DrawSegm;

            int          type_cell = HOLE;
            DrawSegm = (DRAWSEGMENT*) item;
            tmpSegm.SetLayer( DrawSegm->GetLayer() );

            if( DrawSegm->GetLayer() == EDGE_N )
            {
                tmpSegm.SetLayer( UNDEFINED_LAYER );
                type_cell |= CELL_is_EDGE;
            }

            tmpSegm.SetStart( DrawSegm->GetStart() );
            tmpSegm.SetEnd(   DrawSegm->GetEnd() );
            tmpSegm.SetShape( DrawSegm->GetShape() );
            tmpSegm.SetWidth( DrawSegm->GetWidth() );
            tmpSegm.m_Param = DrawSegm->GetAngle();
            tmpSegm.SetNet( -1 );

            TraceSegmentPcb( &tmpSegm, type_cell, marge, WRITE_CELL );
        }
        break;

        case PCB_TEXT_T:
        {
            TEXTE_PCB* PtText;
            PtText = (TEXTE_PCB*) item;

            if( PtText->GetText().Length() == 0 )
                break;

            EDA_RECT textbox = PtText->GetTextBox( -1 );
            ux0 = textbox.GetX();
            uy0 = textbox.GetY();
            dx  = textbox.GetWidth();
            dy  = textbox.GetHeight();

            /* Put bounding box (rectangle) on matrix */
            dx /= 2;
            dy /= 2;

            ux1 = ux0 + dx;
            uy1 = uy0 + dy;

            ux0 -= dx;
            uy0 -= dy;

            layerMask = GetLayerMask( PtText->GetLayer() );

            TraceFilledRectangle( ux0 - marge, uy0 - marge, ux1 + marge,
                                  uy1 + marge, PtText->GetOrientation(),
                                  layerMask, HOLE, WRITE_CELL );

            TraceFilledRectangle( ux0 - via_marge, uy0 - via_marge,
                                  ux1 + via_marge, uy1 + via_marge,
                                  PtText->GetOrientation(),
                                  layerMask, VIA_IMPOSSIBLE, WRITE_OR_CELL );
        }
        break;

        default:
            break;
        }
    }

    /* Put tracks and vias on matrix */
    for( TRACK* track = aPcb->m_Track; track; track = track->Next() )
    {
        if( net_code == track->GetNet() )
            continue;

        TraceSegmentPcb( track, HOLE, marge, WRITE_CELL );
        TraceSegmentPcb( track, VIA_IMPOSSIBLE, via_marge, WRITE_OR_CELL );
    }
}
/* Function to move components in a rectangular area format 4 / 3,
 * starting from the mouse cursor.
 * Footprints are grouped by sheet.
 * Components with the LOCKED status set are not moved
 */
void PCB_EDIT_FRAME::SpreadFootprints( std::vector<MODULE*>* aFootprints,
                                       bool aMoveFootprintsOutsideBoardOnly,
                                       bool aCheckForBoardEdges,
                                       wxPoint aSpreadAreaPosition,
                                       bool aPrepareUndoCommand )
{
    EDA_RECT bbox = GetBoard()->GetBoardEdgesBoundingBox();
    bool     edgesExist = bbox.GetWidth() || bbox.GetHeight();
    // if aFootprintsOutsideBoardOnly is true, and if board outline exists,
    // we have to filter footprints to move:
    bool outsideBrdFilter = aMoveFootprintsOutsideBoardOnly && edgesExist;

    // no edges exist
    if( aMoveFootprintsOutsideBoardOnly && !edgesExist )
    {
        DisplayError( this,
                      _( "Could not automatically place footprints. No board outlines detected." ) );
        return;
    }


    // Build candidate list
    // calculate also the area needed by these footprints
    std::vector <MODULE*> footprintList;

    for( MODULE* footprint : *aFootprints )
    {
        footprint->CalculateBoundingBox();

        if( outsideBrdFilter )
        {
            if( bbox.Contains( footprint->GetPosition() ) )
                continue;
        }

        if( footprint->IsLocked() )
            continue;

        footprintList.push_back( footprint );
    }

    if( footprintList.empty() )
        return;

    // sort footprints by sheet path. we group them later by sheet
    sort( footprintList.begin(), footprintList.end(), sortFootprintsbySheetPath );

    // Undo command: init undo list. If aPrepareUndoCommand == false
    // no undo command will be initialized.
    // Useful when a undo command is already initialized by the caller
    PICKED_ITEMS_LIST  undoList;

    if( aPrepareUndoCommand )
    {
        undoList.m_Status = UR_CHANGED;
        ITEM_PICKER        picker( NULL, UR_CHANGED );

        for( MODULE* footprint : footprintList )
        {
            // Undo: add copy of the footprint to undo list
            picker.SetItem( footprint );
            picker.SetLink( footprint->Clone() );
            undoList.PushItem( picker );
        }
    }

    // Extract and place footprints by sheet
    std::vector <MODULE*> footprintListBySheet;
    std::vector <EDA_RECT> placementSheetAreas;
    double subsurface;
    double placementsurface = 0.0;

    // put the placement area position on mouse cursor.
    // this position will be adjusted later
    wxPoint placementAreaPosition = aSpreadAreaPosition;

    // We sometimes do not want to move footprints inside an existing board.
    // Therefore, move the placement area position outside the board bounding box
    // to the left of the board
    if( aCheckForBoardEdges && edgesExist )
    {
        if( placementAreaPosition.x < bbox.GetEnd().x &&
            placementAreaPosition.y < bbox.GetEnd().y )
        {
            // the placement area could overlap the board
            // move its position to a safe location
            placementAreaPosition.x = bbox.GetEnd().x;
            placementAreaPosition.y = bbox.GetOrigin().y;
        }
    }

    // The placement uses 2 passes:
    // the first pass creates the rectangular areas to place footprints
    // each sheet in schematic creates one rectangular area.
    // the second pass moves footprints inside these areas
    MODULE* footprint;
    for( int pass = 0; pass < 2; pass++ )
    {
        int subareaIdx = 0;
        footprintListBySheet.clear();
        subsurface = 0.0;

        for( unsigned ii = 0; ii < footprintList.size(); ii++ )
        {
            footprint = footprintList[ii];
            bool islastItem = false;

            if( ii == footprintList.size() - 1 ||
                ( footprintList[ii]->GetPath().BeforeLast( '/' ) !=
                  footprintList[ii+1]->GetPath().BeforeLast( '/' ) ) )
                islastItem = true;

            footprintListBySheet.push_back( footprint );
            subsurface += footprint->GetArea( PADDING );

            if( islastItem )
            {
                // end of the footprint sublist relative to the same sheet path
                // calculate placement of the current sublist
                EDA_RECT freeArea;
                int Xsize_allowed = (int) ( sqrt( subsurface ) * 4.0 / 3.0 );
                int Ysize_allowed = (int) ( subsurface / Xsize_allowed );

                freeArea.SetWidth( Xsize_allowed );
                freeArea.SetHeight( Ysize_allowed );
                CRectPlacement placementArea;

                if( pass == 1 )
                {
                    wxPoint areapos = placementSheetAreas[subareaIdx].GetOrigin()
                                      + placementAreaPosition;
                    freeArea.SetOrigin( areapos );
                }

                bool findAreaOnly = pass == 0;
                moveFootprintsInArea( placementArea, footprintListBySheet,
                                      freeArea, findAreaOnly );

                if( pass == 0 )
                {
                    // Populate sheet placement areas list
                    EDA_RECT sub_area;
                    sub_area.SetWidth( placementArea.GetW()*scale );
                    sub_area.SetHeight( placementArea.GetH()*scale );
                    // Add a margin around the sheet placement area:
                    sub_area.Inflate( Millimeter2iu( 1.5 ) );

                    placementSheetAreas.push_back( sub_area );

                    placementsurface += (double) sub_area.GetWidth()*
                                        sub_area.GetHeight();
                }

                // Prepare buffers for next sheet
                subsurface  = 0.0;
                footprintListBySheet.clear();
                subareaIdx++;
            }
        }

        // End of pass:
        // At the end of the first pass, we have to find position of each sheet
        // placement area
        if( pass == 0 )
        {
            int Xsize_allowed = (int) ( sqrt( placementsurface ) * 4.0 / 3.0 );
            int Ysize_allowed = (int) ( placementsurface / Xsize_allowed );
            CRectPlacement placementArea;
            CSubRectArray  vecSubRects;

            fillRectList( vecSubRects, placementSheetAreas );
            spreadRectangles( placementArea, vecSubRects, Xsize_allowed, Ysize_allowed );

            for( unsigned it = 0; it < vecSubRects.size(); ++it )
            {
                TSubRect& srect = vecSubRects[it];
                wxPoint pos( srect.x*scale, srect.y*scale );
                wxSize size( srect.w*scale, srect.h*scale );
                placementSheetAreas[srect.n].SetOrigin( pos );
                placementSheetAreas[srect.n].SetSize( size );
            }
        }
    }   // End pass

    // Undo: commit list
    if( aPrepareUndoCommand )
        SaveCopyInUndoList( undoList, UR_CHANGED );

    OnModify();

    m_canvas->Refresh();
}
void BOARD_PRINTOUT_CONTROLLER::DrawPage()
{
    wxPoint       offset;
    double        userscale;
    EDA_RECT      boardBoundingBox;
    EDA_RECT      drawRect;
    wxDC*         dc = GetDC();
    BASE_SCREEN*  screen = m_Parent->GetScreen();
    bool          printMirror = m_PrintParams.m_PrintMirror;
    wxSize        pageSizeIU = m_Parent->GetPageSizeIU();

    wxBusyCursor  dummy;

    boardBoundingBox = ((GERBVIEW_FRAME*) m_Parent)->GetGerberLayoutBoundingBox();
    wxString titleblockFilename;    // TODO see if we uses the gerber file name

    // Use the page size as the drawing area when the board is shown or the user scale
    // is less than 1.
    if( m_PrintParams.PrintBorderAndTitleBlock() )
        boardBoundingBox = EDA_RECT( wxPoint( 0, 0 ), pageSizeIU );

    // Compute the PCB size in internal units
    userscale = m_PrintParams.m_PrintScale;

    if( m_PrintParams.m_PrintScale == 0 )   //  fit in page option
    {
        if(boardBoundingBox.GetWidth() && boardBoundingBox.GetHeight())
        {
            int margin = Millimeter2iu( 10.0 ); // add a margin around the drawings
            double scaleX = (double)(pageSizeIU.x - (2 * margin)) /
                            boardBoundingBox.GetWidth();
            double scaleY = (double)(pageSizeIU.y - (2 * margin)) /
                            boardBoundingBox.GetHeight();
            userscale = (scaleX < scaleY) ? scaleX : scaleY;
        }
        else
            userscale = 1.0;
    }

    wxSize scaledPageSize = pageSizeIU;
    drawRect.SetSize( scaledPageSize );
    scaledPageSize.x = wxRound( scaledPageSize.x / userscale );
    scaledPageSize.y = wxRound( scaledPageSize.y / userscale );


    if( m_PrintParams.m_PageSetupData )
    {
        // Always scale to the size of the paper.
        FitThisSizeToPageMargins( scaledPageSize, *m_PrintParams.m_PageSetupData );
    }

    // Compute Accurate scale 1
    if( m_PrintParams.m_PrintScale == 1.0 )
    {
        // We want a 1:1 scale, regardless the page setup
        // like page size, margin ...
        MapScreenSizeToPaper(); // set best scale and offset (scale is not used)
        int w, h;
        GetPPIPrinter( &w, &h );
        double accurate_Xscale = (double) w / (IU_PER_MILS*1000);
        double accurate_Yscale = (double) h / (IU_PER_MILS*1000);

        if( IsPreview() )  // Scale must take in account the DC size in Preview
        {
            // Get the size of the DC in pixels
            wxSize       PlotAreaSize;
            dc->GetSize( &PlotAreaSize.x, &PlotAreaSize.y );
            GetPageSizePixels( &w, &h );
            accurate_Xscale *= (double)PlotAreaSize.x / w;
            accurate_Yscale *= (double)PlotAreaSize.y / h;
        }
        // Fine scale adjust
        accurate_Xscale *= m_PrintParams.m_XScaleAdjust;
        accurate_Yscale *= m_PrintParams.m_YScaleAdjust;

        // Set print scale for 1:1 exact scale
        dc->SetUserScale( accurate_Xscale, accurate_Yscale );
    }

    // Get the final size of the DC in pixels
    wxSize       PlotAreaSizeInPixels;
    dc->GetSize( &PlotAreaSizeInPixels.x, &PlotAreaSizeInPixels.y );

    double scalex, scaley;
    dc->GetUserScale( &scalex, &scaley );

    // In some cases the plot origin is the centre of the board outline rather than the center
    // of the selected paper size.
    if( m_PrintParams.CenterOnBoardOutline() )
    {
        // Here we are only drawing the board and it's contents.
        drawRect = boardBoundingBox;
        offset.x += wxRound( (double) -scaledPageSize.x / 2.0 );
        offset.y += wxRound( (double) -scaledPageSize.y / 2.0 );

        wxPoint center = boardBoundingBox.Centre();

        if( printMirror )
        {
            // Calculate the mirrored center of the board.
            center.x = m_Parent->GetPageSizeIU().x - boardBoundingBox.Centre().x;
        }

        offset += center;
    }

    GRResetPenAndBrush( dc );

    EDA_DRAW_PANEL* panel = m_Parent->GetCanvas();
    EDA_RECT        tmp   = *panel->GetClipBox();

    // Set clip box to the max size
    #define MAX_VALUE (INT_MAX/2)   // MAX_VALUE is the max we can use in an integer
                                    // and that allows calculations without overflow
    panel->SetClipBox( EDA_RECT( wxPoint( 0, 0 ), wxSize( MAX_VALUE, MAX_VALUE ) ) );

    screen->m_IsPrinting = true;
    EDA_COLOR_T bg_color = m_Parent->GetDrawBgColor();

    // Print frame reference, if requested, before printing draw layers
    if( m_PrintParams.m_Print_Black_and_White )
        GRForceBlackPen( true );

    if( m_PrintParams.PrintBorderAndTitleBlock() )
        m_Parent->DrawWorkSheet( dc, screen, m_PrintParams.m_PenDefaultSize,
                                 IU_PER_MILS, titleblockFilename );

    if( printMirror )
    {
        // To plot mirror, we reverse the x axis, and modify the plot x origin
        dc->SetAxisOrientation( false, false );

        /* Change plot offset in order to have the draw area at the same location.
         * The plot origin X is just moved from 0 to PlotAreaSizeInPixels.x.
         * just set offset x at PlotAreaSizeInPixels.x.
         */
        int x_dc_offset = PlotAreaSizeInPixels.x;
        x_dc_offset = KiROUND( x_dc_offset  * userscale );
        dc->SetDeviceOrigin( x_dc_offset, 0 );

        panel->SetClipBox( EDA_RECT( wxPoint( -MAX_VALUE / 2, -MAX_VALUE / 2 ),
                                     panel->GetClipBox()->GetSize() ) );
    }

    // screen->m_DrawOrg = offset;
    dc->SetLogicalOrigin( offset.x, offset.y );
    m_Parent->SetDrawBgColor( WHITE );

    // Never force black pen to print draw layers
    // because negative objects need a white pen, not a black pen
    // B&W mode is handled in print page function
    GRForceBlackPen( false );

    m_Parent->PrintPage( dc, m_PrintParams.m_PrintMaskLayer, printMirror, &m_PrintParams );

    m_Parent->SetDrawBgColor( bg_color );
    screen->m_IsPrinting = false;
    panel->SetClipBox( tmp );
}