pcbnew: Optimized zone filling algorithm.
This commit is contained in:
parent
8b68a1736a
commit
316ddadec1
|
@ -478,6 +478,7 @@ include( CheckFindPackageResult )
|
||||||
find_package( OpenMP )
|
find_package( OpenMP )
|
||||||
|
|
||||||
if( OPENMP_FOUND )
|
if( OPENMP_FOUND )
|
||||||
|
message( "FOUND OPENMP" )
|
||||||
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}" )
|
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}" )
|
||||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}" )
|
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}" )
|
||||||
add_definitions( -DUSE_OPENMP )
|
add_definitions( -DUSE_OPENMP )
|
||||||
|
|
|
@ -179,6 +179,7 @@ set( COMMON_WIDGET_SRCS
|
||||||
widgets/indicator_icon.cpp
|
widgets/indicator_icon.cpp
|
||||||
widgets/text_ctrl_eval.cpp
|
widgets/text_ctrl_eval.cpp
|
||||||
widgets/unit_binder.cpp
|
widgets/unit_binder.cpp
|
||||||
|
widgets/progress_reporter.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set( COMMON_PAGE_LAYOUT_SRCS
|
set( COMMON_PAGE_LAYOUT_SRCS
|
||||||
|
@ -273,6 +274,7 @@ set( COMMON_SRCS
|
||||||
kiway_player.cpp
|
kiway_player.cpp
|
||||||
lib_table_base.cpp
|
lib_table_base.cpp
|
||||||
lockfile.cpp
|
lockfile.cpp
|
||||||
|
md5_hash.cpp
|
||||||
msgpanel.cpp
|
msgpanel.cpp
|
||||||
netlist_keywords.cpp
|
netlist_keywords.cpp
|
||||||
observable.cpp
|
observable.cpp
|
||||||
|
|
|
@ -776,9 +776,52 @@ void OPENGL_GAL::DrawPolygon( const VECTOR2D aPointList[], int aListSize )
|
||||||
drawPolygon( points.get(), aListSize );
|
drawPolygon( points.get(), aListSize );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OPENGL_GAL::drawTriangulatedPolyset( const SHAPE_POLY_SET& aPolySet )
|
||||||
|
{
|
||||||
|
currentManager->Shader( SHADER_NONE );
|
||||||
|
currentManager->Color( fillColor.r, fillColor.g, fillColor.b, fillColor.a );
|
||||||
|
|
||||||
|
|
||||||
|
if ( isFillEnabled )
|
||||||
|
{
|
||||||
|
for( int j = 0; j < aPolySet.OutlineCount(); ++j )
|
||||||
|
{
|
||||||
|
auto triPoly = aPolySet.TriangulatedPolygon( j );
|
||||||
|
|
||||||
|
for ( int i = 0; i < triPoly->m_triangleCount; i++ )
|
||||||
|
{
|
||||||
|
VECTOR2I a, b, c;
|
||||||
|
triPoly->GetTriangle( i ,a,b,c);
|
||||||
|
currentManager->Vertex( a.x, a.y, layerDepth );
|
||||||
|
currentManager->Vertex( b.x, b.y, layerDepth );
|
||||||
|
currentManager->Vertex( c.x, c.y, layerDepth );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( isStrokeEnabled )
|
||||||
|
{
|
||||||
|
for( int j = 0; j < aPolySet.OutlineCount(); ++j )
|
||||||
|
{
|
||||||
|
const auto& poly = aPolySet.Polygon( j );
|
||||||
|
|
||||||
|
for( const auto& lc : poly )
|
||||||
|
{
|
||||||
|
DrawPolyline( lc );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void OPENGL_GAL::DrawPolygon( const SHAPE_POLY_SET& aPolySet )
|
void OPENGL_GAL::DrawPolygon( const SHAPE_POLY_SET& aPolySet )
|
||||||
{
|
{
|
||||||
|
if ( aPolySet.IsTriangulationUpToDate() )
|
||||||
|
{
|
||||||
|
drawTriangulatedPolyset( aPolySet );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for( int j = 0; j < aPolySet.OutlineCount(); ++j )
|
for( int j = 0; j < aPolySet.OutlineCount(); ++j )
|
||||||
{
|
{
|
||||||
const SHAPE_LINE_CHAIN& outline = aPolySet.COutline( j );
|
const SHAPE_LINE_CHAIN& outline = aPolySet.COutline( j );
|
||||||
|
@ -1470,7 +1513,6 @@ void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRa
|
||||||
Restore();
|
Restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OPENGL_GAL::drawPolygon( GLdouble* aPoints, int aPointCount )
|
void OPENGL_GAL::drawPolygon( GLdouble* aPoints, int aPointCount )
|
||||||
{
|
{
|
||||||
currentManager->Shader( SHADER_NONE );
|
currentManager->Shader( SHADER_NONE );
|
||||||
|
|
|
@ -32,13 +32,18 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
|
#include <md5_hash.h>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include <geometry/shape.h>
|
#include <geometry/shape.h>
|
||||||
#include <geometry/shape_line_chain.h>
|
#include <geometry/shape_line_chain.h>
|
||||||
#include <geometry/shape_poly_set.h>
|
#include <geometry/shape_poly_set.h>
|
||||||
|
|
||||||
|
#include "poly2tri/poly2tri.h"
|
||||||
|
|
||||||
using namespace ClipperLib;
|
using namespace ClipperLib;
|
||||||
|
|
||||||
SHAPE_POLY_SET::SHAPE_POLY_SET() :
|
SHAPE_POLY_SET::SHAPE_POLY_SET() :
|
||||||
|
@ -55,6 +60,8 @@ SHAPE_POLY_SET::SHAPE_POLY_SET( const SHAPE_POLY_SET& aOther ) :
|
||||||
|
|
||||||
SHAPE_POLY_SET::~SHAPE_POLY_SET()
|
SHAPE_POLY_SET::~SHAPE_POLY_SET()
|
||||||
{
|
{
|
||||||
|
for( auto p : m_triangulatedPolys )
|
||||||
|
delete p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,8 +119,8 @@ bool SHAPE_POLY_SET::GetGlobalIndex( SHAPE_POLY_SET::VERTEX_INDEX aRelativeIndic
|
||||||
unsigned int selectedPolygon = aRelativeIndices.m_polygon;
|
unsigned int selectedPolygon = aRelativeIndices.m_polygon;
|
||||||
|
|
||||||
// Check whether the vertex indices make sense in this poly set
|
// Check whether the vertex indices make sense in this poly set
|
||||||
if( selectedPolygon < m_polys.size() && selectedContour < m_polys[selectedPolygon].size() &&
|
if( selectedPolygon < m_polys.size() && selectedContour < m_polys[selectedPolygon].size()
|
||||||
selectedVertex < m_polys[selectedPolygon][selectedContour].PointCount() )
|
&& selectedVertex < m_polys[selectedPolygon][selectedContour].PointCount() )
|
||||||
{
|
{
|
||||||
POLYGON currentPolygon;
|
POLYGON currentPolygon;
|
||||||
|
|
||||||
|
@ -151,6 +158,7 @@ int SHAPE_POLY_SET::NewOutline()
|
||||||
{
|
{
|
||||||
SHAPE_LINE_CHAIN empty_path;
|
SHAPE_LINE_CHAIN empty_path;
|
||||||
POLYGON poly;
|
POLYGON poly;
|
||||||
|
|
||||||
empty_path.SetClosed( true );
|
empty_path.SetClosed( true );
|
||||||
poly.push_back( empty_path );
|
poly.push_back( empty_path );
|
||||||
m_polys.push_back( poly );
|
m_polys.push_back( poly );
|
||||||
|
@ -161,6 +169,7 @@ int SHAPE_POLY_SET::NewOutline()
|
||||||
int SHAPE_POLY_SET::NewHole( int aOutline )
|
int SHAPE_POLY_SET::NewHole( int aOutline )
|
||||||
{
|
{
|
||||||
SHAPE_LINE_CHAIN empty_path;
|
SHAPE_LINE_CHAIN empty_path;
|
||||||
|
|
||||||
empty_path.SetClosed( true );
|
empty_path.SetClosed( true );
|
||||||
|
|
||||||
// Default outline is the last one
|
// Default outline is the last one
|
||||||
|
@ -214,7 +223,6 @@ void SHAPE_POLY_SET::InsertVertex( int aGlobalIndex, VECTOR2I aNewVertex )
|
||||||
else
|
else
|
||||||
throw( std::out_of_range( "aGlobalIndex-th vertex does not exist" ) );
|
throw( std::out_of_range( "aGlobalIndex-th vertex does not exist" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -331,6 +339,7 @@ const VECTOR2I& SHAPE_POLY_SET::CVertex( SHAPE_POLY_SET::VERTEX_INDEX index ) co
|
||||||
return CVertex( index.m_vertex, index.m_polygon, index.m_contour - 1 );
|
return CVertex( index.m_vertex, index.m_polygon, index.m_contour - 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SHAPE_POLY_SET::GetNeighbourIndexes( int aGlobalIndex, int* aPrevious, int* aNext )
|
bool SHAPE_POLY_SET::GetNeighbourIndexes( int aGlobalIndex, int* aPrevious, int* aNext )
|
||||||
{
|
{
|
||||||
SHAPE_POLY_SET::VERTEX_INDEX index;
|
SHAPE_POLY_SET::VERTEX_INDEX index;
|
||||||
|
@ -451,7 +460,8 @@ int SHAPE_POLY_SET::AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const Path SHAPE_POLY_SET::convertToClipper( const SHAPE_LINE_CHAIN& aPath, bool aRequiredOrientation )
|
const Path SHAPE_POLY_SET::convertToClipper( const SHAPE_LINE_CHAIN& aPath,
|
||||||
|
bool aRequiredOrientation )
|
||||||
{
|
{
|
||||||
Path c_path;
|
Path c_path;
|
||||||
|
|
||||||
|
@ -557,19 +567,25 @@ void SHAPE_POLY_SET::BooleanIntersection( const SHAPE_POLY_SET& b, POLYGON_MODE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SHAPE_POLY_SET::BooleanAdd( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode )
|
void SHAPE_POLY_SET::BooleanAdd( const SHAPE_POLY_SET& a,
|
||||||
|
const SHAPE_POLY_SET& b,
|
||||||
|
POLYGON_MODE aFastMode )
|
||||||
{
|
{
|
||||||
booleanOp( ctUnion, a, b, aFastMode );
|
booleanOp( ctUnion, a, b, aFastMode );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SHAPE_POLY_SET::BooleanSubtract( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode )
|
void SHAPE_POLY_SET::BooleanSubtract( const SHAPE_POLY_SET& a,
|
||||||
|
const SHAPE_POLY_SET& b,
|
||||||
|
POLYGON_MODE aFastMode )
|
||||||
{
|
{
|
||||||
booleanOp( ctDifference, a, b, aFastMode );
|
booleanOp( ctDifference, a, b, aFastMode );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SHAPE_POLY_SET::BooleanIntersection( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode )
|
void SHAPE_POLY_SET::BooleanIntersection( const SHAPE_POLY_SET& a,
|
||||||
|
const SHAPE_POLY_SET& b,
|
||||||
|
POLYGON_MODE aFastMode )
|
||||||
{
|
{
|
||||||
booleanOp( ctIntersection, a, b, aFastMode );
|
booleanOp( ctIntersection, a, b, aFastMode );
|
||||||
}
|
}
|
||||||
|
@ -588,7 +604,8 @@ void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount )
|
||||||
for( const POLYGON& poly : m_polys )
|
for( const POLYGON& poly : m_polys )
|
||||||
{
|
{
|
||||||
for( unsigned int i = 0; i < poly.size(); i++ )
|
for( unsigned int i = 0; i < poly.size(); i++ )
|
||||||
c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), jtRound, etClosedPolygon );
|
c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), jtRound,
|
||||||
|
etClosedPolygon );
|
||||||
}
|
}
|
||||||
|
|
||||||
PolyTree solution;
|
PolyTree solution;
|
||||||
|
@ -641,6 +658,7 @@ void SHAPE_POLY_SET::importTree( PolyTree* tree )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Polygon fracturing code. Work in progress.
|
// Polygon fracturing code. Work in progress.
|
||||||
|
|
||||||
struct FractureEdge
|
struct FractureEdge
|
||||||
|
@ -704,7 +722,8 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
|
||||||
if( (*i)->m_p1.y == (*i)->m_p2.y ) // horizontal edge
|
if( (*i)->m_p1.y == (*i)->m_p2.y ) // horizontal edge
|
||||||
x_intersect = std::max( (*i)->m_p1.x, (*i)->m_p2.x );
|
x_intersect = std::max( (*i)->m_p1.x, (*i)->m_p2.x );
|
||||||
else
|
else
|
||||||
x_intersect = (*i)->m_p1.x + rescale((*i)->m_p2.x - (*i)->m_p1.x, y - (*i)->m_p1.y, (*i)->m_p2.y - (*i)->m_p1.y );
|
x_intersect = (*i)->m_p1.x + rescale( (*i)->m_p2.x - (*i)->m_p1.x, y - (*i)->m_p1.y,
|
||||||
|
(*i)->m_p2.y - (*i)->m_p1.y );
|
||||||
|
|
||||||
int dist = ( x - x_intersect );
|
int dist = ( x - x_intersect );
|
||||||
|
|
||||||
|
@ -720,9 +739,12 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
FractureEdge* lead1 = new FractureEdge( true, VECTOR2I( x_nearest, y ), VECTOR2I( x, y ) );
|
FractureEdge* lead1 =
|
||||||
FractureEdge* lead2 = new FractureEdge( true, VECTOR2I( x, y ), VECTOR2I( x_nearest, y ) );
|
new FractureEdge( true, VECTOR2I( x_nearest, y ), VECTOR2I( x, y ) );
|
||||||
FractureEdge* split_2 = new FractureEdge( true, VECTOR2I( x_nearest, y ), e_nearest->m_p2 );
|
FractureEdge* lead2 =
|
||||||
|
new FractureEdge( true, VECTOR2I( x, y ), VECTOR2I( x_nearest, y ) );
|
||||||
|
FractureEdge* split_2 =
|
||||||
|
new FractureEdge( true, VECTOR2I( x_nearest, y ), e_nearest->m_p2 );
|
||||||
|
|
||||||
edges.push_back( split_2 );
|
edges.push_back( split_2 );
|
||||||
edges.push_back( lead1 );
|
edges.push_back( lead1 );
|
||||||
|
@ -735,6 +757,7 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
|
||||||
lead1->m_next = edge;
|
lead1->m_next = edge;
|
||||||
|
|
||||||
FractureEdge* last;
|
FractureEdge* last;
|
||||||
|
|
||||||
for( last = edge; last->m_next != edge; last = last->m_next )
|
for( last = edge; last->m_next != edge; last = last->m_next )
|
||||||
{
|
{
|
||||||
last->m_connected = true;
|
last->m_connected = true;
|
||||||
|
@ -810,6 +833,7 @@ void SHAPE_POLY_SET::fractureSingle( POLYGON& paths )
|
||||||
if( !fe->m_connected )
|
if( !fe->m_connected )
|
||||||
num_unconnected++;
|
num_unconnected++;
|
||||||
}
|
}
|
||||||
|
|
||||||
first = false; // first path is always the outline
|
first = false; // first path is always the outline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -865,6 +889,169 @@ void SHAPE_POLY_SET::Fracture( POLYGON_MODE aFastMode )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SHAPE_POLY_SET::unfractureSingle( SHAPE_POLY_SET::POLYGON& aPoly )
|
||||||
|
{
|
||||||
|
assert( aPoly.size() == 1 );
|
||||||
|
|
||||||
|
struct EDGE
|
||||||
|
{
|
||||||
|
int m_index = 0;
|
||||||
|
SHAPE_LINE_CHAIN* m_poly = nullptr;
|
||||||
|
bool m_duplicate = false;
|
||||||
|
|
||||||
|
EDGE( SHAPE_LINE_CHAIN* aPolygon, int aIndex ) :
|
||||||
|
m_index( aIndex ),
|
||||||
|
m_poly( aPolygon )
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool compareSegs( const SEG& s1, const SEG& s2 ) const
|
||||||
|
{
|
||||||
|
return (s1.A == s2.A && s1.B == s2.B) || (s1.A == s2.B && s1.B == s2.A);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==( const EDGE& aOther ) const
|
||||||
|
{
|
||||||
|
return compareSegs( m_poly->CSegment( m_index ),
|
||||||
|
aOther.m_poly->CSegment( aOther.m_index ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=( const EDGE& aOther ) const
|
||||||
|
{
|
||||||
|
return !compareSegs( m_poly->CSegment( m_index ),
|
||||||
|
aOther.m_poly->CSegment( aOther.m_index ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HASH
|
||||||
|
{
|
||||||
|
std::size_t operator()( const EDGE& aEdge ) const
|
||||||
|
{
|
||||||
|
const auto& a = aEdge.m_poly->CSegment( aEdge.m_index );
|
||||||
|
|
||||||
|
return (std::size_t) ( a.A.x + a.B.x + a.A.y + a.B.y );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EDGE_LIST_ENTRY
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
EDGE_LIST_ENTRY* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_set<EDGE, EDGE::HASH> uniqueEdges;
|
||||||
|
|
||||||
|
auto lc = aPoly[0];
|
||||||
|
lc.Simplify();
|
||||||
|
|
||||||
|
EDGE_LIST_ENTRY edgeList[ lc.SegmentCount() ];
|
||||||
|
|
||||||
|
for( int i = 0; i < lc.SegmentCount(); i++ )
|
||||||
|
{
|
||||||
|
edgeList[i].index = i;
|
||||||
|
edgeList[i].next = &edgeList[ (i != lc.SegmentCount() - 1) ? i + 1 : 0 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<EDGE_LIST_ENTRY*> queue;
|
||||||
|
|
||||||
|
for( int i = 0; i < lc.SegmentCount(); i++ )
|
||||||
|
{
|
||||||
|
EDGE e( &lc, i );
|
||||||
|
uniqueEdges.insert( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
for( int i = 0; i < lc.SegmentCount(); i++ )
|
||||||
|
{
|
||||||
|
EDGE e( &lc, i );
|
||||||
|
auto it = uniqueEdges.find( e );
|
||||||
|
|
||||||
|
if( it != uniqueEdges.end() && it->m_index != i )
|
||||||
|
{
|
||||||
|
int e1 = it->m_index;
|
||||||
|
int e2 = i;
|
||||||
|
|
||||||
|
if( e1 > e2 )
|
||||||
|
std::swap( e1, e2 );
|
||||||
|
|
||||||
|
int e1_prev = e1 - 1;
|
||||||
|
|
||||||
|
if( e1_prev < 0 )
|
||||||
|
e1_prev = lc.SegmentCount() - 1;
|
||||||
|
|
||||||
|
int e2_prev = e2 - 1;
|
||||||
|
|
||||||
|
if( e2_prev < 0 )
|
||||||
|
e2_prev = lc.SegmentCount() - 1;
|
||||||
|
|
||||||
|
int e1_next = e1 + 1;
|
||||||
|
|
||||||
|
if( e1_next == lc.SegmentCount() )
|
||||||
|
e1_next = 0;
|
||||||
|
|
||||||
|
int e2_next = e2 + 1;
|
||||||
|
|
||||||
|
if( e2_next == lc.SegmentCount() )
|
||||||
|
e2_next = 0;
|
||||||
|
|
||||||
|
edgeList[e1_prev].next = &edgeList[ e2_next ];
|
||||||
|
edgeList[e2_prev].next = &edgeList[ e1_next ];
|
||||||
|
edgeList[i].next = nullptr;
|
||||||
|
edgeList[it->m_index].next = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for( int i = 0; i < lc.SegmentCount(); i++ )
|
||||||
|
{
|
||||||
|
if( edgeList[i].next )
|
||||||
|
queue.insert( &edgeList[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
EDGE_LIST_ENTRY* edgeBuf[ lc.SegmentCount() ];
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
int outline = -1;
|
||||||
|
|
||||||
|
POLYGON result;
|
||||||
|
|
||||||
|
while( queue.size() )
|
||||||
|
{
|
||||||
|
auto e_first = (*queue.begin() );
|
||||||
|
auto e = e_first;
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
edgeBuf[cnt++] = e;
|
||||||
|
e = e->next;
|
||||||
|
} while( e != e_first );
|
||||||
|
|
||||||
|
SHAPE_LINE_CHAIN outl;
|
||||||
|
|
||||||
|
for( int i = 0; i < cnt; i++ )
|
||||||
|
{
|
||||||
|
auto p = lc.CPoint( edgeBuf[i]->index );
|
||||||
|
outl.Append( p );
|
||||||
|
queue.erase( edgeBuf[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
outl.SetClosed( true );
|
||||||
|
|
||||||
|
bool cw = outl.Area() > 0.0;
|
||||||
|
|
||||||
|
if( cw )
|
||||||
|
outline = n;
|
||||||
|
|
||||||
|
result.push_back( outl );
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( outline >= 0 );
|
||||||
|
|
||||||
|
if( outline !=0 )
|
||||||
|
std::swap( result[0], result[outline] );
|
||||||
|
|
||||||
|
aPoly = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SHAPE_POLY_SET::HasHoles() const
|
bool SHAPE_POLY_SET::HasHoles() const
|
||||||
{
|
{
|
||||||
// Iterate through all the polygons on the set
|
// Iterate through all the polygons on the set
|
||||||
|
@ -880,6 +1067,17 @@ bool SHAPE_POLY_SET::HasHoles() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SHAPE_POLY_SET::Unfracture( POLYGON_MODE aFastMode )
|
||||||
|
{
|
||||||
|
for( POLYGON& path : m_polys )
|
||||||
|
{
|
||||||
|
unfractureSingle( path );
|
||||||
|
}
|
||||||
|
|
||||||
|
Simplify( aFastMode ); // remove overlapping holes/degeneracy
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SHAPE_POLY_SET::Simplify( POLYGON_MODE aFastMode )
|
void SHAPE_POLY_SET::Simplify( POLYGON_MODE aFastMode )
|
||||||
{
|
{
|
||||||
SHAPE_POLY_SET empty;
|
SHAPE_POLY_SET empty;
|
||||||
|
@ -930,12 +1128,15 @@ const std::string SHAPE_POLY_SET::Format() const
|
||||||
for( unsigned i = 0; i < m_polys.size(); i++ )
|
for( unsigned i = 0; i < m_polys.size(); i++ )
|
||||||
{
|
{
|
||||||
ss << "poly " << m_polys[i].size() << "\n";
|
ss << "poly " << m_polys[i].size() << "\n";
|
||||||
|
|
||||||
for( unsigned j = 0; j < m_polys[i].size(); j++ )
|
for( unsigned j = 0; j < m_polys[i].size(); j++ )
|
||||||
{
|
{
|
||||||
ss << m_polys[i][j].PointCount() << "\n";
|
ss << m_polys[i][j].PointCount() << "\n";
|
||||||
|
|
||||||
for( int v = 0; v < m_polys[i][j].PointCount(); v++ )
|
for( int v = 0; v < m_polys[i][j].PointCount(); v++ )
|
||||||
ss << m_polys[i][j].CPoint( v ).x << " " << m_polys[i][j].CPoint( v ).y << "\n";
|
ss << m_polys[i][j].CPoint( v ).x << " " << m_polys[i][j].CPoint( v ).y << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
ss << "\n";
|
ss << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -982,6 +1183,7 @@ bool SHAPE_POLY_SET::Parse( std::stringstream& aStream )
|
||||||
|
|
||||||
aStream >> tmp;
|
aStream >> tmp;
|
||||||
int n_vertices = atoi( tmp.c_str() );
|
int n_vertices = atoi( tmp.c_str() );
|
||||||
|
|
||||||
for( int v = 0; v < n_vertices; v++ )
|
for( int v = 0; v < n_vertices; v++ )
|
||||||
{
|
{
|
||||||
VECTOR2I p;
|
VECTOR2I p;
|
||||||
|
@ -996,6 +1198,7 @@ bool SHAPE_POLY_SET::Parse( std::stringstream& aStream )
|
||||||
|
|
||||||
m_polys.push_back( paths );
|
m_polys.push_back( paths );
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1222,7 +1425,6 @@ bool SHAPE_POLY_SET::Contains( const VECTOR2I& aP, int aSubpolyIndex ) const
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1253,6 +1455,7 @@ bool SHAPE_POLY_SET::containsSingle( const VECTOR2I& aP, int aSubpolyIndex ) con
|
||||||
for( int holeIdx = 0; holeIdx < HoleCount( aSubpolyIndex ); holeIdx++ )
|
for( int holeIdx = 0; holeIdx < HoleCount( aSubpolyIndex ); holeIdx++ )
|
||||||
{
|
{
|
||||||
const SHAPE_LINE_CHAIN hole = CHole( aSubpolyIndex, holeIdx );
|
const SHAPE_LINE_CHAIN hole = CHole( aSubpolyIndex, holeIdx );
|
||||||
|
|
||||||
// If the point is inside a hole (and not on its edge),
|
// If the point is inside a hole (and not on its edge),
|
||||||
// it is outside of the polygon
|
// it is outside of the polygon
|
||||||
if( pointInPolygon( aP, hole ) && !hole.PointOnEdge( aP ) )
|
if( pointInPolygon( aP, hole ) && !hole.PointOnEdge( aP ) )
|
||||||
|
@ -1285,8 +1488,8 @@ bool SHAPE_POLY_SET::pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN&
|
||||||
|
|
||||||
if( ipNext.y == aP.y )
|
if( ipNext.y == aP.y )
|
||||||
{
|
{
|
||||||
if( ( ipNext.x == aP.x ) || ( ip.y == aP.y &&
|
if( ( ipNext.x == aP.x ) || ( ip.y == aP.y
|
||||||
( ( ipNext.x > aP.x ) == ( ip.x < aP.x ) ) ) )
|
&& ( ( ipNext.x > aP.x ) == ( ip.x < aP.x ) ) ) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1667,3 +1870,191 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode,
|
||||||
|
|
||||||
return newPoly;
|
return newPoly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef std::map<p2t::Point*, int> P2T_MAP;
|
||||||
|
typedef std::vector<p2t::Point*> P2T_VEC;
|
||||||
|
|
||||||
|
static void convert( const SHAPE_LINE_CHAIN& outl,
|
||||||
|
P2T_VEC& buffer,
|
||||||
|
P2T_MAP& pointMap,
|
||||||
|
SHAPE_POLY_SET::TRIANGULATED_POLYGON& aPoly )
|
||||||
|
{
|
||||||
|
buffer.clear();
|
||||||
|
|
||||||
|
for( int i = 0; i < outl.PointCount(); i++ )
|
||||||
|
{
|
||||||
|
const auto& p = outl.CPoint( i );
|
||||||
|
auto p2 = new p2t::Point( p.x, p.y );
|
||||||
|
pointMap[p2] = aPoly.AddVertex( p );
|
||||||
|
buffer.push_back( p2 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SHAPE_POLY_SET::TRIANGULATED_POLYGON::~TRIANGULATED_POLYGON()
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SHAPE_POLY_SET::TRIANGULATED_POLYGON::Clear()
|
||||||
|
{
|
||||||
|
if( m_vertices )
|
||||||
|
delete[] m_vertices;
|
||||||
|
|
||||||
|
if( m_triangles )
|
||||||
|
delete[] m_triangles;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SHAPE_POLY_SET::TRIANGULATED_POLYGON::AllocateVertices( int aSize )
|
||||||
|
{
|
||||||
|
m_vertices = new VECTOR2I[aSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SHAPE_POLY_SET::TRIANGULATED_POLYGON::AllocateTriangles( int aSize )
|
||||||
|
{
|
||||||
|
m_triangles = new TRI[aSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int totalVertexCount( const SHAPE_POLY_SET::POLYGON& aPoly )
|
||||||
|
{
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
for( const auto& outl : aPoly )
|
||||||
|
{
|
||||||
|
cnt += outl.PointCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SHAPE_POLY_SET::triangulateSingle( const POLYGON& aPoly,
|
||||||
|
SHAPE_POLY_SET::TRIANGULATED_POLYGON& aResult )
|
||||||
|
{
|
||||||
|
assert( aPoly.size() >= 1 );
|
||||||
|
|
||||||
|
P2T_MAP pointMap;
|
||||||
|
P2T_VEC outline;
|
||||||
|
|
||||||
|
aResult.AllocateVertices( totalVertexCount( aPoly ) );
|
||||||
|
|
||||||
|
convert( aPoly[0], outline, pointMap, aResult );
|
||||||
|
|
||||||
|
std::unique_ptr<p2t::CDT> cdt( new p2t::CDT( outline ) );
|
||||||
|
|
||||||
|
for( int i = 1; i < aPoly.size(); i++ )
|
||||||
|
{
|
||||||
|
std::vector<p2t::Point*> hole;
|
||||||
|
|
||||||
|
convert( aPoly[i], hole, pointMap, aResult );
|
||||||
|
|
||||||
|
cdt->AddHole( hole );
|
||||||
|
}
|
||||||
|
|
||||||
|
cdt->Triangulate();
|
||||||
|
|
||||||
|
aResult.AllocateTriangles( cdt->GetTriangles().size() );
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for( auto tri : cdt->GetTriangles() )
|
||||||
|
{
|
||||||
|
TRIANGULATED_POLYGON::TRI t;
|
||||||
|
|
||||||
|
t.a = pointMap[ tri->GetPoint( 0 ) ];
|
||||||
|
t.b = pointMap[ tri->GetPoint( 1 ) ];
|
||||||
|
t.c = pointMap[ tri->GetPoint( 2 ) ];
|
||||||
|
|
||||||
|
aResult.m_triangles[ i ] = t;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for( auto iter = pointMap.begin(); iter!=pointMap.end(); ++iter )
|
||||||
|
delete iter->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SHAPE_POLY_SET::IsTriangulationUpToDate() const
|
||||||
|
{
|
||||||
|
if( !m_triangulationValid )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( !m_hash.IsValid() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto hash = checksum();
|
||||||
|
|
||||||
|
return hash == m_hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SHAPE_POLY_SET::CacheTriangulation()
|
||||||
|
{
|
||||||
|
bool recalculate = !m_hash.IsValid();
|
||||||
|
MD5_HASH hash;
|
||||||
|
|
||||||
|
if( !m_triangulationValid )
|
||||||
|
recalculate = true;
|
||||||
|
|
||||||
|
if( !recalculate )
|
||||||
|
{
|
||||||
|
hash = checksum();
|
||||||
|
|
||||||
|
if( m_hash != hash )
|
||||||
|
{
|
||||||
|
m_hash = hash;
|
||||||
|
recalculate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !recalculate )
|
||||||
|
return;
|
||||||
|
|
||||||
|
SHAPE_POLY_SET tmpSet = *this;
|
||||||
|
tmpSet.Unfracture( PM_FAST );
|
||||||
|
|
||||||
|
m_triangulatedPolys.clear();
|
||||||
|
|
||||||
|
for( int i = 0; i < tmpSet.OutlineCount(); i++ )
|
||||||
|
{
|
||||||
|
auto p = new TRIANGULATED_POLYGON();
|
||||||
|
m_triangulatedPolys.push_back( p );
|
||||||
|
triangulateSingle( tmpSet.Polygon( i ), *p );
|
||||||
|
}
|
||||||
|
|
||||||
|
m_triangulationValid = true;
|
||||||
|
m_hash = checksum();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MD5_HASH SHAPE_POLY_SET::checksum() const
|
||||||
|
{
|
||||||
|
MD5_HASH hash;
|
||||||
|
|
||||||
|
hash.Hash( m_polys.size() );
|
||||||
|
|
||||||
|
for( const auto& outline : m_polys )
|
||||||
|
{
|
||||||
|
hash.Hash( outline.size() );
|
||||||
|
|
||||||
|
for( const auto& lc : outline )
|
||||||
|
{
|
||||||
|
hash.Hash( lc.PointCount() );
|
||||||
|
|
||||||
|
for( int i = 0; i < lc.PointCount(); i++ )
|
||||||
|
{
|
||||||
|
hash.Hash( lc.CPoint( i ).x );
|
||||||
|
hash.Hash( lc.CPoint( i ).y );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hash.Finalize();
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,250 @@
|
||||||
|
// Code by: B-Con (http://b-con.us)
|
||||||
|
// Released under the GNU GPL
|
||||||
|
// MD5 Hash Digest implementation (little endian byte order)
|
||||||
|
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include <md5_hash.h>
|
||||||
|
|
||||||
|
// DBL_INT_ADD treats two unsigned ints a and b as one 64-bit integer and adds c to it
|
||||||
|
#define DBL_INT_ADD(a,b,c) if (a > 0xffffffff - c) ++b; a += c;
|
||||||
|
#define ROTLEFT(a,b) ((a << b) | (a >> (32-b)))
|
||||||
|
|
||||||
|
#define F(x,y,z) ((x & y) | (~x & z))
|
||||||
|
#define G(x,y,z) ((x & z) | (y & ~z))
|
||||||
|
#define H(x,y,z) (x ^ y ^ z)
|
||||||
|
#define I(x,y,z) (y ^ (x | ~z))
|
||||||
|
|
||||||
|
#define FF(a,b,c,d,m,s,t) { a += F(b,c,d) + m + t; \
|
||||||
|
a = b + ROTLEFT(a,s); }
|
||||||
|
#define GG(a,b,c,d,m,s,t) { a += G(b,c,d) + m + t; \
|
||||||
|
a = b + ROTLEFT(a,s); }
|
||||||
|
#define HH(a,b,c,d,m,s,t) { a += H(b,c,d) + m + t; \
|
||||||
|
a = b + ROTLEFT(a,s); }
|
||||||
|
#define II(a,b,c,d,m,s,t) { a += I(b,c,d) + m + t; \
|
||||||
|
a = b + ROTLEFT(a,s); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MD5_HASH::MD5_HASH()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
MD5_HASH::MD5_HASH( const MD5_HASH& aOther )
|
||||||
|
{
|
||||||
|
m_valid = aOther.m_valid;
|
||||||
|
m_ctx = aOther.m_ctx;
|
||||||
|
memcpy( m_hash, aOther.m_hash, 16 );
|
||||||
|
}
|
||||||
|
|
||||||
|
MD5_HASH::~MD5_HASH()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MD5_HASH& MD5_HASH::operator=( const MD5_HASH& aOther )
|
||||||
|
{
|
||||||
|
m_valid = aOther.m_valid;
|
||||||
|
m_ctx = aOther.m_ctx;
|
||||||
|
memcpy( m_hash, aOther.m_hash, 16 );
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MD5_HASH::Init()
|
||||||
|
{
|
||||||
|
//printf("%p init\n", this);
|
||||||
|
m_valid = false;
|
||||||
|
md5_init(&m_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5_HASH::Hash ( uint8_t *data, uint32_t length )
|
||||||
|
{
|
||||||
|
md5_update(&m_ctx, data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5_HASH::Hash ( int value )
|
||||||
|
{
|
||||||
|
md5_update(&m_ctx, (uint8_t*) &value, sizeof(int) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5_HASH::Finalize()
|
||||||
|
{
|
||||||
|
//printf("%p final\n", this);
|
||||||
|
md5_final(&m_ctx, m_hash);
|
||||||
|
m_valid = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MD5_HASH::operator==( const MD5_HASH& aOther ) const
|
||||||
|
{
|
||||||
|
return ( memcmp( m_hash, aOther.m_hash, 16 ) == 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MD5_HASH::operator!=( const MD5_HASH& aOther ) const
|
||||||
|
{
|
||||||
|
return ( memcmp( m_hash, aOther.m_hash, 16 ) != 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MD5_HASH::md5_transform(MD5_CTX *ctx, uint8_t data[])
|
||||||
|
{
|
||||||
|
uint32_t a,b,c,d,m[16],i,j;
|
||||||
|
|
||||||
|
// MD5 specifies big endian byte order, but this implementation assumes a little
|
||||||
|
// endian byte order CPU. Reverse all the bytes upon input, and re-reverse them
|
||||||
|
// on output (in md5_final()).
|
||||||
|
for (i=0,j=0; i < 16; ++i, j += 4)
|
||||||
|
m[i] = (data[j]) + (data[j+1] << 8) + (data[j+2] << 16) + (data[j+3] << 24);
|
||||||
|
|
||||||
|
a = ctx->state[0];
|
||||||
|
b = ctx->state[1];
|
||||||
|
c = ctx->state[2];
|
||||||
|
d = ctx->state[3];
|
||||||
|
|
||||||
|
FF(a,b,c,d,m[0], 7,0xd76aa478);
|
||||||
|
FF(d,a,b,c,m[1], 12,0xe8c7b756);
|
||||||
|
FF(c,d,a,b,m[2], 17,0x242070db);
|
||||||
|
FF(b,c,d,a,m[3], 22,0xc1bdceee);
|
||||||
|
FF(a,b,c,d,m[4], 7,0xf57c0faf);
|
||||||
|
FF(d,a,b,c,m[5], 12,0x4787c62a);
|
||||||
|
FF(c,d,a,b,m[6], 17,0xa8304613);
|
||||||
|
FF(b,c,d,a,m[7], 22,0xfd469501);
|
||||||
|
FF(a,b,c,d,m[8], 7,0x698098d8);
|
||||||
|
FF(d,a,b,c,m[9], 12,0x8b44f7af);
|
||||||
|
FF(c,d,a,b,m[10],17,0xffff5bb1);
|
||||||
|
FF(b,c,d,a,m[11],22,0x895cd7be);
|
||||||
|
FF(a,b,c,d,m[12], 7,0x6b901122);
|
||||||
|
FF(d,a,b,c,m[13],12,0xfd987193);
|
||||||
|
FF(c,d,a,b,m[14],17,0xa679438e);
|
||||||
|
FF(b,c,d,a,m[15],22,0x49b40821);
|
||||||
|
|
||||||
|
GG(a,b,c,d,m[1], 5,0xf61e2562);
|
||||||
|
GG(d,a,b,c,m[6], 9,0xc040b340);
|
||||||
|
GG(c,d,a,b,m[11],14,0x265e5a51);
|
||||||
|
GG(b,c,d,a,m[0], 20,0xe9b6c7aa);
|
||||||
|
GG(a,b,c,d,m[5], 5,0xd62f105d);
|
||||||
|
GG(d,a,b,c,m[10], 9,0x02441453);
|
||||||
|
GG(c,d,a,b,m[15],14,0xd8a1e681);
|
||||||
|
GG(b,c,d,a,m[4], 20,0xe7d3fbc8);
|
||||||
|
GG(a,b,c,d,m[9], 5,0x21e1cde6);
|
||||||
|
GG(d,a,b,c,m[14], 9,0xc33707d6);
|
||||||
|
GG(c,d,a,b,m[3], 14,0xf4d50d87);
|
||||||
|
GG(b,c,d,a,m[8], 20,0x455a14ed);
|
||||||
|
GG(a,b,c,d,m[13], 5,0xa9e3e905);
|
||||||
|
GG(d,a,b,c,m[2], 9,0xfcefa3f8);
|
||||||
|
GG(c,d,a,b,m[7], 14,0x676f02d9);
|
||||||
|
GG(b,c,d,a,m[12],20,0x8d2a4c8a);
|
||||||
|
|
||||||
|
HH(a,b,c,d,m[5], 4,0xfffa3942);
|
||||||
|
HH(d,a,b,c,m[8], 11,0x8771f681);
|
||||||
|
HH(c,d,a,b,m[11],16,0x6d9d6122);
|
||||||
|
HH(b,c,d,a,m[14],23,0xfde5380c);
|
||||||
|
HH(a,b,c,d,m[1], 4,0xa4beea44);
|
||||||
|
HH(d,a,b,c,m[4], 11,0x4bdecfa9);
|
||||||
|
HH(c,d,a,b,m[7], 16,0xf6bb4b60);
|
||||||
|
HH(b,c,d,a,m[10],23,0xbebfbc70);
|
||||||
|
HH(a,b,c,d,m[13], 4,0x289b7ec6);
|
||||||
|
HH(d,a,b,c,m[0], 11,0xeaa127fa);
|
||||||
|
HH(c,d,a,b,m[3], 16,0xd4ef3085);
|
||||||
|
HH(b,c,d,a,m[6], 23,0x04881d05);
|
||||||
|
HH(a,b,c,d,m[9], 4,0xd9d4d039);
|
||||||
|
HH(d,a,b,c,m[12],11,0xe6db99e5);
|
||||||
|
HH(c,d,a,b,m[15],16,0x1fa27cf8);
|
||||||
|
HH(b,c,d,a,m[2], 23,0xc4ac5665);
|
||||||
|
|
||||||
|
II(a,b,c,d,m[0], 6,0xf4292244);
|
||||||
|
II(d,a,b,c,m[7], 10,0x432aff97);
|
||||||
|
II(c,d,a,b,m[14],15,0xab9423a7);
|
||||||
|
II(b,c,d,a,m[5], 21,0xfc93a039);
|
||||||
|
II(a,b,c,d,m[12], 6,0x655b59c3);
|
||||||
|
II(d,a,b,c,m[3], 10,0x8f0ccc92);
|
||||||
|
II(c,d,a,b,m[10],15,0xffeff47d);
|
||||||
|
II(b,c,d,a,m[1], 21,0x85845dd1);
|
||||||
|
II(a,b,c,d,m[8], 6,0x6fa87e4f);
|
||||||
|
II(d,a,b,c,m[15],10,0xfe2ce6e0);
|
||||||
|
II(c,d,a,b,m[6], 15,0xa3014314);
|
||||||
|
II(b,c,d,a,m[13],21,0x4e0811a1);
|
||||||
|
II(a,b,c,d,m[4], 6,0xf7537e82);
|
||||||
|
II(d,a,b,c,m[11],10,0xbd3af235);
|
||||||
|
II(c,d,a,b,m[2], 15,0x2ad7d2bb);
|
||||||
|
II(b,c,d,a,m[9], 21,0xeb86d391);
|
||||||
|
|
||||||
|
ctx->state[0] += a;
|
||||||
|
ctx->state[1] += b;
|
||||||
|
ctx->state[2] += c;
|
||||||
|
ctx->state[3] += d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5_HASH::md5_init(MD5_CTX *ctx)
|
||||||
|
{
|
||||||
|
ctx->datalen = 0;
|
||||||
|
ctx->bitlen[0] = 0;
|
||||||
|
ctx->bitlen[1] = 0;
|
||||||
|
ctx->state[0] = 0x67452301;
|
||||||
|
ctx->state[1] = 0xEFCDAB89;
|
||||||
|
ctx->state[2] = 0x98BADCFE;
|
||||||
|
ctx->state[3] = 0x10325476;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5_HASH::md5_update(MD5_CTX *ctx, uint8_t data[], uint32_t len)
|
||||||
|
{
|
||||||
|
uint32_t t,i;
|
||||||
|
|
||||||
|
for (i=0; i < len; ++i) {
|
||||||
|
ctx->data[ctx->datalen] = data[i];
|
||||||
|
ctx->datalen++;
|
||||||
|
if (ctx->datalen == 64) {
|
||||||
|
md5_transform(ctx,ctx->data);
|
||||||
|
DBL_INT_ADD(ctx->bitlen[0],ctx->bitlen[1],512);
|
||||||
|
ctx->datalen = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5_HASH::md5_final(MD5_CTX *ctx, uint8_t hash[])
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
i = ctx->datalen;
|
||||||
|
|
||||||
|
// Pad whatever data is left in the buffer.
|
||||||
|
if (ctx->datalen < 56) {
|
||||||
|
ctx->data[i++] = 0x80;
|
||||||
|
while (i < 56)
|
||||||
|
ctx->data[i++] = 0x00;
|
||||||
|
}
|
||||||
|
else if (ctx->datalen >= 56) {
|
||||||
|
ctx->data[i++] = 0x80;
|
||||||
|
while (i < 64)
|
||||||
|
ctx->data[i++] = 0x00;
|
||||||
|
md5_transform(ctx,ctx->data);
|
||||||
|
memset(ctx->data,0,56);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append to the padding the total message's length in bits and transform.
|
||||||
|
DBL_INT_ADD(ctx->bitlen[0],ctx->bitlen[1],8 * ctx->datalen);
|
||||||
|
ctx->data[56] = ctx->bitlen[0];
|
||||||
|
ctx->data[57] = ctx->bitlen[0] >> 8;
|
||||||
|
ctx->data[58] = ctx->bitlen[0] >> 16;
|
||||||
|
ctx->data[59] = ctx->bitlen[0] >> 24;
|
||||||
|
ctx->data[60] = ctx->bitlen[1];
|
||||||
|
ctx->data[61] = ctx->bitlen[1] >> 8;
|
||||||
|
ctx->data[62] = ctx->bitlen[1] >> 16;
|
||||||
|
ctx->data[63] = ctx->bitlen[1] >> 24;
|
||||||
|
md5_transform(ctx,ctx->data);
|
||||||
|
|
||||||
|
// Since this implementation uses little endian byte ordering and MD uses big endian,
|
||||||
|
// reverse all the bytes when copying the final state to the output hash.
|
||||||
|
for (i=0; i < 4; ++i) {
|
||||||
|
hash[i] = (ctx->state[0] >> (i*8)) & 0x000000ff;
|
||||||
|
hash[i+4] = (ctx->state[1] >> (i*8)) & 0x000000ff;
|
||||||
|
hash[i+8] = (ctx->state[2] >> (i*8)) & 0x000000ff;
|
||||||
|
hash[i+12] = (ctx->state[3] >> (i*8)) & 0x000000ff;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 CERN
|
||||||
|
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||||
|
*
|
||||||
|
* 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 <widgets/progress_reporter.h>
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
PROGRESS_REPORTER::PROGRESS_REPORTER( int aNumPhases ) :
|
||||||
|
m_phase( 0 ),
|
||||||
|
m_progress( 0 ),
|
||||||
|
m_maxProgress( 1 ),
|
||||||
|
m_numPhases( aNumPhases )
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
void PROGRESS_REPORTER::BeginPhase( int aPhase )
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard( m_lock );
|
||||||
|
m_phase = aPhase;
|
||||||
|
m_progress = 0;
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PROGRESS_REPORTER::AdvancePhase( )
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard( m_lock );
|
||||||
|
m_phase++;
|
||||||
|
m_progress = 0;
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PROGRESS_REPORTER::Report( const wxString& aMessage )
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard( m_lock );
|
||||||
|
m_message = aMessage;
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PROGRESS_REPORTER::SetMaxProgress ( int aMaxProgress )
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard( m_lock );
|
||||||
|
m_maxProgress = aMaxProgress;
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PROGRESS_REPORTER::AdvanceProgress( )
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard( m_lock );
|
||||||
|
m_progress++;
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
int PROGRESS_REPORTER::currentProgress() const
|
||||||
|
{
|
||||||
|
double current = (1.0 / (double)m_numPhases) * ( (double) m_phase + ( (double) m_progress / (double) m_maxProgress ) );
|
||||||
|
|
||||||
|
return (int)(current * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
WX_PROGRESS_REPORTER::WX_PROGRESS_REPORTER( wxWindow* aParent,
|
||||||
|
const wxString& aTitle,
|
||||||
|
int aNumPhases ) :
|
||||||
|
PROGRESS_REPORTER( aNumPhases ),
|
||||||
|
wxProgressDialog( aTitle, wxT( "" ), 1, aParent, wxPD_AUTO_HIDE | wxPD_CAN_ABORT |
|
||||||
|
wxPD_APP_MODAL | wxPD_ELAPSED_TIME )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WX_PROGRESS_REPORTER::~WX_PROGRESS_REPORTER()
|
||||||
|
{
|
||||||
|
Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WX_PROGRESS_REPORTER::updateUI()
|
||||||
|
{
|
||||||
|
int cur = currentProgress();
|
||||||
|
SetRange( 1000 );
|
||||||
|
Update( cur, m_message );
|
||||||
|
}
|
|
@ -387,6 +387,12 @@ private:
|
||||||
*/
|
*/
|
||||||
void drawPolygon( GLdouble* aPoints, int aPointCount );
|
void drawPolygon( GLdouble* aPoints, int aPointCount );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Draws a set of polygons with a cached triangulation. Way faster than drawPolygon.
|
||||||
|
*/
|
||||||
|
void drawTriangulatedPolyset( const SHAPE_POLY_SET& aPoly );
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Draws a single character using bitmap font.
|
* @brief Draws a single character using bitmap font.
|
||||||
* Its main purpose is to be used in BitmapText() function.
|
* Its main purpose is to be used in BitmapText() function.
|
||||||
|
|
|
@ -102,6 +102,16 @@ public:
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator==( const SEG& aSeg ) const
|
||||||
|
{
|
||||||
|
return (A == aSeg.A && B == aSeg.B) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=( const SEG& aSeg ) const
|
||||||
|
{
|
||||||
|
return (A != aSeg.A || B != aSeg.B);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function LineProject()
|
* Function LineProject()
|
||||||
*
|
*
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
|
|
||||||
#include "clipper.hpp"
|
#include "clipper.hpp"
|
||||||
|
|
||||||
|
#include <md5_hash.h>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class SHAPE_POLY_SET
|
* Class SHAPE_POLY_SET
|
||||||
|
@ -57,6 +59,42 @@ class SHAPE_POLY_SET : public SHAPE
|
||||||
///> the remaining (if any), are the holes
|
///> the remaining (if any), are the holes
|
||||||
typedef std::vector<SHAPE_LINE_CHAIN> POLYGON;
|
typedef std::vector<SHAPE_LINE_CHAIN> POLYGON;
|
||||||
|
|
||||||
|
struct TRIANGULATED_POLYGON
|
||||||
|
{
|
||||||
|
~TRIANGULATED_POLYGON();
|
||||||
|
|
||||||
|
struct TRI
|
||||||
|
{
|
||||||
|
TRI(){};
|
||||||
|
|
||||||
|
int a, b, c;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
void AllocateVertices( int aSize );
|
||||||
|
void AllocateTriangles ( int aSize );
|
||||||
|
|
||||||
|
void GetTriangle( int index, VECTOR2I& a, VECTOR2I& b, VECTOR2I& c ) const
|
||||||
|
{
|
||||||
|
auto tri = &m_triangles[ index ];
|
||||||
|
a = m_vertices[ tri->a ];
|
||||||
|
b = m_vertices[ tri->b ];
|
||||||
|
c = m_vertices[ tri->c ];
|
||||||
|
}
|
||||||
|
|
||||||
|
int AddVertex( const VECTOR2I& aP )
|
||||||
|
{
|
||||||
|
m_vertices[ m_vertexCount ] = aP;
|
||||||
|
return (m_vertexCount++);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRI* m_triangles = nullptr;
|
||||||
|
VECTOR2I* m_vertices = nullptr;
|
||||||
|
int m_vertexCount = 0;
|
||||||
|
int m_triangleCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Struct VERTEX_INDEX
|
* Struct VERTEX_INDEX
|
||||||
*
|
*
|
||||||
|
@ -538,6 +576,17 @@ class SHAPE_POLY_SET : public SHAPE
|
||||||
return m_polys[aIndex];
|
return m_polys[aIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const POLYGON& Polygon( int aIndex ) const
|
||||||
|
{
|
||||||
|
return m_polys[aIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
const TRIANGULATED_POLYGON* TriangulatedPolygon( int aIndex ) const
|
||||||
|
{
|
||||||
|
return m_triangulatedPolys[aIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const SHAPE_LINE_CHAIN& COutline( int aIndex ) const
|
const SHAPE_LINE_CHAIN& COutline( int aIndex ) const
|
||||||
{
|
{
|
||||||
return m_polys[aIndex][0];
|
return m_polys[aIndex][0];
|
||||||
|
@ -761,6 +810,8 @@ class SHAPE_POLY_SET : public SHAPE
|
||||||
///> For aFastMode meaning, see function booleanOp
|
///> For aFastMode meaning, see function booleanOp
|
||||||
void Fracture( POLYGON_MODE aFastMode );
|
void Fracture( POLYGON_MODE aFastMode );
|
||||||
|
|
||||||
|
void Unfracture( POLYGON_MODE aFastMode );
|
||||||
|
|
||||||
///> Returns true if the polygon set has any holes.
|
///> Returns true if the polygon set has any holes.
|
||||||
bool HasHoles() const;
|
bool HasHoles() const;
|
||||||
|
|
||||||
|
@ -990,6 +1041,7 @@ class SHAPE_POLY_SET : public SHAPE
|
||||||
|
|
||||||
|
|
||||||
void fractureSingle( POLYGON& paths );
|
void fractureSingle( POLYGON& paths );
|
||||||
|
void unfractureSingle ( POLYGON& path );
|
||||||
void importTree( ClipperLib::PolyTree* tree );
|
void importTree( ClipperLib::PolyTree* tree );
|
||||||
|
|
||||||
/** Function booleanOp
|
/** Function booleanOp
|
||||||
|
@ -1039,6 +1091,8 @@ class SHAPE_POLY_SET : public SHAPE
|
||||||
FILLETED
|
FILLETED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function chamferFilletPolygon
|
* Function chamferFilletPolygon
|
||||||
* Returns the camfered or filleted version of the aIndex-th polygon in the set, depending
|
* Returns the camfered or filleted version of the aIndex-th polygon in the set, depending
|
||||||
|
@ -1059,6 +1113,21 @@ class SHAPE_POLY_SET : public SHAPE
|
||||||
typedef std::vector<POLYGON> POLYSET;
|
typedef std::vector<POLYGON> POLYSET;
|
||||||
|
|
||||||
POLYSET m_polys;
|
POLYSET m_polys;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void CacheTriangulation();
|
||||||
|
bool IsTriangulationUpToDate() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void triangulateSingle( const POLYGON& aPoly, SHAPE_POLY_SET::TRIANGULATED_POLYGON& aResult );
|
||||||
|
|
||||||
|
MD5_HASH checksum() const;
|
||||||
|
|
||||||
|
std::vector<TRIANGULATED_POLYGON*> m_triangulatedPolys;
|
||||||
|
bool m_triangulationValid = false;
|
||||||
|
MD5_HASH m_hash;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
// Code by: B-Con (http://b-con.us)
|
||||||
|
// Released under the GNU GPL
|
||||||
|
// MD5 Hash Digest implementation (little endian byte order)
|
||||||
|
|
||||||
|
#ifndef __MD5_HASH_H
|
||||||
|
#define __MD5_HASH_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class MD5_HASH
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
MD5_HASH();
|
||||||
|
MD5_HASH( const MD5_HASH& aOther );
|
||||||
|
|
||||||
|
~MD5_HASH();
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
void Hash ( uint8_t *data, uint32_t length );
|
||||||
|
void Hash ( int value );
|
||||||
|
void Finalize();
|
||||||
|
bool IsValid() const { return m_valid; };
|
||||||
|
|
||||||
|
void SetValid( bool aValid ) { m_valid = aValid; }
|
||||||
|
|
||||||
|
MD5_HASH& operator=( const MD5_HASH& aOther );
|
||||||
|
|
||||||
|
bool operator==( const MD5_HASH& aOther ) const;
|
||||||
|
bool operator!=( const MD5_HASH& aOther ) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct MD5_CTX {
|
||||||
|
uint8_t data[64];
|
||||||
|
uint32_t datalen;
|
||||||
|
uint32_t bitlen[2];
|
||||||
|
uint32_t state[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
void md5_transform(MD5_CTX *ctx, uint8_t data[]);
|
||||||
|
void md5_init(MD5_CTX *ctx);
|
||||||
|
void md5_update(MD5_CTX *ctx, uint8_t data[], uint32_t len);
|
||||||
|
void md5_final(MD5_CTX *ctx, uint8_t hash[]);
|
||||||
|
|
||||||
|
bool m_valid;
|
||||||
|
MD5_CTX m_ctx;
|
||||||
|
uint8_t m_hash[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 CERN
|
||||||
|
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, you may find one here:
|
||||||
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||||
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||||
|
* or you may write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PROGRESS_REPORTER
|
||||||
|
#define __PROGRESS_REPORTER
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <wx/progdlg.h>
|
||||||
|
|
||||||
|
class PROGRESS_REPORTER
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
PROGRESS_REPORTER( int aNumPhases );
|
||||||
|
PROGRESS_REPORTER( const PROGRESS_REPORTER& ) = delete;
|
||||||
|
|
||||||
|
void BeginPhase( int aPhase );
|
||||||
|
void AdvancePhase( );
|
||||||
|
void Report ( const wxString& aMessage );
|
||||||
|
void SetMaxProgress ( int aMaxProgress );
|
||||||
|
void AdvanceProgress( );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
int currentProgress() const;
|
||||||
|
virtual void updateUI() = 0;
|
||||||
|
|
||||||
|
wxString m_message;
|
||||||
|
int m_phase, m_numPhases;
|
||||||
|
int m_progress, m_maxProgress;
|
||||||
|
|
||||||
|
std::mutex m_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WX_PROGRESS_REPORTER : public PROGRESS_REPORTER, public wxProgressDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
WX_PROGRESS_REPORTER( wxWindow *aParent, const wxString &aTitle, int aNumPhases );
|
||||||
|
~WX_PROGRESS_REPORTER();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
virtual void updateUI() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -315,6 +315,7 @@ set( PCBNEW_CLASS_SRCS
|
||||||
tools/tool_event_utils.cpp
|
tools/tool_event_utils.cpp
|
||||||
tools/size_menu.cpp
|
tools/size_menu.cpp
|
||||||
tools/selection.cpp
|
tools/selection.cpp
|
||||||
|
tools/zone_filler_tool.cpp
|
||||||
|
|
||||||
footprint_preview_panel.cpp
|
footprint_preview_panel.cpp
|
||||||
)
|
)
|
||||||
|
|
|
@ -1312,3 +1312,8 @@ void ZONE_CONTAINER::SwapData( BOARD_ITEM* aImage )
|
||||||
|
|
||||||
std::swap( *((ZONE_CONTAINER*) this), *((ZONE_CONTAINER*) aImage) );
|
std::swap( *((ZONE_CONTAINER*) this), *((ZONE_CONTAINER*) aImage) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ZONE_CONTAINER::CacheTriangulation()
|
||||||
|
{
|
||||||
|
m_FilledPolysList.CacheTriangulation();
|
||||||
|
}
|
||||||
|
|
|
@ -170,11 +170,11 @@ public:
|
||||||
int GetClearance( BOARD_CONNECTED_ITEM* aItem = NULL ) const override;
|
int GetClearance( BOARD_CONNECTED_ITEM* aItem = NULL ) const override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function TestForCopperIslandAndRemoveInsulatedIslands
|
* Function RemoveInsulatedCopperIslands
|
||||||
* Remove insulated copper islands found in m_FilledPolysList.
|
* Remove insulated copper islands found in m_FilledPolysList.
|
||||||
* @param aPcb = the board to analyze
|
* @param aPcb = the board to analyze
|
||||||
*/
|
*/
|
||||||
void TestForCopperIslandAndRemoveInsulatedIslands( BOARD* aPcb );
|
void RemoveInsulatedCopperIslands( BOARD* aPcb );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function IsOnCopperLayer
|
* Function IsOnCopperLayer
|
||||||
|
@ -329,7 +329,7 @@ public:
|
||||||
bool BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET* aOutlineBuffer = NULL );
|
bool BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET* aOutlineBuffer = NULL );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function AddClearanceAreasPolygonsToPolysList
|
* Function ComputeRawFilledAreas
|
||||||
* Add non copper areas polygons (pads and tracks with clearance)
|
* Add non copper areas polygons (pads and tracks with clearance)
|
||||||
* to a filled copper area
|
* to a filled copper area
|
||||||
* used in BuildFilledSolidAreasPolygons when calculating filled areas in a zone
|
* used in BuildFilledSolidAreasPolygons when calculating filled areas in a zone
|
||||||
|
@ -340,8 +340,7 @@ public:
|
||||||
* @param aPcb: the current board
|
* @param aPcb: the current board
|
||||||
* _NG version uses SHAPE_POLY_SET instead of Boost.Polygon
|
* _NG version uses SHAPE_POLY_SET instead of Boost.Polygon
|
||||||
*/
|
*/
|
||||||
void AddClearanceAreasPolygonsToPolysList( BOARD* aPcb );
|
void ComputeRawFilledAreas( BOARD* aPcb );
|
||||||
void AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb );
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -602,6 +601,8 @@ public:
|
||||||
return m_FilledPolysList;
|
return m_FilledPolysList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CacheTriangulation();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function AddFilledPolysList
|
* Function AddFilledPolysList
|
||||||
* sets the list of filled polygons.
|
* sets the list of filled polygons.
|
||||||
|
@ -728,7 +729,6 @@ public:
|
||||||
|
|
||||||
const std::vector<SEG>& GetHatchLines() const { return m_HatchLines; }
|
const std::vector<SEG>& GetHatchLines() const { return m_HatchLines; }
|
||||||
|
|
||||||
|
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
|
virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
|
||||||
#endif
|
#endif
|
||||||
|
@ -810,6 +810,8 @@ private:
|
||||||
int m_hatchPitch; // for DIAGONAL_EDGE, distance between 2 hatch lines
|
int m_hatchPitch; // for DIAGONAL_EDGE, distance between 2 hatch lines
|
||||||
std::vector<SEG> m_HatchLines; // hatch lines
|
std::vector<SEG> m_HatchLines; // hatch lines
|
||||||
|
|
||||||
|
std::vector<int> m_insulatedIslands;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Union to handle conversion between references to wxPoint and to VECTOR2I.
|
* Union to handle conversion between references to wxPoint and to VECTOR2I.
|
||||||
*
|
*
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <connectivity_algo.h>
|
#include <connectivity_algo.h>
|
||||||
|
#include <widgets/progress_reporter.h>
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#ifdef PROFILE
|
#ifdef PROFILE
|
||||||
#include <profile.h>
|
#include <profile.h>
|
||||||
|
@ -289,9 +293,11 @@ bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
|
void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
|
||||||
{
|
{
|
||||||
|
std::mutex cnListLock;
|
||||||
|
|
||||||
|
//PROF_COUNTER cnt("search");
|
||||||
int totalDirtyCount = 0;
|
int totalDirtyCount = 0;
|
||||||
|
|
||||||
if( m_lastSearchWithZones != aIncludeZones )
|
if( m_lastSearchWithZones != aIncludeZones )
|
||||||
|
@ -304,7 +310,7 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
|
||||||
|
|
||||||
m_lastSearchWithZones = aIncludeZones;
|
m_lastSearchWithZones = aIncludeZones;
|
||||||
|
|
||||||
auto checkForConnection = [] ( const CN_ANCHOR_PTR point, CN_ITEM* aRefItem, int aMaxDist = 0 )
|
auto checkForConnection = [ &cnListLock ] ( const CN_ANCHOR_PTR point, CN_ITEM* aRefItem, int aMaxDist = 0 )
|
||||||
{
|
{
|
||||||
const auto parent = aRefItem->Parent();
|
const auto parent = aRefItem->Parent();
|
||||||
|
|
||||||
|
@ -331,7 +337,10 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
|
||||||
case PCB_VIA_T:
|
case PCB_VIA_T:
|
||||||
|
|
||||||
if( parent->HitTest( wxPoint( point->Pos().x, point->Pos().y ) ) )
|
if( parent->HitTest( wxPoint( point->Pos().x, point->Pos().y ) ) )
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock( cnListLock );
|
||||||
CN_ITEM::Connect( aRefItem, point->Item() );
|
CN_ITEM::Connect( aRefItem, point->Item() );
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -344,7 +353,10 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
|
||||||
|
|
||||||
if( d_start.EuclideanNorm() < aMaxDist
|
if( d_start.EuclideanNorm() < aMaxDist
|
||||||
|| d_end.EuclideanNorm() < aMaxDist )
|
|| d_end.EuclideanNorm() < aMaxDist )
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock( cnListLock );
|
||||||
CN_ITEM::Connect( aRefItem, point->Item() );
|
CN_ITEM::Connect( aRefItem, point->Item() );
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,6 +375,7 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
|
||||||
|
|
||||||
if( zoneItem->ContainsAnchor( point ) )
|
if( zoneItem->ContainsAnchor( point ) )
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock( cnListLock );
|
||||||
CN_ITEM::Connect( zoneItem, point->Item() );
|
CN_ITEM::Connect( zoneItem, point->Item() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,7 +387,7 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto checkInterZoneConnection = [] ( CN_ZONE* testedZone, CN_ZONE* aRefZone )
|
auto checkInterZoneConnection = [ &cnListLock ] ( CN_ZONE* testedZone, CN_ZONE* aRefZone )
|
||||||
{
|
{
|
||||||
const auto parentZone = static_cast<const ZONE_CONTAINER*>( aRefZone->Parent() );
|
const auto parentZone = static_cast<const ZONE_CONTAINER*>( aRefZone->Parent() );
|
||||||
|
|
||||||
|
@ -399,6 +412,8 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
|
||||||
{
|
{
|
||||||
if( testedZone->ContainsPoint( outline.CPoint( i ) ) )
|
if( testedZone->ContainsPoint( outline.CPoint( i ) ) )
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock( cnListLock );
|
||||||
|
|
||||||
CN_ITEM::Connect( aRefZone, testedZone );
|
CN_ITEM::Connect( aRefZone, testedZone );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -412,6 +427,8 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
|
||||||
{
|
{
|
||||||
if( aRefZone->ContainsPoint( outline2.CPoint( i ) ) )
|
if( aRefZone->ContainsPoint( outline2.CPoint( i ) ) )
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock( cnListLock );
|
||||||
|
|
||||||
CN_ITEM::Connect( aRefZone, testedZone );
|
CN_ITEM::Connect( aRefZone, testedZone );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -496,8 +513,19 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
|
||||||
|
|
||||||
if( aIncludeZones )
|
if( aIncludeZones )
|
||||||
{
|
{
|
||||||
for( auto& item : m_zoneList )
|
int cnt = 0;
|
||||||
|
|
||||||
|
if( m_progressReporter )
|
||||||
{
|
{
|
||||||
|
m_progressReporter->SetMaxProgress( m_zoneList.Size() );
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_OPENMP
|
||||||
|
#pragma omp parallel for schedule(dynamic)
|
||||||
|
#endif
|
||||||
|
for(int i = 0; i < m_zoneList.Size(); i++ )
|
||||||
|
{
|
||||||
|
auto item = m_zoneList[i];
|
||||||
auto zoneItem = static_cast<CN_ZONE *> (item);
|
auto zoneItem = static_cast<CN_ZONE *> (item);
|
||||||
auto searchZones = std::bind( checkForConnection, _1, zoneItem );
|
auto searchZones = std::bind( checkForConnection, _1, zoneItem );
|
||||||
|
|
||||||
|
@ -509,6 +537,16 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
|
||||||
m_padList.FindNearby( zoneItem->BBox(), searchZones );
|
m_padList.FindNearby( zoneItem->BBox(), searchZones );
|
||||||
m_zoneList.FindNearbyZones( zoneItem->BBox(), std::bind( checkInterZoneConnection, _1, zoneItem ) );
|
m_zoneList.FindNearbyZones( zoneItem->BBox(), std::bind( checkInterZoneConnection, _1, zoneItem ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock( cnListLock );
|
||||||
|
cnt++;
|
||||||
|
|
||||||
|
if (m_progressReporter)
|
||||||
|
{
|
||||||
|
m_progressReporter->AdvanceProgress();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_zoneList.ClearDirtyFlags();
|
m_zoneList.ClearDirtyFlags();
|
||||||
|
@ -589,6 +627,7 @@ const CN_CONNECTIVITY_ALGO::CLUSTERS CN_CONNECTIVITY_ALGO::SearchClusters( CLUST
|
||||||
CN_ITEM* head = nullptr;
|
CN_ITEM* head = nullptr;
|
||||||
CLUSTERS clusters;
|
CLUSTERS clusters;
|
||||||
|
|
||||||
|
|
||||||
if( isDirty() )
|
if( isDirty() )
|
||||||
searchConnections( includeZones );
|
searchConnections( includeZones );
|
||||||
|
|
||||||
|
@ -798,7 +837,6 @@ void CN_CONNECTIVITY_ALGO::PropagateNets()
|
||||||
propagateConnections();
|
propagateConnections();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector<int>& aIslands )
|
void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector<int>& aIslands )
|
||||||
{
|
{
|
||||||
if( aZone->GetFilledPolysList().IsEmpty() )
|
if( aZone->GetFilledPolysList().IsEmpty() )
|
||||||
|
@ -828,6 +866,40 @@ void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std
|
||||||
wxLogTrace( "CN", "Found %u isolated islands\n", (unsigned)aIslands.size() );
|
wxLogTrace( "CN", "Found %u isolated islands\n", (unsigned)aIslands.size() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones )
|
||||||
|
{
|
||||||
|
for ( auto& z : aZones )
|
||||||
|
{
|
||||||
|
if( z.m_zone->GetFilledPolysList().IsEmpty() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Remove( z.m_zone );
|
||||||
|
Add( z.m_zone );
|
||||||
|
}
|
||||||
|
|
||||||
|
m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
|
||||||
|
|
||||||
|
for ( auto& zone : aZones )
|
||||||
|
{
|
||||||
|
if( zone.m_zone->GetFilledPolysList().IsEmpty() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for( auto cluster : m_connClusters )
|
||||||
|
{
|
||||||
|
if( cluster->Contains( zone.m_zone ) && cluster->IsOrphaned() )
|
||||||
|
{
|
||||||
|
for( auto z : *cluster )
|
||||||
|
{
|
||||||
|
if( z->Parent() == zone.m_zone )
|
||||||
|
{
|
||||||
|
zone.m_islands.push_back( static_cast<CN_ZONE*>(z)->SubpolyIndex() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const CN_CONNECTIVITY_ALGO::CLUSTERS& CN_CONNECTIVITY_ALGO::GetClusters()
|
const CN_CONNECTIVITY_ALGO::CLUSTERS& CN_CONNECTIVITY_ALGO::GetClusters()
|
||||||
{
|
{
|
||||||
|
@ -995,3 +1067,8 @@ bool CN_ANCHOR::IsDangling() const
|
||||||
|
|
||||||
return validCount <= 1;
|
return validCount <= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CN_CONNECTIVITY_ALGO::SetProgressReporter( PROGRESS_REPORTER* aReporter )
|
||||||
|
{
|
||||||
|
m_progressReporter = aReporter;
|
||||||
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ class BOARD;
|
||||||
class BOARD_CONNECTED_ITEM;
|
class BOARD_CONNECTED_ITEM;
|
||||||
class BOARD_ITEM;
|
class BOARD_ITEM;
|
||||||
class ZONE_CONTAINER;
|
class ZONE_CONTAINER;
|
||||||
|
class PROGRESS_REPORTER;
|
||||||
|
|
||||||
class CN_ANCHOR
|
class CN_ANCHOR
|
||||||
{
|
{
|
||||||
|
@ -439,6 +440,8 @@ public:
|
||||||
ITER begin() { return m_items.begin(); };
|
ITER begin() { return m_items.begin(); };
|
||||||
ITER end() { return m_items.end(); };
|
ITER end() { return m_items.end(); };
|
||||||
|
|
||||||
|
CN_ITEM* operator[] ( int aIndex ) { return m_items[aIndex]; }
|
||||||
|
|
||||||
std::vector<CN_ANCHOR_PTR>& Anchors() { return m_anchors; }
|
std::vector<CN_ANCHOR_PTR>& Anchors() { return m_anchors; }
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
|
@ -806,6 +809,7 @@ public:
|
||||||
CLUSTERS m_connClusters;
|
CLUSTERS m_connClusters;
|
||||||
CLUSTERS m_ratsnestClusters;
|
CLUSTERS m_ratsnestClusters;
|
||||||
std::vector<bool> m_dirtyNets;
|
std::vector<bool> m_dirtyNets;
|
||||||
|
PROGRESS_REPORTER* m_progressReporter = nullptr;
|
||||||
|
|
||||||
void searchConnections( bool aIncludeZones = false );
|
void searchConnections( bool aIncludeZones = false );
|
||||||
|
|
||||||
|
@ -883,6 +887,8 @@ public:
|
||||||
|
|
||||||
void PropagateNets();
|
void PropagateNets();
|
||||||
void FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector<int>& aIslands );
|
void FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector<int>& aIslands );
|
||||||
|
void FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones );
|
||||||
|
|
||||||
bool CheckConnectivity( std::vector<CN_DISJOINT_NET_ENTRY>& aReport );
|
bool CheckConnectivity( std::vector<CN_DISJOINT_NET_ENTRY>& aReport );
|
||||||
|
|
||||||
const CLUSTERS& GetClusters();
|
const CLUSTERS& GetClusters();
|
||||||
|
@ -894,6 +900,7 @@ public:
|
||||||
void ForEachItem( std::function<void(CN_ITEM*)> aFunc );
|
void ForEachItem( std::function<void(CN_ITEM*)> aFunc );
|
||||||
|
|
||||||
void MarkNetAsDirty( int aNet );
|
void MarkNetAsDirty( int aNet );
|
||||||
|
void SetProgressReporter( PROGRESS_REPORTER* aReporter );
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -218,6 +218,10 @@ void CONNECTIVITY_DATA::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone,
|
||||||
m_connAlgo->FindIsolatedCopperIslands( aZone, aIslands );
|
m_connAlgo->FindIsolatedCopperIslands( aZone, aIslands );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CONNECTIVITY_DATA::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones )
|
||||||
|
{
|
||||||
|
m_connAlgo->FindIsolatedCopperIslands( aZones );
|
||||||
|
}
|
||||||
|
|
||||||
void CONNECTIVITY_DATA::ComputeDynamicRatsnest( const std::vector<BOARD_ITEM*>& aItems )
|
void CONNECTIVITY_DATA::ComputeDynamicRatsnest( const std::vector<BOARD_ITEM*>& aItems )
|
||||||
{
|
{
|
||||||
|
@ -617,3 +621,9 @@ void CONNECTIVITY_DATA::MarkItemNetAsDirty( BOARD_ITEM *aItem )
|
||||||
m_connAlgo->MarkNetAsDirty( static_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetCode() );
|
m_connAlgo->MarkNetAsDirty( static_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetCode() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CONNECTIVITY_DATA::SetProgressReporter( PROGRESS_REPORTER* aReporter )
|
||||||
|
{
|
||||||
|
m_progressReporter = aReporter;
|
||||||
|
m_connAlgo->SetProgressReporter( m_progressReporter );
|
||||||
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ class RN_DATA;
|
||||||
class RN_NET;
|
class RN_NET;
|
||||||
class TRACK;
|
class TRACK;
|
||||||
class D_PAD;
|
class D_PAD;
|
||||||
|
class PROGRESS_REPORTER;
|
||||||
|
|
||||||
struct CN_DISJOINT_NET_ENTRY
|
struct CN_DISJOINT_NET_ENTRY
|
||||||
{
|
{
|
||||||
|
@ -53,6 +54,12 @@ struct CN_DISJOINT_NET_ENTRY
|
||||||
VECTOR2I anchorA, anchorB;
|
VECTOR2I anchorA, anchorB;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CN_ZONE_ISOLATED_ISLAND_LIST
|
||||||
|
{
|
||||||
|
ZONE_CONTAINER *m_zone;
|
||||||
|
std::vector<int> m_islands;
|
||||||
|
};
|
||||||
|
|
||||||
struct RN_DYNAMIC_LINE
|
struct RN_DYNAMIC_LINE
|
||||||
{
|
{
|
||||||
int netCode;
|
int netCode;
|
||||||
|
@ -136,6 +143,7 @@ public:
|
||||||
* @param aIslands list of islands that have no connections (outline indices in the polygon set)
|
* @param aIslands list of islands that have no connections (outline indices in the polygon set)
|
||||||
*/
|
*/
|
||||||
void FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector<int>& aIslands );
|
void FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector<int>& aIslands );
|
||||||
|
void FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function RecalculateRatsnest()
|
* Function RecalculateRatsnest()
|
||||||
|
@ -216,6 +224,8 @@ public:
|
||||||
|
|
||||||
void MarkItemNetAsDirty( BOARD_ITEM* aItem );
|
void MarkItemNetAsDirty( BOARD_ITEM* aItem );
|
||||||
|
|
||||||
|
void SetProgressReporter( PROGRESS_REPORTER* aReporter );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void updateRatsnest();
|
void updateRatsnest();
|
||||||
|
@ -226,6 +236,8 @@ private:
|
||||||
|
|
||||||
std::vector<RN_DYNAMIC_LINE> m_dynamicRatsnest;
|
std::vector<RN_DYNAMIC_LINE> m_dynamicRatsnest;
|
||||||
std::vector<RN_NET*> m_nets;
|
std::vector<RN_NET*> m_nets;
|
||||||
|
|
||||||
|
PROGRESS_REPORTER* m_progressReporter;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -136,13 +136,16 @@ PCB_DRAW_PANEL_GAL::~PCB_DRAW_PANEL_GAL()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PCB_DRAW_PANEL_GAL::DisplayBoard( const BOARD* aBoard )
|
void PCB_DRAW_PANEL_GAL::DisplayBoard( BOARD* aBoard )
|
||||||
{
|
{
|
||||||
m_view->Clear();
|
m_view->Clear();
|
||||||
|
|
||||||
// Load zones
|
// Load zones
|
||||||
for( int i = 0; i < aBoard->GetAreaCount(); ++i )
|
for( auto zone : aBoard->Zones() )
|
||||||
m_view->Add( (KIGFX::VIEW_ITEM*) ( aBoard->GetArea( i ) ) );
|
{
|
||||||
|
zone->CacheTriangulation();
|
||||||
|
m_view->Add( zone );
|
||||||
|
}
|
||||||
|
|
||||||
// Load drawings
|
// Load drawings
|
||||||
for( auto drawing : const_cast<BOARD*>(aBoard)->Drawings() )
|
for( auto drawing : const_cast<BOARD*>(aBoard)->Drawings() )
|
||||||
|
|
|
@ -50,7 +50,7 @@ public:
|
||||||
* adds all items from the current board to the VIEW, so they can be displayed by GAL.
|
* adds all items from the current board to the VIEW, so they can be displayed by GAL.
|
||||||
* @param aBoard is the PCB to be loaded.
|
* @param aBoard is the PCB to be loaded.
|
||||||
*/
|
*/
|
||||||
void DisplayBoard( const BOARD* aBoard );
|
void DisplayBoard( BOARD* aBoard );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function SetWorksheet
|
* Function SetWorksheet
|
||||||
|
|
|
@ -1143,6 +1143,9 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone, int aLayer )
|
||||||
m_gal->SetIsStroke( true );
|
m_gal->SetIsStroke( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_gal->DrawPolygon( polySet );
|
||||||
|
|
||||||
|
#if 0
|
||||||
for( int i = 0; i < polySet.OutlineCount(); i++ )
|
for( int i = 0; i < polySet.OutlineCount(); i++ )
|
||||||
{
|
{
|
||||||
const SHAPE_LINE_CHAIN& outline = polySet.COutline( i );
|
const SHAPE_LINE_CHAIN& outline = polySet.COutline( i );
|
||||||
|
@ -1164,9 +1167,12 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone, int aLayer )
|
||||||
m_gal->DrawPolyline( corners );
|
m_gal->DrawPolyline( corners );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
corners.clear();
|
corners.clear();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
*/
|
*/
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include "pcb_editor_control.h"
|
#include "pcb_editor_control.h"
|
||||||
#include "pcb_actions.h"
|
#include "pcb_actions.h"
|
||||||
|
@ -55,6 +57,12 @@
|
||||||
#include <origin_viewitem.h>
|
#include <origin_viewitem.h>
|
||||||
#include <profile.h>
|
#include <profile.h>
|
||||||
|
|
||||||
|
#include <widgets/progress_reporter.h>
|
||||||
|
|
||||||
|
#ifdef USE_OPENMP
|
||||||
|
#include <omp.h>
|
||||||
|
#endif /* USE_OPENMP */
|
||||||
|
|
||||||
#include <tools/tool_event_utils.h>
|
#include <tools/tool_event_utils.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
@ -82,24 +90,6 @@ TOOL_ACTION PCB_ACTIONS::trackViaSizeChanged( "pcbnew.EditorControl.trackViaSize
|
||||||
AS_GLOBAL, 0,
|
AS_GLOBAL, 0,
|
||||||
"", "", NULL, AF_NOTIFY );
|
"", "", NULL, AF_NOTIFY );
|
||||||
|
|
||||||
|
|
||||||
// Zone actions
|
|
||||||
TOOL_ACTION PCB_ACTIONS::zoneFill( "pcbnew.EditorControl.zoneFill",
|
|
||||||
AS_GLOBAL, 0,
|
|
||||||
_( "Fill" ), _( "Fill zone(s)" ), fill_zone_xpm );
|
|
||||||
|
|
||||||
TOOL_ACTION PCB_ACTIONS::zoneFillAll( "pcbnew.EditorControl.zoneFillAll",
|
|
||||||
AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZONE_FILL_OR_REFILL ),
|
|
||||||
_( "Fill All" ), _( "Fill all zones" ) );
|
|
||||||
|
|
||||||
TOOL_ACTION PCB_ACTIONS::zoneUnfill( "pcbnew.EditorControl.zoneUnfill",
|
|
||||||
AS_GLOBAL, 0,
|
|
||||||
_( "Unfill" ), _( "Unfill zone(s)" ), zone_unfill_xpm );
|
|
||||||
|
|
||||||
TOOL_ACTION PCB_ACTIONS::zoneUnfillAll( "pcbnew.EditorControl.zoneUnfillAll",
|
|
||||||
AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZONE_REMOVE_FILLED ),
|
|
||||||
_( "Unfill All" ), _( "Unfill all zones" ) );
|
|
||||||
|
|
||||||
TOOL_ACTION PCB_ACTIONS::zoneMerge( "pcbnew.EditorControl.zoneMerge",
|
TOOL_ACTION PCB_ACTIONS::zoneMerge( "pcbnew.EditorControl.zoneMerge",
|
||||||
AS_GLOBAL, 0,
|
AS_GLOBAL, 0,
|
||||||
_( "Merge Zones" ), _( "Merge zones" ) );
|
_( "Merge Zones" ), _( "Merge zones" ) );
|
||||||
|
@ -175,6 +165,7 @@ public:
|
||||||
Add( PCB_ACTIONS::drawSimilarZone );
|
Add( PCB_ACTIONS::drawSimilarZone );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CONTEXT_MENU* create() const override
|
CONTEXT_MENU* create() const override
|
||||||
{
|
{
|
||||||
|
@ -655,139 +646,6 @@ int PCB_EDITOR_CONTROL::PlaceTarget( const TOOL_EVENT& aEvent )
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Zone actions
|
|
||||||
int PCB_EDITOR_CONTROL::ZoneFill( const TOOL_EVENT& aEvent )
|
|
||||||
{
|
|
||||||
auto selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
|
|
||||||
const auto& selection = selTool->GetSelection();
|
|
||||||
auto connectivity = getModel<BOARD>()->GetConnectivity();
|
|
||||||
|
|
||||||
BOARD_COMMIT commit( this );
|
|
||||||
|
|
||||||
for( auto item : selection )
|
|
||||||
{
|
|
||||||
assert( item->Type() == PCB_ZONE_AREA_T );
|
|
||||||
|
|
||||||
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*> ( item );
|
|
||||||
|
|
||||||
commit.Modify( zone );
|
|
||||||
|
|
||||||
m_frame->Fill_Zone( zone );
|
|
||||||
zone->SetIsFilled( true );
|
|
||||||
}
|
|
||||||
|
|
||||||
commit.Push( _( "Fill Zone" ) );
|
|
||||||
|
|
||||||
connectivity->RecalculateRatsnest();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int PCB_EDITOR_CONTROL::ZoneFillAll( const TOOL_EVENT& aEvent )
|
|
||||||
{
|
|
||||||
BOARD* board = getModel<BOARD>();
|
|
||||||
auto connectivity = getModel<BOARD>()->GetConnectivity();
|
|
||||||
int areaCount = board->GetAreaCount();
|
|
||||||
const wxString fmt = _( "Filling zone %d out of %d (net %s)..." );
|
|
||||||
wxString msg;
|
|
||||||
bool aborted = false;
|
|
||||||
|
|
||||||
// Create a message with a long net name, and build a wxProgressDialog
|
|
||||||
// with a correct size to show this long net name
|
|
||||||
msg.Printf( fmt, 000, areaCount, wxT("XXXXXXXXXXXXXXXXX" ) );
|
|
||||||
|
|
||||||
auto progressDialog = new wxProgressDialog( _( "Fill All Zones" ), msg,
|
|
||||||
areaCount, frame(),
|
|
||||||
wxPD_AUTO_HIDE | wxPD_CAN_ABORT |
|
|
||||||
wxPD_APP_MODAL | wxPD_ELAPSED_TIME );
|
|
||||||
|
|
||||||
BOARD_COMMIT commit( this );
|
|
||||||
|
|
||||||
for( int i = 0; i < areaCount; ++i )
|
|
||||||
{
|
|
||||||
ZONE_CONTAINER* zone = board->GetArea( i );
|
|
||||||
|
|
||||||
msg.Printf( fmt, i, areaCount, GetChars( zone->GetNetname() ) );
|
|
||||||
|
|
||||||
commit.Modify( zone );
|
|
||||||
|
|
||||||
if( !progressDialog->Update( i, msg ) )
|
|
||||||
{
|
|
||||||
aborted = true;
|
|
||||||
break; // Aborted by user
|
|
||||||
}
|
|
||||||
|
|
||||||
m_frame->Fill_Zone( zone );
|
|
||||||
zone->SetIsFilled( true );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( aborted )
|
|
||||||
commit.Revert();
|
|
||||||
else
|
|
||||||
commit.Push( _( "Fill All Zones" ) );
|
|
||||||
|
|
||||||
connectivity->RecalculateRatsnest();
|
|
||||||
progressDialog->Destroy();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int PCB_EDITOR_CONTROL::ZoneUnfill( const TOOL_EVENT& aEvent )
|
|
||||||
{
|
|
||||||
auto selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
|
|
||||||
const auto& selection = selTool->GetSelection();
|
|
||||||
auto connectivity = getModel<BOARD>()->GetConnectivity();
|
|
||||||
|
|
||||||
BOARD_COMMIT commit( this );
|
|
||||||
|
|
||||||
for( auto item : selection )
|
|
||||||
{
|
|
||||||
assert( item->Type() == PCB_ZONE_AREA_T );
|
|
||||||
|
|
||||||
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
|
|
||||||
|
|
||||||
commit.Modify( zone );
|
|
||||||
|
|
||||||
zone->SetIsFilled( false );
|
|
||||||
zone->ClearFilledPolysList();
|
|
||||||
}
|
|
||||||
|
|
||||||
commit.Push( _( "Unfill Zone" ) );
|
|
||||||
|
|
||||||
connectivity->RecalculateRatsnest();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int PCB_EDITOR_CONTROL::ZoneUnfillAll( const TOOL_EVENT& aEvent )
|
|
||||||
{
|
|
||||||
BOARD* board = getModel<BOARD>();
|
|
||||||
auto connectivity = getModel<BOARD>()->GetConnectivity();
|
|
||||||
|
|
||||||
BOARD_COMMIT commit( this );
|
|
||||||
|
|
||||||
for( int i = 0; i < board->GetAreaCount(); ++i )
|
|
||||||
{
|
|
||||||
ZONE_CONTAINER* zone = board->GetArea( i );
|
|
||||||
|
|
||||||
commit.Modify( zone );
|
|
||||||
|
|
||||||
zone->SetIsFilled( false );
|
|
||||||
zone->ClearFilledPolysList();
|
|
||||||
}
|
|
||||||
|
|
||||||
commit.Push( _( "Unfill All Zones" ) );
|
|
||||||
|
|
||||||
connectivity->RecalculateRatsnest();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static bool mergeZones( BOARD_COMMIT& aCommit, std::vector<ZONE_CONTAINER *>& aOriginZones,
|
static bool mergeZones( BOARD_COMMIT& aCommit, std::vector<ZONE_CONTAINER *>& aOriginZones,
|
||||||
std::vector<ZONE_CONTAINER *>& aMergedZones )
|
std::vector<ZONE_CONTAINER *>& aMergedZones )
|
||||||
{
|
{
|
||||||
|
@ -819,6 +677,7 @@ static bool mergeZones( BOARD_COMMIT& aCommit, std::vector<ZONE_CONTAINER *>& aO
|
||||||
|
|
||||||
aOriginZones[0]->SetLocalFlags( 1 );
|
aOriginZones[0]->SetLocalFlags( 1 );
|
||||||
aOriginZones[0]->Hatch();
|
aOriginZones[0]->Hatch();
|
||||||
|
aOriginZones[0]->CacheTriangulation();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1250,10 +1109,6 @@ void PCB_EDITOR_CONTROL::setTransitions()
|
||||||
Go( &PCB_EDITOR_CONTROL::ViaSizeDec, PCB_ACTIONS::viaSizeDec.MakeEvent() );
|
Go( &PCB_EDITOR_CONTROL::ViaSizeDec, PCB_ACTIONS::viaSizeDec.MakeEvent() );
|
||||||
|
|
||||||
// Zone actions
|
// Zone actions
|
||||||
Go( &PCB_EDITOR_CONTROL::ZoneFill, PCB_ACTIONS::zoneFill.MakeEvent() );
|
|
||||||
Go( &PCB_EDITOR_CONTROL::ZoneFillAll, PCB_ACTIONS::zoneFillAll.MakeEvent() );
|
|
||||||
Go( &PCB_EDITOR_CONTROL::ZoneUnfill, PCB_ACTIONS::zoneUnfill.MakeEvent() );
|
|
||||||
Go( &PCB_EDITOR_CONTROL::ZoneUnfillAll, PCB_ACTIONS::zoneUnfillAll.MakeEvent() );
|
|
||||||
Go( &PCB_EDITOR_CONTROL::ZoneMerge, PCB_ACTIONS::zoneMerge.MakeEvent() );
|
Go( &PCB_EDITOR_CONTROL::ZoneMerge, PCB_ACTIONS::zoneMerge.MakeEvent() );
|
||||||
Go( &PCB_EDITOR_CONTROL::ZoneDuplicate, PCB_ACTIONS::zoneDuplicate.MakeEvent() );
|
Go( &PCB_EDITOR_CONTROL::ZoneDuplicate, PCB_ACTIONS::zoneDuplicate.MakeEvent() );
|
||||||
|
|
||||||
|
|
|
@ -57,10 +57,6 @@ public:
|
||||||
int ViaSizeDec( const TOOL_EVENT& aEvent );
|
int ViaSizeDec( const TOOL_EVENT& aEvent );
|
||||||
|
|
||||||
// Zone actions
|
// Zone actions
|
||||||
int ZoneFill( const TOOL_EVENT& aEvent );
|
|
||||||
int ZoneFillAll( const TOOL_EVENT& aEvent );
|
|
||||||
int ZoneUnfill( const TOOL_EVENT& aEvent );
|
|
||||||
int ZoneUnfillAll( const TOOL_EVENT& aEvent );
|
|
||||||
int ZoneMerge( const TOOL_EVENT& aEvent );
|
int ZoneMerge( const TOOL_EVENT& aEvent );
|
||||||
|
|
||||||
///> Duplicates a zone onto a layer (prompts for new layer)
|
///> Duplicates a zone onto a layer (prompts for new layer)
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include <tools/pad_tool.h>
|
#include <tools/pad_tool.h>
|
||||||
#include <tools/microwave_tool.h>
|
#include <tools/microwave_tool.h>
|
||||||
#include <tools/position_relative_tool.h>
|
#include <tools/position_relative_tool.h>
|
||||||
|
#include <tools/zone_filler_tool.h>
|
||||||
#include <tools/pcb_actions.h>
|
#include <tools/pcb_actions.h>
|
||||||
|
|
||||||
#include <router/router_tool.h>
|
#include <router/router_tool.h>
|
||||||
|
@ -62,4 +63,5 @@ void PCB_ACTIONS::RegisterAllTools( TOOL_MANAGER* aToolManager )
|
||||||
aToolManager->RegisterTool( new ALIGN_DISTRIBUTE_TOOL );
|
aToolManager->RegisterTool( new ALIGN_DISTRIBUTE_TOOL );
|
||||||
aToolManager->RegisterTool( new MICROWAVE_TOOL );
|
aToolManager->RegisterTool( new MICROWAVE_TOOL );
|
||||||
aToolManager->RegisterTool( new POSITION_RELATIVE_TOOL );
|
aToolManager->RegisterTool( new POSITION_RELATIVE_TOOL );
|
||||||
|
aToolManager->RegisterTool( new ZONE_FILLER_TOOL );
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,327 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014-2017 CERN
|
||||||
|
* Copyright (C) 2014-2017 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
|
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
||||||
|
*
|
||||||
|
* 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 <cstdint>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <painter.h>
|
||||||
|
#include <project.h>
|
||||||
|
#include <pcbnew_id.h>
|
||||||
|
#include <wxPcbStruct.h>
|
||||||
|
#include <class_board.h>
|
||||||
|
#include <class_zone.h>
|
||||||
|
#include <pcb_draw_panel_gal.h>
|
||||||
|
#include <class_module.h>
|
||||||
|
#include <connectivity_data.h>
|
||||||
|
#include <board_commit.h>
|
||||||
|
|
||||||
|
#include <widgets/progress_reporter.h>
|
||||||
|
#include <tool/tool_manager.h>
|
||||||
|
#include <bitmaps.h>
|
||||||
|
#include <hotkeys.h>
|
||||||
|
|
||||||
|
#include "pcb_actions.h"
|
||||||
|
#include "selection_tool.h"
|
||||||
|
#include "zone_filler_tool.h"
|
||||||
|
|
||||||
|
#ifdef USE_OPENMP
|
||||||
|
#include <omp.h>
|
||||||
|
#endif /* USE_OPENMP */
|
||||||
|
|
||||||
|
// Zone actions
|
||||||
|
TOOL_ACTION PCB_ACTIONS::zoneFill( "pcbnew.ZoneFiller.zoneFill",
|
||||||
|
AS_GLOBAL, 0,
|
||||||
|
_( "Fill" ), _( "Fill zone(s)" ), fill_zone_xpm );
|
||||||
|
|
||||||
|
TOOL_ACTION PCB_ACTIONS::zoneFillAll( "pcbnew.ZoneFiller.zoneFillAll",
|
||||||
|
AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZONE_FILL_OR_REFILL ),
|
||||||
|
_( "Fill All" ), _( "Fill all zones" ) );
|
||||||
|
|
||||||
|
TOOL_ACTION PCB_ACTIONS::zoneUnfill( "pcbnew.ZoneFiller.zoneUnfill",
|
||||||
|
AS_GLOBAL, 0,
|
||||||
|
_( "Unfill" ), _( "Unfill zone(s)" ), zone_unfill_xpm );
|
||||||
|
|
||||||
|
TOOL_ACTION PCB_ACTIONS::zoneUnfillAll( "pcbnew.ZoneFiller.zoneUnfillAll",
|
||||||
|
AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ZONE_REMOVE_FILLED ),
|
||||||
|
_( "Unfill All" ), _( "Unfill all zones" ) );
|
||||||
|
|
||||||
|
|
||||||
|
class ZONE_FILLER
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZONE_FILLER( BOARD* aBoard, COMMIT* aCommit );
|
||||||
|
~ZONE_FILLER();
|
||||||
|
|
||||||
|
void SetProgressReporter( PROGRESS_REPORTER* aReporter );
|
||||||
|
void Fill( std::vector<ZONE_CONTAINER*> aZones );
|
||||||
|
void Unfill( std::vector<ZONE_CONTAINER*> aZones );
|
||||||
|
|
||||||
|
private:
|
||||||
|
COMMIT* m_commit;
|
||||||
|
PROGRESS_REPORTER* m_progressReporter;
|
||||||
|
BOARD* m_board;
|
||||||
|
};
|
||||||
|
|
||||||
|
ZONE_FILLER::ZONE_FILLER( BOARD* aBoard, COMMIT* aCommit ) :
|
||||||
|
m_commit( aCommit ),
|
||||||
|
m_board( aBoard )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ZONE_FILLER::~ZONE_FILLER()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ZONE_FILLER::SetProgressReporter( PROGRESS_REPORTER* aReporter )
|
||||||
|
{
|
||||||
|
m_progressReporter = aReporter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ZONE_FILLER::Fill( std::vector<ZONE_CONTAINER*> aZones )
|
||||||
|
{
|
||||||
|
std::vector<CN_ZONE_ISOLATED_ISLAND_LIST> toFill;
|
||||||
|
|
||||||
|
assert( m_commit );
|
||||||
|
|
||||||
|
// Remove segment zones
|
||||||
|
m_board->m_Zone.DeleteAll();
|
||||||
|
|
||||||
|
int ii;
|
||||||
|
|
||||||
|
for( auto zone : aZones )
|
||||||
|
{
|
||||||
|
// Keepout zones are not filled
|
||||||
|
if( zone->GetIsKeepout() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CN_ZONE_ISOLATED_ISLAND_LIST l;
|
||||||
|
|
||||||
|
l.m_zone = zone;
|
||||||
|
|
||||||
|
toFill.push_back( l );
|
||||||
|
}
|
||||||
|
|
||||||
|
int zoneCount = m_board->GetAreaCount();
|
||||||
|
|
||||||
|
for( int i = 0; i < toFill.size(); i++ )
|
||||||
|
{
|
||||||
|
m_commit->Modify( toFill[i].m_zone );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_progressReporter )
|
||||||
|
{
|
||||||
|
m_progressReporter->Report( _( "Calculating zone fills..." ) );
|
||||||
|
m_progressReporter->SetMaxProgress( toFill.size() );
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_OPENMP
|
||||||
|
#pragma omp parallel for schedule(dynamic)
|
||||||
|
#endif
|
||||||
|
for( int i = 0; i < toFill.size(); i++ )
|
||||||
|
{
|
||||||
|
toFill[i].m_zone->BuildFilledSolidAreasPolygons( m_board );
|
||||||
|
|
||||||
|
m_progressReporter->AdvanceProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_progressReporter )
|
||||||
|
{
|
||||||
|
m_progressReporter->AdvancePhase();
|
||||||
|
m_progressReporter->Report( _( "Removing insulated copper islands..." ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
m_board->GetConnectivity()->SetProgressReporter( m_progressReporter );
|
||||||
|
m_board->GetConnectivity()->FindIsolatedCopperIslands( toFill );
|
||||||
|
|
||||||
|
for( auto& zone : toFill )
|
||||||
|
{
|
||||||
|
std::sort( zone.m_islands.begin(), zone.m_islands.end(), std::greater<int>() );
|
||||||
|
SHAPE_POLY_SET poly = zone.m_zone->GetFilledPolysList();
|
||||||
|
|
||||||
|
for( auto idx : zone.m_islands )
|
||||||
|
{
|
||||||
|
poly.DeletePolygon( idx );
|
||||||
|
}
|
||||||
|
|
||||||
|
zone.m_zone->AddFilledPolysList( poly );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_progressReporter )
|
||||||
|
{
|
||||||
|
m_progressReporter->AdvancePhase();
|
||||||
|
m_progressReporter->Report( _( "Caching polygon triangulations..." ) );
|
||||||
|
m_progressReporter->SetMaxProgress( toFill.size() );
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_OPENMP
|
||||||
|
#pragma omp parallel for schedule(dynamic)
|
||||||
|
#endif
|
||||||
|
for( int i = 0; i < toFill.size(); i++ )
|
||||||
|
{
|
||||||
|
m_progressReporter->AdvanceProgress();
|
||||||
|
toFill[i].m_zone->CacheTriangulation();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_progressReporter->AdvancePhase();
|
||||||
|
m_progressReporter->Report( _( "Committing changes..." ) );
|
||||||
|
|
||||||
|
m_commit->Push( _( "Fill Zones" ), false );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ZONE_FILLER_TOOL::ZONE_FILLER_TOOL() :
|
||||||
|
PCB_TOOL( "pcbnew.ZoneFiller" )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ZONE_FILLER_TOOL::~ZONE_FILLER_TOOL()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ZONE_FILLER_TOOL::Reset( RESET_REASON aReason )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zone actions
|
||||||
|
int ZONE_FILLER_TOOL::ZoneFill( const TOOL_EVENT& aEvent )
|
||||||
|
{
|
||||||
|
auto selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
|
||||||
|
const auto& selection = selTool->GetSelection();
|
||||||
|
std::vector<ZONE_CONTAINER*> toFill;
|
||||||
|
|
||||||
|
BOARD_COMMIT commit( this );
|
||||||
|
|
||||||
|
for( auto item : selection )
|
||||||
|
{
|
||||||
|
assert( item->Type() == PCB_ZONE_AREA_T );
|
||||||
|
|
||||||
|
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*> ( item );
|
||||||
|
|
||||||
|
toFill.push_back(zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<WX_PROGRESS_REPORTER> progressReporter(
|
||||||
|
new WX_PROGRESS_REPORTER( frame(), _( "Fill Zones" ), 3 )
|
||||||
|
);
|
||||||
|
|
||||||
|
ZONE_FILLER filler( board(), &commit );
|
||||||
|
filler.SetProgressReporter( progressReporter.get() );
|
||||||
|
filler.Fill( toFill );
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ZONE_FILLER_TOOL::ZoneFillAll( const TOOL_EVENT& aEvent )
|
||||||
|
{
|
||||||
|
std::vector<ZONE_CONTAINER*> toFill;
|
||||||
|
|
||||||
|
BOARD_COMMIT commit( this );
|
||||||
|
|
||||||
|
for( auto zone : board()->Zones() )
|
||||||
|
{
|
||||||
|
toFill.push_back(zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<WX_PROGRESS_REPORTER> progressReporter(
|
||||||
|
new WX_PROGRESS_REPORTER( frame(), _( "Fill All Zones" ), 3 )
|
||||||
|
);
|
||||||
|
|
||||||
|
ZONE_FILLER filler( board(), &commit );
|
||||||
|
filler.SetProgressReporter( progressReporter.get() );
|
||||||
|
filler.Fill( toFill );
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ZONE_FILLER_TOOL::ZoneUnfill( const TOOL_EVENT& aEvent )
|
||||||
|
{
|
||||||
|
auto selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
|
||||||
|
const auto& selection = selTool->GetSelection();
|
||||||
|
auto connectivity = getModel<BOARD>()->GetConnectivity();
|
||||||
|
|
||||||
|
BOARD_COMMIT commit( this );
|
||||||
|
|
||||||
|
for( auto item : selection )
|
||||||
|
{
|
||||||
|
assert( item->Type() == PCB_ZONE_AREA_T );
|
||||||
|
|
||||||
|
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
|
||||||
|
|
||||||
|
commit.Modify( zone );
|
||||||
|
|
||||||
|
zone->SetIsFilled( false );
|
||||||
|
zone->ClearFilledPolysList();
|
||||||
|
}
|
||||||
|
|
||||||
|
commit.Push( _( "Unfill Zone" ) );
|
||||||
|
|
||||||
|
connectivity->RecalculateRatsnest();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ZONE_FILLER_TOOL::ZoneUnfillAll( const TOOL_EVENT& aEvent )
|
||||||
|
{
|
||||||
|
BOARD* board = getModel<BOARD>();
|
||||||
|
auto connectivity = getModel<BOARD>()->GetConnectivity();
|
||||||
|
|
||||||
|
BOARD_COMMIT commit( this );
|
||||||
|
|
||||||
|
for( int i = 0; i < board->GetAreaCount(); ++i )
|
||||||
|
{
|
||||||
|
ZONE_CONTAINER* zone = board->GetArea( i );
|
||||||
|
|
||||||
|
commit.Modify( zone );
|
||||||
|
|
||||||
|
zone->SetIsFilled( false );
|
||||||
|
zone->ClearFilledPolysList();
|
||||||
|
}
|
||||||
|
|
||||||
|
commit.Push( _( "Unfill All Zones" ) );
|
||||||
|
|
||||||
|
connectivity->RecalculateRatsnest();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ZONE_FILLER_TOOL::setTransitions()
|
||||||
|
{
|
||||||
|
// Zone actions
|
||||||
|
Go( &ZONE_FILLER_TOOL::ZoneFill, PCB_ACTIONS::zoneFill.MakeEvent() );
|
||||||
|
Go( &ZONE_FILLER_TOOL::ZoneFillAll, PCB_ACTIONS::zoneFillAll.MakeEvent() );
|
||||||
|
Go( &ZONE_FILLER_TOOL::ZoneUnfill, PCB_ACTIONS::zoneUnfill.MakeEvent() );
|
||||||
|
Go( &ZONE_FILLER_TOOL::ZoneUnfillAll, PCB_ACTIONS::zoneUnfillAll.MakeEvent() );
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 - 2017 CERN
|
||||||
|
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, you may find one here:
|
||||||
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||||
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||||
|
* or you may write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ZONE_FILLER_TOOL_H
|
||||||
|
#define ZONE_FILLER_TOOL_H
|
||||||
|
|
||||||
|
#include <tools/pcb_tool.h>
|
||||||
|
|
||||||
|
|
||||||
|
class PCB_EDIT_FRAME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ZONE_FILLER_TOOL
|
||||||
|
*
|
||||||
|
* Handles actions specific to filling copper zones.
|
||||||
|
*/
|
||||||
|
class ZONE_FILLER_TOOL : public PCB_TOOL
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZONE_FILLER_TOOL();
|
||||||
|
~ZONE_FILLER_TOOL();
|
||||||
|
|
||||||
|
/// @copydoc TOOL_INTERACTIVE::Reset()
|
||||||
|
void Reset( RESET_REASON aReason ) override;
|
||||||
|
|
||||||
|
// Zone actions
|
||||||
|
int ZoneFill( const TOOL_EVENT& aEvent );
|
||||||
|
int ZoneFillAll( const TOOL_EVENT& aEvent );
|
||||||
|
int ZoneUnfill( const TOOL_EVENT& aEvent );
|
||||||
|
int ZoneUnfillAll( const TOOL_EVENT& aEvent );
|
||||||
|
|
||||||
|
private:
|
||||||
|
///> Sets up handlers for various events.
|
||||||
|
void setTransitions() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -29,15 +29,82 @@
|
||||||
|
|
||||||
#include <algorithm> // sort
|
#include <algorithm> // sort
|
||||||
|
|
||||||
#include <fctsys.h>
|
#include <cmath>
|
||||||
#include <trigo.h>
|
#include <sstream>
|
||||||
#include <wxPcbStruct.h>
|
|
||||||
#include <convert_basic_shapes_to_polygon.h>
|
|
||||||
|
|
||||||
|
#include <fctsys.h>
|
||||||
|
#include <wxPcbStruct.h>
|
||||||
|
#include <trigo.h>
|
||||||
|
|
||||||
|
#include <class_board.h>
|
||||||
|
#include <class_module.h>
|
||||||
|
#include <class_track.h>
|
||||||
|
#include <class_edge_mod.h>
|
||||||
|
#include <class_drawsegment.h>
|
||||||
|
#include <class_pcb_text.h>
|
||||||
#include <class_zone.h>
|
#include <class_zone.h>
|
||||||
|
#include <project.h>
|
||||||
|
|
||||||
#include <pcbnew.h>
|
#include <pcbnew.h>
|
||||||
#include <zones.h>
|
#include <zones.h>
|
||||||
|
#include <convert_basic_shapes_to_polygon.h>
|
||||||
|
|
||||||
|
#include <geometry/shape_poly_set.h>
|
||||||
|
#include <geometry/shape_file_io.h>
|
||||||
|
#include <geometry/convex_hull.h>
|
||||||
|
|
||||||
|
#include <connectivity_data.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* Functions to convert some board items to polygons
|
||||||
|
* (pads, tracks ..)
|
||||||
|
* This is used to calculate filled areas in copper zones.
|
||||||
|
* Filled areas are areas remainder of the full zone area after removed all polygons
|
||||||
|
* calculated from these items shapes and the clearance area
|
||||||
|
*
|
||||||
|
* Important note:
|
||||||
|
* Because filled areas must have a minimum thickness to match with Design rule, they are
|
||||||
|
* draw in 2 step:
|
||||||
|
* 1 - filled polygons are drawn
|
||||||
|
* 2 - polygon outlines are drawn with a "minimum thickness width" ( or with a minimum
|
||||||
|
* thickness pen )
|
||||||
|
* So outlines of filled polygons are calculated with the constraint they match with clearance,
|
||||||
|
* taking in account outlines have thickness
|
||||||
|
* This ensures:
|
||||||
|
* - areas meet the minimum thickness requirement.
|
||||||
|
* - shapes are smoothed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Polygon calculations can use fast mode or force strickly simple polygons after calculations
|
||||||
|
// Forcing strickly simple polygons is time consuming, and we have not see issues in fast mode
|
||||||
|
// so we use fast mode when possible (intermediate calculations)
|
||||||
|
// (choice is SHAPE_POLY_SET::PM_STRICTLY_SIMPLE or SHAPE_POLY_SET::PM_FAST)
|
||||||
|
#define POLY_CALC_MODE SHAPE_POLY_SET::PM_FAST
|
||||||
|
|
||||||
|
/* DEBUG OPTION:
|
||||||
|
* To emit zone data to a file when filling zones for the debugging purposes,
|
||||||
|
* set this 'true' and build.
|
||||||
|
*/
|
||||||
|
static const bool s_DumpZonesWhenFilling = false;
|
||||||
|
|
||||||
|
extern void BuildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer,
|
||||||
|
BOARD* aPcb, ZONE_CONTAINER* aZone,
|
||||||
|
double aArcCorrection,
|
||||||
|
double aRoundPadThermalRotation);
|
||||||
|
|
||||||
|
|
||||||
|
extern void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||||
|
D_PAD& aPad,
|
||||||
|
int aThermalGap,
|
||||||
|
int aCopperThickness,
|
||||||
|
int aMinThicknessValue,
|
||||||
|
int aCircleToSegmentsCount,
|
||||||
|
double aCorrectionFactor,
|
||||||
|
double aThermalRot );
|
||||||
|
|
||||||
|
// Local Variables:
|
||||||
|
static double s_thermalRot = 450; // angle of stubs in thermal reliefs for round pads
|
||||||
|
|
||||||
|
|
||||||
/* Build the filled solid areas data from real outlines (stored in m_Poly)
|
/* Build the filled solid areas data from real outlines (stored in m_Poly)
|
||||||
* The solid areas can be more than one on copper layers, and do not have holes
|
* The solid areas can be more than one on copper layers, and do not have holes
|
||||||
|
@ -51,7 +118,7 @@
|
||||||
* if not null:
|
* if not null:
|
||||||
* Only the zone outline (with holes, if any) are stored in aCornerBuffer
|
* Only the zone outline (with holes, if any) are stored in aCornerBuffer
|
||||||
* with holes linked. Therefore only one polygon is created
|
* with holes linked. Therefore only one polygon is created
|
||||||
* This function calls AddClearanceAreasPolygonsToPolysList()
|
* This function calls ComputeRawFilledAreas()
|
||||||
* to add holes for pads and tracks and other items not in net.
|
* to add holes for pads and tracks and other items not in net.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -109,7 +176,7 @@ bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET*
|
||||||
|
|
||||||
if( IsOnCopperLayer() )
|
if( IsOnCopperLayer() )
|
||||||
{
|
{
|
||||||
AddClearanceAreasPolygonsToPolysList_NG( aPcb );
|
ComputeRawFilledAreas( aPcb );
|
||||||
|
|
||||||
if( m_FillMode ) // if fill mode uses segments, create them:
|
if( m_FillMode ) // if fill mode uses segments, create them:
|
||||||
{
|
{
|
||||||
|
@ -283,7 +350,7 @@ bool fillPolygonWithHorizontalSegments( const SHAPE_LINE_CHAIN& aPolygon,
|
||||||
|
|
||||||
// Sort intersection points by increasing x value:
|
// Sort intersection points by increasing x value:
|
||||||
// So 2 consecutive points are the ends of a segment
|
// So 2 consecutive points are the ends of a segment
|
||||||
sort( x_coordinates.begin(), x_coordinates.end() );
|
std::sort( x_coordinates.begin(), x_coordinates.end() );
|
||||||
|
|
||||||
// An even number of coordinates is expected, because a segment has 2 ends.
|
// An even number of coordinates is expected, because a segment has 2 ends.
|
||||||
// An if this algorithm always works, it must always find an even count.
|
// An if this algorithm always works, it must always find an even count.
|
||||||
|
@ -310,3 +377,502 @@ bool fillPolygonWithHorizontalSegments( const SHAPE_LINE_CHAIN& aPolygon,
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeatures )
|
||||||
|
{
|
||||||
|
int segsPerCircle;
|
||||||
|
double correctionFactor;
|
||||||
|
|
||||||
|
// Set the number of segments in arc approximations
|
||||||
|
if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF )
|
||||||
|
segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF;
|
||||||
|
else
|
||||||
|
segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF;
|
||||||
|
|
||||||
|
/* calculates the coeff to compensate radius reduction of holes clearance
|
||||||
|
* due to the segment approx.
|
||||||
|
* For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
|
||||||
|
* s_Correction is 1 /cos( PI/s_CircleToSegmentsCount )
|
||||||
|
*/
|
||||||
|
correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle );
|
||||||
|
|
||||||
|
aFeatures.RemoveAllContours();
|
||||||
|
|
||||||
|
int outline_half_thickness = m_ZoneMinThickness / 2;
|
||||||
|
|
||||||
|
// When removing holes, the holes must be expanded by outline_half_thickness
|
||||||
|
// to take in account the thickness of the zone outlines
|
||||||
|
int zone_clearance = GetClearance() + outline_half_thickness;
|
||||||
|
|
||||||
|
// When holes are created by non copper items (edge cut items), use only
|
||||||
|
// the m_ZoneClearance parameter (zone clearance with no netclass clearance)
|
||||||
|
int zone_to_edgecut_clearance = GetZoneClearance() + outline_half_thickness;
|
||||||
|
|
||||||
|
/* store holes (i.e. tracks and pads areas as polygons outlines)
|
||||||
|
* in a polygon list
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* items ouside the zone bounding box are skipped
|
||||||
|
* the bounding box is the zone bounding box + the biggest clearance found in Netclass list
|
||||||
|
*/
|
||||||
|
EDA_RECT item_boundingbox;
|
||||||
|
EDA_RECT zone_boundingbox = GetBoundingBox();
|
||||||
|
int biggest_clearance = aPcb->GetDesignSettings().GetBiggestClearanceValue();
|
||||||
|
biggest_clearance = std::max( biggest_clearance, zone_clearance );
|
||||||
|
zone_boundingbox.Inflate( biggest_clearance );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First : Add pads. Note: pads having the same net as zone are left in zone.
|
||||||
|
* Thermal shapes will be created later if necessary
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Use a dummy pad to calculate hole clearance when a pad is not on all copper layers
|
||||||
|
* and this pad has a hole
|
||||||
|
* This dummy pad has the size and shape of the hole
|
||||||
|
* Therefore, this dummy pad is a circle or an oval.
|
||||||
|
* A pad must have a parent because some functions expect a non null parent
|
||||||
|
* to find the parent board, and some other data
|
||||||
|
*/
|
||||||
|
MODULE dummymodule( aPcb ); // Creates a dummy parent
|
||||||
|
D_PAD dummypad( &dummymodule );
|
||||||
|
|
||||||
|
for( MODULE* module = aPcb->m_Modules; module; module = module->Next() )
|
||||||
|
{
|
||||||
|
D_PAD* nextpad;
|
||||||
|
|
||||||
|
for( D_PAD* pad = module->PadsList(); pad != NULL; pad = nextpad )
|
||||||
|
{
|
||||||
|
nextpad = pad->Next(); // pad pointer can be modified by next code, so
|
||||||
|
// calculate the next pad here
|
||||||
|
|
||||||
|
if( !pad->IsOnLayer( GetLayer() ) )
|
||||||
|
{
|
||||||
|
/* Test for pads that are on top or bottom only and have a hole.
|
||||||
|
* There are curious pads but they can be used for some components that are
|
||||||
|
* inside the board (in fact inside the hole. Some photo diodes and Leds are
|
||||||
|
* like this)
|
||||||
|
*/
|
||||||
|
if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Use a dummy pad to calculate a hole shape that have the same dimension as
|
||||||
|
// the pad hole
|
||||||
|
dummypad.SetSize( pad->GetDrillSize() );
|
||||||
|
dummypad.SetOrientation( pad->GetOrientation() );
|
||||||
|
dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
|
||||||
|
PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE );
|
||||||
|
dummypad.SetPosition( pad->GetPosition() );
|
||||||
|
|
||||||
|
pad = &dummypad;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: netcode <=0 means not connected item
|
||||||
|
if( ( pad->GetNetCode() != GetNetCode() ) || ( pad->GetNetCode() <= 0 ) )
|
||||||
|
{
|
||||||
|
int item_clearance = pad->GetClearance() + outline_half_thickness;
|
||||||
|
item_boundingbox = pad->GetBoundingBox();
|
||||||
|
item_boundingbox.Inflate( item_clearance );
|
||||||
|
|
||||||
|
if( item_boundingbox.Intersects( zone_boundingbox ) )
|
||||||
|
{
|
||||||
|
int clearance = std::max( zone_clearance, item_clearance );
|
||||||
|
|
||||||
|
// PAD_SHAPE_CUSTOM can have a specific keepout, to avoid to break the shape
|
||||||
|
if( pad->GetShape() == PAD_SHAPE_CUSTOM &&
|
||||||
|
pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
|
||||||
|
{
|
||||||
|
// the pad shape in zone can be its convex hull or
|
||||||
|
// the shape itself
|
||||||
|
SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() );
|
||||||
|
outline.Inflate( KiROUND( clearance*correctionFactor) , segsPerCircle );
|
||||||
|
pad->CustomShapeAsPolygonToBoardPosition( &outline,
|
||||||
|
pad->GetPosition(), pad->GetOrientation() );
|
||||||
|
|
||||||
|
if( pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
|
||||||
|
{
|
||||||
|
std::vector<wxPoint> convex_hull;
|
||||||
|
BuildConvexHull( convex_hull, outline );
|
||||||
|
|
||||||
|
aFeatures.NewOutline();
|
||||||
|
for( unsigned ii = 0; ii < convex_hull.size(); ++ii )
|
||||||
|
aFeatures.Append( convex_hull[ii] );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
aFeatures.Append( outline );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pad->TransformShapeWithClearanceToPolygon( aFeatures,
|
||||||
|
clearance,
|
||||||
|
segsPerCircle,
|
||||||
|
correctionFactor );
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pads are removed from zone if the setup is PAD_ZONE_CONN_NONE
|
||||||
|
// or if they have a custom shape, because a thermal relief will break
|
||||||
|
// the shape
|
||||||
|
if( GetPadConnection( pad ) == PAD_ZONE_CONN_NONE ||
|
||||||
|
pad->GetShape() == PAD_SHAPE_CUSTOM )
|
||||||
|
{
|
||||||
|
int gap = zone_clearance;
|
||||||
|
int thermalGap = GetThermalReliefGap( pad );
|
||||||
|
gap = std::max( gap, thermalGap );
|
||||||
|
item_boundingbox = pad->GetBoundingBox();
|
||||||
|
item_boundingbox.Inflate( gap );
|
||||||
|
|
||||||
|
if( item_boundingbox.Intersects( zone_boundingbox ) )
|
||||||
|
{
|
||||||
|
// PAD_SHAPE_CUSTOM has a specific keepout, to avoid to break the shape
|
||||||
|
// the pad shape in zone can be its convex hull or the shape itself
|
||||||
|
if( pad->GetShape() == PAD_SHAPE_CUSTOM &&
|
||||||
|
pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
|
||||||
|
{
|
||||||
|
// the pad shape in zone can be its convex hull or
|
||||||
|
// the shape itself
|
||||||
|
SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() );
|
||||||
|
outline.Inflate( KiROUND( gap*correctionFactor) , segsPerCircle );
|
||||||
|
pad->CustomShapeAsPolygonToBoardPosition( &outline,
|
||||||
|
pad->GetPosition(), pad->GetOrientation() );
|
||||||
|
|
||||||
|
std::vector<wxPoint> convex_hull;
|
||||||
|
BuildConvexHull( convex_hull, outline );
|
||||||
|
|
||||||
|
aFeatures.NewOutline();
|
||||||
|
for( unsigned ii = 0; ii < convex_hull.size(); ++ii )
|
||||||
|
aFeatures.Append( convex_hull[ii] );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pad->TransformShapeWithClearanceToPolygon( aFeatures,
|
||||||
|
gap, segsPerCircle, correctionFactor );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add holes (i.e. tracks and vias areas as polygons outlines)
|
||||||
|
* in cornerBufferPolysToSubstract
|
||||||
|
*/
|
||||||
|
for( TRACK* track = aPcb->m_Track; track; track = track->Next() )
|
||||||
|
{
|
||||||
|
if( !track->IsOnLayer( GetLayer() ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( track->GetNetCode() == GetNetCode() && (GetNetCode() != 0) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int item_clearance = track->GetClearance() + outline_half_thickness;
|
||||||
|
item_boundingbox = track->GetBoundingBox();
|
||||||
|
|
||||||
|
if( item_boundingbox.Intersects( zone_boundingbox ) )
|
||||||
|
{
|
||||||
|
int clearance = std::max( zone_clearance, item_clearance );
|
||||||
|
track->TransformShapeWithClearanceToPolygon( aFeatures,
|
||||||
|
clearance,
|
||||||
|
segsPerCircle,
|
||||||
|
correctionFactor );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add module edge items that are on copper layers
|
||||||
|
* Pcbnew allows these items to be on copper layers in microwave applictions
|
||||||
|
* This is a bad thing, but must be handled here, until a better way is found
|
||||||
|
*/
|
||||||
|
for( MODULE* module = aPcb->m_Modules; module; module = module->Next() )
|
||||||
|
{
|
||||||
|
for( BOARD_ITEM* item = module->GraphicalItemsList(); item; item = item->Next() )
|
||||||
|
{
|
||||||
|
if( !item->IsOnLayer( GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( item->Type() != PCB_MODULE_EDGE_T )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
item_boundingbox = item->GetBoundingBox();
|
||||||
|
|
||||||
|
if( item_boundingbox.Intersects( zone_boundingbox ) )
|
||||||
|
{
|
||||||
|
int zclearance = zone_clearance;
|
||||||
|
|
||||||
|
if( item->IsOnLayer( Edge_Cuts ) )
|
||||||
|
// use only the m_ZoneClearance, not the clearance using
|
||||||
|
// the netclass value, because we do not have a copper item
|
||||||
|
zclearance = zone_to_edgecut_clearance;
|
||||||
|
|
||||||
|
( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon(
|
||||||
|
aFeatures, zclearance, segsPerCircle, correctionFactor );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add graphic items (copper texts) and board edges
|
||||||
|
// Currently copper texts have no net, so only the zone_clearance
|
||||||
|
// is used.
|
||||||
|
for( auto item : aPcb->Drawings() )
|
||||||
|
{
|
||||||
|
if( item->GetLayer() != GetLayer() && item->GetLayer() != Edge_Cuts )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int zclearance = zone_clearance;
|
||||||
|
|
||||||
|
if( item->GetLayer() == Edge_Cuts )
|
||||||
|
// use only the m_ZoneClearance, not the clearance using
|
||||||
|
// the netclass value, because we do not have a copper item
|
||||||
|
zclearance = zone_to_edgecut_clearance;
|
||||||
|
|
||||||
|
switch( item->Type() )
|
||||||
|
{
|
||||||
|
case PCB_LINE_T:
|
||||||
|
( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
|
||||||
|
aFeatures,
|
||||||
|
zclearance, segsPerCircle, correctionFactor );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PCB_TEXT_T:
|
||||||
|
( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon(
|
||||||
|
aFeatures, zclearance );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add zones outlines having an higher priority and keepout
|
||||||
|
for( int ii = 0; ii < GetBoard()->GetAreaCount(); ii++ )
|
||||||
|
{
|
||||||
|
ZONE_CONTAINER* zone = GetBoard()->GetArea( ii );
|
||||||
|
|
||||||
|
// If the zones share no common layers
|
||||||
|
if( !CommonLayerExists( zone->GetLayerSet() ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( !zone->GetIsKeepout() && zone->GetPriority() <= GetPriority() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( zone->GetIsKeepout() && ! zone->GetDoNotAllowCopperPour() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// A highter priority zone or keepout area is found: remove this area
|
||||||
|
item_boundingbox = zone->GetBoundingBox();
|
||||||
|
|
||||||
|
if( !item_boundingbox.Intersects( zone_boundingbox ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Add the zone outline area.
|
||||||
|
// However if the zone has the same net as the current zone,
|
||||||
|
// do not add any clearance.
|
||||||
|
// the zone will be connected to the current zone, but filled areas
|
||||||
|
// will use different parameters (clearance, thermal shapes )
|
||||||
|
bool same_net = GetNetCode() == zone->GetNetCode();
|
||||||
|
bool use_net_clearance = true;
|
||||||
|
int min_clearance = zone_clearance;
|
||||||
|
|
||||||
|
// Do not forget to make room to draw the thick outlines
|
||||||
|
// of the hole created by the area of the zone to remove
|
||||||
|
int holeclearance = zone->GetClearance() + outline_half_thickness;
|
||||||
|
|
||||||
|
// The final clearance is obviously the max value of each zone clearance
|
||||||
|
min_clearance = std::max( min_clearance, holeclearance );
|
||||||
|
|
||||||
|
if( zone->GetIsKeepout() || same_net )
|
||||||
|
{
|
||||||
|
// Just take in account the fact the outline has a thickness, so
|
||||||
|
// the actual area to substract is inflated to take in account this fact
|
||||||
|
min_clearance = outline_half_thickness;
|
||||||
|
use_net_clearance = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
zone->TransformOutlinesShapeWithClearanceToPolygon(
|
||||||
|
aFeatures, min_clearance, use_net_clearance );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove thermal symbols
|
||||||
|
for( MODULE* module = aPcb->m_Modules; module; module = module->Next() )
|
||||||
|
{
|
||||||
|
for( D_PAD* pad = module->PadsList(); pad != NULL; pad = pad->Next() )
|
||||||
|
{
|
||||||
|
// Rejects non-standard pads with tht-only thermal reliefs
|
||||||
|
if( GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL
|
||||||
|
&& pad->GetAttribute() != PAD_ATTRIB_STANDARD )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL
|
||||||
|
&& GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( !pad->IsOnLayer( GetLayer() ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( pad->GetNetCode() != GetNetCode() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
item_boundingbox = pad->GetBoundingBox();
|
||||||
|
int thermalGap = GetThermalReliefGap( pad );
|
||||||
|
item_boundingbox.Inflate( thermalGap, thermalGap );
|
||||||
|
|
||||||
|
if( item_boundingbox.Intersects( zone_boundingbox ) )
|
||||||
|
{
|
||||||
|
CreateThermalReliefPadPolygon( aFeatures,
|
||||||
|
*pad, thermalGap,
|
||||||
|
GetThermalReliefCopperBridge( pad ),
|
||||||
|
m_ZoneMinThickness,
|
||||||
|
segsPerCircle,
|
||||||
|
correctionFactor, s_thermalRot );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function ComputeRawFilledAreas
|
||||||
|
* Supports a min thickness area constraint.
|
||||||
|
* Add non copper areas polygons (pads and tracks with clearance)
|
||||||
|
* to the filled copper area found
|
||||||
|
* in BuildFilledPolysListData after calculating filled areas in a zone
|
||||||
|
* Non filled copper areas are pads and track and their clearance areas
|
||||||
|
* The filled copper area must be computed just before.
|
||||||
|
* BuildFilledPolysListData() call this function just after creating the
|
||||||
|
* filled copper area polygon (without clearance areas)
|
||||||
|
* to do that this function:
|
||||||
|
* 1 - Creates the main outline (zone outline) using a correction to shrink the resulting area
|
||||||
|
* with m_ZoneMinThickness/2 value.
|
||||||
|
* The result is areas with a margin of m_ZoneMinThickness/2
|
||||||
|
* When drawing outline with segments having a thickness of m_ZoneMinThickness, the
|
||||||
|
* outlines will match exactly the initial outlines
|
||||||
|
* 3 - Add all non filled areas (pads, tracks) in group B with a clearance of m_Clearance +
|
||||||
|
* m_ZoneMinThickness/2
|
||||||
|
* in a buffer
|
||||||
|
* - If Thermal shapes are wanted, add non filled area, in order to create these thermal shapes
|
||||||
|
* 4 - calculates the polygon A - B
|
||||||
|
* 5 - put resulting list of polygons (filled areas) in m_FilledPolysList
|
||||||
|
* This zone contains pads with the same net.
|
||||||
|
* 6 - Remove insulated copper islands
|
||||||
|
* 7 - If Thermal shapes are wanted, remove unconnected stubs in thermal shapes:
|
||||||
|
* creates a buffer of polygons corresponding to stubs to remove
|
||||||
|
* sub them to the filled areas.
|
||||||
|
* Remove new insulated copper islands
|
||||||
|
*/
|
||||||
|
|
||||||
|
void ZONE_CONTAINER::ComputeRawFilledAreas( BOARD* aPcb )
|
||||||
|
{
|
||||||
|
int segsPerCircle;
|
||||||
|
double correctionFactor;
|
||||||
|
int outline_half_thickness = m_ZoneMinThickness / 2;
|
||||||
|
|
||||||
|
|
||||||
|
std::unique_ptr<SHAPE_FILE_IO> dumper( new SHAPE_FILE_IO(
|
||||||
|
s_DumpZonesWhenFilling ? "zones_dump.txt" : "", SHAPE_FILE_IO::IOM_APPEND ) );
|
||||||
|
|
||||||
|
// Set the number of segments in arc approximations
|
||||||
|
if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF )
|
||||||
|
segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF;
|
||||||
|
else
|
||||||
|
segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF;
|
||||||
|
|
||||||
|
/* calculates the coeff to compensate radius reduction of holes clearance
|
||||||
|
* due to the segment approx.
|
||||||
|
* For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
|
||||||
|
* s_Correction is 1 /cos( PI/s_CircleToSegmentsCount )
|
||||||
|
*/
|
||||||
|
correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle );
|
||||||
|
|
||||||
|
CPOLYGONS_LIST tmp;
|
||||||
|
|
||||||
|
if(s_DumpZonesWhenFilling)
|
||||||
|
dumper->BeginGroup("clipper-zone");
|
||||||
|
|
||||||
|
SHAPE_POLY_SET solidAreas = *m_smoothedPoly;
|
||||||
|
|
||||||
|
solidAreas.Inflate( -outline_half_thickness, segsPerCircle );
|
||||||
|
solidAreas.Simplify( POLY_CALC_MODE );
|
||||||
|
|
||||||
|
SHAPE_POLY_SET holes;
|
||||||
|
|
||||||
|
if(s_DumpZonesWhenFilling)
|
||||||
|
dumper->Write( &solidAreas, "solid-areas" );
|
||||||
|
|
||||||
|
tmp.RemoveAllContours();
|
||||||
|
buildFeatureHoleList( aPcb, holes );
|
||||||
|
|
||||||
|
if(s_DumpZonesWhenFilling)
|
||||||
|
dumper->Write( &holes, "feature-holes" );
|
||||||
|
|
||||||
|
holes.Simplify( POLY_CALC_MODE );
|
||||||
|
|
||||||
|
if (s_DumpZonesWhenFilling)
|
||||||
|
dumper->Write( &holes, "feature-holes-postsimplify" );
|
||||||
|
|
||||||
|
// Generate the filled areas (currently, without thermal shapes, which will
|
||||||
|
// be created later).
|
||||||
|
// Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to generate strictly simple polygons
|
||||||
|
// needed by Gerber files and Fracture()
|
||||||
|
solidAreas.BooleanSubtract( holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||||
|
|
||||||
|
if (s_DumpZonesWhenFilling)
|
||||||
|
dumper->Write( &solidAreas, "solid-areas-minus-holes" );
|
||||||
|
|
||||||
|
SHAPE_POLY_SET areas_fractured = solidAreas;
|
||||||
|
areas_fractured.Fracture( POLY_CALC_MODE );
|
||||||
|
|
||||||
|
if (s_DumpZonesWhenFilling)
|
||||||
|
dumper->Write( &areas_fractured, "areas_fractured" );
|
||||||
|
|
||||||
|
m_FilledPolysList = areas_fractured;
|
||||||
|
|
||||||
|
SHAPE_POLY_SET thermalHoles;
|
||||||
|
|
||||||
|
// Test thermal stubs connections and add polygons to remove unconnected stubs.
|
||||||
|
// (this is a refinement for thermal relief shapes)
|
||||||
|
if( GetNetCode() > 0 )
|
||||||
|
BuildUnconnectedThermalStubsPolygonList( thermalHoles, aPcb, this,
|
||||||
|
correctionFactor, s_thermalRot );
|
||||||
|
|
||||||
|
// remove copper areas corresponding to not connected stubs
|
||||||
|
if( !thermalHoles.IsEmpty() )
|
||||||
|
{
|
||||||
|
thermalHoles.Simplify( POLY_CALC_MODE );
|
||||||
|
// Remove unconnected stubs. Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to
|
||||||
|
// generate strictly simple polygons
|
||||||
|
// needed by Gerber files and Fracture()
|
||||||
|
solidAreas.BooleanSubtract( thermalHoles, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||||
|
|
||||||
|
if( s_DumpZonesWhenFilling )
|
||||||
|
dumper->Write( &thermalHoles, "thermal-holes" );
|
||||||
|
|
||||||
|
// put these areas in m_FilledPolysList
|
||||||
|
SHAPE_POLY_SET th_fractured = solidAreas;
|
||||||
|
th_fractured.Fracture( POLY_CALC_MODE );
|
||||||
|
|
||||||
|
if( s_DumpZonesWhenFilling )
|
||||||
|
dumper->Write ( &th_fractured, "th_fractured" );
|
||||||
|
|
||||||
|
m_FilledPolysList = th_fractured;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
m_RawPolysList = m_FilledPolysList;
|
||||||
|
|
||||||
|
if(s_DumpZonesWhenFilling)
|
||||||
|
dumper->EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZONE_CONTAINER::RemoveInsulatedCopperIslands( BOARD* aPcb )
|
||||||
|
{
|
||||||
|
std::vector<int> islands;
|
||||||
|
|
||||||
|
auto connectivity = aPcb->GetConnectivity();
|
||||||
|
|
||||||
|
connectivity->FindIsolatedCopperIslands( this, islands );
|
||||||
|
|
||||||
|
std::sort( islands.begin(), islands.end(), std::greater<int>() );
|
||||||
|
|
||||||
|
for( auto idx : islands )
|
||||||
|
{
|
||||||
|
m_FilledPolysList.DeletePolygon( idx );
|
||||||
|
}
|
||||||
|
|
||||||
|
connectivity->Update( this );
|
||||||
|
}
|
||||||
|
|
|
@ -128,9 +128,15 @@ int PCB_EDIT_FRAME::Fill_Zone( ZONE_CONTAINER* aZone )
|
||||||
//OnModify();
|
//OnModify();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int PCB_EDIT_FRAME::Fill_All_Zones( wxWindow * aActiveWindow, bool aVerbose )
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
int PCB_EDIT_FRAME::Fill_All_Zones( wxWindow * aActiveWindow, bool aVerbose )
|
int PCB_EDIT_FRAME::Fill_All_Zones( wxWindow * aActiveWindow, bool aVerbose )
|
||||||
{
|
{
|
||||||
int errorLevel = 0;
|
int errorLevel = 0;
|
||||||
|
@ -197,3 +203,4 @@ int PCB_EDIT_FRAME::Fill_All_Zones( wxWindow * aActiveWindow, bool aVerbose )
|
||||||
|
|
||||||
return errorLevel;
|
return errorLevel;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -1,587 +0,0 @@
|
||||||
/*
|
|
||||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
|
||||||
* Copyright (C) 1992-2016 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
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Functions to convert some board items to polygons
|
|
||||||
* (pads, tracks ..)
|
|
||||||
* This is used to calculate filled areas in copper zones.
|
|
||||||
* Filled areas are areas remainder of the full zone area after removed all polygons
|
|
||||||
* calculated from these items shapes and the clearance area
|
|
||||||
*
|
|
||||||
* Important note:
|
|
||||||
* Because filled areas must have a minimum thickness to match with Design rule, they are
|
|
||||||
* draw in 2 step:
|
|
||||||
* 1 - filled polygons are drawn
|
|
||||||
* 2 - polygon outlines are drawn with a "minimum thickness width" ( or with a minimum
|
|
||||||
* thickness pen )
|
|
||||||
* So outlines of filled polygons are calculated with the constraint they match with clearance,
|
|
||||||
* taking in account outlines have thickness
|
|
||||||
* This ensures:
|
|
||||||
* - areas meet the minimum thickness requirement.
|
|
||||||
* - shapes are smoothed.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Polygon calculations can use fast mode or force strickly simple polygons after calculations
|
|
||||||
// Forcing strickly simple polygons is time consuming, and we have not see issues in fast mode
|
|
||||||
// so we use fast mode when possible (intermediate calculations)
|
|
||||||
// (choice is SHAPE_POLY_SET::PM_STRICTLY_SIMPLE or SHAPE_POLY_SET::PM_FAST)
|
|
||||||
#define POLY_CALC_MODE SHAPE_POLY_SET::PM_FAST
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include <fctsys.h>
|
|
||||||
#include <wxPcbStruct.h>
|
|
||||||
#include <trigo.h>
|
|
||||||
|
|
||||||
#include <class_board.h>
|
|
||||||
#include <class_module.h>
|
|
||||||
#include <class_track.h>
|
|
||||||
#include <class_edge_mod.h>
|
|
||||||
#include <class_drawsegment.h>
|
|
||||||
#include <class_pcb_text.h>
|
|
||||||
#include <class_zone.h>
|
|
||||||
#include <project.h>
|
|
||||||
|
|
||||||
#include <pcbnew.h>
|
|
||||||
#include <zones.h>
|
|
||||||
#include <convert_basic_shapes_to_polygon.h>
|
|
||||||
|
|
||||||
#include <geometry/shape_poly_set.h>
|
|
||||||
#include <geometry/shape_file_io.h>
|
|
||||||
#include <geometry/convex_hull.h>
|
|
||||||
|
|
||||||
/* DEBUG OPTION:
|
|
||||||
* To emit zone data to a file when filling zones for the debugging purposes,
|
|
||||||
* set this 'true' and build.
|
|
||||||
*/
|
|
||||||
static const bool s_DumpZonesWhenFilling = false;
|
|
||||||
|
|
||||||
extern void BuildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer,
|
|
||||||
BOARD* aPcb, ZONE_CONTAINER* aZone,
|
|
||||||
double aArcCorrection,
|
|
||||||
double aRoundPadThermalRotation);
|
|
||||||
|
|
||||||
extern void Test_For_Copper_Island_And_Remove( BOARD* aPcb,
|
|
||||||
ZONE_CONTAINER* aZone_container );
|
|
||||||
|
|
||||||
extern void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|
||||||
D_PAD& aPad,
|
|
||||||
int aThermalGap,
|
|
||||||
int aCopperThickness,
|
|
||||||
int aMinThicknessValue,
|
|
||||||
int aCircleToSegmentsCount,
|
|
||||||
double aCorrectionFactor,
|
|
||||||
double aThermalRot );
|
|
||||||
|
|
||||||
// Local Variables:
|
|
||||||
static double s_thermalRot = 450; // angle of stubs in thermal reliefs for round pads
|
|
||||||
|
|
||||||
void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeatures )
|
|
||||||
{
|
|
||||||
int segsPerCircle;
|
|
||||||
double correctionFactor;
|
|
||||||
|
|
||||||
// Set the number of segments in arc approximations
|
|
||||||
if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF )
|
|
||||||
segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF;
|
|
||||||
else
|
|
||||||
segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF;
|
|
||||||
|
|
||||||
/* calculates the coeff to compensate radius reduction of holes clearance
|
|
||||||
* due to the segment approx.
|
|
||||||
* For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
|
|
||||||
* s_Correction is 1 /cos( PI/s_CircleToSegmentsCount )
|
|
||||||
*/
|
|
||||||
correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle );
|
|
||||||
|
|
||||||
aFeatures.RemoveAllContours();
|
|
||||||
|
|
||||||
int outline_half_thickness = m_ZoneMinThickness / 2;
|
|
||||||
|
|
||||||
// When removing holes, the holes must be expanded by outline_half_thickness
|
|
||||||
// to take in account the thickness of the zone outlines
|
|
||||||
int zone_clearance = GetClearance() + outline_half_thickness;
|
|
||||||
|
|
||||||
// When holes are created by non copper items (edge cut items), use only
|
|
||||||
// the m_ZoneClearance parameter (zone clearance with no netclass clearance)
|
|
||||||
int zone_to_edgecut_clearance = GetZoneClearance() + outline_half_thickness;
|
|
||||||
|
|
||||||
/* store holes (i.e. tracks and pads areas as polygons outlines)
|
|
||||||
* in a polygon list
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* items ouside the zone bounding box are skipped
|
|
||||||
* the bounding box is the zone bounding box + the biggest clearance found in Netclass list
|
|
||||||
*/
|
|
||||||
EDA_RECT item_boundingbox;
|
|
||||||
EDA_RECT zone_boundingbox = GetBoundingBox();
|
|
||||||
int biggest_clearance = aPcb->GetDesignSettings().GetBiggestClearanceValue();
|
|
||||||
biggest_clearance = std::max( biggest_clearance, zone_clearance );
|
|
||||||
zone_boundingbox.Inflate( biggest_clearance );
|
|
||||||
|
|
||||||
/*
|
|
||||||
* First : Add pads. Note: pads having the same net as zone are left in zone.
|
|
||||||
* Thermal shapes will be created later if necessary
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Use a dummy pad to calculate hole clearance when a pad is not on all copper layers
|
|
||||||
* and this pad has a hole
|
|
||||||
* This dummy pad has the size and shape of the hole
|
|
||||||
* Therefore, this dummy pad is a circle or an oval.
|
|
||||||
* A pad must have a parent because some functions expect a non null parent
|
|
||||||
* to find the parent board, and some other data
|
|
||||||
*/
|
|
||||||
MODULE dummymodule( aPcb ); // Creates a dummy parent
|
|
||||||
D_PAD dummypad( &dummymodule );
|
|
||||||
|
|
||||||
for( MODULE* module = aPcb->m_Modules; module; module = module->Next() )
|
|
||||||
{
|
|
||||||
D_PAD* nextpad;
|
|
||||||
|
|
||||||
for( D_PAD* pad = module->PadsList(); pad != NULL; pad = nextpad )
|
|
||||||
{
|
|
||||||
nextpad = pad->Next(); // pad pointer can be modified by next code, so
|
|
||||||
// calculate the next pad here
|
|
||||||
|
|
||||||
if( !pad->IsOnLayer( GetLayer() ) )
|
|
||||||
{
|
|
||||||
/* Test for pads that are on top or bottom only and have a hole.
|
|
||||||
* There are curious pads but they can be used for some components that are
|
|
||||||
* inside the board (in fact inside the hole. Some photo diodes and Leds are
|
|
||||||
* like this)
|
|
||||||
*/
|
|
||||||
if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Use a dummy pad to calculate a hole shape that have the same dimension as
|
|
||||||
// the pad hole
|
|
||||||
dummypad.SetSize( pad->GetDrillSize() );
|
|
||||||
dummypad.SetOrientation( pad->GetOrientation() );
|
|
||||||
dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
|
|
||||||
PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE );
|
|
||||||
dummypad.SetPosition( pad->GetPosition() );
|
|
||||||
|
|
||||||
pad = &dummypad;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: netcode <=0 means not connected item
|
|
||||||
if( ( pad->GetNetCode() != GetNetCode() ) || ( pad->GetNetCode() <= 0 ) )
|
|
||||||
{
|
|
||||||
int item_clearance = pad->GetClearance() + outline_half_thickness;
|
|
||||||
item_boundingbox = pad->GetBoundingBox();
|
|
||||||
item_boundingbox.Inflate( item_clearance );
|
|
||||||
|
|
||||||
if( item_boundingbox.Intersects( zone_boundingbox ) )
|
|
||||||
{
|
|
||||||
int clearance = std::max( zone_clearance, item_clearance );
|
|
||||||
|
|
||||||
// PAD_SHAPE_CUSTOM can have a specific keepout, to avoid to break the shape
|
|
||||||
if( pad->GetShape() == PAD_SHAPE_CUSTOM &&
|
|
||||||
pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
|
|
||||||
{
|
|
||||||
// the pad shape in zone can be its convex hull or
|
|
||||||
// the shape itself
|
|
||||||
SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() );
|
|
||||||
outline.Inflate( KiROUND( clearance*correctionFactor) , segsPerCircle );
|
|
||||||
pad->CustomShapeAsPolygonToBoardPosition( &outline,
|
|
||||||
pad->GetPosition(), pad->GetOrientation() );
|
|
||||||
|
|
||||||
if( pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
|
|
||||||
{
|
|
||||||
std::vector<wxPoint> convex_hull;
|
|
||||||
BuildConvexHull( convex_hull, outline );
|
|
||||||
|
|
||||||
aFeatures.NewOutline();
|
|
||||||
for( unsigned ii = 0; ii < convex_hull.size(); ++ii )
|
|
||||||
aFeatures.Append( convex_hull[ii] );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
aFeatures.Append( outline );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
pad->TransformShapeWithClearanceToPolygon( aFeatures,
|
|
||||||
clearance,
|
|
||||||
segsPerCircle,
|
|
||||||
correctionFactor );
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pads are removed from zone if the setup is PAD_ZONE_CONN_NONE
|
|
||||||
// or if they have a custom shape, because a thermal relief will break
|
|
||||||
// the shape
|
|
||||||
if( GetPadConnection( pad ) == PAD_ZONE_CONN_NONE ||
|
|
||||||
pad->GetShape() == PAD_SHAPE_CUSTOM )
|
|
||||||
{
|
|
||||||
int gap = zone_clearance;
|
|
||||||
int thermalGap = GetThermalReliefGap( pad );
|
|
||||||
gap = std::max( gap, thermalGap );
|
|
||||||
item_boundingbox = pad->GetBoundingBox();
|
|
||||||
item_boundingbox.Inflate( gap );
|
|
||||||
|
|
||||||
if( item_boundingbox.Intersects( zone_boundingbox ) )
|
|
||||||
{
|
|
||||||
// PAD_SHAPE_CUSTOM has a specific keepout, to avoid to break the shape
|
|
||||||
// the pad shape in zone can be its convex hull or the shape itself
|
|
||||||
if( pad->GetShape() == PAD_SHAPE_CUSTOM &&
|
|
||||||
pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
|
|
||||||
{
|
|
||||||
// the pad shape in zone can be its convex hull or
|
|
||||||
// the shape itself
|
|
||||||
SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() );
|
|
||||||
outline.Inflate( KiROUND( gap*correctionFactor) , segsPerCircle );
|
|
||||||
pad->CustomShapeAsPolygonToBoardPosition( &outline,
|
|
||||||
pad->GetPosition(), pad->GetOrientation() );
|
|
||||||
|
|
||||||
std::vector<wxPoint> convex_hull;
|
|
||||||
BuildConvexHull( convex_hull, outline );
|
|
||||||
|
|
||||||
aFeatures.NewOutline();
|
|
||||||
for( unsigned ii = 0; ii < convex_hull.size(); ++ii )
|
|
||||||
aFeatures.Append( convex_hull[ii] );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
pad->TransformShapeWithClearanceToPolygon( aFeatures,
|
|
||||||
gap, segsPerCircle, correctionFactor );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add holes (i.e. tracks and vias areas as polygons outlines)
|
|
||||||
* in cornerBufferPolysToSubstract
|
|
||||||
*/
|
|
||||||
for( TRACK* track = aPcb->m_Track; track; track = track->Next() )
|
|
||||||
{
|
|
||||||
if( !track->IsOnLayer( GetLayer() ) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( track->GetNetCode() == GetNetCode() && (GetNetCode() != 0) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int item_clearance = track->GetClearance() + outline_half_thickness;
|
|
||||||
item_boundingbox = track->GetBoundingBox();
|
|
||||||
|
|
||||||
if( item_boundingbox.Intersects( zone_boundingbox ) )
|
|
||||||
{
|
|
||||||
int clearance = std::max( zone_clearance, item_clearance );
|
|
||||||
track->TransformShapeWithClearanceToPolygon( aFeatures,
|
|
||||||
clearance,
|
|
||||||
segsPerCircle,
|
|
||||||
correctionFactor );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add module edge items that are on copper layers
|
|
||||||
* Pcbnew allows these items to be on copper layers in microwave applictions
|
|
||||||
* This is a bad thing, but must be handled here, until a better way is found
|
|
||||||
*/
|
|
||||||
for( MODULE* module = aPcb->m_Modules; module; module = module->Next() )
|
|
||||||
{
|
|
||||||
for( BOARD_ITEM* item = module->GraphicalItemsList(); item; item = item->Next() )
|
|
||||||
{
|
|
||||||
if( !item->IsOnLayer( GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( item->Type() != PCB_MODULE_EDGE_T )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
item_boundingbox = item->GetBoundingBox();
|
|
||||||
|
|
||||||
if( item_boundingbox.Intersects( zone_boundingbox ) )
|
|
||||||
{
|
|
||||||
int zclearance = zone_clearance;
|
|
||||||
|
|
||||||
if( item->IsOnLayer( Edge_Cuts ) )
|
|
||||||
// use only the m_ZoneClearance, not the clearance using
|
|
||||||
// the netclass value, because we do not have a copper item
|
|
||||||
zclearance = zone_to_edgecut_clearance;
|
|
||||||
|
|
||||||
( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon(
|
|
||||||
aFeatures, zclearance, segsPerCircle, correctionFactor );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add graphic items (copper texts) and board edges
|
|
||||||
// Currently copper texts have no net, so only the zone_clearance
|
|
||||||
// is used.
|
|
||||||
for( auto item : aPcb->Drawings() )
|
|
||||||
{
|
|
||||||
if( item->GetLayer() != GetLayer() && item->GetLayer() != Edge_Cuts )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int zclearance = zone_clearance;
|
|
||||||
|
|
||||||
if( item->GetLayer() == Edge_Cuts )
|
|
||||||
// use only the m_ZoneClearance, not the clearance using
|
|
||||||
// the netclass value, because we do not have a copper item
|
|
||||||
zclearance = zone_to_edgecut_clearance;
|
|
||||||
|
|
||||||
switch( item->Type() )
|
|
||||||
{
|
|
||||||
case PCB_LINE_T:
|
|
||||||
( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
|
|
||||||
aFeatures,
|
|
||||||
zclearance, segsPerCircle, correctionFactor );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PCB_TEXT_T:
|
|
||||||
( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon(
|
|
||||||
aFeatures, zclearance );
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add zones outlines having an higher priority and keepout
|
|
||||||
for( int ii = 0; ii < GetBoard()->GetAreaCount(); ii++ )
|
|
||||||
{
|
|
||||||
ZONE_CONTAINER* zone = GetBoard()->GetArea( ii );
|
|
||||||
|
|
||||||
// If the zones share no common layers
|
|
||||||
if( !CommonLayerExists( zone->GetLayerSet() ) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( !zone->GetIsKeepout() && zone->GetPriority() <= GetPriority() )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( zone->GetIsKeepout() && ! zone->GetDoNotAllowCopperPour() )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// A highter priority zone or keepout area is found: remove this area
|
|
||||||
item_boundingbox = zone->GetBoundingBox();
|
|
||||||
|
|
||||||
if( !item_boundingbox.Intersects( zone_boundingbox ) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Add the zone outline area.
|
|
||||||
// However if the zone has the same net as the current zone,
|
|
||||||
// do not add any clearance.
|
|
||||||
// the zone will be connected to the current zone, but filled areas
|
|
||||||
// will use different parameters (clearance, thermal shapes )
|
|
||||||
bool same_net = GetNetCode() == zone->GetNetCode();
|
|
||||||
bool use_net_clearance = true;
|
|
||||||
int min_clearance = zone_clearance;
|
|
||||||
|
|
||||||
// Do not forget to make room to draw the thick outlines
|
|
||||||
// of the hole created by the area of the zone to remove
|
|
||||||
int holeclearance = zone->GetClearance() + outline_half_thickness;
|
|
||||||
|
|
||||||
// The final clearance is obviously the max value of each zone clearance
|
|
||||||
min_clearance = std::max( min_clearance, holeclearance );
|
|
||||||
|
|
||||||
if( zone->GetIsKeepout() || same_net )
|
|
||||||
{
|
|
||||||
// Just take in account the fact the outline has a thickness, so
|
|
||||||
// the actual area to substract is inflated to take in account this fact
|
|
||||||
min_clearance = outline_half_thickness;
|
|
||||||
use_net_clearance = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
zone->TransformOutlinesShapeWithClearanceToPolygon(
|
|
||||||
aFeatures, min_clearance, use_net_clearance );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove thermal symbols
|
|
||||||
for( MODULE* module = aPcb->m_Modules; module; module = module->Next() )
|
|
||||||
{
|
|
||||||
for( D_PAD* pad = module->PadsList(); pad != NULL; pad = pad->Next() )
|
|
||||||
{
|
|
||||||
// Rejects non-standard pads with tht-only thermal reliefs
|
|
||||||
if( GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL
|
|
||||||
&& pad->GetAttribute() != PAD_ATTRIB_STANDARD )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL
|
|
||||||
&& GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( !pad->IsOnLayer( GetLayer() ) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( pad->GetNetCode() != GetNetCode() )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
item_boundingbox = pad->GetBoundingBox();
|
|
||||||
int thermalGap = GetThermalReliefGap( pad );
|
|
||||||
item_boundingbox.Inflate( thermalGap, thermalGap );
|
|
||||||
|
|
||||||
if( item_boundingbox.Intersects( zone_boundingbox ) )
|
|
||||||
{
|
|
||||||
CreateThermalReliefPadPolygon( aFeatures,
|
|
||||||
*pad, thermalGap,
|
|
||||||
GetThermalReliefCopperBridge( pad ),
|
|
||||||
m_ZoneMinThickness,
|
|
||||||
segsPerCircle,
|
|
||||||
correctionFactor, s_thermalRot );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function AddClearanceAreasPolygonsToPolysList
|
|
||||||
* Supports a min thickness area constraint.
|
|
||||||
* Add non copper areas polygons (pads and tracks with clearance)
|
|
||||||
* to the filled copper area found
|
|
||||||
* in BuildFilledPolysListData after calculating filled areas in a zone
|
|
||||||
* Non filled copper areas are pads and track and their clearance areas
|
|
||||||
* The filled copper area must be computed just before.
|
|
||||||
* BuildFilledPolysListData() call this function just after creating the
|
|
||||||
* filled copper area polygon (without clearance areas)
|
|
||||||
* to do that this function:
|
|
||||||
* 1 - Creates the main outline (zone outline) using a correction to shrink the resulting area
|
|
||||||
* with m_ZoneMinThickness/2 value.
|
|
||||||
* The result is areas with a margin of m_ZoneMinThickness/2
|
|
||||||
* When drawing outline with segments having a thickness of m_ZoneMinThickness, the
|
|
||||||
* outlines will match exactly the initial outlines
|
|
||||||
* 3 - Add all non filled areas (pads, tracks) in group B with a clearance of m_Clearance +
|
|
||||||
* m_ZoneMinThickness/2
|
|
||||||
* in a buffer
|
|
||||||
* - If Thermal shapes are wanted, add non filled area, in order to create these thermal shapes
|
|
||||||
* 4 - calculates the polygon A - B
|
|
||||||
* 5 - put resulting list of polygons (filled areas) in m_FilledPolysList
|
|
||||||
* This zone contains pads with the same net.
|
|
||||||
* 6 - Remove insulated copper islands
|
|
||||||
* 7 - If Thermal shapes are wanted, remove unconnected stubs in thermal shapes:
|
|
||||||
* creates a buffer of polygons corresponding to stubs to remove
|
|
||||||
* sub them to the filled areas.
|
|
||||||
* Remove new insulated copper islands
|
|
||||||
*/
|
|
||||||
|
|
||||||
void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb )
|
|
||||||
{
|
|
||||||
int segsPerCircle;
|
|
||||||
double correctionFactor;
|
|
||||||
int outline_half_thickness = m_ZoneMinThickness / 2;
|
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<SHAPE_FILE_IO> dumper( new SHAPE_FILE_IO(
|
|
||||||
s_DumpZonesWhenFilling ? "zones_dump.txt" : "", SHAPE_FILE_IO::IOM_APPEND ) );
|
|
||||||
|
|
||||||
// Set the number of segments in arc approximations
|
|
||||||
if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF )
|
|
||||||
segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF;
|
|
||||||
else
|
|
||||||
segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF;
|
|
||||||
|
|
||||||
/* calculates the coeff to compensate radius reduction of holes clearance
|
|
||||||
* due to the segment approx.
|
|
||||||
* For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
|
|
||||||
* s_Correction is 1 /cos( PI/s_CircleToSegmentsCount )
|
|
||||||
*/
|
|
||||||
correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle );
|
|
||||||
|
|
||||||
CPOLYGONS_LIST tmp;
|
|
||||||
|
|
||||||
if(s_DumpZonesWhenFilling)
|
|
||||||
dumper->BeginGroup("clipper-zone");
|
|
||||||
|
|
||||||
SHAPE_POLY_SET solidAreas = *m_smoothedPoly;
|
|
||||||
|
|
||||||
solidAreas.Inflate( -outline_half_thickness, segsPerCircle );
|
|
||||||
solidAreas.Simplify( POLY_CALC_MODE );
|
|
||||||
|
|
||||||
SHAPE_POLY_SET holes;
|
|
||||||
|
|
||||||
if(s_DumpZonesWhenFilling)
|
|
||||||
dumper->Write( &solidAreas, "solid-areas" );
|
|
||||||
|
|
||||||
tmp.RemoveAllContours();
|
|
||||||
buildFeatureHoleList( aPcb, holes );
|
|
||||||
|
|
||||||
if(s_DumpZonesWhenFilling)
|
|
||||||
dumper->Write( &holes, "feature-holes" );
|
|
||||||
|
|
||||||
holes.Simplify( POLY_CALC_MODE );
|
|
||||||
|
|
||||||
if (s_DumpZonesWhenFilling)
|
|
||||||
dumper->Write( &holes, "feature-holes-postsimplify" );
|
|
||||||
|
|
||||||
// Generate the filled areas (currently, without thermal shapes, which will
|
|
||||||
// be created later).
|
|
||||||
// Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to generate strictly simple polygons
|
|
||||||
// needed by Gerber files and Fracture()
|
|
||||||
solidAreas.BooleanSubtract( holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
|
||||||
|
|
||||||
if (s_DumpZonesWhenFilling)
|
|
||||||
dumper->Write( &solidAreas, "solid-areas-minus-holes" );
|
|
||||||
|
|
||||||
SHAPE_POLY_SET areas_fractured = solidAreas;
|
|
||||||
areas_fractured.Fracture( POLY_CALC_MODE );
|
|
||||||
|
|
||||||
if (s_DumpZonesWhenFilling)
|
|
||||||
dumper->Write( &areas_fractured, "areas_fractured" );
|
|
||||||
|
|
||||||
m_FilledPolysList = areas_fractured;
|
|
||||||
|
|
||||||
SHAPE_POLY_SET thermalHoles;
|
|
||||||
|
|
||||||
// Test thermal stubs connections and add polygons to remove unconnected stubs.
|
|
||||||
// (this is a refinement for thermal relief shapes)
|
|
||||||
if( GetNetCode() > 0 )
|
|
||||||
BuildUnconnectedThermalStubsPolygonList( thermalHoles, aPcb, this,
|
|
||||||
correctionFactor, s_thermalRot );
|
|
||||||
|
|
||||||
// remove copper areas corresponding to not connected stubs
|
|
||||||
if( !thermalHoles.IsEmpty() )
|
|
||||||
{
|
|
||||||
thermalHoles.Simplify( POLY_CALC_MODE );
|
|
||||||
// Remove unconnected stubs. Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to
|
|
||||||
// generate strictly simple polygons
|
|
||||||
// needed by Gerber files and Fracture()
|
|
||||||
solidAreas.BooleanSubtract( thermalHoles, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
|
||||||
|
|
||||||
if( s_DumpZonesWhenFilling )
|
|
||||||
dumper->Write( &thermalHoles, "thermal-holes" );
|
|
||||||
|
|
||||||
// put these areas in m_FilledPolysList
|
|
||||||
SHAPE_POLY_SET th_fractured = solidAreas;
|
|
||||||
th_fractured.Fracture( POLY_CALC_MODE );
|
|
||||||
|
|
||||||
if( s_DumpZonesWhenFilling )
|
|
||||||
dumper->Write ( &th_fractured, "th_fractured" );
|
|
||||||
|
|
||||||
m_FilledPolysList = th_fractured;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
m_RawPolysList = m_FilledPolysList;
|
|
||||||
|
|
||||||
if( GetNetCode() > 0 )
|
|
||||||
TestForCopperIslandAndRemoveInsulatedIslands( aPcb );
|
|
||||||
|
|
||||||
if(s_DumpZonesWhenFilling)
|
|
||||||
dumper->EndGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList( BOARD* aPcb )
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -28,21 +28,3 @@
|
||||||
#include <class_board.h>
|
#include <class_board.h>
|
||||||
#include <class_zone.h>
|
#include <class_zone.h>
|
||||||
#include <connectivity_data.h>
|
#include <connectivity_data.h>
|
||||||
|
|
||||||
void ZONE_CONTAINER::TestForCopperIslandAndRemoveInsulatedIslands( BOARD* aPcb )
|
|
||||||
{
|
|
||||||
std::vector<int> islands;
|
|
||||||
|
|
||||||
auto connectivity = aPcb->GetConnectivity();
|
|
||||||
|
|
||||||
connectivity->FindIsolatedCopperIslands( this, islands );
|
|
||||||
|
|
||||||
std::sort( islands.begin(), islands.end(), std::greater<int>() );
|
|
||||||
|
|
||||||
for( auto idx : islands )
|
|
||||||
{
|
|
||||||
m_FilledPolysList.DeletePolygon( idx );
|
|
||||||
}
|
|
||||||
|
|
||||||
connectivity->Update( this );
|
|
||||||
}
|
|
||||||
|
|
|
@ -13,6 +13,11 @@ set(POLYGON_SRCS
|
||||||
PolyLine.cpp
|
PolyLine.cpp
|
||||||
polygon_test_point_inside.cpp
|
polygon_test_point_inside.cpp
|
||||||
clipper.cpp
|
clipper.cpp
|
||||||
|
./poly2tri/sweep/sweep.cc
|
||||||
|
./poly2tri/sweep/sweep_context.cc
|
||||||
|
./poly2tri/sweep/cdt.cc
|
||||||
|
./poly2tri/sweep/advancing_front.cc
|
||||||
|
./poly2tri/common/shapes.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(polygon STATIC ${POLYGON_SRCS})
|
add_library(polygon STATIC ${POLYGON_SRCS})
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace p2t {
|
namespace p2t {
|
||||||
|
|
||||||
Triangle::Triangle(Point& a, Point& b, Point& c)
|
Triangle::Triangle(Point& a, Point& b, Point& c)
|
||||||
{
|
{
|
||||||
points_[0] = &a; points_[1] = &b; points_[2] = &c;
|
points_[0] = &a; points_[1] = &b; points_[2] = &c;
|
||||||
|
@ -41,7 +42,6 @@ Triangle::Triangle( Point& a, Point& b, Point& c )
|
||||||
interior_ = false;
|
interior_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Update neighbor pointers
|
// Update neighbor pointers
|
||||||
void Triangle::MarkNeighbor(Point* p1, Point* p2, Triangle* t)
|
void Triangle::MarkNeighbor(Point* p1, Point* p2, Triangle* t)
|
||||||
{
|
{
|
||||||
|
@ -55,50 +55,39 @@ void Triangle::MarkNeighbor( Point* p1, Point* p2, Triangle* t )
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Exhaustive search to update neighbor pointers
|
// Exhaustive search to update neighbor pointers
|
||||||
void Triangle::MarkNeighbor(Triangle& t)
|
void Triangle::MarkNeighbor(Triangle& t)
|
||||||
{
|
{
|
||||||
if( t.Contains( points_[1], points_[2] ) )
|
if (t.Contains(points_[1], points_[2])) {
|
||||||
{
|
|
||||||
neighbors_[0] = &t;
|
neighbors_[0] = &t;
|
||||||
t.MarkNeighbor(points_[1], points_[2], this);
|
t.MarkNeighbor(points_[1], points_[2], this);
|
||||||
}
|
} else if (t.Contains(points_[0], points_[2])) {
|
||||||
else if( t.Contains( points_[0], points_[2] ) )
|
|
||||||
{
|
|
||||||
neighbors_[1] = &t;
|
neighbors_[1] = &t;
|
||||||
t.MarkNeighbor(points_[0], points_[2], this);
|
t.MarkNeighbor(points_[0], points_[2], this);
|
||||||
}
|
} else if (t.Contains(points_[0], points_[1])) {
|
||||||
else if( t.Contains( points_[0], points_[1] ) )
|
|
||||||
{
|
|
||||||
neighbors_[2] = &t;
|
neighbors_[2] = &t;
|
||||||
t.MarkNeighbor(points_[0], points_[1], this);
|
t.MarkNeighbor(points_[0], points_[1], this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all references to all other triangles and points
|
* Clears all references to all other triangles and points
|
||||||
*/
|
*/
|
||||||
void Triangle::Clear()
|
void Triangle::Clear()
|
||||||
{
|
{
|
||||||
Triangle *t;
|
Triangle *t;
|
||||||
|
|
||||||
for( int i=0; i<3; i++ )
|
for( int i=0; i<3; i++ )
|
||||||
{
|
{
|
||||||
t = neighbors_[i];
|
t = neighbors_[i];
|
||||||
|
|
||||||
if( t != NULL )
|
if( t != NULL )
|
||||||
{
|
{
|
||||||
t->ClearNeighbor( this );
|
t->ClearNeighbor( this );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearNeighbors();
|
ClearNeighbors();
|
||||||
points_[0]=points_[1]=points_[2] = NULL;
|
points_[0]=points_[1]=points_[2] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Triangle::ClearNeighbor(Triangle *triangle )
|
void Triangle::ClearNeighbor(Triangle *triangle )
|
||||||
{
|
{
|
||||||
if( neighbors_[0] == triangle )
|
if( neighbors_[0] == triangle )
|
||||||
|
@ -115,7 +104,6 @@ void Triangle::ClearNeighbor( Triangle* triangle )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Triangle::ClearNeighbors()
|
void Triangle::ClearNeighbors()
|
||||||
{
|
{
|
||||||
neighbors_[0] = NULL;
|
neighbors_[0] = NULL;
|
||||||
|
@ -123,29 +111,21 @@ void Triangle::ClearNeighbors()
|
||||||
neighbors_[2] = NULL;
|
neighbors_[2] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Triangle::ClearDelunayEdges()
|
void Triangle::ClearDelunayEdges()
|
||||||
{
|
{
|
||||||
delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false;
|
delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Point* Triangle::OppositePoint(Triangle& t, Point& p)
|
Point* Triangle::OppositePoint(Triangle& t, Point& p)
|
||||||
{
|
{
|
||||||
Point *cw = t.PointCW(p);
|
Point *cw = t.PointCW(p);
|
||||||
|
|
||||||
/*
|
|
||||||
double x = cw->x;
|
double x = cw->x;
|
||||||
double y = cw->y;
|
double y = cw->y;
|
||||||
|
|
||||||
x = p.x;
|
x = p.x;
|
||||||
y = p.y;
|
y = p.y;
|
||||||
*/
|
|
||||||
|
|
||||||
return PointCW(*cw);
|
return PointCW(*cw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Legalized triangle by rotating clockwise around point(0)
|
// Legalized triangle by rotating clockwise around point(0)
|
||||||
void Triangle::Legalize(Point& point)
|
void Triangle::Legalize(Point& point)
|
||||||
{
|
{
|
||||||
|
@ -154,347 +134,233 @@ void Triangle::Legalize( Point& point )
|
||||||
points_[2] = &point;
|
points_[2] = &point;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Legalize triagnle by rotating clockwise around oPoint
|
// Legalize triagnle by rotating clockwise around oPoint
|
||||||
void Triangle::Legalize(Point& opoint, Point& npoint)
|
void Triangle::Legalize(Point& opoint, Point& npoint)
|
||||||
{
|
{
|
||||||
if( &opoint == points_[0] )
|
if (&opoint == points_[0]) {
|
||||||
{
|
|
||||||
points_[1] = points_[0];
|
points_[1] = points_[0];
|
||||||
points_[0] = points_[2];
|
points_[0] = points_[2];
|
||||||
points_[2] = &npoint;
|
points_[2] = &npoint;
|
||||||
}
|
} else if (&opoint == points_[1]) {
|
||||||
else if( &opoint == points_[1] )
|
|
||||||
{
|
|
||||||
points_[2] = points_[1];
|
points_[2] = points_[1];
|
||||||
points_[1] = points_[0];
|
points_[1] = points_[0];
|
||||||
points_[0] = &npoint;
|
points_[0] = &npoint;
|
||||||
}
|
} else if (&opoint == points_[2]) {
|
||||||
else if( &opoint == points_[2] )
|
|
||||||
{
|
|
||||||
points_[0] = points_[2];
|
points_[0] = points_[2];
|
||||||
points_[2] = points_[1];
|
points_[2] = points_[1];
|
||||||
points_[1] = &npoint;
|
points_[1] = &npoint;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Triangle::Index(const Point* p)
|
int Triangle::Index(const Point* p)
|
||||||
{
|
{
|
||||||
if( p == points_[0] )
|
if (p == points_[0]) {
|
||||||
{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
} else if (p == points_[1]) {
|
||||||
else if( p == points_[1] )
|
|
||||||
{
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
} else if (p == points_[2]) {
|
||||||
else if( p == points_[2] )
|
|
||||||
{
|
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(0);
|
assert(0);
|
||||||
return 0; // you better hope its a Debug build.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Triangle::EdgeIndex(const Point* p1, const Point* p2)
|
int Triangle::EdgeIndex(const Point* p1, const Point* p2)
|
||||||
{
|
{
|
||||||
if( points_[0] == p1 )
|
if (points_[0] == p1) {
|
||||||
{
|
if (points_[1] == p2) {
|
||||||
if( points_[1] == p2 )
|
|
||||||
{
|
|
||||||
return 2;
|
return 2;
|
||||||
}
|
} else if (points_[2] == p2) {
|
||||||
else if( points_[2] == p2 )
|
|
||||||
{
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
} else if (points_[1] == p1) {
|
||||||
else if( points_[1] == p1 )
|
if (points_[2] == p2) {
|
||||||
{
|
|
||||||
if( points_[2] == p2 )
|
|
||||||
{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
} else if (points_[0] == p2) {
|
||||||
else if( points_[0] == p2 )
|
|
||||||
{
|
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
} else if (points_[2] == p1) {
|
||||||
else if( points_[2] == p1 )
|
if (points_[0] == p2) {
|
||||||
{
|
|
||||||
if( points_[0] == p2 )
|
|
||||||
{
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
} else if (points_[1] == p2) {
|
||||||
else if( points_[1] == p2 )
|
|
||||||
{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Triangle::MarkConstrainedEdge(const int index)
|
void Triangle::MarkConstrainedEdge(const int index)
|
||||||
{
|
{
|
||||||
constrained_edge[index] = true;
|
constrained_edge[index] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Triangle::MarkConstrainedEdge(Edge& edge)
|
void Triangle::MarkConstrainedEdge(Edge& edge)
|
||||||
{
|
{
|
||||||
MarkConstrainedEdge(edge.p, edge.q);
|
MarkConstrainedEdge(edge.p, edge.q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Mark edge as constrained
|
// Mark edge as constrained
|
||||||
void Triangle::MarkConstrainedEdge(Point* p, Point* q)
|
void Triangle::MarkConstrainedEdge(Point* p, Point* q)
|
||||||
{
|
{
|
||||||
if( (q == points_[0] && p == points_[1]) || (q == points_[1] && p == points_[0]) )
|
if ((q == points_[0] && p == points_[1]) || (q == points_[1] && p == points_[0])) {
|
||||||
{
|
|
||||||
constrained_edge[2] = true;
|
constrained_edge[2] = true;
|
||||||
}
|
} else if ((q == points_[0] && p == points_[2]) || (q == points_[2] && p == points_[0])) {
|
||||||
else if( (q == points_[0] && p == points_[2]) || (q == points_[2] && p == points_[0]) )
|
|
||||||
{
|
|
||||||
constrained_edge[1] = true;
|
constrained_edge[1] = true;
|
||||||
}
|
} else if ((q == points_[1] && p == points_[2]) || (q == points_[2] && p == points_[1])) {
|
||||||
else if( (q == points_[1] && p == points_[2]) || (q == points_[2] && p == points_[1]) )
|
|
||||||
{
|
|
||||||
constrained_edge[0] = true;
|
constrained_edge[0] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// The point counter-clockwise to given point
|
// The point counter-clockwise to given point
|
||||||
Point* Triangle::PointCW(Point& point)
|
Point* Triangle::PointCW(Point& point)
|
||||||
{
|
{
|
||||||
if( &point == points_[0] )
|
if (&point == points_[0]) {
|
||||||
{
|
|
||||||
return points_[2];
|
return points_[2];
|
||||||
}
|
} else if (&point == points_[1]) {
|
||||||
else if( &point == points_[1] )
|
|
||||||
{
|
|
||||||
return points_[0];
|
return points_[0];
|
||||||
}
|
} else if (&point == points_[2]) {
|
||||||
else if( &point == points_[2] )
|
|
||||||
{
|
|
||||||
return points_[1];
|
return points_[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(0);
|
assert(0);
|
||||||
return NULL; // you better hope its a Debug build.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// The point counter-clockwise to given point
|
// The point counter-clockwise to given point
|
||||||
Point* Triangle::PointCCW(Point& point)
|
Point* Triangle::PointCCW(Point& point)
|
||||||
{
|
{
|
||||||
if( &point == points_[0] )
|
if (&point == points_[0]) {
|
||||||
{
|
|
||||||
return points_[1];
|
return points_[1];
|
||||||
}
|
} else if (&point == points_[1]) {
|
||||||
else if( &point == points_[1] )
|
|
||||||
{
|
|
||||||
return points_[2];
|
return points_[2];
|
||||||
}
|
} else if (&point == points_[2]) {
|
||||||
else if( &point == points_[2] )
|
|
||||||
{
|
|
||||||
return points_[0];
|
return points_[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(0);
|
assert(0);
|
||||||
return NULL; // you better hope its a Debug build.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// The neighbor clockwise to given point
|
// The neighbor clockwise to given point
|
||||||
Triangle* Triangle::NeighborCW(Point& point)
|
Triangle* Triangle::NeighborCW(Point& point)
|
||||||
{
|
{
|
||||||
if( &point == points_[0] )
|
if (&point == points_[0]) {
|
||||||
{
|
|
||||||
return neighbors_[1];
|
return neighbors_[1];
|
||||||
}
|
} else if (&point == points_[1]) {
|
||||||
else if( &point == points_[1] )
|
|
||||||
{
|
|
||||||
return neighbors_[2];
|
return neighbors_[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
return neighbors_[0];
|
return neighbors_[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// The neighbor counter-clockwise to given point
|
// The neighbor counter-clockwise to given point
|
||||||
Triangle* Triangle::NeighborCCW(Point& point)
|
Triangle* Triangle::NeighborCCW(Point& point)
|
||||||
{
|
{
|
||||||
if( &point == points_[0] )
|
if (&point == points_[0]) {
|
||||||
{
|
|
||||||
return neighbors_[2];
|
return neighbors_[2];
|
||||||
}
|
} else if (&point == points_[1]) {
|
||||||
else if( &point == points_[1] )
|
|
||||||
{
|
|
||||||
return neighbors_[0];
|
return neighbors_[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return neighbors_[1];
|
return neighbors_[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Triangle::GetConstrainedEdgeCCW(Point& p)
|
bool Triangle::GetConstrainedEdgeCCW(Point& p)
|
||||||
{
|
{
|
||||||
if( &p == points_[0] )
|
if (&p == points_[0]) {
|
||||||
{
|
|
||||||
return constrained_edge[2];
|
return constrained_edge[2];
|
||||||
}
|
} else if (&p == points_[1]) {
|
||||||
else if( &p == points_[1] )
|
|
||||||
{
|
|
||||||
return constrained_edge[0];
|
return constrained_edge[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return constrained_edge[1];
|
return constrained_edge[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Triangle::GetConstrainedEdgeCW(Point& p)
|
bool Triangle::GetConstrainedEdgeCW(Point& p)
|
||||||
{
|
{
|
||||||
if( &p == points_[0] )
|
if (&p == points_[0]) {
|
||||||
{
|
|
||||||
return constrained_edge[1];
|
return constrained_edge[1];
|
||||||
}
|
} else if (&p == points_[1]) {
|
||||||
else if( &p == points_[1] )
|
|
||||||
{
|
|
||||||
return constrained_edge[2];
|
return constrained_edge[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
return constrained_edge[0];
|
return constrained_edge[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Triangle::SetConstrainedEdgeCCW(Point& p, bool ce)
|
void Triangle::SetConstrainedEdgeCCW(Point& p, bool ce)
|
||||||
{
|
{
|
||||||
if( &p == points_[0] )
|
if (&p == points_[0]) {
|
||||||
{
|
|
||||||
constrained_edge[2] = ce;
|
constrained_edge[2] = ce;
|
||||||
}
|
} else if (&p == points_[1]) {
|
||||||
else if( &p == points_[1] )
|
|
||||||
{
|
|
||||||
constrained_edge[0] = ce;
|
constrained_edge[0] = ce;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
constrained_edge[1] = ce;
|
constrained_edge[1] = ce;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Triangle::SetConstrainedEdgeCW(Point& p, bool ce)
|
void Triangle::SetConstrainedEdgeCW(Point& p, bool ce)
|
||||||
{
|
{
|
||||||
if( &p == points_[0] )
|
if (&p == points_[0]) {
|
||||||
{
|
|
||||||
constrained_edge[1] = ce;
|
constrained_edge[1] = ce;
|
||||||
}
|
} else if (&p == points_[1]) {
|
||||||
else if( &p == points_[1] )
|
|
||||||
{
|
|
||||||
constrained_edge[2] = ce;
|
constrained_edge[2] = ce;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
constrained_edge[0] = ce;
|
constrained_edge[0] = ce;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Triangle::GetDelunayEdgeCCW(Point& p)
|
bool Triangle::GetDelunayEdgeCCW(Point& p)
|
||||||
{
|
{
|
||||||
if( &p == points_[0] )
|
if (&p == points_[0]) {
|
||||||
{
|
|
||||||
return delaunay_edge[2];
|
return delaunay_edge[2];
|
||||||
}
|
} else if (&p == points_[1]) {
|
||||||
else if( &p == points_[1] )
|
|
||||||
{
|
|
||||||
return delaunay_edge[0];
|
return delaunay_edge[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return delaunay_edge[1];
|
return delaunay_edge[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Triangle::GetDelunayEdgeCW(Point& p)
|
bool Triangle::GetDelunayEdgeCW(Point& p)
|
||||||
{
|
{
|
||||||
if( &p == points_[0] )
|
if (&p == points_[0]) {
|
||||||
{
|
|
||||||
return delaunay_edge[1];
|
return delaunay_edge[1];
|
||||||
}
|
} else if (&p == points_[1]) {
|
||||||
else if( &p == points_[1] )
|
|
||||||
{
|
|
||||||
return delaunay_edge[2];
|
return delaunay_edge[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
return delaunay_edge[0];
|
return delaunay_edge[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Triangle::SetDelunayEdgeCCW(Point& p, bool e)
|
void Triangle::SetDelunayEdgeCCW(Point& p, bool e)
|
||||||
{
|
{
|
||||||
if( &p == points_[0] )
|
if (&p == points_[0]) {
|
||||||
{
|
|
||||||
delaunay_edge[2] = e;
|
delaunay_edge[2] = e;
|
||||||
}
|
} else if (&p == points_[1]) {
|
||||||
else if( &p == points_[1] )
|
|
||||||
{
|
|
||||||
delaunay_edge[0] = e;
|
delaunay_edge[0] = e;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
delaunay_edge[1] = e;
|
delaunay_edge[1] = e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Triangle::SetDelunayEdgeCW(Point& p, bool e)
|
void Triangle::SetDelunayEdgeCW(Point& p, bool e)
|
||||||
{
|
{
|
||||||
if( &p == points_[0] )
|
if (&p == points_[0]) {
|
||||||
{
|
|
||||||
delaunay_edge[1] = e;
|
delaunay_edge[1] = e;
|
||||||
}
|
} else if (&p == points_[1]) {
|
||||||
else if( &p == points_[1] )
|
|
||||||
{
|
|
||||||
delaunay_edge[2] = e;
|
delaunay_edge[2] = e;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
delaunay_edge[0] = e;
|
delaunay_edge[0] = e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// The neighbor across to given point
|
// The neighbor across to given point
|
||||||
Triangle& Triangle::NeighborAcross(Point& opoint)
|
Triangle& Triangle::NeighborAcross(Point& opoint)
|
||||||
{
|
{
|
||||||
if( &opoint == points_[0] )
|
if (&opoint == points_[0]) {
|
||||||
{
|
|
||||||
return *neighbors_[0];
|
return *neighbors_[0];
|
||||||
}
|
} else if (&opoint == points_[1]) {
|
||||||
else if( &opoint == points_[1] )
|
|
||||||
{
|
|
||||||
return *neighbors_[1];
|
return *neighbors_[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
return *neighbors_[2];
|
return *neighbors_[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Triangle::DebugPrint()
|
void Triangle::DebugPrint()
|
||||||
{
|
{
|
||||||
std::cout << points_[0]->x << "," << points_[0]->y << " ";
|
using namespace std;
|
||||||
std::cout << points_[1]->x << "," << points_[1]->y << " ";
|
cout << points_[0]->x << "," << points_[0]->y << " ";
|
||||||
std::cout << points_[2]->x << "," << points_[2]->y << "\n";
|
cout << points_[1]->x << "," << points_[1]->y << " ";
|
||||||
|
cout << points_[2]->x << "," << points_[2]->y << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,10 +39,11 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace p2t {
|
namespace p2t {
|
||||||
|
|
||||||
struct Edge;
|
struct Edge;
|
||||||
|
|
||||||
struct Point
|
struct Point {
|
||||||
{
|
|
||||||
double x, y;
|
double x, y;
|
||||||
|
|
||||||
/// Default constructor does nothing (for performance).
|
/// Default constructor does nothing (for performance).
|
||||||
|
@ -56,7 +57,7 @@ struct Point
|
||||||
std::vector<Edge*> edge_list;
|
std::vector<Edge*> edge_list;
|
||||||
|
|
||||||
/// Construct using coordinates.
|
/// Construct using coordinates.
|
||||||
Point( double ax, double ay ) : x( ax ), y( ay ) {}
|
Point(double x, double y) : x(x), y(y) {}
|
||||||
|
|
||||||
/// Set this point to all zeros.
|
/// Set this point to all zeros.
|
||||||
void set_zero()
|
void set_zero()
|
||||||
|
@ -76,7 +77,6 @@ struct Point
|
||||||
Point operator -() const
|
Point operator -() const
|
||||||
{
|
{
|
||||||
Point v;
|
Point v;
|
||||||
|
|
||||||
v.set(-x, -y);
|
v.set(-x, -y);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
@ -112,35 +112,29 @@ struct Point
|
||||||
double Normalize()
|
double Normalize()
|
||||||
{
|
{
|
||||||
double len = Length();
|
double len = Length();
|
||||||
|
|
||||||
x /= len;
|
x /= len;
|
||||||
y /= len;
|
y /= len;
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Represents a simple polygon's edge
|
// Represents a simple polygon's edge
|
||||||
struct Edge
|
struct Edge {
|
||||||
{
|
|
||||||
Point* p, *q;
|
Point* p, *q;
|
||||||
|
|
||||||
/// Constructor
|
/// Constructor
|
||||||
Edge(Point& p1, Point& p2) : p(&p1), q(&p2)
|
Edge(Point& p1, Point& p2) : p(&p1), q(&p2)
|
||||||
{
|
{
|
||||||
if( p1.y > p2.y )
|
if (p1.y > p2.y) {
|
||||||
{
|
|
||||||
q = &p1;
|
q = &p1;
|
||||||
p = &p2;
|
p = &p2;
|
||||||
}
|
} else if (p1.y == p2.y) {
|
||||||
else if( p1.y == p2.y )
|
if (p1.x > p2.x) {
|
||||||
{
|
|
||||||
if( p1.x > p2.x )
|
|
||||||
{
|
|
||||||
q = &p1;
|
q = &p1;
|
||||||
p = &p2;
|
p = &p2;
|
||||||
}
|
} else if (p1.x == p2.x) {
|
||||||
else if( p1.x == p2.x )
|
|
||||||
{
|
|
||||||
// Repeat points
|
// Repeat points
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
@ -153,8 +147,7 @@ struct Edge
|
||||||
// Triangle-based data structures are know to have better performance than quad-edge structures
|
// Triangle-based data structures are know to have better performance than quad-edge structures
|
||||||
// See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator"
|
// See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator"
|
||||||
// "Triangulations in CGAL"
|
// "Triangulations in CGAL"
|
||||||
class Triangle
|
class Triangle {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// Constructor
|
/// Constructor
|
||||||
|
@ -197,7 +190,6 @@ public:
|
||||||
bool Contains(Point* p, Point* q);
|
bool Contains(Point* p, Point* q);
|
||||||
void Legalize(Point& point);
|
void Legalize(Point& point);
|
||||||
void Legalize(Point& opoint, Point& npoint);
|
void Legalize(Point& opoint, Point& npoint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all references to all other triangles and points
|
* Clears all references to all other triangles and points
|
||||||
*/
|
*/
|
||||||
|
@ -226,70 +218,57 @@ private:
|
||||||
|
|
||||||
inline bool cmp(const Point* a, const Point* b)
|
inline bool cmp(const Point* a, const Point* b)
|
||||||
{
|
{
|
||||||
if( a->y < b->y )
|
if (a->y < b->y) {
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
} else if (a->y == b->y) {
|
||||||
else if( a->y == b->y )
|
|
||||||
{
|
|
||||||
// Make sure q is point with greater x value
|
// Make sure q is point with greater x value
|
||||||
if( a->x < b->x )
|
if (a->x < b->x) {
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Add two points_ component-wise.
|
/// Add two points_ component-wise.
|
||||||
inline Point operator +(const Point& a, const Point& b)
|
inline Point operator +(const Point& a, const Point& b)
|
||||||
{
|
{
|
||||||
return Point(a.x + b.x, a.y + b.y);
|
return Point(a.x + b.x, a.y + b.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Subtract two points_ component-wise.
|
/// Subtract two points_ component-wise.
|
||||||
inline Point operator -(const Point& a, const Point& b)
|
inline Point operator -(const Point& a, const Point& b)
|
||||||
{
|
{
|
||||||
return Point(a.x - b.x, a.y - b.y);
|
return Point(a.x - b.x, a.y - b.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Multiply point by scalar
|
/// Multiply point by scalar
|
||||||
inline Point operator *(double s, const Point& a)
|
inline Point operator *(double s, const Point& a)
|
||||||
{
|
{
|
||||||
return Point(s * a.x, s * a.y);
|
return Point(s * a.x, s * a.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool operator ==(const Point& a, const Point& b)
|
inline bool operator ==(const Point& a, const Point& b)
|
||||||
{
|
{
|
||||||
return a.x == b.x && a.y == b.y;
|
return a.x == b.x && a.y == b.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool operator !=(const Point& a, const Point& b)
|
inline bool operator !=(const Point& a, const Point& b)
|
||||||
{
|
{
|
||||||
return !(a.x == b.x) && !(a.y == b.y);
|
return !(a.x == b.x) && !(a.y == b.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Peform the dot product on two vectors.
|
/// Peform the dot product on two vectors.
|
||||||
inline double Dot(const Point& a, const Point& b)
|
inline double Dot(const Point& a, const Point& b)
|
||||||
{
|
{
|
||||||
return a.x * b.x + a.y * b.y;
|
return a.x * b.x + a.y * b.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Perform the cross product on two vectors. In 2D this produces a scalar.
|
/// Perform the cross product on two vectors. In 2D this produces a scalar.
|
||||||
inline double Cross(const Point& a, const Point& b)
|
inline double Cross(const Point& a, const Point& b)
|
||||||
{
|
{
|
||||||
return a.x * b.y - a.y * b.x;
|
return a.x * b.y - a.y * b.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Perform the cross product on a point and a scalar. In 2D this produces
|
/// Perform the cross product on a point and a scalar. In 2D this produces
|
||||||
/// a point.
|
/// a point.
|
||||||
inline Point Cross(const Point& a, double s)
|
inline Point Cross(const Point& a, double s)
|
||||||
|
@ -297,7 +276,6 @@ inline Point Cross( const Point& a, double s )
|
||||||
return Point(s * a.y, -s * a.x);
|
return Point(s * a.y, -s * a.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Perform the cross product on a scalar and a point. In 2D this produces
|
/// Perform the cross product on a scalar and a point. In 2D this produces
|
||||||
/// a point.
|
/// a point.
|
||||||
inline Point Cross(const double s, const Point& a)
|
inline Point Cross(const double s, const Point& a)
|
||||||
|
@ -305,47 +283,43 @@ inline Point Cross( const double s, const Point& a )
|
||||||
return Point(-s * a.y, s * a.x);
|
return Point(-s * a.y, s * a.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline Point* Triangle::GetPoint(const int& index)
|
inline Point* Triangle::GetPoint(const int& index)
|
||||||
{
|
{
|
||||||
return points_[index];
|
return points_[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline Triangle* Triangle::GetNeighbor(const int& index)
|
inline Triangle* Triangle::GetNeighbor(const int& index)
|
||||||
{
|
{
|
||||||
return neighbors_[index];
|
return neighbors_[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool Triangle::Contains(Point* p)
|
inline bool Triangle::Contains(Point* p)
|
||||||
{
|
{
|
||||||
return p == points_[0] || p == points_[1] || p == points_[2];
|
return p == points_[0] || p == points_[1] || p == points_[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool Triangle::Contains(const Edge& e)
|
inline bool Triangle::Contains(const Edge& e)
|
||||||
{
|
{
|
||||||
return Contains(e.p) && Contains(e.q);
|
return Contains(e.p) && Contains(e.q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool Triangle::Contains(Point* p, Point* q)
|
inline bool Triangle::Contains(Point* p, Point* q)
|
||||||
{
|
{
|
||||||
return Contains(p) && Contains(q);
|
return Contains(p) && Contains(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool Triangle::IsInterior()
|
inline bool Triangle::IsInterior()
|
||||||
{
|
{
|
||||||
return interior_;
|
return interior_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void Triangle::IsInterior(bool b)
|
inline void Triangle::IsInterior(bool b)
|
||||||
{
|
{
|
||||||
interior_ = b;
|
interior_ = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -39,13 +39,12 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
namespace p2t {
|
namespace p2t {
|
||||||
|
|
||||||
const double PI_3div4 = 3 * M_PI / 4;
|
const double PI_3div4 = 3 * M_PI / 4;
|
||||||
const double PI_div2 = 1.57079632679489661923;
|
const double PI_div2 = 1.57079632679489661923;
|
||||||
const double EPSILON = 1e-12;
|
const double EPSILON = 1e-12;
|
||||||
|
|
||||||
enum Orientation {
|
enum Orientation { CW, CCW, COLLINEAR };
|
||||||
CW, CCW, COLLINEAR
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forumla to calculate signed area<br>
|
* Forumla to calculate signed area<br>
|
||||||
|
@ -62,72 +61,63 @@ Orientation Orient2d( Point& pa, Point& pb, Point& pc )
|
||||||
double detleft = (pa.x - pc.x) * (pb.y - pc.y);
|
double detleft = (pa.x - pc.x) * (pb.y - pc.y);
|
||||||
double detright = (pa.y - pc.y) * (pb.x - pc.x);
|
double detright = (pa.y - pc.y) * (pb.x - pc.x);
|
||||||
double val = detleft - detright;
|
double val = detleft - detright;
|
||||||
|
if (val > -EPSILON && val < EPSILON) {
|
||||||
if( val > -EPSILON && val < EPSILON )
|
|
||||||
{
|
|
||||||
return COLLINEAR;
|
return COLLINEAR;
|
||||||
}
|
} else if (val > 0) {
|
||||||
else if( val > 0 )
|
|
||||||
{
|
|
||||||
return CCW;
|
return CCW;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CW;
|
return CW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd)
|
|
||||||
* {
|
|
||||||
* double pdx = pd.x;
|
|
||||||
* double pdy = pd.y;
|
|
||||||
* double adx = pa.x - pdx;
|
|
||||||
* double ady = pa.y - pdy;
|
|
||||||
* double bdx = pb.x - pdx;
|
|
||||||
* double bdy = pb.y - pdy;
|
|
||||||
*
|
|
||||||
* double adxbdy = adx * bdy;
|
|
||||||
* double bdxady = bdx * ady;
|
|
||||||
* double oabd = adxbdy - bdxady;
|
|
||||||
*
|
|
||||||
* if (oabd <= EPSILON) {
|
|
||||||
* return false;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* double cdx = pc.x - pdx;
|
|
||||||
* double cdy = pc.y - pdy;
|
|
||||||
*
|
|
||||||
* double cdxady = cdx * ady;
|
|
||||||
* double adxcdy = adx * cdy;
|
|
||||||
* double ocad = cdxady - adxcdy;
|
|
||||||
*
|
|
||||||
* if (ocad <= EPSILON) {
|
|
||||||
* return false;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* return true;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd)
|
bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd)
|
||||||
{
|
{
|
||||||
double oadb = (pa.x - pb.x) * (pd.y - pb.y) - (pd.x - pb.x) * (pa.y - pb.y);
|
double pdx = pd.x;
|
||||||
|
double pdy = pd.y;
|
||||||
|
double adx = pa.x - pdx;
|
||||||
|
double ady = pa.y - pdy;
|
||||||
|
double bdx = pb.x - pdx;
|
||||||
|
double bdy = pb.y - pdy;
|
||||||
|
|
||||||
if( oadb >= -EPSILON )
|
double adxbdy = adx * bdy;
|
||||||
{
|
double bdxady = bdx * ady;
|
||||||
|
double oabd = adxbdy - bdxady;
|
||||||
|
|
||||||
|
if (oabd <= EPSILON) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
double oadc = (pa.x - pc.x) * (pd.y - pc.y) - (pd.x - pc.x) * (pa.y - pc.y);
|
double cdx = pc.x - pdx;
|
||||||
|
double cdy = pc.y - pdy;
|
||||||
|
|
||||||
if( oadc <= EPSILON )
|
double cdxady = cdx * ady;
|
||||||
{
|
double adxcdy = adx * cdy;
|
||||||
|
double ocad = cdxady - adxcdy;
|
||||||
|
|
||||||
|
if (ocad <= EPSILON) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd)
|
||||||
|
{
|
||||||
|
double oadb = (pa.x - pb.x)*(pd.y - pb.y) - (pd.x - pb.x)*(pa.y - pb.y);
|
||||||
|
if (oadb >= -EPSILON) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double oadc = (pa.x - pc.x)*(pd.y - pc.y) - (pd.x - pc.x)*(pa.y - pc.y);
|
||||||
|
if (oadc <= EPSILON) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "advancing_front.h"
|
#include "advancing_front.h"
|
||||||
|
|
||||||
namespace p2t {
|
namespace p2t {
|
||||||
|
|
||||||
AdvancingFront::AdvancingFront(Node& head, Node& tail)
|
AdvancingFront::AdvancingFront(Node& head, Node& tail)
|
||||||
{
|
{
|
||||||
head_ = &head;
|
head_ = &head;
|
||||||
|
@ -38,38 +39,28 @@ AdvancingFront::AdvancingFront( Node& head, Node& tail )
|
||||||
search_node_ = &head;
|
search_node_ = &head;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Node* AdvancingFront::LocateNode(const double& x)
|
Node* AdvancingFront::LocateNode(const double& x)
|
||||||
{
|
{
|
||||||
Node* node = search_node_;
|
Node* node = search_node_;
|
||||||
|
|
||||||
if( x < node->value )
|
if (x < node->value) {
|
||||||
{
|
while ((node = node->prev) != NULL) {
|
||||||
while( (node = node->prev) != NULL )
|
if (x >= node->value) {
|
||||||
{
|
|
||||||
if( x >= node->value )
|
|
||||||
{
|
|
||||||
search_node_ = node;
|
search_node_ = node;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
while ((node = node->next) != NULL) {
|
||||||
{
|
if (x < node->value) {
|
||||||
while( (node = node->next) != NULL )
|
|
||||||
{
|
|
||||||
if( x < node->value )
|
|
||||||
{
|
|
||||||
search_node_ = node->prev;
|
search_node_ = node->prev;
|
||||||
return node->prev;
|
return node->prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Node* AdvancingFront::FindSearchNode(const double& x)
|
Node* AdvancingFront::FindSearchNode(const double& x)
|
||||||
{
|
{
|
||||||
(void)x; // suppress compiler warnings "unused parameter 'x'"
|
(void)x; // suppress compiler warnings "unused parameter 'x'"
|
||||||
|
@ -77,59 +68,42 @@ Node* AdvancingFront::FindSearchNode( const double& x )
|
||||||
return search_node_;
|
return search_node_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Node* AdvancingFront::LocatePoint(const Point* point)
|
Node* AdvancingFront::LocatePoint(const Point* point)
|
||||||
{
|
{
|
||||||
const double px = point->x;
|
const double px = point->x;
|
||||||
Node* node = FindSearchNode(px);
|
Node* node = FindSearchNode(px);
|
||||||
const double nx = node->point->x;
|
const double nx = node->point->x;
|
||||||
|
|
||||||
if( px == nx )
|
if (px == nx) {
|
||||||
{
|
if (point != node->point) {
|
||||||
if( point != node->point )
|
|
||||||
{
|
|
||||||
// We might have two nodes with same x value for a short time
|
// We might have two nodes with same x value for a short time
|
||||||
if( point == node->prev->point )
|
if (point == node->prev->point) {
|
||||||
{
|
|
||||||
node = node->prev;
|
node = node->prev;
|
||||||
}
|
} else if (point == node->next->point) {
|
||||||
else if( point == node->next->point )
|
|
||||||
{
|
|
||||||
node = node->next;
|
node = node->next;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (px < nx) {
|
||||||
else if( px < nx )
|
while ((node = node->prev) != NULL) {
|
||||||
{
|
if (point == node->point) {
|
||||||
while( (node = node->prev) != NULL )
|
|
||||||
{
|
|
||||||
if( point == node->point )
|
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
while ((node = node->next) != NULL) {
|
||||||
{
|
|
||||||
while( (node = node->next) != NULL )
|
|
||||||
{
|
|
||||||
if (point == node->point)
|
if (point == node->point)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(node) search_node_ = node;
|
||||||
if( node )
|
|
||||||
search_node_ = node;
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AdvancingFront::~AdvancingFront()
|
AdvancingFront::~AdvancingFront()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,11 +35,11 @@
|
||||||
#include "../common/shapes.h"
|
#include "../common/shapes.h"
|
||||||
|
|
||||||
namespace p2t {
|
namespace p2t {
|
||||||
|
|
||||||
struct Node;
|
struct Node;
|
||||||
|
|
||||||
// Advancing front node
|
// Advancing front node
|
||||||
struct Node
|
struct Node {
|
||||||
{
|
|
||||||
Point* point;
|
Point* point;
|
||||||
Triangle* triangle;
|
Triangle* triangle;
|
||||||
|
|
||||||
|
@ -52,15 +52,14 @@ struct Node
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Node( Point& p, Triangle& t ) : point( &p ), triangle( &t ), next( NULL ), prev( NULL ), value(
|
Node(Point& p, Triangle& t) : point(&p), triangle(&t), next(NULL), prev(NULL), value(p.x)
|
||||||
p.x )
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Advancing front
|
// Advancing front
|
||||||
class AdvancingFront
|
class AdvancingFront {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
AdvancingFront(Node& head, Node& tail);
|
AdvancingFront(Node& head, Node& tail);
|
||||||
|
@ -90,36 +89,30 @@ inline Node* AdvancingFront::head()
|
||||||
{
|
{
|
||||||
return head_;
|
return head_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void AdvancingFront::set_head(Node* node)
|
inline void AdvancingFront::set_head(Node* node)
|
||||||
{
|
{
|
||||||
head_ = node;
|
head_ = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline Node* AdvancingFront::tail()
|
inline Node* AdvancingFront::tail()
|
||||||
{
|
{
|
||||||
return tail_;
|
return tail_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void AdvancingFront::set_tail(Node* node)
|
inline void AdvancingFront::set_tail(Node* node)
|
||||||
{
|
{
|
||||||
tail_ = node;
|
tail_ = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline Node* AdvancingFront::search()
|
inline Node* AdvancingFront::search()
|
||||||
{
|
{
|
||||||
return search_node_;
|
return search_node_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void AdvancingFront::set_search(Node* node)
|
inline void AdvancingFront::set_search(Node* node)
|
||||||
{
|
{
|
||||||
search_node_ = node;
|
search_node_ = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,46 +31,42 @@
|
||||||
#include "cdt.h"
|
#include "cdt.h"
|
||||||
|
|
||||||
namespace p2t {
|
namespace p2t {
|
||||||
|
|
||||||
CDT::CDT(std::vector<Point*> polyline)
|
CDT::CDT(std::vector<Point*> polyline)
|
||||||
{
|
{
|
||||||
sweep_context_ = new SweepContext(polyline);
|
sweep_context_ = new SweepContext(polyline);
|
||||||
sweep_ = new Sweep;
|
sweep_ = new Sweep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CDT::AddHole(std::vector<Point*> polyline)
|
void CDT::AddHole(std::vector<Point*> polyline)
|
||||||
{
|
{
|
||||||
sweep_context_->AddHole(polyline);
|
sweep_context_->AddHole(polyline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CDT::AddPoint(Point* point) {
|
||||||
void CDT::AddPoint( Point* point )
|
|
||||||
{
|
|
||||||
sweep_context_->AddPoint(point);
|
sweep_context_->AddPoint(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CDT::Triangulate()
|
void CDT::Triangulate()
|
||||||
{
|
{
|
||||||
sweep_->Triangulate(*sweep_context_);
|
sweep_->Triangulate(*sweep_context_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<p2t::Triangle*> CDT::GetTriangles()
|
std::vector<p2t::Triangle*> CDT::GetTriangles()
|
||||||
{
|
{
|
||||||
return sweep_context_->GetTriangles();
|
return sweep_context_->GetTriangles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::list<p2t::Triangle*> CDT::GetMap()
|
std::list<p2t::Triangle*> CDT::GetMap()
|
||||||
{
|
{
|
||||||
return sweep_context_->GetMap();
|
return sweep_context_->GetMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CDT::~CDT()
|
CDT::~CDT()
|
||||||
{
|
{
|
||||||
delete sweep_context_;
|
delete sweep_context_;
|
||||||
delete sweep_;
|
delete sweep_;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "../common/utils.h"
|
#include "../common/utils.h"
|
||||||
|
|
||||||
namespace p2t {
|
namespace p2t {
|
||||||
|
|
||||||
// Triangulate simple polygon with holes
|
// Triangulate simple polygon with holes
|
||||||
void Sweep::Triangulate(SweepContext& tcx)
|
void Sweep::Triangulate(SweepContext& tcx)
|
||||||
{
|
{
|
||||||
|
@ -46,30 +47,23 @@ void Sweep::Triangulate( SweepContext& tcx )
|
||||||
FinalizationPolygon(tcx);
|
FinalizationPolygon(tcx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::SweepPoints(SweepContext& tcx)
|
void Sweep::SweepPoints(SweepContext& tcx)
|
||||||
{
|
{
|
||||||
for( int jj = 1; jj < tcx.point_count(); jj++ )
|
for (int i = 1; i < tcx.point_count(); i++) {
|
||||||
{
|
Point& point = *tcx.GetPoint(i);
|
||||||
Point& point = *tcx.GetPoint( jj );
|
|
||||||
Node* node = &PointEvent(tcx, point);
|
Node* node = &PointEvent(tcx, point);
|
||||||
|
for (unsigned int i = 0; i < point.edge_list.size(); i++) {
|
||||||
for( unsigned int i = 0; i < point.edge_list.size(); i++ )
|
|
||||||
{
|
|
||||||
EdgeEvent(tcx, point.edge_list[i], node);
|
EdgeEvent(tcx, point.edge_list[i], node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::FinalizationPolygon(SweepContext& tcx)
|
void Sweep::FinalizationPolygon(SweepContext& tcx)
|
||||||
{
|
{
|
||||||
// Get an Internal triangle to start with
|
// Get an Internal triangle to start with
|
||||||
Triangle* t = tcx.front()->head()->next->triangle;
|
Triangle* t = tcx.front()->head()->next->triangle;
|
||||||
Point* p = tcx.front()->head()->next->point;
|
Point* p = tcx.front()->head()->next->point;
|
||||||
|
while (!t->GetConstrainedEdgeCW(*p)) {
|
||||||
while( !t->GetConstrainedEdgeCW( *p ) )
|
|
||||||
{
|
|
||||||
t = t->NeighborCCW(*p);
|
t = t->NeighborCCW(*p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +71,6 @@ void Sweep::FinalizationPolygon( SweepContext& tcx )
|
||||||
tcx.MeshClean(*t);
|
tcx.MeshClean(*t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Node& Sweep::PointEvent(SweepContext& tcx, Point& point)
|
Node& Sweep::PointEvent(SweepContext& tcx, Point& point)
|
||||||
{
|
{
|
||||||
Node& node = tcx.LocateNode(point);
|
Node& node = tcx.LocateNode(point);
|
||||||
|
@ -85,8 +78,7 @@ Node& Sweep::PointEvent( SweepContext& tcx, Point& point )
|
||||||
|
|
||||||
// Only need to check +epsilon since point never have smaller
|
// Only need to check +epsilon since point never have smaller
|
||||||
// x value than node due to how we fetch nodes from the front
|
// x value than node due to how we fetch nodes from the front
|
||||||
if( point.x <= node.point->x + EPSILON )
|
if (point.x <= node.point->x + EPSILON) {
|
||||||
{
|
|
||||||
Fill(tcx, node);
|
Fill(tcx, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,14 +88,12 @@ Node& Sweep::PointEvent( SweepContext& tcx, Point& point )
|
||||||
return new_node;
|
return new_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
||||||
{
|
{
|
||||||
tcx.edge_event.constrained_edge = edge;
|
tcx.edge_event.constrained_edge = edge;
|
||||||
tcx.edge_event.right = (edge->p->x > edge->q->x);
|
tcx.edge_event.right = (edge->p->x > edge->q->x);
|
||||||
|
|
||||||
if( IsEdgeSideOfTriangle( *node->triangle, *edge->p, *edge->q ) )
|
if (IsEdgeSideOfTriangle(*node->triangle, *edge->p, *edge->q)) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,104 +104,76 @@ void Sweep::EdgeEvent( SweepContext& tcx, Edge* edge, Node* node )
|
||||||
EdgeEvent(tcx, *edge->p, *edge->q, node->triangle, *edge->q);
|
EdgeEvent(tcx, *edge->p, *edge->q, node->triangle, *edge->q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point)
|
void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point)
|
||||||
{
|
{
|
||||||
if( IsEdgeSideOfTriangle( *triangle, ep, eq ) )
|
if (IsEdgeSideOfTriangle(*triangle, ep, eq)) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point* p1 = triangle->PointCCW(point);
|
Point* p1 = triangle->PointCCW(point);
|
||||||
Orientation o1 = Orient2d(eq, *p1, ep);
|
Orientation o1 = Orient2d(eq, *p1, ep);
|
||||||
|
if (o1 == COLLINEAR) {
|
||||||
if( o1 == COLLINEAR )
|
if( triangle->Contains(&eq, p1)) {
|
||||||
{
|
|
||||||
if( triangle->Contains( &eq, p1 ) )
|
|
||||||
{
|
|
||||||
triangle->MarkConstrainedEdge(&eq, p1 );
|
triangle->MarkConstrainedEdge(&eq, p1 );
|
||||||
// We are modifying the constraint maybe it would be better to
|
// We are modifying the constraint maybe it would be better to
|
||||||
// not change the given constraint and just keep a variable for the new constraint
|
// not change the given constraint and just keep a variable for the new constraint
|
||||||
tcx.edge_event.constrained_edge->q = p1;
|
tcx.edge_event.constrained_edge->q = p1;
|
||||||
triangle = &triangle->NeighborAcross(point);
|
triangle = &triangle->NeighborAcross(point);
|
||||||
EdgeEvent( tcx, ep, *p1, triangle, *p1 );
|
EdgeEvent( tcx, ep, *p1, triangle, *p1 );
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
std::runtime_error("EdgeEvent - collinear points not supported");
|
std::runtime_error("EdgeEvent - collinear points not supported");
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point* p2 = triangle->PointCW(point);
|
Point* p2 = triangle->PointCW(point);
|
||||||
Orientation o2 = Orient2d(eq, *p2, ep);
|
Orientation o2 = Orient2d(eq, *p2, ep);
|
||||||
|
if (o2 == COLLINEAR) {
|
||||||
if( o2 == COLLINEAR )
|
if( triangle->Contains(&eq, p2)) {
|
||||||
{
|
|
||||||
if( triangle->Contains( &eq, p2 ) )
|
|
||||||
{
|
|
||||||
triangle->MarkConstrainedEdge(&eq, p2 );
|
triangle->MarkConstrainedEdge(&eq, p2 );
|
||||||
// We are modifying the constraint maybe it would be better to
|
// We are modifying the constraint maybe it would be better to
|
||||||
// not change the given constraint and just keep a variable for the new constraint
|
// not change the given constraint and just keep a variable for the new constraint
|
||||||
tcx.edge_event.constrained_edge->q = p2;
|
tcx.edge_event.constrained_edge->q = p2;
|
||||||
triangle = &triangle->NeighborAcross(point);
|
triangle = &triangle->NeighborAcross(point);
|
||||||
EdgeEvent( tcx, ep, *p2, triangle, *p2 );
|
EdgeEvent( tcx, ep, *p2, triangle, *p2 );
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
std::runtime_error("EdgeEvent - collinear points not supported");
|
std::runtime_error("EdgeEvent - collinear points not supported");
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( o1 == o2 )
|
if (o1 == o2) {
|
||||||
{
|
|
||||||
// Need to decide if we are rotating CW or CCW to get to a triangle
|
// Need to decide if we are rotating CW or CCW to get to a triangle
|
||||||
// that will cross edge
|
// that will cross edge
|
||||||
if( o1 == CW )
|
if (o1 == CW) {
|
||||||
{
|
|
||||||
triangle = triangle->NeighborCCW(point);
|
triangle = triangle->NeighborCCW(point);
|
||||||
}
|
} else{
|
||||||
else
|
|
||||||
{
|
|
||||||
triangle = triangle->NeighborCW(point);
|
triangle = triangle->NeighborCW(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
EdgeEvent(tcx, ep, eq, triangle, point);
|
EdgeEvent(tcx, ep, eq, triangle, point);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// This triangle crosses constraint so lets flippin start!
|
// This triangle crosses constraint so lets flippin start!
|
||||||
FlipEdgeEvent(tcx, ep, eq, triangle, point);
|
FlipEdgeEvent(tcx, ep, eq, triangle, point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Sweep::IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq)
|
bool Sweep::IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq)
|
||||||
{
|
{
|
||||||
int index = triangle.EdgeIndex(&ep, &eq);
|
int index = triangle.EdgeIndex(&ep, &eq);
|
||||||
|
|
||||||
if( index != -1 )
|
if (index != -1) {
|
||||||
{
|
|
||||||
triangle.MarkConstrainedEdge(index);
|
triangle.MarkConstrainedEdge(index);
|
||||||
Triangle* t = triangle.GetNeighbor(index);
|
Triangle* t = triangle.GetNeighbor(index);
|
||||||
|
if (t) {
|
||||||
if( t )
|
|
||||||
{
|
|
||||||
t->MarkConstrainedEdge(&ep, &eq);
|
t->MarkConstrainedEdge(&ep, &eq);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Node& Sweep::NewFrontTriangle(SweepContext& tcx, Point& point, Node& node)
|
Node& Sweep::NewFrontTriangle(SweepContext& tcx, Point& point, Node& node)
|
||||||
{
|
{
|
||||||
Triangle* triangle = new Triangle(point, *node.point, *node.next->point);
|
Triangle* triangle = new Triangle(point, *node.point, *node.next->point);
|
||||||
|
@ -227,15 +189,13 @@ Node& Sweep::NewFrontTriangle( SweepContext& tcx, Point& point, Node& node )
|
||||||
node.next->prev = new_node;
|
node.next->prev = new_node;
|
||||||
node.next = new_node;
|
node.next = new_node;
|
||||||
|
|
||||||
if( !Legalize( tcx, *triangle ) )
|
if (!Legalize(tcx, *triangle)) {
|
||||||
{
|
|
||||||
tcx.MapTriangleToNodes(*triangle);
|
tcx.MapTriangleToNodes(*triangle);
|
||||||
}
|
}
|
||||||
|
|
||||||
return *new_node;
|
return *new_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::Fill(SweepContext& tcx, Node& node)
|
void Sweep::Fill(SweepContext& tcx, Node& node)
|
||||||
{
|
{
|
||||||
Triangle* triangle = new Triangle(*node.prev->point, *node.point, *node.next->point);
|
Triangle* triangle = new Triangle(*node.prev->point, *node.point, *node.next->point);
|
||||||
|
@ -252,24 +212,21 @@ void Sweep::Fill( SweepContext& tcx, Node& node )
|
||||||
node.next->prev = node.prev;
|
node.next->prev = node.prev;
|
||||||
|
|
||||||
// If it was legalized the triangle has already been mapped
|
// If it was legalized the triangle has already been mapped
|
||||||
if( !Legalize( tcx, *triangle ) )
|
if (!Legalize(tcx, *triangle)) {
|
||||||
{
|
|
||||||
tcx.MapTriangleToNodes(*triangle);
|
tcx.MapTriangleToNodes(*triangle);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n)
|
void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n)
|
||||||
{
|
{
|
||||||
|
|
||||||
// Fill right holes
|
// Fill right holes
|
||||||
Node* node = n.next;
|
Node* node = n.next;
|
||||||
|
|
||||||
while( node->next )
|
while (node->next) {
|
||||||
{
|
|
||||||
// if HoleAngle exceeds 90 degrees then break.
|
// if HoleAngle exceeds 90 degrees then break.
|
||||||
if( LargeHole_DontFill( node ) )
|
if (LargeHole_DontFill(node)) break;
|
||||||
break;
|
|
||||||
|
|
||||||
Fill(tcx, *node);
|
Fill(tcx, *node);
|
||||||
node = node->next;
|
node = node->next;
|
||||||
}
|
}
|
||||||
|
@ -277,79 +234,57 @@ void Sweep::FillAdvancingFront( SweepContext& tcx, Node& n )
|
||||||
// Fill left holes
|
// Fill left holes
|
||||||
node = n.prev;
|
node = n.prev;
|
||||||
|
|
||||||
while( node->prev )
|
while (node->prev) {
|
||||||
{
|
|
||||||
// if HoleAngle exceeds 90 degrees then break.
|
// if HoleAngle exceeds 90 degrees then break.
|
||||||
if( LargeHole_DontFill( node ) )
|
if (LargeHole_DontFill(node)) break;
|
||||||
break;
|
|
||||||
|
|
||||||
Fill(tcx, *node);
|
Fill(tcx, *node);
|
||||||
node = node->prev;
|
node = node->prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill right basins
|
// Fill right basins
|
||||||
if( n.next && n.next->next )
|
if (n.next && n.next->next) {
|
||||||
{
|
|
||||||
double angle = BasinAngle(n);
|
double angle = BasinAngle(n);
|
||||||
|
if (angle < PI_3div4) {
|
||||||
if( angle < PI_3div4 )
|
|
||||||
{
|
|
||||||
FillBasin(tcx, n);
|
FillBasin(tcx, n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// True if HoleAngle exceeds 90 degrees.
|
// True if HoleAngle exceeds 90 degrees.
|
||||||
bool Sweep::LargeHole_DontFill( Node* node )
|
bool Sweep::LargeHole_DontFill(Node* node) {
|
||||||
{
|
|
||||||
Node* nextNode = node->next;
|
Node* nextNode = node->next;
|
||||||
Node* prevNode = node->prev;
|
Node* prevNode = node->prev;
|
||||||
|
|
||||||
if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point))
|
if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check additional points on front.
|
// Check additional points on front.
|
||||||
Node* next2Node = nextNode->next;
|
Node* next2Node = nextNode->next;
|
||||||
|
|
||||||
// "..Plus.." because only want angles on same side as point being added.
|
// "..Plus.." because only want angles on same side as point being added.
|
||||||
if( (next2Node != NULL)
|
if ((next2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, next2Node->point, prevNode->point))
|
||||||
&& !AngleExceedsPlus90DegreesOrIsNegative( node->point, next2Node->point,
|
|
||||||
prevNode->point ) )
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Node* prev2Node = prevNode->prev;
|
Node* prev2Node = prevNode->prev;
|
||||||
|
|
||||||
// "..Plus.." because only want angles on same side as point being added.
|
// "..Plus.." because only want angles on same side as point being added.
|
||||||
if( (prev2Node != NULL)
|
if ((prev2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, nextNode->point, prev2Node->point))
|
||||||
&& !AngleExceedsPlus90DegreesOrIsNegative( node->point, nextNode->point,
|
|
||||||
prev2Node->point ) )
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Sweep::AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb) {
|
||||||
bool Sweep::AngleExceeds90Degrees( Point* origin, Point* pa, Point* pb )
|
|
||||||
{
|
|
||||||
double angle = Angle(*origin, *pa, *pb);
|
double angle = Angle(*origin, *pa, *pb);
|
||||||
bool exceeds90Degrees = ((angle > PI_div2) || (angle < -PI_div2));
|
bool exceeds90Degrees = ((angle > PI_div2) || (angle < -PI_div2));
|
||||||
|
|
||||||
return exceeds90Degrees;
|
return exceeds90Degrees;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Sweep::AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb) {
|
||||||
bool Sweep::AngleExceedsPlus90DegreesOrIsNegative( Point* origin, Point* pa, Point* pb )
|
|
||||||
{
|
|
||||||
double angle = Angle(*origin, *pa, *pb);
|
double angle = Angle(*origin, *pa, *pb);
|
||||||
bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0);
|
bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0);
|
||||||
|
|
||||||
return exceedsPlus90DegreesOrIsNegative;
|
return exceedsPlus90DegreesOrIsNegative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double Sweep::Angle(Point& origin, Point& pa, Point& pb) {
|
||||||
double Sweep::Angle( Point& origin, Point& pa, Point& pb )
|
|
||||||
{
|
|
||||||
/* Complex plane
|
/* Complex plane
|
||||||
* ab = cosA +i*sinA
|
* ab = cosA +i*sinA
|
||||||
* ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
|
* ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
|
||||||
|
@ -367,20 +302,16 @@ double Sweep::Angle( Point& origin, Point& pa, Point& pb )
|
||||||
double x = ax * by - ay * bx;
|
double x = ax * by - ay * bx;
|
||||||
double y = ax * bx + ay * by;
|
double y = ax * bx + ay * by;
|
||||||
double angle = atan2(x, y);
|
double angle = atan2(x, y);
|
||||||
|
|
||||||
return angle;
|
return angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double Sweep::BasinAngle(Node& node)
|
double Sweep::BasinAngle(Node& node)
|
||||||
{
|
{
|
||||||
double ax = node.point->x - node.next->next->point->x;
|
double ax = node.point->x - node.next->next->point->x;
|
||||||
double ay = node.point->y - node.next->next->point->y;
|
double ay = node.point->y - node.next->next->point->y;
|
||||||
|
|
||||||
return atan2(ay, ax);
|
return atan2(ay, ax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double Sweep::HoleAngle(Node& node)
|
double Sweep::HoleAngle(Node& node)
|
||||||
{
|
{
|
||||||
/* Complex plane
|
/* Complex plane
|
||||||
|
@ -395,40 +326,34 @@ double Sweep::HoleAngle( Node& node )
|
||||||
double ay = node.next->point->y - node.point->y;
|
double ay = node.next->point->y - node.point->y;
|
||||||
double bx = node.prev->point->x - node.point->x;
|
double bx = node.prev->point->x - node.point->x;
|
||||||
double by = node.prev->point->y - node.point->y;
|
double by = node.prev->point->y - node.point->y;
|
||||||
|
|
||||||
return atan2(ax * by - ay * bx, ax * bx + ay * by);
|
return atan2(ax * by - ay * bx, ax * bx + ay * by);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Sweep::Legalize(SweepContext& tcx, Triangle& t)
|
bool Sweep::Legalize(SweepContext& tcx, Triangle& t)
|
||||||
{
|
{
|
||||||
// To legalize a triangle we start by finding if any of the three edges
|
// To legalize a triangle we start by finding if any of the three edges
|
||||||
// violate the Delaunay condition
|
// violate the Delaunay condition
|
||||||
for( int i = 0; i < 3; i++ )
|
for (int i = 0; i < 3; i++) {
|
||||||
{
|
|
||||||
if (t.delaunay_edge[i])
|
if (t.delaunay_edge[i])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Triangle* ot = t.GetNeighbor(i);
|
Triangle* ot = t.GetNeighbor(i);
|
||||||
|
|
||||||
if( ot )
|
if (ot) {
|
||||||
{
|
|
||||||
Point* p = t.GetPoint(i);
|
Point* p = t.GetPoint(i);
|
||||||
Point* op = ot->OppositePoint(t, *p);
|
Point* op = ot->OppositePoint(t, *p);
|
||||||
int oi = ot->Index(op);
|
int oi = ot->Index(op);
|
||||||
|
|
||||||
// If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization)
|
// If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization)
|
||||||
// then we should not try to legalize
|
// then we should not try to legalize
|
||||||
if( ot->constrained_edge[oi] || ot->delaunay_edge[oi] )
|
if (ot->constrained_edge[oi] || ot->delaunay_edge[oi]) {
|
||||||
{
|
|
||||||
t.constrained_edge[i] = ot->constrained_edge[oi];
|
t.constrained_edge[i] = ot->constrained_edge[oi];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool inside = Incircle(*p, *t.PointCCW(*p), *t.PointCW(*p), *op);
|
bool inside = Incircle(*p, *t.PointCCW(*p), *t.PointCW(*p), *op);
|
||||||
|
|
||||||
if( inside )
|
if (inside) {
|
||||||
{
|
|
||||||
// Lets mark this shared edge as Delaunay
|
// Lets mark this shared edge as Delaunay
|
||||||
t.delaunay_edge[i] = true;
|
t.delaunay_edge[i] = true;
|
||||||
ot->delaunay_edge[oi] = true;
|
ot->delaunay_edge[oi] = true;
|
||||||
|
@ -441,14 +366,11 @@ bool Sweep::Legalize( SweepContext& tcx, Triangle& t )
|
||||||
|
|
||||||
// Make sure that triangle to node mapping is done only one time for a specific triangle
|
// Make sure that triangle to node mapping is done only one time for a specific triangle
|
||||||
bool not_legalized = !Legalize(tcx, t);
|
bool not_legalized = !Legalize(tcx, t);
|
||||||
|
if (not_legalized) {
|
||||||
if( not_legalized )
|
|
||||||
{
|
|
||||||
tcx.MapTriangleToNodes(t);
|
tcx.MapTriangleToNodes(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
not_legalized = !Legalize(tcx, *ot);
|
not_legalized = !Legalize(tcx, *ot);
|
||||||
|
|
||||||
if (not_legalized)
|
if (not_legalized)
|
||||||
tcx.MapTriangleToNodes(*ot);
|
tcx.MapTriangleToNodes(*ot);
|
||||||
|
|
||||||
|
@ -465,11 +387,9 @@ bool Sweep::Legalize( SweepContext& tcx, Triangle& t )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd)
|
bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd)
|
||||||
{
|
{
|
||||||
double adx = pa.x - pd.x;
|
double adx = pa.x - pd.x;
|
||||||
|
@ -506,11 +426,9 @@ bool Sweep::Incircle( Point& pa, Point& pb, Point& pc, Point& pd )
|
||||||
return det > 0;
|
return det > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op)
|
void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op)
|
||||||
{
|
{
|
||||||
Triangle* n1, *n2, *n3, *n4;
|
Triangle* n1, *n2, *n3, *n4;
|
||||||
|
|
||||||
n1 = t.NeighborCCW(p);
|
n1 = t.NeighborCCW(p);
|
||||||
n2 = t.NeighborCW(p);
|
n2 = t.NeighborCW(p);
|
||||||
n3 = ot.NeighborCCW(op);
|
n3 = ot.NeighborCCW(op);
|
||||||
|
@ -550,59 +468,38 @@ void Sweep::RotateTrianglePair( Triangle& t, Point& p, Triangle& ot, Point& op )
|
||||||
// the right side.
|
// the right side.
|
||||||
t.ClearNeighbors();
|
t.ClearNeighbors();
|
||||||
ot.ClearNeighbors();
|
ot.ClearNeighbors();
|
||||||
|
if (n1) ot.MarkNeighbor(*n1);
|
||||||
if( n1 )
|
if (n2) t.MarkNeighbor(*n2);
|
||||||
ot.MarkNeighbor( *n1 );
|
if (n3) t.MarkNeighbor(*n3);
|
||||||
|
if (n4) ot.MarkNeighbor(*n4);
|
||||||
if( n2 )
|
|
||||||
t.MarkNeighbor( *n2 );
|
|
||||||
|
|
||||||
if( n3 )
|
|
||||||
t.MarkNeighbor( *n3 );
|
|
||||||
|
|
||||||
if( n4 )
|
|
||||||
ot.MarkNeighbor( *n4 );
|
|
||||||
|
|
||||||
t.MarkNeighbor(ot);
|
t.MarkNeighbor(ot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::FillBasin(SweepContext& tcx, Node& node)
|
void Sweep::FillBasin(SweepContext& tcx, Node& node)
|
||||||
{
|
{
|
||||||
if( Orient2d( *node.point, *node.next->point, *node.next->next->point ) == CCW )
|
if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
|
||||||
{
|
|
||||||
tcx.basin.left_node = node.next->next;
|
tcx.basin.left_node = node.next->next;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
tcx.basin.left_node = node.next;
|
tcx.basin.left_node = node.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the bottom and right node
|
// Find the bottom and right node
|
||||||
tcx.basin.bottom_node = tcx.basin.left_node;
|
tcx.basin.bottom_node = tcx.basin.left_node;
|
||||||
|
|
||||||
while (tcx.basin.bottom_node->next
|
while (tcx.basin.bottom_node->next
|
||||||
&& tcx.basin.bottom_node->point->y >= tcx.basin.bottom_node->next->point->y )
|
&& tcx.basin.bottom_node->point->y >= tcx.basin.bottom_node->next->point->y) {
|
||||||
{
|
|
||||||
tcx.basin.bottom_node = tcx.basin.bottom_node->next;
|
tcx.basin.bottom_node = tcx.basin.bottom_node->next;
|
||||||
}
|
}
|
||||||
|
if (tcx.basin.bottom_node == tcx.basin.left_node) {
|
||||||
if( tcx.basin.bottom_node == tcx.basin.left_node )
|
|
||||||
{
|
|
||||||
// No valid basin
|
// No valid basin
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tcx.basin.right_node = tcx.basin.bottom_node;
|
tcx.basin.right_node = tcx.basin.bottom_node;
|
||||||
|
|
||||||
while (tcx.basin.right_node->next
|
while (tcx.basin.right_node->next
|
||||||
&& tcx.basin.right_node->point->y < tcx.basin.right_node->next->point->y )
|
&& tcx.basin.right_node->point->y < tcx.basin.right_node->next->point->y) {
|
||||||
{
|
|
||||||
tcx.basin.right_node = tcx.basin.right_node->next;
|
tcx.basin.right_node = tcx.basin.right_node->next;
|
||||||
}
|
}
|
||||||
|
if (tcx.basin.right_node == tcx.basin.bottom_node) {
|
||||||
if( tcx.basin.right_node == tcx.basin.bottom_node )
|
|
||||||
{
|
|
||||||
// No valid basins
|
// No valid basins
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -613,52 +510,34 @@ void Sweep::FillBasin( SweepContext& tcx, Node& node )
|
||||||
FillBasinReq(tcx, tcx.basin.bottom_node);
|
FillBasinReq(tcx, tcx.basin.bottom_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::FillBasinReq(SweepContext& tcx, Node* node)
|
void Sweep::FillBasinReq(SweepContext& tcx, Node* node)
|
||||||
{
|
{
|
||||||
// if shallow stop filling
|
// if shallow stop filling
|
||||||
if( IsShallow( tcx, *node ) )
|
if (IsShallow(tcx, *node)) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Fill(tcx, *node);
|
Fill(tcx, *node);
|
||||||
|
|
||||||
if( node->prev == tcx.basin.left_node && node->next == tcx.basin.right_node )
|
if (node->prev == tcx.basin.left_node && node->next == tcx.basin.right_node) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
} else if (node->prev == tcx.basin.left_node) {
|
||||||
else if( node->prev == tcx.basin.left_node )
|
|
||||||
{
|
|
||||||
Orientation o = Orient2d(*node->point, *node->next->point, *node->next->next->point);
|
Orientation o = Orient2d(*node->point, *node->next->point, *node->next->next->point);
|
||||||
|
if (o == CW) {
|
||||||
if( o == CW )
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = node->next;
|
node = node->next;
|
||||||
}
|
} else if (node->next == tcx.basin.right_node) {
|
||||||
else if( node->next == tcx.basin.right_node )
|
|
||||||
{
|
|
||||||
Orientation o = Orient2d(*node->point, *node->prev->point, *node->prev->prev->point);
|
Orientation o = Orient2d(*node->point, *node->prev->point, *node->prev->prev->point);
|
||||||
|
if (o == CCW) {
|
||||||
if( o == CCW )
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = node->prev;
|
node = node->prev;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Continue with the neighbor node with lowest Y value
|
// Continue with the neighbor node with lowest Y value
|
||||||
if( node->prev->point->y < node->next->point->y )
|
if (node->prev->point->y < node->next->point->y) {
|
||||||
{
|
|
||||||
node = node->prev;
|
node = node->prev;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
node = node->next;
|
node = node->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,71 +545,51 @@ void Sweep::FillBasinReq( SweepContext& tcx, Node* node )
|
||||||
FillBasinReq(tcx, node);
|
FillBasinReq(tcx, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Sweep::IsShallow(SweepContext& tcx, Node& node)
|
bool Sweep::IsShallow(SweepContext& tcx, Node& node)
|
||||||
{
|
{
|
||||||
double height;
|
double height;
|
||||||
|
|
||||||
if( tcx.basin.left_highest )
|
if (tcx.basin.left_highest) {
|
||||||
{
|
|
||||||
height = tcx.basin.left_node->point->y - node.point->y;
|
height = tcx.basin.left_node->point->y - node.point->y;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
height = tcx.basin.right_node->point->y - node.point->y;
|
height = tcx.basin.right_node->point->y - node.point->y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if shallow stop filling
|
// if shallow stop filling
|
||||||
if( tcx.basin.width > height )
|
if (tcx.basin.width > height) {
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
void Sweep::FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
||||||
{
|
{
|
||||||
if( tcx.edge_event.right )
|
if (tcx.edge_event.right) {
|
||||||
{
|
|
||||||
FillRightAboveEdgeEvent(tcx, edge, node);
|
FillRightAboveEdgeEvent(tcx, edge, node);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
FillLeftAboveEdgeEvent(tcx, edge, node);
|
FillLeftAboveEdgeEvent(tcx, edge, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
void Sweep::FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
||||||
{
|
{
|
||||||
while( node->next->point->x < edge->p->x )
|
while (node->next->point->x < edge->p->x) {
|
||||||
{
|
|
||||||
// Check if next node is below the edge
|
// Check if next node is below the edge
|
||||||
if( Orient2d( *edge->q, *node->next->point, *edge->p ) == CCW )
|
if (Orient2d(*edge->q, *node->next->point, *edge->p) == CCW) {
|
||||||
{
|
|
||||||
FillRightBelowEdgeEvent(tcx, edge, *node);
|
FillRightBelowEdgeEvent(tcx, edge, *node);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
node = node->next;
|
node = node->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
||||||
{
|
{
|
||||||
if( node.point->x < edge->p->x )
|
if (node.point->x < edge->p->x) {
|
||||||
{
|
if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
|
||||||
if( Orient2d( *node.point, *node.next->point, *node.next->next->point ) == CCW )
|
|
||||||
{
|
|
||||||
// Concave
|
// Concave
|
||||||
FillRightConcaveEdgeEvent(tcx, edge, node);
|
FillRightConcaveEdgeEvent(tcx, edge, node);
|
||||||
}
|
} else{
|
||||||
else
|
|
||||||
{
|
|
||||||
// Convex
|
// Convex
|
||||||
FillRightConvexEdgeEvent(tcx, edge, node);
|
FillRightConvexEdgeEvent(tcx, edge, node);
|
||||||
// Retry this one
|
// Retry this one
|
||||||
|
@ -739,85 +598,61 @@ void Sweep::FillRightBelowEdgeEvent( SweepContext& tcx, Edge* edge, Node& node )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
||||||
{
|
{
|
||||||
Fill(tcx, *node.next);
|
Fill(tcx, *node.next);
|
||||||
|
if (node.next->point != edge->p) {
|
||||||
if( node.next->point != edge->p )
|
|
||||||
{
|
|
||||||
// Next above or below edge?
|
// Next above or below edge?
|
||||||
if( Orient2d( *edge->q, *node.next->point, *edge->p ) == CCW )
|
if (Orient2d(*edge->q, *node.next->point, *edge->p) == CCW) {
|
||||||
{
|
|
||||||
// Below
|
// Below
|
||||||
if( Orient2d( *node.point, *node.next->point, *node.next->next->point ) == CCW )
|
if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
|
||||||
{
|
|
||||||
// Next is concave
|
// Next is concave
|
||||||
FillRightConcaveEdgeEvent(tcx, edge, node);
|
FillRightConcaveEdgeEvent(tcx, edge, node);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Next is convex
|
// Next is convex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
||||||
{
|
{
|
||||||
// Next concave or convex?
|
// Next concave or convex?
|
||||||
if( Orient2d( *node.next->point, *node.next->next->point,
|
if (Orient2d(*node.next->point, *node.next->next->point, *node.next->next->next->point) == CCW) {
|
||||||
*node.next->next->next->point ) == CCW )
|
|
||||||
{
|
|
||||||
// Concave
|
// Concave
|
||||||
FillRightConcaveEdgeEvent(tcx, edge, *node.next);
|
FillRightConcaveEdgeEvent(tcx, edge, *node.next);
|
||||||
}
|
} else{
|
||||||
else
|
|
||||||
{
|
|
||||||
// Convex
|
// Convex
|
||||||
// Next above or below edge?
|
// Next above or below edge?
|
||||||
if( Orient2d( *edge->q, *node.next->next->point, *edge->p ) == CCW )
|
if (Orient2d(*edge->q, *node.next->next->point, *edge->p) == CCW) {
|
||||||
{
|
|
||||||
// Below
|
// Below
|
||||||
FillRightConvexEdgeEvent(tcx, edge, *node.next);
|
FillRightConvexEdgeEvent(tcx, edge, *node.next);
|
||||||
}
|
} else{
|
||||||
else
|
|
||||||
{
|
|
||||||
// Above
|
// Above
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
void Sweep::FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
||||||
{
|
{
|
||||||
while( node->prev->point->x > edge->p->x )
|
while (node->prev->point->x > edge->p->x) {
|
||||||
{
|
|
||||||
// Check if next node is below the edge
|
// Check if next node is below the edge
|
||||||
if( Orient2d( *edge->q, *node->prev->point, *edge->p ) == CW )
|
if (Orient2d(*edge->q, *node->prev->point, *edge->p) == CW) {
|
||||||
{
|
|
||||||
FillLeftBelowEdgeEvent(tcx, edge, *node);
|
FillLeftBelowEdgeEvent(tcx, edge, *node);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
node = node->prev;
|
node = node->prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
void Sweep::FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
||||||
{
|
{
|
||||||
if( node.point->x > edge->p->x )
|
if (node.point->x > edge->p->x) {
|
||||||
{
|
if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) {
|
||||||
if( Orient2d( *node.point, *node.prev->point, *node.prev->prev->point ) == CW )
|
|
||||||
{
|
|
||||||
// Concave
|
// Concave
|
||||||
FillLeftConcaveEdgeEvent(tcx, edge, node);
|
FillLeftConcaveEdgeEvent(tcx, edge, node);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Convex
|
// Convex
|
||||||
FillLeftConvexEdgeEvent(tcx, edge, node);
|
FillLeftConvexEdgeEvent(tcx, edge, node);
|
||||||
// Retry this one
|
// Retry this one
|
||||||
|
@ -826,109 +661,84 @@ void Sweep::FillLeftBelowEdgeEvent( SweepContext& tcx, Edge* edge, Node& node )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
||||||
{
|
{
|
||||||
// Next concave or convex?
|
// Next concave or convex?
|
||||||
if( Orient2d( *node.prev->point, *node.prev->prev->point,
|
if (Orient2d(*node.prev->point, *node.prev->prev->point, *node.prev->prev->prev->point) == CW) {
|
||||||
*node.prev->prev->prev->point ) == CW )
|
|
||||||
{
|
|
||||||
// Concave
|
// Concave
|
||||||
FillLeftConcaveEdgeEvent(tcx, edge, *node.prev);
|
FillLeftConcaveEdgeEvent(tcx, edge, *node.prev);
|
||||||
}
|
} else{
|
||||||
else
|
|
||||||
{
|
|
||||||
// Convex
|
// Convex
|
||||||
// Next above or below edge?
|
// Next above or below edge?
|
||||||
if( Orient2d( *edge->q, *node.prev->prev->point, *edge->p ) == CW )
|
if (Orient2d(*edge->q, *node.prev->prev->point, *edge->p) == CW) {
|
||||||
{
|
|
||||||
// Below
|
// Below
|
||||||
FillLeftConvexEdgeEvent(tcx, edge, *node.prev);
|
FillLeftConvexEdgeEvent(tcx, edge, *node.prev);
|
||||||
}
|
} else{
|
||||||
else
|
|
||||||
{
|
|
||||||
// Above
|
// Above
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
||||||
{
|
{
|
||||||
Fill(tcx, *node.prev);
|
Fill(tcx, *node.prev);
|
||||||
|
if (node.prev->point != edge->p) {
|
||||||
if( node.prev->point != edge->p )
|
|
||||||
{
|
|
||||||
// Next above or below edge?
|
// Next above or below edge?
|
||||||
if( Orient2d( *edge->q, *node.prev->point, *edge->p ) == CW )
|
if (Orient2d(*edge->q, *node.prev->point, *edge->p) == CW) {
|
||||||
{
|
|
||||||
// Below
|
// Below
|
||||||
if( Orient2d( *node.point, *node.prev->point, *node.prev->prev->point ) == CW )
|
if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) {
|
||||||
{
|
|
||||||
// Next is concave
|
// Next is concave
|
||||||
FillLeftConcaveEdgeEvent(tcx, edge, node);
|
FillLeftConcaveEdgeEvent(tcx, edge, node);
|
||||||
}
|
} else{
|
||||||
else
|
|
||||||
{
|
|
||||||
// Next is convex
|
// Next is convex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p)
|
void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p)
|
||||||
{
|
{
|
||||||
Triangle& ot = t->NeighborAcross(p);
|
Triangle& ot = t->NeighborAcross(p);
|
||||||
Point& op = *ot.OppositePoint(*t, p);
|
Point& op = *ot.OppositePoint(*t, p);
|
||||||
|
|
||||||
if( InScanArea( p, *t->PointCCW( p ), *t->PointCW( p ), op ) )
|
if (&ot == NULL) {
|
||||||
{
|
// If we want to integrate the fillEdgeEvent do it here
|
||||||
|
// With current implementation we should never get here
|
||||||
|
//throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) {
|
||||||
// Lets rotate shared edge one vertex CW
|
// Lets rotate shared edge one vertex CW
|
||||||
RotateTrianglePair(*t, p, ot, op);
|
RotateTrianglePair(*t, p, ot, op);
|
||||||
tcx.MapTriangleToNodes(*t);
|
tcx.MapTriangleToNodes(*t);
|
||||||
tcx.MapTriangleToNodes(ot);
|
tcx.MapTriangleToNodes(ot);
|
||||||
|
|
||||||
if( p == eq && op == ep )
|
if (p == eq && op == ep) {
|
||||||
{
|
if (eq == *tcx.edge_event.constrained_edge->q && ep == *tcx.edge_event.constrained_edge->p) {
|
||||||
if( eq == *tcx.edge_event.constrained_edge->q
|
|
||||||
&& ep == *tcx.edge_event.constrained_edge->p )
|
|
||||||
{
|
|
||||||
t->MarkConstrainedEdge(&ep, &eq);
|
t->MarkConstrainedEdge(&ep, &eq);
|
||||||
ot.MarkConstrainedEdge(&ep, &eq);
|
ot.MarkConstrainedEdge(&ep, &eq);
|
||||||
Legalize(tcx, *t);
|
Legalize(tcx, *t);
|
||||||
Legalize(tcx, ot);
|
Legalize(tcx, ot);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// XXX: I think one of the triangles should be legalized here?
|
// XXX: I think one of the triangles should be legalized here?
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
Orientation o = Orient2d(eq, op, ep);
|
Orientation o = Orient2d(eq, op, ep);
|
||||||
t = &NextFlipTriangle(tcx, (int)o, *t, ot, p, op);
|
t = &NextFlipTriangle(tcx, (int)o, *t, ot, p, op);
|
||||||
FlipEdgeEvent(tcx, ep, eq, t, p);
|
FlipEdgeEvent(tcx, ep, eq, t, p);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
Point& newP = NextFlipPoint(ep, eq, ot, op);
|
Point& newP = NextFlipPoint(ep, eq, ot, op);
|
||||||
FlipScanEdgeEvent(tcx, ep, eq, *t, ot, newP);
|
FlipScanEdgeEvent(tcx, ep, eq, *t, ot, newP);
|
||||||
EdgeEvent(tcx, ep, eq, t, p);
|
EdgeEvent(tcx, ep, eq, t, p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Triangle& Sweep::NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op)
|
||||||
Triangle& Sweep::NextFlipTriangle( SweepContext& tcx,
|
|
||||||
int o,
|
|
||||||
Triangle& t,
|
|
||||||
Triangle& ot,
|
|
||||||
Point& p,
|
|
||||||
Point& op )
|
|
||||||
{
|
|
||||||
if( o == CCW )
|
|
||||||
{
|
{
|
||||||
|
if (o == CCW) {
|
||||||
// ot is not crossing edge after flip
|
// ot is not crossing edge after flip
|
||||||
int edge_index = ot.EdgeIndex(&p, &op);
|
int edge_index = ot.EdgeIndex(&p, &op);
|
||||||
ot.delaunay_edge[edge_index] = true;
|
ot.delaunay_edge[edge_index] = true;
|
||||||
|
@ -946,29 +756,20 @@ Triangle& Sweep::NextFlipTriangle( SweepContext& tcx,
|
||||||
return ot;
|
return ot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op)
|
Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op)
|
||||||
{
|
{
|
||||||
Orientation o2d = Orient2d(eq, op, ep);
|
Orientation o2d = Orient2d(eq, op, ep);
|
||||||
|
if (o2d == CW) {
|
||||||
if( o2d == CW )
|
|
||||||
{
|
|
||||||
// Right
|
// Right
|
||||||
return *ot.PointCCW(op);
|
return *ot.PointCCW(op);
|
||||||
}
|
} else if (o2d == CCW) {
|
||||||
else if( o2d == CCW )
|
|
||||||
{
|
|
||||||
// Left
|
// Left
|
||||||
return *ot.PointCW(op);
|
return *ot.PointCW(op);
|
||||||
}
|
} else{
|
||||||
|
|
||||||
//throw new RuntimeException("[Unsupported] Opposing point on constrained edge");
|
//throw new RuntimeException("[Unsupported] Opposing point on constrained edge");
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|
||||||
// Never executed, due tu assert( 0 ). Just to avoid compil warning
|
|
||||||
return ep;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle,
|
void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle,
|
||||||
Triangle& t, Point& p)
|
Triangle& t, Point& p)
|
||||||
|
@ -976,8 +777,14 @@ void Sweep::FlipScanEdgeEvent( SweepContext& tcx, Point& ep, Point& eq, Triangle
|
||||||
Triangle& ot = t.NeighborAcross(p);
|
Triangle& ot = t.NeighborAcross(p);
|
||||||
Point& op = *ot.OppositePoint(t, p);
|
Point& op = *ot.OppositePoint(t, p);
|
||||||
|
|
||||||
if( InScanArea( eq, *flip_triangle.PointCCW( eq ), *flip_triangle.PointCW( eq ), op ) )
|
if (&t.NeighborAcross(p) == NULL) {
|
||||||
{
|
// If we want to integrate the fillEdgeEvent do it here
|
||||||
|
// With current implementation we should never get here
|
||||||
|
//throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) {
|
||||||
// flip with new edge op->eq
|
// flip with new edge op->eq
|
||||||
FlipEdgeEvent(tcx, eq, op, &ot, op);
|
FlipEdgeEvent(tcx, eq, op, &ot, op);
|
||||||
// TODO: Actually I just figured out that it should be possible to
|
// TODO: Actually I just figured out that it should be possible to
|
||||||
|
@ -987,21 +794,19 @@ void Sweep::FlipScanEdgeEvent( SweepContext& tcx, Point& ep, Point& eq, Triangle
|
||||||
// also need to set a new flip_triangle first
|
// also need to set a new flip_triangle first
|
||||||
// Turns out at first glance that this is somewhat complicated
|
// Turns out at first glance that this is somewhat complicated
|
||||||
// so it will have to wait.
|
// so it will have to wait.
|
||||||
}
|
} else{
|
||||||
else
|
|
||||||
{
|
|
||||||
Point& newP = NextFlipPoint(ep, eq, ot, op);
|
Point& newP = NextFlipPoint(ep, eq, ot, op);
|
||||||
FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP);
|
FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Sweep::~Sweep() {
|
||||||
|
|
||||||
Sweep::~Sweep()
|
|
||||||
{
|
|
||||||
// Clean up memory
|
// Clean up memory
|
||||||
for( unsigned i = 0; i < nodes_.size(); i++ )
|
for(int i = 0; i < nodes_.size(); i++) {
|
||||||
{
|
|
||||||
delete nodes_[i];
|
delete nodes_[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,10 +136,10 @@ private:
|
||||||
* a,b and c<br>
|
* a,b and c<br>
|
||||||
* d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW<br>
|
* d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW<br>
|
||||||
* This preknowledge gives us a way to optimize the incircle test
|
* This preknowledge gives us a way to optimize the incircle test
|
||||||
* @param pa - triangle point, opposite d
|
* @param a - triangle point, opposite d
|
||||||
* @param pb - triangle point
|
* @param b - triangle point
|
||||||
* @param pc - triangle point
|
* @param c - triangle point
|
||||||
* @param pd - point opposite a
|
* @param d - point opposite a
|
||||||
* @return true if d is inside circle, false if on circle edge
|
* @return true if d is inside circle, false if on circle edge
|
||||||
*/
|
*/
|
||||||
bool Incircle(Point& pa, Point& pb, Point& pc, Point& pd);
|
bool Incircle(Point& pa, Point& pb, Point& pc, Point& pd);
|
||||||
|
@ -204,6 +204,7 @@ private:
|
||||||
*
|
*
|
||||||
* @param tcx
|
* @param tcx
|
||||||
* @param node - bottom_node
|
* @param node - bottom_node
|
||||||
|
* @param cnt - counter used to alternate on even and odd numbers
|
||||||
*/
|
*/
|
||||||
void FillBasinReq(SweepContext& tcx, Node* node);
|
void FillBasinReq(SweepContext& tcx, Node* node);
|
||||||
|
|
||||||
|
@ -267,7 +268,7 @@ private:
|
||||||
* @param tcx
|
* @param tcx
|
||||||
* @param ep - last point on the edge we are traversing
|
* @param ep - last point on the edge we are traversing
|
||||||
* @param eq - first point on the edge we are traversing
|
* @param eq - first point on the edge we are traversing
|
||||||
* @param flip_triangle - the current triangle sharing the point eq with edge
|
* @param flipTriangle - the current triangle sharing the point eq with edge
|
||||||
* @param t
|
* @param t
|
||||||
* @param p
|
* @param p
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "advancing_front.h"
|
#include "advancing_front.h"
|
||||||
|
|
||||||
namespace p2t {
|
namespace p2t {
|
||||||
|
|
||||||
SweepContext::SweepContext(std::vector<Point*> polyline) :
|
SweepContext::SweepContext(std::vector<Point*> polyline) :
|
||||||
front_(0),
|
front_(0),
|
||||||
head_(0),
|
head_(0),
|
||||||
|
@ -49,55 +50,42 @@ SweepContext::SweepContext( std::vector<Point*> polyline ) :
|
||||||
InitEdges(points_);
|
InitEdges(points_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SweepContext::AddHole(std::vector<Point*> polyline)
|
void SweepContext::AddHole(std::vector<Point*> polyline)
|
||||||
{
|
{
|
||||||
InitEdges(polyline);
|
InitEdges(polyline);
|
||||||
|
for(unsigned int i = 0; i < polyline.size(); i++) {
|
||||||
for( unsigned int i = 0; i < polyline.size(); i++ )
|
|
||||||
{
|
|
||||||
points_.push_back(polyline[i]);
|
points_.push_back(polyline[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SweepContext::AddPoint(Point* point) {
|
||||||
void SweepContext::AddPoint( Point* point )
|
|
||||||
{
|
|
||||||
points_.push_back(point);
|
points_.push_back(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<Triangle*> SweepContext::GetTriangles()
|
std::vector<Triangle*> SweepContext::GetTriangles()
|
||||||
{
|
{
|
||||||
return triangles_;
|
return triangles_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::list<Triangle*> SweepContext::GetMap()
|
std::list<Triangle*> SweepContext::GetMap()
|
||||||
{
|
{
|
||||||
return map_;
|
return map_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SweepContext::InitTriangulation()
|
void SweepContext::InitTriangulation()
|
||||||
{
|
{
|
||||||
double xmax(points_[0]->x), xmin(points_[0]->x);
|
double xmax(points_[0]->x), xmin(points_[0]->x);
|
||||||
double ymax(points_[0]->y), ymin(points_[0]->y);
|
double ymax(points_[0]->y), ymin(points_[0]->y);
|
||||||
|
|
||||||
// Calculate bounds.
|
// Calculate bounds.
|
||||||
for( unsigned int i = 0; i < points_.size(); i++ )
|
for (unsigned int i = 0; i < points_.size(); i++) {
|
||||||
{
|
|
||||||
Point& p = *points_[i];
|
Point& p = *points_[i];
|
||||||
|
|
||||||
if (p.x > xmax)
|
if (p.x > xmax)
|
||||||
xmax = p.x;
|
xmax = p.x;
|
||||||
|
|
||||||
if (p.x < xmin)
|
if (p.x < xmin)
|
||||||
xmin = p.x;
|
xmin = p.x;
|
||||||
|
|
||||||
if (p.y > ymax)
|
if (p.y > ymax)
|
||||||
ymax = p.y;
|
ymax = p.y;
|
||||||
|
|
||||||
if (p.y < ymin)
|
if (p.y < ymin)
|
||||||
ymin = p.y;
|
ymin = p.y;
|
||||||
}
|
}
|
||||||
|
@ -109,42 +97,37 @@ void SweepContext::InitTriangulation()
|
||||||
|
|
||||||
// Sort points along y-axis
|
// Sort points along y-axis
|
||||||
std::sort(points_.begin(), points_.end(), cmp);
|
std::sort(points_.begin(), points_.end(), cmp);
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void SweepContext::InitEdges(std::vector<Point*> polyline)
|
void SweepContext::InitEdges(std::vector<Point*> polyline)
|
||||||
{
|
{
|
||||||
int num_points = polyline.size();
|
int num_points = polyline.size();
|
||||||
|
for (int i = 0; i < num_points; i++) {
|
||||||
for( int i = 0; i < num_points; i++ )
|
|
||||||
{
|
|
||||||
int j = i < num_points - 1 ? i + 1 : 0;
|
int j = i < num_points - 1 ? i + 1 : 0;
|
||||||
edge_list.push_back(new Edge(*polyline[i], *polyline[j]));
|
edge_list.push_back(new Edge(*polyline[i], *polyline[j]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Point* SweepContext::GetPoint(const int& index)
|
Point* SweepContext::GetPoint(const int& index)
|
||||||
{
|
{
|
||||||
return points_[index];
|
return points_[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SweepContext::AddToMap(Triangle* triangle)
|
void SweepContext::AddToMap(Triangle* triangle)
|
||||||
{
|
{
|
||||||
map_.push_back(triangle);
|
map_.push_back(triangle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Node& SweepContext::LocateNode(Point& point)
|
Node& SweepContext::LocateNode(Point& point)
|
||||||
{
|
{
|
||||||
// TODO implement search tree
|
// TODO implement search tree
|
||||||
return *front_->LocateNode(point.x);
|
return *front_->LocateNode(point.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SweepContext::CreateAdvancingFront(std::vector<Node*> nodes)
|
void SweepContext::CreateAdvancingFront(std::vector<Node*> nodes)
|
||||||
{
|
{
|
||||||
|
|
||||||
(void) nodes;
|
(void) nodes;
|
||||||
// Initial triangle
|
// Initial triangle
|
||||||
Triangle* triangle = new Triangle(*points_[0], *tail_, *head_);
|
Triangle* triangle = new Triangle(*points_[0], *tail_, *head_);
|
||||||
|
@ -164,52 +147,40 @@ void SweepContext::CreateAdvancingFront( std::vector<Node*> nodes )
|
||||||
af_tail_->prev = af_middle_;
|
af_tail_->prev = af_middle_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SweepContext::RemoveNode(Node* node)
|
void SweepContext::RemoveNode(Node* node)
|
||||||
{
|
{
|
||||||
delete node;
|
delete node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SweepContext::MapTriangleToNodes(Triangle& t)
|
void SweepContext::MapTriangleToNodes(Triangle& t)
|
||||||
{
|
{
|
||||||
for( int i = 0; i < 3; i++ )
|
for (int i = 0; i < 3; i++) {
|
||||||
{
|
if (!t.GetNeighbor(i)) {
|
||||||
if( !t.GetNeighbor( i ) )
|
|
||||||
{
|
|
||||||
Node* n = front_->LocatePoint(t.PointCW(*t.GetPoint(i)));
|
Node* n = front_->LocatePoint(t.PointCW(*t.GetPoint(i)));
|
||||||
|
|
||||||
if (n)
|
if (n)
|
||||||
n->triangle = &t;
|
n->triangle = &t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SweepContext::RemoveFromMap(Triangle* triangle)
|
void SweepContext::RemoveFromMap(Triangle* triangle)
|
||||||
{
|
{
|
||||||
map_.remove(triangle);
|
map_.remove(triangle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SweepContext::MeshClean(Triangle& triangle)
|
void SweepContext::MeshClean(Triangle& triangle)
|
||||||
{
|
{
|
||||||
std::vector<Triangle *> triangles;
|
std::vector<Triangle *> triangles;
|
||||||
|
|
||||||
triangles.push_back(&triangle);
|
triangles.push_back(&triangle);
|
||||||
|
|
||||||
while( !triangles.empty() )
|
while(!triangles.empty()){
|
||||||
{
|
|
||||||
Triangle *t = triangles.back();
|
Triangle *t = triangles.back();
|
||||||
triangles.pop_back();
|
triangles.pop_back();
|
||||||
|
|
||||||
if( t != NULL && !t->IsInterior() )
|
if (t != NULL && !t->IsInterior()) {
|
||||||
{
|
|
||||||
t->IsInterior(true);
|
t->IsInterior(true);
|
||||||
triangles_.push_back(t);
|
triangles_.push_back(t);
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
for( int i = 0; i < 3; i++ )
|
|
||||||
{
|
|
||||||
if (!t->constrained_edge[i])
|
if (!t->constrained_edge[i])
|
||||||
triangles.push_back(t->GetNeighbor(i));
|
triangles.push_back(t->GetNeighbor(i));
|
||||||
}
|
}
|
||||||
|
@ -217,9 +188,9 @@ void SweepContext::MeshClean( Triangle& triangle )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SweepContext::~SweepContext()
|
SweepContext::~SweepContext()
|
||||||
{
|
{
|
||||||
|
|
||||||
// Clean up memory
|
// Clean up memory
|
||||||
|
|
||||||
delete head_;
|
delete head_;
|
||||||
|
@ -231,15 +202,15 @@ SweepContext::~SweepContext()
|
||||||
|
|
||||||
typedef std::list<Triangle*> type_list;
|
typedef std::list<Triangle*> type_list;
|
||||||
|
|
||||||
for( type_list::iterator iter = map_.begin(); iter != map_.end(); ++iter )
|
for(type_list::iterator iter = map_.begin(); iter != map_.end(); ++iter) {
|
||||||
{
|
|
||||||
Triangle* ptr = *iter;
|
Triangle* ptr = *iter;
|
||||||
delete ptr;
|
delete ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
for( unsigned int i = 0; i < edge_list.size(); i++ )
|
for(unsigned int i = 0; i < edge_list.size(); i++) {
|
||||||
{
|
|
||||||
delete edge_list[i];
|
delete edge_list[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
namespace p2t {
|
namespace p2t {
|
||||||
|
|
||||||
// Inital triangle factor, seed triangle will extend 30% of
|
// Inital triangle factor, seed triangle will extend 30% of
|
||||||
// PointSet width to both left and right.
|
// PointSet width to both left and right.
|
||||||
const double kAlpha = 0.3;
|
const double kAlpha = 0.3;
|
||||||
|
@ -47,8 +48,7 @@ struct Node;
|
||||||
struct Edge;
|
struct Edge;
|
||||||
class AdvancingFront;
|
class AdvancingFront;
|
||||||
|
|
||||||
class SweepContext
|
class SweepContext {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// Constructor
|
/// Constructor
|
||||||
|
@ -92,21 +92,18 @@ public:
|
||||||
void MeshClean(Triangle& triangle);
|
void MeshClean(Triangle& triangle);
|
||||||
|
|
||||||
std::vector<Triangle*> GetTriangles();
|
std::vector<Triangle*> GetTriangles();
|
||||||
|
|
||||||
std::list<Triangle*> GetMap();
|
std::list<Triangle*> GetMap();
|
||||||
|
|
||||||
std::vector<Edge*> edge_list;
|
std::vector<Edge*> edge_list;
|
||||||
|
|
||||||
struct Basin
|
struct Basin {
|
||||||
{
|
|
||||||
Node* left_node;
|
Node* left_node;
|
||||||
Node* bottom_node;
|
Node* bottom_node;
|
||||||
Node* right_node;
|
Node* right_node;
|
||||||
double width;
|
double width;
|
||||||
bool left_highest;
|
bool left_highest;
|
||||||
|
|
||||||
Basin() : left_node( NULL ), bottom_node( NULL ), right_node( NULL ), width( 0.0 ),
|
Basin() : left_node(NULL), bottom_node(NULL), right_node(NULL), width(0.0), left_highest(false)
|
||||||
left_highest( false )
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,8 +117,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EdgeEvent
|
struct EdgeEvent {
|
||||||
{
|
|
||||||
Edge* constrained_edge;
|
Edge* constrained_edge;
|
||||||
bool right;
|
bool right;
|
||||||
|
|
||||||
|
@ -152,6 +148,7 @@ private:
|
||||||
|
|
||||||
void InitTriangulation();
|
void InitTriangulation();
|
||||||
void InitEdges(std::vector<Point*> polyline);
|
void InitEdges(std::vector<Point*> polyline);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline AdvancingFront* SweepContext::front()
|
inline AdvancingFront* SweepContext::front()
|
||||||
|
@ -159,35 +156,31 @@ inline AdvancingFront* SweepContext::front()
|
||||||
return front_;
|
return front_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline int SweepContext::point_count()
|
inline int SweepContext::point_count()
|
||||||
{
|
{
|
||||||
return points_.size();
|
return points_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void SweepContext::set_head(Point* p1)
|
inline void SweepContext::set_head(Point* p1)
|
||||||
{
|
{
|
||||||
head_ = p1;
|
head_ = p1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline Point* SweepContext::head()
|
inline Point* SweepContext::head()
|
||||||
{
|
{
|
||||||
return head_;
|
return head_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void SweepContext::set_tail(Point* p1)
|
inline void SweepContext::set_tail(Point* p1)
|
||||||
{
|
{
|
||||||
tail_ = p1;
|
tail_ = p1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline Point* SweepContext::tail()
|
inline Point* SweepContext::tail()
|
||||||
{
|
{
|
||||||
return tail_;
|
return tail_;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -12,3 +12,4 @@ endif()
|
||||||
|
|
||||||
add_subdirectory( geometry )
|
add_subdirectory( geometry )
|
||||||
add_subdirectory( pcb_test_window )
|
add_subdirectory( pcb_test_window )
|
||||||
|
add_subdirectory( polygon_triangulation )
|
|
@ -0,0 +1,92 @@
|
||||||
|
#
|
||||||
|
# This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2017 CERN
|
||||||
|
# @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, you may find one here:
|
||||||
|
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||||
|
# or you may search the http://www.gnu.org website for the version 2 license,
|
||||||
|
# or you may write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
#find_package(Boost COMPONENTS unit_test_framework REQUIRED)
|
||||||
|
#find_package( wxWidgets 3.0.0 COMPONENTS gl aui adv html core net base xml stc REQUIRED )
|
||||||
|
|
||||||
|
add_definitions(-DPCBNEW -DBOOST_TEST_DYN_LINK)
|
||||||
|
|
||||||
|
if( BUILD_GITHUB_PLUGIN )
|
||||||
|
set( GITHUB_PLUGIN_LIBRARIES github_plugin )
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_dependencies( pnsrouter pcbcommon pcad2kicadpcb ${GITHUB_PLUGIN_LIBRARIES} )
|
||||||
|
|
||||||
|
add_executable(test_polygon_triangulation
|
||||||
|
../common/mocks.cpp
|
||||||
|
../../common/base_units.cpp
|
||||||
|
test_polygon_triangulation.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories( BEFORE ${INC_BEFORE} )
|
||||||
|
include_directories(
|
||||||
|
${CMAKE_SOURCE_DIR}
|
||||||
|
${CMAKE_SOURCE_DIR}/include
|
||||||
|
${CMAKE_SOURCE_DIR}/3d-viewer
|
||||||
|
${CMAKE_SOURCE_DIR}/common
|
||||||
|
${CMAKE_SOURCE_DIR}/pcbnew
|
||||||
|
${CMAKE_SOURCE_DIR}/pcbnew/router
|
||||||
|
${CMAKE_SOURCE_DIR}/pcbnew/tools
|
||||||
|
${CMAKE_SOURCE_DIR}/pcbnew/dialogs
|
||||||
|
${CMAKE_SOURCE_DIR}/polygon
|
||||||
|
${CMAKE_SOURCE_DIR}/common/geometry
|
||||||
|
${CMAKE_SOURCE_DIR}/qa/common
|
||||||
|
${Boost_INCLUDE_DIR}
|
||||||
|
${INC_AFTER}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries( test_polygon_triangulation
|
||||||
|
polygon
|
||||||
|
pnsrouter
|
||||||
|
common
|
||||||
|
pcbcommon
|
||||||
|
bitmaps
|
||||||
|
polygon
|
||||||
|
pnsrouter
|
||||||
|
common
|
||||||
|
pcbcommon
|
||||||
|
bitmaps
|
||||||
|
polygon
|
||||||
|
pnsrouter
|
||||||
|
common
|
||||||
|
pcbcommon
|
||||||
|
bitmaps
|
||||||
|
polygon
|
||||||
|
pnsrouter
|
||||||
|
common
|
||||||
|
pcbcommon
|
||||||
|
bitmaps
|
||||||
|
gal
|
||||||
|
pcad2kicadpcb
|
||||||
|
common
|
||||||
|
pcbcommon
|
||||||
|
${GITHUB_PLUGIN_LIBRARIES}
|
||||||
|
common
|
||||||
|
pcbcommon
|
||||||
|
${Boost_FILESYSTEM_LIBRARY}
|
||||||
|
${Boost_SYSTEM_LIBRARY}
|
||||||
|
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
|
||||||
|
${wxWidgets_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,262 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 CERN
|
||||||
|
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <geometry/shape_poly_set.h>
|
||||||
|
#include <geometry/shape_line_chain.h>
|
||||||
|
|
||||||
|
#include <io_mgr.h>
|
||||||
|
#include <kicad_plugin.h>
|
||||||
|
|
||||||
|
#include <class_board.h>
|
||||||
|
#include <class_zone.h>
|
||||||
|
#include <profile.h>
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void unfracture( SHAPE_POLY_SET::POLYGON* aPoly, SHAPE_POLY_SET::POLYGON* aResult )
|
||||||
|
{
|
||||||
|
assert( aPoly->size() == 1 );
|
||||||
|
|
||||||
|
struct EDGE
|
||||||
|
{
|
||||||
|
int m_index = 0;
|
||||||
|
SHAPE_LINE_CHAIN* m_poly = nullptr;
|
||||||
|
bool m_duplicate = false;
|
||||||
|
|
||||||
|
EDGE( SHAPE_LINE_CHAIN *aPolygon, int aIndex ) :
|
||||||
|
m_index(aIndex),
|
||||||
|
m_poly(aPolygon)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool compareSegs( const SEG& s1, const SEG& s2) const
|
||||||
|
{
|
||||||
|
return (s1.A == s2.A && s1.B == s2.B) || (s1.A == s2.B && s1.B == s2.A);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==( const EDGE& aOther ) const
|
||||||
|
{
|
||||||
|
return compareSegs( m_poly->CSegment(m_index), aOther.m_poly->CSegment(aOther.m_index) );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=( const EDGE& aOther ) const
|
||||||
|
{
|
||||||
|
return ! compareSegs( m_poly->CSegment(m_index), aOther.m_poly->CSegment(aOther.m_index) );
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HASH
|
||||||
|
{
|
||||||
|
std::size_t operator()( const EDGE& aEdge ) const
|
||||||
|
{
|
||||||
|
const auto& a = aEdge.m_poly->CSegment(aEdge.m_index);
|
||||||
|
return (std::size_t) ( a.A.x + a.B.x + a.A.y + a.B.y );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EDGE_LIST_ENTRY
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
EDGE_LIST_ENTRY *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_set<EDGE, EDGE::HASH> uniqueEdges;
|
||||||
|
|
||||||
|
auto lc = (*aPoly)[0];
|
||||||
|
lc.Simplify();
|
||||||
|
|
||||||
|
EDGE_LIST_ENTRY edgeList[ lc.SegmentCount() ];
|
||||||
|
|
||||||
|
for(int i = 0; i < lc.SegmentCount(); i++)
|
||||||
|
{
|
||||||
|
edgeList[i].index = i;
|
||||||
|
edgeList[i].next = &edgeList[ (i != lc.SegmentCount() - 1) ? i + 1 : 0 ];
|
||||||
|
//printf("n %p\n", edgeList[i].next);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<EDGE_LIST_ENTRY*> queue;
|
||||||
|
|
||||||
|
for(int i = 0; i < lc.SegmentCount(); i++)
|
||||||
|
{
|
||||||
|
EDGE e ( &lc, i );
|
||||||
|
uniqueEdges.insert( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < lc.SegmentCount(); i++)
|
||||||
|
{
|
||||||
|
EDGE e ( &lc, i );
|
||||||
|
auto it = uniqueEdges.find(e);
|
||||||
|
if (it != uniqueEdges.end() && it->m_index != i )
|
||||||
|
{
|
||||||
|
int e1 = it->m_index;
|
||||||
|
int e2 = i;
|
||||||
|
if( e1 > e2 )
|
||||||
|
std::swap(e1, e2);
|
||||||
|
|
||||||
|
// printf("e1 %d e2 %d\n", e1, e2 ) ;
|
||||||
|
|
||||||
|
int e1_prev = e1 - 1;
|
||||||
|
if (e1_prev < 0)
|
||||||
|
e1_prev = lc.SegmentCount() - 1;
|
||||||
|
|
||||||
|
int e2_prev = e2 - 1;
|
||||||
|
if (e2_prev < 0)
|
||||||
|
e2_prev = lc.SegmentCount() - 1;
|
||||||
|
|
||||||
|
int e1_next = e1 + 1;
|
||||||
|
if (e1_next == lc.SegmentCount() )
|
||||||
|
e1_next = 0;
|
||||||
|
|
||||||
|
int e2_next = e2 + 1;
|
||||||
|
if (e2_next == lc.SegmentCount() )
|
||||||
|
e2_next = 0;
|
||||||
|
|
||||||
|
edgeList[e1_prev].next = &edgeList[ e2_next ];
|
||||||
|
edgeList[e2_prev].next = &edgeList[ e1_next ];
|
||||||
|
edgeList[i].next = nullptr;
|
||||||
|
edgeList[it->m_index].next = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < lc.SegmentCount(); i++)
|
||||||
|
{
|
||||||
|
if ( edgeList[i].next )
|
||||||
|
queue.insert ( &edgeList[i] );
|
||||||
|
//else
|
||||||
|
//printf("Skip %d\n", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
EDGE_LIST_ENTRY* edgeBuf[ lc.SegmentCount() ];
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
int outline = -1;
|
||||||
|
aResult->clear();
|
||||||
|
while (queue.size())
|
||||||
|
{
|
||||||
|
auto e_first = (*queue.begin());
|
||||||
|
auto e = e_first;
|
||||||
|
int cnt=0;
|
||||||
|
do {
|
||||||
|
// printf("e %p cnt %d IDX %d\n", e, cnt, e->index);
|
||||||
|
edgeBuf[cnt++] = e;
|
||||||
|
e = e->next;
|
||||||
|
} while( e != e_first );
|
||||||
|
|
||||||
|
SHAPE_LINE_CHAIN outl;
|
||||||
|
|
||||||
|
for(int i = 0; i < cnt ;i++)
|
||||||
|
{
|
||||||
|
auto p = lc.CPoint(edgeBuf[i]->index);
|
||||||
|
// printf("append %d %d\n", p.x, p.y);
|
||||||
|
outl.Append( p );
|
||||||
|
queue.erase( edgeBuf[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// auto p_last = lc.CPoint( edgeBuf[cnt-1]->index + 1 );
|
||||||
|
//printf("appendl %d %d\n", p_last.x, p_last.y);
|
||||||
|
// outl.Append( p_last );
|
||||||
|
|
||||||
|
outl.SetClosed(true);
|
||||||
|
|
||||||
|
bool cw = outl.Area() > 0.0;
|
||||||
|
|
||||||
|
if(cw)
|
||||||
|
outline = n;
|
||||||
|
|
||||||
|
aResult->push_back(outl);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(outline >= 0);
|
||||||
|
|
||||||
|
if(outline !=0 )
|
||||||
|
std::swap( (*aResult) [0], (*aResult)[outline] );
|
||||||
|
}
|
||||||
|
|
||||||
|
BOARD* loadBoard( const std::string& filename )
|
||||||
|
{
|
||||||
|
PLUGIN::RELEASER pi( new PCB_IO );
|
||||||
|
BOARD* brd = nullptr;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
brd = pi->Load( wxString( filename.c_str() ), NULL, NULL );
|
||||||
|
}
|
||||||
|
catch( const IO_ERROR& ioe )
|
||||||
|
{
|
||||||
|
wxString msg = wxString::Format( _( "Error loading board.\n%s" ),
|
||||||
|
ioe.Problem() );
|
||||||
|
|
||||||
|
printf( "%s\n", (const char*) msg.mb_str() );
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return brd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
auto brd = loadBoard( argc > 1 ? argv[1] : "../../../../tests/dp.kicad_pcb");
|
||||||
|
|
||||||
|
if(!brd)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
|
||||||
|
PROF_COUNTER cnt("allBoard");
|
||||||
|
|
||||||
|
|
||||||
|
#pragma omp parallel for schedule(dynamic)
|
||||||
|
for( int z = 0; z<brd->GetAreaCount(); z++)
|
||||||
|
{
|
||||||
|
auto zone = brd->GetArea(z);
|
||||||
|
SHAPE_POLY_SET poly = zone->GetFilledPolysList();
|
||||||
|
|
||||||
|
poly.CacheTriangulation();
|
||||||
|
|
||||||
|
(void) poly;
|
||||||
|
printf("zone %d/%d\n", (z+1), brd->GetAreaCount() );
|
||||||
|
#if 0
|
||||||
|
PROF_COUNTER unfrac("unfrac");
|
||||||
|
poly.Unfracture( SHAPE_POLY_SET::PM_FAST );
|
||||||
|
unfrac.Show();
|
||||||
|
|
||||||
|
PROF_COUNTER triangulate("triangulate");
|
||||||
|
|
||||||
|
for(int i =0; i< poly.OutlineCount(); i++)
|
||||||
|
{
|
||||||
|
poly.triangulatePoly( &poly.Polygon(i) );
|
||||||
|
}
|
||||||
|
triangulate.Show();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt.Show();
|
||||||
|
|
||||||
|
delete brd;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue