From 437e2783fbf70255eaecacecb31ad6dc709a1386 Mon Sep 17 00:00:00 2001 From: Roberto Fernandez Bautista Date: Sat, 19 Jun 2021 10:56:22 +0100 Subject: [PATCH] Use Clipper Z value to detect arcs post clipper --- .../include/geometry/shape_line_chain.h | 39 +++++++++++- libs/kimath/include/geometry/shape_poly_set.h | 4 +- libs/kimath/src/geometry/shape_line_chain.cpp | 63 +++++++++++++++---- libs/kimath/src/geometry/shape_poly_set.cpp | 52 ++++++++++----- 4 files changed, 126 insertions(+), 32 deletions(-) diff --git a/libs/kimath/include/geometry/shape_line_chain.h b/libs/kimath/include/geometry/shape_line_chain.h index 18ca5bf95d..7a7b54bedf 100644 --- a/libs/kimath/include/geometry/shape_line_chain.h +++ b/libs/kimath/include/geometry/shape_line_chain.h @@ -33,6 +33,31 @@ #include #include +/** + * 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 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>( m_points.size(), SHAPES_ARE_PT ); } - SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath ); + SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath, const std::vector& aZValueBuffer, + const std::vector& 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& aZValueBuffer, + std::vector& aArcBuffer ) const; private: diff --git a/libs/kimath/include/geometry/shape_poly_set.h b/libs/kimath/include/geometry/shape_poly_set.h index 86957eefad..4735e3c2b7 100644 --- a/libs/kimath/include/geometry/shape_poly_set.h +++ b/libs/kimath/include/geometry/shape_poly_set.h @@ -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& aZValueBuffer, + const std::vector& aArcBuffe ); /** * This is the engine to execute all polygon boolean transforms (AND, OR, ... and polygon diff --git a/libs/kimath/src/geometry/shape_line_chain.cpp b/libs/kimath/src/geometry/shape_line_chain.cpp index bd094f34e5..2d188e78a9 100644 --- a/libs/kimath/src/geometry/shape_line_chain.cpp +++ b/libs/kimath/src/geometry/shape_line_chain.cpp @@ -28,6 +28,7 @@ #include #include // for INT_MAX #include // for hypot +#include #include // for basic_string #include @@ -54,32 +55,67 @@ SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const std::vector& 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& aZValueBuffer, + const std::vector& aArcBuffer ) : + SHAPE_LINE_CHAIN_BASE( SH_LINE_CHAIN ), + m_closed( true ), m_width( 0 ) { + std::map 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& aZValueBuffer, + std::vector& 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 } diff --git a/libs/kimath/src/geometry/shape_poly_set.cpp b/libs/kimath/src/geometry/shape_poly_set.cpp index 72420d401e..13533fc2b6 100644 --- a/libs/kimath/src/geometry/shape_poly_set.cpp +++ b/libs/kimath/src/geometry/shape_poly_set.cpp @@ -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 possibleArcs; - - aShape.GetArcs( possibleArcs ); - aOtherShape.GetArcs( possibleArcs ); + std::vector zValues; + std::vector 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 zValues; + std::vector 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& aZValueBuffer, + const std::vector& 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 ); }