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.be...@gmail.com> 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.char...@wanadoo.fr> 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@lists.launchpad.net > > 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.be...@gmail.com> 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
_______________________________________________ Mailing list: https://launchpad.net/~kicad-developers Post to : kicad-developers@lists.launchpad.net Unsubscribe : https://launchpad.net/~kicad-developers More help : https://help.launchpad.net/ListHelp