diff --git a/libs/kimath/CMakeLists.txt b/libs/kimath/CMakeLists.txt index c587c8e532..a477761267 100644 --- a/libs/kimath/CMakeLists.txt +++ b/libs/kimath/CMakeLists.txt @@ -30,6 +30,7 @@ set( KIMATH_SRCS src/geometry/shape_rect.cpp src/geometry/shape_compound.cpp src/geometry/shape_segment.cpp + src/geometry/vertex_set.cpp src/math/vector2.cpp diff --git a/libs/kimath/include/geometry/polygon_triangulation.h b/libs/kimath/include/geometry/polygon_triangulation.h index 8bbee33537..b95af05a4b 100644 --- a/libs/kimath/include/geometry/polygon_triangulation.h +++ b/libs/kimath/include/geometry/polygon_triangulation.h @@ -1,11 +1,11 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Modifications Copyright (C) 2018-2024 KiCad Developers + * Copyright (C) 2018 KiCad Developers, see AUTHORS.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 + * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -15,8 +15,8 @@ * * 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, + * http://www.gnu.org/licenses/gpl-3.0.html + * or you may search the http://www.gnu.org website for the version 3 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * @@ -54,16 +54,18 @@ #include #include #include +#include #include #include #include #define TRIANGULATE_TRACE "triangulate" -class POLYGON_TRIANGULATION +class POLYGON_TRIANGULATION : public VERTEX_SET { public: POLYGON_TRIANGULATION( SHAPE_POLY_SET::TRIANGULATED_POLYGON& aResult ) : + VERTEX_SET( ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel ), m_vertices_original_size( 0 ), m_result( aResult ) {}; @@ -81,6 +83,9 @@ public: /// therefore cannot be polygons VERTEX* firstVertex = createList( aPoly ); + for( const VECTOR2I& pt : aPoly.CPoints() ) + m_result.AddVertex( pt ); + if( !firstVertex || firstVertex->prev == firstVertex->next ) return true; @@ -114,228 +119,6 @@ public: } private: - struct VERTEX - { - VERTEX( size_t aIndex, double aX, double aY, POLYGON_TRIANGULATION* aParent ) : - i( aIndex ), - x( aX ), - y( aY ), - parent( aParent ) - { - } - - VERTEX& operator=( const VERTEX& ) = delete; - VERTEX& operator=( VERTEX&& ) = delete; - - bool operator==( const VERTEX& rhs ) const - { - return this->x == rhs.x && this->y == rhs.y; - } - bool operator!=( const VERTEX& rhs ) const { return !( *this == rhs ); } - - - /** - * Split 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 - * vertex index value from the original polygon. In this way, we have - * two polygons that both share the same vertices. - * - * @return the newly created vertex in the polygon that does not include the - * reference vertex. - */ - VERTEX* split( VERTEX* b ) - { - parent->m_vertices.emplace_back( i, x, y, parent ); - VERTEX* a2 = &parent->m_vertices.back(); - parent->m_vertices.emplace_back( b->i, b->x, b->y, parent ); - VERTEX* b2 = &parent->m_vertices.back(); - VERTEX* an = next; - VERTEX* bp = b->prev; - - next = b; - b->prev = this; - - a2->next = an; - an->prev = a2; - - b2->next = a2; - a2->prev = b2; - - bp->next = b2; - b2->prev = bp; - - return b2; - } - - /** - * Remove the node from the linked list and z-ordered linked list. - */ - void remove() - { - next->prev = prev; - prev->next = next; - - if( prevZ ) - prevZ->nextZ = nextZ; - - if( nextZ ) - nextZ->prevZ = prevZ; - - next = nullptr; - prev = nullptr; - nextZ = nullptr; - prevZ = nullptr; - } - - void updateOrder() - { - if( !z ) - z = parent->zOrder( x, y ); - } - - /** - * After inserting or changing nodes, this function should be called to - * remove duplicate vertices and ensure z-ordering is correct. - */ - void updateList() - { - VERTEX* p = next; - - while( p != this ) - { - /** - * Remove duplicates - */ - if( *p == *p->next ) - { - p = p->prev; - p->next->remove(); - - if( p == p->next ) - break; - } - - p->updateOrder(); - p = p->next; - }; - - updateOrder(); - zSort(); - } - - /** - * Sort all vertices in this vertex's list by their Morton code. - */ - void zSort() - { - std::deque queue; - - queue.push_back( this ); - - for( auto p = next; p && p != this; p = p->next ) - queue.push_back( p ); - - std::sort( queue.begin(), queue.end(), []( const VERTEX* a, const VERTEX* b ) - { - if( a->z != b->z ) - return a->z < b->z; - - if( a->x != b->x ) - return a->x < b->x; - - if( a->y != b->y ) - return a->y < b->y; - - return a->i < b->i; - } ); - - VERTEX* prev_elem = nullptr; - - for( auto elem : queue ) - { - if( prev_elem ) - prev_elem->nextZ = elem; - - elem->prevZ = prev_elem; - prev_elem = elem; - } - - prev_elem->nextZ = nullptr; - } - - - /** - * Check to see if triangle surrounds our current vertex - */ - bool inTriangle( const VERTEX& a, const VERTEX& b, const VERTEX& c ) - { - return ( c.x - x ) * ( a.y - y ) - ( a.x - x ) * ( c.y - y ) >= 0 - && ( a.x - x ) * ( b.y - y ) - ( b.x - x ) * ( a.y - y ) >= 0 - && ( b.x - x ) * ( c.y - y ) - ( c.x - x ) * ( b.y - y ) >= 0; - } - - /** - * Returns the signed area of the polygon connected to the current vertex, - * optionally ending at a specified vertex. - */ - double area( const VERTEX* aEnd = nullptr ) const - { - const VERTEX* p = this; - double a = 0.0; - - do - { - a += ( p->x + p->next->x ) * ( p->next->y - p->y ); - p = p->next; - } while( p != this && p != aEnd ); - - if( p != this ) - a += ( p->x + aEnd->x ) * ( aEnd->y - p->y ); - - return a / 2; - } - - const size_t i; - double x; - double y; - POLYGON_TRIANGULATION* parent; - - // previous and next vertices nodes in a polygon ring - VERTEX* prev = nullptr; - VERTEX* next = nullptr; - - // z-order curve value - int32_t z = 0; - - // previous and next nodes in z-order - VERTEX* prevZ = nullptr; - VERTEX* nextZ = nullptr; - }; - - /** - * Calculate the Morton code of the Vertex - * http://www.graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN - * - */ - int32_t zOrder( const double aX, const double aY ) const - { - int32_t x = static_cast( 32767.0 * ( aX - m_bbox.GetX() ) / m_bbox.GetWidth() ); - int32_t y = static_cast( 32767.0 * ( aY - m_bbox.GetY() ) / m_bbox.GetHeight() ); - - x = ( x | ( x << 8 ) ) & 0x00FF00FF; - x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; - x = ( x | ( x << 2 ) ) & 0x33333333; - x = ( x | ( x << 1 ) ) & 0x55555555; - - y = ( y | ( y << 8 ) ) & 0x00FF00FF; - y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; - y = ( y | ( y << 2 ) ) & 0x33333333; - y = ( y | ( y << 1 ) ) & 0x55555555; - - return x | ( y << 1 ); - } - /** * Outputs a list of vertices that have not yet been triangulated. @@ -510,57 +293,6 @@ private: return retval; } - /** - * Take a #SHAPE_LINE_CHAIN and links each point into a circular, doubly-linked list. - */ - VERTEX* createList( const SHAPE_LINE_CHAIN& points ) - { - wxLogTrace( TRIANGULATE_TRACE, "Creating list from %d points", points.PointCount() ); - - VERTEX* tail = nullptr; - double sum = 0.0; - - // Check for winding order - for( int i = 0; i < points.PointCount(); i++ ) - { - VECTOR2D p1 = points.CPoint( i ); - VECTOR2D p2 = points.CPoint( i + 1 ); - - sum += ( ( p2.x - p1.x ) * ( p2.y + p1.y ) ); - } - - VECTOR2I last_pt{ std::numeric_limits::max(), std::numeric_limits::max() }; - VECTOR2I::extended_type sq_dist = ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel; - sq_dist *= sq_dist; - - auto addVertex = [&]( int i ) - { - const VECTOR2I& pt = points.CPoint( i ); - VECTOR2I diff = pt - last_pt; - if( diff.SquaredEuclideanNorm() > sq_dist ) - { - tail = insertVertex( pt, tail ); - last_pt = pt; - } - }; - - if( sum > 0.0 ) - { - for( int i = points.PointCount() - 1; i >= 0; i-- ) - addVertex( i ); - } - else - { - for( int i = 0; i < points.PointCount(); i++ ) - addVertex( i ); - } - - if( tail && ( *tail == *tail->next ) ) - tail->next->remove(); - - return tail; - } - /** * Walk through a circular linked list starting at \a aPoint. * @@ -823,7 +555,7 @@ private: { double x = a->x * ( 1.0 - step * i ) + b->x * ( step * i ); double y = a->y * ( 1.0 - step * i ) + b->y * ( step * i ); - last = insertVertex( VECTOR2I( x, y ), last ); + last = insertTriVertex( VECTOR2I( x, y ), last ); } } @@ -956,14 +688,6 @@ private: } - /** - * Return the twice the signed area of the triangle formed by vertices p, q, and r. - */ - double area( const VERTEX* p, const VERTEX* q, const VERTEX* r ) const - { - return ( q->y - p->y ) * ( r->x - q->x ) - ( q->x - p->x ) * ( r->y - q->y ); - } - constexpr int sign( double aVal ) const { @@ -1035,76 +759,19 @@ private: return false; } - /** - * Check 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 immediately adjacent to vertex a. - * - * @return true if the segment from a->b is inside a's polygon next to vertex a. - */ - bool locallyInside( const VERTEX* a, const VERTEX* b ) const - { - if( area( a->prev, a, a->next ) < 0 ) - return area( a, b, a->next ) >= 0 && area( a, a->prev, b ) >= 0; - else - return area( a, b, a->prev ) < 0 || area( a, a->next, b ) < 0; - } - - /** - * Check to see if the segment halfway point between a and b is inside the polygon - */ - bool middleInside( const VERTEX* a, const VERTEX* b ) const - { - const VERTEX* p = a; - bool inside = false; - double px = ( a->x + b->x ) / 2; - double py = ( a->y + b->y ) / 2; - - do - { - if( ( ( p->y > py ) != ( p->next->y > py ) ) - && ( px < ( p->next->x - p->x ) * ( py - p->y ) / ( p->next->y - p->y ) + p->x ) ) - inside = !inside; - - p = p->next; - } while( p != a ); - - return inside; - } - /** * Create an entry in the vertices lookup and optionally inserts the newly created vertex * into an existing linked list. * * @return a pointer to the newly created vertex. */ - VERTEX* insertVertex( const VECTOR2I& pt, VERTEX* last ) + VERTEX* insertTriVertex( const VECTOR2I& pt, VERTEX* last ) { m_result.AddVertex( pt ); - m_vertices.emplace_back( m_result.GetVertexCount() - 1, pt.x, pt.y, this ); - - VERTEX* p = &m_vertices.back(); - - if( !last ) - { - p->prev = p; - p->next = p; - } - else - { - p->next = last->next; - p->prev = last; - last->next->prev = p; - last->next = p; - } - return p; + return insertVertex( m_result.GetVertexCount() - 1, pt, nullptr ); } private: - BOX2I m_bbox; - std::deque m_vertices; size_t m_vertices_original_size; SHAPE_POLY_SET::TRIANGULATED_POLYGON& m_result; }; diff --git a/libs/kimath/include/geometry/vertex_set.h b/libs/kimath/include/geometry/vertex_set.h new file mode 100644 index 0000000000..0dae5cd1c3 --- /dev/null +++ b/libs/kimath/include/geometry/vertex_set.h @@ -0,0 +1,352 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 20218 KiCad Developers, see AUTHORS.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 3 + * 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/gpl-3.0.html + * or you may search the http://www.gnu.org website for the version 3 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + * Based on Uniform Plane Subdivision algorithm from Lamot, Marko, and Borut Žalik. + * "A fast polygon triangulation algorithm based on uniform plane subdivision." + * Computers & graphics 27, no. 2 (2003): 239-253. + * + * Code derived from: + * K-3D which is Copyright (c) 2005-2006, Romain Behar, GPL-2, license above + * earcut which is Copyright (c) 2016, Mapbox, ISC + * + * ISC License: + * Permission to use, copy, modify, and/or distribute this software for any purpose + * with or without fee is hereby granted, provided that the above copyright notice + * and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + * + */ + +#ifndef VERTEX_SET_H +#define VERTEX_SET_H + +#include +#include + +#include +#include + +class VERTEX; + +class VERTEX_SET +{ + friend class VERTEX; + +public: + VERTEX_SET( int aSimplificationLevel ) + { + m_simplificationLevel = aSimplificationLevel * ( VECTOR2I::extended_type ) aSimplificationLevel; + } + ~VERTEX_SET() {} + + void SetBoundingBox( const BOX2I& aBBox ); + + /** + * Insert a vertex into the vertex set + * @param aIndex the index of the vertex + * @param pt the point to insert + * @param last the last vertex in the list + * @return the newly inserted vertex + */ + VERTEX* insertVertex( int aIndex, const VECTOR2I& pt, VERTEX* last ); + + /** + * Create a list of vertices from a line chain + * @param points the line chain to create the list from + * @return the first vertex in the list + */ + VERTEX* createList( const SHAPE_LINE_CHAIN& points ); + +protected: + + /** + * Get the next vertex in the outline, avoiding steiner points + * and points that overlap with splits + * @param aPt the current vertex + * @return the next vertex in the outline + */ + VERTEX* getNextOutlineVertex( const VERTEX* aPt ) const; + + /** + * Get the previous vertex in the outline, avoiding steiner points + * and points that overlap with splits + * @param aPt the current vertex + * @return the previous vertex in the outline + */ + VERTEX* getPrevOutlineVertex( const VERTEX* aPt ) const; + + /** + * Check 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 immediately adjacent to vertex a. + * + * @return true if the segment from a->b is inside a's polygon next to vertex a. + */ + bool locallyInside( const VERTEX* a, const VERTEX* b ) const; + + /** + * Check if the middle of the segment from a to b is inside the polygon + * @param a the first vertex + * @param b the second vertex + * @return true if the point is in the middle of the triangle + */ + bool middleInside( const VERTEX* a, const VERTEX* b ) const; + + /** + * Check if two vertices are at the same point + * @param aA the first vertex + * @param aB the second vertex + * @return true if the vertices are at the same point + */ + bool same_point( const VERTEX* aA, const VERTEX* aB ) const; + + /** + * Note that while the inputs are doubles, these are scaled + * by the size of the bounding box to fit into a 32-bit Morton code + * @return the Morton code for the point (aX, aY) + */ + int32_t zOrder( const double aX, const double aY ) const; + + /** + * @return the area of the triangle defined by the three vertices + */ + double area( const VERTEX* p, const VERTEX* q, const VERTEX* r ) const; + + BOX2I m_bbox; + std::deque m_vertices; + VECTOR2I::extended_type m_simplificationLevel; +}; + +class VERTEX +{ + public: + + VERTEX( int aIndex, double aX, double aY, VERTEX_SET* aParent ) : + i( aIndex ), + x( aX ), + y( aY ), + parent( aParent ) + { + } + + VERTEX& operator=( const VERTEX& ) = delete; + VERTEX& operator=( VERTEX&& ) = delete; + + bool operator==( const VERTEX& rhs ) const + { + return this->x == rhs.x && this->y == rhs.y; + } + bool operator!=( const VERTEX& rhs ) const { return !( *this == rhs ); } + + /** + * Remove the node from the linked list and z-ordered linked list. + */ + void remove() + { + next->prev = prev; + prev->next = next; + + if( prevZ ) + prevZ->nextZ = nextZ; + + if( nextZ ) + nextZ->prevZ = prevZ; + + next = nullptr; + prev = nullptr; + nextZ = nullptr; + prevZ = nullptr; + } + + + + /** + * Split 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 + * vertex index value from the original polygon. In this way, we have + * two polygons that both share the same vertices. + * + * @return the newly created vertex in the polygon that does not include the + * reference vertex. + */ + VERTEX* split( VERTEX* b ) + { + parent->m_vertices.emplace_back( i, x, y, parent ); + VERTEX* a2 = parent->insertVertex( i, VECTOR2I( x, y ), nullptr ); + parent->m_vertices.emplace_back( b->i, b->x, b->y, parent ); + VERTEX* b2 = &parent->m_vertices.back(); + VERTEX* an = next; + VERTEX* bp = b->prev; + + next = b; + b->prev = this; + + a2->next = an; + an->prev = a2; + + b2->next = a2; + a2->prev = b2; + + bp->next = b2; + b2->prev = bp; + + return b2; + } + + void updateOrder() + { + if( !z ) + z = parent->zOrder( x, y ); + } + + /** + * After inserting or changing nodes, this function should be called to + * remove duplicate vertices and ensure z-ordering is correct. + */ + void updateList() + { + VERTEX* p = next; + + while( p != this ) + { + /** + * Remove duplicates + */ + if( *p == *p->next ) + { + p = p->prev; + p->next->remove(); + + if( p == p->next ) + break; + } + + p->updateOrder(); + p = p->next; + }; + + updateOrder(); + zSort(); + } + + /** + * Sort all vertices in this vertex's list by their Morton code. + */ + void zSort() + { + std::deque queue; + + queue.push_back( this ); + + for( VERTEX* p = next; p && p != this; p = p->next ) + queue.push_back( p ); + + std::sort( queue.begin(), queue.end(), []( const VERTEX* a, const VERTEX* b ) + { + if( a->z != b->z ) + return a->z < b->z; + + if( a->x != b->x ) + return a->x < b->x; + + if( a->y != b->y ) + return a->y < b->y; + + return a->i < b->i; + } ); + + VERTEX* prev_elem = nullptr; + + for( VERTEX* elem : queue ) + { + if( prev_elem ) + prev_elem->nextZ = elem; + + elem->prevZ = prev_elem; + prev_elem = elem; + } + + prev_elem->nextZ = nullptr; + } + + + /** + * Check to see if triangle surrounds our current vertex + */ + bool inTriangle( const VERTEX& a, const VERTEX& b, const VERTEX& c ) + { + return ( c.x - x ) * ( a.y - y ) - ( a.x - x ) * ( c.y - y ) >= 0 + && ( a.x - x ) * ( b.y - y ) - ( b.x - x ) * ( a.y - y ) >= 0 + && ( b.x - x ) * ( c.y - y ) - ( c.x - x ) * ( b.y - y ) >= 0; + } + + /** + * Returns the signed area of the polygon connected to the current vertex, + * optionally ending at a specified vertex. + */ + double area( const VERTEX* aEnd = nullptr ) const + { + const VERTEX* p = this; + double a = 0.0; + + do + { + a += ( p->x + p->next->x ) * ( p->next->y - p->y ); + p = p->next; + } while( p != this && p != aEnd ); + + if( p != this ) + a += ( p->x + aEnd->x ) * ( aEnd->y - p->y ); + + return a / 2; + } + + const int i; + const double x; + const double y; + VERTEX_SET* parent; + + // previous and next vertices nodes in a polygon ring + VERTEX* prev = nullptr; + VERTEX* next = nullptr; + + // z-order curve value + int32_t z = 0; + + // previous and next nodes in z-order + VERTEX* prevZ = nullptr; + VERTEX* nextZ = nullptr; +}; + + + +#endif // VERTEX_SET_H diff --git a/libs/kimath/src/geometry/vertex_set.cpp b/libs/kimath/src/geometry/vertex_set.cpp new file mode 100644 index 0000000000..b81937989a --- /dev/null +++ b/libs/kimath/src/geometry/vertex_set.cpp @@ -0,0 +1,206 @@ +#include + +void VERTEX_SET::SetBoundingBox( const BOX2I& aBBox ) { m_bbox = aBBox; } + + +/** + * Take a #SHAPE_LINE_CHAIN and links each point into a circular, doubly-linked list. + */ +VERTEX* VERTEX_SET::createList( const SHAPE_LINE_CHAIN& points ) +{ + VERTEX* tail = nullptr; + double sum = 0.0; + + // Check for winding order + for( int i = 0; i < points.PointCount(); i++ ) + { + VECTOR2D p1 = points.CPoint( i ); + VECTOR2D p2 = points.CPoint( i + 1 ); + + sum += ( ( p2.x - p1.x ) * ( p2.y + p1.y ) ); + } + + VECTOR2I last_pt{ std::numeric_limits::max(), std::numeric_limits::max() }; + + auto addVertex = [&]( int i ) + { + const VECTOR2I& pt = points.CPoint( i ); + VECTOR2I diff = pt - last_pt; + if( diff.SquaredEuclideanNorm() > m_simplificationLevel ) + { + tail = insertVertex( i, pt, tail ); + last_pt = pt; + } + }; + + if( sum > 0.0 ) + { + for( int i = points.PointCount() - 1; i >= 0; i-- ) + addVertex( i ); + } + else + { + for( int i = 0; i < points.PointCount(); i++ ) + addVertex( i ); + } + + if( tail && ( *tail == *tail->next ) ) + { + tail->next->remove(); + } + + return tail; +} + + +/** + * Calculate the Morton code of the VERTEX + * http://www.graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN + * + */ +int32_t VERTEX_SET::zOrder( const double aX, const double aY ) const +{ + int32_t x = static_cast( 32767.0 * ( aX - m_bbox.GetX() ) / m_bbox.GetWidth() ); + int32_t y = static_cast( 32767.0 * ( aY - m_bbox.GetY() ) / m_bbox.GetHeight() ); + + x = ( x | ( x << 8 ) ) & 0x00FF00FF; + x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; + x = ( x | ( x << 2 ) ) & 0x33333333; + x = ( x | ( x << 1 ) ) & 0x55555555; + + y = ( y | ( y << 8 ) ) & 0x00FF00FF; + y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; + y = ( y | ( y << 2 ) ) & 0x33333333; + y = ( y | ( y << 1 ) ) & 0x55555555; + + return x | ( y << 1 ); +} + + +/** + * Return the twice the signed area of the triangle formed by vertices p, q, and r. + */ +double VERTEX_SET::area( const VERTEX* p, const VERTEX* q, const VERTEX* r ) const +{ + return ( q->y - p->y ) * ( r->x - q->x ) - ( q->x - p->x ) * ( r->y - q->y ); +} + + +bool VERTEX_SET::same_point( const VERTEX* aA, const VERTEX* aB ) const +{ + return aA && aB && aA->x == aB->x && aA->y == aB->y; +} + +VERTEX* VERTEX_SET::getNextOutlineVertex( const VERTEX* aPt ) const +{ + VERTEX* nz = aPt->nextZ; + VERTEX* pz = aPt->prevZ; + + // If we hit a fracture point, we want to continue around the + // edge we are working on and not switch to the pair edge + // However, this will depend on which direction the initial + // fracture hit is. If we find that we skip directly to + // a new fracture point, then we know that we are proceeding + // in the wrong direction from the fracture and should + // fall through to the next point + if( same_point( aPt, nz ) && same_point( aPt->next, nz->prev ) + && aPt->y == aPt->next->y ) + { + return nz->next; + } + + if( same_point( aPt, pz ) && same_point( aPt->next, pz->prev ) + && aPt->y == aPt->next->y ) + { + return pz->next; + } + + return aPt->next; +} + +VERTEX* VERTEX_SET::getPrevOutlineVertex( const VERTEX* aPt ) const +{ + VERTEX* nz = aPt->nextZ; + VERTEX* pz = aPt->prevZ; + + // If we hit a fracture point, we want to continue around the + // edge we are working on and not switch to the pair edge + // However, this will depend on which direction the initial + // fracture hit is. If we find that we skip directly to + // a new fracture point, then we know that we are proceeding + // in the wrong direction from the fracture and should + // fall through to the next point + if( same_point( aPt, nz ) + && aPt->y == aPt->prev->y) + { + return nz->prev; + } + + if( same_point( aPt, pz ) + && aPt->y == aPt->prev->y ) + { + return pz->prev; + } + + return aPt->prev; + +} + + +bool VERTEX_SET::locallyInside( const VERTEX* a, const VERTEX* b ) const +{ + const VERTEX* an = getNextOutlineVertex( a ); + const VERTEX* ap = getPrevOutlineVertex( a ); + + if( area( ap, a, an ) < 0 ) + return area( a, b, an ) >= 0 && area( a, ap, b ) >= 0; + else + return area( a, b, ap ) < 0 || area( a, an, b ) < 0; +} + + +bool VERTEX_SET::middleInside( const VERTEX* a, const VERTEX* b ) const +{ + const VERTEX* p = a; + bool inside = false; + double px = ( a->x + b->x ) / 2; + double py = ( a->y + b->y ) / 2; + + do + { + if( ( ( p->y > py ) != ( p->next->y > py ) ) + && ( px < ( p->next->x - p->x ) * ( py - p->y ) / ( p->next->y - p->y ) + p->x ) ) + inside = !inside; + + p = p->next; + } while( p != a ); + + return inside; +} + +/** + * Create an entry in the vertices lookup and optionally inserts the newly created vertex + * into an existing linked list. + * + * @return a pointer to the newly created vertex. + */ +VERTEX* VERTEX_SET::insertVertex( int aIndex, const VECTOR2I& pt, VERTEX* last ) +{ + m_vertices.emplace_back( aIndex, pt.x, pt.y, this ); + + VERTEX* p = &m_vertices.back(); + + if( !last ) + { + p->prev = p; + p->next = p; + } + else + { + p->next = last->next; + p->prev = last; + last->next->prev = p; + last->next = p; + } + return p; +} \ No newline at end of file diff --git a/pcbnew/drc/drc_test_provider_connection_width.cpp b/pcbnew/drc/drc_test_provider_connection_width.cpp index 4511fb89ad..9ba7ee5490 100644 --- a/pcbnew/drc/drc_test_provider_connection_width.cpp +++ b/pcbnew/drc/drc_test_provider_connection_width.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -111,10 +112,11 @@ private: }; -class POLYGON_TEST +class POLYGON_TEST : public VERTEX_SET { public: POLYGON_TEST( int aLimit ) : + VERTEX_SET( 0 ), m_limit( aLimit ) { }; @@ -129,12 +131,12 @@ public: m_vertices.front().updateList(); - Vertex* p = m_vertices.front().next; - std::set all_hits; + VERTEX* p = m_vertices.front().next; + std::set all_hits; while( p != &m_vertices.front() ) { - Vertex* match = nullptr; + VERTEX* match = nullptr; // Only run the expensive search if we don't already have a match for the point if( ( all_hits.empty() || all_hits.count( p ) == 0 ) && ( match = getKink( p ) ) != nullptr ) @@ -162,222 +164,6 @@ public: } -private: - struct Vertex - { - Vertex( int aIndex, double aX, double aY, POLYGON_TEST* aParent ) : - i( aIndex ), - x( aX ), - y( aY ), - parent( aParent ) - { - } - - Vertex& operator=( const Vertex& ) = delete; - Vertex& operator=( Vertex&& ) = delete; - - bool operator==( const Vertex& rhs ) const - { - return this->x == rhs.x && this->y == rhs.y; - } - bool operator!=( const Vertex& rhs ) const { return !( *this == rhs ); } - - /** - * Remove the node from the linked list and z-ordered linked list. - */ - void remove() - { - next->prev = prev; - prev->next = next; - - if( prevZ ) - prevZ->nextZ = nextZ; - - if( nextZ ) - nextZ->prevZ = prevZ; - - next = nullptr; - prev = nullptr; - nextZ = nullptr; - prevZ = nullptr; - } - - void updateOrder() - { - if( !z ) - z = parent->zOrder( x, y ); - } - - /** - * After inserting or changing nodes, this function should be called to - * remove duplicate vertices and ensure z-ordering is correct. - */ - void updateList() - { - Vertex* p = next; - - while( p != this ) - { - /** - * Remove duplicates - */ - if( *p == *p->next ) - { - p = p->prev; - p->next->remove(); - - if( p == p->next ) - break; - } - - p->updateOrder(); - p = p->next; - }; - - updateOrder(); - zSort(); - } - - /** - * Sort all vertices in this vertex's list by their Morton code. - */ - void zSort() - { - std::deque queue; - - queue.push_back( this ); - - for( Vertex* p = next; p && p != this; p = p->next ) - queue.push_back( p ); - - std::sort( queue.begin(), queue.end(), []( const Vertex* a, const Vertex* b ) - { - if( a->z != b->z ) - return a->z < b->z; - - if( a->x != b->x ) - return a->x < b->x; - - if( a->y != b->y ) - return a->y < b->y; - - return a->i < b->i; - } ); - - Vertex* prev_elem = nullptr; - - for( Vertex* elem : queue ) - { - if( prev_elem ) - prev_elem->nextZ = elem; - - elem->prevZ = prev_elem; - prev_elem = elem; - } - - prev_elem->nextZ = nullptr; - } - - const int i; - const double x; - const double y; - POLYGON_TEST* parent; - - // previous and next vertices nodes in a polygon ring - Vertex* prev = nullptr; - Vertex* next = nullptr; - - // z-order curve value - int32_t z = 0; - - // previous and next nodes in z-order - Vertex* prevZ = nullptr; - Vertex* nextZ = nullptr; - }; - - /** - * Calculate the Morton code of the Vertex - * http://www.graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN - * - */ - int32_t zOrder( const double aX, const double aY ) const - { - int32_t x = static_cast( 32767.0 * ( aX - m_bbox.GetX() ) / m_bbox.GetWidth() ); - int32_t y = static_cast( 32767.0 * ( aY - m_bbox.GetY() ) / m_bbox.GetHeight() ); - - x = ( x | ( x << 8 ) ) & 0x00FF00FF; - x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; - x = ( x | ( x << 2 ) ) & 0x33333333; - x = ( x | ( x << 1 ) ) & 0x55555555; - - y = ( y | ( y << 8 ) ) & 0x00FF00FF; - y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; - y = ( y | ( y << 2 ) ) & 0x33333333; - y = ( y | ( y << 1 ) ) & 0x55555555; - - return x | ( y << 1 ); - } - - constexpr bool same_point( const Vertex* aA, const Vertex* aB ) const - { - return aA && aB && aA->x == aB->x && aA->y == aB->y; - } - - Vertex* getNextOutlineVertex( const Vertex* aPt ) const - { - Vertex* nz = aPt->nextZ; - Vertex* pz = aPt->prevZ; - - // If we hit a fracture point, we want to continue around the - // edge we are working on and not switch to the pair edge - // However, this will depend on which direction the initial - // fracture hit is. If we find that we skip directly to - // a new fracture point, then we know that we are proceeding - // in the wrong direction from the fracture and should - // fall through to the next point - if( same_point( aPt, nz ) && same_point( aPt->next, nz->prev ) - && aPt->y == aPt->next->y ) - { - return nz->next; - } - - if( same_point( aPt, pz ) && same_point( aPt->next, pz->prev ) - && aPt->y == aPt->next->y ) - { - return pz->next; - } - - return aPt->next; - } - - Vertex* getPrevOutlineVertex( const Vertex* aPt ) const - { - Vertex* nz = aPt->nextZ; - Vertex* pz = aPt->prevZ; - - // If we hit a fracture point, we want to continue around the - // edge we are working on and not switch to the pair edge - // However, this will depend on which direction the initial - // fracture hit is. If we find that we skip directly to - // a new fracture point, then we know that we are proceeding - // in the wrong direction from the fracture and should - // fall through to the next point - if( same_point( aPt, nz ) - && aPt->y == aPt->prev->y) - { - return nz->prev; - } - - if( same_point( aPt, pz ) - && aPt->y == aPt->prev->y ) - { - return pz->prev; - } - - return aPt->prev; - - } - /** * Checks to see if there is a "substantial" protrusion in each polygon produced by the cut from * aA to aB. Substantial in this case means that the polygon bulges out to a wider cross-section @@ -386,7 +172,7 @@ private: * @param aB Ending point in the polygon * @return True if the two polygons are both "substantial" */ - bool isSubstantial( const Vertex* aA, const Vertex* aB ) const + bool isSubstantial( const VERTEX* aA, const VERTEX* aB ) const { bool x_change = false; bool y_change = false; @@ -396,8 +182,8 @@ private: size_t checked = 0; size_t total_pts = m_vertices.size(); - const Vertex* p0 = aA; - const Vertex* p = getNextOutlineVertex( p0 ); + const VERTEX* p0 = aA; + const VERTEX* p = getNextOutlineVertex( p0 ); while( !same_point( p, aB ) // We've reached the other inflection point && !same_point( p, aA ) // We've gone around in a circle @@ -458,43 +244,8 @@ private: return ( same_point( p, aA ) || ( x_change && y_change ) ); } - /** - * Take a #SHAPE_LINE_CHAIN and links each point into a circular, doubly-linked list. - */ - Vertex* createList( const SHAPE_LINE_CHAIN& points ) - { - Vertex* tail = nullptr; - double sum = 0.0; - // Check for winding order - for( int i = 0; i < points.PointCount(); i++ ) - { - VECTOR2D p1 = points.CPoint( i ); - VECTOR2D p2 = points.CPoint( i + 1 ); - - sum += ( ( p2.x - p1.x ) * ( p2.y + p1.y ) ); - } - - if( sum > 0.0 ) - { - for( int i = points.PointCount() - 1; i >= 0; i--) - tail = insertVertex( i, points.CPoint( i ), tail ); - } - else - { - for( int i = 0; i < points.PointCount(); i++ ) - tail = insertVertex( i, points.CPoint( i ), tail ); - } - - if( tail && ( *tail == *tail->next ) ) - { - tail->next->remove(); - } - - return tail; - } - - Vertex* getKink( Vertex* aPt ) const + VERTEX* getKink( VERTEX* aPt ) const { // The point needs to be at a concave surface if( locallyInside( aPt->prev, aPt->next ) ) @@ -506,9 +257,9 @@ private: const SEG::ecoord limit2 = SEG::Square( m_limit ); // first look for points in increasing z-order - Vertex* p = aPt->nextZ; + VERTEX* p = aPt->nextZ; SEG::ecoord min_dist = std::numeric_limits::max(); - Vertex* retval = nullptr; + VERTEX* retval = nullptr; while( p && p->z <= maxZ ) { @@ -546,67 +297,8 @@ private: return retval; } - - /** - * Return the twice the signed area of the triangle formed by vertices p, q, and r. - */ - double area( const Vertex* p, const Vertex* q, const Vertex* r ) const - { - return ( q->y - p->y ) * ( r->x - q->x ) - ( q->x - p->x ) * ( r->y - q->y ); - } - - - /** - * Check 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 immediately adjacent to vertex a. - * - * @return true if the segment from a->b is inside a's polygon next to vertex a. - */ - bool locallyInside( const Vertex* a, const Vertex* b ) const - { - const Vertex* an = getNextOutlineVertex( a ); - const Vertex* ap = getPrevOutlineVertex( a ); - - if( area( ap, a, an ) < 0 ) - return area( a, b, an ) >= 0 && area( a, ap, b ) >= 0; - else - return area( a, b, ap ) < 0 || area( a, an, b ) < 0; - } - - /** - * Create an entry in the vertices lookup and optionally inserts the newly created vertex - * into an existing linked list. - * - * @return a pointer to the newly created vertex. - */ - Vertex* insertVertex( int aIndex, const VECTOR2I& pt, Vertex* last ) - { - m_vertices.emplace_back( aIndex, pt.x, pt.y, this ); - - Vertex* p = &m_vertices.back(); - - if( !last ) - { - p->prev = p; - p->next = p; - } - else - { - p->next = last->next; - p->prev = last; - last->next->prev = p; - last->next = p; - } - return p; - } - private: int m_limit; - BOX2I m_bbox; - std::deque m_vertices; std::set> m_hits; };