CPolyLine -> SHAPE_POLY_SET refactor.
Removes the need of using the legacy code in polygon/PolyLine.{h,cpp}, refactoring all CPolyLine instances with SHAPE_POLY_SET instances. The remaining legacy methods have been ported to SHAPE_POLY_SET; mainly: Chamfer, Fillet, {,Un}Hatch. The iteration over the polygon vertices have been simplified using the family of ITERATOR classes.
This commit is contained in:
parent
5aa1610362
commit
f68ce306bd
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 CERN
|
||||
* Copyright (C) 2015-2017 CERN
|
||||
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
|
||||
*
|
||||
* Point in polygon algorithm adapted from Clipper Library (C) Angus Johnson,
|
||||
* subject to Clipper library license.
|
||||
|
@ -32,6 +33,8 @@
|
|||
#include <list>
|
||||
#include <algorithm>
|
||||
|
||||
#include <common.h>
|
||||
|
||||
#include <geometry/shape.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
#include <geometry/shape_poly_set.h>
|
||||
|
@ -41,7 +44,12 @@ using namespace ClipperLib;
|
|||
SHAPE_POLY_SET::SHAPE_POLY_SET() :
|
||||
SHAPE( SH_POLY_SET )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
SHAPE_POLY_SET::SHAPE_POLY_SET( const SHAPE_POLY_SET& aOther ) :
|
||||
SHAPE( SH_POLY_SET ), m_polys( aOther.m_polys )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
@ -50,6 +58,95 @@ SHAPE_POLY_SET::~SHAPE_POLY_SET()
|
|||
}
|
||||
|
||||
|
||||
SHAPE* SHAPE_POLY_SET::Clone() const
|
||||
{
|
||||
return new SHAPE_POLY_SET( *this );
|
||||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::GetRelativeIndices( int aGlobalIdx,
|
||||
SHAPE_POLY_SET::VERTEX_INDEX* aRelativeIndices ) const
|
||||
{
|
||||
int polygonIdx = 0;
|
||||
unsigned int contourIdx = 0;
|
||||
int vertexIdx = 0;
|
||||
|
||||
int currentGlobalIdx = 0;
|
||||
|
||||
for( polygonIdx = 0; polygonIdx < OutlineCount(); polygonIdx++ )
|
||||
{
|
||||
const POLYGON currentPolygon = CPolygon( polygonIdx );
|
||||
|
||||
for( contourIdx = 0; contourIdx < currentPolygon.size(); contourIdx++ )
|
||||
{
|
||||
SHAPE_LINE_CHAIN currentContour = currentPolygon[contourIdx];
|
||||
int totalPoints = currentContour.PointCount();
|
||||
|
||||
for( vertexIdx = 0; vertexIdx < totalPoints; vertexIdx++ )
|
||||
{
|
||||
// Check if the current vertex is the globally indexed as aGlobalIdx
|
||||
if( currentGlobalIdx == aGlobalIdx )
|
||||
{
|
||||
aRelativeIndices->m_polygon = polygonIdx;
|
||||
aRelativeIndices->m_contour = contourIdx;
|
||||
aRelativeIndices->m_vertex = vertexIdx;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Advance
|
||||
currentGlobalIdx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::GetGlobalIndex( SHAPE_POLY_SET::VERTEX_INDEX aRelativeIndices,
|
||||
int& aGlobalIdx )
|
||||
{
|
||||
int selectedVertex = aRelativeIndices.m_vertex;
|
||||
unsigned int selectedContour = aRelativeIndices.m_contour;
|
||||
unsigned int selectedPolygon = aRelativeIndices.m_polygon;
|
||||
|
||||
// Check whether the vertex indices make sense in this poly set
|
||||
if( selectedPolygon < m_polys.size() && selectedContour < m_polys[selectedPolygon].size() &&
|
||||
selectedVertex < m_polys[selectedPolygon][selectedContour].PointCount() )
|
||||
{
|
||||
POLYGON currentPolygon;
|
||||
|
||||
aGlobalIdx = 0;
|
||||
|
||||
for( unsigned int polygonIdx = 0; polygonIdx < selectedPolygon; polygonIdx++ )
|
||||
{
|
||||
currentPolygon = Polygon( polygonIdx );
|
||||
|
||||
for( unsigned int contourIdx = 0; contourIdx < currentPolygon.size(); contourIdx++ )
|
||||
{
|
||||
aGlobalIdx += currentPolygon[contourIdx].PointCount();
|
||||
}
|
||||
}
|
||||
|
||||
currentPolygon = Polygon( selectedPolygon );
|
||||
|
||||
for( unsigned int contourIdx = 0; contourIdx < selectedContour; contourIdx ++ )
|
||||
{
|
||||
aGlobalIdx += currentPolygon[contourIdx].PointCount();
|
||||
}
|
||||
|
||||
aGlobalIdx += selectedVertex;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int SHAPE_POLY_SET::NewOutline()
|
||||
{
|
||||
SHAPE_LINE_CHAIN empty_path;
|
||||
|
@ -66,13 +163,18 @@ int SHAPE_POLY_SET::NewHole( int aOutline )
|
|||
SHAPE_LINE_CHAIN empty_path;
|
||||
empty_path.SetClosed( true );
|
||||
|
||||
m_polys.back().push_back( empty_path );
|
||||
// Default outline is the last one
|
||||
if( aOutline < 0 )
|
||||
aOutline += m_polys.size();
|
||||
|
||||
// Add hole to the selected outline
|
||||
m_polys[aOutline].push_back( empty_path );
|
||||
|
||||
return m_polys.back().size() - 2;
|
||||
}
|
||||
|
||||
|
||||
int SHAPE_POLY_SET::Append( int x, int y, int aOutline, int aHole )
|
||||
int SHAPE_POLY_SET::Append( int x, int y, int aOutline, int aHole, bool aAllowDuplication )
|
||||
{
|
||||
if( aOutline < 0 )
|
||||
aOutline += m_polys.size();
|
||||
|
@ -87,12 +189,34 @@ int SHAPE_POLY_SET::Append( int x, int y, int aOutline, int aHole )
|
|||
assert( aOutline < (int)m_polys.size() );
|
||||
assert( idx < (int)m_polys[aOutline].size() );
|
||||
|
||||
m_polys[aOutline][idx].Append( x, y );
|
||||
m_polys[aOutline][idx].Append( x, y, aAllowDuplication );
|
||||
|
||||
return m_polys[aOutline][idx].PointCount();
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::InsertVertex( int aGlobalIndex, VECTOR2I aNewVertex )
|
||||
{
|
||||
VERTEX_INDEX index;
|
||||
|
||||
if( aGlobalIndex < 0 )
|
||||
aGlobalIndex = 0;
|
||||
|
||||
if( aGlobalIndex >= TotalVertices() ){
|
||||
Append( aNewVertex );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Assure the position to be inserted exists; throw an exception otherwise
|
||||
if( GetRelativeIndices( aGlobalIndex, &index ) )
|
||||
m_polys[index.m_polygon][index.m_contour].Insert( index.m_vertex, aNewVertex );
|
||||
else
|
||||
throw( std::out_of_range( "aGlobalIndex-th vertex does not exist" ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
int SHAPE_POLY_SET::VertexCount( int aOutline , int aHole ) const
|
||||
{
|
||||
if( aOutline < 0 )
|
||||
|
@ -112,26 +236,22 @@ int SHAPE_POLY_SET::VertexCount( int aOutline , int aHole ) const
|
|||
}
|
||||
|
||||
|
||||
const VECTOR2I& SHAPE_POLY_SET::CVertex( int index, int aOutline , int aHole ) const
|
||||
SHAPE_POLY_SET SHAPE_POLY_SET::Subset( int aFirstPolygon, int aLastPolygon )
|
||||
{
|
||||
if( aOutline < 0 )
|
||||
aOutline += m_polys.size();
|
||||
assert( aFirstPolygon >= 0 && aLastPolygon <= OutlineCount() );
|
||||
|
||||
int idx;
|
||||
SHAPE_POLY_SET newPolySet;
|
||||
|
||||
if( aHole < 0 )
|
||||
idx = 0;
|
||||
else
|
||||
idx = aHole + 1;
|
||||
for( int index = aFirstPolygon; index < aLastPolygon; index++ )
|
||||
{
|
||||
newPolySet.m_polys.push_back( Polygon( index ) );
|
||||
}
|
||||
|
||||
assert( aOutline < (int)m_polys.size() );
|
||||
assert( idx < (int)m_polys[aOutline].size() );
|
||||
|
||||
return m_polys[aOutline][idx].CPoint( index );
|
||||
return newPolySet;
|
||||
}
|
||||
|
||||
|
||||
VECTOR2I& SHAPE_POLY_SET::Vertex( int index, int aOutline , int aHole )
|
||||
VECTOR2I& SHAPE_POLY_SET::Vertex( int aIndex, int aOutline, int aHole )
|
||||
{
|
||||
if( aOutline < 0 )
|
||||
aOutline += m_polys.size();
|
||||
|
@ -146,7 +266,116 @@ VECTOR2I& SHAPE_POLY_SET::Vertex( int index, int aOutline , int aHole )
|
|||
assert( aOutline < (int)m_polys.size() );
|
||||
assert( idx < (int)m_polys[aOutline].size() );
|
||||
|
||||
return m_polys[aOutline][idx].Point( index );
|
||||
return m_polys[aOutline][idx].Point( aIndex );
|
||||
}
|
||||
|
||||
|
||||
const VECTOR2I& SHAPE_POLY_SET::CVertex( int aIndex, int aOutline, int aHole ) const
|
||||
{
|
||||
if( aOutline < 0 )
|
||||
aOutline += m_polys.size();
|
||||
|
||||
int idx;
|
||||
|
||||
if( aHole < 0 )
|
||||
idx = 0;
|
||||
else
|
||||
idx = aHole + 1;
|
||||
|
||||
assert( aOutline < (int)m_polys.size() );
|
||||
assert( idx < (int)m_polys[aOutline].size() );
|
||||
|
||||
return m_polys[aOutline][idx].CPoint( aIndex );
|
||||
}
|
||||
|
||||
|
||||
VECTOR2I& SHAPE_POLY_SET::Vertex( int aGlobalIndex )
|
||||
{
|
||||
SHAPE_POLY_SET::VERTEX_INDEX index;
|
||||
|
||||
// Assure the passed index references a legal position; abort otherwise
|
||||
if( !GetRelativeIndices( aGlobalIndex, &index ) )
|
||||
throw( std::out_of_range( "aGlobalIndex-th vertex does not exist" ) );
|
||||
|
||||
return m_polys[index.m_polygon][index.m_contour].Point( index.m_vertex );
|
||||
}
|
||||
|
||||
|
||||
const VECTOR2I& SHAPE_POLY_SET::CVertex( int aGlobalIndex ) const
|
||||
{
|
||||
SHAPE_POLY_SET::VERTEX_INDEX index;
|
||||
|
||||
// Assure the passed index references a legal position; abort otherwise
|
||||
if( !GetRelativeIndices( aGlobalIndex, &index ) )
|
||||
throw( std::out_of_range( "aGlobalIndex-th vertex does not exist" ) );
|
||||
|
||||
return m_polys[index.m_polygon][index.m_contour].CPoint( index.m_vertex );
|
||||
}
|
||||
|
||||
|
||||
VECTOR2I& SHAPE_POLY_SET::Vertex( SHAPE_POLY_SET::VERTEX_INDEX index )
|
||||
{
|
||||
return Vertex( index.m_vertex, index.m_polygon, index.m_contour - 1 );
|
||||
}
|
||||
|
||||
|
||||
const VECTOR2I& SHAPE_POLY_SET::CVertex( SHAPE_POLY_SET::VERTEX_INDEX index ) const
|
||||
{
|
||||
return CVertex( index.m_vertex, index.m_polygon, index.m_contour - 1 );
|
||||
}
|
||||
|
||||
|
||||
SEG SHAPE_POLY_SET::Edge( int aGlobalIndex )
|
||||
{
|
||||
SHAPE_POLY_SET::VERTEX_INDEX indices;
|
||||
|
||||
// If the edge does not exist, throw an exception, it is an illegal access memory error
|
||||
if( !GetRelativeIndices( aGlobalIndex, &indices ) )
|
||||
throw( std::out_of_range( "aGlobalIndex-th edge does not exist" ) );
|
||||
|
||||
return m_polys[indices.m_polygon][indices.m_contour].Segment( indices.m_vertex );
|
||||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::IsPolygonSelfIntersecting( int aPolygonIndex )
|
||||
{
|
||||
// Get polygon
|
||||
const POLYGON poly = CPolygon( aPolygonIndex );
|
||||
|
||||
SEGMENT_ITERATOR iterator = IterateSegmentsWithHoles( aPolygonIndex );
|
||||
SEGMENT_ITERATOR innerIterator;
|
||||
|
||||
for( iterator = IterateSegmentsWithHoles( aPolygonIndex ); iterator; iterator++ )
|
||||
{
|
||||
SEG firstSegment = *iterator;
|
||||
|
||||
// Iterate through all remaining segments.
|
||||
innerIterator = iterator;
|
||||
|
||||
// Start in the next segment, we don't want to check collision between a segment and itself
|
||||
for( innerIterator++; innerIterator; innerIterator++ )
|
||||
{
|
||||
SEG secondSegment = *innerIterator;
|
||||
|
||||
// Check whether the two segments built collide, only when they are not adjacent.
|
||||
if( !iterator.IsAdjacent( innerIterator ) && firstSegment.Collide( secondSegment, 0 ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::IsSelfIntersecting()
|
||||
{
|
||||
for( unsigned int polygon = 0; polygon < m_polys.size(); polygon++ )
|
||||
{
|
||||
if( IsPolygonSelfIntersecting( polygon ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -205,9 +434,12 @@ const SHAPE_LINE_CHAIN SHAPE_POLY_SET::convertFromClipper( const Path& aPath )
|
|||
for( unsigned int i = 0; i < aPath.size(); i++ )
|
||||
lc.Append( aPath[i].X, aPath[i].Y );
|
||||
|
||||
lc.SetClosed( true );
|
||||
|
||||
return lc;
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::booleanOp( ClipType aType, const SHAPE_POLY_SET& aOtherShape,
|
||||
POLYGON_MODE aFastMode )
|
||||
{
|
||||
|
@ -411,6 +643,7 @@ struct FractureEdge
|
|||
|
||||
typedef std::vector<FractureEdge*> FractureEdgeSet;
|
||||
|
||||
|
||||
static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
|
||||
{
|
||||
int x = edge->m_p1.x;
|
||||
|
@ -478,6 +711,7 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::fractureSingle( POLYGON& paths )
|
||||
{
|
||||
FractureEdgeSet edges;
|
||||
|
@ -590,6 +824,21 @@ void SHAPE_POLY_SET::Fracture( POLYGON_MODE aFastMode )
|
|||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::HasHoles() const
|
||||
{
|
||||
// Iterate through all the polygons on the set
|
||||
for( const POLYGON& paths : m_polys )
|
||||
{
|
||||
// If any of them has more than one contour, it is a hole.
|
||||
if( paths.size() > 1 )
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return false if and only if every polygon has just one outline, without holes.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::Simplify( POLYGON_MODE aFastMode )
|
||||
{
|
||||
SHAPE_POLY_SET empty;
|
||||
|
@ -598,6 +847,39 @@ void SHAPE_POLY_SET::Simplify( POLYGON_MODE aFastMode )
|
|||
}
|
||||
|
||||
|
||||
int SHAPE_POLY_SET::NormalizeAreaOutlines()
|
||||
{
|
||||
// We are expecting only one main outline, but this main outline can have holes
|
||||
// if holes: combine holes and remove them from the main outline.
|
||||
// Note also we are using SHAPE_POLY_SET::PM_STRICTLY_SIMPLE in polygon
|
||||
// calculations, but it is not mandatory. It is used mainly
|
||||
// because there is usually only very few vertices in area outlines
|
||||
SHAPE_POLY_SET::POLYGON& outline = Polygon( 0 );
|
||||
SHAPE_POLY_SET holesBuffer;
|
||||
|
||||
// Move holes stored in outline to holesBuffer:
|
||||
// The first SHAPE_LINE_CHAIN is the main outline, others are holes
|
||||
while( outline.size() > 1 )
|
||||
{
|
||||
holesBuffer.AddOutline( outline.back() );
|
||||
outline.pop_back();
|
||||
}
|
||||
|
||||
Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
|
||||
// If any hole, substract it to main outline
|
||||
if( holesBuffer.OutlineCount() )
|
||||
{
|
||||
holesBuffer.Simplify( SHAPE_POLY_SET::PM_FAST);
|
||||
BooleanSubtract( holesBuffer, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
}
|
||||
|
||||
RemoveNullSegments();
|
||||
|
||||
return OutlineCount();
|
||||
}
|
||||
|
||||
|
||||
const std::string SHAPE_POLY_SET::Format() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
@ -694,12 +976,108 @@ const BOX2I SHAPE_POLY_SET::BBox( int aClearance ) const
|
|||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::PointOnEdge( const VECTOR2I& aP ) const
|
||||
{
|
||||
// Iterate through all the polygons in the set
|
||||
for( const POLYGON& polygon : m_polys )
|
||||
{
|
||||
// Iterate through all the line chains in the polygon
|
||||
for( const SHAPE_LINE_CHAIN& lineChain : polygon )
|
||||
{
|
||||
if( lineChain.PointOnEdge( aP ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::Collide( const VECTOR2I& aP, int aClearance ) const
|
||||
{
|
||||
SHAPE_POLY_SET polySet = SHAPE_POLY_SET( *this );
|
||||
|
||||
// Inflate the polygon if necessary.
|
||||
if( aClearance > 0 )
|
||||
{
|
||||
// fixme: the number of arc segments should not be hardcoded
|
||||
polySet.Inflate( aClearance, 8 );
|
||||
}
|
||||
|
||||
// There is a collision if and only if the point is inside of the polygon.
|
||||
return polySet.Contains( aP );
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::RemoveAllContours()
|
||||
{
|
||||
m_polys.clear();
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::RemoveContour( int aContourIdx, int aPolygonIdx )
|
||||
{
|
||||
// Default polygon is the last one
|
||||
if( aPolygonIdx < 0 )
|
||||
aPolygonIdx += m_polys.size();
|
||||
|
||||
m_polys[aPolygonIdx].erase( m_polys[aPolygonIdx].begin() + aContourIdx );
|
||||
}
|
||||
|
||||
|
||||
int SHAPE_POLY_SET::RemoveNullSegments()
|
||||
{
|
||||
int removed = 0;
|
||||
|
||||
ITERATOR iterator = IterateWithHoles();
|
||||
|
||||
VECTOR2I contourStart = *iterator;
|
||||
VECTOR2I segmentStart, segmentEnd;
|
||||
|
||||
VERTEX_INDEX indexStart;
|
||||
|
||||
while( iterator )
|
||||
{
|
||||
// Obtain first point and its index
|
||||
segmentStart = *iterator;
|
||||
indexStart = iterator.GetIndex();
|
||||
|
||||
// Obtain last point
|
||||
if( iterator.IsEndContour() )
|
||||
{
|
||||
segmentEnd = contourStart;
|
||||
|
||||
// Advance
|
||||
iterator++;
|
||||
|
||||
if( iterator )
|
||||
contourStart = *iterator;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Advance
|
||||
iterator++;
|
||||
|
||||
if( iterator )
|
||||
segmentEnd = *iterator;
|
||||
}
|
||||
|
||||
// Remove segment start if both points are equal
|
||||
if( segmentStart == segmentEnd )
|
||||
{
|
||||
RemoveVertex( indexStart );
|
||||
removed++;
|
||||
|
||||
// Advance the iterator one position, as there is one vertex less.
|
||||
if( iterator )
|
||||
iterator++;
|
||||
}
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::DeletePolygon( int aIdx )
|
||||
{
|
||||
m_polys.erase( m_polys.begin() + aIdx );
|
||||
|
@ -718,22 +1096,128 @@ void SHAPE_POLY_SET::Append( const VECTOR2I& aP, int aOutline, int aHole )
|
|||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::CollideVertex( const VECTOR2I& aPoint,
|
||||
SHAPE_POLY_SET::VERTEX_INDEX& aClosestVertex, int aClearance )
|
||||
{
|
||||
// Shows whether there was a collision
|
||||
bool collision = false;
|
||||
|
||||
// Difference vector between each vertex and aPoint.
|
||||
VECTOR2D delta;
|
||||
double distance, clearance;
|
||||
|
||||
// Convert clearance to double for precission when comparing distances
|
||||
clearance = aClearance;
|
||||
|
||||
for( ITERATOR iterator = IterateWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
// Get the difference vector between current vertex and aPoint
|
||||
delta = *iterator - aPoint;
|
||||
|
||||
// Compute distance
|
||||
distance = delta.EuclideanNorm();
|
||||
|
||||
// Check for collisions
|
||||
if( distance <= clearance )
|
||||
{
|
||||
collision = true;
|
||||
|
||||
// Update aClearance to look for closer vertices
|
||||
clearance = distance;
|
||||
|
||||
// Store the indices that identify the vertex
|
||||
aClosestVertex = iterator.GetIndex();
|
||||
}
|
||||
}
|
||||
|
||||
return collision;
|
||||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::CollideEdge( const VECTOR2I& aPoint,
|
||||
SHAPE_POLY_SET::VERTEX_INDEX& aClosestVertex, int aClearance )
|
||||
{
|
||||
// Shows whether there was a collision
|
||||
bool collision = false;
|
||||
|
||||
SEGMENT_ITERATOR iterator;
|
||||
|
||||
for( iterator = IterateSegmentsWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
SEG currentSegment = *iterator;
|
||||
int distance = currentSegment.Distance( aPoint );
|
||||
|
||||
// Check for collisions
|
||||
if( distance <= aClearance )
|
||||
{
|
||||
collision = true;
|
||||
|
||||
// Update aClearance to look for closer edges
|
||||
aClearance = distance;
|
||||
|
||||
// Store the indices that identify the vertex
|
||||
aClosestVertex = iterator.GetIndex();
|
||||
}
|
||||
}
|
||||
|
||||
return collision;
|
||||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::Contains( const VECTOR2I& aP, int aSubpolyIndex ) const
|
||||
{
|
||||
// fixme: support holes!
|
||||
|
||||
if( m_polys.size() == 0 ) // empty set?
|
||||
return false;
|
||||
|
||||
// If there is a polygon specified, check the condition against that polygon
|
||||
if( aSubpolyIndex >= 0 )
|
||||
return pointInPolygon( aP, m_polys[aSubpolyIndex][0] );
|
||||
return containsSingle( aP, aSubpolyIndex );
|
||||
|
||||
for( const POLYGON& polys : m_polys )
|
||||
// In any other case, check it against all polygons in the set
|
||||
for( int polygonIdx = 0; polygonIdx < OutlineCount(); polygonIdx++ )
|
||||
{
|
||||
if( polys.size() == 0 )
|
||||
continue;
|
||||
if( containsSingle( aP, polygonIdx ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::RemoveVertex( int aGlobalIndex )
|
||||
{
|
||||
VERTEX_INDEX index;
|
||||
|
||||
// Assure the to be removed vertex exists, abort otherwise
|
||||
if( GetRelativeIndices( aGlobalIndex, &index ) )
|
||||
RemoveVertex( index );
|
||||
else
|
||||
throw( std::out_of_range( "aGlobalIndex-th vertex does not exist" ) );
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::RemoveVertex( VERTEX_INDEX aIndex )
|
||||
{
|
||||
m_polys[aIndex.m_polygon][aIndex.m_contour].Remove( aIndex.m_vertex );
|
||||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::containsSingle( const VECTOR2I& aP, int aSubpolyIndex ) const
|
||||
{
|
||||
// Check that the point is inside the outline
|
||||
if( pointInPolygon( aP, m_polys[aSubpolyIndex][0] ) )
|
||||
{
|
||||
// Check that the point is not in any of the holes
|
||||
for( int holeIdx = 0; holeIdx < HoleCount( aSubpolyIndex ); holeIdx++ )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN hole = CHole( aSubpolyIndex, holeIdx );
|
||||
// If the point is inside a hole (and not on its edge),
|
||||
// it is outside of the polygon
|
||||
if( pointInPolygon( aP, hole ) && !hole.PointOnEdge( aP ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( pointInPolygon( aP, polys[0] ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -832,3 +1316,313 @@ int SHAPE_POLY_SET::TotalVertices() const
|
|||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::ChamferPolygon( unsigned int aDistance, int aIndex )
|
||||
{
|
||||
return chamferFilletPolygon( CORNER_MODE::CHAMFERED, aDistance, aIndex );
|
||||
}
|
||||
|
||||
|
||||
SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::FilletPolygon( unsigned int aRadius,
|
||||
unsigned int aSegments,
|
||||
int aIndex )
|
||||
{
|
||||
return chamferFilletPolygon(CORNER_MODE::FILLETED, aRadius, aIndex, aSegments );
|
||||
}
|
||||
|
||||
|
||||
int SHAPE_POLY_SET::DistanceToPolygon( VECTOR2I aPoint, int aPolygonIndex )
|
||||
{
|
||||
// We calculate the min dist between the segment and each outline segment
|
||||
// However, if the segment to test is inside the outline, and does not cross
|
||||
// any edge, it can be seen outside the polygon.
|
||||
// Therefore test if a segment end is inside ( testing only one end is enough )
|
||||
if( containsSingle( aPoint, aPolygonIndex ) )
|
||||
return 0;
|
||||
|
||||
SEGMENT_ITERATOR iterator = IterateSegmentsWithHoles( aPolygonIndex );
|
||||
|
||||
SEG polygonEdge = *iterator;
|
||||
int minDistance = polygonEdge.Distance( aPoint );
|
||||
|
||||
for( iterator++; iterator && minDistance > 0; iterator++ )
|
||||
{
|
||||
polygonEdge = *iterator;
|
||||
|
||||
int currentDistance = polygonEdge.Distance( aPoint );
|
||||
|
||||
if( currentDistance < minDistance )
|
||||
minDistance = currentDistance;
|
||||
}
|
||||
|
||||
return minDistance;
|
||||
}
|
||||
|
||||
|
||||
int SHAPE_POLY_SET::DistanceToPolygon( SEG aSegment, int aPolygonIndex, int aSegmentWidth )
|
||||
{
|
||||
// We calculate the min dist between the segment and each outline segment
|
||||
// However, if the segment to test is inside the outline, and does not cross
|
||||
// any edge, it can be seen outside the polygon.
|
||||
// Therefore test if a segment end is inside ( testing only one end is enough )
|
||||
if( containsSingle( aSegment.A, aPolygonIndex ) )
|
||||
return 0;
|
||||
|
||||
SEGMENT_ITERATOR iterator = IterateSegmentsWithHoles( aPolygonIndex );
|
||||
|
||||
SEG polygonEdge = *iterator;
|
||||
int minDistance = polygonEdge.Distance( aSegment );
|
||||
|
||||
for( iterator++; iterator && minDistance > 0; iterator++ )
|
||||
{
|
||||
polygonEdge = *iterator;
|
||||
|
||||
int currentDistance = polygonEdge.Distance( aSegment );
|
||||
|
||||
if( currentDistance < minDistance )
|
||||
minDistance = currentDistance;
|
||||
}
|
||||
|
||||
// Take into account the width of the segment
|
||||
if( aSegmentWidth > 0 )
|
||||
minDistance -= aSegmentWidth/2;
|
||||
|
||||
// Return the maximum of minDistance and zero
|
||||
return minDistance < 0 ? 0 : minDistance;
|
||||
}
|
||||
|
||||
|
||||
int SHAPE_POLY_SET::Distance( VECTOR2I aPoint )
|
||||
{
|
||||
int currentDistance;
|
||||
int minDistance = DistanceToPolygon( aPoint, 0 );
|
||||
|
||||
// Iterate through all the polygons and get the minimum distance.
|
||||
for( unsigned int polygonIdx = 1; polygonIdx < m_polys.size(); polygonIdx++ )
|
||||
{
|
||||
currentDistance = DistanceToPolygon( aPoint, polygonIdx );
|
||||
|
||||
if( currentDistance < minDistance )
|
||||
minDistance = currentDistance;
|
||||
}
|
||||
|
||||
return minDistance;
|
||||
}
|
||||
|
||||
|
||||
int SHAPE_POLY_SET::Distance( SEG aSegment, int aSegmentWidth )
|
||||
{
|
||||
int currentDistance;
|
||||
int minDistance = DistanceToPolygon( aSegment, 0 );
|
||||
|
||||
// Iterate through all the polygons and get the minimum distance.
|
||||
for( unsigned int polygonIdx = 1; polygonIdx < m_polys.size(); polygonIdx++ )
|
||||
{
|
||||
currentDistance = DistanceToPolygon( aSegment, polygonIdx, aSegmentWidth );
|
||||
|
||||
if( currentDistance < minDistance )
|
||||
minDistance = currentDistance;
|
||||
}
|
||||
|
||||
return minDistance;
|
||||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::IsVertexInHole( int aGlobalIdx )
|
||||
{
|
||||
VERTEX_INDEX index;
|
||||
|
||||
// Get the polygon and contour where the vertex is. If the vertex does not exist, return false
|
||||
if( !GetRelativeIndices( aGlobalIdx, &index ) )
|
||||
return false;
|
||||
|
||||
// The contour is a hole if its index is greater than zero
|
||||
return index.m_contour > 0;
|
||||
}
|
||||
|
||||
|
||||
SHAPE_POLY_SET SHAPE_POLY_SET::Chamfer( int aDistance )
|
||||
{
|
||||
SHAPE_POLY_SET chamfered;
|
||||
|
||||
for( unsigned int polygonIdx = 0; polygonIdx < m_polys.size(); polygonIdx++ )
|
||||
chamfered.m_polys.push_back( ChamferPolygon( aDistance, polygonIdx ) );
|
||||
|
||||
return chamfered;
|
||||
}
|
||||
|
||||
|
||||
SHAPE_POLY_SET SHAPE_POLY_SET::Fillet( int aRadius, int aSegments )
|
||||
{
|
||||
SHAPE_POLY_SET filleted;
|
||||
|
||||
for( size_t polygonIdx = 0; polygonIdx < m_polys.size(); polygonIdx++ )
|
||||
filleted.m_polys.push_back( FilletPolygon( aRadius, aSegments, polygonIdx ) );
|
||||
|
||||
return filleted;
|
||||
}
|
||||
|
||||
|
||||
SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode,
|
||||
unsigned int aDistance,
|
||||
int aIndex,
|
||||
int aSegments )
|
||||
{
|
||||
// Null segments create serious issues in calculations. Remove them:
|
||||
RemoveNullSegments();
|
||||
|
||||
SHAPE_POLY_SET::POLYGON currentPoly = Polygon( aIndex );
|
||||
SHAPE_POLY_SET::POLYGON newPoly;
|
||||
|
||||
// If the chamfering distance is zero, then the polygon remain intact.
|
||||
if( aDistance == 0 )
|
||||
{
|
||||
return currentPoly;
|
||||
}
|
||||
|
||||
// Iterate through all the contours (outline and holes) of the polygon.
|
||||
for( SHAPE_LINE_CHAIN &currContour : currentPoly )
|
||||
{
|
||||
// Generate a new contour in the new polygon
|
||||
SHAPE_LINE_CHAIN newContour;
|
||||
|
||||
// Iterate through the vertices of the contour
|
||||
for( int currVertex = 0; currVertex < currContour.PointCount(); currVertex++ )
|
||||
{
|
||||
// Current vertex
|
||||
int x1 = currContour.Point( currVertex ).x;
|
||||
int y1 = currContour.Point( currVertex ).y;
|
||||
|
||||
// Indices for previous and next vertices.
|
||||
int prevVertex;
|
||||
int nextVertex;
|
||||
|
||||
// Previous and next vertices indices computation. Necessary to manage the edge cases.
|
||||
|
||||
// Previous vertex is the last one if the current vertex is the first one
|
||||
prevVertex = currVertex == 0 ? currContour.PointCount() - 1 : currVertex - 1;
|
||||
|
||||
// next vertex is the first one if the current vertex is the last one.
|
||||
nextVertex = currVertex == currContour.PointCount() - 1 ? 0 : currVertex + 1;
|
||||
|
||||
// Previous vertex computation
|
||||
double xa = currContour.Point( prevVertex ).x - x1;
|
||||
double ya = currContour.Point( prevVertex ).y - y1;
|
||||
|
||||
// Next vertex computation
|
||||
double xb = currContour.Point( nextVertex ).x - x1;
|
||||
double yb = currContour.Point( nextVertex ).y - y1;
|
||||
|
||||
// Compute the new distances
|
||||
double lena = hypot( xa, ya );
|
||||
double lenb = hypot( xb, yb );
|
||||
|
||||
// Make the final computations depending on the mode selected, chamfered or filleted.
|
||||
if( aMode == CORNER_MODE::CHAMFERED )
|
||||
{
|
||||
double distance = aDistance;
|
||||
|
||||
// Chamfer one half of an edge at most
|
||||
if( 0.5 * lena < distance )
|
||||
distance = 0.5 * lena;
|
||||
|
||||
if( 0.5 * lenb < distance )
|
||||
distance = 0.5 * lenb;
|
||||
|
||||
int nx1 = KiROUND( distance * xa / lena );
|
||||
int ny1 = KiROUND( distance * ya / lena );
|
||||
|
||||
newContour.Append( x1 + nx1, y1 + ny1 );
|
||||
|
||||
int nx2 = KiROUND( distance * xb / lenb );
|
||||
int ny2 = KiROUND( distance * yb / lenb );
|
||||
|
||||
newContour.Append( x1 + nx2, y1 + ny2 );
|
||||
}
|
||||
else // CORNER_MODE = FILLETED
|
||||
{
|
||||
double cosine = ( xa * xb + ya * yb ) / ( lena * lenb );
|
||||
|
||||
double radius = aDistance;
|
||||
double denom = sqrt( 2.0 / ( 1 + cosine ) - 1 );
|
||||
|
||||
// Do nothing in case of parallel edges
|
||||
if( std::isinf( denom ) )
|
||||
continue;
|
||||
|
||||
// Limit rounding distance to one half of an edge
|
||||
if( 0.5 * lena * denom < radius )
|
||||
radius = 0.5 * lena * denom;
|
||||
|
||||
if( 0.5 * lenb * denom < radius )
|
||||
radius = 0.5 * lenb * denom;
|
||||
|
||||
// Calculate fillet arc absolute center point (xc, yx)
|
||||
double k = radius / sqrt( .5 * ( 1 - cosine ) );
|
||||
double lenab = sqrt( ( xa / lena + xb / lenb ) * ( xa / lena + xb / lenb ) +
|
||||
( ya / lena + yb / lenb ) * ( ya / lena + yb / lenb ) );
|
||||
double xc = x1 + k * ( xa / lena + xb / lenb ) / lenab;
|
||||
double yc = y1 + k * ( ya / lena + yb / lenb ) / lenab;
|
||||
|
||||
// Calculate arc start and end vectors
|
||||
k = radius / sqrt( 2 / ( 1 + cosine ) - 1 );
|
||||
double xs = x1 + k * xa / lena - xc;
|
||||
double ys = y1 + k * ya / lena - yc;
|
||||
double xe = x1 + k * xb / lenb - xc;
|
||||
double ye = y1 + k * yb / lenb - yc;
|
||||
|
||||
// Cosine of arc angle
|
||||
double argument = ( xs * xe + ys * ye ) / ( radius * radius );
|
||||
|
||||
// Make sure the argument is in [-1,1], interval in which the acos function is
|
||||
// defined
|
||||
if( argument < -1 )
|
||||
argument = -1;
|
||||
else if( argument > 1 )
|
||||
argument = 1;
|
||||
|
||||
double arcAngle = acos( argument );
|
||||
|
||||
// Calculate the number of segments
|
||||
unsigned int segments = ceil( (double) aSegments * ( arcAngle / ( 2 * M_PI ) ) );
|
||||
|
||||
double deltaAngle = arcAngle / segments;
|
||||
double startAngle = atan2( -ys, xs );
|
||||
|
||||
// Flip arc for inner corners
|
||||
if( xa * yb - ya * xb <= 0 )
|
||||
deltaAngle *= -1;
|
||||
|
||||
double nx = xc + xs;
|
||||
double ny = yc + ys;
|
||||
|
||||
newContour.Append( KiROUND( nx ), KiROUND( ny ) );
|
||||
|
||||
// Store the previous added corner to make a sanity check
|
||||
int prevX = KiROUND( nx );
|
||||
int prevY = KiROUND( ny );
|
||||
|
||||
for( unsigned int j = 0; j < segments; j++ )
|
||||
{
|
||||
nx = xc + cos( startAngle + (j + 1) * deltaAngle ) * radius;
|
||||
ny = yc - sin( startAngle + (j + 1) * deltaAngle ) * radius;
|
||||
|
||||
// Sanity check: the rounding can produce repeated corners; do not add them.
|
||||
if( KiROUND( nx ) != prevX || KiROUND( ny ) != prevY )
|
||||
{
|
||||
newContour.Append( KiROUND( nx ), KiROUND( ny ) );
|
||||
prevX = KiROUND( nx );
|
||||
prevY = KiROUND( ny );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the current contour and add it the new polygon
|
||||
newContour.SetClosed( true );
|
||||
newPoly.push_back( newContour );
|
||||
}
|
||||
|
||||
return newPoly;
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ principle should be easily implemented by adapting the current STL containers.
|
|||
%ignore GetCommandOptions;
|
||||
|
||||
%rename(getWxRect) operator wxRect;
|
||||
%rename(getBOX2I) operator BOX2I;
|
||||
%ignore operator <<;
|
||||
%ignore operator=;
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#define CLASS_EDA_RECT_H
|
||||
|
||||
#include <wx/gdicmn.h>
|
||||
#include <math/box2.h>
|
||||
|
||||
/**
|
||||
* Class EDA_RECT
|
||||
|
@ -78,6 +79,7 @@ public:
|
|||
* @return true if aPoint is inside the boundary box. A point on a edge is seen as inside
|
||||
*/
|
||||
bool Contains( const wxPoint& aPoint ) const;
|
||||
|
||||
/**
|
||||
* Function Contains
|
||||
* @param x = the x coordinate of the point to test
|
||||
|
@ -177,6 +179,18 @@ public:
|
|||
return wxRect( rect.m_Pos, rect.m_Size );
|
||||
}
|
||||
|
||||
/**
|
||||
* Function operator(BOX2I)
|
||||
* overloads the cast operator to return a BOX2I
|
||||
* @return BOX2I - this box shaped as a BOX2I object.
|
||||
*/
|
||||
operator BOX2I() const
|
||||
{
|
||||
EDA_RECT rect( m_Pos, m_Size );
|
||||
rect.Normalize();
|
||||
return BOX2I( rect.GetPosition(), rect.GetEnd() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Function Inflate
|
||||
* inflates the rectangle horizontally by \a dx and vertically by \a dy. If \a dx
|
||||
|
|
|
@ -38,6 +38,8 @@ class SEG
|
|||
{
|
||||
private:
|
||||
typedef VECTOR2I::extended_type ecoord;
|
||||
VECTOR2I m_a;
|
||||
VECTOR2I m_b;
|
||||
|
||||
public:
|
||||
friend inline std::ostream& operator<<( std::ostream& aStream, const SEG& aSeg );
|
||||
|
@ -46,13 +48,15 @@ public:
|
|||
* to an object the segment belongs to (e.g. a line chain) or references to locally stored
|
||||
* points (m_a, m_b).
|
||||
*/
|
||||
VECTOR2I A;
|
||||
VECTOR2I B;
|
||||
VECTOR2I& A;
|
||||
VECTOR2I& B;
|
||||
|
||||
/** Default constructor
|
||||
* Creates an empty (0, 0) segment, locally-referenced
|
||||
*/
|
||||
SEG()
|
||||
SEG() :
|
||||
A( m_a ),
|
||||
B( m_b )
|
||||
{
|
||||
m_index = -1;
|
||||
}
|
||||
|
@ -62,9 +66,11 @@ public:
|
|||
* Creates a segment between (aX1, aY1) and (aX2, aY2), locally referenced
|
||||
*/
|
||||
SEG( int aX1, int aY1, int aX2, int aY2 ) :
|
||||
A ( VECTOR2I( aX1, aY1 ) ),
|
||||
B ( VECTOR2I( aX2, aY2 ) )
|
||||
A( m_a ),
|
||||
B( m_b )
|
||||
{
|
||||
A = VECTOR2I( aX1, aY1 );
|
||||
B = VECTOR2I( aX2, aY2 );
|
||||
m_index = -1;
|
||||
}
|
||||
|
||||
|
@ -72,11 +78,37 @@ public:
|
|||
* Constructor
|
||||
* Creates a segment between (aA) and (aB), locally referenced
|
||||
*/
|
||||
SEG( const VECTOR2I& aA, const VECTOR2I& aB ) : A( aA ), B( aB )
|
||||
SEG( const VECTOR2I& aA, const VECTOR2I& aB ) :
|
||||
A( m_a ),
|
||||
B( m_b )
|
||||
{
|
||||
A = aA;
|
||||
B = aB;
|
||||
m_index = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Creates a segment between (aA) and (aB), referencing the passed points
|
||||
*/
|
||||
SEG( VECTOR2I& aA, VECTOR2I& aB ) :
|
||||
A( aA ),
|
||||
B( aB )
|
||||
{
|
||||
m_index = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Creates a segment between (aA) and (aB), referencing the passed points
|
||||
*/
|
||||
SEG( VECTOR2I& aA, VECTOR2I& aB, int aIndex ) :
|
||||
A( aA ),
|
||||
B( aB )
|
||||
{
|
||||
m_index = aIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Creates a segment between (aA) and (aB), referenced to a multi-segment shape
|
||||
|
@ -84,16 +116,25 @@ public:
|
|||
* @param aB reference to the end point in the parent shape
|
||||
* @param aIndex index of the segment within the parent shape
|
||||
*/
|
||||
SEG( const VECTOR2I& aA, const VECTOR2I& aB, int aIndex ) : A( aA ), B( aB )
|
||||
SEG( const VECTOR2I& aA, const VECTOR2I& aB, int aIndex ) :
|
||||
A( m_a ),
|
||||
B( m_b )
|
||||
{
|
||||
A = aA;
|
||||
B = aB;
|
||||
m_index = aIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*/
|
||||
SEG( const SEG& aSeg ) : A( aSeg.A ), B( aSeg.B ), m_index( aSeg.m_index )
|
||||
SEG( const SEG& aSeg ) :
|
||||
A( m_a ),
|
||||
B( m_b ),
|
||||
m_index( aSeg.m_index )
|
||||
{
|
||||
A = aSeg.A;
|
||||
B = aSeg.B;
|
||||
}
|
||||
|
||||
SEG& operator=( const SEG& aSeg )
|
||||
|
|
|
@ -410,6 +410,16 @@ public:
|
|||
*/
|
||||
void Remove( int aStartIndex, int aEndIndex );
|
||||
|
||||
/**
|
||||
* Function Remove()
|
||||
* removes the aIndex-th point from the line chain.
|
||||
* @param aIndex is the index of the point to be removed.
|
||||
*/
|
||||
void Remove( int aIndex )
|
||||
{
|
||||
Remove( aIndex, aIndex );
|
||||
}
|
||||
|
||||
/**
|
||||
* Function Split()
|
||||
*
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2015 CERN
|
||||
* Copyright (C) 2015-2017 CERN
|
||||
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -39,6 +40,14 @@
|
|||
* Represents a set of closed polygons. Polygons may be nonconvex, self-intersecting
|
||||
* and have holes. Provides boolean operations (using Clipper library as the backend).
|
||||
*
|
||||
* Let us define the terms used on this class to clarify methods names and comments:
|
||||
* - Polygon: each polygon in the set.
|
||||
* - Outline: first polyline in each polygon; represents its outer contour.
|
||||
* - Hole: second and following polylines in the polygon.
|
||||
* - Contour: each polyline of each polygon in the set, whether or not it is an
|
||||
* outline or a hole.
|
||||
* - Vertex (or corner): each one of the points that define a contour.
|
||||
*
|
||||
* TODO: add convex partitioning & spatial index
|
||||
*/
|
||||
class SHAPE_POLY_SET : public SHAPE
|
||||
|
@ -48,39 +57,97 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
///> the remaining (if any), are the holes
|
||||
typedef std::vector<SHAPE_LINE_CHAIN> POLYGON;
|
||||
|
||||
/**
|
||||
* Struct VERTEX_INDEX
|
||||
*
|
||||
* Structure to hold the necessary information in order to index a vertex on a
|
||||
* SHAPE_POLY_SET object: the polygon index, the contour index relative to the polygon and
|
||||
* the vertex index relative the contour.
|
||||
*/
|
||||
typedef struct VERTEX_INDEX
|
||||
{
|
||||
int m_polygon; /*!< m_polygon is the index of the polygon. */
|
||||
int m_contour; /*!< m_contour is the index of the contour relative to the polygon. */
|
||||
int m_vertex; /*!< m_vertex is the index of the vertex relative to the contour. */
|
||||
|
||||
VERTEX_INDEX() : m_polygon(-1), m_contour(-1), m_vertex(-1)
|
||||
{
|
||||
}
|
||||
} VERTEX_INDEX;
|
||||
|
||||
/**
|
||||
* Class ITERATOR_TEMPLATE
|
||||
*
|
||||
* Base class for iterating over all vertices in a given SHAPE_POLY_SET
|
||||
* Base class for iterating over all vertices in a given SHAPE_POLY_SET.
|
||||
*/
|
||||
template <class T>
|
||||
class ITERATOR_TEMPLATE
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Function IsEndContour.
|
||||
* @return bool - true if the current vertex is the last one of the current contour
|
||||
* (outline or hole); false otherwise.
|
||||
*/
|
||||
bool IsEndContour() const
|
||||
{
|
||||
return m_currentVertex + 1 == m_poly->CPolygon( m_currentOutline )[0].PointCount();
|
||||
return m_currentVertex + 1 == m_poly->CPolygon( m_currentPolygon )[m_currentContour].PointCount();
|
||||
}
|
||||
|
||||
bool IsLastContour() const
|
||||
/**
|
||||
* Function IsLastOutline.
|
||||
* @return bool - true if the current outline is the last one; false otherwise.
|
||||
*/
|
||||
bool IsLastPolygon() const
|
||||
{
|
||||
return m_currentOutline == m_lastOutline;
|
||||
return m_currentPolygon == m_lastPolygon;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return m_currentOutline <= m_lastOutline;
|
||||
return m_currentPolygon <= m_lastPolygon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function Advance
|
||||
* advances the indices of the current vertex/outline/contour, checking whether the
|
||||
* vertices in the holes have to be iterated through
|
||||
*/
|
||||
void Advance()
|
||||
{
|
||||
// Advance vertex index
|
||||
m_currentVertex ++;
|
||||
|
||||
if( m_currentVertex >= m_poly->CPolygon( m_currentOutline )[0].PointCount() )
|
||||
// Check whether the user wants to iterate through the vertices of the holes
|
||||
// and behave accordingly
|
||||
if( m_iterateHoles )
|
||||
{
|
||||
// If the last vertex of the contour was reached, advance the contour index
|
||||
if( m_currentVertex >= m_poly->CPolygon( m_currentPolygon )[m_currentContour].PointCount() )
|
||||
{
|
||||
m_currentVertex = 0;
|
||||
m_currentOutline++;
|
||||
m_currentContour++;
|
||||
|
||||
// If the last contour of the current polygon was reached, advance the
|
||||
// outline index
|
||||
int totalContours = m_poly->CPolygon( m_currentPolygon ).size();
|
||||
|
||||
if( m_currentContour >= totalContours )
|
||||
{
|
||||
m_currentContour = 0;
|
||||
m_currentPolygon++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the last vertex of the outline was reached, advance to the following polygon
|
||||
if( m_currentVertex >= m_poly->CPolygon( m_currentPolygon )[0].PointCount() )
|
||||
{
|
||||
m_currentVertex = 0;
|
||||
m_currentPolygon++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,7 +163,7 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
|
||||
T& Get()
|
||||
{
|
||||
return m_poly->Polygon( m_currentOutline )[0].Point( m_currentVertex );
|
||||
return m_poly->Polygon( m_currentPolygon )[m_currentContour].Point( m_currentVertex );
|
||||
}
|
||||
|
||||
T& operator*()
|
||||
|
@ -109,22 +176,230 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
return &Get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function GetIndex
|
||||
* @return VERTEX_INDEX - indices of the current polygon, contour and vertex.
|
||||
*/
|
||||
VERTEX_INDEX GetIndex()
|
||||
{
|
||||
VERTEX_INDEX index;
|
||||
|
||||
index.m_polygon = m_currentPolygon;
|
||||
index.m_contour = m_currentContour;
|
||||
index.m_vertex = m_currentVertex;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
friend class SHAPE_POLY_SET;
|
||||
|
||||
SHAPE_POLY_SET* m_poly;
|
||||
int m_currentOutline;
|
||||
int m_lastOutline;
|
||||
int m_currentPolygon;
|
||||
int m_currentContour;
|
||||
int m_currentVertex;
|
||||
int m_lastPolygon;
|
||||
bool m_iterateHoles;
|
||||
};
|
||||
|
||||
/**
|
||||
* Class SEGMENT_ITERATOR_TEMPLATE
|
||||
*
|
||||
* Base class for iterating over all segments in a given SHAPE_POLY_SET.
|
||||
*/
|
||||
template <class T>
|
||||
class SEGMENT_ITERATOR_TEMPLATE
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Function IsLastOutline.
|
||||
* @return bool - true if the current outline is the last one.
|
||||
*/
|
||||
bool IsLastPolygon() const
|
||||
{
|
||||
return m_currentPolygon == m_lastPolygon;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return m_currentPolygon <= m_lastPolygon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function Advance
|
||||
* advances the indices of the current vertex/outline/contour, checking whether the
|
||||
* vertices in the holes have to be iterated through
|
||||
*/
|
||||
void Advance()
|
||||
{
|
||||
// Advance vertex index
|
||||
m_currentSegment++;
|
||||
int last;
|
||||
|
||||
// Check whether the user wants to iterate through the vertices of the holes
|
||||
// and behave accordingly
|
||||
if( m_iterateHoles )
|
||||
{
|
||||
last = m_poly->CPolygon( m_currentPolygon )[m_currentContour].SegmentCount();
|
||||
|
||||
// If the last vertex of the contour was reached, advance the contour index
|
||||
if( m_currentSegment >= last )
|
||||
{
|
||||
m_currentSegment = 0;
|
||||
m_currentContour++;
|
||||
|
||||
// If the last contour of the current polygon was reached, advance the
|
||||
// outline index
|
||||
int totalContours = m_poly->CPolygon( m_currentPolygon ).size();
|
||||
|
||||
if( m_currentContour >= totalContours )
|
||||
{
|
||||
m_currentContour = 0;
|
||||
m_currentPolygon++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
last = m_poly->CPolygon( m_currentPolygon )[0].SegmentCount();
|
||||
// If the last vertex of the outline was reached, advance to the following
|
||||
// polygon
|
||||
if( m_currentSegment >= last )
|
||||
{
|
||||
m_currentSegment = 0;
|
||||
m_currentPolygon++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void operator++( int dummy )
|
||||
{
|
||||
Advance();
|
||||
}
|
||||
|
||||
void operator++()
|
||||
{
|
||||
Advance();
|
||||
}
|
||||
|
||||
T Get()
|
||||
{
|
||||
return m_poly->Polygon( m_currentPolygon )[m_currentContour].Segment( m_currentSegment );
|
||||
}
|
||||
|
||||
T operator*()
|
||||
{
|
||||
return Get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function GetIndex
|
||||
* @return VERTEX_INDEX - indices of the current polygon, contour and vertex.
|
||||
*/
|
||||
VERTEX_INDEX GetIndex()
|
||||
{
|
||||
VERTEX_INDEX index;
|
||||
|
||||
index.m_polygon = m_currentPolygon;
|
||||
index.m_contour = m_currentContour;
|
||||
index.m_vertex = m_currentSegment;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function IsAdjacent
|
||||
* @param aOther is an iterator pointing to another segment.
|
||||
* @return bool - true if both iterators point to the same segment of the same
|
||||
* contour of the same polygon of the same polygon set; false
|
||||
* otherwise.
|
||||
*/
|
||||
bool IsAdjacent( SEGMENT_ITERATOR_TEMPLATE<T> aOther )
|
||||
{
|
||||
// Check that both iterators point to the same contour of the same polygon of the
|
||||
// same polygon set
|
||||
if( m_poly == aOther.m_poly && m_currentPolygon == aOther.m_currentPolygon &&
|
||||
m_currentContour == aOther.m_currentContour )
|
||||
{
|
||||
// Compute the total number of segments
|
||||
int numSeg;
|
||||
numSeg = m_poly->CPolygon( m_currentPolygon )[m_currentContour].SegmentCount();
|
||||
|
||||
// Compute the difference of the segment indices. If it is exactly one, they
|
||||
// are adjacent. The only missing case where they also are adjacent is when
|
||||
// the segments are the first and last one, in which case the difference
|
||||
// always equals the total number of segments minus one.
|
||||
int indexDiff = abs( m_currentSegment - aOther.m_currentSegment );
|
||||
|
||||
return ( indexDiff == 1 ) || ( indexDiff == (numSeg - 1) );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class SHAPE_POLY_SET;
|
||||
|
||||
SHAPE_POLY_SET* m_poly;
|
||||
int m_currentPolygon;
|
||||
int m_currentContour;
|
||||
int m_currentSegment;
|
||||
int m_lastPolygon;
|
||||
bool m_iterateHoles;
|
||||
};
|
||||
|
||||
// Iterator and const iterator types to visit polygon's points.
|
||||
typedef ITERATOR_TEMPLATE<VECTOR2I> ITERATOR;
|
||||
typedef ITERATOR_TEMPLATE<const VECTOR2I> CONST_ITERATOR;
|
||||
|
||||
// Iterator and const iterator types to visit polygon's edges.
|
||||
typedef SEGMENT_ITERATOR_TEMPLATE<SEG> SEGMENT_ITERATOR;
|
||||
typedef SEGMENT_ITERATOR_TEMPLATE<const SEG> CONST_SEGMENT_ITERATOR;
|
||||
|
||||
SHAPE_POLY_SET();
|
||||
|
||||
/**
|
||||
* Copy constructor SHAPE_POLY_SET
|
||||
* Performs a deep copy of \p aOther into \p this.
|
||||
* @param aOther is the SHAPE_POLY_SET object that will be copied.
|
||||
*/
|
||||
SHAPE_POLY_SET( const SHAPE_POLY_SET& aOther );
|
||||
|
||||
~SHAPE_POLY_SET();
|
||||
|
||||
/**
|
||||
* Function GetRelativeIndices
|
||||
*
|
||||
* Converts a global vertex index ---i.e., a number that globally identifies a vertex in a
|
||||
* concatenated list of all vertices in all contours--- and get the index of the vertex
|
||||
* relative to the contour relative to the polygon in which it is.
|
||||
* @param aGlobalIdx is the global index of the corner whose structured index wants to
|
||||
* be found
|
||||
* @param aPolygonIdx is the index of the polygon in which the expected vertex is.
|
||||
* @param aContourIdx is the index of the contour in the aPolygonIdx-th polygon in which
|
||||
* the expected vertex is.
|
||||
* @param aVertexIdx is the index of the vertex in the aContourIdx-th contour in which
|
||||
* the expected vertex is.
|
||||
* @return bool - true if the global index is correct and the information in
|
||||
* aRelativeIndices is valid; false otherwise.
|
||||
*/
|
||||
bool GetRelativeIndices( int aGlobalIdx, VERTEX_INDEX* aRelativeIndices) const;
|
||||
|
||||
/**
|
||||
* Function GetGlobalIndex
|
||||
* computes the global index of a vertex from the relative indices of polygon, contour and
|
||||
* vertex.
|
||||
* @param aRelativeIndices is the set of relative indices.
|
||||
* @param aGlobalIdx [out] is the computed global index.
|
||||
* @return bool - true if the relative indices are correct; false otherwise. The cmoputed
|
||||
* global index is returned in the \p aGlobalIdx reference.
|
||||
*/
|
||||
bool GetGlobalIndex( VERTEX_INDEX aRelativeIndices, int& aGlobalIdx );
|
||||
|
||||
/// @copydoc SHAPE::Clone()
|
||||
SHAPE* Clone() const override;
|
||||
|
||||
///> Creates a new empty polygon in the set and returns its index
|
||||
int NewOutline();
|
||||
|
||||
|
@ -138,7 +413,20 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
int AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline = -1 );
|
||||
|
||||
///> Appends a vertex at the end of the given outline/hole (default: the last outline)
|
||||
int Append( int x, int y, int aOutline = -1, int aHole = -1 );
|
||||
/**
|
||||
* Function Append
|
||||
* adds a new vertex to the contour indexed by \p aOutline and \p aHole (defaults to the
|
||||
* outline of the last polygon).
|
||||
* @param x is the x coordinate of the new vertex.
|
||||
* @param y is the y coordinate of the new vertex.
|
||||
* @param aOutline is the index of the polygon.
|
||||
* @param aHole is the index of the hole (-1 for the main outline),
|
||||
* @param aAllowDuplication is a flag to indicate whether it is allowed to add this
|
||||
* corner even if it is duplicated.
|
||||
* @return int - the number of corners of the selected contour after the addition.
|
||||
*/
|
||||
int Append( int x, int y, int aOutline = -1, int aHole = -1,
|
||||
bool aAllowDuplication = false );
|
||||
|
||||
///> Merges polygons from two sets.
|
||||
void Append( const SHAPE_POLY_SET& aSet );
|
||||
|
@ -146,13 +434,59 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
///> Appends a vertex at the end of the given outline/hole (default: the last outline)
|
||||
void Append( const VECTOR2I& aP, int aOutline = -1, int aHole = -1 );
|
||||
|
||||
///> Returns the index-th vertex in a given hole outline within a given outline
|
||||
VECTOR2I& Vertex( int index, int aOutline = -1, int aHole = -1 );
|
||||
/**
|
||||
* Function InsertVertex
|
||||
* Adds a vertex in the globally indexed position aGlobalIndex.
|
||||
* @param aGlobalIndex is the global index of the position in which teh new vertex will be
|
||||
* inserted.
|
||||
* @param aNewVertex is the new inserted vertex.
|
||||
*/
|
||||
void InsertVertex( int aGlobalIndex, VECTOR2I aNewVertex );
|
||||
|
||||
///> Returns the index-th vertex in a given hole outline within a given outline
|
||||
const VECTOR2I& CVertex( int index, int aOutline = -1, int aHole = -1 ) const;
|
||||
VECTOR2I& Vertex( int aIndex, int aOutline, int aHole );
|
||||
|
||||
///> Returns true if any of the outlines is self-intersecting
|
||||
///> Returns the index-th vertex in a given hole outline within a given outline
|
||||
const VECTOR2I& CVertex( int aIndex, int aOutline, int aHole ) const;
|
||||
|
||||
///> Returns the aGlobalIndex-th vertex in the poly set
|
||||
VECTOR2I& Vertex( int aGlobalIndex );
|
||||
|
||||
///> Returns the aGlobalIndex-th vertex in the poly set
|
||||
const VECTOR2I& CVertex( int aGlobalIndex ) const;
|
||||
|
||||
///> Returns the index-th vertex in a given hole outline within a given outline
|
||||
VECTOR2I& Vertex( VERTEX_INDEX aIndex );
|
||||
|
||||
///> Returns the index-th vertex in a given hole outline within a given outline
|
||||
const VECTOR2I& CVertex( VERTEX_INDEX aIndex ) const;
|
||||
|
||||
/**
|
||||
* Function Edge
|
||||
* Returns a reference to the aGlobalIndex-th segment in the polygon set. Modifying the
|
||||
* points in the returned object will modify the corresponding vertices on the polygon set.
|
||||
* @param aGlobalIndex is index of the edge, globally indexed between all edges in all
|
||||
* contours
|
||||
* @return SEG - the aGlobalIndex-th segment, whose points are references to the polygon
|
||||
* points.
|
||||
*/
|
||||
SEG Edge( int aGlobalIndex );
|
||||
|
||||
|
||||
/**
|
||||
* Function IsPolygonSelfIntersecting.
|
||||
* Checks whether the aPolygonIndex-th polygon in the set is self intersecting.
|
||||
* @param aPolygonIndex index of the polygon that wants to be checked.
|
||||
* @return bool - true if the aPolygonIndex-th polygon is self intersecting, false
|
||||
* otherwise.
|
||||
*/
|
||||
bool IsPolygonSelfIntersecting( int aPolygonIndex );
|
||||
|
||||
/**
|
||||
* Function IsSelfIntersecting
|
||||
* Checks whether any of the polygons in the set is self intersecting.
|
||||
* @return bool - true if any of the polygons is self intersecting, false otherwise.
|
||||
*/
|
||||
bool IsSelfIntersecting();
|
||||
|
||||
///> Returns the number of outlines in the set
|
||||
|
@ -175,6 +509,22 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
return m_polys[aIndex][0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Function Subset
|
||||
* returns a subset of the polygons in this set, the ones between aFirstPolygon and
|
||||
* aLastPolygon.
|
||||
* @param aFirstPolygon is the first polygon to be included in the returned set.
|
||||
* @param aLastPolygon is the first polygon to be excluded of the returned set.
|
||||
* @return SHAPE_POLY_SET - a set containing the polygons between aFirstPolygon (included)
|
||||
* and aLastPolygon (excluded).
|
||||
*/
|
||||
SHAPE_POLY_SET Subset( int aFirstPolygon, int aLastPolygon );
|
||||
|
||||
SHAPE_POLY_SET UnitSet( int aPolygonIndex )
|
||||
{
|
||||
return Subset( aPolygonIndex, aPolygonIndex + 1 );
|
||||
}
|
||||
|
||||
///> Returns the reference to aHole-th hole in the aIndex-th outline
|
||||
SHAPE_LINE_CHAIN& Hole( int aOutline, int aHole )
|
||||
{
|
||||
|
@ -202,39 +552,83 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
return m_polys[aIndex];
|
||||
}
|
||||
|
||||
///> Returns an iterator object, for iterating between aFirst and aLast outline.
|
||||
ITERATOR Iterate( int aFirst, int aLast )
|
||||
/**
|
||||
* Function Iterate
|
||||
* returns an object to iterate through the points of the polygons between \p aFirst and
|
||||
* \p aLast.
|
||||
* @param aFirst is the first polygon whose points will be iterated.
|
||||
* @param aLast is the last polygon whose points will be iterated.
|
||||
* @param aIterateHoles is a flag to indicate whether the points of the holes should be
|
||||
* iterated.
|
||||
* @return ITERATOR - the iterator object.
|
||||
*/
|
||||
ITERATOR Iterate( int aFirst, int aLast, bool aIterateHoles = false )
|
||||
{
|
||||
ITERATOR iter;
|
||||
|
||||
iter.m_poly = this;
|
||||
iter.m_currentOutline = aFirst;
|
||||
iter.m_lastOutline = aLast < 0 ? OutlineCount() - 1 : aLast;
|
||||
iter.m_currentPolygon = aFirst;
|
||||
iter.m_lastPolygon = aLast < 0 ? OutlineCount() - 1 : aLast;
|
||||
iter.m_currentContour = 0;
|
||||
iter.m_currentVertex = 0;
|
||||
iter.m_iterateHoles = aIterateHoles;
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
///> Returns an iterator object, for iterating aOutline-th outline
|
||||
/**
|
||||
* Function Iterate
|
||||
* @param aOutline the index of the polygon to be iterated.
|
||||
* @return ITERATOR - an iterator object to visit all points in the main outline of the
|
||||
* aOutline-th polygon, without visiting the points in the holes.
|
||||
*/
|
||||
ITERATOR Iterate( int aOutline )
|
||||
{
|
||||
return Iterate( aOutline, aOutline );
|
||||
}
|
||||
|
||||
///> Returns an iterator object, for all outlines in the set (no holes)
|
||||
/**
|
||||
* Function IterateWithHoles
|
||||
* @param aOutline the index of the polygon to be iterated.
|
||||
* @return ITERATOR - an iterator object to visit all points in the main outline of the
|
||||
* aOutline-th polygon, visiting also the points in the holes.
|
||||
*/
|
||||
ITERATOR IterateWithHoles( int aOutline )
|
||||
{
|
||||
return Iterate( aOutline, aOutline, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Function Iterate
|
||||
* @return ITERATOR - an iterator object to visit all points in all outlines of the set,
|
||||
* without visiting the points in the holes.
|
||||
*/
|
||||
ITERATOR Iterate()
|
||||
{
|
||||
return Iterate( 0, OutlineCount() - 1 );
|
||||
}
|
||||
|
||||
CONST_ITERATOR CIterate( int aFirst, int aLast ) const
|
||||
/**
|
||||
* Function IterateWithHoles
|
||||
* @return ITERATOR - an iterator object to visit all points in all outlines of the set,
|
||||
* visiting also the points in the holes.
|
||||
*/
|
||||
ITERATOR IterateWithHoles()
|
||||
{
|
||||
return Iterate( 0, OutlineCount() - 1, true );
|
||||
}
|
||||
|
||||
|
||||
CONST_ITERATOR CIterate( int aFirst, int aLast, bool aIterateHoles = false ) const
|
||||
{
|
||||
CONST_ITERATOR iter;
|
||||
|
||||
iter.m_poly = const_cast<SHAPE_POLY_SET*>( this );
|
||||
iter.m_currentOutline = aFirst;
|
||||
iter.m_lastOutline = aLast < 0 ? OutlineCount() - 1 : aLast;
|
||||
iter.m_currentPolygon = aFirst;
|
||||
iter.m_lastPolygon = aLast < 0 ? OutlineCount() - 1 : aLast;
|
||||
iter.m_currentContour = 0;
|
||||
iter.m_currentVertex = 0;
|
||||
iter.m_iterateHoles = aIterateHoles;
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
@ -244,11 +638,80 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
return CIterate( aOutline, aOutline );
|
||||
}
|
||||
|
||||
CONST_ITERATOR CIterateWithHoles( int aOutline ) const
|
||||
{
|
||||
return CIterate( aOutline, aOutline, true );
|
||||
}
|
||||
|
||||
CONST_ITERATOR CIterate() const
|
||||
{
|
||||
return CIterate( 0, OutlineCount() - 1 );
|
||||
}
|
||||
|
||||
CONST_ITERATOR CIterateWithHoles() const
|
||||
{
|
||||
return CIterate( 0, OutlineCount() - 1, true );
|
||||
}
|
||||
|
||||
ITERATOR IterateFromVertexWithHoles( int aGlobalIdx )
|
||||
{
|
||||
// Build iterator
|
||||
ITERATOR iter = IterateWithHoles();
|
||||
|
||||
// Get the relative indices of the globally indexed vertex
|
||||
VERTEX_INDEX indices;
|
||||
|
||||
if( !GetRelativeIndices( aGlobalIdx, &indices ) )
|
||||
throw( std::out_of_range( "aGlobalIndex-th vertex does not exist" ) );
|
||||
|
||||
// Adjust where the iterator is pointing
|
||||
iter.m_currentPolygon = indices.m_polygon;
|
||||
iter.m_currentContour = indices.m_contour;
|
||||
iter.m_currentVertex = indices.m_vertex;
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
///> Returns an iterator object, for iterating between aFirst and aLast outline, with or
|
||||
/// without holes (default: without)
|
||||
SEGMENT_ITERATOR IterateSegments( int aFirst, int aLast, bool aIterateHoles = false )
|
||||
{
|
||||
SEGMENT_ITERATOR iter;
|
||||
|
||||
iter.m_poly = this;
|
||||
iter.m_currentPolygon = aFirst;
|
||||
iter.m_lastPolygon = aLast < 0 ? OutlineCount() - 1 : aLast;
|
||||
iter.m_currentContour = 0;
|
||||
iter.m_currentSegment = 0;
|
||||
iter.m_iterateHoles = aIterateHoles;
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
///> Returns an iterator object, for iterating aPolygonIdx-th polygon edges
|
||||
SEGMENT_ITERATOR IterateSegments( int aPolygonIdx )
|
||||
{
|
||||
return IterateSegments( aPolygonIdx, aPolygonIdx );
|
||||
}
|
||||
|
||||
///> Returns an iterator object, for all outlines in the set (no holes)
|
||||
SEGMENT_ITERATOR IterateSegments()
|
||||
{
|
||||
return IterateSegments( 0, OutlineCount() - 1 );
|
||||
}
|
||||
|
||||
///> Returns an iterator object, for all outlines in the set (with holes)
|
||||
SEGMENT_ITERATOR IterateSegmentsWithHoles()
|
||||
{
|
||||
return IterateSegments( 0, OutlineCount() - 1, true );
|
||||
}
|
||||
|
||||
///> Returns an iterator object, for the aOutline-th outline in the set (with holes)
|
||||
SEGMENT_ITERATOR IterateSegmentsWithHoles( int aOutline )
|
||||
{
|
||||
return IterateSegments( aOutline, aOutline, true );
|
||||
}
|
||||
|
||||
/** operations on polygons use a aFastMode param
|
||||
* if aFastMode is PM_FAST (true) the result can be a weak polygon
|
||||
* if aFastMode is PM_STRICTLY_SIMPLE (false) (default) the result is (theorically) a strictly
|
||||
|
@ -307,6 +770,15 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
///> For aFastMode meaning, see function booleanOp
|
||||
void Simplify( POLYGON_MODE aFastMode );
|
||||
|
||||
/**
|
||||
* Function NormalizeAreaOutlines
|
||||
* Convert a self-intersecting polygon to one (or more) non self-intersecting polygon(s)
|
||||
* Removes null segments.
|
||||
* @return int - the polygon count (always >= 1, because there is at least one polygon)
|
||||
* There are new polygons only if the polygon count is > 1.
|
||||
*/
|
||||
int NormalizeAreaOutlines();
|
||||
|
||||
/// @copydoc SHAPE::Format()
|
||||
const std::string Format() const override;
|
||||
|
||||
|
@ -324,13 +796,59 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
|
||||
const BOX2I BBox( int aClearance = 0 ) const override;
|
||||
|
||||
/**
|
||||
* Function PointOnEdge()
|
||||
*
|
||||
* Checks if point aP lies on an edge or vertex of some of the outlines or holes.
|
||||
* @param aP is the point to check.
|
||||
* @return bool - true if the point lies on the edge of any polygon.
|
||||
*/
|
||||
bool PointOnEdge( const VECTOR2I& aP ) const;
|
||||
|
||||
/**
|
||||
* Function Collide
|
||||
* Checks whether the point aP collides with the inside of the polygon set; if the point
|
||||
* lies on an edge or on a corner of any of the polygons, there is no collision: the edges
|
||||
* does not belong to the polygon itself.
|
||||
* @param aP is the VECTOR2I point whose collision with respect to the poly set
|
||||
* will be tested.
|
||||
* @param aClearance is the security distance; if the point lies closer to the polygon
|
||||
* than aClearance distance, then there is a collision.
|
||||
* @return bool - true if the point aP collides with the polygon; false in any other case.
|
||||
*/
|
||||
bool Collide( const VECTOR2I& aP, int aClearance = 0 ) const override;
|
||||
|
||||
// fixme: add collision support
|
||||
bool Collide( const VECTOR2I& aP, int aClearance = 0 ) const override { return false; }
|
||||
bool Collide( const SEG& aSeg, int aClearance = 0 ) const override { return false; }
|
||||
|
||||
/**
|
||||
* Function CollideVertex
|
||||
* Checks whether aPoint collides with any vertex of any of the contours of the polygon.
|
||||
* @param aPoint is the VECTOR2I point whose collision with respect to the polygon
|
||||
* will be tested.
|
||||
* @param aClearance is the security distance; if \p aPoint lies closer to a vertex than
|
||||
* aClearance distance, then there is a collision.
|
||||
* @param aClosestVertex is the index of the closes vertex to \p aPoint.
|
||||
* @return bool - true if there is a collision, false in any other case.
|
||||
*/
|
||||
bool CollideVertex( const VECTOR2I& aPoint, VERTEX_INDEX& aClosestVertex,
|
||||
int aClearance = 0 );
|
||||
|
||||
///> Returns true is a given subpolygon contains the point aP. If aSubpolyIndex < 0 (default value),
|
||||
///> checks all polygons in the set
|
||||
/**
|
||||
* Function CollideEdge
|
||||
* Checks whether aPoint collides with any edge of any of the contours of the polygon.
|
||||
* @param aPoint is the VECTOR2I point whose collision with respect to the polygon
|
||||
* will be tested.
|
||||
* @param aClearance is the security distance; if \p aPoint lies closer to a vertex than
|
||||
* aClearance distance, then there is a collision.
|
||||
* @param aClosestVertex is the index of the closes vertex to \p aPoint.
|
||||
* @return bool - true if there is a collision, false in any other case.
|
||||
*/
|
||||
bool CollideEdge( const VECTOR2I& aPoint, VERTEX_INDEX& aClosestVertex,
|
||||
int aClearance = 0 );
|
||||
|
||||
///> Returns true if a given subpolygon contains the point aP. If aSubpolyIndex < 0
|
||||
///> (default value), checks all polygons in the set
|
||||
bool Contains( const VECTOR2I& aP, int aSubpolyIndex = -1 ) const;
|
||||
|
||||
///> Returns true if the set is empty (no polygons at all)
|
||||
|
@ -339,15 +857,133 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
return m_polys.size() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function RemoveVertex
|
||||
* deletes the aGlobalIndex-th vertex.
|
||||
* @param aGlobalIndex is the global index of the to-be-removed vertex.
|
||||
*/
|
||||
void RemoveVertex( int aGlobalIndex );
|
||||
|
||||
/**
|
||||
* Function RemoveVertex
|
||||
* deletes the vertex indexed by aIndex (index of polygon, contour and vertex).
|
||||
* @param aindex is the set of relative indices of the to-be-removed vertex.
|
||||
*/
|
||||
void RemoveVertex( VERTEX_INDEX aRelativeIndices );
|
||||
|
||||
///> Removes all outlines & holes (clears) the polygon set.
|
||||
void RemoveAllContours();
|
||||
|
||||
/**
|
||||
* Function RemoveContour
|
||||
* deletes the aContourIdx-th contour of the aPolygonIdx-th polygon in the set.
|
||||
* @param aContourIdx is the index of the contour in the aPolygonIdx-th polygon to be
|
||||
* removed.
|
||||
* @param aPolygonIdx is the index of the polygon in which the to-be-removed contour is.
|
||||
* Defaults to the last polygon in the set.
|
||||
*/
|
||||
void RemoveContour( int aContourIdx, int aPolygonIdx = -1 );
|
||||
|
||||
/**
|
||||
* Function RemoveNullSegments
|
||||
* looks for null segments; ie, segments whose ends are exactly the same and deletes them.
|
||||
* @return int - the number of deleted segments.
|
||||
*/
|
||||
int RemoveNullSegments();
|
||||
|
||||
///> Returns total number of vertices stored in the set.
|
||||
int TotalVertices() const;
|
||||
|
||||
///> Deletes aIdx-th polygon from the set
|
||||
void DeletePolygon( int aIdx );
|
||||
|
||||
/**
|
||||
* Function Chamfer
|
||||
* returns a chamfered version of the aIndex-th polygon.
|
||||
* @param aDistance is the chamfering distance.
|
||||
* @param aIndex is the index of the polygon to be chamfered.
|
||||
* @return POLYGON - A polygon containing the chamfered version of the aIndex-th polygon.
|
||||
*/
|
||||
POLYGON ChamferPolygon( unsigned int aDistance, int aIndex = 0 );
|
||||
|
||||
/**
|
||||
* Function Fillet
|
||||
* returns a filleted version of the aIndex-th polygon.
|
||||
* @param aRadius is the fillet radius.
|
||||
* @param aSegments is the number of segments / fillet.
|
||||
* @param aIndex is the index of the polygon to be filleted
|
||||
* @return POLYGON - A polygon containing the filleted version of the aIndex-th polygon.
|
||||
*/
|
||||
POLYGON FilletPolygon( unsigned int aRadius, unsigned int aSegments, int aIndex = 0 );
|
||||
|
||||
/**
|
||||
* Function Chamfer
|
||||
* returns a chamfered version of the polygon set.
|
||||
* @param aDistance is the chamfering distance.
|
||||
* @return SHAPE_POLY_SET - A set containing the chamfered version of this set.
|
||||
*/
|
||||
SHAPE_POLY_SET Chamfer( int aDistance );
|
||||
|
||||
/**
|
||||
* Function Fillet
|
||||
* returns a filleted version of the polygon set.
|
||||
* @param aRadius is the fillet radius.
|
||||
* @param aSegments is the number of segments / fillet.
|
||||
* @return SHAPE_POLY_SET - A set containing the filleted version of this set.
|
||||
*/
|
||||
SHAPE_POLY_SET Fillet( int aRadius, int aSegments );
|
||||
|
||||
/**
|
||||
* Function DistanceToPolygon
|
||||
* computes the minimum distance between the aIndex-th polygon and aPoint.
|
||||
* @param aPoint is the point whose distance to the aIndex-th polygon has to be measured.
|
||||
* @param aIndex is the index of the polygon whose distace to aPoint has to be measured.
|
||||
* @return int - The minimum distance between aPoint and all the segments of the aIndex-th
|
||||
* polygon. If the point is contained in the polygon, the distance is zero.
|
||||
*/
|
||||
int DistanceToPolygon( VECTOR2I aPoint, int aIndex );
|
||||
|
||||
/**
|
||||
* Function DistanceToPolygon
|
||||
* computes the minimum distance between the aIndex-th polygon and aSegment with a
|
||||
* possible width.
|
||||
* @param aSegment is the segment whose distance to the aIndex-th polygon has to be
|
||||
* measured.
|
||||
* @param aIndex is the index of the polygon whose distace to aPoint has to be measured.
|
||||
* @param aSegmentWidth is the width of the segment; defaults to zero.
|
||||
* @return int - The minimum distance between aSegment and all the segments of the
|
||||
* aIndex-th polygon. If the point is contained in the polygon, the
|
||||
* distance is zero.
|
||||
*/
|
||||
int DistanceToPolygon( SEG aSegment, int aIndex, int aSegmentWidth = 0 );
|
||||
|
||||
/**
|
||||
* Function DistanceToPolygon
|
||||
* computes the minimum distance between aPoint and all the polygons in the set
|
||||
* @param aPoint is the point whose distance to the set has to be measured.
|
||||
* @return int - The minimum distance between aPoint and all the polygons in the set. If
|
||||
* the point is contained in any of the polygons, the distance is zero.
|
||||
*/
|
||||
int Distance( VECTOR2I point );
|
||||
|
||||
/**
|
||||
* Function DistanceToPolygon
|
||||
* computes the minimum distance between aSegment and all the polygons in the set.
|
||||
* @param aSegment is the segment whose distance to the polygon set has to be measured.
|
||||
* @param aSegmentWidth is the width of the segment; defaults to zero.
|
||||
* @return int - The minimum distance between aSegment and all the polygons in the set.
|
||||
* If the point is contained in the polygon, the distance is zero.
|
||||
*/
|
||||
int Distance( SEG aSegment, int aSegmentWidth = 0 );
|
||||
|
||||
/**
|
||||
* Function IsVertexInHole.
|
||||
* checks whether the aGlobalIndex-th vertex belongs to a hole.
|
||||
* @param aGlobalIdx is the index of the vertex.
|
||||
* @return bool - true if the globally indexed aGlobalIdx-th vertex belongs to a hole.
|
||||
*/
|
||||
bool IsVertexInHole( int aGlobalIdx );
|
||||
|
||||
private:
|
||||
|
||||
SHAPE_LINE_CHAIN& getContourForCorner( int aCornerId, int& aIndexWithinContour );
|
||||
|
@ -381,6 +1017,47 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
const ClipperLib::Path convertToClipper( const SHAPE_LINE_CHAIN& aPath, bool aRequiredOrientation );
|
||||
const SHAPE_LINE_CHAIN convertFromClipper( const ClipperLib::Path& aPath );
|
||||
|
||||
/**
|
||||
* containsSingle function
|
||||
* Checks whether the point aP is inside the aSubpolyIndex-th polygon of the polyset. If
|
||||
* the points lies on an edge, the polygon is considered to contain it.
|
||||
* @param aP is the VECTOR2I point whose position with respect to the inside of
|
||||
* the aSubpolyIndex-th polygon will be tested.
|
||||
* @param aSubpolyIndex is an integer specifying which polygon in the set has to be
|
||||
* checked.
|
||||
* @return bool - true if aP is inside aSubpolyIndex-th polygon; false in any other
|
||||
* case.
|
||||
*/
|
||||
bool containsSingle( const VECTOR2I& aP, int aSubpolyIndex ) const;
|
||||
|
||||
/**
|
||||
* Operations ChamferPolygon and FilletPolygon are computed under the private chamferFillet
|
||||
* method; this enum is defined to make the necessary distinction when calling this method
|
||||
* from the public ChamferPolygon and FilletPolygon methods.
|
||||
*/
|
||||
enum CORNER_MODE
|
||||
{
|
||||
CHAMFERED,
|
||||
FILLETED
|
||||
};
|
||||
|
||||
/**
|
||||
* Function chamferFilletPolygon
|
||||
* Returns the camfered or filleted version of the aIndex-th polygon in the set, depending
|
||||
* on the aMode selected
|
||||
* @param aMode represent which action will be taken: CORNER_MODE::CHAMFERED will
|
||||
* return a chamfered version of the polygon, CORNER_MODE::FILLETED will
|
||||
* return a filleted version of the polygon.
|
||||
* @param aDistance is the chamfering distance if aMode = CHAMFERED; if aMode = FILLETED,
|
||||
* is the filleting radius.
|
||||
* @param aIndex is the index of the polygon that will be chamfered/filleted.
|
||||
* @param aSegments is the number of filleting segments if aMode = FILLETED. If aMode =
|
||||
* CHAMFERED, it is unused.
|
||||
* @return POLYGON - the chamfered/filleted version of the polygon.
|
||||
*/
|
||||
POLYGON chamferFilletPolygon( CORNER_MODE aMode, unsigned int aDistance,
|
||||
int aIndex, int aSegments = -1 );
|
||||
|
||||
typedef std::vector<POLYGON> Polyset;
|
||||
|
||||
Polyset m_polys;
|
||||
|
|
|
@ -116,6 +116,16 @@ public:
|
|||
return VECTOR2<CastedType>( (CastedType) x, (CastedType) y );
|
||||
}
|
||||
|
||||
/**
|
||||
* (wxPoint)
|
||||
* implements the cast to wxPoint.
|
||||
* @return wxPoint - the vector cast to wxPoint.
|
||||
*/
|
||||
explicit operator wxPoint() const
|
||||
{
|
||||
return wxPoint( x, y );
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
// virtual ~VECTOR2();
|
||||
|
||||
|
|
|
@ -2361,7 +2361,8 @@ ZONE_CONTAINER* BOARD::InsertArea( int netcode, int iarea, LAYER_ID layer, int x
|
|||
else
|
||||
m_ZoneDescriptorList.push_back( new_area );
|
||||
|
||||
new_area->Outline()->Start( layer, x, y, hatch );
|
||||
new_area->SetHatchStyle( (ZONE_CONTAINER::HATCH_STYLE) hatch );
|
||||
new_area->AppendCorner( wxPoint( x, y ) );
|
||||
|
||||
return new_area;
|
||||
}
|
||||
|
@ -2369,45 +2370,47 @@ ZONE_CONTAINER* BOARD::InsertArea( int netcode, int iarea, LAYER_ID layer, int x
|
|||
|
||||
bool BOARD::NormalizeAreaPolygon( PICKED_ITEMS_LIST * aNewZonesList, ZONE_CONTAINER* aCurrArea )
|
||||
{
|
||||
CPolyLine* curr_polygon = aCurrArea->Outline();
|
||||
|
||||
// mark all areas as unmodified except this one, if modified
|
||||
for( unsigned ia = 0; ia < m_ZoneDescriptorList.size(); ia++ )
|
||||
m_ZoneDescriptorList[ia]->SetLocalFlags( 0 );
|
||||
|
||||
aCurrArea->SetLocalFlags( 1 );
|
||||
|
||||
if( curr_polygon->IsPolygonSelfIntersecting() )
|
||||
if( aCurrArea->Outline()->IsSelfIntersecting() )
|
||||
{
|
||||
std::vector<CPolyLine*>* pa = new std::vector<CPolyLine*>;
|
||||
curr_polygon->UnHatch();
|
||||
int n_poly = aCurrArea->Outline()->NormalizeAreaOutlines( pa );
|
||||
aCurrArea->UnHatch();
|
||||
|
||||
// Normalize copied area and store resulting number of polygons
|
||||
int n_poly = aCurrArea->Outline()->NormalizeAreaOutlines();
|
||||
|
||||
// If clipping has created some polygons, we must add these new copper areas.
|
||||
if( n_poly > 1 )
|
||||
{
|
||||
ZONE_CONTAINER* NewArea;
|
||||
|
||||
// Move the newly created polygons to new areas, removing them from the current area
|
||||
for( int ip = 1; ip < n_poly; ip++ )
|
||||
{
|
||||
// create new copper area and copy poly into it
|
||||
CPolyLine* new_p = (*pa)[ip - 1];
|
||||
// Create new copper area and copy poly into it
|
||||
SHAPE_POLY_SET* new_p = new SHAPE_POLY_SET( aCurrArea->Outline()->UnitSet( ip ) );
|
||||
NewArea = AddArea( aNewZonesList, aCurrArea->GetNetCode(), aCurrArea->GetLayer(),
|
||||
wxPoint(0, 0), CPolyLine::NO_HATCH );
|
||||
wxPoint(0, 0), aCurrArea->GetHatchStyle() );
|
||||
|
||||
// remove the poly that was automatically created for the new area
|
||||
// and replace it with a poly from NormalizeAreaOutlines
|
||||
delete NewArea->Outline();
|
||||
NewArea->SetOutline( new_p );
|
||||
NewArea->Outline()->Hatch();
|
||||
NewArea->Hatch();
|
||||
NewArea->SetLocalFlags( 1 );
|
||||
}
|
||||
|
||||
SHAPE_POLY_SET* new_p = new SHAPE_POLY_SET( aCurrArea->Outline()->UnitSet( 0 ) );
|
||||
delete aCurrArea->Outline();
|
||||
aCurrArea->SetOutline( new_p );
|
||||
}
|
||||
}
|
||||
|
||||
delete pa;
|
||||
}
|
||||
|
||||
curr_polygon->Hatch();
|
||||
aCurrArea->Hatch();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
ZONE_CONTAINER::ZONE_CONTAINER( BOARD* aBoard ) :
|
||||
BOARD_CONNECTED_ITEM( aBoard, PCB_ZONE_AREA_T )
|
||||
{
|
||||
m_CornerSelection = -1;
|
||||
m_CornerSelection = nullptr; // no corner is selected
|
||||
m_IsFilled = false; // fill status : true when the zone is filled
|
||||
m_FillMode = 0; // How to fill areas: 0 = use filled polygons, != 0 fill with segments
|
||||
m_priority = 0;
|
||||
|
@ -65,7 +65,7 @@ ZONE_CONTAINER::ZONE_CONTAINER( BOARD* aBoard ) :
|
|||
SetDoNotAllowTracks( true ); // has meaning only if m_isKeepout == true
|
||||
m_cornerRadius = 0;
|
||||
SetLocalFlags( 0 ); // flags tempoarry used in zone calculations
|
||||
m_Poly = new CPolyLine(); // Outlines
|
||||
m_Poly = new SHAPE_POLY_SET(); // Outlines
|
||||
aBoard->GetZoneSettings().ExportSetting( *this );
|
||||
}
|
||||
|
||||
|
@ -77,10 +77,10 @@ ZONE_CONTAINER::ZONE_CONTAINER( const ZONE_CONTAINER& aZone ) :
|
|||
|
||||
// Should the copy be on the same net?
|
||||
SetNetCode( aZone.GetNetCode() );
|
||||
m_Poly = new CPolyLine( *aZone.m_Poly );
|
||||
m_Poly = new SHAPE_POLY_SET( *aZone.m_Poly );
|
||||
|
||||
// For corner moving, corner index to drag, or -1 if no selection
|
||||
m_CornerSelection = -1;
|
||||
// For corner moving, corner index to drag, or nullptr if no selection
|
||||
m_CornerSelection = nullptr;
|
||||
m_IsFilled = aZone.m_IsFilled;
|
||||
m_ZoneClearance = aZone.m_ZoneClearance; // clearance value
|
||||
m_ZoneMinThickness = aZone.m_ZoneMinThickness;
|
||||
|
@ -101,6 +101,10 @@ ZONE_CONTAINER::ZONE_CONTAINER( const ZONE_CONTAINER& aZone ) :
|
|||
m_cornerSmoothingType = aZone.m_cornerSmoothingType;
|
||||
m_cornerRadius = aZone.m_cornerRadius;
|
||||
|
||||
m_hatchStyle = aZone.m_hatchStyle;
|
||||
m_hatchPitch = aZone.m_hatchPitch;
|
||||
m_HatchLines = aZone.m_HatchLines;
|
||||
|
||||
SetLocalFlags( aZone.GetLocalFlags() );
|
||||
}
|
||||
|
||||
|
@ -109,9 +113,11 @@ ZONE_CONTAINER& ZONE_CONTAINER::operator=( const ZONE_CONTAINER& aOther )
|
|||
{
|
||||
BOARD_CONNECTED_ITEM::operator=( aOther );
|
||||
|
||||
m_Poly->RemoveAllContours();
|
||||
m_Poly->Copy( aOther.m_Poly ); // copy outlines
|
||||
m_CornerSelection = -1; // for corner moving, corner index to drag or -1 if no selection
|
||||
// Replace the outlines for aOther outlines.
|
||||
delete m_Poly;
|
||||
m_Poly = new SHAPE_POLY_SET( *aOther.m_Poly );
|
||||
|
||||
m_CornerSelection = nullptr; // for corner moving, corner index to (null if no selection)
|
||||
m_ZoneClearance = aOther.m_ZoneClearance; // clearance value
|
||||
m_ZoneMinThickness = aOther.m_ZoneMinThickness;
|
||||
m_FillMode = aOther.m_FillMode; // filling mode (segments/polygons)
|
||||
|
@ -119,9 +125,9 @@ ZONE_CONTAINER& ZONE_CONTAINER::operator=( const ZONE_CONTAINER& aOther )
|
|||
m_PadConnection = aOther.m_PadConnection;
|
||||
m_ThermalReliefGap = aOther.m_ThermalReliefGap;
|
||||
m_ThermalReliefCopperBridge = aOther.m_ThermalReliefCopperBridge;
|
||||
m_Poly->SetHatchStyle( aOther.m_Poly->GetHatchStyle() );
|
||||
m_Poly->SetHatchPitch( aOther.m_Poly->GetHatchPitch() );
|
||||
m_Poly->m_HatchLines = aOther.m_Poly->m_HatchLines; // copy vector <CSegment>
|
||||
SetHatchStyle( aOther.GetHatchStyle() );
|
||||
SetHatchPitch( aOther.GetHatchPitch() );
|
||||
m_HatchLines = aOther.m_HatchLines; // copy vector <SEG>
|
||||
m_FilledPolysList.RemoveAllContours();
|
||||
m_FilledPolysList.Append( aOther.m_FilledPolysList );
|
||||
m_FillSegmList.clear();
|
||||
|
@ -134,7 +140,8 @@ ZONE_CONTAINER& ZONE_CONTAINER::operator=( const ZONE_CONTAINER& aOther )
|
|||
ZONE_CONTAINER::~ZONE_CONTAINER()
|
||||
{
|
||||
delete m_Poly;
|
||||
m_Poly = NULL;
|
||||
delete m_smoothedPoly;
|
||||
delete m_CornerSelection;
|
||||
}
|
||||
|
||||
|
||||
|
@ -159,9 +166,13 @@ bool ZONE_CONTAINER::UnFill()
|
|||
|
||||
const wxPoint& ZONE_CONTAINER::GetPosition() const
|
||||
{
|
||||
static const wxPoint dummy;
|
||||
const WX_VECTOR_CONVERTER* pos;
|
||||
|
||||
return m_Poly ? GetCornerPosition( 0 ) : dummy;
|
||||
// The retrieved vertex is a VECTOR2I. Casting it to a union WX_VECTOR_CONVERTER, we can later
|
||||
// return the object shaped as a wxPoint. See the definition of the union in class_zone.h for
|
||||
// more information on this hack.
|
||||
pos = reinterpret_cast<const WX_VECTOR_CONVERTER*>( &GetCornerPosition( 0 ) );
|
||||
return pos->wx;
|
||||
}
|
||||
|
||||
|
||||
|
@ -195,38 +206,29 @@ void ZONE_CONTAINER::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE aDrawMod
|
|||
color.a = 0.588;
|
||||
|
||||
// draw the lines
|
||||
int i_start_contour = 0;
|
||||
std::vector<wxPoint> lines;
|
||||
lines.reserve( (GetNumCorners() * 2) + 2 );
|
||||
|
||||
for( int ic = 0; ic < GetNumCorners(); ic++ )
|
||||
// Iterate through the segments of the outline
|
||||
for( auto iterator = m_Poly->IterateSegmentsWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
seg_start = GetCornerPosition( ic ) + offset;
|
||||
// Create the segment
|
||||
SEG segment = *iterator;
|
||||
|
||||
if( !m_Poly->m_CornersList.IsEndContour( ic ) && ic < GetNumCorners() - 1 )
|
||||
{
|
||||
seg_end = GetCornerPosition( ic + 1 ) + offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
seg_end = GetCornerPosition( i_start_contour ) + offset;
|
||||
i_start_contour = ic + 1;
|
||||
}
|
||||
|
||||
lines.push_back( seg_start );
|
||||
lines.push_back( seg_end );
|
||||
lines.push_back( static_cast<wxPoint>( segment.A ) + offset );
|
||||
lines.push_back( static_cast<wxPoint>( segment.B ) + offset );
|
||||
}
|
||||
|
||||
GRLineArray( panel->GetClipBox(), DC, lines, 0, color );
|
||||
|
||||
// draw hatches
|
||||
lines.clear();
|
||||
lines.reserve( (m_Poly->m_HatchLines.size() * 2) + 2 );
|
||||
lines.reserve( (m_HatchLines.size() * 2) + 2 );
|
||||
|
||||
for( unsigned ic = 0; ic < m_Poly->m_HatchLines.size(); ic++ )
|
||||
for( unsigned ic = 0; ic < m_HatchLines.size(); ic++ )
|
||||
{
|
||||
seg_start = m_Poly->m_HatchLines[ic].m_Start + offset;
|
||||
seg_end = m_Poly->m_HatchLines[ic].m_End + offset;
|
||||
seg_start = static_cast<wxPoint>( m_HatchLines[ic].A ) + offset;
|
||||
seg_end = static_cast<wxPoint>( m_HatchLines[ic].B ) + offset;
|
||||
lines.push_back( seg_start );
|
||||
lines.push_back( seg_end );
|
||||
}
|
||||
|
@ -357,7 +359,7 @@ const EDA_RECT ZONE_CONTAINER::GetBoundingBox() const
|
|||
|
||||
for( int i = 0; i<count; ++i )
|
||||
{
|
||||
wxPoint corner = GetCornerPosition( i );
|
||||
wxPoint corner = static_cast<wxPoint>( GetCornerPosition( i ) );
|
||||
|
||||
ymax = std::max( ymax, corner.y );
|
||||
xmax = std::max( xmax, corner.x );
|
||||
|
@ -391,45 +393,63 @@ void ZONE_CONTAINER::DrawWhileCreateOutline( EDA_DRAW_PANEL* panel, wxDC* DC,
|
|||
color = COLOR4D( DARKDARKGRAY );
|
||||
}
|
||||
|
||||
// draw the lines
|
||||
wxPoint start_contour_pos = GetCornerPosition( 0 );
|
||||
int icmax = GetNumCorners() - 1;
|
||||
// Object to iterate through the corners of the outlines
|
||||
SHAPE_POLY_SET::ITERATOR iterator = m_Poly->Iterate();
|
||||
|
||||
for( int ic = 0; ic <= icmax; ic++ )
|
||||
// Segment start and end
|
||||
VECTOR2I seg_start, seg_end;
|
||||
|
||||
// Remember the first point of this contour
|
||||
VECTOR2I contour_first_point = *iterator;
|
||||
|
||||
// Iterate through all the corners of the outlines and build the segments to draw
|
||||
while( iterator )
|
||||
{
|
||||
int xi = GetCornerPosition( ic ).x;
|
||||
int yi = GetCornerPosition( ic ).y;
|
||||
int xf, yf;
|
||||
// Get the first point of the current segment
|
||||
seg_start = *iterator;
|
||||
|
||||
if( !m_Poly->m_CornersList.IsEndContour( ic ) && ic < icmax )
|
||||
// Get the last point of the current segment, handling the case where the end of the
|
||||
// contour is reached, when the last point of the segment is the first point of the
|
||||
// contour
|
||||
if( !iterator.IsEndContour() )
|
||||
{
|
||||
is_close_segment = false;
|
||||
xf = GetCornerPosition( ic + 1 ).x;
|
||||
yf = GetCornerPosition( ic + 1 ).y;
|
||||
|
||||
if( m_Poly->m_CornersList.IsEndContour( ic + 1 ) || (ic == icmax - 1) )
|
||||
current_gr_mode = GR_XOR;
|
||||
else
|
||||
// Set GR mode to default
|
||||
current_gr_mode = draw_mode;
|
||||
|
||||
SHAPE_POLY_SET::ITERATOR iterator_copy = iterator;
|
||||
iterator_copy++;
|
||||
if( iterator_copy.IsEndContour() )
|
||||
current_gr_mode = GR_XOR;
|
||||
|
||||
is_close_segment = false;
|
||||
|
||||
iterator++;
|
||||
seg_end = *iterator;
|
||||
}
|
||||
else // Draw the line from last corner to the first corner of the current contour
|
||||
else
|
||||
{
|
||||
is_close_segment = true;
|
||||
current_gr_mode = GR_XOR;
|
||||
xf = start_contour_pos.x;
|
||||
yf = start_contour_pos.y;
|
||||
|
||||
// Prepare the next contour for drawing, if exists
|
||||
if( ic < icmax )
|
||||
start_contour_pos = GetCornerPosition( ic + 1 );
|
||||
seg_end = contour_first_point;
|
||||
|
||||
// Reassign first point of the contour to the next contour start
|
||||
iterator++;
|
||||
|
||||
if( iterator )
|
||||
contour_first_point = *iterator;
|
||||
|
||||
// Set GR mode to XOR
|
||||
current_gr_mode = GR_XOR;
|
||||
}
|
||||
|
||||
GRSetDrawMode( DC, current_gr_mode );
|
||||
|
||||
if( is_close_segment )
|
||||
GRLine( panel->GetClipBox(), DC, xi, yi, xf, yf, 0, WHITE );
|
||||
GRLine( panel->GetClipBox(), DC, seg_start.x, seg_start.y, seg_end.x, seg_end.y, 0,
|
||||
WHITE );
|
||||
else
|
||||
GRLine( panel->GetClipBox(), DC, xi, yi, xf, yf, 0, color );
|
||||
GRLine( panel->GetClipBox(), DC, seg_start.x, seg_start.y, seg_end.x, seg_end.y, 0,
|
||||
color );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -462,90 +482,109 @@ void ZONE_CONTAINER::SetCornerRadius( unsigned int aRadius )
|
|||
|
||||
bool ZONE_CONTAINER::HitTest( const wxPoint& aPosition ) const
|
||||
{
|
||||
if( HitTestForCorner( aPosition ) >= 0 )
|
||||
return true;
|
||||
|
||||
if( HitTestForEdge( aPosition ) >= 0 )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return HitTestForCorner( aPosition ) || HitTestForEdge( aPosition );
|
||||
}
|
||||
|
||||
|
||||
void ZONE_CONTAINER::SetSelectedCorner( const wxPoint& aPosition )
|
||||
{
|
||||
m_CornerSelection = HitTestForCorner( aPosition );
|
||||
SHAPE_POLY_SET::VERTEX_INDEX corner;
|
||||
|
||||
if( m_CornerSelection < 0 )
|
||||
m_CornerSelection = HitTestForEdge( aPosition );
|
||||
// If there is some corner to be selected, assign it to m_CornerSelection
|
||||
if( HitTestForCorner( aPosition, corner ) || HitTestForEdge( aPosition, corner ) )
|
||||
{
|
||||
if( m_CornerSelection == nullptr )
|
||||
m_CornerSelection = new SHAPE_POLY_SET::VERTEX_INDEX;
|
||||
|
||||
*m_CornerSelection = corner;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Zones outlines have no thickness, so it Hit Test functions
|
||||
// we must have a default distance between the test point
|
||||
// and a corner or a zone edge:
|
||||
#define MAX_DIST_IN_MM 0.25
|
||||
|
||||
int ZONE_CONTAINER::HitTestForCorner( const wxPoint& refPos ) const
|
||||
bool ZONE_CONTAINER::HitTestForCorner( const wxPoint& refPos,
|
||||
SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const
|
||||
{
|
||||
int distmax = Millimeter2iu( MAX_DIST_IN_MM );
|
||||
return m_Poly->HitTestForCorner( refPos, distmax );
|
||||
|
||||
return m_Poly->CollideVertex( VECTOR2I( refPos ), aCornerHit, distmax );
|
||||
}
|
||||
|
||||
|
||||
int ZONE_CONTAINER::HitTestForEdge( const wxPoint& refPos ) const
|
||||
bool ZONE_CONTAINER::HitTestForCorner( const wxPoint& refPos ) const
|
||||
{
|
||||
SHAPE_POLY_SET::VERTEX_INDEX dummy;
|
||||
return HitTestForCorner( refPos, dummy );
|
||||
}
|
||||
|
||||
|
||||
bool ZONE_CONTAINER::HitTestForEdge( const wxPoint& refPos,
|
||||
SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const
|
||||
{
|
||||
int distmax = Millimeter2iu( MAX_DIST_IN_MM );
|
||||
return m_Poly->HitTestForEdge( refPos, distmax );
|
||||
|
||||
return m_Poly->CollideEdge( VECTOR2I( refPos ), aCornerHit, distmax );
|
||||
}
|
||||
|
||||
|
||||
bool ZONE_CONTAINER::HitTestForEdge( const wxPoint& refPos ) const
|
||||
{
|
||||
SHAPE_POLY_SET::VERTEX_INDEX dummy;
|
||||
return HitTestForEdge( refPos, dummy );
|
||||
}
|
||||
|
||||
|
||||
bool ZONE_CONTAINER::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
|
||||
{
|
||||
EDA_RECT arect = aRect;
|
||||
arect.Inflate( aAccuracy );
|
||||
EDA_RECT bbox = m_Poly->GetBoundingBox();
|
||||
// Convert to BOX2I
|
||||
BOX2I aBox = aRect;
|
||||
aBox.Inflate( aAccuracy );
|
||||
BOX2I bbox = m_Poly->BBox();
|
||||
bbox.Normalize();
|
||||
|
||||
if( aContained )
|
||||
return arect.Contains( bbox );
|
||||
else // Test for intersection between aRect and the polygon
|
||||
return aBox.Contains( bbox );
|
||||
else // Test for intersection between aBox and the polygon
|
||||
// For a polygon, using its bounding box has no sense here
|
||||
{
|
||||
// Fast test: if aRect is outside the polygon bounding box,
|
||||
// Fast test: if aBox is outside the polygon bounding box,
|
||||
// rectangles cannot intersect
|
||||
if( ! bbox.Intersects( arect ) )
|
||||
if( ! bbox.Intersects( aBox ) )
|
||||
return false;
|
||||
|
||||
// aRect is inside the polygon bounding box,
|
||||
// aBox is inside the polygon bounding box,
|
||||
// and can intersect the polygon: use a fine test.
|
||||
// aRect intersects the polygon if at least one aRect corner
|
||||
// aBox intersects the polygon if at least one aBox corner
|
||||
// is inside the polygon
|
||||
wxPoint corner = arect.GetOrigin();
|
||||
wxPoint corner = static_cast<wxPoint>( aBox.GetOrigin() );
|
||||
|
||||
if( HitTestInsideZone( corner ) )
|
||||
return true;
|
||||
|
||||
corner.x = arect.GetEnd().x;
|
||||
corner.x = aBox.GetEnd().x;
|
||||
|
||||
if( HitTestInsideZone( corner ) )
|
||||
return true;
|
||||
|
||||
corner = arect.GetEnd();
|
||||
corner = static_cast<wxPoint>( aBox.GetEnd() );
|
||||
|
||||
if( HitTestInsideZone( corner ) )
|
||||
return true;
|
||||
|
||||
corner.x = arect.GetOrigin().x;
|
||||
corner.x = aBox.GetOrigin().x;
|
||||
|
||||
if( HitTestInsideZone( corner ) )
|
||||
return true;
|
||||
|
||||
// No corner inside arect, but outlines can intersect arect
|
||||
// if one of outline corners is inside arect
|
||||
int count = m_Poly->GetCornersCount();
|
||||
// No corner inside aBox, but outlines can intersect aBox
|
||||
// if one of outline corners is inside aBox
|
||||
int count = m_Poly->TotalVertices();
|
||||
for( int ii =0; ii < count; ii++ )
|
||||
{
|
||||
if( arect.Contains( m_Poly->GetPos( ii ) ) )
|
||||
if( aBox.Contains( m_Poly->Vertex( ii ) ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -595,9 +634,8 @@ void ZONE_CONTAINER::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
|
|||
|
||||
// Display Cutout instead of Outline for holes inside a zone
|
||||
// i.e. when num contour !=0
|
||||
int ncont = m_Poly->GetContour( m_CornerSelection );
|
||||
|
||||
if( ncont )
|
||||
// Check whether the selected corner is in a hole; i.e., in any contour but the first one.
|
||||
if( m_CornerSelection != nullptr && m_CornerSelection->m_contour > 0 )
|
||||
msg << wxT( " " ) << _( "(Cutout)" );
|
||||
|
||||
aList.push_back( MSG_PANEL_ITEM( _( "Type" ), msg, DARKCYAN ) );
|
||||
|
@ -648,7 +686,7 @@ void ZONE_CONTAINER::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
|
|||
|
||||
aList.push_back( MSG_PANEL_ITEM( _( "Layer" ), GetLayerName(), BROWN ) );
|
||||
|
||||
msg.Printf( wxT( "%d" ), (int) m_Poly->m_CornersList.GetCornersCount() );
|
||||
msg.Printf( wxT( "%d" ), (int) m_Poly->TotalVertices() );
|
||||
aList.push_back( MSG_PANEL_ITEM( _( "Corners" ), msg, BLUE ) );
|
||||
|
||||
if( m_FillMode )
|
||||
|
@ -659,7 +697,7 @@ void ZONE_CONTAINER::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
|
|||
aList.push_back( MSG_PANEL_ITEM( _( "Fill Mode" ), msg, BROWN ) );
|
||||
|
||||
// Useful for statistics :
|
||||
msg.Printf( wxT( "%d" ), (int) m_Poly->m_HatchLines.size() );
|
||||
msg.Printf( wxT( "%d" ), (int) m_HatchLines.size() );
|
||||
aList.push_back( MSG_PANEL_ITEM( _( "Hatch Lines" ), msg, BLUE ) );
|
||||
|
||||
if( !m_FilledPolysList.IsEmpty() )
|
||||
|
@ -675,12 +713,9 @@ void ZONE_CONTAINER::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
|
|||
void ZONE_CONTAINER::Move( const wxPoint& offset )
|
||||
{
|
||||
/* move outlines */
|
||||
for( unsigned ii = 0; ii < m_Poly->m_CornersList.GetCornersCount(); ii++ )
|
||||
{
|
||||
SetCornerPosition( ii, GetCornerPosition( ii ) + offset );
|
||||
}
|
||||
m_Poly->Move( VECTOR2I( offset ) );
|
||||
|
||||
m_Poly->Hatch();
|
||||
Hatch();
|
||||
|
||||
m_FilledPolysList.Move( VECTOR2I( offset.x, offset.y ) );
|
||||
|
||||
|
@ -694,23 +729,10 @@ void ZONE_CONTAINER::Move( const wxPoint& offset )
|
|||
|
||||
void ZONE_CONTAINER::MoveEdge( const wxPoint& offset, int aEdge )
|
||||
{
|
||||
// Move the start point of the selected edge:
|
||||
SetCornerPosition( aEdge, GetCornerPosition( aEdge ) + offset );
|
||||
m_Poly->Edge( aEdge ).A += VECTOR2I( offset );
|
||||
m_Poly->Edge( aEdge ).B += VECTOR2I( offset );
|
||||
|
||||
// Move the end point of the selected edge:
|
||||
if( m_Poly->m_CornersList.IsEndContour( aEdge ) || aEdge == GetNumCorners() - 1 )
|
||||
{
|
||||
int icont = m_Poly->GetContour( aEdge );
|
||||
aEdge = m_Poly->GetContourStart( icont );
|
||||
}
|
||||
else
|
||||
{
|
||||
aEdge++;
|
||||
}
|
||||
|
||||
SetCornerPosition( aEdge, GetCornerPosition( aEdge ) + offset );
|
||||
|
||||
m_Poly->Hatch();
|
||||
Hatch();
|
||||
}
|
||||
|
||||
|
||||
|
@ -718,18 +740,18 @@ void ZONE_CONTAINER::Rotate( const wxPoint& centre, double angle )
|
|||
{
|
||||
wxPoint pos;
|
||||
|
||||
for( unsigned ic = 0; ic < m_Poly->m_CornersList.GetCornersCount(); ic++ )
|
||||
for( auto iterator = m_Poly->IterateWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
pos = m_Poly->m_CornersList.GetPos( ic );
|
||||
pos = static_cast<wxPoint>( *iterator );
|
||||
RotatePoint( &pos, centre, angle );
|
||||
m_Poly->SetX( ic, pos.x );
|
||||
m_Poly->SetY( ic, pos.y );
|
||||
iterator->x = pos.x;
|
||||
iterator->y = pos.y;
|
||||
}
|
||||
|
||||
m_Poly->Hatch();
|
||||
Hatch();
|
||||
|
||||
/* rotate filled areas: */
|
||||
for( SHAPE_POLY_SET::ITERATOR ic = m_FilledPolysList.Iterate(); ic; ++ic )
|
||||
for( auto ic = m_FilledPolysList.Iterate(); ic; ++ic )
|
||||
RotatePoint( &ic->x, &ic->y, centre.x, centre.y, angle );
|
||||
|
||||
for( unsigned ic = 0; ic < m_FillSegmList.size(); ic++ )
|
||||
|
@ -750,15 +772,15 @@ void ZONE_CONTAINER::Flip( const wxPoint& aCentre )
|
|||
|
||||
void ZONE_CONTAINER::Mirror( const wxPoint& mirror_ref )
|
||||
{
|
||||
for( unsigned ic = 0; ic < m_Poly->m_CornersList.GetCornersCount(); ic++ )
|
||||
for( auto iterator = m_Poly->IterateWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
int py = mirror_ref.y - m_Poly->m_CornersList.GetY( ic );
|
||||
m_Poly->m_CornersList.SetY( ic, py + mirror_ref.y );
|
||||
int py = mirror_ref.y - iterator->y;
|
||||
iterator->y = py + mirror_ref.y;
|
||||
}
|
||||
|
||||
m_Poly->Hatch();
|
||||
Hatch();
|
||||
|
||||
for( SHAPE_POLY_SET::ITERATOR ic = m_FilledPolysList.Iterate(); ic; ++ic )
|
||||
for( auto ic = m_FilledPolysList.Iterate(); ic; ++ic )
|
||||
{
|
||||
int py = mirror_ref.y - ic->y;
|
||||
ic->y = py + mirror_ref.y;
|
||||
|
@ -786,28 +808,28 @@ void ZONE_CONTAINER::AddPolygon( std::vector< wxPoint >& aPolygon )
|
|||
if( aPolygon.empty() )
|
||||
return;
|
||||
|
||||
SHAPE_LINE_CHAIN outline;
|
||||
|
||||
// Create an outline and populate it with the points of aPolygon
|
||||
for( unsigned i = 0; i < aPolygon.size(); i++ )
|
||||
{
|
||||
if( i == 0 )
|
||||
m_Poly->Start( GetLayer(), aPolygon[i].x, aPolygon[i].y, GetHatchStyle() );
|
||||
else
|
||||
AppendCorner( aPolygon[i] );
|
||||
outline.Append( VECTOR2I( aPolygon[i] ) );
|
||||
}
|
||||
|
||||
m_Poly->CloseLastContour();
|
||||
outline.SetClosed( true );
|
||||
|
||||
// Add the outline as a new polygon in the polygon set
|
||||
m_Poly->AddOutline( outline );
|
||||
}
|
||||
|
||||
|
||||
|
||||
wxString ZONE_CONTAINER::GetSelectMenuText() const
|
||||
{
|
||||
wxString text;
|
||||
NETINFO_ITEM* net;
|
||||
BOARD* board = GetBoard();
|
||||
|
||||
int ncont = m_Poly->GetContour( m_CornerSelection );
|
||||
|
||||
if( ncont )
|
||||
// Check whether the selected contour is a hole (contour index > 0)
|
||||
if( m_CornerSelection != nullptr && m_CornerSelection->m_contour > 0 )
|
||||
text << wxT( " " ) << _( "(Cutout)" );
|
||||
|
||||
if( GetIsKeepout() )
|
||||
|
@ -850,6 +872,204 @@ wxString ZONE_CONTAINER::GetSelectMenuText() const
|
|||
}
|
||||
|
||||
|
||||
int ZONE_CONTAINER::GetHatchPitch() const
|
||||
{
|
||||
return m_hatchPitch;
|
||||
}
|
||||
|
||||
|
||||
void ZONE_CONTAINER::SetHatch( int aHatchStyle, int aHatchPitch, bool aRebuildHatch )
|
||||
{
|
||||
SetHatchPitch( aHatchPitch );
|
||||
m_hatchStyle = (ZONE_CONTAINER::HATCH_STYLE) aHatchStyle;
|
||||
|
||||
if( aRebuildHatch )
|
||||
Hatch();
|
||||
}
|
||||
|
||||
|
||||
void ZONE_CONTAINER::SetHatchPitch( int aPitch )
|
||||
{
|
||||
m_hatchPitch = aPitch;
|
||||
}
|
||||
|
||||
|
||||
void ZONE_CONTAINER::UnHatch()
|
||||
{
|
||||
m_HatchLines.clear();
|
||||
}
|
||||
|
||||
|
||||
// Creates hatch lines inside the outline of the complex polygon
|
||||
// sort function used in ::Hatch to sort points by descending wxPoint.x values
|
||||
bool sortEndsByDescendingX( const VECTOR2I& ref, const VECTOR2I& tst )
|
||||
{
|
||||
return tst.x < ref.x;
|
||||
}
|
||||
|
||||
// Implementation copied from old CPolyLine
|
||||
void ZONE_CONTAINER::Hatch()
|
||||
{
|
||||
UnHatch();
|
||||
|
||||
if( m_hatchStyle == NO_HATCH || m_hatchPitch == 0 || m_Poly->IsEmpty() )
|
||||
return;
|
||||
|
||||
// define range for hatch lines
|
||||
int min_x = m_Poly->Vertex( 0 ).x;
|
||||
int max_x = m_Poly->Vertex( 0 ).x;
|
||||
int min_y = m_Poly->Vertex( 0 ).y;
|
||||
int max_y = m_Poly->Vertex( 0 ).y;
|
||||
|
||||
for( auto iterator = m_Poly->IterateWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
if( iterator->x < min_x )
|
||||
min_x = iterator->x;
|
||||
|
||||
if( iterator->x > max_x )
|
||||
max_x = iterator->x;
|
||||
|
||||
if( iterator->y < min_y )
|
||||
min_y = iterator->y;
|
||||
|
||||
if( iterator->y > max_y )
|
||||
max_y = iterator->y;
|
||||
}
|
||||
|
||||
// Calculate spacing between 2 hatch lines
|
||||
int spacing;
|
||||
|
||||
if( m_hatchStyle == DIAGONAL_EDGE )
|
||||
spacing = m_hatchPitch;
|
||||
else
|
||||
spacing = m_hatchPitch * 2;
|
||||
|
||||
// set the "length" of hatch lines (the length on horizontal axis)
|
||||
int hatch_line_len = m_hatchPitch;
|
||||
|
||||
// To have a better look, give a slope depending on the layer
|
||||
LAYER_NUM layer = GetLayer();
|
||||
int slope_flag = (layer & 1) ? 1 : -1; // 1 or -1
|
||||
double slope = 0.707106 * slope_flag; // 45 degrees slope
|
||||
int max_a, min_a;
|
||||
|
||||
if( slope_flag == 1 )
|
||||
{
|
||||
max_a = KiROUND( max_y - slope * min_x );
|
||||
min_a = KiROUND( min_y - slope * max_x );
|
||||
}
|
||||
else
|
||||
{
|
||||
max_a = KiROUND( max_y - slope * max_x );
|
||||
min_a = KiROUND( min_y - slope * min_x );
|
||||
}
|
||||
|
||||
min_a = (min_a / spacing) * spacing;
|
||||
|
||||
// calculate an offset depending on layer number,
|
||||
// for a better look of hatches on a multilayer board
|
||||
int offset = (layer * 7) / 8;
|
||||
min_a += offset;
|
||||
|
||||
// loop through hatch lines
|
||||
#define MAXPTS 200 // Usually we store only few values per one hatch line
|
||||
// depending on the complexity of the zone outline
|
||||
|
||||
static std::vector<VECTOR2I> pointbuffer;
|
||||
pointbuffer.clear();
|
||||
pointbuffer.reserve( MAXPTS + 2 );
|
||||
|
||||
for( int a = min_a; a < max_a; a += spacing )
|
||||
{
|
||||
// get intersection points for this hatch line
|
||||
|
||||
// Note: because we should have an even number of intersections with the
|
||||
// current hatch line and the zone outline (a closed polygon,
|
||||
// or a set of closed polygons), if an odd count is found
|
||||
// we skip this line (should not occur)
|
||||
pointbuffer.clear();
|
||||
|
||||
// Iterate through all vertices
|
||||
for( auto iterator = m_Poly->IterateSegmentsWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
double x, y, x2, y2;
|
||||
int ok;
|
||||
|
||||
SEG segment = *iterator;
|
||||
|
||||
ok = FindLineSegmentIntersection( a, slope,
|
||||
segment.A.x, segment.A.y,
|
||||
segment.B.x, segment.B.y,
|
||||
&x, &y, &x2, &y2 );
|
||||
|
||||
if( ok )
|
||||
{
|
||||
VECTOR2I point( KiROUND( x ), KiROUND( y ) );
|
||||
pointbuffer.push_back( point );
|
||||
}
|
||||
|
||||
if( ok == 2 )
|
||||
{
|
||||
VECTOR2I point( KiROUND( x2 ), KiROUND( y2 ) );
|
||||
pointbuffer.push_back( point );
|
||||
}
|
||||
|
||||
if( pointbuffer.size() >= MAXPTS ) // overflow
|
||||
{
|
||||
wxASSERT( 0 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure we have found an even intersection points count
|
||||
// because intersections are the ends of segments
|
||||
// inside the polygon(s) and a segment has 2 ends.
|
||||
// if not, this is a strange case (a bug ?) so skip this hatch
|
||||
if( pointbuffer.size() % 2 != 0 )
|
||||
continue;
|
||||
|
||||
// sort points in order of descending x (if more than 2) to
|
||||
// ensure the starting point and the ending point of the same segment
|
||||
// are stored one just after the other.
|
||||
if( pointbuffer.size() > 2 )
|
||||
sort( pointbuffer.begin(), pointbuffer.end(), sortEndsByDescendingX );
|
||||
|
||||
// creates lines or short segments inside the complex polygon
|
||||
for( unsigned ip = 0; ip < pointbuffer.size(); ip += 2 )
|
||||
{
|
||||
int dx = pointbuffer[ip + 1].x - pointbuffer[ip].x;
|
||||
|
||||
// Push only one line for diagonal hatch,
|
||||
// or for small lines < twice the line length
|
||||
// else push 2 small lines
|
||||
if( m_hatchStyle == DIAGONAL_FULL || fabs( dx ) < 2 * hatch_line_len )
|
||||
{
|
||||
m_HatchLines.push_back( SEG( pointbuffer[ip], pointbuffer[ip + 1] ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
double dy = pointbuffer[ip + 1].y - pointbuffer[ip].y;
|
||||
slope = dy / dx;
|
||||
|
||||
if( dx > 0 )
|
||||
dx = hatch_line_len;
|
||||
else
|
||||
dx = -hatch_line_len;
|
||||
|
||||
int x1 = KiROUND( pointbuffer[ip].x + dx );
|
||||
int x2 = KiROUND( pointbuffer[ip + 1].x - dx );
|
||||
int y1 = KiROUND( pointbuffer[ip].y + dx * slope );
|
||||
int y2 = KiROUND( pointbuffer[ip + 1].y - dx * slope );
|
||||
|
||||
m_HatchLines.push_back(SEG(pointbuffer[ip].x, pointbuffer[ip].y, x1, y1));
|
||||
|
||||
m_HatchLines.push_back( SEG( pointbuffer[ip+1].x, pointbuffer[ip+1].y, x2, y2 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BITMAP_DEF ZONE_CONTAINER::GetMenuImage() const
|
||||
{
|
||||
return add_zone_xpm;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
|
@ -38,6 +37,7 @@
|
|||
#include <class_board_connected_item.h>
|
||||
#include <layers_id_colors_and_visibility.h>
|
||||
#include <PolyLine.h>
|
||||
#include <geometry/shape_poly_set.h>
|
||||
#include <class_zone_settings.h>
|
||||
|
||||
|
||||
|
@ -79,6 +79,11 @@ class ZONE_CONTAINER : public BOARD_CONNECTED_ITEM
|
|||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Zone hatch styles
|
||||
*/
|
||||
typedef enum HATCH_STYLE { NO_HATCH, DIAGONAL_FULL, DIAGONAL_EDGE } HATCH_STYLE;
|
||||
|
||||
ZONE_CONTAINER( BOARD* parent );
|
||||
|
||||
ZONE_CONTAINER( const ZONE_CONTAINER& aZone );
|
||||
|
@ -88,6 +93,14 @@ public:
|
|||
|
||||
/**
|
||||
* Function GetPosition
|
||||
*
|
||||
* Returns a reference to the first corner of the polygon set.
|
||||
*
|
||||
* \warning The implementation of this function relies on the fact that wxPoint and VECTOR2I
|
||||
* have the same layout. If you intend to use the returned reference directly, please note
|
||||
* that you are _only_ allowed to use members x and y. Any use on anything that is not one of
|
||||
* these members will have undefined behaviour.
|
||||
*
|
||||
* @return a wxPoint, position of the first point of the outline
|
||||
*/
|
||||
const wxPoint& GetPosition() const override;
|
||||
|
@ -196,8 +209,30 @@ public:
|
|||
int GetMinThickness() const { return m_ZoneMinThickness; }
|
||||
void SetMinThickness( int aMinThickness ) { m_ZoneMinThickness = aMinThickness; }
|
||||
|
||||
int GetSelectedCorner() const { return m_CornerSelection; }
|
||||
void SetSelectedCorner( int aCorner ) { m_CornerSelection = aCorner; }
|
||||
int GetSelectedCorner() const
|
||||
{
|
||||
// Transform relative indices to global index
|
||||
int globalIndex;
|
||||
m_Poly->GetGlobalIndex( *m_CornerSelection, globalIndex );
|
||||
|
||||
return globalIndex;
|
||||
}
|
||||
|
||||
void SetSelectedCorner( int aCorner )
|
||||
{
|
||||
SHAPE_POLY_SET::VERTEX_INDEX selectedCorner;
|
||||
|
||||
// If the global index of the corner is correct, assign it to m_CornerSelection
|
||||
if( m_Poly->GetRelativeIndices( aCorner, &selectedCorner ) )
|
||||
{
|
||||
if( m_CornerSelection == nullptr )
|
||||
m_CornerSelection = new SHAPE_POLY_SET::VERTEX_INDEX;
|
||||
|
||||
*m_CornerSelection = selectedCorner;
|
||||
}
|
||||
else
|
||||
throw( std::out_of_range( "aCorner-th vertex does not exist" ) );
|
||||
}
|
||||
|
||||
///
|
||||
// Like HitTest but selects the current corner to be operated on
|
||||
|
@ -209,10 +244,10 @@ public:
|
|||
std::vector <SEGMENT>& FillSegments() { return m_FillSegmList; }
|
||||
const std::vector <SEGMENT>& FillSegments() const { return m_FillSegmList; }
|
||||
|
||||
CPolyLine* Outline() { return m_Poly; }
|
||||
const CPolyLine* Outline() const { return const_cast< CPolyLine* >( m_Poly ); }
|
||||
SHAPE_POLY_SET* Outline() { return m_Poly; }
|
||||
const SHAPE_POLY_SET* Outline() const { return const_cast< SHAPE_POLY_SET* >( m_Poly ); }
|
||||
|
||||
void SetOutline( CPolyLine* aOutline ) { m_Poly = aOutline; }
|
||||
void SetOutline( SHAPE_POLY_SET* aOutline ) { m_Poly = aOutline; }
|
||||
|
||||
/**
|
||||
* Function HitTest
|
||||
|
@ -231,7 +266,7 @@ public:
|
|||
*/
|
||||
bool HitTestInsideZone( const wxPoint& aPosition ) const
|
||||
{
|
||||
return m_Poly->TestPointInside( aPosition.x, aPosition.y );
|
||||
return m_Poly->Contains( VECTOR2I( aPosition ), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -314,21 +349,41 @@ public:
|
|||
bool aUseNetClearance );
|
||||
/**
|
||||
* Function HitTestForCorner
|
||||
* tests if the given wxPoint near a corner
|
||||
* Set m_CornerSelection to -1 if nothing found, or index of corner
|
||||
* @return true if found
|
||||
* @param refPos : A wxPoint to test
|
||||
* tests if the given wxPoint is near a corner.
|
||||
* @param refPos is the wxPoint to test.
|
||||
* @param aCornerHit [out] is the index of the closest vertex found, useless when return
|
||||
* value is false.
|
||||
* @return bool - true if some corner was found to be closer to refPos than aClearance; false
|
||||
* otherwise.
|
||||
*/
|
||||
int HitTestForCorner( const wxPoint& refPos ) const;
|
||||
bool HitTestForCorner( const wxPoint& refPos, SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const;
|
||||
|
||||
/**
|
||||
* Function HitTestForCorner
|
||||
* tests if the given wxPoint is near a corner.
|
||||
* @param refPos is the wxPoint to test.
|
||||
* @return bool - true if some corner was found to be closer to refPos than aClearance; false
|
||||
* otherwise.
|
||||
*/
|
||||
bool HitTestForCorner( const wxPoint& refPos ) const;
|
||||
|
||||
/**
|
||||
* Function HitTestForEdge
|
||||
* tests if the given wxPoint is near a segment defined by 2 corners.
|
||||
* Set m_CornerSelection to -1 if nothing found, or index of the starting corner of vertice
|
||||
* @return true if found
|
||||
* @param refPos : A wxPoint to test
|
||||
* @param refPos is the wxPoint to test.
|
||||
* @param aCornerHit [out] is the index of the closest vertex found, useless when return
|
||||
* value is false.
|
||||
* @return bool - true if some edge was found to be closer to refPos than aClearance.
|
||||
*/
|
||||
int HitTestForEdge( const wxPoint& refPos ) const;
|
||||
bool HitTestForEdge( const wxPoint& refPos, SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const;
|
||||
|
||||
/**
|
||||
* Function HitTestForEdge
|
||||
* tests if the given wxPoint is near a segment defined by 2 corners.
|
||||
* @param refPos is the wxPoint to test.
|
||||
* @return bool - true if some edge was found to be closer to refPos than aClearance.
|
||||
*/
|
||||
bool HitTestForEdge( const wxPoint& refPos ) const;
|
||||
|
||||
/** @copydoc BOARD_ITEM::HitTest(const EDA_RECT& aRect,
|
||||
* bool aContained = true, int aAccuracy ) const
|
||||
|
@ -410,7 +465,37 @@ public:
|
|||
|
||||
int GetNumCorners( void ) const
|
||||
{
|
||||
return m_Poly->GetCornersCount();
|
||||
return m_Poly->TotalVertices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function Iterate
|
||||
* returns an iterator to visit all points of the zone's main outline without holes.
|
||||
* @return SHAPE_POLY_SET::ITERATOR - an iterator to visit the zone vertices without holes.
|
||||
*/
|
||||
SHAPE_POLY_SET::ITERATOR Iterate()
|
||||
{
|
||||
return m_Poly->Iterate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function IterateWithHoles
|
||||
* returns an iterator to visit all points of the zone's main outline with holes.
|
||||
* @return SHAPE_POLY_SET::ITERATOR - an iterator to visit the zone vertices with holes.
|
||||
*/
|
||||
SHAPE_POLY_SET::ITERATOR IterateWithHoles()
|
||||
{
|
||||
return m_Poly->IterateWithHoles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function CIterateWithHoles
|
||||
* returns an iterator to visit all points of the zone's main outline with holes.
|
||||
* @return SHAPE_POLY_SET::ITERATOR - an iterator to visit the zone vertices with holes.
|
||||
*/
|
||||
SHAPE_POLY_SET::CONST_ITERATOR CIterateWithHoles() const
|
||||
{
|
||||
return m_Poly->CIterateWithHoles();
|
||||
}
|
||||
|
||||
void RemoveAllContours( void )
|
||||
|
@ -418,30 +503,62 @@ public:
|
|||
m_Poly->RemoveAllContours();
|
||||
}
|
||||
|
||||
const wxPoint& GetCornerPosition( int aCornerIndex ) const
|
||||
const VECTOR2I& GetCornerPosition( int aCornerIndex ) const
|
||||
{
|
||||
return m_Poly->GetPos( aCornerIndex );
|
||||
SHAPE_POLY_SET::VERTEX_INDEX index;
|
||||
|
||||
// Convert global to relative indices
|
||||
if( !m_Poly->GetRelativeIndices( aCornerIndex, &index ) )
|
||||
throw( std::out_of_range( "aCornerIndex-th vertex does not exist" ) );
|
||||
|
||||
return m_Poly->CVertex( index );
|
||||
}
|
||||
|
||||
void SetCornerPosition( int aCornerIndex, wxPoint new_pos )
|
||||
{
|
||||
m_Poly->SetX( aCornerIndex, new_pos.x );
|
||||
m_Poly->SetY( aCornerIndex, new_pos.y );
|
||||
SHAPE_POLY_SET::VERTEX_INDEX relativeIndices;
|
||||
|
||||
// Convert global to relative indices
|
||||
if( m_Poly->GetRelativeIndices( aCornerIndex, &relativeIndices ) )
|
||||
{
|
||||
m_Poly->Vertex( relativeIndices ).x = new_pos.x;
|
||||
m_Poly->Vertex( relativeIndices ).y = new_pos.y;
|
||||
}
|
||||
else
|
||||
throw( std::out_of_range( "aCornerIndex-th vertex does not exist" ) );
|
||||
}
|
||||
|
||||
void AppendCorner( wxPoint position )
|
||||
/**
|
||||
* Function NewHole
|
||||
* creates a new hole on the zone; i.e., a new contour on the zone's outline.
|
||||
*/
|
||||
void NewHole()
|
||||
{
|
||||
m_Poly->AppendCorner( position.x, position.y );
|
||||
m_Poly->NewHole();
|
||||
}
|
||||
|
||||
int GetHatchStyle() const
|
||||
/**
|
||||
* Function AppendCorner
|
||||
* @param position is the position of the new corner.
|
||||
* @param aAllowDuplication is a flag to indicate whether it is allowed to add this corner
|
||||
* even if it is duplicated.
|
||||
*/
|
||||
void AppendCorner( wxPoint position, bool aAllowDuplication = false )
|
||||
{
|
||||
return m_Poly->GetHatchStyle();
|
||||
if( m_Poly->OutlineCount() == 0 )
|
||||
m_Poly->NewOutline();
|
||||
|
||||
m_Poly->Append( position.x, position.y, -1, -1, aAllowDuplication );
|
||||
}
|
||||
|
||||
void SetHatchStyle( CPolyLine::HATCH_STYLE aStyle )
|
||||
HATCH_STYLE GetHatchStyle() const
|
||||
{
|
||||
m_Poly->SetHatchStyle( aStyle );
|
||||
return m_hatchStyle;
|
||||
}
|
||||
|
||||
void SetHatchStyle( HATCH_STYLE aStyle )
|
||||
{
|
||||
m_hatchStyle = aStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -485,9 +602,9 @@ public:
|
|||
* Function GetSmoothedPoly
|
||||
* returns a pointer to the corner-smoothed version of
|
||||
* m_Poly if it exists, otherwise it returns m_Poly.
|
||||
* @return CPolyLine* - pointer to the polygon.
|
||||
* @return SHAPE_POLY_SET* - pointer to the polygon.
|
||||
*/
|
||||
CPolyLine* GetSmoothedPoly() const
|
||||
SHAPE_POLY_SET* GetSmoothedPoly() const
|
||||
{
|
||||
if( m_smoothedPoly )
|
||||
return m_smoothedPoly;
|
||||
|
@ -534,6 +651,58 @@ public:
|
|||
void SetDoNotAllowVias( bool aEnable ) { m_doNotAllowVias = aEnable; }
|
||||
void SetDoNotAllowTracks( bool aEnable ) { m_doNotAllowTracks = aEnable; }
|
||||
|
||||
/**
|
||||
* Hatch related methods
|
||||
*/
|
||||
|
||||
/**
|
||||
* Function GetHatchPitch
|
||||
* @return int - the zone hatch pitch in iu.
|
||||
*/
|
||||
int GetHatchPitch() const;
|
||||
|
||||
/**
|
||||
* Function GetDefaultHatchPitchMils
|
||||
* @return int - the default hatch pitch in mils.
|
||||
*
|
||||
* \todo This value is hardcoded, but it should be user configurable.
|
||||
*/
|
||||
static int GetDefaultHatchPitchMils() { return 20; }
|
||||
|
||||
/**
|
||||
* Function SetHatch
|
||||
* sets all hatch parameters for the zone.
|
||||
* @param aHatchStyle is the style of the hatch, specified as one of HATCH_STYLE possible
|
||||
* values.
|
||||
* @param aHatchPitch is the hatch pitch in iu.
|
||||
* @param aRebuildHatch is a flag to indicate whether to re-hatch after having set the
|
||||
* previous parameters.
|
||||
*/
|
||||
void SetHatch( int aHatchStyle, int aHatchPitch, bool aRebuildHatch );
|
||||
|
||||
/**
|
||||
* Function SetHatchPitch
|
||||
* sets the hatch pitch parameter for the zone.
|
||||
* @param aPitch is the hatch pitch in iu.
|
||||
*/
|
||||
void SetHatchPitch( int aPitch );
|
||||
|
||||
/**
|
||||
* Function UnHatch
|
||||
* clears the zone's hatch.
|
||||
*/
|
||||
void UnHatch();
|
||||
|
||||
/**
|
||||
* Function Hatch
|
||||
* computes the hatch lines depending on the hatch parameters and stores it in the zone's
|
||||
* attribute m_HatchLines.
|
||||
*/
|
||||
void Hatch();
|
||||
|
||||
const std::vector<SEG>& GetHatchLines() const { return m_HatchLines; }
|
||||
|
||||
|
||||
#if defined(DEBUG)
|
||||
virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
|
||||
#endif
|
||||
|
@ -543,8 +712,8 @@ public:
|
|||
private:
|
||||
void buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeatures );
|
||||
|
||||
CPolyLine* m_Poly; ///< Outline of the zone.
|
||||
CPolyLine* m_smoothedPoly; // Corner-smoothed version of m_Poly
|
||||
SHAPE_POLY_SET* m_Poly; ///< Outline of the zone.
|
||||
SHAPE_POLY_SET* m_smoothedPoly; // Corner-smoothed version of m_Poly
|
||||
int m_cornerSmoothingType;
|
||||
unsigned int m_cornerRadius;
|
||||
|
||||
|
@ -587,8 +756,8 @@ private:
|
|||
/// How to fill areas: 0 => use filled polygons, 1 => fill with segments.
|
||||
int m_FillMode;
|
||||
|
||||
/// The index of the corner being moved or -1 if no corner is selected.
|
||||
int m_CornerSelection;
|
||||
/// The index of the corner being moved or nullptr if no corner is selected.
|
||||
SHAPE_POLY_SET::VERTEX_INDEX* m_CornerSelection;
|
||||
|
||||
/// Variable used in polygon calculations.
|
||||
int m_localFlgs;
|
||||
|
@ -602,14 +771,52 @@ private:
|
|||
* from outlines (m_Poly) but unlike m_Poly these filled polygons have no hole
|
||||
* (they are all in one piece) In very simple cases m_FilledPolysList is same
|
||||
* as m_Poly. In less simple cases (when m_Poly has holes) m_FilledPolysList is
|
||||
|
||||
|
||||
|
||||
* a polygon equivalent to m_Poly, without holes but with extra outline segment
|
||||
* connecting "holes" with external main outline. In complex cases an outline
|
||||
* described by m_Poly can have many filled areas
|
||||
*/
|
||||
SHAPE_POLY_SET m_FilledPolysList;
|
||||
|
||||
HATCH_STYLE m_hatchStyle; // hatch style, see enum above
|
||||
int m_hatchPitch; // for DIAGONAL_EDGE, distance between 2 hatch lines
|
||||
std::vector<SEG> m_HatchLines; // hatch lines
|
||||
|
||||
/**
|
||||
* Union to handle conversion between references to wxPoint and to VECTOR2I.
|
||||
*
|
||||
* The function GetPosition(), that returns a reference to a wxPoint, needs some existing
|
||||
* wxPoint object that it can point to. The header of this function cannot be changed, as it
|
||||
* overrides the function from the base class BOARD_ITEM. This made sense when ZONE_CONTAINER
|
||||
* was implemented using the legacy CPolyLine class, that worked with wxPoints. However,
|
||||
* m_Poly is now a SHAPE_POLY_SET, whose corners are objects of type VECTOR2I, not wxPoint.
|
||||
* Thus, we cannot directly reference the first corner of m_Poly, so a modified version of it
|
||||
* that can be read as a wxPoint needs to be handled.
|
||||
* Taking advantage of the fact that both wxPoint and VECTOR2I have the same memory layout
|
||||
* (two integers: x, y), this union let us convert a reference to a VECTOR2I into a reference
|
||||
* to a wxPoint.
|
||||
*
|
||||
* The idea is the following: in GetPosition(), m_Poly->GetCornerPosition( 0 ) returns a
|
||||
* reference to the first corner of the polygon set. If we retrieve its memory direction, we
|
||||
* can tell the compiler to cast that pointer to a WX_VECTOR_CONVERTER pointer. We can finally
|
||||
* shape that memory layout as a wxPoint picking the wx member of the union.
|
||||
*
|
||||
* Although this solution is somewhat unstable, as it relies on the fact that the memory
|
||||
* layout is exactly the same, it is the best attempt to keep backwards compatibility while
|
||||
* using the new SHAPE_POLY_SET.
|
||||
*/
|
||||
typedef union {
|
||||
wxPoint wx;
|
||||
VECTOR2I vector;
|
||||
} WX_VECTOR_CONVERTER;
|
||||
|
||||
// Sanity check: assure that the conversion VECTOR2I->wxPoint using the previous union is
|
||||
// correct, making sure that the access for x and y attributes is still safe.
|
||||
static_assert(offsetof(wxPoint,x) == offsetof(VECTOR2I,x),
|
||||
"wxPoint::x and VECTOR2I::x have different offsets");
|
||||
|
||||
static_assert(offsetof(wxPoint,y) == offsetof(VECTOR2I,y),
|
||||
"wxPoint::y and VECTOR2I::y have different offsets");
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ ZONE_SETTINGS::ZONE_SETTINGS()
|
|||
m_ZoneMinThickness = Mils2iu( ZONE_THICKNESS_MIL );
|
||||
m_NetcodeSelection = 0; // Net code selection for the current zone
|
||||
m_CurrentZone_Layer = F_Cu; // Layer used to create the current zone
|
||||
m_Zone_HatchingStyle = CPolyLine::DIAGONAL_EDGE; // Option to show the zone area (outlines only, short hatches or full hatches
|
||||
m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_EDGE; // Option to show the zone area (outlines only, short hatches or full hatches
|
||||
|
||||
m_ArcToSegmentsCount = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF; // Option to select number of segments to approximate a circle
|
||||
// ARC_APPROX_SEGMENTS_COUNT_LOW_DEF
|
||||
|
@ -115,12 +115,11 @@ void ZONE_SETTINGS::ExportSetting( ZONE_CONTAINER& aTarget, bool aFullExport ) c
|
|||
aTarget.SetPriority( m_ZonePriority );
|
||||
aTarget.SetNetCode( m_NetcodeSelection );
|
||||
aTarget.SetLayer( m_CurrentZone_Layer );
|
||||
aTarget.Outline()->SetLayer( m_CurrentZone_Layer );
|
||||
}
|
||||
|
||||
// call SetHatch last, because hatch lines will be rebuilt,
|
||||
// using new parameters values
|
||||
aTarget.Outline()->SetHatch( m_Zone_HatchingStyle, Mils2iu( 20 ), true );
|
||||
aTarget.SetHatch( m_Zone_HatchingStyle, Mils2iu( aTarget.GetDefaultHatchPitchMils() ), true );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -214,15 +214,15 @@ void DIALOG_COPPER_ZONE::initDialog()
|
|||
|
||||
switch( m_settings.m_Zone_HatchingStyle )
|
||||
{
|
||||
case CPolyLine::NO_HATCH:
|
||||
case ZONE_CONTAINER::NO_HATCH:
|
||||
m_OutlineAppearanceCtrl->SetSelection( 0 );
|
||||
break;
|
||||
|
||||
case CPolyLine::DIAGONAL_EDGE:
|
||||
case ZONE_CONTAINER::DIAGONAL_EDGE:
|
||||
m_OutlineAppearanceCtrl->SetSelection( 1 );
|
||||
break;
|
||||
|
||||
case CPolyLine::DIAGONAL_FULL:
|
||||
case ZONE_CONTAINER::DIAGONAL_FULL:
|
||||
m_OutlineAppearanceCtrl->SetSelection( 2 );
|
||||
break;
|
||||
}
|
||||
|
@ -355,15 +355,15 @@ bool DIALOG_COPPER_ZONE::AcceptOptions( bool aPromptForErrors, bool aUseExportab
|
|||
switch( m_OutlineAppearanceCtrl->GetSelection() )
|
||||
{
|
||||
case 0:
|
||||
m_settings.m_Zone_HatchingStyle = CPolyLine::NO_HATCH;
|
||||
m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::NO_HATCH;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
m_settings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_EDGE;
|
||||
m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_EDGE;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
m_settings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_FULL;
|
||||
m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_FULL;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <confirm.h>
|
||||
#include <pcbnew.h>
|
||||
#include <wxPcbStruct.h>
|
||||
#include <class_zone.h>
|
||||
#include <zones.h>
|
||||
#include <base_units.h>
|
||||
|
||||
|
@ -131,15 +132,15 @@ void DIALOG_KEEPOUT_AREA_PROPERTIES::initDialog()
|
|||
|
||||
switch( m_zonesettings.m_Zone_HatchingStyle )
|
||||
{
|
||||
case CPolyLine::NO_HATCH:
|
||||
case ZONE_CONTAINER::NO_HATCH:
|
||||
m_OutlineAppearanceCtrl->SetSelection( 0 );
|
||||
break;
|
||||
|
||||
case CPolyLine::DIAGONAL_EDGE:
|
||||
case ZONE_CONTAINER::DIAGONAL_EDGE:
|
||||
m_OutlineAppearanceCtrl->SetSelection( 1 );
|
||||
break;
|
||||
|
||||
case CPolyLine::DIAGONAL_FULL:
|
||||
case ZONE_CONTAINER::DIAGONAL_FULL:
|
||||
m_OutlineAppearanceCtrl->SetSelection( 2 );
|
||||
break;
|
||||
}
|
||||
|
@ -226,15 +227,15 @@ bool DIALOG_KEEPOUT_AREA_PROPERTIES::AcceptOptionsForKeepOut()
|
|||
switch( m_OutlineAppearanceCtrl->GetSelection() )
|
||||
{
|
||||
case 0:
|
||||
m_zonesettings.m_Zone_HatchingStyle = CPolyLine::NO_HATCH;
|
||||
m_zonesettings.m_Zone_HatchingStyle = ZONE_CONTAINER::NO_HATCH;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
m_zonesettings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_EDGE;
|
||||
m_zonesettings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_EDGE;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
m_zonesettings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_FULL;
|
||||
m_zonesettings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_FULL;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -121,15 +121,15 @@ void DIALOG_NON_COPPER_ZONES_EDITOR::Init()
|
|||
|
||||
switch( m_settings.m_Zone_HatchingStyle )
|
||||
{
|
||||
case CPolyLine::NO_HATCH:
|
||||
case ZONE_CONTAINER::NO_HATCH:
|
||||
m_OutlineAppearanceCtrl->SetSelection( 0 );
|
||||
break;
|
||||
|
||||
case CPolyLine::DIAGONAL_EDGE:
|
||||
case ZONE_CONTAINER::DIAGONAL_EDGE:
|
||||
m_OutlineAppearanceCtrl->SetSelection( 1 );
|
||||
break;
|
||||
|
||||
case CPolyLine::DIAGONAL_FULL:
|
||||
case ZONE_CONTAINER::DIAGONAL_FULL:
|
||||
m_OutlineAppearanceCtrl->SetSelection( 2 );
|
||||
break;
|
||||
}
|
||||
|
@ -202,15 +202,15 @@ void DIALOG_NON_COPPER_ZONES_EDITOR::OnOkClick( wxCommandEvent& event )
|
|||
switch( m_OutlineAppearanceCtrl->GetSelection() )
|
||||
{
|
||||
case 0:
|
||||
m_settings.m_Zone_HatchingStyle = CPolyLine::NO_HATCH;
|
||||
m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::NO_HATCH;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
m_settings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_EDGE;
|
||||
m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_EDGE;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
m_settings.m_Zone_HatchingStyle = CPolyLine::DIAGONAL_FULL;
|
||||
m_settings.m_Zone_HatchingStyle = ZONE_CONTAINER::DIAGONAL_FULL;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
|
@ -650,7 +649,7 @@ void DRC::testKeepoutAreas()
|
|||
if( segm->GetLayer() != area->GetLayer() )
|
||||
continue;
|
||||
|
||||
if( area->Outline()->Distance( segm->GetStart(), segm->GetEnd(),
|
||||
if( area->Outline()->Distance( SEG( segm->GetStart(), segm->GetEnd() ),
|
||||
segm->GetWidth() ) == 0 )
|
||||
{
|
||||
addMarkerToPcb( fillMarker( segm, NULL,
|
||||
|
@ -821,7 +820,7 @@ bool DRC::doTrackKeepoutDrc( TRACK* aRefSeg )
|
|||
if( aRefSeg->GetLayer() != area->GetLayer() )
|
||||
continue;
|
||||
|
||||
if( area->Outline()->Distance( aRefSeg->GetStart(), aRefSeg->GetEnd(),
|
||||
if( area->Outline()->Distance( SEG( aRefSeg->GetStart(), aRefSeg->GetEnd() ),
|
||||
aRefSeg->GetWidth() ) == 0 )
|
||||
{
|
||||
m_currentMarker = fillMarker( aRefSeg, NULL,
|
||||
|
@ -1056,7 +1055,7 @@ bool DRC::doFootprintOverlappingDrc()
|
|||
msg.Printf( _( "footprints '%s' and '%s' overlap on front (top) layer" ),
|
||||
footprint->GetReference().GetData(),
|
||||
candidate->GetReference().GetData() );
|
||||
VECTOR2I& pos = courtyard.Vertex( 0, 0 );
|
||||
VECTOR2I& pos = courtyard.Vertex( 0, 0, -1 );
|
||||
wxPoint loc( pos.x, pos.y );
|
||||
m_currentMarker = fillMarker( loc, DRCE_OVERLAPPING_FOOTPRINTS, msg, m_currentMarker );
|
||||
addMarkerToPcb( m_currentMarker );
|
||||
|
@ -1091,7 +1090,7 @@ bool DRC::doFootprintOverlappingDrc()
|
|||
msg.Printf( _( "footprints '%s' and '%s' overlap on back (bottom) layer" ),
|
||||
footprint->GetReference().GetData(),
|
||||
candidate->GetReference().GetData() );
|
||||
VECTOR2I& pos = courtyard.Vertex( 0, 0 );
|
||||
VECTOR2I& pos = courtyard.Vertex( 0, 0, -1 );
|
||||
wxPoint loc( pos.x, pos.y );
|
||||
m_currentMarker = fillMarker( loc, DRCE_OVERLAPPING_FOOTPRINTS, msg, m_currentMarker );
|
||||
addMarkerToPcb( m_currentMarker );
|
||||
|
|
|
@ -1597,17 +1597,15 @@ void EAGLE_PLUGIN::loadPlain( CPTREE& aGraphics )
|
|||
zone->SetLayer( layer );
|
||||
zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
|
||||
|
||||
CPolyLine::HATCH_STYLE outline_hatch = CPolyLine::DIAGONAL_EDGE;
|
||||
ZONE_CONTAINER::HATCH_STYLE outline_hatch = ZONE_CONTAINER::DIAGONAL_EDGE;
|
||||
|
||||
zone->Outline()->Start( layer, kicad_x( r.x1 ), kicad_y( r.y1 ), outline_hatch );
|
||||
zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y1 ) ) );
|
||||
zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y1 ) ) );
|
||||
zone->AppendCorner( wxPoint( kicad_x( r.x2 ), kicad_y( r.y2 ) ) );
|
||||
zone->AppendCorner( wxPoint( kicad_x( r.x1 ), kicad_y( r.y2 ) ) );
|
||||
zone->Outline()->CloseLastContour();
|
||||
|
||||
// this is not my fault:
|
||||
zone->Outline()->SetHatch(
|
||||
outline_hatch, Mils2iu( zone->Outline()->GetDefaultHatchPitchMils() ), true );
|
||||
zone->SetHatch( outline_hatch, Mils2iu( zone->GetDefaultHatchPitchMils() ), true );
|
||||
}
|
||||
|
||||
m_xpath->pop();
|
||||
|
@ -2736,7 +2734,6 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals )
|
|||
zone->SetLayer( layer );
|
||||
zone->SetNetCode( netCode );
|
||||
|
||||
bool first = true;
|
||||
for( CITER vi = it->second.begin(); vi != it->second.end(); ++vi )
|
||||
{
|
||||
if( vi->first != "vertex" ) // skip <xmlattr> node
|
||||
|
@ -2744,32 +2741,21 @@ void EAGLE_PLUGIN::loadSignals( CPTREE& aSignals )
|
|||
|
||||
EVERTEX v( vi->second );
|
||||
|
||||
// the ZONE_CONTAINER API needs work, as you can see:
|
||||
if( first )
|
||||
{
|
||||
zone->Outline()->Start( layer, kicad_x( v.x ), kicad_y( v.y ),
|
||||
CPolyLine::NO_HATCH);
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
// Append the corner
|
||||
zone->AppendCorner( wxPoint( kicad_x( v.x ), kicad_y( v.y ) ) );
|
||||
}
|
||||
|
||||
zone->Outline()->CloseLastContour();
|
||||
|
||||
// If the pour is a cutout it needs to be set to a keepout
|
||||
if( p.pour == EPOLYGON::CUTOUT )
|
||||
{
|
||||
zone->SetIsKeepout( true );
|
||||
zone->SetDoNotAllowCopperPour( true );
|
||||
zone->Outline()->SetHatchStyle( CPolyLine::NO_HATCH );
|
||||
zone->SetHatchStyle( ZONE_CONTAINER::NO_HATCH );
|
||||
}
|
||||
|
||||
// if spacing is set the zone should be hatched
|
||||
if( p.spacing )
|
||||
zone->Outline()->SetHatch( CPolyLine::DIAGONAL_EDGE,
|
||||
*p.spacing,
|
||||
true );
|
||||
zone->SetHatch( ZONE_CONTAINER::DIAGONAL_EDGE, *p.spacing, true );
|
||||
|
||||
// clearances, etc.
|
||||
zone->SetArcSegmentCount( 32 ); // @todo: should be a constructor default?
|
||||
|
|
|
@ -613,7 +613,7 @@ void PCB_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event )
|
|||
* and start move the new corner
|
||||
*/
|
||||
zone_cont->Draw( m_canvas, &dc, GR_XOR );
|
||||
zone_cont->Outline()->InsertCorner( zone_cont->GetSelectedCorner(), pos.x, pos.y );
|
||||
zone_cont->Outline()->InsertVertex( zone_cont->GetSelectedCorner(), pos );
|
||||
zone_cont->SetSelectedCorner( zone_cont->GetSelectedCorner() + 1 );
|
||||
zone_cont->Draw( m_canvas, &dc, GR_XOR );
|
||||
m_canvas->SetAutoPanRequest( true );
|
||||
|
|
|
@ -1518,13 +1518,13 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const
|
|||
switch( aZone->GetHatchStyle() )
|
||||
{
|
||||
default:
|
||||
case CPolyLine::NO_HATCH: hatch = "none"; break;
|
||||
case CPolyLine::DIAGONAL_EDGE: hatch = "edge"; break;
|
||||
case CPolyLine::DIAGONAL_FULL: hatch = "full"; break;
|
||||
case ZONE_CONTAINER::NO_HATCH: hatch = "none"; break;
|
||||
case ZONE_CONTAINER::DIAGONAL_EDGE: hatch = "edge"; break;
|
||||
case ZONE_CONTAINER::DIAGONAL_FULL: hatch = "full"; break;
|
||||
}
|
||||
|
||||
m_out->Print( 0, " (hatch %s %s)\n", hatch.c_str(),
|
||||
FMT_IU( aZone->Outline()->GetHatchPitch() ).c_str() );
|
||||
FMT_IU( aZone->GetHatchPitch() ).c_str() );
|
||||
|
||||
if( aZone->GetPriority() > 0 )
|
||||
m_out->Print( aNestLevel+1, "(priority %d)\n", aZone->GetPriority() );
|
||||
|
@ -1606,22 +1606,21 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const
|
|||
|
||||
m_out->Print( 0, ")\n" );
|
||||
|
||||
const CPOLYGONS_LIST& cv = aZone->Outline()->m_CornersList;
|
||||
int newLine = 0;
|
||||
|
||||
if( cv.GetCornersCount() )
|
||||
if( aZone->GetNumCorners() )
|
||||
{
|
||||
m_out->Print( aNestLevel+1, "(polygon\n");
|
||||
m_out->Print( aNestLevel+2, "(pts\n" );
|
||||
|
||||
for( unsigned it = 0; it < cv.GetCornersCount(); ++it )
|
||||
for( auto iterator = aZone->IterateWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
if( newLine == 0 )
|
||||
m_out->Print( aNestLevel+3, "(xy %s %s)",
|
||||
FMT_IU( cv.GetX( it ) ).c_str(), FMT_IU( cv.GetY( it ) ).c_str() );
|
||||
FMT_IU( iterator->x ).c_str(), FMT_IU( iterator->y ).c_str() );
|
||||
else
|
||||
m_out->Print( 0, " (xy %s %s)",
|
||||
FMT_IU( cv.GetX( it ) ).c_str(), FMT_IU( cv.GetY( it ) ).c_str() );
|
||||
FMT_IU( iterator->x ).c_str(), FMT_IU( iterator->y ).c_str() );
|
||||
|
||||
if( newLine < 4 )
|
||||
{
|
||||
|
@ -1633,14 +1632,14 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const
|
|||
m_out->Print( 0, "\n" );
|
||||
}
|
||||
|
||||
if( cv.IsEndContour( it ) )
|
||||
if( iterator.IsEndContour() )
|
||||
{
|
||||
if( newLine != 0 )
|
||||
m_out->Print( 0, "\n" );
|
||||
|
||||
m_out->Print( aNestLevel+2, ")\n" );
|
||||
|
||||
if( it+1 != cv.GetCornersCount() )
|
||||
if( !iterator.IsLastPolygon() )
|
||||
{
|
||||
newLine = 0;
|
||||
m_out->Print( aNestLevel+1, ")\n" );
|
||||
|
@ -1662,7 +1661,7 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const
|
|||
m_out->Print( aNestLevel+1, "(filled_polygon\n" );
|
||||
m_out->Print( aNestLevel+2, "(pts\n" );
|
||||
|
||||
for( SHAPE_POLY_SET::CONST_ITERATOR it = fv.CIterate(); it; ++it )
|
||||
for( auto it = fv.CIterate(); it; ++it )
|
||||
{
|
||||
if( newLine == 0 )
|
||||
m_out->Print( aNestLevel+3, "(xy %s %s)",
|
||||
|
@ -1688,7 +1687,7 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const
|
|||
|
||||
m_out->Print( aNestLevel+2, ")\n" );
|
||||
|
||||
if( !it.IsLastContour() )
|
||||
if( !it.IsLastPolygon() )
|
||||
{
|
||||
newLine = 0;
|
||||
m_out->Print( aNestLevel+1, ")\n" );
|
||||
|
|
|
@ -2474,7 +2474,7 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER()
|
|||
{
|
||||
unique_ptr<ZONE_CONTAINER> zc( new ZONE_CONTAINER( m_board ) );
|
||||
|
||||
CPolyLine::HATCH_STYLE outline_hatch = CPolyLine::NO_HATCH;
|
||||
ZONE_CONTAINER::HATCH_STYLE outline_hatch = ZONE_CONTAINER::NO_HATCH;
|
||||
bool sawCorner = false;
|
||||
char buf[1024];
|
||||
char* line;
|
||||
|
@ -2489,17 +2489,13 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER()
|
|||
// e.g. "ZCorner 25650 49500 0"
|
||||
BIU x = biuParse( line + SZ( "ZCorner" ), &data );
|
||||
BIU y = biuParse( data, &data );
|
||||
int flag = intParse( data );
|
||||
|
||||
if( !sawCorner )
|
||||
zc->Outline()->Start( zc->GetLayer(), x, y, outline_hatch );
|
||||
zc->NewHole();
|
||||
else
|
||||
zc->AppendCorner( wxPoint( x, y ) );
|
||||
|
||||
sawCorner = true;
|
||||
|
||||
if( flag )
|
||||
zc->Outline()->CloseLastContour();
|
||||
}
|
||||
|
||||
else if( TESTLINE( "ZInfo" ) ) // general info found
|
||||
|
@ -2540,9 +2536,9 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER()
|
|||
|
||||
switch( *hopt ) // upper case required
|
||||
{
|
||||
case 'N': outline_hatch = CPolyLine::NO_HATCH; break;
|
||||
case 'E': outline_hatch = CPolyLine::DIAGONAL_EDGE; break;
|
||||
case 'F': outline_hatch = CPolyLine::DIAGONAL_FULL; break;
|
||||
case 'N': outline_hatch = ZONE_CONTAINER::NO_HATCH; break;
|
||||
case 'E': outline_hatch = ZONE_CONTAINER::DIAGONAL_EDGE; break;
|
||||
case 'F': outline_hatch = ZONE_CONTAINER::DIAGONAL_FULL; break;
|
||||
|
||||
default:
|
||||
m_error.Printf( wxT( "Bad ZAux for CZONE_CONTAINER '%s'" ), zc->GetNetname().GetData() );
|
||||
|
@ -2724,8 +2720,7 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER()
|
|||
|
||||
// Hatch here, after outlines corners are read
|
||||
// Set hatch here, after outlines corners are read
|
||||
zc->Outline()->SetHatch( outline_hatch,
|
||||
Mils2iu( CPolyLine::GetDefaultHatchPitchMils() ),
|
||||
zc->SetHatch( outline_hatch, Mils2iu( ZONE_CONTAINER::GetDefaultHatchPitchMils() ),
|
||||
true );
|
||||
|
||||
m_board->Add( zc.release() );
|
||||
|
|
|
@ -716,14 +716,14 @@ void PCB_EDIT_FRAME::createPopUpMenuForZones( ZONE_CONTAINER* edge_zone, wxMenu*
|
|||
edge_zone->GetIsKeepout() ? _("Keepout Area") : _( "Zones" ),
|
||||
KiBitmap( add_zone_xpm ) );
|
||||
|
||||
if( edge_zone->HitTestForCorner( RefPos( true ) ) >= 0 )
|
||||
if( edge_zone->HitTestForCorner( RefPos( true ) ) )
|
||||
{
|
||||
AddMenuItem( zones_menu, ID_POPUP_PCB_MOVE_ZONE_CORNER,
|
||||
_( "Move Corner" ), KiBitmap( move_xpm ) );
|
||||
AddMenuItem( zones_menu, ID_POPUP_PCB_DELETE_ZONE_CORNER,
|
||||
_( "Delete Corner" ), KiBitmap( delete_xpm ) );
|
||||
}
|
||||
else if( edge_zone->HitTestForEdge( RefPos( true ) ) >= 0 )
|
||||
else if( edge_zone->HitTestForEdge( RefPos( true ) ) )
|
||||
{
|
||||
AddMenuItem( zones_menu, ID_POPUP_PCB_ADD_ZONE_CORNER,
|
||||
_( "Create Corner" ), KiBitmap( add_corner_xpm ) );
|
||||
|
@ -771,7 +771,7 @@ void PCB_EDIT_FRAME::createPopUpMenuForZones( ZONE_CONTAINER* edge_zone, wxMenu*
|
|||
zones_menu->AppendSeparator();
|
||||
|
||||
if( edge_zone->GetSelectedCorner() >= 0 &&
|
||||
edge_zone->Outline()->IsCutoutContour( edge_zone->GetSelectedCorner() ) )
|
||||
edge_zone->Outline()->IsVertexInHole( edge_zone->GetSelectedCorner() ) )
|
||||
AddMenuItem( zones_menu, ID_POPUP_PCB_DELETE_ZONE_CUTOUT,
|
||||
_( "Delete Cutout" ), KiBitmap( delete_xpm ) );
|
||||
|
||||
|
|
|
@ -177,26 +177,19 @@ void PCB_POLYGON::AddToBoard()
|
|||
zone->SetNetCode( m_netCode );
|
||||
|
||||
// add outline
|
||||
int outline_hatch = CPolyLine::DIAGONAL_EDGE;
|
||||
int outline_hatch = ZONE_CONTAINER::DIAGONAL_EDGE;
|
||||
|
||||
zone->Outline()->Start( m_KiCadLayer, KiROUND( m_outline[i]->x ),
|
||||
KiROUND( m_outline[i]->y ), outline_hatch );
|
||||
|
||||
for( i = 1; i < (int) m_outline.GetCount(); i++ )
|
||||
for( i = 0; i < (int) m_outline.GetCount(); i++ )
|
||||
{
|
||||
zone->AppendCorner( wxPoint( KiROUND( m_outline[i]->x ),
|
||||
KiROUND( m_outline[i]->y ) ) );
|
||||
}
|
||||
|
||||
zone->Outline()->CloseLastContour();
|
||||
|
||||
zone->SetZoneClearance( m_width );
|
||||
|
||||
zone->SetPriority( m_priority );
|
||||
|
||||
zone->Outline()->SetHatch( outline_hatch,
|
||||
Mils2iu( zone->Outline()->GetDefaultHatchPitchMils() ),
|
||||
true );
|
||||
zone->SetHatch( outline_hatch, Mils2iu( zone->GetDefaultHatchPitchMils() ), true );
|
||||
|
||||
if ( m_objType == wxT( 'K' ) )
|
||||
{
|
||||
|
|
|
@ -973,12 +973,11 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone )
|
|||
m_gal->SetIsStroke( true );
|
||||
m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
|
||||
|
||||
const CPolyLine* polygon = aZone->Outline();
|
||||
for( int i = 0; i < polygon->GetCornersCount(); ++i )
|
||||
for( auto iterator = aZone->CIterateWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
corners.push_back( VECTOR2D( polygon->GetPos( i ) ) );
|
||||
corners.push_back( VECTOR2D( *iterator ) );
|
||||
|
||||
if( polygon->IsEndContour( i ) )
|
||||
if( iterator.IsEndContour() )
|
||||
{
|
||||
// The last point for closing the polyline
|
||||
corners.push_back( corners[0] );
|
||||
|
@ -986,11 +985,8 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone )
|
|||
corners.clear();
|
||||
}
|
||||
|
||||
for( unsigned ic = 0; ic < polygon->m_HatchLines.size(); ic++ )
|
||||
{
|
||||
auto& hatchLine = polygon->m_HatchLines[ic];
|
||||
m_gal->DrawLine( hatchLine.m_Start, hatchLine.m_End );
|
||||
}
|
||||
for( const SEG& hatchLine : aZone->GetHatchLines() )
|
||||
m_gal->DrawLine( hatchLine.A, hatchLine.B );
|
||||
}
|
||||
|
||||
// Draw the filling
|
||||
|
|
|
@ -2644,9 +2644,9 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR )
|
|||
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
|
||||
wxT( " as ZONE_CONTAINER." ) );
|
||||
|
||||
CPolyLine::HATCH_STYLE hatchStyle = CPolyLine::NO_HATCH;
|
||||
ZONE_CONTAINER::HATCH_STYLE hatchStyle = ZONE_CONTAINER::NO_HATCH;
|
||||
|
||||
int hatchPitch = Mils2iu( CPolyLine::GetDefaultHatchPitchMils() );
|
||||
int hatchPitch = Mils2iu( ZONE_CONTAINER::GetDefaultHatchPitchMils() );
|
||||
wxPoint pt;
|
||||
T token;
|
||||
int tmp;
|
||||
|
@ -2709,9 +2709,9 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR )
|
|||
switch( token )
|
||||
{
|
||||
default:
|
||||
case T_none: hatchStyle = CPolyLine::NO_HATCH; break;
|
||||
case T_edge: hatchStyle = CPolyLine::DIAGONAL_EDGE; break;
|
||||
case T_full: hatchStyle = CPolyLine::DIAGONAL_FULL;
|
||||
case T_none: hatchStyle = ZONE_CONTAINER::NO_HATCH; break;
|
||||
case T_edge: hatchStyle = ZONE_CONTAINER::DIAGONAL_EDGE; break;
|
||||
case T_full: hatchStyle = ZONE_CONTAINER::DIAGONAL_FULL;
|
||||
}
|
||||
|
||||
hatchPitch = parseBoardUnits( "hatch pitch" );
|
||||
|
@ -2956,7 +2956,7 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR )
|
|||
}
|
||||
|
||||
// Set hatch here, after outlines corners are read
|
||||
zone->Outline()->SetHatch( hatchStyle, hatchPitch, true );
|
||||
zone->SetHatch( hatchStyle, hatchPitch, true );
|
||||
}
|
||||
|
||||
if( !pts.IsEmpty() )
|
||||
|
|
|
@ -646,7 +646,7 @@ void BRDITEMS_PLOTTER::PlotFilledAreas( ZONE_CONTAINER* aZone )
|
|||
*
|
||||
* in non filled mode the outline is plotted, but not the filling items
|
||||
*/
|
||||
for( SHAPE_POLY_SET::CONST_ITERATOR ic = polysList.CIterate(); ic; ++ic )
|
||||
for( auto ic = polysList.CIterate(); ic; ++ic )
|
||||
{
|
||||
wxPoint pos( ic->x, ic->y );
|
||||
cornerList.push_back( pos );
|
||||
|
|
|
@ -418,7 +418,7 @@ RN_POLY::RN_POLY( const SHAPE_POLY_SET* aParent,
|
|||
m_bbox( aBBox ),
|
||||
m_parentPolyset( aParent )
|
||||
{
|
||||
const VECTOR2I& p = aParent->CVertex( 0, aSubpolygonIndex );
|
||||
const VECTOR2I& p = aParent->CVertex( 0, aSubpolygonIndex, -1 );
|
||||
|
||||
m_node = aConnections.AddNode( p.x, p.y );
|
||||
|
||||
|
|
|
@ -1016,26 +1016,27 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard )
|
|||
|
||||
mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
|
||||
|
||||
int count = item->Outline()->m_CornersList.GetCornersCount();
|
||||
int ndx = 0; // used in 2 for() loops below
|
||||
for( ; ndx<count; ++ndx )
|
||||
// Handle the main outlines
|
||||
SHAPE_POLY_SET::ITERATOR iterator;
|
||||
for( iterator = item->IterateWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
wxPoint point( item->Outline()->m_CornersList[ndx].x,
|
||||
item->Outline()->m_CornersList[ndx].y );
|
||||
wxPoint point( iterator->x, iterator->y );
|
||||
mainPolygon->AppendPoint( mapPt(point) );
|
||||
|
||||
// this was the end of the main polygon
|
||||
if( item->Outline()->m_CornersList[ndx].end_contour )
|
||||
if( iterator.IsEndContour() )
|
||||
break;
|
||||
}
|
||||
|
||||
WINDOW* window = 0;
|
||||
PATH* cutout = 0;
|
||||
|
||||
bool isStartContour = true;
|
||||
|
||||
// handle the cutouts
|
||||
for( ++ndx; ndx<count; ++ndx )
|
||||
for( iterator++; iterator; iterator++ )
|
||||
{
|
||||
if( item->Outline()->m_CornersList[ndx-1].end_contour )
|
||||
if( isStartContour )
|
||||
{
|
||||
window = new WINDOW( plane );
|
||||
|
||||
|
@ -1048,11 +1049,14 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard )
|
|||
cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
|
||||
}
|
||||
|
||||
// If the point in this iteration is the last of the contour, the next iteration
|
||||
// will start with a new contour.
|
||||
isStartContour = iterator.IsEndContour();
|
||||
|
||||
wxASSERT( window );
|
||||
wxASSERT( cutout );
|
||||
|
||||
wxPoint point(item->Outline()->m_CornersList[ndx].x,
|
||||
item->Outline()->m_CornersList[ndx].y );
|
||||
wxPoint point(iterator->x, iterator->y );
|
||||
cutout->AppendPoint( mapPt(point) );
|
||||
}
|
||||
}
|
||||
|
@ -1093,41 +1097,44 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard )
|
|||
|
||||
mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
|
||||
|
||||
int count = item->Outline()->m_CornersList.GetCornersCount();
|
||||
int ndx = 0; // used in 2 for() loops below
|
||||
for( ; ndx<count; ++ndx )
|
||||
// Handle the main outlines
|
||||
SHAPE_POLY_SET::ITERATOR iterator;
|
||||
for( iterator = item->IterateWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
wxPoint point( item->Outline()->m_CornersList[ndx].x,
|
||||
item->Outline()->m_CornersList[ndx].y );
|
||||
wxPoint point( iterator->x, iterator->y );
|
||||
mainPolygon->AppendPoint( mapPt(point) );
|
||||
|
||||
// this was the end of the main polygon
|
||||
if( item->Outline()->m_CornersList[ndx].end_contour )
|
||||
if( iterator.IsEndContour() )
|
||||
break;
|
||||
}
|
||||
|
||||
WINDOW* window = 0;
|
||||
PATH* cutout = 0;
|
||||
|
||||
bool isStartContour = true;
|
||||
|
||||
// handle the cutouts
|
||||
for( ++ndx; ndx<count; ++ndx )
|
||||
for( iterator++; iterator; iterator++ )
|
||||
{
|
||||
if( item->Outline()->m_CornersList[ndx-1].end_contour )
|
||||
if( isStartContour )
|
||||
{
|
||||
window = new WINDOW( keepout );
|
||||
keepout->AddWindow( window );
|
||||
|
||||
cutout = new PATH( window, T_polygon );
|
||||
|
||||
window->SetShape( cutout );
|
||||
|
||||
cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
|
||||
}
|
||||
|
||||
isStartContour = iterator.IsEndContour();
|
||||
|
||||
wxASSERT( window );
|
||||
wxASSERT( cutout );
|
||||
|
||||
wxPoint point(item->Outline()->m_CornersList[ndx].x,
|
||||
item->Outline()->m_CornersList[ndx].y );
|
||||
wxPoint point(iterator->x, iterator->y );
|
||||
cutout->AppendPoint( mapPt(point) );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -339,17 +339,15 @@ void GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos )
|
|||
|
||||
case PCB_ZONE_AREA_T:
|
||||
{
|
||||
const CPolyLine* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
|
||||
int cornersCount = outline->GetCornersCount();
|
||||
const SHAPE_POLY_SET* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
|
||||
|
||||
SHAPE_LINE_CHAIN lc;
|
||||
lc.SetClosed( true );
|
||||
|
||||
for( int i = 0; i < cornersCount; ++i )
|
||||
for( auto iter = outline->CIterateWithHoles(); iter; iter++ )
|
||||
{
|
||||
const VECTOR2I p ( outline->GetPos( i ) );
|
||||
addAnchor( p, CORNER, aItem );
|
||||
lc.Append( p );
|
||||
addAnchor( *iter, CORNER, aItem );
|
||||
lc.Append( *iter );
|
||||
}
|
||||
|
||||
addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );
|
||||
|
|
|
@ -750,22 +750,19 @@ int PCB_EDITOR_CONTROL::ZoneUnfillAll( const TOOL_EVENT& aEvent )
|
|||
static bool mergeZones( BOARD_COMMIT& aCommit, std::vector<ZONE_CONTAINER *>& aOriginZones,
|
||||
std::vector<ZONE_CONTAINER *>& aMergedZones )
|
||||
{
|
||||
SHAPE_POLY_SET mergedOutlines = ConvertPolyListToPolySet( aOriginZones[0]->Outline()->m_CornersList );
|
||||
|
||||
for( unsigned int i = 1; i < aOriginZones.size(); i++ )
|
||||
{
|
||||
SHAPE_POLY_SET areaToMergePoly = ConvertPolyListToPolySet( aOriginZones[i]->Outline()->m_CornersList );
|
||||
|
||||
mergedOutlines.BooleanAdd( areaToMergePoly, SHAPE_POLY_SET::PM_FAST );
|
||||
aOriginZones[0]->Outline()->BooleanAdd( *aOriginZones[i]->Outline(),
|
||||
SHAPE_POLY_SET::PM_FAST );
|
||||
}
|
||||
|
||||
mergedOutlines.Simplify( SHAPE_POLY_SET::PM_FAST );
|
||||
aOriginZones[0]->Outline()->Simplify( SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
// We should have one polygon with hole
|
||||
// We can have 2 polygons with hole, if the 2 initial polygons have only one common corner
|
||||
// and therefore cannot be merged (they are dectected as intersecting)
|
||||
// but we should never have more than 2 polys
|
||||
if( mergedOutlines.OutlineCount() > 1 )
|
||||
if( aOriginZones[0]->Outline()->OutlineCount() > 1 )
|
||||
{
|
||||
wxLogMessage( wxT( "BOARD::CombineAreas error: more than 2 polys after merging" ) );
|
||||
return false;
|
||||
|
@ -779,9 +776,8 @@ static bool mergeZones( BOARD_COMMIT& aCommit, std::vector<ZONE_CONTAINER *>& aO
|
|||
aCommit.Modify( aOriginZones[0] );
|
||||
aMergedZones.push_back( aOriginZones[0] );
|
||||
|
||||
aOriginZones[0]->Outline()->m_CornersList = ConvertPolySetToPolyList( mergedOutlines );
|
||||
aOriginZones[0]->SetLocalFlags( 1 );
|
||||
aOriginZones[0]->Outline()->Hatch();
|
||||
aOriginZones[0]->Hatch();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -128,14 +128,16 @@ public:
|
|||
|
||||
case PCB_ZONE_AREA_T:
|
||||
{
|
||||
const CPolyLine* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
|
||||
int cornersCount = outline->GetCornersCount();
|
||||
const SHAPE_POLY_SET* outline;
|
||||
outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
|
||||
|
||||
for( int i = 0; i < cornersCount; ++i )
|
||||
int cornersCount = outline->TotalVertices();
|
||||
|
||||
for( auto iterator = outline->CIterateWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
points->AddPoint( outline->GetPos( i ) );
|
||||
points->AddPoint( *iterator );
|
||||
|
||||
if( outline->IsEndContour( i ) )
|
||||
if( iterator.IsEndContour() )
|
||||
points->AddBreak();
|
||||
}
|
||||
|
||||
|
@ -456,16 +458,15 @@ void POINT_EDITOR::updateItem() const
|
|||
{
|
||||
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
|
||||
zone->ClearFilledPolysList();
|
||||
CPolyLine* outline = zone->Outline();
|
||||
SHAPE_POLY_SET* outline = zone->Outline();
|
||||
|
||||
for( int i = 0; i < outline->GetCornersCount(); ++i )
|
||||
for( int i = 0; i < outline->TotalVertices(); ++i )
|
||||
{
|
||||
VECTOR2I point = m_editPoints->Point( i ).GetPosition();
|
||||
outline->SetX( i, point.x );
|
||||
outline->SetY( i, point.y );
|
||||
outline->Vertex( i ) = point;
|
||||
}
|
||||
|
||||
outline->Hatch();
|
||||
zone->Hatch();
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -584,9 +585,9 @@ void POINT_EDITOR::updatePoints()
|
|||
case PCB_ZONE_AREA_T:
|
||||
{
|
||||
const ZONE_CONTAINER* zone = static_cast<const ZONE_CONTAINER*>( item );
|
||||
const CPolyLine* outline = zone->Outline();
|
||||
const SHAPE_POLY_SET* outline = zone->Outline();
|
||||
|
||||
if( m_editPoints->PointsSize() != (unsigned) outline->GetCornersCount() )
|
||||
if( m_editPoints->PointsSize() != (unsigned) outline->TotalVertices() )
|
||||
{
|
||||
getView()->Remove( m_editPoints.get() );
|
||||
m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
|
||||
|
@ -594,8 +595,8 @@ void POINT_EDITOR::updatePoints()
|
|||
}
|
||||
else
|
||||
{
|
||||
for( int i = 0; i < outline->GetCornersCount(); ++i )
|
||||
m_editPoints->Point( i ).SetPosition( outline->GetPos( i ) );
|
||||
for( int i = 0; i < outline->TotalVertices(); ++i )
|
||||
m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) );
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -775,19 +776,18 @@ int POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent )
|
|||
if( item->Type() == PCB_ZONE_AREA_T )
|
||||
{
|
||||
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
|
||||
CPolyLine* outline = zone->Outline();
|
||||
SHAPE_POLY_SET* outline = zone->Outline();
|
||||
|
||||
commit.Modify( zone );
|
||||
|
||||
// Handle the last segment, so other segments can be easily handled in a loop
|
||||
unsigned int nearestIdx = outline->GetCornersCount() - 1, nextNearestIdx = 0;
|
||||
SEG side( VECTOR2I( outline->GetPos( nearestIdx ) ),
|
||||
VECTOR2I( outline->GetPos( nextNearestIdx ) ) );
|
||||
unsigned int nearestIdx = outline->TotalVertices() - 1, nextNearestIdx = 0;
|
||||
SEG side( outline->Vertex( nearestIdx ), outline->Vertex( nextNearestIdx ) );
|
||||
unsigned int nearestDist = side.Distance( cursorPos );
|
||||
|
||||
for( int i = 0; i < outline->GetCornersCount() - 1; ++i )
|
||||
for( int i = 0; i < outline->TotalVertices() - 1; ++i )
|
||||
{
|
||||
side = SEG( VECTOR2I( outline->GetPos( i ) ), VECTOR2I( outline->GetPos( i + 1 ) ) );
|
||||
side = SEG( outline->Vertex( i ), outline->Vertex( i + 1 ) );
|
||||
|
||||
unsigned int distance = side.Distance( cursorPos );
|
||||
if( distance < nearestDist )
|
||||
|
@ -799,8 +799,8 @@ int POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent )
|
|||
}
|
||||
|
||||
// Find the point on the closest segment
|
||||
VECTOR2I sideOrigin( outline->GetPos( nearestIdx ) );
|
||||
VECTOR2I sideEnd( outline->GetPos( nextNearestIdx ) );
|
||||
VECTOR2I sideOrigin = outline->Vertex( nearestIdx );
|
||||
VECTOR2I sideEnd = outline->Vertex( nextNearestIdx );
|
||||
SEG nearestSide( sideOrigin, sideEnd );
|
||||
VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
|
||||
|
||||
|
@ -809,7 +809,7 @@ int POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent )
|
|||
if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
|
||||
nearestPoint = ( sideOrigin + sideEnd ) / 2;
|
||||
|
||||
outline->InsertCorner( nearestIdx, nearestPoint.x, nearestPoint.y );
|
||||
outline->InsertVertex( nearestIdx, nearestPoint );
|
||||
|
||||
commit.Push( _( "Add a zone corner" ) );
|
||||
}
|
||||
|
@ -871,14 +871,14 @@ int POINT_EDITOR::removeCorner( const TOOL_EVENT& aEvent )
|
|||
BOARD_COMMIT commit( frame );
|
||||
|
||||
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
|
||||
CPolyLine* outline = zone->Outline();
|
||||
SHAPE_POLY_SET* outline = zone->Outline();
|
||||
commit.Modify( zone );
|
||||
|
||||
for( int i = 0; i < outline->GetCornersCount(); ++i )
|
||||
for( int i = 0; i < outline->TotalVertices(); ++i )
|
||||
{
|
||||
if( VECTOR2I( outline->GetPos( i ) ) == m_editedPoint->GetPosition() )
|
||||
if( outline->Vertex( i ) == m_editedPoint->GetPosition() )
|
||||
{
|
||||
outline->DeleteCorner( i );
|
||||
outline->RemoveVertex( i );
|
||||
setEditedPoint( NULL );
|
||||
commit.Push( _( "Remove a zone corner" ) );
|
||||
break;
|
||||
|
|
|
@ -110,14 +110,16 @@ void ZONE_CREATE_HELPER::performZoneCutout( ZONE_CONTAINER& aExistingZone,
|
|||
auto& board = *m_tool.getModel<BOARD>();
|
||||
auto& toolMgr = *m_tool.GetManager();
|
||||
|
||||
aExistingZone.Outline()->NewOutline();
|
||||
|
||||
// Copy cutout corners into existing zone
|
||||
for( int ii = 0; ii < aCutout.GetNumCorners(); ii++ )
|
||||
{
|
||||
aExistingZone.AppendCorner( aCutout.GetCornerPosition( ii ) );
|
||||
aExistingZone.Outline()->Append( aCutout.GetCornerPosition( ii ) );
|
||||
}
|
||||
|
||||
// Close the current corner list
|
||||
aExistingZone.Outline()->CloseLastContour();
|
||||
aExistingZone.Outline()->Outline( 0 ).SetClosed( true );
|
||||
|
||||
board.OnAreaPolygonModified( nullptr, &aExistingZone );
|
||||
|
||||
|
@ -217,18 +219,16 @@ void ZONE_CREATE_HELPER::OnComplete( const POLYGON_GEOM_MANAGER& aMgr )
|
|||
}
|
||||
else
|
||||
{
|
||||
m_zone->Outline()->Start( m_zone->GetLayer(),
|
||||
finalPoints[0].x, finalPoints[0].y,
|
||||
m_zone->GetHatchStyle() );
|
||||
m_zone->Outline()->NewOutline();
|
||||
|
||||
for( size_t i = 1; i < finalPoints.size(); ++i )
|
||||
for( const auto& pt : finalPoints )
|
||||
{
|
||||
m_zone->AppendCorner( { finalPoints[i].x, finalPoints[i].y } );
|
||||
m_zone->Outline()->Append( pt );
|
||||
}
|
||||
|
||||
m_zone->Outline()->CloseLastContour();
|
||||
m_zone->Outline()->Outline( 0 ).SetClosed( true );
|
||||
m_zone->Outline()->RemoveNullSegments();
|
||||
m_zone->Outline()->Hatch();
|
||||
m_zone->Hatch();
|
||||
|
||||
// hand the zone over to the committer
|
||||
commitZone( std::move( m_zone ) );
|
||||
|
|
|
@ -75,11 +75,13 @@ bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET*
|
|||
switch( m_cornerSmoothingType )
|
||||
{
|
||||
case ZONE_SETTINGS::SMOOTHING_CHAMFER:
|
||||
m_smoothedPoly = m_Poly->Chamfer( m_cornerRadius );
|
||||
m_smoothedPoly = new SHAPE_POLY_SET();
|
||||
*m_smoothedPoly = m_Poly->Chamfer( m_cornerRadius );
|
||||
break;
|
||||
|
||||
case ZONE_SETTINGS::SMOOTHING_FILLET:
|
||||
m_smoothedPoly = m_Poly->Fillet( m_cornerRadius, m_ArcToSegmentsCount );
|
||||
m_smoothedPoly = new SHAPE_POLY_SET();
|
||||
*m_smoothedPoly = m_Poly->Fillet( m_cornerRadius, m_ArcToSegmentsCount );
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -88,12 +90,13 @@ bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET*
|
|||
// We can avoid issues by creating a very small chamfer which remove acute angles,
|
||||
// or left it without chamfer and use only CPOLYGONS_LIST::InflateOutline to create
|
||||
// clearance areas
|
||||
m_smoothedPoly = m_Poly->Chamfer( Millimeter2iu( 0.0 ) );
|
||||
m_smoothedPoly = new SHAPE_POLY_SET();
|
||||
*m_smoothedPoly = m_Poly->Chamfer( Millimeter2iu( 0.0 ) );
|
||||
break;
|
||||
}
|
||||
|
||||
if( aOutlineBuffer )
|
||||
aOutlineBuffer->Append( ConvertPolyListToPolySet( m_smoothedPoly->m_CornersList ) );
|
||||
aOutlineBuffer->Append( *m_smoothedPoly );
|
||||
|
||||
/* For copper layers, we now must add holes in the Polygon list.
|
||||
* holes are pads and tracks with their clearance area
|
||||
|
@ -118,7 +121,7 @@ bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET*
|
|||
{
|
||||
m_FillMode = 0; // Fill by segments is no more used in non copper layers
|
||||
// force use solid polygons (usefull only for old boards)
|
||||
m_FilledPolysList = ConvertPolyListToPolySet( m_smoothedPoly->m_CornersList );
|
||||
m_FilledPolysList = *m_smoothedPoly;
|
||||
|
||||
// The filled areas are deflated by -m_ZoneMinThickness / 2, because
|
||||
// the outlines are drawn with a line thickness = m_ZoneMinThickness to
|
||||
|
|
|
@ -137,7 +137,7 @@ void PCB_EDIT_FRAME::duplicateZone( wxDC* aDC, ZONE_CONTAINER* aZone )
|
|||
if( success )
|
||||
{
|
||||
zoneSettings.ExportSetting( *newZone );
|
||||
newZone->Outline()->Hatch();
|
||||
newZone->Hatch();
|
||||
|
||||
s_AuxiliaryList.ClearListAndDeleteItems();
|
||||
s_PickedList.ClearListAndDeleteItems();
|
||||
|
@ -187,7 +187,7 @@ int PCB_EDIT_FRAME::Delete_LastCreatedCorner( wxDC* DC )
|
|||
|
||||
if( zone->GetNumCorners() > 2 )
|
||||
{
|
||||
zone->Outline()->DeleteCorner( zone->GetNumCorners() - 1 );
|
||||
zone->Outline()->RemoveVertex( zone->GetNumCorners() - 1 );
|
||||
|
||||
if( m_canvas->IsMouseCaptured() )
|
||||
m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
|
||||
|
@ -256,11 +256,10 @@ void PCB_EDIT_FRAME::Start_Move_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone,
|
|||
|
||||
// Prepare copy of old zones, for undo/redo.
|
||||
// if the corner is new, remove it from list, save and insert it in list
|
||||
int cx = aZone->Outline()->GetX( corner_id );
|
||||
int cy = aZone->Outline()->GetY( corner_id );
|
||||
VECTOR2I corner = aZone->Outline()->Vertex( corner_id );
|
||||
|
||||
if ( IsNewCorner )
|
||||
aZone->Outline()->DeleteCorner( corner_id );
|
||||
aZone->Outline()->RemoveVertex( corner_id );
|
||||
|
||||
s_AuxiliaryList.ClearListAndDeleteItems();
|
||||
s_PickedList.ClearListAndDeleteItems();
|
||||
|
@ -268,12 +267,12 @@ void PCB_EDIT_FRAME::Start_Move_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone,
|
|||
SaveCopyOfZones( s_PickedList, GetBoard(), aZone->GetNetCode(), aZone->GetLayer() );
|
||||
|
||||
if ( IsNewCorner )
|
||||
aZone->Outline()->InsertCorner(corner_id-1, cx, cy );
|
||||
aZone->Outline()->InsertVertex(corner_id-1, corner );
|
||||
|
||||
aZone->SetFlags( IN_EDIT );
|
||||
m_canvas->SetMouseCapture( Show_Zone_Corner_Or_Outline_While_Move_Mouse,
|
||||
Abort_Zone_Move_Corner_Or_Outlines );
|
||||
s_CornerInitialPosition = aZone->GetCornerPosition( corner_id );
|
||||
s_CornerInitialPosition = static_cast<wxPoint>( aZone->GetCornerPosition( corner_id ) );
|
||||
s_CornerIsNew = IsNewCorner;
|
||||
s_AddCutoutToCurrentZone = false;
|
||||
s_CurrentZone = NULL;
|
||||
|
@ -370,7 +369,7 @@ void PCB_EDIT_FRAME::Remove_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone )
|
|||
{
|
||||
OnModify();
|
||||
|
||||
if( aZone->Outline()->GetCornersCount() <= 3 )
|
||||
if( aZone->Outline()->TotalVertices() <= 3 )
|
||||
{
|
||||
m_canvas->RefreshDrawingRect( aZone->GetBoundingBox() );
|
||||
|
||||
|
@ -395,7 +394,7 @@ void PCB_EDIT_FRAME::Remove_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone )
|
|||
s_AuxiliaryList.ClearListAndDeleteItems();
|
||||
s_PickedList. ClearListAndDeleteItems();
|
||||
SaveCopyOfZones( s_PickedList, GetBoard(), aZone->GetNetCode(), aZone->GetLayer() );
|
||||
aZone->Outline()->DeleteCorner( aZone->GetSelectedCorner() );
|
||||
aZone->Outline()->RemoveVertex( aZone->GetSelectedCorner() );
|
||||
|
||||
// modify zones outlines according to the new aZone shape
|
||||
GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, aZone );
|
||||
|
@ -449,12 +448,12 @@ void Abort_Zone_Move_Corner_Or_Outlines( EDA_DRAW_PANEL* Panel, wxDC* DC )
|
|||
{
|
||||
if( s_CornerIsNew )
|
||||
{
|
||||
zone->Outline()->DeleteCorner( zone->GetSelectedCorner() );
|
||||
zone->Outline()->RemoveVertex( zone->GetSelectedCorner() );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxPoint pos = s_CornerInitialPosition;
|
||||
zone->Outline()->MoveCorner( zone->GetSelectedCorner(), pos.x, pos.y );
|
||||
zone->Outline()->Vertex( zone->GetSelectedCorner() ) = pos;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -500,7 +499,7 @@ void Show_Zone_Corner_Or_Outline_While_Move_Mouse( EDA_DRAW_PANEL* aPanel, wxDC*
|
|||
}
|
||||
else
|
||||
{
|
||||
zone->Outline()->MoveCorner( zone->GetSelectedCorner(), pos.x, pos.y );
|
||||
zone->Outline()->Vertex( zone->GetSelectedCorner() ) = pos;
|
||||
}
|
||||
|
||||
zone->Draw( aPanel, aDC, GR_XOR );
|
||||
|
@ -667,12 +666,11 @@ int PCB_EDIT_FRAME::Begin_Zone( wxDC* DC )
|
|||
{
|
||||
zoneInfo.ExportSetting( *zone );
|
||||
|
||||
zone->Outline()->Start( zoneInfo.m_CurrentZone_Layer,
|
||||
GetCrossHairPosition().x,
|
||||
GetCrossHairPosition().y,
|
||||
zone->GetHatchStyle() );
|
||||
zone->SetLayer( zoneInfo.m_CurrentZone_Layer );
|
||||
|
||||
// A duplicated corner is needed; null segments are removed when the zone is finished.
|
||||
zone->AppendCorner( GetCrossHairPosition() );
|
||||
zone->AppendCorner( GetCrossHairPosition(), true );
|
||||
|
||||
if( g_Drc_On && (m_drc->Drc( zone, 0 ) == BAD_DRC) && zone->IsOnCopperLayer() )
|
||||
{
|
||||
|
@ -704,8 +702,14 @@ int PCB_EDIT_FRAME::Begin_Zone( wxDC* DC )
|
|||
// Ok, we can add a new corner
|
||||
if( m_canvas->IsMouseCaptured() )
|
||||
m_canvas->CallMouseCapture( DC, wxPoint(0,0), false );
|
||||
zone->AppendCorner( GetCrossHairPosition() );
|
||||
|
||||
// It is necessary to allow duplication of the points, as we have to handle the
|
||||
// continuous drawing while creating the zone at the same time as we build it. Null
|
||||
// segments are removed when the zone is finished, in End_Zone.
|
||||
zone->AppendCorner( GetCrossHairPosition(), true );
|
||||
|
||||
SetCurItem( zone ); // calls DisplayInfo().
|
||||
|
||||
if( m_canvas->IsMouseCaptured() )
|
||||
m_canvas->CallMouseCapture( DC, wxPoint(0,0), false );
|
||||
}
|
||||
|
@ -768,7 +772,6 @@ bool PCB_EDIT_FRAME::End_Zone( wxDC* DC )
|
|||
// Put new zone in list
|
||||
if( !s_CurrentZone )
|
||||
{
|
||||
zone->Outline()->CloseLastContour(); // Close the current corner list
|
||||
GetBoard()->Add( zone );
|
||||
|
||||
// Add this zone in picked list, as new item
|
||||
|
@ -777,12 +780,8 @@ bool PCB_EDIT_FRAME::End_Zone( wxDC* DC )
|
|||
}
|
||||
else // Append this outline as a cutout to an existing zone
|
||||
{
|
||||
for( int ii = 0; ii < zone->GetNumCorners(); ii++ )
|
||||
{
|
||||
s_CurrentZone->AppendCorner( zone->GetCornerPosition( ii ) );
|
||||
}
|
||||
s_CurrentZone->Outline()->AddHole( zone->Outline()->Outline( 0 ) );
|
||||
|
||||
s_CurrentZone->Outline()->CloseLastContour(); // Close the current corner list
|
||||
zone->RemoveAllContours(); // All corners are copied in s_CurrentZone. Free corner list.
|
||||
zone = s_CurrentZone;
|
||||
}
|
||||
|
@ -847,7 +846,7 @@ static void Show_New_Edge_While_Move_Mouse( EDA_DRAW_PANEL* aPanel, wxDC* aDC,
|
|||
if( pcbframe->GetZoneSettings().m_Zone_45_Only )
|
||||
{
|
||||
// calculate the new position as allowed
|
||||
wxPoint StartPoint = zone->GetCornerPosition( icorner - 1 );
|
||||
wxPoint StartPoint = static_cast<wxPoint>( zone->GetCornerPosition( icorner - 1 ) );
|
||||
CalculateSegmentEndPoint( c_pos, StartPoint.x, StartPoint.y, &c_pos.x, &c_pos.y );
|
||||
}
|
||||
|
||||
|
@ -964,7 +963,12 @@ void PCB_EDIT_FRAME::Edit_Zone_Params( wxDC* DC, ZONE_CONTAINER* aZone )
|
|||
|
||||
void PCB_EDIT_FRAME::Delete_Zone_Contour( wxDC* DC, ZONE_CONTAINER* aZone )
|
||||
{
|
||||
int ncont = aZone->Outline()->GetContour( aZone->GetSelectedCorner() );
|
||||
// Get contour in which the selected corner is
|
||||
SHAPE_POLY_SET::VERTEX_INDEX indices;
|
||||
|
||||
// If the selected corner does not exist, abort
|
||||
if( !aZone->Outline()->GetRelativeIndices( aZone->GetSelectedCorner(), &indices ) )
|
||||
throw( std::out_of_range( "Zone selected corner does not exist" ) );
|
||||
|
||||
EDA_RECT dirty = aZone->GetBoundingBox();
|
||||
|
||||
|
@ -974,7 +978,7 @@ void PCB_EDIT_FRAME::Delete_Zone_Contour( wxDC* DC, ZONE_CONTAINER* aZone )
|
|||
// Remove current filling:
|
||||
aZone->UnFill();
|
||||
|
||||
if( ncont == 0 ) // This is the main outline: remove all
|
||||
if( indices.m_contour == 0 ) // This is the main outline: remove all
|
||||
{
|
||||
SaveCopyInUndoList( aZone, UR_DELETED );
|
||||
GetBoard()->Remove( aZone );
|
||||
|
@ -983,7 +987,7 @@ void PCB_EDIT_FRAME::Delete_Zone_Contour( wxDC* DC, ZONE_CONTAINER* aZone )
|
|||
else
|
||||
{
|
||||
SaveCopyInUndoList( aZone, UR_CHANGED );
|
||||
aZone->Outline()->RemoveContour( ncont );
|
||||
aZone->Outline()->RemoveContour( indices.m_contour, indices.m_polygon );
|
||||
}
|
||||
|
||||
m_canvas->RefreshDrawingRect( dirty );
|
||||
|
|
|
@ -435,7 +435,7 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb )
|
|||
if(g_DumpZonesWhenFilling)
|
||||
dumper->BeginGroup("clipper-zone");
|
||||
|
||||
SHAPE_POLY_SET solidAreas = ConvertPolyListToPolySet( m_smoothedPoly->m_CornersList );
|
||||
SHAPE_POLY_SET solidAreas = *m_smoothedPoly;
|
||||
|
||||
solidAreas.Inflate( -outline_half_thickness, segsPerCircle );
|
||||
solidAreas.Simplify( POLY_CALC_MODE );
|
||||
|
|
|
@ -115,8 +115,7 @@ bool ZONE_CONTAINER::IsSame( const ZONE_CONTAINER& aZoneToCompare )
|
|||
wxASSERT( m_Poly ); // m_Poly == NULL Should never happen
|
||||
wxASSERT( aZoneToCompare.Outline() );
|
||||
|
||||
if( Outline()->m_CornersList.GetList() !=
|
||||
aZoneToCompare.Outline()->m_CornersList.GetList() ) // Compare vector
|
||||
if( Outline() != aZoneToCompare.Outline() ) // Compare vector
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -102,7 +102,7 @@ bool BOARD::CombineAllAreasInNet( PICKED_ITEMS_LIST* aDeletedList, int aNetCode,
|
|||
continue;
|
||||
|
||||
// legal polygon
|
||||
EDA_RECT b1 = curr_area->Outline()->GetBoundingBox();
|
||||
BOX2I b1 = curr_area->Outline()->BBox();
|
||||
bool mod_ia1 = false;
|
||||
|
||||
for( unsigned ia2 = m_ZoneDescriptorList.size() - 1; ia2 > ia1; ia2-- )
|
||||
|
@ -121,7 +121,7 @@ bool BOARD::CombineAllAreasInNet( PICKED_ITEMS_LIST* aDeletedList, int aNetCode,
|
|||
if( curr_area->GetLayer() != area2->GetLayer() )
|
||||
continue;
|
||||
|
||||
EDA_RECT b2 = area2->Outline()->GetBoundingBox();
|
||||
BOX2I b2 = area2->Outline()->BBox();
|
||||
|
||||
if( b1.Intersects( b2 ) )
|
||||
{
|
||||
|
@ -189,93 +189,46 @@ bool BOARD::TestAreaIntersection( ZONE_CONTAINER* area_ref, ZONE_CONTAINER* area
|
|||
if( area_ref->GetLayer() != area_to_test->GetLayer() )
|
||||
return false;
|
||||
|
||||
CPolyLine* poly1 = area_ref->Outline();
|
||||
CPolyLine* poly2 = area_to_test->Outline();
|
||||
SHAPE_POLY_SET* poly1 = area_ref->Outline();
|
||||
SHAPE_POLY_SET* poly2 = area_to_test->Outline();
|
||||
|
||||
// test bounding rects
|
||||
EDA_RECT b1 = poly1->GetBoundingBox();
|
||||
EDA_RECT b2 = poly2->GetBoundingBox();
|
||||
BOX2I b1 = poly1->BBox();
|
||||
BOX2I b2 = poly2->BBox();
|
||||
|
||||
if( ! b1.Intersects( b2 ) )
|
||||
return false;
|
||||
|
||||
// now test for intersecting segments
|
||||
for( int icont1 = 0; icont1<poly1->GetContoursCount(); icont1++ )
|
||||
// Now test for intersecting segments
|
||||
for( auto segIterator1 = poly1->IterateSegmentsWithHoles(); segIterator1; segIterator1++ )
|
||||
{
|
||||
int is1 = poly1->GetContourStart( icont1 );
|
||||
int ie1 = poly1->GetContourEnd( icont1 );
|
||||
// Build segment
|
||||
SEG firstSegment = *segIterator1;
|
||||
|
||||
for( int ic1 = is1; ic1<=ie1; ic1++ )
|
||||
for( auto segIterator2 = poly2->IterateSegmentsWithHoles(); segIterator2; segIterator2++ )
|
||||
{
|
||||
int xi1 = poly1->GetX( ic1 );
|
||||
int yi1 = poly1->GetY( ic1 );
|
||||
int xf1, yf1;
|
||||
// Build second segment
|
||||
SEG secondSegment = *segIterator2;
|
||||
|
||||
if( ic1 < ie1 )
|
||||
{
|
||||
xf1 = poly1->GetX( ic1 + 1 );
|
||||
yf1 = poly1->GetY( ic1 + 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
xf1 = poly1->GetX( is1 );
|
||||
yf1 = poly1->GetY( is1 );
|
||||
}
|
||||
|
||||
for( int icont2 = 0; icont2<poly2->GetContoursCount(); icont2++ )
|
||||
{
|
||||
int is2 = poly2->GetContourStart( icont2 );
|
||||
int ie2 = poly2->GetContourEnd( icont2 );
|
||||
|
||||
for( int ic2 = is2; ic2<=ie2; ic2++ )
|
||||
{
|
||||
int xi2 = poly2->GetX( ic2 );
|
||||
int yi2 = poly2->GetY( ic2 );
|
||||
int xf2, yf2;
|
||||
|
||||
if( ic2 < ie2 )
|
||||
{
|
||||
xf2 = poly2->GetX( ic2 + 1 );
|
||||
yf2 = poly2->GetY( ic2 + 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
xf2 = poly2->GetX( is2 );
|
||||
yf2 = poly2->GetY( is2 );
|
||||
}
|
||||
|
||||
bool intersect = FindSegmentIntersections( xi1, yi1, xf1, yf1,
|
||||
xi2, yi2, xf2, yf2 );
|
||||
if( intersect )
|
||||
// Check whether the two segments built collide
|
||||
if( firstSegment.Collide( secondSegment, 0 ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a contour is inside another contour, no segments intersects, but the zones
|
||||
// can be combined if a corner is inside an outline (only one corner is enought)
|
||||
for( int ic2 = 0; ic2 < poly2->GetCornersCount(); ic2++ )
|
||||
{
|
||||
int x = poly2->GetX( ic2 );
|
||||
int y = poly2->GetY( ic2 );
|
||||
|
||||
if( poly1->TestPointInside( x, y ) )
|
||||
// can be combined if a corner is inside an outline (only one corner is enough)
|
||||
for( auto iter = poly2->IterateWithHoles(); iter; iter++ )
|
||||
{
|
||||
if( poly1->Contains( *iter ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for( int ic1 = 0; ic1 < poly1->GetCornersCount(); ic1++ )
|
||||
{
|
||||
int x = poly1->GetX( ic1 );
|
||||
int y = poly1->GetY( ic1 );
|
||||
|
||||
if( poly2->TestPointInside( x, y ) )
|
||||
for( auto iter = poly1->IterateWithHoles(); iter; iter++ )
|
||||
{
|
||||
if( poly2->Contains( *iter ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -290,8 +243,8 @@ bool BOARD::CombineAreas( PICKED_ITEMS_LIST* aDeletedList, ZONE_CONTAINER* area_
|
|||
return false;
|
||||
}
|
||||
|
||||
SHAPE_POLY_SET mergedOutlines = ConvertPolyListToPolySet( area_ref->Outline()->m_CornersList );
|
||||
SHAPE_POLY_SET areaToMergePoly = ConvertPolyListToPolySet( area_to_combine->Outline()->m_CornersList );
|
||||
SHAPE_POLY_SET mergedOutlines = *area_ref->Outline();
|
||||
SHAPE_POLY_SET areaToMergePoly = *area_to_combine->Outline();
|
||||
|
||||
mergedOutlines.BooleanAdd( areaToMergePoly, SHAPE_POLY_SET::PM_FAST );
|
||||
mergedOutlines.Simplify( SHAPE_POLY_SET::PM_FAST );
|
||||
|
@ -309,12 +262,14 @@ bool BOARD::CombineAreas( PICKED_ITEMS_LIST* aDeletedList, ZONE_CONTAINER* area_
|
|||
if( mergedOutlines.OutlineCount() > 1 )
|
||||
return false;
|
||||
|
||||
area_ref->Outline()->m_CornersList = ConvertPolySetToPolyList( mergedOutlines );
|
||||
// Update the area with the new merged outline
|
||||
delete area_ref->Outline();
|
||||
area_ref->SetOutline( new SHAPE_POLY_SET( mergedOutlines ) );
|
||||
|
||||
RemoveArea( aDeletedList, area_to_combine );
|
||||
|
||||
area_ref->SetLocalFlags( 1 );
|
||||
area_ref->Outline()->Hatch();
|
||||
area_ref->Hatch();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -329,7 +284,7 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E
|
|||
for( int ia = 0; ia < GetAreaCount(); ia++ )
|
||||
{
|
||||
ZONE_CONTAINER* Area_Ref = GetArea( ia );
|
||||
CPolyLine* refSmoothedPoly = Area_Ref->GetSmoothedPoly();
|
||||
SHAPE_POLY_SET* refSmoothedPoly = Area_Ref->GetSmoothedPoly();
|
||||
|
||||
if( !Area_Ref->IsOnCopperLayer() )
|
||||
continue;
|
||||
|
@ -341,7 +296,7 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E
|
|||
for( int ia2 = 0; ia2 < GetAreaCount(); ia2++ )
|
||||
{
|
||||
ZONE_CONTAINER* area_to_test = GetArea( ia2 );
|
||||
CPolyLine* testSmoothedPoly = area_to_test->GetSmoothedPoly();
|
||||
SHAPE_POLY_SET* testSmoothedPoly = area_to_test->GetSmoothedPoly();
|
||||
|
||||
if( Area_Ref == area_to_test )
|
||||
continue;
|
||||
|
@ -375,16 +330,18 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E
|
|||
zone2zoneClearance = 1;
|
||||
|
||||
// test for some corners of Area_Ref inside area_to_test
|
||||
for( int ic = 0; ic < refSmoothedPoly->GetCornersCount(); ic++ )
|
||||
for( auto iterator = refSmoothedPoly->IterateWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
int x = refSmoothedPoly->GetX( ic );
|
||||
int y = refSmoothedPoly->GetY( ic );
|
||||
VECTOR2I currentVertex = *iterator;
|
||||
|
||||
if( testSmoothedPoly->TestPointInside( x, y ) )
|
||||
if( testSmoothedPoly->Contains( currentVertex ) )
|
||||
{
|
||||
// COPPERAREA_COPPERAREA error: copper area ref corner inside copper area
|
||||
if( aCreate_Markers )
|
||||
{
|
||||
int x = currentVertex.x;
|
||||
int y = currentVertex.y;
|
||||
|
||||
wxString msg1 = Area_Ref->GetSelectMenuText();
|
||||
wxString msg2 = area_to_test->GetSelectMenuText();
|
||||
MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_INSIDE_COPPERAREA,
|
||||
|
@ -399,16 +356,18 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E
|
|||
}
|
||||
|
||||
// test for some corners of area_to_test inside Area_Ref
|
||||
for( int ic2 = 0; ic2 < testSmoothedPoly->GetCornersCount(); ic2++ )
|
||||
for( auto iterator = testSmoothedPoly->IterateWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
int x = testSmoothedPoly->GetX( ic2 );
|
||||
int y = testSmoothedPoly->GetY( ic2 );
|
||||
VECTOR2I currentVertex = *iterator;
|
||||
|
||||
if( refSmoothedPoly->TestPointInside( x, y ) )
|
||||
if( refSmoothedPoly->Contains( currentVertex ) )
|
||||
{
|
||||
// COPPERAREA_COPPERAREA error: copper area corner inside copper area ref
|
||||
if( aCreate_Markers )
|
||||
{
|
||||
int x = currentVertex.x;
|
||||
int y = currentVertex.y;
|
||||
|
||||
wxString msg1 = area_to_test->GetSelectMenuText();
|
||||
wxString msg2 = Area_Ref->GetSelectMenuText();
|
||||
MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_INSIDE_COPPERAREA,
|
||||
|
@ -422,53 +381,33 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E
|
|||
}
|
||||
}
|
||||
|
||||
// now test spacing between areas
|
||||
for( int icont = 0; icont < refSmoothedPoly->GetContoursCount(); icont++ )
|
||||
{
|
||||
int ic_start = refSmoothedPoly->GetContourStart( icont );
|
||||
int ic_end = refSmoothedPoly->GetContourEnd( icont );
|
||||
|
||||
for( int ic = ic_start; ic<=ic_end; ic++ )
|
||||
// Iterate through all the segments of refSmoothedPoly
|
||||
for( auto refIt = refSmoothedPoly->IterateSegmentsWithHoles(); refIt; refIt++ )
|
||||
{
|
||||
int ax1 = refSmoothedPoly->GetX( ic );
|
||||
int ay1 = refSmoothedPoly->GetY( ic );
|
||||
int ax2, ay2;
|
||||
// Build ref segment
|
||||
SEG refSegment = *refIt;
|
||||
|
||||
if( ic == ic_end )
|
||||
// Iterate through all the segments in testSmoothedPoly
|
||||
for( auto testIt = testSmoothedPoly->IterateSegmentsWithHoles(); testIt; testIt++ )
|
||||
{
|
||||
ax2 = refSmoothedPoly->GetX( ic_start );
|
||||
ay2 = refSmoothedPoly->GetY( ic_start );
|
||||
}
|
||||
else
|
||||
{
|
||||
ax2 = refSmoothedPoly->GetX( ic + 1 );
|
||||
ay2 = refSmoothedPoly->GetY( ic + 1 );
|
||||
}
|
||||
|
||||
for( int icont2 = 0; icont2 < testSmoothedPoly->GetContoursCount(); icont2++ )
|
||||
{
|
||||
int ic_start2 = testSmoothedPoly->GetContourStart( icont2 );
|
||||
int ic_end2 = testSmoothedPoly->GetContourEnd( icont2 );
|
||||
|
||||
for( int ic2 = ic_start2; ic2<=ic_end2; ic2++ )
|
||||
{
|
||||
int bx1 = testSmoothedPoly->GetX( ic2 );
|
||||
int by1 = testSmoothedPoly->GetY( ic2 );
|
||||
int bx2, by2;
|
||||
|
||||
if( ic2 == ic_end2 )
|
||||
{
|
||||
bx2 = testSmoothedPoly->GetX( ic_start2 );
|
||||
by2 = testSmoothedPoly->GetY( ic_start2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
bx2 = testSmoothedPoly->GetX( ic2 + 1 );
|
||||
by2 = testSmoothedPoly->GetY( ic2 + 1 );
|
||||
}
|
||||
// Build test segment
|
||||
SEG testSegment = *testIt;
|
||||
|
||||
int x, y;
|
||||
|
||||
int ax1, ay1, ax2, ay2;
|
||||
ax1 = refSegment.A.x;
|
||||
ay1 = refSegment.A.y;
|
||||
ax2 = refSegment.B.x;
|
||||
ay2 = refSegment.B.y;
|
||||
|
||||
int bx1, by1, bx2, by2;
|
||||
bx1 = testSegment.A.x;
|
||||
by1 = testSegment.A.y;
|
||||
bx2 = testSegment.B.x;
|
||||
by2 = testSegment.B.y;
|
||||
|
||||
int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2,
|
||||
0,
|
||||
ax1, ay1, ax2, ay2,
|
||||
|
@ -496,8 +435,6 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nerrors;
|
||||
}
|
||||
|
@ -507,31 +444,22 @@ bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
|
|||
{
|
||||
if( !aArea->IsOnCopperLayer() ) // Cannot have a Drc error if not on copper layer
|
||||
return true;
|
||||
// Get polygon, contour and vertex index.
|
||||
SHAPE_POLY_SET::VERTEX_INDEX index;
|
||||
|
||||
wxPoint start = aArea->GetCornerPosition( aCornerIndex );
|
||||
wxPoint end;
|
||||
// If the vertex does not exist, there is no conflict
|
||||
if( !aArea->Outline()->GetRelativeIndices( aCornerIndex, &index ) )
|
||||
return true;
|
||||
|
||||
// Search the end point of the edge starting at aCornerIndex
|
||||
if( aArea->Outline()->m_CornersList[aCornerIndex].end_contour == false
|
||||
&& aCornerIndex < (aArea->GetNumCorners() - 1) )
|
||||
{
|
||||
end = aArea->GetCornerPosition( aCornerIndex + 1 );
|
||||
}
|
||||
else // aCornerIndex is the last corner of an outline.
|
||||
// the corresponding end point of the segment is the first corner of the outline
|
||||
{
|
||||
int ii = aCornerIndex - 1;
|
||||
end = aArea->GetCornerPosition( ii );
|
||||
// Retrieve the selected contour
|
||||
SHAPE_LINE_CHAIN contour;
|
||||
contour = aArea->Outline()->Polygon( index.m_polygon )[index.m_contour];
|
||||
|
||||
while( ii >= 0 )
|
||||
{
|
||||
if( aArea->Outline()->m_CornersList[ii].end_contour )
|
||||
break;
|
||||
// Retrieve the segment that starts at aCornerIndex-th corner.
|
||||
SEG selectedSegment = contour.Segment( index.m_vertex );
|
||||
|
||||
end = aArea->GetCornerPosition( ii );
|
||||
ii--;
|
||||
}
|
||||
}
|
||||
VECTOR2I start = selectedSegment.A;
|
||||
VECTOR2I end = selectedSegment.B;
|
||||
|
||||
// iterate through all areas
|
||||
for( int ia2 = 0; ia2 < m_pcb->GetAreaCount(); ia2++ )
|
||||
|
@ -562,10 +490,10 @@ bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
|
|||
zone_clearance = 1;
|
||||
|
||||
// test for ending line inside area_to_test
|
||||
if( area_to_test->Outline()->TestPointInside( end.x, end.y ) )
|
||||
if( area_to_test->Outline()->Contains( end ) )
|
||||
{
|
||||
// COPPERAREA_COPPERAREA error: corner inside copper area
|
||||
m_currentMarker = fillMarker( aArea, end,
|
||||
m_currentMarker = fillMarker( aArea, static_cast<wxPoint>( end ),
|
||||
COPPERAREA_INSIDE_COPPERAREA,
|
||||
m_currentMarker );
|
||||
return false;
|
||||
|
@ -577,27 +505,16 @@ bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
|
|||
int ax2 = end.x;
|
||||
int ay2 = end.y;
|
||||
|
||||
for( int icont2 = 0; icont2 < area_to_test->Outline()->GetContoursCount(); icont2++ )
|
||||
// Iterate through all edges in the polygon.
|
||||
SHAPE_POLY_SET::SEGMENT_ITERATOR iterator;
|
||||
for( iterator = area_to_test->Outline()->IterateSegmentsWithHoles(); iterator; iterator++ )
|
||||
{
|
||||
int ic_start2 = area_to_test->Outline()->GetContourStart( icont2 );
|
||||
int ic_end2 = area_to_test->Outline()->GetContourEnd( icont2 );
|
||||
SEG segment = *iterator;
|
||||
|
||||
for( int ic2 = ic_start2; ic2<=ic_end2; ic2++ )
|
||||
{
|
||||
int bx1 = area_to_test->Outline()->GetX( ic2 );
|
||||
int by1 = area_to_test->Outline()->GetY( ic2 );
|
||||
int bx2, by2;
|
||||
|
||||
if( ic2 == ic_end2 )
|
||||
{
|
||||
bx2 = area_to_test->Outline()->GetX( ic_start2 );
|
||||
by2 = area_to_test->Outline()->GetY( ic_start2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
bx2 = area_to_test->Outline()->GetX( ic2 + 1 );
|
||||
by2 = area_to_test->Outline()->GetY( ic2 + 1 );
|
||||
}
|
||||
int bx1 = segment.A.x;
|
||||
int by1 = segment.A.y;
|
||||
int bx2 = segment.B.x;
|
||||
int by2 = segment.B.y;
|
||||
|
||||
int x, y; // variables containing the intersecting point coordinates
|
||||
int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2,
|
||||
|
@ -615,7 +532,7 @@ bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
|
|||
m_currentMarker );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -354,6 +354,9 @@ CPolyLine* CPolyLine::Chamfer( unsigned int aDistance )
|
|||
|
||||
int nx2 = KiROUND( distance * xb / lenb );
|
||||
int ny2 = KiROUND( distance * yb / lenb );
|
||||
|
||||
// Due to rounding errors, repeated corners could be added; this check prevents it
|
||||
if(nx1 != nx2 || ny1 != ny2)
|
||||
newPoly->AppendCorner( x1 + nx2, y1 + ny2 );
|
||||
}
|
||||
|
||||
|
@ -423,7 +426,7 @@ CPolyLine* CPolyLine::Fillet( unsigned int aRadius, unsigned int aSegments )
|
|||
double denom = sqrt( 2.0 / ( 1 + cosine ) - 1 );
|
||||
|
||||
// Do nothing in case of parallel edges
|
||||
if( !std::isfinite( denom ) )
|
||||
if( std::isinf( denom ) )
|
||||
continue;
|
||||
|
||||
// Limit rounding distance to one half of an edge
|
||||
|
@ -480,11 +483,22 @@ CPolyLine* CPolyLine::Fillet( unsigned int aRadius, unsigned int aSegments )
|
|||
else
|
||||
newPoly->AppendCorner( KiROUND( nx ), KiROUND( ny ) );
|
||||
|
||||
// Store the previous added corner to make a sanity check
|
||||
int prevX = KiROUND(nx);
|
||||
int prevY = KiROUND(ny);
|
||||
|
||||
for( unsigned int j = 0; j < segments; j++ )
|
||||
{
|
||||
nx = xc + cos( startAngle + (j + 1) * deltaAngle ) * radius;
|
||||
ny = yc - sin( startAngle + (j + 1) * deltaAngle ) * radius;
|
||||
|
||||
// Due to rounding errors, repeated corners could be added; this check prevents it
|
||||
if(KiROUND(nx) != prevX || KiROUND(ny) != prevY)
|
||||
{
|
||||
newPoly->AppendCorner( KiROUND( nx ), KiROUND( ny ) );
|
||||
prevX = KiROUND(nx);
|
||||
prevY = KiROUND(ny);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1154,7 +1168,7 @@ int CPolyLine::HitTestForEdge( const wxPoint& aPos, int aDistMax ) const
|
|||
if( m_CornersList.IsEndContour ( item_pos ) || end_segm >= lim )
|
||||
{
|
||||
unsigned tmp = first_corner_pos;
|
||||
first_corner_pos = end_segm; // first_corner_pos is now the beginning of the next outline
|
||||
first_corner_pos = end_segm; // first_corner_pos is the beginning of next outline
|
||||
end_segm = tmp; // end_segm is the beginning of the current outline
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue