mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-04-11 13:10:13 +00:00
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
common
include
pcbnew
class_board.cppclass_zone.cppclass_zone.hclass_zone_settings.cpp
dialogs
drc.cppeagle_plugin.cppedit.cppkicad_plugin.cpplegacy_plugin.cpponrightclick.cpppcad2kicadpcb_plugin
pcb_painter.cpppcb_parser.cppplot_brditems_plotter.cppratsnest_data.cppspecctra_export.cpptools
zone_filling_algorithm.cppzones_by_polygon.cppzones_convert_brd_items_to_polygons_with_Boost.cppzones_functions_for_undo_redo.cppzones_test_and_combine_areas.cpppolygon
@ -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 )
|
||||
{
|
||||
@ -363,7 +595,7 @@ void SHAPE_POLY_SET::importTree( PolyTree* tree )
|
||||
for( unsigned int i = 0; i < n->Childs.size(); i++ )
|
||||
paths.push_back( convertFromClipper( n->Childs[i]->Contour ) );
|
||||
|
||||
m_polys.push_back(paths);
|
||||
m_polys.push_back( paths );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -607,10 +889,10 @@ const std::string SHAPE_POLY_SET::Format() const
|
||||
for( unsigned i = 0; i < m_polys.size(); i++ )
|
||||
{
|
||||
ss << "poly " << m_polys[i].size() << "\n";
|
||||
for( unsigned j = 0; j < m_polys[i].size(); j++)
|
||||
for( unsigned j = 0; j < m_polys[i].size(); j++ )
|
||||
{
|
||||
ss << m_polys[i][j].PointCount() << "\n";
|
||||
for( int v = 0; v < m_polys[i][j].PointCount(); v++)
|
||||
for( int v = 0; v < m_polys[i][j].PointCount(); v++ )
|
||||
ss << m_polys[i][j].CPoint( v ).x << " " << m_polys[i][j].CPoint( v ).y << "\n";
|
||||
}
|
||||
ss << "\n";
|
||||
@ -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,25 +1096,131 @@ 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( pointInPolygon( aP, polys[0] ) )
|
||||
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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -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 )
|
||||
{
|
||||
m_currentVertex = 0;
|
||||
m_currentOutline++;
|
||||
// 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_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 );
|
||||
}
|
||||
}
|
||||
|
||||
delete pa;
|
||||
SHAPE_POLY_SET* new_p = new SHAPE_POLY_SET( aCurrArea->Outline()->UnitSet( 0 ) );
|
||||
delete aCurrArea->Outline();
|
||||
aCurrArea->SetOutline( new_p );
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
// Set GR mode to default
|
||||
current_gr_mode = draw_mode;
|
||||
|
||||
if( m_Poly->m_CornersList.IsEndContour( ic + 1 ) || (ic == icmax - 1) )
|
||||
SHAPE_POLY_SET::ITERATOR iterator_copy = iterator;
|
||||
iterator_copy++;
|
||||
if( iterator_copy.IsEndContour() )
|
||||
current_gr_mode = GR_XOR;
|
||||
else
|
||||
current_gr_mode = draw_mode;
|
||||
|
||||
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,29 +808,29 @@ 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 )
|
||||
text << wxT( " " ) << _( "(Cutout)" );
|
||||
// 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() )
|
||||
text << wxT( " " ) << _( "(Keepout)" );
|
||||
@ -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 );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -310,25 +345,45 @@ public:
|
||||
* if both aMinClearanceValue = 0 and aUseNetClearance = false: create the zone outline polygon.
|
||||
*/
|
||||
void TransformOutlinesShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
int aMinClearanceValue,
|
||||
bool aUseNetClearance );
|
||||
int aMinClearanceValue,
|
||||
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;
|
||||
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
|
||||
zone->AppendCorner( wxPoint( kicad_x( v.x ), kicad_y( v.y ) ) );
|
||||
// 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,9 +2720,8 @@ 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() ),
|
||||
true );
|
||||
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 );
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user