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

View File

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

View File

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

View File

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