kicad-developers team mailing list archive
-
kicad-developers team
-
Mailing list archive
-
Message #27912
Re: [PATCH] CPolyLine -> SHAPE_POLY_SET refactor
Hi!
These last days I have detected a bunch of more bugs in my code regarding
zone editing, cutout zones, combination of self-intersecting polygons and
some drawing issues. All of them are now fixed, and some more tests are
added, both in my branch [1] and in the attached patches. Still, more
testing is needed, I don't want to break anything.
JP, I don't see the zoom and refill bugs you catched, can you provide me
with a test board in which these bugs occur, or describe a little bit more
a way to reproduce them? Thanks!
Btw, I don't know if I'm managing my branch in the correct way. I'm merging
the latest master revisions, maybe is it better to keep the branch clean
using rebase? I'm following the last comments in section Managing your own
branch [2].
Best,
Alejandro
[1] https://git.launchpad.net/~agarciamontoro/kicad/?h=polygon-refactor
[2] http://kicad-pcb.org/contribute/developers/#_managing_your_own_branch
2017-02-01 15:45 GMT+01:00 Maciej Sumiński <maciej.suminski@xxxxxxx>:
> On 02/01/2017 02:36 PM, jp charras wrote:
> [snip]
> > * In GAL canvas, depending on the zoom level and the size of the filled
> zones areas, filled zones
> > areas are displayed or not.
> > Same for zone outlines.
>
> Without looking at the code, my first shot is incorrect bounding box
> calculation (ViewBBox() method).
>
> Regards,
> Orson
>
>
> _______________________________________________
> Mailing list: https://launchpad.net/~kicad-developers
> Post to : kicad-developers@xxxxxxxxxxxxxxxxxxx
> Unsubscribe : https://launchpad.net/~kicad-developers
> More help : https://help.launchpad.net/ListHelp
>
>
From d1cb6ac38a92a0a4f9f5d05cb0f2f47b9b70a444 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Garc=C3=ADa=20Montoro?=
<alejandro.garciamontoro@xxxxxxxxx>
Date: Thu, 16 Feb 2017 18:09:10 +0100
Subject: [PATCH 1/2] CPolyLine -> SHAPE_POLY_SET refactor.
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------2.11.1"
This is a multi-part message in MIME format.
--------------2.11.1
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit
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.
---
common/geometry/shape_poly_set.cpp | 825 ++++++++++++++++++++-
common/swig/kicad.i | 1 +
include/class_eda_rect.h | 14 +
include/geometry/seg.h | 57 +-
include/geometry/shape_line_chain.h | 11 +
include/geometry/shape_poly_set.h | 725 +++++++++++++++++-
include/math/vector2d.h | 10 +
pcbnew/class_board.cpp | 31 +-
pcbnew/class_zone.cpp | 494 ++++++++----
pcbnew/class_zone.h | 277 ++++++-
pcbnew/class_zone_settings.cpp | 5 +-
pcbnew/dialogs/dialog_copper_zones.cpp | 12 +-
pcbnew/dialogs/dialog_keepout_area_properties.cpp | 13 +-
.../dialogs/dialog_non_copper_zones_properties.cpp | 12 +-
pcbnew/drc.cpp | 5 +-
pcbnew/eagle_plugin.cpp | 28 +-
pcbnew/edit.cpp | 2 +-
pcbnew/kicad_plugin.cpp | 23 +-
pcbnew/legacy_plugin.cpp | 19 +-
pcbnew/onrightclick.cpp | 6 +-
pcbnew/pcad2kicadpcb_plugin/pcb_polygon.cpp | 13 +-
pcbnew/pcb_painter.cpp | 9 +-
pcbnew/pcb_parser.cpp | 12 +-
pcbnew/ratsnest_data.cpp | 2 +-
pcbnew/specctra_export.cpp | 51 +-
pcbnew/tools/drawing_tool.cpp | 18 +-
pcbnew/tools/edit_tool.cpp | 26 +-
pcbnew/tools/grid_helper.cpp | 10 +-
pcbnew/tools/pcb_editor_control.cpp | 12 +-
pcbnew/tools/point_editor.cpp | 54 +-
pcbnew/zone_filling_algorithm.cpp | 13 +-
pcbnew/zones_by_polygon.cpp | 57 +-
...es_convert_brd_items_to_polygons_with_Boost.cpp | 2 +-
pcbnew/zones_functions_for_undo_redo.cpp | 3 +-
pcbnew/zones_test_and_combine_areas.cpp | 338 ++++-----
polygon/PolyLine.cpp | 22 +-
36 files changed, 2545 insertions(+), 667 deletions(-)
--------------2.11.1
Content-Type: text/x-patch; name="0001-CPolyLine-SHAPE_POLY_SET-refactor.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0001-CPolyLine-SHAPE_POLY_SET-refactor.patch"
diff --git a/common/geometry/shape_poly_set.cpp b/common/geometry/shape_poly_set.cpp
index 287ebd6b1..95d489cce 100644
--- a/common/geometry/shape_poly_set.cpp
+++ b/common/geometry/shape_poly_set.cpp
@@ -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@xxxxxxx>
+ * @author Alejandro García Montoro <alejandro.garciamontoro@xxxxxxxxx>
*
* Point in polygon algorithm adapted from Clipper Library (C) Angus Johnson,
* subject to Clipper library license.
@@ -35,13 +36,19 @@
#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_poly_set.h>
+#include <common.h> // KiROUND
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 +57,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 +162,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 +188,33 @@ 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; abort otherwise
+ assert( GetRelativeIndices( aGlobalIndex, &index ) );
+
+ m_polys[index.m_polygon][index.m_contour].Insert( index.m_vertex, aNewVertex );
+ }
+
+}
+
+
int SHAPE_POLY_SET::VertexCount( int aOutline , int aHole ) const
{
if( aOutline < 0 )
@@ -112,7 +234,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 )
+{
+ assert( aFirstPolygon >= 0 && aLastPolygon <= OutlineCount() );
+
+ SHAPE_POLY_SET newPolySet;
+
+ for( int index = aFirstPolygon; index < aLastPolygon; index++ )
+ {
+ newPolySet.m_polys.push_back( Polygon( index ) );
+ }
+
+ return newPolySet;
+}
+
+
+VECTOR2I& SHAPE_POLY_SET::Vertex( int index, int aOutline , int aHole )
{
if( aOutline < 0 )
aOutline += m_polys.size();
@@ -127,11 +264,11 @@ const VECTOR2I& SHAPE_POLY_SET::CVertex( int index, int aOutline , int aHole ) c
assert( aOutline < (int)m_polys.size() );
assert( idx < (int)m_polys[aOutline].size() );
- return m_polys[aOutline][idx].CPoint( index );
+ return m_polys[aOutline][idx].Point( index );
}
-VECTOR2I& SHAPE_POLY_SET::Vertex( int index, int aOutline , int aHole )
+const VECTOR2I& SHAPE_POLY_SET::CVertex( int index, int aOutline , int aHole ) const
{
if( aOutline < 0 )
aOutline += m_polys.size();
@@ -146,7 +283,94 @@ 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].CPoint( index );
+}
+
+
+VECTOR2I& SHAPE_POLY_SET::Vertex( int aGlobalIndex )
+{
+ SHAPE_POLY_SET::VERTEX_INDEX index;
+
+ // Assure the passed index references a legal position; abort otherwise
+ assert( GetRelativeIndices( aGlobalIndex, &index ) );
+
+ 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
+ assert( GetRelativeIndices( aGlobalIndex, &index ) );
+
+ 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, abort, it is like an illegal access memory error
+ assert( GetRelativeIndices( aGlobalIndex, &indices ) );
+
+ 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 +429,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 +590,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 +638,7 @@ struct FractureEdge
typedef std::vector<FractureEdge*> FractureEdgeSet;
+
static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
{
int x = edge->m_p1.x;
@@ -478,6 +706,7 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
return 0;
}
+
void SHAPE_POLY_SET::fractureSingle( POLYGON& paths )
{
FractureEdgeSet edges;
@@ -590,6 +819,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 +842,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 +884,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 +971,107 @@ 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.
+ iterator++;
+ }
+ }
+
+ return removed;
+}
+
+
void SHAPE_POLY_SET::DeletePolygon( int aIdx )
{
m_polys.erase( m_polys.begin() + aIdx );
@@ -718,26 +1090,131 @@ void SHAPE_POLY_SET::Append( const VECTOR2I& aP, int aOutline, int aHole )
}
-bool SHAPE_POLY_SET::Contains( const VECTOR2I& aP, int aSubpolyIndex ) const
+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 )
{
- // fixme: support holes!
+ // 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
+{
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
+ assert( GetRelativeIndices( aGlobalIndex, &index ) );
+
+ RemoveVertex( index );
+}
+
+
+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 +1309,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;
+}
diff --git a/common/swig/kicad.i b/common/swig/kicad.i
index 6382369e0..cac7f6fbe 100644
--- a/common/swig/kicad.i
+++ b/common/swig/kicad.i
@@ -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=;
diff --git a/include/class_eda_rect.h b/include/class_eda_rect.h
index ba14b981c..8d74d8781 100644
--- a/include/class_eda_rect.h
+++ b/include/class_eda_rect.h
@@ -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
@@ -178,6 +180,18 @@ public:
}
/**
+ * 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
* and/or \a dy is negative the rectangle is deflated.
diff --git a/include/geometry/seg.h b/include/geometry/seg.h
index 164457b93..ee754fd51 100644
--- a/include/geometry/seg.h
+++ b/include/geometry/seg.h
@@ -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,28 +78,63 @@ 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
* @param aA reference to the start point in the parent shape
* @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 )
diff --git a/include/geometry/shape_line_chain.h b/include/geometry/shape_line_chain.h
index 9f62aaabc..3552b7acd 100644
--- a/include/geometry/shape_line_chain.h
+++ b/include/geometry/shape_line_chain.h
@@ -411,6 +411,17 @@ 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()
*
* Inserts the point aP belonging to one of the our segments, splitting the adjacent
diff --git a/include/geometry/shape_poly_set.h b/include/geometry/shape_poly_set.h
index 842f2de72..159761482 100644
--- a/include/geometry/shape_poly_set.h
+++ b/include/geometry/shape_poly_set.h
@@ -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@xxxxxxx>
+ * @author Alejandro García Montoro <alejandro.garciamontoro@xxxxxxxxx>
*
* 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
@@ -49,38 +58,95 @@ class SHAPE_POLY_SET : public SHAPE
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 +162,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,36 +175,258 @@ class SHAPE_POLY_SET : public SHAPE
return &Get();
}
+ /**
+ * Function GetIndex
+ * @return VERTEX_INDEX - returns the 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 - returns the 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 [description]
+ */
+ 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();
///> Creates a new hole in a given outline
int NewHole( int aOutline = -1 );
- ///> Adds a new outline to the set and returns its index
+ ///> Adds a new outline to the set and returns its indexs
int AddOutline( const SHAPE_LINE_CHAIN& aOutline );
///> Adds a new hole to the given outline (default: last) and returns its index
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 );
+ /**
+ * 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
+ VECTOR2I& Vertex( int index, int aOutline, int aHole );
+
///> Returns the index-th vertex in a given hole outline within a given outline
- VECTOR2I& Vertex( int index, int aOutline = -1, int aHole = -1 );
+ const VECTOR2I& CVertex( int index, 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 index );
///> 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;
+ const VECTOR2I& CVertex( VERTEX_INDEX index ) 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 );;
- ///> Returns true if any of the outlines is self-intersecting
+
+ /**
+ * 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,13 @@ class SHAPE_POLY_SET : public SHAPE
return m_polys[aIndex][0];
}
+ 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 +543,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 +629,79 @@ 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;
+
+ assert( GetRelativeIndices( aGlobalIdx, &indices ) );
+
+ // 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 all outlines 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 +760,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 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 +786,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 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 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 +847,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 A POLYGON object 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 A POLYGON object 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 A SHAPE_POLY_SET object 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 A SHAPE_POLY_SET object 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 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 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 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 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 +1007,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;
diff --git a/include/math/vector2d.h b/include/math/vector2d.h
index df2cfda26..839b14182 100644
--- a/include/math/vector2d.h
+++ b/include/math/vector2d.h
@@ -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();
diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp
index d715f442f..d4bbd0332 100644
--- a/pcbnew/class_board.cpp
+++ b/pcbnew/class_board.cpp
@@ -2363,52 +2363,57 @@ 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;
}
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), ZONE_CONTAINER::NO_HATCH );
// remove the poly that was automatically created for the new area
// and replace it with a poly from NormalizeAreaOutlines
delete NewArea->Outline();
NewArea->SetOutline( new_p );
- NewArea->Outline()->Hatch();
+ NewArea->Hatch();
NewArea->SetLocalFlags( 1 );
}
+
+ SHAPE_POLY_SET* new_p = new SHAPE_POLY_SET( aCurrArea->Outline()->UnitSet( 0 ) );
+ delete aCurrArea->Outline();
+ aCurrArea->SetOutline( new_p );
}
- delete pa;
+ aCurrArea->Hatch();
}
- curr_polygon->Hatch();
return true;
}
diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp
index 0ac7966a0..78293fe48 100644
--- a/pcbnew/class_zone.cpp
+++ b/pcbnew/class_zone.cpp
@@ -52,7 +52,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;
@@ -64,7 +64,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 );
}
@@ -76,10 +76,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;
@@ -100,6 +100,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() );
}
@@ -108,9 +112,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)
@@ -118,9 +124,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();
@@ -133,7 +139,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;
}
@@ -158,9 +165,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;
}
@@ -196,38 +207,31 @@ void ZONE_CONTAINER::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE aDrawMod
SetAlpha( &color, 150 );
// draw the lines
- int i_start_contour = 0;
std::vector<wxPoint> lines;
lines.reserve( (GetNumCorners() * 2) + 2 );
- for( int ic = 0; ic < GetNumCorners(); ic++ )
- {
- seg_start = GetCornerPosition( ic ) + offset;
+ // Object to iterate through the segments of the outline
+ SHAPE_POLY_SET::SEGMENT_ITERATOR 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;
- }
+ for( iterator = m_Poly->IterateSegmentsWithHoles(); iterator; iterator++ )
+ {
+ // Create the segment
+ SEG segment = *iterator;
- 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 );
}
@@ -360,7 +364,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 );
@@ -394,45 +398,62 @@ void ZONE_CONTAINER::DrawWhileCreateOutline( EDA_DRAW_PANEL* panel, wxDC* DC,
ColorTurnToDarkDarkGray( &color );
}
- // 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();
+
+ // Segment start and end
+ VECTOR2I seg_start, seg_end;
- for( int ic = 0; ic <= icmax; ic++ )
+ // 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 );
}
}
@@ -465,90 +486,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;
}
@@ -598,9 +638,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 ) );
@@ -651,7 +690,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 )
@@ -662,7 +701,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() )
@@ -678,12 +717,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 ) );
@@ -697,23 +733,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();
}
@@ -721,15 +744,15 @@ void ZONE_CONTAINER::Rotate( const wxPoint& centre, double angle )
{
wxPoint pos;
- for( unsigned ic = 0; ic < m_Poly->m_CornersList.GetCornersCount(); ic++ )
+ for( SHAPE_POLY_SET::ITERATOR 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 )
@@ -753,13 +776,13 @@ 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( SHAPE_POLY_SET::ITERATOR 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 )
{
@@ -789,18 +812,19 @@ 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
{
@@ -808,10 +832,9 @@ wxString ZONE_CONTAINER::GetSelectMenuText() const
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)" );
@@ -851,3 +874,202 @@ wxString ZONE_CONTAINER::GetSelectMenuText() const
return msg;
}
+
+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( SHAPE_POLY_SET::ITERATOR 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();
+
+ SHAPE_POLY_SET::SEGMENT_ITERATOR iterator;
+
+ // Iterate through all vertices
+ for( 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 ) );
+ }
+ }
+ }
+}
diff --git a/pcbnew/class_zone.h b/pcbnew/class_zone.h
index ab488a196..c41f08f1a 100644
--- a/pcbnew/class_zone.h
+++ b/pcbnew/class_zone.h
@@ -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,28 @@ 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;
+ }
+ }
///
// Like HitTest but selects the current corner to be operated on
@@ -209,10 +242,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 +264,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 +343,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 true if some corner was found to be closer to refPos than aClearance; false
+ * otherwise.
+ */
+ 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 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.
+ * @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 true if some edge was found to be closer to refPos than aClearance.
*/
- int HitTestForCorner( 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.
- * 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.
+ * @return true if some edge was found to be closer to refPos than aClearance.
*/
- int HitTestForEdge( const wxPoint& refPos ) const;
+ bool HitTestForEdge( const wxPoint& refPos ) const;
/** @copydoc BOARD_ITEM::HitTest(const EDA_RECT& aRect,
* bool aContained = true, int aAccuracy ) const
@@ -410,7 +463,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 +501,58 @@ 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
+ assert( m_Poly->GetRelativeIndices( aCornerIndex, &index ) );
+
+ 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
+ assert( m_Poly->GetRelativeIndices( aCornerIndex, &relativeIndices ) );
+
+ m_Poly->Vertex( relativeIndices ).x = new_pos.x;
+ m_Poly->Vertex( relativeIndices ).y = new_pos.y;
}
- 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 +596,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 +645,56 @@ 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();
+
+
#if defined(DEBUG)
virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
#endif
@@ -543,8 +704,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 +748,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 +763,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");
+
};
diff --git a/pcbnew/class_zone_settings.cpp b/pcbnew/class_zone_settings.cpp
index 9dc88ba7d..7aa2fc708 100644
--- a/pcbnew/class_zone_settings.cpp
+++ b/pcbnew/class_zone_settings.cpp
@@ -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 );
}
diff --git a/pcbnew/dialogs/dialog_copper_zones.cpp b/pcbnew/dialogs/dialog_copper_zones.cpp
index 2f34a5157..0b3995250 100644
--- a/pcbnew/dialogs/dialog_copper_zones.cpp
+++ b/pcbnew/dialogs/dialog_copper_zones.cpp
@@ -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;
}
diff --git a/pcbnew/dialogs/dialog_keepout_area_properties.cpp b/pcbnew/dialogs/dialog_keepout_area_properties.cpp
index e9f488700..b8941d991 100644
--- a/pcbnew/dialogs/dialog_keepout_area_properties.cpp
+++ b/pcbnew/dialogs/dialog_keepout_area_properties.cpp
@@ -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;
}
diff --git a/pcbnew/dialogs/dialog_non_copper_zones_properties.cpp b/pcbnew/dialogs/dialog_non_copper_zones_properties.cpp
index 552dd7304..2021b5703 100644
--- a/pcbnew/dialogs/dialog_non_copper_zones_properties.cpp
+++ b/pcbnew/dialogs/dialog_non_copper_zones_properties.cpp
@@ -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;
}
diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp
index bdc14b7e6..a5827a8f8 100644
--- a/pcbnew/drc.cpp
+++ b/pcbnew/drc.cpp
@@ -1,4 +1,3 @@
-
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
@@ -646,7 +645,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 )
{
m_currentMarker = fillMarker( segm, NULL,
@@ -827,7 +826,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,
diff --git a/pcbnew/eagle_plugin.cpp b/pcbnew/eagle_plugin.cpp
index 3913f29a2..229326d3d 100644
--- a/pcbnew/eagle_plugin.cpp
+++ b/pcbnew/eagle_plugin.cpp
@@ -1604,17 +1604,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();
@@ -2764,7 +2762,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
@@ -2772,32 +2769,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?
diff --git a/pcbnew/edit.cpp b/pcbnew/edit.cpp
index c5e345392..a12d3f2fd 100644
--- a/pcbnew/edit.cpp
+++ b/pcbnew/edit.cpp
@@ -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 );
diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp
index 93f3b8e3c..0605bbaef 100644
--- a/pcbnew/kicad_plugin.cpp
+++ b/pcbnew/kicad_plugin.cpp
@@ -1519,13 +1519,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() );
@@ -1607,22 +1607,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( SHAPE_POLY_SET::ITERATOR 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 )
{
@@ -1634,14 +1633,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" );
@@ -1689,7 +1688,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" );
diff --git a/pcbnew/legacy_plugin.cpp b/pcbnew/legacy_plugin.cpp
index 21026b155..8e0d1e36f 100644
--- a/pcbnew/legacy_plugin.cpp
+++ b/pcbnew/legacy_plugin.cpp
@@ -2486,7 +2486,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;
@@ -2501,17 +2501,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
@@ -2552,9 +2548,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() );
@@ -2736,9 +2732,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() );
}
diff --git a/pcbnew/onrightclick.cpp b/pcbnew/onrightclick.cpp
index f0027fc44..561f40bde 100644
--- a/pcbnew/onrightclick.cpp
+++ b/pcbnew/onrightclick.cpp
@@ -715,14 +715,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 ) );
@@ -770,7 +770,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 ) );
diff --git a/pcbnew/pcad2kicadpcb_plugin/pcb_polygon.cpp b/pcbnew/pcad2kicadpcb_plugin/pcb_polygon.cpp
index b3b4ec531..7a09351ce 100644
--- a/pcbnew/pcad2kicadpcb_plugin/pcb_polygon.cpp
+++ b/pcbnew/pcad2kicadpcb_plugin/pcb_polygon.cpp
@@ -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' ) )
{
diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp
index 22060521e..89307a857 100644
--- a/pcbnew/pcb_painter.cpp
+++ b/pcbnew/pcb_painter.cpp
@@ -954,12 +954,13 @@ 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 )
+ SHAPE_POLY_SET::CONST_ITERATOR iterator;
+
+ for( 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] );
diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp
index 744bc7398..dd3c9569e 100644
--- a/pcbnew/pcb_parser.cpp
+++ b/pcbnew/pcb_parser.cpp
@@ -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() )
diff --git a/pcbnew/ratsnest_data.cpp b/pcbnew/ratsnest_data.cpp
index b2e1f4da2..fb2d6032c 100644
--- a/pcbnew/ratsnest_data.cpp
+++ b/pcbnew/ratsnest_data.cpp
@@ -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 );
diff --git a/pcbnew/specctra_export.cpp b/pcbnew/specctra_export.cpp
index eb2c7e51c..809184716 100644
--- a/pcbnew/specctra_export.cpp
+++ b/pcbnew/specctra_export.cpp
@@ -1658,26 +1658,28 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard )
mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
- int count = item->Outline()->m_CornersList.GetCornersCount();
- int ndx = 0; // used in 2 for() loops below
- for( ; ndx<count; ++ndx )
+ // Handle the main outlines
+ SHAPE_POLY_SET::ITERATOR iterator;
+ for( iterator = item->IterateWithHoles(); iterator; iterator++ )
{
- wxPoint point( item->Outline()->m_CornersList[ndx].x,
- item->Outline()->m_CornersList[ndx].y );
+ wxPoint point( iterator->x, iterator->y );
mainPolygon->AppendPoint( mapPt(point) );
// this was the end of the main polygon
- if( item->Outline()->m_CornersList[ndx].end_contour )
+ if( iterator.IsEndContour() )
break;
+
}
WINDOW* window = 0;
PATH* cutout = 0;
+ bool isStartContour = true;
+
// handle the cutouts
- for( ++ndx; ndx<count; ++ndx )
+ for( iterator++; iterator; iterator++ )
{
- if( item->Outline()->m_CornersList[ndx-1].end_contour )
+ if( isStartContour )
{
window = new WINDOW( plane );
@@ -1690,11 +1692,15 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard )
cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
}
+ // If the point in this iteration is the last of the contour, the next iteration
+ // will start with a new contour.
+ isStartContour = iterator.IsEndContour();
+
wxASSERT( window );
wxASSERT( cutout );
- wxPoint point(item->Outline()->m_CornersList[ndx].x,
- item->Outline()->m_CornersList[ndx].y );
+ wxPoint point(iterator->x,
+ iterator->y );
cutout->AppendPoint( mapPt(point) );
}
}
@@ -1735,41 +1741,46 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard )
mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
- int count = item->Outline()->m_CornersList.GetCornersCount();
- int ndx = 0; // used in 2 for() loops below
- for( ; ndx<count; ++ndx )
+ // Handle the main outlines
+ SHAPE_POLY_SET::ITERATOR iterator;
+ for( iterator = item->IterateWithHoles(); iterator; iterator++ )
{
- wxPoint point( item->Outline()->m_CornersList[ndx].x,
- item->Outline()->m_CornersList[ndx].y );
+ wxPoint point( iterator->x, iterator->y );
mainPolygon->AppendPoint( mapPt(point) );
// this was the end of the main polygon
- if( item->Outline()->m_CornersList[ndx].end_contour )
+ if( iterator.IsEndContour() )
break;
+
}
WINDOW* window = 0;
PATH* cutout = 0;
+ bool isStartContour = true;
+
// handle the cutouts
- for( ++ndx; ndx<count; ++ndx )
+ for( iterator++; iterator; iterator++ )
{
- if( item->Outline()->m_CornersList[ndx-1].end_contour )
+ if( isStartContour )
{
window = new WINDOW( keepout );
keepout->AddWindow( window );
cutout = new PATH( window, T_polygon );
+
window->SetShape( cutout );
cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
}
+ isStartContour = iterator.IsEndContour();
+
wxASSERT( window );
wxASSERT( cutout );
- wxPoint point(item->Outline()->m_CornersList[ndx].x,
- item->Outline()->m_CornersList[ndx].y );
+ wxPoint point(iterator->x,
+ iterator->y );
cutout->AppendPoint( mapPt(point) );
}
}
diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp
index 8ced6d3d9..75ca0760d 100644
--- a/pcbnew/tools/drawing_tool.cpp
+++ b/pcbnew/tools/drawing_tool.cpp
@@ -1248,14 +1248,8 @@ bool DRAWING_TOOL::getSourceZoneForAction( ZONE_MODE aMode, ZONE_CONTAINER*& aZo
void DRAWING_TOOL::performZoneCutout( ZONE_CONTAINER& aExistingZone, ZONE_CONTAINER& aCutout )
{
- // Copy cutout corners into existing zone
- for( int ii = 0; ii < aCutout.GetNumCorners(); ii++ )
- {
- aExistingZone.AppendCorner( aCutout.GetCornerPosition( ii ) );
- }
-
- // Close the current corner list
- aExistingZone.Outline()->CloseLastContour();
+ // Add the polyline of aCutout zone as a hole in the existing zone
+ aExistingZone.Outline()->AddHole( aCutout.Outline()->Outline( 0 ) );
m_board->OnAreaPolygonModified( nullptr, &aExistingZone );
@@ -1366,7 +1360,6 @@ int DRAWING_TOOL::drawZone( bool aKeepout, ZONE_MODE aMode )
if( direction45 )
zone->AppendCorner( cursorPos == origin ? line45.GetStart() : line45.GetEnd() );
- zone->Outline()->CloseLastContour();
zone->Outline()->RemoveNullSegments();
if( !aKeepout )
@@ -1426,10 +1419,11 @@ int DRAWING_TOOL::drawZone( bool aKeepout, ZONE_MODE aMode )
m_frame->GetGalCanvas()->SetTopLayer( zone->GetLayer() );
+
// Add the first point
- zone->Outline()->Start( zone->GetLayer(),
- cursorPos.x, cursorPos.y,
- zone->GetHatchStyle() );
+ zone->SetLayer( zone->GetLayer() );
+ zone->AppendCorner( static_cast<wxPoint>( cursorPos ) );
+
origin = cursorPos;
// Helper line represents the currently drawn line of the zone polygon
diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp
index 3e5efb774..6dabfb3b1 100644
--- a/pcbnew/tools/edit_tool.cpp
+++ b/pcbnew/tools/edit_tool.cpp
@@ -179,7 +179,19 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
// Main loop: keep receiving events
do
{
- if( evt->IsAction( &COMMON_ACTIONS::editActivate )
+ if( evt->IsCancel() )
+ {
+ restore = true; // Cancelling the tool means that items have to be restored
+ break; // Finish
+ }
+
+ else if( evt->Action() == TA_UNDO_REDO_PRE )
+ {
+ unselect = true;
+ break;
+ }
+
+ else if( evt->IsAction( &COMMON_ACTIONS::editActivate )
|| evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
{
if( selection.Empty() )
@@ -252,18 +264,6 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
m_toolMgr->RunAction( COMMON_ACTIONS::editModifiedSelection, true );
}
- else if( evt->IsCancel() || evt->IsActivate() )
- {
- restore = true; // Cancelling the tool means that items have to be restored
- break; // Finish
- }
-
- else if( evt->Action() == TA_UNDO_REDO_PRE )
- {
- unselect = true;
- break;
- }
-
// Dispatch TOOL_ACTIONs
else if( evt->Category() == TC_COMMAND )
{
diff --git a/pcbnew/tools/grid_helper.cpp b/pcbnew/tools/grid_helper.cpp
index 2ae97e034..930156b1e 100644
--- a/pcbnew/tools/grid_helper.cpp
+++ b/pcbnew/tools/grid_helper.cpp
@@ -339,17 +339,15 @@ void GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos )
case PCB_ZONE_AREA_T:
{
- const CPolyLine* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
- int cornersCount = outline->GetCornersCount();
+ const SHAPE_POLY_SET* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
SHAPE_LINE_CHAIN lc;
lc.SetClosed( true );
- for( int i = 0; i < cornersCount; ++i )
+ for( SHAPE_POLY_SET::CONST_ITERATOR iter = outline->CIterateWithHoles(); iter; iter++ )
{
- const VECTOR2I p ( outline->GetPos( i ) );
- addAnchor( p, CORNER, aItem );
- lc.Append( p );
+ addAnchor( *iter, CORNER, aItem );
+ lc.Append( *iter );
}
addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );
diff --git a/pcbnew/tools/pcb_editor_control.cpp b/pcbnew/tools/pcb_editor_control.cpp
index 788d77411..6f1070689 100644
--- a/pcbnew/tools/pcb_editor_control.cpp
+++ b/pcbnew/tools/pcb_editor_control.cpp
@@ -636,22 +636,21 @@ int PCB_EDITOR_CONTROL::ZoneUnfillAll( const TOOL_EVENT& aEvent )
static bool mergeZones( BOARD_COMMIT& aCommit, std::vector<ZONE_CONTAINER *>& aOriginZones,
std::vector<ZONE_CONTAINER *>& aMergedZones )
{
- SHAPE_POLY_SET mergedOutlines = ConvertPolyListToPolySet( aOriginZones[0]->Outline()->m_CornersList );
for( unsigned int i = 1; i < aOriginZones.size(); i++ )
{
- SHAPE_POLY_SET areaToMergePoly = ConvertPolyListToPolySet( aOriginZones[i]->Outline()->m_CornersList );
+ SHAPE_POLY_SET areaToMergePoly = *aOriginZones[i]->Outline();
- mergedOutlines.BooleanAdd( areaToMergePoly, SHAPE_POLY_SET::PM_FAST );
+ aOriginZones[0]->Outline()->BooleanAdd( areaToMergePoly, SHAPE_POLY_SET::PM_FAST );
}
- mergedOutlines.Simplify( SHAPE_POLY_SET::PM_FAST );
+ aOriginZones[0]->Outline()->Simplify( SHAPE_POLY_SET::PM_FAST );
// We should have one polygon with hole
// We can have 2 polygons with hole, if the 2 initial polygons have only one common corner
// and therefore cannot be merged (they are dectected as intersecting)
// but we should never have more than 2 polys
- if( mergedOutlines.OutlineCount() > 1 )
+ if( aOriginZones[0]->Outline()->OutlineCount() > 1 )
{
wxLogMessage( wxT( "BOARD::CombineAreas error: more than 2 polys after merging" ) );
return false;
@@ -665,9 +664,8 @@ static bool mergeZones( BOARD_COMMIT& aCommit, std::vector<ZONE_CONTAINER *>& aO
aCommit.Modify( aOriginZones[0] );
aMergedZones.push_back( aOriginZones[0] );
- aOriginZones[0]->Outline()->m_CornersList = ConvertPolySetToPolyList( mergedOutlines );
aOriginZones[0]->SetLocalFlags( 1 );
- aOriginZones[0]->Outline()->Hatch();
+ aOriginZones[0]->Hatch();
return true;
}
diff --git a/pcbnew/tools/point_editor.cpp b/pcbnew/tools/point_editor.cpp
index 40088e9e9..3eba725af 100644
--- a/pcbnew/tools/point_editor.cpp
+++ b/pcbnew/tools/point_editor.cpp
@@ -117,14 +117,18 @@ public:
case PCB_ZONE_AREA_T:
{
- const CPolyLine* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
- int cornersCount = outline->GetCornersCount();
+ const SHAPE_POLY_SET* outline;
+ outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
- for( int i = 0; i < cornersCount; ++i )
+ int cornersCount = outline->TotalVertices();
+
+ SHAPE_POLY_SET::CONST_ITERATOR iterator;
+
+ for( iterator = outline->CIterateWithHoles(); iterator; iterator++ )
{
- points->AddPoint( outline->GetPos( i ) );
+ points->AddPoint( *iterator );
- if( outline->IsEndContour( i ) )
+ if( iterator.IsEndContour() )
points->AddBreak();
}
@@ -449,13 +453,12 @@ void POINT_EDITOR::updateItem() const
{
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
zone->ClearFilledPolysList();
- CPolyLine* outline = zone->Outline();
+ SHAPE_POLY_SET* outline = zone->Outline();
- for( int i = 0; i < outline->GetCornersCount(); ++i )
+ for( int i = 0; i < outline->TotalVertices(); ++i )
{
VECTOR2I point = m_editPoints->Point( i ).GetPosition();
- outline->SetX( i, point.x );
- outline->SetY( i, point.y );
+ outline->Vertex( i ) = point;
}
break;
@@ -575,9 +578,9 @@ void POINT_EDITOR::updatePoints()
case PCB_ZONE_AREA_T:
{
const ZONE_CONTAINER* zone = static_cast<const ZONE_CONTAINER*>( item );
- const CPolyLine* outline = zone->Outline();
+ const SHAPE_POLY_SET* outline = zone->Outline();
- if( m_editPoints->PointsSize() != (unsigned) outline->GetCornersCount() )
+ if( m_editPoints->PointsSize() != (unsigned) outline->TotalVertices() )
{
getView()->Remove( m_editPoints.get() );
m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
@@ -585,8 +588,8 @@ void POINT_EDITOR::updatePoints()
}
else
{
- for( int i = 0; i < outline->GetCornersCount(); ++i )
- m_editPoints->Point( i ).SetPosition( outline->GetPos( i ) );
+ for( int i = 0; i < outline->TotalVertices(); ++i )
+ m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) );
}
break;
@@ -766,19 +769,18 @@ int POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent )
if( item->Type() == PCB_ZONE_AREA_T )
{
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
- CPolyLine* outline = zone->Outline();
+ SHAPE_POLY_SET* outline = zone->Outline();
commit.Modify( zone );
// Handle the last segment, so other segments can be easily handled in a loop
- unsigned int nearestIdx = outline->GetCornersCount() - 1, nextNearestIdx = 0;
- SEG side( VECTOR2I( outline->GetPos( nearestIdx ) ),
- VECTOR2I( outline->GetPos( nextNearestIdx ) ) );
+ unsigned int nearestIdx = outline->TotalVertices() - 1, nextNearestIdx = 0;
+ SEG side( outline->Vertex( nearestIdx ), outline->Vertex( nextNearestIdx ) );
unsigned int nearestDist = side.Distance( cursorPos );
- for( int i = 0; i < outline->GetCornersCount() - 1; ++i )
+ for( int i = 0; i < outline->TotalVertices() - 1; ++i )
{
- side = SEG( VECTOR2I( outline->GetPos( i ) ), VECTOR2I( outline->GetPos( i + 1 ) ) );
+ side = SEG( outline->Vertex( i ), outline->Vertex( i + 1 ) );
unsigned int distance = side.Distance( cursorPos );
if( distance < nearestDist )
@@ -790,8 +792,8 @@ int POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent )
}
// Find the point on the closest segment
- VECTOR2I sideOrigin( outline->GetPos( nearestIdx ) );
- VECTOR2I sideEnd( outline->GetPos( nextNearestIdx ) );
+ VECTOR2I sideOrigin = outline->Vertex( nearestIdx );
+ VECTOR2I sideEnd = outline->Vertex( nextNearestIdx );
SEG nearestSide( sideOrigin, sideEnd );
VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
@@ -800,7 +802,7 @@ int POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent )
if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
nearestPoint = ( sideOrigin + sideEnd ) / 2;
- outline->InsertCorner( nearestIdx, nearestPoint.x, nearestPoint.y );
+ outline->InsertVertex( nearestIdx, nearestPoint );
commit.Push( _( "Add a zone corner" ) );
}
@@ -862,14 +864,14 @@ int POINT_EDITOR::removeCorner( const TOOL_EVENT& aEvent )
BOARD_COMMIT commit( frame );
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
- CPolyLine* outline = zone->Outline();
+ SHAPE_POLY_SET* outline = zone->Outline();
commit.Modify( zone );
- for( int i = 0; i < outline->GetCornersCount(); ++i )
+ for( int i = 0; i < outline->TotalVertices(); ++i )
{
- if( VECTOR2I( outline->GetPos( i ) ) == m_editedPoint->GetPosition() )
+ if( outline->Vertex( i ) == m_editedPoint->GetPosition() )
{
- outline->DeleteCorner( i );
+ outline->RemoveVertex( i );
setEditedPoint( NULL );
commit.Push( _( "Remove a zone corner" ) );
break;
diff --git a/pcbnew/zone_filling_algorithm.cpp b/pcbnew/zone_filling_algorithm.cpp
index a8eed7ebb..c3c5b9802 100644
--- a/pcbnew/zone_filling_algorithm.cpp
+++ b/pcbnew/zone_filling_algorithm.cpp
@@ -75,11 +75,13 @@ bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET*
switch( m_cornerSmoothingType )
{
case ZONE_SETTINGS::SMOOTHING_CHAMFER:
- m_smoothedPoly = m_Poly->Chamfer( m_cornerRadius );
+ m_smoothedPoly = new SHAPE_POLY_SET();
+ *m_smoothedPoly = m_Poly->Chamfer( m_cornerRadius );
break;
case ZONE_SETTINGS::SMOOTHING_FILLET:
- m_smoothedPoly = m_Poly->Fillet( m_cornerRadius, m_ArcToSegmentsCount );
+ m_smoothedPoly = new SHAPE_POLY_SET();
+ *m_smoothedPoly = m_Poly->Fillet( m_cornerRadius, m_ArcToSegmentsCount );
break;
default:
@@ -88,12 +90,13 @@ bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET*
// We can avoid issues by creating a very small chamfer which remove acute angles,
// or left it without chamfer and use only CPOLYGONS_LIST::InflateOutline to create
// clearance areas
- m_smoothedPoly = m_Poly->Chamfer( Millimeter2iu( 0.0 ) );
+ m_smoothedPoly = new SHAPE_POLY_SET();
+ *m_smoothedPoly = m_Poly->Chamfer( Millimeter2iu( 0.0 ) );
break;
}
if( aOutlineBuffer )
- aOutlineBuffer->Append( ConvertPolyListToPolySet( m_smoothedPoly->m_CornersList ) );
+ aOutlineBuffer->Append( *m_smoothedPoly );
/* For copper layers, we now must add holes in the Polygon list.
* holes are pads and tracks with their clearance area
@@ -118,7 +121,7 @@ bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET*
{
m_FillMode = 0; // Fill by segments is no more used in non copper layers
// force use solid polygons (usefull only for old boards)
- m_FilledPolysList = ConvertPolyListToPolySet( m_smoothedPoly->m_CornersList );
+ m_FilledPolysList = *m_smoothedPoly;
// The filled areas are deflated by -m_ZoneMinThickness / 2, because
// the outlines are drawn with a line thickness = m_ZoneMinThickness to
diff --git a/pcbnew/zones_by_polygon.cpp b/pcbnew/zones_by_polygon.cpp
index a29fd8098..ca2d9bdbc 100644
--- a/pcbnew/zones_by_polygon.cpp
+++ b/pcbnew/zones_by_polygon.cpp
@@ -135,7 +135,7 @@ void PCB_EDIT_FRAME::duplicateZone( wxDC* aDC, ZONE_CONTAINER* aZone )
if( success )
{
zoneSettings.ExportSetting( *newZone );
- newZone->Outline()->Hatch();
+ newZone->Hatch();
s_AuxiliaryList.ClearListAndDeleteItems();
s_PickedList.ClearListAndDeleteItems();
@@ -185,7 +185,7 @@ int PCB_EDIT_FRAME::Delete_LastCreatedCorner( wxDC* DC )
if( zone->GetNumCorners() > 2 )
{
- zone->Outline()->DeleteCorner( zone->GetNumCorners() - 1 );
+ zone->Outline()->RemoveVertex( zone->GetNumCorners() - 1 );
if( m_canvas->IsMouseCaptured() )
m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
@@ -254,11 +254,10 @@ void PCB_EDIT_FRAME::Start_Move_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone,
// Prepare copy of old zones, for undo/redo.
// if the corner is new, remove it from list, save and insert it in list
- int cx = aZone->Outline()->GetX( corner_id );
- int cy = aZone->Outline()->GetY( corner_id );
+ VECTOR2I corner = aZone->Outline()->Vertex( corner_id );
if ( IsNewCorner )
- aZone->Outline()->DeleteCorner( corner_id );
+ aZone->Outline()->RemoveVertex( corner_id );
s_AuxiliaryList.ClearListAndDeleteItems();
s_PickedList.ClearListAndDeleteItems();
@@ -266,12 +265,12 @@ void PCB_EDIT_FRAME::Start_Move_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone,
SaveCopyOfZones( s_PickedList, GetBoard(), aZone->GetNetCode(), aZone->GetLayer() );
if ( IsNewCorner )
- aZone->Outline()->InsertCorner(corner_id-1, cx, cy );
+ aZone->Outline()->InsertVertex(corner_id-1, corner );
aZone->SetFlags( IN_EDIT );
m_canvas->SetMouseCapture( Show_Zone_Corner_Or_Outline_While_Move_Mouse,
Abort_Zone_Move_Corner_Or_Outlines );
- s_CornerInitialPosition = aZone->GetCornerPosition( corner_id );
+ s_CornerInitialPosition = static_cast<wxPoint>( aZone->GetCornerPosition( corner_id ) );
s_CornerIsNew = IsNewCorner;
s_AddCutoutToCurrentZone = false;
s_CurrentZone = NULL;
@@ -368,7 +367,7 @@ void PCB_EDIT_FRAME::Remove_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone )
{
OnModify();
- if( aZone->Outline()->GetCornersCount() <= 3 )
+ if( aZone->Outline()->TotalVertices() <= 3 )
{
m_canvas->RefreshDrawingRect( aZone->GetBoundingBox() );
@@ -393,7 +392,7 @@ void PCB_EDIT_FRAME::Remove_Zone_Corner( wxDC* DC, ZONE_CONTAINER* aZone )
s_AuxiliaryList.ClearListAndDeleteItems();
s_PickedList. ClearListAndDeleteItems();
SaveCopyOfZones( s_PickedList, GetBoard(), aZone->GetNetCode(), aZone->GetLayer() );
- aZone->Outline()->DeleteCorner( aZone->GetSelectedCorner() );
+ aZone->Outline()->RemoveVertex( aZone->GetSelectedCorner() );
// modify zones outlines according to the new aZone shape
GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, aZone );
@@ -447,12 +446,12 @@ void Abort_Zone_Move_Corner_Or_Outlines( EDA_DRAW_PANEL* Panel, wxDC* DC )
{
if( s_CornerIsNew )
{
- zone->Outline()->DeleteCorner( zone->GetSelectedCorner() );
+ zone->Outline()->RemoveVertex( zone->GetSelectedCorner() );
}
else
{
wxPoint pos = s_CornerInitialPosition;
- zone->Outline()->MoveCorner( zone->GetSelectedCorner(), pos.x, pos.y );
+ zone->Outline()->Vertex( zone->GetSelectedCorner() ) = pos;
}
}
@@ -498,7 +497,7 @@ void Show_Zone_Corner_Or_Outline_While_Move_Mouse( EDA_DRAW_PANEL* aPanel, wxDC*
}
else
{
- zone->Outline()->MoveCorner( zone->GetSelectedCorner(), pos.x, pos.y );
+ zone->Outline()->Vertex( zone->GetSelectedCorner() ) = pos;
}
zone->Draw( aPanel, aDC, GR_XOR );
@@ -665,12 +664,11 @@ int PCB_EDIT_FRAME::Begin_Zone( wxDC* DC )
{
zoneInfo.ExportSetting( *zone );
- zone->Outline()->Start( zoneInfo.m_CurrentZone_Layer,
- GetCrossHairPosition().x,
- GetCrossHairPosition().y,
- zone->GetHatchStyle() );
+ zone->SetLayer( zoneInfo.m_CurrentZone_Layer );
+ // A duplicated corner is needed; null segments are removed when the zone is finished.
zone->AppendCorner( GetCrossHairPosition() );
+ zone->AppendCorner( GetCrossHairPosition(), true );
if( g_Drc_On && (m_drc->Drc( zone, 0 ) == BAD_DRC) && zone->IsOnCopperLayer() )
{
@@ -702,8 +700,14 @@ int PCB_EDIT_FRAME::Begin_Zone( wxDC* DC )
// Ok, we can add a new corner
if( m_canvas->IsMouseCaptured() )
m_canvas->CallMouseCapture( DC, wxPoint(0,0), false );
- zone->AppendCorner( GetCrossHairPosition() );
+
+ // It is necessary to allow duplication of the points, as we have to handle the
+ // continuous drawing while creating the zone at the same time as we build it. Null
+ // segments are removed when the zone is finished, in End_Zone.
+ zone->AppendCorner( GetCrossHairPosition(), true );
+
SetCurItem( zone ); // calls DisplayInfo().
+
if( m_canvas->IsMouseCaptured() )
m_canvas->CallMouseCapture( DC, wxPoint(0,0), false );
}
@@ -766,7 +770,6 @@ bool PCB_EDIT_FRAME::End_Zone( wxDC* DC )
// Put new zone in list
if( !s_CurrentZone )
{
- zone->Outline()->CloseLastContour(); // Close the current corner list
GetBoard()->Add( zone );
// Add this zone in picked list, as new item
@@ -775,12 +778,8 @@ bool PCB_EDIT_FRAME::End_Zone( wxDC* DC )
}
else // Append this outline as a cutout to an existing zone
{
- for( int ii = 0; ii < zone->GetNumCorners(); ii++ )
- {
- s_CurrentZone->AppendCorner( zone->GetCornerPosition( ii ) );
- }
+ s_CurrentZone->Outline()->AddHole( zone->Outline()->Outline( 0 ) );
- s_CurrentZone->Outline()->CloseLastContour(); // Close the current corner list
zone->RemoveAllContours(); // All corners are copied in s_CurrentZone. Free corner list.
zone = s_CurrentZone;
}
@@ -845,7 +844,7 @@ static void Show_New_Edge_While_Move_Mouse( EDA_DRAW_PANEL* aPanel, wxDC* aDC,
if( pcbframe->GetZoneSettings().m_Zone_45_Only )
{
// calculate the new position as allowed
- wxPoint StartPoint = zone->GetCornerPosition( icorner - 1 );
+ wxPoint StartPoint = static_cast<wxPoint>( zone->GetCornerPosition( icorner - 1 ) );
CalculateSegmentEndPoint( c_pos, StartPoint.x, StartPoint.y, &c_pos.x, &c_pos.y );
}
@@ -939,7 +938,11 @@ void PCB_EDIT_FRAME::Edit_Zone_Params( wxDC* DC, ZONE_CONTAINER* aZone )
void PCB_EDIT_FRAME::Delete_Zone_Contour( wxDC* DC, ZONE_CONTAINER* aZone )
{
- int ncont = aZone->Outline()->GetContour( aZone->GetSelectedCorner() );
+ // Get contour in which the selected corner is
+ SHAPE_POLY_SET::VERTEX_INDEX indices;
+
+ // If the selected corner does not exist, abort
+ assert( aZone->Outline()->GetRelativeIndices( aZone->GetSelectedCorner(), &indices ) );
EDA_RECT dirty = aZone->GetBoundingBox();
@@ -949,7 +952,7 @@ void PCB_EDIT_FRAME::Delete_Zone_Contour( wxDC* DC, ZONE_CONTAINER* aZone )
// Remove current filling:
aZone->UnFill();
- if( ncont == 0 ) // This is the main outline: remove all
+ if( indices.m_contour == 0 ) // This is the main outline: remove all
{
SaveCopyInUndoList( aZone, UR_DELETED );
GetBoard()->Remove( aZone );
@@ -958,7 +961,7 @@ void PCB_EDIT_FRAME::Delete_Zone_Contour( wxDC* DC, ZONE_CONTAINER* aZone )
else
{
SaveCopyInUndoList( aZone, UR_CHANGED );
- aZone->Outline()->RemoveContour( ncont );
+ aZone->Outline()->RemoveContour( indices.m_contour, indices.m_polygon );
}
m_canvas->RefreshDrawingRect( dirty );
diff --git a/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp b/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp
index f975c6a55..4ae50562e 100644
--- a/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp
+++ b/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp
@@ -435,7 +435,7 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb )
if(g_DumpZonesWhenFilling)
dumper->BeginGroup("clipper-zone");
- SHAPE_POLY_SET solidAreas = ConvertPolyListToPolySet( m_smoothedPoly->m_CornersList );
+ SHAPE_POLY_SET solidAreas = *m_smoothedPoly;
solidAreas.Inflate( -outline_half_thickness, segsPerCircle );
solidAreas.Simplify( POLY_CALC_MODE );
diff --git a/pcbnew/zones_functions_for_undo_redo.cpp b/pcbnew/zones_functions_for_undo_redo.cpp
index 35d139245..4d4f9d1af 100644
--- a/pcbnew/zones_functions_for_undo_redo.cpp
+++ b/pcbnew/zones_functions_for_undo_redo.cpp
@@ -115,8 +115,7 @@ bool ZONE_CONTAINER::IsSame( const ZONE_CONTAINER& aZoneToCompare )
wxASSERT( m_Poly ); // m_Poly == NULL Should never happen
wxASSERT( aZoneToCompare.Outline() );
- if( Outline()->m_CornersList.GetList() !=
- aZoneToCompare.Outline()->m_CornersList.GetList() ) // Compare vector
+ if( Outline() != aZoneToCompare.Outline() ) // Compare vector
return false;
return true;
diff --git a/pcbnew/zones_test_and_combine_areas.cpp b/pcbnew/zones_test_and_combine_areas.cpp
index 0fe0848b1..b73867c1e 100644
--- a/pcbnew/zones_test_and_combine_areas.cpp
+++ b/pcbnew/zones_test_and_combine_areas.cpp
@@ -102,7 +102,7 @@ bool BOARD::CombineAllAreasInNet( PICKED_ITEMS_LIST* aDeletedList, int aNetCode,
continue;
// legal polygon
- EDA_RECT b1 = curr_area->Outline()->GetBoundingBox();
+ BOX2I b1 = curr_area->Outline()->BBox();
bool mod_ia1 = false;
for( unsigned ia2 = m_ZoneDescriptorList.size() - 1; ia2 > ia1; ia2-- )
@@ -121,7 +121,7 @@ bool BOARD::CombineAllAreasInNet( PICKED_ITEMS_LIST* aDeletedList, int aNetCode,
if( curr_area->GetLayer() != area2->GetLayer() )
continue;
- EDA_RECT b2 = area2->Outline()->GetBoundingBox();
+ BOX2I b2 = area2->Outline()->BBox();
if( b1.Intersects( b2 ) )
{
@@ -189,92 +189,48 @@ bool BOARD::TestAreaIntersection( ZONE_CONTAINER* area_ref, ZONE_CONTAINER* area
if( area_ref->GetLayer() != area_to_test->GetLayer() )
return false;
- CPolyLine* poly1 = area_ref->Outline();
- CPolyLine* poly2 = area_to_test->Outline();
+ SHAPE_POLY_SET* poly1 = area_ref->Outline();
+ SHAPE_POLY_SET* poly2 = area_to_test->Outline();
// test bounding rects
- EDA_RECT b1 = poly1->GetBoundingBox();
- EDA_RECT b2 = poly2->GetBoundingBox();
+ BOX2I b1 = poly1->BBox();
+ BOX2I b2 = poly2->BBox();
if( ! b1.Intersects( b2 ) )
return false;
// now test for intersecting segments
- for( int icont1 = 0; icont1<poly1->GetContoursCount(); icont1++ )
+ SHAPE_POLY_SET::SEGMENT_ITERATOR segIterator1, segIterator2;
+
+ // Iterate through all the vertices
+ for( segIterator1 = poly1->IterateSegmentsWithHoles(); segIterator1; segIterator1++ )
{
- int is1 = poly1->GetContourStart( icont1 );
- int ie1 = poly1->GetContourEnd( icont1 );
+ // Build segment
+ SEG firstSegment = *segIterator1;
- for( int ic1 = is1; ic1<=ie1; ic1++ )
+ for( segIterator2 = poly2->IterateSegmentsWithHoles(); segIterator2; segIterator2++ )
{
- int xi1 = poly1->GetX( ic1 );
- int yi1 = poly1->GetY( ic1 );
- int xf1, yf1;
-
- if( ic1 < ie1 )
- {
- xf1 = poly1->GetX( ic1 + 1 );
- yf1 = poly1->GetY( ic1 + 1 );
- }
- else
- {
- xf1 = poly1->GetX( is1 );
- yf1 = poly1->GetY( is1 );
- }
-
- for( int icont2 = 0; icont2<poly2->GetContoursCount(); icont2++ )
- {
- int is2 = poly2->GetContourStart( icont2 );
- int ie2 = poly2->GetContourEnd( icont2 );
-
- for( int ic2 = is2; ic2<=ie2; ic2++ )
- {
- int xi2 = poly2->GetX( ic2 );
- int yi2 = poly2->GetY( ic2 );
- int xf2, yf2;
+ // Build second segment
+ SEG secondSegment = *segIterator2;
- if( ic2 < ie2 )
- {
- xf2 = poly2->GetX( ic2 + 1 );
- yf2 = poly2->GetY( ic2 + 1 );
- }
- else
- {
- xf2 = poly2->GetX( is2 );
- yf2 = poly2->GetY( is2 );
- }
-
- bool intersect = FindSegmentIntersections( xi1, yi1, xf1, yf1,
- xi2, yi2, xf2, yf2 );
- if( intersect )
- return true;
- }
- }
+ // Check whether the two segments built collide
+ if( firstSegment.Collide( secondSegment, 0 ) )
+ return true;
}
}
- // If a contour is inside an other contour, no segments intersects, but the zones
- // can be combined if a corner is inside an outline (only one corner is enought)
- for( int ic2 = 0; ic2 < poly2->GetCornersCount(); ic2++ )
+ // If a contour is inside another contour, no segments intersects, but the zones
+ // can be combined if a corner is inside an outline (only one corner is enough)
+ for( SHAPE_POLY_SET::ITERATOR iter = poly2->IterateWithHoles(); iter; iter++ )
{
- int x = poly2->GetX( ic2 );
- int y = poly2->GetY( ic2 );
-
- if( poly1->TestPointInside( x, y ) )
- {
+ if( poly1->Contains( *iter ) )
return true;
- }
}
- for( int ic1 = 0; ic1 < poly1->GetCornersCount(); ic1++ )
+ for( SHAPE_POLY_SET::ITERATOR iter = poly1->IterateWithHoles(); iter; iter++ )
{
- int x = poly1->GetX( ic1 );
- int y = poly1->GetY( ic1 );
-
- if( poly2->TestPointInside( x, y ) )
- {
+ if( poly2->Contains( *iter ) )
return true;
- }
}
return false;
@@ -290,8 +246,8 @@ bool BOARD::CombineAreas( PICKED_ITEMS_LIST* aDeletedList, ZONE_CONTAINER* area_
return false;
}
- SHAPE_POLY_SET mergedOutlines = ConvertPolyListToPolySet( area_ref->Outline()->m_CornersList );
- SHAPE_POLY_SET areaToMergePoly = ConvertPolyListToPolySet( area_to_combine->Outline()->m_CornersList );
+ SHAPE_POLY_SET mergedOutlines = *area_ref->Outline();
+ SHAPE_POLY_SET areaToMergePoly = *area_to_combine->Outline();
mergedOutlines.BooleanAdd( areaToMergePoly, SHAPE_POLY_SET::PM_FAST );
mergedOutlines.Simplify( SHAPE_POLY_SET::PM_FAST );
@@ -309,12 +265,14 @@ bool BOARD::CombineAreas( PICKED_ITEMS_LIST* aDeletedList, ZONE_CONTAINER* area_
if( mergedOutlines.OutlineCount() > 1 )
return false;
- area_ref->Outline()->m_CornersList = ConvertPolySetToPolyList( mergedOutlines );
+ // Update the area with the new merged outline
+ delete area_ref->Outline();
+ area_ref->SetOutline( new SHAPE_POLY_SET( mergedOutlines ) );
RemoveArea( aDeletedList, area_to_combine );
area_ref->SetLocalFlags( 1 );
- area_ref->Outline()->Hatch();
+ area_ref->Hatch();
return true;
}
@@ -329,7 +287,7 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E
for( int ia = 0; ia < GetAreaCount(); ia++ )
{
ZONE_CONTAINER* Area_Ref = GetArea( ia );
- CPolyLine* refSmoothedPoly = Area_Ref->GetSmoothedPoly();
+ SHAPE_POLY_SET* refSmoothedPoly = Area_Ref->GetSmoothedPoly();
if( !Area_Ref->IsOnCopperLayer() )
continue;
@@ -341,7 +299,7 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E
for( int ia2 = 0; ia2 < GetAreaCount(); ia2++ )
{
ZONE_CONTAINER* area_to_test = GetArea( ia2 );
- CPolyLine* testSmoothedPoly = area_to_test->GetSmoothedPoly();
+ SHAPE_POLY_SET* testSmoothedPoly = area_to_test->GetSmoothedPoly();
if( Area_Ref == area_to_test )
continue;
@@ -374,17 +332,22 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E
if( Area_Ref->GetIsKeepout() )
zone2zoneClearance = 1;
+ // Object to iterate through the polygon vertices
+ SHAPE_POLY_SET::ITERATOR iterator;
+
// test for some corners of Area_Ref inside area_to_test
- for( int ic = 0; ic < refSmoothedPoly->GetCornersCount(); ic++ )
+ for( iterator = refSmoothedPoly->IterateWithHoles(); iterator; iterator++ )
{
- int x = refSmoothedPoly->GetX( ic );
- int y = refSmoothedPoly->GetY( ic );
+ VECTOR2I currentVertex = *iterator;
- if( testSmoothedPoly->TestPointInside( x, y ) )
+ if( testSmoothedPoly->Contains( currentVertex ) )
{
// COPPERAREA_COPPERAREA error: copper area ref corner inside copper area
if( aCreate_Markers )
{
+ int x = currentVertex.x;
+ int y = currentVertex.y;
+
wxString msg1 = Area_Ref->GetSelectMenuText();
wxString msg2 = area_to_test->GetSelectMenuText();
MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_INSIDE_COPPERAREA,
@@ -399,16 +362,18 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E
}
// test for some corners of area_to_test inside Area_Ref
- for( int ic2 = 0; ic2 < testSmoothedPoly->GetCornersCount(); ic2++ )
+ for( iterator = testSmoothedPoly->IterateWithHoles(); iterator; iterator++ )
{
- int x = testSmoothedPoly->GetX( ic2 );
- int y = testSmoothedPoly->GetY( ic2 );
+ VECTOR2I currentVertex = *iterator;
- if( refSmoothedPoly->TestPointInside( x, y ) )
+ if( refSmoothedPoly->Contains( currentVertex ) )
{
// COPPERAREA_COPPERAREA error: copper area corner inside copper area ref
if( aCreate_Markers )
{
+ int x = currentVertex.x;
+ int y = currentVertex.y;
+
wxString msg1 = area_to_test->GetSelectMenuText();
wxString msg2 = Area_Ref->GetSelectMenuText();
MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_INSIDE_COPPERAREA,
@@ -422,77 +387,58 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E
}
}
- // now test spacing between areas
- for( int icont = 0; icont < refSmoothedPoly->GetContoursCount(); icont++ )
- {
- int ic_start = refSmoothedPoly->GetContourStart( icont );
- int ic_end = refSmoothedPoly->GetContourEnd( icont );
- for( int ic = ic_start; ic<=ic_end; ic++ )
- {
- int ax1 = refSmoothedPoly->GetX( ic );
- int ay1 = refSmoothedPoly->GetY( ic );
- int ax2, ay2;
+ // Define an iterator to visit all edges in the polygons.
+ SHAPE_POLY_SET::SEGMENT_ITERATOR testIt, refIt;
- if( ic == ic_end )
- {
- ax2 = refSmoothedPoly->GetX( ic_start );
- ay2 = refSmoothedPoly->GetY( ic_start );
- }
- else
- {
- ax2 = refSmoothedPoly->GetX( ic + 1 );
- ay2 = refSmoothedPoly->GetY( ic + 1 );
- }
+ // Iterate through all the segments of refSmoothedPoly
+ for( refIt = refSmoothedPoly->IterateSegmentsWithHoles(); refIt; refIt++ )
+ {
+ // Build ref segment
+ SEG refSegment = *refIt;
- for( int icont2 = 0; icont2 < testSmoothedPoly->GetContoursCount(); icont2++ )
+ // Iterate through all the segments in testSmoothedPoly
+ for( testIt = testSmoothedPoly->IterateSegmentsWithHoles(); testIt; testIt++ )
+ {
+ // Build test segment
+ SEG testSegment = *testIt;
+
+ int x, y;
+
+ int ax1, ay1, ax2, ay2;
+ ax1 = refSegment.A.x;
+ ay1 = refSegment.A.y;
+ ax2 = refSegment.B.x;
+ ay2 = refSegment.B.y;
+
+ int bx1, by1, bx2, by2;
+ bx1 = testSegment.A.x;
+ by1 = testSegment.A.y;
+ bx2 = testSegment.B.x;
+ by2 = testSegment.B.y;
+
+ int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2,
+ 0,
+ ax1, ay1, ax2, ay2,
+ 0,
+ zone2zoneClearance,
+ &x, &y );
+
+ if( d < zone2zoneClearance )
{
- int ic_start2 = testSmoothedPoly->GetContourStart( icont2 );
- int ic_end2 = testSmoothedPoly->GetContourEnd( icont2 );
-
- for( int ic2 = ic_start2; ic2<=ic_end2; ic2++ )
+ // COPPERAREA_COPPERAREA error : intersect or too close
+ if( aCreate_Markers )
{
- int bx1 = testSmoothedPoly->GetX( ic2 );
- int by1 = testSmoothedPoly->GetY( ic2 );
- int bx2, by2;
-
- if( ic2 == ic_end2 )
- {
- bx2 = testSmoothedPoly->GetX( ic_start2 );
- by2 = testSmoothedPoly->GetY( ic_start2 );
- }
- else
- {
- bx2 = testSmoothedPoly->GetX( ic2 + 1 );
- by2 = testSmoothedPoly->GetY( ic2 + 1 );
- }
-
- int x, y;
-
- int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2,
- 0,
- ax1, ay1, ax2, ay2,
- 0,
- zone2zoneClearance,
- &x, &y );
-
- if( d < zone2zoneClearance )
- {
- // COPPERAREA_COPPERAREA error : intersect or too close
- if( aCreate_Markers )
- {
- wxString msg1 = Area_Ref->GetSelectMenuText();
- wxString msg2 = area_to_test->GetSelectMenuText();
- MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_CLOSE_TO_COPPERAREA,
- wxPoint( x, y ),
- msg1, wxPoint( x, y ),
- msg2, wxPoint( x, y ) );
- Add( marker );
- }
-
- nerrors++;
- }
+ wxString msg1 = Area_Ref->GetSelectMenuText();
+ wxString msg2 = area_to_test->GetSelectMenuText();
+ MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_CLOSE_TO_COPPERAREA,
+ wxPoint( x, y ),
+ msg1, wxPoint( x, y ),
+ msg2, wxPoint( x, y ) );
+ Add( marker );
}
+
+ nerrors++;
}
}
}
@@ -507,31 +453,22 @@ bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
{
if( !aArea->IsOnCopperLayer() ) // Cannot have a Drc error if not on copper layer
return true;
+ // Get polygon, contour and vertex index.
+ SHAPE_POLY_SET::VERTEX_INDEX index;
- wxPoint start = aArea->GetCornerPosition( aCornerIndex );
- wxPoint end;
+ // If the vertex does not exist, there is no conflict
+ if( !aArea->Outline()->GetRelativeIndices( aCornerIndex, &index ) )
+ return true;
- // Search the end point of the edge starting at aCornerIndex
- if( aArea->Outline()->m_CornersList[aCornerIndex].end_contour == false
- && aCornerIndex < (aArea->GetNumCorners() - 1) )
- {
- end = aArea->GetCornerPosition( aCornerIndex + 1 );
- }
- else // aCornerIndex is the last corner of an outline.
- // the corresponding end point of the segment is the first corner of the outline
- {
- int ii = aCornerIndex - 1;
- end = aArea->GetCornerPosition( ii );
+ // Retrieve the selected contour
+ SHAPE_LINE_CHAIN contour;
+ contour = aArea->Outline()->Polygon( index.m_polygon )[index.m_contour];
- while( ii >= 0 )
- {
- if( aArea->Outline()->m_CornersList[ii].end_contour )
- break;
+ // Retrieve the segment that starts at aCornerIndex-th corner.
+ SEG selectedSegment = contour.Segment( index.m_vertex );
- end = aArea->GetCornerPosition( ii );
- ii--;
- }
- }
+ VECTOR2I start = selectedSegment.A;
+ VECTOR2I end = selectedSegment.B;
// iterate through all areas
for( int ia2 = 0; ia2 < m_pcb->GetAreaCount(); ia2++ )
@@ -562,10 +499,10 @@ bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
zone_clearance = 1;
// test for ending line inside area_to_test
- if( area_to_test->Outline()->TestPointInside( end.x, end.y ) )
+ if( area_to_test->Outline()->Contains( end ) )
{
// COPPERAREA_COPPERAREA error: corner inside copper area
- m_currentMarker = fillMarker( aArea, end,
+ m_currentMarker = fillMarker( aArea, static_cast<wxPoint>( end ),
COPPERAREA_INSIDE_COPPERAREA,
m_currentMarker );
return false;
@@ -577,45 +514,34 @@ bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
int ax2 = end.x;
int ay2 = end.y;
- for( int icont2 = 0; icont2 < area_to_test->Outline()->GetContoursCount(); icont2++ )
+ // Iterate through all edges in the polygon.
+ SHAPE_POLY_SET::SEGMENT_ITERATOR iterator;
+ for( iterator = area_to_test->Outline()->IterateSegmentsWithHoles(); iterator; iterator++ )
{
- int ic_start2 = area_to_test->Outline()->GetContourStart( icont2 );
- int ic_end2 = area_to_test->Outline()->GetContourEnd( icont2 );
-
- for( int ic2 = ic_start2; ic2<=ic_end2; ic2++ )
+ SEG segment = *iterator;
+
+ int bx1 = segment.A.x;
+ int by1 = segment.A.y;
+ int bx2 = segment.B.x;
+ int by2 = segment.B.y;
+
+ int x, y; // variables containing the intersecting point coordinates
+ int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2,
+ 0,
+ ax1, ay1, ax2, ay2,
+ 0,
+ zone_clearance,
+ &x, &y );
+
+ if( d < zone_clearance )
{
- int bx1 = area_to_test->Outline()->GetX( ic2 );
- int by1 = area_to_test->Outline()->GetY( ic2 );
- int bx2, by2;
-
- if( ic2 == ic_end2 )
- {
- bx2 = area_to_test->Outline()->GetX( ic_start2 );
- by2 = area_to_test->Outline()->GetY( ic_start2 );
- }
- else
- {
- bx2 = area_to_test->Outline()->GetX( ic2 + 1 );
- by2 = area_to_test->Outline()->GetY( ic2 + 1 );
- }
-
- int x, y; // variables containing the intersecting point coordinates
- int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2,
- 0,
- ax1, ay1, ax2, ay2,
- 0,
- zone_clearance,
- &x, &y );
-
- if( d < zone_clearance )
- {
- // COPPERAREA_COPPERAREA error : edge intersect or too close
- m_currentMarker = fillMarker( aArea, wxPoint( x, y ),
- COPPERAREA_CLOSE_TO_COPPERAREA,
- m_currentMarker );
- return false;
- }
+ // COPPERAREA_COPPERAREA error : edge intersect or too close
+ m_currentMarker = fillMarker( aArea, wxPoint( x, y ),
+ COPPERAREA_CLOSE_TO_COPPERAREA,
+ m_currentMarker );
+ return false;
}
+
}
}
diff --git a/polygon/PolyLine.cpp b/polygon/PolyLine.cpp
index 304fba5bf..50946ac18 100644
--- a/polygon/PolyLine.cpp
+++ b/polygon/PolyLine.cpp
@@ -354,7 +354,10 @@ CPolyLine* CPolyLine::Chamfer( unsigned int aDistance )
int nx2 = KiROUND( distance * xb / lenb );
int ny2 = KiROUND( distance * yb / lenb );
- newPoly->AppendCorner( x1 + nx2, y1 + ny2 );
+
+ // Due to rounding errors, repeated corners could be added; this check prevents it
+ if(nx1 != nx2 || ny1 != ny2)
+ newPoly->AppendCorner( x1 + nx2, y1 + ny2 );
}
newPoly->CloseLastContour();
@@ -423,7 +426,7 @@ CPolyLine* CPolyLine::Fillet( unsigned int aRadius, unsigned int aSegments )
double denom = sqrt( 2.0 / ( 1 + cosine ) - 1 );
// Do nothing in case of parallel edges
- if( !std::isfinite( denom ) )
+ if( std::isinf( denom ) )
continue;
// Limit rounding distance to one half of an edge
@@ -480,11 +483,22 @@ CPolyLine* CPolyLine::Fillet( unsigned int aRadius, unsigned int aSegments )
else
newPoly->AppendCorner( KiROUND( nx ), KiROUND( ny ) );
+ // Store the previous added corner to make a sanity check
+ int prevX = KiROUND(nx);
+ int prevY = KiROUND(ny);
+
for( unsigned int j = 0; j < segments; j++ )
{
nx = xc + cos( startAngle + (j + 1) * deltaAngle ) * radius;
ny = yc - sin( startAngle + (j + 1) * deltaAngle ) * radius;
- newPoly->AppendCorner( KiROUND( nx ), KiROUND( ny ) );
+
+ // Due to rounding errors, repeated corners could be added; this check prevents it
+ if(KiROUND(nx) != prevX || KiROUND(ny) != prevY)
+ {
+ newPoly->AppendCorner( KiROUND( nx ), KiROUND( ny ) );
+ prevX = KiROUND(nx);
+ prevY = KiROUND(ny);
+ }
}
}
@@ -1154,7 +1168,7 @@ int CPolyLine::HitTestForEdge( const wxPoint& aPos, int aDistMax ) const
if( m_CornersList.IsEndContour ( item_pos ) || end_segm >= lim )
{
unsigned tmp = first_corner_pos;
- first_corner_pos = end_segm; // first_corner_pos is now the beginning of the next outline
+ first_corner_pos = end_segm; // first_corner_pos is the beginning of next outline
end_segm = tmp; // end_segm is the beginning of the current outline
}
--------------2.11.1--
From e794dbb1c6732899a983c81c2d61c9b69523cfa2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Garc=C3=ADa=20Montoro?=
<alejandro.garciamontoro@xxxxxxxxx>
Date: Thu, 16 Feb 2017 18:12:08 +0100
Subject: [PATCH 2/2] Tests CPolyLine -> SHAPE_POLY_SET refactor.
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------2.11.1"
This is a multi-part message in MIME format.
--------------2.11.1
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit
Adds BOOST tests to the qa folder included in the geometry subdirectory.
The tests check whether the refactored code is consistent with the
legacy code. They also check some of the new code, as the family of
ITERATOR classes.
---
qa/CMakeLists.txt | 2 +
qa/data/fixtures_geometry.h | 298 ++++++++++++++++++++++++++++++++++++
qa/geometry/CMakeLists.txt | 53 +++++++
qa/geometry/test_chamfer_fillet.cpp | 159 +++++++++++++++++++
qa/geometry/test_collision.cpp | 150 ++++++++++++++++++
qa/geometry/test_iterator.cpp | 133 ++++++++++++++++
qa/geometry/test_module.cpp | 32 ++++
qa/geometry/test_segment.cpp | 59 +++++++
8 files changed, 886 insertions(+)
create mode 100644 qa/data/fixtures_geometry.h
create mode 100644 qa/geometry/CMakeLists.txt
create mode 100644 qa/geometry/test_chamfer_fillet.cpp
create mode 100644 qa/geometry/test_collision.cpp
create mode 100644 qa/geometry/test_iterator.cpp
create mode 100644 qa/geometry/test_module.cpp
create mode 100644 qa/geometry/test_segment.cpp
--------------2.11.1
Content-Type: text/x-patch; name="0002-Tests-CPolyLine-SHAPE_POLY_SET-refactor.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0002-Tests-CPolyLine-SHAPE_POLY_SET-refactor.patch"
diff --git a/qa/CMakeLists.txt b/qa/CMakeLists.txt
index bd579a597..330a2c81f 100644
--- a/qa/CMakeLists.txt
+++ b/qa/CMakeLists.txt
@@ -9,3 +9,5 @@ if( KICAD_SCRIPTING_MODULES )
)
endif()
+
+add_subdirectory( geometry )
diff --git a/qa/data/fixtures_geometry.h b/qa/data/fixtures_geometry.h
new file mode 100644
index 000000000..e025d461f
--- /dev/null
+++ b/qa/data/fixtures_geometry.h
@@ -0,0 +1,298 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 CERN
+ * @author Alejandro García Montoro <alejandro.garciamontoro@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef __FIXTURES_H
+#define __FIXTURES_H
+
+#include <geometry/shape_poly_set.h>
+#include <geometry/shape_line_chain.h>
+#include <polygon/PolyLine.h>
+
+/**
+ * Common data for the tests:
+ * 1. holeyPolySet: A polyset containing one single squared outline with two holes: a
+ * non-convex pentagon and a triangle.
+ * 2.solidPolySet: A polyset with three empty outlines and no holes.
+ * 3. uniqueVertexPolySet: A polyset with one single outline that contains just one vertex.
+ * 4. emptyPolySet: A polyset with no outlines.
+ */
+struct CommonTestData
+{
+ // Polygon sets common for all the tests
+ SHAPE_POLY_SET emptyPolySet;
+ SHAPE_POLY_SET uniqueVertexPolySet;
+ SHAPE_POLY_SET solidPolySet;
+ SHAPE_POLY_SET holeyPolySet;
+
+ // Vectors containing the information with which the polygons are populated.
+ std::vector<VECTOR2I> uniquePoints;
+ std::vector<VECTOR2I> holeyPoints;
+ std::vector<SEG> holeySegments;
+
+ /**
+ * Constructor.
+ */
+ CommonTestData()
+ {
+ // UniqueVertexPolySet shall have a unique vertex
+ uniquePoints.push_back( VECTOR2I( 100, 50 ) );
+
+ // Populate the holey polygon set points with 12 points
+
+ // Square
+ holeyPoints.push_back( VECTOR2I( 100,100 ) );
+ holeyPoints.push_back( VECTOR2I( 0,100 ) );
+ holeyPoints.push_back( VECTOR2I( 0,0 ) );
+ holeyPoints.push_back( VECTOR2I( 100,0 ) );
+
+ // Pentagon
+ holeyPoints.push_back( VECTOR2I( 10,10 ) );
+ holeyPoints.push_back( VECTOR2I( 10,20 ) );
+ holeyPoints.push_back( VECTOR2I( 15,15 ) );
+ holeyPoints.push_back( VECTOR2I( 20,20 ) );
+ holeyPoints.push_back( VECTOR2I( 20,10 ) );
+
+ // Triangle
+ holeyPoints.push_back( VECTOR2I( 40,10 ) );
+ holeyPoints.push_back( VECTOR2I( 40,20 ) );
+ holeyPoints.push_back( VECTOR2I( 60,10 ) );
+
+ // Save the segments of the holeyPolySet.
+ holeySegments.push_back( SEG( holeyPoints[0], holeyPoints[1] ) );
+ holeySegments.push_back( SEG( holeyPoints[1], holeyPoints[2] ) );
+ holeySegments.push_back( SEG( holeyPoints[2], holeyPoints[3] ) );
+ holeySegments.push_back( SEG( holeyPoints[3], holeyPoints[0] ) );
+
+ // Pentagon segments
+ holeySegments.push_back( SEG( holeyPoints[4], holeyPoints[5] ) );
+ holeySegments.push_back( SEG( holeyPoints[5], holeyPoints[6] ) );
+ holeySegments.push_back( SEG( holeyPoints[6], holeyPoints[7] ) );
+ holeySegments.push_back( SEG( holeyPoints[7], holeyPoints[8] ) );
+ holeySegments.push_back( SEG( holeyPoints[8], holeyPoints[4] ) );
+
+ // Triangle segments
+ holeySegments.push_back( SEG( holeyPoints[ 9], holeyPoints[10] ) );
+ holeySegments.push_back( SEG( holeyPoints[10], holeyPoints[11] ) );
+ holeySegments.push_back( SEG( holeyPoints[11], holeyPoints[9] ) );
+
+ // Auxiliary variables to store the contours that will be added to the polygons
+ SHAPE_LINE_CHAIN polyLine, hole;
+
+ // Create a polygon set with a unique vertex
+ polyLine.Append( uniquePoints[0] );
+ polyLine.SetClosed( true );
+ uniqueVertexPolySet.AddOutline(polyLine);
+
+ // Create a polygon set without holes
+ solidPolySet.NewOutline();
+ solidPolySet.NewOutline();
+ solidPolySet.NewOutline();
+
+ // Create a polygon set with holes
+
+ // Adds a new squared outline
+ polyLine.Clear();
+
+ for( int i = 0; i < 4; i++ )
+ polyLine.Append( holeyPoints[i] );
+
+ polyLine.SetClosed( true );
+
+ holeyPolySet.AddOutline(polyLine);
+
+ // Adds a new hole (a pentagon)
+ for( int i = 4; i < 9; i++ )
+ hole.Append( holeyPoints[i] );
+
+ hole.SetClosed( true );
+ holeyPolySet.AddHole( hole );
+
+
+ // Adds a new hole (a triangle)
+ hole.Clear();
+ for( int i = 9; i < 12; i++ )
+ hole.Append( holeyPoints[i] );
+
+ hole.SetClosed( true );
+ holeyPolySet.AddHole( hole );
+ }
+
+ ~CommonTestData(){}
+};
+
+/**
+ * Fixture for the ChamferFillet test suite. It contains an instance of the common data and the
+ * holeyPolySet replicated as a CPolyLine, in order to test behaviour of old and new Chamfer and
+ * Fillet methods.
+ */
+struct ChamferFilletFixture {
+ // Structure to store the common data.
+ struct CommonTestData common;
+
+ // CPolyLine representing the same polygon in polySet.
+ CPolyLine legacyPolyLine;
+
+ /**
+ * Constructor.
+ */
+ ChamferFilletFixture()
+ {
+ // Replicate the vertices in the polySet outline
+ legacyPolyLine.Start( 0, common.holeyPoints[0].x, common.holeyPoints[0].y,
+ CPolyLine::NO_HATCH );
+
+ for( int i = 1; i < 4; i++ )
+ {
+ VECTOR2I point = common.holeyPoints[i];
+ legacyPolyLine.AppendCorner( point.x, point.y );
+ }
+
+ legacyPolyLine.CloseLastContour();
+
+ // Add the non-convex pentagon hole
+ legacyPolyLine.Start( 0, common.holeyPoints[4].x, common.holeyPoints[4].y,
+ CPolyLine::NO_HATCH );
+
+ for( int i = 5; i < 9; i++ )
+ {
+ VECTOR2I point = common.holeyPoints[i];
+ legacyPolyLine.AppendCorner( point.x, point.y );
+ }
+
+ legacyPolyLine.CloseLastContour();
+
+ // Add the triangle hole
+ legacyPolyLine.Start( 0, common.holeyPoints[9].x, common.holeyPoints[9].y,
+ CPolyLine::NO_HATCH );
+
+ for( int i = 10; i < 12; i++ )
+ {
+ VECTOR2I point = common.holeyPoints[i];
+ legacyPolyLine.AppendCorner( point.x, point.y );
+ }
+
+ legacyPolyLine.CloseLastContour();
+ }
+
+ ~ChamferFilletFixture(){}
+};
+
+/**
+ * Fixture for the Collision test suite. It contains an instance of the common data and two
+ * vectors containing colliding and non-colliding points.
+ */
+struct CollisionFixture {
+ // Structure to store the common data.
+ struct CommonTestData common;
+
+ // Vectors containing colliding and non-colliding points
+ std::vector<VECTOR2I> collidingPoints, nonCollidingPoints;
+
+ /**
+ * Constructor
+ */
+ CollisionFixture()
+ {
+ // Create points colliding with the poly set.
+
+ // Inside the polygon
+ collidingPoints.push_back( VECTOR2I( 10,90 ) );
+
+ // Inside the polygon, but on a re-entrant angle of a hole
+ collidingPoints.push_back( VECTOR2I( 15,16 ) );
+
+ // On a hole edge => inside the polygon
+ collidingPoints.push_back( VECTOR2I( 40,25 ) );
+
+ // On the outline edge => inside the polygon
+ collidingPoints.push_back( VECTOR2I( 0,10 ) );
+
+ // Create points not colliding with the poly set.
+
+ // Completely outside of the polygon
+ nonCollidingPoints.push_back( VECTOR2I( 200,200 ) );
+
+ // Inside the outline and inside a hole => outside the polygon
+ nonCollidingPoints.push_back( VECTOR2I( 15,12 ) );
+ }
+
+ ~CollisionFixture(){}
+};
+
+/**
+* Fixture for the Iterator test suite. It contains an instance of the common data, three polysets with null segments and a vector containing their points.
+*/
+struct IteratorFixture {
+ // Structure to store the common data.
+ struct CommonTestData common;
+
+ // Polygons to test whether the RemoveNullSegments method works
+ SHAPE_POLY_SET lastNullSegmentPolySet;
+ SHAPE_POLY_SET firstNullSegmentPolySet;
+ SHAPE_POLY_SET insideNullSegmentPolySet;
+
+ // Null segments points
+ std::vector<VECTOR2I> nullPoints;
+
+ IteratorFixture()
+ {
+ nullPoints.push_back( VECTOR2I( 100,100 ) );
+ nullPoints.push_back( VECTOR2I( 0,100 ) );
+ nullPoints.push_back( VECTOR2I( 0, 0 ) );
+
+ // Create a polygon with its last segment null
+ SHAPE_LINE_CHAIN polyLine;
+ polyLine.Append( nullPoints[0] );
+ polyLine.Append( nullPoints[1] );
+ polyLine.Append( nullPoints[2] );
+ polyLine.Append( nullPoints[2], true );
+ polyLine.SetClosed( true );
+
+ lastNullSegmentPolySet.AddOutline(polyLine);
+
+ // Create a polygon with its first segment null
+ polyLine.Clear();
+ polyLine.Append( nullPoints[0] );
+ polyLine.Append( nullPoints[0], true );
+ polyLine.Append( nullPoints[1] );
+ polyLine.Append( nullPoints[2] );
+ polyLine.SetClosed( true );
+
+ firstNullSegmentPolySet.AddOutline(polyLine);
+
+ // Create a polygon with an inside segment null
+ polyLine.Clear();
+ polyLine.Append( nullPoints[0] );
+ polyLine.Append( nullPoints[1] );
+ polyLine.Append( nullPoints[1], true );
+ polyLine.Append( nullPoints[2] );
+ polyLine.SetClosed( true );
+
+ insideNullSegmentPolySet.AddOutline(polyLine);
+ }
+
+ ~IteratorFixture(){}
+};
+
+#endif //__FIXTURES_H
diff --git a/qa/geometry/CMakeLists.txt b/qa/geometry/CMakeLists.txt
new file mode 100644
index 000000000..51bd00e81
--- /dev/null
+++ b/qa/geometry/CMakeLists.txt
@@ -0,0 +1,53 @@
+#
+# This program source code file is part of KiCad, a free EDA CAD application.
+#
+# Copyright (C) 2017 CERN
+# @author Alejandro García Montoro <alejandro.garciamontoro@xxxxxxxxx>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, you may find one here:
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# or you may search the http://www.gnu.org website for the version 2 license,
+# or you may write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+find_package(Boost COMPONENTS system filesystem unit_test_framework REQUIRED)
+find_package( wxWidgets 3.0.0 COMPONENTS gl aui adv html core net base xml stc REQUIRED )
+
+add_executable(qa_geometry
+ test_module.cpp
+ test_chamfer_fillet.cpp
+ test_collision.cpp
+ test_iterator.cpp
+ test_segment.cpp
+)
+
+include_directories(
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_SOURCE_DIR}/polygon
+ ${CMAKE_SOURCE_DIR}/common/geometry
+)
+
+target_link_libraries(qa_geometry
+ ${CMAKE_BINARY_DIR}/polygon/libpolygon.a
+ ${CMAKE_BINARY_DIR}/common/libcommon.a
+ ${CMAKE_BINARY_DIR}/bitmaps_png/libbitmaps.a
+ ${CMAKE_BINARY_DIR}/polygon/libpolygon.a
+ ${Boost_FILESYSTEM_LIBRARY}
+ ${Boost_SYSTEM_LIBRARY}
+ ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
+ ${wxWidgets_LIBRARIES}
+)
+
+add_dependencies( qa_geometry pcbnew )
diff --git a/qa/geometry/test_chamfer_fillet.cpp b/qa/geometry/test_chamfer_fillet.cpp
new file mode 100644
index 000000000..8d5c9f475
--- /dev/null
+++ b/qa/geometry/test_chamfer_fillet.cpp
@@ -0,0 +1,159 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 CERN
+ * @author Alejandro García Montoro <alejandro.garciamontoro@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/test_case_template.hpp>
+#include <geometry/shape_poly_set.h>
+#include <geometry/shape_line_chain.h>
+#include <polygon/PolyLine.h>
+#include <algorithm>
+
+#include <qa/data/fixtures_geometry.h>
+
+/**
+ * Declares the ChamferFilletFixture struct as the boost test fixture.
+ */
+BOOST_FIXTURE_TEST_SUITE( ChamferFillet, ChamferFilletFixture )
+
+/**
+ * Function lexicographicOrder
+ * defines a lexicographic order between two VECTOR2I objects. Used along with std::sort
+ * when checking that two polygons have the same vertices.
+ * @param i is a VECTOR2I object.
+ * @param j is a VECTOR2I object.
+ * @return bool - true if (i.x, i.y) < (j.x, j.y) using the lexicographic order,
+ * i.e., i.x < j.x or i.x = j.x and i.y < j.y; false in any other case.
+ */
+bool lexicographicOrder( VECTOR2I i, VECTOR2I j )
+{
+ if( i.x != j.x )
+ return( i.x < j.x );
+ else
+ return( i.y < j.y );
+}
+
+/**
+ * Function TestLineChainEqualCPolyLine
+ * tests the equality between a SHAPE_LINE_CHAIN polygon and a polygon inside a
+ * CPolyLine object using Boost test suite.
+ * @param lineChain is a SHAPE_LINE_CHAIN polygon object.
+ * @param polyLine is a CPolyLine polygon object.
+ * @param contourIdx is the index of the contour inside polyLine that has to be tested
+ * against lineChain.
+ */
+void TestLineChainEqualCPolyLine(SHAPE_LINE_CHAIN& lineChain, CPolyLine& polyLine,
+ int contourIdx = 0)
+{
+ // Arrays to store the polygon points lexicographically ordered
+ std::vector<VECTOR2I> chainPoints;
+ std::vector<VECTOR2I> polyPoints;
+
+ // Populate the array storing the new data with the lineChain corners
+ for (int pointIdx = 0; pointIdx < lineChain.PointCount(); pointIdx++) {
+ chainPoints.push_back(lineChain.Point(pointIdx));
+ }
+
+ int start = polyLine.GetContourStart(contourIdx);
+ int end = polyLine.GetContourEnd(contourIdx);
+
+ // Populate the array storing the legacy data with the polyLine corners
+ for (int pointIdx = start; pointIdx <= end; pointIdx++) {
+ polyPoints.push_back( VECTOR2I(polyLine.GetX(pointIdx), polyLine.GetY(pointIdx)) );
+ }
+
+ // Order the vectors in a lexicographic way
+ std::sort(chainPoints.begin(), chainPoints.end(), lexicographicOrder);
+ std::sort(polyPoints.begin(), polyPoints.end(), lexicographicOrder);
+
+ // Compare every point coordinate to check the equality
+ BOOST_CHECK_EQUAL_COLLECTIONS(chainPoints.begin(), chainPoints.end(),
+ polyPoints.begin(), polyPoints.end());
+}
+
+/**
+ * Tests the SHAPE_POLY_SET::ChamferPolygon, which has been refactored into SHAPE_POLY_SET from
+ * CPolyLine::Chamfer. Assuming the code in CPolyLine is right, this test ensures the behaviour of
+ * the new refactored code does not change anything.
+ */
+BOOST_AUTO_TEST_CASE( Chamfer )
+{
+ SHAPE_POLY_SET::POLYGON actual;
+ CPolyLine expected;
+
+ // Test different distances, up to the half of the minimum segment longitude
+ for (int distance = 0; distance < 5; distance++) {
+ // Chamfered polygon to be tested.
+ actual = common.holeyPolySet.ChamferPolygon( distance, 0 );
+
+ // Chamfered polygon assumed to be right.
+ expected = *legacyPolyLine.Chamfer( distance );
+
+ // Double check that there are no repeated corners in the legacy shape.
+ expected.RemoveNullSegments();
+
+ // Test equality
+ for (size_t contourIdx = 0; contourIdx < actual.size(); contourIdx++)
+ {
+ TestLineChainEqualCPolyLine(actual[contourIdx], expected, contourIdx);
+ }
+ }
+}
+
+/**
+ * Tests the SHAPE_POLY_SET::FilletPolygon, which has been refactored into
+ * SHAPE_POLY_SET from CPolyLine::Fillet.
+ * Assuming the code in CPolyLine is right, this test ensures the behaviour of the new
+ * refactored code does not change anything.
+ */
+BOOST_AUTO_TEST_CASE( Fillet )
+{
+ SHAPE_POLY_SET::POLYGON actual;
+ CPolyLine expected;
+
+ // Test different radius, up to the half of the minimum segment longitude
+ for (int radius = 1; radius < 5; radius++)
+ {
+ // Test different number of segments
+ for (size_t segments = 1; segments < 100; segments++)
+ {
+ // Chamfered polygon to be tested.
+ actual = common.holeyPolySet.FilletPolygon( radius, segments, 0 );
+
+ // Chamfered polygon assumed to be right.
+ expected = *legacyPolyLine.Fillet( radius, segments );
+
+ // Double check that there are no repeated corners in the legacy shape.
+ expected.RemoveNullSegments();
+
+ // Test equality
+ for (size_t contourIdx = 0; contourIdx < actual.size(); contourIdx++)
+ {
+ TestLineChainEqualCPolyLine(actual[contourIdx], expected, contourIdx);
+ }
+ }
+ }
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/qa/geometry/test_collision.cpp b/qa/geometry/test_collision.cpp
new file mode 100644
index 000000000..850b0d988
--- /dev/null
+++ b/qa/geometry/test_collision.cpp
@@ -0,0 +1,150 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 CERN
+ * @author Alejandro García Montoro <alejandro.garciamontoro@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/test_case_template.hpp>
+#include <geometry/shape_poly_set.h>
+#include <geometry/shape_line_chain.h>
+
+#include <qa/data/fixtures_geometry.h>
+
+/**
+ * Declares the CollisionFixture as the boost test suite fixture.
+ */
+BOOST_FIXTURE_TEST_SUITE( Collision, CollisionFixture )
+
+/**
+ * Simple dummy test to check that HasHoles() definition is right
+ */
+BOOST_AUTO_TEST_CASE( HasHoles )
+{
+ BOOST_CHECK( !common.solidPolySet.HasHoles() );
+ BOOST_CHECK( common.holeyPolySet.HasHoles() );
+}
+
+/**
+ * This test checks basic behaviour of PointOnEdge, testing if points on corners, outline edges
+ * and hole edges are detected as colliding.
+ */
+BOOST_AUTO_TEST_CASE( PointOnEdge )
+{
+ // Check points on corners
+ BOOST_CHECK( common.holeyPolySet.PointOnEdge( VECTOR2I( 0,50 ) ) );
+
+ // Check points on outline edges
+ BOOST_CHECK( common.holeyPolySet.PointOnEdge( VECTOR2I( 0,10 ) ) );
+
+ // Check points on hole edges
+ BOOST_CHECK( common.holeyPolySet.PointOnEdge( VECTOR2I( 10,11 ) ) );
+
+ // Check points inside a hole -> not in edge
+ BOOST_CHECK( !common.holeyPolySet.PointOnEdge( VECTOR2I( 12,12 ) ) );
+
+ // Check points inside the polygon and outside any hole -> not on edge
+ BOOST_CHECK( !common.holeyPolySet.PointOnEdge( VECTOR2I( 90,90 ) ) );
+
+ // Check points outside the polygon -> not on edge
+ BOOST_CHECK( !common.holeyPolySet.PointOnEdge( VECTOR2I( 200,200 ) ) );
+}
+
+/**
+ * This test checks that the function Contains, whose behaviour has been updated to also manage
+ * holey polygons, does the right work.
+ */
+BOOST_AUTO_TEST_CASE( pointInPolygonSet )
+{
+ // Check that the set contains the points that collide with it
+ for( const VECTOR2I& point : collidingPoints )
+ {
+ BOOST_CHECK( common.holeyPolySet.Contains( point ) );
+ }
+
+ // Check that the set does not contain any point outside of it
+ for( const VECTOR2I& point : nonCollidingPoints )
+ {
+ BOOST_CHECK( !common.holeyPolySet.Contains( point ) );
+ }
+}
+
+/**
+ * This test checks the behaviour of the Collide (with a point) method.
+ */
+BOOST_AUTO_TEST_CASE( Collide )
+{
+ // When clearance = 0, the behaviour should be the same as with Contains
+
+ // Check that the set collides with the colliding points
+ for( const VECTOR2I& point : collidingPoints )
+ {
+ BOOST_CHECK( common.holeyPolySet.Collide( point, 0 ) );
+ }
+
+ // Check that the set does not collide with the non colliding points
+ for( const VECTOR2I& point : nonCollidingPoints )
+ {
+ BOOST_CHECK( !common.holeyPolySet.Collide( point, 0 ) );
+ }
+
+ // Checks with clearance > 0
+
+ // Point at the offset zone outside of the outline => collision!
+ BOOST_CHECK( common.holeyPolySet.Collide( VECTOR2I( -1,10 ), 5 ) );
+
+ // Point at the offset zone outside of a hole => collision!
+ BOOST_CHECK( common.holeyPolySet.Collide( VECTOR2I( 11,11 ), 5 ) );
+}
+
+/**
+ * This test checks the behaviour of the CollideVertex method, testing whether the collision with
+ * vertices is well detected
+ */
+BOOST_AUTO_TEST_CASE( CollideVertex )
+{
+ // Variable to store the index of the corner hit
+ SHAPE_POLY_SET::VERTEX_INDEX cornerHit;
+
+ // Check that the set collides with the colliding points
+ for( const VECTOR2I& point : common.holeyPoints )
+ {
+ BOOST_CHECK( common.holeyPolySet.CollideVertex( point, cornerHit, 0 ) );
+ }
+}
+
+/**
+ * This test checks the behaviour of the CollideVertex method, testing whether the collision with
+ * vertices is well detected
+ */
+BOOST_AUTO_TEST_CASE( CollideVertexWithClearance )
+{
+ // Variable to store the index of the corner hit
+ SHAPE_POLY_SET::VERTEX_INDEX cornerHit;
+
+ // Check that the set collides with the colliding points
+ for( const VECTOR2I& point : common.holeyPoints )
+ {
+ BOOST_CHECK( common.holeyPolySet.CollideVertex( point + VECTOR2I(1,1), cornerHit, 2 ) );
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/qa/geometry/test_iterator.cpp b/qa/geometry/test_iterator.cpp
new file mode 100644
index 000000000..b4876a55f
--- /dev/null
+++ b/qa/geometry/test_iterator.cpp
@@ -0,0 +1,133 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 CERN
+ * @author Alejandro García Montoro <alejandro.garciamontoro@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/test_case_template.hpp>
+#include <geometry/shape_poly_set.h>
+#include <geometry/shape_line_chain.h>
+
+#include <qa/data/fixtures_geometry.h>
+
+/**
+ * Declares the IteratorFixture as the boost test suite fixture.
+ */
+BOOST_FIXTURE_TEST_SUITE( PolygonIterator, IteratorFixture )
+
+/**
+ * Checks whether the iteration on the vertices of a common polygon is correct.
+ */
+BOOST_AUTO_TEST_CASE( VertexIterator )
+{
+ SHAPE_POLY_SET::ITERATOR iterator;
+ int vertexIndex = 0;
+
+ for( iterator = common.holeyPolySet.IterateWithHoles(); iterator; iterator++ )
+ {
+ BOOST_CHECK_EQUAL( common.holeyPoints[vertexIndex], *iterator );
+ vertexIndex++;
+ }
+}
+
+/**
+ * Checks whether the iteration on the segments of a common polygon is correct.
+ */
+BOOST_AUTO_TEST_CASE( SegmentIterator )
+{
+ SHAPE_POLY_SET::SEGMENT_ITERATOR iterator;
+ int segmentIndex = 0;
+
+ for( iterator = common.holeyPolySet.IterateSegmentsWithHoles(); iterator; iterator++ ){
+ SEG segment = *iterator;
+
+ BOOST_CHECK_EQUAL( common.holeySegments[segmentIndex].A, segment.A );
+ BOOST_CHECK_EQUAL( common.holeySegments[segmentIndex].B, segment.B );
+
+ segmentIndex++;
+ }
+}
+
+/**
+ * Checks whether the iteration on the segments of an empty polygon is correct.
+ */
+BOOST_AUTO_TEST_CASE( EmptyPolygon )
+{
+ SHAPE_POLY_SET::SEGMENT_ITERATOR iterator;
+
+ for( iterator = common.emptyPolySet.IterateSegmentsWithHoles(); iterator; iterator++ )
+ {
+ BOOST_FAIL( "Empty set is being iterated!" );
+ }
+}
+
+/**
+ * Checks whether the iteration on the segments of a polygon with one vertex is correct.
+ */
+BOOST_AUTO_TEST_CASE( UniqueVertex )
+{
+ SHAPE_POLY_SET::SEGMENT_ITERATOR iterator;
+ iterator = common.uniqueVertexPolySet.IterateSegmentsWithHoles();
+
+ SEG segment = *iterator;
+ BOOST_CHECK_EQUAL( segment.A, common.uniquePoints[0] );
+ BOOST_CHECK_EQUAL( segment.B, common.uniquePoints[0] );
+
+ iterator++;
+
+ BOOST_CHECK( !iterator );
+}
+
+/**
+ * Checks whether the counting of the total number of vertices is correct.
+ */
+BOOST_AUTO_TEST_CASE( TotalVertices )
+{
+ BOOST_CHECK_EQUAL( common.emptyPolySet.TotalVertices(), 0 );
+ BOOST_CHECK_EQUAL( common.uniqueVertexPolySet.TotalVertices(), 1 );
+ BOOST_CHECK_EQUAL( common.solidPolySet.TotalVertices(), 0 );
+ BOOST_CHECK_EQUAL( common.holeyPolySet.TotalVertices(), 12 );
+}
+
+/**
+ * Checks whether the removal of null segments, wherever they are placed, is correct.
+ */
+BOOST_AUTO_TEST_CASE( RemoveNullSegments )
+{
+ SHAPE_POLY_SET polygonSets[3] = {lastNullSegmentPolySet,
+ firstNullSegmentPolySet,
+ insideNullSegmentPolySet};
+
+ for( SHAPE_POLY_SET polygonSet : polygonSets )
+ {
+ BOOST_CHECK_EQUAL( polygonSet.TotalVertices(), 4 );
+ BOOST_CHECK_EQUAL( polygonSet.RemoveNullSegments(), 1);
+ BOOST_CHECK_EQUAL( polygonSet.TotalVertices(), 3 );
+
+ BOOST_CHECK_EQUAL( polygonSet.CVertex(0), nullPoints[0] );
+ BOOST_CHECK_EQUAL( polygonSet.CVertex(1), nullPoints[1] );
+ BOOST_CHECK_EQUAL( polygonSet.CVertex(2), nullPoints[2] );
+ }
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/qa/geometry/test_module.cpp b/qa/geometry/test_module.cpp
new file mode 100644
index 000000000..79fe6a903
--- /dev/null
+++ b/qa/geometry/test_module.cpp
@@ -0,0 +1,32 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 CERN
+ * @author Alejandro García Montoro <alejandro.garciamontoro@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * Main file for the geometry tests to be compiled
+ */
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MODULE "CPolyLine -> SHAPE_POLY_SET refactor module"
+
+#include <boost/test/unit_test.hpp>
diff --git a/qa/geometry/test_segment.cpp b/qa/geometry/test_segment.cpp
new file mode 100644
index 000000000..c37a7fa18
--- /dev/null
+++ b/qa/geometry/test_segment.cpp
@@ -0,0 +1,59 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 CERN
+ * @author Alejandro García Montoro <alejandro.garciamontoro@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/test_case_template.hpp>
+#include <geometry/seg.h>
+
+#include <qa/data/fixtures_geometry.h>
+
+/**
+ * Declares the IteratorFixture as the boost test suite fixture.
+ */
+BOOST_FIXTURE_TEST_SUITE( SegmentReference, CommonTestData )
+
+/**
+ * Checks whether the construction of a segment referencing external points works.
+ */
+BOOST_AUTO_TEST_CASE( SegmentReference )
+{
+ VECTOR2I A( 10, 20 );
+ VECTOR2I B( 100, 200 );
+
+ // Build a segment referencing the previous points
+ SEG segment(A, B);
+
+ BOOST_CHECK_EQUAL( A, VECTOR2I( 10, 20) );
+ BOOST_CHECK_EQUAL( B, VECTOR2I( 100, 200) );
+
+ // Modify the ends of the segments
+ segment.A += VECTOR2I( 10, 10 );
+ segment.B += VECTOR2I( 100, 100 );
+
+ // Check that the original points are also modified
+ BOOST_CHECK_EQUAL( A, VECTOR2I( 20, 30) );
+ BOOST_CHECK_EQUAL( B, VECTOR2I( 200, 300) );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
--------------2.11.1--
Follow ups
References