Use Clipper Z value to detect arcs post clipper
This commit is contained in:
parent
cad27f9baa
commit
437e2783fb
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue