Use Clipper Z value to detect arcs post clipper

This commit is contained in:
Roberto Fernandez Bautista 2021-06-19 10:56:22 +01:00 committed by Jon Evans
parent cad27f9baa
commit 437e2783fb
4 changed files with 126 additions and 32 deletions

View File

@ -33,6 +33,31 @@
#include <geometry/shape_arc.h> #include <geometry/shape_arc.h>
#include <math/vector2d.h> #include <math/vector2d.h>
/**
* Holds information on each point of a SHAPE_LINE_CHAIN that is retrievable
* after an operation with ClipperLib
*/
struct CLIPPER_Z_VALUE
{
CLIPPER_Z_VALUE( const std::pair<ssize_t, ssize_t> aShapeIndices, ssize_t aOffset = 0 )
{
m_FirstArcIdx = aShapeIndices.first;
m_SecondArcIdx = aShapeIndices.second;
auto offsetVal = [&]( ssize_t& aVal )
{
if( aVal >= 0 )
aVal += aOffset;
};
offsetVal( m_FirstArcIdx );
offsetVal( m_SecondArcIdx );
}
ssize_t m_FirstArcIdx;
ssize_t m_SecondArcIdx;
};
/** /**
* Represent a polyline (an zero-thickness chain of connected line segments). * Represent a polyline (an zero-thickness chain of connected line segments).
@ -152,7 +177,8 @@ public:
m_shapes = std::vector<std::pair<ssize_t, ssize_t>>( m_points.size(), SHAPES_ARE_PT ); m_shapes = std::vector<std::pair<ssize_t, ssize_t>>( m_points.size(), SHAPES_ARE_PT );
} }
SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath ); SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath, const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
const std::vector<SHAPE_ARC>& aArcBuffer );
virtual ~SHAPE_LINE_CHAIN() virtual ~SHAPE_LINE_CHAIN()
{} {}
@ -698,7 +724,12 @@ public:
const VECTOR2I PointAlong( int aPathLength ) const; const VECTOR2I PointAlong( int aPathLength ) const;
double Area() const; /**
* Return the area of this chain
* @param aAbsolute If true, returns a positive value. Otherwise the value depends on the
* orientation of the chain
*/
double Area( bool aAbsolute = true ) const;
size_t ArcCount() const size_t ArcCount() const
{ {
@ -810,7 +841,9 @@ protected:
/** /**
* Create a new Clipper path from the SHAPE_LINE_CHAIN in a given orientation * Create a new Clipper path from the SHAPE_LINE_CHAIN in a given orientation
*/ */
ClipperLib::Path convertToClipper( bool aRequiredOrientation ) const; ClipperLib::Path convertToClipper( bool aRequiredOrientation,
std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
std::vector<SHAPE_ARC>& aArcBuffer ) const;
private: private:

View File

@ -1341,7 +1341,9 @@ public:
private: private:
void fractureSingle( POLYGON& paths ); void fractureSingle( POLYGON& paths );
void unfractureSingle ( POLYGON& path ); void unfractureSingle ( POLYGON& path );
void importTree( ClipperLib::PolyTree* tree ); void importTree( ClipperLib::PolyTree* tree,
const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
const std::vector<SHAPE_ARC>& aArcBuffe );
/** /**
* This is the engine to execute all polygon boolean transforms (AND, OR, ... and polygon * This is the engine to execute all polygon boolean transforms (AND, OR, ... and polygon

View File

@ -28,6 +28,7 @@
#include <algorithm> #include <algorithm>
#include <limits.h> // for INT_MAX #include <limits.h> // for INT_MAX
#include <math.h> // for hypot #include <math.h> // for hypot
#include <map>
#include <string> // for basic_string #include <string> // for basic_string
#include <clipper.hpp> #include <clipper.hpp>
@ -54,32 +55,67 @@ SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const std::vector<int>& aV)
} }
} }
SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath ) : SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath,
SHAPE_LINE_CHAIN_BASE( SH_LINE_CHAIN ), const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
m_closed( true ), const std::vector<SHAPE_ARC>& aArcBuffer ) :
m_width( 0 ) SHAPE_LINE_CHAIN_BASE( SH_LINE_CHAIN ),
m_closed( true ), m_width( 0 )
{ {
std::map<ssize_t, ssize_t> loadedArcs;
m_points.reserve( aPath.size() ); m_points.reserve( aPath.size() );
m_shapes.reserve( aPath.size() ); m_shapes.reserve( aPath.size() );
auto loadArc =
[&]( ssize_t aArcIndex ) -> ssize_t
{
if( aArcIndex == SHAPE_IS_PT )
{
return SHAPE_IS_PT;
}
else if( loadedArcs.count( aArcIndex ) == 0 )
{
loadedArcs.insert( { aArcIndex, m_arcs.size() } );
m_arcs.push_back( aArcBuffer.at( aArcIndex ) );
}
return loadedArcs.at(aArcIndex);
};
for( size_t ii = 0; ii < aPath.size(); ++ii ) for( size_t ii = 0; ii < aPath.size(); ++ii )
{ {
Append( aPath[ii].X, aPath[ii].Y ); Append( aPath[ii].X, aPath[ii].Y );
m_shapes[ii].first = loadArc( aZValueBuffer[aPath[ii].Z].m_FirstArcIdx );
m_shapes[ii].second = loadArc( aZValueBuffer[aPath[ii].Z].m_SecondArcIdx );
} }
} }
ClipperLib::Path SHAPE_LINE_CHAIN::convertToClipper( bool aRequiredOrientation ) const ClipperLib::Path SHAPE_LINE_CHAIN::convertToClipper( bool aRequiredOrientation,
std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
std::vector<SHAPE_ARC>& aArcBuffer ) const
{ {
ClipperLib::Path c_path; ClipperLib::Path c_path;
SHAPE_LINE_CHAIN input;
bool orientation = Area( false ) >= 0;
ssize_t shape_offset = aArcBuffer.size();
for( int i = 0; i < PointCount(); i++ ) if( orientation != aRequiredOrientation )
input = Reverse();
else
input = *this;
for( int i = 0; i < input.PointCount(); i++ )
{ {
const VECTOR2I& vertex = CPoint( i ); const VECTOR2I& vertex = input.CPoint( i );
c_path.push_back( ClipperLib::IntPoint( vertex.x, vertex.y ) );
CLIPPER_Z_VALUE z_value( input.m_shapes[i], shape_offset );
size_t z_value_ptr = aZValueBuffer.size();
aZValueBuffer.push_back( z_value );
c_path.push_back( ClipperLib::IntPoint( vertex.x, vertex.y, z_value_ptr ) );
} }
if( Orientation( c_path ) != aRequiredOrientation ) aArcBuffer.insert( aArcBuffer.end(), input.m_arcs.begin(), input.m_arcs.end() );
ReversePath( c_path );
return c_path; return c_path;
} }
@ -1761,7 +1797,7 @@ const VECTOR2I SHAPE_LINE_CHAIN::PointAlong( int aPathLength ) const
} }
double SHAPE_LINE_CHAIN::Area() const double SHAPE_LINE_CHAIN::Area( bool aAbsolute ) const
{ {
// see https://www.mathopenref.com/coordpolygonarea2.html // see https://www.mathopenref.com/coordpolygonarea2.html
@ -1778,7 +1814,10 @@ double SHAPE_LINE_CHAIN::Area() const
j = i; j = i;
} }
return std::fabs( area * 0.5 ); // The result would be negative if points are anti-clockwise if( aAbsolute )
return std::fabs( area * 0.5 ); // The result would be negative if points are anti-clockwise
else
return -area * 0.5; // The result would be negative if points are anti-clockwise
} }

View File

@ -516,31 +516,43 @@ void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET
c.StrictlySimple( aFastMode == PM_STRICTLY_SIMPLE ); c.StrictlySimple( aFastMode == PM_STRICTLY_SIMPLE );
// All possible arcs in the resulting shape of the booleanop std::vector<CLIPPER_Z_VALUE> zValues;
std::vector<SHAPE_ARC> possibleArcs; std::vector<SHAPE_ARC> arcBuffer;
aShape.GetArcs( possibleArcs );
aOtherShape.GetArcs( possibleArcs );
for( const POLYGON& poly : aShape.m_polys ) for( const POLYGON& poly : aShape.m_polys )
{ {
for( size_t i = 0 ; i < poly.size(); i++ ) for( size_t i = 0; i < poly.size(); i++ )
c.AddPath( poly[i].convertToClipper( i == 0 ), ClipperLib::ptSubject, true ); {
c.AddPath( poly[i].convertToClipper( i == 0, zValues, arcBuffer ),
ClipperLib::ptSubject, true );
}
} }
for( const POLYGON& poly : aOtherShape.m_polys ) for( const POLYGON& poly : aOtherShape.m_polys )
{ {
for( size_t i = 0; i < poly.size(); i++ ) for( size_t i = 0; i < poly.size(); i++ )
c.AddPath( poly[i].convertToClipper( i == 0 ), ClipperLib::ptClip, true ); {
c.AddPath( poly[i].convertToClipper( i == 0, zValues, arcBuffer ),
ClipperLib::ptClip, true );
}
} }
ClipperLib::PolyTree solution; ClipperLib::PolyTree solution;
ClipperLib::ZFillCallback callback =
[]( ClipperLib::IntPoint & e1bot, ClipperLib::IntPoint & e1top,
ClipperLib::IntPoint & e2bot, ClipperLib::IntPoint & e2top,
ClipperLib::IntPoint & pt )
{
//@todo write callback to handle arc intersections
int i = 0;
};
c.ZFillFunction( callback ); // register callback
c.Execute( aType, solution, ClipperLib::pftNonZero, ClipperLib::pftNonZero ); c.Execute( aType, solution, ClipperLib::pftNonZero, ClipperLib::pftNonZero );
importTree( &solution ); importTree( &solution, zValues, arcBuffer );
detectArcs( possibleArcs );
} }
@ -651,10 +663,16 @@ void SHAPE_POLY_SET::Inflate( int aAmount, int aCircleSegCount, CORNER_STRATEGY
break; break;
} }
std::vector<CLIPPER_Z_VALUE> zValues;
std::vector<SHAPE_ARC> arcBuffer;
for( const POLYGON& poly : m_polys ) for( const POLYGON& poly : m_polys )
{ {
for( size_t i = 0; i < poly.size(); i++ ) for( size_t i = 0; i < poly.size(); i++ )
c.AddPath( poly[i].convertToClipper( i == 0 ), joinType, etClosedPolygon ); {
c.AddPath( poly[i].convertToClipper( i == 0, zValues, arcBuffer ),
joinType, etClosedPolygon );
}
} }
PolyTree solution; PolyTree solution;
@ -685,11 +703,13 @@ void SHAPE_POLY_SET::Inflate( int aAmount, int aCircleSegCount, CORNER_STRATEGY
c.MiterFallback = miterFallback; c.MiterFallback = miterFallback;
c.Execute( solution, aAmount ); c.Execute( solution, aAmount );
importTree( &solution ); importTree( &solution, zValues, arcBuffer );
} }
void SHAPE_POLY_SET::importTree( ClipperLib::PolyTree* tree ) void SHAPE_POLY_SET::importTree( ClipperLib::PolyTree* tree,
const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
const std::vector<SHAPE_ARC>& aArcBuffer )
{ {
m_polys.clear(); m_polys.clear();
@ -700,10 +720,10 @@ void SHAPE_POLY_SET::importTree( ClipperLib::PolyTree* tree )
POLYGON paths; POLYGON paths;
paths.reserve( n->Childs.size() + 1 ); paths.reserve( n->Childs.size() + 1 );
paths.push_back( n->Contour ); paths.emplace_back( n->Contour, aZValueBuffer, aArcBuffer );
for( unsigned int i = 0; i < n->Childs.size(); i++ ) for( unsigned int i = 0; i < n->Childs.size(); i++ )
paths.push_back( n->Childs[i]->Contour ); paths.emplace_back( n->Childs[i]->Contour, aZValueBuffer, aArcBuffer );
m_polys.push_back( paths ); m_polys.push_back( paths );
} }