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)
This commit is contained in:
charras 2009-02-20 14:31:16 +00:00
parent e1fb4c786f
commit f2c03f2b97
8 changed files with 379 additions and 36 deletions

View File

@ -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 <jean-pierre.charras@inpg.fr>
================================================================================
++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 <dick@softplc.com>
================================================================================
++pcbnew

View File

@ -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<wxPoint> 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

View File

@ -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)

View File

@ -1,4 +1,4 @@
EXTRACPPFLAGS += -I$(SYSINCLUDE) -I./ -Ibitmaps -I../include
EXTRACPPFLAGS += -I$(SYSINCLUDE) -I./ -Ibitmaps -I../include -I../polygon
COMMON = ../include/colors.h

View File

@ -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`

View File

@ -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)

View File

@ -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)

View File

@ -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 <vector>
#include <functional>
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<PointF> pointVector;
typedef vector<PointF>::iterator pointIterator;
typedef vector<PointF>::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 Comp>
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 Comp>
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 Boundary, class Stage>
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<REAL> > BoundaryRight;
typedef BoundaryHor < greater_equal<REAL> > BoundaryTop;
typedef BoundaryVert < greater_equal<REAL> > BoundaryLeft;
typedef BoundaryHor < less<REAL> > BoundaryBottom;
// Next typedefs define the four stages. First template parameter is the boundary,
// second template parameter is the next stage.
typedef ClipStage<BoundaryBottom, OutputStage> ClipBottom;
typedef ClipStage<BoundaryLeft, ClipBottom> ClipLeft;
typedef ClipStage<BoundaryTop, ClipLeft> ClipTop;
typedef ClipStage<BoundaryRight, ClipTop> ClipRight;
// Our data members.
OutputStage m_stageOut;
ClipBottom m_stageBottom;
ClipLeft m_stageLeft;
ClipTop m_stageTop;
ClipRight m_stageRight;
};
#endif