Extract common code into VERTEX_SET mixin
The VERTEX_SET is useful when we need to quickly find elements that are close to each other. Extracting to a mixin keeps the code from diverging between implementations and simplifies that maintenance.
This commit is contained in:
parent
3c88b1ebc7
commit
4fff28220e
|
@ -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
|
||||
|
|
|
@ -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 <clipper.hpp>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
#include <geometry/shape_poly_set.h>
|
||||
#include <geometry/vertex_set.h>
|
||||
#include <math/box2.h>
|
||||
#include <math/vector2d.h>
|
||||
|
||||
#include <wx/log.h>
|
||||
|
||||
#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<VERTEX*> 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<int32_t>( 32767.0 * ( aX - m_bbox.GetX() ) / m_bbox.GetWidth() );
|
||||
int32_t y = static_cast<int32_t>( 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<int>::max(), std::numeric_limits<int>::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<VERTEX> m_vertices;
|
||||
size_t m_vertices_original_size;
|
||||
SHAPE_POLY_SET::TRIANGULATED_POLYGON& m_result;
|
||||
};
|
||||
|
|
|
@ -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 <algorithm>
|
||||
#include <deque>
|
||||
|
||||
#include <math/box2.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
|
||||
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<VERTEX> 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<VERTEX*> 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
|
|
@ -0,0 +1,206 @@
|
|||
#include <geometry/vertex_set.h>
|
||||
|
||||
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<int>::max(), std::numeric_limits<int>::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<int32_t>( 32767.0 * ( aX - m_bbox.GetX() ) / m_bbox.GetWidth() );
|
||||
int32_t y = static_cast<int32_t>( 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;
|
||||
}
|
|
@ -40,6 +40,7 @@
|
|||
#include <footprint.h>
|
||||
#include <geometry/seg.h>
|
||||
#include <geometry/shape_poly_set.h>
|
||||
#include <geometry/vertex_set.h>
|
||||
#include <math/box2.h>
|
||||
#include <math/vector2d.h>
|
||||
#include <pcb_shape.h>
|
||||
|
@ -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<Vertex*> all_hits;
|
||||
VERTEX* p = m_vertices.front().next;
|
||||
std::set<VERTEX*> 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<Vertex*> 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<int32_t>( 32767.0 * ( aX - m_bbox.GetX() ) / m_bbox.GetWidth() );
|
||||
int32_t y = static_cast<int32_t>( 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<SEG::ecoord>::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<Vertex> m_vertices;
|
||||
std::set<std::pair<int, int>> m_hits;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue