Add line style drawing.

This commit is contained in:
Jeff Young 2018-09-07 22:33:01 +01:00
parent 808beed191
commit fee52e127f
6 changed files with 189 additions and 117 deletions

View File

@ -27,6 +27,7 @@
* @brief a few functions useful in geometry calculations.
*/
#include <eda_rect.h>
#include <geometry/geometry_utils.h>
int GetArcToSegmentCount( int aRadius, int aErrorMax, double aArcAngleDegree )
@ -61,3 +62,87 @@ double GetCircletoPolyCorrectionFactor( int aSegCountforCircle )
return 1.0 / cos( M_PI / aSegCountforCircle );
}
/***
* Utility for the line clipping code, returns the boundary code of
* a point. Bit allocation is arbitrary
*/
inline int clipOutCode( const EDA_RECT *aClipBox, int x, int y )
{
int code;
if( y < aClipBox->GetY() )
code = 2;
else if( y > aClipBox->GetBottom() )
code = 1;
else
code = 0;
if( x < aClipBox->GetX() )
code |= 4;
else if( x > aClipBox->GetRight() )
code |= 8;
return code;
}
bool ClipLine( const EDA_RECT *aClipBox, int &x1, int &y1, int &x2, int &y2 )
{
// Stock Cohen-Sutherland algorithm; check *any* CG book for details
int outcode1 = clipOutCode( aClipBox, x1, y1 );
int outcode2 = clipOutCode( aClipBox, x2, y2 );
while( outcode1 || outcode2 )
{
// Fast reject
if( outcode1 & outcode2 )
return true;
// Choose a side to clip
int thisoutcode, x, y;
if( outcode1 )
thisoutcode = outcode1;
else
thisoutcode = outcode2;
/* One clip round
* Since we use the full range of 32 bit ints, the proportion
* computation has to be done in 64 bits to avoid horrible
* results */
if( thisoutcode & 1 ) // Clip the bottom
{
y = aClipBox->GetBottom();
x = x1 + (x2 - x1) * int64_t(y - y1) / (y2 - y1);
}
else if( thisoutcode & 2 ) // Clip the top
{
y = aClipBox->GetY();
x = x1 + (x2 - x1) * int64_t(y - y1) / (y2 - y1);
}
else if( thisoutcode & 8 ) // Clip the right
{
x = aClipBox->GetRight();
y = y1 + (y2 - y1) * int64_t(x - x1) / (x2 - x1);
}
else // if( thisoutcode & 4), obviously, clip the left
{
x = aClipBox->GetX();
y = y1 + (y2 - y1) * int64_t(x - x1) / (x2 - x1);
}
// Put the result back and update the boundary code
// No ambiguity, otherwise it would have been a fast reject
if( thisoutcode == outcode1 )
{
x1 = x;
y1 = y;
outcode1 = clipOutCode( aClipBox, x1, y1 );
}
else
{
x2 = x;
y2 = y;
outcode2 = clipOutCode( aClipBox, x2, y2 );
}
}
return false;
}

View File

@ -35,6 +35,7 @@
#include <math_for_graphics.h>
#include <wx/graphics.h>
#include <wx/tokenzr.h>
#include <geometry/geometry_utils.h>
#if defined(__WXMAC__) && defined(USE_WX_GRAPHICS_CONTEXT)
#include <wx/dcgraph.h>
#endif
@ -99,102 +100,6 @@ static COLOR4D s_DC_lastbrushcolor( 0, 0, 0, 0 );
static bool s_DC_lastbrushfill = false;
static wxDC* s_DC_lastDC = NULL;
/***
* Utility for the line clipping code, returns the boundary code of
* a point. Bit allocation is arbitrary
*/
static inline int clipOutCode( const EDA_RECT *aClipBox, int x, int y )
{
int code;
if( y < aClipBox->GetY() )
code = 2;
else if( y > aClipBox->GetBottom() )
code = 1;
else
code = 0;
if( x < aClipBox->GetX() )
code |= 4;
else if( x > aClipBox->GetRight() )
code |= 8;
return code;
}
/**
* Test if any part of a line falls within the bounds of a rectangle.
*
* Please note that this is only accurate for lines that are one pixel wide.
*
* @param aClipBox - The rectangle to test.
* @param x1 - X coordinate of one end of a line.
* @param y1 - Y coordinate of one end of a line.
* @param x2 - X coordinate of the other end of a line.
* @param y2 - Y coordinate of the other end of a line.
*
* @return - False if any part of the line lies within the rectangle.
*/
static bool clipLine( const EDA_RECT *aClipBox, int &x1, int &y1, int &x2, int &y2 )
{
// Stock Cohen-Sutherland algorithm; check *any* CG book for details
int outcode1 = clipOutCode( aClipBox, x1, y1 );
int outcode2 = clipOutCode( aClipBox, x2, y2 );
while( outcode1 || outcode2 )
{
// Fast reject
if( outcode1 & outcode2 )
return true;
// Choose a side to clip
int thisoutcode, x, y;
if( outcode1 )
thisoutcode = outcode1;
else
thisoutcode = outcode2;
/* One clip round
* Since we use the full range of 32 bit ints, the proportion
* computation has to be done in 64 bits to avoid horrible
* results */
if( thisoutcode & 1 ) // Clip the bottom
{
y = aClipBox->GetBottom();
x = x1 + (x2 - x1) * int64_t(y - y1) / (y2 - y1);
}
else if( thisoutcode & 2 ) // Clip the top
{
y = aClipBox->GetY();
x = x1 + (x2 - x1) * int64_t(y - y1) / (y2 - y1);
}
else if( thisoutcode & 8 ) // Clip the right
{
x = aClipBox->GetRight();
y = y1 + (y2 - y1) * int64_t(x - x1) / (x2 - x1);
}
else // if( thisoutcode & 4), obviously, clip the left
{
x = aClipBox->GetX();
y = y1 + (y2 - y1) * int64_t(x - x1) / (x2 - x1);
}
// Put the result back and update the boundary code
// No ambiguity, otherwise it would have been a fast reject
if( thisoutcode == outcode1 )
{
x1 = x;
y1 = y;
outcode1 = clipOutCode( aClipBox, x1, y1 );
}
else
{
x2 = x;
y2 = y;
outcode2 = clipOutCode( aClipBox, x2, y2 );
}
}
return false;
}
static void WinClipAndDrawLine( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width )
{
GRLastMoveToX = x2;
@ -204,7 +109,7 @@ static void WinClipAndDrawLine( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int
{
EDA_RECT clipbox(*ClipBox);
clipbox.Inflate(width/2);
if( clipLine( &clipbox, x1, y1, x2, y2 ) )
if( ClipLine( &clipbox, x1, y1, x2, y2 ) )
return;
}
@ -478,7 +383,7 @@ void GRLineArray( EDA_RECT* aClipBox, wxDC* aDC, std::vector<wxPoint>& aLines,
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 ) )
if( ( aClipBox == NULL ) || !ClipLine( aClipBox, x1, y1, x2, y2 ) )
{
path.MoveToPoint( x1, y1 );
path.AddLineToPoint( x2, y2 );
@ -496,7 +401,7 @@ void GRLineArray( EDA_RECT* aClipBox, wxDC* aDC, std::vector<wxPoint>& aLines,
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 ) )
if( ( aClipBox == NULL ) || !ClipLine( aClipBox, x1, y1, x2, y2 ) )
aDC->DrawLine( x1, y1, x2, y2 );
}
}
@ -518,7 +423,7 @@ void GRCSegm( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2,
EDA_RECT clipbox(*ClipBox);
clipbox.Inflate(width/2);
if( clipLine( &clipbox, x1, y1, x2, y2 ) )
if( ClipLine( &clipbox, x1, y1, x2, y2 ) )
return;
}

View File

@ -48,7 +48,7 @@
#include <base_screen.h>
#include <draw_graphic_text.h>
#include <geometry/shape_line_chain.h>
#include <geometry/geometry_utils.h>
PLOTTER::PLOTTER( )
{
@ -65,9 +65,6 @@ PLOTTER::PLOTTER( )
// Temporary init to avoid not initialized vars, will be set later
m_IUsPerDecimil = 1; // will be set later to the actual value
iuPerDeviceUnit = 1; // will be set later to the actual value
m_dotMarkLength_mm = 0.1; // Dotted line parameter in mm: segment
// Dashed line parameter is 5 * dotted line mark
// Dashed line gap is 3 * dotted line mark
}
PLOTTER::~PLOTTER()
@ -134,24 +131,26 @@ double PLOTTER::userToDeviceSize( double size ) const
}
#define IU_PER_MILS ( m_IUsPerDecimil * 10 )
double PLOTTER::GetDotMarkLenIU() const
{
return userToDeviceSize( std::max( 1.0,
m_dotMarkLength_mm * 10000 / 25.4 * m_IUsPerDecimil - GetCurrentLineWidth() ) );
return userToDeviceSize( DOT_MARK_LEN( GetCurrentLineWidth() ) );
}
double PLOTTER::GetDashMarkLenIU() const
{
return std::max( GetDashGapLenIU(), 5.0 * GetDotMarkLenIU() );
return userToDeviceSize( DASH_MARK_LEN( GetCurrentLineWidth() ) );
}
double PLOTTER::GetDashGapLenIU() const
{
return 3.0 * GetDotMarkLenIU() + userToDeviceSize( 2 * GetCurrentLineWidth() );
return userToDeviceSize( DASH_GAP_LEN( GetCurrentLineWidth() ) );
}
void PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius,
FILL_T fill, int width )
{

View File

@ -44,9 +44,9 @@
#include <sch_bus_entry.h>
#include <sch_bitmap.h>
#include <draw_graphic_text.h>
#include <geometry/geometry_utils.h>
#include <lib_edit_frame.h>
#include <plotter.h>
#include <template_fieldnames.h>
#include <class_libentry.h>
#include <class_library.h>
@ -57,8 +57,6 @@
#include "sch_painter.h"
#include <draw_graphic_text.h>
namespace KIGFX
{
@ -899,10 +897,58 @@ void SCH_PAINTER::draw( SCH_LINE *aLine, int aLayer )
int width = aLine->GetPenSize();
m_gal->SetIsStroke(true);
m_gal->SetIsStroke( true );
m_gal->SetStrokeColor(color);
m_gal->SetLineWidth( width );
if( aLine->GetLineStyle() <= PLOTDASHTYPE_SOLID )
{
m_gal->DrawLine( aLine->GetStartPoint(), aLine->GetEndPoint() );
}
else
{
VECTOR2D start = aLine->GetStartPoint();
VECTOR2D end = aLine->GetEndPoint();
EDA_RECT clip( wxPoint( start.x, start.y ), wxSize( end.x - start.x, end.y - start.y ) );
clip.Normalize();
double theta = atan2( end.y - start.y, end.x - start.x );
double strokes[] = { 1.0, DASH_GAP_LEN( width ), 1.0, DASH_GAP_LEN( width ) };
switch( aLine->GetLineStyle() )
{
case PLOTDASHTYPE_DASH:
strokes[0] = strokes[2] = DASH_MARK_LEN( width );
break;
case PLOTDASHTYPE_DOT:
strokes[0] = strokes[2] = DOT_MARK_LEN( width );
break;
case PLOTDASHTYPE_DASHDOT:
strokes[0] = DASH_MARK_LEN( width );
strokes[2] = DOT_MARK_LEN( width );
break;
}
for( size_t i = 0; i < 100000; ++i )
{
// Calculations MUST be done in doubles to keep from accumulating rounding
// errors as we go.
VECTOR2D next( start.x + strokes[ i % 4 ] * cos( theta ),
start.y + strokes[ i % 4 ] * sin( theta ) );
// Drawing each segment can be done rounded.
wxPoint segStart( KiROUND( start.x ), KiROUND( start.y ) );
wxPoint segEnd( KiROUND( next.x ), KiROUND( next.y ) );
if( ClipLine( &clip, segStart.x, segStart.y, segEnd.x, segEnd.y ) )
break;
else if( i % 2 == 0 )
m_gal->DrawLine( segStart, segEnd );
start = next;
}
}
if( aLine->IsStartDangling() )
drawDanglingSymbol( m_gal, aLine->GetStartPoint());

View File

@ -32,6 +32,9 @@
#include <math/vector2d.h>
class EDA_RECT;
/**
* @return the number of segments to approximate a arc by segments
* with a given max error (this number is >= 1)
@ -95,5 +98,42 @@ VECTOR2<T> GetVectorSnapped45( const VECTOR2<T>& aVec )
return newVec;
}
/**
* Test if any part of a line falls within the bounds of a rectangle.
*
* Please note that this is only accurate for lines that are one pixel wide.
*
* @param aClipBox - The rectangle to test.
* @param x1 - X coordinate of one end of a line.
* @param y1 - Y coordinate of one end of a line.
* @param x2 - X coordinate of the other end of a line.
* @param y2 - Y coordinate of the other end of a line.
*
* @return - False if any part of the line lies within the rectangle.
*/
bool ClipLine( const EDA_RECT *aClipBox, int &x1, int &y1, int &x2, int &y2 );
/**
* Dashed and dotted line patterns.
*
* Note: these are all macros because they're included from files with different
* IU definitions.
*/
#define DOT_WIDTH_MILS 0.0254
#define DOT_MARK_LEN( aLineWidth ) \
( std::max( 1.0, IU_PER_MILS * DOT_WIDTH_MILS - aLineWidth ) )
#define DASH_GAP_LEN( aLineWidth ) \
( 3.0 * DOT_MARK_LEN( aLineWidth ) + ( 2.0 * aLineWidth ) )
#define DASH_MARK_LEN( aLineWidth ) \
( std::max( DASH_GAP_LEN( aLineWidth ), 5.0 * DOT_MARK_LEN( aLineWidth ) ) )
#endif // #ifndef GEOMETRY_UTILS_H

View File

@ -96,9 +96,6 @@ enum PlotDashType {
*/
class PLOTTER
{
private:
double m_dotMarkLength_mm ; ///< Dotted line parameter in mm: segment
public:
// These values are used as flag for pen or aperture selection
static const int DO_NOT_SET_LINE_WIDTH = -2; // Skip selection