← Back to team overview

kicad-developers team mailing list archive

Re: [PATCH] CPolyLine -> SHAPE_POLY_SET refactor

 

Whoops, sorry, wrong patches.

These are the ones.

2017-01-30 13:57 GMT+01:00 Alejandro Garcia Montoro <
alejandro.garciamontoro@xxxxxxxxx>:

> Hi!
>
> I finally managed to refactor all code from polygon/PolyLine.{h,cpp}, yay!
>
> The first patch (which is quite long) contains all the refactoring and its
> propagated changes, whereas the second adds some tests to qa/ in order to
> check that the new code is consistent with the old one.
>
> The major changes are on shape_poly_set.{h,cpp}, where the refactored
> methods are added. There are also major changes on class_zone.{h,cpp}, as
> the zone representation has changed from CPolyLine to SHAPE_POLY_SET. A
> bunch of loops in several files, which iterate through the vertices or
> segments of the polygons are simplified using the family of ITERATOR
> classes, based on the previous work of Tomasz.
>
> This patch could potentially break *a lot* of things, so it would be great
> if you could play around and check that everything remains the same. I've
> only tested the changes on Linux; tests on Windows are on the way, but I
> have no access to an OSX machine, so I definitely need some help there.
>
> Any comments are more than welcome.
>
> Thanks to the CERN guys for all the help :)
>
> Best,
> Alejandro
>
From f40cd8e9196c1efc5888dc234ee33c411d939521 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Garc=C3=ADa=20Montoro?=
 <alejandro.garciamontoro@xxxxxxxxx>
Date: Mon, 30 Jan 2017 14:20:03 +0100
Subject: [PATCH 1/2] CPolyLine -> SHAPE_POLY_SET refactor
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------2.11.0"

This is a multi-part message in MIME format.
--------------2.11.0
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                 | 792 ++++++++++++++++++++-
 common/swig/kicad.i                                |   1 +
 include/class_eda_rect.h                           |  14 +
 include/geometry/shape_line_chain.h                |  11 +
 include/geometry/shape_poly_set.h                  | 686 +++++++++++++++++-
 include/math/vector2d.h                            |  10 +
 pcbnew/class_board.cpp                             |  28 +-
 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/specctra_export.cpp                         |  51 +-
 pcbnew/tools/drawing_tool.cpp                      |   8 +-
 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                        |  54 +-
 ...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            | 343 ++++-----
 33 files changed, 2404 insertions(+), 644 deletions(-)


--------------2.11.0
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..536a11548 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 )
+{
+    int polygonIdx = 0;
+    unsigned int contourIdx = 0;
+    int vertexIdx = 0;
+
+    int currentGlobalIdx = 0;
+
+    for( polygonIdx = 0; polygonIdx < OutlineCount(); polygonIdx++ )
+    {
+        POLYGON currentPolygon = Polygon( 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,23 @@ 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;
+
+    // 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 +224,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 +254,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 +273,72 @@ 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( 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
+            if( 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;
 }
 
 
@@ -208,6 +400,7 @@ const SHAPE_LINE_CHAIN SHAPE_POLY_SET::convertFromClipper( const Path& aPath )
     return lc;
 }
 
+
 void SHAPE_POLY_SET::booleanOp( ClipType aType, const SHAPE_POLY_SET& aOtherShape,
                                 POLYGON_MODE aFastMode )
 {
@@ -363,7 +556,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 +604,7 @@ struct FractureEdge
 
 typedef std::vector<FractureEdge*> FractureEdgeSet;
 
+
 static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
 {
     int x = edge->m_p1.x;
@@ -478,6 +672,7 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
     return 0;
 }
 
+
 void SHAPE_POLY_SET::fractureSingle( POLYGON& paths )
 {
     FractureEdgeSet edges;
@@ -590,6 +785,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 +808,37 @@ 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 );
+    }
+
+    return OutlineCount();
+}
+
+
 const std::string SHAPE_POLY_SET::Format() const
 {
     std::stringstream ss;
@@ -607,10 +848,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 +935,109 @@ 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();
+
+    POLYGON polygon = m_polys[aPolygonIdx];
+
+    polygon.erase( polygon.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 +1056,132 @@ 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.
+    VECTOR2I delta;
+
+    // Precompute squared aClearance
+    int squaredClearance = aClearance*aClearance;
+
+    for( ITERATOR iterator = IterateWithHoles(); iterator; iterator++ )
+    {
+        // Get the difference vector between current vertex and aPoint
+        delta = *iterator - aPoint;
+
+        // Compute distance
+        ecoord squaredDistance = delta.SquaredEuclideanNorm();
+
+        // Check for collisions
+        if( squaredDistance <= squaredClearance )
+        {
+            collision = true;
+
+            // Update aClearance to look for closer vertices
+            squaredClearance = squaredDistance;
+
+            // Store the indices that identify the vertex
+            aClosestVertex.m_polygon = iterator.m_currentPolygon;
+            aClosestVertex.m_contour = iterator.m_currentContour;
+            aClosestVertex.m_vertex = iterator.m_currentVertex;
+        }
+    }
+
+    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 +1276,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:
+    Simplify( PM_FAST );
+
+    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/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..458b2d872 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 )
+                {
+                    // 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
                 {
-                    m_currentVertex = 0;
-                    m_currentOutline++;
+                    // 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,229 @@ 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()
+            {
+                POLYGON poly = m_poly->Polygon( m_currentPolygon );
+                return poly[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;
+            }
+
+        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);
+
+        /**
+         * 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 +405,53 @@ 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 = -1, int aHole = -1 );
 
         ///> 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;
 
-        ///> Returns true if any of the outlines is self-intersecting
+        ///> 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( 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 );;
+
+
+        /**
+         * 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 +474,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 +508,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 +594,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 +725,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 +751,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 );
+
+        /**
+         * 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 is a given subpolygon contains the point aP. If aSubpolyIndex < 0 (default value),
-        ///> checks all polygons in the set
+        ///> 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 +812,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 +972,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..c7f2c25de 100644
--- a/pcbnew/class_board.cpp
+++ b/pcbnew/class_board.cpp
@@ -2363,14 +2363,16 @@ 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();
+    SHAPE_POLY_SET* curr_polygon = aCurrArea->Outline();
 
     // mark all areas as unmodified except this one, if modified
     for( unsigned ia = 0; ia < m_ZoneDescriptorList.size(); ia++ )
@@ -2378,11 +2380,15 @@ bool BOARD::NormalizeAreaPolygon( PICKED_ITEMS_LIST * aNewZonesList, ZONE_CONTAI
 
     aCurrArea->SetLocalFlags( 1 );
 
-    if( curr_polygon->IsPolygonSelfIntersecting() )
+    if( curr_polygon->IsSelfIntersecting() )
     {
-        std::vector<CPolyLine*>* pa = new std::vector<CPolyLine*>;
-        curr_polygon->UnHatch();
-        int n_poly = aCurrArea->Outline()->NormalizeAreaOutlines( pa );
+        aCurrArea->UnHatch();
+
+        // Copy area outline
+        SHAPE_POLY_SET* pa = new SHAPE_POLY_SET( *curr_polygon );
+
+        // Normalize copied area and store resulting number of polygons
+        int n_poly = pa->NormalizeAreaOutlines();
 
         // If clipping has created some polygons, we must add these new copper areas.
         if( n_poly > 1 )
@@ -2391,16 +2397,16 @@ bool BOARD::NormalizeAreaPolygon( PICKED_ITEMS_LIST * aNewZonesList, ZONE_CONTAI
 
             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( pa->UnitSet( ip - 1 ) );
                 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 );
             }
         }
@@ -2408,7 +2414,7 @@ bool BOARD::NormalizeAreaPolygon( PICKED_ITEMS_LIST * aNewZonesList, ZONE_CONTAI
         delete pa;
     }
 
-    curr_polygon->Hatch();
+    aCurrArea->Hatch();
 
     return true;
 }
diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp
index 0ac7966a0..7bfc89a2b 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->IterateSegments(); 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,12 @@ 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 );
+    SEG edgeSegment = m_Poly->Edge(aEdge);
 
-    // 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 );
+    edgeSegment.A += VECTOR2I(offset);
+    edgeSegment.B += VECTOR2I(offset);
 
-    m_Poly->Hatch();
+    Hatch();
 }
 
 
@@ -721,15 +746,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 +778,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 +814,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 +834,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 +876,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..55a0b006c 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, m_CornerSelection ) )
+        {
+            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 12ebcb3bc..2ed48f5a1 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 f5ed5d065..823987622 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 6d7045fb9..af43f256a 100644
--- a/pcbnew/pcad2kicadpcb_plugin/pcb_polygon.cpp
+++ b/pcbnew/pcad2kicadpcb_plugin/pcb_polygon.cpp
@@ -174,26 +174,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 3235abbcc..5cf779d27 100644
--- a/pcbnew/pcb_painter.cpp
+++ b/pcbnew/pcb_painter.cpp
@@ -949,12 +949,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/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 c0b897361..752df6bb0 100644
--- a/pcbnew/tools/drawing_tool.cpp
+++ b/pcbnew/tools/drawing_tool.cpp
@@ -1252,7 +1252,6 @@ int DRAWING_TOOL::drawZone( bool aKeepout )
                     if( direction45 )
                         zone->AppendCorner( cursorPos == origin ? line45.GetStart() : line45.GetEnd() );
 
-                    zone->Outline()->CloseLastContour();
                     zone->Outline()->RemoveNullSegments();
 
                     if( !aKeepout )
@@ -1322,10 +1321,11 @@ int DRAWING_TOOL::drawZone( bool aKeepout )
                     zoneInfo.ExportSetting( *zone );
                     m_frame->GetGalCanvas()->SetTopLayer( zoneInfo.m_CurrentZone_Layer );
 
+                    zone->SetLayer( zoneInfo.m_CurrentZone_Layer );
+
                     // Add the first point
-                    zone->Outline()->Start( zoneInfo.m_CurrentZone_Layer,
-                                            cursorPos.x, cursorPos.y,
-                                            zone->GetHatchStyle() );
+                    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 4847fc57e..36dd9e648 100644
--- a/pcbnew/tools/edit_tool.cpp
+++ b/pcbnew/tools/edit_tool.cpp
@@ -171,7 +171,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 ) )
         {
             BOARD_ITEM* curr_item = selection.Front();
@@ -241,18 +253,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 5fc9779e4..379ebfc7c 100644
--- a/pcbnew/tools/pcb_editor_control.cpp
+++ b/pcbnew/tools/pcb_editor_control.cpp
@@ -616,22 +616,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;
@@ -645,9 +644,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 dcb58482e..fa1ea06c1 100644
--- a/pcbnew/tools/point_editor.cpp
+++ b/pcbnew/tools/point_editor.cpp
@@ -116,14 +116,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();
                 }
 
@@ -448,13 +452,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;
@@ -571,9 +574,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() );
@@ -581,8 +584,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;
@@ -762,19 +765,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 )
@@ -786,8 +788,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 );
 
@@ -796,7 +798,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" ) );
     }
@@ -858,14 +860,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..a93b476fb 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
@@ -777,10 +780,9 @@ bool PCB_EDIT_FRAME::End_Zone( wxDC* DC )
     {
         for( int ii = 0; ii < zone->GetNumCorners(); ii++ )
         {
-            s_CurrentZone->AppendCorner( zone->GetCornerPosition( ii ) );
+            s_CurrentZone->AppendCorner( static_cast<wxPoint>( zone->GetCornerPosition( ii ) ) );
         }
 
-        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 +847,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 +941,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 +955,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 +964,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..f34eb647f 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;
-
-                    if( ic2 < ie2 )
-                    {
-                        xf2 = poly2->GetX( ic2 + 1 );
-                        yf2 = poly2->GetY( ic2 + 1 );
-                    }
-                    else
-                    {
-                        xf2 = poly2->GetX( is2 );
-                        yf2 = poly2->GetY( is2 );
-                    }
+            // Build second segment
+            SEG secondSegment = *segIterator2;
 
-                    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,31 +246,26 @@ 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 );
-
-    mergedOutlines.BooleanAdd( areaToMergePoly, SHAPE_POLY_SET::PM_FAST  );
-    mergedOutlines.Simplify( SHAPE_POLY_SET::PM_FAST );
+    area_ref->Outline()->BooleanAdd( *(area_to_combine->Outline()), SHAPE_POLY_SET::PM_FAST  );
+    area_ref->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() > 2 )
+    if( area_ref->Outline()->OutlineCount() > 2 )
     {
         wxLogMessage(wxT("BOARD::CombineAreas error: more than 2 polys after merging") );
         return false;
     }
 
-    if( mergedOutlines.OutlineCount() > 1 )
+    if( area_ref->Outline()->OutlineCount() > 1 )
         return false;
 
-    area_ref->Outline()->m_CornersList = ConvertPolySetToPolyList( mergedOutlines );
-
     RemoveArea( aDeletedList, area_to_combine );
 
     area_ref->SetLocalFlags( 1 );
-    area_ref->Outline()->Hatch();
+    area_ref->Hatch();
 
     return true;
 }
@@ -329,7 +280,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 +292,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 +325,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 +355,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 +380,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 +446,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 +492,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 +507,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;
             }
+
         }
     }
 

--------------2.11.0--


From 6b89d3b6719995684b93d326353ffde857af38f4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Garc=C3=ADa=20Montoro?=
 <alejandro.garciamontoro@xxxxxxxxx>
Date: Mon, 30 Jan 2017 14:20:46 +0100
Subject: [PATCH 2/2] Tests CPolyLine -> SHAPE_POLY_SET refactor
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------2.11.0"

This is a multi-part message in MIME format.
--------------2.11.0
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.
---
 polygon/PolyLine.cpp                |  22 ++-
 qa/CMakeLists.txt                   |   2 +
 qa/data/fixtures_geometry.h         | 298 ++++++++++++++++++++++++++++++++++++
 qa/geometry/CMakeLists.txt          |  50 ++++++
 qa/geometry/test_chamfer_fillet.cpp | 159 +++++++++++++++++++
 qa/geometry/test_collision.cpp      | 119 ++++++++++++++
 qa/geometry/test_iterator.cpp       | 133 ++++++++++++++++
 qa/geometry/test_module.cpp         |  32 ++++
 8 files changed, 811 insertions(+), 4 deletions(-)
 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


--------------2.11.0
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/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
         }
 
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..cd31e308c
--- /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 not 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 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..02f308cc7
--- /dev/null
+++ b/qa/geometry/CMakeLists.txt
@@ -0,0 +1,50 @@
+#
+# 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
+)
+
+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}
+)
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..9af0dbd7b
--- /dev/null
+++ b/qa/geometry/test_collision.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 ) );
+}
+
+
+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>

--------------2.11.0--



Follow ups

References