/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2011 Wayne Stambaugh * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ /********************************/ /* Low level graphics routines */ /********************************/ #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__WXMAC__) && defined(USE_WX_GRAPHICS_CONTEXT) #include #endif static const bool FILLED = true; static const bool NOT_FILLED = false; /* Important Note: * These drawing functions clip draw item before send these items to wxDC draw * functions. For guy who asks why i did it, see a sample of problems encountered * when pixels * coordinates overflow 16 bits values: * http://trac.wxwidgets.org/ticket/10446 * Problems can be found under Windows **and** Linux (mainly when drawing arcs) * (mainly at low zoom values (2, 1 or 0.5), in Pcbnew) * some of these problems could be now fixed in recent distributions. * * Currently (feb 2009) there are overflow problems when drawing solid (filled) * polygons under linux without clipping * * So before removing clipping functions, be aware these bug (they are not in * KiCad or wxWidgets) are fixed by testing how are drawn complex lines arcs * and solid polygons under Windows and Linux and remember users can have old * versions with bugs */ /* Definitions for enabling and disabling debugging features in gr_basic.cpp. * Please remember to set these back to 0 before making LAUNCHPAD commits. */ #define DEBUG_DUMP_CLIP_ERROR_COORDS 0 // Set to 1 to dump clip algorithm errors. #define DEBUG_DUMP_CLIP_COORDS 0 // Set to 1 to dump clipped coordinates. // For draw mode = XOR GR_XOR or GR_NXOR by background color GR_DRAWMODE g_XorMode = GR_NXOR; static void ClipAndDrawPoly( EDA_RECT * ClipBox, wxDC * DC, wxPoint Points[], int n ); /* These functions are used by corresponding functions * ( GRSCircle is called by GRCircle for instance) after mapping coordinates * from user units to screen units(pixels coordinates) */ static void GRSRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2, int aWidth, COLOR4D aColor, wxPenStyle aStyle = wxPENSTYLE_SOLID ); /**/ static int GRLastMoveToX, GRLastMoveToY; static bool s_ForceBlackPen; /* if true: draws in black instead of * color for printing. */ static int xcliplo = 0, ycliplo = 0, xcliphi = 2000, ycliphi = 2000; static COLOR4D s_DC_lastcolor( 0, 0, 0, 0 ); static COLOR4D s_DC_lastbrushcolor( 0, 0, 0, 0 ); static bool s_DC_lastbrushfill = false; static wxDC* s_DC_lastDC = NULL; static void WinClipAndDrawLine( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width ) { GRLastMoveToX = x2; GRLastMoveToY = y2; if( ClipBox ) { EDA_RECT clipbox(*ClipBox); clipbox.Inflate(width/2); if( ClipLine( &clipbox, x1, y1, x2, y2 ) ) return; } DC->DrawLine( x1, y1, x2, y2 ); } /* Forcing a reset of the current pen. * Must be called after changing the graphical device before any trace. */ void GRResetPenAndBrush( wxDC* DC ) { GRSetBrush( DC, BLACK ); // Force no fill s_DC_lastbrushcolor = COLOR4D::UNSPECIFIED; s_DC_lastcolor = COLOR4D::UNSPECIFIED; s_DC_lastDC = NULL; } /** * Function GRSetColorPen * sets a pen style, width, color, and alpha into the given device context. */ void GRSetColorPen( wxDC* DC, COLOR4D Color, int width, wxPenStyle style ) { wxDash dots[2] = { 1, 3 }; // Under OSX and while printing when wxPen is set to 0, renderer follows the request drawing // nothing & in the bitmap world the minimum is enough to light a pixel, in vectorial one not if( width <= 1 ) width = DC->DeviceToLogicalXRel( 1 ); if( s_ForceBlackPen ) Color = COLOR4D::BLACK; const wxPen& curr_pen = DC->GetPen(); if( !curr_pen.IsOk() || curr_pen.GetColour() != Color.ToColour() || curr_pen.GetWidth() != width || curr_pen.GetStyle() != style ) { wxPen pen; pen.SetColour( Color.ToColour() ); if( style == wxPENSTYLE_DOT ) { style = wxPENSTYLE_USER_DASH; pen.SetDashes( 2, dots ); } pen.SetWidth( width ); pen.SetStyle( style ); DC->SetPen( pen ); } else // Should be not needed, but on Linux, in printing process // the curr pen settings needs to be sometimes re-initialized // Clearly, this is due to a bug, related to SetBrush(), // but we have to live with it, at least on wxWidgets 3.0 DC->SetPen( curr_pen ); } void GRSetBrush( wxDC* DC, COLOR4D Color, bool fill ) { if( s_ForceBlackPen ) Color = COLOR4D::BLACK; if( s_DC_lastbrushcolor != Color || s_DC_lastbrushfill != fill || s_DC_lastDC != DC ) { wxBrush brush; brush.SetColour( Color.ToColour() ); if( fill ) brush.SetStyle( wxBRUSHSTYLE_SOLID ); else brush.SetStyle( wxBRUSHSTYLE_TRANSPARENT ); DC->SetBrush( brush ); s_DC_lastbrushcolor = Color; s_DC_lastbrushfill = fill; s_DC_lastDC = DC; } } /** * Function GRForceBlackPen * @param flagforce True to force a black pen whenever the asked color */ void GRForceBlackPen( bool flagforce ) { s_ForceBlackPen = flagforce; } /** * Function GetGRForceBlackPenState * @return s_ForceBlackPen (True if a black pen was forced) */ bool GetGRForceBlackPenState( void ) { return s_ForceBlackPen; } /*************************************/ /* Set the device context draw mode. */ /*************************************/ void GRSetDrawMode( wxDC* DC, GR_DRAWMODE draw_mode ) { if( draw_mode & GR_OR ) #if defined(__WXMAC__) && (wxMAC_USE_CORE_GRAPHICS || wxCHECK_VERSION( 2, 9, 0 ) ) DC->SetLogicalFunction( wxCOPY ); #elif defined( USE_WX_GRAPHICS_CONTEXT ) DC->SetLogicalFunction( wxCOPY ); #else DC->SetLogicalFunction( wxOR ); #endif else if( draw_mode & GR_XOR ) #if defined( USE_WX_GRAPHICS_CONTEXT ) DC->SetLogicalFunction( wxCOPY ); #else DC->SetLogicalFunction( wxXOR ); #endif else if( draw_mode & GR_NXOR ) #if defined(__WXMAC__) && (wxMAC_USE_CORE_GRAPHICS || wxCHECK_VERSION( 2, 9, 0 ) ) DC->SetLogicalFunction( wxXOR ); #elif defined( USE_WX_GRAPHICS_CONTEXT ) DC->SetLogicalFunction( wxCOPY ); #else DC->SetLogicalFunction( wxEQUIV ); #endif else if( draw_mode & GR_INVERT ) #if defined( USE_WX_GRAPHICS_CONTEXT ) DC->SetLogicalFunction( wxCOPY ); #else DC->SetLogicalFunction( wxINVERT ); #endif else if( draw_mode & GR_COPY ) DC->SetLogicalFunction( wxCOPY ); #ifdef USE_WX_OVERLAY DC->SetLogicalFunction( wxCOPY ); #endif } void GRPutPixel( EDA_RECT* ClipBox, wxDC* DC, int x, int y, COLOR4D Color ) { if( ClipBox && !ClipBox->Contains( x, y ) ) return; GRSetColorPen( DC, Color ); DC->DrawPoint( x, y ); } /* * Draw a line, in object space. */ void GRLine( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color, wxPenStyle aStyle) { GRSetColorPen( DC, Color, width, aStyle ); WinClipAndDrawLine( ClipBox, DC, x1, y1, x2, y2, width ); GRLastMoveToX = x2; GRLastMoveToY = y2; } void GRLine( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aStart, wxPoint aEnd, int aWidth, COLOR4D aColor, wxPenStyle aStyle ) { GRLine( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth, aColor, aStyle ); } void GRDashedLine( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color ) { GRLine( ClipBox, DC, x1, y1, x2, y2, width, Color, wxPENSTYLE_SHORT_DASH ); } void GRDottedLine( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color ) { GRLine( ClipBox, DC, x1, y1, x2, y2, width, Color, wxPENSTYLE_DOT ); } /* * Move to a new position, in object space. */ void GRMoveTo( int x, int y ) { GRLastMoveToX = x; GRLastMoveToY = y; } /* * Draw line to a new position, in object space. */ void GRLineTo( EDA_RECT* ClipBox, wxDC* DC, int x, int y, int width, COLOR4D Color ) { GRLine( ClipBox, DC, GRLastMoveToX, GRLastMoveToY, x, y, width, Color ); } void GRMixedLine( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color ) { GRLine( ClipBox, DC, x1, y1, x2, y2, width, Color, wxPENSTYLE_DOT_DASH ); } /** * Function GRLineArray * draws an array of lines (not a polygon). * @param aClipBox = the clip box * @param aDC = the device context into which drawing should occur. * @param aLines = a list of pair of coordinate in user space: a pair for each line. * @param aWidth = the width of each line. * @param aColor = color to draw the lines * @see COLOR4D */ void GRLineArray( EDA_RECT* aClipBox, wxDC* aDC, std::vector& aLines, int aWidth, COLOR4D aColor ) { if( aLines.empty() ) return; GRSetColorPen( aDC, aColor, aWidth ); if( aClipBox ) aClipBox->Inflate( aWidth / 2 ); #if defined( __WXMAC__ ) && defined( USE_WX_GRAPHICS_CONTEXT ) wxGCDC *gcdc = wxDynamicCast( aDC, wxGCDC ); if( gcdc ) { wxGraphicsContext *gc = gcdc->GetGraphicsContext(); // create path wxGraphicsPath path = gc->CreatePath(); for( unsigned i = 0; i < aLines.size(); i += 2 ) { int x1 = aLines[i].x; int y1 = aLines[i].y; int x2 = aLines[i+1].x; int y2 = aLines[i+1].y; if( ( aClipBox == NULL ) || !ClipLine( aClipBox, x1, y1, x2, y2 ) ) { path.MoveToPoint( x1, y1 ); path.AddLineToPoint( x2, y2 ); } } // draw path gc->StrokePath( path ); } else #endif { for( unsigned i = 0; i < aLines.size(); i += 2 ) { int x1 = aLines[i].x; int y1 = aLines[i].y; int x2 = aLines[i+1].x; int y2 = aLines[i+1].y; if( ( aClipBox == NULL ) || !ClipLine( aClipBox, x1, y1, x2, y2 ) ) aDC->DrawLine( x1, y1, x2, y2 ); } } GRMoveTo( aLines[aLines.size() - 1].x, aLines[aLines.size() - 1].y ); if( aClipBox ) aClipBox->Inflate(-aWidth/2); } // Draw the outline of a thick segment wih rounded ends void GRCSegm( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width, int aPenSize, COLOR4D Color ) { GRLastMoveToX = x2; GRLastMoveToY = y2; if( ClipBox ) { EDA_RECT clipbox(*ClipBox); clipbox.Inflate(width/2); if( ClipLine( &clipbox, x1, y1, x2, y2 ) ) return; } if( width <= 2 ) /* single line or 2 pixels */ { GRSetColorPen( DC, Color, width ); DC->DrawLine( x1, y1, x2, y2 ); return; } GRSetBrush( DC, Color, NOT_FILLED ); GRSetColorPen( DC, Color, aPenSize ); int radius = (width + 1) >> 1; int dx = x2 - x1; int dy = y2 - y1; double angle = -ArcTangente( dy, dx ); wxPoint start; wxPoint end; wxPoint org( x1, y1); int len = (int) hypot( dx, dy ); // We know if the DC is mirrored, to draw arcs int slx = DC->DeviceToLogicalX( 1 ) - DC->DeviceToLogicalX( 0 ); int sly = DC->DeviceToLogicalY( 1 ) - DC->DeviceToLogicalY( 0 ); bool mirrored = (slx > 0 && sly < 0) || (slx < 0 && sly > 0); // first edge start.x = 0; start.y = radius; end.x = len; end.y = radius; RotatePoint( &start, angle); RotatePoint( &end, angle); start += org; end += org; DC->DrawLine( start, end ); // first rounded end end.x = 0; end.y = -radius; RotatePoint( &end, angle); end += org; if( !mirrored ) DC->DrawArc( end, start, org ); else DC->DrawArc( start, end, org ); // second edge start.x = len; start.y = -radius; RotatePoint( &start, angle); start += org; DC->DrawLine( start, end ); // second rounded end end.x = len; end.y = radius; RotatePoint( &end, angle); end += org; if( !mirrored ) DC->DrawArc( end.x, end.y, start.x, start.y, x2, y2 ); else DC->DrawArc( start.x, start.y, end.x, end.y, x2, y2 ); } void GRCSegm( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color ) { GRCSegm( ClipBox, DC, x1, y1, x2, y2, width, 0, Color ); } void GRCSegm( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aStart, wxPoint aEnd, int aWidth, COLOR4D aColor ) { GRCSegm( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth, 0, aColor ); } /* * Draw segment (full) with rounded ends in object space (real coords.). */ void GRFillCSegm( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color ) { GRSetColorPen( DC, Color, width ); WinClipAndDrawLine( ClipBox, DC, x1, y1, x2, y2, width ); } void GRFilledSegment( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aStart, wxPoint aEnd, int aWidth, COLOR4D aColor ) { GRSetColorPen( aDC, aColor, aWidth ); WinClipAndDrawLine( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth ); } static bool IsGRSPolyDrawable( EDA_RECT* ClipBox, int n, wxPoint Points[] ) { if( !ClipBox ) return true; if( n <= 0 ) return false; int Xmin, Xmax, Ymin, Ymax; Xmin = Xmax = Points[0].x; Ymin = Ymax = Points[0].y; for( int ii = 1; ii < n; ii++ ) // calculate rectangle { Xmin = std::min( Xmin, Points[ii].x ); Xmax = std::max( Xmax, Points[ii].x ); Ymin = std::min( Ymin, Points[ii].y ); Ymax = std::max( Ymax, Points[ii].y ); } xcliplo = ClipBox->GetX(); ycliplo = ClipBox->GetY(); xcliphi = ClipBox->GetRight(); ycliphi = ClipBox->GetBottom(); if( Xmax < xcliplo ) return false; if( Xmin > xcliphi ) return false; if( Ymax < ycliplo ) return false; if( Ymin > ycliphi ) return false; return true; } /* * Draw a new polyline and fill it if Fill, in screen space. */ static void GRSPoly( EDA_RECT* ClipBox, wxDC* DC, int n, wxPoint Points[], bool Fill, int width, COLOR4D Color, COLOR4D BgColor ) { if( !IsGRSPolyDrawable( ClipBox, n, Points ) ) return; if( Fill && ( n > 2 ) ) { GRSetBrush( DC, BgColor, FILLED ); GRSetColorPen( DC, Color, width ); /* clip before send the filled polygon to wxDC, because under linux * (GTK?) polygons having large coordinates are incorrectly drawn * (integer overflow in coordinates, I am guessing) */ ClipAndDrawPoly( ClipBox, DC, Points, n ); } else { #if defined( __WXMAC__ ) && defined( USE_WX_GRAPHICS_CONTEXT ) wxGCDC *gcdc = wxDynamicCast( DC, wxGCDC ); if( gcdc ) { wxGraphicsContext *gc = gcdc->GetGraphicsContext(); // set pen GRSetColorPen( DC, Color, width ); // create path wxGraphicsPath path = gc->CreatePath(); path.MoveToPoint( Points[0].x, Points[0].y ); for( int i = 1; i < n; ++i ) { path.AddLineToPoint( Points[i].x, Points[i].y ); } // draw path gc->StrokePath( path ); // correctly update last position GRMoveTo( Points[n - 1].x, Points[n - 1].y ); } else #endif { GRMoveTo( Points[0].x, Points[0].y ); for( int i = 1; i < n; ++i ) { GRLineTo( ClipBox, DC, Points[i].x, Points[i].y, width, Color ); } } } } /* * Draw a new closed polyline and fill it if Fill, in screen space. */ static void GRSClosedPoly( EDA_RECT* aClipBox, wxDC* aDC, int aPointCount, wxPoint aPoints[], bool aFill, int aWidth, COLOR4D aColor, COLOR4D aBgColor ) { if( !IsGRSPolyDrawable( aClipBox, aPointCount, aPoints ) ) return; if( aFill && ( aPointCount > 2 ) ) { GRLastMoveToX = aPoints[aPointCount - 1].x; GRLastMoveToY = aPoints[aPointCount - 1].y; GRSetBrush( aDC, aBgColor, FILLED ); GRSetColorPen( aDC, aColor, aWidth ); ClipAndDrawPoly( aClipBox, aDC, aPoints, aPointCount ); } else { #if defined( __WXMAC__ ) && defined( USE_WX_GRAPHICS_CONTEXT ) wxGCDC *gcdc = wxDynamicCast( aDC, wxGCDC ); if( gcdc ) { wxGraphicsContext *gc = gcdc->GetGraphicsContext(); // set pen GRSetColorPen( aDC, aColor, aWidth ); // create path wxGraphicsPath path = gc->CreatePath(); path.MoveToPoint( aPoints[0].x, aPoints[0].y ); for( int i = 1; i < aPointCount; ++i ) { path.AddLineToPoint( aPoints[i].x, aPoints[i].y ); } if( aPoints[aPointCount - 1] != aPoints[0] ) path.AddLineToPoint( aPoints[0].x, aPoints[0].y ); // draw path gc->StrokePath( path ); // correctly update last position GRMoveTo( aPoints[aPointCount - 1].x, aPoints[aPointCount - 1].y ); } else #endif { GRMoveTo( aPoints[0].x, aPoints[0].y ); for( int i = 1; i < aPointCount; ++i ) { GRLineTo( aClipBox, aDC, aPoints[i].x, aPoints[i].y, aWidth, aColor ); } int lastpt = aPointCount - 1; // Close the polygon if( aPoints[lastpt] != aPoints[0] ) { GRLineTo( aClipBox, aDC, aPoints[0].x, aPoints[0].y, aWidth, aColor ); } } } } /* * Draw a new polyline and fill it if Fill, in drawing space. */ void GRPoly( EDA_RECT* ClipBox, wxDC* DC, int n, wxPoint Points[], bool Fill, int width, COLOR4D Color, COLOR4D BgColor ) { GRSPoly( ClipBox, DC, n, Points, Fill, width, Color, BgColor ); } /* * Draw a closed polyline and fill it if Fill, in object space. */ void GRClosedPoly( EDA_RECT* ClipBox, wxDC* DC, int n, wxPoint Points[], bool Fill, COLOR4D Color, COLOR4D BgColor ) { GRClosedPoly( ClipBox, DC, n, Points, Fill, 0, Color, BgColor ); } void GRClosedPoly( EDA_RECT* ClipBox, wxDC* DC, int n, wxPoint Points[], bool Fill, int width, COLOR4D Color, COLOR4D BgColor ) { GRSClosedPoly( ClipBox, DC, n, Points, Fill, width, Color, BgColor ); } static bool clipCircle( EDA_RECT* aClipBox, int xc, int yc, int r, int aWidth ) { // Clip circles that are outside the ClipBox. if( aClipBox ) { int x0, y0, xm, ym; x0 = aClipBox->GetX(); y0 = aClipBox->GetY(); xm = aClipBox->GetRight(); ym = aClipBox->GetBottom(); r += aWidth; if( xc < ( x0 - r ) ) return true; if( yc < ( y0 - r ) ) return true; if( xc > ( r + xm ) ) return true; if( yc > ( r + ym ) ) return true; } return false; } void GRCircle( EDA_RECT* ClipBox, wxDC* DC, int xc, int yc, int r, int width, COLOR4D Color ) { if( clipCircle( ClipBox, xc, yc, r, width ) || r <= 0 ) return; GRSetBrush( DC, Color, NOT_FILLED ); GRSetColorPen( DC, Color, width ); DC->DrawEllipse( xc - r, yc - r, r + r, r + r ); } void GRCircle( EDA_RECT* ClipBox, wxDC* DC, int x, int y, int r, COLOR4D Color ) { GRCircle( ClipBox, DC, x, y, r, 0, Color ); } void GRCircle( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aPos, int aRadius, int aWidth, COLOR4D aColor ) { GRCircle( aClipBox, aDC, aPos.x, aPos.y, aRadius, aWidth, aColor ); } void GRFilledCircle( EDA_RECT* ClipBox, wxDC* DC, int x, int y, int r, int width, COLOR4D Color, COLOR4D BgColor ) { if( clipCircle( ClipBox, x, y, r, width ) || r <= 0 ) return; GRSetBrush( DC, BgColor, FILLED ); GRSetColorPen( DC, Color, width ); DC->DrawEllipse( x - r, y - r, r + r, r + r ); } void GRFilledCircle( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aPos, int aRadius, COLOR4D aColor ) { GRFilledCircle( aClipBox, aDC, aPos.x, aPos.y, aRadius, 0, aColor, aColor ); } /* * Draw an arc in user space. */ void GRArc1( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int xc, int yc, COLOR4D Color ) { GRArc1( ClipBox, DC, x1, y1, x2, y2, xc, yc, 0, Color ); } /* * Draw an arc, width = width in user space. */ void GRArc1( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int xc, int yc, int width, COLOR4D Color ) { /* Clip arcs off screen. */ if( ClipBox ) { int x0, y0, xm, ym, r; x0 = ClipBox->GetX(); y0 = ClipBox->GetY(); xm = ClipBox->GetRight(); ym = ClipBox->GetBottom(); r = KiROUND( Distance( x1, y1, xc, yc ) ); if( xc < ( x0 - r ) ) return; if( yc < ( y0 - r ) ) return; if( xc > ( r + xm ) ) return; if( yc > ( r + ym ) ) return; } GRSetBrush( DC, Color ); GRSetColorPen( DC, Color, width ); DC->DrawArc( x1, y1, x2, y2, xc, yc ); } void GRArc1( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aStart, wxPoint aEnd, wxPoint aCenter, int aWidth, COLOR4D aColor ) { GRArc1( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aCenter.x, aCenter.y, aWidth, aColor ); } /* * Draw a filled arc in drawing space. */ void GRFilledArc( EDA_RECT* ClipBox, wxDC* DC, int x, int y, double StAngle, double EndAngle, int r, int width, COLOR4D Color, COLOR4D BgColor ) { int x1, y1, x2, y2; /* Clip arcs off screen */ if( ClipBox ) { int x0, y0, xm, ym; x0 = ClipBox->GetX(); y0 = ClipBox->GetY(); xm = ClipBox->GetRight(); ym = ClipBox->GetBottom(); if( x < ( x0 - r - 1 ) ) return; if( y < ( y0 - r - 1 ) ) return; if( x > ( r + xm + 1 ) ) return; if( y > ( r + ym + 1 ) ) return; } x1 = r; y1 = 0; RotatePoint( &x1, &y1, EndAngle ); x2 = r; y2 = 0; RotatePoint( &x2, &y2, StAngle ); GRSetBrush( DC, BgColor, FILLED ); GRSetColorPen( DC, Color, width ); DC->DrawArc( x + x1, y - y1, x + x2, y - y2, x, y ); } void GRFilledArc( EDA_RECT* ClipBox, wxDC* DC, int x, int y, double StAngle, double EndAngle, int r, COLOR4D Color, COLOR4D BgColor ) { GRFilledArc( ClipBox, DC, x, y, StAngle, EndAngle, r, 0, Color, BgColor ); } /* * Draw an arc in drawing space. */ void GRArc( EDA_RECT* ClipBox, wxDC* DC, int xc, int yc, double StAngle, double EndAngle, int r, COLOR4D Color ) { int x1, y1, x2, y2; /* Clip arcs off screen */ if( ClipBox ) { int radius = r + 1; int x0, y0, xm, ym, x, y; x0 = ClipBox->GetX(); y0 = ClipBox->GetY(); xm = ClipBox->GetRight(); ym = ClipBox->GetBottom(); x = xc; y = yc; if( x < ( x0 - radius ) ) return; if( y < ( y0 - radius ) ) return; if( x > ( xm + radius ) ) return; if( y > ( ym + radius ) ) return; } x1 = r; y1 = 0; RotatePoint( &x1, &y1, EndAngle ); x2 = r; y2 = 0; RotatePoint( &x2, &y2, StAngle ); GRSetBrush( DC, Color, NOT_FILLED ); GRSetColorPen( DC, Color ); DC->DrawArc( xc + x1, yc - y1, xc + x2, yc - y2, xc, yc ); } /* * Draw an arc with width = width in drawing space. */ void GRArc( EDA_RECT* ClipBox, wxDC* DC, int x, int y, double StAngle, double EndAngle, int r, int width, COLOR4D Color ) { int x1, y1, x2, y2; /* Clip arcs off screen. */ if( ClipBox ) { int x0, y0, xm, ym; x0 = ClipBox->GetX(); y0 = ClipBox->GetY(); xm = ClipBox->GetRight(); ym = ClipBox->GetBottom(); if( x < ( x0 - r - width ) ) return; if( y < ( y0 - r - width ) ) return; if( x > ( r + xm + width ) ) return; if( y > ( r + ym + width ) ) return; } x1 = r; y1 = 0; RotatePoint( &x1, &y1, EndAngle ); x2 = r; y2 = 0; RotatePoint( &x2, &y2, StAngle ); GRSetBrush( DC, Color ); GRSetColorPen( DC, Color, width ); DC->DrawArc( x + x1, y - y1, x + x2, y - y2, x, y ); } /* * Draw a rectangle in drawing space. */ void GRRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2, COLOR4D aColor ) { GRSRect( aClipBox, aDC, x1, y1, x2, y2, 0, aColor ); } void GRRectPs( EDA_RECT* aClipBox, wxDC* aDC, const EDA_RECT& aRect, COLOR4D aColor, wxPenStyle aStyle ) { int x1 = aRect.GetX(); int y1 = aRect.GetY(); int x2 = aRect.GetRight(); int y2 = aRect.GetBottom(); GRSRect( aClipBox, aDC, x1, y1, x2, y2, 0, aColor, aStyle ); } /* * Draw a rectangle (thick lines) in drawing space. */ void GRRect( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color ) { GRSRect( ClipBox, DC, x1, y1, x2, y2, width, Color ); } void GRRect( EDA_RECT* aClipBox, wxDC* aDC, const EDA_RECT& aRect, int aWidth, COLOR4D aColor ) { int x1 = aRect.GetX(); int y1 = aRect.GetY(); int x2 = aRect.GetRight(); int y2 = aRect.GetBottom(); GRSRect( aClipBox, aDC, x1, y1, x2, y2, aWidth, aColor ); } /* * Draw a rectangle (filled with AreaColor) in drawing space. */ void GRFilledRect( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, COLOR4D Color, COLOR4D BgColor ) { GRSFilledRect( ClipBox, DC, x1, y1, x2, y2, 0, Color, BgColor ); } /* * Draw a rectangle (filled with AreaColor) in drawing space. */ void GRFilledRect( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color, COLOR4D BgColor ) { GRSFilledRect( ClipBox, DC, x1, y1, x2, y2, width, Color, BgColor ); } /* * Draw a rectangle in screen space. */ void GRSRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2, int aWidth, COLOR4D aColor, wxPenStyle aStyle ) { wxPoint points[5]; points[0] = wxPoint(x1, y1); points[1] = wxPoint(x1, y2); points[2] = wxPoint(x2, y2); points[3] = wxPoint(x2, y1); points[4] = points[0]; GRSClosedPoly( aClipBox, aDC, 5, points, NOT_FILLED, aWidth, aColor, aColor ); } void GRSFilledRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2, int aWidth, COLOR4D aColor, COLOR4D aBgColor ) { wxPoint points[5]; points[0] = wxPoint(x1, y1); points[1] = wxPoint(x1, y2); points[2] = wxPoint(x2, y2); points[3] = wxPoint(x2, y1); points[4] = points[0]; GRSetBrush( aDC, aBgColor, FILLED ); GRSetColorPen( aDC, aBgColor, aWidth ); if( aClipBox && (aWidth > 0) ) { EDA_RECT clipbox(*aClipBox); clipbox.Inflate(aWidth); ClipAndDrawPoly(&clipbox, aDC, points, 5); // polygon approach is more accurate } else ClipAndDrawPoly(aClipBox, aDC, points, 5 ); } /** * Function ClipAndDrawPoly * Used to clip a polygon and draw it as Filled Polygon * uses the Sutherland and Hodgman algo to clip the given poly against a * rectangle. This rectangle is the drawing area this is useful under * Linux (2009) because filled polygons are incorrectly drawn if they have * too large coordinates (seems due to integer overflows in calculations) * Could be removed in some years, if become unnecessary. */ /* Note: aClipBox == NULL is legal, so if aClipBox == NULL, * the polygon is drawn, but not clipped */ #include void ClipAndDrawPoly( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aPoints[], int n ) { if( aClipBox == NULL ) { aDC->DrawPolygon( n, aPoints ); return; } // A clip box exists: clip and draw the polygon. static std::vector clippedPolygon; static pointVector inputPolygon, outputPolygon; inputPolygon.clear(); outputPolygon.clear(); clippedPolygon.clear(); for( int ii = 0; ii < n; ii++ ) inputPolygon.push_back( PointF( (REAL) aPoints[ii].x, (REAL) aPoints[ii].y ) ); RectF window( (REAL) aClipBox->GetX(), (REAL) aClipBox->GetY(), (REAL) aClipBox->GetWidth(), (REAL) aClipBox->GetHeight() ); SutherlandHodgman sh( window ); sh.Clip( inputPolygon, outputPolygon ); for( cpointIterator cit = outputPolygon.begin(); cit != outputPolygon.end(); ++cit ) { clippedPolygon.push_back( wxPoint( KiROUND( cit->X ), KiROUND( cit->Y ) ) ); } if( clippedPolygon.size() ) aDC->DrawPolygon( clippedPolygon.size(), &clippedPolygon[0] ); } void GRBezier( EDA_RECT* aClipBox, wxDC* aDC, std::vector& aPoint, int aWidth, COLOR4D aColor ) { std::vector output; BEZIER_POLY converter( aPoint ); converter.GetPoly( output, aWidth ); GRPoly( aClipBox, aDC, output.size(), &output[0], false, aWidth, aColor, aColor ); } void GRDrawAnchor( EDA_RECT *aClipBox, wxDC *aDC, int x, int y, int aSize, COLOR4D aColor ) { int anchor_size = aDC->DeviceToLogicalXRel( aSize ); GRLine( aClipBox, aDC, x - anchor_size, y, x + anchor_size, y, 0, aColor ); GRLine( aClipBox, aDC, x, y - anchor_size, x, y + anchor_size, 0, aColor ); } void GRDrawWrappedText( wxDC& aDC, wxString const& aText ) { wxStringTokenizer tokenizer( aText, " " ); wxSize const dc_size = aDC.GetSize(); wxSize const margin = aDC.GetTextExtent( " " ); std::vector lines; wxString line_accumulator; int total_height = 0; while( tokenizer.HasMoreTokens() ) { wxString word = tokenizer.GetNextToken(); wxSize linesize = aDC.GetTextExtent( line_accumulator + " " + word ); if( linesize.x >= dc_size.x - margin.x && !line_accumulator.IsEmpty() ) { lines.push_back( line_accumulator ); line_accumulator = word; } else { line_accumulator += " "; line_accumulator += word; } } if( !line_accumulator.IsEmpty() ) { lines.push_back( line_accumulator ); } for( auto const& line: lines ) { wxSize linesize = aDC.GetTextExtent( line ); total_height += linesize.y; } int top = ( dc_size.y - total_height ) / 2; int pos = top; for( auto const& line: lines ) { wxSize linesize = aDC.GetTextExtent( line ); aDC.DrawText( line, ( dc_size.x - linesize.x ) / 2, pos ); pos += linesize.y; } }