kicad-developers team mailing list archive
-
kicad-developers team
-
Mailing list archive
-
Message #38707
Re: PCBNew: Segments/polygons not rendered correctly (from Bug 1806411])
Sorry, I failed to rebase that over the newest changes. This should
apply better as a patch.
Cheers,
John
On Wed, Dec 19, 2018 at 3:15 PM John Beard <john.j.beard@xxxxxxxxx> wrote:
>
> Hi,
>
> I've added a few simple cases to the qa_common unit tests.
>
> I don't know if I will have covered cases which would have exposed any
> bugs here, but any known corner cases (pass or fail) should be added.
>
> Also, they assume the polygons come in a fixed order. I don't know if
> that's a correct assumption, or if the test should merely check the
> triangle is somewhere in the list.
>
> These tests only cover PolygonTriangulation, not the use of it in
> SHAPE_POLY_SET::CacheTriangulation(), so the Fracture bugs aren't
> (yet...) covered. But it would probably be a good idea to also add a
> test including those cases of SHAPE_POLY_SET and check that:
>
> 1) The tests fail before the recent fixes (i.e. the tests are
> sensitive to the bug), and
> 2) They do pass now
>
> Cheers,
>
> John
> On Wed, Dec 19, 2018 at 1:57 PM jp charras <jp.charras@xxxxxxxxxx> wrote:
> >
> > Le 19/12/2018 à 04:48, Seth Hillbrand a écrit :
> > > Am 2018-12-18 14:19, schrieb jp charras:
> > >>
> > >> Sorry Seth,
> > >>
> > >> But with your fixes, CacheTriangulation() crashes with degenerated
> > >> polygons.
> > >>
> > >> To see that, modify gerbview_painter.cpp, line 265 to remove
> > >> absolutePolygon.COutline( 0 ).PointCount() < 3
> > >> and try to load the test file test_polygons_with_arcs.gbr in Gerbview.
> > >
> > > Hi JP-
> > >
> > > Thanks for finding that. The issue was that the Fracture() call
> > > resulted in an empty polygon set with no outlines and I hadn't re-tested
> > > its validity. 64f1fb9e7 fixes the bug.
> > >
> > > -Seth
> >
> >
> > Looks good to me now.
> > And CacheTriangulation() is really faster than GLU tesselation.
> > Thanks.
> >
> >
> > --
> > Jean-Pierre CHARRAS
> >
> > _______________________________________________
> > Mailing list: https://launchpad.net/~kicad-developers
> > Post to : kicad-developers@xxxxxxxxxxxxxxxxxxx
> > Unsubscribe : https://launchpad.net/~kicad-developers
> > More help : https://help.launchpad.net/ListHelp
From 40aefd8f761b0ba4ea33ef84fa83d02e14a2d103 Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Wed, 19 Dec 2018 13:41:40 +0000
Subject: [PATCH] QA: Add test on Polygon Triangulation
This adds some basic tests on:
* SHAPE_POLY_SET::TRIANGULATED_POLYGON
** Basic run though of the API
* PolygonTriangulation
** Run though a list of a few polys, checking the results make sense
Also add some documentation to these interfaces.
---
include/geometry/polygon_triangulation.h | 36 +-
include/geometry/shape_poly_set.h | 128 +++----
qa/common/CMakeLists.txt | 2 +
.../geometry/test_polygon_triangulation.cpp | 332 ++++++++++++++++++
.../geometry/test_triangulated_polygon.cpp | 80 +++++
.../include/unit_test_utils/unit_test_utils.h | 39 ++
6 files changed, 527 insertions(+), 90 deletions(-)
create mode 100644 qa/common/geometry/test_polygon_triangulation.cpp
create mode 100644 qa/common/geometry/test_triangulated_polygon.cpp
diff --git a/include/geometry/polygon_triangulation.h b/include/geometry/polygon_triangulation.h
index b419b45c6..7b0ff5834 100644
--- a/include/geometry/polygon_triangulation.h
+++ b/include/geometry/polygon_triangulation.h
@@ -51,13 +51,23 @@
#include <vector>
#include <math/box2.h>
+#include <geometry/shape_poly_set.h>
+
#include "clipper.hpp"
+/**
+ * Class that performs triangulation on a polygon: splitting it into a
+ * number of triangles between the existing vertices that completely cover
+ * the original area.
+ */
class PolygonTriangulation
{
public:
-
+ /**
+ * @param aResult a #SHAPE_POLY_SET::TRIANGULATED_POLYGON that will be used
+ * to store the result of successful tessellations.
+ */
PolygonTriangulation( SHAPE_POLY_SET::TRIANGULATED_POLYGON& aResult ) :
m_result( aResult )
{};
@@ -80,7 +90,6 @@ private:
/**
- * Function split
* Splits the referenced polygon between the reference point and
* vertex b, assuming they are in the same polygon. Notes that while we
* create a new vertex pointer for the linked list, we maintain the same
@@ -115,7 +124,6 @@ private:
}
/**
- * Function remove
* Removes the node from the linked list and z-ordered linked list.
*/
void remove()
@@ -141,7 +149,6 @@ private:
}
/**
- * Function updateList
* After inserting or changing nodes, this function should be called to
* remove duplicate vertices and ensure z-ordering is correct
*/
@@ -257,7 +264,6 @@ private:
}
/**
- * Function removeNullTriangles
* Iterates through the list to remove NULL triangles if they exist.
* This should only be called as a last resort when tesselation fails
* as the NULL triangles are inserted as Steiner points to improve the
@@ -294,7 +300,6 @@ private:
}
/**
- * Function createList
* Takes a Clipper path and converts it into a circular, doubly-linked
* list for triangulation
*/
@@ -337,7 +342,6 @@ private:
}
/**
- * Function createList
* Takes the SHAPE_LINE_CHAIN and links each point into a
* circular, doubly-linked list
*/
@@ -371,7 +375,6 @@ private:
}
/**
- * Function: earcutList
* Walks through a circular linked list starting at aPoint. For each point,
* test to see if the adjacent points form a triangle that is completely enclosed
* by the remaining polygon (an "ear" sticking off the polygon). If the three points
@@ -449,13 +452,12 @@ private:
}
/**
- * Function isEar
* Checks whether the given vertex is in the middle of an ear.
* This works by walking forward and backward in zOrder to the limits
* of the minimal bounding box formed around the triangle, checking whether
* any points are located inside the given triangle.
*
- * Returns true if aEar is the apex point of a ear in the polygon
+ * @return true if aEar is the apex point of a ear in the polygon
*/
bool isEar( Vertex* aEar ) const
{
@@ -506,7 +508,6 @@ private:
}
/**
- * Function splitPolygon
* If we cannot find an ear to slice in the current polygon list, we
* use this to split the polygon into two separate lists and slice them each
* independently. This is assured to generate at least one new ear if the
@@ -555,7 +556,6 @@ private:
}
/**
- * Function area
* Returns the twice the signed area of the triangle formed by vertices
* p, q, r.
*/
@@ -565,7 +565,6 @@ private:
}
/**
- * Function intersects
* Checks for intersection between two segments, end points included.
* Returns true if p1-p2 intersects q1-q2
*/
@@ -579,7 +578,6 @@ private:
}
/**
- * Function intersectsPolygon
* Checks whether the segment from vertex a -> vertex b crosses any of the segments
* of the polygon of which vertex a is a member.
* Return true if the segment intersects the edge of the polygon
@@ -602,7 +600,6 @@ private:
}
/**
- * Function locallyInside
* Checks whether the segment from vertex a -> vertex b is inside the polygon
* around the immediate area of vertex a. We don't define the exact area
* over which the segment is inside but it is guaranteed to be inside the polygon
@@ -618,7 +615,6 @@ private:
}
/**
- * Function insertVertex
* Creates an entry in the vertices lookup and optionally inserts the newly
* created vertex into an existing linked list.
* Returns a pointer to the newly created vertex
@@ -644,9 +640,13 @@ private:
return p;
}
-
public:
-
+ /**
+ * Tessellate the given polygon into triangles and store in the
+ * result object.
+ * @param aPoly the polygon to tessellate
+ * @return true if the polygon could be tessellated, false if not
+ */
bool TesselatePolygon( const SHAPE_LINE_CHAIN& aPoly )
{
m_bbox = aPoly.BBox();
diff --git a/include/geometry/shape_poly_set.h b/include/geometry/shape_poly_set.h
index 57a6dc9a6..7f540d9eb 100644
--- a/include/geometry/shape_poly_set.h
+++ b/include/geometry/shape_poly_set.h
@@ -59,9 +59,17 @@ class SHAPE_POLY_SET : public SHAPE
///> N.B. SWIG only supports typedef, so avoid c++ 'using' keyword
typedef std::vector<SHAPE_LINE_CHAIN> POLYGON;
+ /**
+ * Represents a set of vertices and a set of triangles made of edges
+ * between the vertices.
+ */
class TRIANGULATED_POLYGON
{
public:
+ /**
+ * A triangle connecting three vertices. The members represent
+ * the indices of the vertices in some other list.
+ */
struct TRI
{
TRI( int _a = 0, int _b = 0, int _c = 0 ) : a( _a ), b( _b ), c( _c )
@@ -71,12 +79,22 @@ class SHAPE_POLY_SET : public SHAPE
int a, b, c;
};
+ /**
+ * Remove all vertices and triangles from this object.
+ */
void Clear()
{
m_vertices.clear();
m_triangles.clear();
}
+ /**
+ * Get the vertex coordinates for the n-th triangle in this polygon
+ * @param index the index of the desired triangle (must be less than #GetTriangleCount())
+ * @param a coordinates of vertex "a"
+ * @param b coordinates of vertex "b"
+ * @param c coordinates of vertex "c"
+ */
void GetTriangle( int index, VECTOR2I& a, VECTOR2I& b, VECTOR2I& c ) const
{
auto tri = m_triangles[ index ];
@@ -85,6 +103,11 @@ class SHAPE_POLY_SET : public SHAPE
c = m_vertices[ tri.c ];
}
+ /**
+ * Add a new triangle to the list. The vertices that the triangle
+ * connects must be present before calling #GetTriangle
+ * @param aTri The triangle to add
+ */
void AddTriangle( const TRI& aTri )
{
m_triangles.push_back( aTri );
@@ -95,16 +118,30 @@ class SHAPE_POLY_SET : public SHAPE
m_triangles.emplace_back( a, b, c );
}
+ /**
+ * Append a vertex to the list.
+ * @param aP vertex to add
+ */
void AddVertex( const VECTOR2I& aP )
{
m_vertices.push_back( aP );
}
+ /**
+ * Get the number of triangles in this object. Do not try to
+ * access a triangle with a higher index!
+ * @return number of triangles
+ */
size_t GetTriangleCount() const
{
return m_triangles.size();
}
+ /**
+ * Get the number of vertices. Triangles should not refer to vertex
+ * indices higher than this!
+ * @return number of vertices
+ */
size_t GetVertexCount() const
{
return m_vertices.size();
@@ -117,8 +154,6 @@ class SHAPE_POLY_SET : public SHAPE
};
/**
- * 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.
@@ -135,8 +170,6 @@ class SHAPE_POLY_SET : public SHAPE
} VERTEX_INDEX;
/**
- * Class ITERATOR_TEMPLATE
- *
* Base class for iterating over all vertices in a given SHAPE_POLY_SET.
*/
template <class T>
@@ -145,7 +178,6 @@ class SHAPE_POLY_SET : public SHAPE
public:
/**
- * Function IsEndContour.
* @return bool - true if the current vertex is the last one of the current contour
* (outline or hole); false otherwise.
*/
@@ -155,7 +187,6 @@ class SHAPE_POLY_SET : public SHAPE
}
/**
- * Function IsLastOutline.
* @return bool - true if the current outline is the last one; false otherwise.
*/
bool IsLastPolygon() const
@@ -169,7 +200,6 @@ class SHAPE_POLY_SET : public SHAPE
}
/**
- * Function Advance
* advances the indices of the current vertex/outline/contour, checking whether the
* vertices in the holes have to be iterated through
*/
@@ -236,7 +266,6 @@ class SHAPE_POLY_SET : public SHAPE
}
/**
- * Function GetIndex
* @return VERTEX_INDEX - indices of the current polygon, contour and vertex.
*/
VERTEX_INDEX GetIndex()
@@ -263,8 +292,6 @@ class SHAPE_POLY_SET : public SHAPE
};
/**
- * Class SEGMENT_ITERATOR_TEMPLATE
- *
* Base class for iterating over all segments in a given SHAPE_POLY_SET.
*/
template <class T>
@@ -272,7 +299,6 @@ class SHAPE_POLY_SET : public SHAPE
{
public:
/**
- * Function IsLastOutline.
* @return bool - true if the current outline is the last one.
*/
bool IsLastPolygon() const
@@ -286,7 +312,6 @@ class SHAPE_POLY_SET : public SHAPE
}
/**
- * Function Advance
* advances the indices of the current vertex/outline/contour, checking whether the
* vertices in the holes have to be iterated through
*/
@@ -353,7 +378,6 @@ class SHAPE_POLY_SET : public SHAPE
}
/**
- * Function GetIndex
* @return VERTEX_INDEX - indices of the current polygon, contour and vertex.
*/
VERTEX_INDEX GetIndex()
@@ -368,7 +392,6 @@ class SHAPE_POLY_SET : public SHAPE
}
/**
- * Function IsAdjacent
* @param aOther is an iterator pointing to another segment.
* @return bool - true if both iterators point to the same segment of the same
* contour of the same polygon of the same polygon set; false
@@ -419,7 +442,6 @@ class SHAPE_POLY_SET : public SHAPE
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.
* @param aDeepCopy if true, make new copies of the triangulated unique_ptr vector
@@ -429,8 +451,6 @@ class SHAPE_POLY_SET : public SHAPE
~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.
@@ -443,8 +463,7 @@ class SHAPE_POLY_SET : public SHAPE
bool GetRelativeIndices( int aGlobalIdx, VERTEX_INDEX* aRelativeIndices) const;
/**
- * Function GetGlobalIndex
- * computes the global index of a vertex from the relative indices of polygon, contour and
+ * 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.
@@ -468,10 +487,8 @@ class SHAPE_POLY_SET : public SHAPE
///> 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)
/**
- * Function Append
- * adds a new vertex to the contour indexed by \p aOutline and \p aHole (defaults to the
+ * 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.
@@ -491,7 +508,6 @@ class SHAPE_POLY_SET : public SHAPE
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.
@@ -532,7 +548,6 @@ class SHAPE_POLY_SET : public SHAPE
/**
- * 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
@@ -541,7 +556,6 @@ class SHAPE_POLY_SET : public SHAPE
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.
*/
@@ -574,7 +588,6 @@ class SHAPE_POLY_SET : public SHAPE
}
/**
- * Function Subset
* returns a subset of the polygons in this set, the ones between aFirstPolygon and
* aLastPolygon.
* @param aFirstPolygon is the first polygon to be included in the returned set.
@@ -627,7 +640,6 @@ class SHAPE_POLY_SET : public SHAPE
}
/**
- * 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.
@@ -651,7 +663,6 @@ class SHAPE_POLY_SET : public SHAPE
}
/**
- * 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.
@@ -662,7 +673,6 @@ class SHAPE_POLY_SET : public SHAPE
}
/**
- * 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.
@@ -673,7 +683,6 @@ class SHAPE_POLY_SET : public SHAPE
}
/**
- * Function Iterate
* @return ITERATOR - an iterator object to visit all points in all outlines of the set,
* without visiting the points in the holes.
*/
@@ -683,7 +692,6 @@ class SHAPE_POLY_SET : public SHAPE
}
/**
- * Function IterateWithHoles
* @return ITERATOR - an iterator object to visit all points in all outlines of the set,
* visiting also the points in the holes.
*/
@@ -786,7 +794,8 @@ class SHAPE_POLY_SET : public SHAPE
return IterateSegments( aOutline, aOutline, true );
}
- /** operations on polygons use a aFastMode param
+ /**
+ * 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
* simple polygon, but calculations can be really significantly time consuming
@@ -850,7 +859,6 @@ class SHAPE_POLY_SET : public SHAPE
void Simplify( POLYGON_MODE aFastMode );
/**
- * Function NormalizeAreaOutlines
* Convert a self-intersecting polygon to one (or more) non self-intersecting polygon(s)
* Removes null segments.
* @return int - the polygon count (always >= 1, because there is at least one polygon)
@@ -868,7 +876,6 @@ class SHAPE_POLY_SET : public SHAPE
void Move( const VECTOR2I& aVector ) override;
/**
- * Function Rotate
* rotates all vertices by a given angle
* @param aCenter is the rotation center
* @param aAngle rotation angle in radians
@@ -884,8 +891,6 @@ class SHAPE_POLY_SET : public SHAPE
const BOX2I BBox( int aClearance = 0 ) const override;
/**
- * Function PointOnEdge()
- *
* Checks if point aP lies on an edge or vertex of some of the outlines or holes.
* @param aP is the point to check.
* @return bool - true if the point lies on the edge of any polygon.
@@ -893,7 +898,6 @@ class SHAPE_POLY_SET : public SHAPE
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.
@@ -906,7 +910,6 @@ class SHAPE_POLY_SET : public SHAPE
bool Collide( const VECTOR2I& aP, int aClearance = 0 ) const override;
/**
- * Function Collide
* Checks whether the segment aSeg collides with the inside of the polygon set; if the
* segment touches an edge or a corner of any of the polygons, there is no collision:
* the edges do not belong to the polygon itself.
@@ -920,7 +923,6 @@ class SHAPE_POLY_SET : public SHAPE
bool Collide( const SEG& aSeg, int aClearance = 0 ) const override;
/**
- * 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.
@@ -933,7 +935,6 @@ class SHAPE_POLY_SET : public SHAPE
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.
@@ -962,15 +963,13 @@ class SHAPE_POLY_SET : public SHAPE
}
/**
- * Function RemoveVertex
- * deletes the aGlobalIndex-th vertex.
+ * 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).
+ * Deletes the vertex indexed by aIndex (index of polygon, contour and vertex).
* @param aRelativeIndices is the set of relative indices of the to-be-removed vertex.
*/
void RemoveVertex( VERTEX_INDEX aRelativeIndices );
@@ -979,8 +978,7 @@ class SHAPE_POLY_SET : public SHAPE
void RemoveAllContours();
/**
- * Function RemoveContour
- * deletes the aContourIdx-th contour of the aPolygonIdx-th polygon in the set.
+ * 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.
@@ -989,8 +987,7 @@ class SHAPE_POLY_SET : public SHAPE
void RemoveContour( int aContourIdx, int aPolygonIdx = -1 );
/**
- * Function RemoveNullSegments
- * looks for null segments; ie, segments whose ends are exactly the same and deletes them.
+ * Looks for null segments; ie, segments whose ends are exactly the same and deletes them.
* @return int - the number of deleted segments.
*/
int RemoveNullSegments();
@@ -1002,8 +999,7 @@ class SHAPE_POLY_SET : public SHAPE
void DeletePolygon( int aIdx );
/**
- * Function Chamfer
- * returns a chamfered version of the aIndex-th polygon.
+ * Returns a chamfered version of the aIndex-th polygon.
* @param aDistance is the chamfering distance.
* @param aIndex is the index of the polygon to be chamfered.
* @return POLYGON - A polygon containing the chamfered version of the aIndex-th polygon.
@@ -1011,8 +1007,7 @@ class SHAPE_POLY_SET : public SHAPE
POLYGON ChamferPolygon( unsigned int aDistance, int aIndex = 0 );
/**
- * Function Fillet
- * returns a filleted version of the aIndex-th polygon.
+ * Returns a filleted version of the aIndex-th polygon.
* @param aRadius is the fillet radius.
* @param aErrorMax is the maximum allowable deviation of the polygon from the circle
* @param aIndex is the index of the polygon to be filleted
@@ -1021,16 +1016,14 @@ class SHAPE_POLY_SET : public SHAPE
POLYGON FilletPolygon( unsigned int aRadius, int aErrorMax, int aIndex = 0 );
/**
- * Function Chamfer
- * returns a chamfered version of the polygon set.
+ * Returns a chamfered version of the polygon set.
* @param aDistance is the chamfering distance.
* @return SHAPE_POLY_SET - A set containing the chamfered version of this set.
*/
SHAPE_POLY_SET Chamfer( int aDistance );
/**
- * Function Fillet
- * returns a filleted version of the polygon set.
+ * Returns a filleted version of the polygon set.
* @param aRadius is the fillet radius.
* @param aErrorMax is the maximum allowable deviation of the polygon from the circle
* @return SHAPE_POLY_SET - A set containing the filleted version of this set.
@@ -1038,8 +1031,7 @@ class SHAPE_POLY_SET : public SHAPE
SHAPE_POLY_SET Fillet( int aRadius, int aErrorMax );
/**
- * Function DistanceToPolygon
- * computes the minimum distance between the aIndex-th polygon and aPoint.
+ * Computes the minimum distance between the aIndex-th polygon and aPoint.
* @param aPoint is the point whose distance to the aIndex-th polygon has to be measured.
* @param aIndex is the index of the polygon whose distace to aPoint has to be measured.
* @return int - The minimum distance between aPoint and all the segments of the aIndex-th
@@ -1048,8 +1040,7 @@ class SHAPE_POLY_SET : public SHAPE
int DistanceToPolygon( VECTOR2I aPoint, int aIndex );
/**
- * Function DistanceToPolygon
- * computes the minimum distance between the aIndex-th polygon and aSegment with a
+ * 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.
@@ -1062,8 +1053,7 @@ class SHAPE_POLY_SET : public SHAPE
int DistanceToPolygon( SEG aSegment, int aIndex, int aSegmentWidth = 0 );
/**
- * Function DistanceToPolygon
- * computes the minimum distance between aPoint and all the polygons in the set
+ * Computes the minimum distance between aPoint and all the polygons in the set
* @param aPoint is the point whose distance to the set has to be measured.
* @return int - The minimum distance between aPoint and all the polygons in the set. If
* the point is contained in any of the polygons, the distance is zero.
@@ -1071,8 +1061,7 @@ class SHAPE_POLY_SET : public SHAPE
int Distance( VECTOR2I aPoint );
/**
- * Function DistanceToPolygon
- * computes the minimum distance between aSegment and all the polygons in the set.
+ * Computes the minimum distance between aSegment and all the polygons in the set.
* @param aSegment is the segment whose distance to the polygon set has to be measured.
* @param aSegmentWidth is the width of the segment; defaults to zero.
* @return int - The minimum distance between aSegment and all the polygons in the set.
@@ -1081,8 +1070,7 @@ class SHAPE_POLY_SET : public SHAPE
int Distance( const SEG& aSegment, int aSegmentWidth = 0 );
/**
- * Function IsVertexInHole.
- * checks whether the aGlobalIndex-th vertex belongs to a hole.
+ * 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.
*/
@@ -1099,8 +1087,8 @@ class SHAPE_POLY_SET : public SHAPE
void unfractureSingle ( POLYGON& path );
void importTree( ClipperLib::PolyTree* tree );
- /** Function booleanOp
- * this is the engine to execute all polygon boolean transforms
+ /**
+ * This is the engine to execute all polygon boolean transforms
* (AND, OR, ... and polygon simplification (merging overlaping polygons)
* @param aType is the transform type ( see ClipperLib::ClipType )
* @param aOtherShape is the SHAPE_LINE_CHAIN to combine with me.
@@ -1120,7 +1108,6 @@ class SHAPE_POLY_SET : public SHAPE
bool pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath ) const;
/**
- * 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
@@ -1144,10 +1131,7 @@ class SHAPE_POLY_SET : public SHAPE
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
diff --git a/qa/common/CMakeLists.txt b/qa/common/CMakeLists.txt
index 7ea7084f5..c57b1ebf3 100644
--- a/qa/common/CMakeLists.txt
+++ b/qa/common/CMakeLists.txt
@@ -43,6 +43,8 @@ add_executable( qa_common
libeval/test_numeric_evaluator.cpp
geometry/test_fillet.cpp
+ geometry/test_polygon_triangulation.cpp
+ geometry/test_triangulated_polygon.cpp
view/test_zoom_controller.cpp
)
diff --git a/qa/common/geometry/test_polygon_triangulation.cpp b/qa/common/geometry/test_polygon_triangulation.cpp
new file mode 100644
index 000000000..d94e3de06
--- /dev/null
+++ b/qa/common/geometry/test_polygon_triangulation.cpp
@@ -0,0 +1,332 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2018 KiCad Developers, see CHANGELOG.TXT for contributors.
+ *
+ * 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 <unit_test_utils/unit_test_utils.h>
+
+#include <geometry/polygon_triangulation.h>
+
+/**
+ * A type holding a triplet of vectors, representing a triangle
+ */
+using VEC_TRI = std::tuple<VECTOR2I, VECTOR2I, VECTOR2I>;
+
+/**
+ * Boost printer for the VEC_TRI type
+ */
+template <> struct BOOST_PRINT::print_log_value<VEC_TRI>
+{
+ void operator()( std::ostream& os, const VEC_TRI& v )
+ {
+ os << "(" << std::get<0>( v ) << "," << std::get<1>( v ) << ", " << std::get<2>( v ) << ")";
+ }
+};
+
+
+struct POLY_TRI_FIXTURE
+{
+ POLY_TRI_FIXTURE() : m_triangulator( m_result )
+ {
+ }
+
+ /// The triangulator to use
+ PolygonTriangulation m_triangulator;
+
+ /// The result that will be written out
+ SHAPE_POLY_SET::TRIANGULATED_POLYGON m_result;
+};
+
+
+BOOST_FIXTURE_TEST_SUITE( PolygonTri, POLY_TRI_FIXTURE )
+
+struct POLY_CASE
+{
+ std::string m_name;
+ std::vector<VECTOR2I> m_pts;
+ bool m_exp_success;
+ size_t m_exp_vtx_cnt;
+ std::vector<VEC_TRI> m_exp_tri_vtxs;
+};
+
+const static std::vector<POLY_CASE> poly_cases = {
+ {
+ "null",
+ {},
+ false,
+ 0,
+ {},
+ },
+ {
+ "point",
+ {
+ { 10, 10 },
+ },
+ false,
+ 0,
+ {},
+ },
+ {
+ "line",
+ {
+ { 10, 10 },
+ { 20, 20 },
+ },
+ false,
+ 2,
+ {},
+ },
+ {
+ "triangle CW unclosed",
+ {
+ { 0, 0 },
+ { 0, 10 },
+ { 10, 10 },
+ },
+ true,
+ 3,
+ {
+ {
+ { 0, 10 },
+ { 0, 0 },
+ { 10, 10 },
+ },
+ },
+ },
+ {
+ "triangle CW closed",
+ {
+ { 0, 0 },
+ { 0, 10 },
+ { 10, 10 },
+ { 0, 0 },
+ },
+ true,
+ 4,
+ {
+ {
+ { 0, 10 },
+ { 0, 0 },
+ { 10, 10 },
+ },
+ },
+ },
+ {
+ "triangle CCW",
+ {
+ { 0, 0 },
+ { 10, 0 },
+ { 10, 10 },
+ },
+ true,
+ 3,
+ {
+ // one triangle, CCW
+ {
+ { 10, 0 },
+ { 10, 10 },
+ { 0, 0 },
+ },
+ },
+ },
+ {
+ "rectangle CCW",
+ {
+ { 0, 0 },
+ { 20, 0 },
+ { 20, 10 },
+ { 0, 10 },
+ },
+ true,
+ 4,
+ {
+ // upper triangle, CCW
+ {
+ { 20, 10 },
+ { 0, 10 },
+ { 0, 0 },
+ },
+ // lower triangle, CCW
+ {
+ { 0, 0 },
+ { 20, 0 },
+ { 20, 10 },
+ },
+ },
+ },
+ {
+ // concave shape, L with outer corner on 0, 0
+ // Splits into 4 triangles radiating out from 0, 0
+ "l-shape CCW",
+ {
+ { 0, 0 },
+ { 20, 0 },
+ { 20, 10 },
+ { 10, 10 },
+ { 10, 20 },
+ { 0, 20 },
+ },
+ true,
+ 6,
+ {
+ // upper left triangle, CCW
+ {
+ { 10, 20 },
+ { 0, 20 },
+ { 0, 0 },
+ },
+ // lower right tri, CCW
+ {
+ { 0, 0 },
+ { 20, 0 },
+ { 20, 10 },
+ },
+ // upper right tri, CCW
+ {
+ { 10, 10 },
+ { 10, 20 },
+ { 0, 0 },
+ },
+ // lower left triangle, CCW
+ {
+ { 0, 0 },
+ { 20, 10 },
+ { 10, 10 },
+ },
+ },
+ },
+ {
+ // rectangle, but one side (the top) has a
+ // collinear point
+ "rectangle with collinear side",
+ {
+ { 0, 0 },
+ { 20, 0 },
+ { 20, 10 },
+ { 10, 10 },
+ { 0, 10 },
+ },
+ true,
+ 5,
+ {
+ // upper/left triangle, CCW
+ {
+ { 10, 10 },
+ { 0, 10 },
+ { 0, 0 },
+ },
+ // middle tri, CCW
+ {
+ { 0, 0 },
+ { 20, 0 },
+ { 20, 10 },
+ },
+ // lower/right tri (CCW)
+ {
+ { 20, 10 },
+ { 10, 10 },
+ { 0, 0 },
+ },
+ },
+ },
+ {
+ // self intersecting hourglass
+ "self-intersecting hourglass",
+ {
+ { 0, 0 },
+ { 10, 0 },
+ { 0, 10 },
+ { 10, 10 },
+ },
+ false,
+ 4,
+ {},
+ },
+ {
+ // self touching hourglass, with vertices at centre
+ "self-touching hourglass",
+ {
+ { 0, 0 },
+ { 5, 5 },
+ { 0, 10 },
+ { 10, 10 },
+ { 5, 5 },
+ { 10, 0 },
+ },
+ true,
+ 6,
+ {
+ // But only the lower triangle comes out
+ {
+ { 10, 0 },
+ { 5, 5 },
+ { 0, 0 },
+ },
+ },
+ },
+};
+
+/**
+ * Tesselation of the defined polygons
+ */
+BOOST_AUTO_TEST_CASE( Polys )
+{
+ for( const auto& c : poly_cases )
+ {
+ BOOST_TEST_CONTEXT( c.m_name )
+ {
+ SHAPE_LINE_CHAIN chain( c.m_pts.data(), c.m_pts.size() );
+
+ const bool result = m_triangulator.TesselatePolygon( chain );
+
+ BOOST_CHECK_EQUAL( c.m_exp_success, result );
+
+ BOOST_CHECK_EQUAL( c.m_exp_vtx_cnt, m_result.GetVertexCount() );
+
+ if( result )
+ {
+ BOOST_CHECK_EQUAL( c.m_exp_tri_vtxs.size(), m_result.GetTriangleCount() );
+
+ // Check the triangles match (only if we have the right number)
+ if( c.m_exp_tri_vtxs.size() == m_result.GetTriangleCount() )
+ {
+ // Check each triangle matches
+ // If the order doesn't matter, could just check each triangle
+ // exists in the result.
+ for( size_t i = 0; i < m_result.GetTriangleCount(); ++i )
+ {
+ VECTOR2I va, vb, vc;
+ m_result.GetTriangle( i, va, vb, vc );
+
+ VEC_TRI got{ va, vb, vc };
+
+ BOOST_CHECK_EQUAL( got, c.m_exp_tri_vtxs[i] );
+ }
+ }
+ }
+ }
+
+ m_result.Clear();
+ }
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/qa/common/geometry/test_triangulated_polygon.cpp b/qa/common/geometry/test_triangulated_polygon.cpp
new file mode 100644
index 000000000..b5f252ed9
--- /dev/null
+++ b/qa/common/geometry/test_triangulated_polygon.cpp
@@ -0,0 +1,80 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2018 KiCad Developers, see CHANGELOG.TXT for contributors.
+ *
+ * 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 <unit_test_utils/unit_test_utils.h>
+
+#include <geometry/shape_poly_set.h>
+
+
+BOOST_AUTO_TEST_SUITE( TriangulatedPolygon )
+
+/**
+ * Basic ops
+ */
+BOOST_AUTO_TEST_CASE( Basic )
+{
+ SHAPE_POLY_SET::TRIANGULATED_POLYGON tpoly;
+
+ // empty tri poly
+ BOOST_CHECK_EQUAL( 0, tpoly.GetVertexCount() );
+ BOOST_CHECK_EQUAL( 0, tpoly.GetTriangleCount() );
+
+ tpoly.AddVertex( { 0, 0 } );
+ tpoly.AddVertex( { 10, 0 } );
+ tpoly.AddVertex( { 0, 10 } );
+ tpoly.AddVertex( { 10, 10 } );
+
+ BOOST_CHECK_EQUAL( 4, tpoly.GetVertexCount() );
+ BOOST_CHECK_EQUAL( 0, tpoly.GetTriangleCount() );
+
+ tpoly.AddTriangle( 3, 0, 1 ); // A CCW triangle
+
+ BOOST_CHECK_EQUAL( 1, tpoly.GetTriangleCount() );
+
+ tpoly.AddTriangle( SHAPE_POLY_SET::TRIANGULATED_POLYGON::TRI{
+ 3, 2, 0 } ); // A CCW triangle, ctro with a TRI
+
+ BOOST_CHECK_EQUAL( 2, tpoly.GetTriangleCount() );
+
+ // retreive and check the triangle points
+ VECTOR2I a, b, c;
+
+ tpoly.GetTriangle( 0, a, b, c );
+ BOOST_CHECK_EQUAL( a, VECTOR2I( 10, 10 ) );
+ BOOST_CHECK_EQUAL( b, VECTOR2I( 0, 0 ) );
+ BOOST_CHECK_EQUAL( c, VECTOR2I( 10, 0 ) );
+
+ tpoly.GetTriangle( 1, a, b, c );
+ BOOST_CHECK_EQUAL( a, VECTOR2I( 10, 10 ) );
+ BOOST_CHECK_EQUAL( b, VECTOR2I( 0, 10 ) );
+ BOOST_CHECK_EQUAL( c, VECTOR2I( 0, 0 ) );
+
+ // clear and check empty
+ tpoly.Clear();
+
+ BOOST_CHECK_EQUAL( 0, tpoly.GetVertexCount() );
+ BOOST_CHECK_EQUAL( 0, tpoly.GetTriangleCount() );
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/qa/unit_test_utils/include/unit_test_utils/unit_test_utils.h b/qa/unit_test_utils/include/unit_test_utils/unit_test_utils.h
index 8efcd46f2..9e54e0fbb 100644
--- a/qa/unit_test_utils/include/unit_test_utils/unit_test_utils.h
+++ b/qa/unit_test_utils/include/unit_test_utils/unit_test_utils.h
@@ -24,6 +24,9 @@
#ifndef UNIT_TEST_UTILS__H
#define UNIT_TEST_UTILS__H
+#include <boost/test/test_case_template.hpp>
+#include <boost/test/unit_test.hpp>
+
#include <functional>
/**
@@ -50,4 +53,40 @@
*/
#undef BOOST_TEST
+
+#if BOOST_VERSION < 105900
+
+/*
+ * BOOST_TEST_INFO is not available before 1.59. It's not critical for
+ * test pass/fail, it's just info, so just pass along to a logging
+ * function.
+ *
+ * This can be removed when our minimum boost version is 1.59 or higher.
+ */
+#define BOOST_TEST_INFO( A ) BOOST_TEST_MESSAGE( A )
+
+/*
+ *
+ * BOOST_TEST_CONTEXT provides scoped info, but again, only after 1.59.
+ * Replacing with a call to BOOST_TEST_MESSAGE will work, and the
+ * scoping will still work for newer boosts.
+ *
+ * This can be removed when our minimum boost version is 1.59 or higher.
+ */
+#define BOOST_TEST_CONTEXT( A ) BOOST_TEST_MESSAGE( A );
+
+#endif
+
+/*
+ * Define a helper to make it easier to use the right namespace for
+ * defining the print helpers like this:
+ *
+ * template<>
+ * struct BOOST_PRINT::print_log_value< MY_TYPE >
+ */
+#if BOOST_VERSION < 105900
+namespace BOOST_PRINT = boost::test_tools;
+#else
+namespace BOOST_PRINT = boost::test_tools::tt_detail;
+#endif
#endif // UNIT_TEST_UTILS__H
\ No newline at end of file
--
2.19.2
Follow ups
References