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 <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).
|
||||
|
@ -152,7 +177,8 @@ public:
|
|||
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()
|
||||
{}
|
||||
|
@ -698,7 +724,12 @@ public:
|
|||
|
||||
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
|
||||
{
|
||||
|
@ -810,7 +841,9 @@ protected:
|
|||
/**
|
||||
* 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:
|
||||
|
||||
|
|
|
@ -1341,7 +1341,9 @@ public:
|
|||
private:
|
||||
void fractureSingle( POLYGON& paths );
|
||||
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
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <algorithm>
|
||||
#include <limits.h> // for INT_MAX
|
||||
#include <math.h> // for hypot
|
||||
#include <map>
|
||||
#include <string> // for basic_string
|
||||
|
||||
#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_BASE( SH_LINE_CHAIN ),
|
||||
m_closed( true ),
|
||||
m_width( 0 )
|
||||
SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath,
|
||||
const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer,
|
||||
const std::vector<SHAPE_ARC>& aArcBuffer ) :
|
||||
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_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 )
|
||||
{
|
||||
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;
|
||||
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 );
|
||||
c_path.push_back( ClipperLib::IntPoint( vertex.x, vertex.y ) );
|
||||
const VECTOR2I& vertex = input.CPoint( i );
|
||||
|
||||
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 )
|
||||
ReversePath( c_path );
|
||||
aArcBuffer.insert( aArcBuffer.end(), input.m_arcs.begin(), input.m_arcs.end() );
|
||||
|
||||
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
|
||||
|
||||
|
@ -1778,7 +1814,10 @@ double SHAPE_LINE_CHAIN::Area() const
|
|||
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 );
|
||||
|
||||
// All possible arcs in the resulting shape of the booleanop
|
||||
std::vector<SHAPE_ARC> possibleArcs;
|
||||
|
||||
aShape.GetArcs( possibleArcs );
|
||||
aOtherShape.GetArcs( possibleArcs );
|
||||
std::vector<CLIPPER_Z_VALUE> zValues;
|
||||
std::vector<SHAPE_ARC> arcBuffer;
|
||||
|
||||
for( const POLYGON& poly : aShape.m_polys )
|
||||
{
|
||||
for( size_t i = 0 ; i < poly.size(); i++ )
|
||||
c.AddPath( poly[i].convertToClipper( i == 0 ), ClipperLib::ptSubject, true );
|
||||
for( size_t i = 0; i < poly.size(); i++ )
|
||||
{
|
||||
c.AddPath( poly[i].convertToClipper( i == 0, zValues, arcBuffer ),
|
||||
ClipperLib::ptSubject, true );
|
||||
}
|
||||
}
|
||||
|
||||
for( const POLYGON& poly : aOtherShape.m_polys )
|
||||
{
|
||||
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::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 );
|
||||
|
||||
importTree( &solution );
|
||||
|
||||
detectArcs( possibleArcs );
|
||||
importTree( &solution, zValues, arcBuffer );
|
||||
}
|
||||
|
||||
|
||||
|
@ -651,10 +663,16 @@ void SHAPE_POLY_SET::Inflate( int aAmount, int aCircleSegCount, CORNER_STRATEGY
|
|||
break;
|
||||
}
|
||||
|
||||
std::vector<CLIPPER_Z_VALUE> zValues;
|
||||
std::vector<SHAPE_ARC> arcBuffer;
|
||||
|
||||
for( const POLYGON& poly : m_polys )
|
||||
{
|
||||
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;
|
||||
|
@ -685,11 +703,13 @@ void SHAPE_POLY_SET::Inflate( int aAmount, int aCircleSegCount, CORNER_STRATEGY
|
|||
c.MiterFallback = miterFallback;
|
||||
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();
|
||||
|
||||
|
@ -700,10 +720,10 @@ void SHAPE_POLY_SET::importTree( ClipperLib::PolyTree* tree )
|
|||
POLYGON paths;
|
||||
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++ )
|
||||
paths.push_back( n->Childs[i]->Contour );
|
||||
paths.emplace_back( n->Childs[i]->Contour, aZValueBuffer, aArcBuffer );
|
||||
|
||||
m_polys.push_back( paths );
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue