pcbnew: Optimized zone filling algorithm.

This commit is contained in:
Tomasz Włostowski 2017-11-23 17:20:27 +01:00
parent 8b68a1736a
commit 316ddadec1
46 changed files with 3998 additions and 2754 deletions

View File

@ -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 )

View File

@ -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

View File

@ -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 );

File diff suppressed because it is too large Load Diff

250
common/md5_hash.cpp Normal file
View File

@ -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;
}
}

View File

@ -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 );
}

View File

@ -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.

View File

@ -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()
* *

View File

@ -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

50
include/md5_hash.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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
) )

View File

@ -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();
}

View File

@ -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.
* *

View File

@ -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;
}

View File

@ -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 );
}; };

View File

@ -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 );
}

View File

@ -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

View File

@ -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() )

View File

@ -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

View File

@ -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
} }
} }

View File

@ -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() );

View File

@ -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)

View File

@ -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 );
} }

View File

@ -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() );
}

View File

@ -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

View File

@ -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 );
}

View File

@ -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;
} }
*/

View File

@ -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 )
{
}

View File

@ -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 );
}

View File

@ -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})

View File

@ -32,74 +32,63 @@
#include <iostream> #include <iostream>
namespace p2t { namespace p2t {
Triangle::Triangle( Point& a, Point& b, Point& c )
{
points_[0] = &a; points_[1] = &b; points_[2] = &c;
neighbors_[0] = NULL; neighbors_[1] = NULL; neighbors_[2] = NULL;
constrained_edge[0] = constrained_edge[1] = constrained_edge[2] = false;
delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false;
interior_ = false;
}
Triangle::Triangle(Point& a, Point& b, Point& c)
{
points_[0] = &a; points_[1] = &b; points_[2] = &c;
neighbors_[0] = NULL; neighbors_[1] = NULL; neighbors_[2] = NULL;
constrained_edge[0] = constrained_edge[1] = constrained_edge[2] = false;
delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = 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)
{ {
if( (p1 == points_[2] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[2]) ) if ((p1 == points_[2] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[2]))
neighbors_[0] = t; neighbors_[0] = t;
else if( (p1 == points_[0] && p2 == points_[2]) || (p1 == points_[2] && p2 == points_[0]) ) else if ((p1 == points_[0] && p2 == points_[2]) || (p1 == points_[2] && p2 == points_[0]))
neighbors_[1] = t; neighbors_[1] = t;
else if( (p1 == points_[0] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[0]) ) else if ((p1 == points_[0] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[0]))
neighbors_[2] = t; neighbors_[2] = t;
else else
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])) {
} neighbors_[1] = &t;
else if( t.Contains( points_[0], points_[2] ) ) t.MarkNeighbor(points_[0], points_[2], this);
{ } else if (t.Contains(points_[0], points_[1])) {
neighbors_[1] = &t; neighbors_[2] = &t;
t.MarkNeighbor( points_[0], points_[2], this ); t.MarkNeighbor(points_[0], points_[1], this);
} }
else if( t.Contains( points_[0], points_[1] ) )
{
neighbors_[2] = &t;
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,386 +104,263 @@ void Triangle::ClearNeighbor( Triangle* triangle )
} }
} }
void Triangle::ClearNeighbors() void Triangle::ClearNeighbors()
{ {
neighbors_[0] = NULL; neighbors_[0] = NULL;
neighbors_[1] = NULL; neighbors_[1] = NULL;
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 y = cw->y;
double x = cw->x; x = p.x;
double y = cw->y; y = p.y;
return PointCW(*cw);
x = p.x;
y = p.y;
*/
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)
{ {
points_[1] = points_[0]; points_[1] = points_[0];
points_[0] = points_[2]; points_[0] = points_[2];
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]) {
} points_[2] = points_[1];
else if( &opoint == points_[1] ) points_[1] = points_[0];
{ points_[0] = &npoint;
points_[2] = points_[1]; } else if (&opoint == points_[2]) {
points_[1] = points_[0]; points_[0] = points_[2];
points_[0] = &npoint; points_[2] = points_[1];
} points_[1] = &npoint;
else if( &opoint == points_[2] ) } else {
{ assert(0);
points_[0] = points_[2]; }
points_[2] = points_[1];
points_[1] = &npoint;
}
else
{
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]) {
} return 1;
else if( p == points_[1] ) } else if (p == points_[2]) {
{ return 2;
return 1; }
} assert(0);
else if( p == points_[2] )
{
return 2;
}
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;
{ } else if (points_[2] == p2) {
return 2; return 1;
}
else if( points_[2] == p2 )
{
return 1;
}
} }
else if( points_[1] == p1 ) } else if (points_[1] == p1) {
{ if (points_[2] == p2) {
if( points_[2] == p2 ) return 0;
{ } else if (points_[0] == p2) {
return 0; return 2;
}
else if( points_[0] == p2 )
{
return 2;
}
} }
else if( points_[2] == p1 ) } else if (points_[2] == p1) {
{ if (points_[0] == p2) {
if( points_[0] == p2 ) return 1;
{ } else if (points_[1] == p2) {
return 1; return 0;
}
else if( points_[1] == p2 )
{
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])) {
} constrained_edge[1] = true;
else if( (q == points_[0] && p == points_[2]) || (q == points_[2] && p == points_[0]) ) } else if ((q == points_[1] && p == points_[2]) || (q == points_[2] && p == points_[1])) {
{ constrained_edge[0] = true;
constrained_edge[1] = true; }
}
else if( (q == points_[1] && p == points_[2]) || (q == points_[2] && p == points_[1]) )
{
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]) {
} return points_[0];
else if( &point == points_[1] ) } else if (&point == points_[2]) {
{ return points_[1];
return points_[0]; }
} assert(0);
else if( &point == points_[2] )
{
return points_[1];
}
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]) {
} return points_[2];
else if( &point == points_[1] ) } else if (&point == points_[2]) {
{ return points_[0];
return points_[2]; }
} assert(0);
else if( &point == points_[2] )
{
return points_[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]) {
} return neighbors_[2];
else if( &point == points_[1] ) }
{ return neighbors_[0];
return neighbors_[2];
}
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]) {
} return neighbors_[0];
else if( &point == points_[1] ) }
{ return neighbors_[1];
return neighbors_[0];
}
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[1];
}
bool Triangle::GetConstrainedEdgeCW( Point& p )
{
if( &p == points_[0] )
{
return constrained_edge[1];
}
else if( &p == points_[1] )
{
return constrained_edge[2];
}
return constrained_edge[0]; return constrained_edge[0];
}
return constrained_edge[1];
} }
bool Triangle::GetConstrainedEdgeCW(Point& p)
void Triangle::SetConstrainedEdgeCCW( Point& p, bool ce )
{ {
if( &p == points_[0] ) if (&p == points_[0]) {
{ return constrained_edge[1];
constrained_edge[2] = ce; } else if (&p == points_[1]) {
} return constrained_edge[2];
else if( &p == points_[1] ) }
{ return constrained_edge[0];
constrained_edge[0] = ce;
}
else
{
constrained_edge[1] = ce;
}
} }
void Triangle::SetConstrainedEdgeCCW(Point& p, bool ce)
void Triangle::SetConstrainedEdgeCW( Point& p, bool ce )
{ {
if( &p == points_[0] ) if (&p == points_[0]) {
{ constrained_edge[2] = ce;
constrained_edge[1] = ce; } else if (&p == points_[1]) {
} constrained_edge[0] = ce;
else if( &p == points_[1] ) } else {
{ constrained_edge[1] = ce;
constrained_edge[2] = ce; }
}
else
{
constrained_edge[0] = ce;
}
} }
void Triangle::SetConstrainedEdgeCW(Point& p, bool ce)
bool Triangle::GetDelunayEdgeCCW( Point& p )
{ {
if( &p == points_[0] ) if (&p == points_[0]) {
{ constrained_edge[1] = ce;
return delaunay_edge[2]; } else if (&p == points_[1]) {
} constrained_edge[2] = ce;
else if( &p == points_[1] ) } else {
{ constrained_edge[0] = ce;
return delaunay_edge[0]; }
}
return delaunay_edge[1];
} }
bool Triangle::GetDelunayEdgeCCW(Point& p)
bool Triangle::GetDelunayEdgeCW( Point& p )
{ {
if( &p == points_[0] ) if (&p == points_[0]) {
{ return delaunay_edge[2];
return delaunay_edge[1]; } else if (&p == points_[1]) {
}
else if( &p == points_[1] )
{
return delaunay_edge[2];
}
return delaunay_edge[0]; return delaunay_edge[0];
}
return delaunay_edge[1];
} }
bool Triangle::GetDelunayEdgeCW(Point& p)
void Triangle::SetDelunayEdgeCCW( Point& p, bool e )
{ {
if( &p == points_[0] ) if (&p == points_[0]) {
{ return delaunay_edge[1];
delaunay_edge[2] = e; } else if (&p == points_[1]) {
} return delaunay_edge[2];
else if( &p == points_[1] ) }
{ return delaunay_edge[0];
delaunay_edge[0] = e;
}
else
{
delaunay_edge[1] = e;
}
} }
void Triangle::SetDelunayEdgeCCW(Point& p, bool e)
void Triangle::SetDelunayEdgeCW( Point& p, bool e )
{ {
if( &p == points_[0] ) if (&p == points_[0]) {
{ delaunay_edge[2] = e;
delaunay_edge[1] = e; } else if (&p == points_[1]) {
} delaunay_edge[0] = e;
else if( &p == points_[1] ) } else {
{ delaunay_edge[1] = e;
delaunay_edge[2] = e; }
}
else
{
delaunay_edge[0] = e;
}
} }
void Triangle::SetDelunayEdgeCW(Point& p, bool e)
{
if (&p == points_[0]) {
delaunay_edge[1] = e;
} else if (&p == points_[1]) {
delaunay_edge[2] = e;
} else {
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]) {
} return *neighbors_[1];
else if( &opoint == points_[1] ) }
{ return *neighbors_[2];
return *neighbors_[1];
}
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;
} }
} }

View File

@ -39,313 +39,287 @@
#include <cmath> #include <cmath>
namespace p2t { namespace p2t {
struct Edge; struct Edge;
struct Point struct Point {
{
double x, y;
/// Default constructor does nothing (for performance). double x, y;
Point()
{
x = 0.0;
y = 0.0;
}
/// The edges this point constitutes an upper ending point /// Default constructor does nothing (for performance).
std::vector<Edge*> edge_list; Point()
{
x = 0.0;
y = 0.0;
}
/// Construct using coordinates. /// The edges this point constitutes an upper ending point
Point( double ax, double ay ) : x( ax ), y( ay ) {} std::vector<Edge*> edge_list;
/// Set this point to all zeros. /// Construct using coordinates.
void set_zero() Point(double x, double y) : x(x), y(y) {}
{
x = 0.0;
y = 0.0;
}
/// Set this point to some specified coordinates. /// Set this point to all zeros.
void set( double x_, double y_ ) void set_zero()
{ {
x = x_; x = 0.0;
y = y_; y = 0.0;
} }
/// Negate this point. /// Set this point to some specified coordinates.
Point operator -() const void set(double x_, double y_)
{ {
Point v; x = x_;
y = y_;
}
v.set( -x, -y ); /// Negate this point.
return v; Point operator -() const
} {
Point v;
v.set(-x, -y);
return v;
}
/// Add a point to this point. /// Add a point to this point.
void operator +=( const Point& v ) void operator +=(const Point& v)
{ {
x += v.x; x += v.x;
y += v.y; y += v.y;
} }
/// Subtract a point from this point. /// Subtract a point from this point.
void operator -=( const Point& v ) void operator -=(const Point& v)
{ {
x -= v.x; x -= v.x;
y -= v.y; y -= v.y;
} }
/// Multiply this point by a scalar. /// Multiply this point by a scalar.
void operator *=( double a ) void operator *=(double a)
{ {
x *= a; x *= a;
y *= a; y *= a;
} }
/// Get the length of this point (the norm). /// Get the length of this point (the norm).
double Length() const double Length() const
{ {
return sqrt( x * x + y * y ); return sqrt(x * x + y * y);
} }
/// Convert this point into a unit point. Returns the Length. /// Convert this point into a unit point. Returns the Length.
double Normalize() double Normalize()
{ {
double len = Length(); double len = Length();
x /= len;
y /= len;
return len;
}
x /= len;
y /= len;
return len;
}
}; };
// Represents a simple polygon's edge // Represents a simple polygon's edge
struct Edge struct Edge {
{
Point* p, * q;
/// Constructor Point* p, *q;
Edge( Point& p1, Point& p2 ) : p( &p1 ), q( &p2 )
{
if( p1.y > p2.y )
{
q = &p1;
p = &p2;
}
else if( p1.y == p2.y )
{
if( p1.x > p2.x )
{
q = &p1;
p = &p2;
}
else if( p1.x == p2.x )
{
// Repeat points
assert( false );
}
}
q->edge_list.push_back( this ); /// Constructor
Edge(Point& p1, Point& p2) : p(&p1), q(&p2)
{
if (p1.y > p2.y) {
q = &p1;
p = &p2;
} else if (p1.y == p2.y) {
if (p1.x > p2.x) {
q = &p1;
p = &p2;
} else if (p1.x == p2.x) {
// Repeat points
assert(false);
}
} }
q->edge_list.push_back(this);
}
}; };
// 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
Triangle( Point& a, Point& b, Point& c ); Triangle(Point& a, Point& b, Point& c);
/// Flags to determine if an edge is a Constrained edge /// Flags to determine if an edge is a Constrained edge
bool constrained_edge[3]; bool constrained_edge[3];
/// Flags to determine if an edge is a Delauney edge /// Flags to determine if an edge is a Delauney edge
bool delaunay_edge[3]; bool delaunay_edge[3];
Point* GetPoint( const int& index ); Point* GetPoint(const int& index);
Point* PointCW( Point& point ); Point* PointCW(Point& point);
Point* PointCCW( Point& point ); Point* PointCCW(Point& point);
Point* OppositePoint( Triangle& t, Point& p ); Point* OppositePoint(Triangle& t, Point& p);
Triangle* GetNeighbor( const int& index ); Triangle* GetNeighbor(const int& index);
void MarkNeighbor( Point* p1, Point* p2, Triangle* t ); void MarkNeighbor(Point* p1, Point* p2, Triangle* t);
void MarkNeighbor( Triangle& t ); void MarkNeighbor(Triangle& t);
void MarkConstrainedEdge( const int index ); void MarkConstrainedEdge(const int index);
void MarkConstrainedEdge( Edge& edge ); void MarkConstrainedEdge(Edge& edge);
void MarkConstrainedEdge( Point* p, Point* q ); void MarkConstrainedEdge(Point* p, Point* q);
int Index( const Point* p ); int Index(const Point* p);
int EdgeIndex( const Point* p1, const Point* p2 ); int EdgeIndex(const Point* p1, const Point* p2);
Triangle* NeighborCW( Point& point ); Triangle* NeighborCW(Point& point);
Triangle* NeighborCCW( Point& point ); Triangle* NeighborCCW(Point& point);
bool GetConstrainedEdgeCCW( Point& p ); bool GetConstrainedEdgeCCW(Point& p);
bool GetConstrainedEdgeCW( Point& p ); bool GetConstrainedEdgeCW(Point& p);
void SetConstrainedEdgeCCW( Point& p, bool ce ); void SetConstrainedEdgeCCW(Point& p, bool ce);
void SetConstrainedEdgeCW( Point& p, bool ce ); void SetConstrainedEdgeCW(Point& p, bool ce);
bool GetDelunayEdgeCCW( Point& p ); bool GetDelunayEdgeCCW(Point& p);
bool GetDelunayEdgeCW( Point& p ); bool GetDelunayEdgeCW(Point& p);
void SetDelunayEdgeCCW( Point& p, bool e ); void SetDelunayEdgeCCW(Point& p, bool e);
void SetDelunayEdgeCW( Point& p, bool e ); void SetDelunayEdgeCW(Point& p, bool e);
bool Contains( Point* p );
bool Contains( const Edge& e );
bool Contains( Point* p, Point* q );
void Legalize( Point& point );
void Legalize( Point& opoint, Point& npoint );
bool Contains(Point* p);
bool Contains(const Edge& e);
bool Contains(Point* p, Point* q);
void Legalize(Point& point);
void Legalize(Point& opoint, Point& npoint);
/** /**
* Clears all references to all other triangles and points * Clears all references to all other triangles and points
*/ */
void Clear(); void Clear();
void ClearNeighbor( Triangle* triangle ); void ClearNeighbor(Triangle *triangle );
void ClearNeighbors(); void ClearNeighbors();
void ClearDelunayEdges(); void ClearDelunayEdges();
inline bool IsInterior(); inline bool IsInterior();
inline void IsInterior( bool b ); inline void IsInterior(bool b);
Triangle& NeighborAcross( Point& opoint ); Triangle& NeighborAcross(Point& opoint);
void DebugPrint(); void DebugPrint();
private: private:
/// Triangle points /// Triangle points
Point* points_[3]; Point* points_[3];
/// Neighbor list /// Neighbor list
Triangle* neighbors_[3]; Triangle* neighbors_[3];
/// Has this triangle been marked as an interior triangle? /// Has this triangle been marked as an interior triangle?
bool interior_; bool interior_;
}; };
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) {
// Make sure q is point with greater x value
if (a->x < b->x) {
return true;
} }
else if( a->y == b->y ) }
{ return false;
// Make sure q is point with greater x value
if( a->x < b->x )
{
return true;
}
}
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)
{ {
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)
{ {
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

View File

@ -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_div2 = 1.57079632679489661923;
const double EPSILON = 1e-12;
enum Orientation { const double PI_3div4 = 3 * M_PI / 4;
CW, CCW, COLLINEAR const double PI_div2 = 1.57079632679489661923;
}; const double EPSILON = 1e-12;
enum Orientation { CW, CCW, COLLINEAR };
/** /**
* Forumla to calculate signed area<br> * Forumla to calculate signed area<br>
@ -57,77 +56,68 @@ enum Orientation {
* = (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3) * = (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
* </pre> * </pre>
*/ */
Orientation Orient2d( Point& pa, Point& pb, Point& pc ) 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;
{ } else if (val > 0) {
return COLLINEAR; return CCW;
} }
else if( val > 0 ) return CW;
{
return CCW;
}
return CW;
} }
/* /*
* bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) 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 )
{ {
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;
return false; double oabd = adxbdy - bdxady;
}
double oadc = (pa.x - pc.x) * (pd.y - pc.y) - (pd.x - pc.x) * (pa.y - pc.y); if (oabd <= EPSILON) {
return false;
}
if( oadc <= EPSILON ) double cdx = pc.x - pdx;
{ double cdy = pc.y - pdy;
return false;
}
return true; 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)
{
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

View File

@ -31,105 +31,79 @@
#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;
tail_ = &tail; tail_ = &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;
return node;
}
}
}
else
{
while( (node = node->next) != NULL )
{
if( x < node->value )
{
search_node_ = node->prev;
return node->prev;
}
}
}
return NULL;
}
Node* AdvancingFront::FindSearchNode( const double& x )
{
(void) x; // suppress compiler warnings "unused parameter 'x'"
// TODO: implement BST index
return search_node_;
}
Node* AdvancingFront::LocatePoint( const Point* point )
{
const double px = point->x;
Node* node = FindSearchNode( px );
const double nx = node->point->x;
if( px == nx )
{
if( point != node->point )
{
// We might have two nodes with same x value for a short time
if( point == node->prev->point )
{
node = node->prev;
}
else if( point == node->next->point )
{
node = node->next;
}
else
{
assert( 0 );
}
}
}
else if( px < nx )
{
while( (node = node->prev) != NULL )
{
if( point == node->point )
{
break;
}
}
}
else
{
while( (node = node->next) != NULL )
{
if( point == node->point )
break;
}
}
if( node )
search_node_ = node; search_node_ = node;
return node;
return node; }
}
} else {
while ((node = node->next) != NULL) {
if (x < node->value) {
search_node_ = node->prev;
return node->prev;
}
}
}
return NULL;
} }
Node* AdvancingFront::FindSearchNode(const double& x)
{
(void)x; // suppress compiler warnings "unused parameter 'x'"
// TODO: implement BST index
return search_node_;
}
Node* AdvancingFront::LocatePoint(const Point* point)
{
const double px = point->x;
Node* node = FindSearchNode(px);
const double nx = node->point->x;
if (px == nx) {
if (point != node->point) {
// We might have two nodes with same x value for a short time
if (point == node->prev->point) {
node = node->prev;
} else if (point == node->next->point) {
node = node->next;
} else {
assert(0);
}
}
} else if (px < nx) {
while ((node = node->prev) != NULL) {
if (point == node->point) {
break;
}
}
} else {
while ((node = node->next) != NULL) {
if (point == node->point)
break;
}
}
if(node) search_node_ = node;
return node;
}
AdvancingFront::~AdvancingFront() AdvancingFront::~AdvancingFront()
{ {
} }
} }

View File

@ -35,91 +35,84 @@
#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;
Node* next; Node* next;
Node* prev; Node* prev;
double value; double value;
Node( Point& p ) : point( &p ), triangle( NULL ), next( NULL ), prev( NULL ), value( p.x ) Node(Point& p) : point(&p), triangle(NULL), next(NULL), prev(NULL), value(p.x)
{ {
} }
Node(Point& p, Triangle& t) : point(&p), triangle(&t), next(NULL), prev(NULL), value(p.x)
{
}
Node( Point& p, Triangle& t ) : point( &p ), triangle( &t ), next( NULL ), prev( NULL ), value(
p.x )
{
}
}; };
// Advancing front // Advancing front
class AdvancingFront class AdvancingFront {
{
public: public:
AdvancingFront( Node& head, Node& tail ); AdvancingFront(Node& head, Node& tail);
// Destructor // Destructor
~AdvancingFront(); ~AdvancingFront();
Node* head(); Node* head();
void set_head( Node* node ); void set_head(Node* node);
Node* tail(); Node* tail();
void set_tail( Node* node ); void set_tail(Node* node);
Node* search(); Node* search();
void set_search( Node* node ); void set_search(Node* node);
/// Locate insertion point along advancing front /// Locate insertion point along advancing front
Node* LocateNode( const double& x ); Node* LocateNode(const double& x);
Node* LocatePoint( const Point* point ); Node* LocatePoint(const Point* point);
private: private:
Node* head_, * tail_, * search_node_; Node* head_, *tail_, *search_node_;
Node* FindSearchNode( const double& x ); Node* FindSearchNode(const double& x);
}; };
inline Node* AdvancingFront::head() 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

View File

@ -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_;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -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
*/ */

View File

@ -33,193 +33,164 @@
#include "advancing_front.h" #include "advancing_front.h"
namespace p2t { namespace p2t {
SweepContext::SweepContext( std::vector<Point*> polyline ) :
front_( 0 ), SweepContext::SweepContext(std::vector<Point*> polyline) :
head_( 0 ), front_(0),
tail_( 0 ), head_(0),
af_head_( 0 ), tail_(0),
af_middle_( 0 ), af_head_(0),
af_tail_( 0 ) af_middle_(0),
af_tail_(0)
{ {
basin = Basin(); basin = Basin();
edge_event = EdgeEvent(); edge_event = EdgeEvent();
points_ = polyline; points_ = 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)
xmax = p.x;
if (p.x < xmin)
xmin = p.x;
if (p.y > ymax)
ymax = p.y;
if (p.y < ymin)
ymin = p.y;
}
if( p.x > xmax ) double dx = kAlpha * (xmax - xmin);
xmax = p.x; double dy = kAlpha * (ymax - ymin);
head_ = new Point(xmax + dx, ymin - dy);
tail_ = new Point(xmin - dx, ymin - dy);
if( p.x < xmin ) // Sort points along y-axis
xmin = p.x; std::sort(points_.begin(), points_.end(), cmp);
if( p.y > ymax ) }
ymax = p.y;
if( p.y < ymin ) void SweepContext::InitEdges(std::vector<Point*> polyline)
ymin = p.y; {
int num_points = polyline.size();
for (int i = 0; i < num_points; i++) {
int j = i < num_points - 1 ? i + 1 : 0;
edge_list.push_back(new Edge(*polyline[i], *polyline[j]));
}
}
Point* SweepContext::GetPoint(const int& index)
{
return points_[index];
}
void SweepContext::AddToMap(Triangle* triangle)
{
map_.push_back(triangle);
}
Node& SweepContext::LocateNode(Point& point)
{
// TODO implement search tree
return *front_->LocateNode(point.x);
}
void SweepContext::CreateAdvancingFront(std::vector<Node*> nodes)
{
(void) nodes;
// Initial triangle
Triangle* triangle = new Triangle(*points_[0], *tail_, *head_);
map_.push_back(triangle);
af_head_ = new Node(*triangle->GetPoint(1), *triangle);
af_middle_ = new Node(*triangle->GetPoint(0), *triangle);
af_tail_ = new Node(*triangle->GetPoint(2));
front_ = new AdvancingFront(*af_head_, *af_tail_);
// TODO: More intuitive if head is middles next and not previous?
// so swap head and tail
af_head_->next = af_middle_;
af_middle_->next = af_tail_;
af_middle_->prev = af_head_;
af_tail_->prev = af_middle_;
}
void SweepContext::RemoveNode(Node* node)
{
delete node;
}
void SweepContext::MapTriangleToNodes(Triangle& t)
{
for (int i = 0; i < 3; i++) {
if (!t.GetNeighbor(i)) {
Node* n = front_->LocatePoint(t.PointCW(*t.GetPoint(i)));
if (n)
n->triangle = &t;
} }
}
double dx = kAlpha * (xmax - xmin);
double dy = kAlpha * (ymax - ymin);
head_ = new Point( xmax + dx, ymin - dy );
tail_ = new Point( xmin - dx, ymin - dy );
// Sort points along y-axis
std::sort( points_.begin(), points_.end(), cmp );
} }
void SweepContext::RemoveFromMap(Triangle* triangle)
void SweepContext::InitEdges( std::vector<Point*> polyline )
{ {
int num_points = polyline.size(); map_.remove(triangle);
}
for( int i = 0; i < num_points; i++ ) void SweepContext::MeshClean(Triangle& triangle)
{ {
int j = i < num_points - 1 ? i + 1 : 0; std::vector<Triangle *> triangles;
edge_list.push_back( new Edge( *polyline[i], *polyline[j] ) ); triangles.push_back(&triangle);
while(!triangles.empty()){
Triangle *t = triangles.back();
triangles.pop_back();
if (t != NULL && !t->IsInterior()) {
t->IsInterior(true);
triangles_.push_back(t);
for (int i = 0; i < 3; i++) {
if (!t->constrained_edge[i])
triangles.push_back(t->GetNeighbor(i));
}
} }
}
} }
Point* SweepContext::GetPoint( const int& index )
{
return points_[index];
}
void SweepContext::AddToMap( Triangle* triangle )
{
map_.push_back( triangle );
}
Node& SweepContext::LocateNode( Point& point )
{
// TODO implement search tree
return *front_->LocateNode( point.x );
}
void SweepContext::CreateAdvancingFront( std::vector<Node*> nodes )
{
(void) nodes;
// Initial triangle
Triangle* triangle = new Triangle( *points_[0], *tail_, *head_ );
map_.push_back( triangle );
af_head_ = new Node( *triangle->GetPoint( 1 ), *triangle );
af_middle_ = new Node( *triangle->GetPoint( 0 ), *triangle );
af_tail_ = new Node( *triangle->GetPoint( 2 ) );
front_ = new AdvancingFront( *af_head_, *af_tail_ );
// TODO: More intuitive if head is middles next and not previous?
// so swap head and tail
af_head_->next = af_middle_;
af_middle_->next = af_tail_;
af_middle_->prev = af_head_;
af_tail_->prev = af_middle_;
}
void SweepContext::RemoveNode( Node* node )
{
delete node;
}
void SweepContext::MapTriangleToNodes( Triangle& t )
{
for( int i = 0; i < 3; i++ )
{
if( !t.GetNeighbor( i ) )
{
Node* n = front_->LocatePoint( t.PointCW( *t.GetPoint( i ) ) );
if( n )
n->triangle = &t;
}
}
}
void SweepContext::RemoveFromMap( Triangle* triangle )
{
map_.remove( triangle );
}
void SweepContext::MeshClean( Triangle& triangle )
{
std::vector<Triangle*> triangles;
triangles.push_back( &triangle );
while( !triangles.empty() )
{
Triangle* t = triangles.back();
triangles.pop_back();
if( t != NULL && !t->IsInterior() )
{
t->IsInterior( true );
triangles_.push_back( t );
for( int i = 0; i < 3; i++ )
{
if( !t->constrained_edge[i] )
triangles.push_back( t->GetNeighbor( i ) );
}
}
}
}
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];
} }
} }
} }

View File

@ -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,147 +48,139 @@ struct Node;
struct Edge; struct Edge;
class AdvancingFront; class AdvancingFront;
class SweepContext class SweepContext {
{
public: public:
/// Constructor /// Constructor
SweepContext( std::vector<Point*> polyline ); SweepContext(std::vector<Point*> polyline);
/// Destructor /// Destructor
~SweepContext(); ~SweepContext();
void set_head( Point* p1 ); void set_head(Point* p1);
Point* head(); Point* head();
void set_tail( Point* p1 ); void set_tail(Point* p1);
Point* tail(); Point* tail();
int point_count(); int point_count();
Node& LocateNode( Point& point ); Node& LocateNode(Point& point);
void RemoveNode( Node* node ); void RemoveNode(Node* node);
void CreateAdvancingFront( std::vector<Node*> nodes ); void CreateAdvancingFront(std::vector<Node*> nodes);
/// Try to map a node to all sides of this triangle that don't have a neighbor /// Try to map a node to all sides of this triangle that don't have a neighbor
void MapTriangleToNodes( Triangle& t ); void MapTriangleToNodes(Triangle& t);
void AddToMap( Triangle* triangle ); void AddToMap(Triangle* triangle);
Point* GetPoint( const int& index ); Point* GetPoint(const int& index);
Point* GetPoints(); Point* GetPoints();
void RemoveFromMap( Triangle* triangle ); void RemoveFromMap(Triangle* triangle);
void AddHole( std::vector<Point*> polyline ); void AddHole(std::vector<Point*> polyline);
void AddPoint( Point* point ); void AddPoint(Point* point);
AdvancingFront* front(); AdvancingFront* front();
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 {
Node* left_node;
Node* bottom_node;
Node* right_node;
double width;
bool left_highest;
struct Basin Basin() : left_node(NULL), bottom_node(NULL), right_node(NULL), width(0.0), left_highest(false)
{ {
Node* left_node; }
Node* bottom_node;
Node* right_node;
double width;
bool left_highest;
Basin() : left_node( NULL ), bottom_node( NULL ), right_node( NULL ), width( 0.0 ), void Clear()
left_highest( false ) {
{ left_node = NULL;
} bottom_node = NULL;
right_node = NULL;
width = 0.0;
left_highest = false;
}
};
void Clear() struct EdgeEvent {
{ Edge* constrained_edge;
left_node = NULL; bool right;
bottom_node = NULL;
right_node = NULL;
width = 0.0;
left_highest = false;
}
};
struct EdgeEvent EdgeEvent() : constrained_edge(NULL), right(false)
{ {
Edge* constrained_edge; }
bool right; };
EdgeEvent() : constrained_edge( NULL ), right( false ) Basin basin;
{ EdgeEvent edge_event;
}
};
Basin basin;
EdgeEvent edge_event;
private: private:
friend class Sweep; friend class Sweep;
std::vector<Triangle*> triangles_; std::vector<Triangle*> triangles_;
std::list<Triangle*> map_; std::list<Triangle*> map_;
std::vector<Point*> points_; std::vector<Point*> points_;
// Advancing front // Advancing front
AdvancingFront* front_; AdvancingFront* front_;
// head point used with advancing front // head point used with advancing front
Point* head_; Point* head_;
// tail point used with advancing front // tail point used with advancing front
Point* tail_; Point* tail_;
Node* af_head_, * af_middle_, * af_tail_; Node *af_head_, *af_middle_, *af_tail_;
void InitTriangulation();
void InitEdges(std::vector<Point*> polyline);
void InitTriangulation();
void InitEdges( std::vector<Point*> polyline );
}; };
inline AdvancingFront* SweepContext::front() 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

View File

@ -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 )

View File

@ -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}
)

View File

@ -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;
}