using System.Runtime.InteropServices;
namespace Clipper2Dll
{
using ztype = System.Int64;
using Point64 = Clipper2DllCore.Point<long>;
using PointD = Clipper2DllCore.Point<double>;
using RectD = Clipper2DllCore.Rect<double>;
public static class Clipper2DllCore
{
public enum FillRule { EvenOdd, NonZero, Positive, Negative };
public enum ClipType { None, Intersection, Union, Difference, Xor };
#if USINGZ
public static long VERTEX_FIELD_CNT = 3;
public struct Point<T>
{
public T X;
public T Y;
public ztype Z;
public Point(Point<T> pt)
{
X = pt.X;
Y = pt.Y;
Z = pt.Z;
}
public Point(T x, T y, ztype z = 0)
{
X = x;
Y = y;
Z = z;
}
}
#else
public static long VERTEX_FIELD_CNT = 2;
public struct Point<T>
{
public T X;
public T Y;
public Point(Point<T> pt)
{
X = pt.X;
Y = pt.Y;
}
public Point(T x, T y)
{
X = x;
Y = y;
}
}
#endif
public struct Rect<T> where T : IComparable
{
public T left;
public T top;
public T right;
public T bottom;
public Rect(T l, T t, T r, T b)
{
left = l;
top = t;
right = r;
bottom = b;
}
public bool IsEmpty()
{
return left.CompareTo(right) >= 0 || top.CompareTo(bottom) >= 0;
}
public double Width()
{
if (IsEmpty()) return 0;
dynamic r = right;
dynamic l = left;
return r - l;
}
public double Height()
{
if (IsEmpty()) return 0;
dynamic b = bottom;
dynamic t = top;
return b - t;
}
}
public static RectD InvalidRectD =
new RectD(double.MaxValue, double.MaxValue, -double.MaxValue, -double.MaxValue);
const string dllPath = @"..\..\..\..\..\";
#if USINGZ
const string clipperDll = dllPath + "Clipper2_Z_64.dll";
#else
const string clipperDll = dllPath + "Clipper2_64.dll";
#endif
[DllImport(clipperDll, EntryPoint =
"Version", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Version();
[DllImport(clipperDll, EntryPoint =
"BooleanOp64", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 BooleanOp64(byte clipType, byte fillRule,
long[] subjects, long[]? openSubs, long[]? clips,
out IntPtr solution, out IntPtr openSol, bool preserveCollinear, bool reverseSolution);
[DllImport(clipperDll, EntryPoint =
"BooleanOpD", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 BooleanOpD(byte clipType, byte fillRule,
double[] subjects, double[]? openSubs, double[]? clips,
out IntPtr solution, out IntPtr openSol, Int32 precision, bool preserveCollinear, bool reverseSolution);
[DllImport(clipperDll, EntryPoint =
"DisposeArray64", CallingConvention = CallingConvention.Cdecl)]
public static extern void DisposeArray64(ref IntPtr intptr);
[DllImport(clipperDll, EntryPoint =
"DisposeArrayD", CallingConvention = CallingConvention.Cdecl)]
public static extern void DisposeArrayD(ref IntPtr intptr);
[DllImport(clipperDll, EntryPoint =
"BooleanOp_PolyTree64", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 BooleanOp_PolyTree64(byte cliptype,
byte fillrule, long[] subjects, long[]? openSubs, long[]? clips,
out IntPtr solTree, out IntPtr openSol,
bool preserve_collinear, bool reverse_solution);
[DllImport(clipperDll, EntryPoint =
"BooleanOp_PolyTreeD", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 BooleanOp_PolyTreeD(byte cliptype,
byte fillrule, double[] subjects, double[]? openSubs, double[]? clips,
out IntPtr solTree, out IntPtr openSol, Int32 precision,
bool preserve_collinear, bool reverse_solution);
[DllImport(clipperDll, EntryPoint =
"Triangulate64", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Triangulate64(long[] subject, bool use_delaunay);
[DllImport(clipperDll, EntryPoint =
"TriangulateD", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TriangulateD(double[] subject, int decimal_places, bool use_delaunay);
#if USINGZ
public delegate void DLLZCallback64(Point64 e1bot, Point64 e1top, Point64 e2bot, Point64 e2top, ref Point64 ip);
[DllImport(clipperDll, EntryPoint =
"SetZCallback64", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetZCallback64(DLLZCallback64 callback);
public delegate void DLLZCallbackD(PointD e1bot, PointD e1top, PointD e2bot, PointD e2top, ref PointD ip);
[DllImport(clipperDll, EntryPoint =
"SetZCallbackD", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetZCallbackD(DLLZCallbackD callback);
#endif
public static T[]? CreateCPath<T>(T[] coords)
{
long pathLen = coords.Length / VERTEX_FIELD_CNT;
if (pathLen == 0) return null;
long arrayLen = pathLen * VERTEX_FIELD_CNT + 2;
T[] result = new T[arrayLen];
result[0] = (T)Convert.ChangeType(pathLen, typeof(T));
result[1] = (T)Convert.ChangeType(0, typeof(T));
coords.CopyTo(result, 2);
return result;
}
public static T[] CreateCPathsFromCPathList<T>(List<T[]> listOfCPath)
{
int pathCount = listOfCPath.Count();
int arrayLen = 2;
foreach (T[] path in listOfCPath)
arrayLen += path.Length;
T[] result = new T[arrayLen];
result[0] = (T)Convert.ChangeType(arrayLen, typeof(T));
result[1] = (T)Convert.ChangeType(pathCount, typeof(T));
int idx = 2;
foreach (T[] cpath in listOfCPath)
{
cpath.CopyTo(result, idx);
idx += cpath.Length;
}
return result;
}
public static T[] CreateCPathsFromCoords<T>(T[] coords)
{
long pathLen = coords.Length / VERTEX_FIELD_CNT;
long arrayLen = pathLen * VERTEX_FIELD_CNT + 4;
T[] result = new T[arrayLen];
result[0] = (T)Convert.ChangeType(arrayLen, typeof(T));
result[1] = (T)Convert.ChangeType(1, typeof(T)); result[2] = (T)Convert.ChangeType(pathLen, typeof(T));
result[3] = (T)Convert.ChangeType(0, typeof(T));
coords.CopyTo(result, 4);
return result;
}
#if USINGZ
public static string VertexAsString<T>(T X, T Y, T Z, int precision)
{
if (typeof(T) == typeof(long)) return $"{X},{Y},{Z} ";
else
return string.Format($"{{0:F{precision}}},{{1:F{precision}}},{{2:F{precision}}} ", X, Y, Z);
}
#else
public static string VertexAsString<T>(T X, T Y, int precision)
{
if (typeof(T) == typeof(long)) return $"{X},{Y} ";
else
return string.Format($"{{0:F{precision}}},{{1:F{precision}}} ", X, Y);
}
#endif
public static void LogCPath<T>(T[] cpaths, ref int idx, string spaceIndent)
{
int vertexCnt = Convert.ToInt32(cpaths[idx]);
idx += 2;
for (int i = 0; i < vertexCnt; i++)
#if USINGZ
Console.Write(spaceIndent + VertexAsString<T>(cpaths[idx++], cpaths[idx++], cpaths[idx++], 2));
#else
Console.Write(spaceIndent + VertexAsString<T>(cpaths[idx++], cpaths[idx++], 2));
#endif
Console.Write("\n");
}
public static void LogCPaths<T>(T[]? cpaths, string spaceIndent)
{
if (cpaths == null) return;
int pathCnt = Convert.ToInt32(cpaths[1]);
int idx = 2;
for (int i = 0; i < pathCnt; i++)
LogCPath<T>(cpaths, ref idx, spaceIndent);
}
private static void CPolypathCounter<T>(T[] cpolypath,
ref long idx, ref int pathCount, ref long arrayLen)
{
int vertexCount = Convert.ToInt32(cpolypath[idx++]);
int childCount = Convert.ToInt32(cpolypath[idx++]);
arrayLen += vertexCount * VERTEX_FIELD_CNT + 2;
pathCount++;
idx += vertexCount * VERTEX_FIELD_CNT;
for (int i = 0; i < childCount; i++)
CPolypathCounter(cpolypath, ref idx, ref pathCount, ref arrayLen);
}
private static void ConvertCPolypathToCPaths<T>(T[] cpolypath, ref long idx,
T[] result, ref int resultIdx)
{
T vertexCount = cpolypath[idx++]!;
int vertCnt = (int)Convert.ChangeType(vertexCount, typeof(int));
int childCount = Convert.ToInt32(cpolypath[idx++]);
result[resultIdx++] = vertexCount;
result[resultIdx++] = (T)Convert.ChangeType(0, typeof(T));
for (int i = 0; i < vertCnt; i++)
{
result[resultIdx++] = cpolypath[idx++]; result[resultIdx++] = cpolypath[idx++]; #if USINGZ
result[resultIdx++] = cpolypath[idx++]; #endif
}
for (int i = 0; i < childCount; i++)
ConvertCPolypathToCPaths(cpolypath, ref idx, result, ref resultIdx);
}
public static void ConvertCPolytreeToCPaths<T>(T[] cpolytree, out T[] result)
{
if (cpolytree == null)
{
result = new T[0];
return;
}
int topPathsCount = Convert.ToInt32(cpolytree[1]);
long idx = 2;
int pathCount = 0;
long arrayLen = 2;
for (int i = 0; i < topPathsCount; i++)
CPolypathCounter(cpolytree, ref idx, ref pathCount, ref arrayLen);
result = new T[arrayLen];
result[0] = (T)Convert.ChangeType(arrayLen, typeof(T));
result[1] = (T)Convert.ChangeType(pathCount, typeof(T));
int resultIdx = 2;
idx = 2;
for (int i = 0; i < topPathsCount; i++)
ConvertCPolypathToCPaths(cpolytree, ref idx, result, ref resultIdx);
}
public static void LogPolyPath<T>(T[] polypath,
ref int idx, bool isHole, string spaceIndent, int precision)
{
int polyCnt = Convert.ToInt32(polypath[idx++]);
int childCnt = Convert.ToInt32(polypath[idx++]);
string preamble = isHole ? "Hole: " : (spaceIndent == "") ?
"Polygon: " : "Nested Polygon: ";
Console.Write(spaceIndent + preamble);
spaceIndent += " ";
for (int i = 0; i < polyCnt; i++)
#if USINGZ
Console.Write(VertexAsString<T>(polypath[idx++], polypath[idx++], polypath[idx++], precision));
#else
Console.Write(VertexAsString<T>(polypath[idx++], polypath[idx++], precision));
#endif
Console.Write("\n");
for (int i = 0; i < childCnt; i++)
LogPolyPath<T>(polypath, ref idx, !isHole, spaceIndent, precision);
}
public static void LogPolytree<T>(T[] polytree, int precision)
{
int cnt = Convert.ToInt32(polytree[1]);
int idx = 2;
for (int i = 0; i < cnt; i++)
LogPolyPath<T>(polytree, ref idx, false, " ", precision);
}
public static T[]? GetArrayFromIntPtr<T>(IntPtr paths)
{
if (paths == IntPtr.Zero) return null;
if (typeof(T) == typeof(long))
{
long[] len = new long[1];
Marshal.Copy(paths, len, 0, 1);
long[] res = new long[(int)len[0]];
Marshal.Copy(paths, res, 0, (int)len[0]);
return res as T[];
}
else if (typeof(T) == typeof(double))
{
double[] len = new double[1];
Marshal.Copy(paths, len, 0, 1);
double[] res = new double[(int)len[0]];
Marshal.Copy(paths, res, 0, (int)len[0]);
return res as T[];
}
else return null;
}
public static void ConvertArrayOfLongs(long[] longs, out double[] result)
{
result = new double[longs.Length];
for (int i = 0; i < longs.Length; i++)
result[i] = longs[i];
}
public static double StaticCastLongToDouble(long val)
{
byte[] b = BitConverter.GetBytes(val);
return BitConverter.ToDouble(b, 0);
}
public static long StaticCastDoubleToLong(double val)
{
byte[] b = BitConverter.GetBytes(val);
return BitConverter.ToInt64(b, 0);
}
}
}