axmol/thirdparty/clipper2/clipper.export.h

754 lines
21 KiB
C++

/*******************************************************************************
* Author : Angus Johnson *
* Date : 28 October 2022 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2022 *
* Purpose : This module exports the Clipper2 Library (ie DLL/so) *
* License : http://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
// The exported functions below refer to simple structures that
// can be understood across multiple languages. Consequently
// Path64, PathD, Polytree64 etc are converted from classes
// (std::vector<> etc) into the following data structures:
//
// CPath64 (int64_t*) & CPathD (double_t*):
// Path64 and PathD are converted into arrays of x,y coordinates.
// However in these arrays the first x,y coordinate pair is a
// counter with 'x' containing the number of following coordinate
// pairs. ('y' must always be 0.)
//__________________________________
// |counter|coord1|coord2|...|coordN|
// |N ,0 |x1, y1|x2, y2|...|xN, yN|
// __________________________________
//
// CPaths64 (int64_t**) & CPathsD (double_t**):
// These are arrays of pointers to CPath64 and CPathD where
// the first pointer is to a 'counter path'. This 'counter
// path' has a single x,y coord pair where 'y' contains
// the number of paths that follow (and with 'x' always 0).
// _______________________________
// |counter|path1|path2|...|pathN|
// |addr0 |addr1|addr2|...|addrN| (*addr0[0]=0; *addr0[1]=N)
// _______________________________
//
// The structures of CPolytree64 and CPolytreeD are defined
// below and they don't need to be repeated or explained here.
//
// Finally, the pointer structures created and exported through
// these functions can't safely be destroyed externally, so
// a number of 'dispose functions are also exported.
#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"
namespace Clipper2Lib {
typedef int64_t* CPath64;
typedef int64_t** CPaths64;
typedef double* CPathD;
typedef double** CPathsD;
typedef struct CPolyPath64 {
CPath64 polygon;
uint32_t is_hole;
uint32_t child_count;
CPolyPath64* childs;
}
CPolyTree64;
typedef struct CPolyPathD {
CPathD polygon;
uint32_t is_hole;
uint32_t child_count;
CPolyPathD* childs;
}
CPolyTreeD;
template <typename T>
struct CRect {
T left;
T top;
T right;
T bottom;
};
typedef CRect<int64_t> CRect64;
typedef CRect<double> CRectD;
template <typename T>
inline bool CRectIsEmpty(const CRect<T>& rect)
{
return (rect.right <= rect.left) || (rect.bottom <= rect.top);
}
template <typename T>
inline Rect<T> CRectToRect(const CRect<T>& rect)
{
Rect<T> result;
result.left = rect.left;
result.top = rect.top;
result.right = rect.right;
result.bottom = rect.bottom;
return result;
}
inline CPath64 CreateCPath64(size_t cnt1, size_t cnt2);
inline CPath64 CreateCPath64(const Path64& p);
inline CPaths64 CreateCPaths64(const Paths64& pp);
inline Path64 ConvertCPath64(const CPath64& p);
inline Paths64 ConvertCPaths64(const CPaths64& pp);
inline CPathD CreateCPathD(size_t cnt1, size_t cnt2);
inline CPathD CreateCPathD(const PathD& p);
inline CPathsD CreateCPathsD(const PathsD& pp);
inline PathD ConvertCPathD(const CPathD& p);
inline PathsD ConvertCPathsD(const CPathsD& pp);
// the following function avoid multiple conversions
inline Path64 ConvertCPathD(const CPathD& p, double scale);
inline Paths64 ConvertCPathsD(const CPathsD& pp, double scale);
inline CPathD CreateCPathD(const Path64& p, double scale);
inline CPathsD CreateCPathsD(const Paths64& pp, double scale);
inline CPolyTree64* CreateCPolyTree64(const PolyTree64& pt);
inline CPolyTreeD* CreateCPolyTreeD(const PolyTree64& pt, double scale);
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
EXTERN_DLL_EXPORT const char* Version()
{
return CLIPPER2_VERSION;
}
EXTERN_DLL_EXPORT void DisposeExportedCPath64(CPath64 p)
{
if (p) delete[] p;
}
EXTERN_DLL_EXPORT void DisposeExportedCPaths64(CPaths64& pp)
{
if (!pp) return;
CPaths64 v = pp;
CPath64 cnts = *v;
const size_t cnt = static_cast<size_t>(cnts[1]);
for (size_t i = 0; i <= cnt; ++i) //nb: cnt +1
DisposeExportedCPath64(*v++);
delete[] pp;
pp = nullptr;
}
EXTERN_DLL_EXPORT void DisposeExportedCPathD(CPathD p)
{
if (p) delete[] p;
}
EXTERN_DLL_EXPORT void DisposeExportedCPathsD(CPathsD& pp)
{
if (!pp) return;
CPathsD v = pp;
CPathD cnts = *v;
size_t cnt = static_cast<size_t>(cnts[1]);
for (size_t i = 0; i <= cnt; ++i) //nb: cnt +1
DisposeExportedCPathD(*v++);
delete[] pp;
pp = nullptr;
}
EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
uint8_t fillrule, const CPaths64 subjects,
const CPaths64 subjects_open, const CPaths64 clips,
CPaths64& solution, CPaths64& solution_open,
bool preserve_collinear = true, bool reverse_solution = false)
{
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, sol_open;
sub = ConvertCPaths64(subjects);
sub_open = ConvertCPaths64(subjects_open);
clp = ConvertCPaths64(clips);
Clipper64 clipper;
clipper.PreserveCollinear = preserve_collinear;
clipper.ReverseSolution = reverse_solution;
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 = CreateCPaths64(sol);
solution_open = CreateCPaths64(sol_open);
return 0; //success !!
}
EXTERN_DLL_EXPORT int BooleanOpPt64(uint8_t cliptype,
uint8_t fillrule, const CPaths64 subjects,
const CPaths64 subjects_open, const CPaths64 clips,
CPolyTree64*& solution, CPaths64& solution_open,
bool preserve_collinear = true, bool reverse_solution = false)
{
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 = ConvertCPaths64(subjects);
sub_open = ConvertCPaths64(subjects_open);
clp = ConvertCPaths64(clips);
PolyTree64 pt;
Clipper64 clipper;
clipper.PreserveCollinear = preserve_collinear;
clipper.ReverseSolution = reverse_solution;
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), pt, sol_open))
return -1; // clipping bug - should never happen :)
solution = CreateCPolyTree64(pt);
solution_open = CreateCPaths64(sol_open);
return 0; //success !!
}
EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype,
uint8_t fillrule, const CPathsD subjects,
const CPathsD subjects_open, const CPathsD clips,
CPathsD& solution, CPathsD& solution_open, int precision = 2,
bool preserve_collinear = true, bool reverse_solution = false)
{
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);
Paths64 sub, sub_open, clp, sol, sol_open;
sub = ConvertCPathsD(subjects, scale);
sub_open = ConvertCPathsD(subjects_open, scale);
clp = ConvertCPathsD(clips, scale);
Clipper64 clipper;
clipper.PreserveCollinear = preserve_collinear;
clipper.ReverseSolution = reverse_solution;
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;
if (sol.size() > 0) solution = CreateCPathsD(sol, 1 / scale);
if (sol_open.size() > 0)
solution_open = CreateCPathsD(sol_open, 1 / scale);
return 0;
}
EXTERN_DLL_EXPORT int BooleanOpPtD(uint8_t cliptype,
uint8_t fillrule, const CPathsD subjects,
const CPathsD subjects_open, const CPathsD clips,
CPolyTreeD*& solution, CPathsD& solution_open, int precision = 2,
bool preserve_collinear = true, bool reverse_solution = false)
{
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);
Paths64 sub, sub_open, clp, sol_open;
sub = ConvertCPathsD(subjects, scale);
sub_open = ConvertCPathsD(subjects_open, scale);
clp = ConvertCPathsD(clips, scale);
PolyTree64 sol;
Clipper64 clipper;
clipper.PreserveCollinear = preserve_collinear;
clipper.ReverseSolution = reverse_solution;
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 = CreateCPolyTreeD(sol, 1 / scale);
if (sol_open.size() > 0)
solution_open = CreateCPathsD(sol_open, 1 / scale);
return 0;
}
EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths,
double delta, uint8_t jt, uint8_t et, double miter_limit = 2.0,
double arc_tolerance = 0.0, bool reverse_solution = false)
{
Paths64 pp;
pp = ConvertCPaths64(paths);
ClipperOffset clip_offset( miter_limit,
arc_tolerance, reverse_solution);
clip_offset.AddPaths(pp, JoinType(jt), EndType(et));
Paths64 result = clip_offset.Execute(delta);
return CreateCPaths64(result);
}
EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
double delta, uint8_t jt, uint8_t et,
double precision = 2, double miter_limit = 2.0,
double arc_tolerance = 0.0, bool reverse_solution = false)
{
if (precision < -8 || precision > 8 || !paths) return nullptr;
const double scale = std::pow(10, precision);
ClipperOffset clip_offset(miter_limit, arc_tolerance, reverse_solution);
Paths64 pp = ConvertCPathsD(paths, scale);
clip_offset.AddPaths(pp, JoinType(jt), EndType(et));
Paths64 result = clip_offset.Execute(delta * scale);
return CreateCPathsD(result, 1/scale);
}
EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect,
const CPaths64 paths)
{
log(rect.left);
log(rect.right);
if (CRectIsEmpty(rect) || !paths) return nullptr;
Rect64 r64 = CRectToRect(rect);
class RectClip rc(r64);
Paths64 pp = ConvertCPaths64(paths);
Paths64 result;
result.reserve(pp.size());
for (const Path64& p : pp)
{
Rect64 pathRec = Bounds(p);
if (!r64.Intersects(pathRec)) continue;
if (r64.Contains(pathRec))
result.push_back(p);
else
{
Path64 p2 = rc.Execute(p);
if (!p2.empty()) result.push_back(std::move(p2));
}
}
return CreateCPaths64(result);
}
EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect,
const CPathsD paths, int precision = 2)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
if (precision < -8 || precision > 8) return nullptr;
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(CRectToRect(rect), scale);
Paths64 pp = ConvertCPathsD(paths, scale);
class RectClip rc(r);
Paths64 result;
result.reserve(pp.size());
for (const Path64& p : pp)
{
Rect64 pathRec = Bounds(p);
if (!r.Intersects(pathRec)) continue;
if (r.Contains(pathRec))
result.push_back(p);
else
{
Path64 p2 = rc.Execute(p);
if (!p2.empty()) result.push_back(std::move(p2));
}
}
return CreateCPathsD(result, 1/scale);
}
EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect,
const CPaths64 paths)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
Rect64 r = CRectToRect(rect);
class RectClipLines rcl (r);
Paths64 pp = ConvertCPaths64(paths);
Paths64 result;
result.reserve(pp.size());
for (const Path64& p : pp)
{
Rect64 pathRec = Bounds(p);
if (!r.Intersects(pathRec)) continue;
if (r.Contains(pathRec))
result.push_back(p);
else
{
Paths64 pp2 = rcl.Execute(p);
if (!pp2.empty())
result.insert(result.end(), pp2.begin(), pp2.end());
}
}
return CreateCPaths64(result);
}
EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect,
const CPathsD paths, int precision = 2)
{
Paths64 result;
if (CRectIsEmpty(rect) || !paths) return nullptr;
if (precision < -8 || precision > 8) return nullptr;
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(CRectToRect(rect), scale);
class RectClipLines rcl(r);
Paths64 pp = ConvertCPathsD(paths, scale);
result.reserve(pp.size());
for (const Path64& p : pp)
{
Rect64 pathRec = Bounds(p);
if (!r.Intersects(pathRec)) continue;
if (r.Contains(pathRec))
result.push_back(p);
else
{
Paths64 pp2 = rcl.Execute(p);
if (pp2.empty()) continue;
result.insert(result.end(), pp2.begin(), pp2.end());
}
}
return CreateCPathsD(result, 1/scale);
}
inline CPath64 CreateCPath64(size_t cnt1, size_t cnt2)
{
// create a dummy counter path
CPath64 result = new int64_t[2 + cnt1 *2];
result[0] = cnt1;
result[1] = cnt2;
return result;
}
inline CPath64 CreateCPath64(const Path64& p)
{
size_t cnt = p.size();
if (!cnt) return nullptr;
CPath64 result = CreateCPath64(cnt, 0);
CPath64 v = result;
v += 2; // skip counters
for (const Point64& pt : p)
{
*v++ = pt.x;
*v++ = pt.y;
}
return result;
}
inline Path64 ConvertCPath64(const CPath64& p)
{
Path64 result;
if (p && *p)
{
CPath64 v = p;
const size_t cnt = static_cast<size_t>(p[0]);
v += 2; // skip counters
result.reserve(cnt);
for (size_t i = 0; i < cnt; ++i)
{
// x,y here avoids right to left function evaluation
// result.push_back(Point64(*v++, *v++));
int64_t x = *v++;
int64_t y = *v++;
result.push_back(Point64(x, y));
}
}
return result;
}
inline CPaths64 CreateCPaths64(const Paths64& pp)
{
size_t cnt = pp.size(), cnt2 = cnt;
// don't allocate space for empty paths
for (size_t i = 0; i < cnt; ++i)
if (!pp[i].size()) --cnt2;
if (!cnt2) return nullptr;
CPaths64 result = new int64_t* [cnt2 + 1];
CPaths64 v = result;
*v++ = CreateCPath64(0, cnt2); // assign a counter path
for (const Path64& p : pp)
{
*v = CreateCPath64(p);
if (*v) ++v;
}
return result;
}
inline Paths64 ConvertCPaths64(const CPaths64& pp)
{
Paths64 result;
if (pp)
{
CPaths64 v = pp;
CPath64 cnts = pp[0];
const size_t cnt = static_cast<size_t>(cnts[1]); // nb 2nd cnt
++v; // skip cnts
result.reserve(cnt);
for (size_t i = 0; i < cnt; ++i)
result.push_back(ConvertCPath64(*v++));
}
return result;
}
inline CPathD CreateCPathD(size_t cnt1, size_t cnt2)
{
// create a dummy path counter
CPathD result = new double[2 + cnt1 * 2];
result[0] = static_cast<double>(cnt1);
result[1] = static_cast<double>(cnt2);
return result;
}
inline CPathD CreateCPathD(const PathD& p)
{
size_t cnt = p.size();
if (!cnt) return nullptr;
CPathD result = CreateCPathD(cnt, 0);
CPathD v = result;
v += 2; // skip counters
for (const PointD& pt : p)
{
*v++ = pt.x;
*v++ = pt.y;
}
return result;
}
inline PathD ConvertCPathD(const CPathD& p)
{
PathD result;
if (p)
{
CPathD v = p;
size_t cnt = static_cast<size_t>(v[0]);
v += 2; // skip counters
result.reserve(cnt);
for (size_t i = 0; i < cnt; ++i)
{
// x,y here avoids right to left function evaluation
// result.push_back(PointD(*v++, *v++));
double x = *v++;
double y = *v++;
result.push_back(PointD(x, y));
}
}
return result;
}
inline CPathsD CreateCPathsD(const PathsD& pp)
{
size_t cnt = pp.size(), cnt2 = cnt;
// don't allocate space for empty paths
for (size_t i = 0; i < cnt; ++i)
if (!pp[i].size()) --cnt2;
if (!cnt2) return nullptr;
CPathsD result = new double * [cnt2 + 1];
CPathsD v = result;
*v++ = CreateCPathD(0, cnt2); // assign counter path
for (const PathD& p : pp)
{
*v = CreateCPathD(p);
if (*v) { ++v; }
}
return result;
}
inline PathsD ConvertCPathsD(const CPathsD& pp)
{
PathsD result;
if (pp)
{
CPathsD v = pp;
CPathD cnts = v[0];
size_t cnt = static_cast<size_t>(cnts[1]);
++v; // skip cnts path
result.reserve(cnt);
for (size_t i = 0; i < cnt; ++i)
result.push_back(ConvertCPathD(*v++));
}
return result;
}
inline Path64 ConvertCPathD(const CPathD& p, double scale)
{
Path64 result;
if (p)
{
CPathD v = p;
size_t cnt = static_cast<size_t>(*v);
v += 2; // skip counters
result.reserve(cnt);
for (size_t i = 0; i < cnt; ++i)
{
// x,y here avoids right to left function evaluation
// result.push_back(PointD(*v++, *v++));
double x = *v++ * scale;
double y = *v++ * scale;
result.push_back(Point64(x, y));
}
}
return result;
}
inline Paths64 ConvertCPathsD(const CPathsD& pp, double scale)
{
Paths64 result;
if (pp)
{
CPathsD v = pp;
CPathD cnts = v[0];
size_t cnt = static_cast<size_t>(cnts[1]);
result.reserve(cnt);
++v; // skip cnts path
for (size_t i = 0; i < cnt; ++i)
result.push_back(ConvertCPathD(*v++, scale));
}
return result;
}
inline CPathD CreateCPathD(const Path64& p, double scale)
{
size_t cnt = p.size();
if (!cnt) return nullptr;
CPathD result = CreateCPathD(cnt, 0);
CPathD v = result;
v += 2; // skip cnts
for (const Point64& pt : p)
{
*v++ = pt.x * scale;
*v++ = pt.y * scale;
}
return result;
}
inline CPathsD CreateCPathsD(const Paths64& pp, double scale)
{
size_t cnt = pp.size(), cnt2 = cnt;
// don't allocate space for empty paths
for (size_t i = 0; i < cnt; ++i)
if (!pp[i].size()) --cnt2;
if (!cnt2) return nullptr;
CPathsD result = new double* [cnt2 + 1];
CPathsD v = result;
*v++ = CreateCPathD(0, cnt2);
for (const Path64& p : pp)
{
*v = CreateCPathD(p, scale);
if (*v) ++v;
}
return result;
}
inline void InitCPolyPath64(CPolyTree64* cpt,
bool is_hole, const PolyPath64* pp)
{
cpt->polygon = CreateCPath64(pp->Polygon());
cpt->is_hole = is_hole;
size_t child_cnt = pp->Count();
cpt->child_count = child_cnt;
cpt->childs = nullptr;
if (!child_cnt) return;
cpt->childs = new CPolyPath64[child_cnt];
CPolyPath64* child = cpt->childs;
for (const PolyPath64* pp_child : *pp)
InitCPolyPath64(child++, !is_hole, pp_child);
}
inline CPolyTree64* CreateCPolyTree64(const PolyTree64& pt)
{
CPolyTree64* result = new CPolyTree64();
result->polygon = nullptr;
result->is_hole = false;
size_t child_cnt = pt.Count();
result->childs = nullptr;
result->child_count = child_cnt;
if (!child_cnt) return result;
result->childs = new CPolyPath64[child_cnt];
CPolyPath64* child = result->childs;
for (const PolyPath64* pp : pt)
InitCPolyPath64(child++, true, pp);
return result;
}
inline void DisposeCPolyPath64(CPolyPath64* cpp)
{
if (!cpp->child_count) return;
CPolyPath64* child = cpp->childs;
for (size_t i = 0; i < cpp->child_count; ++i)
DisposeCPolyPath64(child);
delete[] cpp->childs;
}
EXTERN_DLL_EXPORT void DisposeExportedCPolyTree64(CPolyTree64*& cpt)
{
if (!cpt) return;
DisposeCPolyPath64(cpt);
delete cpt;
cpt = nullptr;
}
inline void InitCPolyPathD(CPolyTreeD* cpt,
bool is_hole, const PolyPath64* pp, double scale)
{
cpt->polygon = CreateCPathD(pp->Polygon(), scale);
cpt->is_hole = is_hole;
size_t child_cnt = pp->Count();
cpt->child_count = child_cnt;
cpt->childs = nullptr;
if (!child_cnt) return;
cpt->childs = new CPolyPathD[child_cnt];
CPolyPathD* child = cpt->childs;
for (const PolyPath64* pp_child : *pp)
InitCPolyPathD(child++, !is_hole, pp_child, scale);
}
inline CPolyTreeD* CreateCPolyTreeD(const PolyTree64& pt, double scale)
{
CPolyTreeD* result = new CPolyTreeD();
result->polygon = nullptr;
result->is_hole = false;
size_t child_cnt = pt.Count();
result->child_count = static_cast<uint32_t>(child_cnt);
result->childs = nullptr;
if (!child_cnt) return result;
result->childs = new CPolyPathD[child_cnt];
CPolyPathD* child = result->childs;
for (const PolyPath64* pp : pt)
InitCPolyPathD(child++, true, pp, scale);
return result;
}
inline void DisposeCPolyPathD(CPolyPathD* cpp)
{
if (!cpp->child_count) return;
CPolyPathD* child = cpp->childs;
for (size_t i = 0; i < cpp->child_count; ++i)
DisposeCPolyPathD(child++);
delete[] cpp->childs;
}
EXTERN_DLL_EXPORT void DisposeExportedCPolyTreeD(CPolyTreeD*& cpt)
{
if (!cpt) return;
DisposeCPolyPathD(cpt);
delete cpt;
cpt = nullptr;
}
} // end Clipper2Lib namespace
#endif // CLIPPER2_EXPORT_H