From f2c03f2b9703538d86f7d8c6ae4983501aeee889 Mon Sep 17 00:00:00 2001 From: charras Date: Fri, 20 Feb 2009 14:31:16 +0000 Subject: [PATCH] gr_basic.cpp now clips filled polygons,using the Sutherland and Hodgman algo to clip the poly against a rectangle Filled polygons are now correctly displayed under Linux(i hope) --- CHANGELOG.txt | 12 ++ common/gr_basic.cpp | 111 +++++++++--- common/makefile.gtk | 2 +- common/makefile.include | 2 +- libs.linux | 4 +- libs.win | 4 +- pcbnew/makefile.gtk | 8 +- polygon/SutherlandHodgmanClipPoly.h | 272 ++++++++++++++++++++++++++++ 8 files changed, 379 insertions(+), 36 deletions(-) create mode 100644 polygon/SutherlandHodgmanClipPoly.h diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 47c00fce4a..1fe4e0ed27 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -5,6 +5,18 @@ Started 2007-June-11 Please add newer entries at the top, list the date and your name with email address. +2009-Feb-17 UPDATE Jean-Pierre Charras +================================================================================ +++gr_basic.cpp + Added: Function ClipAndDrawFilledPoly() + Used to clip a polygon and display 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 unnecesary. + + 2009-Feb-17 UPDATE Dick Hollenbeck ================================================================================ ++pcbnew diff --git a/common/gr_basic.cpp b/common/gr_basic.cpp index 8c83310e10..393b0ac9ab 100644 --- a/common/gr_basic.cpp +++ b/common/gr_basic.cpp @@ -16,21 +16,27 @@ #endif /* Important Note: - * These drawing functions clip draw item before send these items to wxDC draw functions. - * For guy who aks why i did it, see a sample of problems encounted 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. + * These drawing functions clip draw item before send these items to wxDC draw functions. + * For guy who aks why i did it, see a sample of problems encounted 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 + * Currently (feb 2009) there are overflow problems when drawing solid (filled) polygons under linux without clipping * - * So before removing cliping 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 + * So before removing cliping 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 */ +#define USE_CLIP_FILLED_POLYGONS + +#ifdef USE_CLIP_FILLED_POLYGONS +void ClipAndDrawFilledPoly( EDA_Rect * ClipBox, wxDC * DC, wxPoint Points[], int n ); +#endif + /* global variables */ extern BASE_SCREEN* ActiveScreen; @@ -39,15 +45,15 @@ static int GRLastMoveToX, GRLastMoveToY; static int Text_Color = LIGHTGRAY; static int PenMinWidth = 1; /* largeur minimum de la plume (DOIT etre > 0) - * (utile pour trace sur imprimante) */ + * (utile pour trace sur imprimante) */ static int ForceBlackPen; /* si != 0 : traces en noir (utilise pour trace - * sur imprimante */ + * sur imprimante */ static int xcliplo = 0, - ycliplo = 0, - xcliphi = 2000, - ycliphi = 2000; /* coord de la surface de trace */ -static int lastcolor = -1; -static int lastwidth = -1; + ycliplo = 0, + xcliphi = 2000, + ycliphi = 2000; /* coord de la surface de trace */ +static int lastcolor = -1; +static int lastwidth = -1; static int s_Last_Pen_Style = -1; static wxDC* lastDC = NULL; @@ -74,10 +80,12 @@ static inline int USCALE( us arg, us num, us den ) } -static int inline ZoomValue( int val ) { +static int inline ZoomValue( int val ) +{ return ActiveScreen->Scale( val ); } + /****************************************/ /* External reference for the mappings. */ /****************************************/ @@ -355,11 +363,13 @@ bool GetGRForceBlackPenState( void ) void GRSetDrawMode( wxDC* DC, int draw_mode ) { if( draw_mode & GR_OR ) -#if defined (__WXMAC__) && wxMAC_USE_CORE_GRAPHICS +#if defined(__WXMAC__) && wxMAC_USE_CORE_GRAPHICS + DC->SetLogicalFunction( wxCOPY ); #else + DC->SetLogicalFunction( wxOR ); #endif else if( draw_mode & GR_XOR ) @@ -449,7 +459,7 @@ void GRSDashedLine( EDA_Rect* ClipBox, { GRLastMoveToX = x2; GRLastMoveToY = y2; - lastcolor = -1; + lastcolor = -1; GRSetColorPen( DC, Color, width, wxSHORT_DASH ); GRSLine( ClipBox, DC, x1, y1, x2, y2, width, Color ); lastcolor = -1; @@ -877,7 +887,16 @@ static void GRSPoly( EDA_Rect* ClipBox, wxDC* DC, int n, wxPoint Points[], bool if( Fill && ( n > 2 ) ) { GRSetBrush( DC, BgColor, FILLED ); - DC->DrawPolygon( n, Points ); + + + /* clip before send the filled polygon to wxDC, because under linux (GTK?) + * polygonsl having large coordinates are incorrectly drawn + */ +#ifdef USE_CLIP_FILLED_POLYGONS + ClipAndDrawFilledPoly( ClipBox, DC, Points, n ); +#else + DC->DrawPolygon( n, Points ); //does not work very well under linux +#endif } else { @@ -927,11 +946,11 @@ static void GRSClosedPoly( EDA_Rect* ClipBox, wxDC* DC, int aPointCount, wxPoint /* not used - * static void GRSClosedPoly( EDA_Rect* ClipBox, wxDC* DC, int n, wxPoint Points[], - * bool Fill, int Color, int BgColor ) - * { - * GRSClosedPoly( ClipBox, DC, n, Points, Fill, 0, Color, BgColor ); - * } + * static void GRSClosedPoly( EDA_Rect* ClipBox, wxDC* DC, int n, wxPoint Points[], + * bool Fill, int Color, int BgColor ) + * { + * GRSClosedPoly( ClipBox, DC, n, Points, Fill, 0, Color, BgColor ); + * } */ @@ -1531,3 +1550,41 @@ void GRSetTextBgColor( wxDC* DC, wxFont*, int Color ) ColorRefs[Color].m_Blue ) ); } + +#ifdef USE_CLIP_FILLED_POLYGONS + +/** Function ClipAndDrawFilledPoly + * 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 unnecesary. + */ +#include "SutherlandHodgmanClipPoly.h" +void ClipAndDrawFilledPoly( EDA_Rect* aClipBox, wxDC* aDC, wxPoint aPoints[], int n ) +{ + static 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( (int)round( cit->X ), (int)round( cit->Y ) ) ); + } + + if ( clippedPolygon.size() ) + aDC->DrawPolygon( clippedPolygon.size(), &clippedPolygon[0] ); +} +#endif diff --git a/common/makefile.gtk b/common/makefile.gtk index cddad4b949..d1c718db12 100644 --- a/common/makefile.gtk +++ b/common/makefile.gtk @@ -13,7 +13,7 @@ include makefile.include -include *.d # specfic Compiler flags and options -CPPFLAGS += $(EXTRACPPFLAGS) -I./ -I../include -I$(BOOST) +CPPFLAGS += $(EXTRACPPFLAGS) -I./ -I../include EDACPPFLAGS = $(CPPFLAGS) diff --git a/common/makefile.include b/common/makefile.include index 64ccf591b9..9303263a1f 100644 --- a/common/makefile.include +++ b/common/makefile.include @@ -1,4 +1,4 @@ -EXTRACPPFLAGS += -I$(SYSINCLUDE) -I./ -Ibitmaps -I../include +EXTRACPPFLAGS += -I$(SYSINCLUDE) -I./ -Ibitmaps -I../include -I../polygon COMMON = ../include/colors.h diff --git a/libs.linux b/libs.linux index 440581c379..761a0e5230 100644 --- a/libs.linux +++ b/libs.linux @@ -87,6 +87,8 @@ ifdef USE_MATCH_LAYER CPPFLAGS += -DUSE_MATCH_LAYER endif +CPPFLAGS += -I $(BOOST_LIB) + ifdef KICAD_PYTHON PYTHON_VERSION=2.5 PYLIBS= -L/usr/lib @@ -100,7 +102,7 @@ endif MESALIBSPATH = /usr/local/lib # Boost headers (location of boost/) -BOOST = ../ +BOOST_LIB = ../ #for static link: add wx gl lib LIBVERSION=`wx-config --release` diff --git a/libs.win b/libs.win index 00f660d378..85581d5c3b 100644 --- a/libs.win +++ b/libs.win @@ -29,7 +29,7 @@ endif endif LIBVERSION = 2.8 -BOOST_PATH=/f/boost/boost +BOOST_LIB=/f/boost/boost # You must comment or uncomment this line to disable/enable python support @@ -45,7 +45,7 @@ ALL_LDFLAGS = -s #-v FINAL = 1 endif -CPPFLAGS += -I $(BOOST_PATH) +CPPFLAGS += -I $(BOOST_LIB) ALL_CPPFLAGS = `$(WXWIN)/wx-config --cppflags` $(CPPFLAGS) EDACPPFLAGS = $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) $(EXTRACPPFLAGS) diff --git a/pcbnew/makefile.gtk b/pcbnew/makefile.gtk index 964ff168e4..4cf45172da 100644 --- a/pcbnew/makefile.gtk +++ b/pcbnew/makefile.gtk @@ -14,15 +14,15 @@ include makefile.include -include *.d # Additional compiler flags -CPPFLAGS += $(EXTRACPPFLAGS) -I$(BOOST_LIB) -EDACPPFLAGS = $(CPPFLAGS) $(BOOST_LIB) +CPPFLAGS += $(EXTRACPPFLAGS) +EDACPPFLAGS = $(CPPFLAGS) $(TARGET): $(OBJECTS) makefile.gtk makefile.include $(LIBVIEWER3D) $(EXTRALIBS) ../libs.linux $(LD) $(LDFLAGS) $(OBJECTS) $(LIBVIEWER3D) $(LIBS_WITH_GL)\ -o $(TARGET) - - + + install: $(TARGET) mkdir -p $(KICAD_BIN) cp $(TARGET) $(KICAD_BIN) diff --git a/polygon/SutherlandHodgmanClipPoly.h b/polygon/SutherlandHodgmanClipPoly.h new file mode 100644 index 0000000000..4e0378448b --- /dev/null +++ b/polygon/SutherlandHodgmanClipPoly.h @@ -0,0 +1,272 @@ +/******************************************************************************** +* Copyright (C) 2004 Sjaak Priester +* +* This 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 2 of the License, or +* (at your option) any later version. +* +* This file 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 Tinter; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +********************************************************************************/ + +// SutherlandHodgman +// Class to perform polygon clipping against an upright rectangular boundary window. +// Implementation of Sutherland-Hodgman algorithm (1974). +// +// Version 1.0 (C) 2004, Sjaak Priester, Amsterdam. +// mailto:sjaak@sjaakpriester.nl +// http://www.sjaakpriester.nl + +#ifndef __SUTHERLAND_HODGMAN_H__ +#define __SUTHERLAND_HODGMAN_H__ + + +#include +#include + +using namespace std; +#ifndef _GDIPLUS_H + +// I designed this with GDI+ in mind. However, this particular code doesn't +// use GDI+ at all, only some of it's variable types. +// These definitions are substitutes for those of GDI+. +typedef double REAL; +class PointF +{ +public: + REAL X; + REAL Y; + + PointF() : X( 0 ) + , Y( 0 ) { } + PointF( const PointF& p ) : X( p.X ) + , Y( p.Y ) { } + PointF( REAL x, REAL y ) : X( x ) + , Y( y ) { } + PointF operator+( const PointF& p ) const { return PointF( X + p.X, Y + p.Y ); } + PointF operator-( const PointF& p ) const { return PointF( X - p.X, Y - p.Y ); } + bool Equals( const PointF& p ) { return (X == p.X) && (Y == p.Y); } +}; + +class RectF +{ +public: + REAL X; + REAL Y; + REAL Width; + REAL Height; + + RectF() { X = 0, Y = 0, Height = 0, Width = 0; } + RectF( const RectF& r ) + { + X = r.X; Y = r.Y; Height = r.Height, Width = r.Width; + } + + + RectF( REAL x, REAL y, REAL w, REAL h ) : X( x ), Y( y ),Width( w ), Height( h ) + { } + REAL GetLeft() const { return X; } + REAL GetTop() const { return Y; } + REAL GetRight() const { return X + Width; } + REAL GetBottom() const { return Y + Height; } +}; + +#endif // _GDIPLUS_H + +typedef vector pointVector; +typedef vector::iterator pointIterator; +typedef vector::const_iterator cpointIterator; + +class SutherlandHodgman +{ +public: + + // Constructor. Parameter is the boundary rectangle. + // SutherlandHodgman expects a 'normalized' boundary rectangle, meaning + // that boundaries.GetRight() > boundaries.GetLeft() and + // boundaries.GetBottom() > boundaries.GetTop(). + // In other words: boundary.Width > 0 and boundaries.Height > 0. + // If this is violated, nothing will be output. + SutherlandHodgman( RectF& boundaries ) : + m_stageBottom( m_stageOut, boundaries.GetBottom() ) + , /* Initialize each stage */ m_stageLeft( m_stageBottom, boundaries.GetLeft() ) + , /* with its next stage and */ m_stageTop( m_stageLeft, boundaries.GetTop() ) + , /* the boundary position. */ m_stageRight( m_stageTop, boundaries.GetRight() ) + { + } + + + void Clip( pointVector& input, pointVector& clipped ) + { + clipped.clear(); + m_stageOut.SetDestination( &clipped ); + + // Clip each input vertex. + for( cpointIterator it = input.begin(); it != input.end(); ++it ) + m_stageRight.HandleVertex( *it ); + + // Do the final step. + m_stageRight.Finalize(); + } + + +private: + + // Implementation of a horizontal boundary (top or bottom). + // Comp is a std::binary_function object, comparing its two parameters, f.i. std::less. + + template + + class BoundaryHor + { +public: + BoundaryHor( REAL y ) : m_Y( y ) { } + bool IsInside( const PointF& pnt ) const + { + return Comp ()( pnt.Y, m_Y ); + } // return true if pnt.Y is at the inside of the boundary + PointF Intersect( const PointF& p0, const PointF& p1 ) const // return intersection point of line p0...p1 with boundary + { // assumes p0...p1 is not strictly horizontal + PointF d = p1 - p0; + REAL xslope = d.X / d.Y; + + PointF r; + + r.Y = m_Y; + r.X = p0.X + xslope * (m_Y - p0.Y); + return r; + } + + +private: + REAL m_Y; + }; + + // Implementation of a vertical boundary (left or right). + template + class BoundaryVert + { +public: + BoundaryVert( REAL x ) : m_X( x ) + { } + bool IsInside( const PointF& pnt ) const + { + return Comp() ( pnt.X, m_X ); + } + PointF Intersect( const PointF& p0, const PointF& p1 ) const // assumes p0...p1 is not strictly vertical + { + PointF d = p1 - p0; + REAL yslope = d.Y / d.X; + + PointF r; + + r.X = m_X; + r.Y = p0.Y + yslope * (m_X - p0.X); + return r; + } + + +private: + REAL m_X; + }; + + // This template class is the workhorse of the algorithm. It handles the clipping against one boundary. + // Boundary is either BoundaryHor or BoundaryVert, Stage is the next ClipStage, or the output stage. + template + class ClipStage : private Boundary + { +public: + ClipStage( Stage& nextStage, REAL position ) : + Boundary( position ) , m_NextStage( nextStage ), m_bFirst( true ) + { } + void HandleVertex( const PointF& pntCurrent ) // Function to handle one vertex + { + bool bCurrentInside = this->IsInside( pntCurrent ); // See if vertex is inside the boundary. + + if( m_bFirst ) // If this is the first vertex... + { + m_pntFirst = pntCurrent; // ... just remember it,... + + m_bFirst = false; + } + else // Common cases, not the first vertex. + { + if( bCurrentInside ) // If this vertex is inside... + { + if( !m_bPreviousInside ) // ... and the previous one was outside + m_NextStage.HandleVertex( this->Intersect( m_pntPrevious, pntCurrent ) ); + + // ... first output the intersection point. + + m_NextStage.HandleVertex( pntCurrent ); // Output the current vertex. + } + else if( m_bPreviousInside ) // If this vertex is outside, and the previous one was inside... + m_NextStage.HandleVertex( this->Intersect( m_pntPrevious, pntCurrent ) ); + + // ... output the intersection point. + + // If neither current vertex nor the previous one are inside, output nothing. + } + m_pntPrevious = pntCurrent; // Be prepared for next vertex. + m_bPreviousInside = bCurrentInside; + } + + + void Finalize() + { + HandleVertex( m_pntFirst ); // Close the polygon. + m_NextStage.Finalize(); // Delegate to the next stage. + } + + +private: + Stage& m_NextStage; // the next stage + bool m_bFirst; // true if no vertices have been handled + PointF m_pntFirst; // the first vertex + PointF m_pntPrevious; // the previous vertex + bool m_bPreviousInside; // true if the previous vertex was inside the Boundary + }; + + class OutputStage + { +public: + OutputStage() : m_pDest( 0 ) { } + void SetDestination( pointVector* pDest ) { m_pDest = pDest; } + void HandleVertex( const PointF& pnt ) { m_pDest->push_back( pnt ); } // Append the vertex to the output container. + void Finalize() { } // Do nothing. +private: + pointVector* m_pDest; + }; + + // These typedefs define the four boundaries. In keeping up with the GDI/GDI+ interpretation of + // rectangles, we include the left and top boundaries, but not the right and bottom boundaries. + // In other words: a vertex on the left boundary is considered to be inside, but a vertex + // on the right boundary is considered to be outside. + typedef BoundaryVert < less > BoundaryRight; + typedef BoundaryHor < greater_equal > BoundaryTop; + typedef BoundaryVert < greater_equal > BoundaryLeft; + typedef BoundaryHor < less > BoundaryBottom; + + // Next typedefs define the four stages. First template parameter is the boundary, + // second template parameter is the next stage. + typedef ClipStage ClipBottom; + typedef ClipStage ClipLeft; + typedef ClipStage ClipTop; + typedef ClipStage ClipRight; + + // Our data members. + OutputStage m_stageOut; + ClipBottom m_stageBottom; + ClipLeft m_stageLeft; + ClipTop m_stageTop; + ClipRight m_stageRight; +}; + +#endif