Update triangulation to handle poly-intersection
Polygon intersections happen against the original outline, not against the currently remaining polygon. This avoids pathalogical cases Adds new simplification system to avoid duplicated points Adds new edge-splitting algorithm to provide additional fall-back Verifies that polygon cuts do not swap holes for outlines (negative area) Fixes https://gitlab.com/kicad/code/kicad/-/issues/17559
This commit is contained in:
parent
79174f8223
commit
c3f6a84d66
|
@ -81,6 +81,8 @@ public:
|
|||
/// therefore cannot be polygons
|
||||
VERTEX* firstVertex = createList( aPoly );
|
||||
|
||||
wxLogTrace( TRIANGULATE_TRACE, "Created list with %f area", firstVertex->area() );
|
||||
|
||||
if( !firstVertex || firstVertex->prev == firstVertex->next )
|
||||
return false;
|
||||
|
||||
|
@ -96,7 +98,10 @@ public:
|
|||
auto retval = earcutList( firstVertex );
|
||||
|
||||
if( !retval )
|
||||
{
|
||||
wxLogTrace( TRIANGULATE_TRACE, "Tesselation failed, logging remaining vertices" );
|
||||
logRemaining();
|
||||
}
|
||||
|
||||
m_vertices.clear();
|
||||
return retval;
|
||||
|
@ -266,25 +271,29 @@ private:
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the signed area of the polygon connected to the current vertex
|
||||
*/
|
||||
double area() const
|
||||
* Returns the signed area of the polygon connected to the current vertex,
|
||||
* optionally ending at a specified vertex.
|
||||
*/
|
||||
double area( const VERTEX* aEnd = nullptr ) const
|
||||
{
|
||||
const VERTEX* p = this;
|
||||
double a = 0.0;
|
||||
|
||||
do
|
||||
{
|
||||
a += ( p->x * p->next->y - p->next->x * p->y );
|
||||
a += ( p->x + p->next->x ) * ( p->next->y - p->y );
|
||||
p = p->next;
|
||||
} while( p != this );
|
||||
} while( p != this && p != aEnd );
|
||||
|
||||
if( p != this )
|
||||
a += ( p->x + aEnd->x ) * ( aEnd->y - p->y );
|
||||
|
||||
return a / 2;
|
||||
}
|
||||
|
||||
const size_t i;
|
||||
const double x;
|
||||
const double y;
|
||||
double x;
|
||||
double y;
|
||||
POLYGON_TRIANGULATION* parent;
|
||||
|
||||
// previous and next vertices nodes in a polygon ring
|
||||
|
@ -335,34 +344,42 @@ private:
|
|||
if( !p.next || p.next == &p || seen.find( &p ) != seen.end() )
|
||||
continue;
|
||||
|
||||
seen.insert( &p );
|
||||
|
||||
// Don't both logging tiny areas
|
||||
if( std::abs( p.area() ) < 10 )
|
||||
continue;
|
||||
|
||||
int count = 1;
|
||||
wxString msg = wxString::Format( "Remaining: %d,%d,", static_cast<int>( p.x ),
|
||||
static_cast<int>( p.y ) );
|
||||
VERTEX* q = p.next;
|
||||
|
||||
do
|
||||
{
|
||||
msg += wxString::Format( "%d,%d,", static_cast<int>( q->x ),
|
||||
static_cast<int>( q->y ) );
|
||||
seen.insert( q );
|
||||
q = q->next;
|
||||
count++;
|
||||
} while( q != &p );
|
||||
|
||||
// Don't log anything that only has 2 or fewer points
|
||||
if( count < 3 )
|
||||
continue;
|
||||
|
||||
msg.RemoveLast();
|
||||
wxLogTrace( TRIANGULATE_TRACE, msg );
|
||||
logVertices( &p, &seen );
|
||||
}
|
||||
}
|
||||
|
||||
void logVertices( VERTEX* aStart, std::set<VERTEX*>* aSeen )
|
||||
{
|
||||
if( aSeen && aSeen->count( aStart ) )
|
||||
return;
|
||||
|
||||
if( aSeen )
|
||||
aSeen->insert( aStart );
|
||||
|
||||
int count = 1;
|
||||
VERTEX* p = aStart->next;
|
||||
wxString msg = wxString::Format( "Vertices: %d,%d,", static_cast<int>( aStart->x ),
|
||||
static_cast<int>( aStart->y ) );
|
||||
|
||||
do
|
||||
{
|
||||
msg += wxString::Format( "%d,%d,", static_cast<int>( p->x ), static_cast<int>( p->y ) );
|
||||
|
||||
if( aSeen )
|
||||
aSeen->insert( p );
|
||||
|
||||
p = p->next;
|
||||
count++;
|
||||
} while( p != aStart );
|
||||
|
||||
if( count < 3 ) // Don't log anything that only has 2 or fewer points
|
||||
return;
|
||||
|
||||
msg.RemoveLast();
|
||||
wxLogTrace( TRIANGULATE_TRACE, msg );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Iterate through the list to remove NULL triangles if they exist.
|
||||
*
|
||||
|
@ -374,10 +391,18 @@ private:
|
|||
{
|
||||
VERTEX* retval = nullptr;
|
||||
VERTEX* p = aStart->next;
|
||||
size_t count = 0;
|
||||
|
||||
while( p != aStart )
|
||||
{
|
||||
if( *p == *( p->next ) || area( p->prev, p, p->next ) == 0.0 )
|
||||
// We make a dummy triangle that is actually part of the existing line segment
|
||||
// and measure its area. This will not be exactly zero due to floating point
|
||||
// errors. We then look for areas that are less than 4 times the area of the
|
||||
// dummy triangle. For small triangles, this is a small number
|
||||
VERTEX tmp( 0, 0.5 * ( p->prev->x + p->next->x ), 0.5 * ( p->prev->y + p->next->y ), this );
|
||||
double null_area = 4.0 * std::abs( area( p->prev, &tmp, p->next ) );
|
||||
|
||||
if( *p == *( p->next ) || std::abs( area( p->prev, p, p->next ) ) <= null_area )
|
||||
{
|
||||
// This is a spike, remove it, leaving only one point
|
||||
if( *( p->next ) == *( p->prev ) )
|
||||
|
@ -386,6 +411,7 @@ private:
|
|||
p = p->prev;
|
||||
p->next->remove();
|
||||
retval = aStart;
|
||||
++count;
|
||||
|
||||
if( p == p->next )
|
||||
break;
|
||||
|
@ -398,61 +424,29 @@ private:
|
|||
|
||||
// We needed an end point above that wouldn't be removed, so
|
||||
// here we do the final check for this as a Steiner point
|
||||
if( area( aStart->prev, aStart, aStart->next ) == 0.0 )
|
||||
VERTEX tmp( 0, 0.5 * ( aStart->prev->x + aStart->next->x ),
|
||||
0.5 * ( aStart->prev->y + aStart->next->y ), this );
|
||||
double null_area = 4.0 * std::abs( area( aStart->prev, &tmp, aStart->next ) );
|
||||
|
||||
if( std::abs( area( aStart->prev, aStart, aStart->next ) ) <= null_area )
|
||||
{
|
||||
retval = p->next;
|
||||
p->remove();
|
||||
++count;
|
||||
}
|
||||
|
||||
wxLogTrace( TRIANGULATE_TRACE, "Removed %zu NULL triangles", count );
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a Clipper path and converts it into a circular, doubly-linked list for triangulation.
|
||||
*/
|
||||
VERTEX* createList( const ClipperLib::Path& aPath )
|
||||
{
|
||||
VERTEX* tail = nullptr;
|
||||
double sum = 0.0;
|
||||
auto len = aPath.size();
|
||||
|
||||
// Check for winding order
|
||||
for( size_t i = 0; i < len; i++ )
|
||||
{
|
||||
auto p1 = aPath.at( i );
|
||||
auto p2 = aPath.at( ( i + 1 ) < len ? i + 1 : 0 );
|
||||
|
||||
sum += ( ( p2.X - p1.X ) * ( p2.Y + p1.Y ) );
|
||||
}
|
||||
|
||||
if( sum <= 0.0 )
|
||||
{
|
||||
for( auto point : aPath )
|
||||
tail = insertVertex( VECTOR2I( point.X, point.Y ), tail );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( size_t i = 0; i < len; i++ )
|
||||
{
|
||||
auto p = aPath.at( len - i - 1 );
|
||||
tail = insertVertex( VECTOR2I( p.X, p.Y ), tail );
|
||||
}
|
||||
}
|
||||
|
||||
if( tail && ( *tail == *tail->next ) )
|
||||
{
|
||||
tail->next->remove();
|
||||
}
|
||||
|
||||
return tail;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a #SHAPE_LINE_CHAIN and links each point into a circular, doubly-linked list.
|
||||
*/
|
||||
VERTEX* createList( const SHAPE_LINE_CHAIN& points )
|
||||
{
|
||||
wxLogTrace( TRIANGULATE_TRACE, "Creating list from %d points", points.PointCount() );
|
||||
|
||||
VERTEX* tail = nullptr;
|
||||
double sum = 0.0;
|
||||
|
||||
|
@ -465,17 +459,34 @@ private:
|
|||
sum += ( ( p2.x - p1.x ) * ( p2.y + p1.y ) );
|
||||
}
|
||||
|
||||
VECTOR2I last_pt{ std::numeric_limits<int>::max(), std::numeric_limits<int>::max() };
|
||||
VECTOR2I::extended_type sq_dist = ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel;
|
||||
sq_dist *= sq_dist;
|
||||
|
||||
auto addVertex = [&]( int i )
|
||||
{
|
||||
const VECTOR2I& pt = points.CPoint( i );
|
||||
VECTOR2I diff = pt - last_pt;
|
||||
if( diff.SquaredEuclideanNorm() > sq_dist )
|
||||
{
|
||||
tail = insertVertex( pt, tail );
|
||||
last_pt = pt;
|
||||
}
|
||||
};
|
||||
|
||||
if( sum > 0.0 )
|
||||
for( int i = points.PointCount() - 1; i >= 0; i--)
|
||||
tail = insertVertex( points.CPoint( i ), tail );
|
||||
{
|
||||
for( int i = points.PointCount() - 1; i >= 0; i-- )
|
||||
addVertex( i );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int i = 0; i < points.PointCount(); i++ )
|
||||
tail = insertVertex( points.CPoint( i ), tail );
|
||||
addVertex( i );
|
||||
}
|
||||
|
||||
if( tail && ( *tail == *tail->next ) )
|
||||
{
|
||||
tail->next->remove();
|
||||
}
|
||||
|
||||
return tail;
|
||||
}
|
||||
|
@ -495,12 +506,15 @@ private:
|
|||
*/
|
||||
bool earcutList( VERTEX* aPoint, int pass = 0 )
|
||||
{
|
||||
wxLogTrace( TRIANGULATE_TRACE, "earcutList starting at %p for pass %d", aPoint, pass );
|
||||
|
||||
if( !aPoint )
|
||||
return true;
|
||||
|
||||
VERTEX* stop = aPoint;
|
||||
VERTEX* prev;
|
||||
VERTEX* next;
|
||||
int internal_pass = 1;
|
||||
|
||||
while( aPoint->prev != aPoint->next )
|
||||
{
|
||||
|
@ -511,7 +525,14 @@ private:
|
|||
{
|
||||
// Tiny ears cannot be seen on the screen
|
||||
if( !isTooSmall( aPoint ) )
|
||||
{
|
||||
m_result.AddTriangle( prev->i, aPoint->i, next->i );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogTrace( TRIANGULATE_TRACE, "Ignoring tiny ear with area %f",
|
||||
area( prev, aPoint, next ) );
|
||||
}
|
||||
|
||||
aPoint->remove();
|
||||
|
||||
|
@ -528,6 +549,9 @@ private:
|
|||
locallyInside( prev, nextNext ) &&
|
||||
locallyInside( nextNext, prev ) )
|
||||
{
|
||||
wxLogTrace( TRIANGULATE_TRACE,
|
||||
"Local intersection detected. Merging minor triangle with area %f",
|
||||
area( prev, aPoint, nextNext ) );
|
||||
m_result.AddTriangle( prev->i, aPoint->i, nextNext->i );
|
||||
|
||||
// remove two nodes involved
|
||||
|
@ -548,16 +572,35 @@ private:
|
|||
*/
|
||||
if( aPoint == stop )
|
||||
{
|
||||
// First, try to remove the remaining steiner points
|
||||
// If aPoint is a steiner, we need to re-assign both the start and stop points
|
||||
if( auto newPoint = removeNullTriangles( aPoint ) )
|
||||
VERTEX* newPoint;
|
||||
|
||||
// Removing null triangles will remove steiner points as well as colinear points
|
||||
// that are three in a row. Because our next step is to subdivide the polygon,
|
||||
// we need to allow it to add the subdivided points first. This is why we only
|
||||
// run the RemoveNullTriangles function after the first pass.
|
||||
if( ( internal_pass == 2 ) && ( newPoint = removeNullTriangles( aPoint ) ) )
|
||||
{
|
||||
aPoint = newPoint;
|
||||
stop = newPoint;
|
||||
continue;
|
||||
}
|
||||
|
||||
++internal_pass;
|
||||
|
||||
// This will subdivide the polygon 2 times. The first pass will add enough points
|
||||
// such that each edge is less than the average edge length. If this doesn't work
|
||||
// The next pass will remove the null triangles (above) and subdivide the polygon
|
||||
// again, this time adding one point to each long edge (and thereby changing the locations)
|
||||
if( internal_pass < 4 )
|
||||
{
|
||||
wxLogTrace( TRIANGULATE_TRACE, "Subdividing polygon" );
|
||||
subdividePolygon( aPoint, internal_pass );
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we don't have any NULL triangles left, cut the polygon in two and try again
|
||||
wxLogTrace( TRIANGULATE_TRACE, "Splitting polygon" );
|
||||
|
||||
if( !splitPolygon( aPoint ) )
|
||||
return false;
|
||||
|
||||
|
@ -661,6 +704,59 @@ private:
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a new vertex halfway between each existing pair of vertices.
|
||||
*/
|
||||
void subdividePolygon( VERTEX* aStart, int pass = 0 )
|
||||
{
|
||||
VERTEX* p = aStart;
|
||||
|
||||
struct VertexComparator {
|
||||
bool operator()(const std::pair<VERTEX*,double>& a, const std::pair<VERTEX*,double>& b) const {
|
||||
return a.second > b.second;
|
||||
}
|
||||
};
|
||||
|
||||
std::set<std::pair<VERTEX*,double>, VertexComparator> longest;
|
||||
double avg = 0.0;
|
||||
|
||||
do
|
||||
{
|
||||
double len = ( p->x - p->next->x ) * ( p->x - p->next->x ) +
|
||||
( p->y - p->next->y ) * ( p->y - p->next->y );
|
||||
longest.emplace( p, len );
|
||||
|
||||
avg += len;
|
||||
p = p->next;
|
||||
} while (p != aStart);
|
||||
|
||||
avg /= longest.size();
|
||||
wxLogTrace( TRIANGULATE_TRACE, "Average length: %f", avg );
|
||||
|
||||
for( auto it = longest.begin(); it != longest.end() && it->second > avg; ++it )
|
||||
{
|
||||
wxLogTrace( TRIANGULATE_TRACE, "Subdividing edge with length %f", it->second );
|
||||
VERTEX* a = it->first;
|
||||
VERTEX* b = a->next;
|
||||
VERTEX* last = a;
|
||||
|
||||
// We adjust the number of divisions based on the pass in order to progressively
|
||||
// subdivide the polygon when triangulation fails
|
||||
int divisions = avg / it->second + 2 + pass;
|
||||
double step = 1.0 / divisions;
|
||||
|
||||
for( int i = 1; i < divisions; i++ )
|
||||
{
|
||||
double x = a->x * ( 1.0 - step * i ) + b->x * ( step * i );
|
||||
double y = a->y * ( 1.0 - step * i ) + b->y * ( step * i );
|
||||
last = insertVertex( VECTOR2I( x, y ), last );
|
||||
}
|
||||
}
|
||||
|
||||
// update z-order of the vertices
|
||||
aStart->updateList();
|
||||
}
|
||||
|
||||
/**
|
||||
* If we cannot find an ear to slice in the current polygon list, we
|
||||
* use this to split the polygon into two separate lists and slice them each
|
||||
|
@ -671,39 +767,61 @@ private:
|
|||
{
|
||||
VERTEX* origPoly = start;
|
||||
|
||||
// If we have fewer than 4 points, we cannot split the polygon
|
||||
if( !start || !start->next || start->next == start->prev
|
||||
|| start->next->next == start->prev )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Our first attempts to split the polygon will be at overlapping points.
|
||||
// These are natural split points and we only need to switch the loop directions
|
||||
// to generate two new loops. Since they are overlapping, we are do not
|
||||
// need to create a new segment to disconnect the two loops.
|
||||
do
|
||||
{
|
||||
VERTEX* nextZ = origPoly->nextZ;
|
||||
std::vector<VERTEX*> overlapPoints;
|
||||
VERTEX* z_pt = origPoly;
|
||||
|
||||
if( nextZ && nextZ != origPoly->next && nextZ != origPoly->prev && *nextZ == *origPoly )
|
||||
while ( z_pt->prevZ && *z_pt->prevZ == *origPoly )
|
||||
z_pt = z_pt->prevZ;
|
||||
|
||||
overlapPoints.push_back( z_pt );
|
||||
|
||||
while( z_pt->nextZ && *z_pt->nextZ == *origPoly )
|
||||
{
|
||||
std::swap( origPoly->next, nextZ->next );
|
||||
origPoly->next->prev = origPoly;
|
||||
nextZ->next->prev = nextZ;
|
||||
|
||||
origPoly->updateList();
|
||||
nextZ->updateList();
|
||||
return earcutList( origPoly ) && earcutList( nextZ );
|
||||
z_pt = z_pt->nextZ;
|
||||
overlapPoints.push_back( z_pt );
|
||||
}
|
||||
|
||||
VERTEX* prevZ = origPoly->prevZ;
|
||||
|
||||
if( prevZ && prevZ != origPoly->next && prevZ != origPoly->prev && *prevZ == *origPoly )
|
||||
if( overlapPoints.size() != 2 || overlapPoints[0]->next == overlapPoints[1]
|
||||
|| overlapPoints[0]->prev == overlapPoints[1] )
|
||||
{
|
||||
std::swap( origPoly->next, prevZ->next );
|
||||
origPoly->next->prev = origPoly;
|
||||
prevZ->next->prev = prevZ;
|
||||
|
||||
origPoly->updateList();
|
||||
prevZ->updateList();
|
||||
return earcutList( origPoly ) && earcutList( prevZ );
|
||||
origPoly = origPoly->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
origPoly = origPoly->next;
|
||||
if( overlapPoints[0]->area( overlapPoints[1] ) < 0 || overlapPoints[1]->area( overlapPoints[0] ) < 0 )
|
||||
{
|
||||
wxLogTrace( TRIANGULATE_TRACE, "Split generated a hole, skipping" );
|
||||
origPoly = origPoly->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
wxLogTrace( TRIANGULATE_TRACE, "Splitting at overlap point %f, %f", overlapPoints[0]->x, overlapPoints[0]->y );
|
||||
std::swap( overlapPoints[0]->next, overlapPoints[1]->next );
|
||||
overlapPoints[0]->next->prev = overlapPoints[0];
|
||||
overlapPoints[1]->next->prev = overlapPoints[1];
|
||||
|
||||
overlapPoints[0]->updateList();
|
||||
overlapPoints[1]->updateList();
|
||||
logVertices( overlapPoints[0], nullptr );
|
||||
logVertices( overlapPoints[1], nullptr );
|
||||
bool retval = earcutList( overlapPoints[0] ) && earcutList( overlapPoints[1] );
|
||||
|
||||
wxLogTrace( TRIANGULATE_TRACE, "%s at first overlap split", retval ? "Success" : "Failed" );
|
||||
return retval;
|
||||
|
||||
|
||||
} while ( origPoly != start );
|
||||
|
||||
|
@ -718,14 +836,17 @@ private:
|
|||
while( marker != origPoly->prev )
|
||||
{
|
||||
// Find a diagonal line that is wholly enclosed by the polygon interior
|
||||
if( origPoly->i != marker->i && goodSplit( origPoly, marker ) )
|
||||
if( origPoly->next && origPoly->i != marker->i && goodSplit( origPoly, marker ) )
|
||||
{
|
||||
VERTEX* newPoly = origPoly->split( marker );
|
||||
|
||||
origPoly->updateList();
|
||||
newPoly->updateList();
|
||||
|
||||
return earcutList( origPoly ) && earcutList( newPoly );
|
||||
bool retval = earcutList( origPoly ) && earcutList( newPoly );
|
||||
|
||||
wxLogTrace( TRIANGULATE_TRACE, "%s at split", retval ? "Success" : "Failed" );
|
||||
return retval;
|
||||
}
|
||||
|
||||
marker = marker->next;
|
||||
|
@ -734,6 +855,7 @@ private:
|
|||
origPoly = origPoly->next;
|
||||
} while( origPoly != start );
|
||||
|
||||
wxLogTrace( TRIANGULATE_TRACE, "Could not find a valid split point" );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -754,9 +876,9 @@ private:
|
|||
bool local_split = locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b );
|
||||
bool same_dir = area( a->prev, a, b->prev ) != 0.0 || area( a, b->prev, b ) != 0.0;
|
||||
bool has_len = ( *a == *b ) && area( a->prev, a, a->next ) > 0 && area( b->prev, b, b->next ) > 0;
|
||||
bool pos_area = a->area( b ) > 0 && b->area( a ) > 0;
|
||||
|
||||
|
||||
return no_intersect && local_split && ( same_dir || has_len ) && !a_on_edge && !b_on_edge;
|
||||
return no_intersect && local_split && ( same_dir || has_len ) && !a_on_edge && !b_on_edge && pos_area;
|
||||
|
||||
}
|
||||
|
||||
|
@ -824,20 +946,25 @@ private:
|
|||
*/
|
||||
bool intersectsPolygon( const VERTEX* a, const VERTEX* b ) const
|
||||
{
|
||||
const VERTEX* p = a->next;
|
||||
|
||||
do
|
||||
for( auto it = m_vertices.begin(); it != m_vertices.end(); )
|
||||
{
|
||||
if( p->i != a->i &&
|
||||
p->next->i != a->i &&
|
||||
p->i != b->i &&
|
||||
p->next->i != b->i && intersects( p, p->next, a, b ) )
|
||||
const VERTEX* p = &*it;
|
||||
const VERTEX* q = &*( ++it );
|
||||
|
||||
if( p->i == a->i || p->i == b->i || q->i == a->i || q->i == b->i )
|
||||
continue;
|
||||
|
||||
if( intersects( p, q, a, b ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
p = p->next;
|
||||
} while( p != a );
|
||||
if( m_vertices.front().i == a->i || m_vertices.front().i == b->i
|
||||
|| m_vertices.back().i == a->i || m_vertices.back().i == b->i )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
return intersects( a, b, &m_vertices.back(), &m_vertices.front() );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -122,6 +122,18 @@ public:
|
|||
virtual size_t GetPointCount() const override { return 3; }
|
||||
virtual size_t GetSegmentCount() const override { return 3; }
|
||||
|
||||
double Area() const
|
||||
{
|
||||
VECTOR2I& aa = parent->m_vertices[a];
|
||||
VECTOR2I& bb = parent->m_vertices[b];
|
||||
VECTOR2I& cc = parent->m_vertices[c];
|
||||
|
||||
VECTOR2D ba = bb - aa;
|
||||
VECTOR2D cb = cc - bb;
|
||||
|
||||
return std::abs( cb.Cross( ba ) * 0.5 );
|
||||
}
|
||||
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
|
|
|
@ -3301,6 +3301,11 @@ void SHAPE_POLY_SET::cacheTriangulation( bool aPartition, bool aSimplify,
|
|||
triangulationValid = true;
|
||||
}
|
||||
|
||||
if( triangulationValid && wxLog::IsLevelEnabled(wxLOG_Trace, wxLOG_COMPONENT) )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return triangulationValid;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,764 @@
|
|||
(kicad_pcb
|
||||
(version 20240225)
|
||||
(generator "pcbnew")
|
||||
(generator_version "8.99")
|
||||
(general
|
||||
(thickness 2.93)
|
||||
(legacy_teardrops no)
|
||||
)
|
||||
(paper "A3")
|
||||
(layers
|
||||
(0 "F.Cu" signal)
|
||||
(31 "B.Cu" signal)
|
||||
(34 "B.Paste" user)
|
||||
(35 "F.Paste" user)
|
||||
(36 "B.SilkS" user "B.Silkscreen")
|
||||
(37 "F.SilkS" user "F.Silkscreen")
|
||||
(38 "B.Mask" user)
|
||||
(39 "F.Mask" user)
|
||||
(40 "Dwgs.User" user "User.Drawings")
|
||||
(41 "Cmts.User" user "User.Comments")
|
||||
(42 "Eco1.User" user "User.Eco1")
|
||||
(44 "Edge.Cuts" user)
|
||||
(45 "Margin" user)
|
||||
(46 "B.CrtYd" user "B.Courtyard")
|
||||
(47 "F.CrtYd" user "F.Courtyard")
|
||||
(48 "B.Fab" user)
|
||||
(49 "F.Fab" user)
|
||||
)
|
||||
(setup
|
||||
(stackup
|
||||
(layer "F.SilkS"
|
||||
(type "Top Silk Screen")
|
||||
(color "White")
|
||||
)
|
||||
(layer "F.Paste"
|
||||
(type "Top Solder Paste")
|
||||
)
|
||||
(layer "F.Mask"
|
||||
(type "Top Solder Mask")
|
||||
(color "Green")
|
||||
(thickness 0.01)
|
||||
)
|
||||
(layer "F.Cu"
|
||||
(type "copper")
|
||||
(thickness 0.7)
|
||||
)
|
||||
(layer "dielectric 1"
|
||||
(type "core")
|
||||
(thickness 1.51)
|
||||
(material "FR4")
|
||||
(epsilon_r 4.5)
|
||||
(loss_tangent 0.02)
|
||||
)
|
||||
(layer "B.Cu"
|
||||
(type "copper")
|
||||
(thickness 0.7)
|
||||
)
|
||||
(layer "B.Mask"
|
||||
(type "Bottom Solder Mask")
|
||||
(color "Green")
|
||||
(thickness 0.01)
|
||||
)
|
||||
(layer "B.Paste"
|
||||
(type "Bottom Solder Paste")
|
||||
)
|
||||
(layer "B.SilkS"
|
||||
(type "Bottom Silk Screen")
|
||||
(color "White")
|
||||
)
|
||||
(copper_finish "None")
|
||||
(dielectric_constraints no)
|
||||
)
|
||||
(pad_to_mask_clearance 0)
|
||||
(allow_soldermask_bridges_in_footprints no)
|
||||
(pcbplotparams
|
||||
(layerselection 0x00010f0_ffffffff)
|
||||
(plot_on_all_layers_selection 0x0000000_00000000)
|
||||
(disableapertmacros no)
|
||||
(usegerberextensions no)
|
||||
(usegerberattributes yes)
|
||||
(usegerberadvancedattributes yes)
|
||||
(creategerberjobfile yes)
|
||||
(dashed_line_dash_ratio 12.000000)
|
||||
(dashed_line_gap_ratio 3.000000)
|
||||
(svgprecision 6)
|
||||
(plotframeref no)
|
||||
(viasonmask no)
|
||||
(mode 1)
|
||||
(useauxorigin no)
|
||||
(hpglpennumber 1)
|
||||
(hpglpenspeed 20)
|
||||
(hpglpendiameter 15.000000)
|
||||
(pdf_front_fp_property_popups yes)
|
||||
(pdf_back_fp_property_popups yes)
|
||||
(pdf_metadata yes)
|
||||
(dxfpolygonmode yes)
|
||||
(dxfimperialunits yes)
|
||||
(dxfusepcbnewfont yes)
|
||||
(psnegative no)
|
||||
(psa4output no)
|
||||
(plotreference yes)
|
||||
(plotvalue yes)
|
||||
(plotfptext yes)
|
||||
(plotinvisibletext no)
|
||||
(sketchpadsonfab no)
|
||||
(subtractmaskfromsilk no)
|
||||
(outputformat 1)
|
||||
(mirror no)
|
||||
(drillshape 0)
|
||||
(scaleselection 1)
|
||||
(outputdirectory "production/")
|
||||
)
|
||||
)
|
||||
(net 0 "")
|
||||
(net 1 "GND")
|
||||
(footprint "0IBF_IC_spezial:SO-8_3.9x4.9mm_P1.27mm_IBF"
|
||||
(layer "B.Cu")
|
||||
(uuid "1ad96dda-c5a4-4d6f-ba7d-da49edca76c6")
|
||||
(at 69 -14 -90)
|
||||
(descr "SO8, IBFEEW standard")
|
||||
(tags "SOIC SO8")
|
||||
(property "Reference" "IC5"
|
||||
(at 0 3.4 -90)
|
||||
(layer "B.SilkS")
|
||||
(uuid "91cf874a-2683-4eac-8c02-ec27e7cef146")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
(justify mirror)
|
||||
)
|
||||
)
|
||||
(property "Value" "AD8418AWBRZ"
|
||||
(at 0 -3.4 -90)
|
||||
(layer "B.Fab")
|
||||
(uuid "ae3c89cf-d675-4ab4-8a7d-35f8cb2bc764")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
(justify mirror)
|
||||
)
|
||||
)
|
||||
(property "Footprint" "0IBF_IC_spezial:SO-8_3.9x4.9mm_P1.27mm_IBF"
|
||||
(at 0 0 90)
|
||||
(unlocked yes)
|
||||
(layer "B.Fab")
|
||||
(hide yes)
|
||||
(uuid "c154c976-206c-4254-8621-109b26ffe6e1")
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify mirror)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" ""
|
||||
(at 0 0 90)
|
||||
(unlocked yes)
|
||||
(layer "B.Fab")
|
||||
(hide yes)
|
||||
(uuid "d03956b1-495c-450d-9f53-dc64e416b740")
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify mirror)
|
||||
)
|
||||
)
|
||||
(property "Description" "OPV, current sense over the TOP OPV, differential amplifier, high common mode range (70V) / Gain=20 / bis 250kHz SO8"
|
||||
(at 0 0 90)
|
||||
(unlocked yes)
|
||||
(layer "B.Fab")
|
||||
(hide yes)
|
||||
(uuid "25a61d2e-91cd-458e-996b-07adc5ee1c5f")
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
)
|
||||
(justify mirror)
|
||||
)
|
||||
)
|
||||
(property "Bemerkung" ""
|
||||
(at 0 0 90)
|
||||
(unlocked yes)
|
||||
(layer "B.Fab")
|
||||
(hide yes)
|
||||
(uuid "92dfeab9-ec22-42da-8058-31270f643fbc")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
(justify mirror)
|
||||
)
|
||||
)
|
||||
(property "MF" "AD"
|
||||
(at 0 0 90)
|
||||
(unlocked yes)
|
||||
(layer "B.Fab")
|
||||
(hide yes)
|
||||
(uuid "59d72731-d1bd-4cf1-8f00-795ecb55befd")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
(justify mirror)
|
||||
)
|
||||
)
|
||||
(property "MPN" "AD8418AWBRZ"
|
||||
(at 0 0 90)
|
||||
(unlocked yes)
|
||||
(layer "B.Fab")
|
||||
(hide yes)
|
||||
(uuid "d3f4f278-9ceb-465c-a04d-50b8dbf3d937")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
(justify mirror)
|
||||
)
|
||||
)
|
||||
(property "RS" "798-9903"
|
||||
(at 0 0 90)
|
||||
(unlocked yes)
|
||||
(layer "B.Fab")
|
||||
(hide yes)
|
||||
(uuid "dadeae0e-1ba2-4823-8a0d-6a2c3e497294")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
(justify mirror)
|
||||
)
|
||||
)
|
||||
(property "Farnell" "2383472"
|
||||
(at 0 0 90)
|
||||
(unlocked yes)
|
||||
(layer "B.Fab")
|
||||
(hide yes)
|
||||
(uuid "bb4037c8-2139-4dd0-8121-203fefeae157")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
(justify mirror)
|
||||
)
|
||||
)
|
||||
(property "mouser" "584-AD8418AWBRZ "
|
||||
(at 0 0 90)
|
||||
(unlocked yes)
|
||||
(layer "B.Fab")
|
||||
(hide yes)
|
||||
(uuid "5fbe0553-ff44-4294-a653-c897779ca6cd")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
(justify mirror)
|
||||
)
|
||||
)
|
||||
(property "digikey" "505-AD8418AWBRZ-ND"
|
||||
(at 0 0 90)
|
||||
(unlocked yes)
|
||||
(layer "B.Fab")
|
||||
(hide yes)
|
||||
(uuid "be3953fd-f3c4-453c-bf98-83f21661bed2")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
(justify mirror)
|
||||
)
|
||||
)
|
||||
(property "Alternative" "AD8418AWHRZ"
|
||||
(at 0 0 90)
|
||||
(unlocked yes)
|
||||
(layer "B.Fab")
|
||||
(hide yes)
|
||||
(uuid "80345cbe-3cff-44c1-a8da-cdc22317152e")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
(justify mirror)
|
||||
)
|
||||
)
|
||||
(path "/21ecf582-2fe5-40fc-9946-89f54de57e7c/e362915b-176f-4d63-b94c-3c4bc02f69ce")
|
||||
(attr smd)
|
||||
(fp_line
|
||||
(start -1.4 2.6)
|
||||
(end -2.3 2.6)
|
||||
(stroke
|
||||
(width 0.12)
|
||||
(type solid)
|
||||
)
|
||||
(layer "B.SilkS")
|
||||
(uuid "59c84261-214c-4045-b201-b933060eca42")
|
||||
)
|
||||
(fp_line
|
||||
(start -1.4 2.6)
|
||||
(end 1.5 2.6)
|
||||
(stroke
|
||||
(width 0.12)
|
||||
(type solid)
|
||||
)
|
||||
(layer "B.SilkS")
|
||||
(uuid "7770e627-ff1c-4cb5-9985-440e844d900f")
|
||||
)
|
||||
(fp_line
|
||||
(start -1.4 2.6)
|
||||
(end -1.4 -2.5)
|
||||
(stroke
|
||||
(width 0.12)
|
||||
(type solid)
|
||||
)
|
||||
(layer "B.SilkS")
|
||||
(uuid "deb670a5-2b5e-4af6-aeb7-f0e19f653a5f")
|
||||
)
|
||||
(fp_line
|
||||
(start 1.5 2.6)
|
||||
(end 1.5 -2.5)
|
||||
(stroke
|
||||
(width 0.12)
|
||||
(type solid)
|
||||
)
|
||||
(layer "B.SilkS")
|
||||
(uuid "9e6fe172-e3f0-4a7d-9a20-69e6090a13b3")
|
||||
)
|
||||
(fp_line
|
||||
(start -1.4 -2.5)
|
||||
(end 1.5 -2.5)
|
||||
(stroke
|
||||
(width 0.12)
|
||||
(type solid)
|
||||
)
|
||||
(layer "B.SilkS")
|
||||
(uuid "a34eee1c-44e0-4385-a098-6de3bae9ccb8")
|
||||
)
|
||||
(fp_circle
|
||||
(center -0.9 1.9)
|
||||
(end -0.758579 1.9)
|
||||
(stroke
|
||||
(width 0.3)
|
||||
(type solid)
|
||||
)
|
||||
(fill none)
|
||||
(layer "B.SilkS")
|
||||
(uuid "4f640165-a9f9-43b2-9a98-99c0a62aca93")
|
||||
)
|
||||
(fp_rect
|
||||
(start 3.6 2.6)
|
||||
(end -3.6 -2.6)
|
||||
(stroke
|
||||
(width 0.05)
|
||||
(type solid)
|
||||
)
|
||||
(fill none)
|
||||
(layer "B.CrtYd")
|
||||
(uuid "9fafdfb7-50ee-475d-b7d4-bb5d5f876ddb")
|
||||
)
|
||||
(fp_line
|
||||
(start -2 2.45)
|
||||
(end -2 -2.45)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "B.Fab")
|
||||
(uuid "c7436507-07fb-46f8-956f-0311d4aa71e1")
|
||||
)
|
||||
(fp_line
|
||||
(start 1.95 2.45)
|
||||
(end -2 2.45)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "B.Fab")
|
||||
(uuid "9eb58b5d-b7c4-44b1-b922-bf6540946081")
|
||||
)
|
||||
(fp_line
|
||||
(start -2 -2.45)
|
||||
(end 1.95 -2.45)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "B.Fab")
|
||||
(uuid "2cee7285-1029-4ee3-9c66-f5ab4015596b")
|
||||
)
|
||||
(fp_line
|
||||
(start 1.95 -2.45)
|
||||
(end 1.95 2.45)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "B.Fab")
|
||||
(uuid "36e74742-80ac-4087-854e-8c822e8edec0")
|
||||
)
|
||||
(fp_rect
|
||||
(start -2 1.8)
|
||||
(end -2.7 2)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(fill solid)
|
||||
(layer "B.Fab")
|
||||
(uuid "ca52708f-b42d-4fbe-85ef-a9ca4d5eae07")
|
||||
)
|
||||
(fp_rect
|
||||
(start -2 0.530524)
|
||||
(end -2.7 0.730524)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(fill solid)
|
||||
(layer "B.Fab")
|
||||
(uuid "4c71da20-e6d0-44c9-8a28-37334c1f03e8")
|
||||
)
|
||||
(fp_rect
|
||||
(start -2.010594 -0.7)
|
||||
(end -2.710594 -0.5)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(fill solid)
|
||||
(layer "B.Fab")
|
||||
(uuid "4992741c-abd9-455d-a0f3-a58debc209b2")
|
||||
)
|
||||
(fp_rect
|
||||
(start -1.960594 -2)
|
||||
(end -2.660594 -1.8)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(fill solid)
|
||||
(layer "B.Fab")
|
||||
(uuid "cb3d1cf6-2687-4f89-878d-76a2ec02cab9")
|
||||
)
|
||||
(fp_circle
|
||||
(center -0.9 1.9)
|
||||
(end -0.758579 1.9)
|
||||
(stroke
|
||||
(width 0.3)
|
||||
(type solid)
|
||||
)
|
||||
(fill none)
|
||||
(layer "B.Fab")
|
||||
(uuid "3ec80412-5185-4a19-b416-225283df27de")
|
||||
)
|
||||
(fp_text user "${REFERENCE}"
|
||||
(at 0 -0.2 0)
|
||||
(layer "B.Fab")
|
||||
(uuid "2182389f-6690-4415-9442-9029d6188c5d")
|
||||
(effects
|
||||
(font
|
||||
(size 0.98 0.98)
|
||||
(thickness 0.15)
|
||||
)
|
||||
(justify mirror)
|
||||
)
|
||||
)
|
||||
(pad "2" smd roundrect
|
||||
(at -2.575 0.635 270)
|
||||
(size 1.75 0.6)
|
||||
(layers "B.Cu" "B.Paste" "B.Mask")
|
||||
(roundrect_rratio 0.25)
|
||||
(net 1 "GND")
|
||||
(pinfunction "gnd")
|
||||
(pintype "input")
|
||||
(uuid "e5cb8713-1203-435e-89cf-405cc13573f8")
|
||||
)
|
||||
(pad "3" smd roundrect
|
||||
(at -2.575 -0.635 270)
|
||||
(size 1.75 0.6)
|
||||
(layers "B.Cu" "B.Paste" "B.Mask")
|
||||
(roundrect_rratio 0.25)
|
||||
(net 1 "GND")
|
||||
(pinfunction "ref2")
|
||||
(pintype "input")
|
||||
(uuid "fefd8876-a0f1-45c7-9cb9-4bf1469da825")
|
||||
)
|
||||
(model "${KICAD6_3DMODEL_DIR}/Package_SO.3dshapes/SOIC-8_3.9x4.9mm_P1.27mm.wrl"
|
||||
(offset
|
||||
(xyz 0 0 0)
|
||||
)
|
||||
(scale
|
||||
(xyz 1 1 1)
|
||||
)
|
||||
(rotate
|
||||
(xyz 0 0 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(gr_circle
|
||||
(center 55 -47)
|
||||
(end 65 -47)
|
||||
(locked yes)
|
||||
(stroke
|
||||
(width 0.15)
|
||||
(type default)
|
||||
)
|
||||
(fill none)
|
||||
(layer "Cmts.User")
|
||||
(uuid "4662a067-9f60-4ec4-80f5-5b883188d85a")
|
||||
)
|
||||
(gr_line
|
||||
(start 78 -119)
|
||||
(end 78 21)
|
||||
(stroke
|
||||
(width 0.15)
|
||||
(type default)
|
||||
)
|
||||
(layer "Cmts.User")
|
||||
(uuid "bf5a10c5-bece-4392-bf41-15786c10b42f")
|
||||
)
|
||||
(gr_line
|
||||
(start -20 -47)
|
||||
(end 120 -47)
|
||||
(locked yes)
|
||||
(stroke
|
||||
(width 0.15)
|
||||
(type default)
|
||||
)
|
||||
(layer "Cmts.User")
|
||||
(uuid "e4ee0deb-a01a-4a3d-9440-f9cf8dec9745")
|
||||
)
|
||||
(gr_line
|
||||
(start 55.25 -120)
|
||||
(end 55.25 20)
|
||||
(locked yes)
|
||||
(stroke
|
||||
(width 0.15)
|
||||
(type default)
|
||||
)
|
||||
(layer "Cmts.User")
|
||||
(uuid "f7eb59e2-1cd0-46e1-a39b-01a9441cc9f0")
|
||||
)
|
||||
(gr_line
|
||||
(start 110.5 -95)
|
||||
(end 104 -101.2)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type default)
|
||||
)
|
||||
(layer "Edge.Cuts")
|
||||
(uuid "19557c0c-8a26-402f-ba86-3c9441f3f322")
|
||||
)
|
||||
(gr_line
|
||||
(start 110.5 -1.27)
|
||||
(end 110.5 -95)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type default)
|
||||
)
|
||||
(layer "Edge.Cuts")
|
||||
(uuid "2c6ccce8-2443-4f53-bdc6-23b6573b46bd")
|
||||
)
|
||||
(gr_line
|
||||
(start 5 -97.5)
|
||||
(end 8 -101.2)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type default)
|
||||
)
|
||||
(layer "Edge.Cuts")
|
||||
(uuid "47d9bdef-d743-4104-867d-863ccee27302")
|
||||
)
|
||||
(gr_line
|
||||
(start 0 -1.27)
|
||||
(end 71.12 -1.27)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type default)
|
||||
)
|
||||
(layer "Edge.Cuts")
|
||||
(uuid "6519683c-afc2-40bd-9759-e72b2bbcbdf8")
|
||||
)
|
||||
(gr_line
|
||||
(start 0 -95)
|
||||
(end 2 -97.5)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type default)
|
||||
)
|
||||
(layer "Edge.Cuts")
|
||||
(uuid "8a178068-3413-4e86-993a-090afdda555b")
|
||||
)
|
||||
(gr_line
|
||||
(start 5 -97.5)
|
||||
(end 2 -97.5)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type default)
|
||||
)
|
||||
(layer "Edge.Cuts")
|
||||
(uuid "a10eb751-171b-4a2e-9311-0e6ddf378147")
|
||||
)
|
||||
(gr_line
|
||||
(start 71.12 -1.27)
|
||||
(end 110.5 -1.27)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type default)
|
||||
)
|
||||
(layer "Edge.Cuts")
|
||||
(uuid "ca421978-203f-416c-adc9-837c0311de64")
|
||||
)
|
||||
(gr_line
|
||||
(start 0 -95)
|
||||
(end 0 -1.27)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type default)
|
||||
)
|
||||
(layer "Edge.Cuts")
|
||||
(uuid "f2966019-2f39-4eac-89ae-f67c452bf71a")
|
||||
)
|
||||
(gr_line
|
||||
(start 104 -101.2)
|
||||
(end 8 -101.2)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type default)
|
||||
)
|
||||
(layer "Edge.Cuts")
|
||||
(uuid "f4537f53-f7b5-4dc9-b61b-145ea11df1ed")
|
||||
)
|
||||
(gr_text "TOP"
|
||||
(at -14.3 50.65 0)
|
||||
(layer "F.Cu")
|
||||
(uuid "ae1f1aff-d924-4501-93ab-977713ab026e")
|
||||
(effects
|
||||
(font
|
||||
(size 1.5 1.5)
|
||||
(thickness 0.3)
|
||||
(bold yes)
|
||||
)
|
||||
(justify left bottom)
|
||||
)
|
||||
)
|
||||
(gr_text "Pumpensteuerung SEQU\nBedienteil 140mm v31a"
|
||||
(at -5.25 52.1 0)
|
||||
(layer "F.Cu")
|
||||
(uuid "e03ce3b4-270c-4001-a4a7-fa9658e4de2b")
|
||||
(effects
|
||||
(font
|
||||
(size 1.3 1.3)
|
||||
(thickness 0.2)
|
||||
)
|
||||
(justify left bottom)
|
||||
)
|
||||
)
|
||||
(gr_text "compare GND zone fill around both PAD4 on both AD8418:\n- these pin is unconnected in eeschema (not available in the symbol)\n- on one footprint pad 4 is leaved unconnected\n- on the other footprint pad4 is overflooded with copper"
|
||||
(at 8.6 34 0)
|
||||
(layer "F.SilkS")
|
||||
(uuid "a9f77747-db5c-4f04-aa3d-be45ba408159")
|
||||
(effects
|
||||
(font
|
||||
(size 3 3)
|
||||
(thickness 0.4)
|
||||
)
|
||||
(justify left bottom)
|
||||
)
|
||||
)
|
||||
(zone
|
||||
(net 1)
|
||||
(net_name "GND")
|
||||
(layer "B.Cu")
|
||||
(uuid "28454119-9ab7-4f74-87f5-a0c5fc0bb6ed")
|
||||
(name "gnd")
|
||||
(hatch edge 0.5)
|
||||
(priority 2)
|
||||
(connect_pads
|
||||
(clearance 0.4)
|
||||
)
|
||||
(min_thickness 0.2)
|
||||
(filled_areas_thickness no)
|
||||
(fill yes
|
||||
(thermal_gap 0.4)
|
||||
(thermal_bridge_width 0.23)
|
||||
)
|
||||
(polygon
|
||||
(pts
|
||||
(xy 55.242749 -5.605916) (xy 95.722142 -5.547992) (xy 95.74 -34.12555) (xy 55.180826 -34.138786)
|
||||
)
|
||||
)
|
||||
(filled_polygon
|
||||
(layer "B.Cu")
|
||||
(pts
|
||||
(xy 95.64097 -34.125582) (xy 95.668861 -34.121563) (xy 95.694489 -34.109849) (xy 95.715778 -34.091389)
|
||||
(xy 95.731005 -34.067679) (xy 95.738935 -34.040639) (xy 95.739938 -34.02652) (xy 95.722203 -5.647071)
|
||||
(xy 95.718175 -5.619182) (xy 95.706454 -5.593558) (xy 95.687987 -5.572273) (xy 95.664273 -5.557054)
|
||||
(xy 95.637231 -5.549132) (xy 95.623071 -5.548133) (xy 55.341386 -5.605774) (xy 55.313507 -5.609824)
|
||||
(xy 55.287891 -5.621567) (xy 55.266622 -5.64005) (xy 55.251422 -5.663777) (xy 55.243522 -5.690825)
|
||||
(xy 55.242534 -5.704545) (xy 55.221709 -15.299999) (xy 68.48 -15.299999) (xy 68.548262 -15.3) (xy 68.54828 -15.300002)
|
||||
(xy 68.614309 -15.308019) (xy 68.614317 -15.30802) (xy 68.678918 -15.323942) (xy 68.741139 -15.347539)
|
||||
(xy 68.741153 -15.347545) (xy 68.800055 -15.378459) (xy 68.800059 -15.378462) (xy 68.854811 -15.416255)
|
||||
(xy 68.854817 -15.41626) (xy 68.904605 -15.460368) (xy 68.904624 -15.460387) (xy 68.925898 -15.4844)
|
||||
(xy 68.947395 -15.502617) (xy 68.973154 -15.514041) (xy 69.001088 -15.517744) (xy 69.028934 -15.513428)
|
||||
(xy 69.054436 -15.501441) (xy 69.074102 -15.4844) (xy 69.095375 -15.460387) (xy 69.095394 -15.460368)
|
||||
(xy 69.145182 -15.41626) (xy 69.145188 -15.416255) (xy 69.19994 -15.378462) (xy 69.199944 -15.378459)
|
||||
(xy 69.258846 -15.347545) (xy 69.25886 -15.347539) (xy 69.321081 -15.323942) (xy 69.385678 -15.308021)
|
||||
(xy 69.451723 -15.3) (xy 69.519999 -15.3) (xy 69.52 -15.300001) (xy 69.52 -16.459999) (xy 69.519999 -16.46)
|
||||
(xy 68.480001 -16.46) (xy 68.48 -16.459999) (xy 68.48 -15.299999) (xy 69.75 -15.299999) (xy 69.818262 -15.3)
|
||||
(xy 69.81828 -15.300002) (xy 69.884309 -15.308019) (xy 69.884317 -15.30802) (xy 69.948918 -15.323942)
|
||||
(xy 70.011139 -15.347539) (xy 70.011153 -15.347545) (xy 70.070055 -15.378459) (xy 70.070059 -15.378462)
|
||||
(xy 70.124811 -15.416255) (xy 70.124817 -15.41626) (xy 70.174605 -15.460368) (xy 70.174631 -15.460394)
|
||||
(xy 70.218739 -15.510182) (xy 70.218744 -15.510188) (xy 70.256537 -15.56494) (xy 70.25654 -15.564944)
|
||||
(xy 70.287454 -15.623846) (xy 70.28746 -15.62386) (xy 70.311057 -15.686081) (xy 70.326978 -15.750678)
|
||||
(xy 70.334999 -15.816723) (xy 70.335 -15.816735) (xy 70.335 -16.459999) (xy 70.334999 -16.46) (xy 69.750001 -16.46)
|
||||
(xy 69.75 -16.459999) (xy 69.75 -15.299999) (xy 68.48 -15.299999) (xy 55.221709 -15.299999) (xy 55.220588 -15.816737)
|
||||
(xy 67.665001 -15.816737) (xy 67.665002 -15.81672) (xy 67.673019 -15.75069) (xy 67.67302 -15.750682)
|
||||
(xy 67.688942 -15.686081) (xy 67.712539 -15.62386) (xy 67.712545 -15.623846) (xy 67.743459 -15.564944)
|
||||
(xy 67.743462 -15.56494) (xy 67.781255 -15.510188) (xy 67.78126 -15.510182) (xy 67.825368 -15.460394)
|
||||
(xy 67.825394 -15.460368) (xy 67.875182 -15.41626) (xy 67.875188 -15.416255) (xy 67.92994 -15.378462)
|
||||
(xy 67.929944 -15.378459) (xy 67.988846 -15.347545) (xy 67.98886 -15.347539) (xy 68.051081 -15.323942)
|
||||
(xy 68.115678 -15.308021) (xy 68.181723 -15.3) (xy 68.249999 -15.3) (xy 68.25 -15.300001) (xy 68.25 -16.459999)
|
||||
(xy 68.249999 -16.46) (xy 67.665002 -16.46) (xy 67.665001 -16.459999) (xy 67.665001 -15.816737)
|
||||
(xy 55.220588 -15.816737) (xy 55.218693 -16.690001) (xy 67.665 -16.690001) (xy 67.665001 -16.69)
|
||||
(xy 68.249999 -16.69) (xy 68.25 -16.690001) (xy 68.48 -16.690001) (xy 68.480001 -16.69) (xy 69.519999 -16.69)
|
||||
(xy 69.52 -16.690001) (xy 69.75 -16.690001) (xy 69.750001 -16.69) (xy 70.334998 -16.69) (xy 70.334999 -16.690001)
|
||||
(xy 70.334999 -17.333262) (xy 70.334997 -17.333279) (xy 70.32698 -17.399309) (xy 70.326979 -17.399317)
|
||||
(xy 70.311057 -17.463918) (xy 70.28746 -17.526139) (xy 70.287454 -17.526153) (xy 70.25654 -17.585055)
|
||||
(xy 70.256537 -17.585059) (xy 70.218744 -17.639811) (xy 70.218739 -17.639817) (xy 70.174631 -17.689605)
|
||||
(xy 70.174605 -17.689631) (xy 70.124817 -17.733739) (xy 70.124811 -17.733744) (xy 70.070059 -17.771537)
|
||||
(xy 70.070055 -17.77154) (xy 70.011153 -17.802454) (xy 70.011139 -17.80246) (xy 69.948918 -17.826057)
|
||||
(xy 69.884321 -17.841978) (xy 69.81827 -17.849999) (xy 69.75 -17.849998) (xy 69.75 -16.690001) (xy 69.52 -16.690001)
|
||||
(xy 69.52 -17.849998) (xy 69.519999 -17.849999) (xy 69.451737 -17.849999) (xy 69.451721 -17.849998)
|
||||
(xy 69.38569 -17.84198) (xy 69.385682 -17.841979) (xy 69.321081 -17.826057) (xy 69.25886 -17.80246)
|
||||
(xy 69.258846 -17.802454) (xy 69.199944 -17.77154) (xy 69.19994 -17.771537) (xy 69.145188 -17.733744)
|
||||
(xy 69.145182 -17.733739) (xy 69.095394 -17.689631) (xy 69.095369 -17.689606) (xy 69.074103 -17.665601)
|
||||
(xy 69.052606 -17.647383) (xy 69.026848 -17.635959) (xy 68.998914 -17.632255) (xy 68.971068 -17.636571)
|
||||
(xy 68.945566 -17.648557) (xy 68.925897 -17.665601) (xy 68.90463 -17.689606) (xy 68.904605 -17.689631)
|
||||
(xy 68.854817 -17.733739) (xy 68.854811 -17.733744) (xy 68.800059 -17.771537) (xy 68.800055 -17.77154)
|
||||
(xy 68.741153 -17.802454) (xy 68.741139 -17.80246) (xy 68.678918 -17.826057) (xy 68.614321 -17.841978)
|
||||
(xy 68.54827 -17.849999) (xy 68.48 -17.849998) (xy 68.48 -16.690001) (xy 68.25 -16.690001) (xy 68.25 -17.849998)
|
||||
(xy 68.249999 -17.849999) (xy 68.181737 -17.849999) (xy 68.181721 -17.849998) (xy 68.11569 -17.84198)
|
||||
(xy 68.115682 -17.841979) (xy 68.051081 -17.826057) (xy 67.98886 -17.80246) (xy 67.988846 -17.802454)
|
||||
(xy 67.929944 -17.77154) (xy 67.92994 -17.771537) (xy 67.875188 -17.733744) (xy 67.875182 -17.733739)
|
||||
(xy 67.825394 -17.689631) (xy 67.825368 -17.689605) (xy 67.78126 -17.639817) (xy 67.781255 -17.639811)
|
||||
(xy 67.743462 -17.585059) (xy 67.743459 -17.585055) (xy 67.712545 -17.526153) (xy 67.712539 -17.526139)
|
||||
(xy 67.688942 -17.463918) (xy 67.673021 -17.399321) (xy 67.665 -17.333276) (xy 67.665 -16.690001)
|
||||
(xy 55.218693 -16.690001) (xy 55.181041 -34.03954) (xy 55.184991 -34.067438) (xy 55.196641 -34.093096)
|
||||
(xy 55.215048 -34.114431) (xy 55.23872 -34.129717) (xy 55.265739 -34.137715) (xy 55.280073 -34.138753)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
|
@ -46,6 +46,7 @@ set( QA_PCBNEW_SRCS
|
|||
test_reference_image_load.cpp
|
||||
test_save_load.cpp
|
||||
test_tracks_cleaner.cpp
|
||||
test_triangulation.cpp
|
||||
test_zone_filler.cpp
|
||||
|
||||
drc/test_custom_rule_severities.cpp
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2021 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
|
||||
*/
|
||||
|
||||
#include <qa_utils/wx_utils/unit_test_utils.h>
|
||||
#include <pcbnew_utils/board_test_utils.h>
|
||||
#include <board.h>
|
||||
#include <board_design_settings.h>
|
||||
#include <pad.h>
|
||||
#include <pcb_track.h>
|
||||
#include <footprint.h>
|
||||
#include <zone.h>
|
||||
#include <drc/drc_item.h>
|
||||
#include <settings/settings_manager.h>
|
||||
|
||||
|
||||
struct TRIANGULATE_TEST_FIXTURE
|
||||
{
|
||||
TRIANGULATE_TEST_FIXTURE() :
|
||||
m_settingsManager( true /* headless */ )
|
||||
{ }
|
||||
|
||||
SETTINGS_MANAGER m_settingsManager;
|
||||
std::unique_ptr<BOARD> m_board;
|
||||
};
|
||||
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( RegressionTriangulationTests, TRIANGULATE_TEST_FIXTURE )
|
||||
{
|
||||
std::vector<wxString> tests = {
|
||||
"issue2568",
|
||||
"issue5313",
|
||||
"issue5320",
|
||||
"issue5567",
|
||||
"issue5830",
|
||||
"issue6039",
|
||||
"issue6260",
|
||||
"issue7086",
|
||||
"issue14294",
|
||||
"issue17559"
|
||||
};
|
||||
|
||||
|
||||
for( const wxString& relPath : tests )
|
||||
{
|
||||
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
|
||||
|
||||
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||
|
||||
for( ZONE* zone : m_board->Zones() )
|
||||
{
|
||||
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq())
|
||||
{
|
||||
auto poly = zone->GetFilledPolysList( layer );
|
||||
double area = poly->Area();
|
||||
double tri_area = 0.0;
|
||||
|
||||
for( int ii = 0; ii < poly->TriangulatedPolyCount(); ii++ )
|
||||
{
|
||||
const auto tri_poly = poly->TriangulatedPolygon( ii );
|
||||
|
||||
for( auto& tri : tri_poly->Triangles() )
|
||||
tri_area += tri.Area();
|
||||
}
|
||||
|
||||
double diff = std::abs( area - tri_area );
|
||||
|
||||
// The difference should be less than 1e-4 square mm
|
||||
BOOST_CHECK_MESSAGE( diff < 1e8, "Triangulation area mismatch in " + relPath + " layer " + LayerName( layer ) + " difference: " + std::to_string( diff ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue