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 ); }
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; }
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(); }
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; }
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 ); }
/* * 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 ); }
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; }
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 }
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: ; } }
/** * 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 ); }