feat: modules moved and engine moved to submodule

This commit is contained in:
Jan van der Weide 2025-04-12 18:40:44 +02:00
parent dfb5e645cd
commit c33d2130cc
5136 changed files with 225275 additions and 64485 deletions

View file

@ -1,26 +1,23 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 12 May 2024 *
* Website : http://www.angusj.com *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : Core Clipper Library structures and functions *
* License : http://www.boost.org/LICENSE_1_0.txt *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_CORE_H
#define CLIPPER_CORE_H
#include "clipper2/clipper.version.h"
#include <cstdint>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
#include <climits>
#include <numeric>
#include <optional>
#include "clipper2/clipper.version.h"
#include <cmath>
#define CLIPPER2_THROW(exception) std::abort()
@ -33,7 +30,7 @@ namespace Clipper2Lib
public:
explicit Clipper2Exception(const char* description) :
m_descr(description) {}
virtual const char* what() const throw() override { return m_descr.c_str(); }
virtual const char* what() const noexcept override { return m_descr.c_str(); }
private:
std::string m_descr;
};
@ -90,6 +87,9 @@ namespace Clipper2Lib
CLIPPER2_THROW(Clipper2Exception(undefined_error));
case range_error_i:
CLIPPER2_THROW(Clipper2Exception(range_error));
// Should never happen, but adding this to stop a compiler warning
default:
CLIPPER2_THROW(Clipper2Exception("Unknown error"));
}
#else
if(error_code) {}; // only to stop compiler 'parameter not used' warning
@ -109,6 +109,10 @@ namespace Clipper2Lib
//https://en.wikipedia.org/wiki/Nonzero-rule
enum class FillRule { EvenOdd, NonZero, Positive, Negative };
#ifdef USINGZ
using z_type = int64_t;
#endif
// Point ------------------------------------------------------------------------
template <typename T>
@ -116,10 +120,10 @@ namespace Clipper2Lib
T x;
T y;
#ifdef USINGZ
int64_t z;
z_type z;
template <typename T2>
inline void Init(const T2 x_ = 0, const T2 y_ = 0, const int64_t z_ = 0)
inline void Init(const T2 x_ = 0, const T2 y_ = 0, const z_type z_ = 0)
{
if constexpr (std::is_integral_v<T> &&
is_round_invocable<T2>::value && !std::is_integral_v<T2>)
@ -139,7 +143,7 @@ namespace Clipper2Lib
explicit Point() : x(0), y(0), z(0) {};
template <typename T2>
Point(const T2 x_, const T2 y_, const int64_t z_ = 0)
Point(const T2 x_, const T2 y_, const z_type z_ = 0)
{
Init(x_, y_);
z = z_;
@ -152,7 +156,7 @@ namespace Clipper2Lib
}
template <typename T2>
explicit Point(const Point<T2>& p, int64_t z_)
explicit Point(const Point<T2>& p, z_type z_)
{
Init(p.x, p.y, z_);
}
@ -162,7 +166,7 @@ namespace Clipper2Lib
return Point(x * scale, y * scale, z);
}
void SetZ(const int64_t z_value) { z = z_value; }
void SetZ(const z_type z_value) { z = z_value; }
friend std::ostream& operator<<(std::ostream& os, const Point& point)
{
@ -326,10 +330,10 @@ namespace Clipper2Lib
{
Path<T> result;
result.reserve(4);
result.push_back(Point<T>(left, top));
result.push_back(Point<T>(right, top));
result.push_back(Point<T>(right, bottom));
result.push_back(Point<T>(left, bottom));
result.emplace_back(left, top);
result.emplace_back(right, top);
result.emplace_back(right, bottom);
result.emplace_back(left, bottom);
return result;
}
@ -364,6 +368,22 @@ namespace Clipper2Lib
top == other.top && bottom == other.bottom;
}
Rect<T>& operator+=(const Rect<T>& other)
{
left = (std::min)(left, other.left);
top = (std::min)(top, other.top);
right = (std::max)(right, other.right);
bottom = (std::max)(bottom, other.bottom);
return *this;
}
Rect<T> operator+(const Rect<T>& other) const
{
Rect<T> result = *this;
result += other;
return result;
}
friend std::ostream& operator<<(std::ostream& os, const Rect<T>& rect) {
os << "(" << rect.left << "," << rect.top << "," << rect.right << "," << rect.bottom << ") ";
return os;
@ -597,13 +617,13 @@ namespace Clipper2Lib
result.reserve(path.size());
typename Path<T>::const_iterator path_iter = path.cbegin();
Point<T> first_pt = *path_iter++, last_pt = first_pt;
result.push_back(first_pt);
result.emplace_back(first_pt);
for (; path_iter != path.cend(); ++path_iter)
{
if (!NearEqual(*path_iter, last_pt, max_dist_sqrd))
{
last_pt = *path_iter;
result.push_back(last_pt);
result.emplace_back(last_pt);
}
}
if (!is_closed_path) return result;
@ -621,7 +641,7 @@ namespace Clipper2Lib
for (typename Paths<T>::const_iterator paths_citer = paths.cbegin();
paths_citer != paths.cend(); ++paths_citer)
{
result.push_back(StripNearEqual(*paths_citer, max_dist_sqrd, is_closed_path));
result.emplace_back(std::move(StripNearEqual(*paths_citer, max_dist_sqrd, is_closed_path)));
}
return result;
}
@ -697,11 +717,11 @@ namespace Clipper2Lib
{
// Work around LLVM issue: https://github.com/llvm/llvm-project/issues/16778
// Details: https://github.com/godotengine/godot/pull/95964#issuecomment-2306581804
//#if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
// const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
// const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
// return ab == cd;
//#else
// #if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
// const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
// const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
// return ab == cd;
// #else
// nb: unsigned values needed for calculating overflow carry
const auto abs_a = static_cast<uint64_t>(std::abs(a));
const auto abs_b = static_cast<uint64_t>(std::abs(b));
@ -768,7 +788,7 @@ namespace Clipper2Lib
const Point<T>& line1, const Point<T>& line2)
{
//perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
//see http://en.wikipedia.org/wiki/Perpendicular_distance
//see https://en.wikipedia.org/wiki/Perpendicular_distance
double a = static_cast<double>(pt.x - line1.x);
double b = static_cast<double>(pt.y - line1.y);
double c = static_cast<double>(line2.x - line1.x);

View file

@ -1,25 +1,19 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 5 July 2024 *
* Website : http://www.angusj.com *
* Date : 17 September 2024 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This is the main polygon clipping module *
* License : http://www.boost.org/LICENSE_1_0.txt *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_ENGINE_H
#define CLIPPER_ENGINE_H
#include <cstdlib>
#include <stdint.h> //#541
#include <iostream>
#include <queue>
#include <vector>
#include <functional>
#include <numeric>
#include <memory>
#include "clipper2/clipper.core.h"
#include <queue>
#include <functional>
#include <memory>
namespace Clipper2Lib {
@ -32,13 +26,13 @@ namespace Clipper2Lib {
struct HorzSegment;
//Note: all clipping operations except for Difference are commutative.
enum class ClipType { None, Intersection, Union, Difference, Xor };
enum class ClipType { NoClip, Intersection, Union, Difference, Xor };
enum class PathType { Subject, Clip };
enum class JoinWith { None, Left, Right };
enum class JoinWith { NoJoin, Left, Right };
enum class VertexFlags : uint32_t {
None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8
Empty = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8
};
constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b)
@ -55,7 +49,7 @@ namespace Clipper2Lib {
Point64 pt;
Vertex* next = nullptr;
Vertex* prev = nullptr;
VertexFlags flags = VertexFlags::None;
VertexFlags flags = VertexFlags::Empty;
};
struct OutPt {
@ -131,7 +125,7 @@ namespace Clipper2Lib {
Vertex* vertex_top = nullptr;
LocalMinima* local_min = nullptr; // the bottom of an edge 'bound' (also Vatti)
bool is_left_bound = false;
JoinWith join_with = JoinWith::None;
JoinWith join_with = JoinWith::NoJoin;
};
struct LocalMinima {
@ -167,7 +161,7 @@ namespace Clipper2Lib {
};
#ifdef USINGZ
typedef std::function<void(const Point64& e1bot, const Point64& e1top,
typedef std::function<void(const Point64& e1bot, const Point64& e1top,
const Point64& e2bot, const Point64& e2top, Point64& pt)> ZCallback64;
typedef std::function<void(const PointD& e1bot, const PointD& e1top,
@ -197,7 +191,7 @@ namespace Clipper2Lib {
class ClipperBase {
private:
ClipType cliptype_ = ClipType::None;
ClipType cliptype_ = ClipType::NoClip;
FillRule fillrule_ = FillRule::EvenOdd;
FillRule fillpos = FillRule::Positive;
int64_t bot_y_ = 0;
@ -210,7 +204,7 @@ namespace Clipper2Lib {
std::vector<Vertex*> vertex_lists_;
std::priority_queue<int64_t> scanline_list_;
IntersectNodeList intersect_nodes_;
HorzSegmentList horz_seg_list_;
HorzSegmentList horz_seg_list_;
std::vector<HorzJoin> horz_join_list_;
void Reset();
inline void InsertScanline(int64_t y);

View file

@ -1,16 +1,16 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 14 May 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Date : 24 January 2025 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 *
* Purpose : This module exports the Clipper2 Library (ie DLL/so) *
* License : http://www.boost.org/LICENSE_1_0.txt *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
/*
Boolean clipping:
cliptype: None=0, Intersection=1, Union=2, Difference=3, Xor=4
cliptype: NoClip=0, Intersection=1, Union=2, Difference=3, Xor=4
fillrule: EvenOdd=0, NonZero=1, Positive=2, Negative=3
Polygon offsetting (inflate/deflate):
@ -19,73 +19,104 @@
The path structures used extensively in other parts of this library are all
based on std::vector classes. Since C++ classes can't be accessed by other
languages, these paths are converted into very simple array data structures
(of either int64_t for CPath64 or double for CPathD) that can be parsed by
just about any programming language.
languages, these paths are exported here as very simple array structures
(either of int64_t or double) that can be parsed by just about any
programming language.
These 2D paths are defined by series of x and y coordinates together with an
optional user-defined 'z' value (see Z-values below). Hence, a vertex refers
to a single x and y coordinate (+/- a user-defined value). Data structures
have names with suffixes that indicate the array type (either int64_t or
double). For example, the data structure CPath64 contains an array of int64_t
values, whereas the data structure CPathD contains an array of double.
Where documentation omits the type suffix (eg CPath), it is referring to an
array whose data type could be either int64_t or double.
For conciseness, the following letters are used in the diagrams below:
N: Number of vertices in a given path
C: Count (ie number) of paths (or PolyPaths) in the structure
A: Number of elements in an array
CPath64 and CPathD:
These are arrays of consecutive x and y path coordinates preceeded by
a pair of values containing the path's length (N) and a 0 value.
__________________________________
|counter|coord1|coord2|...|coordN|
|N, 0 |x1, y1|x2, y2|...|xN, yN|
__________________________________
These are arrays of either int64_t or double values. Apart from
the first two elements, these arrays are a series of vertices
that together define a path. The very first element contains the
number of vertices (N) in the path, while second element should
contain a 0 value.
_______________________________________________________________
| counters | vertex1 | vertex2 | ... | vertexN |
| N, 0 | x1, y1, (z1) | x2, y2, (z2) | ... | xN, yN, (zN) |
---------------------------------------------------------------
CPaths64 and CPathsD:
These are also arrays containing any number of consecutive CPath64 or
CPathD structures. But preceeding these consecutive paths, there is pair of
values that contain the total length of the array structure (A) and the
number of CPath64 or CPathD it contains (C). The space these structures will
occupy in memory = A * sizeof(int64_t) or A * sizeof(double) respectively.
_______________________________
|counter|path1|path2|...|pathC|
|A , C | |
_______________________________
These are also arrays of either int64_t or double values that
contain any number of consecutive CPath structures. However,
preceding the first path is a pair of values. The first value
contains the length of the entire array structure (A), and the
second contains the number (ie count) of contained paths (C).
Memory allocation for CPaths64 = A * sizeof(int64_t)
Memory allocation for CPathsD = A * sizeof(double)
__________________________________________
| counters | path1 | path2 | ... | pathC |
| A, C | | | ... | |
------------------------------------------
CPolytree64 and CPolytreeD:
These are also arrays consisting of CPolyPath structures that represent
individual paths in a tree structure. However, the very first (ie top)
CPolyPath is just the tree container that doesn't have a path. And because
of that, its structure will be very slightly different from the remaining
CPolyPath. This difference will be discussed below.
The entire polytree structure is an array of int64_t or double. The
first element in the array indicates the array's total length (A).
The second element indicates the number (C) of CPolyPath structures
that are the TOP LEVEL CPolyPath in the polytree, and these top
level CPolyPath immediately follow these first two array elements.
These top level CPolyPath structures may, in turn, contain nested
CPolyPath children, and these collectively make a tree structure.
_________________________________________________________
| counters | CPolyPath1 | CPolyPath2 | ... | CPolyPathC |
| A, C | | | ... | |
---------------------------------------------------------
CPolyPath64 and CPolyPathD:
These are simple arrays consisting of a series of path coordinates followed
by any number of child (ie nested) CPolyPath. Preceeding these are two values
indicating the length of the path (N) and the number of child CPolyPath (C).
____________________________________________________________
|counter|coord1|coord2|...|coordN| child1|child2|...|childC|
|N , C |x1, y1|x2, y2|...|xN, yN| |
____________________________________________________________
These array structures consist of a pair of counter values followed by a
series of polygon vertices and a series of nested CPolyPath children.
The first counter values indicates the number of vertices in the
polygon (N), and the second counter indicates the CPolyPath child count (C).
_____________________________________________________________________________
|cntrs |vertex1 |vertex2 |...|vertexN |child1|child2|...|childC|
|N, C |x1, y1, (z1)| x2, y2, (z2)|...|xN, yN, (zN)| | |...| |
-----------------------------------------------------------------------------
As mentioned above, the very first CPolyPath structure is just a container
that owns (both directly and indirectly) every other CPolyPath in the tree.
Since this first CPolyPath has no path, instead of a path length, its very
first value will contain the total length of the CPolytree array (not its
total bytes length).
Again, all theses exported structures (CPaths64, CPathsD, CPolyTree64 &
CPolyTreeD) are arrays of either type int64_t or double, and the first
value in these arrays will always be the length of that array.
DisposeArray64 & DisposeArrayD:
All array structures are allocated in heap memory which will eventually
need to be released. However, since applications linking to these DLL
functions may use different memory managers, the only safe way to release
this memory is to use the exported DisposeArray functions.
(Optional) Z-Values:
Structures will only contain user-defined z-values when the USINGZ
pre-processor identifier is used. The library does not assign z-values
because this field is intended for users to assign custom values to vertices.
Z-values in input paths (subject and clip) will be copied to solution paths.
New vertices at path intersections will generate a callback event that allows
users to assign z-values at these new vertices. The user's callback function
must conform with the DLLZCallback definition and be registered with the
DLL via SetZCallback. To assist the user in assigning z-values, the library
passes in the callback function the new intersection point together with
the four vertices that define the two segments that are intersecting.
These array structures are allocated in heap memory which will eventually
need to be released. However, since applications dynamically linking to
these functions may use different memory managers, the only safe way to
free up this memory is to use the exported DisposeArray64 and
DisposeArrayD functions (see below).
*/
#ifndef CLIPPER2_EXPORT_H
#define CLIPPER2_EXPORT_H
#include <cstdlib>
#include <vector>
#include "clipper2/clipper.core.h"
#include "clipper2/clipper.engine.h"
#include "clipper2/clipper.offset.h"
#include "clipper2/clipper.rectclip.h"
#include <cstdlib>
namespace Clipper2Lib {
@ -127,6 +158,12 @@ inline Rect<T> CRectToRect(const CRect<T>& rect)
return result;
}
template <typename T1, typename T2>
inline T1 Reinterpret(T2 value) {
return *reinterpret_cast<T1*>(&value);
}
#ifdef _WIN32
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
#else
@ -178,11 +215,22 @@ EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths,
double delta, uint8_t jointype, uint8_t endtype,
double miter_limit = 2.0, double arc_tolerance = 0.0,
bool reverse_solution = false);
EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
double delta, uint8_t jointype, uint8_t endtype,
int precision = 2, double miter_limit = 2.0,
double arc_tolerance = 0.0, bool reverse_solution = false);
EXTERN_DLL_EXPORT CPaths64 InflatePath64(const CPath64 path,
double delta, uint8_t jointype, uint8_t endtype,
double miter_limit = 2.0, double arc_tolerance = 0.0,
bool reverse_solution = false);
EXTERN_DLL_EXPORT CPathsD InflatePathD(const CPathD path,
double delta, uint8_t jointype, uint8_t endtype,
int precision = 2, double miter_limit = 2.0,
double arc_tolerance = 0.0, bool reverse_solution = false);
// RectClip & RectClipLines:
EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect,
const CPaths64 paths);
@ -197,6 +245,15 @@ EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect,
// INTERNAL FUNCTIONS
//////////////////////////////////////////////////////
#ifdef USINGZ
ZCallback64 dllCallback64 = nullptr;
ZCallbackD dllCallbackD = nullptr;
constexpr int EXPORT_VERTEX_DIMENSIONALITY = 3;
#else
constexpr int EXPORT_VERTEX_DIMENSIONALITY = 2;
#endif
template <typename T>
static void GetPathCountAndCPathsArrayLen(const Paths<T>& paths,
size_t& cnt, size_t& array_len)
@ -206,30 +263,47 @@ static void GetPathCountAndCPathsArrayLen(const Paths<T>& paths,
for (const Path<T>& path : paths)
if (path.size())
{
array_len += path.size() * 2 + 2;
array_len += path.size() * EXPORT_VERTEX_DIMENSIONALITY + 2;
++cnt;
}
}
static size_t GetPolyPath64ArrayLen(const PolyPath64& pp)
static size_t GetPolyPathArrayLen64(const PolyPath64& pp)
{
size_t result = 2; // poly_length + child_count
result += pp.Polygon().size() * 2;
result += pp.Polygon().size() * EXPORT_VERTEX_DIMENSIONALITY;
//plus nested children :)
for (size_t i = 0; i < pp.Count(); ++i)
result += GetPolyPath64ArrayLen(*pp[i]);
result += GetPolyPathArrayLen64(*pp[i]);
return result;
}
static void GetPolytreeCountAndCStorageSize(const PolyTree64& tree,
static size_t GetPolyPathArrayLenD(const PolyPathD& pp)
{
size_t result = 2; // poly_length + child_count
result += pp.Polygon().size() * EXPORT_VERTEX_DIMENSIONALITY;
//plus nested children :)
for (size_t i = 0; i < pp.Count(); ++i)
result += GetPolyPathArrayLenD(*pp[i]);
return result;
}
static void GetPolytreeCountAndCStorageSize64(const PolyTree64& tree,
size_t& cnt, size_t& array_len)
{
cnt = tree.Count(); // nb: top level count only
array_len = GetPolyPath64ArrayLen(tree);
array_len = GetPolyPathArrayLen64(tree);
}
static void GetPolytreeCountAndCStorageSizeD(const PolyTreeD& tree,
size_t& cnt, size_t& array_len)
{
cnt = tree.Count(); // nb: top level count only
array_len = GetPolyPathArrayLenD(tree);
}
template <typename T>
static T* CreateCPaths(const Paths<T>& paths)
static T* CreateCPathsFromPathsT(const Paths<T>& paths)
{
size_t cnt = 0, array_len = 0;
GetPathCountAndCPathsArrayLen(paths, cnt, array_len);
@ -245,11 +319,38 @@ static T* CreateCPaths(const Paths<T>& paths)
{
*v++ = pt.x;
*v++ = pt.y;
#ifdef USINGZ
*v++ = Reinterpret<T>(pt.z);
#endif
}
}
return result;
}
CPathsD CreateCPathsDFromPathsD(const PathsD& paths)
{
if (!paths.size()) return nullptr;
size_t cnt, array_len;
GetPathCountAndCPathsArrayLen(paths, cnt, array_len);
CPathsD result = new double[array_len], v = result;
*v++ = (double)array_len;
*v++ = (double)cnt;
for (const PathD& path : paths)
{
if (!path.size()) continue;
*v = (double)path.size();
++v; *v++ = 0;
for (const PointD& pt : path)
{
*v++ = pt.x;
*v++ = pt.y;
#ifdef USINGZ
* v++ = Reinterpret<double>(pt.z);
#endif
}
}
return result;
}
CPathsD CreateCPathsDFromPaths64(const Paths64& paths, double scale)
{
@ -268,13 +369,16 @@ CPathsD CreateCPathsDFromPaths64(const Paths64& paths, double scale)
{
*v++ = pt.x * scale;
*v++ = pt.y * scale;
#ifdef USINGZ
*v++ = Reinterpret<double>(pt.z);
#endif
}
}
return result;
}
template <typename T>
static Path<T> ConvertCPath(T* path)
static Path<T> ConvertCPathToPathT(T* path)
{
Path<T> result;
if (!path) return result;
@ -284,14 +388,19 @@ static Path<T> ConvertCPath(T* path)
result.reserve(cnt);
for (size_t j = 0; j < cnt; ++j)
{
T x = *v++, y = *v++;
result.push_back(Point<T>(x, y));
T x = *v++, y = *v++;
#ifdef USINGZ
z_type z = Reinterpret<z_type>(*v++);
result.emplace_back(x, y, z);
#else
result.emplace_back(x, y);
#endif
}
return result;
}
template <typename T>
static Paths<T> ConvertCPaths(T* paths)
static Paths<T> ConvertCPathsToPathsT(T* paths)
{
Paths<T> result;
if (!paths) return result;
@ -301,19 +410,45 @@ static Paths<T> ConvertCPaths(T* paths)
for (size_t i = 0; i < cnt; ++i)
{
size_t cnt2 = static_cast<size_t>(*v);
v += 2;
v += 2;
Path<T> path;
path.reserve(cnt2);
for (size_t j = 0; j < cnt2; ++j)
{
T x = *v++, y = *v++;
path.push_back(Point<T>(x, y));
#ifdef USINGZ
z_type z = Reinterpret<z_type>(*v++);
path.emplace_back(x, y, z);
#else
path.emplace_back(x, y);
#endif
}
result.push_back(path);
result.emplace_back(std::move(path));
}
return result;
}
static Path64 ConvertCPathDToPath64WithScale(const CPathD path, double scale)
{
Path64 result;
if (!path) return result;
double* v = path;
size_t cnt = static_cast<size_t>(*v);
v += 2; // skip 0 value
result.reserve(cnt);
for (size_t j = 0; j < cnt; ++j)
{
double x = *v++ * scale;
double y = *v++ * scale;
#ifdef USINGZ
z_type z = Reinterpret<z_type>(*v++);
result.emplace_back(x, y, z);
#else
result.emplace_back(x, y);
#endif
}
return result;
}
static Paths64 ConvertCPathsDToPaths64(const CPathsD paths, double scale)
{
@ -333,42 +468,78 @@ static Paths64 ConvertCPathsDToPaths64(const CPathsD paths, double scale)
{
double x = *v++ * scale;
double y = *v++ * scale;
path.push_back(Point64(x, y));
#ifdef USINGZ
z_type z = Reinterpret<z_type>(*v++);
path.emplace_back(x, y, z);
#else
path.emplace_back(x, y);
#endif
}
result.push_back(path);
result.emplace_back(std::move(path));
}
return result;
}
template <typename T>
static void CreateCPolyPath(const PolyPath64* pp, T*& v, T scale)
static void CreateCPolyPath64(const PolyPath64* pp, int64_t*& v)
{
*v++ = static_cast<T>(pp->Polygon().size());
*v++ = static_cast<T>(pp->Count());
*v++ = static_cast<int64_t>(pp->Polygon().size());
*v++ = static_cast<int64_t>(pp->Count());
for (const Point64& pt : pp->Polygon())
{
*v++ = static_cast<T>(pt.x * scale);
*v++ = static_cast<T>(pt.y * scale);
*v++ = pt.x;
*v++ = pt.y;
#ifdef USINGZ
* v++ = Reinterpret<int64_t>(pt.z); // raw memory copy
#endif
}
for (size_t i = 0; i < pp->Count(); ++i)
CreateCPolyPath(pp->Child(i), v, scale);
CreateCPolyPath64(pp->Child(i), v);
}
template <typename T>
static T* CreateCPolyTree(const PolyTree64& tree, T scale)
static void CreateCPolyPathD(const PolyPathD* pp, double*& v)
{
*v++ = static_cast<double>(pp->Polygon().size());
*v++ = static_cast<double>(pp->Count());
for (const PointD& pt : pp->Polygon())
{
*v++ = pt.x;
*v++ = pt.y;
#ifdef USINGZ
* v++ = Reinterpret<double>(pt.z); // raw memory copy
#endif
}
for (size_t i = 0; i < pp->Count(); ++i)
CreateCPolyPathD(pp->Child(i), v);
}
static int64_t* CreateCPolyTree64(const PolyTree64& tree)
{
if (scale == 0) scale = 1;
size_t cnt, array_len;
GetPolytreeCountAndCStorageSize(tree, cnt, array_len);
GetPolytreeCountAndCStorageSize64(tree, cnt, array_len);
if (!cnt) return nullptr;
// allocate storage
T* result = new T[array_len];
T* v = result;
*v++ = static_cast<T>(array_len);
*v++ = static_cast<T>(tree.Count());
int64_t* result = new int64_t[array_len];
int64_t* v = result;
*v++ = static_cast<int64_t>(array_len);
*v++ = static_cast<int64_t>(tree.Count());
for (size_t i = 0; i < tree.Count(); ++i)
CreateCPolyPath(tree.Child(i), v, scale);
CreateCPolyPath64(tree.Child(i), v);
return result;
}
static double* CreateCPolyTreeD(const PolyTreeD& tree)
{
double scale = std::log10(tree.Scale());
size_t cnt, array_len;
GetPolytreeCountAndCStorageSizeD(tree, cnt, array_len);
if (!cnt) return nullptr;
// allocate storage
double* result = new double[array_len];
double* v = result;
*v++ = static_cast<double>(array_len);
*v++ = static_cast<double>(tree.Count());
for (size_t i = 0; i < tree.Count(); ++i)
CreateCPolyPathD(tree.Child(i), v);
return result;
}
@ -391,20 +562,24 @@ EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
Paths64 sub, sub_open, clp, sol, sol_open;
sub = ConvertCPaths(subjects);
sub_open = ConvertCPaths(subjects_open);
clp = ConvertCPaths(clips);
sub = ConvertCPathsToPathsT(subjects);
sub_open = ConvertCPathsToPathsT(subjects_open);
clp = ConvertCPathsToPathsT(clips);
Clipper64 clipper;
clipper.PreserveCollinear(preserve_collinear);
clipper.ReverseSolution(reverse_solution);
#ifdef USINGZ
if (dllCallback64)
clipper.SetZCallback(dllCallback64);
#endif
if (sub.size() > 0) clipper.AddSubject(sub);
if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
if (clp.size() > 0) clipper.AddClip(clp);
if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open))
return -1; // clipping bug - should never happen :)
solution = CreateCPaths(sol);
solution_open = CreateCPaths(sol_open);
solution = CreateCPathsFromPathsT(sol);
solution_open = CreateCPathsFromPathsT(sol_open);
return 0; //success !!
}
@ -417,22 +592,26 @@ EXTERN_DLL_EXPORT int BooleanOp_PolyTree64(uint8_t cliptype,
if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
Paths64 sub, sub_open, clp, sol_open;
sub = ConvertCPaths(subjects);
sub_open = ConvertCPaths(subjects_open);
clp = ConvertCPaths(clips);
sub = ConvertCPathsToPathsT(subjects);
sub_open = ConvertCPathsToPathsT(subjects_open);
clp = ConvertCPathsToPathsT(clips);
PolyTree64 tree;
Clipper64 clipper;
clipper.PreserveCollinear(preserve_collinear);
clipper.ReverseSolution(reverse_solution);
#ifdef USINGZ
if (dllCallback64)
clipper.SetZCallback(dllCallback64);
#endif
if (sub.size() > 0) clipper.AddSubject(sub);
if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
if (clp.size() > 0) clipper.AddClip(clp);
if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), tree, sol_open))
return -1; // clipping bug - should never happen :)
sol_tree = CreateCPolyTree(tree, (int64_t)1);
solution_open = CreateCPaths(sol_open);
sol_tree = CreateCPolyTree64(tree);
solution_open = CreateCPathsFromPathsT(sol_open);
return 0; //success !!
}
@ -445,23 +624,27 @@ EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype,
if (precision < -8 || precision > 8) return -5;
if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
const double scale = std::pow(10, precision);
//const double scale = std::pow(10, precision);
Paths64 sub, sub_open, clp, sol, sol_open;
sub = ConvertCPathsDToPaths64(subjects, scale);
sub_open = ConvertCPathsDToPaths64(subjects_open, scale);
clp = ConvertCPathsDToPaths64(clips, scale);
PathsD sub, sub_open, clp, sol, sol_open;
sub = ConvertCPathsToPathsT(subjects);
sub_open = ConvertCPathsToPathsT(subjects_open);
clp = ConvertCPathsToPathsT(clips);
Clipper64 clipper;
ClipperD clipper(precision);
clipper.PreserveCollinear(preserve_collinear);
clipper.ReverseSolution(reverse_solution);
#ifdef USINGZ
if (dllCallbackD)
clipper.SetZCallback(dllCallbackD);
#endif
if (sub.size() > 0) clipper.AddSubject(sub);
if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
if (clp.size() > 0) clipper.AddClip(clp);
if (!clipper.Execute(ClipType(cliptype),
FillRule(fillrule), sol, sol_open)) return -1;
solution = CreateCPathsDFromPaths64(sol, 1 / scale);
solution_open = CreateCPathsDFromPaths64(sol_open, 1 / scale);
solution = CreateCPathsDFromPathsD(sol);
solution_open = CreateCPathsDFromPathsD(sol_open);
return 0;
}
@ -474,27 +657,30 @@ EXTERN_DLL_EXPORT int BooleanOp_PolyTreeD(uint8_t cliptype,
if (precision < -8 || precision > 8) return -5;
if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
double scale = std::pow(10, precision);
//double scale = std::pow(10, precision);
int err = 0;
Paths64 sub, sub_open, clp, sol_open;
sub = ConvertCPathsDToPaths64(subjects, scale);
sub_open = ConvertCPathsDToPaths64(subjects_open, scale);
clp = ConvertCPathsDToPaths64(clips, scale);
PathsD sub, sub_open, clp, sol_open;
sub = ConvertCPathsToPathsT(subjects);
sub_open = ConvertCPathsToPathsT(subjects_open);
clp = ConvertCPathsToPathsT(clips);
PolyTree64 tree;
Clipper64 clipper;
PolyTreeD tree;
ClipperD clipper(precision);
clipper.PreserveCollinear(preserve_collinear);
clipper.ReverseSolution(reverse_solution);
#ifdef USINGZ
if (dllCallbackD)
clipper.SetZCallback(dllCallbackD);
#endif
if (sub.size() > 0) clipper.AddSubject(sub);
if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
if (clp.size() > 0) clipper.AddClip(clp);
if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), tree, sol_open))
return -1; // clipping bug - should never happen :)
solution = CreateCPolyTree(tree, 1/scale);
solution_open = CreateCPathsDFromPaths64(sol_open, 1 / scale);
solution = CreateCPolyTreeD(tree);
solution_open = CreateCPathsDFromPathsD(sol_open);
return 0; //success !!
}
@ -503,13 +689,13 @@ EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths,
double arc_tolerance, bool reverse_solution)
{
Paths64 pp;
pp = ConvertCPaths(paths);
pp = ConvertCPathsToPathsT(paths);
ClipperOffset clip_offset( miter_limit,
arc_tolerance, reverse_solution);
clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype));
Paths64 result;
clip_offset.Execute(delta, result);
return CreateCPaths(result);
return CreateCPathsFromPathsT(result);
}
EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
@ -525,18 +711,49 @@ EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype));
Paths64 result;
clip_offset.Execute(delta * scale, result);
return CreateCPathsDFromPaths64(result, 1 / scale);
}
EXTERN_DLL_EXPORT CPaths64 InflatePath64(const CPath64 path,
double delta, uint8_t jointype, uint8_t endtype, double miter_limit,
double arc_tolerance, bool reverse_solution)
{
Path64 pp;
pp = ConvertCPathToPathT(path);
ClipperOffset clip_offset(miter_limit,
arc_tolerance, reverse_solution);
clip_offset.AddPath(pp, JoinType(jointype), EndType(endtype));
Paths64 result;
clip_offset.Execute(delta, result);
return CreateCPathsFromPathsT(result);
}
EXTERN_DLL_EXPORT CPathsD InflatePathD(const CPathD path,
double delta, uint8_t jointype, uint8_t endtype,
int precision, double miter_limit,
double arc_tolerance, bool reverse_solution)
{
if (precision < -8 || precision > 8 || !path) return nullptr;
const double scale = std::pow(10, precision);
ClipperOffset clip_offset(miter_limit, arc_tolerance, reverse_solution);
Path64 pp = ConvertCPathDToPath64WithScale(path, scale);
clip_offset.AddPath(pp, JoinType(jointype), EndType(endtype));
Paths64 result;
clip_offset.Execute(delta * scale, result);
return CreateCPathsDFromPaths64(result, 1 / scale);
}
EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect, const CPaths64 paths)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
Rect64 r64 = CRectToRect(rect);
class RectClip64 rc(r64);
Paths64 pp = ConvertCPaths(paths);
Paths64 pp = ConvertCPathsToPathsT(paths);
Paths64 result = rc.Execute(pp);
return CreateCPaths(result);
return CreateCPathsFromPathsT(result);
}
EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect, const CPathsD paths, int precision)
@ -560,9 +777,9 @@ EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect,
if (CRectIsEmpty(rect) || !paths) return nullptr;
Rect64 r = CRectToRect(rect);
class RectClipLines64 rcl (r);
Paths64 pp = ConvertCPaths(paths);
Paths64 pp = ConvertCPathsToPathsT(paths);
Paths64 result = rcl.Execute(pp);
return CreateCPaths(result);
return CreateCPathsFromPathsT(result);
}
EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect,
@ -581,20 +798,35 @@ EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect,
EXTERN_DLL_EXPORT CPaths64 MinkowskiSum64(const CPath64& cpattern, const CPath64& cpath, bool is_closed)
{
Path64 path = ConvertCPath(cpath);
Path64 pattern = ConvertCPath(cpattern);
Path64 path = ConvertCPathToPathT(cpath);
Path64 pattern = ConvertCPathToPathT(cpattern);
Paths64 solution = MinkowskiSum(pattern, path, is_closed);
return CreateCPaths(solution);
return CreateCPathsFromPathsT(solution);
}
EXTERN_DLL_EXPORT CPaths64 MinkowskiDiff64(const CPath64& cpattern, const CPath64& cpath, bool is_closed)
{
Path64 path = ConvertCPath(cpath);
Path64 pattern = ConvertCPath(cpattern);
Path64 path = ConvertCPathToPathT(cpath);
Path64 pattern = ConvertCPathToPathT(cpattern);
Paths64 solution = MinkowskiDiff(pattern, path, is_closed);
return CreateCPaths(solution);
return CreateCPathsFromPathsT(solution);
}
} // end Clipper2Lib namespace
#ifdef USINGZ
typedef void (*DLLZCallback64)(const Point64& e1bot, const Point64& e1top, const Point64& e2bot, const Point64& e2top, Point64& pt);
typedef void (*DLLZCallbackD)(const PointD& e1bot, const PointD& e1top, const PointD& e2bot, const PointD& e2top, PointD& pt);
EXTERN_DLL_EXPORT void SetZCallback64(DLLZCallback64 callback)
{
dllCallback64 = callback;
}
EXTERN_DLL_EXPORT void SetZCallbackD(DLLZCallbackD callback)
{
dllCallbackD = callback;
}
#endif
}
#endif // CLIPPER2_EXPORT_H

View file

@ -1,24 +1,21 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 27 April 2024 *
* Website : http://www.angusj.com *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This module provides a simple interface to the Clipper Library *
* License : http://www.boost.org/LICENSE_1_0.txt *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_H
#define CLIPPER_H
#include <cstdlib>
#include <type_traits>
#include <vector>
#include "clipper2/clipper.core.h"
#include "clipper2/clipper.engine.h"
#include "clipper2/clipper.offset.h"
#include "clipper2/clipper.minkowski.h"
#include "clipper2/clipper.rectclip.h"
#include <type_traits>
namespace Clipper2Lib {
@ -272,14 +269,14 @@ namespace Clipper2Lib {
inline void PolyPathToPaths64(const PolyPath64& polypath, Paths64& paths)
{
paths.push_back(polypath.Polygon());
paths.emplace_back(polypath.Polygon());
for (const auto& child : polypath)
PolyPathToPaths64(*child, paths);
}
inline void PolyPathToPathsD(const PolyPathD& polypath, PathsD& paths)
{
paths.push_back(polypath.Polygon());
paths.emplace_back(polypath.Polygon());
for (const auto& child : polypath)
PolyPathToPathsD(*child, paths);
}
@ -348,9 +345,9 @@ namespace Clipper2Lib {
result.reserve(array_size / 2);
for (size_t i = 0; i < array_size; i +=2)
#ifdef USINGZ
result.push_back( U{ an_array[i], an_array[i + 1], 0} );
result.emplace_back( an_array[i], an_array[i + 1], 0 );
#else
result.push_back( U{ an_array[i], an_array[i + 1]} );
result.emplace_back( an_array[i], an_array[i + 1] );
#endif
}
@ -518,20 +515,20 @@ namespace Clipper2Lib {
}
prevIt = srcIt++;
dst.push_back(*prevIt);
dst.emplace_back(*prevIt);
for (; srcIt != stop; ++srcIt)
{
if (!IsCollinear(*prevIt, *srcIt, *(srcIt + 1)))
{
prevIt = srcIt;
dst.push_back(*prevIt);
dst.emplace_back(*prevIt);
}
}
if (is_open_path)
dst.push_back(*srcIt);
dst.emplace_back(*srcIt);
else if (!IsCollinear(*prevIt, *stop, dst[0]))
dst.push_back(*stop);
dst.emplace_back(*stop);
else
{
while (dst.size() > 2 &&
@ -603,10 +600,10 @@ namespace Clipper2Lib {
double dx = co, dy = si;
Path<T> result;
result.reserve(steps);
result.push_back(Point<T>(center.x + radiusX, static_cast<double>(center.y)));
result.emplace_back(center.x + radiusX, static_cast<double>(center.y));
for (size_t i = 1; i < steps; ++i)
{
result.push_back(Point<T>(center.x + radiusX * dx, center.y + radiusY * dy));
result.emplace_back(center.x + radiusX * dx, center.y + radiusY * dy);
double x = dx * co - dy * si;
dy = dy * co + dx * si;
dx = x;
@ -700,7 +697,7 @@ namespace Clipper2Lib {
Path<T> result;
result.reserve(len);
for (typename Path<T>::size_type i = 0; i < len; ++i)
if (!flags[i]) result.push_back(path[i]);
if (!flags[i]) result.emplace_back(path[i]);
return result;
}
@ -711,7 +708,7 @@ namespace Clipper2Lib {
Paths<T> result;
result.reserve(paths.size());
for (const auto& path : paths)
result.push_back(SimplifyPath(path, epsilon, isClosedPath));
result.emplace_back(std::move(SimplifyPath(path, epsilon, isClosedPath)));
return result;
}
@ -749,7 +746,7 @@ namespace Clipper2Lib {
result.reserve(len);
for (typename Path<T>::size_type i = 0; i < len; ++i)
if (flags[i])
result.push_back(path[i]);
result.emplace_back(path[i]);
return result;
}

View file

@ -1,18 +1,15 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 1 November 2023 *
* Website : http://www.angusj.com *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : Minkowski Sum and Difference *
* License : http://www.boost.org/LICENSE_1_0.txt *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_MINKOWSKI_H
#define CLIPPER_MINKOWSKI_H
#include <cstdlib>
#include <vector>
#include <string>
#include "clipper2/clipper.core.h"
namespace Clipper2Lib
@ -35,7 +32,7 @@ namespace Clipper2Lib
Path64 path2(pattern.size());
std::transform(pattern.cbegin(), pattern.cend(),
path2.begin(), [p](const Point64& pt2) {return p + pt2; });
tmp.push_back(path2);
tmp.emplace_back(std::move(path2));
}
}
else
@ -45,7 +42,7 @@ namespace Clipper2Lib
Path64 path2(pattern.size());
std::transform(pattern.cbegin(), pattern.cend(),
path2.begin(), [p](const Point64& pt2) {return p - pt2; });
tmp.push_back(path2);
tmp.emplace_back(std::move(path2));
}
}
@ -59,14 +56,14 @@ namespace Clipper2Lib
Path64 quad;
quad.reserve(4);
{
quad.push_back(tmp[g][h]);
quad.push_back(tmp[i][h]);
quad.push_back(tmp[i][j]);
quad.push_back(tmp[g][j]);
quad.emplace_back(tmp[g][h]);
quad.emplace_back(tmp[i][h]);
quad.emplace_back(tmp[i][j]);
quad.emplace_back(tmp[g][j]);
};
if (!IsPositive(quad))
std::reverse(quad.begin(), quad.end());
result.push_back(quad);
result.emplace_back(std::move(quad));
h = j;
}
g = i;

View file

@ -1,10 +1,10 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 24 March 2024 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Date : 22 January 2025 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 *
* Purpose : Path Offset (Inflate/Shrink) *
* License : http://www.boost.org/LICENSE_1_0.txt *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_OFFSET_H_
@ -12,6 +12,7 @@
#include "clipper.core.h"
#include "clipper.engine.h"
#include <optional>
namespace Clipper2Lib {
@ -96,7 +97,7 @@ public:
void AddPaths(const Paths64& paths, JoinType jt_, EndType et_);
void Clear() { groups_.clear(); norms.clear(); };
void Execute(double delta, Paths64& paths);
void Execute(double delta, Paths64& sols_64);
void Execute(double delta, PolyTree64& polytree);
void Execute(DeltaCallback64 delta_cb, Paths64& paths);

View file

@ -1,19 +1,17 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 5 July 2024 *
* Website : http://www.angusj.com *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : FAST rectangular clipping *
* License : http://www.boost.org/LICENSE_1_0.txt *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_RECTCLIP_H
#define CLIPPER_RECTCLIP_H
#include <cstdlib>
#include <vector>
#include <queue>
#include "clipper2/clipper.core.h"
#include <queue>
namespace Clipper2Lib
{

View file

@ -1,6 +1,6 @@
#ifndef CLIPPER_VERSION_H
#define CLIPPER_VERSION_H
constexpr auto CLIPPER2_VERSION = "1.4.0";
constexpr auto CLIPPER2_VERSION = "1.5.2";
#endif // CLIPPER_VERSION_H