[mapnik-clipper] 01/02: Imported Upstream version 0.0~20150707-33c9329+ds

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Sat Sep 12 16:09:50 UTC 2015


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository mapnik-clipper.

commit 2923135d2d21ffb6c18be38cd93c7afaa3481fe2
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Sat Sep 12 14:03:53 2015 +0200

    Imported Upstream version 0.0~20150707-33c9329+ds
---
 C#/ConsoleDemo/ConsoleDemo.sln                     |   45 +
 C#/ConsoleDemo/ConsoleDemo/ConsoleDemo.csproj      |  103 +
 C#/ConsoleDemo/ConsoleDemo/Program.cs              |  453 ++
 .../ConsoleDemo/Properties/AssemblyInfo.cs         |   36 +
 C#/ConsoleDemo/ConsoleDemo/bin/Release/clip.txt    |    6 +
 C#/ConsoleDemo/ConsoleDemo/bin/Release/subj.txt    |   13 +
 C#/GuiDemo/GuiDemo.sln                             |   45 +
 C#/GuiDemo/GuiDemo/Form1.Designer.cs               |  419 ++
 C#/GuiDemo/GuiDemo/Form1.cs                        |  682 +++
 C#/GuiDemo/GuiDemo/Form1.resx                      |  225 +
 C#/GuiDemo/GuiDemo/GuiDemo.csproj                  |  135 +
 C#/GuiDemo/GuiDemo/Program.cs                      |   21 +
 C#/GuiDemo/GuiDemo/Properties/AssemblyInfo.cs      |   36 +
 .../GuiDemo/Properties/Resources.Designer.cs       |   70 +
 C#/GuiDemo/GuiDemo/Properties/Resources.resx       |  124 +
 C#/GuiDemo/GuiDemo/Properties/Settings.Designer.cs |   26 +
 C#/GuiDemo/GuiDemo/Properties/Settings.settings    |    7 +
 C#/GuiDemo/GuiDemo/Settings.cs                     |   28 +
 C#/GuiDemo/GuiDemo/aust.bin                        |  Bin 0 -> 6828 bytes
 C#/clipper_library/Properties/AssemblyInfo.cs      |   36 +
 C#/clipper_library/clipper.cs                      | 4839 +++++++++++++++++
 C#/clipper_library/clipper_library.csproj          |   54 +
 Delphi/cairo demo/Cairo Resources.txt              |   12 +
 Delphi/cairo demo/CairoClipperDemo1.dpr            |  267 +
 Delphi/cairo demo/CairoClipperDemo1.res            |  Bin 0 -> 876 bytes
 Delphi/cairo demo/cairo_clipper.pas                |  121 +
 Delphi/clipper.pas                                 | 5514 ++++++++++++++++++++
 Delphi/main demo/GR32_Misc.pas                     |  295 ++
 Delphi/main demo/clipper_demo.dpr                  |   15 +
 Delphi/main demo/clipper_demo.res                  |  Bin 0 -> 6672 bytes
 Delphi/main demo/main.dfm                          |  394 ++
 Delphi/main demo/main.pas                          |  797 +++
 Delphi/main demo/polygons.res                      |  Bin 0 -> 19376 bytes
 Documentation/offset_triginometry.svg              |   42 +
 Documentation/offset_triginometry2.svg             |   28 +
 Documentation/offset_triginometry3.svg             |  391 ++
 License.txt                                        |   24 +
 README                                             |  407 ++
 Third Party/Flash/AS3_flash_readme.txt             |    2 +
 Third Party/Go/Go_readme.txt                       |    2 +
 Third Party/Haskell/Haskell_readme.txt             |    4 +
 Third Party/LuaJIT/LuaJIT_readme.txt               |    1 +
 Third Party/Matlab/matlab_readme.txt               |    3 +
 Third Party/Matlab/mexclipper.cpp                  |  283 +
 Third Party/ObjectiveC/objectivec_readme.txt       |    4 +
 Third Party/perl/perl_readme.txt                   |    5 +
 Third Party/python/clipper.py                      | 2259 ++++++++
 Third Party/python/clipper_demo.py                 |  267 +
 Third Party/python/python_readme.txt               |   10 +
 Third Party/ruby/ruby_readme.txt                   |    4 +
 cpp/CMakeLists.txt                                 |   21 +
 cpp/clipper.cpp                                    | 4577 ++++++++++++++++
 cpp/clipper.hpp                                    |  435 ++
 cpp/cpp_agg/agg_conv_clipper.h                     |  295 ++
 cpp/cpp_agg/clipper_test.cpp                       |  574 ++
 cpp/cpp_agg/clipper_test.sln                       |   20 +
 cpp/cpp_agg/clipper_test.vcxproj                   |  114 +
 cpp/cpp_agg/clipper_test.vcxproj.filters           |  118 +
 cpp/cpp_agg/icon.res                               |  Bin 0 -> 5556 bytes
 cpp/cpp_cairo/Cairo Resources.txt                  |    6 +
 cpp/cpp_cairo/cairo.sln                            |   20 +
 cpp/cpp_cairo/cairo.vcxproj                        |   93 +
 cpp/cpp_cairo/cairo_clipper.cpp                    |  134 +
 cpp/cpp_cairo/cairo_clipper.hpp                    |   59 +
 cpp/cpp_cairo/libcairo-2.lib                       |  Bin 0 -> 83700 bytes
 cpp/cpp_cairo/main.cpp                             |  182 +
 cpp/cpp_console/clipper_console_demo.cpp           |  461 ++
 cpp/cpp_console/clipper_console_demo.sln           |   20 +
 cpp/cpp_console/clipper_console_demo.vcxproj       |   92 +
 cpp/cpp_opengl/clipper_demo.sln                    |   20 +
 cpp/cpp_opengl/clipper_demo.vcxproj                |   98 +
 cpp/cpp_opengl/icon.res                            |  Bin 0 -> 5556 bytes
 cpp/cpp_opengl/main.cpp                            |  621 +++
 cpp/cpp_opengl/menu.res                            |  Bin 0 -> 1364 bytes
 cpp/fix_members.sh                                 |   24 +
 cpp/polyclipping.pc.cmakein                        |   13 +
 mapnik-changes.md                                  |   24 +
 77 files changed, 26575 insertions(+)

diff --git a/C#/ConsoleDemo/ConsoleDemo.sln b/C#/ConsoleDemo/ConsoleDemo.sln
new file mode 100644
index 0000000..0ee75df
--- /dev/null
+++ b/C#/ConsoleDemo/ConsoleDemo.sln
@@ -0,0 +1,45 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual C# Express 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleDemo", "ConsoleDemo\ConsoleDemo.csproj", "{185E6664-6A68-4377-99BE-4D4BFED19298}"
+	ProjectSection(ProjectDependencies) = postProject
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66} = {9B062971-A88E-4A3D-B3C9-12B78D15FA66}
+	EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "clipper_library", "..\clipper_library\clipper_library.csproj", "{9B062971-A88E-4A3D-B3C9-12B78D15FA66}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Debug|Mixed Platforms = Debug|Mixed Platforms
+		Debug|x86 = Debug|x86
+		Release|Any CPU = Release|Any CPU
+		Release|Mixed Platforms = Release|Mixed Platforms
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{185E6664-6A68-4377-99BE-4D4BFED19298}.Debug|Any CPU.ActiveCfg = Debug|x86
+		{185E6664-6A68-4377-99BE-4D4BFED19298}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+		{185E6664-6A68-4377-99BE-4D4BFED19298}.Debug|Mixed Platforms.Build.0 = Debug|x86
+		{185E6664-6A68-4377-99BE-4D4BFED19298}.Debug|x86.ActiveCfg = Debug|x86
+		{185E6664-6A68-4377-99BE-4D4BFED19298}.Debug|x86.Build.0 = Debug|x86
+		{185E6664-6A68-4377-99BE-4D4BFED19298}.Release|Any CPU.ActiveCfg = Release|x86
+		{185E6664-6A68-4377-99BE-4D4BFED19298}.Release|Mixed Platforms.ActiveCfg = Release|x86
+		{185E6664-6A68-4377-99BE-4D4BFED19298}.Release|Mixed Platforms.Build.0 = Release|x86
+		{185E6664-6A68-4377-99BE-4D4BFED19298}.Release|x86.ActiveCfg = Release|x86
+		{185E6664-6A68-4377-99BE-4D4BFED19298}.Release|x86.Build.0 = Release|x86
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Release|Any CPU.Build.0 = Release|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Release|x86.ActiveCfg = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/C#/ConsoleDemo/ConsoleDemo/ConsoleDemo.csproj b/C#/ConsoleDemo/ConsoleDemo/ConsoleDemo.csproj
new file mode 100644
index 0000000..ab09c30
--- /dev/null
+++ b/C#/ConsoleDemo/ConsoleDemo/ConsoleDemo.csproj
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{185E6664-6A68-4377-99BE-4D4BFED19298}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>ConsoleDemo</RootNamespace>
+    <AssemblyName>ConsoleDemo</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <TargetFrameworkProfile>Client</TargetFrameworkProfile>
+    <FileAlignment>512</FileAlignment>
+    <PublishUrl>publish\</PublishUrl>
+    <Install>true</Install>
+    <InstallFrom>Disk</InstallFrom>
+    <UpdateEnabled>false</UpdateEnabled>
+    <UpdateMode>Foreground</UpdateMode>
+    <UpdateInterval>7</UpdateInterval>
+    <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+    <UpdatePeriodically>false</UpdatePeriodically>
+    <UpdateRequired>false</UpdateRequired>
+    <MapFileExtensions>true</MapFileExtensions>
+    <ApplicationRevision>0</ApplicationRevision>
+    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+    <IsWebBootstrapper>false</IsWebBootstrapper>
+    <UseApplicationTrust>false</UseApplicationTrust>
+    <BootstrapperEnabled>true</BootstrapperEnabled>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <UseVSHostingProcess>true</UseVSHostingProcess>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <UseVSHostingProcess>true</UseVSHostingProcess>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
+      <Visible>False</Visible>
+      <ProductName>Microsoft .NET Framework 4 Client Profile %28x86 and x64%29</ProductName>
+      <Install>true</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+      <Visible>False</Visible>
+      <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+      <Install>false</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+      <Visible>False</Visible>
+      <ProductName>.NET Framework 3.5 SP1</ProductName>
+      <Install>false</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
+      <Visible>False</Visible>
+      <ProductName>Windows Installer 3.1</ProductName>
+      <Install>true</Install>
+    </BootstrapperPackage>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\clipper_library\clipper_library.csproj">
+      <Project>{9B062971-A88E-4A3D-B3C9-12B78D15FA66}</Project>
+      <Name>clipper_library</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
diff --git a/C#/ConsoleDemo/ConsoleDemo/Program.cs b/C#/ConsoleDemo/ConsoleDemo/Program.cs
new file mode 100644
index 0000000..83f056c
--- /dev/null
+++ b/C#/ConsoleDemo/ConsoleDemo/Program.cs
@@ -0,0 +1,453 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Globalization;
+using ClipperLib;
+
+namespace ClipperTest1
+{
+    using Path = List<IntPoint>;
+    using Paths = List<List<IntPoint>>;
+
+    class Program
+    {
+
+        //a very simple class that builds an SVG file with any number of 
+        //polygons of the specified formats ...
+        class SVGBuilder
+        {
+
+            public class StyleInfo
+            {
+                public PolyFillType pft;
+                public Color brushClr;
+                public Color penClr;
+                public double penWidth;
+                public int[] dashArray;
+                public Boolean showCoords;
+                public StyleInfo Clone()
+                {
+                    StyleInfo si = new StyleInfo();
+                    si.pft = this.pft;
+                    si.brushClr = this.brushClr;
+                    si.dashArray = this.dashArray;
+                    si.penClr = this.penClr;
+                    si.penWidth = this.penWidth;
+                    si.showCoords = this.showCoords;
+                    return si;
+                }
+                public StyleInfo() 
+                {
+                    pft = PolyFillType.pftNonZero;
+                    brushClr = Color.AntiqueWhite;
+                    dashArray = null;
+                    penClr = Color.Black;
+                    penWidth = 0.8;
+                    showCoords = false;
+                }
+            }
+
+            public class PolyInfo
+            {
+                public Paths polygons;
+                public StyleInfo si;
+            }
+
+            public StyleInfo style;
+            private List<PolyInfo> PolyInfoList;
+            const string svg_header = "<?xml version=\"1.0\" standalone=\"no\"?>\n" +
+              "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"\n" +
+              "\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n\n" +
+              "<svg width=\"{0}px\" height=\"{1}px\" viewBox=\"0 0 {2} {3}\" " +
+              "version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n\n";
+            const string svg_path_format = "\"\n style=\"fill:{0};" +
+                " fill-opacity:{1:f2}; fill-rule:{2}; stroke:{3};" +
+                " stroke-opacity:{4:f2}; stroke-width:{5:f2};\"/>\n\n";
+
+            public SVGBuilder()
+            {
+                PolyInfoList = new List<PolyInfo>();
+                style = new StyleInfo();
+            }
+
+            public void AddPaths(Paths poly)
+            {
+                if (poly.Count == 0) return;
+                PolyInfo pi = new PolyInfo();
+                pi.polygons = poly;
+                pi.si = style.Clone();
+                PolyInfoList.Add(pi);
+            }
+
+            public Boolean SaveToFile(string filename, double scale = 1.0, int margin = 10)
+            {
+                if (scale == 0) scale = 1.0;
+                if (margin < 0) margin = 0;
+                
+                //calculate the bounding rect ...
+                int i = 0, j = 0;
+                while (i < PolyInfoList.Count)
+                {
+                    j = 0;
+                    while (j < PolyInfoList[i].polygons.Count && 
+                        PolyInfoList[i].polygons[j].Count == 0) j++;
+                    if (j < PolyInfoList[i].polygons.Count) break;
+                    i++;
+                }
+                if (i == PolyInfoList.Count) return false;
+                IntRect rec = new IntRect();
+                rec.left = PolyInfoList[i].polygons[j][0].X;
+                rec.right = rec.left;
+                rec.top = PolyInfoList[0].polygons[j][0].Y;
+                rec.bottom = rec.top;
+
+                for ( ; i < PolyInfoList.Count; i++ )
+                {
+                    foreach (Path pg in PolyInfoList[i].polygons)
+                        foreach (IntPoint pt in pg)
+                        {
+                            if (pt.X < rec.left) rec.left = pt.X;
+                            else if (pt.X > rec.right) rec.right = pt.X;
+                            if (pt.Y < rec.top) rec.top = pt.Y;
+                            else if (pt.Y > rec.bottom) rec.bottom = pt.Y;
+                        }
+                }
+
+                rec.left = (Int64)((double)rec.left * scale);
+                rec.top = (Int64)((double)rec.top * scale);
+                rec.right = (Int64)((double)rec.right * scale);
+                rec.bottom = (Int64)((double)rec.bottom * scale);
+                Int64 offsetX = -rec.left + margin;
+                Int64 offsetY = -rec.top + margin;
+
+                StreamWriter writer = new StreamWriter(filename);
+                if (writer == null) return false;
+                writer.Write(svg_header,
+                    (rec.right - rec.left) + margin * 2,
+                    (rec.bottom - rec.top) + margin * 2,
+                    (rec.right - rec.left) + margin * 2,
+                    (rec.bottom - rec.top) + margin * 2);
+
+                foreach (PolyInfo pi in PolyInfoList)
+                {
+                    writer.Write(" <path d=\"");
+                    foreach (Path p in pi.polygons)
+                    {
+                        if (p.Count < 3) continue;
+                        writer.Write(String.Format(NumberFormatInfo.InvariantInfo, " M {0:f2} {1:f2}",
+                            (double)((double)p[0].X * scale + offsetX),
+                            (double)((double)p[0].Y * scale + offsetY)));
+                        for (int k = 1; k < p.Count; k++)
+                        {
+                            writer.Write(String.Format(NumberFormatInfo.InvariantInfo, " L {0:f2} {1:f2}",
+                            (double)((double)p[k].X * scale + offsetX),
+                            (double)((double)p[k].Y * scale + offsetY)));
+                        }
+                        writer.Write(" z");
+                    }
+
+                    writer.Write(String.Format(NumberFormatInfo.InvariantInfo, svg_path_format,
+                    ColorTranslator.ToHtml(pi.si.brushClr),
+                    (float)pi.si.brushClr.A /255,
+                    (pi.si.pft == PolyFillType.pftEvenOdd ? "evenodd" : "nonzero"),
+                    ColorTranslator.ToHtml(pi.si.penClr),
+                    (float)pi.si.penClr.A / 255,
+                    pi.si.penWidth));
+
+                    if (pi.si.showCoords)
+                    {
+                        writer.Write("<g font-family=\"Verdana\" font-size=\"11\" fill=\"black\">\n\n");
+                        foreach (Path p in pi.polygons)
+                        {
+                            foreach (IntPoint pt in p)
+                            {
+                                Int64 x = pt.X;
+                                Int64 y = pt.Y;
+                                writer.Write(String.Format(
+                                    "<text x=\"{0}\" y=\"{1}\">{2},{3}</text>\n",
+                                    (int)(x * scale + offsetX), (int)(y * scale + offsetY), x, y));
+
+                            }
+                            writer.Write("\n");
+                        }
+                        writer.Write("</g>\n");
+                    }
+                }
+                writer.Write("</svg>\n");
+                writer.Close();
+                return true;
+            }
+        }
+
+        ////////////////////////////////////////////////
+
+        static bool LoadFromFile(string filename, Paths ppg, int dec_places, int xOffset = 0, int yOffset = 0)
+        {
+            double scaling;
+            scaling = Math.Pow(10, dec_places);
+
+            ppg.Clear();
+            if (!File.Exists(filename)) return false;
+            StreamReader sr = new StreamReader(filename);
+            if (sr == null) return false;
+            string line;
+            if ((line = sr.ReadLine()) == null) return false;
+            int polyCnt, vertCnt;
+            if (!Int32.TryParse(line, out polyCnt) || polyCnt < 0) return false;
+            ppg.Capacity = polyCnt;
+            for (int i = 0; i < polyCnt; i++)
+            {
+                if ((line = sr.ReadLine()) == null) return false;
+                if (!Int32.TryParse(line, out vertCnt) || vertCnt < 0) return false;
+                Path pg = new Path(vertCnt);
+                ppg.Add(pg);
+                if (scaling > 0.999 & scaling < 1.001)
+                    for (int j = 0; j < vertCnt; j++)
+                    {
+                        Int64 x, y;
+                        if ((line = sr.ReadLine()) == null) return false;
+                        char[] delimiters = new char[] { ',', ' ' };
+                        string[] vals = line.Split(delimiters);
+                        if (vals.Length < 2) return false;
+                        if (!Int64.TryParse(vals[0], out x)) return false;
+                        if (!Int64.TryParse(vals[1], out y))
+                            if (vals.Length < 2 || !Int64.TryParse(vals[2], out y)) return false;
+                        x = x + xOffset;
+                        y = y + yOffset;
+                        pg.Add(new IntPoint(x, y));
+                    }
+                else
+                    for (int j = 0; j < vertCnt; j++)
+                    {
+                        double x, y;
+                        if ((line = sr.ReadLine()) == null) return false;
+                        char[] delimiters = new char[] { ',', ' ' };
+                        string[] vals = line.Split(delimiters);
+                        if (vals.Length < 2) return false;
+                        if (!double.TryParse(vals[0], out x)) return false;
+                        if (!double.TryParse(vals[1], out y))
+                            if (vals.Length < 2 || !double.TryParse(vals[2], out y)) return false;
+                        x = x * scaling + xOffset;
+                        y = y * scaling + yOffset;
+                        pg.Add(new IntPoint((Int64)Math.Round(x), (Int64)Math.Round(y)));
+                    }
+            }
+            return true;
+        }
+
+        ////////////////////////////////////////////////
+        static void SaveToFile(string filename, Paths ppg, int dec_places)
+        {
+            double scaling = Math.Pow(10, dec_places);
+            StreamWriter writer = new StreamWriter(filename);
+            if (writer == null) return;
+            writer.Write("{0}\r\n", ppg.Count);
+            foreach (Path pg in ppg)
+            {
+                writer.Write("{0}\r\n", pg.Count);
+                foreach (IntPoint ip in pg)
+                    writer.Write("{0:0.####}, {1:0.####}\r\n", (double)ip.X / scaling, (double)ip.Y / scaling);
+            }
+            writer.Close();
+        }
+
+        ////////////////////////////////////////////////
+
+        static void OutputFileFormat()
+        {
+            Console.WriteLine("The expected (text) file format is ...");
+            Console.WriteLine("Polygon Count");
+            Console.WriteLine("First polygon vertex count");
+            Console.WriteLine("first X, Y coordinate of first polygon");
+            Console.WriteLine("second X, Y coordinate of first polygon");
+            Console.WriteLine("etc.");
+            Console.WriteLine("Second polygon vertex count (if there is one)");
+            Console.WriteLine("first X, Y coordinate of second polygon");
+            Console.WriteLine("second X, Y coordinate of second polygon");
+            Console.WriteLine("etc.");
+        }
+
+        ////////////////////////////////////////////////
+
+        static Path IntsToPolygon(int[] ints)
+        {
+            int len1 = ints.Length /2;
+            Path result = new Path(len1);
+            for (int i = 0; i < len1; i++)
+              result.Add(new IntPoint(ints[i * 2], ints[i * 2 +1]));
+            return result;
+        }
+
+        ////////////////////////////////////////////////
+
+        static Path MakeRandomPolygon(Random r,  int maxWidth, int maxHeight, int edgeCount, Int64 scale = 1)
+        {
+            Path result = new Path(edgeCount);
+            for (int i = 0; i < edgeCount; i++)
+            {
+                result.Add(new IntPoint(r.Next(maxWidth)*scale, r.Next(maxHeight)*scale));
+            }
+            return result;
+        }
+        ////////////////////////////////////////////////
+        
+        static void Main(string[] args)
+        {
+          ////quick test with random polygons ...
+          //Paths ss = new Paths(1), cc = new Paths(1), sss = new Paths();
+          //Random r = new Random((int)DateTime.Now.Ticks);
+          //int scale = 1000000000; //tests 128bit math
+          //ss.Add(MakeRandomPolygon(r, 400, 350, 9, scale));
+          //cc.Add(MakeRandomPolygon(r, 400, 350, 9, scale));
+          //Clipper cpr = new Clipper();
+          //cpr.AddPaths(ss, PolyType.ptSubject, true);
+          //cpr.AddPaths(cc, PolyType.ptClip, true);
+          //cpr.Execute(ClipType.ctUnion, sss, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
+          //sss = Clipper.OffsetPolygons(sss, -5.0 * scale, JoinType.jtMiter, 4);
+          //SVGBuilder svg1 = new SVGBuilder();
+          //svg1.style.brushClr = Color.FromArgb(0x20, 0, 0, 0x9c);
+          //svg1.style.penClr = Color.FromArgb(0xd3, 0xd3, 0xda);
+          //svg1.AddPaths(ss);
+          //svg1.style.brushClr = Color.FromArgb(0x20, 0x9c, 0, 0);
+          //svg1.style.penClr = Color.FromArgb(0xff, 0xa0, 0x7a);
+          //svg1.AddPaths(cc);
+          //svg1.style.brushClr = Color.FromArgb(0xAA, 0x80, 0xff, 0x9c);
+          //svg1.style.penClr = Color.FromArgb(0, 0x33, 0);
+          //svg1.AddPaths(sss);
+          //svg1.SaveToFile("solution.svg", 1.0 / scale);
+          //return;
+
+          if (args.Length < 5)
+            {
+                string appname = System.Environment.GetCommandLineArgs()[0];
+                appname = System.IO.Path.GetFileName(appname);
+                Console.WriteLine("");
+                Console.WriteLine("Usage:");
+                Console.WriteLine("  {0} CLIPTYPE s_file c_file INPUT_DEC_PLACES SVG_SCALE [S_FILL, C_FILL]", appname);
+                Console.WriteLine("  where ...");
+                Console.WriteLine("  CLIPTYPE = INTERSECTION|UNION|DIFFERENCE|XOR");
+                Console.WriteLine("  FILLMODE = NONZERO|EVENODD");
+                Console.WriteLine("  INPUT_DEC_PLACES = signific. decimal places for subject & clip coords.");
+                Console.WriteLine("  SVG_SCALE = scale of SVG image as power of 10. (Fractions are accepted.)");
+                Console.WriteLine("  both S_FILL and C_FILL are optional. The default is EVENODD.");
+                Console.WriteLine("Example:");
+                Console.WriteLine("  Intersect polygons, rnd to 4 dec places, SVG is 1/100 normal size ...");
+                Console.WriteLine("  {0} INTERSECTION subj.txt clip.txt 0 0 NONZERO NONZERO", appname);
+                return;
+            }
+
+            ClipType ct;
+            switch (args[0].ToUpper())
+            {
+                case "INTERSECTION": ct = ClipType.ctIntersection; break;
+                case "UNION": ct = ClipType.ctUnion; break;
+                case "DIFFERENCE": ct = ClipType.ctDifference; break;
+                case "XOR": ct = ClipType.ctXor; break;
+                default: Console.WriteLine("Error: invalid operation - {0}", args[0]); return;
+            }
+
+            string subjFilename = args[1];
+            string clipFilename = args[2];
+            if (!File.Exists(subjFilename))
+            {
+                Console.WriteLine("Error: file - {0} - does not exist.", subjFilename);
+                return;
+            }
+            if (!File.Exists(clipFilename))
+            {
+                Console.WriteLine("Error: file - {0} - does not exist.", clipFilename);
+                return;
+            }
+
+            int decimal_places = 0;
+            if (!Int32.TryParse(args[3], out decimal_places))
+            {
+                Console.WriteLine("Error: invalid number of decimal places - {0}", args[3]);
+                return;
+            }
+            if (decimal_places > 8) decimal_places = 8;
+            else if (decimal_places < 0) decimal_places = 0;
+
+            double svg_scale = 0;
+            if (!double.TryParse(args[4], out svg_scale))
+            {
+                Console.WriteLine("Error: invalid value for SVG_SCALE - {0}", args[4]);
+                return;
+            }
+            if (svg_scale < -18) svg_scale = -18;
+            else if (svg_scale > 18) svg_scale = 18;
+            svg_scale = Math.Pow(10, svg_scale - decimal_places);//nb: also compensate for decimal places
+
+
+            PolyFillType pftSubj = PolyFillType.pftEvenOdd;
+            PolyFillType pftClip = PolyFillType.pftEvenOdd;
+            if (args.Length > 6)
+            {
+                switch (args[5].ToUpper())
+                {
+                    case "EVENODD": pftSubj = PolyFillType.pftEvenOdd; break;
+                    case "NONZERO": pftSubj = PolyFillType.pftNonZero; break;
+                    default: Console.WriteLine("Error: invalid cliptype - {0}", args[5]); return;
+                }
+                switch (args[6].ToUpper())
+                {
+                    case "EVENODD": pftClip = PolyFillType.pftEvenOdd; break;
+                    case "NONZERO": pftClip = PolyFillType.pftNonZero; break;
+                    default: Console.WriteLine("Error: invalid cliptype - {0}", args[6]); return;
+                }
+            }
+
+            Paths subjs = new Paths();
+            Paths clips = new Paths();
+            if (!LoadFromFile(subjFilename, subjs, decimal_places))
+            {
+                Console.WriteLine("Error processing subject polygons file - {0} ", subjFilename);
+                OutputFileFormat();
+                return;
+            }
+            if (!LoadFromFile(clipFilename, clips, decimal_places))
+            {
+                Console.WriteLine("Error processing clip polygons file - {0} ", clipFilename);
+                OutputFileFormat();
+                return;
+            }
+
+            Console.WriteLine("wait ...");
+            Clipper cp = new Clipper();
+            cp.AddPaths(subjs, PolyType.ptSubject, true);
+            cp.AddPaths(clips, PolyType.ptClip, true);
+
+            Paths solution = new Paths();
+            //Paths solution = new Paths();
+            if (cp.Execute(ct, solution, pftSubj, pftClip))
+            {
+                SaveToFile("solution.txt", solution, decimal_places);
+
+                //solution = Clipper.OffsetPolygons(solution, -4, JoinType.jtRound);
+
+                SVGBuilder svg = new SVGBuilder();
+                svg.style.brushClr = Color.FromArgb(0x20, 0, 0, 0x9c);
+                svg.style.penClr = Color.FromArgb(0xd3, 0xd3, 0xda);
+                svg.AddPaths(subjs);
+                svg.style.brushClr = Color.FromArgb(0x20, 0x9c, 0, 0);
+                svg.style.penClr = Color.FromArgb(0xff, 0xa0, 0x7a);
+                svg.AddPaths(clips);
+                svg.style.brushClr = Color.FromArgb(0xAA, 0x80, 0xff, 0x9c);
+                svg.style.penClr = Color.FromArgb(0, 0x33, 0);
+                svg.AddPaths(solution);
+                svg.SaveToFile("solution.svg", svg_scale);
+
+                Console.WriteLine("finished!");
+            }
+            else
+            {
+                Console.WriteLine("failed!");
+            }
+        }
+
+    } //class Program
+}
diff --git a/C#/ConsoleDemo/ConsoleDemo/Properties/AssemblyInfo.cs b/C#/ConsoleDemo/ConsoleDemo/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..aac993a
--- /dev/null
+++ b/C#/ConsoleDemo/ConsoleDemo/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ClipperTest1")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Angus Johnson")]
+[assembly: AssemblyProduct("ClipperTest1")]
+[assembly: AssemblyCopyright("Copyright © Angus Johnson 2010-14")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("a8009709-1ebc-44a6-a094-298ec0b23d42")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/C#/ConsoleDemo/ConsoleDemo/bin/Release/clip.txt b/C#/ConsoleDemo/ConsoleDemo/bin/Release/clip.txt
new file mode 100644
index 0000000..6a2a2b0
--- /dev/null
+++ b/C#/ConsoleDemo/ConsoleDemo/bin/Release/clip.txt
@@ -0,0 +1,6 @@
+1
+4
+120, 10,
+240, 10,
+240, 90,
+120, 90
\ No newline at end of file
diff --git a/C#/ConsoleDemo/ConsoleDemo/bin/Release/subj.txt b/C#/ConsoleDemo/ConsoleDemo/bin/Release/subj.txt
new file mode 100644
index 0000000..7033a4c
--- /dev/null
+++ b/C#/ConsoleDemo/ConsoleDemo/bin/Release/subj.txt
@@ -0,0 +1,13 @@
+1
+11
+0, 0,
+200, 0,
+200, 100,
+50, 100,
+50, 50,
+75, 75,
+100, 50,
+75, 25,
+50, 50,
+50, 100,
+0, 100,
diff --git a/C#/GuiDemo/GuiDemo.sln b/C#/GuiDemo/GuiDemo.sln
new file mode 100644
index 0000000..d17656f
--- /dev/null
+++ b/C#/GuiDemo/GuiDemo.sln
@@ -0,0 +1,45 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual C# Express 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GuiDemo", "GuiDemo\GuiDemo.csproj", "{8BD44147-3290-4A73-BAA2-1C171566BC25}"
+	ProjectSection(ProjectDependencies) = postProject
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66} = {9B062971-A88E-4A3D-B3C9-12B78D15FA66}
+	EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "clipper_library", "..\clipper_library\clipper_library.csproj", "{9B062971-A88E-4A3D-B3C9-12B78D15FA66}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Debug|Mixed Platforms = Debug|Mixed Platforms
+		Debug|x86 = Debug|x86
+		Release|Any CPU = Release|Any CPU
+		Release|Mixed Platforms = Release|Mixed Platforms
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{8BD44147-3290-4A73-BAA2-1C171566BC25}.Debug|Any CPU.ActiveCfg = Debug|x86
+		{8BD44147-3290-4A73-BAA2-1C171566BC25}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+		{8BD44147-3290-4A73-BAA2-1C171566BC25}.Debug|Mixed Platforms.Build.0 = Debug|x86
+		{8BD44147-3290-4A73-BAA2-1C171566BC25}.Debug|x86.ActiveCfg = Debug|x86
+		{8BD44147-3290-4A73-BAA2-1C171566BC25}.Debug|x86.Build.0 = Debug|x86
+		{8BD44147-3290-4A73-BAA2-1C171566BC25}.Release|Any CPU.ActiveCfg = Release|x86
+		{8BD44147-3290-4A73-BAA2-1C171566BC25}.Release|Mixed Platforms.ActiveCfg = Release|x86
+		{8BD44147-3290-4A73-BAA2-1C171566BC25}.Release|Mixed Platforms.Build.0 = Release|x86
+		{8BD44147-3290-4A73-BAA2-1C171566BC25}.Release|x86.ActiveCfg = Release|x86
+		{8BD44147-3290-4A73-BAA2-1C171566BC25}.Release|x86.Build.0 = Release|x86
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Release|Any CPU.Build.0 = Release|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{9B062971-A88E-4A3D-B3C9-12B78D15FA66}.Release|x86.ActiveCfg = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/C#/GuiDemo/GuiDemo/Form1.Designer.cs b/C#/GuiDemo/GuiDemo/Form1.Designer.cs
new file mode 100644
index 0000000..79e0846
--- /dev/null
+++ b/C#/GuiDemo/GuiDemo/Form1.Designer.cs
@@ -0,0 +1,419 @@
+namespace WindowsFormsApplication1
+{
+    partial class Form1
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
+            this.statusStrip1 = new System.Windows.Forms.StatusStrip();
+            this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel();
+            this.panel1 = new System.Windows.Forms.Panel();
+            this.bSave = new System.Windows.Forms.Button();
+            this.groupBox3 = new System.Windows.Forms.GroupBox();
+            this.rbNone = new System.Windows.Forms.RadioButton();
+            this.rbXor = new System.Windows.Forms.RadioButton();
+            this.rbDifference = new System.Windows.Forms.RadioButton();
+            this.rbUnion = new System.Windows.Forms.RadioButton();
+            this.rbIntersect = new System.Windows.Forms.RadioButton();
+            this.groupBox2 = new System.Windows.Forms.GroupBox();
+            this.rbTest2 = new System.Windows.Forms.RadioButton();
+            this.rbTest1 = new System.Windows.Forms.RadioButton();
+            this.groupBox1 = new System.Windows.Forms.GroupBox();
+            this.label2 = new System.Windows.Forms.Label();
+            this.nudOffset = new System.Windows.Forms.NumericUpDown();
+            this.lblCount = new System.Windows.Forms.Label();
+            this.nudCount = new System.Windows.Forms.NumericUpDown();
+            this.rbNonZero = new System.Windows.Forms.RadioButton();
+            this.rbEvenOdd = new System.Windows.Forms.RadioButton();
+            this.bRefresh = new System.Windows.Forms.Button();
+            this.bCancel = new System.Windows.Forms.Button();
+            this.panel2 = new System.Windows.Forms.Panel();
+            this.pictureBox1 = new System.Windows.Forms.PictureBox();
+            this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog();
+            this.statusStrip1.SuspendLayout();
+            this.panel1.SuspendLayout();
+            this.groupBox3.SuspendLayout();
+            this.groupBox2.SuspendLayout();
+            this.groupBox1.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.nudOffset)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nudCount)).BeginInit();
+            this.panel2.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // statusStrip1
+            // 
+            this.statusStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Visible;
+            this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.toolStripStatusLabel1});
+            this.statusStrip1.Location = new System.Drawing.Point(0, 459);
+            this.statusStrip1.Name = "statusStrip1";
+            this.statusStrip1.Size = new System.Drawing.Size(716, 22);
+            this.statusStrip1.TabIndex = 4;
+            // 
+            // toolStripStatusLabel1
+            // 
+            this.toolStripStatusLabel1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+            this.toolStripStatusLabel1.Name = "toolStripStatusLabel1";
+            this.toolStripStatusLabel1.Size = new System.Drawing.Size(0, 17);
+            // 
+            // panel1
+            // 
+            this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
+            this.panel1.Controls.Add(this.bSave);
+            this.panel1.Controls.Add(this.groupBox3);
+            this.panel1.Controls.Add(this.groupBox2);
+            this.panel1.Controls.Add(this.groupBox1);
+            this.panel1.Controls.Add(this.bRefresh);
+            this.panel1.Controls.Add(this.bCancel);
+            this.panel1.Dock = System.Windows.Forms.DockStyle.Left;
+            this.panel1.Location = new System.Drawing.Point(0, 0);
+            this.panel1.Name = "panel1";
+            this.panel1.Size = new System.Drawing.Size(121, 459);
+            this.panel1.TabIndex = 5;
+            // 
+            // bSave
+            // 
+            this.bSave.Location = new System.Drawing.Point(9, 412);
+            this.bSave.Name = "bSave";
+            this.bSave.Size = new System.Drawing.Size(100, 25);
+            this.bSave.TabIndex = 9;
+            this.bSave.Text = "S&ave as SVG File";
+            this.bSave.UseVisualStyleBackColor = true;
+            this.bSave.Click += new System.EventHandler(this.bSave_Click);
+            // 
+            // groupBox3
+            // 
+            this.groupBox3.Controls.Add(this.rbNone);
+            this.groupBox3.Controls.Add(this.rbXor);
+            this.groupBox3.Controls.Add(this.rbDifference);
+            this.groupBox3.Controls.Add(this.rbUnion);
+            this.groupBox3.Controls.Add(this.rbIntersect);
+            this.groupBox3.Location = new System.Drawing.Point(9, 12);
+            this.groupBox3.Name = "groupBox3";
+            this.groupBox3.Size = new System.Drawing.Size(100, 125);
+            this.groupBox3.TabIndex = 5;
+            this.groupBox3.TabStop = false;
+            this.groupBox3.Text = "&Boolean Op:";
+            // 
+            // rbNone
+            // 
+            this.rbNone.AutoSize = true;
+            this.rbNone.Location = new System.Drawing.Point(14, 100);
+            this.rbNone.Name = "rbNone";
+            this.rbNone.Size = new System.Drawing.Size(51, 17);
+            this.rbNone.TabIndex = 4;
+            this.rbNone.Text = "None";
+            this.rbNone.UseVisualStyleBackColor = true;
+            this.rbNone.CheckedChanged += new System.EventHandler(this.rbNonZero_Click);
+            // 
+            // rbXor
+            // 
+            this.rbXor.AutoSize = true;
+            this.rbXor.Location = new System.Drawing.Point(14, 81);
+            this.rbXor.Name = "rbXor";
+            this.rbXor.Size = new System.Drawing.Size(48, 17);
+            this.rbXor.TabIndex = 3;
+            this.rbXor.Text = "XOR";
+            this.rbXor.UseVisualStyleBackColor = true;
+            this.rbXor.CheckedChanged += new System.EventHandler(this.rbNonZero_Click);
+            // 
+            // rbDifference
+            // 
+            this.rbDifference.AutoSize = true;
+            this.rbDifference.Location = new System.Drawing.Point(14, 60);
+            this.rbDifference.Name = "rbDifference";
+            this.rbDifference.Size = new System.Drawing.Size(74, 17);
+            this.rbDifference.TabIndex = 2;
+            this.rbDifference.Text = "Difference";
+            this.rbDifference.UseVisualStyleBackColor = true;
+            this.rbDifference.CheckedChanged += new System.EventHandler(this.rbNonZero_Click);
+            // 
+            // rbUnion
+            // 
+            this.rbUnion.AutoSize = true;
+            this.rbUnion.Location = new System.Drawing.Point(14, 39);
+            this.rbUnion.Name = "rbUnion";
+            this.rbUnion.Size = new System.Drawing.Size(53, 17);
+            this.rbUnion.TabIndex = 1;
+            this.rbUnion.Text = "Union";
+            this.rbUnion.UseVisualStyleBackColor = true;
+            this.rbUnion.CheckedChanged += new System.EventHandler(this.rbNonZero_Click);
+            // 
+            // rbIntersect
+            // 
+            this.rbIntersect.AutoSize = true;
+            this.rbIntersect.Checked = true;
+            this.rbIntersect.Location = new System.Drawing.Point(14, 19);
+            this.rbIntersect.Name = "rbIntersect";
+            this.rbIntersect.Size = new System.Drawing.Size(66, 17);
+            this.rbIntersect.TabIndex = 0;
+            this.rbIntersect.TabStop = true;
+            this.rbIntersect.Text = "Intersect";
+            this.rbIntersect.UseVisualStyleBackColor = true;
+            this.rbIntersect.CheckedChanged += new System.EventHandler(this.rbNonZero_Click);
+            // 
+            // groupBox2
+            // 
+            this.groupBox2.Controls.Add(this.rbTest2);
+            this.groupBox2.Controls.Add(this.rbTest1);
+            this.groupBox2.Location = new System.Drawing.Point(9, 310);
+            this.groupBox2.Name = "groupBox2";
+            this.groupBox2.Size = new System.Drawing.Size(100, 61);
+            this.groupBox2.TabIndex = 7;
+            this.groupBox2.TabStop = false;
+            this.groupBox2.Text = "Sample";
+            // 
+            // rbTest2
+            // 
+            this.rbTest2.AutoSize = true;
+            this.rbTest2.Location = new System.Drawing.Point(14, 35);
+            this.rbTest2.Name = "rbTest2";
+            this.rbTest2.Size = new System.Drawing.Size(46, 17);
+            this.rbTest2.TabIndex = 1;
+            this.rbTest2.Text = "&Two";
+            this.rbTest2.UseVisualStyleBackColor = true;
+            this.rbTest2.Click += new System.EventHandler(this.rbTest1_Click);
+            // 
+            // rbTest1
+            // 
+            this.rbTest1.AutoSize = true;
+            this.rbTest1.Checked = true;
+            this.rbTest1.Location = new System.Drawing.Point(14, 17);
+            this.rbTest1.Name = "rbTest1";
+            this.rbTest1.Size = new System.Drawing.Size(45, 17);
+            this.rbTest1.TabIndex = 0;
+            this.rbTest1.TabStop = true;
+            this.rbTest1.Text = "&One";
+            this.rbTest1.UseVisualStyleBackColor = true;
+            this.rbTest1.Click += new System.EventHandler(this.rbTest1_Click);
+            // 
+            // groupBox1
+            // 
+            this.groupBox1.Controls.Add(this.label2);
+            this.groupBox1.Controls.Add(this.nudOffset);
+            this.groupBox1.Controls.Add(this.lblCount);
+            this.groupBox1.Controls.Add(this.nudCount);
+            this.groupBox1.Controls.Add(this.rbNonZero);
+            this.groupBox1.Controls.Add(this.rbEvenOdd);
+            this.groupBox1.Location = new System.Drawing.Point(9, 144);
+            this.groupBox1.Name = "groupBox1";
+            this.groupBox1.Size = new System.Drawing.Size(100, 159);
+            this.groupBox1.TabIndex = 6;
+            this.groupBox1.TabStop = false;
+            this.groupBox1.Text = "Options:";
+            // 
+            // label2
+            // 
+            this.label2.AutoSize = true;
+            this.label2.Location = new System.Drawing.Point(11, 108);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(38, 13);
+            this.label2.TabIndex = 4;
+            this.label2.Text = "O&ffset:";
+            // 
+            // nudOffset
+            // 
+            this.nudOffset.DecimalPlaces = 1;
+            this.nudOffset.Location = new System.Drawing.Point(14, 126);
+            this.nudOffset.Maximum = new decimal(new int[] {
+            10,
+            0,
+            0,
+            0});
+            this.nudOffset.Minimum = new decimal(new int[] {
+            10,
+            0,
+            0,
+            -2147483648});
+            this.nudOffset.Name = "nudOffset";
+            this.nudOffset.Size = new System.Drawing.Size(54, 20);
+            this.nudOffset.TabIndex = 5;
+            this.nudOffset.ValueChanged += new System.EventHandler(this.nudCount_ValueChanged);
+            // 
+            // lblCount
+            // 
+            this.lblCount.AutoSize = true;
+            this.lblCount.Location = new System.Drawing.Point(11, 62);
+            this.lblCount.Name = "lblCount";
+            this.lblCount.Size = new System.Drawing.Size(71, 13);
+            this.lblCount.TabIndex = 2;
+            this.lblCount.Text = "Vertex &Count:";
+            // 
+            // nudCount
+            // 
+            this.nudCount.Location = new System.Drawing.Point(14, 80);
+            this.nudCount.Minimum = new decimal(new int[] {
+            3,
+            0,
+            0,
+            0});
+            this.nudCount.Name = "nudCount";
+            this.nudCount.Size = new System.Drawing.Size(54, 20);
+            this.nudCount.TabIndex = 3;
+            this.nudCount.Value = new decimal(new int[] {
+            50,
+            0,
+            0,
+            0});
+            this.nudCount.ValueChanged += new System.EventHandler(this.bRefresh_Click);
+            // 
+            // rbNonZero
+            // 
+            this.rbNonZero.AutoSize = true;
+            this.rbNonZero.Checked = true;
+            this.rbNonZero.Location = new System.Drawing.Point(14, 39);
+            this.rbNonZero.Name = "rbNonZero";
+            this.rbNonZero.Size = new System.Drawing.Size(67, 17);
+            this.rbNonZero.TabIndex = 1;
+            this.rbNonZero.TabStop = true;
+            this.rbNonZero.Text = "Non&Zero";
+            this.rbNonZero.UseVisualStyleBackColor = true;
+            this.rbNonZero.Click += new System.EventHandler(this.rbNonZero_Click);
+            // 
+            // rbEvenOdd
+            // 
+            this.rbEvenOdd.AutoSize = true;
+            this.rbEvenOdd.Location = new System.Drawing.Point(14, 21);
+            this.rbEvenOdd.Name = "rbEvenOdd";
+            this.rbEvenOdd.Size = new System.Drawing.Size(70, 17);
+            this.rbEvenOdd.TabIndex = 0;
+            this.rbEvenOdd.Text = "&EvenOdd";
+            this.rbEvenOdd.UseVisualStyleBackColor = true;
+            this.rbEvenOdd.Click += new System.EventHandler(this.rbNonZero_Click);
+            // 
+            // bRefresh
+            // 
+            this.bRefresh.Location = new System.Drawing.Point(9, 381);
+            this.bRefresh.Name = "bRefresh";
+            this.bRefresh.Size = new System.Drawing.Size(100, 25);
+            this.bRefresh.TabIndex = 8;
+            this.bRefresh.Text = "&New Sample";
+            this.bRefresh.UseVisualStyleBackColor = true;
+            this.bRefresh.Click += new System.EventHandler(this.bRefresh_Click);
+            // 
+            // bCancel
+            // 
+            this.bCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+            this.bCancel.Location = new System.Drawing.Point(9, 458);
+            this.bCancel.Name = "bCancel";
+            this.bCancel.Size = new System.Drawing.Size(100, 27);
+            this.bCancel.TabIndex = 11;
+            this.bCancel.Text = "E&xit";
+            this.bCancel.UseVisualStyleBackColor = true;
+            this.bCancel.Click += new System.EventHandler(this.bClose_Click);
+            // 
+            // panel2
+            // 
+            this.panel2.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
+            this.panel2.Controls.Add(this.pictureBox1);
+            this.panel2.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.panel2.Location = new System.Drawing.Point(121, 0);
+            this.panel2.Name = "panel2";
+            this.panel2.Size = new System.Drawing.Size(595, 459);
+            this.panel2.TabIndex = 6;
+            // 
+            // pictureBox1
+            // 
+            this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.pictureBox1.Location = new System.Drawing.Point(0, 0);
+            this.pictureBox1.Name = "pictureBox1";
+            this.pictureBox1.Size = new System.Drawing.Size(591, 455);
+            this.pictureBox1.TabIndex = 1;
+            this.pictureBox1.TabStop = false;
+            this.pictureBox1.DoubleClick += new System.EventHandler(this.bRefresh_Click);
+            // 
+            // saveFileDialog1
+            // 
+            this.saveFileDialog1.DefaultExt = "svg";
+            this.saveFileDialog1.Filter = "SVG Files (*.svg)|*.svg";
+            // 
+            // Form1
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(716, 481);
+            this.Controls.Add(this.panel2);
+            this.Controls.Add(this.panel1);
+            this.Controls.Add(this.statusStrip1);
+            this.DoubleBuffered = true;
+            this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+            this.KeyPreview = true;
+            this.Name = "Form1";
+            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+            this.Text = "Clipper C# Demo1";
+            this.Load += new System.EventHandler(this.Form1_Load);
+            this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
+            this.Resize += new System.EventHandler(this.Form1_Resize);
+            this.statusStrip1.ResumeLayout(false);
+            this.statusStrip1.PerformLayout();
+            this.panel1.ResumeLayout(false);
+            this.groupBox3.ResumeLayout(false);
+            this.groupBox3.PerformLayout();
+            this.groupBox2.ResumeLayout(false);
+            this.groupBox2.PerformLayout();
+            this.groupBox1.ResumeLayout(false);
+            this.groupBox1.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.nudOffset)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nudCount)).EndInit();
+            this.panel2.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.StatusStrip statusStrip1;
+        private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1;
+        private System.Windows.Forms.Panel panel1;
+        private System.Windows.Forms.GroupBox groupBox3;
+        private System.Windows.Forms.RadioButton rbNone;
+        private System.Windows.Forms.RadioButton rbXor;
+        private System.Windows.Forms.RadioButton rbDifference;
+        private System.Windows.Forms.RadioButton rbUnion;
+        private System.Windows.Forms.RadioButton rbIntersect;
+        private System.Windows.Forms.GroupBox groupBox2;
+        private System.Windows.Forms.RadioButton rbTest2;
+        private System.Windows.Forms.RadioButton rbTest1;
+        private System.Windows.Forms.GroupBox groupBox1;
+        private System.Windows.Forms.Label label2;
+        private System.Windows.Forms.NumericUpDown nudOffset;
+        private System.Windows.Forms.Label lblCount;
+        private System.Windows.Forms.NumericUpDown nudCount;
+        private System.Windows.Forms.RadioButton rbNonZero;
+        private System.Windows.Forms.RadioButton rbEvenOdd;
+        private System.Windows.Forms.Button bRefresh;
+        private System.Windows.Forms.Button bCancel;
+        private System.Windows.Forms.Panel panel2;
+        private System.Windows.Forms.PictureBox pictureBox1;
+        private System.Windows.Forms.Button bSave;
+        private System.Windows.Forms.SaveFileDialog saveFileDialog1;
+    }
+}
+
diff --git a/C#/GuiDemo/GuiDemo/Form1.cs b/C#/GuiDemo/GuiDemo/Form1.cs
new file mode 100644
index 0000000..ec2cd66
--- /dev/null
+++ b/C#/GuiDemo/GuiDemo/Form1.cs
@@ -0,0 +1,682 @@
+//#define UsePolyTree
+
+using System;
+using System.Diagnostics;
+using System.Text;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Reflection;
+using System.Linq;
+using System.Windows.Forms;
+using System.Globalization;
+using ClipperLib;
+
+namespace WindowsFormsApplication1
+{
+  using Polygon = List<IntPoint>;
+  using Polygons = List<List<IntPoint>>;
+
+  public partial class Form1 : Form
+  {
+    private Bitmap mybitmap;
+    private Polygons subjects = new Polygons();
+    private Polygons clips = new Polygons();
+    private Polygons solution = new Polygons();
+#if UsePolyTree
+    private PolyTree solutionTree = new PolyTree();
+#endif
+    //Here we are scaling all coordinates up by 100 when they're passed to Clipper 
+    //via Polygon (or Polygons) objects because Clipper no longer accepts floating  
+    //point values. Likewise when Clipper returns a solution in a Polygons object, 
+    //we need to scale down these returned values by the same amount before displaying.
+    private float scale = 100; //or 1 or 10 or 10000 etc for lesser or greater precision.
+
+    //------------------------------------------------------------------------------
+    //---------------------------------------------------------------------
+
+    //a very simple class that builds an SVG file with any number of 
+    //polygons of the specified formats ...
+    class SVGBuilder
+    {
+      public class StyleInfo
+      {
+        public PolyFillType pft;
+        public Color brushClr;
+        public Color penClr;
+        public double penWidth;
+        public int[] dashArray;
+        public Boolean showCoords;
+        public StyleInfo Clone()
+        {
+          StyleInfo si = new StyleInfo();
+          si.pft = this.pft;
+          si.brushClr = this.brushClr;
+          si.dashArray = this.dashArray;
+          si.penClr = this.penClr;
+          si.penWidth = this.penWidth;
+          si.showCoords = this.showCoords;
+          return si;
+        }
+        public StyleInfo()
+        {
+          pft = PolyFillType.pftNonZero;
+          brushClr = Color.AntiqueWhite;
+          dashArray = null;
+          penClr = Color.Black;
+          penWidth = 0.8;
+          showCoords = false;
+        }
+      }
+
+      public class PolyInfo
+      {
+        public Polygons polygons;
+        public StyleInfo si;
+      }
+
+      public StyleInfo style;
+      private List<PolyInfo> PolyInfoList;
+      const string svg_header = "<?xml version=\"1.0\" standalone=\"no\"?>\n" +
+        "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"\n" +
+        "\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n\n" +
+        "<svg width=\"{0}px\" height=\"{1}px\" viewBox=\"0 0 {2} {3}\" " +
+        "version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n\n";
+      const string svg_path_format = "\"\n style=\"fill:{0};" +
+          " fill-opacity:{1:f2}; fill-rule:{2}; stroke:{3};" +
+          " stroke-opacity:{4:f2}; stroke-width:{5:f2};\"/>\n\n";
+
+      public SVGBuilder()
+      {
+        PolyInfoList = new List<PolyInfo>();
+        style = new StyleInfo();
+      }
+
+      public void AddPolygons(Polygons poly)
+      {
+        if (poly.Count == 0) return;
+        PolyInfo pi = new PolyInfo();
+        pi.polygons = poly;
+        pi.si = style.Clone();
+        PolyInfoList.Add(pi);
+      }
+
+      public Boolean SaveToFile(string filename, double scale = 1.0, int margin = 10)
+      {
+        if (scale == 0) scale = 1.0;
+        if (margin < 0) margin = 0;
+
+        //calculate the bounding rect ...
+        int i = 0, j = 0;
+        while (i < PolyInfoList.Count)
+        {
+          j = 0;
+          while (j < PolyInfoList[i].polygons.Count &&
+              PolyInfoList[i].polygons[j].Count == 0) j++;
+          if (j < PolyInfoList[i].polygons.Count) break;
+          i++;
+        }
+        if (i == PolyInfoList.Count) return false;
+        IntRect rec = new IntRect();
+        rec.left = PolyInfoList[i].polygons[j][0].X;
+        rec.right = rec.left;
+        rec.top = PolyInfoList[0].polygons[j][0].Y;
+        rec.bottom = rec.top;
+
+        for (; i < PolyInfoList.Count; i++)
+        {
+          foreach (Polygon pg in PolyInfoList[i].polygons)
+            foreach (IntPoint pt in pg)
+            {
+              if (pt.X < rec.left) rec.left = pt.X;
+              else if (pt.X > rec.right) rec.right = pt.X;
+              if (pt.Y < rec.top) rec.top = pt.Y;
+              else if (pt.Y > rec.bottom) rec.bottom = pt.Y;
+            }
+        }
+
+        rec.left = (Int64)(rec.left * scale);
+        rec.top = (Int64)(rec.top * scale);
+        rec.right = (Int64)(rec.right * scale);
+        rec.bottom = (Int64)(rec.bottom * scale);
+        Int64 offsetX = -rec.left + margin;
+        Int64 offsetY = -rec.top + margin;
+
+        using (StreamWriter writer = new StreamWriter(filename))
+        {
+          writer.Write(svg_header,
+              (rec.right - rec.left) + margin * 2,
+              (rec.bottom - rec.top) + margin * 2,
+              (rec.right - rec.left) + margin * 2,
+              (rec.bottom - rec.top) + margin * 2);
+
+          foreach (PolyInfo pi in PolyInfoList)
+          {
+            writer.Write(" <path d=\"");
+            foreach (Polygon p in pi.polygons)
+            {
+              if (p.Count < 3) continue;
+              writer.Write(String.Format(NumberFormatInfo.InvariantInfo, " M {0:f2} {1:f2}",
+                  (double)((double)p[0].X * scale + offsetX),
+                  (double)((double)p[0].Y * scale + offsetY)));
+              for (int k = 1; k < p.Count; k++)
+              {
+                writer.Write(String.Format(NumberFormatInfo.InvariantInfo, " L {0:f2} {1:f2}",
+                (double)((double)p[k].X * scale + offsetX),
+                (double)((double)p[k].Y * scale + offsetY)));
+              }
+              writer.Write(" z");
+            }
+
+            writer.Write(String.Format(NumberFormatInfo.InvariantInfo, svg_path_format,
+            ColorTranslator.ToHtml(pi.si.brushClr),
+            (float)pi.si.brushClr.A / 255,
+            (pi.si.pft == PolyFillType.pftEvenOdd ? "evenodd" : "nonzero"),
+            ColorTranslator.ToHtml(pi.si.penClr),
+            (float)pi.si.penClr.A / 255,
+            pi.si.penWidth));
+
+            if (pi.si.showCoords)
+            {
+              writer.Write("<g font-family=\"Verdana\" font-size=\"11\" fill=\"black\">\n\n");
+              foreach (Polygon p in pi.polygons)
+              {
+                foreach (IntPoint pt in p)
+                {
+                  Int64 x = pt.X;
+                  Int64 y = pt.Y;
+                  writer.Write(String.Format(
+                      "<text x=\"{0}\" y=\"{1}\">{2},{3}</text>\n",
+                      (int)(x * scale + offsetX), (int)(y * scale + offsetY), x, y));
+
+                }
+                writer.Write("\n");
+              }
+              writer.Write("</g>\n");
+            }
+          }
+          writer.Write("</svg>\n");
+        }
+        return true;
+      }
+    }
+
+    //------------------------------------------------------------------------------
+    //------------------------------------------------------------------------------
+
+    static private PointF[] PolygonToPointFArray(Polygon pg, float scale)
+    {
+      PointF[] result = new PointF[pg.Count];
+      for (int i = 0; i < pg.Count; ++i)
+      {
+        result[i].X = (float)pg[i].X / scale;
+        result[i].Y = (float)pg[i].Y / scale;
+      }
+      return result;
+    }
+
+    public Form1()
+    {
+      InitializeComponent();
+      this.MouseWheel += new MouseEventHandler(Form1_MouseWheel);
+      mybitmap = new Bitmap(
+        pictureBox1.ClientRectangle.Width,
+        pictureBox1.ClientRectangle.Height,
+        PixelFormat.Format32bppArgb);
+    }
+    //---------------------------------------------------------------------
+
+    private void Form1_MouseWheel(object sender, MouseEventArgs e)
+    {
+      if (e.Delta > 0 && nudOffset.Value < 10) nudOffset.Value += (decimal)0.5;
+      else if (e.Delta < 0 && nudOffset.Value > -10) nudOffset.Value -= (decimal)0.5;
+    }
+    //---------------------------------------------------------------------
+
+    private void bRefresh_Click(object sender, EventArgs e)
+    {
+      DrawBitmap();
+    }
+    //---------------------------------------------------------------------
+
+    private void GenerateAustPlusRandomEllipses(int count)
+    {
+      subjects.Clear();
+      //load map of Australia from resource ...
+      Assembly _assembly = Assembly.GetExecutingAssembly();
+      using (BinaryReader polyStream = new BinaryReader(_assembly.GetManifestResourceStream("GuiDemo.aust.bin")))
+      {
+        int polyCnt = polyStream.ReadInt32();
+        for (int i = 0; i < polyCnt; ++i)
+        {
+          int vertCnt = polyStream.ReadInt32();
+          Polygon pg = new Polygon(vertCnt);
+          for (int j = 0; j < vertCnt; ++j)
+          {
+            float x = polyStream.ReadSingle() * scale;
+            float y = polyStream.ReadSingle() * scale;
+            pg.Add(new IntPoint((int)x, (int)y));
+          }
+          subjects.Add(pg);
+        }
+      }
+      clips.Clear();
+      Random rand = new Random();
+      using (GraphicsPath path = new GraphicsPath())
+      {
+        const int ellipse_size = 100, margin = 10;
+        for (int i = 0; i < count; ++i)
+        {
+          int w = pictureBox1.ClientRectangle.Width - ellipse_size - margin * 2;
+          int h = pictureBox1.ClientRectangle.Height - ellipse_size - margin * 2 - statusStrip1.Height;
+
+          int x = rand.Next(w) + margin;
+          int y = rand.Next(h) + margin;
+          int size = rand.Next(ellipse_size - 20) + 20;
+          path.Reset();
+          path.AddEllipse(x, y, size, size);
+          path.Flatten();
+          Polygon clip = new Polygon(path.PathPoints.Count());
+          foreach (PointF p in path.PathPoints)
+            clip.Add(new IntPoint((int)(p.X * scale), (int)(p.Y * scale)));
+          clips.Add(clip);
+        }
+      }
+    }
+    //---------------------------------------------------------------------
+
+    private IntPoint GenerateRandomPoint(int l, int t, int r, int b, Random rand)
+    {
+      int Q = 10;
+      return new IntPoint(
+        Convert.ToInt64((rand.Next(r / Q) * Q + l + 10) * scale),
+        Convert.ToInt64((rand.Next(b / Q) * Q + t + 10) * scale));
+    }
+    //---------------------------------------------------------------------
+
+    private void GenerateRandomPolygon(int count)
+    {
+      int Q = 10;
+      Random rand = new Random();
+      int l = 10;
+      int t = 10;
+      int r = (pictureBox1.ClientRectangle.Width - 20) / Q * Q;
+      int b = (pictureBox1.ClientRectangle.Height - 20) / Q * Q;
+
+      subjects.Clear();
+      clips.Clear();
+
+      Polygon subj = new Polygon(count);
+      for (int i = 0; i < count; ++i)
+        subj.Add(GenerateRandomPoint(l, t, r, b, rand));
+      subjects.Add(subj);
+
+      Polygon clip = new Polygon(count);
+      for (int i = 0; i < count; ++i)
+        clip.Add(GenerateRandomPoint(l, t, r, b, rand));
+      clips.Add(clip);
+    }
+    //---------------------------------------------------------------------
+
+    ClipType GetClipType()
+    {
+      if (rbIntersect.Checked) return ClipType.ctIntersection;
+      if (rbUnion.Checked) return ClipType.ctUnion;
+      if (rbDifference.Checked) return ClipType.ctDifference;
+      else return ClipType.ctXor;
+    }
+    //---------------------------------------------------------------------
+
+    PolyFillType GetPolyFillType()
+    {
+      if (rbNonZero.Checked) return PolyFillType.pftNonZero;
+      else return PolyFillType.pftEvenOdd;
+    }
+    //---------------------------------------------------------------------
+
+    bool LoadFromFile(string filename, Polygons ppg, double scale = 0,
+      int xOffset = 0, int yOffset = 0)
+    {
+      double scaling = Math.Pow(10, scale);
+      ppg.Clear();
+      if (!File.Exists(filename)) return false;
+      using (StreamReader sr = new StreamReader(filename))
+      {
+        string line;
+        if ((line = sr.ReadLine()) == null)
+          return false;
+        int polyCnt, vertCnt;
+        if (!Int32.TryParse(line, out polyCnt) || polyCnt < 0)
+          return false;
+        ppg.Capacity = polyCnt;
+        for (int i = 0; i < polyCnt; i++)
+        {
+          if ((line = sr.ReadLine()) == null)
+            return false;
+          if (!Int32.TryParse(line, out vertCnt) || vertCnt < 0)
+            return false;
+          Polygon pg = new Polygon(vertCnt);
+          ppg.Add(pg);
+          for (int j = 0; j < vertCnt; j++)
+          {
+            double x, y;
+            if ((line = sr.ReadLine()) == null)
+              return false;
+            char[] delimiters = new char[] { ',', ' ' };
+            string[] vals = line.Split(delimiters);
+            if (vals.Length < 2)
+              return false;
+            if (!double.TryParse(vals[0], out x))
+              return false;
+            if (!double.TryParse(vals[1], out y))
+              if (vals.Length < 2 || !double.TryParse(vals[2], out y))
+                return false;
+            x = x * scaling + xOffset;
+            y = y * scaling + yOffset;
+            pg.Add(new IntPoint((int)Math.Round(x), (int)Math.Round(y)));
+          }
+        }
+      }
+      return true;
+    }
+    //------------------------------------------------------------------------------
+
+    void SaveToFile(string filename, Polygons ppg, int scale = 0)
+    {
+      double scaling = Math.Pow(10, scale);
+      using (StreamWriter writer = new StreamWriter(filename))
+      {
+        writer.Write("{0}\n", ppg.Count);
+        foreach (Polygon pg in ppg)
+        {
+          writer.Write("{0}\n", pg.Count);
+          foreach (IntPoint ip in pg)
+            writer.Write("{0:0.0000}, {1:0.0000}\n", ip.X / scaling, ip.Y / scaling);
+        }
+      }
+    }
+    //---------------------------------------------------------------------------
+
+    private void DrawBitmap(bool justClip = false)
+    {
+      Cursor.Current = Cursors.WaitCursor;
+      try
+      {
+        if (!justClip)
+        {
+          if (rbTest2.Checked)
+            GenerateAustPlusRandomEllipses((int)nudCount.Value);
+          else
+            GenerateRandomPolygon((int)nudCount.Value);
+        }
+        using (Graphics newgraphic = Graphics.FromImage(mybitmap))
+        using (GraphicsPath path = new GraphicsPath())
+        {
+          newgraphic.SmoothingMode = SmoothingMode.AntiAlias;
+          newgraphic.Clear(Color.White);
+          if (rbNonZero.Checked)
+            path.FillMode = FillMode.Winding;
+
+          //draw subjects ...
+          foreach (Polygon pg in subjects)
+          {
+            PointF[] pts = PolygonToPointFArray(pg, scale);
+            path.AddPolygon(pts);
+            pts = null;
+          }
+          using (Pen myPen = new Pen(Color.FromArgb(196, 0xC3, 0xC9, 0xCF), (float)0.6))
+          using (SolidBrush myBrush = new SolidBrush(Color.FromArgb(127, 0xDD, 0xDD, 0xF0)))
+          {
+            newgraphic.FillPath(myBrush, path);
+            newgraphic.DrawPath(myPen, path);
+            path.Reset();
+
+            //draw clips ...
+            if (rbNonZero.Checked)
+              path.FillMode = FillMode.Winding;
+            foreach (Polygon pg in clips)
+            {
+              PointF[] pts = PolygonToPointFArray(pg, scale);
+              path.AddPolygon(pts);
+              pts = null;
+            }
+            myPen.Color = Color.FromArgb(196, 0xF9, 0xBE, 0xA6);
+            myBrush.Color = Color.FromArgb(127, 0xFF, 0xE0, 0xE0);
+            newgraphic.FillPath(myBrush, path);
+            newgraphic.DrawPath(myPen, path);
+
+            //do the clipping ...
+            if ((clips.Count > 0 || subjects.Count > 0) && !rbNone.Checked)
+            {
+              Polygons solution2 = new Polygons();
+              Clipper c = new Clipper();
+              c.AddPaths(subjects, PolyType.ptSubject, true);
+              c.AddPaths(clips, PolyType.ptClip, true);
+              solution.Clear();
+#if UsePolyTree
+              bool succeeded = c.Execute(GetClipType(), solutionTree, GetPolyFillType(), GetPolyFillType());
+              //nb: we aren't doing anything useful here with solutionTree except to show
+              //that it works. Convert PolyTree back to Polygons structure ...
+              Clipper.PolyTreeToPolygons(solutionTree, solution);
+#else
+              bool succeeded = c.Execute(GetClipType(), solution, GetPolyFillType(), GetPolyFillType());
+#endif
+              if (succeeded)
+              {
+                //SaveToFile("solution", solution);
+                myBrush.Color = Color.Black;
+                path.Reset();
+
+                //It really shouldn't matter what FillMode is used for solution
+                //polygons because none of the solution polygons overlap. 
+                //However, FillMode.Winding will show any orientation errors where 
+                //holes will be stroked (outlined) correctly but filled incorrectly  ...
+                path.FillMode = FillMode.Winding;
+
+                //or for something fancy ...
+
+                if (nudOffset.Value != 0)
+                {
+                  ClipperOffset co = new ClipperOffset();
+                  co.AddPaths(solution, JoinType.jtRound, EndType.etClosedPolygon);
+                  co.Execute(ref solution2, (double)nudOffset.Value * scale);
+                }
+                else
+                  solution2 = new Polygons(solution);
+
+                foreach (Polygon pg in solution2)
+                {
+                  PointF[] pts = PolygonToPointFArray(pg, scale);
+                  if (pts.Count() > 2)
+                    path.AddPolygon(pts);
+                  pts = null;
+                }
+                myBrush.Color = Color.FromArgb(127, 0x66, 0xEF, 0x7F);
+                myPen.Color = Color.FromArgb(255, 0, 0x33, 0);
+                myPen.Width = 1.0f;
+                newgraphic.FillPath(myBrush, path);
+                newgraphic.DrawPath(myPen, path);
+
+                //now do some fancy testing ...
+                using (Font f = new Font("Arial", 8))
+                using (SolidBrush b = new SolidBrush(Color.Navy))
+                {
+                  double subj_area = 0, clip_area = 0, int_area = 0, union_area = 0;
+                  c.Clear();
+                  c.AddPaths(subjects, PolyType.ptSubject, true);
+                  c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType());
+                  foreach (Polygon pg in solution2)
+                    subj_area += Clipper.Area(pg);
+                  c.Clear();
+                  c.AddPaths(clips, PolyType.ptClip, true);
+                  c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType());
+                  foreach (Polygon pg in solution2)
+                    clip_area += Clipper.Area(pg);
+                  c.AddPaths(subjects, PolyType.ptSubject, true);
+                  c.Execute(ClipType.ctIntersection, solution2, GetPolyFillType(), GetPolyFillType());
+                  foreach (Polygon pg in solution2)
+                    int_area += Clipper.Area(pg);
+                  c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType());
+                  foreach (Polygon pg in solution2)
+                    union_area += Clipper.Area(pg);
+
+                  using (StringFormat lftStringFormat = new StringFormat())
+                  using (StringFormat rtStringFormat = new StringFormat())
+                  {
+                    lftStringFormat.Alignment = StringAlignment.Near;
+                    lftStringFormat.LineAlignment = StringAlignment.Near;
+                    rtStringFormat.Alignment = StringAlignment.Far;
+                    rtStringFormat.LineAlignment = StringAlignment.Near;
+                    Rectangle rec = new Rectangle(pictureBox1.ClientSize.Width - 108,
+                                     pictureBox1.ClientSize.Height - 116, 104, 106);
+                    newgraphic.FillRectangle(new SolidBrush(Color.FromArgb(196, Color.WhiteSmoke)), rec);
+                    newgraphic.DrawRectangle(myPen, rec);
+                    rec.Inflate(new Size(-2, 0));
+                    newgraphic.DrawString("Areas", f, b, rec, rtStringFormat);
+                    rec.Offset(new Point(0, 14));
+                    newgraphic.DrawString("subj: ", f, b, rec, lftStringFormat);
+                    newgraphic.DrawString((subj_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat);
+                    rec.Offset(new Point(0, 12));
+                    newgraphic.DrawString("clip: ", f, b, rec, lftStringFormat);
+                    newgraphic.DrawString((clip_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat);
+                    rec.Offset(new Point(0, 12));
+                    newgraphic.DrawString("intersect: ", f, b, rec, lftStringFormat);
+                    newgraphic.DrawString((int_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat);
+                    rec.Offset(new Point(0, 12));
+                    newgraphic.DrawString("---------", f, b, rec, rtStringFormat);
+                    rec.Offset(new Point(0, 10));
+                    newgraphic.DrawString("s + c - i: ", f, b, rec, lftStringFormat);
+                    newgraphic.DrawString(((subj_area + clip_area - int_area) / 100000).ToString("0,0"), f, b, rec, rtStringFormat);
+                    rec.Offset(new Point(0, 10));
+                    newgraphic.DrawString("---------", f, b, rec, rtStringFormat);
+                    rec.Offset(new Point(0, 10));
+                    newgraphic.DrawString("union: ", f, b, rec, lftStringFormat);
+                    newgraphic.DrawString((union_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat);
+                    rec.Offset(new Point(0, 10));
+                    newgraphic.DrawString("---------", f, b, rec, rtStringFormat);
+                  }
+                }
+              } //end if succeeded
+            } //end if something to clip
+            pictureBox1.Image = mybitmap;
+          }
+        }
+      }
+      finally
+      {
+        Cursor.Current = Cursors.Default;
+      }
+    }
+    //---------------------------------------------------------------------
+
+    private void Form1_Load(object sender, EventArgs e)
+    {
+      toolStripStatusLabel1.Text =
+          "Tip: Use the mouse-wheel (or +,-,0) to adjust the offset of the solution polygons.";
+      DrawBitmap();
+    }
+    //---------------------------------------------------------------------
+
+    private void bClose_Click(object sender, EventArgs e)
+    {
+      Close();
+    }
+    //---------------------------------------------------------------------
+
+    private void Form1_Resize(object sender, EventArgs e)
+    {
+      if (pictureBox1.ClientRectangle.Width == 0 ||
+          pictureBox1.ClientRectangle.Height == 0) return;
+      if (mybitmap != null)
+        mybitmap.Dispose();
+      mybitmap = new Bitmap(
+          pictureBox1.ClientRectangle.Width,
+          pictureBox1.ClientRectangle.Height,
+          PixelFormat.Format32bppArgb);
+      pictureBox1.Image = mybitmap;
+      DrawBitmap();
+    }
+    //---------------------------------------------------------------------
+
+    private void rbNonZero_Click(object sender, EventArgs e)
+    {
+      DrawBitmap(true);
+    }
+    //---------------------------------------------------------------------
+
+    private void Form1_KeyDown(object sender, KeyEventArgs e)
+    {
+      switch (e.KeyCode)
+      {
+        case Keys.Escape:
+          this.Close();
+          return;
+        case Keys.F1:
+          MessageBox.Show(this.Text + "\nby Angus Johnson\nCopyright © 2010, 2011",
+          this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
+          e.Handled = true;
+          return;
+        case Keys.Oemplus:
+        case Keys.Add:
+          if (nudOffset.Value == 10) return;
+          nudOffset.Value += (decimal)0.5;
+          e.Handled = true;
+          break;
+        case Keys.OemMinus:
+        case Keys.Subtract:
+          if (nudOffset.Value == -10) return;
+          nudOffset.Value -= (decimal)0.5;
+          e.Handled = true;
+          break;
+        case Keys.NumPad0:
+        case Keys.D0:
+          if (nudOffset.Value == 0) return;
+          nudOffset.Value = (decimal)0;
+          e.Handled = true;
+          break;
+        default: return;
+      }
+
+    }
+    //---------------------------------------------------------------------
+
+    private void nudCount_ValueChanged(object sender, EventArgs e)
+    {
+      DrawBitmap(true);
+    }
+    //---------------------------------------------------------------------
+
+    private void rbTest1_Click(object sender, EventArgs e)
+    {
+      if (rbTest1.Checked)
+        lblCount.Text = "Vertex &Count:";
+      else
+        lblCount.Text = "Ellipse &Count:";
+      DrawBitmap();
+    }
+    //---------------------------------------------------------------------
+
+    private void bSave_Click(object sender, EventArgs e)
+    {
+      //save to SVG ...
+      if (saveFileDialog1.ShowDialog() == DialogResult.OK)
+      {
+        SVGBuilder svg = new SVGBuilder();
+        svg.style.brushClr = Color.FromArgb(0x10, 0, 0, 0x9c);
+        svg.style.penClr = Color.FromArgb(0xd3, 0xd3, 0xda);
+        svg.AddPolygons(subjects);
+        svg.style.brushClr = Color.FromArgb(0x10, 0x9c, 0, 0);
+        svg.style.penClr = Color.FromArgb(0xff, 0xa0, 0x7a);
+        svg.AddPolygons(clips);
+        svg.style.brushClr = Color.FromArgb(0xAA, 0x80, 0xff, 0x9c);
+        svg.style.penClr = Color.FromArgb(0, 0x33, 0);
+        svg.AddPolygons(solution);
+        svg.SaveToFile(saveFileDialog1.FileName, 1.0 / scale);
+      }
+    }
+    //---------------------------------------------------------------------
+
+  }
+}
diff --git a/C#/GuiDemo/GuiDemo/Form1.resx b/C#/GuiDemo/GuiDemo/Form1.resx
new file mode 100644
index 0000000..2f4b5d7
--- /dev/null
+++ b/C#/GuiDemo/GuiDemo/Form1.resx
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <metadata name="saveFileDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>127, 17</value>
+  </metadata>
+  <metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>25</value>
+  </metadata>
+  <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+  <data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        AAABAAIAICAAAAAAAACoEAAAJgAAABAQAAAAAAAAaAQAAM4QAAAoAAAAIAAAAEAAAAABACAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7A72bOwO9EwAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwO9YzsDvf8AAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADsDvTM8Bb35PAS9/AAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMFRAxPBUQM3wVEDT8FRA0+dPDVGLAWG6zsD
+        vf8+B77jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwO9AwAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwVEDD8FRA3fBUgXJwVED/8FRA//BUQP/v1AF/y4M
+        RP8AAAD/Eg0m/y4HevTBUQMHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAA7A71TOwO9nzsDvT8AAAAAAAAAAAAAAAAAAAAAAAAAAMFRA2vBUgXtwVED/8VhGPzQik7/16Rw/9uz
+        hP9YOWf/AAAA/yZIS/8LFRb/AAAA/6dHBOXBUQNXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAA7A72zOwO9/z0GvuI7A71/NgOsIhMIAFPBUgS0wVED/8hqJP3bs4T/5ty6/+bc
+        uv/m3Lr/i3iT/wICBv8kREf/eOPs/1uttP8AAAD/rlgZ/cFRA//BUQObwVEDAwAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAADsDvRM8Bb3tOwO9/zsDvf8eAl//AAAA/xsNEf+NZUb/5ty6/+bc
+        uv/m3Lr/5ty6/7ytqf8HBBL/FSUp/3Tc5f955u//W620/wAAAP/Kv6L/0YxR/8FRA//CUwbCwVEDAwAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADsDvVM7A73/VDan/wICA/8cNTj/CxUW/wAA
+        AP8XEiT/gnOB/+bcuv/VyrL/Egon/wkQEv9qytL/eebv/3nm7/9brbT/AAAA/8rBo//m3Lr/2KZz/8FR
+        A//BUQOTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADoDuqwOAir/Dhga/3LY
+        4f924Oj/RIKH/wsVFv8AAAD/EAkl/xsPNv8CAwT/W620/3nm7/955u//eebv/1uttP8AAAD/ysGj/+bc
+        uv/m3Lr/0YxR/8FRA//BUQNLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGggZZgAA
+        AP87cHT/eebv/3nm7/955u//duDo/0SCh/8LFRb/AAAA/0qNkv955u//eebv/3nm7/955u//W620/wAA
+        AP/KwaP/5ty6/+bcuv/m2rj/xmMb/MFSBNjBUQMDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAC8TwN5KxER/wUHCv9jvMP/eebv/3nm7/955u//eebv/3bg6P9Voqn/eebv/3nm7/955u//eebv/3nm
+        7/9brbT/AAAA/8rBo//m3Lr/5ty6/+bcuv/Zqnn/wVED/8FRA1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAMFSBcm9aSj6EQ0b/w4XHf9y2OH/eebv/3nm7/955u//eebv/3nm7/955u//eebv/3nm
+        7/955u//eebv/1uttP8AAAD/oI2h/+bcuv/m3Lr/5ty6/+bcuv/EXBL7wVEDpwAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAADBUQMTwVED/9SXX/+6r57/BAIJ/yE8Qv944+z/eebv/3nm7/955u//eebv/3nm
+        7/955u//eebv/3nm7/955u//W620/wAAAP8EBAr/GAs5/1M/cP+xoaT/5ty6/8+FSP/BUgT1AAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMFRAzvBUQP/27OE/+bcuv+FdYT/AAAA/ztvdP955u//eebv/3nm
+        7/955u//eebv/3nm7/955u//eebv/3nm7/924Oj/Upuh/y9aXv8NGRr/AAAA/wAAAP8OCxz/Pic9/4M1
+        Dv+uSQMeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwVEDU8FRA//fwpj/5ty6/+TZuv8cDTv/AAAA/2bD
+        yv955u//eebv/3nm7/955u//eebv/3nm7/955u//eebv/3nm7/955u//eebv/3nm7/9lv8f/Qn6D/yA9
+        QP8EBgf/AAAA/zsIlcw7A71bOwO9BwAAAAAAAAAAAAAAAAAAAADBUQNTwVED/9/CmP/m3Lr/Xkh8/wAA
+        AP8xXmH/eebv/3nm7/955u//eebv/3nm7/955u//eebv/3nm7/955u//eebv/3nm7/955u//eebv/3nm
+        7/955u//eebv/1emrP8AAAD/NAOm/zsDvf87A73zOwO9jwAAAAAAAAAAAAAAAMFRAzvBUQP/27OE/4Vw
+        k/8DAwn/ID1A/3jj7P955u//eebv/3nm7/955u//eebv/3nm7/955u//eebv/3nm7/955u//eebv/3nm
+        7/903OX/V6as/zlscP8aMTT/AgME/wAAAP83CJ3POwO9dzsDvTcAAAAAAAAAAAAAAAAAAAAAwVEDE8FR
+        A/+ibGb/BwQS/xMiJf903OX/eebv/3nm7/955u//eebv/3nm7/955u//eebv/3nm7/955u//duDo/1em
+        rP85bHD/GjE0/wIDBP8AAAD/AgED/x4XKv9OLjH/hTgJ+AAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAqUUZ0A0EH/8LExb/aMbO/3nm7/955u//eebv/3nm7/955u//eebv/3nm7/955u//eebv/3nm
+        7/9brbT/AAAA/wEBA/8bFCr/UENY/5uQhv/c07L/5ty6/8RcEvvBUQOnAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAACkChAoVBS72AgME/12xuP955u//eebv/3nm7/955u//duDo/1+0u/955u//eebv/3nm
+        7/955u//eebv/1uttP8AAAD/w7qe/+bcuv/m3Lr/5ty6/+bcuv/Zqnn/wVED/8FRA1cAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAIAJnwAAAAP8/d3z/eebv/3nm7/9mw8r/RoWL/yJAQ/8DBgf/AAAA/1Wi
+        qf955u//eebv/3nm7/955u//W620/wAAAP/KwaP/5ty6/+bcuv/m3Lr/5tq4/8ZjG/zBUgTYwVEDAwAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAADsDvXM4A7L/BgMM/xowNP8xXmH/Dhod/wAAAP8AAAD/FRIb/1pS
+        Uv8uKyz/DBQZ/3LY4f955u//eebv/3nm7/9brbT/AAAA/8rBo//m3Lr/5ty6/+bcuv/RjFH/wVED/8FR
+        A0sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7A71HPAS9/DsDvf8eAl//AAAA/wIBAv8mDw//dV9L/8a+
+        oP/m3Lr/5ty6/8rBo/8GBgn/LVRZ/3nm7/955u//eebv/1uttP8AAAD/ysGj/+bcuv/m3Lr/2KZz/8FR
+        A//BUQOTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwO9JzsDvec7A723OwO9aykCgjAAAABXYCgBFsJU
+        BtvBUwX+1Ztl/+bcuv/m3Lr/5ty6/4N8bP8AAAD/VaKp/3nm7/955u//W620/wAAAP/KwaP/5tq4/9GO
+        U//BUQP/wlMGwsFRAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7A70jOwO9BwAAAAAAAAAAAAAAAAAA
+        AAAAAAAAwVEDC8FSBLTBUQP/ynMw/t26jf/m3Lr/49m4/yomKf8MFBn/ctjh/3nm7/9brbT/AAAA/8Cb
+        cf/IaiT9wVED/8FRA5vBUQMDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMFRA2vBUgXxwVED/8ZjG/zQik7/totf/wQDBv8pTVL/W620/0SC
+        h/8AAAD/qkcD/8FTBebBUQNXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMFRAw/BUQN3wVIFycFRA//BUQP/ZysG/wAA
+        AP8AAAD/AAAA/wAAAP+RPQJ9wVEDBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwVEDE8FR
+        Azu6TgNVSBdAnywCjv8sAo7/JAJynwAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAOwO9nzsDvf87A71/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7A70PPAS95DsDvXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7A71TOwO9XwAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//8////+f////H///4B/+/wAP/j4AB/8AA
+        AH/AAAA/4AAAP/AAAB/wAAAP8AAAD/AAAA/gAAAP4AAAB+AAAAHgAAAA4AAAAeAAAAfwAAAP4AAAD+AA
+        AA/AAAAfgAAAPwAAAD8+AAB//4AB///AA///+Af///+P////j////8//KAAAABAAAAAgAAAAAQAgAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADsD
+        vRk4A6/kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADBUQMFwVEDIrBH
+        GiU3BK3GOASv5gAAAAAAAAAAAAAAAAAAAAAAAAAAOwO9FjgDrts4BK5VAAAAAMFRAxvBUQSdxmQd8c1+
+        P/9RJSz/ERsi/0UZKrjBUQMWAAAAAAAAAAAAAAAAAAAAAAAAAAA4BK/wOASw9xICMJ2GRBns46OF/+Oj
+        hf+NgYL/LFJX/2rJ0f9eRi//xWEZ5sJTBjIAAAAAAAAAAAAAAAAAAAAAOwO9FTURjuooSk7/MV5h/ykn
+        L/96cHL/HjM8/3Xf6P9qytL/ZWFS/+Ojhf/FYRnkwVEDEwAAAAAAAAAAAAAAAAAAAAAyFAq3R4aM/3nm
+        7/9rzNP/NmZq/23Q2P955u//asrS/2VhUv/jo4X/46OF/8FRA4wAAAAAAAAAAAAAAADBUQMFxWol8Dc1
+        OP9ht8D/eebv/3nm7/955u//eebv/2rK0v8pJCv/joGH/+Ojhf/GYxvmAAAAAAAAAAAAAAAAwVEDJM+G
+        Sf/jo4X/Fh8s/3Td5v955u//eebv/3nm7/945e3/XbC3/ztwdP8cMjj/MRkV/zgErN87A70CAAAAAMFR
+        AyTPhkn/c2Z1/zJgY/955u//eebv/3nm7/955u//eebv/3nm7/9v1Nz/UZqh/xYqLP84BK7ROAOv5AAA
+        AADBUQMFhUEp8yNAR/945O3/eebv/3nm7/955u//eebv/0qNkv8cLTT/OzY5/3lyZv+SRBXmAAAAAQAA
+        AAAAAAAAIAJoMxYgK/1y2eH/aMXN/0WDiP9Lj5X/eebv/3nm7/8uV1r/46OF/+Ojhf/jo4X/wVEDjAAA
+        AAAAAAAAOwO9EjoDutsQDSj/GiIk/09HO/+Ph3j/Q0I8/2S+xv955u//Llda/+Ojhf/jo4X/xWEZ5MFR
+        AxMAAAAAAAAAADgDrt84A67eDwEuIrlQBT/GZh7s46OF/+Ojhf8jNzv/d+Ps/y5XWv/jo4X/xWIZ5sJT
+        BjIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwVEDG8FRBJ7GZR7xeUMc/yE/Qv8RISL/rkoEmsFR
+        AxYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwVEDBb1PAyQ4BKnqNwOr9AAA
+        AAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwO9BDgD
+        r+gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/nwAA/B8AABAPAACABwAAgAMAAMADAACAAwAAgAAAAIAA
+        AACAAQAAgAMAAAADAAAABwAA8A8AAPwfAAD/PwAA
+</value>
+  </data>
+</root>
\ No newline at end of file
diff --git a/C#/GuiDemo/GuiDemo/GuiDemo.csproj b/C#/GuiDemo/GuiDemo/GuiDemo.csproj
new file mode 100644
index 0000000..d592685
--- /dev/null
+++ b/C#/GuiDemo/GuiDemo/GuiDemo.csproj
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{8BD44147-3290-4A73-BAA2-1C171566BC25}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>GuiDemo</RootNamespace>
+    <AssemblyName>GuiDemo</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <TargetFrameworkProfile>
+    </TargetFrameworkProfile>
+    <FileAlignment>512</FileAlignment>
+    <IsWebBootstrapper>false</IsWebBootstrapper>
+    <PublishUrl>publish\</PublishUrl>
+    <Install>true</Install>
+    <InstallFrom>Disk</InstallFrom>
+    <UpdateEnabled>false</UpdateEnabled>
+    <UpdateMode>Foreground</UpdateMode>
+    <UpdateInterval>7</UpdateInterval>
+    <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+    <UpdatePeriodically>false</UpdatePeriodically>
+    <UpdateRequired>false</UpdateRequired>
+    <MapFileExtensions>true</MapFileExtensions>
+    <ApplicationRevision>0</ApplicationRevision>
+    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+    <UseApplicationTrust>false</UseApplicationTrust>
+    <BootstrapperEnabled>true</BootstrapperEnabled>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Deployment" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Form1.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Form1.Designer.cs">
+      <DependentUpon>Form1.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Settings.cs" />
+    <EmbeddedResource Include="Form1.resx">
+      <DependentUpon>Form1.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+      <SubType>Designer</SubType>
+    </EmbeddedResource>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Resources.resx</DependentUpon>
+      <DesignTime>True</DesignTime>
+    </Compile>
+    <EmbeddedResource Include="aust.bin" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
+      <Visible>False</Visible>
+      <ProductName>Microsoft .NET Framework 4 Client Profile %28x86 and x64%29</ProductName>
+      <Install>true</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+      <Visible>False</Visible>
+      <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+      <Install>false</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+      <Visible>False</Visible>
+      <ProductName>.NET Framework 3.5 SP1</ProductName>
+      <Install>false</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
+      <Visible>False</Visible>
+      <ProductName>Windows Installer 3.1</ProductName>
+      <Install>true</Install>
+    </BootstrapperPackage>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\clipper_library\clipper_library.csproj">
+      <Project>{9B062971-A88E-4A3D-B3C9-12B78D15FA66}</Project>
+      <Name>clipper_library</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
diff --git a/C#/GuiDemo/GuiDemo/Program.cs b/C#/GuiDemo/GuiDemo/Program.cs
new file mode 100644
index 0000000..cc4a2c7
--- /dev/null
+++ b/C#/GuiDemo/GuiDemo/Program.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace WindowsFormsApplication1
+{
+    static class Program
+    {
+        /// <summary>
+        /// The main entry point for the application.
+        /// </summary>
+        [STAThread]
+        static void Main()
+        {
+            Application.EnableVisualStyles();
+            Application.SetCompatibleTextRenderingDefault(false);
+            Application.Run(new Form1());
+        }
+    }
+}
diff --git a/C#/GuiDemo/GuiDemo/Properties/AssemblyInfo.cs b/C#/GuiDemo/GuiDemo/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..eb3329d
--- /dev/null
+++ b/C#/GuiDemo/GuiDemo/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ClipperCSharpDemo1")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Angus Johnson")]
+[assembly: AssemblyProduct("ClipperCSharpDemo1")]
+[assembly: AssemblyCopyright("Copyright © Angus Johnson 2010-14")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("7eac3b0b-6f6f-4b48-b26b-c7acb1f769f5")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/C#/GuiDemo/GuiDemo/Properties/Resources.Designer.cs b/C#/GuiDemo/GuiDemo/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..84210c0
--- /dev/null
+++ b/C#/GuiDemo/GuiDemo/Properties/Resources.Designer.cs
@@ -0,0 +1,70 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.235
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace GuiDemo.Properties {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GuiDemo.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        internal static byte[] aust {
+            get {
+                object obj = ResourceManager.GetObject("aust", resourceCulture);
+                return ((byte[])(obj));
+            }
+        }
+    }
+}
diff --git a/C#/GuiDemo/GuiDemo/Properties/Resources.resx b/C#/GuiDemo/GuiDemo/Properties/Resources.resx
new file mode 100644
index 0000000..4ab87d2
--- /dev/null
+++ b/C#/GuiDemo/GuiDemo/Properties/Resources.resx
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="aust" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\aust.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+</root>
\ No newline at end of file
diff --git a/C#/GuiDemo/GuiDemo/Properties/Settings.Designer.cs b/C#/GuiDemo/GuiDemo/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..e31c5b7
--- /dev/null
+++ b/C#/GuiDemo/GuiDemo/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.235
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace GuiDemo.Properties {
+    
+    
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+        
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+        
+        public static Settings Default {
+            get {
+                return defaultInstance;
+            }
+        }
+    }
+}
diff --git a/C#/GuiDemo/GuiDemo/Properties/Settings.settings b/C#/GuiDemo/GuiDemo/Properties/Settings.settings
new file mode 100644
index 0000000..abf36c5
--- /dev/null
+++ b/C#/GuiDemo/GuiDemo/Properties/Settings.settings
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+  <Settings />
+</SettingsFile>
diff --git a/C#/GuiDemo/GuiDemo/Settings.cs b/C#/GuiDemo/GuiDemo/Settings.cs
new file mode 100644
index 0000000..dfa1e2a
--- /dev/null
+++ b/C#/GuiDemo/GuiDemo/Settings.cs
@@ -0,0 +1,28 @@
+namespace WindowsFormsApplication1.Properties {
+    
+    
+    // This class allows you to handle specific events on the settings class:
+    //  The SettingChanging event is raised before a setting's value is changed.
+    //  The PropertyChanged event is raised after a setting's value is changed.
+    //  The SettingsLoaded event is raised after the setting values are loaded.
+    //  The SettingsSaving event is raised before the setting values are saved.
+    internal sealed partial class Settings {
+        
+        public Settings() {
+            // // To add event handlers for saving and changing settings, uncomment the lines below:
+            //
+            // this.SettingChanging += this.SettingChangingEventHandler;
+            //
+            // this.SettingsSaving += this.SettingsSavingEventHandler;
+            //
+        }
+        
+        private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) {
+            // Add code to handle the SettingChangingEvent event here.
+        }
+        
+        private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) {
+            // Add code to handle the SettingsSaving event here.
+        }
+    }
+}
diff --git a/C#/GuiDemo/GuiDemo/aust.bin b/C#/GuiDemo/GuiDemo/aust.bin
new file mode 100644
index 0000000..9d3bd08
Binary files /dev/null and b/C#/GuiDemo/GuiDemo/aust.bin differ
diff --git a/C#/clipper_library/Properties/AssemblyInfo.cs b/C#/clipper_library/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..6586d04
--- /dev/null
+++ b/C#/clipper_library/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("clipper_library")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Angus Johnson")]
+[assembly: AssemblyProduct("clipper_library")]
+[assembly: AssemblyCopyright("Copyright © Angus Johnson 2010-14")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("51a6bdca-bc4e-4b2c-ae69-36e2497204f2")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/C#/clipper_library/clipper.cs b/C#/clipper_library/clipper.cs
new file mode 100644
index 0000000..e3613ff
--- /dev/null
+++ b/C#/clipper_library/clipper.cs
@@ -0,0 +1,4839 @@
+/*******************************************************************************
+*                                                                              *
+* Author    :  Angus Johnson                                                   *
+* Version   :  6.2.9                                                           *
+* Date      :  16 February 2015                                                *
+* Website   :  http://www.angusj.com                                           *
+* Copyright :  Angus Johnson 2010-2015                                         *
+*                                                                              *
+* License:                                                                     *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt                                         *
+*                                                                              *
+* Attributions:                                                                *
+* The code in this library is an extension of Bala Vatti's clipping algorithm: *
+* "A generic solution to polygon clipping"                                     *
+* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63.             *
+* http://portal.acm.org/citation.cfm?id=129906                                 *
+*                                                                              *
+* Computer graphics and geometric modeling: implementation and algorithms      *
+* By Max K. Agoston                                                            *
+* Springer; 1 edition (January 4, 2005)                                        *
+* http://books.google.com/books?q=vatti+clipping+agoston                       *
+*                                                                              *
+* See also:                                                                    *
+* "Polygon Offsetting by Computing Winding Numbers"                            *
+* Paper no. DETC2005-85513 pp. 565-575                                         *
+* ASME 2005 International Design Engineering Technical Conferences             *
+* and Computers and Information in Engineering Conference (IDETC/CIE2005)      *
+* September 24-28, 2005 , Long Beach, California, USA                          *
+* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf              *
+*                                                                              *
+*******************************************************************************/
+
+/*******************************************************************************
+*                                                                              *
+* This is a translation of the Delphi Clipper library and the naming style     *
+* used has retained a Delphi flavour.                                          *
+*                                                                              *
+*******************************************************************************/
+
+//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
+//improve performance but coordinate values are limited to the range +/- 46340
+//#define use_int32
+
+//use_xyz: adds a Z member to IntPoint. Adds a minor cost to performance.
+//#define use_xyz
+
+//use_lines: Enables open path clipping. Adds a very minor cost to performance.
+#define use_lines
+
+
+using System;
+using System.Collections.Generic;
+//using System.Text;          //for Int128.AsString() & StringBuilder
+//using System.IO;            //debugging with streamReader & StreamWriter
+//using System.Windows.Forms; //debugging to clipboard
+
+namespace ClipperLib
+{
+
+#if use_int32
+  using cInt = Int32;
+#else
+  using cInt = Int64;
+#endif
+
+  using Path = List<IntPoint>;
+  using Paths = List<List<IntPoint>>;
+
+  public struct DoublePoint
+  {
+    public double X;
+    public double Y;
+
+    public DoublePoint(double x = 0, double y = 0)
+    {
+      this.X = x; this.Y = y;
+    }
+    public DoublePoint(DoublePoint dp)
+    {
+      this.X = dp.X; this.Y = dp.Y;
+    }
+    public DoublePoint(IntPoint ip)
+    {
+      this.X = ip.X; this.Y = ip.Y;
+    }
+  };
+
+
+  //------------------------------------------------------------------------------
+  // PolyTree & PolyNode classes
+  //------------------------------------------------------------------------------
+
+  public class PolyTree : PolyNode
+  {
+      internal List<PolyNode> m_AllPolys = new List<PolyNode>();
+
+      //The GC probably handles this cleanup more efficiently ...
+      //~PolyTree(){Clear();}
+        
+      public void Clear() 
+      {
+          for (int i = 0; i < m_AllPolys.Count; i++)
+              m_AllPolys[i] = null;
+          m_AllPolys.Clear(); 
+          m_Childs.Clear(); 
+      }
+        
+      public PolyNode GetFirst()
+      {
+          if (m_Childs.Count > 0)
+              return m_Childs[0];
+          else
+              return null;
+      }
+
+      public int Total
+      {
+          get 
+          { 
+            int result = m_AllPolys.Count;
+            //with negative offsets, ignore the hidden outer polygon ...
+            if (result > 0 && m_Childs[0] != m_AllPolys[0]) result--;
+            return result;
+          }
+      }
+
+  }
+        
+  public class PolyNode 
+  {
+      internal PolyNode m_Parent;
+      internal Path m_polygon = new Path();
+      internal int m_Index;
+      internal JoinType m_jointype;
+      internal EndType m_endtype;
+      internal List<PolyNode> m_Childs = new List<PolyNode>();
+
+      private bool IsHoleNode()
+      {
+          bool result = true;
+          PolyNode node = m_Parent;
+          while (node != null)
+          {
+              result = !result;
+              node = node.m_Parent;
+          }
+          return result;
+      }
+
+      public int ChildCount
+      {
+          get { return m_Childs.Count; }
+      }
+
+      public Path Contour
+      {
+          get { return m_polygon; }
+      }
+
+      internal void AddChild(PolyNode Child)
+      {
+          int cnt = m_Childs.Count;
+          m_Childs.Add(Child);
+          Child.m_Parent = this;
+          Child.m_Index = cnt;
+      }
+
+      public PolyNode GetNext()
+      {
+          if (m_Childs.Count > 0) 
+              return m_Childs[0]; 
+          else
+              return GetNextSiblingUp();        
+      }
+  
+      internal PolyNode GetNextSiblingUp()
+      {
+          if (m_Parent == null)
+              return null;
+          else if (m_Index == m_Parent.m_Childs.Count - 1)
+              return m_Parent.GetNextSiblingUp();
+          else
+              return m_Parent.m_Childs[m_Index + 1];
+      }
+
+      public List<PolyNode> Childs
+      {
+          get { return m_Childs; }
+      }
+
+      public PolyNode Parent
+      {
+          get { return m_Parent; }
+      }
+
+      public bool IsHole
+      {
+          get { return IsHoleNode(); }
+      }
+
+      public bool IsOpen { get; set; }
+  }
+
+
+  //------------------------------------------------------------------------------
+  // Int128 struct (enables safe math on signed 64bit integers)
+  // eg Int128 val1((Int64)9223372036854775807); //ie 2^63 -1
+  //    Int128 val2((Int64)9223372036854775807);
+  //    Int128 val3 = val1 * val2;
+  //    val3.ToString => "85070591730234615847396907784232501249" (8.5e+37)
+  //------------------------------------------------------------------------------
+
+  internal struct Int128
+  {
+    private Int64 hi;
+    private UInt64 lo;
+
+    public Int128(Int64 _lo)
+    {
+      lo = (UInt64)_lo;
+      if (_lo < 0) hi = -1;
+      else hi = 0;
+    }
+
+    public Int128(Int64 _hi, UInt64 _lo)
+    {
+      lo = _lo;
+      hi = _hi;
+    }
+
+    public Int128(Int128 val)
+    {
+      hi = val.hi;
+      lo = val.lo;
+    }
+
+    public bool IsNegative()
+    {
+      return hi < 0;
+    }
+
+    public static bool operator ==(Int128 val1, Int128 val2)
+    {
+      if ((object)val1 == (object)val2) return true;
+      else if ((object)val1 == null || (object)val2 == null) return false;
+      return (val1.hi == val2.hi && val1.lo == val2.lo);
+    }
+
+    public static bool operator !=(Int128 val1, Int128 val2)
+    {
+      return !(val1 == val2);
+    }
+
+    public override bool Equals(System.Object obj)
+    {
+      if (obj == null || !(obj is Int128))
+        return false;
+      Int128 i128 = (Int128)obj;
+      return (i128.hi == hi && i128.lo == lo);
+    }
+
+    public override int GetHashCode()
+    {
+      return hi.GetHashCode() ^ lo.GetHashCode();
+    }
+
+    public static bool operator >(Int128 val1, Int128 val2)
+    {
+      if (val1.hi != val2.hi)
+        return val1.hi > val2.hi;
+      else
+        return val1.lo > val2.lo;
+    }
+
+    public static bool operator <(Int128 val1, Int128 val2)
+    {
+      if (val1.hi != val2.hi)
+        return val1.hi < val2.hi;
+      else
+        return val1.lo < val2.lo;
+    }
+
+    public static Int128 operator +(Int128 lhs, Int128 rhs)
+    {
+      lhs.hi += rhs.hi;
+      lhs.lo += rhs.lo;
+      if (lhs.lo < rhs.lo) lhs.hi++;
+      return lhs;
+    }
+
+    public static Int128 operator -(Int128 lhs, Int128 rhs)
+    {
+      return lhs + -rhs;
+    }
+
+    public static Int128 operator -(Int128 val)
+    {
+      if (val.lo == 0)
+        return new Int128(-val.hi, 0);
+      else
+        return new Int128(~val.hi, ~val.lo + 1);
+    }
+
+    public static explicit operator double(Int128 val)
+    {
+      const double shift64 = 18446744073709551616.0; //2^64
+      if (val.hi < 0)
+      {
+        if (val.lo == 0)
+          return (double)val.hi * shift64;
+        else
+          return -(double)(~val.lo + ~val.hi * shift64);
+      }
+      else
+        return (double)(val.lo + val.hi * shift64);
+    }
+    
+    //nb: Constructing two new Int128 objects every time we want to multiply longs  
+    //is slow. So, although calling the Int128Mul method doesn't look as clean, the 
+    //code runs significantly faster than if we'd used the * operator.
+
+    public static Int128 Int128Mul(Int64 lhs, Int64 rhs)
+    {
+      bool negate = (lhs < 0) != (rhs < 0);
+      if (lhs < 0) lhs = -lhs;
+      if (rhs < 0) rhs = -rhs;
+      UInt64 int1Hi = (UInt64)lhs >> 32;
+      UInt64 int1Lo = (UInt64)lhs & 0xFFFFFFFF;
+      UInt64 int2Hi = (UInt64)rhs >> 32;
+      UInt64 int2Lo = (UInt64)rhs & 0xFFFFFFFF;
+
+      //nb: see comments in clipper.pas
+      UInt64 a = int1Hi * int2Hi;
+      UInt64 b = int1Lo * int2Lo;
+      UInt64 c = int1Hi * int2Lo + int1Lo * int2Hi;
+
+      UInt64 lo;
+      Int64 hi;
+      hi = (Int64)(a + (c >> 32));
+
+      unchecked { lo = (c << 32) + b; }
+      if (lo < b) hi++;
+      Int128 result = new Int128(hi, lo);
+      return negate ? -result : result;
+    }
+
+  };
+
+  //------------------------------------------------------------------------------
+  //------------------------------------------------------------------------------
+
+  public struct IntPoint
+  {
+    public cInt X;
+    public cInt Y;
+#if use_xyz
+    public cInt Z;
+    
+    public IntPoint(cInt x, cInt y, cInt z = 0)
+    {
+      this.X = x; this.Y = y; this.Z = z;
+    }
+    
+    public IntPoint(double x, double y, double z = 0)
+    {
+      this.X = (cInt)x; this.Y = (cInt)y; this.Z = (cInt)z;
+    }
+    
+    public IntPoint(DoublePoint dp)
+    {
+      this.X = (cInt)dp.X; this.Y = (cInt)dp.Y; this.Z = 0;
+    }
+
+    public IntPoint(IntPoint pt)
+    {
+      this.X = pt.X; this.Y = pt.Y; this.Z = pt.Z;
+    }
+#else
+    public IntPoint(cInt X, cInt Y)
+    {
+        this.X = X; this.Y = Y;
+    }
+    public IntPoint(double x, double y)
+    {
+      this.X = (cInt)x; this.Y = (cInt)y;
+    }
+
+    public IntPoint(IntPoint pt)
+    {
+        this.X = pt.X; this.Y = pt.Y;
+    }
+#endif
+
+    public static bool operator ==(IntPoint a, IntPoint b)
+    {
+      return a.X == b.X && a.Y == b.Y;
+    }
+
+    public static bool operator !=(IntPoint a, IntPoint b)
+    {
+      return a.X != b.X  || a.Y != b.Y; 
+    }
+
+    public override bool Equals(object obj)
+    {
+      if (obj == null) return false;
+      if (obj is IntPoint)
+      {
+        IntPoint a = (IntPoint)obj;
+        return (X == a.X) && (Y == a.Y);
+      }
+      else return false;
+    }
+
+    public override int GetHashCode()
+    {
+      //simply prevents a compiler warning
+      return base.GetHashCode();
+    }
+
+  }// end struct IntPoint
+
+  public struct IntRect
+  {
+    public cInt left;
+    public cInt top;
+    public cInt right;
+    public cInt bottom;
+
+    public IntRect(cInt l, cInt t, cInt r, cInt b)
+    {
+      this.left = l; this.top = t;
+      this.right = r; this.bottom = b;
+    }
+    public IntRect(IntRect ir)
+    {
+      this.left = ir.left; this.top = ir.top;
+      this.right = ir.right; this.bottom = ir.bottom;
+    }
+  }
+
+  public enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
+  public enum PolyType { ptSubject, ptClip };
+  
+  //By far the most widely used winding rules for polygon filling are
+  //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
+  //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
+  //see http://glprogramming.com/red/chapter11.html
+  public enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
+  
+  public enum JoinType { jtSquare, jtRound, jtMiter };
+  public enum EndType { etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound };
+
+  internal enum EdgeSide {esLeft, esRight};
+  internal enum Direction {dRightToLeft, dLeftToRight};
+    
+  internal class TEdge {
+    internal IntPoint Bot;
+    internal IntPoint Curr;
+    internal IntPoint Top;
+    internal IntPoint Delta;
+    internal double Dx;
+    internal PolyType PolyTyp;
+    internal EdgeSide Side;
+    internal int WindDelta; //1 or -1 depending on winding direction
+    internal int WindCnt;
+    internal int WindCnt2; //winding count of the opposite polytype
+    internal int OutIdx;
+    internal TEdge Next;
+    internal TEdge Prev;
+    internal TEdge NextInLML;
+    internal TEdge NextInAEL;
+    internal TEdge PrevInAEL;
+    internal TEdge NextInSEL;
+    internal TEdge PrevInSEL;
+  };
+
+  public class IntersectNode
+  {
+      internal TEdge Edge1;
+      internal TEdge Edge2;
+      internal IntPoint Pt;
+  };
+
+  public class MyIntersectNodeSort : IComparer<IntersectNode>
+  {
+    public int Compare(IntersectNode node1, IntersectNode node2)
+    {
+      cInt i = node2.Pt.Y - node1.Pt.Y;
+      if (i > 0) return 1;
+      else if (i < 0) return -1;
+      else return 0;
+    }
+  }
+
+  internal class LocalMinima
+  {
+    internal cInt Y;
+    internal TEdge LeftBound;
+    internal TEdge RightBound;
+    internal LocalMinima Next;
+  };
+
+  internal class Scanbeam
+  {
+      internal cInt Y;
+      internal Scanbeam Next;
+  };
+
+  internal class Maxima
+  {
+      internal cInt X;
+      internal Maxima Next;
+      internal Maxima Prev;
+  };
+
+  internal class OutRec
+  {
+    internal int Idx;
+    internal bool IsHole;
+    internal bool IsOpen;
+    internal OutRec FirstLeft; //see comments in clipper.pas
+    internal OutPt Pts;
+    internal OutPt BottomPt;
+    internal PolyNode PolyNode;
+  };
+
+  internal class OutPt
+  {
+    internal int Idx;
+    internal IntPoint Pt;
+    internal OutPt Next;
+    internal OutPt Prev;
+  };
+
+  internal class Join
+  {
+    internal OutPt OutPt1;
+    internal OutPt OutPt2;
+    internal IntPoint OffPt;
+  };
+
+  public class ClipperBase
+  {    
+    protected const double horizontal = -3.4E+38;
+    protected const int Skip = -2;
+    protected const int Unassigned = -1;
+    protected const double tolerance = 1.0E-20;
+    internal static bool near_zero(double val){return (val > -tolerance) && (val < tolerance);}
+
+#if use_int32
+    public const cInt loRange = 0x7FFF;
+    public const cInt hiRange = 0x7FFF;
+#else
+    public const cInt loRange = 0x3FFFFFFF;
+    public const cInt hiRange = 0x3FFFFFFFFFFFFFFFL; 
+#endif
+
+    internal LocalMinima m_MinimaList;
+    internal LocalMinima m_CurrentLM;
+    internal List<List<TEdge>> m_edges = new List<List<TEdge>>();
+    internal bool m_UseFullRange;
+    internal bool m_HasOpenPaths;
+
+    //------------------------------------------------------------------------------
+
+    public bool PreserveCollinear
+    {
+      get;
+      set;
+    }
+    //------------------------------------------------------------------------------
+
+    public void Swap(ref cInt val1, ref cInt val2)
+    {
+      cInt tmp = val1;
+      val1 = val2;
+      val2 = tmp;
+    }
+    //------------------------------------------------------------------------------
+
+    internal static bool IsHorizontal(TEdge e)
+    {
+      return e.Delta.Y == 0;
+    }
+    //------------------------------------------------------------------------------
+
+    internal bool PointIsVertex(IntPoint pt, OutPt pp)
+    {
+      OutPt pp2 = pp;
+      do
+      {
+        if (pp2.Pt == pt) return true;
+        pp2 = pp2.Next;
+      }
+      while (pp2 != pp);
+      return false;
+    }
+    //------------------------------------------------------------------------------
+
+    internal bool PointOnLineSegment(IntPoint pt, 
+        IntPoint linePt1, IntPoint linePt2, bool UseFullRange)
+    {
+      if (UseFullRange)
+        return ((pt.X == linePt1.X) && (pt.Y == linePt1.Y)) ||
+          ((pt.X == linePt2.X) && (pt.Y == linePt2.Y)) ||
+          (((pt.X > linePt1.X) == (pt.X < linePt2.X)) &&
+          ((pt.Y > linePt1.Y) == (pt.Y < linePt2.Y)) &&
+          ((Int128.Int128Mul((pt.X - linePt1.X), (linePt2.Y - linePt1.Y)) ==
+          Int128.Int128Mul((linePt2.X - linePt1.X), (pt.Y - linePt1.Y)))));
+      else
+        return ((pt.X == linePt1.X) && (pt.Y == linePt1.Y)) ||
+          ((pt.X == linePt2.X) && (pt.Y == linePt2.Y)) ||
+          (((pt.X > linePt1.X) == (pt.X < linePt2.X)) &&
+          ((pt.Y > linePt1.Y) == (pt.Y < linePt2.Y)) &&
+          ((pt.X - linePt1.X) * (linePt2.Y - linePt1.Y) ==
+            (linePt2.X - linePt1.X) * (pt.Y - linePt1.Y)));
+    }
+    //------------------------------------------------------------------------------
+
+    internal bool PointOnPolygon(IntPoint pt, OutPt pp, bool UseFullRange)
+    {
+      OutPt pp2 = pp;
+      while (true)
+      {
+        if (PointOnLineSegment(pt, pp2.Pt, pp2.Next.Pt, UseFullRange))
+          return true;
+        pp2 = pp2.Next;
+        if (pp2 == pp) break;
+      }
+      return false;
+    }
+    //------------------------------------------------------------------------------
+
+    internal static bool SlopesEqual(TEdge e1, TEdge e2, bool UseFullRange)
+    {
+        if (UseFullRange)
+          return Int128.Int128Mul(e1.Delta.Y, e2.Delta.X) ==
+              Int128.Int128Mul(e1.Delta.X, e2.Delta.Y);
+        else return (cInt)(e1.Delta.Y) * (e2.Delta.X) ==
+          (cInt)(e1.Delta.X) * (e2.Delta.Y);
+    }
+    //------------------------------------------------------------------------------
+
+    protected static bool SlopesEqual(IntPoint pt1, IntPoint pt2,
+        IntPoint pt3, bool UseFullRange)
+    {
+        if (UseFullRange)
+            return Int128.Int128Mul(pt1.Y - pt2.Y, pt2.X - pt3.X) ==
+              Int128.Int128Mul(pt1.X - pt2.X, pt2.Y - pt3.Y);
+        else return
+          (cInt)(pt1.Y - pt2.Y) * (pt2.X - pt3.X) - (cInt)(pt1.X - pt2.X) * (pt2.Y - pt3.Y) == 0;
+    }
+    //------------------------------------------------------------------------------
+
+    protected static bool SlopesEqual(IntPoint pt1, IntPoint pt2,
+        IntPoint pt3, IntPoint pt4, bool UseFullRange)
+    {
+        if (UseFullRange)
+            return Int128.Int128Mul(pt1.Y - pt2.Y, pt3.X - pt4.X) ==
+              Int128.Int128Mul(pt1.X - pt2.X, pt3.Y - pt4.Y);
+        else return
+          (cInt)(pt1.Y - pt2.Y) * (pt3.X - pt4.X) - (cInt)(pt1.X - pt2.X) * (pt3.Y - pt4.Y) == 0;
+    }
+    //------------------------------------------------------------------------------
+
+    internal ClipperBase() //constructor (nb: no external instantiation)
+    {
+        m_MinimaList = null;
+        m_CurrentLM = null;
+        m_UseFullRange = false;
+        m_HasOpenPaths = false;
+    }
+    //------------------------------------------------------------------------------
+
+    public virtual void Clear()
+    {
+        DisposeLocalMinimaList();
+        for (int i = 0; i < m_edges.Count; ++i)
+        {
+            for (int j = 0; j < m_edges[i].Count; ++j) m_edges[i][j] = null;
+            m_edges[i].Clear();
+        }
+        m_edges.Clear();
+        m_UseFullRange = false;
+        m_HasOpenPaths = false;
+    }
+    //------------------------------------------------------------------------------
+
+    private void DisposeLocalMinimaList()
+    {
+        while( m_MinimaList != null )
+        {
+            LocalMinima tmpLm = m_MinimaList.Next;
+            m_MinimaList = null;
+            m_MinimaList = tmpLm;
+        }
+        m_CurrentLM = null;
+    }
+    //------------------------------------------------------------------------------
+
+    void RangeTest(IntPoint Pt, ref bool useFullRange)
+    {
+      if (useFullRange)
+      {
+        if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) 
+          throw new ClipperException("Coordinate outside allowed range");
+      }
+      else if (Pt.X > loRange || Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) 
+      {
+        useFullRange = true;
+        RangeTest(Pt, ref useFullRange);
+      }
+    }
+    //------------------------------------------------------------------------------
+
+    private void InitEdge(TEdge e, TEdge eNext,
+      TEdge ePrev, IntPoint pt)
+    {
+      e.Next = eNext;
+      e.Prev = ePrev;
+      e.Curr = pt;
+      e.OutIdx = Unassigned;
+    }
+    //------------------------------------------------------------------------------
+
+    private void InitEdge2(TEdge e, PolyType polyType)
+    {
+      if (e.Curr.Y >= e.Next.Curr.Y)
+      {
+        e.Bot = e.Curr;
+        e.Top = e.Next.Curr;
+      }
+      else
+      {
+        e.Top = e.Curr;
+        e.Bot = e.Next.Curr;
+      }
+      SetDx(e);
+      e.PolyTyp = polyType;
+    }
+    //------------------------------------------------------------------------------
+
+    private TEdge FindNextLocMin(TEdge E)
+    {
+      TEdge E2;
+      for (;;)
+      {
+        while (E.Bot != E.Prev.Bot || E.Curr == E.Top) E = E.Next;
+        if (E.Dx != horizontal && E.Prev.Dx != horizontal) break;
+        while (E.Prev.Dx == horizontal) E = E.Prev;
+        E2 = E;
+        while (E.Dx == horizontal) E = E.Next;
+        if (E.Top.Y == E.Prev.Bot.Y) continue; //ie just an intermediate horz.
+        if (E2.Prev.Bot.X < E.Bot.X) E = E2;
+        break;
+      }
+      return E;
+    }
+    //------------------------------------------------------------------------------
+
+    private TEdge ProcessBound(TEdge E, bool LeftBoundIsForward)
+    {
+      TEdge EStart, Result = E;
+      TEdge Horz;
+
+      if (Result.OutIdx == Skip)
+      {
+        //check if there are edges beyond the skip edge in the bound and if so
+        //create another LocMin and calling ProcessBound once more ...
+        E = Result;
+        if (LeftBoundIsForward)
+        {
+          while (E.Top.Y == E.Next.Bot.Y) E = E.Next;
+          while (E != Result && E.Dx == horizontal) E = E.Prev;
+        }
+        else
+        {
+          while (E.Top.Y == E.Prev.Bot.Y) E = E.Prev;
+          while (E != Result && E.Dx == horizontal) E = E.Next;
+        }
+        if (E == Result)
+        {
+          if (LeftBoundIsForward) Result = E.Next;
+          else Result = E.Prev;
+        }
+        else
+        {
+          //there are more edges in the bound beyond result starting with E
+          if (LeftBoundIsForward)
+            E = Result.Next;
+          else
+            E = Result.Prev;
+          LocalMinima locMin = new LocalMinima();
+          locMin.Next = null;
+          locMin.Y = E.Bot.Y;
+          locMin.LeftBound = null;
+          locMin.RightBound = E;
+          E.WindDelta = 0;
+          Result = ProcessBound(E, LeftBoundIsForward);
+          InsertLocalMinima(locMin);
+        }
+        return Result;
+      }
+
+      if (E.Dx == horizontal)
+      {
+        //We need to be careful with open paths because this may not be a
+        //true local minima (ie E may be following a skip edge).
+        //Also, consecutive horz. edges may start heading left before going right.
+        if (LeftBoundIsForward) EStart = E.Prev;
+        else EStart = E.Next;
+        if (EStart.Dx == horizontal) //ie an adjoining horizontal skip edge
+        {
+        if (EStart.Bot.X != E.Bot.X && EStart.Top.X != E.Bot.X)
+            ReverseHorizontal(E);
+        }
+        else if (EStart.Bot.X != E.Bot.X)
+        ReverseHorizontal(E);
+      }
+
+      EStart = E;
+      if (LeftBoundIsForward)
+      {
+        while (Result.Top.Y == Result.Next.Bot.Y && Result.Next.OutIdx != Skip)
+          Result = Result.Next;
+        if (Result.Dx == horizontal && Result.Next.OutIdx != Skip)
+        {
+          //nb: at the top of a bound, horizontals are added to the bound
+          //only when the preceding edge attaches to the horizontal's left vertex
+          //unless a Skip edge is encountered when that becomes the top divide
+          Horz = Result;
+          while (Horz.Prev.Dx == horizontal) Horz = Horz.Prev;
+          if (Horz.Prev.Top.X > Result.Next.Top.X) Result = Horz.Prev;
+        }
+        while (E != Result)
+        {
+          E.NextInLML = E.Next;
+          if (E.Dx == horizontal && E != EStart && E.Bot.X != E.Prev.Top.X) 
+            ReverseHorizontal(E);
+          E = E.Next;
+        }
+        if (E.Dx == horizontal && E != EStart && E.Bot.X != E.Prev.Top.X) 
+          ReverseHorizontal(E);
+        Result = Result.Next; //move to the edge just beyond current bound
+      }
+      else
+      {
+        while (Result.Top.Y == Result.Prev.Bot.Y && Result.Prev.OutIdx != Skip)
+          Result = Result.Prev;
+        if (Result.Dx == horizontal && Result.Prev.OutIdx != Skip)
+        {
+          Horz = Result;
+          while (Horz.Next.Dx == horizontal) Horz = Horz.Next;
+          if (Horz.Next.Top.X == Result.Prev.Top.X || 
+              Horz.Next.Top.X > Result.Prev.Top.X) Result = Horz.Next;
+        }
+
+        while (E != Result)
+        {
+          E.NextInLML = E.Prev;
+          if (E.Dx == horizontal && E != EStart && E.Bot.X != E.Next.Top.X) 
+            ReverseHorizontal(E);
+          E = E.Prev;
+        }
+        if (E.Dx == horizontal && E != EStart && E.Bot.X != E.Next.Top.X) 
+          ReverseHorizontal(E);
+        Result = Result.Prev; //move to the edge just beyond current bound
+      }
+      return Result;
+    }
+    //------------------------------------------------------------------------------
+
+
+    public bool AddPath(Path pg, PolyType polyType, bool Closed)
+    {
+#if use_lines
+      if (!Closed && polyType == PolyType.ptClip)
+        throw new ClipperException("AddPath: Open paths must be subject.");
+#else
+      if (!Closed)
+        throw new ClipperException("AddPath: Open paths have been disabled.");
+#endif
+
+      int highI = (int)pg.Count - 1;
+      if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI;
+      while (highI > 0 && (pg[highI] == pg[highI - 1])) --highI;
+      if ((Closed && highI < 2) || (!Closed && highI < 1)) return false;
+
+      //create a new edge array ...
+      List<TEdge> edges = new List<TEdge>(highI+1);
+      for (int i = 0; i <= highI; i++) edges.Add(new TEdge());
+          
+      bool IsFlat = true;
+
+      //1. Basic (first) edge initialization ...
+      edges[1].Curr = pg[1];
+      RangeTest(pg[0], ref m_UseFullRange);
+      RangeTest(pg[highI], ref m_UseFullRange);
+      InitEdge(edges[0], edges[1], edges[highI], pg[0]);
+      InitEdge(edges[highI], edges[0], edges[highI - 1], pg[highI]);
+      for (int i = highI - 1; i >= 1; --i)
+      {
+        RangeTest(pg[i], ref m_UseFullRange);
+        InitEdge(edges[i], edges[i + 1], edges[i - 1], pg[i]);
+      }
+      TEdge eStart = edges[0];
+
+      //2. Remove duplicate vertices, and (when closed) collinear edges ...
+      TEdge E = eStart, eLoopStop = eStart;
+      for (;;)
+      {
+        //nb: allows matching start and end points when not Closed ...
+        if (E.Curr == E.Next.Curr && (Closed || E.Next != eStart))
+        {
+          if (E == E.Next) break;
+          if (E == eStart) eStart = E.Next;
+          E = RemoveEdge(E);
+          eLoopStop = E;
+          continue;
+        }
+        if (E.Prev == E.Next) 
+          break; //only two vertices
+        else if (Closed &&
+          SlopesEqual(E.Prev.Curr, E.Curr, E.Next.Curr, m_UseFullRange) && 
+          (!PreserveCollinear ||
+          !Pt2IsBetweenPt1AndPt3(E.Prev.Curr, E.Curr, E.Next.Curr))) 
+        {
+          //Collinear edges are allowed for open paths but in closed paths
+          //the default is to merge adjacent collinear edges into a single edge.
+          //However, if the PreserveCollinear property is enabled, only overlapping
+          //collinear edges (ie spikes) will be removed from closed paths.
+          if (E == eStart) eStart = E.Next;
+          E = RemoveEdge(E);
+          E = E.Prev;
+          eLoopStop = E;
+          continue;
+        }
+        E = E.Next;
+        if ((E == eLoopStop) || (!Closed && E.Next == eStart)) break;
+      }
+
+      if ((!Closed && (E == E.Next)) || (Closed && (E.Prev == E.Next)))
+        return false;
+
+      if (!Closed)
+      {
+        m_HasOpenPaths = true;
+        eStart.Prev.OutIdx = Skip;
+      }
+
+      //3. Do second stage of edge initialization ...
+      E = eStart;
+      do
+      {
+        InitEdge2(E, polyType);
+        E = E.Next;
+        if (IsFlat && E.Curr.Y != eStart.Curr.Y) IsFlat = false;
+      }
+      while (E != eStart);
+
+      //4. Finally, add edge bounds to LocalMinima list ...
+
+      //Totally flat paths must be handled differently when adding them
+      //to LocalMinima list to avoid endless loops etc ...
+      if (IsFlat) 
+      {
+        if (Closed) return false;
+        E.Prev.OutIdx = Skip;
+        LocalMinima locMin = new LocalMinima();
+        locMin.Next = null;
+        locMin.Y = E.Bot.Y;
+        locMin.LeftBound = null;
+        locMin.RightBound = E;
+        locMin.RightBound.Side = EdgeSide.esRight;
+        locMin.RightBound.WindDelta = 0;
+        for ( ; ; )
+        {
+          if (E.Bot.X != E.Prev.Top.X) ReverseHorizontal(E);
+          if (E.Next.OutIdx == Skip) break;
+          E.NextInLML = E.Next;
+          E = E.Next;
+        }
+        InsertLocalMinima(locMin);
+        m_edges.Add(edges);
+        return true;
+      }
+
+      m_edges.Add(edges);
+      bool leftBoundIsForward;
+      TEdge EMin = null;
+
+      //workaround to avoid an endless loop in the while loop below when
+      //open paths have matching start and end points ...
+      if (E.Prev.Bot == E.Prev.Top) E = E.Next;
+
+      for (;;)
+      {
+        E = FindNextLocMin(E);
+        if (E == EMin) break;
+        else if (EMin == null) EMin = E;
+
+        //E and E.Prev now share a local minima (left aligned if horizontal).
+        //Compare their slopes to find which starts which bound ...
+        LocalMinima locMin = new LocalMinima();
+        locMin.Next = null;
+        locMin.Y = E.Bot.Y;
+        if (E.Dx < E.Prev.Dx) 
+        {
+          locMin.LeftBound = E.Prev;
+          locMin.RightBound = E;
+          leftBoundIsForward = false; //Q.nextInLML = Q.prev
+        } else
+        {
+          locMin.LeftBound = E;
+          locMin.RightBound = E.Prev;
+          leftBoundIsForward = true; //Q.nextInLML = Q.next
+        }
+        locMin.LeftBound.Side = EdgeSide.esLeft;
+        locMin.RightBound.Side = EdgeSide.esRight;
+
+        if (!Closed) locMin.LeftBound.WindDelta = 0;
+        else if (locMin.LeftBound.Next == locMin.RightBound)
+          locMin.LeftBound.WindDelta = -1;
+        else locMin.LeftBound.WindDelta = 1;
+        locMin.RightBound.WindDelta = -locMin.LeftBound.WindDelta;
+
+        E = ProcessBound(locMin.LeftBound, leftBoundIsForward);
+        if (E.OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward);
+
+        TEdge E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward);
+        if (E2.OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward);
+
+        if (locMin.LeftBound.OutIdx == Skip)
+          locMin.LeftBound = null;
+        else if (locMin.RightBound.OutIdx == Skip)
+          locMin.RightBound = null;
+        InsertLocalMinima(locMin);
+        if (!leftBoundIsForward) E = E2;
+      }
+      return true;
+
+    }
+    //------------------------------------------------------------------------------
+
+    public bool AddPaths(Paths ppg, PolyType polyType, bool closed)
+    {
+      bool result = false;
+      for (int i = 0; i < ppg.Count; ++i)
+        if (AddPath(ppg[i], polyType, closed)) result = true;
+      return result;
+    }
+    //------------------------------------------------------------------------------
+
+    internal bool Pt2IsBetweenPt1AndPt3(IntPoint pt1, IntPoint pt2, IntPoint pt3)
+    {
+      if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) return false;
+      else if (pt1.X != pt3.X) return (pt2.X > pt1.X) == (pt2.X < pt3.X);
+      else return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y);
+    }
+    //------------------------------------------------------------------------------
+
+    TEdge RemoveEdge(TEdge e)
+    {
+      //removes e from double_linked_list (but without removing from memory)
+      e.Prev.Next = e.Next;
+      e.Next.Prev = e.Prev;
+      TEdge result = e.Next;
+      e.Prev = null; //flag as removed (see ClipperBase.Clear)
+      return result;
+    }
+    //------------------------------------------------------------------------------
+
+    private void SetDx(TEdge e)
+    {
+      e.Delta.X = (e.Top.X - e.Bot.X);
+      e.Delta.Y = (e.Top.Y - e.Bot.Y);
+      if (e.Delta.Y == 0) e.Dx = horizontal;
+      else e.Dx = (double)(e.Delta.X) / (e.Delta.Y);
+    }
+    //---------------------------------------------------------------------------
+
+    private void InsertLocalMinima(LocalMinima newLm)
+    {
+      if( m_MinimaList == null )
+      {
+        m_MinimaList = newLm;
+      }
+      else if( newLm.Y >= m_MinimaList.Y )
+      {
+        newLm.Next = m_MinimaList;
+        m_MinimaList = newLm;
+      } else
+      {
+        LocalMinima tmpLm = m_MinimaList;
+        while( tmpLm.Next != null  && ( newLm.Y < tmpLm.Next.Y ) )
+          tmpLm = tmpLm.Next;
+        newLm.Next = tmpLm.Next;
+        tmpLm.Next = newLm;
+      }
+    }
+    //------------------------------------------------------------------------------
+
+    protected void PopLocalMinima()
+    {
+        if (m_CurrentLM == null) return;
+        m_CurrentLM = m_CurrentLM.Next;
+    }
+    //------------------------------------------------------------------------------
+
+    private void ReverseHorizontal(TEdge e)
+    {
+      //swap horizontal edges' top and bottom x's so they follow the natural
+      //progression of the bounds - ie so their xbots will align with the
+      //adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
+      Swap(ref e.Top.X, ref e.Bot.X);
+#if use_xyz
+      Swap(ref e.Top.Z, ref e.Bot.Z);
+#endif
+    }
+    //------------------------------------------------------------------------------
+
+    protected virtual void Reset()
+    {
+      m_CurrentLM = m_MinimaList;
+      if (m_CurrentLM == null) return; //ie nothing to process
+
+      //reset all edges ...
+      LocalMinima lm = m_MinimaList;
+      while (lm != null)
+      {
+        TEdge e = lm.LeftBound;
+        if (e != null)
+        {
+          e.Curr = e.Bot;
+          e.Side = EdgeSide.esLeft;
+          e.OutIdx = Unassigned;
+        }
+        e = lm.RightBound;
+        if (e != null)
+        {
+          e.Curr = e.Bot;
+          e.Side = EdgeSide.esRight;
+          e.OutIdx = Unassigned;
+        }
+        lm = lm.Next;
+      }
+    }
+    //------------------------------------------------------------------------------
+
+    public static IntRect GetBounds(Paths paths)
+    {
+      int i = 0, cnt = paths.Count;
+      while (i < cnt && paths[i].Count == 0) i++;
+      if (i == cnt) return new IntRect(0,0,0,0);
+      IntRect result = new IntRect();
+      result.left = paths[i][0].X;
+      result.right = result.left;
+      result.top = paths[i][0].Y;
+      result.bottom = result.top;
+      for (; i < cnt; i++)
+        for (int j = 0; j < paths[i].Count; j++)
+        {
+          if (paths[i][j].X < result.left) result.left = paths[i][j].X;
+          else if (paths[i][j].X > result.right) result.right = paths[i][j].X;
+          if (paths[i][j].Y < result.top) result.top = paths[i][j].Y;
+          else if (paths[i][j].Y > result.bottom) result.bottom = paths[i][j].Y;
+        }
+      return result;
+    }
+
+  } //end ClipperBase
+
+  public class Clipper : ClipperBase
+  {
+      //InitOptions that can be passed to the constructor ...
+      public const int ioReverseSolution = 1;
+      public const int ioStrictlySimple = 2;
+      public const int ioPreserveCollinear = 4;
+
+      private List<OutRec> m_PolyOuts;
+      private ClipType m_ClipType;
+      private Scanbeam m_Scanbeam;
+      private Maxima m_Maxima;
+      private TEdge m_ActiveEdges;
+      private TEdge m_SortedEdges;
+      private List<IntersectNode> m_IntersectList;
+      IComparer<IntersectNode> m_IntersectNodeComparer;
+      private bool m_ExecuteLocked;
+      private PolyFillType m_ClipFillType;
+      private PolyFillType m_SubjFillType;
+      private List<Join> m_Joins;
+      private List<Join> m_GhostJoins;
+      private bool m_UsingPolyTree;
+#if use_xyz
+      public delegate void ZFillCallback(IntPoint bot1, IntPoint top1, 
+        IntPoint bot2, IntPoint top2, ref IntPoint pt);
+      public ZFillCallback ZFillFunction { get; set; }
+#endif
+      public Clipper(int InitOptions = 0): base() //constructor
+      {
+          m_Scanbeam = null;
+          m_Maxima = null;
+          m_ActiveEdges = null;
+          m_SortedEdges = null;
+          m_IntersectList = new List<IntersectNode>();
+          m_IntersectNodeComparer = new MyIntersectNodeSort();
+          m_ExecuteLocked = false;
+          m_UsingPolyTree = false;
+          m_PolyOuts = new List<OutRec>();
+          m_Joins = new List<Join>();
+          m_GhostJoins = new List<Join>();
+          ReverseSolution = (ioReverseSolution & InitOptions) != 0;
+          StrictlySimple = (ioStrictlySimple & InitOptions) != 0;
+          PreserveCollinear = (ioPreserveCollinear & InitOptions) != 0;
+#if use_xyz
+          ZFillFunction = null;
+#endif
+      }
+      //------------------------------------------------------------------------------
+
+      private void InsertScanbeam(cInt Y)
+      {
+          //single-linked list: sorted descending, ignoring dups.
+          if (m_Scanbeam == null)
+          {
+              m_Scanbeam = new Scanbeam();
+              m_Scanbeam.Next = null;
+              m_Scanbeam.Y = Y;
+          }
+          else if (Y > m_Scanbeam.Y)
+          {
+              Scanbeam newSb = new Scanbeam();
+              newSb.Y = Y;
+              newSb.Next = m_Scanbeam;
+              m_Scanbeam = newSb;
+          }
+          else
+          {
+              Scanbeam sb2 = m_Scanbeam;
+              while (sb2.Next != null && (Y <= sb2.Next.Y)) sb2 = sb2.Next;
+              if (Y == sb2.Y) return; //ie ignores duplicates
+              Scanbeam newSb = new Scanbeam();
+              newSb.Y = Y;
+              newSb.Next = sb2.Next;
+              sb2.Next = newSb;
+          }
+      }
+      //------------------------------------------------------------------------------
+
+      private void InsertMaxima(cInt X)
+      {
+          //double-linked list: sorted ascending, ignoring dups.
+          Maxima newMax = new Maxima();
+          newMax.X = X;
+          if (m_Maxima == null)
+          {
+              m_Maxima = newMax;
+              m_Maxima.Next = null;
+              m_Maxima.Prev = null;
+          }
+          else if (X < m_Maxima.X)
+          {
+              newMax.Next = m_Maxima;
+              newMax.Prev = null;
+              m_Maxima = newMax;
+          }
+          else
+          {
+              Maxima m = m_Maxima;
+              while (m.Next != null && (X >= m.Next.X)) m = m.Next;
+              if (X == m.X) return; //ie ignores duplicates (& CG to clean up newMax)
+              //insert newMax between m and m.Next ...
+              newMax.Next = m.Next;
+              newMax.Prev = m;
+              if (m.Next != null) m.Next.Prev = newMax;
+              m.Next = newMax;
+          }
+      }
+      //------------------------------------------------------------------------------
+
+      protected override void Reset() 
+      {
+        base.Reset();
+        m_Scanbeam = null;
+        m_Maxima = null;
+        m_ActiveEdges = null;
+        m_SortedEdges = null;
+        LocalMinima lm = m_MinimaList;
+        while (lm != null)
+        {
+          InsertScanbeam(lm.Y);
+          lm = lm.Next;
+        }
+      }
+      //------------------------------------------------------------------------------
+
+      public bool ReverseSolution
+      {
+        get;
+        set;
+      }
+      //------------------------------------------------------------------------------
+
+      public bool StrictlySimple
+      {
+        get; 
+        set;
+      }
+      //------------------------------------------------------------------------------
+       
+      public bool Execute(ClipType clipType, Paths solution, 
+          PolyFillType FillType = PolyFillType.pftEvenOdd)
+      {
+          return Execute(clipType, solution, FillType, FillType);
+      }
+      //------------------------------------------------------------------------------
+
+      public bool Execute(ClipType clipType, PolyTree polytree,
+          PolyFillType FillType = PolyFillType.pftEvenOdd)
+      {
+          return Execute(clipType, polytree, FillType, FillType);
+      }
+      //------------------------------------------------------------------------------
+
+      public bool Execute(ClipType clipType, Paths solution,
+          PolyFillType subjFillType, PolyFillType clipFillType)
+      {
+          if (m_ExecuteLocked) return false;
+          if (m_HasOpenPaths) throw 
+            new ClipperException("Error: PolyTree struct is needed for open path clipping.");
+
+          m_ExecuteLocked = true;
+          solution.Clear();
+          m_SubjFillType = subjFillType;
+          m_ClipFillType = clipFillType;
+          m_ClipType = clipType;
+          m_UsingPolyTree = false;
+          bool succeeded;
+          try
+          {
+            succeeded = ExecuteInternal();
+            //build the return polygons ...
+            if (succeeded) BuildResult(solution);
+          }
+          finally
+          {
+            DisposeAllPolyPts();
+            m_ExecuteLocked = false;
+          }
+          return succeeded;
+      }
+      //------------------------------------------------------------------------------
+
+      public bool Execute(ClipType clipType, PolyTree polytree,
+          PolyFillType subjFillType, PolyFillType clipFillType)
+      {
+          if (m_ExecuteLocked) return false;
+          m_ExecuteLocked = true;
+          m_SubjFillType = subjFillType;
+          m_ClipFillType = clipFillType;
+          m_ClipType = clipType;
+          m_UsingPolyTree = true;
+          bool succeeded;
+          try
+          {
+            succeeded = ExecuteInternal();
+            //build the return polygons ...
+            if (succeeded) BuildResult2(polytree);
+          }
+          finally
+          {
+            DisposeAllPolyPts();
+            m_ExecuteLocked = false;
+          }
+          return succeeded;
+      }
+      //------------------------------------------------------------------------------
+
+      internal void FixHoleLinkage(OutRec outRec)
+      {
+        //skip if an outermost polygon or
+        //already already points to the correct FirstLeft ...
+        if (outRec.FirstLeft == null ||
+              (outRec.IsHole != outRec.FirstLeft.IsHole &&
+              outRec.FirstLeft.Pts != null)) return;
+
+        OutRec orfl = outRec.FirstLeft;
+        while (orfl != null && ((orfl.IsHole == outRec.IsHole) || orfl.Pts == null))
+          orfl = orfl.FirstLeft;
+        outRec.FirstLeft = orfl;
+      }
+      //------------------------------------------------------------------------------
+
+      private bool ExecuteInternal()
+      {
+        try
+        {
+          Reset();
+          if (m_CurrentLM == null) return false;
+
+          cInt botY = PopScanbeam();
+          do
+          {
+            InsertLocalMinimaIntoAEL(botY);
+            ProcessHorizontals();
+            m_GhostJoins.Clear();
+            if (m_Scanbeam == null) break;
+            cInt topY = PopScanbeam();
+            if (!ProcessIntersections(topY)) return false;
+            ProcessEdgesAtTopOfScanbeam(topY);
+            botY = topY;
+          } while (m_Scanbeam != null || m_CurrentLM != null);
+
+          //fix orientations ...
+          for (int i = 0; i < m_PolyOuts.Count; i++)
+          {
+            OutRec outRec = m_PolyOuts[i];
+            if (outRec.Pts == null || outRec.IsOpen) continue;
+            if ((outRec.IsHole ^ ReverseSolution) == (Area(outRec) > 0))
+              ReversePolyPtLinks(outRec.Pts);
+          }
+
+          JoinCommonEdges();
+
+          for (int i = 0; i < m_PolyOuts.Count; i++)
+          {
+            OutRec outRec = m_PolyOuts[i];
+            if (outRec.Pts == null) 
+                continue;
+            else if (outRec.IsOpen)
+                FixupOutPolyline(outRec);
+            else
+                FixupOutPolygon(outRec);
+          }
+
+          if (StrictlySimple) DoSimplePolygons();
+          return true;
+        }
+        //catch { return false; }
+        finally 
+        {
+          m_Joins.Clear();
+          m_GhostJoins.Clear();          
+        }
+      }
+      //------------------------------------------------------------------------------
+
+      private cInt PopScanbeam()
+      {
+        cInt Y = m_Scanbeam.Y;
+        m_Scanbeam = m_Scanbeam.Next;
+        return Y;
+      }
+      //------------------------------------------------------------------------------
+
+      private void DisposeAllPolyPts(){
+        for (int i = 0; i < m_PolyOuts.Count; ++i) DisposeOutRec(i);
+        m_PolyOuts.Clear();
+      }
+      //------------------------------------------------------------------------------
+
+      void DisposeOutRec(int index)
+      {
+        OutRec outRec = m_PolyOuts[index];
+        outRec.Pts = null;
+        outRec = null;
+        m_PolyOuts[index] = null;
+      }
+      //------------------------------------------------------------------------------
+
+      private void AddJoin(OutPt Op1, OutPt Op2, IntPoint OffPt)
+      {
+        Join j = new Join();
+        j.OutPt1 = Op1;
+        j.OutPt2 = Op2;
+        j.OffPt = OffPt;
+        m_Joins.Add(j);
+      }
+      //------------------------------------------------------------------------------
+
+      private void AddGhostJoin(OutPt Op, IntPoint OffPt)
+      {
+        Join j = new Join();
+        j.OutPt1 = Op;
+        j.OffPt = OffPt;
+        m_GhostJoins.Add(j);
+      }
+      //------------------------------------------------------------------------------
+
+#if use_xyz
+      internal void SetZ(ref IntPoint pt, TEdge e1, TEdge e2)
+      {
+        if (pt.Z != 0 || ZFillFunction == null) return;
+        else if (pt == e1.Bot) pt.Z = e1.Bot.Z;
+        else if (pt == e1.Top) pt.Z = e1.Top.Z;
+        else if (pt == e2.Bot) pt.Z = e2.Bot.Z;
+        else if (pt == e2.Top) pt.Z = e2.Top.Z;
+        else ZFillFunction(e1.Bot, e1.Top, e2.Bot, e2.Top, ref pt);
+      }
+      //------------------------------------------------------------------------------
+#endif
+
+      private void InsertLocalMinimaIntoAEL(cInt botY)
+      {
+        while(  m_CurrentLM != null  && ( m_CurrentLM.Y == botY ) )
+        {
+          TEdge lb = m_CurrentLM.LeftBound;
+          TEdge rb = m_CurrentLM.RightBound;
+          PopLocalMinima();
+
+          OutPt Op1 = null;
+          if (lb == null)
+          {
+            InsertEdgeIntoAEL(rb, null);
+            SetWindingCount(rb);
+            if (IsContributing(rb))
+              Op1 = AddOutPt(rb, rb.Bot);
+          }
+          else if (rb == null)
+          {
+            InsertEdgeIntoAEL(lb, null);
+            SetWindingCount(lb);
+            if (IsContributing(lb))
+              Op1 = AddOutPt(lb, lb.Bot);
+            InsertScanbeam(lb.Top.Y);
+          }
+          else
+          {
+            InsertEdgeIntoAEL(lb, null);
+            InsertEdgeIntoAEL(rb, lb);
+            SetWindingCount(lb);
+            rb.WindCnt = lb.WindCnt;
+            rb.WindCnt2 = lb.WindCnt2;
+            if (IsContributing(lb))
+              Op1 = AddLocalMinPoly(lb, rb, lb.Bot);
+            InsertScanbeam(lb.Top.Y);
+          }
+
+          if (rb != null)
+          {
+            if (IsHorizontal(rb))
+              AddEdgeToSEL(rb);
+            else
+              InsertScanbeam(rb.Top.Y);
+          }
+
+          if (lb == null || rb == null) continue;
+
+          //if output polygons share an Edge with a horizontal rb, they'll need joining later ...
+          if (Op1 != null && IsHorizontal(rb) && 
+            m_GhostJoins.Count > 0 && rb.WindDelta != 0)
+          {
+            for (int i = 0; i < m_GhostJoins.Count; i++)
+            {
+              //if the horizontal Rb and a 'ghost' horizontal overlap, then convert
+              //the 'ghost' join to a real join ready for later ...
+              Join j = m_GhostJoins[i];
+              if (HorzSegmentsOverlap(j.OutPt1.Pt.X, j.OffPt.X, rb.Bot.X, rb.Top.X))
+                AddJoin(j.OutPt1, Op1, j.OffPt);
+            }
+          }
+
+          if (lb.OutIdx >= 0 && lb.PrevInAEL != null &&
+            lb.PrevInAEL.Curr.X == lb.Bot.X &&
+            lb.PrevInAEL.OutIdx >= 0 &&
+            SlopesEqual(lb.PrevInAEL, lb, m_UseFullRange) &&
+            lb.WindDelta != 0 && lb.PrevInAEL.WindDelta != 0)
+          {
+            OutPt Op2 = AddOutPt(lb.PrevInAEL, lb.Bot);
+            AddJoin(Op1, Op2, lb.Top);
+          }
+
+          if( lb.NextInAEL != rb )
+          {
+
+            if (rb.OutIdx >= 0 && rb.PrevInAEL.OutIdx >= 0 &&
+              SlopesEqual(rb.PrevInAEL, rb, m_UseFullRange) &&
+              rb.WindDelta != 0 && rb.PrevInAEL.WindDelta != 0)
+            {
+              OutPt Op2 = AddOutPt(rb.PrevInAEL, rb.Bot);
+              AddJoin(Op1, Op2, rb.Top);
+            }
+
+            TEdge e = lb.NextInAEL;
+            if (e != null)
+              while (e != rb)
+              {
+                //nb: For calculating winding counts etc, IntersectEdges() assumes
+                //that param1 will be to the right of param2 ABOVE the intersection ...
+                IntersectEdges(rb, e, lb.Curr); //order important here
+                e = e.NextInAEL;
+              }
+          }
+        }
+      }
+      //------------------------------------------------------------------------------
+
+      private void InsertEdgeIntoAEL(TEdge edge, TEdge startEdge)
+      {
+        if (m_ActiveEdges == null)
+        {
+          edge.PrevInAEL = null;
+          edge.NextInAEL = null;
+          m_ActiveEdges = edge;
+        }
+        else if (startEdge == null && E2InsertsBeforeE1(m_ActiveEdges, edge))
+        {
+          edge.PrevInAEL = null;
+          edge.NextInAEL = m_ActiveEdges;
+          m_ActiveEdges.PrevInAEL = edge;
+          m_ActiveEdges = edge;
+        }
+        else
+        {
+          if (startEdge == null) startEdge = m_ActiveEdges;
+          while (startEdge.NextInAEL != null &&
+            !E2InsertsBeforeE1(startEdge.NextInAEL, edge))
+            startEdge = startEdge.NextInAEL;
+          edge.NextInAEL = startEdge.NextInAEL;
+          if (startEdge.NextInAEL != null) startEdge.NextInAEL.PrevInAEL = edge;
+          edge.PrevInAEL = startEdge;
+          startEdge.NextInAEL = edge;
+        }
+      }
+      //----------------------------------------------------------------------
+
+      private bool E2InsertsBeforeE1(TEdge e1, TEdge e2)
+      {
+          if (e2.Curr.X == e1.Curr.X)
+          {
+              if (e2.Top.Y > e1.Top.Y)
+                  return e2.Top.X < TopX(e1, e2.Top.Y);
+              else return e1.Top.X > TopX(e2, e1.Top.Y);
+          }
+          else return e2.Curr.X < e1.Curr.X;
+      }
+      //------------------------------------------------------------------------------
+
+      private bool IsEvenOddFillType(TEdge edge) 
+      {
+        if (edge.PolyTyp == PolyType.ptSubject)
+            return m_SubjFillType == PolyFillType.pftEvenOdd; 
+        else
+            return m_ClipFillType == PolyFillType.pftEvenOdd;
+      }
+      //------------------------------------------------------------------------------
+
+      private bool IsEvenOddAltFillType(TEdge edge) 
+      {
+        if (edge.PolyTyp == PolyType.ptSubject)
+            return m_ClipFillType == PolyFillType.pftEvenOdd; 
+        else
+            return m_SubjFillType == PolyFillType.pftEvenOdd;
+      }
+      //------------------------------------------------------------------------------
+
+      private bool IsContributing(TEdge edge)
+      {
+          PolyFillType pft, pft2;
+          if (edge.PolyTyp == PolyType.ptSubject)
+          {
+              pft = m_SubjFillType;
+              pft2 = m_ClipFillType;
+          }
+          else
+          {
+              pft = m_ClipFillType;
+              pft2 = m_SubjFillType;
+          }
+
+          switch (pft)
+          {
+              case PolyFillType.pftEvenOdd:
+                  //return false if a subj line has been flagged as inside a subj polygon
+                  if (edge.WindDelta == 0 && edge.WindCnt != 1) return false;
+                  break;
+              case PolyFillType.pftNonZero:
+                  if (Math.Abs(edge.WindCnt) != 1) return false;
+                  break;
+              case PolyFillType.pftPositive:
+                  if (edge.WindCnt != 1) return false;
+                  break;
+              default: //PolyFillType.pftNegative
+                  if (edge.WindCnt != -1) return false; 
+                  break;
+          }
+
+          switch (m_ClipType)
+          {
+            case ClipType.ctIntersection:
+                switch (pft2)
+                {
+                    case PolyFillType.pftEvenOdd:
+                    case PolyFillType.pftNonZero:
+                        return (edge.WindCnt2 != 0);
+                    case PolyFillType.pftPositive:
+                        return (edge.WindCnt2 > 0);
+                    default:
+                        return (edge.WindCnt2 < 0);
+                }
+            case ClipType.ctUnion:
+                switch (pft2)
+                {
+                    case PolyFillType.pftEvenOdd:
+                    case PolyFillType.pftNonZero:
+                        return (edge.WindCnt2 == 0);
+                    case PolyFillType.pftPositive:
+                        return (edge.WindCnt2 <= 0);
+                    default:
+                        return (edge.WindCnt2 >= 0);
+                }
+            case ClipType.ctDifference:
+                if (edge.PolyTyp == PolyType.ptSubject)
+                    switch (pft2)
+                    {
+                        case PolyFillType.pftEvenOdd:
+                        case PolyFillType.pftNonZero:
+                            return (edge.WindCnt2 == 0);
+                        case PolyFillType.pftPositive:
+                            return (edge.WindCnt2 <= 0);
+                        default:
+                            return (edge.WindCnt2 >= 0);
+                    }
+                else
+                    switch (pft2)
+                    {
+                        case PolyFillType.pftEvenOdd:
+                        case PolyFillType.pftNonZero:
+                            return (edge.WindCnt2 != 0);
+                        case PolyFillType.pftPositive:
+                            return (edge.WindCnt2 > 0);
+                        default:
+                            return (edge.WindCnt2 < 0);
+                    }
+            case ClipType.ctXor:
+                if (edge.WindDelta == 0) //XOr always contributing unless open
+                  switch (pft2)
+                  {
+                    case PolyFillType.pftEvenOdd:
+                    case PolyFillType.pftNonZero:
+                      return (edge.WindCnt2 == 0);
+                    case PolyFillType.pftPositive:
+                      return (edge.WindCnt2 <= 0);
+                    default:
+                      return (edge.WindCnt2 >= 0);
+                  }
+                else
+                  return true;
+          }
+          return true;
+      }
+      //------------------------------------------------------------------------------
+
+      private void SetWindingCount(TEdge edge)
+      {
+        TEdge e = edge.PrevInAEL;
+        //find the edge of the same polytype that immediately preceeds 'edge' in AEL
+        while (e != null && ((e.PolyTyp != edge.PolyTyp) || (e.WindDelta == 0))) e = e.PrevInAEL;
+        if (e == null)
+        {
+          edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta);
+          edge.WindCnt2 = 0;
+          e = m_ActiveEdges; //ie get ready to calc WindCnt2
+        }
+        else if (edge.WindDelta == 0 && m_ClipType != ClipType.ctUnion)
+        {
+          edge.WindCnt = 1;
+          edge.WindCnt2 = e.WindCnt2;
+          e = e.NextInAEL; //ie get ready to calc WindCnt2
+        }
+        else if (IsEvenOddFillType(edge))
+        {
+          //EvenOdd filling ...
+          if (edge.WindDelta == 0)
+          {
+            //are we inside a subj polygon ...
+            bool Inside = true;
+            TEdge e2 = e.PrevInAEL;
+            while (e2 != null)
+            {
+              if (e2.PolyTyp == e.PolyTyp && e2.WindDelta != 0)
+                Inside = !Inside;
+              e2 = e2.PrevInAEL;
+            }
+            edge.WindCnt = (Inside ? 0 : 1);
+          }
+          else
+          {
+            edge.WindCnt = edge.WindDelta;
+          }
+          edge.WindCnt2 = e.WindCnt2;
+          e = e.NextInAEL; //ie get ready to calc WindCnt2
+        }
+        else
+        {
+          //nonZero, Positive or Negative filling ...
+          if (e.WindCnt * e.WindDelta < 0)
+          {
+            //prev edge is 'decreasing' WindCount (WC) toward zero
+            //so we're outside the previous polygon ...
+            if (Math.Abs(e.WindCnt) > 1)
+            {
+              //outside prev poly but still inside another.
+              //when reversing direction of prev poly use the same WC 
+              if (e.WindDelta * edge.WindDelta < 0) edge.WindCnt = e.WindCnt;
+              //otherwise continue to 'decrease' WC ...
+              else edge.WindCnt = e.WindCnt + edge.WindDelta;
+            }
+            else
+              //now outside all polys of same polytype so set own WC ...
+              edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta);
+          }
+          else
+          {
+            //prev edge is 'increasing' WindCount (WC) away from zero
+            //so we're inside the previous polygon ...
+            if (edge.WindDelta == 0)
+              edge.WindCnt = (e.WindCnt < 0 ? e.WindCnt - 1 : e.WindCnt + 1);
+            //if wind direction is reversing prev then use same WC
+            else if (e.WindDelta * edge.WindDelta < 0)
+              edge.WindCnt = e.WindCnt;
+            //otherwise add to WC ...
+            else edge.WindCnt = e.WindCnt + edge.WindDelta;
+          }
+          edge.WindCnt2 = e.WindCnt2;
+          e = e.NextInAEL; //ie get ready to calc WindCnt2
+        }
+
+        //update WindCnt2 ...
+        if (IsEvenOddAltFillType(edge))
+        {
+          //EvenOdd filling ...
+          while (e != edge)
+          {
+            if (e.WindDelta != 0)
+              edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0);
+            e = e.NextInAEL;
+          }
+        }
+        else
+        {
+          //nonZero, Positive or Negative filling ...
+          while (e != edge)
+          {
+            edge.WindCnt2 += e.WindDelta;
+            e = e.NextInAEL;
+          }
+        }
+      }
+      //------------------------------------------------------------------------------
+
+      private void AddEdgeToSEL(TEdge edge)
+      {
+          //SEL pointers in PEdge are reused to build a list of horizontal edges.
+          //However, we don't need to worry about order with horizontal edge processing.
+          if (m_SortedEdges == null)
+          {
+              m_SortedEdges = edge;
+              edge.PrevInSEL = null;
+              edge.NextInSEL = null;
+          }
+          else
+          {
+              edge.NextInSEL = m_SortedEdges;
+              edge.PrevInSEL = null;
+              m_SortedEdges.PrevInSEL = edge;
+              m_SortedEdges = edge;
+          }
+      }
+      //------------------------------------------------------------------------------
+
+      private void CopyAELToSEL()
+      {
+          TEdge e = m_ActiveEdges;
+          m_SortedEdges = e;
+          while (e != null)
+          {
+              e.PrevInSEL = e.PrevInAEL;
+              e.NextInSEL = e.NextInAEL;
+              e = e.NextInAEL;
+          }
+      }
+      //------------------------------------------------------------------------------
+
+      private void SwapPositionsInAEL(TEdge edge1, TEdge edge2)
+      {
+        //check that one or other edge hasn't already been removed from AEL ...
+          if (edge1.NextInAEL == edge1.PrevInAEL ||
+            edge2.NextInAEL == edge2.PrevInAEL) return;
+        
+          if (edge1.NextInAEL == edge2)
+          {
+              TEdge next = edge2.NextInAEL;
+              if (next != null)
+                  next.PrevInAEL = edge1;
+              TEdge prev = edge1.PrevInAEL;
+              if (prev != null)
+                  prev.NextInAEL = edge2;
+              edge2.PrevInAEL = prev;
+              edge2.NextInAEL = edge1;
+              edge1.PrevInAEL = edge2;
+              edge1.NextInAEL = next;
+          }
+          else if (edge2.NextInAEL == edge1)
+          {
+              TEdge next = edge1.NextInAEL;
+              if (next != null)
+                  next.PrevInAEL = edge2;
+              TEdge prev = edge2.PrevInAEL;
+              if (prev != null)
+                  prev.NextInAEL = edge1;
+              edge1.PrevInAEL = prev;
+              edge1.NextInAEL = edge2;
+              edge2.PrevInAEL = edge1;
+              edge2.NextInAEL = next;
+          }
+          else
+          {
+              TEdge next = edge1.NextInAEL;
+              TEdge prev = edge1.PrevInAEL;
+              edge1.NextInAEL = edge2.NextInAEL;
+              if (edge1.NextInAEL != null)
+                  edge1.NextInAEL.PrevInAEL = edge1;
+              edge1.PrevInAEL = edge2.PrevInAEL;
+              if (edge1.PrevInAEL != null)
+                  edge1.PrevInAEL.NextInAEL = edge1;
+              edge2.NextInAEL = next;
+              if (edge2.NextInAEL != null)
+                  edge2.NextInAEL.PrevInAEL = edge2;
+              edge2.PrevInAEL = prev;
+              if (edge2.PrevInAEL != null)
+                  edge2.PrevInAEL.NextInAEL = edge2;
+          }
+
+          if (edge1.PrevInAEL == null)
+              m_ActiveEdges = edge1;
+          else if (edge2.PrevInAEL == null)
+              m_ActiveEdges = edge2;
+      }
+      //------------------------------------------------------------------------------
+
+      private void SwapPositionsInSEL(TEdge edge1, TEdge edge2)
+      {
+          if (edge1.NextInSEL == null && edge1.PrevInSEL == null)
+              return;
+          if (edge2.NextInSEL == null && edge2.PrevInSEL == null)
+              return;
+
+          if (edge1.NextInSEL == edge2)
+          {
+              TEdge next = edge2.NextInSEL;
+              if (next != null)
+                  next.PrevInSEL = edge1;
+              TEdge prev = edge1.PrevInSEL;
+              if (prev != null)
+                  prev.NextInSEL = edge2;
+              edge2.PrevInSEL = prev;
+              edge2.NextInSEL = edge1;
+              edge1.PrevInSEL = edge2;
+              edge1.NextInSEL = next;
+          }
+          else if (edge2.NextInSEL == edge1)
+          {
+              TEdge next = edge1.NextInSEL;
+              if (next != null)
+                  next.PrevInSEL = edge2;
+              TEdge prev = edge2.PrevInSEL;
+              if (prev != null)
+                  prev.NextInSEL = edge1;
+              edge1.PrevInSEL = prev;
+              edge1.NextInSEL = edge2;
+              edge2.PrevInSEL = edge1;
+              edge2.NextInSEL = next;
+          }
+          else
+          {
+              TEdge next = edge1.NextInSEL;
+              TEdge prev = edge1.PrevInSEL;
+              edge1.NextInSEL = edge2.NextInSEL;
+              if (edge1.NextInSEL != null)
+                  edge1.NextInSEL.PrevInSEL = edge1;
+              edge1.PrevInSEL = edge2.PrevInSEL;
+              if (edge1.PrevInSEL != null)
+                  edge1.PrevInSEL.NextInSEL = edge1;
+              edge2.NextInSEL = next;
+              if (edge2.NextInSEL != null)
+                  edge2.NextInSEL.PrevInSEL = edge2;
+              edge2.PrevInSEL = prev;
+              if (edge2.PrevInSEL != null)
+                  edge2.PrevInSEL.NextInSEL = edge2;
+          }
+
+          if (edge1.PrevInSEL == null)
+              m_SortedEdges = edge1;
+          else if (edge2.PrevInSEL == null)
+              m_SortedEdges = edge2;
+      }
+      //------------------------------------------------------------------------------
+
+
+      private void AddLocalMaxPoly(TEdge e1, TEdge e2, IntPoint pt)
+      {
+          AddOutPt(e1, pt);
+          if (e2.WindDelta == 0) AddOutPt(e2, pt);
+          if (e1.OutIdx == e2.OutIdx)
+          {
+              e1.OutIdx = Unassigned;
+              e2.OutIdx = Unassigned;
+          }
+          else if (e1.OutIdx < e2.OutIdx) 
+              AppendPolygon(e1, e2);
+          else 
+              AppendPolygon(e2, e1);
+      }
+      //------------------------------------------------------------------------------
+
+      private OutPt AddLocalMinPoly(TEdge e1, TEdge e2, IntPoint pt)
+      {
+        OutPt result;
+        TEdge e, prevE;
+        if (IsHorizontal(e2) || (e1.Dx > e2.Dx))
+        {
+          result = AddOutPt(e1, pt);
+          e2.OutIdx = e1.OutIdx;
+          e1.Side = EdgeSide.esLeft;
+          e2.Side = EdgeSide.esRight;
+          e = e1;
+          if (e.PrevInAEL == e2)
+            prevE = e2.PrevInAEL; 
+          else
+            prevE = e.PrevInAEL;
+        }
+        else
+        {
+          result = AddOutPt(e2, pt);
+          e1.OutIdx = e2.OutIdx;
+          e1.Side = EdgeSide.esRight;
+          e2.Side = EdgeSide.esLeft;
+          e = e2;
+          if (e.PrevInAEL == e1)
+              prevE = e1.PrevInAEL;
+          else
+              prevE = e.PrevInAEL;
+        }
+
+        if (prevE != null && prevE.OutIdx >= 0 &&
+            (TopX(prevE, pt.Y) == TopX(e, pt.Y)) &&
+            SlopesEqual(e, prevE, m_UseFullRange) &&
+            (e.WindDelta != 0) && (prevE.WindDelta != 0))
+        {
+          OutPt outPt = AddOutPt(prevE, pt);
+          AddJoin(result, outPt, e.Top);
+        }
+        return result;
+      }
+      //------------------------------------------------------------------------------
+
+      private OutRec CreateOutRec()
+      {
+        OutRec result = new OutRec();
+        result.Idx = Unassigned;
+        result.IsHole = false;
+        result.IsOpen = false;
+        result.FirstLeft = null;
+        result.Pts = null;
+        result.BottomPt = null;
+        result.PolyNode = null;
+        m_PolyOuts.Add(result);
+        result.Idx = m_PolyOuts.Count - 1;
+        return result;
+      }
+      //------------------------------------------------------------------------------
+
+      private OutPt AddOutPt(TEdge e, IntPoint pt)
+      {
+          if (e.OutIdx < 0)
+          {
+              OutRec outRec = CreateOutRec();
+              outRec.IsOpen = (e.WindDelta == 0);
+              OutPt newOp = new OutPt();
+              outRec.Pts = newOp;
+              newOp.Idx = outRec.Idx;
+              newOp.Pt = pt;
+              newOp.Next = newOp;
+              newOp.Prev = newOp;
+              if (!outRec.IsOpen)
+                  SetHoleState(e, outRec);
+              e.OutIdx = outRec.Idx; //nb: do this after SetZ !
+              return newOp;
+          }
+          else
+          {
+              OutRec outRec = m_PolyOuts[e.OutIdx];
+              //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most'
+              OutPt op = outRec.Pts;
+              bool ToFront = (e.Side == EdgeSide.esLeft);
+              if (ToFront && pt == op.Pt) return op;
+              else if (!ToFront && pt == op.Prev.Pt) return op.Prev;
+
+              OutPt newOp = new OutPt();
+              newOp.Idx = outRec.Idx;
+              newOp.Pt = pt;
+              newOp.Next = op;
+              newOp.Prev = op.Prev;
+              newOp.Prev.Next = newOp;
+              op.Prev = newOp;
+              if (ToFront) outRec.Pts = newOp;
+              return newOp;
+          }
+      }
+      //------------------------------------------------------------------------------
+
+      private OutPt GetLastOutPt(TEdge e)
+      {
+        OutRec outRec = m_PolyOuts[e.OutIdx];
+        if (e.Side == EdgeSide.esLeft) 
+            return outRec.Pts;
+        else
+            return outRec.Pts.Prev;
+      }
+      //------------------------------------------------------------------------------
+
+      internal void SwapPoints(ref IntPoint pt1, ref IntPoint pt2)
+      {
+          IntPoint tmp = new IntPoint(pt1);
+          pt1 = pt2;
+          pt2 = tmp;
+      }
+      //------------------------------------------------------------------------------
+
+      private bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b)
+      {
+        if (seg1a > seg1b) Swap(ref seg1a, ref seg1b);
+        if (seg2a > seg2b) Swap(ref seg2a, ref seg2b);
+        return (seg1a < seg2b) && (seg2a < seg1b);
+      }
+      //------------------------------------------------------------------------------
+  
+      private void SetHoleState(TEdge e, OutRec outRec)
+      {
+          bool isHole = false;
+          TEdge e2 = e.PrevInAEL;
+          while (e2 != null)
+          {
+              if (e2.OutIdx >= 0 && e2.WindDelta != 0) 
+              {
+                  isHole = !isHole;
+                  if (outRec.FirstLeft == null)
+                      outRec.FirstLeft = m_PolyOuts[e2.OutIdx];
+              }
+              e2 = e2.PrevInAEL;
+          }
+          if (isHole) 
+            outRec.IsHole = true;
+      }
+      //------------------------------------------------------------------------------
+
+      private double GetDx(IntPoint pt1, IntPoint pt2)
+      {
+          if (pt1.Y == pt2.Y) return horizontal;
+          else return (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y);
+      }
+      //---------------------------------------------------------------------------
+
+      private bool FirstIsBottomPt(OutPt btmPt1, OutPt btmPt2)
+      {
+        OutPt p = btmPt1.Prev;
+        while ((p.Pt == btmPt1.Pt) && (p != btmPt1)) p = p.Prev;
+        double dx1p = Math.Abs(GetDx(btmPt1.Pt, p.Pt));
+        p = btmPt1.Next;
+        while ((p.Pt == btmPt1.Pt) && (p != btmPt1)) p = p.Next;
+        double dx1n = Math.Abs(GetDx(btmPt1.Pt, p.Pt));
+
+        p = btmPt2.Prev;
+        while ((p.Pt == btmPt2.Pt) && (p != btmPt2)) p = p.Prev;
+        double dx2p = Math.Abs(GetDx(btmPt2.Pt, p.Pt));
+        p = btmPt2.Next;
+        while ((p.Pt == btmPt2.Pt) && (p != btmPt2)) p = p.Next;
+        double dx2n = Math.Abs(GetDx(btmPt2.Pt, p.Pt));
+        return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n);
+      }
+      //------------------------------------------------------------------------------
+
+      private OutPt GetBottomPt(OutPt pp)
+      {
+        OutPt dups = null;
+        OutPt p = pp.Next;
+        while (p != pp)
+        {
+          if (p.Pt.Y > pp.Pt.Y)
+          {
+            pp = p;
+            dups = null;
+          }
+          else if (p.Pt.Y == pp.Pt.Y && p.Pt.X <= pp.Pt.X)
+          {
+            if (p.Pt.X < pp.Pt.X)
+            {
+                dups = null;
+                pp = p;
+            } else
+            {
+              if (p.Next != pp && p.Prev != pp) dups = p;
+            }
+          }
+          p = p.Next;
+        }
+        if (dups != null)
+        {
+          //there appears to be at least 2 vertices at bottomPt so ...
+          while (dups != p)
+          {
+            if (!FirstIsBottomPt(p, dups)) pp = dups;
+            dups = dups.Next;
+            while (dups.Pt != pp.Pt) dups = dups.Next;
+          }
+        }
+        return pp;
+      }
+      //------------------------------------------------------------------------------
+
+      private OutRec GetLowermostRec(OutRec outRec1, OutRec outRec2)
+      {
+          //work out which polygon fragment has the correct hole state ...
+          if (outRec1.BottomPt == null) 
+              outRec1.BottomPt = GetBottomPt(outRec1.Pts);
+          if (outRec2.BottomPt == null) 
+              outRec2.BottomPt = GetBottomPt(outRec2.Pts);
+          OutPt bPt1 = outRec1.BottomPt;
+          OutPt bPt2 = outRec2.BottomPt;
+          if (bPt1.Pt.Y > bPt2.Pt.Y) return outRec1;
+          else if (bPt1.Pt.Y < bPt2.Pt.Y) return outRec2;
+          else if (bPt1.Pt.X < bPt2.Pt.X) return outRec1;
+          else if (bPt1.Pt.X > bPt2.Pt.X) return outRec2;
+          else if (bPt1.Next == bPt1) return outRec2;
+          else if (bPt2.Next == bPt2) return outRec1;
+          else if (FirstIsBottomPt(bPt1, bPt2)) return outRec1;
+          else return outRec2;
+      }
+      //------------------------------------------------------------------------------
+
+      bool Param1RightOfParam2(OutRec outRec1, OutRec outRec2)
+      {
+          do
+          {
+              outRec1 = outRec1.FirstLeft;
+              if (outRec1 == outRec2) return true;
+          } while (outRec1 != null);
+          return false;
+      }
+      //------------------------------------------------------------------------------
+
+      private OutRec GetOutRec(int idx)
+      {
+        OutRec outrec = m_PolyOuts[idx];
+        while (outrec != m_PolyOuts[outrec.Idx])
+          outrec = m_PolyOuts[outrec.Idx];
+        return outrec;
+      }
+      //------------------------------------------------------------------------------
+
+      private void AppendPolygon(TEdge e1, TEdge e2)
+      {
+        //get the start and ends of both output polygons ...
+        OutRec outRec1 = m_PolyOuts[e1.OutIdx];
+        OutRec outRec2 = m_PolyOuts[e2.OutIdx];
+
+        OutRec holeStateRec;
+        if (Param1RightOfParam2(outRec1, outRec2)) 
+            holeStateRec = outRec2;
+        else if (Param1RightOfParam2(outRec2, outRec1))
+            holeStateRec = outRec1;
+        else
+            holeStateRec = GetLowermostRec(outRec1, outRec2);
+
+        OutPt p1_lft = outRec1.Pts;
+        OutPt p1_rt = p1_lft.Prev;
+        OutPt p2_lft = outRec2.Pts;
+        OutPt p2_rt = p2_lft.Prev;
+
+        EdgeSide side;
+        //join e2 poly onto e1 poly and delete pointers to e2 ...
+        if(  e1.Side == EdgeSide.esLeft )
+        {
+          if (e2.Side == EdgeSide.esLeft)
+          {
+            //z y x a b c
+            ReversePolyPtLinks(p2_lft);
+            p2_lft.Next = p1_lft;
+            p1_lft.Prev = p2_lft;
+            p1_rt.Next = p2_rt;
+            p2_rt.Prev = p1_rt;
+            outRec1.Pts = p2_rt;
+          } else
+          {
+            //x y z a b c
+            p2_rt.Next = p1_lft;
+            p1_lft.Prev = p2_rt;
+            p2_lft.Prev = p1_rt;
+            p1_rt.Next = p2_lft;
+            outRec1.Pts = p2_lft;
+          }
+          side = EdgeSide.esLeft;
+        } else
+        {
+          if (e2.Side == EdgeSide.esRight)
+          {
+            //a b c z y x
+            ReversePolyPtLinks( p2_lft );
+            p1_rt.Next = p2_rt;
+            p2_rt.Prev = p1_rt;
+            p2_lft.Next = p1_lft;
+            p1_lft.Prev = p2_lft;
+          } else
+          {
+            //a b c x y z
+            p1_rt.Next = p2_lft;
+            p2_lft.Prev = p1_rt;
+            p1_lft.Prev = p2_rt;
+            p2_rt.Next = p1_lft;
+          }
+          side = EdgeSide.esRight;
+        }
+
+        outRec1.BottomPt = null; 
+        if (holeStateRec == outRec2)
+        {
+            if (outRec2.FirstLeft != outRec1)
+                outRec1.FirstLeft = outRec2.FirstLeft;
+            outRec1.IsHole = outRec2.IsHole;
+        }
+        outRec2.Pts = null;
+        outRec2.BottomPt = null;
+
+        outRec2.FirstLeft = outRec1;
+
+        int OKIdx = e1.OutIdx;
+        int ObsoleteIdx = e2.OutIdx;
+
+        e1.OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly
+        e2.OutIdx = Unassigned;
+
+        TEdge e = m_ActiveEdges;
+        while( e != null )
+        {
+          if( e.OutIdx == ObsoleteIdx )
+          {
+            e.OutIdx = OKIdx;
+            e.Side = side;
+            break;
+          }
+          e = e.NextInAEL;
+        }
+        outRec2.Idx = outRec1.Idx;
+      }
+      //------------------------------------------------------------------------------
+
+      private void ReversePolyPtLinks(OutPt pp)
+      {
+          if (pp == null) return;
+          OutPt pp1;
+          OutPt pp2;
+          pp1 = pp;
+          do
+          {
+              pp2 = pp1.Next;
+              pp1.Next = pp1.Prev;
+              pp1.Prev = pp2;
+              pp1 = pp2;
+          } while (pp1 != pp);
+      }
+      //------------------------------------------------------------------------------
+
+      private static void SwapSides(TEdge edge1, TEdge edge2)
+      {
+          EdgeSide side = edge1.Side;
+          edge1.Side = edge2.Side;
+          edge2.Side = side;
+      }
+      //------------------------------------------------------------------------------
+
+      private static void SwapPolyIndexes(TEdge edge1, TEdge edge2)
+      {
+          int outIdx = edge1.OutIdx;
+          edge1.OutIdx = edge2.OutIdx;
+          edge2.OutIdx = outIdx;
+      }
+      //------------------------------------------------------------------------------
+
+      private void IntersectEdges(TEdge e1, TEdge e2, IntPoint pt)
+      {
+          //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before
+          //e2 in AEL except when e1 is being inserted at the intersection point ...
+
+        bool e1Contributing = (e1.OutIdx >= 0);
+        bool e2Contributing = (e2.OutIdx >= 0);
+
+#if use_xyz
+          SetZ(ref pt, e1, e2);
+#endif
+
+#if use_lines
+          //if either edge is on an OPEN path ...
+          if (e1.WindDelta == 0 || e2.WindDelta == 0)
+          {
+            //ignore subject-subject open path intersections UNLESS they
+            //are both open paths, AND they are both 'contributing maximas' ...
+            if (e1.WindDelta == 0 && e2.WindDelta == 0) return;
+            //if intersecting a subj line with a subj poly ...
+            else if (e1.PolyTyp == e2.PolyTyp && 
+              e1.WindDelta != e2.WindDelta && m_ClipType == ClipType.ctUnion)
+            {
+              if (e1.WindDelta == 0)
+              {
+                if (e2Contributing)
+                {
+                  AddOutPt(e1, pt);
+                  if (e1Contributing) e1.OutIdx = Unassigned;
+                }
+              }
+              else
+              {
+                if (e1Contributing)
+                {
+                  AddOutPt(e2, pt);
+                  if (e2Contributing) e2.OutIdx = Unassigned;
+                }
+              }
+            }
+            else if (e1.PolyTyp != e2.PolyTyp)
+            {
+              if ((e1.WindDelta == 0) && Math.Abs(e2.WindCnt) == 1 && 
+                (m_ClipType != ClipType.ctUnion || e2.WindCnt2 == 0))
+              {
+                AddOutPt(e1, pt);
+                if (e1Contributing) e1.OutIdx = Unassigned;
+              }
+              else if ((e2.WindDelta == 0) && (Math.Abs(e1.WindCnt) == 1) && 
+                (m_ClipType != ClipType.ctUnion || e1.WindCnt2 == 0))
+              {
+                AddOutPt(e2, pt);
+                if (e2Contributing) e2.OutIdx = Unassigned;
+              }
+            }
+            return;
+          }
+#endif
+
+          //update winding counts...
+  //assumes that e1 will be to the Right of e2 ABOVE the intersection
+          if (e1.PolyTyp == e2.PolyTyp)
+          {
+              if (IsEvenOddFillType(e1))
+              {
+                  int oldE1WindCnt = e1.WindCnt;
+                  e1.WindCnt = e2.WindCnt;
+                  e2.WindCnt = oldE1WindCnt;
+              }
+              else
+              {
+                  if (e1.WindCnt + e2.WindDelta == 0) e1.WindCnt = -e1.WindCnt;
+                  else e1.WindCnt += e2.WindDelta;
+                  if (e2.WindCnt - e1.WindDelta == 0) e2.WindCnt = -e2.WindCnt;
+                  else e2.WindCnt -= e1.WindDelta;
+              }
+          }
+          else
+          {
+              if (!IsEvenOddFillType(e2)) e1.WindCnt2 += e2.WindDelta;
+              else e1.WindCnt2 = (e1.WindCnt2 == 0) ? 1 : 0;
+              if (!IsEvenOddFillType(e1)) e2.WindCnt2 -= e1.WindDelta;
+              else e2.WindCnt2 = (e2.WindCnt2 == 0) ? 1 : 0;
+          }
+
+          PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2;
+          if (e1.PolyTyp == PolyType.ptSubject)
+          {
+              e1FillType = m_SubjFillType;
+              e1FillType2 = m_ClipFillType;
+          }
+          else
+          {
+              e1FillType = m_ClipFillType;
+              e1FillType2 = m_SubjFillType;
+          }
+          if (e2.PolyTyp == PolyType.ptSubject)
+          {
+              e2FillType = m_SubjFillType;
+              e2FillType2 = m_ClipFillType;
+          }
+          else
+          {
+              e2FillType = m_ClipFillType;
+              e2FillType2 = m_SubjFillType;
+          }
+
+          int e1Wc, e2Wc;
+          switch (e1FillType)
+          {
+              case PolyFillType.pftPositive: e1Wc = e1.WindCnt; break;
+              case PolyFillType.pftNegative: e1Wc = -e1.WindCnt; break;
+              default: e1Wc = Math.Abs(e1.WindCnt); break;
+          }
+          switch (e2FillType)
+          {
+              case PolyFillType.pftPositive: e2Wc = e2.WindCnt; break;
+              case PolyFillType.pftNegative: e2Wc = -e2.WindCnt; break;
+              default: e2Wc = Math.Abs(e2.WindCnt); break;
+          }
+
+          if (e1Contributing && e2Contributing)
+          {
+            if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) ||
+              (e1.PolyTyp != e2.PolyTyp && m_ClipType != ClipType.ctXor))
+            {
+              AddLocalMaxPoly(e1, e2, pt);
+            }
+            else
+            {
+              AddOutPt(e1, pt);
+              AddOutPt(e2, pt);
+              SwapSides(e1, e2);
+              SwapPolyIndexes(e1, e2);
+            }
+          }
+          else if (e1Contributing)
+          {
+              if (e2Wc == 0 || e2Wc == 1)
+              {
+                AddOutPt(e1, pt);
+                SwapSides(e1, e2);
+                SwapPolyIndexes(e1, e2);
+              }
+
+          }
+          else if (e2Contributing)
+          {
+              if (e1Wc == 0 || e1Wc == 1)
+              {
+                AddOutPt(e2, pt);
+                SwapSides(e1, e2);
+                SwapPolyIndexes(e1, e2);
+              }
+          }
+          else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1))
+          {
+              //neither edge is currently contributing ...
+              cInt e1Wc2, e2Wc2;
+              switch (e1FillType2)
+              {
+                  case PolyFillType.pftPositive: e1Wc2 = e1.WindCnt2; break;
+                  case PolyFillType.pftNegative: e1Wc2 = -e1.WindCnt2; break;
+                  default: e1Wc2 = Math.Abs(e1.WindCnt2); break;
+              }
+              switch (e2FillType2)
+              {
+                  case PolyFillType.pftPositive: e2Wc2 = e2.WindCnt2; break;
+                  case PolyFillType.pftNegative: e2Wc2 = -e2.WindCnt2; break;
+                  default: e2Wc2 = Math.Abs(e2.WindCnt2); break;
+              }
+
+              if (e1.PolyTyp != e2.PolyTyp)
+              {
+                AddLocalMinPoly(e1, e2, pt);
+              }
+              else if (e1Wc == 1 && e2Wc == 1)
+                switch (m_ClipType)
+                {
+                  case ClipType.ctIntersection:
+                    if (e1Wc2 > 0 && e2Wc2 > 0)
+                      AddLocalMinPoly(e1, e2, pt);
+                    break;
+                  case ClipType.ctUnion:
+                    if (e1Wc2 <= 0 && e2Wc2 <= 0)
+                      AddLocalMinPoly(e1, e2, pt);
+                    break;
+                  case ClipType.ctDifference:
+                    if (((e1.PolyTyp == PolyType.ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
+                        ((e1.PolyTyp == PolyType.ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
+                          AddLocalMinPoly(e1, e2, pt);
+                    break;
+                  case ClipType.ctXor:
+                    AddLocalMinPoly(e1, e2, pt);
+                    break;
+                }
+              else
+                SwapSides(e1, e2);
+          }
+      }
+      //------------------------------------------------------------------------------
+
+      private void DeleteFromAEL(TEdge e)
+      {
+          TEdge AelPrev = e.PrevInAEL;
+          TEdge AelNext = e.NextInAEL;
+          if (AelPrev == null && AelNext == null && (e != m_ActiveEdges))
+              return; //already deleted
+          if (AelPrev != null)
+              AelPrev.NextInAEL = AelNext;
+          else m_ActiveEdges = AelNext;
+          if (AelNext != null)
+              AelNext.PrevInAEL = AelPrev;
+          e.NextInAEL = null;
+          e.PrevInAEL = null;
+      }
+      //------------------------------------------------------------------------------
+
+      private void DeleteFromSEL(TEdge e)
+      {
+          TEdge SelPrev = e.PrevInSEL;
+          TEdge SelNext = e.NextInSEL;
+          if (SelPrev == null && SelNext == null && (e != m_SortedEdges))
+              return; //already deleted
+          if (SelPrev != null)
+              SelPrev.NextInSEL = SelNext;
+          else m_SortedEdges = SelNext;
+          if (SelNext != null)
+              SelNext.PrevInSEL = SelPrev;
+          e.NextInSEL = null;
+          e.PrevInSEL = null;
+      }
+      //------------------------------------------------------------------------------
+
+      private void UpdateEdgeIntoAEL(ref TEdge e)
+      {
+          if (e.NextInLML == null)
+              throw new ClipperException("UpdateEdgeIntoAEL: invalid call");
+          TEdge AelPrev = e.PrevInAEL;
+          TEdge AelNext = e.NextInAEL;
+          e.NextInLML.OutIdx = e.OutIdx;
+          if (AelPrev != null)
+              AelPrev.NextInAEL = e.NextInLML;
+          else m_ActiveEdges = e.NextInLML;
+          if (AelNext != null)
+              AelNext.PrevInAEL = e.NextInLML;
+          e.NextInLML.Side = e.Side;
+          e.NextInLML.WindDelta = e.WindDelta;
+          e.NextInLML.WindCnt = e.WindCnt;
+          e.NextInLML.WindCnt2 = e.WindCnt2;
+          e = e.NextInLML;
+          e.Curr = e.Bot;
+          e.PrevInAEL = AelPrev;
+          e.NextInAEL = AelNext;
+          if (!IsHorizontal(e)) InsertScanbeam(e.Top.Y);
+      }
+      //------------------------------------------------------------------------------
+
+      private void ProcessHorizontals()
+      {
+          TEdge horzEdge = m_SortedEdges;
+          while (horzEdge != null)
+          {
+              DeleteFromSEL(horzEdge);
+              ProcessHorizontal(horzEdge);
+              horzEdge = m_SortedEdges;
+          }
+      }
+      //------------------------------------------------------------------------------
+
+      void GetHorzDirection(TEdge HorzEdge, out Direction Dir, out cInt Left, out cInt Right)
+      {
+        if (HorzEdge.Bot.X < HorzEdge.Top.X)
+        {
+          Left = HorzEdge.Bot.X;
+          Right = HorzEdge.Top.X;
+          Dir = Direction.dLeftToRight;
+        } else
+        {
+          Left = HorzEdge.Top.X;
+          Right = HorzEdge.Bot.X;
+          Dir = Direction.dRightToLeft;
+        }
+      }
+      //------------------------------------------------------------------------
+
+      private void ProcessHorizontal(TEdge horzEdge)
+      {
+        Direction dir;
+        cInt horzLeft, horzRight;
+        bool IsOpen = horzEdge.OutIdx >= 0 && m_PolyOuts[horzEdge.OutIdx].IsOpen;
+
+        GetHorzDirection(horzEdge, out dir, out horzLeft, out horzRight);
+
+        TEdge eLastHorz = horzEdge, eMaxPair = null;
+        while (eLastHorz.NextInLML != null && IsHorizontal(eLastHorz.NextInLML)) 
+          eLastHorz = eLastHorz.NextInLML;
+        if (eLastHorz.NextInLML == null)
+          eMaxPair = GetMaximaPair(eLastHorz);
+
+        Maxima currMax = m_Maxima;
+        if (currMax != null)
+        {
+            //get the first maxima in range (X) ...
+            if (dir == Direction.dLeftToRight)
+            {
+              while (currMax != null && currMax.X <= horzEdge.Bot.X)
+                  currMax = currMax.Next;
+              if (currMax != null && currMax.X >= eLastHorz.Top.X) 
+                  currMax = null;
+            }
+            else
+            {
+              while (currMax.Next != null && currMax.Next.X < horzEdge.Bot.X) 
+                  currMax = currMax.Next;
+              if (currMax.X <= eLastHorz.Top.X) currMax = null;
+            }
+        }
+
+        OutPt op1 = null;
+        for (;;) //loop through consec. horizontal edges
+        {
+          bool IsLastHorz = (horzEdge == eLastHorz);
+          TEdge e = GetNextInAEL(horzEdge, dir);
+          while(e != null)
+          {
+
+              //this code block inserts extra coords into horizontal edges (in output
+              //polygons) whereever maxima touch these horizontal edges. This helps
+              //'simplifying' polygons (ie if the Simplify property is set).
+              if (currMax != null)
+              {
+                  if (dir == Direction.dLeftToRight)
+                  {
+                      while (currMax != null && currMax.X < e.Curr.X) 
+                      {
+                        if (horzEdge.OutIdx >= 0 && !IsOpen) 
+                          AddOutPt(horzEdge, new IntPoint(currMax.X, horzEdge.Bot.Y));
+                        currMax = currMax.Next;                  
+                      }
+                  }
+                  else
+                  {
+                      while (currMax != null && currMax.X > e.Curr.X)
+                      {
+                          if (horzEdge.OutIdx >= 0 && !IsOpen)
+                            AddOutPt(horzEdge, new IntPoint(currMax.X, horzEdge.Bot.Y));
+                        currMax = currMax.Prev;
+                      }
+                  }
+              };
+
+              if ((dir == Direction.dLeftToRight && e.Curr.X > horzRight) ||
+                (dir == Direction.dRightToLeft && e.Curr.X < horzLeft)) break;
+                                
+              //Also break if we've got to the end of an intermediate horizontal edge ...
+              //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal.
+              if (e.Curr.X == horzEdge.Top.X && horzEdge.NextInLML != null && 
+                e.Dx < horzEdge.NextInLML.Dx) break;
+
+              if (horzEdge.OutIdx >= 0 && !IsOpen)  //note: may be done multiple times
+              {
+                  op1 = AddOutPt(horzEdge, e.Curr);
+                  TEdge eNextHorz = m_SortedEdges;
+                  while (eNextHorz != null)
+                  {
+                      if (eNextHorz.OutIdx >= 0 &&
+                        HorzSegmentsOverlap(horzEdge.Bot.X,
+                        horzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X))
+                      {
+                          OutPt op2 = GetLastOutPt(eNextHorz);
+                          AddJoin(op2, op1, eNextHorz.Top);
+                      }
+                      eNextHorz = eNextHorz.NextInSEL;
+                  }
+                  AddGhostJoin(op1, horzEdge.Bot);
+              }
+            
+              //OK, so far we're still in range of the horizontal Edge  but make sure
+              //we're at the last of consec. horizontals when matching with eMaxPair
+              if(e == eMaxPair && IsLastHorz)
+              {
+                if (horzEdge.OutIdx >= 0)
+                  AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge.Top);
+                DeleteFromAEL(horzEdge);
+                DeleteFromAEL(eMaxPair);
+                return;
+              }
+              
+              if(dir == Direction.dLeftToRight)
+              {
+                IntPoint Pt = new IntPoint(e.Curr.X, horzEdge.Curr.Y);
+                IntersectEdges(horzEdge, e, Pt);
+              }
+              else
+              {
+                IntPoint Pt = new IntPoint(e.Curr.X, horzEdge.Curr.Y);
+                IntersectEdges(e, horzEdge, Pt);
+              }
+              TEdge eNext = GetNextInAEL(e, dir);
+              SwapPositionsInAEL(horzEdge, e);
+              e = eNext;
+          } //end while(e != null)
+
+          //Break out of loop if HorzEdge.NextInLML is not also horizontal ...
+          if (horzEdge.NextInLML == null || !IsHorizontal(horzEdge.NextInLML)) break;
+
+          UpdateEdgeIntoAEL(ref horzEdge);
+          if (horzEdge.OutIdx >= 0) AddOutPt(horzEdge, horzEdge.Bot);
+          GetHorzDirection(horzEdge, out dir, out horzLeft, out horzRight);
+
+        } //end for (;;)
+
+        if (horzEdge.OutIdx >= 0 && op1 == null)
+        {
+            op1 = GetLastOutPt(horzEdge);
+            TEdge eNextHorz = m_SortedEdges;
+            while (eNextHorz != null)
+            {
+                if (eNextHorz.OutIdx >= 0 &&
+                  HorzSegmentsOverlap(horzEdge.Bot.X,
+                  horzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X))
+                {
+                    OutPt op2 = GetLastOutPt(eNextHorz);
+                    AddJoin(op2, op1, eNextHorz.Top);
+                }
+                eNextHorz = eNextHorz.NextInSEL;
+            }
+            AddGhostJoin(op1, horzEdge.Top);
+        }
+
+        if (horzEdge.NextInLML != null)
+        {
+          if(horzEdge.OutIdx >= 0)
+          {
+            op1 = AddOutPt( horzEdge, horzEdge.Top);
+
+            UpdateEdgeIntoAEL(ref horzEdge);
+            if (horzEdge.WindDelta == 0) return;
+            //nb: HorzEdge is no longer horizontal here
+            TEdge ePrev = horzEdge.PrevInAEL;
+            TEdge eNext = horzEdge.NextInAEL;
+            if (ePrev != null && ePrev.Curr.X == horzEdge.Bot.X &&
+              ePrev.Curr.Y == horzEdge.Bot.Y && ePrev.WindDelta != 0 &&
+              (ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y &&
+              SlopesEqual(horzEdge, ePrev, m_UseFullRange)))
+            {
+              OutPt op2 = AddOutPt(ePrev, horzEdge.Bot);
+              AddJoin(op1, op2, horzEdge.Top);
+            }
+            else if (eNext != null && eNext.Curr.X == horzEdge.Bot.X &&
+              eNext.Curr.Y == horzEdge.Bot.Y && eNext.WindDelta != 0 &&
+              eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y &&
+              SlopesEqual(horzEdge, eNext, m_UseFullRange))
+            {
+              OutPt op2 = AddOutPt(eNext, horzEdge.Bot);
+              AddJoin(op1, op2, horzEdge.Top);
+            }
+          }
+          else
+            UpdateEdgeIntoAEL(ref horzEdge); 
+        }
+        else
+        {
+          if (horzEdge.OutIdx >= 0) AddOutPt(horzEdge, horzEdge.Top);
+          DeleteFromAEL(horzEdge);
+        }
+      }
+      //------------------------------------------------------------------------------
+
+      private TEdge GetNextInAEL(TEdge e, Direction Direction)
+      {
+          return Direction == Direction.dLeftToRight ? e.NextInAEL: e.PrevInAEL;
+      }
+      //------------------------------------------------------------------------------
+
+      private bool IsMinima(TEdge e)
+      {
+          return e != null && (e.Prev.NextInLML != e) && (e.Next.NextInLML != e);
+      }
+      //------------------------------------------------------------------------------
+
+      private bool IsMaxima(TEdge e, double Y)
+      {
+          return (e != null && e.Top.Y == Y && e.NextInLML == null);
+      }
+      //------------------------------------------------------------------------------
+
+      private bool IsIntermediate(TEdge e, double Y)
+      {
+          return (e.Top.Y == Y && e.NextInLML != null);
+      }
+      //------------------------------------------------------------------------------
+
+      private TEdge GetMaximaPair(TEdge e)
+      {
+        TEdge result = null;
+        if ((e.Next.Top == e.Top) && e.Next.NextInLML == null)
+          result = e.Next;
+        else if ((e.Prev.Top == e.Top) && e.Prev.NextInLML == null)
+          result = e.Prev;
+        if (result != null && (result.OutIdx == Skip ||
+          (result.NextInAEL == result.PrevInAEL && !IsHorizontal(result))))
+          return null;
+        return result;
+      }
+      //------------------------------------------------------------------------------
+
+      private bool ProcessIntersections(cInt topY)
+      {
+        if( m_ActiveEdges == null ) return true;
+        try {
+          BuildIntersectList(topY);
+          if ( m_IntersectList.Count == 0) return true;
+          if (m_IntersectList.Count == 1 || FixupIntersectionOrder()) 
+              ProcessIntersectList();
+          else 
+              return false;
+        }
+        catch {
+          m_SortedEdges = null;
+          m_IntersectList.Clear();
+          throw new ClipperException("ProcessIntersections error");
+        }
+        m_SortedEdges = null;
+        return true;
+      }
+      //------------------------------------------------------------------------------
+
+      private void BuildIntersectList(cInt topY)
+      {
+        if ( m_ActiveEdges == null ) return;
+
+        //prepare for sorting ...
+        TEdge e = m_ActiveEdges;
+        m_SortedEdges = e;
+        while( e != null )
+        {
+          e.PrevInSEL = e.PrevInAEL;
+          e.NextInSEL = e.NextInAEL;
+          e.Curr.X = TopX( e, topY );
+          e = e.NextInAEL;
+        }
+
+        //bubblesort ...
+        bool isModified = true;
+        while( isModified && m_SortedEdges != null )
+        {
+          isModified = false;
+          e = m_SortedEdges;
+          while( e.NextInSEL != null )
+          {
+            TEdge eNext = e.NextInSEL;
+            IntPoint pt;
+            if (e.Curr.X > eNext.Curr.X)
+            {
+                IntersectPoint(e, eNext, out pt);
+                IntersectNode newNode = new IntersectNode();
+                newNode.Edge1 = e;
+                newNode.Edge2 = eNext;
+                newNode.Pt = pt;
+                m_IntersectList.Add(newNode);
+
+                SwapPositionsInSEL(e, eNext);
+                isModified = true;
+            }
+            else
+              e = eNext;
+          }
+          if( e.PrevInSEL != null ) e.PrevInSEL.NextInSEL = null;
+          else break;
+        }
+        m_SortedEdges = null;
+      }
+      //------------------------------------------------------------------------------
+
+      private bool EdgesAdjacent(IntersectNode inode)
+      {
+        return (inode.Edge1.NextInSEL == inode.Edge2) ||
+          (inode.Edge1.PrevInSEL == inode.Edge2);
+      }
+      //------------------------------------------------------------------------------
+
+      private static int IntersectNodeSort(IntersectNode node1, IntersectNode node2)
+      {
+        //the following typecast is safe because the differences in Pt.Y will
+        //be limited to the height of the scanbeam.
+        return (int)(node2.Pt.Y - node1.Pt.Y); 
+      }
+      //------------------------------------------------------------------------------
+
+      private bool FixupIntersectionOrder()
+      {
+        //pre-condition: intersections are sorted bottom-most first.
+        //Now it's crucial that intersections are made only between adjacent edges,
+        //so to ensure this the order of intersections may need adjusting ...
+        m_IntersectList.Sort(m_IntersectNodeComparer);
+
+        CopyAELToSEL();
+        int cnt = m_IntersectList.Count;
+        for (int i = 0; i < cnt; i++)
+        {
+          if (!EdgesAdjacent(m_IntersectList[i]))
+          {
+            int j = i + 1;
+            while (j < cnt && !EdgesAdjacent(m_IntersectList[j])) j++;
+            if (j == cnt) return false;
+
+            IntersectNode tmp = m_IntersectList[i];
+            m_IntersectList[i] = m_IntersectList[j];
+            m_IntersectList[j] = tmp;
+
+          }
+          SwapPositionsInSEL(m_IntersectList[i].Edge1, m_IntersectList[i].Edge2);
+        }
+          return true;
+      }
+      //------------------------------------------------------------------------------
+
+      private void ProcessIntersectList()
+      {
+        for (int i = 0; i < m_IntersectList.Count; i++)
+        {
+          IntersectNode iNode = m_IntersectList[i];
+          {
+            IntersectEdges(iNode.Edge1, iNode.Edge2, iNode.Pt);
+            SwapPositionsInAEL(iNode.Edge1, iNode.Edge2);
+          }
+        }
+        m_IntersectList.Clear();
+      }
+      //------------------------------------------------------------------------------
+
+      internal static cInt Round(double value)
+      {
+          return value < 0 ? (cInt)(value - 0.5) : (cInt)(value + 0.5);
+      }
+      //------------------------------------------------------------------------------
+
+      private static cInt TopX(TEdge edge, cInt currentY)
+      {
+          if (currentY == edge.Top.Y)
+              return edge.Top.X;
+          return edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y));
+      }
+      //------------------------------------------------------------------------------
+
+      private void IntersectPoint(TEdge edge1, TEdge edge2, out IntPoint ip)
+      {
+        ip = new IntPoint();
+        double b1, b2;
+        //nb: with very large coordinate values, it's possible for SlopesEqual() to 
+        //return false but for the edge.Dx value be equal due to double precision rounding.
+        if (edge1.Dx == edge2.Dx)
+        {
+          ip.Y = edge1.Curr.Y;
+          ip.X = TopX(edge1, ip.Y);
+          return;
+        }
+
+        if (edge1.Delta.X == 0)
+        {
+            ip.X = edge1.Bot.X;
+            if (IsHorizontal(edge2))
+            {
+                ip.Y = edge2.Bot.Y;
+            }
+            else
+            {
+                b2 = edge2.Bot.Y - (edge2.Bot.X / edge2.Dx);
+                ip.Y = Round(ip.X / edge2.Dx + b2);
+            }
+        }
+        else if (edge2.Delta.X == 0)
+        {
+            ip.X = edge2.Bot.X;
+            if (IsHorizontal(edge1))
+            {
+                ip.Y = edge1.Bot.Y;
+            }
+            else
+            {
+                b1 = edge1.Bot.Y - (edge1.Bot.X / edge1.Dx);
+                ip.Y = Round(ip.X / edge1.Dx + b1);
+            }
+        }
+        else
+        {
+            b1 = edge1.Bot.X - edge1.Bot.Y * edge1.Dx;
+            b2 = edge2.Bot.X - edge2.Bot.Y * edge2.Dx;
+            double q = (b2 - b1) / (edge1.Dx - edge2.Dx);
+            ip.Y = Round(q);
+            if (Math.Abs(edge1.Dx) < Math.Abs(edge2.Dx))
+                ip.X = Round(edge1.Dx * q + b1);
+            else
+                ip.X = Round(edge2.Dx * q + b2);
+        }
+
+        if (ip.Y < edge1.Top.Y || ip.Y < edge2.Top.Y)
+        {
+          if (edge1.Top.Y > edge2.Top.Y)
+            ip.Y = edge1.Top.Y;
+          else
+            ip.Y = edge2.Top.Y;
+          if (Math.Abs(edge1.Dx) < Math.Abs(edge2.Dx))
+            ip.X = TopX(edge1, ip.Y);
+          else
+            ip.X = TopX(edge2, ip.Y);
+        }
+        //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ...
+        if (ip.Y > edge1.Curr.Y)
+        {
+          ip.Y = edge1.Curr.Y;
+          //better to use the more vertical edge to derive X ...
+          if (Math.Abs(edge1.Dx) > Math.Abs(edge2.Dx)) 
+            ip.X = TopX(edge2, ip.Y);
+          else 
+            ip.X = TopX(edge1, ip.Y);
+        }
+      }
+      //------------------------------------------------------------------------------
+
+      private void ProcessEdgesAtTopOfScanbeam(cInt topY)
+      {
+        TEdge e = m_ActiveEdges;
+        while(e != null)
+        {
+          //1. process maxima, treating them as if they're 'bent' horizontal edges,
+          //   but exclude maxima with horizontal edges. nb: e can't be a horizontal.
+          bool IsMaximaEdge = IsMaxima(e, topY);
+
+          if(IsMaximaEdge)
+          {
+            TEdge eMaxPair = GetMaximaPair(e);
+            IsMaximaEdge = (eMaxPair == null || !IsHorizontal(eMaxPair));
+          }
+
+          if(IsMaximaEdge)
+          {
+            if (StrictlySimple) InsertMaxima(e.Top.X);
+            TEdge ePrev = e.PrevInAEL;
+            DoMaxima(e);
+            if( ePrev == null) e = m_ActiveEdges;
+            else e = ePrev.NextInAEL;
+          }
+          else
+          {
+            //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ...
+            if (IsIntermediate(e, topY) && IsHorizontal(e.NextInLML))
+            {
+              UpdateEdgeIntoAEL(ref e);
+              if (e.OutIdx >= 0)
+                AddOutPt(e, e.Bot);
+              AddEdgeToSEL(e);
+            } 
+            else
+            {
+              e.Curr.X = TopX( e, topY );
+              e.Curr.Y = topY;
+            }
+
+            //When StrictlySimple and 'e' is being touched by another edge, then
+            //make sure both edges have a vertex here ...
+            if (StrictlySimple)
+            {
+              TEdge ePrev = e.PrevInAEL;
+              if ((e.OutIdx >= 0) && (e.WindDelta != 0) && ePrev != null &&
+                (ePrev.OutIdx >= 0) && (ePrev.Curr.X == e.Curr.X) &&
+                (ePrev.WindDelta != 0))
+              {
+                IntPoint ip = new IntPoint(e.Curr);
+#if use_xyz
+                SetZ(ref ip, ePrev, e);
+#endif
+                OutPt op = AddOutPt(ePrev, ip);
+                OutPt op2 = AddOutPt(e, ip);
+                AddJoin(op, op2, ip); //StrictlySimple (type-3) join
+              }
+            }
+
+            e = e.NextInAEL;
+          }
+        }
+
+        //3. Process horizontals at the Top of the scanbeam ...
+        ProcessHorizontals();
+        m_Maxima = null;
+
+        //4. Promote intermediate vertices ...
+        e = m_ActiveEdges;
+        while (e != null)
+        {
+          if(IsIntermediate(e, topY))
+          {
+            OutPt op = null;
+            if( e.OutIdx >= 0 ) 
+              op = AddOutPt(e, e.Top);
+            UpdateEdgeIntoAEL(ref e);
+
+            //if output polygons share an edge, they'll need joining later ...
+            TEdge ePrev = e.PrevInAEL;
+            TEdge eNext = e.NextInAEL;
+            if (ePrev != null && ePrev.Curr.X == e.Bot.X &&
+              ePrev.Curr.Y == e.Bot.Y && op != null &&
+              ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y &&
+              SlopesEqual(e, ePrev, m_UseFullRange) &&
+              (e.WindDelta != 0) && (ePrev.WindDelta != 0))
+            {
+              OutPt op2 = AddOutPt(ePrev, e.Bot);
+              AddJoin(op, op2, e.Top);
+            }
+            else if (eNext != null && eNext.Curr.X == e.Bot.X &&
+              eNext.Curr.Y == e.Bot.Y && op != null &&
+              eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y &&
+              SlopesEqual(e, eNext, m_UseFullRange) &&
+              (e.WindDelta != 0) && (eNext.WindDelta != 0))
+            {
+              OutPt op2 = AddOutPt(eNext, e.Bot);
+              AddJoin(op, op2, e.Top);
+            }
+          }
+          e = e.NextInAEL;
+        }
+      }
+      //------------------------------------------------------------------------------
+
+      private void DoMaxima(TEdge e)
+      {
+        TEdge eMaxPair = GetMaximaPair(e);
+        if (eMaxPair == null)
+        {
+          if (e.OutIdx >= 0)
+            AddOutPt(e, e.Top);
+          DeleteFromAEL(e);
+          return;
+        }
+
+        TEdge eNext = e.NextInAEL;
+        while(eNext != null && eNext != eMaxPair)
+        {
+          IntersectEdges(e, eNext, e.Top);
+          SwapPositionsInAEL(e, eNext);
+          eNext = e.NextInAEL;
+        }
+
+        if(e.OutIdx == Unassigned && eMaxPair.OutIdx == Unassigned)
+        {
+          DeleteFromAEL(e);
+          DeleteFromAEL(eMaxPair);
+        }
+        else if( e.OutIdx >= 0 && eMaxPair.OutIdx >= 0 )
+        {
+          if (e.OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e.Top);
+          DeleteFromAEL(e);
+          DeleteFromAEL(eMaxPair);
+        }
+#if use_lines
+        else if (e.WindDelta == 0)
+        {
+          if (e.OutIdx >= 0) 
+          {
+            AddOutPt(e, e.Top);
+            e.OutIdx = Unassigned;
+          }
+          DeleteFromAEL(e);
+
+          if (eMaxPair.OutIdx >= 0)
+          {
+            AddOutPt(eMaxPair, e.Top);
+            eMaxPair.OutIdx = Unassigned;
+          }
+          DeleteFromAEL(eMaxPair);
+        } 
+#endif
+        else throw new ClipperException("DoMaxima error");
+      }
+      //------------------------------------------------------------------------------
+
+      public static void ReversePaths(Paths polys)
+      {
+        foreach (var poly in polys) { poly.Reverse(); }
+      }
+      //------------------------------------------------------------------------------
+
+      public static bool Orientation(Path poly)
+      {
+          return Area(poly) >= 0;
+      }
+      //------------------------------------------------------------------------------
+
+      private int PointCount(OutPt pts)
+      {
+          if (pts == null) return 0;
+          int result = 0;
+          OutPt p = pts;
+          do
+          {
+              result++;
+              p = p.Next;
+          }
+          while (p != pts);
+          return result;
+      }
+      //------------------------------------------------------------------------------
+
+      private void BuildResult(Paths polyg)
+      {
+          polyg.Clear();
+          polyg.Capacity = m_PolyOuts.Count;
+          for (int i = 0; i < m_PolyOuts.Count; i++)
+          {
+              OutRec outRec = m_PolyOuts[i];
+              if (outRec.Pts == null) continue;
+              OutPt p = outRec.Pts.Prev;
+              int cnt = PointCount(p);
+              if (cnt < 2) continue;
+              Path pg = new Path(cnt);
+              for (int j = 0; j < cnt; j++)
+              {
+                  pg.Add(p.Pt);
+                  p = p.Prev;
+              }
+              polyg.Add(pg);
+          }
+      }
+      //------------------------------------------------------------------------------
+
+      private void BuildResult2(PolyTree polytree)
+      {
+          polytree.Clear();
+
+          //add each output polygon/contour to polytree ...
+          polytree.m_AllPolys.Capacity = m_PolyOuts.Count;
+          for (int i = 0; i < m_PolyOuts.Count; i++)
+          {
+              OutRec outRec = m_PolyOuts[i];
+              int cnt = PointCount(outRec.Pts);
+              if ((outRec.IsOpen && cnt < 2) || 
+                (!outRec.IsOpen && cnt < 3)) continue;
+              FixHoleLinkage(outRec);
+              PolyNode pn = new PolyNode();
+              polytree.m_AllPolys.Add(pn);
+              outRec.PolyNode = pn;
+              pn.m_polygon.Capacity = cnt;
+              OutPt op = outRec.Pts.Prev;
+              for (int j = 0; j < cnt; j++)
+              {
+                  pn.m_polygon.Add(op.Pt);
+                  op = op.Prev;
+              }
+          }
+
+          //fixup PolyNode links etc ...
+          polytree.m_Childs.Capacity = m_PolyOuts.Count;
+          for (int i = 0; i < m_PolyOuts.Count; i++)
+          {
+              OutRec outRec = m_PolyOuts[i];
+              if (outRec.PolyNode == null) continue;
+              else if (outRec.IsOpen)
+              {
+                outRec.PolyNode.IsOpen = true;
+                polytree.AddChild(outRec.PolyNode);
+              }
+              else if (outRec.FirstLeft != null && 
+                outRec.FirstLeft.PolyNode != null)
+                  outRec.FirstLeft.PolyNode.AddChild(outRec.PolyNode);
+              else
+                polytree.AddChild(outRec.PolyNode);
+          }
+      }
+      //------------------------------------------------------------------------------
+
+      private void FixupOutPolyline(OutRec outrec)
+      {
+        OutPt pp = outrec.Pts;
+        OutPt lastPP = pp.Prev;
+        while (pp != lastPP)
+        {
+            pp = pp.Next;
+            if (pp.Pt == pp.Prev.Pt)
+            {
+                if (pp == lastPP) lastPP = pp.Prev;
+                OutPt tmpPP = pp.Prev;
+                tmpPP.Next = pp.Next;
+                pp.Next.Prev = tmpPP;
+                pp = tmpPP;
+            }
+        }
+        if (pp == pp.Prev) outrec.Pts = null;
+      }
+      //------------------------------------------------------------------------------
+
+      private void FixupOutPolygon(OutRec outRec)
+      {
+          //FixupOutPolygon() - removes duplicate points and simplifies consecutive
+          //parallel edges by removing the middle vertex.
+          OutPt lastOK = null;
+          outRec.BottomPt = null;
+          OutPt pp = outRec.Pts;
+          bool preserveCol = PreserveCollinear || StrictlySimple;
+          for (;;)
+          {
+              if (pp.Prev == pp || pp.Prev == pp.Next)
+              {
+                  outRec.Pts = null;
+                  return;
+              }
+              //test for duplicate points and collinear edges ...
+              if ((pp.Pt == pp.Next.Pt) || (pp.Pt == pp.Prev.Pt) ||
+                (SlopesEqual(pp.Prev.Pt, pp.Pt, pp.Next.Pt, m_UseFullRange) &&
+                (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp.Prev.Pt, pp.Pt, pp.Next.Pt))))
+              {
+                  lastOK = null;
+                  pp.Prev.Next = pp.Next;
+                  pp.Next.Prev = pp.Prev;
+                  pp = pp.Prev;
+              }
+              else if (pp == lastOK) break;
+              else
+              {
+                  if (lastOK == null) lastOK = pp;
+                  pp = pp.Next;
+              }
+          }
+          outRec.Pts = pp;
+      }
+      //------------------------------------------------------------------------------
+
+      OutPt DupOutPt(OutPt outPt, bool InsertAfter)
+      {
+        OutPt result = new OutPt();
+        result.Pt = outPt.Pt;
+        result.Idx = outPt.Idx;
+        if (InsertAfter)
+        {
+          result.Next = outPt.Next;
+          result.Prev = outPt;
+          outPt.Next.Prev = result;
+          outPt.Next = result;
+        } 
+        else
+        {
+          result.Prev = outPt.Prev;
+          result.Next = outPt;
+          outPt.Prev.Next = result;
+          outPt.Prev = result;
+        }
+        return result;
+      }
+      //------------------------------------------------------------------------------
+
+      bool GetOverlap(cInt a1, cInt a2, cInt b1, cInt b2, out cInt Left, out cInt Right)
+      {
+        if (a1 < a2)
+        {
+          if (b1 < b2) {Left = Math.Max(a1,b1); Right = Math.Min(a2,b2);}
+          else {Left = Math.Max(a1,b2); Right = Math.Min(a2,b1);}
+        } 
+        else
+        {
+          if (b1 < b2) {Left = Math.Max(a2,b1); Right = Math.Min(a1,b2);}
+          else { Left = Math.Max(a2, b2); Right = Math.Min(a1, b1); }
+        }
+        return Left < Right;
+      }
+      //------------------------------------------------------------------------------
+
+      bool JoinHorz(OutPt op1, OutPt op1b, OutPt op2, OutPt op2b, 
+        IntPoint Pt, bool DiscardLeft)
+      {
+        Direction Dir1 = (op1.Pt.X > op1b.Pt.X ? 
+          Direction.dRightToLeft : Direction.dLeftToRight);
+        Direction Dir2 = (op2.Pt.X > op2b.Pt.X ?
+          Direction.dRightToLeft : Direction.dLeftToRight);
+        if (Dir1 == Dir2) return false;
+
+        //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we
+        //want Op1b to be on the Right. (And likewise with Op2 and Op2b.)
+        //So, to facilitate this while inserting Op1b and Op2b ...
+        //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b,
+        //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.)
+        if (Dir1 == Direction.dLeftToRight) 
+        {
+          while (op1.Next.Pt.X <= Pt.X && 
+            op1.Next.Pt.X >= op1.Pt.X && op1.Next.Pt.Y == Pt.Y)  
+              op1 = op1.Next;
+          if (DiscardLeft && (op1.Pt.X != Pt.X)) op1 = op1.Next;
+          op1b = DupOutPt(op1, !DiscardLeft);
+          if (op1b.Pt != Pt) 
+          {
+            op1 = op1b;
+            op1.Pt = Pt;
+            op1b = DupOutPt(op1, !DiscardLeft);
+          }
+        } 
+        else
+        {
+          while (op1.Next.Pt.X >= Pt.X && 
+            op1.Next.Pt.X <= op1.Pt.X && op1.Next.Pt.Y == Pt.Y) 
+              op1 = op1.Next;
+          if (!DiscardLeft && (op1.Pt.X != Pt.X)) op1 = op1.Next;
+          op1b = DupOutPt(op1, DiscardLeft);
+          if (op1b.Pt != Pt)
+          {
+            op1 = op1b;
+            op1.Pt = Pt;
+            op1b = DupOutPt(op1, DiscardLeft);
+          }
+        }
+
+        if (Dir2 == Direction.dLeftToRight)
+        {
+          while (op2.Next.Pt.X <= Pt.X && 
+            op2.Next.Pt.X >= op2.Pt.X && op2.Next.Pt.Y == Pt.Y)
+              op2 = op2.Next;
+          if (DiscardLeft && (op2.Pt.X != Pt.X)) op2 = op2.Next;
+          op2b = DupOutPt(op2, !DiscardLeft);
+          if (op2b.Pt != Pt)
+          {
+            op2 = op2b;
+            op2.Pt = Pt;
+            op2b = DupOutPt(op2, !DiscardLeft);
+          };
+        } else
+        {
+          while (op2.Next.Pt.X >= Pt.X && 
+            op2.Next.Pt.X <= op2.Pt.X && op2.Next.Pt.Y == Pt.Y) 
+              op2 = op2.Next;
+          if (!DiscardLeft && (op2.Pt.X != Pt.X)) op2 = op2.Next;
+          op2b = DupOutPt(op2, DiscardLeft);
+          if (op2b.Pt != Pt)
+          {
+            op2 = op2b;
+            op2.Pt = Pt;
+            op2b = DupOutPt(op2, DiscardLeft);
+          };
+        };
+
+        if ((Dir1 == Direction.dLeftToRight) == DiscardLeft)
+        {
+          op1.Prev = op2;
+          op2.Next = op1;
+          op1b.Next = op2b;
+          op2b.Prev = op1b;
+        }
+        else
+        {
+          op1.Next = op2;
+          op2.Prev = op1;
+          op1b.Prev = op2b;
+          op2b.Next = op1b;
+        }
+        return true;
+      }
+      //------------------------------------------------------------------------------
+
+      private bool JoinPoints(Join j, OutRec outRec1, OutRec outRec2)
+      {
+        OutPt op1 = j.OutPt1, op1b;
+        OutPt op2 = j.OutPt2, op2b;
+
+        //There are 3 kinds of joins for output polygons ...
+        //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere
+        //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal).
+        //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same
+        //location at the Bottom of the overlapping segment (& Join.OffPt is above).
+        //3. StrictlySimple joins where edges touch but are not collinear and where
+        //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point.
+        bool isHorizontal = (j.OutPt1.Pt.Y == j.OffPt.Y);
+
+        if (isHorizontal && (j.OffPt == j.OutPt1.Pt) && (j.OffPt == j.OutPt2.Pt))
+        {          
+          //Strictly Simple join ...
+          if (outRec1 != outRec2) return false;
+          op1b = j.OutPt1.Next;
+          while (op1b != op1 && (op1b.Pt == j.OffPt)) 
+            op1b = op1b.Next;
+          bool reverse1 = (op1b.Pt.Y > j.OffPt.Y);
+          op2b = j.OutPt2.Next;
+          while (op2b != op2 && (op2b.Pt == j.OffPt)) 
+            op2b = op2b.Next;
+          bool reverse2 = (op2b.Pt.Y > j.OffPt.Y);
+          if (reverse1 == reverse2) return false;
+          if (reverse1)
+          {
+            op1b = DupOutPt(op1, false);
+            op2b = DupOutPt(op2, true);
+            op1.Prev = op2;
+            op2.Next = op1;
+            op1b.Next = op2b;
+            op2b.Prev = op1b;
+            j.OutPt1 = op1;
+            j.OutPt2 = op1b;
+            return true;
+          } else
+          {
+            op1b = DupOutPt(op1, true);
+            op2b = DupOutPt(op2, false);
+            op1.Next = op2;
+            op2.Prev = op1;
+            op1b.Prev = op2b;
+            op2b.Next = op1b;
+            j.OutPt1 = op1;
+            j.OutPt2 = op1b;
+            return true;
+          }
+        } 
+        else if (isHorizontal)
+        {
+          //treat horizontal joins differently to non-horizontal joins since with
+          //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt
+          //may be anywhere along the horizontal edge.
+          op1b = op1;
+          while (op1.Prev.Pt.Y == op1.Pt.Y && op1.Prev != op1b && op1.Prev != op2)
+            op1 = op1.Prev;
+          while (op1b.Next.Pt.Y == op1b.Pt.Y && op1b.Next != op1 && op1b.Next != op2)
+            op1b = op1b.Next;
+          if (op1b.Next == op1 || op1b.Next == op2) return false; //a flat 'polygon'
+
+          op2b = op2;
+          while (op2.Prev.Pt.Y == op2.Pt.Y && op2.Prev != op2b && op2.Prev != op1b)
+            op2 = op2.Prev;
+          while (op2b.Next.Pt.Y == op2b.Pt.Y && op2b.Next != op2 && op2b.Next != op1)
+            op2b = op2b.Next;
+          if (op2b.Next == op2 || op2b.Next == op1) return false; //a flat 'polygon'
+
+          cInt Left, Right;
+          //Op1 -. Op1b & Op2 -. Op2b are the extremites of the horizontal edges
+          if (!GetOverlap(op1.Pt.X, op1b.Pt.X, op2.Pt.X, op2b.Pt.X, out Left, out Right))
+            return false;
+
+          //DiscardLeftSide: when overlapping edges are joined, a spike will created
+          //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up
+          //on the discard Side as either may still be needed for other joins ...
+          IntPoint Pt;
+          bool DiscardLeftSide;
+          if (op1.Pt.X >= Left && op1.Pt.X <= Right) 
+          {
+            Pt = op1.Pt; DiscardLeftSide = (op1.Pt.X > op1b.Pt.X);
+          } 
+          else if (op2.Pt.X >= Left&& op2.Pt.X <= Right) 
+          {
+            Pt = op2.Pt; DiscardLeftSide = (op2.Pt.X > op2b.Pt.X);
+          } 
+          else if (op1b.Pt.X >= Left && op1b.Pt.X <= Right)
+          {
+            Pt = op1b.Pt; DiscardLeftSide = op1b.Pt.X > op1.Pt.X;
+          } 
+          else
+          {
+            Pt = op2b.Pt; DiscardLeftSide = (op2b.Pt.X > op2.Pt.X);
+          }
+          j.OutPt1 = op1;
+          j.OutPt2 = op2;
+          return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide);
+        } else
+        {
+          //nb: For non-horizontal joins ...
+          //    1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y
+          //    2. Jr.OutPt1.Pt > Jr.OffPt.Y
+
+          //make sure the polygons are correctly oriented ...
+          op1b = op1.Next;
+          while ((op1b.Pt == op1.Pt) && (op1b != op1)) op1b = op1b.Next;
+          bool Reverse1 = ((op1b.Pt.Y > op1.Pt.Y) ||
+            !SlopesEqual(op1.Pt, op1b.Pt, j.OffPt, m_UseFullRange));
+          if (Reverse1)
+          {
+            op1b = op1.Prev;
+            while ((op1b.Pt == op1.Pt) && (op1b != op1)) op1b = op1b.Prev;
+            if ((op1b.Pt.Y > op1.Pt.Y) ||
+              !SlopesEqual(op1.Pt, op1b.Pt, j.OffPt, m_UseFullRange)) return false;
+          };
+          op2b = op2.Next;
+          while ((op2b.Pt == op2.Pt) && (op2b != op2)) op2b = op2b.Next;
+          bool Reverse2 = ((op2b.Pt.Y > op2.Pt.Y) ||
+            !SlopesEqual(op2.Pt, op2b.Pt, j.OffPt, m_UseFullRange));
+          if (Reverse2)
+          {
+            op2b = op2.Prev;
+            while ((op2b.Pt == op2.Pt) && (op2b != op2)) op2b = op2b.Prev;
+            if ((op2b.Pt.Y > op2.Pt.Y) ||
+              !SlopesEqual(op2.Pt, op2b.Pt, j.OffPt, m_UseFullRange)) return false;
+          }
+
+          if ((op1b == op1) || (op2b == op2) || (op1b == op2b) ||
+            ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false;
+
+          if (Reverse1)
+          {
+            op1b = DupOutPt(op1, false);
+            op2b = DupOutPt(op2, true);
+            op1.Prev = op2;
+            op2.Next = op1;
+            op1b.Next = op2b;
+            op2b.Prev = op1b;
+            j.OutPt1 = op1;
+            j.OutPt2 = op1b;
+            return true;
+          } else
+          {
+            op1b = DupOutPt(op1, true);
+            op2b = DupOutPt(op2, false);
+            op1.Next = op2;
+            op2.Prev = op1;
+            op1b.Prev = op2b;
+            op2b.Next = op1b;
+            j.OutPt1 = op1;
+            j.OutPt2 = op1b;
+            return true;
+          }
+        }
+      }
+      //----------------------------------------------------------------------
+
+      public static int PointInPolygon(IntPoint pt, Path path)
+      {
+        //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
+        //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
+        //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
+        int result = 0, cnt = path.Count;
+        if (cnt < 3) return 0;
+        IntPoint ip = path[0];
+        for (int i = 1; i <= cnt; ++i)
+        {
+          IntPoint ipNext = (i == cnt ? path[0] : path[i]);
+          if (ipNext.Y == pt.Y)
+          {
+            if ((ipNext.X == pt.X) || (ip.Y == pt.Y &&
+              ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1;
+          }
+          if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y))
+          {
+            if (ip.X >= pt.X)
+            {
+              if (ipNext.X > pt.X) result = 1 - result;
+              else
+              {
+                double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) -
+                  (double)(ipNext.X - pt.X) * (ip.Y - pt.Y);
+                if (d == 0) return -1;
+                else if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result;
+              }
+            }
+            else
+            {
+              if (ipNext.X > pt.X)
+              {
+                double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) -
+                  (double)(ipNext.X - pt.X) * (ip.Y - pt.Y);
+                if (d == 0) return -1;
+                else if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result;
+              }
+            }
+          }
+          ip = ipNext;
+        }
+        return result;
+      }
+      //------------------------------------------------------------------------------
+
+      //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
+      //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
+      private static int PointInPolygon(IntPoint pt, OutPt op)
+      {
+        //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
+        int result = 0;
+        OutPt startOp = op;
+        cInt ptx = pt.X, pty = pt.Y;
+        cInt poly0x = op.Pt.X, poly0y = op.Pt.Y;
+        do
+        {
+          op = op.Next;
+          cInt poly1x = op.Pt.X, poly1y = op.Pt.Y;
+
+          if (poly1y == pty)
+          {
+            if ((poly1x == ptx) || (poly0y == pty &&
+              ((poly1x > ptx) == (poly0x < ptx)))) return -1;
+          }
+          if ((poly0y < pty) != (poly1y < pty))
+          {
+            if (poly0x >= ptx)
+            {
+              if (poly1x > ptx) result = 1 - result;
+              else
+              {
+                double d = (double)(poly0x - ptx) * (poly1y - pty) -
+                  (double)(poly1x - ptx) * (poly0y - pty);
+                if (d == 0) return -1;
+                if ((d > 0) == (poly1y > poly0y)) result = 1 - result;
+              }
+            }
+            else
+            {
+              if (poly1x > ptx)
+              {
+                double d = (double)(poly0x - ptx) * (poly1y - pty) -
+                  (double)(poly1x - ptx) * (poly0y - pty);
+                if (d == 0) return -1;
+                if ((d > 0) == (poly1y > poly0y)) result = 1 - result;
+              }
+            }
+          }
+          poly0x = poly1x; poly0y = poly1y;
+        } while (startOp != op);
+        return result;
+      }
+      //------------------------------------------------------------------------------
+
+      private static bool Poly2ContainsPoly1(OutPt outPt1, OutPt outPt2)
+      {
+        OutPt op = outPt1;
+        do
+        {
+          //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon
+          int res = PointInPolygon(op.Pt, outPt2);
+          if (res >= 0) return res > 0;
+          op = op.Next;
+        }
+        while (op != outPt1);
+        return true;
+      }
+      //----------------------------------------------------------------------
+
+      private void FixupFirstLefts1(OutRec OldOutRec, OutRec NewOutRec)
+      { 
+          for (int i = 0; i < m_PolyOuts.Count; i++)
+          {
+              OutRec outRec = m_PolyOuts[i];
+              if (outRec.Pts == null || outRec.FirstLeft == null) continue;
+              OutRec firstLeft = ParseFirstLeft(outRec.FirstLeft);
+              if (firstLeft == OldOutRec)
+              {
+                  if (Poly2ContainsPoly1(outRec.Pts, NewOutRec.Pts))
+                      outRec.FirstLeft = NewOutRec;
+              }
+          }
+      }
+      //----------------------------------------------------------------------
+
+      private void FixupFirstLefts2(OutRec OldOutRec, OutRec NewOutRec)
+      { 
+          foreach (OutRec outRec in m_PolyOuts)
+              if (outRec.FirstLeft == OldOutRec) outRec.FirstLeft = NewOutRec;
+      }
+      //----------------------------------------------------------------------
+
+      private static OutRec ParseFirstLeft(OutRec FirstLeft)
+      {
+        while (FirstLeft != null && FirstLeft.Pts == null) 
+          FirstLeft = FirstLeft.FirstLeft;
+        return FirstLeft;
+      }
+      //------------------------------------------------------------------------------
+
+    private void JoinCommonEdges()
+      {
+        for (int i = 0; i < m_Joins.Count; i++)
+        {
+          Join join = m_Joins[i];
+
+          OutRec outRec1 = GetOutRec(join.OutPt1.Idx);
+          OutRec outRec2 = GetOutRec(join.OutPt2.Idx);
+
+          if (outRec1.Pts == null || outRec2.Pts == null) continue;
+          if (outRec1.IsOpen || outRec2.IsOpen) continue;
+
+          //get the polygon fragment with the correct hole state (FirstLeft)
+          //before calling JoinPoints() ...
+          OutRec holeStateRec;
+          if (outRec1 == outRec2) holeStateRec = outRec1;
+          else if (Param1RightOfParam2(outRec1, outRec2)) holeStateRec = outRec2;
+          else if (Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1;
+          else holeStateRec = GetLowermostRec(outRec1, outRec2);
+
+          if (!JoinPoints(join, outRec1, outRec2)) continue;
+
+          if (outRec1 == outRec2)
+          {
+            //instead of joining two polygons, we've just created a new one by
+            //splitting one polygon into two.
+            outRec1.Pts = join.OutPt1;
+            outRec1.BottomPt = null;
+            outRec2 = CreateOutRec();
+            outRec2.Pts = join.OutPt2;
+
+            //update all OutRec2.Pts Idx's ...
+            UpdateOutPtIdxs(outRec2);
+
+            //We now need to check every OutRec.FirstLeft pointer. If it points
+            //to OutRec1 it may need to point to OutRec2 instead ...
+            if (m_UsingPolyTree)
+              for (int j = 0; j < m_PolyOuts.Count - 1; j++)
+              {
+                OutRec oRec = m_PolyOuts[j];
+                if (oRec.Pts == null || ParseFirstLeft(oRec.FirstLeft) != outRec1 ||
+                  oRec.IsHole == outRec1.IsHole) continue;
+                if (Poly2ContainsPoly1(oRec.Pts, join.OutPt2))
+                  oRec.FirstLeft = outRec2;
+              }
+
+            if (Poly2ContainsPoly1(outRec2.Pts, outRec1.Pts))
+            {
+              //outRec2 is contained by outRec1 ...
+              outRec2.IsHole = !outRec1.IsHole;
+              outRec2.FirstLeft = outRec1;
+
+              //fixup FirstLeft pointers that may need reassigning to OutRec1
+              if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1);
+
+              if ((outRec2.IsHole ^ ReverseSolution) == (Area(outRec2) > 0))
+                ReversePolyPtLinks(outRec2.Pts);
+
+            }
+            else if (Poly2ContainsPoly1(outRec1.Pts, outRec2.Pts))
+            {
+              //outRec1 is contained by outRec2 ...
+              outRec2.IsHole = outRec1.IsHole;
+              outRec1.IsHole = !outRec2.IsHole;
+              outRec2.FirstLeft = outRec1.FirstLeft;
+              outRec1.FirstLeft = outRec2;
+
+              //fixup FirstLeft pointers that may need reassigning to OutRec1
+              if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2);
+
+              if ((outRec1.IsHole ^ ReverseSolution) == (Area(outRec1) > 0))
+                ReversePolyPtLinks(outRec1.Pts);
+            }
+            else
+            {
+              //the 2 polygons are completely separate ...
+              outRec2.IsHole = outRec1.IsHole;
+              outRec2.FirstLeft = outRec1.FirstLeft;
+
+              //fixup FirstLeft pointers that may need reassigning to OutRec2
+              if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2);
+            }
+     
+          } else
+          {
+            //joined 2 polygons together ...
+
+            outRec2.Pts = null;
+            outRec2.BottomPt = null;
+            outRec2.Idx = outRec1.Idx;
+
+            outRec1.IsHole = holeStateRec.IsHole;
+            if (holeStateRec == outRec2) 
+              outRec1.FirstLeft = outRec2.FirstLeft;
+            outRec2.FirstLeft = outRec1;
+
+            //fixup FirstLeft pointers that may need reassigning to OutRec1
+            if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1);
+          }
+        }
+      }
+      //------------------------------------------------------------------------------
+
+      private void UpdateOutPtIdxs(OutRec outrec)
+      {  
+        OutPt op = outrec.Pts;
+        do
+        {
+          op.Idx = outrec.Idx;
+          op = op.Prev;
+        }
+        while(op != outrec.Pts);
+      }
+      //------------------------------------------------------------------------------
+
+      private void DoSimplePolygons()
+      {
+        int i = 0;
+        while (i < m_PolyOuts.Count) 
+        {
+          OutRec outrec = m_PolyOuts[i++];
+          OutPt op = outrec.Pts;
+          if (op == null || outrec.IsOpen) continue;
+          do //for each Pt in Polygon until duplicate found do ...
+          {
+            OutPt op2 = op.Next;
+            while (op2 != outrec.Pts) 
+            {
+              if ((op.Pt == op2.Pt) && op2.Next != op && op2.Prev != op) 
+              {
+                //split the polygon into two ...
+                OutPt op3 = op.Prev;
+                OutPt op4 = op2.Prev;
+                op.Prev = op4;
+                op4.Next = op;
+                op2.Prev = op3;
+                op3.Next = op2;
+
+                outrec.Pts = op;
+                OutRec outrec2 = CreateOutRec();
+                outrec2.Pts = op2;
+                UpdateOutPtIdxs(outrec2);
+                if (Poly2ContainsPoly1(outrec2.Pts, outrec.Pts))
+                {
+                  //OutRec2 is contained by OutRec1 ...
+                  outrec2.IsHole = !outrec.IsHole;
+                  outrec2.FirstLeft = outrec;
+                  if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec);
+                }
+                else
+                  if (Poly2ContainsPoly1(outrec.Pts, outrec2.Pts))
+                {
+                  //OutRec1 is contained by OutRec2 ...
+                  outrec2.IsHole = outrec.IsHole;
+                  outrec.IsHole = !outrec2.IsHole;
+                  outrec2.FirstLeft = outrec.FirstLeft;
+                  outrec.FirstLeft = outrec2;
+                  if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2);
+                }
+                  else
+                {
+                  //the 2 polygons are separate ...
+                  outrec2.IsHole = outrec.IsHole;
+                  outrec2.FirstLeft = outrec.FirstLeft;
+                  if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2);
+                }
+                op2 = op; //ie get ready for the next iteration
+              }
+              op2 = op2.Next;
+            }
+            op = op.Next;
+          }
+          while (op != outrec.Pts);
+        }
+      }
+      //------------------------------------------------------------------------------
+
+      public static double Area(Path poly)
+      {
+        int cnt = (int)poly.Count;
+        if (cnt < 3) return 0;
+        double a = 0;
+        for (int i = 0, j = cnt - 1; i < cnt; ++i)
+        {
+          a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y);
+          j = i;
+        }
+        return -a * 0.5;
+      }
+      //------------------------------------------------------------------------------
+
+      double Area(OutRec outRec)
+      {
+        OutPt op = outRec.Pts;
+        if (op == null) return 0;
+        double a = 0;
+        do {
+          a = a + (double)(op.Prev.Pt.X + op.Pt.X) * (double)(op.Prev.Pt.Y - op.Pt.Y);
+          op = op.Next;
+        } while (op != outRec.Pts);
+        return a * 0.5;
+      }
+
+      //------------------------------------------------------------------------------
+      // SimplifyPolygon functions ...
+      // Convert self-intersecting polygons into simple polygons
+      //------------------------------------------------------------------------------
+
+      public static Paths SimplifyPolygon(Path poly, 
+            PolyFillType fillType = PolyFillType.pftEvenOdd)
+      {
+          Paths result = new Paths();
+          Clipper c = new Clipper();
+          c.StrictlySimple = true;
+          c.AddPath(poly, PolyType.ptSubject, true);
+          c.Execute(ClipType.ctUnion, result, fillType, fillType);
+          return result;
+      }
+      //------------------------------------------------------------------------------
+
+      public static Paths SimplifyPolygons(Paths polys,
+          PolyFillType fillType = PolyFillType.pftEvenOdd)
+      {
+          Paths result = new Paths();
+          Clipper c = new Clipper();
+          c.StrictlySimple = true;
+          c.AddPaths(polys, PolyType.ptSubject, true);
+          c.Execute(ClipType.ctUnion, result, fillType, fillType);
+          return result;
+      }
+      //------------------------------------------------------------------------------
+
+      private static double DistanceSqrd(IntPoint pt1, IntPoint pt2)
+      {
+        double dx = ((double)pt1.X - pt2.X);
+        double dy = ((double)pt1.Y - pt2.Y);
+        return (dx*dx + dy*dy);
+      }
+      //------------------------------------------------------------------------------
+
+      private static double DistanceFromLineSqrd(IntPoint pt, IntPoint ln1, IntPoint ln2)
+      {
+        //The equation of a line in general form (Ax + By + C = 0)
+        //given 2 points (x¹,y¹) & (x²,y²) is ...
+        //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0
+        //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹
+        //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
+        //see http://en.wikipedia.org/wiki/Perpendicular_distance
+        double A = ln1.Y - ln2.Y;
+        double B = ln2.X - ln1.X;
+        double C = A * ln1.X  + B * ln1.Y;
+        C = A * pt.X + B * pt.Y - C;
+        return (C * C) / (A * A + B * B);
+      }
+      //---------------------------------------------------------------------------
+
+      private static bool SlopesNearCollinear(IntPoint pt1, 
+          IntPoint pt2, IntPoint pt3, double distSqrd)
+      {
+        //this function is more accurate when the point that's GEOMETRICALLY 
+        //between the other 2 points is the one that's tested for distance.  
+        //nb: with 'spikes', either pt1 or pt3 is geometrically between the other pts                    
+        if (Math.Abs(pt1.X - pt2.X) > Math.Abs(pt1.Y - pt2.Y))
+	      {
+          if ((pt1.X > pt2.X) == (pt1.X < pt3.X))
+            return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd;
+          else if ((pt2.X > pt1.X) == (pt2.X < pt3.X))
+            return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;
+		      else
+	          return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd;
+	      }
+	      else
+	      {
+          if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y))
+            return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd;
+          else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y))
+            return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;
+		      else
+            return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd;
+	      }
+      }
+      //------------------------------------------------------------------------------
+
+      private static bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd)
+      {
+          double dx = (double)pt1.X - pt2.X;
+          double dy = (double)pt1.Y - pt2.Y;
+          return ((dx * dx) + (dy * dy) <= distSqrd);
+      }
+      //------------------------------------------------------------------------------
+
+      private static OutPt ExcludeOp(OutPt op)
+      {
+        OutPt result = op.Prev;
+        result.Next = op.Next;
+        op.Next.Prev = result;
+        result.Idx = 0;
+        return result;
+      }
+      //------------------------------------------------------------------------------
+
+      public static Path CleanPolygon(Path path, double distance = 1.415)
+      {
+        //distance = proximity in units/pixels below which vertices will be stripped. 
+        //Default ~= sqrt(2) so when adjacent vertices or semi-adjacent vertices have 
+        //both x & y coords within 1 unit, then the second vertex will be stripped.
+
+        int cnt = path.Count;
+
+        if (cnt == 0) return new Path();
+
+        OutPt [] outPts = new OutPt[cnt];
+        for (int i = 0; i < cnt; ++i) outPts[i] = new OutPt();
+
+        for (int i = 0; i < cnt; ++i)
+        {
+          outPts[i].Pt = path[i];
+          outPts[i].Next = outPts[(i + 1) % cnt];
+          outPts[i].Next.Prev = outPts[i];
+          outPts[i].Idx = 0;
+        }
+
+        double distSqrd = distance * distance;
+        OutPt op = outPts[0];
+        while (op.Idx == 0 && op.Next != op.Prev)
+        {
+          if (PointsAreClose(op.Pt, op.Prev.Pt, distSqrd))
+          {
+            op = ExcludeOp(op);
+            cnt--;
+          }
+          else if (PointsAreClose(op.Prev.Pt, op.Next.Pt, distSqrd))
+          {
+            ExcludeOp(op.Next);
+            op = ExcludeOp(op);
+            cnt -= 2;
+          }
+          else if (SlopesNearCollinear(op.Prev.Pt, op.Pt, op.Next.Pt, distSqrd))
+          {
+            op = ExcludeOp(op);
+            cnt--;
+          }
+          else
+          {
+            op.Idx = 1;
+            op = op.Next;
+          }
+        }
+
+        if (cnt < 3) cnt = 0;
+        Path result = new Path(cnt);
+        for (int i = 0; i < cnt; ++i)
+        {
+          result.Add(op.Pt);
+          op = op.Next;
+        }
+        outPts = null;
+        return result;
+      }
+      //------------------------------------------------------------------------------
+
+      public static Paths CleanPolygons(Paths polys,
+          double distance = 1.415)
+      {
+        Paths result = new Paths(polys.Count);
+        for (int i = 0; i < polys.Count; i++)
+          result.Add(CleanPolygon(polys[i], distance));
+        return result;
+      }
+      //------------------------------------------------------------------------------
+
+      internal static Paths Minkowski(Path pattern, Path path, bool IsSum, bool IsClosed)
+      {
+        int delta = (IsClosed ? 1 : 0);
+        int polyCnt = pattern.Count;
+        int pathCnt = path.Count;
+        Paths result = new Paths(pathCnt);
+        if (IsSum)
+          for (int i = 0; i < pathCnt; i++)
+          {
+            Path p = new Path(polyCnt);
+            foreach (IntPoint ip in pattern)
+              p.Add(new IntPoint(path[i].X + ip.X, path[i].Y + ip.Y));
+            result.Add(p);
+          }
+        else
+          for (int i = 0; i < pathCnt; i++)
+          {
+            Path p = new Path(polyCnt);
+            foreach (IntPoint ip in pattern)
+              p.Add(new IntPoint(path[i].X - ip.X, path[i].Y - ip.Y));
+            result.Add(p);
+          }
+
+        Paths quads = new Paths((pathCnt + delta) * (polyCnt + 1));
+        for (int i = 0; i < pathCnt - 1 + delta; i++)
+          for (int j = 0; j < polyCnt; j++)
+          {
+            Path quad = new Path(4);
+            quad.Add(result[i % pathCnt][j % polyCnt]);
+            quad.Add(result[(i + 1) % pathCnt][j % polyCnt]);
+            quad.Add(result[(i + 1) % pathCnt][(j + 1) % polyCnt]);
+            quad.Add(result[i % pathCnt][(j + 1) % polyCnt]);
+            if (!Orientation(quad)) quad.Reverse();
+            quads.Add(quad);
+          }
+        return quads;
+      }
+      //------------------------------------------------------------------------------
+
+      public static Paths MinkowskiSum(Path pattern, Path path, bool pathIsClosed)
+      {
+        Paths paths = Minkowski(pattern, path, true, pathIsClosed);
+        Clipper c = new Clipper();
+        c.AddPaths(paths, PolyType.ptSubject, true);
+        c.Execute(ClipType.ctUnion, paths, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
+        return paths;
+      }
+      //------------------------------------------------------------------------------
+
+      private static Path TranslatePath(Path path, IntPoint delta) 
+      {
+        Path outPath = new Path(path.Count);
+        for (int i = 0; i < path.Count; i++)
+          outPath.Add(new IntPoint(path[i].X + delta.X, path[i].Y + delta.Y));
+        return outPath;
+      }
+      //------------------------------------------------------------------------------
+
+      public static Paths MinkowskiSum(Path pattern, Paths paths, bool pathIsClosed)
+      {
+        Paths solution = new Paths();
+        Clipper c = new Clipper();
+        for (int i = 0; i < paths.Count; ++i)
+        {
+          Paths tmp = Minkowski(pattern, paths[i], true, pathIsClosed);
+          c.AddPaths(tmp, PolyType.ptSubject, true);
+          if (pathIsClosed)
+          {
+            Path path = TranslatePath(paths[i], pattern[0]);
+            c.AddPath(path, PolyType.ptClip, true);
+          }
+        }
+        c.Execute(ClipType.ctUnion, solution, 
+          PolyFillType.pftNonZero, PolyFillType.pftNonZero);
+        return solution;
+      }
+      //------------------------------------------------------------------------------
+
+      public static Paths MinkowskiDiff(Path poly1, Path poly2)
+      {
+        Paths paths = Minkowski(poly1, poly2, false, true);
+        Clipper c = new Clipper();
+        c.AddPaths(paths, PolyType.ptSubject, true);
+        c.Execute(ClipType.ctUnion, paths, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
+        return paths;
+      }
+      //------------------------------------------------------------------------------
+
+      internal enum NodeType { ntAny, ntOpen, ntClosed };
+
+      public static Paths PolyTreeToPaths(PolyTree polytree)
+      {
+
+        Paths result = new Paths();
+        result.Capacity = polytree.Total;
+        AddPolyNodeToPaths(polytree, NodeType.ntAny, result);
+        return result;
+      }
+      //------------------------------------------------------------------------------
+
+      internal static void AddPolyNodeToPaths(PolyNode polynode, NodeType nt, Paths paths)
+      {
+        bool match = true;
+        switch (nt)
+        {
+          case NodeType.ntOpen: return;
+          case NodeType.ntClosed: match = !polynode.IsOpen; break;
+          default: break;
+        }
+
+        if (polynode.m_polygon.Count > 0 && match)
+          paths.Add(polynode.m_polygon);
+        foreach (PolyNode pn in polynode.Childs)
+          AddPolyNodeToPaths(pn, nt, paths);
+      }
+      //------------------------------------------------------------------------------
+
+      public static Paths OpenPathsFromPolyTree(PolyTree polytree)
+      {
+        Paths result = new Paths();
+        result.Capacity = polytree.ChildCount;
+        for (int i = 0; i < polytree.ChildCount; i++)
+          if (polytree.Childs[i].IsOpen)
+            result.Add(polytree.Childs[i].m_polygon);
+        return result;
+      }
+      //------------------------------------------------------------------------------
+
+      public static Paths ClosedPathsFromPolyTree(PolyTree polytree)
+      {
+        Paths result = new Paths();
+        result.Capacity = polytree.Total;
+        AddPolyNodeToPaths(polytree, NodeType.ntClosed, result);
+        return result;
+      }
+      //------------------------------------------------------------------------------
+
+  } //end Clipper
+
+  public class ClipperOffset
+  {
+    private Paths m_destPolys;
+    private Path m_srcPoly;
+    private Path m_destPoly;
+    private List<DoublePoint> m_normals = new List<DoublePoint>();
+    private double m_delta, m_sinA, m_sin, m_cos;
+    private double m_miterLim, m_StepsPerRad;
+
+    private IntPoint m_lowest;
+    private PolyNode m_polyNodes = new PolyNode();
+
+    public double ArcTolerance { get; set; }
+    public double MiterLimit { get; set; }
+
+    private const double two_pi = Math.PI * 2;
+    private const double def_arc_tolerance = 0.25;
+
+    public ClipperOffset(
+      double miterLimit = 2.0, double arcTolerance = def_arc_tolerance)
+    {
+      MiterLimit = miterLimit;
+      ArcTolerance = arcTolerance;
+      m_lowest.X = -1;
+    }
+    //------------------------------------------------------------------------------
+
+    public void Clear()
+    {
+      m_polyNodes.Childs.Clear();
+      m_lowest.X = -1;
+    }
+    //------------------------------------------------------------------------------
+
+    internal static cInt Round(double value)
+    {
+      return value < 0 ? (cInt)(value - 0.5) : (cInt)(value + 0.5);
+    }
+    //------------------------------------------------------------------------------
+
+    public void AddPath(Path path, JoinType joinType, EndType endType)
+    {
+      int highI = path.Count - 1;
+      if (highI < 0) return;
+      PolyNode newNode = new PolyNode();
+      newNode.m_jointype = joinType;
+      newNode.m_endtype = endType;
+
+      //strip duplicate points from path and also get index to the lowest point ...
+      if (endType == EndType.etClosedLine || endType == EndType.etClosedPolygon)
+        while (highI > 0 && path[0] == path[highI]) highI--;
+      newNode.m_polygon.Capacity = highI + 1;
+      newNode.m_polygon.Add(path[0]);
+      int j = 0, k = 0;
+      for (int i = 1; i <= highI; i++)
+        if (newNode.m_polygon[j] != path[i])
+        {
+          j++;
+          newNode.m_polygon.Add(path[i]);
+          if (path[i].Y > newNode.m_polygon[k].Y ||
+            (path[i].Y == newNode.m_polygon[k].Y &&
+            path[i].X < newNode.m_polygon[k].X)) k = j;
+        }
+      if (endType == EndType.etClosedPolygon && j < 2) return;
+
+      m_polyNodes.AddChild(newNode);
+
+      //if this path's lowest pt is lower than all the others then update m_lowest
+      if (endType != EndType.etClosedPolygon) return;
+      if (m_lowest.X < 0)
+        m_lowest = new IntPoint(m_polyNodes.ChildCount - 1, k);
+      else
+      {
+        IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X].m_polygon[(int)m_lowest.Y];
+        if (newNode.m_polygon[k].Y > ip.Y ||
+          (newNode.m_polygon[k].Y == ip.Y &&
+          newNode.m_polygon[k].X < ip.X))
+          m_lowest = new IntPoint(m_polyNodes.ChildCount - 1, k);
+      }
+    }
+    //------------------------------------------------------------------------------
+
+    public void AddPaths(Paths paths, JoinType joinType, EndType endType)
+    {
+      foreach (Path p in paths)
+        AddPath(p, joinType, endType);
+    }
+    //------------------------------------------------------------------------------
+
+    private void FixOrientations()
+    {
+      //fixup orientations of all closed paths if the orientation of the
+      //closed path with the lowermost vertex is wrong ...
+      if (m_lowest.X >= 0 && 
+        !Clipper.Orientation(m_polyNodes.Childs[(int)m_lowest.X].m_polygon))
+      {
+        for (int i = 0; i < m_polyNodes.ChildCount; i++)
+        {
+          PolyNode node = m_polyNodes.Childs[i];
+          if (node.m_endtype == EndType.etClosedPolygon ||
+            (node.m_endtype == EndType.etClosedLine && 
+            Clipper.Orientation(node.m_polygon)))
+            node.m_polygon.Reverse();
+        }
+      }
+      else
+      {
+        for (int i = 0; i < m_polyNodes.ChildCount; i++)
+        {
+          PolyNode node = m_polyNodes.Childs[i];
+          if (node.m_endtype == EndType.etClosedLine &&
+            !Clipper.Orientation(node.m_polygon))
+          node.m_polygon.Reverse();
+        }
+      }
+    }
+    //------------------------------------------------------------------------------
+
+    internal static DoublePoint GetUnitNormal(IntPoint pt1, IntPoint pt2)
+    {
+      double dx = (pt2.X - pt1.X);
+      double dy = (pt2.Y - pt1.Y);
+      if ((dx == 0) && (dy == 0)) return new DoublePoint();
+
+      double f = 1 * 1.0 / Math.Sqrt(dx * dx + dy * dy);
+      dx *= f;
+      dy *= f;
+
+      return new DoublePoint(dy, -dx);
+    }
+    //------------------------------------------------------------------------------
+
+    private void DoOffset(double delta)
+    {
+      m_destPolys = new Paths();
+      m_delta = delta;
+
+      //if Zero offset, just copy any CLOSED polygons to m_p and return ...
+      if (ClipperBase.near_zero(delta)) 
+      {
+        m_destPolys.Capacity = m_polyNodes.ChildCount;
+        for (int i = 0; i < m_polyNodes.ChildCount; i++)
+        {
+          PolyNode node = m_polyNodes.Childs[i];
+          if (node.m_endtype == EndType.etClosedPolygon)
+            m_destPolys.Add(node.m_polygon);
+        }
+        return;
+      }
+
+      //see offset_triginometry3.svg in the documentation folder ...
+      if (MiterLimit > 2) m_miterLim = 2 / (MiterLimit * MiterLimit);
+      else m_miterLim = 0.5;
+
+      double y;
+      if (ArcTolerance <= 0.0) 
+        y = def_arc_tolerance;
+      else if (ArcTolerance > Math.Abs(delta) * def_arc_tolerance)
+        y = Math.Abs(delta) * def_arc_tolerance;
+      else 
+        y = ArcTolerance;
+      //see offset_triginometry2.svg in the documentation folder ...
+      double steps = Math.PI / Math.Acos(1 - y / Math.Abs(delta));
+      m_sin = Math.Sin(two_pi / steps);
+      m_cos = Math.Cos(two_pi / steps);
+      m_StepsPerRad = steps / two_pi;
+      if (delta < 0.0) m_sin = -m_sin;
+
+      m_destPolys.Capacity = m_polyNodes.ChildCount * 2;
+      for (int i = 0; i < m_polyNodes.ChildCount; i++)
+      {
+        PolyNode node = m_polyNodes.Childs[i];
+        m_srcPoly = node.m_polygon;
+
+        int len = m_srcPoly.Count;
+
+        if (len == 0 || (delta <= 0 && (len < 3 || 
+          node.m_endtype != EndType.etClosedPolygon)))
+            continue;
+
+        m_destPoly = new Path();
+
+        if (len == 1)
+        {
+          if (node.m_jointype == JoinType.jtRound)
+          {
+            double X = 1.0, Y = 0.0;
+            for (int j = 1; j <= steps; j++)
+            {
+              m_destPoly.Add(new IntPoint(
+                Round(m_srcPoly[0].X + X * delta),
+                Round(m_srcPoly[0].Y + Y * delta)));
+              double X2 = X;
+              X = X * m_cos - m_sin * Y;
+              Y = X2 * m_sin + Y * m_cos;
+            }
+          }
+          else
+          {
+            double X = -1.0, Y = -1.0;
+            for (int j = 0; j < 4; ++j)
+            {
+              m_destPoly.Add(new IntPoint(
+                Round(m_srcPoly[0].X + X * delta),
+                Round(m_srcPoly[0].Y + Y * delta)));
+              if (X < 0) X = 1;
+              else if (Y < 0) Y = 1;
+              else X = -1;
+            }
+          }
+          m_destPolys.Add(m_destPoly);
+          continue;
+        }
+
+        //build m_normals ...
+        m_normals.Clear();
+        m_normals.Capacity = len;
+        for (int j = 0; j < len - 1; j++)
+          m_normals.Add(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1]));
+        if (node.m_endtype == EndType.etClosedLine || 
+          node.m_endtype == EndType.etClosedPolygon)
+          m_normals.Add(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0]));
+        else
+          m_normals.Add(new DoublePoint(m_normals[len - 2]));
+
+        if (node.m_endtype == EndType.etClosedPolygon)
+        {
+          int k = len - 1;
+          for (int j = 0; j < len; j++)
+            OffsetPoint(j, ref k, node.m_jointype);
+          m_destPolys.Add(m_destPoly);
+        }
+        else if (node.m_endtype == EndType.etClosedLine)
+        {
+          int k = len - 1;
+          for (int j = 0; j < len; j++)
+            OffsetPoint(j, ref k, node.m_jointype);
+          m_destPolys.Add(m_destPoly);
+          m_destPoly = new Path();
+          //re-build m_normals ...
+          DoublePoint n = m_normals[len - 1];
+          for (int j = len - 1; j > 0; j--)
+            m_normals[j] = new DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y);
+          m_normals[0] = new DoublePoint(-n.X, -n.Y);
+          k = 0;
+          for (int j = len - 1; j >= 0; j--)
+            OffsetPoint(j, ref k, node.m_jointype);
+          m_destPolys.Add(m_destPoly);
+        }
+        else
+        {
+          int k = 0;
+          for (int j = 1; j < len - 1; ++j)
+            OffsetPoint(j, ref k, node.m_jointype);
+
+          IntPoint pt1;
+          if (node.m_endtype == EndType.etOpenButt)
+          {
+            int j = len - 1;
+            pt1 = new IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X *
+              delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta));
+            m_destPoly.Add(pt1);
+            pt1 = new IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X *
+              delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta));
+            m_destPoly.Add(pt1);
+          }
+          else
+          {
+            int j = len - 1;
+            k = len - 2;
+            m_sinA = 0;
+            m_normals[j] = new DoublePoint(-m_normals[j].X, -m_normals[j].Y);
+            if (node.m_endtype == EndType.etOpenSquare)
+              DoSquare(j, k);
+            else
+              DoRound(j, k);
+          }
+
+          //re-build m_normals ...
+          for (int j = len - 1; j > 0; j--)
+            m_normals[j] = new DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y);
+
+          m_normals[0] = new DoublePoint(-m_normals[1].X, -m_normals[1].Y);
+
+          k = len - 1;
+          for (int j = k - 1; j > 0; --j)
+            OffsetPoint(j, ref k, node.m_jointype);
+
+          if (node.m_endtype == EndType.etOpenButt)
+          {
+            pt1 = new IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta),
+              (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta));
+            m_destPoly.Add(pt1);
+            pt1 = new IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta),
+              (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta));
+            m_destPoly.Add(pt1);
+          }
+          else
+          {
+            k = 1;
+            m_sinA = 0;
+            if (node.m_endtype == EndType.etOpenSquare)
+              DoSquare(0, 1);
+            else
+              DoRound(0, 1);
+          }
+          m_destPolys.Add(m_destPoly);
+        }
+      }
+    }
+    //------------------------------------------------------------------------------
+
+    public void Execute(ref Paths solution, double delta)
+    {
+      solution.Clear();
+      FixOrientations();
+      DoOffset(delta);
+      //now clean up 'corners' ...
+      Clipper clpr = new Clipper();
+      clpr.AddPaths(m_destPolys, PolyType.ptSubject, true);
+      if (delta > 0)
+      {
+        clpr.Execute(ClipType.ctUnion, solution,
+          PolyFillType.pftPositive, PolyFillType.pftPositive);
+      }
+      else
+      {
+        IntRect r = Clipper.GetBounds(m_destPolys);
+        Path outer = new Path(4);
+
+        outer.Add(new IntPoint(r.left - 10, r.bottom + 10));
+        outer.Add(new IntPoint(r.right + 10, r.bottom + 10));
+        outer.Add(new IntPoint(r.right + 10, r.top - 10));
+        outer.Add(new IntPoint(r.left - 10, r.top - 10));
+
+        clpr.AddPath(outer, PolyType.ptSubject, true);
+        clpr.ReverseSolution = true;
+        clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative);
+        if (solution.Count > 0) solution.RemoveAt(0);
+      }
+    }
+    //------------------------------------------------------------------------------
+
+    public void Execute(ref PolyTree solution, double delta)
+    {
+      solution.Clear();
+      FixOrientations();
+      DoOffset(delta);
+
+      //now clean up 'corners' ...
+      Clipper clpr = new Clipper();
+      clpr.AddPaths(m_destPolys, PolyType.ptSubject, true);
+      if (delta > 0)
+      {
+        clpr.Execute(ClipType.ctUnion, solution,
+          PolyFillType.pftPositive, PolyFillType.pftPositive);
+      }
+      else
+      {
+        IntRect r = Clipper.GetBounds(m_destPolys);
+        Path outer = new Path(4);
+
+        outer.Add(new IntPoint(r.left - 10, r.bottom + 10));
+        outer.Add(new IntPoint(r.right + 10, r.bottom + 10));
+        outer.Add(new IntPoint(r.right + 10, r.top - 10));
+        outer.Add(new IntPoint(r.left - 10, r.top - 10));
+
+        clpr.AddPath(outer, PolyType.ptSubject, true);
+        clpr.ReverseSolution = true;
+        clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative);
+        //remove the outer PolyNode rectangle ...
+        if (solution.ChildCount == 1 && solution.Childs[0].ChildCount > 0)
+        {
+          PolyNode outerNode = solution.Childs[0];
+          solution.Childs.Capacity = outerNode.ChildCount;
+          solution.Childs[0] = outerNode.Childs[0];
+          solution.Childs[0].m_Parent = solution;
+          for (int i = 1; i < outerNode.ChildCount; i++)
+            solution.AddChild(outerNode.Childs[i]);
+        }
+        else
+          solution.Clear();
+      }
+    }
+    //------------------------------------------------------------------------------
+
+    void OffsetPoint(int j, ref int k, JoinType jointype)
+    {
+      //cross product ...
+      m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y);
+
+      if (Math.Abs(m_sinA * m_delta) < 1.0) 
+      {
+        //dot product ...
+        double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y); 
+        if (cosA > 0) // angle ==> 0 degrees
+        {
+          m_destPoly.Add(new IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta),
+            Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta)));
+          return; 
+        }
+        //else angle ==> 180 degrees   
+      }
+      else if (m_sinA > 1.0) m_sinA = 1.0;
+      else if (m_sinA < -1.0) m_sinA = -1.0;
+      
+      if (m_sinA * m_delta < 0)
+      {
+        m_destPoly.Add(new IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta),
+          Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta)));
+        m_destPoly.Add(m_srcPoly[j]);
+        m_destPoly.Add(new IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta),
+          Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta)));
+      }
+      else
+        switch (jointype)
+        {
+          case JoinType.jtMiter:
+            {
+              double r = 1 + (m_normals[j].X * m_normals[k].X +
+                m_normals[j].Y * m_normals[k].Y);
+              if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k);
+              break;
+            }
+          case JoinType.jtSquare: DoSquare(j, k); break;
+          case JoinType.jtRound: DoRound(j, k); break;
+        }
+      k = j;
+    }
+    //------------------------------------------------------------------------------
+
+    internal void DoSquare(int j, int k)
+    {
+      double dx = Math.Tan(Math.Atan2(m_sinA,
+          m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4);
+      m_destPoly.Add(new IntPoint(
+          Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)),
+          Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx))));
+      m_destPoly.Add(new IntPoint(
+          Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)),
+          Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx))));
+    }
+    //------------------------------------------------------------------------------
+
+    internal void DoMiter(int j, int k, double r)
+    {
+      double q = m_delta / r;
+      m_destPoly.Add(new IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q),
+          Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q)));
+    }
+    //------------------------------------------------------------------------------
+
+    internal void DoRound(int j, int k)
+    {
+      double a = Math.Atan2(m_sinA,
+      m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y);
+      int steps = Math.Max((int)Round(m_StepsPerRad * Math.Abs(a)),1);
+
+      double X = m_normals[k].X, Y = m_normals[k].Y, X2;
+      for (int i = 0; i < steps; ++i)
+      {
+        m_destPoly.Add(new IntPoint(
+            Round(m_srcPoly[j].X + X * m_delta),
+            Round(m_srcPoly[j].Y + Y * m_delta)));
+        X2 = X;
+        X = X * m_cos - m_sin * Y;
+        Y = X2 * m_sin + Y * m_cos;
+      }
+      m_destPoly.Add(new IntPoint(
+      Round(m_srcPoly[j].X + m_normals[j].X * m_delta),
+      Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta)));
+    }
+    //------------------------------------------------------------------------------
+  }
+
+  class ClipperException : Exception
+  {
+      public ClipperException(string description) : base(description){}
+  }
+  //------------------------------------------------------------------------------
+
+} //end ClipperLib namespace
diff --git a/C#/clipper_library/clipper_library.csproj b/C#/clipper_library/clipper_library.csproj
new file mode 100644
index 0000000..7810468
--- /dev/null
+++ b/C#/clipper_library/clipper_library.csproj
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{9B062971-A88E-4A3D-B3C9-12B78D15FA66}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>ClipperLib</RootNamespace>
+    <AssemblyName>clipper_library</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="clipper.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
diff --git a/Delphi/cairo demo/Cairo Resources.txt b/Delphi/cairo demo/Cairo Resources.txt
new file mode 100644
index 0000000..95e2e83
--- /dev/null
+++ b/Delphi/cairo demo/Cairo Resources.txt	
@@ -0,0 +1,12 @@
+http://cairographics.org/
+
+The Windows dynamic linked libraries necessary to run Cairo can be downloaded from
+http://www.gtk.org/download/win32.php
+All the dlls listed under the heading "Required third party dependencies" are 
+required except gettext-runtime.dll.
+
+A Delphi library that interfaces with the Cairo dlls is 
+"Cairo Graphics Delphi Bindings" available at
+http://www.rodi.dk/programming_cairo.php
+Direct link to the download - 
+http://www.rodi.dk/download1.php?cairo-delphi-bindings-1.0.zip
diff --git a/Delphi/cairo demo/CairoClipperDemo1.dpr b/Delphi/cairo demo/CairoClipperDemo1.dpr
new file mode 100644
index 0000000..78fa035
--- /dev/null
+++ b/Delphi/cairo demo/CairoClipperDemo1.dpr	
@@ -0,0 +1,267 @@
+{$A8,B-,C+,D+,E-,F-,G+,H+,I+,J-,K-,L+,M-,N+,O+,P+,Q+,R+,S-,T-,U-,V+,W-,X+,Y+,Z1}
+{$MINSTACKSIZE $00004000}
+{$MAXSTACKSIZE $00100000}
+{$IMAGEBASE $00400000}
+{$APPTYPE GUI}
+{$WARN SYMBOL_DEPRECATED ON}
+{$WARN SYMBOL_LIBRARY ON}
+{$WARN SYMBOL_PLATFORM ON}
+{$WARN UNIT_LIBRARY ON}
+{$WARN UNIT_PLATFORM ON}
+{$WARN UNIT_DEPRECATED ON}
+{$WARN HRESULT_COMPAT ON}
+{$WARN HIDING_MEMBER ON}
+{$WARN HIDDEN_VIRTUAL ON}
+{$WARN GARBAGE ON}
+{$WARN BOUNDS_ERROR ON}
+{$WARN ZERO_NIL_COMPAT ON}
+{$WARN STRING_CONST_TRUNCED ON}
+{$WARN FOR_LOOP_VAR_VARPAR ON}
+{$WARN TYPED_CONST_VARPAR ON}
+{$WARN ASG_TO_TYPED_CONST ON}
+{$WARN CASE_LABEL_RANGE ON}
+{$WARN FOR_VARIABLE ON}
+{$WARN CONSTRUCTING_ABSTRACT ON}
+{$WARN COMPARISON_FALSE ON}
+{$WARN COMPARISON_TRUE ON}
+{$WARN COMPARING_SIGNED_UNSIGNED ON}
+{$WARN COMBINING_SIGNED_UNSIGNED ON}
+{$WARN UNSUPPORTED_CONSTRUCT ON}
+{$WARN FILE_OPEN ON}
+{$WARN FILE_OPEN_UNITSRC ON}
+{$WARN BAD_GLOBAL_SYMBOL ON}
+{$WARN DUPLICATE_CTOR_DTOR ON}
+{$WARN INVALID_DIRECTIVE ON}
+{$WARN PACKAGE_NO_LINK ON}
+{$WARN PACKAGED_THREADVAR ON}
+{$WARN IMPLICIT_IMPORT ON}
+{$WARN HPPEMIT_IGNORED ON}
+{$WARN NO_RETVAL ON}
+{$WARN USE_BEFORE_DEF ON}
+{$WARN FOR_LOOP_VAR_UNDEF ON}
+{$WARN UNIT_NAME_MISMATCH ON}
+{$WARN NO_CFG_FILE_FOUND ON}
+{$WARN MESSAGE_DIRECTIVE ON}
+{$WARN IMPLICIT_VARIANTS ON}
+{$WARN UNICODE_TO_LOCALE ON}
+{$WARN LOCALE_TO_UNICODE ON}
+{$WARN IMAGEBASE_MULTIPLE ON}
+{$WARN SUSPICIOUS_TYPECAST ON}
+{$WARN PRIVATE_PROPACCESSOR ON}
+{$WARN UNSAFE_TYPE OFF}
+{$WARN UNSAFE_CODE OFF}
+{$WARN UNSAFE_CAST OFF}
+program CairoClipperDemo1;
+
+uses
+  Windows,
+  sysutils,
+  Messages,
+  Graphics,
+  Math,
+  Cairo in 'cairo.pas',
+  CairoWin32 in 'cairowin32.pas',
+  cairo_clipper in 'cairo_clipper.pas',
+  clipper in '..\..\clipper.pas';
+
+{$R *.res}
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+var
+  offsetVal: integer = 0;
+  bmp: graphics.TBitmap; //just to buffer drawing and minimize flicker
+
+procedure PaintBitmap;
+var
+  surface: Pcairo_surface_t;
+  cr: Pcairo_t;
+  extent: cairo_text_extents_t;
+  clipper: TClipper;
+  ppa: TPaths;
+  rec: TRect;
+  text: string;
+const
+  scaling = 2; //because Clipper now only accepts integer coordinates
+begin
+  //create a cairo context for the bitmap surface ...
+  surface := cairo_win32_surface_create(bmp.canvas.handle);
+  cr := cairo_create(surface);
+  cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
+
+  clipper := TClipper.Create;
+
+  //fill the context background with white ...
+  cairo_rectangle(cr, 0, 0, bmp.Width, bmp.Height);
+  cairo_set_source_rgba(cr, 1, 1, 1, 1);
+  cairo_fill(cr);
+
+  //create a circular pattern, add the path to clipper and then draw it ...
+  cairo_arc(cr, 165,110,70,0,2*3.1415926);
+  cairo_close_path(cr); //important because we can only clip polygons
+  CairoToPointArray(cr, ppa, scaling);
+  clipper.AddPaths(ppa, ptSubject, true);
+  cairo_set_line_width(cr, 2.0);
+  cairo_set_source_rgba(cr, 0, 0, 1, 0.25);
+  cairo_fill_preserve(cr);
+  cairo_set_source_rgba(cr, 0, 0, 0, 0.5);
+  cairo_stroke(cr);
+  cairo_new_path(cr);
+
+  //create a star pattern, add the path to clipper and then draw it ...
+  cairo_move_to(cr, 60,110);
+  cairo_line_to(cr, 240,70);
+  cairo_line_to(cr, 110,210);
+  cairo_line_to(cr, 140,25);
+  cairo_line_to(cr, 230,200);
+  cairo_close_path(cr);
+  cairo_new_sub_path(cr);
+  cairo_arc(cr, 185,50,20,0,2*3.1415926);
+  cairo_close_path(cr);
+  CairoToPointArray(cr, ppa, scaling);
+  clipper.AddPaths(ppa, ptClip, true);
+  cairo_set_source_rgba(cr, 1, 0, 0, 0.25);
+  cairo_fill_preserve(cr);
+  cairo_set_source_rgba(cr, 0, 0, 0, 0.5);
+  cairo_stroke(cr);
+
+  //now clip and draw the paths previously added to clipper ....
+  clipper.Execute(ctIntersection, ppa, pftNonZero, pftNonZero);
+
+  cairo_set_line_width(cr, 2.0);
+  if offsetVal <> 0 then
+  begin
+    with TClipperOffset.Create() do
+    try
+      AddPaths(ppa, jtRound, etClosedPolygon);
+      Execute(ppa, offsetVal*power(10,scaling));
+    finally
+      Free;
+    end;
+  end;
+  PointArrayToCairo(ppa, cr, scaling);
+  cairo_set_source_rgba(cr, 1, 1, 0, 1);
+  cairo_fill_preserve(cr);
+  cairo_set_source_rgba(cr, 0, 0, 0, 1);
+  cairo_stroke(cr);
+
+  GetClientRect(GetActiveWindow, rec);
+  cairo_set_font_size(cr,11);
+  text := 'Polygon offset = '+ inttostr(offsetVal) + '.  (Adjust with arrow keys)';
+  cairo_text_extents(cr, pchar(text), @extent);
+  cairo_move_to(cr, 10, rec.Bottom - extent.height);
+  cairo_show_text(cr, pchar(text));
+
+  //clean up ...
+  cairo_surface_finish(surface);
+end;
+//------------------------------------------------------------------------------
+
+function WndProc(Wnd : HWND; message : UINT;
+  wParam : Integer; lParam: Integer) : Integer; stdcall;
+var
+  dc: HDC;
+  ps: PAINTSTRUCT;
+begin
+  case message of
+    WM_PAINT:
+      begin
+        dc := BeginPaint(Wnd, ps);
+        with bmp do BitBlt(dc,0,0,Width,Height,canvas.Handle,0,0,SRCCOPY);
+        EndPaint(Wnd, ps);
+        result := 0;
+      end;
+
+    WM_CREATE:
+      begin
+        bmp := graphics.TBitmap.Create;
+        result := DefWindowProc(Wnd, message, wParam, lParam);
+      end;
+    WM_DESTROY:
+      begin
+        bmp.Free;
+        PostQuitMessage(0);
+        result := 0;
+      end;
+
+    WM_SIZE:
+      begin
+        bmp.Width := loword(lparam);
+        bmp.Height := hiword(lparam);
+        PaintBitmap;
+        result := DefWindowProc(Wnd, message, wParam, lParam);
+      end;
+
+    WM_KEYDOWN:
+      case wParam of
+        VK_ESCAPE:
+          begin
+            PostQuitMessage(0);
+            result := 0;
+          end;
+        VK_RIGHT, VK_UP:
+          begin
+            if offsetVal < 20 then inc(offsetVal);
+            PaintBitmap;
+            InvalidateRect(0, nil, false);
+            result := 0;
+          end;
+        VK_LEFT, VK_DOWN:
+          begin
+            if offsetVal > -20 then dec(offsetVal);
+            PaintBitmap;
+            InvalidateRect(0, nil, false);
+            result := 0;
+          end;
+        else
+          result := DefWindowProc(Wnd, message, wParam, lParam);
+      end;
+
+   else
+     result := DefWindowProc(Wnd, message, wParam, lParam);
+   end;
+end;
+//------------------------------------------------------------------------------
+
+var
+  hWnd     : THandle;
+  Msg      : TMsg;
+  wndClass : TWndClass;
+begin
+  wndClass.style         := CS_HREDRAW or CS_VREDRAW;
+  wndClass.lpfnWndProc   := @WndProc;
+  wndClass.cbClsExtra    := 0;
+  wndClass.cbWndExtra    := 0;
+  wndClass.hInstance     := hInstance;
+  wndClass.hIcon         := LoadIcon(0, IDI_APPLICATION);
+  wndClass.hCursor       := LoadCursor(0, IDC_ARROW);
+  wndClass.hbrBackground := HBRUSH(GetStockObject(WHITE_BRUSH));
+  wndClass.lpszMenuName  := nil;
+  wndClass.lpszClassName := 'CairoClipper';
+
+  RegisterClass(wndClass);
+
+  hWnd := CreateWindow(
+     'CairoClipper',         // window class name
+     'Cairo-Clipper Demo',   // window caption
+     WS_OVERLAPPEDWINDOW,    // window style
+     Integer(CW_USEDEFAULT), // initial x position
+     Integer(CW_USEDEFAULT), // initial y position
+     400,                    // initial x size
+     300,                    // initial y size
+     0,                      // parent window handle
+     0,                      // window menu handle
+     hInstance,              // program instance handle
+     nil);                   // creation parameters
+
+  ShowWindow(hWnd, SW_SHOW);
+  UpdateWindow(hWnd);
+
+  while(GetMessage(msg, 0, 0, 0)) do
+  begin
+    TranslateMessage(msg);
+    DispatchMessage(msg);
+  end;
+end.
+
diff --git a/Delphi/cairo demo/CairoClipperDemo1.res b/Delphi/cairo demo/CairoClipperDemo1.res
new file mode 100644
index 0000000..d8a5528
Binary files /dev/null and b/Delphi/cairo demo/CairoClipperDemo1.res differ
diff --git a/Delphi/cairo demo/cairo_clipper.pas b/Delphi/cairo demo/cairo_clipper.pas
new file mode 100644
index 0000000..6276606
--- /dev/null
+++ b/Delphi/cairo demo/cairo_clipper.pas	
@@ -0,0 +1,121 @@
+unit cairo_clipper;
+
+(*******************************************************************************
+*                                                                              *
+* Author    :  Angus Johnson                                                   *
+* Version   :  1.2                                                             *
+* Date      :  29 September 2011                                               *
+* Website   :  http://www.angusj.com                                           *
+* Copyright :  Angus Johnson 2010-2011                                         *
+*                                                                              *
+* License:                                                                     *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt                                         *
+*                                                                              *
+*******************************************************************************)
+
+interface
+
+uses
+  SysUtils, Classes, Cairo, types, math, clipper;
+
+//nb: Since Clipper only accepts integer coordinates, fractional values have to
+//be scaled up and down when being passed to and from Clipper. This is easily
+//accomplished by setting the scaling factor (10^x) in the following functions.
+//When scaling, remember that on most platforms, integer is only a 32bit value.
+function PointArrayToCairo(const polys: TPaths;
+  cairo: Pcairo_t; scaling_factor: integer = 2): boolean;
+function CairoToPointArray(cairo: Pcairo_t;
+  out polys: TPaths; scaling_factor: integer = 2): boolean;
+
+implementation
+
+type
+  PCairoPathDataArray = ^TCairoPathDataArray;
+  TCairoPathDataArray =
+    array [0.. MAXINT div sizeof(cairo_path_data_t) -1] of cairo_path_data_t;
+
+function PointArrayToCairo(const polys: TPaths;
+  cairo: Pcairo_t; scaling_factor: integer = 2): boolean;
+var
+  i,j: integer;
+  scaling: double;
+begin
+  result := assigned(cairo);
+  if not result then exit;
+  if abs(scaling_factor) > 6 then
+    raise Exception.Create('PointArrayToCairo: invalid scaling factor');
+  scaling := power(10, scaling_factor);
+  for i := 0 to high(polys) do
+  begin
+    cairo_new_sub_path(cairo);
+    for j := 0 to high(polys[i]) do
+      with polys[i][j] do cairo_line_to(cairo,X/scaling,Y/scaling);
+    cairo_close_path(cairo);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function CairoToPointArray(cairo: Pcairo_t;
+  out polys: TPaths; scaling_factor: integer = 2): boolean;
+const
+  buffLen1: integer = 32;
+  buffLen2: integer = 128;
+var
+  i,currLen1, currLen2: integer;
+  pdHdr: cairo_path_data_t;
+  path: Pcairo_path_t;
+  currPos: TIntPoint;
+  scaling: double;
+begin
+  if abs(scaling_factor) > 6 then
+    raise Exception.Create('PointArrayToCairo: invalid scaling factor');
+  scaling := power(10, scaling_factor);
+  result := false;
+  setlength(polys, buffLen1);
+  currLen1 := 1;
+  currLen2 := 0;
+  currPos := IntPoint(0,0);
+  i := 0;
+  path := cairo_copy_path_flat(cairo);
+  try
+    while i < path.num_data do
+    begin
+      pdHdr := PCairoPathDataArray(path.data)[i];
+      case pdHdr.header._type of
+      CAIRO_PATH_CLOSE_PATH:
+        begin
+          if currLen2 > 1 then //ie: ignore if < 3 points (not a polygon)
+          begin
+            setlength(polys[currLen1-1], currLen2);
+            setlength(polys[currLen1], buffLen2);
+            inc(currLen1);
+          end;
+          currLen2 := 0;
+          currPos := IntPoint(0,0);
+          result := true;
+        end;
+      CAIRO_PATH_MOVE_TO, CAIRO_PATH_LINE_TO:
+        begin
+          result := false;
+          if (pdHdr.header._type = CAIRO_PATH_MOVE_TO) and
+            (currLen2 > 0) then break; //ie enforce ClosePath for polygons
+          if (currLen2 mod buffLen2 = 0) then
+            SetLength(polys[currLen1-1], currLen2 + buffLen2);
+          with PCairoPathDataArray(path.data)[i+1].point do
+            currPos := IntPoint(Round(x*scaling),Round(y*scaling));
+          polys[currLen1-1][currLen2] := currPos;
+          inc(currLen2);
+        end;
+      end;
+      inc(i, pdHdr.header.length);
+    end;
+  finally
+    cairo_path_destroy(path);
+  end;
+  dec(currLen1); //ie enforces a ClosePath
+  setlength(polys, currLen1);
+end;
+//------------------------------------------------------------------------------
+
+end.
diff --git a/Delphi/clipper.pas b/Delphi/clipper.pas
new file mode 100644
index 0000000..859a208
--- /dev/null
+++ b/Delphi/clipper.pas
@@ -0,0 +1,5514 @@
+unit clipper;
+
+(*******************************************************************************
+*                                                                              *
+* Author    :  Angus Johnson                                                   *
+* Version   :  6.2.9                                                           *
+* Date      :  16 February 2015                                                *
+* Website   :  http://www.angusj.com                                           *
+* Copyright :  Angus Johnson 2010-2015                                         *
+*                                                                              *
+* License:                                                                     *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt                                         *
+*                                                                              *
+* Attributions:                                                                *
+* The code in this library is an extension of Bala Vatti's clipping algorithm: *
+* "A generic solution to polygon clipping"                                     *
+* Communications of the ACM, Vol 35, Issue 7 (July 1992) PP 56-63.             *
+* http://portal.acm.org/citation.cfm?id=129906                                 *
+*                                                                              *
+* Computer graphics and geometric modeling: implementation and algorithms      *
+* By Max K. Agoston                                                            *
+* Springer; 1 edition (January 4, 2005)                                        *
+* http://books.google.com/books?q=vatti+clipping+agoston                       *
+*                                                                              *
+* See also:                                                                    *
+* "Polygon Offsetting by Computing Winding Numbers"                            *
+* Paper no. DETC2005-85513 PP. 565-575                                         *
+* ASME 2005 International Design Engineering Technical Conferences             *
+* and Computers and Information in Engineering Conference (IDETC/CIE2005)      *
+* September 24-28, 2005 , Long Beach, California, USA                          *
+* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf              *
+*                                                                              *
+*******************************************************************************)
+
+//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
+//improve performance but coordinate values are limited to the range +/- 46340
+//{$DEFINE use_int32}
+
+//use_xyz: adds a Z member to IntPoint (with only a minor cost to performance)
+//{$DEFINE use_xyz}
+
+//use_lines: Enables open path clipping (with a very minor cost to performance)
+{$DEFINE use_lines}
+
+{$IFDEF FPC}
+  {$DEFINE INLINING}
+  {$DEFINE UInt64Support}
+{$ELSE}
+  // enable LEGACYIFEND for Delphi XE4+
+  {$IF CompilerVersion >= 25.0}
+    {$LEGACYIFEND ON}
+  {$IFEND}
+
+  // use generic lists for NextGen compiler
+  {$IFDEF NEXTGEN}
+    {$DEFINE USEGENERICS}
+  {$ENDIF}
+
+  {$IFDEF ConditionalExpressions}
+    {$IF CompilerVersion >= 15} //Delphi 7
+      {$DEFINE UInt64Support} //nb: Delphi7 only marginally supports UInt64.
+    {$IFEND}
+    {$IF CompilerVersion >= 18} //Delphi 2007
+      //Inline has been supported since D2005.
+      //However D2005 and D2006 have an Inline codegen bug (QC41166).
+      //http://www.thedelphigeek.com/2007/02/nasty-inline-codegen-bug-in-bds-2006.html
+      {$DEFINE INLINING}
+    {$IFEND}
+  {$ENDIF}
+{$ENDIF}
+
+interface
+
+uses
+  SysUtils, Types, Classes,
+  {$IFDEF USEGENERICS}
+    Generics.Collections, Generics.Defaults,
+  {$ENDIF}
+  Math;
+
+const
+  def_arc_tolerance = 0.25;
+
+type
+{$IFDEF use_int32}
+{$IF CompilerVersion < 20} //Delphi 2009
+  cInt = Integer; //Int32 supported since D2009.
+{$ELSE}
+  cInt = Int32;
+{$IFEND}
+{$ELSE}
+  cInt = Int64;
+{$ENDIF}
+
+  PIntPoint = ^TIntPoint;
+{$IFDEF use_xyz}
+  TIntPoint = record X, Y, Z: cInt; end;
+{$ELSE}
+  TIntPoint = record X, Y: cInt; end;
+{$ENDIF}
+
+  TIntRect = record Left, Top, Right, Bottom: cInt; end;
+
+  TDoublePoint = record X, Y: Double; end;
+  TArrayOfDoublePoint = array of TDoublePoint;
+
+{$IFDEF use_xyz}
+  TZFillCallback =
+    procedure (const E1Bot, E1Top, E2Bot, E2Top: TIntPoint; var Pt: TIntPoint);
+{$ENDIF}
+
+  TInitOption = (ioReverseSolution, ioStrictlySimple, ioPreserveCollinear);
+  TInitOptions = set of TInitOption;
+
+  TClipType = (ctIntersection, ctUnion, ctDifference, ctXor);
+  TPolyType = (ptSubject, ptClip);
+  //By far the most widely used winding rules for polygon filling are
+  //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
+  //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
+  //see http://glprogramming.com/red/chapter11.html
+  TPolyFillType = (pftEvenOdd, pftNonZero, pftPositive, pftNegative);
+
+  //TJoinType & TEndType are used by OffsetPaths()
+  TJoinType = (jtSquare, jtRound, jtMiter);
+  TEndType = (etClosedPolygon, etClosedLine,
+    etOpenButt, etOpenSquare, etOpenRound); //and etSingle still to come
+
+  TPath = array of TIntPoint;
+  TPaths = array of TPath;
+
+  TPolyNode = class;
+  TArrayOfPolyNode = array of TPolyNode;
+
+  TPolyNode = class
+  private
+    FPath    : TPath;
+    FParent  : TPolyNode;
+    FIndex   : Integer;
+    FCount   : Integer;
+    FBuffLen : Integer;
+    FIsOpen  : Boolean;
+    FChilds  : TArrayOfPolyNode;
+    FJoinType: TJoinType; //used by ClipperOffset only
+    FEndType : TEndType;  //used by ClipperOffset only
+    function  GetChild(Index: Integer): TPolyNode;
+    function  IsHoleNode: boolean;
+    procedure AddChild(PolyNode: TPolyNode);
+    function  GetNextSiblingUp: TPolyNode;
+  public
+    function  GetNext: TPolyNode;
+    property  ChildCount: Integer read FCount;
+    property  Childs[index: Integer]: TPolyNode read GetChild;
+    property  Parent: TPolyNode read FParent;
+    property  IsHole: Boolean read IsHoleNode;
+    property  IsOpen: Boolean read FIsOpen;
+    property  Contour: TPath read FPath;
+  end;
+
+  TPolyTree = class(TPolyNode)
+  private
+    FAllNodes: TArrayOfPolyNode; //container for ALL PolyNodes
+    function GetTotal: Integer;
+  public
+    procedure Clear;
+    function GetFirst: TPolyNode;
+    destructor Destroy; override;
+    property Total: Integer read GetTotal;
+  end;
+
+  //the definitions below are used internally ...
+  TEdgeSide = (esLeft, esRight);
+  TDirection = (dRightToLeft, dLeftToRight);
+
+  POutPt = ^TOutPt;
+
+  PEdge = ^TEdge;
+  TEdge = record
+    Bot  : TIntPoint;      //bottom
+    Curr : TIntPoint;      //current (ie relative to bottom of current scanbeam)
+    Top  : TIntPoint;      //top
+    Delta: TIntPoint;
+    Dx   : Double;         //inverse of slope
+    PolyType : TPolyType;
+    Side     : TEdgeSide;
+    WindDelta: Integer;    //1 or -1 depending on winding direction
+    WindCnt  : Integer;
+    WindCnt2 : Integer;    //winding count of the opposite PolyType
+    OutIdx   : Integer;
+    Next     : PEdge;
+    Prev     : PEdge;
+    NextInLML: PEdge;
+    PrevInAEL: PEdge;
+    NextInAEL: PEdge;
+    PrevInSEL: PEdge;
+    NextInSEL: PEdge;
+  end;
+
+  PEdgeArray = ^TEdgeArray;
+  TEdgeArray = array[0.. MaxInt div sizeof(TEdge) -1] of TEdge;
+
+  PScanbeam = ^TScanbeam;
+  TScanbeam = record
+    Y    : cInt;
+    Next : PScanbeam;
+  end;
+
+  PMaxima = ^TMaxima;
+  TMaxima = record
+    X     : cInt;
+    Next  : PMaxima;
+    Prev  : PMaxima;
+  end;
+
+  PIntersectNode = ^TIntersectNode;
+  TIntersectNode = record
+    Edge1: PEdge;
+    Edge2: PEdge;
+    Pt   : TIntPoint;
+  end;
+
+  PLocalMinimum = ^TLocalMinimum;
+  TLocalMinimum = record
+    Y         : cInt;
+    LeftBound : PEdge;
+    RightBound: PEdge;
+  end;
+
+  POutRec = ^TOutRec;
+  TOutRec = record
+    Idx         : Integer;
+    BottomPt    : POutPt;
+    IsHole      : Boolean;
+    IsOpen      : Boolean;
+    //The 'FirstLeft' field points to the OutRec representing the polygon
+    //immediately to the left of the current OutRec's polygon. When a polygon is
+    //contained within another polygon, the polygon immediately to its left will
+    //either be its outer polygon or a sibling also contained by the same outer
+    //polygon. By storing  this field, it's easy to sort polygons into a tree
+    //structure which reflects the parent/child relationships of all polygons.
+    FirstLeft   : POutRec;
+    Pts         : POutPt;
+    PolyNode    : TPolyNode;
+  end;
+
+  TOutPt = record
+    Idx      : Integer;
+    Pt       : TIntPoint;
+    Next     : POutPt;
+    Prev     : POutPt;
+  end;
+
+  PJoin = ^TJoin;
+  TJoin = record
+    OutPt1   : POutPt;
+    OutPt2   : POutPt;
+    OffPt    : TIntPoint; //offset point (provides slope of common edges)
+  end;
+
+  {$IFDEF USEGENERICS}
+  TEgdeList = TList<PEdgeArray>;
+  TLocMinList = TList<PLocalMinimum>;
+  {$ELSE}
+  TEgdeList = TList;
+  TLocMinList = TList;
+  {$ENDIF}
+
+  TClipperBase = class
+  private
+    FEdgeList         : TEgdeList;
+    FUse64BitRange    : Boolean;      //see LoRange and HiRange consts notes below
+    FHasOpenPaths     : Boolean;
+    procedure DisposeLocalMinimaList;
+    procedure DisposePolyPts(PP: POutPt);
+    function ProcessBound(E: PEdge; NextIsForward: Boolean): PEdge;
+  protected
+    FLocMinList       : TLocMinList;
+    FCurrentLocMinIdx : Integer;
+    FPreserveCollinear : Boolean;
+    procedure Reset; virtual;
+    property HasOpenPaths: Boolean read FHasOpenPaths;
+  public
+    constructor Create; virtual;
+    destructor Destroy; override;
+    procedure Clear; virtual;
+
+    function AddPath(const Path: TPath; PolyType: TPolyType; Closed: Boolean): Boolean;
+    function AddPaths(const Paths: TPaths; PolyType: TPolyType; Closed: Boolean): Boolean;
+    //PreserveCollinear: Prevents removal of 'inner' vertices when three or
+    //more vertices are collinear in solution polygons.
+    property PreserveCollinear: Boolean
+      read FPreserveCollinear write FPreserveCollinear;
+  end;
+
+
+  {$IFDEF USEGENERICS}
+  TPolyOutList = TList<POutRec>;
+  TJoinList = TList<PJoin>;
+  TIntersecList = TList<PIntersectNode>;
+  {$ELSE}
+  TPolyOutList = TList;
+  TJoinList = TList;
+  TIntersecList = TList;
+  {$ENDIF}
+
+  TClipper = class(TClipperBase)
+  private
+    FPolyOutList      : TPolyOutList;
+    FJoinList         : TJoinList;
+    FGhostJoinList    : TJoinList;
+    FIntersectList    : TIntersecList;
+    FClipType         : TClipType;
+    FScanbeam         : PScanbeam;    //scanbeam list
+    FMaxima           : PMaxima;      //maxima XPos list
+    FActiveEdges      : PEdge;        //active Edge list
+    FSortedEdges      : PEdge;        //used for temporary sorting
+    FClipFillType     : TPolyFillType;
+    FSubjFillType     : TPolyFillType;
+    FExecuteLocked    : Boolean;
+    FReverseOutput    : Boolean;
+    FStrictSimple      : Boolean;
+    FUsingPolyTree    : Boolean;
+{$IFDEF use_xyz}
+    FZFillCallback    : TZFillCallback;
+{$ENDIF}
+    procedure DisposeScanbeamList;
+    procedure InsertScanbeam(const Y: cInt);
+    function PopScanbeam: cInt;
+    procedure InsertMaxima(const X: cInt);
+    procedure DisposeMaximaList;
+    procedure SetWindingCount(Edge: PEdge);
+    function IsEvenOddFillType(Edge: PEdge): Boolean;
+    function IsEvenOddAltFillType(Edge: PEdge): Boolean;
+    procedure AddEdgeToSEL(Edge: PEdge);
+    procedure CopyAELToSEL;
+    procedure InsertLocalMinimaIntoAEL(const BotY: cInt);
+    procedure SwapPositionsInAEL(E1, E2: PEdge);
+    procedure SwapPositionsInSEL(E1, E2: PEdge);
+    procedure ProcessHorizontal(HorzEdge: PEdge);
+    procedure ProcessHorizontals;
+    function ProcessIntersections(const TopY: cInt): Boolean;
+    procedure BuildIntersectList(const TopY: cInt);
+    procedure ProcessIntersectList;
+    procedure DeleteFromAEL(E: PEdge);
+    procedure DeleteFromSEL(E: PEdge);
+    procedure IntersectEdges(E1,E2: PEdge; Pt: TIntPoint);
+    procedure DoMaxima(E: PEdge);
+    procedure UpdateEdgeIntoAEL(var E: PEdge);
+    function FixupIntersectionOrder: Boolean;
+    procedure ProcessEdgesAtTopOfScanbeam(const TopY: cInt);
+    function IsContributing(Edge: PEdge): Boolean;
+    function CreateOutRec: POutRec;
+    function AddOutPt(E: PEdge; const Pt: TIntPoint): POutPt;
+    function GetLastOutPt(E: PEdge): POutPt;
+    procedure AddLocalMaxPoly(E1, E2: PEdge; const Pt: TIntPoint);
+    function AddLocalMinPoly(E1, E2: PEdge; const Pt: TIntPoint): POutPt;
+    function GetOutRec(Idx: integer): POutRec;
+    procedure AppendPolygon(E1, E2: PEdge);
+    procedure DisposeAllOutRecs;
+    procedure DisposeOutRec(Index: Integer);
+    procedure DisposeIntersectNodes;
+    function BuildResult: TPaths;
+    function BuildResult2(PolyTree: TPolyTree): Boolean;
+    procedure FixupOutPolygon(OutRec: POutRec);
+    procedure FixupOutPolyline(OutRec: POutRec);
+    procedure SetHoleState(E: PEdge; OutRec: POutRec);
+    procedure AddJoin(Op1, Op2: POutPt; const OffPt: TIntPoint);
+    procedure ClearJoins;
+    procedure AddGhostJoin(OutPt: POutPt; const OffPt: TIntPoint);
+    procedure ClearGhostJoins;
+    function JoinPoints(Jr: PJoin; OutRec1, OutRec2: POutRec): Boolean;
+    procedure FixupFirstLefts1(OldOutRec, NewOutRec: POutRec);
+    procedure FixupFirstLefts2(OldOutRec, NewOutRec: POutRec);
+    procedure DoSimplePolygons;
+    procedure JoinCommonEdges;
+    procedure FixHoleLinkage(OutRec: POutRec);
+  protected
+    procedure Reset; override;
+    function ExecuteInternal: Boolean; virtual;
+  public
+    function Execute(clipType: TClipType;
+      out solution: TPaths;
+      FillType: TPolyFillType = pftEvenOdd): Boolean; overload;
+    function Execute(clipType: TClipType;
+      out solution: TPaths;
+      subjFillType: TPolyFillType;
+      clipFillType: TPolyFillType): Boolean; overload;
+    function Execute(clipType: TClipType;
+      out PolyTree: TPolyTree;
+      FillType: TPolyFillType = pftEvenOdd): Boolean; overload;
+    function Execute(clipType: TClipType;
+      out PolyTree: TPolyTree;
+      subjFillType: TPolyFillType;
+      clipFillType: TPolyFillType): Boolean; overload;
+    constructor Create(InitOptions: TInitOptions = []); reintroduce; overload;
+    destructor Destroy; override;
+    //ReverseSolution: reverses the default orientation
+    property ReverseSolution: Boolean read FReverseOutput write FReverseOutput;
+    //StrictlySimple: when false (the default) solutions are 'weakly' simple
+    property StrictlySimple: Boolean read FStrictSimple write FStrictSimple;
+{$IFDEF use_xyz}
+    property ZFillFunction: TZFillCallback read FZFillCallback write FZFillCallback;
+{$ENDIF}
+  end;
+
+  TClipperOffset = class
+  private
+    FDelta: Double;
+    FSinA, FSin, FCos: Extended;
+    FMiterLim, FStepsPerRad: Double;
+    FNorms: TArrayOfDoublePoint;
+    FSolution: TPaths;
+    FOutPos: Integer;
+    FInP: TPath;
+    FOutP: TPath;
+
+    FLowest: TIntPoint; //X = Path index, Y = Path offset (to lowest point)
+    FPolyNodes: TPolyNode;
+    FMiterLimit: Double;
+    FArcTolerance: Double;
+
+    procedure AddPoint(const Pt: TIntPoint);
+    procedure DoSquare(J, K: Integer);
+    procedure DoMiter(J, K: Integer; R: Double);
+    procedure DoRound(J, K: Integer);
+    procedure OffsetPoint(J: Integer;
+      var K: Integer; JoinType: TJoinType);
+
+    procedure FixOrientations;
+    procedure DoOffset(Delta: Double);
+  public
+    constructor Create(MiterLimit: Double = 2; ArcTolerance: Double = def_arc_tolerance);
+    destructor Destroy; override;
+    procedure AddPath(const Path: TPath; JoinType: TJoinType; EndType: TEndType);
+    procedure AddPaths(const Paths: TPaths; JoinType: TJoinType; EndType: TEndType);
+    procedure Clear;
+    procedure Execute(out solution: TPaths; Delta: Double); overload;
+    procedure Execute(out solution: TPolyTree; Delta: Double); overload;
+    property MiterLimit: double read FMiterLimit write FMiterLimit;
+    property ArcTolerance: double read FArcTolerance write FArcTolerance;
+
+  end;
+
+function Orientation(const Pts: TPath): Boolean; overload;
+function Area(const Pts: TPath): Double; overload;
+function PointInPolygon (const pt: TIntPoint; const poly: TPath): Integer; overload;
+function GetBounds(const polys: TPaths): TIntRect;
+
+{$IFDEF use_xyz}
+function IntPoint(const X, Y: Int64; Z: Int64 = 0): TIntPoint; overload;
+function IntPoint(const X, Y: Double; Z: Double = 0): TIntPoint; overload;
+{$ELSE}
+function IntPoint(const X, Y: cInt): TIntPoint; overload;
+function IntPoint(const X, Y: Double): TIntPoint; overload;
+{$ENDIF}
+
+function DoublePoint(const X, Y: Double): TDoublePoint; overload;
+function DoublePoint(const Ip: TIntPoint): TDoublePoint; overload;
+
+function ReversePath(const Pts: TPath): TPath;
+function ReversePaths(const Pts: TPaths): TPaths;
+
+//SimplifyPolygon converts a self-intersecting polygon into a simple polygon.
+function SimplifyPolygon(const Poly: TPath; FillType: TPolyFillType = pftEvenOdd): TPaths;
+function SimplifyPolygons(const Polys: TPaths; FillType: TPolyFillType = pftEvenOdd): TPaths;
+
+//CleanPolygon removes adjacent vertices closer than the specified distance.
+function CleanPolygon(const Poly: TPath; Distance: double = 1.415): TPath;
+function CleanPolygons(const Polys: TPaths; Distance: double = 1.415): TPaths;
+
+function MinkowskiSum(const Pattern, Path: TPath; PathIsClosed: Boolean): TPaths; overload;
+function MinkowskiSum(const Pattern: TPath; const Paths: TPaths;
+  PathFillType: TPolyFillType; PathIsClosed: Boolean): TPaths; overload;
+function MinkowskiDiff(const Poly1, Poly2: TPath): TPaths;
+
+function PolyTreeToPaths(PolyTree: TPolyTree): TPaths;
+function ClosedPathsFromPolyTree(PolyTree: TPolyTree): TPaths;
+function OpenPathsFromPolyTree(PolyTree: TPolyTree): TPaths;
+
+const
+  //The SlopesEqual function places the most limits on coordinate values
+  //So, to avoid overflow errors, they must not exceed the following values...
+  //Also, if all coordinates are within +/-LoRange, then calculations will be
+  //faster. Otherwise using Int128 math will render the library ~10-15% slower.
+{$IFDEF use_int32}
+  LoRange: cInt = 46340;
+  HiRange: cInt = 46340;
+{$ELSE}
+  LoRange: cInt = $B504F333;          //3.0e+9
+  HiRange: cInt = $3FFFFFFFFFFFFFFF;  //9.2e+18
+{$ENDIF}
+
+implementation
+
+//NOTE: The Clipper library has been developed with software that uses an
+//inverted Y axis display. Therefore 'above' and 'below' in the code's comments
+//will reflect this. For example: given coord A (0,20) and coord B (0,10),
+//A.Y would be considered BELOW B.Y to correctly understand the comments.
+
+const
+  Horizontal: Double = -3.4e+38;
+
+  Unassigned : Integer = -1;
+  Skip       : Integer = -2; //flag for the edge that closes an open path
+  Tolerance  : double = 1.0E-15;
+  Two_Pi     : double = 2 * PI;
+
+resourcestring
+  rsDoMaxima = 'DoMaxima error';
+  rsUpdateEdgeIntoAEL = 'UpdateEdgeIntoAEL error';
+  rsHorizontal = 'ProcessHorizontal error';
+  rsInvalidInt = 'Coordinate exceeds range bounds';
+  rsIntersect = 'Intersection error';
+  rsOpenPath  = 'AddPath: Open paths must be subject.';
+  rsOpenPath2  = 'AddPath: Open paths have been disabled.';
+  rsOpenPath3  = 'Error: TPolyTree struct is needed for open path clipping.';
+  rsPolylines = 'Error intersecting polylines';
+  rsClipperOffset = 'Error: No PolyTree assigned';
+
+//------------------------------------------------------------------------------
+// TPolyNode methods ...
+//------------------------------------------------------------------------------
+
+function TPolyNode.GetChild(Index: Integer): TPolyNode;
+begin
+  if (Index < 0) or (Index >= FCount) then
+    raise Exception.Create('TPolyNode range error: ' + inttostr(Index));
+  Result := FChilds[Index];
+end;
+//------------------------------------------------------------------------------
+
+procedure TPolyNode.AddChild(PolyNode: TPolyNode);
+begin
+  if FCount = FBuffLen then
+  begin
+    Inc(FBuffLen, 16);
+    SetLength(FChilds, FBuffLen);
+  end;
+  PolyNode.FParent := self;
+  PolyNode.FIndex := FCount;
+  FChilds[FCount] := PolyNode;
+  Inc(FCount);
+end;
+//------------------------------------------------------------------------------
+
+function TPolyNode.IsHoleNode: boolean;
+var
+  Node: TPolyNode;
+begin
+  Result := True;
+  Node := FParent;
+  while Assigned(Node) do
+  begin
+    Result := not Result;
+    Node := Node.FParent;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function TPolyNode.GetNext: TPolyNode;
+begin
+  if FCount > 0 then
+    Result := FChilds[0] else
+    Result := GetNextSiblingUp;
+end;
+//------------------------------------------------------------------------------
+
+function TPolyNode.GetNextSiblingUp: TPolyNode;
+begin
+  if not Assigned(FParent) then //protects against TPolyTree.GetNextSiblingUp()
+    Result := nil
+  else if FIndex = FParent.FCount -1 then
+      Result := FParent.GetNextSiblingUp
+  else
+      Result := FParent.Childs[FIndex +1];
+end;
+
+//------------------------------------------------------------------------------
+// TPolyTree methods ...
+//------------------------------------------------------------------------------
+
+destructor TPolyTree.Destroy;
+begin
+  Clear;
+  inherited;
+end;
+//------------------------------------------------------------------------------
+
+procedure TPolyTree.Clear;
+var
+  I: Integer;
+begin
+  for I := 0 to high(FAllNodes) do FAllNodes[I].Free;
+  FAllNodes := nil;
+  FBuffLen := 16;
+  SetLength(FChilds, FBuffLen);
+  FCount := 0;
+end;
+//------------------------------------------------------------------------------
+
+function TPolyTree.GetFirst: TPolyNode;
+begin
+  if FCount > 0 then
+    Result := FChilds[0] else
+    Result := nil;
+end;
+//------------------------------------------------------------------------------
+
+function TPolyTree.GetTotal: Integer;
+begin
+  Result := length(FAllNodes);
+  //with negative offsets, ignore the hidden outer polygon ...
+  if (Result > 0) and (FAllNodes[0] <> FChilds[0]) then dec(Result);
+end;
+
+{$IFNDEF use_int32}
+
+//------------------------------------------------------------------------------
+// UInt64 math support for Delphi 6
+//------------------------------------------------------------------------------
+
+{$OVERFLOWCHECKS OFF}
+{$IFNDEF UInt64Support}
+function CompareUInt64(const i, j: Int64): Integer;
+begin
+  if Int64Rec(i).Hi < Int64Rec(j).Hi then
+    Result := -1
+  else if Int64Rec(i).Hi > Int64Rec(j).Hi then
+    Result := 1
+  else if Int64Rec(i).Lo < Int64Rec(j).Lo then
+    Result := -1
+  else if Int64Rec(i).Lo > Int64Rec(j).Lo then
+    Result := 1
+  else
+    Result := 0;
+end;
+{$ENDIF}
+
+function UInt64LT(const i, j: Int64): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
+begin
+{$IFDEF UInt64Support}
+  Result := UInt64(i) < UInt64(j);
+{$ELSE}
+  Result := CompareUInt64(i, j) = -1;
+{$ENDIF}
+end;
+
+function UInt64GT(const i, j: Int64): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
+begin
+{$IFDEF UInt64Support}
+  Result := UInt64(i) > UInt64(j);
+{$ELSE}
+  Result := CompareUInt64(i, j) = 1;
+{$ENDIF}
+end;
+{$OVERFLOWCHECKS ON}
+
+//------------------------------------------------------------------------------
+// Int128 Functions ...
+//------------------------------------------------------------------------------
+
+const
+  Mask32Bits = $FFFFFFFF;
+
+type
+
+  //nb: TInt128.Lo is typed Int64 instead of UInt64 to provide Delphi 7
+  //compatability. However while UInt64 isn't a recognised type in
+  //Delphi 7, it can still be used in typecasts.
+  TInt128 = record
+    Hi   : Int64;
+    Lo   : Int64;
+  end;
+
+{$OVERFLOWCHECKS OFF}
+procedure Int128Negate(var Val: TInt128);
+begin
+  if Val.Lo = 0 then
+  begin
+    Val.Hi := -Val.Hi;
+  end else
+  begin
+    Val.Lo := -Val.Lo;
+    Val.Hi := not Val.Hi;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function Int128(const val: Int64): TInt128; overload;
+begin
+  Result.Lo := val;
+  if val < 0 then
+    Result.Hi := -1 else
+    Result.Hi := 0;
+end;
+//------------------------------------------------------------------------------
+
+function Int128Equal(const Int1, Int2: TInt128): Boolean;
+begin
+  Result := (Int1.Lo = Int2.Lo) and (Int1.Hi = Int2.Hi);
+end;
+//------------------------------------------------------------------------------
+
+function Int128LessThan(const Int1, Int2: TInt128): Boolean;
+begin
+  if (Int1.Hi <> Int2.Hi) then Result := Int1.Hi < Int2.Hi
+  else Result := UInt64LT(Int1.Lo, Int2.Lo);
+end;
+//------------------------------------------------------------------------------
+
+function Int128Add(const Int1, Int2: TInt128): TInt128;
+begin
+  Result.Lo := Int1.Lo + Int2.Lo;
+  Result.Hi := Int1.Hi + Int2.Hi;
+  if UInt64LT(Result.Lo, Int1.Lo) then Inc(Result.Hi);
+end;
+//------------------------------------------------------------------------------
+
+function Int128Sub(const Int1, Int2: TInt128): TInt128;
+begin
+  Result.Hi := Int1.Hi - Int2.Hi;
+  Result.Lo := Int1.Lo - Int2.Lo;
+  if UInt64GT(Result.Lo, Int1.Lo) then Dec(Result.Hi);
+end;
+//------------------------------------------------------------------------------
+
+function Int128Mul(Int1, Int2: Int64): TInt128;
+var
+  A, B, C: Int64;
+  Int1Hi, Int1Lo, Int2Hi, Int2Lo: Int64;
+  Negate: Boolean;
+begin
+  //save the Result's sign before clearing both sign bits ...
+  Negate := (Int1 < 0) <> (Int2 < 0);
+  if Int1 < 0 then Int1 := -Int1;
+  if Int2 < 0 then Int2 := -Int2;
+
+  Int1Hi := Int1 shr 32;
+  Int1Lo := Int1 and Mask32Bits;
+  Int2Hi := Int2 shr 32;
+  Int2Lo := Int2 and Mask32Bits;
+
+  A := Int1Hi * Int2Hi;
+  B := Int1Lo * Int2Lo;
+  //because the high (sign) bits in both int1Hi & int2Hi have been zeroed,
+  //there's no risk of 64 bit overflow in the following assignment
+  //(ie: $7FFFFFFF*$FFFFFFFF + $7FFFFFFF*$FFFFFFFF < 64bits)
+  C := Int1Hi*Int2Lo + Int2Hi*Int1Lo;
+  //Result = A shl 64 + C shl 32 + B ...
+  Result.Hi := A + (C shr 32);
+  A := C shl 32;
+
+  Result.Lo := A + B;
+  if UInt64LT(Result.Lo, A) then
+    Inc(Result.Hi);
+
+  if Negate then Int128Negate(Result);
+end;
+//------------------------------------------------------------------------------
+
+function Int128Div(Dividend, Divisor: TInt128{; out Remainder: TInt128}): TInt128;
+var
+  Cntr: TInt128;
+  Negate: Boolean;
+begin
+  if (Divisor.Lo = 0) and (Divisor.Hi = 0) then
+    raise Exception.create('int128Div error: divide by zero');
+
+  Negate := (Divisor.Hi < 0) <> (Dividend.Hi < 0);
+  if Dividend.Hi < 0 then Int128Negate(Dividend);
+  if Divisor.Hi < 0 then Int128Negate(Divisor);
+
+  if Int128LessThan(Divisor, Dividend) then
+  begin
+    Result.Hi := 0;
+    Result.Lo := 0;
+    Cntr.Lo := 1;
+    Cntr.Hi := 0;
+    //while (Dividend >= Divisor) do
+    while not Int128LessThan(Dividend, Divisor) do
+    begin
+      //divisor := divisor shl 1;
+      Divisor.Hi := Divisor.Hi shl 1;
+      if Divisor.Lo < 0 then Inc(Divisor.Hi);
+      Divisor.Lo := Divisor.Lo shl 1;
+
+      //Cntr := Cntr shl 1;
+      Cntr.Hi := Cntr.Hi shl 1;
+      if Cntr.Lo < 0 then Inc(Cntr.Hi);
+      Cntr.Lo := Cntr.Lo shl 1;
+    end;
+    //Divisor := Divisor shr 1;
+    Divisor.Lo := Divisor.Lo shr 1;
+    if Divisor.Hi and $1 = $1 then
+      Int64Rec(Divisor.Lo).Hi := Cardinal(Int64Rec(Divisor.Lo).Hi) or $80000000;
+    Divisor.Hi := Divisor.Hi shr 1;
+
+    //Cntr := Cntr shr 1;
+    Cntr.Lo := Cntr.Lo shr 1;
+    if Cntr.Hi and $1 = $1 then
+      Int64Rec(Cntr.Lo).Hi := Cardinal(Int64Rec(Cntr.Lo).Hi) or $80000000;
+    Cntr.Hi := Cntr.Hi shr 1;
+
+    //while (Cntr > 0) do
+    while not ((Cntr.Hi = 0) and (Cntr.Lo = 0)) do
+    begin
+      //if ( Dividend >= Divisor) then
+      if not Int128LessThan(Dividend, Divisor) then
+      begin
+        //Dividend := Dividend - Divisor;
+        Dividend := Int128Sub(Dividend, Divisor);
+
+        //Result := Result or Cntr;
+        Result.Hi := Result.Hi or Cntr.Hi;
+        Result.Lo := Result.Lo or Cntr.Lo;
+      end;
+      //Divisor := Divisor shr 1;
+      Divisor.Lo := Divisor.Lo shr 1;
+      if Divisor.Hi and $1 = $1 then
+        Int64Rec(Divisor.Lo).Hi := Cardinal(Int64Rec(Divisor.Lo).Hi) or $80000000;
+      Divisor.Hi := Divisor.Hi shr 1;
+
+      //Cntr := Cntr shr 1;
+      Cntr.Lo := Cntr.Lo shr 1;
+      if Cntr.Hi and $1 = $1 then
+        Int64Rec(Cntr.Lo).Hi := Cardinal(Int64Rec(Cntr.Lo).Hi) or $80000000;
+      Cntr.Hi := Cntr.Hi shr 1;
+    end;
+    if Negate then Int128Negate(Result);
+    //Remainder := Dividend;
+  end
+  else if (Divisor.Hi = Dividend.Hi) and (Divisor.Lo = Dividend.Lo) then
+  begin
+    if Negate then Result := Int128(-1) else Result := Int128(1);
+  end else
+  begin
+    Result := Int128(0);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function Int128AsDouble(val: TInt128): Double;
+const
+  shift64: Double = 18446744073709551616.0;
+var
+  lo: Int64;
+begin
+  if (val.Hi < 0) then
+  begin
+    lo := -val.Lo;
+    if lo = 0 then
+      Result := val.Hi * shift64 else
+      Result := -(not val.Hi * shift64 + UInt64(lo));
+  end else
+    Result := val.Hi * shift64 + UInt64(val.Lo);
+end;
+//------------------------------------------------------------------------------
+
+{$OVERFLOWCHECKS ON}
+
+{$ENDIF}
+
+//------------------------------------------------------------------------------
+// Miscellaneous Functions ...
+//------------------------------------------------------------------------------
+
+function PointCount(Pts: POutPt): Integer;
+var
+  P: POutPt;
+begin
+  Result := 0;
+  if not Assigned(Pts) then Exit;
+  P := Pts;
+  repeat
+    Inc(Result);
+    P := P.Next;
+  until P = Pts;
+end;
+//------------------------------------------------------------------------------
+
+function PointsEqual(const P1, P2: TIntPoint): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
+begin
+  Result := (P1.X = P2.X) and (P1.Y = P2.Y);
+end;
+//------------------------------------------------------------------------------
+
+{$IFDEF use_xyz}
+function IntPoint(const X, Y: Int64; Z: Int64 = 0): TIntPoint;
+begin
+  Result.X := X;
+  Result.Y := Y;
+  Result.Z := Z;
+end;
+//------------------------------------------------------------------------------
+
+function IntPoint(const X, Y: Double; Z: Double = 0): TIntPoint;
+begin
+  Result.X := Round(X);
+  Result.Y := Round(Y);
+  Result.Z := Round(Z);
+end;
+//------------------------------------------------------------------------------
+
+{$ELSE}
+
+function IntPoint(const X, Y: cInt): TIntPoint;
+begin
+  Result.X := X;
+  Result.Y := Y;
+end;
+//------------------------------------------------------------------------------
+
+function IntPoint(const X, Y: Double): TIntPoint;
+begin
+  Result.X := Round(X);
+  Result.Y := Round(Y);
+end;
+
+{$ENDIF}
+//------------------------------------------------------------------------------
+
+function DoublePoint(const X, Y: Double): TDoublePoint;
+begin
+  Result.X := X;
+  Result.Y := Y;
+end;
+//------------------------------------------------------------------------------
+
+function DoublePoint(const Ip: TIntPoint): TDoublePoint;
+begin
+  Result.X := Ip.X;
+  Result.Y := Ip.Y;
+end;
+//------------------------------------------------------------------------------
+
+function Area(const Pts: TPath): Double;
+var
+  I, J, Cnt: Integer;
+  D: Double;
+begin
+  Result := 0.0;
+  Cnt := Length(Pts);
+  if (Cnt < 3) then Exit;
+  J := cnt - 1;
+  for I := 0 to Cnt -1 do
+  begin
+    D := (Pts[j].X + Pts[i].X);
+    Result := Result + D * (Pts[j].Y - Pts[i].Y);
+    J := I;
+  end;
+  Result := -Result * 0.5;
+end;
+//------------------------------------------------------------------------------
+
+function Area(OutRec: POutRec): Double; overload;
+var
+  Op: POutPt;
+  D, D2: Double;
+begin
+  D := 0;
+  Op := OutRec.Pts;
+  if Assigned(Op) then
+    repeat
+      D2 := Op.Prev.Pt.X + Op.Pt.X;
+      D := D + D2 * (Op.Prev.Pt.Y - Op.Pt.Y);
+      Op := Op.Next;
+    until Op = OutRec.Pts;
+  Result := D * 0.5;
+end;
+//------------------------------------------------------------------------------
+
+function Orientation(const Pts: TPath): Boolean;
+begin
+  Result := Area(Pts) >= 0;
+end;
+//------------------------------------------------------------------------------
+
+function ReversePath(const Pts: TPath): TPath;
+var
+  I, HighI: Integer;
+begin
+  HighI := high(Pts);
+  SetLength(Result, HighI +1);
+  for I := 0 to HighI do
+    Result[I] := Pts[HighI - I];
+end;
+//------------------------------------------------------------------------------
+
+function ReversePaths(const Pts: TPaths): TPaths;
+var
+  I, J, highJ: Integer;
+begin
+  I := length(Pts);
+  SetLength(Result, I);
+  for I := 0 to I -1 do
+  begin
+    highJ := high(Pts[I]);
+    SetLength(Result[I], highJ+1);
+    for J := 0 to highJ do
+      Result[I][J] := Pts[I][highJ - J];
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function GetBounds(const polys: TPaths): TIntRect;
+var
+  I,J,Len: Integer;
+begin
+  Len := Length(polys);
+  I := 0;
+  while (I < Len) and (Length(polys[I]) = 0) do inc(I);
+  if (I = Len) then
+  begin
+    with Result do begin Left := 0; Top := 0; Right := 0; Bottom := 0; end;
+    Exit;
+  end;
+  Result.Left := polys[I][0].X;
+  Result.Right := Result.Left;
+  Result.Top := polys[I][0].Y;
+  Result.Bottom := Result.Top;
+  for I := I to Len -1 do
+    for J := 0 to High(polys[I]) do
+    begin
+      if polys[I][J].X < Result.Left then Result.Left := polys[I][J].X
+      else if polys[I][J].X > Result.Right then Result.Right := polys[I][J].X;
+      if polys[I][J].Y < Result.Top then Result.Top := polys[I][J].Y
+      else if polys[I][J].Y > Result.Bottom then Result.Bottom := polys[I][J].Y;
+    end;
+end;
+//------------------------------------------------------------------------------
+
+function PointInPolygon (const pt: TIntPoint; const poly: TPath): Integer;
+var
+  i, cnt: Integer;
+  d, d2, d3: double; //use cInt ???
+  ip, ipNext: TIntPoint;
+begin
+  //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
+  //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
+  //nb: if poly bounds are known, test them first before calling this function.
+  result := 0;
+  cnt := Length(poly);
+  if cnt < 3 then Exit;
+  ip := poly[0];
+  for i := 1 to cnt do
+  begin
+    if i < cnt then ipNext := poly[i]
+    else ipNext := poly[0];
+
+    if (ipNext.Y = pt.Y) then
+    begin
+      if (ipNext.X = pt.X) or ((ip.Y = pt.Y) and
+        ((ipNext.X > pt.X) = (ip.X < pt.X))) then
+      begin
+        result := -1;
+        Exit;
+      end;
+    end;
+
+    if ((ip.Y < pt.Y) <> (ipNext.Y < pt.Y)) then
+    begin
+      if (ip.X >= pt.X) then
+      begin
+        if (ipNext.X > pt.X) then
+          result := 1 - result
+        else
+        begin
+          d2 := (ip.X - pt.X);
+          d3 := (ipNext.X - pt.X);
+          d := d2 * (ipNext.Y - pt.Y) - d3 * (ip.Y - pt.Y);
+          if (d = 0) then begin result := -1; Exit; end;
+          if ((d > 0) = (ipNext.Y > ip.Y)) then
+            result := 1 - result;
+        end;
+      end else
+      begin
+        if (ipNext.X > pt.X) then
+        begin
+          d2 := (ip.X - pt.X);
+          d3 := (ipNext.X - pt.X);
+          d := d2 * (ipNext.Y - pt.Y) - d3 * (ip.Y - pt.Y);
+          if (d = 0) then begin result := -1; Exit; end;
+          if ((d > 0) = (ipNext.Y > ip.Y)) then
+            result := 1 - result;
+        end;
+      end;
+    end;
+    ip := ipNext;
+  end;
+end;
+//---------------------------------------------------------------------------
+
+//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
+//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
+function PointInPolygon (const pt: TIntPoint; ops: POutPt): Integer; overload;
+var
+  d, d2, d3: double; //nb: double not cInt avoids potential overflow errors
+  opStart: POutPt;
+  pt1, ptN: TIntPoint;
+begin
+  //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
+  result := 0;
+  opStart := ops;
+  pt1.X := ops.Pt.X; pt1.Y := ops.Pt.Y;
+  repeat
+    ops := ops.Next;
+    ptN.X := ops.Pt.X; ptN.Y := ops.Pt.Y;
+
+    if (ptN.Y = pt.Y) then
+    begin
+      if (ptN.X = pt.X) or ((pt1.Y = pt.Y) and
+        ((ptN.X > pt.X) = (pt1.X < pt.X))) then
+      begin
+        result := -1;
+        Exit;
+      end;
+    end;
+
+    if ((pt1.Y < pt.Y) <> (ptN.Y < pt.Y)) then
+    begin
+      if (pt1.X >= pt.X) then
+      begin
+        if (ptN.X > pt.X) then
+          result := 1 - result
+        else
+        begin
+          d2 := (pt1.X - pt.X);
+          d3 := (ptN.X - pt.X);
+          d := d2 * (ptN.Y - pt.Y) - d3 * (pt1.Y - pt.Y);
+          if (d = 0) then begin result := -1; Exit; end;
+          if ((d > 0) = (ptN.Y > pt1.Y)) then
+            result := 1 - result;
+        end;
+      end else
+      begin
+        if (ptN.X > pt.X) then
+        begin
+          d2 := (pt1.X - pt.X);
+          d3 := (ptN.X - pt.X);
+          d := d2 * (ptN.Y - pt.Y) - d3 * (pt1.Y - pt.Y);
+          if (d = 0) then begin result := -1; Exit; end;
+          if ((d > 0) = (ptN.Y > pt1.Y)) then
+            result := 1 - result;
+        end;
+      end;
+    end;
+    pt1 := ptN;
+  until ops = opStart;
+end;
+//---------------------------------------------------------------------------
+
+function Poly2ContainsPoly1(OutPt1, OutPt2: POutPt): Boolean;
+var
+  res: integer;
+  op: POutPt;
+begin
+  op := OutPt1;
+  repeat
+    //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon
+    res := PointInPolygon(op.Pt, OutPt2);
+    if (res >= 0) then
+    begin
+      Result := res > 0;
+      Exit;
+    end;
+    op := op.Next;
+  until op = OutPt1;
+  Result := true; //all points on line => result = true
+end;
+//---------------------------------------------------------------------------
+
+function SlopesEqual(E1, E2: PEdge;
+  UseFullInt64Range: Boolean): Boolean; overload;
+begin
+{$IFNDEF use_int32}
+  if UseFullInt64Range then
+    Result := Int128Equal(Int128Mul(E1.Delta.Y, E2.Delta.X),
+      Int128Mul(E1.Delta.X, E2.Delta.Y))
+  else
+{$ENDIF}
+    Result := E1.Delta.Y * E2.Delta.X = E1.Delta.X * E2.Delta.Y;
+end;
+//---------------------------------------------------------------------------
+
+function SlopesEqual(const Pt1, Pt2, Pt3: TIntPoint;
+  UseFullInt64Range: Boolean): Boolean; overload;
+begin
+{$IFNDEF use_int32}
+  if UseFullInt64Range then
+    Result := Int128Equal(
+      Int128Mul(Pt1.Y-Pt2.Y, Pt2.X-Pt3.X), Int128Mul(Pt1.X-Pt2.X, Pt2.Y-Pt3.Y))
+  else
+{$ENDIF}
+    Result := (Pt1.Y-Pt2.Y)*(Pt2.X-Pt3.X) = (Pt1.X-Pt2.X)*(Pt2.Y-Pt3.Y);
+end;
+//---------------------------------------------------------------------------
+
+(*****************************************************************************
+*  Dx:                  0(90�)                       Slope:   0  = Dx: -inf  *
+*                       |                            Slope: 0.5  = Dx:   -2  *
+*      +inf (180�) <--- o ---> -inf (0�)             Slope: 2.0  = Dx: -0.5  *
+*                                                    Slope: inf  = Dx:    0  *
+*****************************************************************************)
+
+function GetDx(const Pt1, Pt2: TIntPoint): Double;
+begin
+  if (Pt1.Y = Pt2.Y) then Result := Horizontal
+  else Result := (Pt2.X - Pt1.X)/(Pt2.Y - Pt1.Y);
+end;
+//---------------------------------------------------------------------------
+
+procedure SetDx(E: PEdge); {$IFDEF INLINING} inline; {$ENDIF}
+begin
+  E.Delta.X := (E.Top.X - E.Bot.X);
+  E.Delta.Y := (E.Top.Y - E.Bot.Y);
+  if E.Delta.Y = 0 then E.Dx := Horizontal
+  else E.Dx := E.Delta.X/E.Delta.Y;
+end;
+//---------------------------------------------------------------------------
+
+procedure Swap(var val1, val2: cInt); {$IFDEF INLINING} inline; {$ENDIF}
+var
+  tmp: cInt;
+begin
+  tmp := val1;
+  val1 := val2;
+  val2 := tmp;
+end;
+//---------------------------------------------------------------------------
+
+procedure SwapSides(Edge1, Edge2: PEdge);
+var
+  Side: TEdgeSide;
+begin
+  Side :=  Edge1.Side;
+  Edge1.Side := Edge2.Side;
+  Edge2.Side := Side;
+end;
+//------------------------------------------------------------------------------
+
+procedure SwapPolyIndexes(Edge1, Edge2: PEdge);
+var
+  OutIdx: Integer;
+begin
+  OutIdx :=  Edge1.OutIdx;
+  Edge1.OutIdx := Edge2.OutIdx;
+  Edge2.OutIdx := OutIdx;
+end;
+//------------------------------------------------------------------------------
+
+function TopX(Edge: PEdge; const currentY: cInt): cInt;
+begin
+  if currentY = Edge.Top.Y then Result := Edge.Top.X
+  else if Edge.Top.X = Edge.Bot.X then Result := Edge.Bot.X
+  else Result := Edge.Bot.X + round(Edge.Dx*(currentY - Edge.Bot.Y));
+end;
+//------------------------------------------------------------------------------
+
+{$IFDEF use_xyz}
+Procedure SetZ(var Pt: TIntPoint; E1, E2: PEdge; ZFillFunc: TZFillCallback);
+begin
+  if (Pt.Z <> 0) or not assigned(ZFillFunc) then Exit
+  else if PointsEqual(Pt, E1.Bot) then Pt.Z := E1.Bot.Z
+  else if PointsEqual(Pt, E2.Bot) then Pt.Z := E2.Bot.Z
+  else if PointsEqual(Pt, E1.Top) then Pt.Z := E1.Top.Z
+  else if PointsEqual(Pt, E2.Top) then Pt.Z := E2.Top.Z
+  else ZFillFunc(E1.Bot, E1.Top, E2.Bot, E2.Top, Pt);
+end;
+//------------------------------------------------------------------------------
+{$ENDIF}
+
+procedure IntersectPoint(Edge1, Edge2: PEdge; out ip: TIntPoint);
+var
+  B1,B2,M: Double;
+begin
+{$IFDEF use_xyz}
+  ip.Z := 0;
+{$ENDIF}
+  if (edge1.Dx = edge2.Dx) then
+  begin
+    ip.Y := edge1.Curr.Y;
+    ip.X := TopX(edge1, ip.Y);
+    Exit;
+  end;
+
+  if Edge1.Delta.X = 0 then
+  begin
+    ip.X := Edge1.Bot.X;
+    if Edge2.Dx = Horizontal then
+      ip.Y := Edge2.Bot.Y
+    else
+    begin
+      with Edge2^ do B2 := Bot.Y - (Bot.X/Dx);
+      ip.Y := round(ip.X/Edge2.Dx + B2);
+    end;
+  end
+  else if Edge2.Delta.X = 0 then
+  begin
+    ip.X := Edge2.Bot.X;
+    if Edge1.Dx = Horizontal then
+      ip.Y := Edge1.Bot.Y
+    else
+    begin
+      with Edge1^ do B1 := Bot.Y - (Bot.X/Dx);
+      ip.Y := round(ip.X/Edge1.Dx + B1);
+    end;
+  end else
+  begin
+    with Edge1^ do B1 := Bot.X - Bot.Y * Dx;
+    with Edge2^ do B2 := Bot.X - Bot.Y * Dx;
+    M := (B2-B1)/(Edge1.Dx - Edge2.Dx);
+    ip.Y := round(M);
+    if Abs(Edge1.Dx) < Abs(Edge2.Dx) then
+      ip.X := round(Edge1.Dx * M + B1)
+    else
+      ip.X := round(Edge2.Dx * M + B2);
+  end;
+
+  //The precondition - E.Curr.X > eNext.Curr.X - indicates that the two edges do
+  //intersect below TopY (and hence below the tops of either Edge). However,
+  //when edges are almost parallel, rounding errors may cause False positives -
+  //indicating intersections when there really aren't any. Also, floating point
+  //imprecision can incorrectly place an intersect point beyond/above an Edge.
+  //Therfore, further adjustment to IP is warranted ...
+  if (ip.Y < Edge1.Top.Y) or (ip.Y < Edge2.Top.Y) then
+  begin
+    //Find the lower top of the two edges and compare X's at this Y.
+    //If Edge1's X is greater than Edge2's X then it's fair to assume an
+    //intersection really has occurred...
+    if (Edge1.Top.Y > Edge2.Top.Y) then
+      ip.Y := edge1.Top.Y else
+      ip.Y := edge2.Top.Y;
+    if Abs(Edge1.Dx) < Abs(Edge2.Dx) then
+      ip.X := TopX(Edge1, ip.Y) else
+      ip.X := TopX(Edge2, ip.Y);
+  end;
+  //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ...
+  if (ip.Y > Edge1.Curr.Y) then
+  begin
+    ip.Y := Edge1.Curr.Y;
+    if (abs(Edge1.Dx) > abs(Edge2.Dx)) then //ie use more vertical edge
+      ip.X := TopX(Edge2, ip.Y) else
+      ip.X := TopX(Edge1, ip.Y);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure ReversePolyPtLinks(PP: POutPt);
+var
+  Pp1,Pp2: POutPt;
+begin
+  if not Assigned(PP) then Exit;
+  Pp1 := PP;
+  repeat
+    Pp2:= Pp1.Next;
+    Pp1.Next := Pp1.Prev;
+    Pp1.Prev := Pp2;
+    Pp1 := Pp2;
+  until Pp1 = PP;
+end;
+//------------------------------------------------------------------------------
+
+function Pt2IsBetweenPt1AndPt3(const Pt1, Pt2, Pt3: TIntPoint): Boolean;
+begin
+  //nb: assumes collinearity.
+  if PointsEqual(Pt1, Pt3) or PointsEqual(Pt1, Pt2) or PointsEqual(Pt3, Pt2) then
+    Result := False
+  else if (Pt1.X <> Pt3.X) then
+    Result := (Pt2.X > Pt1.X) = (Pt2.X < Pt3.X)
+  else
+    Result := (Pt2.Y > Pt1.Y) = (Pt2.Y < Pt3.Y);
+end;
+//------------------------------------------------------------------------------
+
+function GetOverlap(const A1, A2, B1, B2: cInt; out Left, Right: cInt): Boolean;
+begin
+  if (A1 < A2) then
+  begin
+    if (B1 < B2) then begin Left := Max(A1,B1); Right := Min(A2,B2); end
+    else begin Left := Max(A1,B2); Right := Min(A2,B1); end;
+  end else
+  begin
+    if (B1 < B2) then begin Left := Max(A2,B1); Right := Min(A1,B2); end
+    else begin Left := Max(A2,B2); Right := Min(A1,B1); end
+  end;
+  Result := Left < Right;
+end;
+//------------------------------------------------------------------------------
+
+procedure UpdateOutPtIdxs(OutRec: POutRec);
+var
+  op: POutPt;
+begin
+  op := OutRec.Pts;
+  repeat
+    op.Idx := OutRec.Idx;
+    op := op.Prev;
+  until op = OutRec.Pts;
+end;
+//------------------------------------------------------------------------------
+
+procedure RangeTest(const Pt: TIntPoint; var Use64BitRange: Boolean);
+begin
+  if Use64BitRange then
+  begin
+    if (Pt.X > HiRange) or (Pt.Y > HiRange) or
+      (-Pt.X > HiRange) or (-Pt.Y > HiRange) then
+        raise exception.Create(rsInvalidInt);
+  end
+  else if (Pt.X > LoRange) or (Pt.Y > LoRange) or
+    (-Pt.X > LoRange) or (-Pt.Y > LoRange) then
+  begin
+    Use64BitRange := true;
+    RangeTest(Pt, Use64BitRange);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure ReverseHorizontal(E: PEdge);
+begin
+  //swap horizontal edges' top and bottom x's so they follow the natural
+  //progression of the bounds - ie so their xbots will align with the
+  //adjoining lower Edge. [Helpful in the ProcessHorizontal() method.]
+  Swap(E.Top.X, E.Bot.X);
+{$IFDEF use_xyz}
+  Swap(E.Top.Z, E.Bot.Z);
+{$ENDIF}
+end;
+//------------------------------------------------------------------------------
+
+procedure InitEdge(E, Next, Prev: PEdge;
+  const Pt: TIntPoint); {$IFDEF INLINING} inline; {$ENDIF}
+begin
+  E.Curr := Pt;
+  E.Next := Next;
+  E.Prev := Prev;
+  E.OutIdx := -1;
+end;
+//------------------------------------------------------------------------------
+
+procedure InitEdge2(E: PEdge; PolyType: TPolyType); {$IFDEF INLINING} inline; {$ENDIF}
+begin
+  if E.Curr.Y >= E.Next.Curr.Y then
+  begin
+    E.Bot := E.Curr;
+    E.Top := E.Next.Curr;
+  end else
+  begin
+    E.Top := E.Curr;
+    E.Bot := E.Next.Curr;
+  end;
+  SetDx(E);
+  E.PolyType := PolyType;
+end;
+//------------------------------------------------------------------------------
+
+function RemoveEdge(E: PEdge): PEdge; {$IFDEF INLINING} inline; {$ENDIF}
+begin
+  //removes E from double_linked_list (but without disposing from memory)
+  E.Prev.Next := E.Next;
+  E.Next.Prev := E.Prev;
+  Result := E.Next;
+  E.Prev := nil; //flag as removed (see ClipperBase.Clear)
+end;
+//------------------------------------------------------------------------------
+
+function FindNextLocMin(E: PEdge): PEdge; {$IFDEF INLINING} inline; {$ENDIF}
+var
+  E2: PEdge;
+begin
+  while True do
+  begin
+    while not PointsEqual(E.Bot, E.Prev.Bot) or
+      PointsEqual(E.Curr, E.Top) do E := E.Next;
+    if (E.Dx <> Horizontal) and (E.Prev.Dx <> Horizontal) then break;
+    while (E.Prev.Dx = Horizontal) do E := E.Prev;
+    E2 := E; //E2 == first horizontal
+    while (E.Dx = Horizontal) do E := E.Next;
+    if (E.Top.Y = E.Prev.Bot.Y) then Continue; //ie just an intermediate horz.
+    //E == first edge past horizontals
+    if E2.Prev.Bot.X < E.Bot.X then E := E2;
+    //E is first horizontal when CW and first past horizontals when CCW
+    break;
+  end;
+  Result := E;
+end;
+
+//------------------------------------------------------------------------------
+// TClipperBase methods ...
+//------------------------------------------------------------------------------
+
+constructor TClipperBase.Create;
+begin
+  inherited;
+  FEdgeList := TEgdeList.Create;
+  FLocMinList := TLocMinList.Create;
+  FCurrentLocMinIdx := 0;
+  FUse64BitRange := False; //ie default is False
+end;
+//------------------------------------------------------------------------------
+
+destructor TClipperBase.Destroy;
+begin
+  Clear;
+  FEdgeList.Free;
+  FLocMinList.Free;
+  inherited;
+end;
+//------------------------------------------------------------------------------
+
+function TClipperBase.ProcessBound(E: PEdge; NextIsForward: Boolean): PEdge;
+var
+  EStart, Horz: PEdge;
+  locMin: PLocalMinimum;
+begin
+  Result := E;
+  if (E.OutIdx = Skip) then
+  begin
+    //check if there are edges beyond the skip edge in the bound and if so
+    //create another LocMin and calling ProcessBound once more ...
+    if NextIsForward then
+    begin
+      while (E.Top.Y = E.Next.Bot.Y) do
+        E := E.Next;
+      //don't include top horizontals here ...
+      while (E <> Result) and (E.Dx = Horizontal) do E := E.Prev;
+    end else
+    begin
+      while (E.Top.Y = E.Prev.Bot.Y) do E := E.Prev;
+      while (E <> Result) and (E.Dx = Horizontal) do E := E.Next;
+    end;
+    if E = Result then
+    begin
+      if NextIsForward then Result := E.Next
+      else Result := E.Prev;
+    end else
+    begin
+      if NextIsForward then
+        E := Result.Next else
+        E := Result.Prev;
+      new(locMin);
+      locMin.Y := E.Bot.Y;
+      locMin.LeftBound := nil;
+      locMin.RightBound := E;
+      E.WindDelta := 0;
+      Result := ProcessBound(E, NextIsForward);
+      FLocMinList.Add(locMin);
+    end;
+    Exit;
+  end;
+
+  if (E.Dx = Horizontal) then
+  begin
+    //We need to be careful with open paths because this may not be a
+    //true local minima (ie E may be following a skip edge).
+    //Also, consecutive horz. edges may start heading left before going right.
+    if NextIsForward then EStart := E.Prev
+    else EStart := E.Next;
+    if (EStart.Dx = Horizontal) then
+    begin
+      if (EStart.Bot.X <> E.Bot.X) and (EStart.Top.X <> E.Bot.X) then
+        ReverseHorizontal(E);
+    end
+    else if (EStart.Bot.X <> E.Bot.X) then
+        ReverseHorizontal(E);
+  end;
+
+  EStart := E;
+  if NextIsForward then
+  begin
+    while (Result.Top.Y = Result.Next.Bot.Y) and (Result.Next.OutIdx <> Skip) do
+      Result := Result.Next;
+    if (Result.Dx = Horizontal) and (Result.Next.OutIdx <> Skip) then
+    begin
+      //nb: at the top of a bound, horizontals are added to the bound
+      //only when the preceding edge attaches to the horizontal's left vertex
+      //unless a Skip edge is encountered when that becomes the top divide
+      Horz := Result;
+      while (Horz.Prev.Dx = Horizontal) do Horz := Horz.Prev;
+      if (Horz.Prev.Top.X > Result.Next.Top.X) then Result := Horz.Prev;
+    end;
+    while (E <> Result) do
+    begin
+      e.NextInLML := e.Next;
+      if (E.Dx = Horizontal) and (e <> EStart) and
+        (E.Bot.X <> E.Prev.Top.X) then ReverseHorizontal(E);
+      E := E.Next;
+    end;
+    if (e <> EStart) and (E.Dx = Horizontal) and (E.Bot.X <> E.Prev.Top.X) then
+      ReverseHorizontal(E);
+    Result := Result.Next; //move to the edge just beyond current bound
+  end else
+  begin
+    while (Result.Top.Y = Result.Prev.Bot.Y) and (Result.Prev.OutIdx <> Skip) do
+      Result := Result.Prev;
+    if (Result.Dx = Horizontal) and (Result.Prev.OutIdx <> Skip) then
+    begin
+      Horz := Result;
+      while (Horz.Next.Dx = Horizontal) do Horz := Horz.Next;
+      if (Horz.Next.Top.X = Result.Prev.Top.X) or
+        (Horz.Next.Top.X > Result.Prev.Top.X) then Result := Horz.Next;
+    end;
+    while (E <> Result) do
+    begin
+      e.NextInLML := e.Prev;
+      if (e.Dx = Horizontal) and (e <> EStart) and
+        (E.Bot.X <> E.Next.Top.X) then ReverseHorizontal(E);
+      E := E.Prev;
+    end;
+    if (e <> EStart) and (E.Dx = Horizontal) and (E.Bot.X <> E.Next.Top.X) then
+      ReverseHorizontal(E);
+    Result := Result.Prev; //move to the edge just beyond current bound
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function TClipperBase.AddPath(const Path: TPath;
+  PolyType: TPolyType; Closed: Boolean): Boolean;
+var
+  I, HighI: Integer;
+  Edges: PEdgeArray;
+  E, E2, EMin, EStart, ELoopStop: PEdge;
+  IsFlat, leftBoundIsForward: Boolean;
+  locMin: PLocalMinimum;
+begin
+{$IFDEF use_lines}
+  if not Closed and (polyType = ptClip) then
+    raise exception.Create(rsOpenPath);
+{$ELSE}
+  if not Closed then raise exception.Create(rsOpenPath2);
+{$ENDIF}
+
+  Result := false;
+  IsFlat := true;
+
+  //1. Basic (first) edge initialization ...
+  HighI := High(Path);
+  if Closed then
+    while (HighI > 0) and PointsEqual(Path[HighI],Path[0]) do Dec(HighI);
+  while (HighI > 0) and PointsEqual(Path[HighI],Path[HighI -1]) do Dec(HighI);
+  if (Closed and (HighI < 2)) or (not Closed and (HighI < 1)) then Exit;
+
+  GetMem(Edges, sizeof(TEdge)*(HighI +1));
+  try
+    FillChar(Edges^, sizeof(TEdge)*(HighI +1), 0);
+    Edges[1].Curr := Path[1];
+    RangeTest(Path[0], FUse64BitRange);
+    RangeTest(Path[HighI], FUse64BitRange);
+    InitEdge(@Edges[0], @Edges[1], @Edges[HighI], Path[0]);
+    InitEdge(@Edges[HighI], @Edges[0], @Edges[HighI-1], Path[HighI]);
+    for I := HighI - 1 downto 1 do
+    begin
+      RangeTest(Path[I], FUse64BitRange);
+      InitEdge(@Edges[I], @Edges[I+1], @Edges[I-1], Path[I]);
+    end;
+  except
+    FreeMem(Edges);
+    raise; //Range test fails
+  end;
+  EStart := @Edges[0];
+
+  //2. Remove duplicate vertices, and (when closed) collinear edges ...
+  E := EStart;
+  ELoopStop := EStart;
+  while (E <> E.Next) do //ie in case loop reduces to a single vertex
+  begin
+    //allow matching start and end points when not Closed ...
+    if PointsEqual(E.Curr, E.Next.Curr) and
+      (Closed or (E.Next <> EStart)) then
+    begin
+      if E = EStart then EStart := E.Next;
+      E := RemoveEdge(E);
+      ELoopStop := E;
+      Continue;
+    end;
+    if (E.Prev = E.Next) then
+      Break //only two vertices
+    else if Closed and
+      SlopesEqual(E.Prev.Curr, E.Curr, E.Next.Curr, FUse64BitRange) and
+      (not FPreserveCollinear or
+      not Pt2IsBetweenPt1AndPt3(E.Prev.Curr, E.Curr, E.Next.Curr)) then
+    begin
+      //Collinear edges are allowed for open paths but in closed paths
+      //the default is to merge adjacent collinear edges into a single edge.
+      //However, if the PreserveCollinear property is enabled, only overlapping
+      //collinear edges (ie spikes) will be removed from closed paths.
+      if E = EStart then EStart := E.Next;
+      E := RemoveEdge(E);
+      E := E.Prev;
+      ELoopStop := E;
+      Continue;
+    end;
+    E := E.Next;
+    //todo - manage open paths which start and end at same point
+    if (E = eLoopStop) then Break;
+    if E = ELoopStop then Break;
+  end;
+
+  if (not Closed and (E = E.Next)) or (Closed and (E.Prev = E.Next)) then
+  begin
+    FreeMem(Edges);
+    Exit;
+  end;
+
+  if not Closed then
+  begin
+    FHasOpenPaths := true;
+    EStart.Prev.OutIdx := Skip;
+  end;
+
+  //3. Do second stage of edge initialization ...
+  E := EStart;
+  repeat
+    InitEdge2(E, polyType);
+    E := E.Next;
+    if IsFlat and (E.Curr.Y <> EStart.Curr.Y) then IsFlat := false;
+  until E = EStart;
+  //4. Finally, add edge bounds to LocalMinima list ...
+
+  //Totally flat paths must be handled differently when adding them
+  //to LocalMinima list to avoid endless loops etc ...
+  if (IsFlat) then
+  begin
+    if Closed then
+    begin
+      FreeMem(Edges);
+      Exit;
+    end;
+    new(locMin);
+    locMin.Y := E.Bot.Y;
+    locMin.LeftBound := nil;
+    locMin.RightBound := E;
+    locMin.RightBound.Side := esRight;
+    locMin.RightBound.WindDelta := 0;
+    while true do
+    begin
+      if E.Bot.X <> E.Prev.Top.X then ReverseHorizontal(E);
+      if E.Next.OutIdx = Skip then break;
+      E.NextInLML := E.Next;
+      E := E.Next;
+    end;
+    FLocMinList.Add(locMin);
+    Result := true;
+    FEdgeList.Add(Edges);
+    Exit;
+  end;
+
+  Result := true;
+  FEdgeList.Add(Edges);
+  EMin := nil;
+
+  //workaround to avoid an endless loop in the while loop below when
+  //open paths have matching start and end points ...
+  if PointsEqual(E.Prev.Bot, E.Prev.Top) then E := E.Next;
+
+  while true do
+  begin
+    E := FindNextLocMin(E);
+    if (E = EMin) then break
+    else if (EMin = nil) then EMin := E;
+
+    //E and E.Prev now share a local minima (left aligned if horizontal).
+    //Compare their slopes to find which starts which bound ...
+    new(locMin);
+    locMin.Y := E.Bot.Y;
+    if (E.Dx < E.Prev.Dx) then
+    begin
+      locMin.LeftBound := E.Prev;
+      locMin.RightBound := E; //can be horz when CW
+      leftBoundIsForward := false; //Q.nextInLML = Q.prev
+    end else
+    begin
+      locMin.LeftBound := E;
+      locMin.RightBound := E.Prev; //can be horz when CCW
+      leftBoundIsForward := true; //Q.nextInLML = Q.next
+    end;
+    locMin.LeftBound.Side := esLeft;
+    locMin.RightBound.Side := esRight;
+
+    if not Closed then locMin.LeftBound.WindDelta := 0
+    else if (locMin.LeftBound.Next = locMin.RightBound) then
+      locMin.LeftBound.WindDelta := -1
+    else locMin.LeftBound.WindDelta := 1;
+    locMin.RightBound.WindDelta := -locMin.LeftBound.WindDelta;
+
+    E := ProcessBound(locMin.LeftBound, leftBoundIsForward);
+    if E.OutIdx = Skip then E := ProcessBound(E, leftBoundIsForward);
+
+    E2 := ProcessBound(locMin.RightBound, not leftBoundIsForward);
+    if E2.OutIdx = Skip then E2 := ProcessBound(E2, not leftBoundIsForward);
+
+    if (locMin.LeftBound.OutIdx = Skip) then locMin.LeftBound := nil
+    else if (locMin.RightBound.OutIdx = Skip) then locMin.RightBound := nil;
+    FLocMinList.Add(locMin);
+
+    if not leftBoundIsForward then E := E2;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function TClipperBase.AddPaths(const Paths: TPaths;
+  PolyType: TPolyType; Closed: Boolean): Boolean;
+var
+  I: Integer;
+begin
+  Result := False;
+  for I := 0 to high(Paths) do
+    if AddPath(Paths[I], PolyType, Closed) then Result := True;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperBase.Clear;
+var
+  I: Integer;
+begin
+  DisposeLocalMinimaList;
+  //dispose of Edges ...
+  for I := 0 to FEdgeList.Count -1 do
+    FreeMem(PEdgeArray(fEdgeList[I]));
+  FEdgeList.Clear;
+
+  FUse64BitRange := False;
+  FHasOpenPaths := False;
+end;
+//------------------------------------------------------------------------------
+
+{$IFNDEF USEGENERICS}
+function LocMinListSort(item1, item2:Pointer): Integer;
+var
+  y: cInt;
+begin
+  y := PLocalMinimum(item2).Y - PLocalMinimum(item1).Y;
+  if y < 0 then result := -1
+  else if y > 0 then result := 1
+  else result := 0;
+end;
+{$ENDIF}
+
+//------------------------------------------------------------------------------
+
+procedure TClipperBase.Reset;
+var
+  i: Integer;
+  Lm: PLocalMinimum;
+begin
+  //Reset() allows various clipping operations to be executed
+  //multiple times on the same polygon sets.
+{$IFDEF USEGENERICS}
+    FLocMinList.Sort(TComparer<PLocalMinimum>.Construct(
+      function (const Item1, Item2 : PLocalMinimum) : integer
+      var
+        y: cInt;
+      begin
+        y := PLocalMinimum(item2).Y - PLocalMinimum(item1).Y;
+        if y < 0 then result := -1
+        else if y > 0 then result := 1
+        else result := 0;
+      end
+    ));
+{$ELSE}
+  FLocMinList.Sort(LocMinListSort);
+{$ENDIF}
+  for i := 0 to FLocMinList.Count -1 do
+  begin
+    Lm := PLocalMinimum(FLocMinList[i]);
+    //resets just the two (L & R) edges attached to each Local Minima ...
+    if assigned(Lm.LeftBound) then
+      with Lm.LeftBound^ do
+      begin
+        Curr := Bot;
+        Side := esLeft;
+        OutIdx := Unassigned;
+      end;
+    if assigned(Lm.RightBound) then
+      with Lm.RightBound^ do
+      begin
+        Curr := Bot;
+        Side := esRight;
+        OutIdx := Unassigned;
+      end;
+  end;
+  FCurrentLocMinIdx := 0;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperBase.DisposePolyPts(PP: POutPt);
+var
+  TmpPp: POutPt;
+begin
+  PP.Prev.Next := nil;
+  while Assigned(PP) do
+  begin
+    TmpPp := PP;
+    PP := PP.Next;
+    dispose(TmpPp);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperBase.DisposeLocalMinimaList;
+var
+  i: Integer;
+begin
+  for i := 0 to FLocMinList.Count -1 do
+    Dispose(PLocalMinimum(FLocMinList[i]));
+  FLocMinList.Clear;
+  FCurrentLocMinIdx := 0;
+end;
+
+//------------------------------------------------------------------------------
+// TClipper methods ...
+//------------------------------------------------------------------------------
+
+constructor TClipper.Create(InitOptions: TInitOptions = []);
+begin
+  inherited Create;
+  FJoinList := TJoinList.Create;
+  FGhostJoinList := TJoinList.Create;
+  FPolyOutList := TPolyOutList.Create;
+  FIntersectList := TIntersecList.Create;
+  if ioReverseSolution in InitOptions then
+    FReverseOutput := true;
+  if ioStrictlySimple in InitOptions then
+    FStrictSimple := true;
+  if ioPreserveCollinear in InitOptions then
+    FPreserveCollinear := true;
+end;
+//------------------------------------------------------------------------------
+
+destructor TClipper.Destroy;
+begin
+  inherited; //this must be first since inherited Destroy calls Clear.
+  DisposeScanbeamList;
+  FJoinList.Free;
+  FGhostJoinList.Free;
+  FPolyOutList.Free;
+  FIntersectList.Free;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.DisposeScanbeamList;
+var
+  SB: PScanbeam;
+begin
+  while Assigned(fScanbeam) do
+  begin
+    SB := FScanbeam.Next;
+    Dispose(fScanbeam);
+    FScanbeam := SB;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.Reset;
+var
+  i: Integer;
+begin
+  inherited Reset;
+  FScanbeam := nil;
+  for i := 0 to FLocMinList.Count -1 do
+    InsertScanbeam(PLocalMinimum(FLocMinList[i]).Y);
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.Execute(clipType: TClipType;
+  out solution: TPaths;
+  FillType: TPolyFillType = pftEvenOdd): Boolean;
+begin
+  Result := Execute(clipType, solution, FillType, FillType);
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.Execute(clipType: TClipType;
+  out solution: TPaths;
+  subjFillType: TPolyFillType; clipFillType: TPolyFillType): Boolean;
+begin
+  Result := False;
+  solution := nil;
+  if FExecuteLocked then Exit;
+  //nb: Open paths can only be returned via the PolyTree structure ...
+  if HasOpenPaths then raise Exception.Create(rsOpenPath3);
+  try try
+    FExecuteLocked := True;
+    FSubjFillType := subjFillType;
+    FClipFillType := clipFillType;
+    FClipType := clipType;
+    FUsingPolyTree := False;
+    Result := ExecuteInternal;
+    solution := BuildResult;
+  except
+    solution := nil;
+    Result := False;
+  end;
+  finally
+    DisposeAllOutRecs;
+    FExecuteLocked := False;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.Execute(clipType: TClipType;
+  out PolyTree: TPolyTree;
+  FillType: TPolyFillType = pftEvenOdd): Boolean;
+begin
+  Result := Execute(clipType, PolyTree, FillType, FillType);
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.Execute(clipType: TClipType;
+  out PolyTree: TPolyTree;
+  subjFillType: TPolyFillType;
+  clipFillType: TPolyFillType): Boolean;
+begin
+  Result := False;
+  if FExecuteLocked or not Assigned(PolyTree) then Exit;
+  try try
+    FExecuteLocked := True;
+    FSubjFillType := subjFillType;
+    FClipFillType := clipFillType;
+    FClipType := clipType;
+    FUsingPolyTree := True;
+    Result := ExecuteInternal and BuildResult2(PolyTree);
+  except
+    Result := False;
+  end;
+  finally
+    DisposeAllOutRecs;
+    FExecuteLocked := False;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.FixHoleLinkage(OutRec: POutRec);
+var
+  orfl: POutRec;
+begin
+  //skip if it's an outermost polygon or if FirstLeft
+  //already points to the outer/owner polygon ...
+  if not Assigned(OutRec.FirstLeft) or
+    ((OutRec.IsHole <> OutRec.FirstLeft.IsHole) and
+      Assigned(OutRec.FirstLeft.Pts)) then Exit;
+  orfl := OutRec.FirstLeft;
+  while Assigned(orfl) and
+    ((orfl.IsHole = OutRec.IsHole) or not Assigned(orfl.Pts)) do
+      orfl := orfl.FirstLeft;
+  OutRec.FirstLeft := orfl;
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.ExecuteInternal: Boolean;
+var
+  I: Integer;
+  OutRec: POutRec;
+  BotY, TopY: cInt;
+begin
+  try
+    Reset;
+    Result := Assigned(fScanbeam);
+    if not Result then Exit;
+
+    BotY := PopScanbeam;
+    repeat
+      InsertLocalMinimaIntoAEL(BotY);
+      ProcessHorizontals;
+      ClearGhostJoins;
+      if not assigned(FScanbeam) then Break;
+      TopY := PopScanbeam;
+      if not ProcessIntersections(TopY) then Exit;
+      ProcessEdgesAtTopOfScanbeam(TopY);
+      BotY := TopY;
+    until not assigned(FScanbeam) and (FCurrentLocMinIdx >= FLocMinList.Count);
+
+    //fix orientations ...
+    for I := 0 to FPolyOutList.Count -1 do
+    begin
+      OutRec := FPolyOutList[I];
+      if Assigned(OutRec.Pts) and not OutRec.IsOpen and
+        ((OutRec.IsHole xor FReverseOutput) = (Area(OutRec) > 0)) then
+          ReversePolyPtLinks(OutRec.Pts);
+    end;
+
+    if FJoinList.count > 0 then JoinCommonEdges;
+
+    //unfortunately FixupOutPolygon() must be done after JoinCommonEdges ...
+    for I := 0 to FPolyOutList.Count -1 do
+    begin
+      OutRec := FPolyOutList[I];
+      if not Assigned(OutRec.Pts) then continue;
+      if OutRec.IsOpen then
+        FixupOutPolyline(OutRec)
+      else
+        FixupOutPolygon(OutRec);
+    end;
+
+    if FStrictSimple then DoSimplePolygons;
+    Result := True;
+  finally
+    ClearJoins;
+    ClearGhostJoins;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.InsertScanbeam(const Y: cInt);
+var
+  newSb, sb: PScanbeam;
+begin
+  //single-linked list: sorted descending, ignoring dups.
+  new(newSb);
+  newSb.Y := Y;
+  if not Assigned(fScanbeam) then
+  begin
+    FScanbeam := newSb;
+    newSb.Next := nil;
+  end else if Y > FScanbeam.Y then
+  begin
+    newSb.Next := FScanbeam;
+    FScanbeam := newSb;
+  end else
+  begin
+    sb := FScanbeam;
+    while Assigned(sb.Next) and (Y <= sb.Next.Y) do sb := sb.Next;
+    if Y <> sb.Y then
+    begin
+      newSb.Next := sb.Next;
+      sb.Next := newSb;
+    end
+    else dispose(newSb);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.PopScanbeam: cInt;
+var
+  Sb: PScanbeam;
+begin
+  Result := FScanbeam.Y;
+  Sb := FScanbeam;
+  FScanbeam := FScanbeam.Next;
+  dispose(Sb);
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.InsertMaxima(const X: cInt);
+var
+  newMax, m: PMaxima;
+begin
+  //double-linked list: sorted ascending, ignoring dups.
+  new(newMax);
+  newMax.X := X;
+  if not Assigned(FMaxima) then
+  begin
+    FMaxima := newMax;
+    newMax.Next := nil;
+    newMax.Prev := nil;
+  end else if X < FMaxima.X then
+  begin
+    newMax.Next := FMaxima;
+    newMax.Prev := nil;
+    FMaxima.Prev := newMax;
+    FMaxima := newMax;
+  end else
+  begin
+    m := FMaxima;
+    while Assigned(m.Next) and (X >= m.Next.X) do m := m.Next;
+    if X <> m.X then
+    begin
+      //insert m1 between m2 and m2.Next ...
+      newMax.Next := m.Next;
+      newMax.Prev := m;
+      if assigned(m.Next) then m.Next.Prev := newMax;
+      m.Next := newMax;
+    end
+    else dispose(newMax);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.DisposeMaximaList;
+var
+  m: PMaxima;
+begin
+  while Assigned(FMaxima) do
+  begin
+    m := FMaxima.Next;
+    Dispose(FMaxima);
+    FMaxima := m;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.DisposeAllOutRecs;
+var
+  I: Integer;
+begin
+  for I := 0 to FPolyOutList.Count -1 do DisposeOutRec(I);
+  FPolyOutList.Clear;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.DisposeOutRec(Index: Integer);
+var
+  OutRec: POutRec;
+begin
+  OutRec := FPolyOutList[Index];
+  if Assigned(OutRec.Pts) then DisposePolyPts(OutRec.Pts);
+  Dispose(OutRec);
+  FPolyOutList[Index] := nil;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.SetWindingCount(Edge: PEdge);
+var
+  E, E2: PEdge;
+  Inside: Boolean;
+begin
+  E := Edge.PrevInAEL;
+  //find the Edge of the same PolyType that immediately preceeds 'Edge' in AEL
+  while Assigned(E) and ((E.PolyType <> Edge.PolyType) or (E.WindDelta = 0)) do
+    E := E.PrevInAEL;
+  if not Assigned(E) then
+  begin
+    if Edge.WindDelta = 0 then Edge.WindCnt := 1
+    else Edge.WindCnt := Edge.WindDelta;
+    Edge.WindCnt2 := 0;
+    E := FActiveEdges; //ie get ready to calc WindCnt2
+  end
+  else if (Edge.WindDelta = 0) and (FClipType <> ctUnion) then
+  begin
+    Edge.WindCnt := 1;
+    Edge.WindCnt2 := E.WindCnt2;
+    E := E.NextInAEL; //ie get ready to calc WindCnt2
+  end
+  else if IsEvenOddFillType(Edge) then
+  begin
+    //even-odd filling ...
+    if (Edge.WindDelta = 0) then  //if edge is part of a line
+    begin
+      //are we inside a subj polygon ...
+      Inside := true;
+      E2 := E.PrevInAEL;
+      while assigned(E2) do
+      begin
+        if (E2.PolyType = E.PolyType) and (E2.WindDelta <> 0) then
+          Inside := not Inside;
+        E2 := E2.PrevInAEL;
+      end;
+      if Inside then Edge.WindCnt := 0
+      else Edge.WindCnt := 1;
+    end
+    else //else a polygon
+    begin
+      Edge.WindCnt := Edge.WindDelta;
+    end;
+    Edge.WindCnt2 := E.WindCnt2;
+    E := E.NextInAEL; //ie get ready to calc WindCnt2
+  end else
+  begin
+    //NonZero, Positive, or Negative filling ...
+    if (E.WindCnt * E.WindDelta < 0) then
+    begin
+      //prev edge is 'decreasing' WindCount (WC) toward zero
+      //so we're outside the previous polygon ...
+      if (Abs(E.WindCnt) > 1) then
+      begin
+        //outside prev poly but still inside another.
+        //when reversing direction of prev poly use the same WC
+        if (E.WindDelta * Edge.WindDelta < 0) then
+          Edge.WindCnt := E.WindCnt
+        //otherwise continue to 'decrease' WC ...
+        else Edge.WindCnt := E.WindCnt + Edge.WindDelta;
+      end
+      else
+        //now outside all polys of same polytype so set own WC ...
+        if Edge.WindDelta = 0 then Edge.WindCnt := 1
+        else Edge.WindCnt := Edge.WindDelta;
+    end else
+    begin
+      //prev edge is 'increasing' WindCount (WC) away from zero
+      //so we're inside the previous polygon ...
+      if (Edge.WindDelta = 0) then
+      begin
+        if (E.WindCnt < 0) then Edge.WindCnt := E.WindCnt -1
+        else Edge.WindCnt := E.WindCnt +1;
+      end
+      //if wind direction is reversing prev then use same WC
+      else if (E.WindDelta * Edge.WindDelta < 0) then
+        Edge.WindCnt := E.WindCnt
+      //otherwise add to WC ...
+      else Edge.WindCnt := E.WindCnt + Edge.WindDelta;
+    end;
+    Edge.WindCnt2 := E.WindCnt2;
+    E := E.NextInAEL; //ie get ready to calc WindCnt2
+  end;
+
+  //update WindCnt2 ...
+  if IsEvenOddAltFillType(Edge) then
+  begin
+    //even-odd filling ...
+    while (E <> Edge) do
+    begin
+      if E.WindDelta = 0 then //do nothing (ie ignore lines)
+      else if Edge.WindCnt2 = 0 then Edge.WindCnt2 := 1
+      else Edge.WindCnt2 := 0;
+      E := E.NextInAEL;
+    end;
+  end else
+  begin
+    //NonZero, Positive, or Negative filling ...
+    while (E <> Edge) do
+    begin
+      Inc(Edge.WindCnt2, E.WindDelta);
+      E := E.NextInAEL;
+    end;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.IsEvenOddFillType(Edge: PEdge): Boolean;
+begin
+  if Edge.PolyType = ptSubject then
+    Result := FSubjFillType = pftEvenOdd else
+    Result := FClipFillType = pftEvenOdd;
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.IsEvenOddAltFillType(Edge: PEdge): Boolean;
+begin
+  if Edge.PolyType = ptSubject then
+    Result := FClipFillType = pftEvenOdd else
+    Result := FSubjFillType = pftEvenOdd;
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.IsContributing(Edge: PEdge): Boolean;
+var
+  Pft, Pft2: TPolyFillType;
+begin
+  if Edge.PolyType = ptSubject then
+  begin
+    Pft := FSubjFillType;
+    Pft2 := FClipFillType;
+  end else
+  begin
+    Pft := FClipFillType;
+    Pft2 := FSubjFillType
+  end;
+
+  case Pft of
+    pftEvenOdd: Result := (Edge.WindDelta <> 0) or (Edge.WindCnt = 1);
+    pftNonZero: Result := abs(Edge.WindCnt) = 1;
+    pftPositive: Result := (Edge.WindCnt = 1);
+    else Result := (Edge.WindCnt = -1);
+  end;
+  if not Result then Exit;
+
+  case FClipType of
+    ctIntersection:
+      case Pft2 of
+        pftEvenOdd, pftNonZero: Result := (Edge.WindCnt2 <> 0);
+        pftPositive: Result := (Edge.WindCnt2 > 0);
+        pftNegative: Result := (Edge.WindCnt2 < 0);
+      end;
+    ctUnion:
+      case Pft2 of
+        pftEvenOdd, pftNonZero: Result := (Edge.WindCnt2 = 0);
+        pftPositive: Result := (Edge.WindCnt2 <= 0);
+        pftNegative: Result := (Edge.WindCnt2 >= 0);
+      end;
+    ctDifference:
+      if Edge.PolyType = ptSubject then
+        case Pft2 of
+          pftEvenOdd, pftNonZero: Result := (Edge.WindCnt2 = 0);
+          pftPositive: Result := (Edge.WindCnt2 <= 0);
+          pftNegative: Result := (Edge.WindCnt2 >= 0);
+        end
+      else
+        case Pft2 of
+          pftEvenOdd, pftNonZero: Result := (Edge.WindCnt2 <> 0);
+          pftPositive: Result := (Edge.WindCnt2 > 0);
+          pftNegative: Result := (Edge.WindCnt2 < 0);
+        end;
+      ctXor:
+        if Edge.WindDelta = 0 then //XOr always contributing unless open
+          case Pft2 of
+            pftEvenOdd, pftNonZero: Result := (Edge.WindCnt2 = 0);
+            pftPositive: Result := (Edge.WindCnt2 <= 0);
+            pftNegative: Result := (Edge.WindCnt2 >= 0);
+          end;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.AddLocalMinPoly(E1, E2: PEdge; const Pt: TIntPoint): POutPt;
+var
+  E, prevE: PEdge;
+  OutPt: POutPt;
+begin
+  if (E2.Dx = Horizontal) or (E1.Dx > E2.Dx) then
+  begin
+    Result := AddOutPt(E1, Pt);
+    E2.OutIdx := E1.OutIdx;
+    E1.Side := esLeft;
+    E2.Side := esRight;
+    E := E1;
+    if E.PrevInAEL = E2 then
+      prevE := E2.PrevInAEL
+    else
+      prevE := E.PrevInAEL;
+  end else
+  begin
+    Result := AddOutPt(E2, Pt);
+    E1.OutIdx := E2.OutIdx;
+    E1.Side := esRight;
+    E2.Side := esLeft;
+
+    E := E2;
+    if E.PrevInAEL = E1 then
+      prevE := E1.PrevInAEL
+    else
+      prevE := E.PrevInAEL;
+  end;
+
+  if Assigned(prevE) and (prevE.OutIdx >= 0) and
+    (TopX(prevE, Pt.Y) = TopX(E, Pt.Y)) and
+    SlopesEqual(E, prevE, FUse64BitRange) and
+    (E.WindDelta <> 0) and (prevE.WindDelta <> 0) then
+  begin
+    OutPt := AddOutPt(prevE, Pt);
+    AddJoin(Result, OutPt, E.Top);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.AddLocalMaxPoly(E1, E2: PEdge; const Pt: TIntPoint);
+begin
+  AddOutPt(E1, Pt);
+  if E2.WindDelta = 0 then AddOutPt(E2, Pt);
+  if (E1.OutIdx = E2.OutIdx) then
+  begin
+    E1.OutIdx := Unassigned;
+    E2.OutIdx := Unassigned;
+  end
+  else if E1.OutIdx < E2.OutIdx then
+    AppendPolygon(E1, E2)
+  else
+    AppendPolygon(E2, E1);
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.AddEdgeToSEL(Edge: PEdge);
+begin
+  //SEL pointers in PEdge are reused to build a list of horizontal edges.
+  //However, we don't need to worry about order with horizontal Edge processing.
+  if not Assigned(FSortedEdges) then
+  begin
+    FSortedEdges := Edge;
+    Edge.PrevInSEL := nil;
+    Edge.NextInSEL := nil;
+  end else
+  begin
+    Edge.NextInSEL := FSortedEdges;
+    Edge.PrevInSEL := nil;
+    FSortedEdges.PrevInSEL := Edge;
+    FSortedEdges := Edge;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.CopyAELToSEL;
+var
+  E: PEdge;
+begin
+  E := FActiveEdges;
+  FSortedEdges := E;
+  while Assigned(E) do
+  begin
+    E.PrevInSEL := E.PrevInAEL;
+    E.NextInSEL := E.NextInAEL;
+    E := E.NextInAEL;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.AddJoin(Op1, Op2: POutPt; const OffPt: TIntPoint);
+var
+  Jr: PJoin;
+begin
+  new(Jr);
+  Jr.OutPt1 := Op1;
+  Jr.OutPt2 := Op2;
+  Jr.OffPt := OffPt;
+  FJoinList.add(Jr);
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.ClearJoins;
+var
+  I: Integer;
+begin
+  for I := 0 to FJoinList.count -1 do
+    Dispose(PJoin(fJoinList[I]));
+  FJoinList.Clear;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.AddGhostJoin(OutPt: POutPt; const OffPt: TIntPoint);
+var
+  Jr: PJoin;
+begin
+  //Ghost joins are used to find horizontal edges at the top of one scanbeam
+  //that coincide with horizontal edges at the bottom of the next. Ghost joins
+  //are converted to real joins when match ups occur.
+  new(Jr);
+  Jr.OutPt1 := OutPt;
+  Jr.OffPt := OffPt;
+  FGhostJoinList.Add(Jr);
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.ClearGhostJoins;
+var
+  I: Integer;
+begin
+  for I := 0 to FGhostJoinList.Count -1 do
+    Dispose(PJoin(FGhostJoinList[I]));
+  FGhostJoinList.Clear;
+end;
+//------------------------------------------------------------------------------
+
+procedure SwapPoints(var Pt1, Pt2: TIntPoint);
+var
+  Tmp: TIntPoint;
+begin
+  Tmp := Pt1;
+  Pt1 := Pt2;
+  Pt2 := Tmp;
+end;
+//------------------------------------------------------------------------------
+
+function HorzSegmentsOverlap(seg1a, seg1b, seg2a, seg2b: cInt): Boolean;
+begin
+  if (seg1a > seg1b) then Swap(seg1a, seg1b);
+  if (seg2a > seg2b) then Swap(seg2a, seg2b);
+  Result := (seg1a < seg2b) and (seg2a < seg1b);
+end;
+//------------------------------------------------------------------------------
+
+function E2InsertsBeforeE1(E1, E2: PEdge): Boolean;
+  {$IFDEF INLINING} inline; {$ENDIF}
+begin
+  if E2.Curr.X = E1.Curr.X then
+  begin
+    if E2.Top.Y > E1.Top.Y then
+      Result := E2.Top.X < TopX(E1, E2.Top.Y) else
+      Result := E1.Top.X > TopX(E2, E1.Top.Y);
+  end else
+    Result := E2.Curr.X < E1.Curr.X;
+end;
+//----------------------------------------------------------------------
+
+procedure TClipper.InsertLocalMinimaIntoAEL(const BotY: cInt);
+
+  procedure InsertEdgeIntoAEL(Edge, StartEdge: PEdge);
+  begin
+    if not Assigned(FActiveEdges) then
+    begin
+      Edge.PrevInAEL := nil;
+      Edge.NextInAEL := nil;
+      FActiveEdges := Edge;
+    end
+    else if not Assigned(StartEdge) and
+      E2InsertsBeforeE1(FActiveEdges, Edge) then
+    begin
+      Edge.PrevInAEL := nil;
+      Edge.NextInAEL := FActiveEdges;
+      FActiveEdges.PrevInAEL := Edge;
+      FActiveEdges := Edge;
+    end else
+    begin
+      if not Assigned(StartEdge) then StartEdge := FActiveEdges;
+      while Assigned(StartEdge.NextInAEL) and
+        not E2InsertsBeforeE1(StartEdge.NextInAEL, Edge) do
+          StartEdge := StartEdge.NextInAEL;
+      Edge.NextInAEL := StartEdge.NextInAEL;
+      if Assigned(StartEdge.NextInAEL) then
+        StartEdge.NextInAEL.PrevInAEL := Edge;
+      Edge.PrevInAEL := StartEdge;
+      StartEdge.NextInAEL := Edge;
+    end;
+  end;
+  //----------------------------------------------------------------------
+
+var
+  I: Integer;
+  E: PEdge;
+  Lb, Rb: PEdge;
+  Jr: PJoin;
+  Op1, Op2: POutPt;
+  LocMin: PLocalMinimum;
+begin
+
+  while (FCurrentLocMinIdx < FLocMinList.Count) do
+  begin
+    LocMin := PLocalMinimum(FLocMinList[FCurrentLocMinIdx]);
+    if (LocMin.Y <> BotY) then break;
+    inc(FCurrentLocMinIdx);
+
+    Lb := LocMin.LeftBound;
+    Rb := LocMin.RightBound;
+    Op1 := nil;
+    if not assigned(Lb) then
+    begin
+      InsertEdgeIntoAEL(Rb, nil);
+      SetWindingCount(Rb);
+      if IsContributing(Rb) then
+        Op1 := AddOutPt(Rb, Rb.Bot);
+    end
+    else if not assigned(Rb) then
+    begin
+      InsertEdgeIntoAEL(Lb, nil);
+      SetWindingCount(Lb);
+      if IsContributing(Lb) then
+        Op1 := AddOutPt(Lb, Lb.Bot);
+      InsertScanbeam(Lb.Top.Y);
+    end else
+    begin
+      InsertEdgeIntoAEL(Lb, nil);
+      InsertEdgeIntoAEL(Rb, Lb);
+      SetWindingCount(Lb);
+      Rb.WindCnt := Lb.WindCnt;
+      Rb.WindCnt2 := Lb.WindCnt2;
+      if IsContributing(Lb) then
+        Op1 := AddLocalMinPoly(Lb, Rb, Lb.Bot);
+      InsertScanbeam(Lb.Top.Y);
+    end;
+
+    if Assigned(Rb) then
+    begin
+      if (Rb.Dx = Horizontal) then
+        AddEdgeToSEL(Rb) else
+        InsertScanbeam(Rb.Top.Y);
+    end;
+
+    if not assigned(Lb) or not assigned(Rb) then Continue;
+
+    //if output polygons share an Edge with rb, they'll need joining later ...
+    if assigned(Op1) and (Rb.Dx = Horizontal) and
+      (FGhostJoinList.Count > 0) and (Rb.WindDelta <> 0) then
+    begin
+      for I := 0 to FGhostJoinList.Count -1 do
+      begin
+        //if the horizontal Rb and a 'ghost' horizontal overlap, then convert
+        //the 'ghost' join to a real join ready for later ...
+        Jr := PJoin(FGhostJoinList[I]);
+        if HorzSegmentsOverlap(Jr.OutPt1.Pt.X, Jr.OffPt.X,
+          Rb.Bot.X, Rb.Top.X) then
+            AddJoin(Jr.OutPt1, Op1, Jr.OffPt);
+      end;
+    end;
+
+    if (Lb.OutIdx >= 0) and assigned(Lb.PrevInAEL) and
+      (Lb.PrevInAEL.Curr.X = Lb.Bot.X) and
+      (Lb.PrevInAEL.OutIdx >= 0) and
+      SlopesEqual(Lb.PrevInAEL, Lb, FUse64BitRange) and
+      (Lb.WindDelta <> 0) and (Lb.PrevInAEL.WindDelta <> 0) then
+    begin
+        Op2 := AddOutPt(Lb.PrevInAEL, Lb.Bot);
+        AddJoin(Op1, Op2, Lb.Top);
+    end;
+
+    if (Lb.NextInAEL <> Rb) then
+    begin
+      if (Rb.OutIdx >= 0) and (Rb.PrevInAEL.OutIdx >= 0) and
+        SlopesEqual(Rb.PrevInAEL, Rb, FUse64BitRange) and
+        (Rb.WindDelta <> 0) and (Rb.PrevInAEL.WindDelta <> 0) then
+      begin
+          Op2 := AddOutPt(Rb.PrevInAEL, Rb.Bot);
+          AddJoin(Op1, Op2, Rb.Top);
+      end;
+
+      E := Lb.NextInAEL;
+      if Assigned(E) then
+        while (E <> Rb) do
+        begin
+          //nb: For calculating winding counts etc, IntersectEdges() assumes
+          //that param1 will be to the right of param2 ABOVE the intersection ...
+          IntersectEdges(Rb, E, Lb.Curr);
+          E := E.NextInAEL;
+        end;
+    end;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.DeleteFromAEL(E: PEdge);
+var
+  AelPrev, AelNext: PEdge;
+begin
+  AelPrev := E.PrevInAEL;
+  AelNext := E.NextInAEL;
+  if not Assigned(AelPrev) and not Assigned(AelNext) and
+    (E <> FActiveEdges) then Exit; //already deleted
+  if Assigned(AelPrev) then AelPrev.NextInAEL := AelNext
+  else FActiveEdges := AelNext;
+  if Assigned(AelNext) then AelNext.PrevInAEL := AelPrev;
+  E.NextInAEL := nil;
+  E.PrevInAEL := nil;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.DeleteFromSEL(E: PEdge);
+var
+  SelPrev, SelNext: PEdge;
+begin
+  SelPrev := E.PrevInSEL;
+  SelNext := E.NextInSEL;
+  if not Assigned(SelPrev) and not Assigned(SelNext) and
+    (E <> FSortedEdges) then Exit; //already deleted
+  if Assigned(SelPrev) then SelPrev.NextInSEL := SelNext
+  else FSortedEdges := SelNext;
+  if Assigned(SelNext) then SelNext.PrevInSEL := SelPrev;
+  E.NextInSEL := nil;
+  E.PrevInSEL := nil;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.IntersectEdges(E1,E2: PEdge; Pt: TIntPoint);
+var
+  E1Contributing, E2contributing: Boolean;
+  E1FillType, E2FillType, E1FillType2, E2FillType2: TPolyFillType;
+  E1Wc, E2Wc, E1Wc2, E2Wc2: Integer;
+begin
+  {IntersectEdges}
+  //E1 will be to the left of E2 BELOW the intersection. Therefore E1 is before
+  //E2 in AEL except when E1 is being inserted at the intersection point ...
+
+  E1Contributing := (E1.OutIdx >= 0);
+  E2contributing := (E2.OutIdx >= 0);
+
+{$IFDEF use_xyz}
+        SetZ(Pt, E1, E2, FZFillCallback);
+{$ENDIF}
+
+{$IFDEF use_lines}
+  //if either edge is on an OPEN path ...
+  if (E1.WindDelta = 0) or (E2.WindDelta = 0) then
+  begin
+    //ignore subject-subject open path intersections ...
+    if (E1.WindDelta = 0) AND (E2.WindDelta = 0) then Exit
+    //if intersecting a subj line with a subj poly ...
+    else if (E1.PolyType = E2.PolyType) and
+      (E1.WindDelta <> E2.WindDelta) and (FClipType = ctUnion) then
+    begin
+      if (E1.WindDelta = 0) then
+      begin
+        if (E2Contributing) then
+        begin
+          AddOutPt(E1, pt);
+          if (E1Contributing) then E1.OutIdx := Unassigned;
+        end;
+      end else
+      begin
+        if (E1Contributing) then
+        begin
+          AddOutPt(E2, pt);
+          if (E2Contributing) then E2.OutIdx := Unassigned;
+        end;
+      end;
+    end
+    else if (E1.PolyType <> E2.PolyType) then
+    begin
+      //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) = 1 ...
+      if (E1.WindDelta = 0) and (Abs(E2.WindCnt) = 1) and
+       ((FClipType <> ctUnion) or (E2.WindCnt2 = 0)) then
+      begin
+        AddOutPt(E1, Pt);
+        if E1Contributing then E1.OutIdx := Unassigned;
+      end
+      else if (E2.WindDelta = 0) and (Abs(E1.WindCnt) = 1) and
+       ((FClipType <> ctUnion) or (E1.WindCnt2 = 0)) then
+      begin
+        AddOutPt(E2, Pt);
+        if E2Contributing then E2.OutIdx := Unassigned;
+      end
+    end;
+    Exit;
+  end;
+{$ENDIF}
+
+  //update winding counts...
+  //assumes that E1 will be to the right of E2 ABOVE the intersection
+  if E1.PolyType = E2.PolyType then
+  begin
+    if IsEvenOddFillType(E1) then
+    begin
+      E1Wc := E1.WindCnt;
+      E1.WindCnt := E2.WindCnt;
+      E2.WindCnt := E1Wc;
+    end else
+    begin
+      if E1.WindCnt + E2.WindDelta = 0 then
+        E1.WindCnt := -E1.WindCnt else
+        Inc(E1.WindCnt, E2.WindDelta);
+      if E2.WindCnt - E1.WindDelta = 0 then
+        E2.WindCnt := -E2.WindCnt else
+        Dec(E2.WindCnt, E1.WindDelta);
+    end;
+  end else
+  begin
+    if not IsEvenOddFillType(E2) then Inc(E1.WindCnt2, E2.WindDelta)
+    else if E1.WindCnt2 = 0 then E1.WindCnt2 := 1
+    else E1.WindCnt2 := 0;
+
+    if not IsEvenOddFillType(E1) then Dec(E2.WindCnt2, E1.WindDelta)
+    else if E2.WindCnt2 = 0 then E2.WindCnt2 := 1
+    else E2.WindCnt2 := 0;
+  end;
+
+  if E1.PolyType = ptSubject then
+  begin
+    E1FillType := FSubjFillType;
+    E1FillType2 := FClipFillType;
+  end else
+  begin
+    E1FillType := FClipFillType;
+    E1FillType2 := FSubjFillType;
+  end;
+  if E2.PolyType = ptSubject then
+  begin
+    E2FillType := FSubjFillType;
+    E2FillType2 := FClipFillType;
+  end else
+  begin
+    E2FillType := FClipFillType;
+    E2FillType2 := FSubjFillType;
+  end;
+
+  case E1FillType of
+    pftPositive: E1Wc := E1.WindCnt;
+    pftNegative : E1Wc := -E1.WindCnt;
+    else E1Wc := abs(E1.WindCnt);
+  end;
+  case E2FillType of
+    pftPositive: E2Wc := E2.WindCnt;
+    pftNegative : E2Wc := -E2.WindCnt;
+    else E2Wc := abs(E2.WindCnt);
+  end;
+
+  if E1Contributing and E2contributing then
+  begin
+    if not (E1Wc in [0,1]) or not (E2Wc in [0,1]) or
+      ((E1.PolyType <> E2.PolyType) and (fClipType <> ctXor)) then
+    begin
+        AddLocalMaxPoly(E1, E2, Pt);
+    end else
+    begin
+      AddOutPt(E1, Pt);
+      AddOutPt(E2, Pt);
+      SwapSides(E1, E2);
+      SwapPolyIndexes(E1, E2);
+    end;
+  end else if E1Contributing then
+  begin
+    if (E2Wc = 0) or (E2Wc = 1) then
+    begin
+      AddOutPt(E1, Pt);
+      SwapSides(E1, E2);
+      SwapPolyIndexes(E1, E2);
+    end;
+  end
+  else if E2contributing then
+  begin
+    if (E1Wc = 0) or (E1Wc = 1) then
+    begin
+      AddOutPt(E2, Pt);
+      SwapSides(E1, E2);
+      SwapPolyIndexes(E1, E2);
+    end;
+  end
+  else if  ((E1Wc = 0) or (E1Wc = 1)) and ((E2Wc = 0) or (E2Wc = 1)) then
+  begin
+    //neither Edge is currently contributing ...
+
+    case E1FillType2 of
+      pftPositive: E1Wc2 := E1.WindCnt2;
+      pftNegative : E1Wc2 := -E1.WindCnt2;
+      else E1Wc2 := abs(E1.WindCnt2);
+    end;
+    case E2FillType2 of
+      pftPositive: E2Wc2 := E2.WindCnt2;
+      pftNegative : E2Wc2 := -E2.WindCnt2;
+      else E2Wc2 := abs(E2.WindCnt2);
+    end;
+
+    if (E1.PolyType <> E2.PolyType) then
+    begin
+      AddLocalMinPoly(E1, E2, Pt);
+    end
+    else if (E1Wc = 1) and (E2Wc = 1) then
+      case FClipType of
+        ctIntersection:
+          if (E1Wc2 > 0) and (E2Wc2 > 0) then
+            AddLocalMinPoly(E1, E2, Pt);
+        ctUnion:
+          if (E1Wc2 <= 0) and (E2Wc2 <= 0) then
+            AddLocalMinPoly(E1, E2, Pt);
+        ctDifference:
+          if ((E1.PolyType = ptClip) and (E1Wc2 > 0) and (E2Wc2 > 0)) or
+            ((E1.PolyType = ptSubject) and (E1Wc2 <= 0) and (E2Wc2 <= 0)) then
+              AddLocalMinPoly(E1, E2, Pt);
+        ctXor:
+          AddLocalMinPoly(E1, E2, Pt);
+      end
+    else
+      swapsides(E1,E2);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function FirstParamIsBottomPt(btmPt1, btmPt2: POutPt): Boolean;
+var
+  Dx1n, Dx1p, Dx2n, Dx2p: Double;
+  P: POutPt;
+begin
+  //Precondition: bottom-points share the same vertex.
+  //Use inverse slopes of adjacent edges (ie dx/dy) to determine the outer
+  //polygon and hence the 'real' bottompoint.
+  //nb: Slope is vertical when dx == 0. If the greater abs(dx) of param1
+  //is greater than or equal both abs(dx) in param2 then param1 is outer.
+  P := btmPt1.Prev;
+  while PointsEqual(P.Pt, btmPt1.Pt) and (P <> btmPt1) do P := P.Prev;
+  Dx1p := abs(GetDx(btmPt1.Pt, P.Pt));
+  P := btmPt1.Next;
+  while PointsEqual(P.Pt, btmPt1.Pt) and (P <> btmPt1) do P := P.Next;
+  Dx1n := abs(GetDx(btmPt1.Pt, P.Pt));
+
+  P := btmPt2.Prev;
+  while PointsEqual(P.Pt, btmPt2.Pt) and (P <> btmPt2) do P := P.Prev;
+  Dx2p := abs(GetDx(btmPt2.Pt, P.Pt));
+  P := btmPt2.Next;
+  while PointsEqual(P.Pt, btmPt2.Pt) and (P <> btmPt2) do P := P.Next;
+  Dx2n := abs(GetDx(btmPt2.Pt, P.Pt));
+  Result := ((Dx1p >= Dx2p) and (Dx1p >= Dx2n)) or
+    ((Dx1n >= Dx2p) and (Dx1n >= Dx2n));
+end;
+//------------------------------------------------------------------------------
+
+function GetBottomPt(PP: POutPt): POutPt;
+var
+  P, Dups: POutPt;
+begin
+  Dups := nil;
+  P := PP.Next;
+  while P <> PP do
+  begin
+    if P.Pt.Y > PP.Pt.Y then
+    begin
+      PP := P;
+      Dups := nil;
+    end
+    else if (P.Pt.Y = PP.Pt.Y) and (P.Pt.X <= PP.Pt.X) then
+    begin
+      if (P.Pt.X < PP.Pt.X) then
+      begin
+        Dups := nil;
+        PP := P;
+      end else
+      begin
+        if (P.Next <> PP) and (P.Prev <> PP) then Dups := P;
+      end;
+    end;
+    P := P.Next;
+  end;
+  if Assigned(Dups) then
+  begin
+    //there appears to be at least 2 vertices at bottom-most point so ...
+    while Dups <> P do
+    begin
+      if not FirstParamIsBottomPt(P, Dups) then PP := Dups;
+      Dups := Dups.Next;
+      while not PointsEqual(Dups.Pt, PP.Pt) do Dups := Dups.Next;
+    end;
+  end;
+  Result := PP;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.SetHoleState(E: PEdge; OutRec: POutRec);
+var
+  E2: PEdge;
+  IsHole: Boolean;
+begin
+  IsHole := False;
+  E2 := E.PrevInAEL;
+  while Assigned(E2) do
+  begin
+    if (E2.OutIdx >= 0) and (E2.WindDelta <> 0) then
+    begin
+      IsHole := not IsHole;
+      if not Assigned(OutRec.FirstLeft) then
+        OutRec.FirstLeft := POutRec(fPolyOutList[E2.OutIdx]);
+    end;
+    E2 := E2.PrevInAEL;
+  end;
+  if IsHole then
+    OutRec.IsHole := True;
+end;
+//------------------------------------------------------------------------------
+
+function GetLowermostRec(OutRec1, OutRec2: POutRec): POutRec;
+var
+  OutPt1, OutPt2: POutPt;
+begin
+  if not assigned(OutRec1.BottomPt) then
+    OutRec1.BottomPt := GetBottomPt(OutRec1.Pts);
+  if not assigned(OutRec2.BottomPt) then
+    OutRec2.BottomPt := GetBottomPt(OutRec2.Pts);
+  OutPt1 := OutRec1.BottomPt;
+  OutPt2 := OutRec2.BottomPt;
+  if (OutPt1.Pt.Y > OutPt2.Pt.Y) then Result := OutRec1
+  else if (OutPt1.Pt.Y < OutPt2.Pt.Y) then Result := OutRec2
+  else if (OutPt1.Pt.X < OutPt2.Pt.X) then Result := OutRec1
+  else if (OutPt1.Pt.X > OutPt2.Pt.X) then Result := OutRec2
+  else if (OutPt1.Next = OutPt1) then Result := OutRec2
+  else if (OutPt2.Next = OutPt2) then Result := OutRec1
+  else if FirstParamIsBottomPt(OutPt1, OutPt2) then Result := OutRec1
+  else Result := OutRec2;
+end;
+//------------------------------------------------------------------------------
+
+function Param1RightOfParam2(OutRec1, OutRec2: POutRec): Boolean;
+begin
+  Result := True;
+  repeat
+    OutRec1 := OutRec1.FirstLeft;
+    if OutRec1 = OutRec2 then Exit;
+  until not Assigned(OutRec1);
+  Result := False;
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.GetOutRec(Idx: integer): POutRec;
+begin
+  Result := FPolyOutList[Idx];
+  while Result <> FPolyOutList[Result.Idx] do
+    Result := FPolyOutList[Result.Idx];
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.AppendPolygon(E1, E2: PEdge);
+var
+  HoleStateRec, OutRec1, OutRec2: POutRec;
+  P1_lft, P1_rt, P2_lft, P2_rt: POutPt;
+  NewSide: TEdgeSide;
+  OKIdx, ObsoleteIdx: Integer;
+  E: PEdge;
+begin
+  OutRec1 := FPolyOutList[E1.OutIdx];
+  OutRec2 := FPolyOutList[E2.OutIdx];
+
+  //First work out which polygon fragment has the correct hole state.
+  //Since we're working from the bottom upward and left to right, the left most
+  //and lowermost polygon is outermost and must have the correct hole state ...
+  if Param1RightOfParam2(OutRec1, OutRec2) then HoleStateRec := OutRec2
+  else if Param1RightOfParam2(OutRec2, OutRec1) then HoleStateRec := OutRec1
+  else HoleStateRec := GetLowermostRec(OutRec1, OutRec2);
+
+  //get the start and ends of both output polygons and
+  //join E2 poly onto E1 poly and delete pointers to E2 ...
+
+  P1_lft := OutRec1.Pts;
+  P2_lft := OutRec2.Pts;
+  P1_rt := P1_lft.Prev;
+  P2_rt := P2_lft.Prev;
+
+  if E1.Side = esLeft then
+  begin
+    if E2.Side = esLeft then
+    begin
+      //z y x a b c
+      ReversePolyPtLinks(P2_lft);
+      P2_lft.Next := P1_lft;
+      P1_lft.Prev := P2_lft;
+      P1_rt.Next := P2_rt;
+      P2_rt.Prev := P1_rt;
+      OutRec1.Pts := P2_rt;
+    end else
+    begin
+      //x y z a b c
+      P2_rt.Next := P1_lft;
+      P1_lft.Prev := P2_rt;
+      P2_lft.Prev := P1_rt;
+      P1_rt.Next := P2_lft;
+      OutRec1.Pts := P2_lft;
+    end;
+    NewSide := esLeft;
+  end else
+  begin
+    if E2.Side = esRight then
+    begin
+      //a b c z y x
+      ReversePolyPtLinks(P2_lft);
+      P1_rt.Next := P2_rt;
+      P2_rt.Prev := P1_rt;
+      P2_lft.Next := P1_lft;
+      P1_lft.Prev := P2_lft;
+    end else
+    begin
+      //a b c x y z
+      P1_rt.Next := P2_lft;
+      P2_lft.Prev := P1_rt;
+      P1_lft.Prev := P2_rt;
+      P2_rt.Next := P1_lft;
+    end;
+    NewSide := esRight;
+  end;
+
+  OutRec1.BottomPt := nil;
+  if HoleStateRec = OutRec2 then
+  begin
+    if OutRec2.FirstLeft <> OutRec1 then
+      OutRec1.FirstLeft := OutRec2.FirstLeft;
+    OutRec1.IsHole := OutRec2.IsHole;
+  end;
+
+  OutRec2.Pts := nil;
+  OutRec2.BottomPt := nil;
+  OutRec2.FirstLeft := OutRec1;
+
+  OKIdx := OutRec1.Idx;
+  ObsoleteIdx := OutRec2.Idx;
+
+  E1.OutIdx := Unassigned; //safe because we only get here via AddLocalMaxPoly
+  E2.OutIdx := Unassigned;
+
+  E := FActiveEdges;
+  while Assigned(E) do
+  begin
+    if (E.OutIdx = ObsoleteIdx) then
+    begin
+      E.OutIdx := OKIdx;
+      E.Side := NewSide;
+      Break;
+    end;
+    E := E.NextInAEL;
+  end;
+
+  OutRec2.Idx := OutRec1.Idx;
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.CreateOutRec: POutRec;
+begin
+  new(Result);
+  Result.IsHole := False;
+  Result.IsOpen := False;
+  Result.FirstLeft := nil;
+  Result.Pts := nil;
+  Result.BottomPt := nil;
+  Result.PolyNode := nil;
+  Result.Idx := FPolyOutList.Add(Result);
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.AddOutPt(E: PEdge; const Pt: TIntPoint): POutPt;
+var
+  OutRec: POutRec;
+  PrevOp, Op: POutPt;
+  ToFront: Boolean;
+begin
+  if E.OutIdx < 0 then
+  begin
+    OutRec := CreateOutRec;
+    OutRec.IsOpen := (E.WindDelta = 0);
+    new(Result);
+    OutRec.Pts := Result;
+    Result.Pt := Pt;
+    Result.Next := Result;
+    Result.Prev := Result;
+    Result.Idx := OutRec.Idx;
+    if not OutRec.IsOpen then
+      SetHoleState(E, OutRec);
+    E.OutIdx := OutRec.Idx;
+  end else
+  begin
+    ToFront := E.Side = esLeft;
+    OutRec := FPolyOutList[E.OutIdx];
+    //OutRec.Pts is the 'left-most' point & OutRec.Pts.Prev is the 'right-most'
+    Op := OutRec.Pts;
+    if ToFront then PrevOp := Op else PrevOp := Op.Prev;
+    if PointsEqual(Pt, PrevOp.Pt) then
+    begin
+      Result := PrevOp;
+      Exit;
+    end;
+    new(Result);
+    Result.Pt := Pt;
+    Result.Idx := OutRec.Idx;
+    Result.Next := Op;
+    Result.Prev := Op.Prev;
+    Op.Prev.Next := Result;
+    Op.Prev := Result;
+    if ToFront then OutRec.Pts := Result;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.GetLastOutPt(E: PEdge): POutPt;
+var
+  OutRec: POutRec;
+begin
+  OutRec := FPolyOutList[E.OutIdx];
+  if E.Side = esLeft then
+    Result := OutRec.Pts else
+    Result := OutRec.Pts.Prev;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.ProcessHorizontals;
+var
+  E: PEdge;
+begin
+  while Assigned(fSortedEdges) do
+  begin
+    E := FSortedEdges;
+    DeleteFromSEL(E);
+    ProcessHorizontal(E);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function IsMinima(E: PEdge): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
+begin
+  Result := Assigned(E) and (E.Prev.NextInLML <> E) and (E.Next.NextInLML <> E);
+end;
+//------------------------------------------------------------------------------
+
+function IsMaxima(E: PEdge; const Y: cInt): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
+begin
+  Result := Assigned(E) and (E.Top.Y = Y) and not Assigned(E.NextInLML);
+end;
+//------------------------------------------------------------------------------
+
+function IsIntermediate(E: PEdge; const Y: cInt): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
+begin
+  Result := (E.Top.Y = Y) and Assigned(E.NextInLML);
+end;
+//------------------------------------------------------------------------------
+
+function GetMaximaPair(E: PEdge): PEdge;
+begin
+  if PointsEqual(E.Next.Top, E.Top) and not assigned(E.Next.NextInLML) then
+    Result := E.Next
+  else if PointsEqual(E.Prev.Top, E.Top) and not assigned(E.Prev.NextInLML) then
+    Result := E.Prev
+  else
+    Result := nil;
+  if assigned(Result) and ((Result.OutIdx = Skip) or
+    //result is false if both NextInAEL & PrevInAEL are nil & not horizontal ...
+    ((Result.NextInAEL = Result.PrevInAEL) and (Result.Dx <> Horizontal))) then
+      Result := nil;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.SwapPositionsInAEL(E1, E2: PEdge);
+var
+  Prev,Next: PEdge;
+begin
+  //check that one or other edge hasn't already been removed from AEL ...
+  if (E1.NextInAEL = E1.PrevInAEL) or (E2.NextInAEL = E2.PrevInAEL) then
+    Exit;
+
+  if E1.NextInAEL = E2 then
+  begin
+    Next := E2.NextInAEL;
+    if Assigned(Next) then Next.PrevInAEL := E1;
+    Prev := E1.PrevInAEL;
+    if Assigned(Prev) then Prev.NextInAEL := E2;
+    E2.PrevInAEL := Prev;
+    E2.NextInAEL := E1;
+    E1.PrevInAEL := E2;
+    E1.NextInAEL := Next;
+  end
+  else if E2.NextInAEL = E1 then
+  begin
+    Next := E1.NextInAEL;
+    if Assigned(Next) then Next.PrevInAEL := E2;
+    Prev := E2.PrevInAEL;
+    if Assigned(Prev) then Prev.NextInAEL := E1;
+    E1.PrevInAEL := Prev;
+    E1.NextInAEL := E2;
+    E2.PrevInAEL := E1;
+    E2.NextInAEL := Next;
+  end else
+  begin
+    Next := E1.NextInAEL;
+    Prev := E1.PrevInAEL;
+    E1.NextInAEL := E2.NextInAEL;
+    if Assigned(E1.NextInAEL) then E1.NextInAEL.PrevInAEL := E1;
+    E1.PrevInAEL := E2.PrevInAEL;
+    if Assigned(E1.PrevInAEL) then E1.PrevInAEL.NextInAEL := E1;
+    E2.NextInAEL := Next;
+    if Assigned(E2.NextInAEL) then E2.NextInAEL.PrevInAEL := E2;
+    E2.PrevInAEL := Prev;
+    if Assigned(E2.PrevInAEL) then E2.PrevInAEL.NextInAEL := E2;
+  end;
+  if not Assigned(E1.PrevInAEL) then FActiveEdges := E1
+  else if not Assigned(E2.PrevInAEL) then FActiveEdges := E2;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.SwapPositionsInSEL(E1, E2: PEdge);
+var
+  Prev,Next: PEdge;
+begin
+  if E1.NextInSEL = E2 then
+  begin
+    Next    := E2.NextInSEL;
+    if Assigned(Next) then Next.PrevInSEL := E1;
+    Prev    := E1.PrevInSEL;
+    if Assigned(Prev) then Prev.NextInSEL := E2;
+    E2.PrevInSEL := Prev;
+    E2.NextInSEL := E1;
+    E1.PrevInSEL := E2;
+    E1.NextInSEL := Next;
+  end
+  else if E2.NextInSEL = E1 then
+  begin
+    Next    := E1.NextInSEL;
+    if Assigned(Next) then Next.PrevInSEL := E2;
+    Prev    := E2.PrevInSEL;
+    if Assigned(Prev) then Prev.NextInSEL := E1;
+    E1.PrevInSEL := Prev;
+    E1.NextInSEL := E2;
+    E2.PrevInSEL := E1;
+    E2.NextInSEL := Next;
+  end else
+  begin
+    Next    := E1.NextInSEL;
+    Prev    := E1.PrevInSEL;
+    E1.NextInSEL := E2.NextInSEL;
+    if Assigned(E1.NextInSEL) then E1.NextInSEL.PrevInSEL := E1;
+    E1.PrevInSEL := E2.PrevInSEL;
+    if Assigned(E1.PrevInSEL) then E1.PrevInSEL.NextInSEL := E1;
+    E2.NextInSEL := Next;
+    if Assigned(E2.NextInSEL) then E2.NextInSEL.PrevInSEL := E2;
+    E2.PrevInSEL := Prev;
+    if Assigned(E2.PrevInSEL) then E2.PrevInSEL.NextInSEL := E2;
+  end;
+  if not Assigned(E1.PrevInSEL) then FSortedEdges := E1
+  else if not Assigned(E2.PrevInSEL) then FSortedEdges := E2;
+end;
+//------------------------------------------------------------------------------
+
+function GetNextInAEL(E: PEdge; Direction: TDirection): PEdge;
+  {$IFDEF INLINING} inline; {$ENDIF}
+begin
+  if Direction = dLeftToRight then
+    Result := E.NextInAEL else
+    Result := E.PrevInAEL;
+end;
+//------------------------------------------------------------------------
+
+procedure GetHorzDirection(HorzEdge: PEdge; out Dir: TDirection;
+  out Left, Right: cInt); {$IFDEF INLINING} inline; {$ENDIF}
+begin
+  if HorzEdge.Bot.X < HorzEdge.Top.X then
+  begin
+    Left := HorzEdge.Bot.X;
+    Right := HorzEdge.Top.X;
+    Dir := dLeftToRight;
+  end else
+  begin
+    Left := HorzEdge.Top.X;
+    Right := HorzEdge.Bot.X;
+    Dir := dRightToLeft;
+  end;
+end;
+//------------------------------------------------------------------------
+
+procedure TClipper.ProcessHorizontal(HorzEdge: PEdge);
+var
+  E, eNext, eNextHorz, ePrev, eMaxPair, eLastHorz: PEdge;
+  HorzLeft, HorzRight: cInt;
+  Direction: TDirection;
+  Pt: TIntPoint;
+  Op1, Op2: POutPt;
+  IsLastHorz, IsOpen: Boolean;
+  currMax: PMaxima;
+begin
+(*******************************************************************************
+* Notes: Horizontal edges (HEs) at scanline intersections (ie at the top or    *
+* bottom of a scanbeam) are processed as if layered. The order in which HEs    *
+* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#]    *
+* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs),      *
+* and with other non-horizontal edges [*]. Once these intersections are        *
+* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into   *
+* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs.    *
+*******************************************************************************)
+
+(*******************************************************************************
+*           \   nb: HE processing order doesn't matter         /          /    *
+*            \                                                /          /     *
+* { --------  \  -------------------  /  \  - (3) o==========%==========o  - } *
+* {            o==========o (2)      /    \       .          .               } *
+* {                       .         /      \      .          .               } *
+* { ----  o===============#========*========*=====#==========o  (1)  ------- } *
+*        /                 \      /          \   /                             *
+*******************************************************************************)
+
+  GetHorzDirection(HorzEdge, Direction, HorzLeft, HorzRight);
+  IsOpen := (HorzEdge.OutIdx >= 0) and
+    POutRec(FPolyOutList[HorzEdge.OutIdx]).IsOpen;
+
+  eLastHorz := HorzEdge;
+  while Assigned(eLastHorz.NextInLML) and
+    (eLastHorz.NextInLML.Dx = Horizontal) do
+      eLastHorz := eLastHorz.NextInLML;
+  if Assigned(eLastHorz.NextInLML) then
+    eMaxPair := nil else
+    eMaxPair := GetMaximaPair(eLastHorz);
+
+  Op1 := nil;
+  currMax := FMaxima;
+  //nb: FMaxima will only be assigned when the Simplify property is set true.
+
+  if assigned(currMax) then
+  begin
+    //get the first useful Maxima ...
+    if (Direction = dLeftToRight) then
+    begin
+      while Assigned(currMax) and (currMax.X <= HorzEdge.Bot.X) do
+        currMax := currMax.Next;
+      if Assigned(currMax) and (currMax.X >= eLastHorz.Top.X) then
+        currMax := nil;
+    end else
+    begin
+      while Assigned(currMax.Next) and (currMax.Next.X < HorzEdge.Bot.X) do
+        currMax := currMax.Next;
+      if (currMax.X <= eLastHorz.Top.X) then currMax := nil;
+    end;
+  end;
+
+  while true do //loops through consec. horizontal edges
+  begin
+    IsLastHorz := (HorzEdge = eLastHorz);
+    E := GetNextInAEL(HorzEdge, Direction);
+    while Assigned(E) do
+    begin
+
+      //this code block inserts extra coords into horizontal edges (in output
+      //polygons) whereever maxima touch these horizontal edges. This helps
+      //'simplifying' polygons (ie if the Simplify property is set).
+      if assigned(currMax) then
+      begin
+        if (Direction = dLeftToRight) then
+        begin
+          while assigned(currMax) and (currMax.X < E.Curr.X) do
+          begin
+            if (HorzEdge.OutIdx >= 0) and not IsOpen then
+              AddOutPt(HorzEdge, IntPoint(currMax.X, HorzEdge.Bot.Y));
+            currMax := currMax.Next;
+          end;
+        end else
+        begin
+          while assigned(currMax) and (currMax.X > E.Curr.X) do
+          begin
+            if (HorzEdge.OutIdx >= 0) and not IsOpen then
+              AddOutPt(HorzEdge, IntPoint(currMax.X, HorzEdge.Bot.Y));
+            currMax := currMax.Prev;
+          end;
+        end;
+      end;
+
+      if ((Direction = dLeftToRight) and (E.Curr.X > HorzRight)) or
+        ((Direction = dRightToLeft) and (E.Curr.X < HorzLeft)) then
+          Break;
+
+      //also break if we've got to the end of an intermediate horizontal edge
+      //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal.
+      if (E.Curr.X = HorzEdge.Top.X) and
+        Assigned(HorzEdge.NextInLML) and (E.Dx < HorzEdge.NextInLML.Dx) then
+          Break;
+
+      if (HorzEdge.OutIdx >= 0) and not IsOpen then //may be done multiple times
+      begin
+        Op1 := AddOutPt(HorzEdge, E.Curr);
+        eNextHorz := FSortedEdges;
+        while Assigned(eNextHorz) do
+        begin
+          if (eNextHorz.OutIdx >= 0) and
+            HorzSegmentsOverlap(HorzEdge.Bot.X,
+            HorzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X) then
+          begin
+            Op2 := GetLastOutPt(eNextHorz);
+            AddJoin(Op2, Op1, eNextHorz.Top);
+          end;
+          eNextHorz := eNextHorz.NextInSEL;
+        end;
+        AddGhostJoin(Op1, HorzEdge.Bot);
+      end;
+
+      //OK, so far we're still in range of the horizontal Edge  but make sure
+      //we're at the last of consec. horizontals when matching with eMaxPair
+      if (E = eMaxPair) and IsLastHorz then
+      begin
+        if HorzEdge.OutIdx >= 0 then
+          AddLocalMaxPoly(HorzEdge, eMaxPair, HorzEdge.Top);
+        deleteFromAEL(HorzEdge);
+        deleteFromAEL(eMaxPair);
+        Exit;
+      end;
+
+      if (Direction = dLeftToRight) then
+      begin
+        Pt := IntPoint(E.Curr.X, HorzEdge.Curr.Y);
+        IntersectEdges(HorzEdge, E, Pt);
+      end else
+      begin
+        Pt := IntPoint(E.Curr.X, HorzEdge.Curr.Y);
+        IntersectEdges(E, HorzEdge, Pt);
+      end;
+      eNext := GetNextInAEL(E, Direction);
+      SwapPositionsInAEL(HorzEdge, E);
+      E := eNext;
+    end;
+
+    //Break out of loop if HorzEdge.NextInLML is not also horizontal ...
+    if not Assigned(HorzEdge.NextInLML) or
+      (HorzEdge.NextInLML.Dx <> Horizontal) then Break;
+
+    UpdateEdgeIntoAEL(HorzEdge);
+    if (HorzEdge.OutIdx >= 0) then AddOutPt(HorzEdge, HorzEdge.Bot);
+    GetHorzDirection(HorzEdge, Direction, HorzLeft, HorzRight);
+  end;
+
+  if (HorzEdge.OutIdx >= 0) and not Assigned(Op1) then
+  begin
+    Op1 := GetLastOutPt(HorzEdge);
+    eNextHorz := FSortedEdges;
+    while Assigned(eNextHorz) do
+    begin
+      if (eNextHorz.OutIdx >= 0) and
+        HorzSegmentsOverlap(HorzEdge.Bot.X,
+        HorzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X) then
+      begin
+        Op2 := GetLastOutPt(eNextHorz);
+        AddJoin(Op2, Op1, eNextHorz.Top);
+      end;
+      eNextHorz := eNextHorz.NextInSEL;
+    end;
+    AddGhostJoin(Op1, HorzEdge.Top);
+  end;
+
+  if Assigned(HorzEdge.NextInLML) then
+  begin
+    if (HorzEdge.OutIdx >= 0) then
+    begin
+      Op1 := AddOutPt(HorzEdge, HorzEdge.Top);
+
+      UpdateEdgeIntoAEL(HorzEdge);
+      if (HorzEdge.WindDelta = 0) then Exit;
+      //nb: HorzEdge is no longer horizontal here
+      ePrev := HorzEdge.PrevInAEL;
+      eNext := HorzEdge.NextInAEL;
+      if Assigned(ePrev) and (ePrev.Curr.X = HorzEdge.Bot.X) and
+        (ePrev.Curr.Y = HorzEdge.Bot.Y) and (ePrev.WindDelta <> 0) and
+        (ePrev.OutIdx >= 0) and (ePrev.Curr.Y > ePrev.Top.Y) and
+        SlopesEqual(HorzEdge, ePrev, FUse64BitRange) then
+      begin
+        Op2 := AddOutPt(ePrev, HorzEdge.Bot);
+        AddJoin(Op1, Op2, HorzEdge.Top);
+      end
+      else if Assigned(eNext) and (eNext.Curr.X = HorzEdge.Bot.X) and
+        (eNext.Curr.Y = HorzEdge.Bot.Y) and (eNext.WindDelta <> 0) and
+          (eNext.OutIdx >= 0) and (eNext.Curr.Y > eNext.Top.Y) and
+        SlopesEqual(HorzEdge, eNext, FUse64BitRange) then
+      begin
+        Op2 := AddOutPt(eNext, HorzEdge.Bot);
+        AddJoin(Op1, Op2, HorzEdge.Top);
+      end;
+    end else
+      UpdateEdgeIntoAEL(HorzEdge);
+  end else
+  begin
+    if (HorzEdge.OutIdx >= 0) then AddOutPt(HorzEdge, HorzEdge.Top);
+    DeleteFromAEL(HorzEdge);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.UpdateEdgeIntoAEL(var E: PEdge);
+var
+  AelPrev, AelNext: PEdge;
+begin
+  //return true when AddOutPt() call needed too
+  if not Assigned(E.NextInLML) then
+    raise exception.Create(rsUpdateEdgeIntoAEL);
+
+  E.NextInLML.OutIdx := E.OutIdx;
+
+  AelPrev := E.PrevInAEL;
+  AelNext := E.NextInAEL;
+  if Assigned(AelPrev) then
+    AelPrev.NextInAEL := E.NextInLML else
+    FActiveEdges := E.NextInLML;
+  if Assigned(AelNext) then
+    AelNext.PrevInAEL := E.NextInLML;
+  E.NextInLML.Side := E.Side;
+  E.NextInLML.WindDelta := E.WindDelta;
+  E.NextInLML.WindCnt := E.WindCnt;
+  E.NextInLML.WindCnt2 := E.WindCnt2;
+  E := E.NextInLML; ////
+  E.Curr := E.Bot;
+  E.PrevInAEL := AelPrev;
+  E.NextInAEL := AelNext;
+  if E.Dx <> Horizontal then
+    InsertScanbeam(E.Top.Y);
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.ProcessIntersections(const TopY: cInt): Boolean;
+begin
+  Result := True;
+  try
+    BuildIntersectList(TopY);
+    if (FIntersectList.Count = 0) then
+      Exit
+    else if FixupIntersectionOrder then
+      ProcessIntersectList()
+    else
+      Result := False;
+  finally
+    DisposeIntersectNodes; //clean up if there's been an error
+    FSortedEdges := nil;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.DisposeIntersectNodes;
+var
+  I: Integer;
+begin
+  for I := 0 to FIntersectList.Count - 1 do
+    Dispose(PIntersectNode(FIntersectList[I]));
+  FIntersectList.Clear;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.BuildIntersectList(const TopY: cInt);
+var
+  E, eNext: PEdge;
+  Pt: TIntPoint;
+  IsModified: Boolean;
+  NewNode: PIntersectNode;
+begin
+  if not Assigned(fActiveEdges) then Exit;
+
+  //prepare for sorting ...
+  E := FActiveEdges;
+  FSortedEdges := E;
+  while Assigned(E) do
+  begin
+    E.PrevInSEL := E.PrevInAEL;
+    E.NextInSEL := E.NextInAEL;
+    E.Curr.X := TopX(E, TopY);
+    E := E.NextInAEL;
+  end;
+
+  //bubblesort (because adjacent swaps are required) ...
+  repeat
+    IsModified := False;
+    E := FSortedEdges;
+    while Assigned(E.NextInSEL) do
+    begin
+      eNext := E.NextInSEL;
+      if (E.Curr.X > eNext.Curr.X) then
+      begin
+        IntersectPoint(E, eNext, Pt);
+        new(NewNode);
+        NewNode.Edge1 := E;
+        NewNode.Edge2 := eNext;
+        NewNode.Pt := Pt;
+        FIntersectList.Add(NewNode);
+
+        SwapPositionsInSEL(E, eNext);
+        IsModified := True;
+      end else
+        E := eNext;
+    end;
+    if Assigned(E.PrevInSEL) then
+      E.PrevInSEL.NextInSEL := nil
+    else Break;
+  until not IsModified;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.ProcessIntersectList;
+var
+  I: Integer;
+begin
+  for I := 0 to FIntersectList.Count - 1 do
+  begin
+    with PIntersectNode(FIntersectList[I])^ do
+    begin
+      IntersectEdges(Edge1, Edge2, Pt);
+      SwapPositionsInAEL(Edge1, Edge2);
+    end;
+    dispose(PIntersectNode(FIntersectList[I]));
+  end;
+  FIntersectList.Clear;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.DoMaxima(E: PEdge);
+var
+  ENext, EMaxPair: PEdge;
+begin
+  EMaxPair := GetMaximaPair(E);
+  if not assigned(EMaxPair) then
+  begin
+    if E.OutIdx >= 0 then
+      AddOutPt(E, E.Top);
+    DeleteFromAEL(E);
+    Exit;
+  end;
+
+  ENext := E.NextInAEL;
+  //rarely, with overlapping collinear edges (in open paths) ENext can be nil
+  while Assigned(ENext) and (ENext <> EMaxPair) do
+  begin
+    IntersectEdges(E, ENext, E.Top);
+    SwapPositionsInAEL(E, ENext);
+    ENext := E.NextInAEL;
+  end;
+
+  if (E.OutIdx = Unassigned) and (EMaxPair.OutIdx = Unassigned) then
+  begin
+    DeleteFromAEL(E);
+    DeleteFromAEL(EMaxPair);
+  end
+  else if (E.OutIdx >= 0) and (EMaxPair.OutIdx >= 0) then
+  begin
+    if E.OutIdx >= 0 then
+      AddLocalMaxPoly(E, EMaxPair, E.Top);
+    deleteFromAEL(E);
+    deleteFromAEL(eMaxPair);
+  end
+{$IFDEF use_lines}
+  else if E.WindDelta = 0 then
+  begin
+    if (E.OutIdx >= 0) then
+    begin
+      AddOutPt(E, E.Top);
+      E.OutIdx := Unassigned;
+    end;
+    DeleteFromAEL(E);
+
+    if (EMaxPair.OutIdx >= 0) then
+    begin
+      AddOutPt(EMaxPair, E.Top);
+      EMaxPair.OutIdx := Unassigned;
+    end;
+    DeleteFromAEL(EMaxPair);
+  end
+{$ENDIF}
+  else
+    raise exception.Create(rsDoMaxima);
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.ProcessEdgesAtTopOfScanbeam(const TopY: cInt);
+var
+  E, EMaxPair, ePrev, eNext: PEdge;
+  Op, Op2: POutPt;
+  IsMaximaEdge: Boolean;
+  Pt: TIntPoint;
+begin
+(*******************************************************************************
+* Notes: Processing edges at scanline intersections (ie at the top or bottom   *
+* of a scanbeam) needs to be done in multiple stages and in the correct order. *
+* Firstly, edges forming a 'maxima' need to be processed and then removed.     *
+* Next, 'intermediate' and 'maxima' horizontal edges are processed. Then edges *
+* that intersect exactly at the top of the scanbeam are processed [%].         *
+* Finally, new minima are added and any intersects they create are processed.  *
+*******************************************************************************)
+
+(*******************************************************************************
+*     \                          /    /          \   /                         *
+*      \   Horizontal minima    /    /            \ /                          *
+* { --  o======================#====o   --------   .     ------------------- } *
+* {       Horizontal maxima    .                   %  scanline intersect     } *
+* { -- o=======================#===================#========o     ---------- } *
+*      |                      /                   / \        \                 *
+*      + maxima intersect    /                   /   \        \                *
+*     /|\                   /                   /     \        \               *
+*    / | \                 /                   /       \        \              *
+*******************************************************************************)
+
+  E := FActiveEdges;
+  while Assigned(E) do
+  begin
+    //1. process maxima, treating them as if they're 'bent' horizontal edges,
+    //   but exclude maxima with Horizontal edges. nb: E can't be a Horizontal.
+    IsMaximaEdge := IsMaxima(E, TopY);
+    if IsMaximaEdge then
+    begin
+      EMaxPair := GetMaximaPair(E);
+      IsMaximaEdge := not assigned(EMaxPair) or (EMaxPair.Dx <> Horizontal);
+    end;
+
+    if IsMaximaEdge then
+    begin
+      if FStrictSimple then
+        InsertMaxima(E.Top.X);
+      //'E' might be removed from AEL, as may any following edges so ...
+      ePrev := E.PrevInAEL;
+      DoMaxima(E);
+      if not Assigned(ePrev) then
+        E := FActiveEdges else
+        E := ePrev.NextInAEL;
+    end else
+    begin
+      //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ...
+      if IsIntermediate(E, TopY) and (E.NextInLML.Dx = Horizontal) then
+      begin
+        UpdateEdgeIntoAEL(E);
+        if (E.OutIdx >= 0) then
+          AddOutPt(E, E.Bot);
+        AddEdgeToSEL(E);
+      end else
+      begin
+        E.Curr.X := TopX(E, TopY);
+        E.Curr.Y := TopY;
+      end;
+
+      //When StrictlySimple and 'e' is being touched by another edge, then
+      //make sure both edges have a vertex here ...
+      if FStrictSimple then
+      begin
+        ePrev := E.PrevInAEL;
+        if (E.OutIdx >= 0) and (E.WindDelta <> 0) and
+          Assigned(ePrev) and (ePrev.Curr.X = E.Curr.X) and
+          (ePrev.OutIdx >= 0) and (ePrev.WindDelta <> 0) then
+        begin
+          Pt := E.Curr;
+{$IFDEF use_xyz}
+          SetZ(Pt, ePrev, E, FZFillCallback);
+{$ENDIF}
+          Op := AddOutPt(ePrev, Pt);
+          Op2 := AddOutPt(E, Pt);
+          AddJoin(Op, Op2, Pt); //strictly-simple (type-3) 'join'
+        end;
+      end;
+
+      E := E.NextInAEL;
+    end;
+  end;
+
+  //3. Process horizontals at the top of the scanbeam ...
+  ProcessHorizontals;
+  if FStrictSimple then DisposeMaximaList;
+
+  //4. Promote intermediate vertices ...
+  E := FActiveEdges;
+  while Assigned(E) do
+  begin
+    if IsIntermediate(E, TopY) then
+    begin
+      if (E.OutIdx >= 0) then
+        Op := AddOutPt(E, E.Top) else
+        Op := nil;
+      UpdateEdgeIntoAEL(E);
+
+      //if output polygons share an Edge, they'll need joining later ...
+      ePrev := E.PrevInAEL;
+      eNext  := E.NextInAEL;
+      if Assigned(ePrev) and (ePrev.Curr.X = E.Bot.X) and
+        (ePrev.Curr.Y = E.Bot.Y) and assigned(Op) and
+        (ePrev.OutIdx >= 0) and (ePrev.Curr.Y > ePrev.Top.Y) and
+        SlopesEqual(E, ePrev, FUse64BitRange) and
+        (E.WindDelta <> 0) and (ePrev.WindDelta <> 0) then
+      begin
+        Op2 := AddOutPt(ePrev, E.Bot);
+        AddJoin(Op, Op2, E.Top);
+      end
+      else if Assigned(eNext) and (eNext.Curr.X = E.Bot.X) and
+        (eNext.Curr.Y = E.Bot.Y) and assigned(Op) and
+          (eNext.OutIdx >= 0) and (eNext.Curr.Y > eNext.Top.Y) and
+        SlopesEqual(E, eNext, FUse64BitRange) and
+        (E.WindDelta <> 0) and (eNext.WindDelta <> 0) then
+      begin
+        Op2 := AddOutPt(eNext, E.Bot);
+        AddJoin(Op, Op2, E.Top);
+      end;
+    end;
+    E := E.NextInAEL;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.BuildResult: TPaths;
+var
+  I, J, K, Cnt: Integer;
+  OutRec: POutRec;
+  Op: POutPt;
+begin
+  J := 0;
+  SetLength(Result, FPolyOutList.Count);
+  for I := 0 to FPolyOutList.Count -1 do
+    if Assigned(fPolyOutList[I]) then
+    begin
+      OutRec := FPolyOutList[I];
+      if not assigned(OutRec.Pts) then Continue;
+
+      Op := OutRec.Pts.Prev;
+      Cnt := PointCount(Op);
+      if (Cnt < 2) then Continue;
+      SetLength(Result[J], Cnt);
+      for K := 0 to Cnt -1 do
+      begin
+        Result[J][K] := Op.Pt;
+        Op := Op.Prev;
+      end;
+      Inc(J);
+    end;
+  SetLength(Result, J);
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.BuildResult2(PolyTree: TPolyTree): Boolean;
+var
+  I, J, Cnt, CntAll: Integer;
+  Op: POutPt;
+  OutRec: POutRec;
+  PolyNode: TPolyNode;
+begin
+  try
+    PolyTree.Clear;
+    SetLength(PolyTree.FAllNodes, FPolyOutList.Count);
+
+    //add PolyTree ...
+    CntAll := 0;
+    for I := 0 to FPolyOutList.Count -1 do
+    begin
+      OutRec := fPolyOutList[I];
+      Cnt := PointCount(OutRec.Pts);
+      if (OutRec.IsOpen and (cnt < 2)) or
+        (not outRec.IsOpen and (cnt < 3)) then Continue;
+      FixHoleLinkage(OutRec);
+
+      PolyNode := TPolyNode.Create;
+      PolyTree.FAllNodes[CntAll] := PolyNode;
+      OutRec.PolyNode := PolyNode;
+      Inc(CntAll);
+      SetLength(PolyNode.FPath, Cnt);
+      Op := OutRec.Pts.Prev;
+      for J := 0 to Cnt -1 do
+      begin
+        PolyNode.FPath[J] := Op.Pt;
+        Op := Op.Prev;
+      end;
+    end;
+
+    //fix Poly links ...
+    SetLength(PolyTree.FAllNodes, CntAll);
+    SetLength(PolyTree.FChilds, CntAll);
+    for I := 0 to FPolyOutList.Count -1 do
+    begin
+      OutRec := fPolyOutList[I];
+      if Assigned(OutRec.PolyNode) then
+      begin
+        if OutRec.IsOpen then
+        begin
+          OutRec.PolyNode.FIsOpen := true;
+          PolyTree.AddChild(OutRec.PolyNode);
+        end
+        else if Assigned(OutRec.FirstLeft) and
+          assigned(OutRec.FirstLeft.PolyNode)then
+          OutRec.FirstLeft.PolyNode.AddChild(OutRec.PolyNode)
+        else
+          PolyTree.AddChild(OutRec.PolyNode);
+      end;
+    end;
+    SetLength(PolyTree.FChilds, PolyTree.FCount);
+    Result := True;
+  except
+    Result := False;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.FixupOutPolyline(OutRec: POutRec);
+var
+  PP, LastPP, TmpPP: POutPt;
+begin
+  //remove duplicate points ...
+  PP := OutRec.Pts;
+  LastPP := PP.Prev;
+  while (PP <> LastPP) do
+  begin
+    PP := PP.Next;
+    //strip duplicate points ...
+    if PointsEqual(PP.Pt, PP.Prev.Pt) then
+    begin
+      if PP = LastPP then LastPP := PP.Prev;
+      TmpPP := PP.Prev;
+      TmpPP.Next := PP.Next;
+      PP.Next.Prev := TmpPP;
+      dispose(PP);
+      PP := TmpPP;
+    end;
+  end;
+
+  if (PP = PP.Prev) then
+  begin
+    Dispose(PP);
+    OutRec.Pts := nil;
+    Exit;
+  end;
+
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.FixupOutPolygon(OutRec: POutRec);
+var
+  PP, Tmp, LastOK: POutPt;
+  PreserveCol: Boolean;
+begin
+  //remove duplicate points and collinear edges
+  LastOK := nil;
+  OutRec.BottomPt := nil; //flag as stale
+  PP := OutRec.Pts;
+  PreserveCol := FPreserveCollinear or FStrictSimple;
+  while True do
+  begin
+    if (PP = PP.Prev) or (PP.Next = PP.Prev) then
+    begin
+      Dispose(PP);
+      OutRec.Pts := nil;
+      Exit;
+    end;
+
+    //test for duplicate points and collinear edges ...
+    if PointsEqual(PP.Pt, PP.Next.Pt) or PointsEqual(PP.Pt, PP.Prev.Pt) or
+      (SlopesEqual(PP.Prev.Pt, PP.Pt, PP.Next.Pt, FUse64BitRange) and
+      (not PreserveCol or
+      not Pt2IsBetweenPt1AndPt3(PP.Prev.Pt, PP.Pt, PP.Next.Pt))) then
+    begin
+      //OK, we need to delete a point ...
+      LastOK := nil;
+      Tmp := PP;
+      PP.Prev.Next := PP.Next;
+      PP.Next.Prev := PP.Prev;
+      PP := PP.Prev;
+      dispose(Tmp);
+    end
+    else if PP = LastOK then Break
+    else
+    begin
+      if not Assigned(LastOK) then LastOK := PP;
+      PP := PP.Next;
+    end;
+  end;
+  OutRec.Pts := PP;
+end;
+//------------------------------------------------------------------------------
+
+function EdgesAdjacent(Inode: PIntersectNode): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
+begin
+  Result := (Inode.Edge1.NextInSEL = Inode.Edge2) or
+    (Inode.Edge1.PrevInSEL = Inode.Edge2);
+end;
+//------------------------------------------------------------------------------
+
+function IntersectListSort(Node1, Node2: Pointer): Integer;
+var
+  i: cInt;
+begin
+  i := PIntersectNode(Node2).Pt.Y - PIntersectNode(Node1).Pt.Y;
+  if i < 0 then Result := -1
+  else if i > 0 then Result := 1
+  else Result := 0;
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.FixupIntersectionOrder: Boolean;
+var
+  I, J, Cnt: Integer;
+  Node: PIntersectNode;
+begin
+  //pre-condition: intersections are sorted bottom-most first.
+  //Now it's crucial that intersections are made only between adjacent edges,
+  //and to ensure this the order of intersections may need adjusting ...
+  Result := True;
+  Cnt := FIntersectList.Count;
+  if Cnt < 2 then exit;
+
+  CopyAELToSEL;
+  {$IFDEF USEGENERICS}
+  FIntersectList.Sort(TComparer<PIntersectNode>.Construct(
+    function (const Node1, Node2 : PIntersectNode) : integer
+    var
+      i: cInt;
+    begin
+      i := PIntersectNode(Node2).Pt.Y - PIntersectNode(Node1).Pt.Y;
+      if i < 0 then Result := -1
+      else if i > 0 then Result := 1
+      else Result := 0;
+    end
+    ));
+  {$ELSE}
+  FIntersectList.Sort(IntersectListSort);
+  {$ENDIF}
+  for I := 0 to Cnt - 1 do
+  begin
+    if not EdgesAdjacent(FIntersectList[I]) then
+    begin
+      J := I + 1;
+      while (J < Cnt) and not EdgesAdjacent(FIntersectList[J]) do inc(J);
+      if J = Cnt then
+      begin
+        Result := False;
+        Exit; //error!!
+      end;
+      //Swap IntersectNodes ...
+      Node := FIntersectList[I];
+      FIntersectList[I] := FIntersectList[J];
+      FIntersectList[J] := Node;
+    end;
+    with PIntersectNode(FIntersectList[I])^ do
+      SwapPositionsInSEL(Edge1, Edge2);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function DupOutPt(OutPt: POutPt; InsertAfter: Boolean = true): POutPt;
+begin
+  new(Result);
+  Result.Pt := OutPt.Pt;
+  Result.Idx := OutPt.Idx;
+  if InsertAfter then
+  begin
+    Result.Next := OutPt.Next;
+    Result.Prev := OutPt;
+    OutPt.Next.Prev := Result;
+    OutPt.Next := Result;
+  end else
+  begin
+    Result.Prev := OutPt.Prev;
+    Result.Next := OutPt;
+    OutPt.Prev.Next := Result;
+    OutPt.Prev := Result;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function JoinHorz(Op1, Op1b, Op2, Op2b: POutPt;
+  const Pt: TIntPoint; DiscardLeft: Boolean): Boolean;
+var
+  Dir1, Dir2: TDirection;
+begin
+  if Op1.Pt.X > Op1b.Pt.X then Dir1 := dRightToLeft else Dir1 := dLeftToRight;
+  if Op2.Pt.X > Op2b.Pt.X then Dir2 := dRightToLeft else Dir2 := dLeftToRight;
+  Result := Dir1 <> Dir2;
+  if not Result then Exit;
+
+  //When DiscardLeft, we want Op1b to be on the left of Op1, otherwise we
+  //want Op1b to be on the right. (And likewise with Op2 and Op2b.)
+  //To facilitate this while inserting Op1b & Op2b when DiscardLeft == true,
+  //make sure we're either AT or RIGHT OF Pt before adding Op1b, otherwise
+  //make sure we're AT or LEFT OF Pt. (Likewise with Op2b.)
+  if Dir1 = dLeftToRight then
+  begin
+    while (Op1.Next.Pt.X <= Pt.X) and
+      (Op1.Next.Pt.X >= Op1.Pt.X) and (Op1.Next.Pt.Y = Pt.Y) do
+      Op1 := Op1.Next;
+    if DiscardLeft and (Op1.Pt.X <> Pt.X) then Op1 := Op1.Next;
+    Op1b := DupOutPt(Op1, not DiscardLeft);
+    if not PointsEqual(Op1b.Pt, Pt) then
+    begin
+      Op1 := Op1b;
+      Op1.Pt := Pt;
+      Op1b := DupOutPt(Op1, not DiscardLeft);
+    end;
+  end else
+  begin
+    while (Op1.Next.Pt.X >= Pt.X) and
+      (Op1.Next.Pt.X <= Op1.Pt.X) and (Op1.Next.Pt.Y = Pt.Y) do
+      Op1 := Op1.Next;
+    if not DiscardLeft and (Op1.Pt.X <> Pt.X) then Op1 := Op1.Next;
+    Op1b := DupOutPt(Op1, DiscardLeft);
+    if not PointsEqual(Op1b.Pt, Pt) then
+    begin
+      Op1 := Op1b;
+      Op1.Pt := Pt;
+      Op1b := DupOutPt(Op1, DiscardLeft);
+    end;
+  end;
+
+  if Dir2 = dLeftToRight then
+  begin
+    while (Op2.Next.Pt.X <= Pt.X) and
+      (Op2.Next.Pt.X >= Op2.Pt.X) and (Op2.Next.Pt.Y = Pt.Y) do
+        Op2 := Op2.Next;
+    if DiscardLeft and (Op2.Pt.X <> Pt.X) then Op2 := Op2.Next;
+    Op2b := DupOutPt(Op2, not DiscardLeft);
+    if not PointsEqual(Op2b.Pt, Pt) then
+    begin
+      Op2 := Op2b;
+      Op2.Pt := Pt;
+      Op2b := DupOutPt(Op2, not DiscardLeft);
+    end;
+  end else
+  begin
+    while (Op2.Next.Pt.X >= Pt.X) and
+      (Op2.Next.Pt.X <= Op2.Pt.X) and (Op2.Next.Pt.Y = Pt.Y) do
+      Op2 := Op2.Next;
+    if not DiscardLeft and (Op2.Pt.X <> Pt.X) then Op2 := Op2.Next;
+    Op2b := DupOutPt(Op2, DiscardLeft);
+    if not PointsEqual(Op2b.Pt, Pt) then
+    begin
+      Op2 := Op2b;
+      Op2.Pt := Pt;
+      Op2b := DupOutPt(Op2, DiscardLeft);
+    end;
+  end;
+
+  if (Dir1 = dLeftToRight) = DiscardLeft then
+  begin
+    Op1.Prev := Op2;
+    Op2.Next := Op1;
+    Op1b.Next := Op2b;
+    Op2b.Prev := Op1b;
+  end
+  else
+  begin
+    Op1.Next := Op2;
+    Op2.Prev := Op1;
+    Op1b.Prev := Op2b;
+    Op2b.Next := Op1b;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function TClipper.JoinPoints(Jr: PJoin; OutRec1, OutRec2: POutRec): Boolean;
+var
+  Op1, Op1b, Op2, Op2b: POutPt;
+  Pt: TIntPoint;
+  Reverse1, Reverse2, DiscardLeftSide: Boolean;
+  IsHorizontal: Boolean;
+  Left, Right: cInt;
+begin
+  Result := False;
+  Op1 := Jr.OutPt1;
+  Op2 := Jr.OutPt2;
+
+  //There are 3 kinds of joins for output polygons ...
+  //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere
+  //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal).
+  //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same
+  //location at the bottom of the overlapping segment (& Join.OffPt is above).
+  //3. StrictlySimple joins where edges touch but are not collinear and where
+  //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point.
+  IsHorizontal := (Jr.OutPt1.Pt.Y = Jr.OffPt.Y);
+
+  if IsHorizontal and PointsEqual(Jr.OffPt, Jr.OutPt1.Pt) and
+  PointsEqual(Jr.OffPt, Jr.OutPt2.Pt) then
+  begin
+    //Strictly Simple join ...
+    if (OutRec1 <> OutRec2) then 
+      Exit;
+
+    Op1b := Jr.OutPt1.Next;
+    while (Op1b <> Op1) and
+      PointsEqual(Op1b.Pt, Jr.OffPt) do Op1b := Op1b.Next;
+    Reverse1 := (Op1b.Pt.Y > Jr.OffPt.Y);
+    Op2b := Jr.OutPt2.Next;
+    while (Op2b <> Op2) and
+      PointsEqual(Op2b.Pt, Jr.OffPt) do Op2b := Op2b.Next;
+    Reverse2 := (Op2b.Pt.Y > Jr.OffPt.Y);
+    if (Reverse1 = Reverse2) then Exit;
+
+    if Reverse1 then
+    begin
+      Op1b := DupOutPt(Op1, False);
+      Op2b := DupOutPt(Op2, True);
+      Op1.Prev := Op2;
+      Op2.Next := Op1;
+      Op1b.Next := Op2b;
+      Op2b.Prev := Op1b;
+      Jr.OutPt1 := Op1;
+      Jr.OutPt2 := Op1b;
+      Result := True;
+    end else
+    begin
+      Op1b := DupOutPt(Op1, True);
+      Op2b := DupOutPt(Op2, False);
+      Op1.Next := Op2;
+      Op2.Prev := Op1;
+      Op1b.Prev := Op2b;
+      Op2b.Next := Op1b;
+      Jr.OutPt1 := Op1;
+      Jr.OutPt2 := Op1b;
+      Result := True;
+    end;
+  end
+  else if IsHorizontal then
+  begin
+    op1b := op1;
+    while (op1.Prev.Pt.Y = op1.Pt.Y) and
+      (op1.Prev <> Op1b) and (op1.Prev <> op2) do
+        op1 := op1.Prev;
+    while (op1b.Next.Pt.Y = op1b.Pt.Y) and
+      (op1b.Next <> Op1) and (op1b.Next <> op2) do
+        op1b := op1b.Next;
+    if (op1b.Next = Op1) or (op1b.Next = op2) then Exit; //a flat 'polygon'
+
+    op2b := op2;
+    while (op2.Prev.Pt.Y = op2.Pt.Y) and
+      (op2.Prev <> Op2b) and (op2.Prev <> op1b) do
+        op2 := op2.Prev;
+    while (op2b.Next.Pt.Y = op2b.Pt.Y) and
+      (op2b.Next <> Op2) and (op2b.Next <> op1) do
+        op2b := op2b.Next;
+    if (op2b.Next = Op2) or (op2b.Next = op1) then Exit; //a flat 'polygon'
+
+    //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges
+    if not GetOverlap(Op1.Pt.X, Op1b.Pt.X, Op2.Pt.X, Op2b.Pt.X, Left, Right) then
+      Exit;
+
+    //DiscardLeftSide: when joining overlapping edges, a spike will be created
+    //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up
+    //on the discard side as either may still be needed for other joins ...
+    if (Op1.Pt.X >= Left) and (Op1.Pt.X <= Right) then
+    begin
+      Pt := Op1.Pt; DiscardLeftSide := Op1.Pt.X > Op1b.Pt.X;
+    end else if (Op2.Pt.X >= Left) and (Op2.Pt.X <= Right) then
+    begin
+      Pt := Op2.Pt; DiscardLeftSide := Op2.Pt.X > Op2b.Pt.X;
+    end else if (Op1b.Pt.X >= Left) and (Op1b.Pt.X <= Right) then
+    begin
+      Pt := Op1b.Pt; DiscardLeftSide := Op1b.Pt.X > Op1.Pt.X;
+    end else
+    begin
+      Pt := Op2b.Pt; DiscardLeftSide := Op2b.Pt.X > Op2.Pt.X;
+    end;
+
+    Result := JoinHorz(Op1, Op1b, Op2, Op2b, Pt, DiscardLeftSide);
+    if not Result then Exit;
+    Jr.OutPt1 := Op1;
+    Jr.OutPt2 := Op2;
+  end else
+  begin
+    //make sure the polygons are correctly oriented ...
+    Op1b := Op1.Next;
+    while PointsEqual(Op1b.Pt, Op1.Pt) and (Op1b <> Op1) do Op1b := Op1b.Next;
+    Reverse1 := (Op1b.Pt.Y > Op1.Pt.Y) or
+      not SlopesEqual(Op1.Pt, Op1b.Pt, Jr.OffPt, FUse64BitRange);
+    if Reverse1 then
+    begin
+      Op1b := Op1.Prev;
+      while PointsEqual(Op1b.Pt, Op1.Pt) and (Op1b <> Op1) do Op1b := Op1b.Prev;
+      if (Op1b.Pt.Y > Op1.Pt.Y) or
+        not SlopesEqual(Op1.Pt, Op1b.Pt, Jr.OffPt, FUse64BitRange) then Exit;
+    end;
+    Op2b := Op2.Next;
+    while PointsEqual(Op2b.Pt, Op2.Pt) and (Op2b <> Op2) do Op2b := Op2b.Next;
+    Reverse2 := (Op2b.Pt.Y > Op2.Pt.Y) or
+      not SlopesEqual(Op2.Pt, Op2b.Pt, Jr.OffPt, FUse64BitRange);
+    if Reverse2 then
+    begin
+      Op2b := Op2.Prev;
+      while PointsEqual(Op2b.Pt, Op2.Pt) and (Op2b <> Op2) do Op2b := Op2b.Prev;
+      if (Op2b.Pt.Y > Op2.Pt.Y) or
+        not SlopesEqual(Op2.Pt, Op2b.Pt, Jr.OffPt, FUse64BitRange) then Exit;
+    end;
+
+    if (Op1b = Op1) or (Op2b = Op2) or (Op1b = Op2b) or
+      ((OutRec1 = OutRec2) and (Reverse1 = Reverse2)) then Exit;
+
+    if Reverse1 then
+    begin
+      Op1b := DupOutPt(Op1, False);
+      Op2b := DupOutPt(Op2, True);
+      Op1.Prev := Op2;
+      Op2.Next := Op1;
+      Op1b.Next := Op2b;
+      Op2b.Prev := Op1b;
+      Jr.OutPt1 := Op1;
+      Jr.OutPt2 := Op1b;
+      Result := True;
+    end else
+    begin
+      Op1b := DupOutPt(Op1, True);
+      Op2b := DupOutPt(Op2, False);
+      Op1.Next := Op2;
+      Op2.Prev := Op1;
+      Op1b.Prev := Op2b;
+      Op2b.Next := Op1b;
+      Jr.OutPt1 := Op1;
+      Jr.OutPt2 := Op1b;
+      Result := True;
+    end;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function ParseFirstLeft(FirstLeft: POutRec): POutRec;
+begin
+  while Assigned(FirstLeft) and not Assigned(FirstLeft.Pts) do
+    FirstLeft := FirstLeft.FirstLeft;
+  Result := FirstLeft;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.FixupFirstLefts1(OldOutRec, NewOutRec: POutRec);
+var
+  I: Integer;
+  OutRec: POutRec;
+  firstLeft: POutRec;
+begin
+  //tests if NewOutRec contains the polygon before reassigning FirstLeft
+  for I := 0 to FPolyOutList.Count -1 do
+  begin
+    OutRec := fPolyOutList[I];
+    if not Assigned(OutRec.Pts) or not Assigned(OutRec.FirstLeft) then continue;
+    firstLeft := ParseFirstLeft(OutRec.FirstLeft);
+    if (firstLeft = OldOutRec) then
+    begin
+      if Poly2ContainsPoly1(OutRec.Pts, NewOutRec.Pts) then
+        OutRec.FirstLeft := NewOutRec;
+    end;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.FixupFirstLefts2(OldOutRec, NewOutRec: POutRec);
+var
+  I: Integer;
+begin
+  //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon
+  for I := 0 to FPolyOutList.Count -1 do
+    with POutRec(fPolyOutList[I])^ do
+      if (FirstLeft = OldOutRec) then FirstLeft := NewOutRec;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.JoinCommonEdges;
+var
+  I, J: Integer;
+  Jr: PJoin;
+  OutRec1, OutRec2, HoleStateRec, oRec: POutRec;
+begin
+  for I := 0 to FJoinList.count -1 do
+  begin
+    Jr := FJoinList[I];
+
+    OutRec1 := GetOutRec(Jr.OutPt1.Idx);
+    OutRec2 := GetOutRec(Jr.OutPt2.Idx);
+
+    if not Assigned(OutRec1.Pts) or not Assigned(OutRec2.Pts) then Continue;
+    if OutRec1.IsOpen or OutRec2.IsOpen then Continue;
+
+    //get the polygon fragment with the correct hole state (FirstLeft)
+    //before calling JoinPoints() ...
+    if OutRec1 = OutRec2 then HoleStateRec := OutRec1
+    else if Param1RightOfParam2(OutRec1, OutRec2) then HoleStateRec := OutRec2
+    else if Param1RightOfParam2(OutRec2, OutRec1) then HoleStateRec := OutRec1
+    else HoleStateRec := GetLowermostRec(OutRec1, OutRec2);
+
+    if not JoinPoints(Jr, OutRec1, OutRec2) then Continue;
+
+    if (OutRec1 = OutRec2) then
+    begin
+      //instead of joining two polygons, we've just created a new one by
+      //splitting one polygon into two.
+      OutRec1.Pts := Jr.OutPt1;
+      OutRec1.BottomPt := nil;
+      OutRec2 := CreateOutRec;
+      OutRec2.Pts := Jr.OutPt2;
+
+      //update all OutRec2.Pts idx's ...
+      UpdateOutPtIdxs(OutRec2);
+
+      //We now need to check every OutRec.FirstLeft pointer. If it points
+      //to OutRec1 it may need to point to OutRec2 instead ...
+      if FUsingPolyTree then
+        for J := 0 to FPolyOutList.Count - 2 do
+        begin
+          oRec := POutRec(FPolyOutList[J]);
+          if not Assigned(oRec.Pts) or
+            (ParseFirstLeft(oRec.FirstLeft) <> OutRec1) or
+            (oRec.IsHole = OutRec1.IsHole) then Continue;
+          if Poly2ContainsPoly1(oRec.Pts, Jr.OutPt2) then
+              oRec.FirstLeft := OutRec2;
+        end;
+
+      //sort out the hole states of both polygon ...
+      if Poly2ContainsPoly1(OutRec2.Pts, OutRec1.Pts) then
+      begin
+        //OutRec2 is contained by OutRec1 ...
+        OutRec2.IsHole := not OutRec1.IsHole;
+        OutRec2.FirstLeft := OutRec1;
+
+        //fixup FirstLeft pointers that may need reassigning to OutRec1
+        if FUsingPolyTree then FixupFirstLefts2(OutRec2, OutRec1);
+
+        if (OutRec2.IsHole xor FReverseOutput) = (Area(OutRec2) > 0) then
+            ReversePolyPtLinks(OutRec2.Pts);
+      end else if Poly2ContainsPoly1(OutRec1.Pts, OutRec2.Pts) then
+      begin
+        //OutRec1 is contained by OutRec2 ...
+        OutRec2.IsHole := OutRec1.IsHole;
+        OutRec1.IsHole := not OutRec2.IsHole;
+        OutRec2.FirstLeft := OutRec1.FirstLeft;
+        OutRec1.FirstLeft := OutRec2;
+
+        //fixup FirstLeft pointers that may need reassigning to OutRec1
+        if FUsingPolyTree then FixupFirstLefts2(OutRec1, OutRec2);
+
+        if (OutRec1.IsHole xor FReverseOutput) = (Area(OutRec1) > 0) then
+          ReversePolyPtLinks(OutRec1.Pts);
+      end else
+      begin
+        //the 2 polygons are completely separate ...
+        OutRec2.IsHole := OutRec1.IsHole;
+        OutRec2.FirstLeft := OutRec1.FirstLeft;
+
+        //fixup FirstLeft pointers that may need reassigning to OutRec2
+        if FUsingPolyTree then FixupFirstLefts1(OutRec1, OutRec2);
+      end;
+    end else
+    begin
+      //joined 2 polygons together ...
+
+      //delete the obsolete pointer ...
+      OutRec2.Pts := nil;
+      OutRec2.BottomPt := nil;
+      OutRec2.Idx := OutRec1.Idx;
+
+      OutRec1.IsHole := HoleStateRec.IsHole;
+      if HoleStateRec = OutRec2 then
+        OutRec1.FirstLeft := OutRec2.FirstLeft;
+      OutRec2.FirstLeft := OutRec1;
+
+      //fixup FirstLeft pointers that may need reassigning to OutRec1
+      if FUsingPolyTree then FixupFirstLefts2(OutRec2, OutRec1);
+    end;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipper.DoSimplePolygons;
+var
+  I: Integer;
+  OutRec1, OutRec2: POutRec;
+  Op, Op2, Op3, Op4: POutPt;
+begin
+  I := 0;
+  while I < FPolyOutList.Count do
+  begin
+    OutRec1 := POutRec(fPolyOutList[I]);
+    inc(I);
+    Op := OutRec1.Pts;
+    if not assigned(Op) or OutRec1.IsOpen then Continue;
+    repeat //for each Pt in Path until duplicate found do ...
+      Op2 := Op.Next;
+      while (Op2 <> OutRec1.Pts) do
+      begin
+        if (PointsEqual(Op.Pt, Op2.Pt) and
+          (Op2.Next <> Op) and (Op2.Prev <> Op)) then
+        begin
+          //split the polygon into two ...
+          Op3 := Op.Prev;
+          Op4 := Op2.Prev;
+          Op.Prev := Op4;
+          Op4.Next := Op;
+          Op2.Prev := Op3;
+          Op3.Next := Op2;
+
+          OutRec1.Pts := Op;
+
+          OutRec2 := CreateOutRec;
+          OutRec2.Pts := Op2;
+          UpdateOutPtIdxs(OutRec2);
+          if Poly2ContainsPoly1(OutRec2.Pts, OutRec1.Pts) then
+          begin
+            //OutRec2 is contained by OutRec1 ...
+            OutRec2.IsHole := not OutRec1.IsHole;
+            OutRec2.FirstLeft := OutRec1;
+            if FUsingPolyTree then FixupFirstLefts2(OutRec2, OutRec1);
+          end
+          else
+          if Poly2ContainsPoly1(OutRec1.Pts, OutRec2.Pts) then
+          begin
+            //OutRec1 is contained by OutRec2 ...
+            OutRec2.IsHole := OutRec1.IsHole;
+            OutRec1.IsHole := not OutRec2.IsHole;
+            OutRec2.FirstLeft := OutRec1.FirstLeft;
+            OutRec1.FirstLeft := OutRec2;
+            if FUsingPolyTree then FixupFirstLefts2(OutRec1, OutRec2);
+          end else
+          begin
+            //the 2 polygons are separate ...
+            OutRec2.IsHole := OutRec1.IsHole;
+            OutRec2.FirstLeft := OutRec1.FirstLeft;
+            if FUsingPolyTree then FixupFirstLefts1(OutRec1, OutRec2);
+          end;
+          Op2 := Op; //ie get ready for the next iteration
+        end;
+        Op2 := Op2.Next;
+      end;
+      Op := Op.Next;
+    until (Op = OutRec1.Pts);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function GetUnitNormal(const Pt1, Pt2: TIntPoint): TDoublePoint;
+var
+  Dx, Dy, F: Single;
+begin
+  if (Pt2.X = Pt1.X) and (Pt2.Y = Pt1.Y) then
+  begin
+    Result.X := 0;
+    Result.Y := 0;
+    Exit;
+  end;
+
+  Dx := (Pt2.X - Pt1.X);
+  Dy := (Pt2.Y - Pt1.Y);
+  F := 1 / Hypot(Dx, Dy);
+  Dx := Dx * F;
+  Dy := Dy * F;
+  Result.X := Dy;
+  Result.Y := -Dx
+end;
+
+//------------------------------------------------------------------------------
+// TClipperOffset
+//------------------------------------------------------------------------------
+
+constructor TClipperOffset.Create(
+  MiterLimit: Double = 2;
+  ArcTolerance: Double = def_arc_tolerance);
+begin
+  inherited Create;
+  FPolyNodes := TPolyNode.Create;
+  FLowest.X := -1;
+  FMiterLimit := MiterLimit;
+  FArcTolerance := ArcTolerance;
+end;
+//------------------------------------------------------------------------------
+
+destructor TClipperOffset.Destroy;
+begin
+  Clear;
+  FPolyNodes.Free;
+  inherited;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperOffset.Clear;
+var
+  I: Integer;
+  PolyNode: TPolyNode;
+begin
+  for I := 0 to FPolyNodes.ChildCount -1 do
+    begin
+      PolyNode:= FPolyNodes.Childs[I];
+      PolyNode.Free;
+    end;
+  FPolyNodes.FCount := 0;
+  FPolyNodes.FBuffLen := 16;
+  SetLength(FPolyNodes.FChilds, 16);
+  FLowest.X := -1;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperOffset.AddPath(const Path: TPath;
+  JoinType: TJoinType; EndType: TEndType);
+var
+  I, J, K, HighI: Integer;
+  NewNode: TPolyNode;
+  ip: TIntPoint;
+begin
+  HighI := High(Path);
+  if HighI < 0 then Exit;
+  NewNode := TPolyNode.Create;
+  NewNode.FJoinType := JoinType;
+  NewNode.FEndType := EndType;
+
+  //strip duplicate points from path and also get index to the lowest point ...
+  if EndType in [etClosedLine, etClosedPolygon] then
+    while (HighI > 0) and PointsEqual(Path[0], Path[HighI]) do dec(HighI);
+  SetLength(NewNode.FPath, HighI +1);
+  NewNode.FPath[0] := Path[0];
+  J := 0; K := 0;
+  for I := 1 to HighI do
+    if not PointsEqual(NewNode.FPath[J], Path[I]) then
+    begin
+      inc(J);
+      NewNode.FPath[J] := Path[I];
+      if (NewNode.FPath[K].Y < Path[I].Y) or
+        ((NewNode.FPath[K].Y = Path[I].Y) and
+        (NewNode.FPath[K].X > Path[I].X)) then
+          K := J;
+    end;
+  inc(J);
+  if J < HighI +1 then
+    SetLength(NewNode.FPath, J);
+  if (EndType = etClosedPolygon) and (J < 3) then
+  begin
+    NewNode.free;
+    Exit;
+  end;
+  FPolyNodes.AddChild(NewNode);
+
+  if EndType <> etClosedPolygon then Exit;
+  //if this path's lowest pt is lower than all the others then update FLowest
+  if (FLowest.X < 0) then
+  begin
+    FLowest := IntPoint(FPolyNodes.ChildCount -1, K);
+  end else
+  begin
+    ip := FPolyNodes.Childs[FLowest.X].FPath[FLowest.Y];
+    if (NewNode.FPath[K].Y > ip.Y) or
+      ((NewNode.FPath[K].Y = ip.Y) and
+      (NewNode.FPath[K].X < ip.X)) then
+        FLowest := IntPoint(FPolyNodes.ChildCount -1, K);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperOffset.AddPaths(const Paths: TPaths;
+  JoinType: TJoinType; EndType: TEndType);
+var
+  I: Integer;
+begin
+  for I := 0 to High(Paths) do AddPath(Paths[I], JoinType, EndType);
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperOffset.FixOrientations;
+var
+  I: Integer;
+begin
+  //fixup orientations of all closed paths if the orientation of the
+  //closed path with the lowermost vertex is wrong ...
+  if (FLowest.X >= 0) and
+    not Orientation(FPolyNodes.Childs[FLowest.X].FPath) then
+  begin
+    for I := 0 to FPolyNodes.ChildCount -1 do
+      if FPolyNodes.Childs[I].FEndType = etClosedPolygon then
+        FPolyNodes.Childs[I].FPath := ReversePath(FPolyNodes.Childs[I].FPath)
+      else if (FPolyNodes.Childs[I].FEndType = etClosedLine) and
+        Orientation(FPolyNodes.Childs[I].FPath) then
+          FPolyNodes.Childs[I].FPath := ReversePath(FPolyNodes.Childs[I].FPath);
+  end else
+  begin
+    for I := 0 to FPolyNodes.ChildCount -1 do
+      if (FPolyNodes.Childs[I].FEndType = etClosedLine) and
+        not Orientation(FPolyNodes.Childs[I].FPath) then
+          FPolyNodes.Childs[I].FPath := ReversePath(FPolyNodes.Childs[I].FPath);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperOffset.DoOffset(Delta: Double);
+var
+  I, J, K, Len, solCount: Integer;
+  X, X2, Y, Steps, AbsDelta: Double;
+  Node: TPolyNode;
+  N: TDoublePoint;
+begin
+  FSolution := nil;
+  FDelta := Delta;
+  AbsDelta := Abs(Delta);
+
+  //if Zero offset, just copy any CLOSED polygons to FSolution and return ...
+  if AbsDelta < Tolerance then
+  begin
+    solCount := 0;
+    SetLength(FSolution, FPolyNodes.ChildCount);
+    for I := 0 to FPolyNodes.ChildCount -1 do
+      if FPolyNodes.Childs[I].FEndType = etClosedPolygon then
+      begin
+        FSolution[solCount] := FPolyNodes.Childs[I].FPath;
+        inc(solCount);
+      end;
+    SetLength(FSolution, solCount);
+    Exit;
+  end;
+
+  //FMiterLimit: see offset_triginometry3.svg in the documentation folder ...
+  if FMiterLimit > 2 then FMiterLim := 2/(sqr(FMiterLimit))
+  else FMiterLim := 0.5;
+
+  if (FArcTolerance <= 0) then Y := def_arc_tolerance
+  else if FArcTolerance > AbsDelta * def_arc_tolerance then
+    Y := AbsDelta * def_arc_tolerance
+  else Y := FArcTolerance;
+
+  //see offset_triginometry2.svg in the documentation folder ...
+  Steps := PI / ArcCos(1 - Y / AbsDelta);  //steps per 360 degrees
+  if (Steps > AbsDelta * Pi) then
+    Steps := AbsDelta * Pi;                //ie excessive precision check
+
+  Math.SinCos(Two_Pi / Steps, FSin, FCos); //sin & cos per step
+  if Delta < 0 then FSin := -FSin;
+  FStepsPerRad := Steps / Two_Pi;
+
+  SetLength(FSolution, FPolyNodes.ChildCount * 2);
+  solCount := 0;
+  for I := 0 to FPolyNodes.ChildCount -1 do
+  begin
+    Node := FPolyNodes.Childs[I];
+    FInP := Node.FPath;
+    Len := length(FInP);
+
+    if (Len = 0) or
+      ((Delta <= 0) and ((Len < 3) or (Node.FEndType <> etClosedPolygon))) then
+        Continue;
+
+    FOutPos := 0;
+    FOutP := nil;
+
+    //if a single vertex then build circle or a square ...
+    if (Len = 1) then
+    begin
+      if Node.FJoinType = jtRound then
+      begin
+        X := 1; Y := 0;
+        for J := 1 to Round(Steps) do
+        begin
+          AddPoint(IntPoint(
+            Round(FInP[0].X + X * FDelta),
+            Round(FInP[0].Y + Y * FDelta)));
+          X2 := X;
+          X := X * FCos - FSin * Y;
+          Y := X2 * FSin + Y * FCos;
+        end
+      end else
+      begin
+        X := -1; Y := -1;
+        for J := 1 to 4 do
+        begin
+          AddPoint(IntPoint( Round(FInP[0].X + X * FDelta),
+            Round(FInP[0].Y + Y * FDelta)));
+          if X < 0 then X := 1
+          else if Y < 0 then Y := 1
+          else X := -1;
+        end;
+      end;
+      SetLength(FOutP, FOutPos);
+      FSolution[solCount] := FOutP;
+      Inc(solCount);
+      Continue;
+    end;
+
+    //build Normals ...
+    SetLength(FNorms, Len);
+    for J := 0 to Len-2 do
+      FNorms[J] := GetUnitNormal(FInP[J], FInP[J+1]);
+    if not (Node.FEndType in [etClosedLine, etClosedPolygon]) then
+      FNorms[Len-1] := FNorms[Len-2] else
+      FNorms[Len-1] := GetUnitNormal(FInP[Len-1], FInP[0]);
+
+    if Node.FEndType = etClosedPolygon then
+    begin
+      K := Len -1;
+      for J := 0 to Len-1 do
+        OffsetPoint(J, K, Node.FJoinType);
+      SetLength(FOutP, FOutPos);
+      FSolution[solCount] := FOutP;
+      Inc(solCount);
+    end
+    else if (Node.FEndType = etClosedLine) then
+    begin
+      K := Len -1;
+      for J := 0 to Len-1 do
+        OffsetPoint(J, K, Node.FJoinType);
+      SetLength(FOutP, FOutPos);
+      FSolution[solCount] := FOutP;
+      Inc(solCount);
+
+      FOutPos := 0;
+      FOutP := nil;
+
+      //re-build Normals ...
+      N := FNorms[Len - 1];
+      for J := Len-1 downto 1 do
+      begin
+        FNorms[J].X := -FNorms[J-1].X;
+        FNorms[J].Y := -FNorms[J-1].Y;
+      end;
+      FNorms[0].X := -N.X;
+      FNorms[0].Y := -N.Y;
+
+      K := 0;
+      for J := Len-1 downto 0 do
+        OffsetPoint(J, K, Node.FJoinType);
+      SetLength(FOutP, FOutPos);
+
+      FSolution[solCount] := FOutP;
+      Inc(solCount);
+    end else
+    begin
+      //offset the polyline going forward ...
+      K := 0;
+      for J := 1 to Len-2 do
+        OffsetPoint(J, K, Node.FJoinType);
+
+      //handle the end (butt, round or square) ...
+      if Node.FEndType = etOpenButt then
+      begin
+        J := Len - 1;
+        AddPoint(IntPoint(round(FInP[J].X + FNorms[J].X *FDelta),
+          round(FInP[J].Y + FNorms[J].Y * FDelta)));
+        AddPoint(IntPoint(round(FInP[J].X - FNorms[J].X *FDelta),
+          round(FInP[J].Y - FNorms[J].Y * FDelta)));
+      end else
+      begin
+        J := Len - 1;
+        K := Len - 2;
+        FNorms[J].X := -FNorms[J].X;
+        FNorms[J].Y := -FNorms[J].Y;
+        FSinA := 0;
+        if Node.FEndType = etOpenSquare then
+          DoSquare(J, K) else
+          DoRound(J, K);
+      end;
+
+      //re-build Normals ...
+      for J := Len-1 downto 1 do
+      begin
+        FNorms[J].X := -FNorms[J-1].X;
+        FNorms[J].Y := -FNorms[J-1].Y;
+      end;
+      FNorms[0].X := -FNorms[1].X;
+      FNorms[0].Y := -FNorms[1].Y;
+
+      //offset the polyline going backward ...
+      K := Len -1;
+      for J := Len -2 downto 1 do
+        OffsetPoint(J, K, Node.FJoinType);
+
+      //finally handle the start (butt, round or square) ...
+      if Node.FEndType = etOpenButt then
+      begin
+        AddPoint(IntPoint(round(FInP[0].X - FNorms[0].X *FDelta),
+          round(FInP[0].Y - FNorms[0].Y * FDelta)));
+        AddPoint(IntPoint(round(FInP[0].X + FNorms[0].X *FDelta),
+          round(FInP[0].Y + FNorms[0].Y * FDelta)));
+      end else
+      begin
+        FSinA := 0;
+        if Node.FEndType = etOpenSquare then
+          DoSquare(0, 1) else
+          DoRound(0, 1);
+      end;
+      SetLength(FOutP, FOutPos);
+      FSolution[solCount] := FOutP;
+      Inc(solCount);
+    end;
+  end;
+  SetLength(FSolution, solCount);
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperOffset.Execute(out solution: TPaths; Delta: Double);
+var
+  I, Len: Integer;
+  Outer: TPath;
+  Bounds: TIntRect;
+begin
+  FixOrientations;
+  DoOffset(Delta);
+  //now clean up 'corners' ...
+  with TClipper.Create do
+  try
+    AddPaths(FSolution, ptSubject, True);
+    if Delta > 0 then
+    begin
+      Execute(ctUnion, solution, pftPositive, pftPositive);
+    end else
+    begin
+      Bounds := GetBounds(FSolution);
+      SetLength(Outer, 4);
+      Outer[0] := IntPoint(Bounds.left-10, Bounds.bottom+10);
+      Outer[1] := IntPoint(Bounds.right+10, Bounds.bottom+10);
+      Outer[2] := IntPoint(Bounds.right+10, Bounds.top-10);
+      Outer[3] := IntPoint(Bounds.left-10, Bounds.top-10);
+      AddPath(Outer, ptSubject, True);
+      ReverseSolution := True;
+      Execute(ctUnion, solution, pftNegative, pftNegative);
+      //delete the outer rectangle ...
+      Len := length(solution);
+      for I := 1 to Len -1 do solution[I-1] := solution[I];
+      if Len > 0 then SetLength(solution, Len -1);
+    end;
+  finally
+    free;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperOffset.Execute(out solution: TPolyTree; Delta: Double);
+var
+  I: Integer;
+  Outer: TPath;
+  Bounds: TIntRect;
+  OuterNode: TPolyNode;
+begin
+  if not assigned(solution) then
+    raise exception.Create(rsClipperOffset);
+  solution.Clear;
+
+  FixOrientations;
+  DoOffset(Delta);
+
+  //now clean up 'corners' ...
+  with TClipper.Create do
+  try
+    AddPaths(FSolution, ptSubject, True);
+    if Delta > 0 then
+    begin
+      Execute(ctUnion, solution, pftPositive, pftPositive);
+    end else
+    begin
+      Bounds := GetBounds(FSolution);
+      SetLength(Outer, 4);
+      Outer[0] := IntPoint(Bounds.left-10, Bounds.bottom+10);
+      Outer[1] := IntPoint(Bounds.right+10, Bounds.bottom+10);
+      Outer[2] := IntPoint(Bounds.right+10, Bounds.top-10);
+      Outer[3] := IntPoint(Bounds.left-10, Bounds.top-10);
+      AddPath(Outer, ptSubject, True);
+      ReverseSolution := True;
+      Execute(ctUnion, solution, pftNegative, pftNegative);
+      //remove the outer PolyNode rectangle ...
+      if (solution.ChildCount = 1) and (solution.Childs[0].ChildCount > 0) then
+      begin
+        OuterNode := solution.Childs[0];
+        SetLength(solution.FChilds, OuterNode.ChildCount);
+        solution.FChilds[0] := OuterNode.Childs[0];
+        solution.FChilds[0].FParent := solution;
+        for I := 1 to OuterNode.ChildCount -1 do
+          solution.AddChild(OuterNode.Childs[I]);
+      end else
+        solution.Clear;
+    end;
+  finally
+    free;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperOffset.AddPoint(const Pt: TIntPoint);
+const
+  BuffLength = 32;
+begin
+  if FOutPos = length(FOutP) then
+    SetLength(FOutP, FOutPos + BuffLength);
+  FOutP[FOutPos] := Pt;
+  Inc(FOutPos);
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperOffset.DoSquare(J, K: Integer);
+var
+  A, Dx: Double;
+begin
+  //see offset_triginometry.svg in the documentation folder ...
+  A := ArcTan2(FSinA, FNorms[K].X * FNorms[J].X + FNorms[K].Y * FNorms[J].Y);
+  Dx := tan(A/4);
+  AddPoint(IntPoint(
+    round(FInP[J].X + FDelta * (FNorms[K].X - FNorms[K].Y *Dx)),
+    round(FInP[J].Y + FDelta * (FNorms[K].Y + FNorms[K].X *Dx))));
+  AddPoint(IntPoint(
+    round(FInP[J].X + FDelta * (FNorms[J].X + FNorms[J].Y *Dx)),
+    round(FInP[J].Y + FDelta * (FNorms[J].Y - FNorms[J].X *Dx))));
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperOffset.DoMiter(J, K: Integer; R: Double);
+var
+  Q: Double;
+begin
+  Q := FDelta / R;
+  AddPoint(IntPoint(round(FInP[J].X + (FNorms[K].X + FNorms[J].X)*Q),
+    round(FInP[J].Y + (FNorms[K].Y + FNorms[J].Y)*Q)));
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperOffset.DoRound(J, K: Integer);
+var
+  I, Steps: Integer;
+  A, X, X2, Y: Double;
+begin
+  A := ArcTan2(FSinA, FNorms[K].X * FNorms[J].X + FNorms[K].Y * FNorms[J].Y);
+  Steps := Max(Round(FStepsPerRad * Abs(A)), 1);
+
+  X := FNorms[K].X;
+  Y := FNorms[K].Y;
+  for I := 1 to Steps do
+  begin
+    AddPoint(IntPoint(
+      round(FInP[J].X + X * FDelta),
+      round(FInP[J].Y + Y * FDelta)));
+    X2 := X;
+    X := X * FCos - FSin * Y;
+    Y := X2 * FSin + Y * FCos;
+  end;
+  AddPoint(IntPoint(
+    round(FInP[J].X + FNorms[J].X * FDelta),
+    round(FInP[J].Y + FNorms[J].Y * FDelta)));
+end;
+//------------------------------------------------------------------------------
+
+procedure TClipperOffset.OffsetPoint(J: Integer;
+  var K: Integer; JoinType: TJoinType);
+var
+  R, cosA: Double;
+begin
+  //cross product ...
+  FSinA := (FNorms[K].X * FNorms[J].Y - FNorms[J].X * FNorms[K].Y);
+  if (Abs(FSinA * FDelta) < 1.0) then
+  begin
+    //very nearly collinear edges can occasionally cause tiny self-intersections
+    //due to rounding so offset with a single vertex here. (nb: The two offset
+    //vertices that would otherwise have been used would be < 1 unit apart.)
+    //dot product ...
+    cosA := (FNorms[K].X * FNorms[J].X + FNorms[J].Y * FNorms[K].Y );
+    if (cosA > 0) then // angle => 0 deg.
+    begin
+      AddPoint(IntPoint(round(FInP[J].X + FNorms[K].X * FDelta),
+        round(FInP[J].Y + FNorms[K].Y * FDelta)));
+      Exit;
+    end
+    //else angle => 180 deg.
+  end
+  else if (FSinA > 1.0) then FSinA := 1.0
+  else if (FSinA < -1.0) then FSinA := -1.0;
+
+  if FSinA * FDelta < 0 then
+  begin
+    AddPoint(IntPoint(round(FInP[J].X + FNorms[K].X * FDelta),
+      round(FInP[J].Y + FNorms[K].Y * FDelta)));
+    AddPoint(FInP[J]);
+    AddPoint(IntPoint(round(FInP[J].X + FNorms[J].X * FDelta),
+      round(FInP[J].Y + FNorms[J].Y * FDelta)));
+  end
+  else
+    case JoinType of
+      jtMiter:
+      begin
+        R := 1 + (FNorms[J].X * FNorms[K].X + FNorms[J].Y * FNorms[K].Y);
+        if (R >= FMiterLim) then DoMiter(J, K, R)
+        else DoSquare(J, K);
+      end;
+      jtSquare: DoSquare(J, K);
+      jtRound: DoRound(J, K);
+    end;
+  K := J;
+end;
+//------------------------------------------------------------------------------
+
+function SimplifyPolygon(const Poly: TPath; FillType: TPolyFillType = pftEvenOdd): TPaths;
+begin
+  with TClipper.Create do
+  try
+    StrictlySimple := True;
+    AddPath(Poly, ptSubject, True);
+    Execute(ctUnion, Result, FillType, FillType);
+  finally
+    free;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function SimplifyPolygons(const Polys: TPaths; FillType: TPolyFillType = pftEvenOdd): TPaths;
+begin
+  with TClipper.Create do
+  try
+    StrictlySimple := True;
+    AddPaths(Polys, ptSubject, True);
+    Execute(ctUnion, Result, FillType, FillType);
+  finally
+    free;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function DistanceSqrd(const Pt1, Pt2: TIntPoint): Double; {$IFDEF INLINING} inline; {$ENDIF}
+var
+  dx, dy: Double;
+begin
+  dx := (Pt1.X - Pt2.X);
+  dy := (Pt1.Y - Pt2.Y);
+  result := (dx*dx + dy*dy);
+end;
+//------------------------------------------------------------------------------
+
+function DistanceFromLineSqrd(const pt, ln1, ln2: TIntPoint): double;
+var
+  A, B, C: double;
+begin
+  //The equation of a line in general form (Ax + By + C = 0)
+  //given 2 points (x�,y�) & (x�,y�) is ...
+  //(y� - y�)x + (x� - x�)y + (y� - y�)x� - (x� - x�)y� = 0
+  //A = (y� - y�); B = (x� - x�); C = (y� - y�)x� - (x� - x�)y�
+  //perpendicular distance of point (x�,y�) = (Ax� + By� + C)/Sqrt(A� + B�)
+  //see http://en.wikipedia.org/wiki/Perpendicular_distance
+  A := ln1.Y - ln2.Y;
+  B := ln2.X - ln1.X;
+  C := A * ln1.X  + B * ln1.Y;
+  C := A * pt.X + B * pt.Y - C;
+  Result := (C * C) / (A * A + B * B);
+end;
+//---------------------------------------------------------------------------
+
+function SlopesNearCollinear(const Pt1, Pt2, Pt3: TIntPoint;
+  DistSqrd: Double): Boolean;
+begin
+  //this function is more accurate when the point that's geometrically
+  //between the other 2 points is the one that's tested for distance.
+  //ie makes it more likely to pick up 'spikes' ...
+  if Abs(Pt1.X - Pt2.X) > Abs(Pt1.Y - Pt2.Y) then
+  begin
+    if (Pt1.X > Pt2.X) = (Pt1.X < Pt3.X) then
+      result := DistanceFromLineSqrd(Pt1, Pt2, Pt3) < DistSqrd
+    else if (Pt2.X > Pt1.X) = (Pt2.X < Pt3.X) then
+      result := DistanceFromLineSqrd(Pt2, Pt1, Pt3) < DistSqrd
+    else
+      result := DistanceFromLineSqrd(Pt3, Pt1, Pt2) < DistSqrd;
+  end else
+  begin
+    if (Pt1.Y > Pt2.Y) = (Pt1.Y < Pt3.Y) then
+      result := DistanceFromLineSqrd(Pt1, Pt2, Pt3) < DistSqrd
+    else if (Pt2.Y > Pt1.Y) = (Pt2.Y < Pt3.Y) then
+      result := DistanceFromLineSqrd(Pt2, Pt1, Pt3) < DistSqrd
+    else
+      result := DistanceFromLineSqrd(Pt3, Pt1, Pt2) < DistSqrd;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function PointsAreClose(const Pt1, Pt2: TIntPoint;
+  DistSqrd: Double): Boolean;
+begin
+  result := DistanceSqrd(Pt1, Pt2) <= DistSqrd;
+end;
+//------------------------------------------------------------------------------
+
+function CleanPolygon(const Poly: TPath; Distance: Double = 1.415): TPath;
+var
+  I, Len: Integer;
+  DistSqrd: double;
+  OutPts: array of TOutPt;
+  op: POutPt;
+
+  function ExcludeOp(op: POutPt): POutPt;
+  begin
+    Result := op.Prev;
+    Result.Next := op.Next;
+    op.Next.Prev := Result;
+    Result.Idx := 0;
+  end;
+
+begin
+  //Distance = proximity in units/pixels below which vertices
+  //will be stripped. Default ~= sqrt(2) so when adjacent
+  //vertices have both x & y coords within 1 unit, then
+  //the second vertex will be stripped.
+  DistSqrd := Round(Distance * Distance);
+  Result := nil;
+  Len := Length(Poly);
+  if Len = 0 then Exit;
+
+  SetLength(OutPts, Len);
+  for I := 0 to Len -1 do
+  begin
+    OutPts[I].Pt := Poly[I];
+    OutPts[I].Next := @OutPts[(I + 1) mod Len];
+    OutPts[I].Next.Prev := @OutPts[I];
+    OutPts[I].Idx := 0;
+  end;
+
+  op := @OutPts[0];
+  while (op.Idx = 0) and (op.Next <> op.Prev) do
+  begin
+    if PointsAreClose(op.Pt, op.Prev.Pt, DistSqrd) then
+    begin
+      op := ExcludeOp(op);
+      Dec(Len);
+    end else if PointsAreClose(op.Prev.Pt, op.Next.Pt, DistSqrd) then
+    begin
+      ExcludeOp(op.Next);
+      op := ExcludeOp(op);
+      Dec(Len, 2);
+    end
+    else if SlopesNearCollinear(op.Prev.Pt, op.Pt, op.Next.Pt, DistSqrd) then
+    begin
+      op := ExcludeOp(op);
+      Dec(Len);
+    end
+    else
+    begin
+      op.Idx := 1;
+      op := op.Next;
+    end;
+  end;
+
+  if Len < 3 then Len := 0;
+  SetLength(Result, Len);
+  for I := 0 to Len -1 do
+  begin
+    Result[I] := op.Pt;
+    op := op.Next;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function CleanPolygons(const Polys: TPaths; Distance: double = 1.415): TPaths;
+var
+  I, Len: Integer;
+begin
+  Len := Length(Polys);
+  SetLength(Result, Len);
+  for I := 0 to Len - 1 do
+    Result[I] := CleanPolygon(Polys[I], Distance);
+end;
+//------------------------------------------------------------------------------
+
+function Minkowski(const Base, Path: TPath;
+  IsSum: Boolean; IsClosed: Boolean): TPaths;
+var
+  i, j, delta, baseLen, pathLen: integer;
+  quad: TPath;
+  tmp: TPaths;
+begin
+  if IsClosed then delta := 1 else delta := 0;
+
+  baseLen := Length(Base);
+  pathLen := Length(Path);
+  setLength(tmp, pathLen);
+  if IsSum then
+    for i := 0 to pathLen -1 do
+    begin
+      setLength(tmp[i], baseLen);
+      for j := 0 to baseLen -1 do
+      begin
+        tmp[i][j].X := Path[i].X + Base[j].X;
+        tmp[i][j].Y := Path[i].Y + Base[j].Y;
+      end;
+    end
+  else
+    for i := 0 to pathLen -1 do
+    begin
+      setLength(tmp[i], baseLen);
+      for j := 0 to baseLen -1 do
+      begin
+        tmp[i][j].X := Path[i].X - Base[j].X;
+        tmp[i][j].Y := Path[i].Y - Base[j].Y;
+      end;
+    end;
+
+  SetLength(quad, 4);
+  SetLength(Result, (pathLen + delta) * (baseLen + 1));
+  for i := 0 to pathLen - 2 + delta do
+  begin
+    for j := 0 to baseLen - 1 do
+    begin
+      quad[0] := tmp[i mod pathLen][j mod baseLen];
+      quad[1] := tmp[(i+1) mod pathLen][j mod baseLen];
+      quad[2] := tmp[(i+1) mod pathLen][(j+1) mod baseLen];
+      quad[3] := tmp[i mod pathLen][(j+1) mod baseLen];
+      if not Orientation(quad) then quad := ReversePath(quad);
+      Result[i*baseLen + j] := copy(quad, 0, 4);
+    end;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function TranslatePath(const Path: TPath; const Delta: TIntPoint): TPath;
+var
+  i, len: Integer;
+begin
+  len := Length(Path);
+  SetLength(Result, len);
+  for i := 0 to High(Path) do
+  begin
+    Result[i].X := Path[i].X + Delta.X;
+    Result[i].Y := Path[i].Y + Delta.Y;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function MinkowskiSum(const Pattern, Path: TPath; PathIsClosed: Boolean): TPaths;
+begin
+  Result := Minkowski(Pattern, Path, true, PathIsClosed);
+  with TClipper.Create() do
+  try
+    AddPaths(Result, ptSubject, True);
+    Execute(ctUnion, Result, pftNonZero);
+  finally
+    Free;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function MinkowskiSum(const Pattern: TPath; const Paths: TPaths;
+  PathFillType: TPolyFillType; PathIsClosed: Boolean): TPaths;
+var
+  I, Cnt: Integer;
+  Paths2: TPaths;
+  Path: TPath;
+begin
+  Result := nil;
+  if Length(Pattern) = 0 then Exit;
+  Cnt := Length(Paths);
+  with TClipper.Create() do
+  try
+    for I := 0 to Cnt -1 do
+    begin
+      Paths2 := Minkowski(Pattern, Paths[I], true, PathIsClosed);
+      AddPaths( Paths2, ptSubject, true);
+      if PathIsClosed then
+      begin
+        Path := TranslatePath(Paths[I], Pattern[0]);
+        AddPath(Path, ptClip, true);
+      end;
+    end;
+    Execute(ctUnion, Result, PathFillType, PathFillType);
+  finally
+    Free;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function MinkowskiDiff(const Poly1, Poly2: TPath): TPaths;
+begin
+  Result := Minkowski(Poly1, Poly2, false, true);
+  with TClipper.Create() do
+  try
+    AddPaths(Result, ptSubject, True);
+    Execute(ctUnion, Result, pftNonZero);
+  finally
+    Free;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+type
+  TNodeType = (ntAny, ntOpen, ntClosed);
+
+procedure AddPolyNodeToPaths(PolyNode: TPolyNode;
+  NodeType: TNodeType; var Paths: TPaths);
+var
+  I: Integer;
+  Match: Boolean;
+begin
+  case NodeType of
+    ntAny: Match := True;
+    ntClosed: Match := not PolyNode.IsOpen;
+    else Exit;
+  end;
+
+  if (Length(PolyNode.Contour) > 0) and Match then
+  begin
+    I := Length(Paths);
+    SetLength(Paths, I +1);
+    Paths[I] := PolyNode.Contour;
+  end;
+  for I := 0 to PolyNode.ChildCount - 1 do
+    AddPolyNodeToPaths(PolyNode.Childs[I], NodeType, Paths);
+end;
+//------------------------------------------------------------------------------
+
+function PolyTreeToPaths(PolyTree: TPolyTree): TPaths;
+begin
+  Result := nil;
+  AddPolyNodeToPaths(PolyTree, ntAny, Result);
+end;
+//------------------------------------------------------------------------------
+
+function ClosedPathsFromPolyTree(PolyTree: TPolyTree): TPaths;
+begin
+  Result := nil;
+  AddPolyNodeToPaths(PolyTree, ntClosed, Result);
+end;
+//------------------------------------------------------------------------------
+
+function OpenPathsFromPolyTree(PolyTree: TPolyTree): TPaths;
+var
+  I, J: Integer;
+begin
+  Result := nil;
+  //Open polys are top level only, so ...
+  for I := 0 to PolyTree.ChildCount - 1 do
+    if PolyTree.Childs[I].IsOpen then
+    begin
+      J := Length(Result);
+      SetLength(Result, J +1);
+      Result[J] := PolyTree.Childs[I].Contour;
+    end;
+end;
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+end.
diff --git a/Delphi/main demo/GR32_Misc.pas b/Delphi/main demo/GR32_Misc.pas
new file mode 100644
index 0000000..9e9f42f
--- /dev/null
+++ b/Delphi/main demo/GR32_Misc.pas	
@@ -0,0 +1,295 @@
+unit GR32_Misc;
+
+(*******************************************************************************
+*                                                                              *
+* Author    :  Angus Johnson                                                   *
+* Website   :  http://www.angusj.com                                           *
+* Copyright :  Angus Johnson 2010                                              *
+*                                                                              *
+* License:                                                                     *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt                                         *
+*                                                                              *
+*******************************************************************************)
+
+interface
+
+{$WARN UNSAFE_CODE OFF}
+
+uses
+  Windows, Types,
+  Classes, SysUtils, Math, GR32, GR32_LowLevel, GR32_Blend, GR32_Transforms,
+  //requires Graphics32 (revision 2180 or later) ...
+  //https://sourceforge.net/p/graphics32/code/HEAD/tree/trunk/Source/
+  Graphics, GR32_Math, GR32_Polygons, GR32_VPR;
+
+type
+  TArrayOfArrayOfArrayOfFixedPoint = array of TArrayOfArrayOfFixedPoint;
+
+function CreateMaskFromPolygon(bitmap: TBitmap32;
+  const polygons: TArrayOfArrayOfFloatPoint;
+  fillMode: TPolyFillMode = pfAlternate): TBitmap32; overload;
+procedure ApplyMask(modifiedBmp, originalBmp, maskBmp: TBitmap32; invertMask: boolean = false);
+procedure Simple3D(bitmap: TBitmap32; const pts: TArrayOfArrayOfFloatPoint;
+  dx,dy,fadeRate: integer; topLeftColor, bottomRightColor: TColor32;
+  fillMode: TPolyFillMode = pfAlternate);
+function GetEllipsePoints(const ellipseRect: TFloatRect): TArrayOfFloatPoint;
+
+const
+  MAXIMUM_SHADOW_FADE = 0;
+  MEDIUM_SHADOW_FADE  = 5;
+  MINIMUM_SHADOW_FADE = 10;
+  NO_SHADOW_FADE      = 11; //anything > 10
+
+implementation
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+procedure OffsetPoints(var pts: TArrayOfFloatPoint; dx, dy: single);
+var
+  i: integer;
+begin
+  for i := 0 to high(pts) do
+    with pts[i] do
+    begin
+      X := X + dx;
+      Y := Y + dy;
+    end;
+end;
+//------------------------------------------------------------------------------
+
+function CreateMaskFromPolygon(bitmap: TBitmap32;
+  const polygons: TArrayOfArrayOfFloatPoint;
+  fillMode: TPolyFillMode = pfAlternate): TBitmap32;
+var
+  highI: integer;
+begin
+  result := TBitmap32.create;
+  with bitmap do result.SetSize(width,height);
+  highI := high(polygons);
+  if highI < 0 then exit;
+  PolyPolygonFS(result, polygons, clWhite32, fillMode);
+  PolyPolyLineFS(result, polygons, clBlack32, true);
+end;
+//------------------------------------------------------------------------------
+
+procedure ApplyMask(modifiedBmp, originalBmp, maskBmp: TBitmap32; invertMask: boolean = false);
+var
+  i: integer;
+  origClr, modClr, mskClr: PColor32Entry;
+begin
+  if not assigned(originalBmp) or not assigned(maskBmp) or
+    (originalBmp.Width <> modifiedBmp.Width) or
+    (originalBmp.Height <> modifiedBmp.Height) or
+    (originalBmp.Height <> maskBmp.Height) or
+    (originalBmp.Height <> maskBmp.Height) then exit;
+
+  origClr := @originalBmp.Bits[0];
+  modClr := @modifiedBmp.Bits[0];
+  mskClr := @maskBmp.Bits[0];
+  for i := 1 to originalBmp.Width * originalBmp.Height do
+  begin
+    //black pixel in mask -> replace modified color with original color
+    //white pixel in mask -> keep modified color
+    if invertMask then
+      MergeMemEx(origClr.ARGB, modClr.ARGB, 255- mskClr.B) else
+      MergeMemEx(origClr.ARGB, modClr.ARGB, mskClr.B);
+    inc(origClr);
+    inc(modClr);
+    inc(mskClr);
+  end;
+  EMMS;
+end;
+//------------------------------------------------------------------------------
+
+procedure SimpleShadow(bitmap: TBitmap32; const pts: TArrayOfArrayOfFloatPoint;
+  dx, dy, fadeRate: integer; shadowColor: TColor32;
+  closed: boolean = false; NoInteriorBleed: boolean = false;
+  fillMode: TPolyFillMode = pfAlternate);
+var
+  i, j, maxD: integer;
+  sx,sy, a, alpha, alphaLinear, alphaExp, dRate: single;
+  p: TArrayOfFloatPoint;
+  originalBitmap, maskBitmap: TBitmap32;
+  sc: TColor32;
+begin
+  if ((dx = 0) and (dy = 0)) or (length(pts) = 0) then exit;
+  
+  if abs(dy) > abs(dx) then
+  begin
+    maxD := abs(dy);
+    sy := sign(dy);
+    sx := dx/maxD;
+  end else
+  begin
+    maxD := abs(dx);
+    sx := sign(dx);
+    sy := dy/maxD;
+  end;
+
+  if fadeRate <= MAXIMUM_SHADOW_FADE then dRate := 0.05
+  else if fadeRate >= MINIMUM_SHADOW_FADE then dRate := 0.95
+  else dRate := fadeRate/10;
+  alpha := AlphaComponent(shadowColor);
+  alphaLinear := alpha*dRate/maxD;
+  alphaExp := exp(ln(dRate)/maxD);
+
+  NoInteriorBleed := NoInteriorBleed and closed;
+  if NoInteriorBleed then
+  begin
+    originalBitmap := TBitmap32.Create;
+    originalBitmap.Assign(bitmap);
+    maskBitmap := CreateMaskFromPolygon(bitmap,pts, fillMode);
+  end else
+  begin
+    originalBitmap := nil;
+    maskBitmap := nil;
+  end;
+
+  try
+    a := alpha;
+    sc := shadowColor;
+    for j := 0 to high(pts) do
+    begin
+      alpha := a;
+      shadowColor := sc;
+      p := copy(pts[j], 0, length(pts[j]));
+      for i := 1 to maxD do
+      begin
+        PolyLineFS(bitmap, p, shadowColor, closed);
+        alpha := alpha * alphaExp;
+        if fadeRate < NO_SHADOW_FADE then
+          shadowColor := SetAlpha(shadowColor, round(alpha - i*alphaLinear));
+        OffsetPoints(p, sx, sy);
+      end;
+    end;
+    if assigned(originalBitmap) then
+      ApplyMask(bitmap, originalBitmap, maskBitmap);
+  finally
+    FreeAndNil(originalBitmap);
+    FreeAndNil(maskBitmap);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure Simple3D(bitmap: TBitmap32; const pts: TArrayOfArrayOfFloatPoint;
+  dx,dy,fadeRate: integer; topLeftColor, bottomRightColor: TColor32;
+  fillMode: TPolyFillMode = pfAlternate); overload;
+var
+  mask, orig: TBitmap32;
+begin
+  orig := TBitmap32.Create;
+  mask := CreateMaskFromPolygon(bitmap,pts, fillMode);
+  try
+    orig.Assign(bitmap);
+    SimpleShadow(bitmap, pts, -dx, -dy, fadeRate, bottomRightColor, true);
+    SimpleShadow(bitmap, pts, dx, dy, fadeRate, topLeftColor, true);
+    ApplyMask(bitmap, orig, mask, true);
+  finally
+    orig.Free;
+    mask.Free;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function GetCBezierPoints(const control_points: array of TFloatPoint): TArrayOfFloatPoint;
+var
+  i, j, arrayLen, resultCnt: integer;
+  ctrlPts: array [ 0..3] of TFloatPoint;
+const
+  cbezier_tolerance = 0.5;
+  half = 0.5;
+
+  procedure RecursiveCBezier(const p1, p2, p3, p4: TFloatPoint);
+  var
+    p12, p23, p34, p123, p234, p1234: TFloatPoint;
+  begin
+    //assess flatness of curve ...
+    //http://groups.google.com/group/comp.graphics.algorithms/tree/browse_frm/thread/d85ca902fdbd746e
+    if abs(p1.x + p3.x - 2*p2.x) + abs(p2.x + p4.x - 2*p3.x) +
+      abs(p1.y + p3.y - 2*p2.y) + abs(p2.y + p4.y - 2*p3.y) < cbezier_tolerance then
+    begin
+      if resultCnt = length(result) then
+        setLength(result, length(result) +128);
+      result[resultCnt] := p4;
+      inc(resultCnt);
+    end else
+    begin
+      p12.X := (p1.X + p2.X) *half;
+      p12.Y := (p1.Y + p2.Y) *half;
+      p23.X := (p2.X + p3.X) *half;
+      p23.Y := (p2.Y + p3.Y) *half;
+      p34.X := (p3.X + p4.X) *half;
+      p34.Y := (p3.Y + p4.Y) *half;
+      p123.X := (p12.X + p23.X) *half;
+      p123.Y := (p12.Y + p23.Y) *half;
+      p234.X := (p23.X + p34.X) *half;
+      p234.Y := (p23.Y + p34.Y) *half;
+      p1234.X := (p123.X + p234.X) *half;
+      p1234.Y := (p123.Y + p234.Y) *half;
+      RecursiveCBezier(p1, p12, p123, p1234);
+      RecursiveCBezier(p1234, p234, p34, p4);
+    end;
+  end;
+
+begin
+  //first check that the 'control_points' count is valid ...
+  arrayLen := length(control_points);
+  if (arrayLen < 4) or ((arrayLen -1) mod 3 <> 0) then exit;
+
+  setLength(result, 128);
+  result[0] := control_points[0];
+  resultCnt := 1;
+  for i := 0 to (arrayLen div 3)-1 do
+  begin
+    for j := 0 to 3 do
+      ctrlPts[j] := control_points[i*3 +j];
+    RecursiveCBezier(ctrlPts[0], ctrlPts[1], ctrlPts[2], ctrlPts[3]);
+  end;
+  SetLength(result,resultCnt);
+end;
+//------------------------------------------------------------------------------
+
+function GetEllipsePoints(const ellipseRect: TFloatRect): TArrayOfFloatPoint;
+const
+  //Magic constant =
+  //  2/3*(1-cos(90deg/2))/sin(90deg/2) = 2/3*(sqrt(2)-1) = 0.276142375 ...
+  offset: single = 0.276142375;
+var
+  midx, midy, offx, offy: single;
+  pts: array [0..12] of TFloatPoint;
+begin
+  with ellipseRect do
+  begin
+    if (abs(Left - Right) <= 0.5) and (abs(Top - Bottom) <= 0.5) then
+    begin
+      setlength(result,1);
+      result[0] := FloatPoint(Left,Top);
+      exit;
+    end;
+
+    midx := (right + left)/2;
+    midy := (bottom + top)/2;
+    offx := (right - left) * offset;
+    offy := (bottom - top) * offset;
+    //draws an ellipse starting at angle 0 and moving anti-clockwise ...
+    pts[0]  := FloatPoint(right, midy);
+    pts[1]  := FloatPoint(right, midy - offy);
+    pts[2]  := FloatPoint(midx + offx, top);
+    pts[3]  := FloatPoint(midx, top);
+    pts[4]  := FloatPoint(midx - offx, top);
+    pts[5]  := FloatPoint(left, midy - offy);
+    pts[6]  := FloatPoint(left, midy);
+    pts[7]  := FloatPoint(left, midy + offy);
+    pts[8]  := FloatPoint(midx - offx, bottom);
+    pts[9]  := FloatPoint(midx, bottom);
+    pts[10] := FloatPoint(midx + offx, bottom);
+    pts[11] := FloatPoint(right, midy + offy);
+    pts[12] := pts[0];
+  end;
+  result := GetCBezierPoints(pts);
+end;
+//------------------------------------------------------------------------------
+
+end.
diff --git a/Delphi/main demo/clipper_demo.dpr b/Delphi/main demo/clipper_demo.dpr
new file mode 100644
index 0000000..8f851b1
--- /dev/null
+++ b/Delphi/main demo/clipper_demo.dpr	
@@ -0,0 +1,15 @@
+program clipper_demo;
+
+uses
+  Forms,
+  main in 'main.pas' {MainForm},
+  GR32_Misc in 'GR32_Misc.pas',
+  clipper in '..\clipper.pas';
+
+{$R *.res}
+
+begin
+  Application.Initialize;
+  Application.CreateForm(TMainForm, MainForm);
+  Application.Run;
+end.
diff --git a/Delphi/main demo/clipper_demo.res b/Delphi/main demo/clipper_demo.res
new file mode 100644
index 0000000..7da8359
Binary files /dev/null and b/Delphi/main demo/clipper_demo.res differ
diff --git a/Delphi/main demo/main.dfm b/Delphi/main demo/main.dfm
new file mode 100644
index 0000000..9d09efa
--- /dev/null
+++ b/Delphi/main demo/main.dfm	
@@ -0,0 +1,394 @@
+object MainForm: TMainForm
+  Left = 235
+  Top = 110
+  Caption = 'Clipper Delphi Demo'
+  ClientHeight = 728
+  ClientWidth = 1012
+  Color = clBtnFace
+  Font.Charset = ARABIC_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -18
+  Font.Name = 'Arial'
+  Font.Style = []
+  KeyPreview = True
+  OldCreateOrder = False
+  Position = poDesktopCenter
+  OnCreate = FormCreate
+  OnKeyPress = FormKeyPress
+  OnMouseWheel = FormMouseWheel
+  OnResize = FormResize
+  PixelsPerInch = 144
+  TextHeight = 21
+  object Panel1: TPanel
+    Left = 0
+    Top = 0
+    Width = 256
+    Height = 709
+    Margins.Left = 4
+    Margins.Top = 4
+    Margins.Right = 4
+    Margins.Bottom = 4
+    Align = alLeft
+    TabOrder = 0
+    object lblClipOpacity: TLabel
+      Left = 24
+      Top = 571
+      Width = 149
+      Height = 21
+      Margins.Left = 4
+      Margins.Top = 4
+      Margins.Right = 4
+      Margins.Bottom = 4
+      Caption = 'Clip Opacity (255):'
+      FocusControl = tbClipOpacity
+    end
+    object lblSubjOpacity: TLabel
+      Left = 24
+      Top = 511
+      Width = 154
+      Height = 21
+      Margins.Left = 4
+      Margins.Top = 4
+      Margins.Right = 4
+      Margins.Bottom = 4
+      Caption = 'Subj &Opacity (255):'
+      FocusControl = tbSubjOpacity
+    end
+    object GroupBox1: TGroupBox
+      Left = 18
+      Top = 11
+      Width = 223
+      Height = 161
+      Margins.Left = 4
+      Margins.Top = 4
+      Margins.Right = 4
+      Margins.Bottom = 4
+      Caption = 'Clipping  Oper&ation'
+      TabOrder = 0
+      object rbIntersection: TRadioButton
+        Left = 20
+        Top = 53
+        Width = 158
+        Height = 24
+        Margins.Left = 4
+        Margins.Top = 4
+        Margins.Right = 4
+        Margins.Bottom = 4
+        Caption = 'Intersection'
+        Checked = True
+        TabOrder = 1
+        TabStop = True
+        OnClick = rbIntersectionClick
+      end
+      object rbUnion: TRadioButton
+        Left = 20
+        Top = 78
+        Width = 158
+        Height = 24
+        Margins.Left = 4
+        Margins.Top = 4
+        Margins.Right = 4
+        Margins.Bottom = 4
+        Caption = 'Union'
+        TabOrder = 2
+        OnClick = rbIntersectionClick
+      end
+      object rbDifference: TRadioButton
+        Left = 20
+        Top = 104
+        Width = 158
+        Height = 23
+        Margins.Left = 4
+        Margins.Top = 4
+        Margins.Right = 4
+        Margins.Bottom = 4
+        Caption = 'Difference'
+        TabOrder = 3
+        OnClick = rbIntersectionClick
+      end
+      object rbXOR: TRadioButton
+        Left = 20
+        Top = 129
+        Width = 158
+        Height = 24
+        Margins.Left = 4
+        Margins.Top = 4
+        Margins.Right = 4
+        Margins.Bottom = 4
+        Caption = 'XOR'
+        TabOrder = 4
+        OnClick = rbIntersectionClick
+      end
+      object rbNone: TRadioButton
+        Left = 20
+        Top = 28
+        Width = 158
+        Height = 24
+        Margins.Left = 4
+        Margins.Top = 4
+        Margins.Right = 4
+        Margins.Bottom = 4
+        Caption = 'None'
+        TabOrder = 0
+        OnClick = rbIntersectionClick
+      end
+    end
+    object rbStatic: TRadioButton
+      Left = 22
+      Top = 181
+      Width = 161
+      Height = 23
+      Margins.Left = 4
+      Margins.Top = 4
+      Margins.Right = 4
+      Margins.Bottom = 4
+      Caption = '&Static Polygons'
+      Checked = True
+      TabOrder = 1
+      TabStop = True
+      OnClick = rbStaticClick
+    end
+    object bExit: TButton
+      Left = 153
+      Top = 634
+      Width = 72
+      Height = 35
+      Margins.Left = 4
+      Margins.Top = 4
+      Margins.Right = 4
+      Margins.Bottom = 4
+      Cancel = True
+      Caption = 'E&xit'
+      TabOrder = 7
+      OnClick = bExitClick
+    end
+    object gbRandom: TGroupBox
+      Left = 15
+      Top = 258
+      Width = 223
+      Height = 236
+      Margins.Left = 4
+      Margins.Top = 4
+      Margins.Right = 4
+      Margins.Bottom = 4
+      TabOrder = 4
+      object lblSubjCount: TLabel
+        Left = 6
+        Top = 56
+        Width = 189
+        Height = 21
+        Margins.Left = 4
+        Margins.Top = 4
+        Margins.Right = 4
+        Margins.Bottom = 4
+        Caption = 'No. Subject edges: (20)'
+        Enabled = False
+        FocusControl = tbSubj
+      end
+      object lblClipCount: TLabel
+        Left = 6
+        Top = 122
+        Width = 160
+        Height = 21
+        Margins.Left = 4
+        Margins.Top = 4
+        Margins.Right = 4
+        Margins.Bottom = 4
+        Caption = 'No. Clip edges (20):'
+        Enabled = False
+        FocusControl = tbClip
+      end
+      object tbSubj: TTrackBar
+        Left = 7
+        Top = 81
+        Width = 203
+        Height = 39
+        Margins.Left = 4
+        Margins.Top = 4
+        Margins.Right = 4
+        Margins.Bottom = 4
+        Enabled = False
+        Max = 100
+        Min = 3
+        Position = 20
+        TabOrder = 2
+        ThumbLength = 16
+        TickStyle = tsNone
+        OnChange = tbSubjChange
+      end
+      object tbClip: TTrackBar
+        Left = 7
+        Top = 148
+        Width = 203
+        Height = 40
+        Margins.Left = 4
+        Margins.Top = 4
+        Margins.Right = 4
+        Margins.Bottom = 4
+        Enabled = False
+        Max = 100
+        Min = 3
+        Position = 20
+        TabOrder = 3
+        ThumbLength = 16
+        TickStyle = tsNone
+        OnChange = tbSubjChange
+      end
+      object bNext: TButton
+        Left = 14
+        Top = 185
+        Width = 188
+        Height = 35
+        Margins.Left = 4
+        Margins.Top = 4
+        Margins.Right = 4
+        Margins.Bottom = 4
+        Caption = '&New Polygons'
+        TabOrder = 4
+        OnClick = bNextClick
+      end
+      object rbEvenOdd: TRadioButton
+        Left = 7
+        Top = 20
+        Width = 102
+        Height = 23
+        Margins.Left = 4
+        Margins.Top = 4
+        Margins.Right = 4
+        Margins.Bottom = 4
+        Caption = 'E&venOdd'
+        Checked = True
+        Enabled = False
+        TabOrder = 0
+        TabStop = True
+        OnClick = rbEvenOddClick
+      end
+      object rbNonZero: TRadioButton
+        Left = 115
+        Top = 20
+        Width = 96
+        Height = 23
+        Margins.Left = 4
+        Margins.Top = 4
+        Margins.Right = 4
+        Margins.Bottom = 4
+        Caption = 'Non&Zero'
+        Enabled = False
+        TabOrder = 1
+        OnClick = rbEvenOddClick
+      end
+    end
+    object rbRandom1: TRadioButton
+      Left = 22
+      Top = 204
+      Width = 205
+      Height = 24
+      Margins.Left = 4
+      Margins.Top = 4
+      Margins.Right = 4
+      Margins.Bottom = 4
+      Caption = 'Random Polygons &1'
+      TabOrder = 2
+      OnClick = rbStaticClick
+    end
+    object tbClipOpacity: TTrackBar
+      Left = 17
+      Top = 595
+      Width = 221
+      Height = 39
+      Margins.Left = 4
+      Margins.Top = 4
+      Margins.Right = 4
+      Margins.Bottom = 4
+      Max = 255
+      Position = 255
+      TabOrder = 6
+      ThumbLength = 16
+      TickStyle = tsNone
+      OnChange = tbClipOpacityChange
+    end
+    object tbSubjOpacity: TTrackBar
+      Left = 17
+      Top = 535
+      Width = 221
+      Height = 39
+      Margins.Left = 4
+      Margins.Top = 4
+      Margins.Right = 4
+      Margins.Bottom = 4
+      Max = 255
+      Position = 255
+      TabOrder = 5
+      ThumbLength = 16
+      TickStyle = tsNone
+      OnChange = tbSubjOpacityChange
+    end
+    object rbRandom2: TRadioButton
+      Left = 22
+      Top = 230
+      Width = 205
+      Height = 23
+      Margins.Left = 4
+      Margins.Top = 4
+      Margins.Right = 4
+      Margins.Bottom = 4
+      Caption = 'Random Polygons &2'
+      TabOrder = 3
+      OnClick = rbStaticClick
+    end
+    object bSaveSvg: TButton
+      Left = 27
+      Top = 634
+      Width = 114
+      Height = 35
+      Margins.Left = 4
+      Margins.Top = 4
+      Margins.Right = 4
+      Margins.Bottom = 4
+      Cancel = True
+      Caption = 'Save S&VG ...'
+      TabOrder = 8
+      OnClick = bSaveSvgClick
+    end
+  end
+  object StatusBar1: TStatusBar
+    Left = 0
+    Top = 709
+    Width = 1012
+    Height = 19
+    Margins.Left = 4
+    Margins.Top = 4
+    Margins.Right = 4
+    Margins.Bottom = 4
+    Panels = <>
+    SimplePanel = True
+  end
+  object ImgView321: TImgView32
+    Left = 256
+    Top = 0
+    Width = 756
+    Height = 709
+    Margins.Left = 4
+    Margins.Top = 4
+    Margins.Right = 4
+    Margins.Bottom = 4
+    Align = alClient
+    Bitmap.ResamplerClassName = 'TNearestResampler'
+    BitmapAlign = baCustom
+    Scale = 1.000000000000000000
+    ScaleMode = smScale
+    ScrollBars.ShowHandleGrip = True
+    ScrollBars.Style = rbsDefault
+    ScrollBars.Size = 16
+    OverSize = 0
+    TabOrder = 2
+    OnDblClick = bNextClick
+    OnResize = ImgView321Resize
+  end
+  object SaveDialog1: TSaveDialog
+    DefaultExt = 'svg'
+    Filter = 'SVG Files (*.svg)|*.svg'
+    Left = 239
+    Top = 32
+  end
+end
diff --git a/Delphi/main demo/main.pas b/Delphi/main demo/main.pas
new file mode 100644
index 0000000..d9978a0
--- /dev/null
+++ b/Delphi/main demo/main.pas	
@@ -0,0 +1,797 @@
+unit main;
+
+(*******************************************************************************
+*                                                                              *
+* Author    :  Angus Johnson                                                   *
+* Website   :  http://www.angusj.com                                           *
+* Copyright :  Angus Johnson 2010-2011                                         *
+*                                                                              *
+* License:                                                                     *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt                                         *
+*                                                                              *
+*******************************************************************************)
+
+interface
+
+uses
+  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+  Dialogs, StdCtrls, ComCtrls, ExtCtrls, Math,
+  //requires Graphics32 (revision 2180 or later) ...
+  //https://sourceforge.net/p/graphics32/code/HEAD/tree/trunk/Source/
+  GR32, GR32_Image, GR32_Polygons,
+  GR32_Misc, clipper;
+
+type
+
+  //SVG Builder structures ////////////////////////////////////////
+
+  TStyleInfo = record
+    pft: TPolyFillType;
+    brushClr: TColor32;
+    penClr: TColor32;
+    dashArray: TArrayOfInteger;
+    penWidth: double;
+    showCoords: boolean;
+  end;
+
+  TPolyInfo = record
+    paths: TPaths;
+    si: TStyleInfo;
+  end;
+  TPolyInfos = array of TPolyInfo;
+
+  TTextInfo = record
+    text: string;
+    x,y: integer;
+    fontName: string;
+    fontSize: integer;
+    fontColor: string;
+  end;
+  TTextInfos = array of TTextInfo;
+
+  /////////////////////////////////////////////////////////////////
+
+  TSvgBuilder = class
+  private
+    polyList: TPolyInfos;
+    textList: TTextInfos;
+    function Color32ToHtml(clr: TColor32): string;
+  public
+    fontName: string;
+    fontSize: integer;
+    fontColor: TColor32;
+    style: TStyleInfo;
+    constructor Create;
+    procedure Clear;
+    procedure AddPaths(const poly: TPaths);
+    procedure AddText(const text: string; X,Y: integer);
+    function SaveToFile(filename: string;
+      scale: double = 1.0; margin: integer = 10): boolean;
+  end;
+
+  TMainForm = class(TForm)
+    Panel1: TPanel;
+    StatusBar1: TStatusBar;
+    ImgView321: TImgView32;
+    GroupBox1: TGroupBox;
+    rbIntersection: TRadioButton;
+    rbUnion: TRadioButton;
+    rbDifference: TRadioButton;
+    rbXOR: TRadioButton;
+    rbStatic: TRadioButton;
+    bExit: TButton;
+    rbNone: TRadioButton;
+    gbRandom: TGroupBox;
+    lblSubjCount: TLabel;
+    lblClipCount: TLabel;
+    tbSubj: TTrackBar;
+    tbClip: TTrackBar;
+    rbRandom1: TRadioButton;
+    bNext: TButton;
+    tbClipOpacity: TTrackBar;
+    lblClipOpacity: TLabel;
+    lblSubjOpacity: TLabel;
+    tbSubjOpacity: TTrackBar;
+    rbRandom2: TRadioButton;
+    rbEvenOdd: TRadioButton;
+    rbNonZero: TRadioButton;
+    bSaveSvg: TButton;
+    SaveDialog1: TSaveDialog;
+    procedure FormCreate(Sender: TObject);
+    procedure ImgView321Resize(Sender: TObject);
+    procedure rbIntersectionClick(Sender: TObject);
+    procedure FormResize(Sender: TObject);
+    procedure tbSubjChange(Sender: TObject);
+    procedure bNextClick(Sender: TObject);
+    procedure bExitClick(Sender: TObject);
+    procedure tbClipOpacityChange(Sender: TObject);
+    procedure rbStaticClick(Sender: TObject);
+    procedure tbSubjOpacityChange(Sender: TObject);
+    procedure rbEvenOddClick(Sender: TObject);
+    procedure FormMouseWheel(Sender: TObject; Shift: TShiftState;
+      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
+    procedure FormKeyPress(Sender: TObject; var Key: Char);
+    procedure bSaveSvgClick(Sender: TObject);
+  private
+    offsetMul2: integer; //accommodates 0.5 steps
+    function GetFillTypeI: TPolyFillType;
+    function GetOpTypeI: TClipType;
+    procedure ShowStaticPolys;
+    procedure ShowRandomPolys1(newPoly: boolean);
+    procedure ShowRandomPolys2(newPoly: boolean);
+    procedure RePaintBitmapI;
+  public
+    { Public declarations }
+  end;
+
+var
+  MainForm: TMainForm;
+
+implementation
+
+//------------------------------------------------------------------------------
+// TSvgBuilder
+//------------------------------------------------------------------------------
+
+constructor TSvgBuilder.Create;
+begin
+  fontName := 'Verdana';
+  fontSize := 12;
+  fontColor := clBlack32;
+  style.brushClr := clWhite32;
+  style.penClr := clBlack32;
+  style.penWidth := 1.5;
+end;
+//------------------------------------------------------------------------------
+
+procedure TSvgBuilder.Clear;
+begin
+  polyList := nil;
+  textList := nil;
+end;
+//------------------------------------------------------------------------------
+
+function TSvgBuilder.Color32ToHtml(clr: TColor32): string;
+begin
+  with TColor32Entry(clr) do
+    result := format('#%.2x%.2x%.2x', [R, G, B]) ;
+end;
+//------------------------------------------------------------------------------
+
+function IntArrayToStr(a: TArrayOfInteger; scale: double = 1.0;
+  dx: integer = 0; dy: integer = 0): string;
+var
+  i: integer;
+begin
+  result := format('%1.1n',[a[0] * scale + dx]);
+  for i := 1 to high(a) do
+    if odd(i) then
+      result := result + format(', %1.1n',[a[i] * scale + dy]) else
+      result := result + format(', %1.1n',[a[i] * scale + dx]);
+end;
+//------------------------------------------------------------------------------
+
+procedure TSvgBuilder.AddPaths(const poly: TPaths);
+var
+  i, len: integer;
+begin
+  i := length(poly);
+  if i = 0 then Exit;
+  len := length(polyList);
+  setlength(polyList, len+1);
+  setlength(polyList[len].paths, i);
+  for i := 0 to i-1 do
+    polyList[len].paths[i] := Copy(poly[i], 0, MaxInt);
+  polyList[len].si.pft := style.pft;
+  polyList[len].si.brushClr := style.brushClr;
+  polyList[len].si.penClr := style.penClr;
+  polyList[len].si.dashArray := copy(style.dashArray, 0, maxint);
+  polyList[len].si.penWidth := style.penWidth;
+  polyList[len].si.showCoords := style.showCoords;
+end;
+//------------------------------------------------------------------------------
+
+procedure TSvgBuilder.AddText(const text: string; X,Y: integer);
+var
+  len: integer;
+begin
+  len := length(textList);
+  setlength(textList, len +1);
+  textList[len].text := text;
+  textList[len].x := X;
+  textList[len].y := Y;
+  textList[len].fontName := fontName;
+  textList[len].fontSize := fontSize;
+  textList[len].fontColor := Color32ToHtml(fontColor);
+end;
+//------------------------------------------------------------------------------
+
+function TSvgBuilder.SaveToFile(filename: string;
+  scale: double = 1.0; margin: integer = 10): boolean;
+const
+  pft_string: array[TPolyFillType] of string = ('evenodd', 'nonzero', 'positive', 'negative');
+  svg_xml_start: array [0..1] of string =
+    ('<?xml version="1.0" standalone="no"?>'+#10+
+     '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"'+#10+
+     '"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'+#10+#10+'<svg ',
+     'version="1.1" xmlns="http://www.w3.org/2000/svg">'+#10+#10);
+  poly_start: string = ' <path d="';
+  svg_xml_end: string = '</svg>'+#10;
+var
+  i,j,k,len,len2: integer;
+  offsetX, offsetY: Int64;
+  rec: TIntRect;
+  ds: char;
+  ss: TStringStream;
+  dashArrayStr: string;
+begin
+  result := false;
+  len := length(polyList);
+  if len = 0 then Exit;
+
+  if (scale = 0) then scale := 1.0;
+  if (margin < 0) then margin := 0;
+  i := 0; j := 0;
+  //calculate the bounding rect (skipping empty polygons) ...
+  while i < len do
+  begin
+    len2 := length(polyList[i].paths);
+    j := 0;
+    while (j < len2) and (length(polyList[i].paths[j]) < 3) do inc(j);
+    if j < len2 then Break;
+    inc(i);
+  end;
+  if i = len then Exit;
+  rec.left := polyList[i].paths[j][0].X;
+  rec.right := rec.left;
+  rec.top := polyList[i].paths[j][0].Y;
+  rec.bottom := rec.top;
+  for i := i to len -1 do
+    for j := 0 to length(polyList[i].paths) -1 do
+      for k := 0 to length(polyList[i].paths[j]) -1 do
+        with polyList[i].paths[j][k] do
+      begin
+        if X < rec.left then rec.left := X
+        else if X > rec.right then rec.right := X;
+        if Y < rec.top then rec.top := Y
+        else if Y > rec.bottom then rec.bottom := Y;
+      end;
+  rec.left := round(rec.left * scale);
+  rec.top := round(rec.top * scale);
+  rec.right := round(rec.right * scale);
+  rec.bottom := round(rec.bottom * scale);
+  offsetX := -rec.left + margin;
+  offsetY := -rec.top + margin;
+
+  ds := DecimalSeparator;
+  DecimalSeparator := '.';
+  ss := TStringStream.Create('');
+  try
+    ss.WriteString(
+      format('%s width="%dpx" height="%dpx" viewBox="0 0 %d %d" %s',
+      [svg_xml_start[0],
+      (rec.right - rec.left) + margin*2,
+      (rec.bottom - rec.top) + margin*2,
+      (rec.right - rec.left) + margin*2,
+      (rec.bottom - rec.top) + margin*2,
+      svg_xml_start[1]]));
+
+    for i := 0 to len -1 do
+      if assigned(polyList[i].paths) then
+      begin
+        ss.WriteString(poly_start);
+        for j := 0 to high(polyList[i].paths) do
+        begin
+          if (length(polyList[i].paths[j]) < 3) then continue;
+          with polyList[i].paths[j][0] do
+            ss.WriteString( format(' M %1.2f %1.2f',
+            [X * scale + offsetX, Y * scale + offsetY]));
+          for k := 1 to high(polyList[i].paths[j]) do
+            with polyList[i].paths[j][k] do
+              ss.WriteString( format(' L %1.2f %1.2f',
+                [X * scale + offsetX, Y * scale + offsetY]));
+          ss.WriteString(' z');
+        end;
+
+        with polyList[i].si do
+        begin
+          if length(dashArray) > 1 then
+            dashArrayStr := 'stroke-dasharray:'+ IntArrayToStr(dashArray) +';'
+          else
+            dashArrayStr := '';
+          ss.WriteString(format('"'#10+
+            ' style="fill:%s; fill-opacity:%1.2n; fill-rule:%s;'#10+
+            ' stroke:%s; stroke-opacity:%1.2n; %s stroke-width:%1.2n;"/>'#10#10,
+              [Color32ToHtml(brushClr), AlphaComponent(brushClr)/255,
+              pft_string[pft], Color32ToHtml(penClr), AlphaComponent(penClr)/255,
+              dashArrayStr, penWidth]));
+        end;
+
+        if polyList[i].si.showCoords then
+        begin
+          ss.WriteString('<g font-family="Verdana" font-size="11" fill="black">'#10#10);
+          for j := 0 to high(polyList[i].paths) do
+          begin
+            if (length(polyList[i].paths[j]) < 3) then continue;
+            for k := 0 to high(polyList[i].paths[j]) do
+              with polyList[i].paths[j][k] do
+                ss.WriteString(format('<text x="%1.0n" y="%1.0n">%1.0n,%1.0n</text>'#10,
+                  [X * scale + offsetX,  Y * scale + offsetY, X, Y]));
+            ss.WriteString(#10);
+          end;
+          ss.WriteString('</g>'#10);
+        end;
+      end;
+
+      for i := 0 to high(textList) do
+        with textList[i] do
+        begin
+          if fontSize < 7 then fontSize := 7 else if fontSize > 30 then fontSize := 30;
+          ss.WriteString(format('<g font-family="%s" font-size="%d" fill="%s">'#10,
+            [fontName, fontSize, fontColor]));
+          ss.WriteString(format('<text x="%1.0n" y="%1.0n">%s</text>'#10'</g>'#10#10,
+            [X * scale + offsetX,  Y * scale + offsetY, text]));
+        end;
+
+    ss.WriteString(svg_xml_end);
+    //finally write to file ...
+    with TFileStream.Create(filename, fmCreate) do
+    try CopyFrom(ss, 0); finally free; end;
+  finally
+    ss.Free;
+    DecimalSeparator := ds;
+  end;
+
+end;
+
+//------------------------------------------------------------------------------
+// Miscellaneous functions ...
+//------------------------------------------------------------------------------
+
+const
+  subjPenColor: TColor32 = $60C3C9CF;
+  subjBrushColor: TColor32 = $00DDDDF0;
+  clipPenColor: TColor32 = $30F9BEA6;
+  clipBrushColor: TColor32 = $00FFE0E0;
+  solPenColor: TColor32 = $7F003300;
+  solBrushColor: TColor32 = $8066EF7F;
+
+var
+  scale: integer = 1; //scale bitmap to 10 decimal places
+  subj: TArrayOfArrayOfFloatPoint = nil;
+  clip: TArrayOfArrayOfFloatPoint = nil;
+  subjI: TPaths = nil;
+  clipI: TPaths = nil;
+  solution: TArrayOfArrayOfFloatPoint = nil;
+  solutionI: TPaths = nil;
+  subjOpacity: cardinal = $FF000000;
+  clipOpacity: cardinal = $FF000000;
+
+{$R *.dfm}
+{$R polygons.res}
+
+//------------------------------------------------------------------------------
+
+function AAFloatPoint2AAPoint(const a: TArrayOfArrayOfFloatPoint;
+  decimals: integer = 0): TPaths;
+var
+  i,j,decScale: integer;
+begin
+  decScale := round(power(10,decimals));
+  setlength(result, length(a));
+  for i := 0 to high(a) do
+  begin
+    setlength(result[i], length(a[i]));
+    for j := 0 to high(a[i]) do
+    begin
+      result[i][j].X := round(a[i][j].X *decScale);
+      result[i][j].Y := round(a[i][j].Y *decScale);
+    end;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function AAPoint2AAFloatPoint(const a: TPaths;
+  decimals: integer = 0): TArrayOfArrayOfFloatPoint;
+var
+  i,j,decScale: integer;
+begin
+  decScale := round(power(10,decimals));
+  setlength(result, length(a));
+  for i := 0 to high(a) do
+  begin
+    setlength(result[i], length(a[i]));
+    for j := 0 to high(a[i]) do
+    begin
+      result[i][j].X := a[i][j].X /decScale;
+      result[i][j].Y := a[i][j].Y /decScale;
+    end;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure LoadBinaryStreamToArrayOfArrayOfFloatPoint(stream: TStream;
+  out fpa: TArrayOfArrayOfFloatPoint);
+var
+  i,j: integer;
+begin
+  try
+    stream.Read(i, sizeof(i));
+    setlength(fpa, i);
+    for i := 0 to i-1 do
+    begin
+      stream.Read(j, sizeof(j));
+      setlength(fpa[i], j);
+      for j := 0 to j-1 do
+        stream.Read(fpa[i][j], sizeof(TFloatPoint));
+    end;
+  except
+    fpa := nil;
+  end;
+end;
+
+//------------------------------------------------------------------------------
+//  TMainForm methods
+//------------------------------------------------------------------------------
+
+procedure TMainForm.FormCreate(Sender: TObject);
+begin
+  tbSubjOpacity.Position := 156;
+  tbClipOpacity.Position := 156;
+  Randomize;
+  StatusBar1.SimpleText :=
+    ' Use the mouse wheel (or +,- & 0) to adjust the clipped region''s offset.';
+  ImgView321.Bitmap.Font.Style := [fsBold];
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.bExitClick(Sender: TObject);
+begin
+  close;
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.ImgView321Resize(Sender: TObject);
+begin
+  ImgView321.SetupBitmap(true, clWhite32);
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.RepaintBitmapI;
+var
+  pfm: TPolyFillMode;
+  sol: TArrayOfArrayOfFloatPoint;
+  solI: TPaths;
+  scaling: single;
+begin
+  ImgView321.Bitmap.Clear(clWhite32);
+
+  if rbEvenOdd.Checked then pfm := pfAlternate else pfm := pfWinding;
+  PolyPolygonFS(ImgView321.Bitmap, subj, subjBrushColor or subjOpacity, pfm);
+  PolyPolylineFS(ImgView321.Bitmap, subj, subjPenColor or subjOpacity, true);
+  PolyPolygonFS(ImgView321.Bitmap, clip, clipBrushColor or clipOpacity, pfm);
+  PolyPolylineFS(ImgView321.Bitmap, clip, clipPenColor or clipOpacity, true);
+  if assigned(solutionI) and not rbNone.Checked then
+  begin
+    if offsetMul2 = 0 then
+    begin
+      sol := AAPoint2AAFloatPoint(solutionI, scale);
+    end else
+    begin
+      //do offsetting ...
+      sol := AAPoint2AAFloatPoint(solutionI, scale);
+      PolyPolylineFS(ImgView321.Bitmap, sol, clGray32, true);
+      scaling := power(10, scale);
+      with TClipperOffset.Create() do
+      try
+        AddPaths(solutionI, jtRound, etClosedPolygon);
+        Execute(solI, offsetMul2/2 *scaling);
+      finally
+        Free;
+      end;
+      sol := AAPoint2AAFloatPoint(solI, scale);
+    end;
+    PolyPolygonFS(ImgView321.Bitmap, sol, solBrushColor);
+
+    //now add a 3D effect to the solution to make it stand out ...
+    Simple3D(ImgView321.Bitmap, sol, 3, 3, MAXIMUM_SHADOW_FADE, clWhite32, clBlack32);
+    PolyPolylineFS(ImgView321.Bitmap, sol, solPenColor, true);
+  end;
+  with ImgView321.Bitmap do
+  begin
+    Textout(10, height-20, format('Offset = %1.1n pixels',[offsetMul2/2]));
+  end;
+  ImgView321.Repaint;
+end;
+//------------------------------------------------------------------------------
+
+function TMainForm.GetFillTypeI: TPolyFillType;
+begin
+  if rbEvenOdd.checked then
+    result := pftEvenOdd else
+    result := pftNonZero;
+end;
+//------------------------------------------------------------------------------
+
+function TMainForm.GetOpTypeI: TClipType;
+begin
+  if rbIntersection.Checked then result := ctIntersection
+  else if rbUnion.Checked then result := ctUnion
+  else if rbDifference.Checked then result := ctDifference
+  else result := ctXor;
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.tbSubjOpacityChange(Sender: TObject);
+begin
+  lblSubjOpacity.Caption := format('Subj &Opacity (%d):',[tbSubjOpacity.Position]);
+  subjOpacity := cardinal(tbSubjOpacity.Position) shl 24;
+  RePaintBitmapI;
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.tbClipOpacityChange(Sender: TObject);
+begin
+  lblClipOpacity.Caption := format('Clip &Opacity (%d):',[tbClipOpacity.Position]);
+  clipOpacity := cardinal(tbClipOpacity.Position) shl 24;
+  RePaintBitmapI;
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.rbStaticClick(Sender: TObject);
+begin
+  if rbStatic.Checked then
+    ShowStaticPolys
+  else if rbRandom1.Checked then
+    ShowRandomPolys1(true)
+  else
+    ShowRandomPolys2(true);
+
+  rbNonZero.Enabled := not rbStatic.Checked;
+  rbEvenOdd.Enabled := not rbStatic.Checked;
+  lblSubjCount.Enabled := rbRandom1.Checked;
+  tbSubj.Enabled := rbRandom1.Checked;
+  tbClip.Enabled := not rbStatic.Checked;
+  lblClipCount.Enabled := not rbStatic.Checked;
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.rbIntersectionClick(Sender: TObject);
+begin
+  if rbStatic.Checked then ShowStaticPolys
+  else if rbRandom1.Checked then ShowRandomPolys1(false)
+  else ShowRandomPolys2(false);
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.bNextClick(Sender: TObject);
+begin
+  if not bNext.Enabled then exit;
+  if rbRandom1.Checked then ShowRandomPolys1(true)
+  else ShowRandomPolys2(true);
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.FormResize(Sender: TObject);
+begin
+  if visible then rbIntersectionClick(nil);
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.tbSubjChange(Sender: TObject);
+begin
+  lblSubjCount.Caption := format('Random Subj Count (%d):',[tbSubj.Position]);
+  lblClipCount.Caption := format('Random Clip Count (%d):',[tbClip.Position]);
+  if not bNext.Enabled then exit;
+  //only update random polygons once the mouse has been released ...
+  if (GetAsyncKeyState(VK_LBUTTON) < 0) then exit;
+  if rbRandom1.Checked then ShowRandomPolys1(true)
+  else ShowRandomPolys2(true);
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.rbEvenOddClick(Sender: TObject);
+begin
+  if rbRandom1.Checked then ShowRandomPolys1(false)
+  else ShowRandomPolys2(false);
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.ShowStaticPolys;
+var
+  rs: TResourceStream;
+begin
+  solution := nil;
+  rs := TResourceStream.Create(HInstance, 'POLYGON', RT_RCDATA);
+  LoadBinaryStreamToArrayOfArrayOfFloatPoint(rs, subj);
+  rs.Free;
+
+  rs := TResourceStream.Create(HInstance, 'CLIP', RT_RCDATA);
+  LoadBinaryStreamToArrayOfArrayOfFloatPoint(rs, clip);
+  rs.Free;
+
+  subjI := AAFloatPoint2AAPoint(subj, scale);
+  clipI := AAFloatPoint2AAPoint(clip, scale);
+
+  if not rbNone.Checked then
+    with TClipper.Create do
+    try
+      AddPaths(subjI, ptSubject, true);
+      AddPaths(clipI, ptClip, true);
+      Execute(GetOpTypeI, solutionI, pftNonZero, pftNonZero);
+    finally
+      free;
+    end;
+  RepaintBitmapI;
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.ShowRandomPolys1(newPoly: boolean);
+var
+  i,highI,w,h: integer;
+  fillType: TPolyFillType;
+begin
+  w := (ImgView321.ClientWidth -30);
+  h := (ImgView321.ClientHeight -30);
+  fillType := GetFillTypeI;
+
+  if newPoly then
+  begin
+    solution := nil;
+    //nb: although for this demo I chose to display just one random subject
+    //and one random clip polygon, it would be very easy to make multiple
+    //subject and clip polygons here. Clipper would handle them just as easily
+    //(as is demonstrated in ShowStaticPolys).
+    setLength(subj, 1);
+    highI := tbSubj.Position -1;
+    setLength(subj[0], highI+1);
+    for i := 0 to highI do
+      subj[0][i] := FloatPoint(10+round(random*w), 10+round(random*h));
+    setLength(clip, 1);
+    highI := tbClip.Position - 1;
+    setLength(clip[0], highI+1);
+    for i := 0 to highI do
+      clip[0][i] := FloatPoint(10+round(random*w), 10+round(random*h));
+  end;
+
+  subjI := AAFloatPoint2AAPoint(subj, scale);
+  clipI := AAFloatPoint2AAPoint(clip, scale);
+
+  if not rbNone.Checked then
+    with TClipper.Create do
+    try
+      AddPaths(subjI, ptSubject, true);
+      AddPaths(clipI, ptClip, true);
+      Execute(GetOpTypeI, solutionI, fillType, fillType);
+    finally
+      free;
+    end;
+  RepaintBitmapI;
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.ShowRandomPolys2(newPoly: boolean);
+var
+  i,j,w,h: integer;
+  pt: TFloatPoint;
+  rec: TFloatRect;
+  fillType: TPolyFillType;
+  rs: TResourceStream;
+begin
+
+  w := (ImgView321.ClientWidth -30);
+  h := (ImgView321.ClientHeight -30);
+  fillType := GetFillTypeI;
+
+  if newPoly then
+  begin
+    solution := nil;
+
+    rs := TResourceStream.Create(HInstance, 'AUSTRALIA', RT_RCDATA);
+    LoadBinaryStreamToArrayOfArrayOfFloatPoint(rs, subj);
+    rs.Free;
+
+    //make bubbles for clip ...
+    setlength(clip, tbClip.Position);
+    for i := 0 to high(clip) do
+    begin
+      pt := FloatPoint(random*(w-100) +50, random*(h-100) +50);
+      j := round(random*45) + 5;
+      rec := FloatRect(pt.X -j, pt.Y - j, pt.X +j, pt.Y + j);
+      clip[i] := GetEllipsePoints(rec);
+    end;
+  end;
+
+  subjI := AAFloatPoint2AAPoint(subj, scale);
+  clipI := AAFloatPoint2AAPoint(clip, scale);
+
+  if not rbNone.Checked then
+    with TClipper.Create do
+    try
+      AddPaths(subjI, ptSubject, true);
+      AddPaths(clipI, ptClip, true);
+      Execute(GetOpTypeI, solutionI, fillType, fillType);
+    finally
+      free;
+    end;
+  RepaintBitmapI;
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.FormMouseWheel(Sender: TObject; Shift: TShiftState;
+  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
+begin
+  if WheelDelta > 0 then
+  begin
+    if offsetMul2 = 20 then exit;
+    inc(offsetMul2);
+    RePaintBitmapI;
+  end
+  else if WheelDelta < 0 then
+  begin
+    if offsetMul2 = -20 then exit;
+    dec(offsetMul2);
+    RePaintBitmapI;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.FormKeyPress(Sender: TObject; var Key: Char);
+begin
+  case Key of
+    '0',')': offsetMul2 := 0;
+    '=','+': if offsetMul2 = 20 then exit else inc(offsetMul2);
+    '-','_': if offsetMul2 = -20 then exit else dec(offsetMul2);
+    else exit;
+  end;
+  RePaintBitmapI;
+end;
+//------------------------------------------------------------------------------
+
+function MakeArrayOfIntPoint(const pts: array of integer): TPath;
+var
+  i, len: integer;
+begin
+  result := nil;
+  len := length(pts) div 2;
+  if len < 1 then exit;
+  setlength(result, len);
+  for i := 0 to len -1 do
+  begin
+    result[i].X := pts[i*2];
+    result[i].Y := pts[i*2 +1];
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TMainForm.bSaveSvgClick(Sender: TObject);
+var
+  invScale: single;
+begin
+  if not SaveDialog1.Execute then exit;
+  invScale := 1/ power(10, scale);
+  with TSvgBuilder.Create do
+  try
+    style.penWidth := 0.8;
+
+    style.brushClr := $0F0000FF;
+    style.penClr := $800099FF;
+    AddPaths(subjI);
+
+    style.brushClr := $0FFFFF00;
+    style.penClr := $80FF9900;
+    AddPaths(clipI);
+
+    style.brushClr := $2000FF00;
+    style.penClr := $FF006600;
+    AddPaths(solutionI);
+
+    SaveToFile(SaveDialog1.FileName, invScale);
+  finally
+    free;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+end.
diff --git a/Delphi/main demo/polygons.res b/Delphi/main demo/polygons.res
new file mode 100644
index 0000000..272463a
Binary files /dev/null and b/Delphi/main demo/polygons.res differ
diff --git a/Documentation/offset_triginometry.svg b/Documentation/offset_triginometry.svg
new file mode 100644
index 0000000..67e8bd3
--- /dev/null
+++ b/Documentation/offset_triginometry.svg
@@ -0,0 +1,42 @@
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="600" width="800" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <g transform="translate(0,-452.4)">
+  <g stroke="#000" fill="none">
+   <path style="color:#000000" stroke-width="1.961" stroke-dasharray="3.9224, 3.9224" d="m193.9 735.7-26.4 46.4c7.4 4.6 16.2 7.3 25.6 7.3 10.4 0 20.2-3.3 28.1-8.9l-27.3-44.8z"/>
+   <path style="color:#000000" stroke-width="1.8" stroke-dasharray="3.6, 3.6" d="m194.5 733.4 57.2-98.9"/>
+   <path style="color:#000000" stroke-width="1.8" d="m10.81 854.2 183.3-314.5 190.3 308.5"/>
+  </g>
+  <g stroke="#000" fill="#0f0">
+   <g stroke-width="2.138">
+    <path style="color:#000000" d="m194.2 635.6 15 21.2h-10v56.4h10l-15 21.2-15-21.2h10v-56.4h-10z"/>
+    <path style="color:#000000" d="m77.71 740.9 25.79 2.7-6.59 7.5 42.49 37.2 6.6-7.5 6 25.3-25.8-2.7 6.6-7.5-42.49-37.3-6.6 7.5z"/>
+    <path style="color:#000000" d="m194.2 635.6 15 21.2h-10v56.4h10l-15 21.2-15-21.2h10v-56.4h-10z"/>
+    <path style="color:#000000" d="m194.2 635.6 15 21.2h-10v56.4h10l-15 21.2-15-21.2h10v-56.4h-10z"/>
+    <path style="color:#000000" d="m194.2 635.6 15 21.2h-10v56.4h10l-15 21.2-15-21.2h10v-56.4h-10z"/>
+   </g>
+   <path style="color:#000000" d="m138.4 633.3h110" fill-rule="evenodd" stroke-dasharray="7.2, 7.2" stroke-width="1.8"/>
+  </g>
+  <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" font-size="20px" line-height="125%" y="543.2041" x="216.81096" font-family="Tahoma" fill="#000000"><tspan y="543.2041" x="216.81096" font-weight="bold">ß</tspan></text>
+  <path style="color:#000000" d="m195.2 737.7 88-52.5" stroke="#000" stroke-dasharray="7.2, 7.2" stroke-width="1.8" fill="none"/>
+  <path style="color:#000000" d="m272.9 633.2 20.5 2.7-8.1 5.9 22.2 30.7-8.2 5.8-22.2-30.7-8.1 5.9z" stroke="#000" stroke-width="1.752" fill="#ff0"/>
+  <g font-family="Tahoma">
+   <g font-size="10px">
+    <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="756.90411" x="110.61097"><tspan y="756.90411" x="110.61097" font-size="14px">delta</tspan></text>
+    <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="687.75928" x="153.25862"><tspan y="687.75928" x="153.25862" font-size="14px">delta</tspan></text>
+    <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="687.40411" x="288.61096"><tspan y="687.40411" x="288.61096" font-size="14px">c</tspan></text>
+    <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="633.0722" x="255.56584"><tspan y="633.0722" x="255.56584" font-size="14px">d</tspan></text>
+    <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="738.10455" x="180.68153"><tspan y="738.10455" x="180.68153" font-size="14px">a</tspan></text>
+    <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="628.76648" x="190.6815"><tspan y="628.76648" x="190.6815" font-size="14px">b</tspan></text>
+    <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="534.92474" x="186.17192"><tspan y="534.92474" x="186.17192" font-size="14px">e</tspan></text>
+   </g>
+   <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" font-size="14px" line-height="125%" y="491.55411" x="434.41095"><tspan y="491.55411" x="434.41095">To square a join at exactly delta offset, such</tspan><tspan y="509.05411" x="434.41095">that (b) is delta distance from (a), and given</tspan><tspan y="526.55414" x="434.41095">that (c) is a perpendicular offset of (a) which</tspan><tspan y="544.054 [...]
+  </g>
+  <path style="color:#000000" d="m272.4 667.3-17.1 10.3 10.3 17.1" stroke="#000" stroke-dasharray="3.6, 3.6" stroke-width="1.8" fill="none"/>
+  <g transform="translate(-9.589,458.9)" stroke="#000" stroke-dasharray="3.6, 3.6" stroke-width="1.8" fill="none">
+   <path style="color:#000000" d="m206 78.44 35.1-60.24"/>
+   <path style="color:#000000" d="m230.1 122.7c14.4-8.7 24.1-24.46 24.1-42.51 0-18.38-10.1-34.42-24.9-42.97"/>
+  </g>
+  <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" font-size="14px" line-height="125%" y="577.60413" x="187.21097" font-family="Tahoma" fill="#000000"><tspan y="577.60413" x="187.21097" font-size="20px" font-weight="bold">Ø</tspan></text>
+  <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" font-size="14px" line-height="125%" y="773.54077" x="186.64101" font-family="Tahoma" fill="#000000"><tspan y="773.54077" x="186.64101" font-size="20px" font-weight="bold">Ø</tspan></text>
+  <path style="color:#000000" d="m109 884.2 85.9-147.8 73.6 119.9" stroke="#000" stroke-width="3.6" fill="none"/>
+ </g>
+</svg>
diff --git a/Documentation/offset_triginometry2.svg b/Documentation/offset_triginometry2.svg
new file mode 100644
index 0000000..9a03cba
--- /dev/null
+++ b/Documentation/offset_triginometry2.svg
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 400 600">
+ <g stroke="#000" fill="none">
+  <path style="color:#000000" d="m513.5 810.4a185.3 203.1 0 1 1 -370.6 0 185.3 203.1 0 1 1 370.6 0z" transform="matrix(1.074 0 0 .98 -426.9 -543.2)" stroke-width="3.509"/>
+  <g>
+   <path transform="translate(-295.0,-530.9)" d="m280 972.4-222.13-74.3 1.96-234.1 223.27-70.5 136.1 190.6z"/>
+   <path style="color:#000000" d="m-30 230 120-90" stroke-dasharray="4, 4"/>
+   <path style="color:#000000" d="m-10 62.08-55.1 189.92l190.8 1" stroke-dasharray="4, 4"/>
+  </g>
+ </g>
+ <g font-size="14px" font-family="Tahoma" fill="#000000">
+  <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="264.27399" x="-75.70369"><tspan x="-75.70369" y="264.27399">c</tspan></text>
+  <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="54.873951" x="-9.90369"><tspan x="-9.90369" y="54.873951">a</tspan></text>
+  <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="254.97398" x="133.90369"><tspan x="133.90369" y="254.97398">b</tspan></text>
+  <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="131.47398" x="93.00369"><tspan x="93.00369" y="131.47398">d</tspan></text>
+  <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="173.41545" x="52.1037"><tspan x="52.1037" y="173.41545">q</tspan></text>
+ </g>
+ <g>
+  <path style="color:#000000" d="m-20 251.2v-0.7c0-20.4-14.3-37.6-33.6-42.5" stroke="#000" fill="none"/>
+  <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" font-family="Tahoma" xml:space="preserve" font-size="20px" line-height="125%" y="245.65685" x="-54.47578" fill="#000000"><tspan y="245.65685" x="-54.47578" font-weight="bold">ß</tspan></text>
+  <path style="color:#000000" d="m44.1 142.9-16.2 11.7 11.7 16.2" stroke="#000" stroke-dasharray="3.6, 3.6" stroke-width="1.8" fill="none"/>
+ </g>
+ <g font-size="14px" font-family="Tahoma" fill="#000000">
+  <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="91.773972" x="154.80371"><tspan x="154.80371" y="91.773972">Calculate the number of steps (S) needed to </tspan><tspan x="154.80371" y="109.27397">construct a flattened path approximating a</tspan><tspan x="154.80371" y="126.77397">circle with radius (R) where the maximum </tspan><tspan x="154.80371" y="144.27397">imprecision  [...]
+  <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="159.97398" x="73.6037"><tspan y="159.97398" x="73.6037" font-weight="bold">L</tspan></text>
+  <text style="letter-spacing:0px;block-progression:tb;text-indent:0;word-spacing:0px;color:#000000;text-transform:none" xml:space="preserve" line-height="125%" y="265.32526" x="28.05185"><tspan y="265.32526" x="28.05185" font-weight="bold">R</tspan></text>
+ </g>
+</svg>
diff --git a/Documentation/offset_triginometry3.svg b/Documentation/offset_triginometry3.svg
new file mode 100644
index 0000000..52eff70
--- /dev/null
+++ b/Documentation/offset_triginometry3.svg
@@ -0,0 +1,391 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="800"
+   height="600"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.2 r9819"
+   sodipodi:docname="offset_triginometry3.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.64"
+     inkscape:cx="-24.27"
+     inkscape:cy="218.1"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1280"
+     inkscape:window-height="753"
+     inkscape:window-x="-4"
+     inkscape:window-y="-4"
+     inkscape:window-maximized="1"
+     showborder="true"
+     inkscape:showpageshadow="true"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3779"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-407.6,90.95)">
+    <path
+       style="color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 4;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="m 604.8,209.6 85.8,-55.7"
+       id="path3907"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="color:#000000;fill:none;stroke:#000000;stroke-width:1.8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="M 420.4,326.1 603.7,10.08 757.6,263.6"
+       id="path2985"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccc" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-backgr [...]
+       x="624.09735"
+       y="215.06464"
+       id="text3861"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3863"
+         x="624.09735"
+         y="215.06464"
+         style="font-weight:bold;-inkscape-font-specification:Tahoma Bold">ß</tspan></text>
+    <path
+       sodipodi:nodetypes="cccccccc"
+       inkscape:connector-curvature="0"
+       id="path3857-2"
+       d="m 603,29.9 15,15.8 -10,0 0,155.1 -10,0 0,-155.1 -10,0 z"
+       style="color:#000000;fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:1.776;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       inkscape:transform-center-x="-20.96"
+       inkscape:transform-center-y="-1.652" />
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-backgr [...]
+       x="682.20001"
+       y="261.375"
+       id="text3952"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3954"
+         x="682.20001"
+         y="261.375"
+         style="font-size:14px">delta</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-backgr [...]
+       x="698.20001"
+       y="151.875"
+       id="text3977"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3979"
+         x="698.20001"
+         y="151.875"
+         style="font-size:14px">c</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-backgr [...]
+       x="599.76099"
+       y="3.3956299"
+       id="text3977-5-1-2"
+       sodipodi:linespacing="125%"
+       inkscape:transform-center-x="-52.52"
+       inkscape:transform-center-y="54.68"><tspan
+         sodipodi:role="line"
+         id="tspan3979-1-5-7"
+         x="599.76099"
+         y="3.3956299"
+         style="font-size:14px">b</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-backgr [...]
+       x="766"
+       y="-49.349998"
+       id="text4077"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="766"
+         y="-49.349998"
+         id="tspan4087">When 'mitering' offset polygons, the maximum distance </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="-31.849998"
+         id="tspan3063">point 'b' can be from point 'a' is set by 'limit' where limit </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="-14.349998"
+         id="tspan3065">is a multiple of delta. Therefore, for any given angle</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="3.1500015"
+         id="tspan3067">we need to know if length(ab) > limit * delta.</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="20.650002"
+         id="tspan4083" /><tspan
+         sodipodi:role="line"
+         x="766"
+         y="38.150002"
+         id="tspan3890">Find the largest angle ß (or smallest Ø since Ø = pi - ß) </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="55.650002"
+         id="tspan3904">for a given limit, expressing ß as sin(ß) or cos(ß) since</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="73.150002"
+         id="tspan3908">these can easily be derived from cross or dot products </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="90.650002"
+         id="tspan3916">respectively.</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="98.598343"
+         id="tspan3884"
+         style="font-size:4px"> </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="113.15"
+         id="tspan3888">angle(abc) = Ø/2 </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="130.64999"
+         id="tspan3083">length(ab) = limit * delta</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="148.14999"
+         id="tspan3087">length(ac) = delta</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="165.64999"
+         id="tspan3089">sin(Ø/2) = delta / (limit * delta) = 1 / limit</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="183.14999"
+         id="tspan3157">Given that sin(Ø/2) = sqrt((1-cos(Ø))/2) **</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="200.64999"
+         id="tspan3151">1 / limit = sqrt((1-cos(Ø))/2)</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="218.14999"
+         id="tspan3161">limit = sqrt(2 / (1-cos(Ø)))</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="235.64999"
+         id="tspan3815"
+         style="font-weight:normal">1-cos(Ø) = 2 / sqr(limit) </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="253.14999"
+         id="tspan3043">Since Ø = pi - ß ...</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="270.64999"
+         id="tspan3868">1-cos(pi - ß) = 2 / sqr(limit)</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="288.14999"
+         id="tspan3047">and given cos(pi-ß) = -cos(ß) ** ... </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="296.09836"
+         style="font-size:4px;font-weight:normal"
+         id="tspan3823"> </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="310.64999"
+         style="font-weight:bold"
+         id="tspan3122">1+cos(ß) = 2 / sqr(limit) </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="318.59836"
+         style="font-size:4px;font-weight:normal"
+         id="tspan3118"> </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="333.14999"
+         style="font-weight:normal"
+         id="tspan3114">cos(ß) = 2 / sqr(limit) - 1</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="350.64999"
+         style="font-weight:bold"
+         id="tspan3051" /><tspan
+         sodipodi:role="line"
+         x="766"
+         y="368.14999"
+         style="font-weight:normal"
+         id="tspan3066">Example: if miter limit = 2 (ie 2 times delta) then </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="385.64999"
+         style="font-weight:normal"
+         id="tspan3070">cos(ß) = (2 / 4) -1 = -0.5 and so ß = 120 degrees. </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="403.14999"
+         style="font-weight:normal"
+         id="tspan3072">Therefore, when ß > 120 deg. (or Ø < 60 deg.), the</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="420.64999"
+         style="font-weight:normal"
+         id="tspan3090">distance point 'b' would be from point 'a' would exceed </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="438.14999"
+         style="font-weight:normal"
+         id="tspan3094">the limit.</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="446.09836"
+         id="tspan3171"
+         style="font-size:4px;font-weight:bold">   </tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="451.09836"
+         style="font-size:4px;font-weight:bold"
+         id="tspan3049" /><tspan
+         sodipodi:role="line"
+         x="766"
+         y="462.78452"
+         id="tspan3163"
+         style="font-size:11px">** see http://en.wikipedia.org/wiki/List_of_trigonometric_identities</tspan><tspan
+         sodipodi:role="line"
+         x="766"
+         y="476.53452"
+         id="tspan3153" /><tspan
+         sodipodi:role="line"
+         x="766"
+         y="490.28452"
+         id="tspan3145" /><tspan
+         sodipodi:role="line"
+         x="766"
+         y="504.03452"
+         id="tspan4124"
+         style="font-weight:bold" /><tspan
+         sodipodi:role="line"
+         x="766"
+         y="517.78448"
+         id="tspan4116" /><tspan
+         sodipodi:role="line"
+         x="766"
+         y="531.53448"
+         id="tspan4095" /></text>
+    <path
+       style="color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 2;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="m 684.7,144.1 -10.6,5.9 5.4,10.1"
+       id="path4091"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccc" />
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-backgr [...]
+       x="588.19745"
+       y="208.67169"
+       id="text3977-1"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3979-7"
+         x="588.19745"
+         y="208.67169"
+         style="font-size:14px">a</tspan></text>
+    <g
+       id="g3841"
+       transform="translate(400,-20.7)">
+      <path
+         sodipodi:nodetypes="cc"
+         inkscape:connector-curvature="0"
+         id="path3026"
+         d="M 175.6,276.8 233.7,175.7"
+         style="color:#000000;fill:none;stroke:#000000;stroke-width:1.8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:7.2, 7.2;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+      <path
+         sodipodi:nodetypes="cc"
+         inkscape:connector-curvature="0"
+         id="path3809"
+         d="m 227.7,188.7 c 27.7,17.9 31.6,57.6 -0.5,76.7"
+         style="color:#000000;fill:none;stroke:#000000;stroke-width:1.81;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:7.24, 7.24;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    </g>
+    <path
+       style="color:#000000;fill:#00ff00;fill-opacity:1;stroke:#000000;stroke-width:2.1383;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="m 724.8,212.4 -9.2,24.2 -5.6,-8.3 -46.8,31.5 5.4,8.3 -26,-0.8 9.4,-24.3 5.5,8.3 47,-31.3 -5.6,-8.3 z"
+       id="path3777-9"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccccc"
+       inkscape:transform-center-x="-96.45"
+       inkscape:transform-center-y="-137.3" />
+    <text
+       xml:space="preserve"
+       style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-backgr [...]
+       x="595.66949"
+       y="246.41019"
+       id="text3845"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3847"
+         x="595.66949"
+         y="246.41019"
+         style="font-size:20px;font-weight:bold;-inkscape-font-specification:Tahoma Bold">Ø</tspan></text>
+    <text
+       sodipodi:linespacing="125%"
+       id="text3849"
+       y="246.41019"
+       x="595.66949"
+       style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-backgr [...]
+       xml:space="preserve"><tspan
+         style="font-size:20px;font-weight:bold;-inkscape-font-specification:Tahoma Bold"
+         y="246.41019"
+         x="595.66949"
+         id="tspan3851"
+         sodipodi:role="line">Ø</tspan></text>
+    <path
+       style="color:#000000;fill:none;stroke:#000000;stroke-width:3.6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="M 530.8,333.3 603,210 677.5,330.2"
+       id="path2985-1-1"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccc" />
+  </g>
+</svg>
diff --git a/License.txt b/License.txt
new file mode 100644
index 0000000..3d94797
--- /dev/null
+++ b/License.txt
@@ -0,0 +1,24 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+http://www.boost.org/LICENSE_1_0.txt
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/README b/README
new file mode 100644
index 0000000..c5d2ef9
--- /dev/null
+++ b/README
@@ -0,0 +1,407 @@
+=====================================================================
+Clipper Change Log
+=====================================================================
+v6.2.1 (31 October 2014) Rev 482
+* Bugfix in ClipperOffset.Execute where the Polytree.IsHole property 
+  was returning incorrect values with negative offsets
+* Very minor improvement to join rounding in ClipperOffset
+* Fixed CPP OpenGL demo.
+
+v6.2.0 (17 October 2014) Rev 477
+* Numerous minor bugfixes, too many to list. 
+  (See revisions 454-475 in Sourceforge Repository)
+* The ZFillFunction (custom callback function) has had its parameters 
+  changed. 
+* Curves demo removed (temporarily).
+* Deprecated functions have been removed. 
+
+v6.1.5 (26 February 2014) Rev 460
+* Improved the joining of output polygons sharing a common edge 
+  when those common edges are horizontal.
+* Fixed a bug in ClipperOffset.AddPath() which would produce
+  incorrect solutions when open paths were added before closed paths.
+* Minor code tidy and performance improvement
+
+v6.1.4 (6 February 2014)
+* Fixed bugs in MinkowskiSum
+* Fixed minor bug when using Clipper.ForceSimplify.
+* Modified use_xyz callback so that all 4 vertices around an
+  intersection point are now passed to the callback function.
+
+v6.1.3a (22 January 2014) Rev 453
+* Fixed buggy PointInPolygon function (C++ and C# only). 
+  Note this bug only affected the newly exported function, the 
+  internal PointInPolygon function used by Clipper was OK.
+ 
+v6.1.3 (19 January 2014) Rev 452
+* Fixed potential endless loop condition when adding open 
+  paths to Clipper.
+* Fixed missing implementation of SimplifyPolygon function 
+  in C++ code.
+* Fixed incorrect upper range constant for polygon coordinates 
+  in Delphi code.
+* Added PointInPolygon function. 
+* Overloaded MinkowskiSum function to accommodate multi-contour 
+  paths.  
+
+v6.1.2 (15 December 2013) Rev 444
+* Fixed broken C++ header file.
+* Minor improvement to joining polygons.
+
+v6.1.1 (13 December 2013) Rev 441
+* Fixed a couple of bugs affecting open paths that could 
+  raise unhandled exceptions.
+  
+v6.1.0 (12 December 2013)
+* Deleted: Previously deprecated code has been removed. 
+* Modified: The OffsetPaths function is now deprecated as it has 
+  been replaced by the ClipperOffset class which is much more 
+  flexible. 
+* Bugfixes: Several minor bugs have been fixed including 
+  occasionally an incorrect nesting within the PolyTree structure.
+
+v6.0.0 (30 October 2013)
+* Added: Open path (polyline) clipping. A new 'Curves' demo 
+  application showcases this (see the 'Curves' directory). 
+* Update: Major improvement in the merging of 
+  shared/collinear edges in clip solutions (see Execute). 
+* Added: The IntPoint structure now has an optional 'Z' member. 
+  (See the precompiler directive use_xyz.) 
+* Added: Users can now force Clipper to use 32bit integers 
+  (via the precompiler directive use_int32) instead of using 
+  64bit integers.
+* Modified: To accommodate open paths, the Polygon and Polygons 
+  structures have been renamed Path and Paths respectively. The 
+  AddPolygon and AddPolygons methods of the ClipperBase class 
+  have been renamed AddPath and AddPaths respectively. Several 
+  other functions have been similarly renamed. 
+* Modified: The PolyNode Class has a new IsOpen property. 
+* Modified: The Clipper class has a new ZFillFunction property. 
+* Added: MinkowskiSum and MinkowskiDiff functions added. 
+* Added: Several other new functions have been added including 
+  PolyTreeToPaths, OpenPathsFromPolyTree and ClosedPathsFromPolyTree. 
+* Added: The Clipper constructor now accepts an optional InitOptions 
+  parameter to simplify setting properties. 
+* Bugfixes: Numerous minor bugs have been fixed. 
+* Deprecated: Version 6 is a major upgrade from previous versions 
+  and quite a number of changes have been made to exposed structures 
+  and functions. To minimize inconvenience to existing library users, 
+  some code has been retained and some added to maintain backward 
+  compatibility. However, because this code will be removed in a 
+  future update, it has been marked as deprecated and a precompiler 
+  directive use_deprecated has been defined.
+
+v5.1.6 (23 May 2013)
+* BugFix: CleanPolygon function was buggy.
+* Changed: The behaviour of the 'miter' JoinType has been 
+  changed so that when squaring occurs, it's no longer 
+  extended up to the miter limit but is squared off at 
+  exactly 'delta' units. (This improves the look of mitering 
+  with larger limits at acute angles.) 
+* Added: New OffsetPolyLines function
+* Update: Minor code refactoring and optimisations
+
+v5.1.5 (5 May 2013)
+* Added: ForceSimple property to Clipper class
+* Update: Improved documentation  
+
+v5.1.4 (24 March 2013)
+* Update: CleanPolygon function enhanced.
+* Update: Documentation improved.  
+
+v5.1.3 (14 March 2013)
+* Bugfix: Minor bugfixes.
+* Update: Documentation significantly improved.  
+
+v5.1.2 (26 February 2013)
+* Bugfix: PolyNode class was missing a constructor. 
+* Update: The MiterLimit parameter in the OffsetPolygons 
+  function has been renamed Limit and can now also be used to 
+  limit the number of vertices used to construct arcs when 
+  JoinType is set to jtRound.
+
+v5.1.0 (17 February 2013)
+* Update: ExPolygons has been replaced with the PolyTree & 
+  PolyNode classes to more fully represent the parent-child 
+  relationships of the polygons returned by Clipper. 
+* Added: New CleanPolygon and CleanPolygons functions. 
+* Bugfix: Another orientation bug fixed.
+
+v5.0.2 - 30 December 2012
+* Bugfix: Significant fixes in and tidy of the internal 
+  Int128 class (which is used only when polygon coordinate 
+  values are greater than ±0x3FFFFFFF (~1.07e9)). 
+* Update: The Area algorithm has been updated and is faster. 
+* Update: Documentation updates. The newish but undocumented 
+  'CheckInputs' parameter of the OffsetPolygons function has been 
+  renamed 'AutoFix' and documented too. The comments on rounding 
+  have also been improved (ie clearer and expanded).
+
+v4.10.0 - 25 December 2012
+* Bugfix: Orientation bugs should now be resolved (finally!).
+* Bugfix: Bug in Int128 class
+
+v4.9.8 - 2 December 2012
+* Bugfix: Further fixes to rare Orientation bug.
+
+v4.9.7 - 29 November 2012
+* Bugfix: Bug that very rarely returned the wrong polygon 
+  orientation.
+* Bugfix: Obscure bug affecting OffsetPolygons when using 
+  jtRound for the JoinType parameter and when polygons also
+  contain very large coordinate values (> +/-100000000000).
+
+v4.9.6 - 9 November 2012
+* Bugfix: Another obscure bug related to joining polygons.
+
+v4.9.4 - 2 November 2012
+* Bugfix: Bugs in Int128 class occasionally causing 
+  wrong orientations.
+* Bugfix: Further fixes related to joining polygons.
+
+v4.9.0 - 9 October 2012
+* Bugfix: Obscure bug related to joining polygons.
+
+v4.8.9 - 25 September 2012
+* Bugfix: Obscure bug related to precision of intersections.
+          
+v4.8.8 - 30 August 2012
+* Bugfix: Fixed bug in OffsetPolygons function introduced in 
+  version 4.8.5.
+
+v4.8.7 - 24 August 2012
+* Bugfix: ReversePolygon function in C++ translation was broken.
+* Bugfix: Two obscure bugs affecting orientation fixed too.
+
+v4.8.6 - 11 August 2012
+* Bugfix: Potential for memory overflow errors when using 
+  ExPolygons structure.
+* Bugfix: The polygon coordinate range has been reduced to 
+  +/- 0x3FFFFFFFFFFFFFFF (4.6e18).
+* Update: ReversePolygons function was misnamed ReversePoints in C++.
+* Update: SimplifyPolygon function now takes a PolyFillType parameter.
+          
+v4.8.5 - 15 July 2012
+* Bugfix: Potential for memory overflow errors in OffsetPolygons().
+
+v4.8.4 - 1 June 2012
+* Bugfix: Another obscure bug affecting ExPolygons structure.
+
+v4.8.3 - 27 May 2012
+* Bugfix: Obscure bug causing incorrect removal of a vertex.
+
+v4.8.2 - 21 May 2012
+* Bugfix: Obscure bug could cause an exception when using 
+  ExPolygon structure.
+
+v4.8.1 - 12 May 2012
+* Update: Cody tidy and minor bug fixes.
+
+v4.8.0 - 30 April 2012
+* Bugfix: Occasional errors in orientation fixed. 
+* Update: Added notes on rounding to the documentation. 
+
+v4.7.6 - 11 April 2012
+* Fixed a bug in Orientation function (affecting C# translations only).
+* Minor documentation update.
+
+v4.7.5 - 28 March 2012
+* Bugfix: Fixed a recently introduced bug that occasionally caused an 
+  unhandled exception in C++ and C# translations. 
+
+v4.7.4 - 15 March 2012
+* Bugfix: Another minor bugfix.
+
+v4.7.2 - 4 March 2012
+* Bugfix: Fixed bug introduced in ver 4.7 which sometimes caused 
+  an exception if ExPolygon structure was passed to Clipper's 
+  Execute method. 
+
+v4.7.1 - 3 March 2012
+* Bugfix: Rare crash when JoinCommonEdges joined polygons that 
+  'cancelled' each other. 
+* Bugfix: Clipper's internal Orientation method occasionally 
+  returned wrong result. 
+* Update: Improved C# code (thanks to numerous excellent suggestions 
+  from David Piepgrass) 
+
+v4.7 - 10 February 2012
+* Improved the joining of output polygons sharing a common edge.
+
+v4.6.6 - 3 February 2012
+* Bugfix: Another obscure bug occasionally causing incorrect 
+  polygon orientation. 
+
+v4.6.5 - 17 January 2012
+* Bugfix: Obscure bug occasionally causing incorrect hole 
+  assignment in ExPolygon structure. 
+
+v4.6.4 - 8 November 2011
+* Added: SimplifyPolygon and SimplifyPolygons functions.
+
+v4.6.3 - 11 November 2011
+* Bugfix: Fixed another minor mitering bug in OffsetPolygons.
+
+v4.6.2 - 10 November 2011
+* Bugfix: Fixed a rare bug in the orientation of polygons 
+  returned by Clipper's Execute() method.
+* Bugfix: Previous update introduced a mitering bug in the
+  OffsetPolygons function.
+
+v4.6 - 29 October 2011
+* Added: Support for Positive and Negative polygon fill 
+  types (in addition to the EvenOdd and NonZero fill types).
+* Bugfix: The OffsetPolygons function was generating the 
+  occasional artefact when 'shrinking' polygons.
+
+v4.5.5 - 8 October 2011
+* Bugfix: Fixed an obscure bug in Clipper's JoinCommonEdges 
+  method. 
+* Update: Replaced IsClockwise function with Orientation 
+  function. The orientation issues affecting OffsetPolygons 
+  should now be finally resolved.
+* Change: The Area function once again returns a signed value.
+ 
+v4.5.1 - 28 September 2011
+* Deleted: The UseFullCoordinateRange property has been 
+  deleted since integer range is now managed implicitly. 
+* BugFix: Minor bug in OffsetPolygon mitering. 
+* Change: C# JoinType enum moved from Clipper class to 
+  ClipperLib namespace. 
+* Change: The Area function now returns the absolute area 
+  (irrespective of orientation). 
+* Change: The IsClockwise function now requires a second 
+  parameter - YAxisPositiveUpward - to accommodate displays 
+  with Y-axis oriented in either direction
+
+v4.4.4 - 10 September 2011
+* Change: Deleted jtButt from JoinType (used by the 
+  OffsetPolygons function). 
+* BugFix: Fixed another minor bug in OffsetPolygons function. 
+* Update: Further improvements to the help file
+
+v4.4.3 - 29 August 2011
+* BugFix: fixed a minor rounding issue in OffsetPolygons 
+  function (affected C++ & C# translations). 
+* BugFix: fixed a minor bug in OffsetPolygons' function 
+  declaration (affected C++ translation only). 
+* Change: 'clipper' namespace changed to 'ClipperLib' 
+  namespace in both C++ and C# code to remove the ambiguity 
+  between the Clipper class and the namespace. (This also 
+  required numerous updates to the accompanying demos.)
+
+v4.4.2 - 26 August 2011
+* BugFix: minor bugfixes in Clipper. 
+* Update: the OffsetPolygons function has been significantly 
+  improved by offering 4 different join styles. 
+
+v4.4.0 - 6 August 2011
+* BugFix: A number of minor bugs have been fixed that mostly 
+  affected the new ExPolygons structure. 
+
+v4.3.0 - 17 June 2011
+* New: ExPolygons structure that explicitly associates 'hole' 
+  polygons with their 'outer' container polygons.
+* New: Execute method overloaded so the solution parameter 
+  can now be either Polygons or ExPolygons.  
+* BugFix: Fixed a rare bug in solution polygons orientation. 
+
+v4.2.8 - 21 May 2011
+* Update: JoinCommonEdges() improved once more. 
+* BugFix: Several minor bugs fixed. 
+
+v4.2.6 - 1 May 2011
+* Bugfix: minor bug in SlopesEqual function.
+* Update: Merging of output polygons sharing common edges 
+  has been significantly inproved
+
+v4.2.4 - 26 April 2011
+  Input polygon coordinates can now contain the full range of 
+  signed 64bit integers (ie +/-9,223,372,036,854,775,807). This 
+  means that floating point values can be converted to and from 
+  Clipper's 64bit integer coordinates structure (IntPoint) and  
+  still retain a precision of up to 18 decimal places. However, 
+  since the large-integer math that supports this expanded range 
+  imposes a small cost on performance (~15%), a new property 
+  UseFullCoordinateRange has been added to the Clipper class to 
+  allow users the choice of whether or not to use this expanded 
+  coordinate range. If this property is disabled, coordinate values 
+  are restricted to +/-1,500,000,000.
+
+v4.2 - 12 April 2011
+  JoinCommonEdges() code significantly improved plus other minor 
+  improvements.
+
+v4.1.2 - 9 April 2011
+* Update: Minor code tidy. 
+* Bugfix: Possible endless loop in JoinCommonEdges() in clipper.pas.
+
+v4.1.1 - 8 April 2011
+* Update: All polygon coordinates are now stored as 64bit integers
+  (though they're still restricted to range -1.5e9 to +1.5e9 pending 
+  the inclusion of code supporting 64bit math).
+* Change: AddPolygon and AddPolygons methods now return boolean 
+  values. 
+* Bugfix: Bug in JoinCommonEdges() caused potential endless loop. 
+* Bugfix: Bug in IsClockwise(). (C++ code only)
+
+v4.0 - 5 April 2011
+* Clipper 4 is a major rewrite of earlier versions. The biggest 
+  change is that floating point values are no longer used, 
+  except for the storing of edge slope values. The main benefit 
+  of this is the issue of numerical robustness has been 
+  addressed. Due to other major code improvements Clipper v4 
+  is approximately 40% faster than Clipper v3. 
+* The AddPolyPolygon method has been renamed to AddPolygons. 
+* The IgnoreOrientation property has been removed. 
+* The clipper_misc library has been merged back into the 
+  main clipper library.
+  
+v3.1.0 - 17 February 2011
+* Bugfix: Obscure bug in TClipperBase.SetDx method that caused 
+  problems with very small edges ( edges <1/1000th pixel in size).
+  
+v3.0.3 - 9 February 2011
+* Bugfix: Significant bug, but only in C# code.
+* Update: Minor refactoring.
+
+v3.0 - 31 January 2011
+* Update: Major rewrite of the portion of code that calculates 
+  the output polygons' orientation.
+* Update: Help file significantly improved.
+* Change: Renamed ForceOrientation property to IgnoreOrientation. 
+  If the orientation of output polygons is not important, or can 
+  be managed separately, clipping routines can be sped up by about 
+  60% by setting IgnoreOrientation to true. Defaults to false.
+* Change: The OffsetPolygon and Area functions have been moved to 
+  the new unit - clipper_misc. 
+
+2.99 - 15 January 2011
+* Bugfix: Obscure bug in AddPolygon method could cause an endless loop. 
+
+2.8 - 20 November 2010
+* Updated: Output polygons which previously shared a common 
+  edge are now merged. 
+* Changed: The orientation of outer polygons is now clockwise 
+  when the display's Y axis is positive downwards (as is 
+  typical for most Windows applications). Inner polygons 
+  (holes) have the opposite orientation.
+* Added: Support module for Cairo Graphics Library (with demo). 
+* Updated: C# and C++ demos.
+
+2.522 - 15 October 2010
+* Added C# translation (thanks to Olivier Lejeune) and 
+  a link to Ruby bindings (thanks to Mike Owens).
+
+2.0 - 30 July 2010
+* Clipper now clips using both the Even-Odd (alternate) and 
+  Non-Zero (winding) polygon filling rules. (Previously Clipper 
+  assumed the Even-Odd rule for polygon filling.)
+  
+1.4c - 16 June 2010
+* Added C++ support for AGG graphics library 
+  
+1.2s - 2 June 2010
+* Added C++ translation of clipper.pas
+
+1.0 - 9 May 2010
diff --git a/Third Party/Flash/AS3_flash_readme.txt b/Third Party/Flash/AS3_flash_readme.txt
new file mode 100644
index 0000000..da54dfd
--- /dev/null
+++ b/Third Party/Flash/AS3_flash_readme.txt	
@@ -0,0 +1,2 @@
+An AS3 Flash port written by Chris Denham:
+https://github.com/ChrisDenham/PolygonClipper.AS3
diff --git a/Third Party/Go/Go_readme.txt b/Third Party/Go/Go_readme.txt
new file mode 100644
index 0000000..a5a085a
--- /dev/null
+++ b/Third Party/Go/Go_readme.txt	
@@ -0,0 +1,2 @@
+An AS3 Flash port written by Chris Tessum:
+https://github.com/ctessum/go.clipper
diff --git a/Third Party/Haskell/Haskell_readme.txt b/Third Party/Haskell/Haskell_readme.txt
new file mode 100644
index 0000000..a341b6d
--- /dev/null
+++ b/Third Party/Haskell/Haskell_readme.txt	
@@ -0,0 +1,4 @@
+
+An Haskell module written by 	Chetan Taralekar <chetant at gmail.com>
+that wraps the Clipper library can be downloaded from:
+http://hackage.haskell.org/package/clipper
diff --git a/Third Party/LuaJIT/LuaJIT_readme.txt b/Third Party/LuaJIT/LuaJIT_readme.txt
new file mode 100644
index 0000000..14ae1b8
--- /dev/null
+++ b/Third Party/LuaJIT/LuaJIT_readme.txt	
@@ -0,0 +1 @@
+http://www.freelists.org/post/luajit/binding-for-clipper-polygon-clipping-library
\ No newline at end of file
diff --git a/Third Party/Matlab/matlab_readme.txt b/Third Party/Matlab/matlab_readme.txt
new file mode 100644
index 0000000..aadd821
--- /dev/null
+++ b/Third Party/Matlab/matlab_readme.txt	
@@ -0,0 +1,3 @@
+To compile your own mexfile run the command:
+
+mex clipper.cpp mexclipper.cpp
diff --git a/Third Party/Matlab/mexclipper.cpp b/Third Party/Matlab/mexclipper.cpp
new file mode 100644
index 0000000..d524524
--- /dev/null
+++ b/Third Party/Matlab/mexclipper.cpp	
@@ -0,0 +1,283 @@
+// mex clipper.cpp mexclipper.cpp
+// 
+// Modified 2014-10-24, Jari Repo (JR), University West, jari.repo at hv.se
+// replaced "Polygons" to "Paths"
+// replaced "AddPolygons" by "AddPaths"
+// replaced the call to "OffsetPolygons" by use of the "ClipperOffset" class
+
+#include "mex.h"
+#include "clipper.hpp"
+
+using namespace ClipperLib;
+
+//void read_polygons_MATLAB(const mxArray *prhs, Polygons &poly)
+void read_polygons_MATLAB(const mxArray *prhs, Paths &poly)
+{
+    int id_x, id_y;
+    int num_contours;
+    int nx, ny;
+    long64 *x, *y;
+    const mxArray *x_in, *y_in;
+    
+    /*  Checking if input is non empty Matlab-structure */
+    if (!mxIsStruct(prhs))
+        mexErrMsgTxt("Input needs to be structure.");
+    if (!mxGetM(prhs) || !mxGetN(prhs))
+        mexErrMsgTxt("Empty structure.");
+    
+    /*  Checking field names and data type  */
+    id_x = mxGetFieldNumber(prhs,"x");
+    if (id_x==-1)
+        mexErrMsgTxt("Input structure must contain a field 'x'.");
+    
+    x_in = mxGetFieldByNumber(prhs, 0, id_x);
+    if (!mxIsInt64(x_in))
+        mexErrMsgTxt("Structure field 'x' must be of type INT64.");
+    
+    id_y = mxGetFieldNumber(prhs,"y");
+    if (id_y==-1)
+        mexErrMsgTxt("Input structure must contain a field 'y'.");
+    y_in = mxGetFieldByNumber(prhs, 0, id_y);
+    if (!mxIsInt64(y_in))
+        mexErrMsgTxt("Structure field 'y' must be of type INT64.");
+    
+    num_contours = mxGetNumberOfElements(prhs);
+    poly.resize(num_contours);
+    for (unsigned i = 0; i < num_contours; i++){
+        x_in = mxGetFieldByNumber(prhs, i, id_x);
+        y_in = mxGetFieldByNumber(prhs, i, id_y);
+        
+        nx = mxGetNumberOfElements(x_in);
+        ny = mxGetNumberOfElements(y_in);
+        if (nx!=ny)
+            mexErrMsgTxt("Structure fields x and y must be the same length.");
+        
+        poly[i].resize(nx);
+        
+        x = (long64*)mxGetData(x_in);
+        y = (long64*)mxGetData(y_in);
+        for (unsigned j = 0; j < nx; j++){
+            poly[i][j].X = x[j];
+            poly[i][j].Y = y[j];
+        }
+    }
+}
+
+//void write_polygons_MATLAB(mxArray *plhs, Polygons &solution)
+void write_polygons_MATLAB(mxArray *plhs, Paths &solution)
+{
+    mxArray *x_out, *y_out;
+    
+    for (unsigned i = 0; i < solution.size(); ++i)
+    {
+        x_out = mxCreateDoubleMatrix(solution[i].size(),1,mxREAL);
+        y_out = mxCreateDoubleMatrix(solution[i].size(),1,mxREAL);
+        for (unsigned j = 0; j < solution[i].size(); ++j)
+        {
+            ((double*)mxGetPr(x_out))[j]=solution[i][j].X;
+            ((double*)mxGetPr(y_out))[j]=solution[i][j].Y;
+        }
+        mxSetFieldByNumber(plhs,i,0,x_out);
+        mxSetFieldByNumber(plhs,i,1,y_out);
+    }
+}
+
+
+void mexFunction(int nlhs, mxArray *plhs[],
+        int nrhs, const mxArray *prhs[])
+{
+    //Polygons subj, clip, solution;
+    Paths subj, clip, solution;
+    const char *field_names[] = {"x","y"};
+    int dims[2];
+    
+    if (nrhs == 0) {
+        mexPrintf("OutPol = clipper(RefPol, ClipPol, Method, [RefF], [ClipF]);\n");
+        mexPrintf(" Clips polygons by Method:\n");
+        mexPrintf("  0 - Difference (RefPol - ClipPol)\n");
+        mexPrintf("  1 - Intersection\n");
+        mexPrintf("  2 - Xor\n");
+        mexPrintf("  3 - Union\n");
+        mexPrintf(" Optionally specifying Fill Types for the polygons:\n");
+        mexPrintf("  0 - Even-Odd (default)\n");
+        mexPrintf("  1 - Non-Zero\n");
+        mexPrintf("  2 - Positive\n");
+        mexPrintf("  3 - Negative\n\n");
+        mexPrintf("Or:\n\n");
+        mexPrintf("OutPol = clipper(RefPol, Delta, MiterLimit);\n");
+        mexPrintf(" Offsets RefPol by Delta (+: outset, -: inset).\n");
+        mexPrintf("  MiterLimit * Delta = max distance of new vertex.\n");
+        mexPrintf("  MiterLimit = 1 for square corners.\n");
+        mexPrintf("  MiterLimit = 0 for round corners.\n\n");
+        mexPrintf("Or:\n\n");
+        mexPrintf("Orientation = clipper(RefPol);\n");
+        mexPrintf(" Returns boolean orientations of polygons.\n\n");
+        mexPrintf("All polygons are structures with the fields ...\n");
+        mexPrintf("  .x:    x-coordinates of contour\n");
+        mexPrintf("  .y:    y-coordinates of contour\n");
+        mexPrintf("All polygons may contain several contours.\n");
+        mexPrintf("\nPolygon Clipping Routine based on clipper v4.7.5.\n");
+        mexPrintf(" Credit goes to Angus Johnson\n");
+        mexPrintf(" http://www.angusj.com/delphi/clipper.php\n\n");
+        return;}
+    
+    /*  Check number of arguments */
+    
+    if (nlhs != 1)
+        mexErrMsgTxt("One output required.");
+    
+    if (nrhs == 1)
+    {
+        // Find the orientation of input polygons
+        bool orient;
+        read_polygons_MATLAB(prhs[0], subj);
+        plhs[0] = mxCreateDoubleMatrix(subj.size(), 1, mxREAL);
+        for (unsigned i = 0; i < subj.size(); ++i)
+        {
+            orient = Orientation(subj[i]);
+            ((double*)mxGetPr(plhs[0]))[i] = (double)orient;
+        }
+    }
+    else if (nrhs >= 3)
+    {
+        if (!mxIsDouble(prhs[2]) || mxGetM(prhs[2])!=1 || mxGetN(prhs[2])!=1)
+            mexErrMsgTxt("Third input must be scalar.");
+        if (mxIsStruct(prhs[1]))
+        {
+            // Clip two input polygons
+            int ct;
+            ClipType CT;
+            PolyFillType SFT, CFT;
+            ct=mxGetScalar(prhs[2]);
+            switch (ct){
+                case 0:
+                    CT=ctDifference;
+                    break;
+                case 1:
+                    CT=ctIntersection;
+                    break;
+                case 2:
+                    CT=ctXor;
+                    break;
+                case 3:
+                    CT=ctUnion;
+                    break;
+                default:
+                    mexErrMsgTxt("Third input must be 0, 1, 2, or 3.");
+            }
+            
+            if (nrhs >= 4)
+            {
+                if (!mxIsDouble(prhs[3]) || mxGetM(prhs[3])!=1 || mxGetN(prhs[3])!=1)
+                    mexErrMsgTxt("Fourth input must be scalar if specified.");
+                
+                int sft;
+    
+                sft=mxGetScalar(prhs[3]);
+                switch (sft){
+                    case 0:
+                        SFT = pftEvenOdd;
+                        break;
+                    case 1:
+                        SFT = pftNonZero;
+                        break;
+                    case 2:
+                        SFT = pftPositive;
+                        break;
+                    case 3:
+                        SFT = pftNegative;
+                        break;
+                    default:
+                        mexErrMsgTxt("Fourth input must be 0, 1, 2, or 3.");
+                }
+                
+            }
+            else
+                SFT=pftEvenOdd;
+            
+            if (nrhs >= 5)
+            {
+                if (!mxIsDouble(prhs[4]) || mxGetM(prhs[4])!=1 || mxGetN(prhs[4])!=1)
+                    mexErrMsgTxt("Fifth input must be scalar if specified.");
+                
+                int cft;
+    
+                cft=mxGetScalar(prhs[4]);
+                switch (cft){
+                    case 0:
+                        CFT = pftEvenOdd;
+                        break;
+                    case 1:
+                        CFT = pftNonZero;
+                        break;
+                    case 2:
+                        CFT = pftPositive;
+                        break;
+                    case 3:
+                        CFT = pftNegative;
+                        break;
+                    default:
+                        mexErrMsgTxt("Fifth input must be 0, 1, 2, or 3.");
+                }
+                
+            }
+            else
+                CFT=pftEvenOdd;
+                
+            /* Import polygons to structures */
+            read_polygons_MATLAB(prhs[0], subj);
+            read_polygons_MATLAB(prhs[1], clip);
+            
+            Clipper c;
+            c.AddPaths(subj, ptSubject, true); //assume closed polygons
+            c.AddPaths(clip, ptClip, true);
+            
+            if (c.Execute(CT, solution, SFT, CFT)){
+                dims[0] = 1;
+                dims[1] = solution.size();
+                plhs[0] = mxCreateStructArray(2, dims, 2, field_names);
+                write_polygons_MATLAB(plhs[0], solution);
+            } else
+                mexErrMsgTxt("Clipper Error.");
+        }
+        else
+        {
+            // Offset single input polygon
+            if (!mxIsDouble(prhs[1]) || mxGetM(prhs[1])!=1 || mxGetN(prhs[1])!=1)
+                mexErrMsgTxt("Second input must be either a structure or a scalar double.");
+            
+            if (nrhs > 3)
+                mexPrintf("Ignoring fill type arguments for offsetting.\n");
+            
+            /* Import polygons to structures */
+            read_polygons_MATLAB(prhs[0], subj);
+            
+            JoinType jt;
+            double delta, ml;
+            
+            delta = mxGetScalar(prhs[1]);
+            ml = mxGetScalar(prhs[2]);
+            
+            if (ml==0)
+                jt = jtRound;
+            else if (ml==1)
+                jt = jtSquare;
+            else
+                jt = jtMiter;
+            
+            ClipperOffset offs( ml );
+            //enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};            
+            offs.AddPaths(subj,jt,etClosedPolygon);
+            offs.Execute(solution, delta);
+            
+            dims[0] = 1;
+            dims[1] = solution.size();
+            plhs[0] = mxCreateStructArray(2, dims, 2, field_names);
+            write_polygons_MATLAB(plhs[0], solution);
+            
+            offs.Clear();
+        }
+    }
+    else
+        mexErrMsgTxt("One or three inputs required.");
+}
diff --git a/Third Party/ObjectiveC/objectivec_readme.txt b/Third Party/ObjectiveC/objectivec_readme.txt
new file mode 100644
index 0000000..548a026
--- /dev/null
+++ b/Third Party/ObjectiveC/objectivec_readme.txt	
@@ -0,0 +1,4 @@
+
+An Objective C wrapper for Clipper by John Swensen (http://www.swengames.com/) can be downloaded from:
+
+https://github.com/jpswensen/ClipperCocoa
\ No newline at end of file
diff --git a/Third Party/perl/perl_readme.txt b/Third Party/perl/perl_readme.txt
new file mode 100644
index 0000000..abcfa41
--- /dev/null
+++ b/Third Party/perl/perl_readme.txt	
@@ -0,0 +1,5 @@
+
+A Perl module written by Steffen M�ller < smueller at cpan.org > 
+that wraps the Clipper library can be downloaded from:
+
+https://metacpan.org/module/Math::Clipper 
\ No newline at end of file
diff --git a/Third Party/python/clipper.py b/Third Party/python/clipper.py
new file mode 100644
index 0000000..8fb137b
--- /dev/null
+++ b/Third Party/python/clipper.py	
@@ -0,0 +1,2259 @@
+#===============================================================================
+#                                                                              #
+# Author    :  Angus Johnson                                                   #
+# Version   :  5.1.6(b)                                                        #
+# Date      :  1 June 2013                                                     #
+# Website   :  http://www.angusj.com                                           #
+# Copyright :  Angus Johnson 2010-2013                                         #
+#                                                                              #
+# License:                                                                     #
+# Use, modification & distribution is subject to Boost Software License Ver 1. #
+# http://www.boost.org/LICENSE_1_0.txt                                         #
+#                                                                              #
+# Attributions:                                                                #
+# The code in this library is an extension of Bala Vatti's clipping algorithm: #
+# "A generic solution to polygon clipping"                                     #
+# Communications of the ACM, Vol 35, Issue 7 (July 1992) PP 56-63.             #
+# http://portal.acm.org/citation.cfm?id=129906                                 #
+#                                                                              #
+# Computer graphics and geometric modeling: implementation and algorithms      #
+# By Max K. Agoston                                                            #
+# Springer; 1 edition (January 4, 2005)                                        #
+# http://books.google.com/books?q=vatti+clipping+agoston                       #
+#                                                                              #
+# See also:                                                                    #
+# "Polygon Offsetting by Computing Winding Numbers"                            #
+# Paper no. DETC2005-85513 PP. 565-575                                         #
+# ASME 2005 International Design Engineering Technical Conferences             #
+# and Computers and Information in Engineering Conference (IDETC/CIE2005)      #
+# September 24-28, 2005 , Long Beach, California, USA                          #
+# http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf              #
+#                                                                              #
+#===============================================================================
+
+import math
+from collections import namedtuple
+
+horizontal = float('-inf')
+
+class ClipType: (Intersection, Union, Difference, Xor) = range(4)
+class PolyType:    (Subject, Clip) = range(2)
+class PolyFillType: (EvenOdd, NonZero, Positive, Negative) = range(4)
+class JoinType: (Square, Round, Miter) = range(3)
+class EndType: (Closed, Butt, Square, Round) = range(4)
+class EdgeSide: (Left, Right) = range(2)
+class Protects: (Neither, Left, Right, Both) = range(4)
+class Direction: (LeftToRight, RightToLeft) = range(2)
+
+Point = namedtuple('Point', 'x y')
+FloatPoint = namedtuple('FloatPoint', 'x y')
+Rect = namedtuple('FloatPoint', 'left top right bottom')
+
+class LocalMinima(object):
+    leftBound = rightBound = nextLm = None
+    def __init__(self, y, leftBound, rightBound):
+        self.y = y
+        self.leftBound = leftBound
+        self.rightBound = rightBound
+
+class Scanbeam(object):
+    __slots__ = ('y','nextSb')
+    def __init__(self, y, nextSb = None):
+        self.y = y
+        self.nextSb = nextSb
+    def __repr__(self):
+        s = 'None'
+        if self.nextSb is not None: s = '<obj>'
+        return "(y:%i, nextSb:%s)" % (self.y, s)
+
+class IntersectNode(object):
+    __slots__ = ('e1','e2','pt','nextIn')
+    def __init__(self, e1, e2, pt):
+        self.e1 = e1
+        self.e2 = e2
+        self.pt = pt
+        self.nextIn = None
+
+class OutPt(object):
+    __slots__ = ('idx','pt','prevOp','nextOp')
+    def __init__(self, idx, pt):
+        self.idx = idx
+        self.pt = pt
+        self.prevOp = None
+        self.nextOp = None
+
+class OutRec(object):
+    __slots__ = ('idx','bottomPt','isHole','FirstLeft', 'pts','PolyNode')
+    def __init__(self, idx):
+        self.idx = idx
+        self.bottomPt = None
+        self.isHole = False
+        self.FirstLeft = None
+        self.pts = None
+        self.PolyNode = None
+
+class JoinRec(object):
+    __slots__ = ('pt1a','pt1b','poly1Idx','pt2a', 'pt2b','poly2Idx')
+
+class HorzJoin(object):
+    edge = None
+    savedIdx = 0
+    prevHj = None
+    nextHj = None
+    def __init__(self, edge, idx):
+        self.edge = edge
+        self.savedIdx = idx
+
+#===============================================================================
+# Unit global functions ...
+#===============================================================================
+def IntsToPoints(ints):
+    result = []
+    for i in range(0, len(ints), 2):
+        result.append(Point(ints[i], ints[i+1]))
+    return result
+
+def Area(polygon):
+    # see http://www.mathopenref.com/coordpolygonarea2.html
+    highI = len(polygon) - 1
+    A = (polygon[highI].x + polygon[0].x) * (polygon[0].y - polygon[highI].y)
+    for i in range(highI):
+        A += (polygon[i].x + polygon[i+1].x) * (polygon[i+1].y - polygon[i].y)
+    return float(A) / 2
+
+def Orientation(polygon):
+    return Area(polygon) > 0.0
+
+#===============================================================================
+# PolyNode & PolyTree classes (+ ancilliary functions)
+#===============================================================================
+class PolyNode(object):
+    """Node of PolyTree"""
+    
+    def __init__(self):
+        self.Contour = []
+        self.Childs = []
+        self.Parent = None
+        self.Index = 0
+        self.ChildCount = 0
+    
+    def IsHole(self):
+        result = True
+        while (self.Parent is not None):
+            result = not result
+            self.Parent = self.Parent.Parent
+        return result
+    
+    def GetNext(self):
+        if (self.ChildCount > 0):
+            return self.Childs[0]
+        else:
+            return self._GetNextSiblingUp()
+    
+    def _AddChild(self, node):
+        self.Childs.append(node)
+        node.Index = self.ChildCount
+        node.Parent = self
+        self.ChildCount += 1
+    
+    def _GetNextSiblingUp(self):
+        if (self.Parent is None):
+            return None
+        elif (self.Index == self.Parent.ChildCount - 1):
+            return self.Parent._GetNextSiblingUp()
+        else:
+            return self.Parent.Childs[self.Index +1]
+
+class PolyTree(PolyNode):
+    """Container for PolyNodes"""
+
+    def __init__(self):
+        PolyNode.__init__(self)
+        self._AllNodes = []
+        
+    def Clear(self):
+        self._AllNodes = []
+        self.Childs = []
+        self.ChildCount = 0
+    
+    def GetFirst(self):
+        if (self.ChildCount > 0):
+            return self.Childs[0]
+        else:
+            return None
+    
+    def Total(self):
+        return len(self._AllNodes)
+
+def _AddPolyNodeToPolygons(polynode, polygons):
+    """Internal function for PolyTreeToPolygons()"""
+    if (len(polynode.Contour) > 0):
+        polygons.append(polynode.Contour)
+    for i in range(polynode.ChildCount):
+        _AddPolyNodeToPolygons(polynode.Childs[i], polygons)
+
+def PolyTreeToPolygons(polyTree):
+    result = []
+    _AddPolyNodeToPolygons(polyTree, result)
+    return result
+
+#===============================================================================
+# Edge class 
+#===============================================================================
+
+class Edge(object):
+
+    def __init__(self):
+        self.Bot = Point(0,0)
+        self.Curr = Point(0,0)
+        self.Top = Point(0,0)
+        self.Delta = Point(0,0) 
+        self.dx = float(0.0)
+        self.polyType = PolyType.Subject 
+        self.side = EdgeSide.Left
+        self.windDelta, self.windCnt, self.windCnt2 = 0, 0, 0 
+        self.outIdx = -1
+        self.nextE, self.prevE, self.nextInLML = None, None, None
+        self.prevInAEL, self.nextInAEL, self.prevInSEL, self.nextInSEL = None, None, None, None
+        
+    def __repr__(self):
+        return "(%i,%i . %i,%i {dx:%0.2f} %i {%x})" % \
+            (self.Bot.x, self.Bot.y, self.Top.x, self.Top.y, self.dx, self.outIdx, id(self))
+
+#===============================================================================
+# ClipperBase class (+ data structs & ancilliary functions)
+#===============================================================================
+
+def _PointsEqual(pt1, pt2):
+    return (pt1.x == pt2.x) and (pt1.y == pt2.y)
+
+def _SlopesEqual(pt1, pt2, pt3, pt4 = None):
+    if pt4 is None:
+        return (pt1.y-pt2.y)*(pt2.x-pt3.x) == (pt1.x-pt2.x)*(pt2.y-pt3.y)
+    else:
+        return (pt1.y-pt2.y)*(pt3.x-pt4.x) == (pt1.x-pt2.x)*(pt3.y-pt4.y)
+
+def _SlopesEqual2(e1, e2):
+    return e1.Delta.y * e2.Delta.x == e1.Delta.x * e2.Delta.y
+
+def _SetDx(e):
+    e.Delta = Point(e.Top.x - e.Bot.x, e.Top.y - e.Bot.y)
+    if e.Delta.y == 0: e.dx = horizontal
+    else: e.dx = float(e.Delta.x)/float(e.Delta.y)
+
+def _SwapSides(e1, e2):
+    side    = e1.side
+    e1.side = e2.side
+    e2.side = side
+
+def _SwapPolyIndexes(e1, e2):
+    idx       = e1.outIdx
+    e1.outIdx = e2.outIdx
+    e2.outIdx = idx
+
+def _InitEdge(e, eNext, ePrev, pt, polyType):
+    e.nextE = eNext
+    e.prevE = ePrev
+    e.Curr = pt
+    if e.Curr.y >= e.nextE.Curr.y:
+        e.Bot = e.Curr
+        e.Top = e.nextE.Curr
+        e.windDelta = 1
+    else:
+        e.Top = e.Curr
+        e.Bot = e.nextE.Curr
+        e.windDelta = -1
+    _SetDx(e)
+    e.outIdx = -1
+    e.PolyType = polyType
+
+def _SwapX(e):
+    e.Curr = Point(e.Top.x, e.Curr.y)
+    e.Top = Point(e.Bot.x, e.Top.y)
+    e.Bot = Point(e.Curr.x, e.Bot.y)
+    
+class ClipperBase(object):
+
+    def __init__(self):
+        self._EdgeList      = []       # 2D array
+        self._LocalMinList  = None     # single-linked list of LocalMinima
+        self._CurrentLocMin = None
+        
+    def _InsertLocalMinima(self, lm):
+        if self._LocalMinList is None:
+            self._LocalMinList = lm
+        elif lm.y >= self._LocalMinList.y:
+            lm.nextLm = self._LocalMinList
+            self._LocalMinList = lm
+        else:
+            tmp = self._LocalMinList
+            while tmp.nextLm is not None and lm.y < tmp.nextLm.y:
+                    tmp = tmp.nextLm
+            lm.nextLm = tmp.nextLm
+            tmp.nextLm = lm
+
+    def _AddBoundsToLML(self, e):
+        e.nextInLML = None
+        e = e.nextE
+        while True:
+            if e.dx == horizontal:
+                if (e.nextE.Top.y < e.Top.y) and (e.nextE.Bot.x > e.prevE.Bot.x): break
+                if (e.Top.x != e.prevE.Bot.x): _SwapX(e)
+                e.nextInLML = e.prevE
+            elif e.Bot.y == e.prevE.Bot.y: break
+            else: e.nextInLML = e.prevE
+            e = e.nextE
+
+        if e.dx == horizontal:
+            if (e.Bot.x != e.prevE.Bot.x): _SwapX(e)
+            lm = LocalMinima(e.prevE.Bot.y, e.prevE, e)
+        elif (e.dx < e.prevE.dx):
+            lm = LocalMinima(e.prevE.Bot.y, e.prevE, e)
+        else:
+            lm = LocalMinima(e.prevE.Bot.y, e, e.prevE)
+        lm.leftBound.side = EdgeSide.Left
+        lm.rightBound.side = EdgeSide.Right
+        self._InsertLocalMinima(lm)
+        while True:
+            if e.nextE.Top.y == e.Top.y and e.nextE.dx != horizontal: break
+            e.nextInLML = e.nextE
+            e = e.nextE
+            if e.dx == horizontal and e.Bot.x != e.prevE.Top.x: _SwapX(e)
+        return e.nextE
+
+    def _Reset(self):
+        lm = self._LocalMinList
+        if lm is not None: self._CurrentLocMin = lm
+        while lm is not None:
+            e = lm.leftBound
+            while e is not None:
+                e.Curr    = e.Bot
+                e.side     = EdgeSide.Left
+                e.outIdx = -1
+                e = e.nextInLML
+            e = lm.rightBound
+            while e is not None:
+                e.Curr    = e.Bot
+                e.side     = EdgeSide.Right
+                e.outIdx = -1
+                e = e.nextInLML
+            lm = lm.nextLm
+            
+    def AddPolygon(self, polygon, polyType):
+        ln = len(polygon)
+        if ln < 3: return False
+        pg = polygon[:]
+        j = 0
+        # remove duplicate points and co-linear points
+        for i in range(1, len(polygon)):
+            if _PointsEqual(pg[j], polygon[i]): 
+                continue
+            elif (j > 0) and _SlopesEqual(pg[j-1], pg[j], polygon[i]):
+                if _PointsEqual(pg[j-1], polygon[i]): j -= 1
+            else: j += 1
+            pg[j] = polygon[i]
+        if (j < 2): return False
+        # remove duplicate points and co-linear edges at the loop around
+        # of the start and end coordinates ...
+        ln = j +1
+        while (ln > 2):
+            if _PointsEqual(pg[j], pg[0]): j -= 1
+            elif _PointsEqual(pg[0], pg[1]) or _SlopesEqual(pg[j], pg[0], pg[1]):
+                pg[0] = pg[j]
+                j -= 1
+            elif _SlopesEqual(pg[j-1], pg[j], pg[0]): j -= 1
+            elif _SlopesEqual(pg[0], pg[1], pg[2]):
+                for i in range(2, j +1): pg[i-1] = pg[i]
+                j -= 1
+            else: break
+            ln -= 1
+        if ln < 3: return False
+        edges = []
+        for i in range(ln):
+            edges.append(Edge())
+        edges[0].Curr = pg[0]
+        _InitEdge(edges[ln-1], edges[0], edges[ln-2], pg[ln-1], polyType)
+        for i in range(ln-2, 0, -1):
+            _InitEdge(edges[i], edges[i+1], edges[i-1], pg[i], polyType)
+        _InitEdge(edges[0], edges[1], edges[ln-1], pg[0], polyType)
+        e = edges[0]
+        eHighest = e
+        while True:
+            e.Curr = e.Bot
+            if e.Top.y < eHighest.Top.y: eHighest = e
+            e = e.nextE
+            if e == edges[0]: break
+        # make sure eHighest is positioned so the following loop works safely ...
+        if eHighest.windDelta > 0: eHighest = eHighest.nextE
+        if eHighest.dx == horizontal: eHighest = eHighest.nextE
+        # finally insert each local minima ...
+        e = eHighest
+        while True:
+            e = self._AddBoundsToLML(e)
+            if e == eHighest: break
+        self._EdgeList.append(edges)
+
+    def AddPolygons(self, polygons, polyType):
+        result = False
+        for p in polygons:
+            if self.AddPolygon(p, polyType): result = True
+        return result
+
+    def Clear(self):
+        self._EdgeList = []
+        self._LocalMinList    = None
+        self._CurrentLocMin = None
+
+    def _PopLocalMinima(self):
+        if self._CurrentLocMin is not None:
+            self._CurrentLocMin = self._CurrentLocMin.nextLm
+
+#===============================================================================
+# Clipper class (+ data structs & ancilliary functions)
+#===============================================================================
+def _IntersectPoint(edge1, edge2):
+    if _SlopesEqual2(edge1, edge2):
+        if (edge2.Bot.y > edge1.Bot.y): y = edge2.Bot.y 
+        else: y = edge1.Bot.y
+        return Point(0, y), False
+    if edge1.dx == 0:
+        x = edge1.Bot.x
+        if edge2.dx == horizontal:
+            y = edge2.Bot.y
+        else:
+            b2 = edge2.Bot.y - float(edge2.Bot.x)/edge2.dx
+            y = round(float(x)/edge2.dx + b2)
+    elif edge2.dx == 0:
+        x = edge2.Bot.x
+        if edge1.dx == horizontal:
+            y = edge1.Bot.y
+        else:
+            b1 = edge1.Bot.y - float(edge1.Bot.x)/edge1.dx
+            y = round(float(x)/edge1.dx + b1)
+    else:
+        b1 = float(edge1.Bot.x) - float(edge1.Bot.y) * edge1.dx
+        b2 = float(edge2.Bot.x) - float(edge2.Bot.y) * edge2.dx
+        m    = (b2-b1)/(edge1.dx - edge2.dx)
+        y    = round(m)
+        if math.fabs(edge1.dx) < math.fabs(edge2.dx):
+            x = round(edge1.dx * m + b1)
+        else:
+            x = round(edge2.dx * m + b2)
+    if (y < edge1.Top.y) or (y < edge2.Top.y):
+        if (edge1.Top.y > edge2.Top.y):
+            return edge1.Top, _TopX(edge2, edge1.Top.y) < edge1.Top.x
+        else:
+            return edge2.Top, _TopX(edge1, edge2.Top.y) > edge2.Top.x
+    else:
+        return Point(x,y), True
+
+def _TopX(e, currentY):
+    if currentY == e.Top.y: return e.Top.x
+    elif e.Top.x == e.Bot.x: return e.Bot.x
+    else: return e.Bot.x + round(e.dx * float(currentY - e.Bot.y))
+
+def _E2InsertsBeforeE1(e1,e2):
+    if (e2.Curr.x == e1.Curr.x): 
+        if (e2.Top.y > e1.Top.y):
+            return e2.Top.x < _TopX(e1, e2.Top.y) 
+        return e1.Top.x > _TopX(e2, e1.Top.y) 
+    else: 
+        return e2.Curr.x < e1.Curr.x
+
+def _IsMinima(e):
+    return e is not None and e.prevE.nextInLML != e and e.nextE.nextInLML != e
+
+def _IsMaxima(e, y):
+    return e is not None and e.Top.y == y and e.nextInLML is None
+
+def _IsIntermediate(e, y):
+    return e.Top.y == y and e.nextInLML is not None
+
+def _GetMaximaPair(e):
+    if not _IsMaxima(e.nextE, e.Top.y) or e.nextE.Top.x != e.Top.x:
+        return e.prevE
+    else:
+        return e.nextE
+
+def _GetnextInAEL(e, direction):
+    if direction == Direction.LeftToRight: return e.nextInAEL
+    else: return e.prevInAEL
+
+def _ProtectLeft(val):
+    if val: return Protects.Both
+    else: return Protects.Right
+
+def _ProtectRight(val):
+    if val: return Protects.Both
+    else: return Protects.Left
+
+def _GetDx(pt1, pt2):
+    if (pt1.y == pt2.y): return horizontal
+    else: return float(pt2.x - pt1.x)/float(pt2.y - pt1.y)
+
+def _Param1RightOfParam2(outRec1, outRec2):
+    while outRec1 is not None:
+        outRec1 = outRec1.FirstLeft
+        if outRec1 == outRec2: return True
+    return False
+
+def _FirstParamIsbottomPt(btmPt1, btmPt2):
+    p = btmPt1.prevOp
+    while _PointsEqual(p.pt, btmPt1.pt) and (p != btmPt1): p = p.prevOp
+    dx1p = abs(_GetDx(btmPt1.pt, p.pt))
+    p = btmPt1.nextOp
+    while _PointsEqual(p.pt, btmPt1.pt) and (p != btmPt1): p = p.nextOp
+    dx1n = abs(_GetDx(btmPt1.pt, p.pt))
+
+    p = btmPt2.prevOp
+    while _PointsEqual(p.pt, btmPt2.pt) and (p != btmPt2): p = p.prevOp
+    dx2p = abs(_GetDx(btmPt2.pt, p.pt))
+    p = btmPt2.nextOp
+    while _PointsEqual(p.pt, btmPt2.pt) and (p != btmPt2): p = p.nextOp
+    dx2n = abs(_GetDx(btmPt2.pt, p.pt))
+    return (dx1p >= dx2p and dx1p >= dx2n) or (dx1n >= dx2p and dx1n >= dx2n)
+
+def _GetBottomPt(pp):
+    dups = None
+    p = pp.nextOp
+    while p != pp:
+        if p.pt.y > pp.pt.y:
+            pp = p
+            dups = None
+        elif p.pt.y == pp.pt.y and p.pt.x <= pp.pt.x:
+            if p.pt.x < pp.pt.x:
+                dups = None
+                pp = p
+            else:
+                if p.nextOp != pp and p.prevOp != pp: dups = p
+        p = p.nextOp
+    if dups is not None:
+        while dups != p:
+            if not _FirstParamIsbottomPt(p, dups): pp = dups
+            dups = dups.nextOp
+            while not _PointsEqual(dups.pt, pp.pt): dups = dups.nextOp
+    return pp
+
+def _GetLowermostRec(outRec1, outRec2):
+    if (outRec1.bottomPt is None): 
+        outPt1 = _GetBottomPt(outRec1.pts)
+    else: outPt1 = outRec1.bottomPt
+    if (outRec2.bottomPt is None): 
+        outPt2 = _GetBottomPt(outRec2.pts)
+    else: outPt2 = outRec2.bottomPt
+    if (outPt1.pt.y > outPt2.pt.y): return outRec1
+    elif (outPt1.pt.y < outPt2.pt.y): return outRec2
+    elif (outPt1.pt.x < outPt2.pt.x): return outRec1
+    elif (outPt1.pt.x > outPt2.pt.x): return outRec2
+    elif (outPt1.nextOp == outPt1): return outRec2
+    elif (outPt2.nextOp == outPt2): return outRec1
+    elif _FirstParamIsbottomPt(outPt1, outPt2): return outRec1
+    else: return outRec2
+
+def _SetHoleState(e, outRec, polyOutList):
+    isHole = False
+    e2 = e.prevInAEL
+    while e2 is not None:
+        if e2.outIdx >= 0:
+            isHole = not isHole
+            if outRec.FirstLeft is None:
+                outRec.FirstLeft = polyOutList[e2.outIdx]
+        e2 = e2.prevInAEL
+    outRec.isHole = isHole
+
+def _PointCount(pts):
+    if pts is None: return 0
+    p = pts
+    result = 0
+    while True:
+        result += 1
+        p = p.nextOp
+        if p == pts: break
+    return result
+
+def _PointIsVertex(pt, outPts):
+    op = outPts
+    while True:
+        if _PointsEqual(op.pt, pt): return True
+        op = op.nextOp
+        if op == outPts: break
+    return False
+               
+def _ReversePolyPtLinks(pp):
+    if pp is None: return
+    pp1 = pp
+    while True:
+        pp2 = pp1.nextOp
+        pp1.nextOp = pp1.prevOp
+        pp1.prevOp = pp2;
+        pp1 = pp2
+        if pp1 == pp: break
+
+def _FixupOutPolygon(outRec):
+    lastOK = None
+    outRec.bottomPt = None
+    pp = outRec.pts
+    while True:
+        if pp.prevOp == pp or pp.nextOp == pp.prevOp:
+            outRec.pts = None
+            return
+        if _PointsEqual(pp.pt, pp.nextOp.pt) or \
+                _SlopesEqual(pp.prevOp.pt, pp.pt, pp.nextOp.pt):
+            lastOK = None
+            pp.prevOp.nextOp = pp.nextOp
+            pp.nextOp.prevOp = pp.prevOp
+            pp = pp.prevOp
+        elif pp == lastOK: break
+        else:
+            if lastOK is None: lastOK = pp
+            pp = pp.nextOp
+    outRec.pts = pp
+
+def _FixHoleLinkage(outRec):
+    if outRec.FirstLeft is None or \
+        (outRec.isHole != outRec.FirstLeft.isHole and \
+            outRec.FirstLeft.pts is not None): return
+    orfl = outRec.FirstLeft
+    while orfl is not None and \
+            (orfl.isHole == outRec.isHole or orfl.pts is None):
+        orfl = orfl.FirstLeft
+    outRec.FirstLeft = orfl
+    
+def _GetOverlapSegment(pt1a, pt1b, pt2a, pt2b):
+    # precondition: segments are co-linear
+    if abs(pt1a.x - pt1b.x) > abs(pt1a.y - pt1b.y):
+        if pt1a.x > pt1b.x: tmp = pt1a; pt1a = pt1b; pt1b = tmp
+        if pt2a.x > pt2b.x: tmp = pt2a; pt2a = pt2b; pt2b = tmp
+        if (pt1a.x > pt2a.x): pt1 = pt1a
+        else: pt1 = pt2a
+        if (pt1b.x < pt2b.x): pt2 = pt1b
+        else: pt2 = pt2b
+        return pt1, pt2, pt1.x < pt2.x
+    else:
+        if pt1a.y < pt1b.y: tmp = pt1a; pt1a = pt1b; pt1b = tmp 
+        if pt2a.y < pt2b.y: tmp = pt2a; pt2a = pt2b; pt2b = tmp
+        if (pt1a.y < pt2a.y): pt1 = pt1a 
+        else: pt1 = pt2a
+        if (pt1b.y > pt2b.y): pt2 = pt1b 
+        else: pt2 = pt2b
+        return pt1, pt2, pt1.y > pt2.y
+
+    
+def _FindSegment(outPt, pt1, pt2):
+    if outPt is None: return outPt, pt1, pt2, False
+    pt1a = pt1; pt2a = pt2
+    outPt2 = outPt
+    while True:
+        if _SlopesEqual(pt1a, pt2a, outPt.pt, outPt.prevOp.pt) and _SlopesEqual(pt1a, pt2a, outPt.pt):
+            pt1, pt2, overlap = _GetOverlapSegment(pt1a, pt2a, outPt.pt, outPt.prevOp.pt)
+            if overlap: return outPt, pt1, pt2, True
+        outPt = outPt.nextOp
+        if outPt == outPt2: return outPt, pt1, pt2, False
+
+def _Pt3IsBetweenPt1AndPt2(pt1, pt2, pt3):
+    if _PointsEqual(pt1, pt3) or _PointsEqual(pt2, pt3): return True
+    elif pt1.x != pt2.x: return (pt1.x < pt3.x) == (pt3.x < pt2.x)
+    else: return (pt1.y < pt3.y) == (pt3.y < pt2.y)
+
+def _InsertPolyPtBetween(outPt1, outPt2, pt):
+    if outPt1 == outPt2: raise Exception("JoinError")
+    result = OutPt(outPt1.idx, pt)
+    if outPt2 == outPt1.nextOp:
+        outPt1.nextOp = result
+        outPt2.prevOp = result
+        result.nextOp = outPt2
+        result.prevOp = outPt1
+    else:
+        outPt2.nextOp = result
+        outPt1.prevOp = result
+        result.nextOp = outPt1
+        result.prevOp = outPt2
+    return result
+
+def _PointOnLineSegment(pt, linePt1, linePt2):
+    return ((pt.x == linePt1.x) and (pt.y == linePt1.y)) or \
+        ((pt.x == linePt2.x) and (pt.y == linePt2.y)) or \
+        (((pt.x > linePt1.x) == (pt.x < linePt2.x)) and \
+        ((pt.y > linePt1.y) == (pt.y < linePt2.y)) and \
+        ((pt.x - linePt1.x) * (linePt2.y - linePt1.y) == \
+        (linePt2.x - linePt1.x) * (pt.y - linePt1.y)))
+
+def _PointOnPolygon(pt, pp):
+    pp2 = pp;
+    while True:
+        if (_PointOnLineSegment(pt, pp2.pt, pp2.nextOp.pt)):
+            return True
+        pp2 = pp2.nextOp
+        if (pp2 == pp): return False
+
+def _PointInPolygon(pt, outPt): 
+    result = False
+    outPt2 = outPt
+    while True:
+        if ((((outPt2.pt.y <= pt.y) and (pt.y < outPt2.prevOp.pt.y)) or \
+            ((outPt2.prevOp.pt.y <= pt.y) and (pt.y < outPt2.pt.y))) and \
+            (pt.x < (outPt2.prevOp.pt.x - outPt2.pt.x) * (pt.y - outPt2.pt.y) / \
+            (outPt2.prevOp.pt.y - outPt2.pt.y) + outPt2.pt.x)): result = not result
+        outPt2 = outPt2.nextOp
+        if outPt2 == outPt: break
+
+def _Poly2ContainsPoly1(outPt1, outPt2):
+    pt = outPt1
+    if (_PointOnPolygon(pt.pt, outPt2)):
+        pt = pt.nextOp
+        while (pt != outPt1 and _PointOnPolygon(pt.pt, outPt2)):
+            pt = pt.nextOp
+        if (pt == outPt1): return True
+    return _PointInPolygon(pt.pt, outPt2)    
+    
+def _EdgesAdjacent(inode):
+    return (inode.e1.nextInSEL == inode.e2) or \
+        (inode.e1.prevInSEL == inode.e2)
+
+def _UpdateOutPtIdxs(outrec):
+    op = outrec.pts
+    while True:
+        op.idx = outrec.idx
+        op = op.prevOp
+        if (op == outrec.pts): break
+
+class Clipper(ClipperBase):
+
+    def __init__(self):
+        ClipperBase.__init__(self)
+
+        self.ReverseSolution     = False
+        self.ForceSimple       = False
+        
+        self._PolyOutList = []        
+        self._ClipType         = ClipType.Intersection
+        self._Scanbeam         = None
+        self._ActiveEdges      = None
+        self._SortedEdges      = None
+        self._IntersectNodes   = None
+        self._ClipFillType     = PolyFillType.EvenOdd
+        self._SubjFillType     = PolyFillType.EvenOdd
+        self._ExecuteLocked    = False
+        self._UsingPolyTree    = False
+        self._JoinList         = None
+        self._HorzJoins        = None
+        
+    def _Reset(self):
+        ClipperBase._Reset(self)
+        self._Scanbeam = None
+        self._PolyOutList = []
+        lm = self._LocalMinList
+        while lm is not None:
+            self._InsertScanbeam(lm.y)
+            lm = lm.nextLm
+
+    def Clear(self):
+        self._PolyOutList = []
+        ClipperBase.Clear(self)
+
+    def _InsertScanbeam(self, y):
+        if self._Scanbeam is None:
+            self._Scanbeam = Scanbeam(y)
+        elif y > self._Scanbeam.y:
+            self._Scanbeam = Scanbeam(y, self._Scanbeam)
+        else:
+            sb = self._Scanbeam
+            while sb.nextSb is not None and y <= sb.nextSb.y:
+                sb = sb.nextSb
+            if y == sb.y: return
+            newSb = Scanbeam(y, sb.nextSb)
+            sb.nextSb = newSb
+
+    def _PopScanbeam(self):
+        result = self._Scanbeam.y
+        self._Scanbeam = self._Scanbeam.nextSb
+        return result
+
+    def _SetWindingCount(self, edge):
+        e = edge.prevInAEL
+        while e is not None and e.PolyType != edge.PolyType:
+            e = e.prevInAEL
+        if e is None:
+            edge.windCnt = edge.windDelta
+            edge.windCnt2 = 0
+            e = self._ActiveEdges
+        elif self._IsEvenOddFillType(edge):
+            edge.windCnt = 1
+            edge.windCnt2 = e.windCnt2
+            e = e.nextInAEL
+        else:
+            if e.windCnt * e.windDelta < 0:
+                if (abs(e.windCnt) > 1):
+                    if (e.windDelta * edge.windDelta < 0): edge.windCnt = e.windCnt
+                    else: edge.windCnt = e.windCnt + edge.windDelta
+                else:
+                    edge.windCnt = e.windCnt + e.windDelta + edge.windDelta
+            elif (abs(e.windCnt) > 1) and (e.windDelta * edge.windDelta < 0):
+                edge.windCnt = e.windCnt
+            elif e.windCnt + edge.windDelta == 0:
+                edge.windCnt = e.windCnt
+            else:
+                edge.windCnt = e.windCnt + edge.windDelta
+            edge.windCnt2 = e.windCnt2
+            e = e.nextInAEL
+        # update windCnt2 ...
+        if self._IsEvenOddAltFillType(edge):
+            while (e != edge):
+                if edge.windCnt2 == 0: edge.windCnt2 = 1
+                else: edge.windCnt2 = 0
+                e = e.nextInAEL
+        else:
+            while (e != edge):
+                edge.windCnt2 += e.windDelta
+                e = e.nextInAEL
+
+    def _IsEvenOddFillType(self, edge):
+        if edge.PolyType == PolyType.Subject:
+            return self._SubjFillType == PolyFillType.EvenOdd
+        else:
+            return self._ClipFillType == PolyFillType.EvenOdd
+
+    def _IsEvenOddAltFillType(self, edge):
+        if edge.PolyType == PolyType.Subject:
+            return self._ClipFillType == PolyFillType.EvenOdd
+        else:
+            return self._SubjFillType == PolyFillType.EvenOdd
+
+    def _IsContributing(self, edge):
+        if edge.PolyType == PolyType.Subject:
+            pft = self._SubjFillType
+            pft2 = self._ClipFillType
+        else:
+            pft = self._ClipFillType
+            pft2 = self._SubjFillType
+        if pft == PolyFillType.EvenOdd or pft == PolyFillType.NonZero:
+            if abs(edge.windCnt) != 1: return False
+        elif pft == PolyFillType.Positive:
+            if edge.windCnt != 1: return False
+        elif pft == PolyFillType.Negative:
+            if edge.windCnt != -1: return False
+
+        if self._ClipType == ClipType.Intersection: ###########
+            if pft2 == PolyFillType.EvenOdd or pft2 == PolyFillType.NonZero:
+                return edge.windCnt2 != 0
+            elif pft2 == PolyFillType.Positive:
+                return edge.windCnt2 > 0
+            else:
+                return edge.windCnt2 < 0 # Negative
+        elif self._ClipType == ClipType.Union:      ###########
+            if pft2 == PolyFillType.EvenOdd or pft2 == PolyFillType.NonZero:
+                return edge.windCnt2 == 0
+            elif pft2 == PolyFillType.Positive:
+                return edge.windCnt2 <= 0
+            else: return edge.windCnt2 >= 0 # Negative
+        elif self._ClipType == ClipType.Difference: ###########
+            if edge.PolyType == PolyType.Subject:
+                if pft2 == PolyFillType.EvenOdd or pft2 == PolyFillType.NonZero:
+                    return edge.windCnt2 == 0
+                elif edge.PolyType == PolyFillType.Positive:
+                    return edge.windCnt2 <= 0
+                else:
+                    return edge.windCnt2 >= 0
+            else:                                   
+                if pft2 == PolyFillType.EvenOdd or pft2 == PolyFillType.NonZero:
+                    return edge.windCnt2 != 0
+                elif pft2 == PolyFillType.Positive:
+                    return edge.windCnt2 > 0
+                else:
+                    return edge.windCnt2 < 0
+        else: # self._ClipType == ClipType.XOR:     ###########
+            return True 
+
+    def _AddEdgeToSEL(self, edge):
+        if self._SortedEdges is None:
+            self._SortedEdges = edge
+            edge.prevInSEL = None
+            edge.nextInSEL = None
+        else:
+            # add edge to front of list ...
+            edge.nextInSEL = self._SortedEdges
+            edge.prevInSEL = None
+            self._SortedEdges.prevInSEL = edge
+            self._SortedEdges = edge
+
+    def _CopyAELToSEL(self):
+        e = self._ActiveEdges
+        self._SortedEdges = e
+        while e is not None:
+            e.prevInSEL = e.prevInAEL
+            e.nextInSEL = e.nextInAEL
+            e = e.nextInAEL
+
+    def _InsertEdgeIntoAEL(self, edge):
+        edge.prevInAEL = None
+        edge.nextInAEL = None
+        if self._ActiveEdges is None:
+            self._ActiveEdges = edge
+        elif _E2InsertsBeforeE1(self._ActiveEdges, edge):
+            edge.nextInAEL = self._ActiveEdges
+            self._ActiveEdges.prevInAEL = edge
+            self._ActiveEdges = edge
+        else:
+            e = self._ActiveEdges
+            while e.nextInAEL is not None and \
+                not _E2InsertsBeforeE1(e.nextInAEL, edge):
+                    e = e.nextInAEL
+            edge.nextInAEL = e.nextInAEL
+            if e.nextInAEL is not None: e.nextInAEL.prevInAEL = edge
+            edge.prevInAEL = e
+            e.nextInAEL = edge
+
+    def _InsertLocalMinimaIntoAEL(self, botY):
+        while self._CurrentLocMin is not None and \
+                 self._CurrentLocMin.y == botY:
+            lb = self._CurrentLocMin.leftBound
+            rb = self._CurrentLocMin.rightBound
+            self._InsertEdgeIntoAEL(lb)
+            self._InsertScanbeam(lb.Top.y)
+            self._InsertEdgeIntoAEL(rb)
+            if self._IsEvenOddFillType(lb):
+                lb.windDelta = 1
+                rb.windDelta = 1
+            else:
+                rb.windDelta = -lb.windDelta
+            self._SetWindingCount(lb)
+            rb.windCnt = lb.windCnt
+            rb.windCnt2 = lb.windCnt2
+            if rb.dx == horizontal:
+                self._AddEdgeToSEL(rb)
+                self._InsertScanbeam(rb.nextInLML.Top.y)
+            else:
+                self._InsertScanbeam(rb.Top.y)
+            if self._IsContributing(lb):
+                self._AddLocalMinPoly(lb, rb, Point(lb.Curr.x, self._CurrentLocMin.y))
+            
+            if rb.outIdx >= 0 and rb.dx == horizontal and self._HorzJoins is not None:
+                hj = self._HorzJoins
+                while True:
+                    dummy1, dummy2, overlap = _GetOverlapSegment(hj.edge.Bot, hj.edge.Top, rb.Bot, rb.Top)
+                    if overlap:
+                        self._AddJoin(hj.edge, rb, hj.savedIdx)
+                    hj = hj.nextHj
+                    if hj == self._HorzJoins: break
+            
+            if (lb.nextInAEL != rb):
+                
+                if rb.outIdx >= 0 and rb.prevInAEL.outIdx >= 0 and _SlopesEqual2(rb.prevInAEL, rb):
+                    self._AddJoin(rb, rb.prevInAEL)
+                
+                e = lb.nextInAEL
+                pt = lb.Curr
+                while e != rb:
+                    self._IntersectEdges(rb, e, pt)
+                    e = e.nextInAEL
+            self._PopLocalMinima()
+
+    def _SwapPositionsInAEL(self, e1, e2):
+        if e1.nextInAEL == e2:
+            nextE = e2.nextInAEL
+            if nextE is not None: nextE.prevInAEL = e1
+            prevE = e1.prevInAEL
+            if prevE is not None: prevE.nextInAEL = e2
+            e2.prevInAEL = prevE
+            e2.nextInAEL = e1
+            e1.prevInAEL = e2
+            e1.nextInAEL = nextE
+        elif e2.nextInAEL == e1:
+            nextE = e1.nextInAEL
+            if nextE is not None: nextE.prevInAEL = e2
+            prevE = e2.prevInAEL
+            if prevE is not None: prevE.nextInAEL = e1
+            e1.prevInAEL = prevE
+            e1.nextInAEL = e2
+            e2.prevInAEL = e1
+            e2.nextInAEL = nextE
+        else:
+            nextE = e1.nextInAEL
+            prevE = e1.prevInAEL
+            e1.nextInAEL = e2.nextInAEL
+            if e1.nextInAEL is not None: e1.nextInAEL.prevInAEL = e1
+            e1.prevInAEL = e2.prevInAEL
+            if e1.prevInAEL is not None: e1.prevInAEL.nextInAEL = e1
+            e2.nextInAEL = nextE
+            if e2.nextInAEL is not None: e2.nextInAEL.prevInAEL = e2
+            e2.prevInAEL = prevE
+            if e2.prevInAEL is not None: e2.prevInAEL.nextInAEL = e2
+        if e1.prevInAEL is None: self._ActiveEdges = e1
+        elif e2.prevInAEL is None: self._ActiveEdges = e2
+
+    def _SwapPositionsInSEL(self, e1, e2):
+        if e1.nextInSEL == e2:
+            nextE = e2.nextInSEL
+            if nextE is not None: nextE.prevInSEL = e1
+            prevE = e1.prevInSEL
+            if prevE is not None: prevE.nextInSEL = e2
+            e2.prevInSEL = prevE
+            e2.nextInSEL = e1
+            e1.prevInSEL = e2
+            e1.nextInSEL = nextE
+        elif e2.nextInSEL == e1:
+            nextE = e1.nextInSEL
+            if nextE is not None: nextE.prevInSEL = e2
+            prevE = e2.prevInSEL
+            if prevE is not None: prevE.nextInSEL = e1
+            e1.prevInSEL = prevE
+            e1.nextInSEL = e2
+            e2.prevInSEL = e1
+            e2.nextInSEL = nextE
+        else:
+            nextE = e1.nextInSEL
+            prevE = e1.prevInSEL
+            e1.nextInSEL = e2.nextInSEL
+            e1.nextInSEL = e2.nextInSEL
+            if e1.nextInSEL is not None: e1.nextInSEL.prevInSEL = e1
+            e1.prevInSEL = e2.prevInSEL
+            if e1.prevInSEL is not None: e1.prevInSEL.nextInSEL = e1
+            e2.nextInSEL = nextE
+            if e2.nextInSEL is not None: e2.nextInSEL.prevInSEL = e2
+            e2.prevInSEL = prevE
+            if e2.prevInSEL is not None: e2.prevInSEL.nextInSEL = e2
+        if e1.prevInSEL is None: self._SortedEdges = e1
+        elif e2.prevInSEL is None: self._SortedEdges = e2
+
+    def _IsTopHorz(self, xPos):
+        e = self._SortedEdges
+        while e is not None:
+            if (xPos >= min(e.Curr.x,e.Top.x)) and (xPos <= max(e.Curr.x,e.Top.x)):
+                return False
+            e = e.nextInSEL
+        return True
+
+    def _ProcessHorizontal(self, horzEdge):
+        if horzEdge.Curr.x < horzEdge.Top.x:
+            horzLeft = horzEdge.Curr.x
+            horzRight = horzEdge.Top.x
+            direction = Direction.LeftToRight
+        else:
+            horzLeft = horzEdge.Top.x
+            horzRight = horzEdge.Curr.x
+            direction = Direction.RightToLeft
+        eMaxPair = None
+        if horzEdge.nextInLML is None:
+            eMaxPair = _GetMaximaPair(horzEdge)
+        e = _GetnextInAEL(horzEdge, direction)
+        while e is not None:
+            if (e.Curr.x == horzEdge.Top.x) and eMaxPair is None:
+                if _SlopesEqual2(e, horzEdge.nextInLML): 
+                    if horzEdge.outIdx >= 0 and e.outIdx >= 0:
+                        self._AddJoin(horzEdge.nextInLML, e, horzEdge.outIdx)
+                    break
+                elif e.dx < horzEdge.nextInLML.dx: break
+            eNext = _GetnextInAEL(e, direction)
+            if eMaxPair is not None or \
+                ((direction == Direction.LeftToRight) and (e.Curr.x < horzRight)) or \
+                ((direction == Direction.RightToLeft) and (e.Curr.x > horzLeft)):
+                if e == eMaxPair:
+                    if direction == Direction.LeftToRight:
+                        self._IntersectEdges(horzEdge, e, Point(e.Curr.x, horzEdge.Curr.y))
+                    else:
+                        self._IntersectEdges(e, horzEdge, Point(e.Curr.x, horzEdge.Curr.y))
+                    return
+                elif e.dx == horizontal and not _IsMinima(e) and e.Curr.x <= e.Top.x:
+                    if direction == Direction.LeftToRight:
+                        self._IntersectEdges(horzEdge, e, Point(e.Curr.x, horzEdge.Curr.y),
+                            _ProtectRight(not self._IsTopHorz(e.Curr.x)))
+                    else:
+                        self._IntersectEdges(e, horzEdge, Point(e.Curr.x, horzEdge.Curr.y),
+                            _ProtectLeft(not self._IsTopHorz(e.Curr.x)))
+                elif (direction == Direction.LeftToRight):
+                    self._IntersectEdges(horzEdge, e, Point(e.Curr.x, horzEdge.Curr.y),
+                        _ProtectRight(not self._IsTopHorz(e.Curr.x)))
+                else:
+                    self._IntersectEdges(e, horzEdge, Point(e.Curr.x, horzEdge.Curr.y),
+                        _ProtectLeft(not self._IsTopHorz(e.Curr.x)))
+                self._SwapPositionsInAEL(horzEdge, e)
+            elif ((direction == Direction.LeftToRight and e.Curr.x >= horzRight) or \
+                (direction == Direction.RightToLeft and e.Curr.x <= horzLeft)): break
+            e = eNext
+        if horzEdge.nextInLML is not None:
+            if horzEdge.outIdx >= 0:
+                self._AddOutPt(horzEdge, horzEdge.Top)
+            self._UpdateEdgeIntoAEL(horzEdge)
+        else:
+            if horzEdge.outIdx >= 0:
+                self._IntersectEdges(horzEdge, eMaxPair, \
+                    Point(horzEdge.Top.x, horzEdge.Curr.y), Protects.Both)
+            if eMaxPair.outIdx >= 0: raise Exception("Clipper: Horizontal Error")
+            self._DeleteFromAEL(eMaxPair)
+            self._DeleteFromAEL(horzEdge)
+
+    def _ProcessHorizontals(self):
+        while self._SortedEdges is not None:
+            e = self._SortedEdges
+            self._DeleteFromSEL(e)
+            self._ProcessHorizontal(e)
+            
+    def _AddJoin(self, e1, e2, e1OutIdx = -1, e2OutIdx = -1):
+        jr = JoinRec()
+        if e1OutIdx >= 0: jr.poly1Idx = e1OutIdx
+        else: jr.poly1Idx = e1.outIdx
+        jr.pt1a = e1.Curr
+        jr.pt1b = e1.Top
+        if e2OutIdx >= 0: jr.poly2Idx = e2OutIdx 
+        else: jr.poly2Idx = e2.outIdx
+        jr.pt2a = e2.Curr
+        jr.pt2b = e2.Top
+        if self._JoinList is None: 
+            self._JoinList = []
+        self._JoinList.append(jr)
+        
+    def _FixupJoinRecs(self, jr, outPt, startIdx):
+        for i in range(startIdx, len(self._JoinList)):
+            jr2 = self._JoinList[i]
+            if jr2.poly1Idx == jr.poly1Idx and _PointIsVertex(jr2.pt1a, outPt):
+                jr2.poly1Idx = jr.poly2Idx
+            if jr2.poly2Idx == jr.poly1Idx and _PointIsVertex(jr2.pt2a, outPt):
+                jr2.poly2Idx = jr.poly2Idx
+                
+    def _AddHorzJoin(self, e, idx):
+        hj = HorzJoin(e, idx)
+        if self._HorzJoins == None:
+            self._HorzJoins = hj
+            hj.nextHj = hj
+            hj.prevHj = hj
+        else:
+            hj.nextHj = self._HorzJoins
+            hj.prevHj = self._HorzJoins.prevHj
+            self._HorzJoins.prevHj.nextHj = hj
+            self._HorzJoins.prevHj = hj
+
+    def _InsertIntersectNode(self, e1, e2, pt):
+        newNode = IntersectNode(e1, e2, pt)
+        if self._IntersectNodes is None:
+            self._IntersectNodes = newNode
+        elif newNode.pt.y > self._IntersectNodes.pt.y:
+            newNode.nextIn = self._IntersectNodes
+            self._IntersectNodes = newNode
+        else:
+            node = self._IntersectNodes
+            while node.nextIn is not None and \
+                newNode.pt.y < node.nextIn.pt.y:
+                node = node.nextIn
+            newNode.nextIn = node.nextIn
+            node.nextIn = newNode
+
+    def _ProcessIntersections(self, botY, topY):
+        try:
+            self._BuildIntersectList(botY, topY)
+            if self._IntersectNodes is None: return True
+            if self._IntersectNodes.nextIn is not None and \
+                not self._FixupIntersectionOrder(): return False 
+            self._ProcessIntersectList()
+            return True
+        finally:
+            self._IntersectNodes = None
+            self._SortedEdges = None
+            
+    def _BuildIntersectList(self, botY, topY):
+        e = self._ActiveEdges
+        if e is None: return
+        self._SortedEdges = e
+        while e is not None:
+            e.prevInSEL = e.prevInAEL
+            e.nextInSEL = e.nextInAEL
+            e.Curr = Point(_TopX(e, topY), e.Curr.y)
+            e = e.nextInAEL
+        while True:
+            isModified = False
+            e = self._SortedEdges
+            while e.nextInSEL is not None:
+                eNext = e.nextInSEL
+                if e.Curr.x <= eNext.Curr.x:
+                    e = eNext
+                    continue
+                pt, intersected = _IntersectPoint(e, eNext)
+                if not intersected and e.Curr.x > eNext.Curr.x +1: 
+                    raise Exception("Intersect Error")  
+                if pt.y > botY:
+                    pt = Point(_TopX(e, botY), botY)
+                self._InsertIntersectNode(e, eNext, pt)
+                self._SwapPositionsInSEL(e, eNext)
+                isModified = True
+            if e.prevInSEL is not None:
+                e.prevInSEL.nextInSEL = None
+            else:
+                break
+            if not isModified: break
+        self._SortedEdges = None
+        return
+
+    def _ProcessIntersectList(self):
+        while self._IntersectNodes is not None:
+            node = self._IntersectNodes
+            self._IntersectEdges(node.e1, node.e2, node.pt, Protects.Both)
+            self._SwapPositionsInAEL(node.e1, node.e2)
+            self._IntersectNodes = node.nextIn
+
+    def _DeleteFromAEL(self, e):
+        aelPrev = e.prevInAEL
+        aelNext = e.nextInAEL
+        if aelPrev is None and aelNext is None and e != self._ActiveEdges:
+            return
+        if aelPrev is not None:
+            aelPrev.nextInAEL = aelNext
+        else:
+            self._ActiveEdges = aelNext
+        if aelNext is not None:
+            aelNext.prevInAEL = aelPrev
+        e.nextInAEL = None
+        e.prevInAEL = None
+
+    def _DeleteFromSEL(self, e):
+        SELPrev = e.prevInSEL
+        SELNext = e.nextInSEL
+        if SELPrev is None and SELNext is None and e != self._SortedEdges:
+            return
+        if SELPrev is not None:
+            SELPrev.nextInSEL = SELNext
+        else:
+            self._SortedEdges = SELNext
+        if SELNext is not None:
+            SELNext.prevInSEL = SELPrev
+        e.nextInSEL = None
+        e.prevInSEL = None
+
+    def _IntersectEdges(self, e1, e2, pt, protects = Protects.Neither):
+        e1stops = protects & Protects.Left == 0 and \
+                e1.nextInLML is None and \
+                e1.Top.x == pt.x and e1.Top.y == pt.y
+        e2stops = protects & Protects.Right == 0 and \
+                e2.nextInLML is None and \
+                e2.Top.x == pt.x and e2.Top.y == pt.y
+        e1Contributing = e1.outIdx >= 0
+        e2contributing = e2.outIdx >= 0
+
+        if e1.PolyType == e2.PolyType:
+            if self._IsEvenOddFillType(e1):
+                e1Wc = e1.windCnt
+                e1.windCnt = e2.windCnt
+                e2.windCnt = e1Wc
+            else:
+                if e1.windCnt + e2.windDelta == 0: e1.windCnt = -e1.windCnt
+                else: e1.windCnt += e2.windDelta
+                if e2.windCnt - e1.windDelta == 0: e2.windCnt = -e2.windCnt
+                else: e2.windCnt -= e1.windDelta
+        else:
+            if not self._IsEvenOddFillType(e2): e1.windCnt2 += e2.windDelta
+            elif e1.windCnt2 == 0: e1.windCnt2 = 1
+            else: e1.windCnt2 = 0
+            if not self._IsEvenOddFillType(e1): e2.windCnt2 -= e1.windDelta
+            elif e2.windCnt2 == 0: e2.windCnt2 = 1
+            else: e2.windCnt2 = 0
+
+        if e1.PolyType == PolyType.Subject:
+            e1FillType = self._SubjFillType
+            e1FillType2 = self._ClipFillType
+        else:
+            e1FillType = self._ClipFillType
+            e1FillType2 = self._SubjFillType
+
+        if e2.PolyType == PolyType.Subject:
+            e2FillType = self._SubjFillType
+            e2FillType2 = self._ClipFillType
+        else:
+            e2FillType = self._ClipFillType
+            e2FillType2 = self._SubjFillType
+
+        if e1FillType == PolyFillType.Positive: e1Wc = e1.windCnt
+        elif e1FillType == PolyFillType.Negative: e1Wc = -e1.windCnt
+        else: e1Wc = abs(e1.windCnt)
+
+        if e2FillType == PolyFillType.Positive: e2Wc = e2.windCnt
+        elif e2FillType == PolyFillType.Negative: e2Wc = -e2.windCnt
+        else: e2Wc = abs(e2.windCnt)
+
+        if e1Contributing and e2contributing:
+            if e1stops or e2stops or \
+                (e1Wc != 0 and e1Wc != 1) or (e2Wc != 0 and e2Wc != 1) or \
+                (e1.PolyType != e2.PolyType and self._ClipType != ClipType.Xor):
+                    self._AddLocalMaxPoly(e1, e2, pt)
+            else:
+                self._AddOutPt(e1, pt)
+                self._AddOutPt(e2, pt)
+                _SwapSides(e1, e2)
+                _SwapPolyIndexes(e1, e2)
+        elif e1Contributing:
+            if (e2Wc == 0 or e2Wc == 1): 
+                self._AddOutPt(e1, pt)
+                _SwapSides(e1, e2)
+                _SwapPolyIndexes(e1, e2)
+        elif e2contributing:
+            if (e1Wc == 0 or e1Wc == 1): 
+                self._AddOutPt(e2, pt)
+                _SwapSides(e1, e2)
+                _SwapPolyIndexes(e1, e2)
+
+        elif    (e1Wc == 0 or e1Wc == 1) and (e2Wc == 0 or e2Wc == 1) and \
+            not e1stops and not e2stops:
+
+            e1FillType2 = e2FillType2 = PolyFillType.EvenOdd
+            if e1FillType2 == PolyFillType.Positive: e1Wc2 = e1.windCnt2
+            elif e1FillType2 == PolyFillType.Negative: e1Wc2 = -e1.windCnt2
+            else: e1Wc2 = abs(e1.windCnt2)
+            if e2FillType2 == PolyFillType.Positive: e2Wc2 = e2.windCnt2
+            elif e2FillType2 == PolyFillType.Negative: e2Wc2 = -e2.windCnt2
+            else: e2Wc2 = abs(e2.windCnt2)
+
+            if e1.PolyType != e2.PolyType:
+                self._AddLocalMinPoly(e1, e2, pt)
+            elif e1Wc == 1 and e2Wc == 1:
+                if self._ClipType == ClipType.Intersection:
+                    if e1Wc2 > 0 and e2Wc2 > 0:
+                        self._AddLocalMinPoly(e1, e2, pt)
+                elif self._ClipType == ClipType.Union:
+                    if e1Wc2 <= 0 and e2Wc2 <= 0:
+                        self._AddLocalMinPoly(e1, e2, pt)
+                elif self._ClipType == ClipType.Difference:
+                    if (e1.PolyType == PolyType.Clip and e1Wc2 > 0 and e2Wc2 > 0) or \
+                        (e1.PolyType == PolyType.Subject and e1Wc2 <= 0 and e2Wc2 <= 0):
+                            self._AddLocalMinPoly(e1, e2, pt)
+                else:
+                    self._AddLocalMinPoly(e1, e2, pt)
+            else:
+                _SwapSides(e1, e2, self._PolyOutList)
+
+        if e1stops != e2stops and \
+            ((e1stops and e1.outIdx >= 0) or (e2stops and e2.outIdx >= 0)):
+                _SwapSides(e1, e2, self._PolyOutList)
+                _SwapPolyIndexes(e1, e2)
+        if e1stops: self._DeleteFromAEL(e1)
+        if e2stops: self._DeleteFromAEL(e2)
+
+    def _DoMaxima(self, e, topY):
+        eMaxPair = _GetMaximaPair(e)
+        x = e.Top.x
+        eNext = e.nextInAEL
+        while eNext != eMaxPair:
+            if eNext is None: raise Exception("DoMaxima error")
+            self._IntersectEdges(e, eNext, Point(x, topY), Protects.Both)
+            self._SwapPositionsInAEL(e, eNext)
+            eNext = e.nextInAEL
+        if e.outIdx < 0 and eMaxPair.outIdx < 0:
+            self._DeleteFromAEL(e)
+            self._DeleteFromAEL(eMaxPair)
+        elif e.outIdx >= 0 and eMaxPair.outIdx >= 0:
+            self._IntersectEdges(e, eMaxPair, Point(x, topY))
+        else:
+            raise Exception("DoMaxima error")
+
+    def _UpdateEdgeIntoAEL(self, e):
+        if e.nextInLML is None:
+            raise Exception("UpdateEdgeIntoAEL error")
+        aelPrev = e.prevInAEL
+        aelNext = e.nextInAEL
+        e.nextInLML.outIdx = e.outIdx
+        if aelPrev is not None:
+            aelPrev.nextInAEL = e.nextInLML
+        else:
+            self._ActiveEdges = e.nextInLML
+        if aelNext is not None:
+            aelNext.prevInAEL = e.nextInLML
+        e.nextInLML.side = e.side
+        e.nextInLML.windDelta = e.windDelta
+        e.nextInLML.windCnt = e.windCnt
+        e.nextInLML.windCnt2 = e.windCnt2
+        e = e.nextInLML
+        e.prevInAEL = aelPrev
+        e.nextInAEL = aelNext
+        if e.dx != horizontal:
+            self._InsertScanbeam(e.Top.y)
+        return e
+
+    def _AddLocalMinPoly(self, e1, e2, pt):
+        if e2.dx == horizontal or e1.dx > e2.dx:
+            self._AddOutPt(e1, pt)
+            e2.outIdx = e1.outIdx
+            e1.side = EdgeSide.Left
+            e2.side = EdgeSide.Right
+            e = e1
+            if e.prevInAEL == e2: prevE = e2.prevInAEL
+            else: prevE = e1.prevInAEL
+        else:
+            self._AddOutPt(e2, pt)
+            e1.outIdx = e2.outIdx
+            e1.side = EdgeSide.Right
+            e2.side = EdgeSide.Left
+            e = e2
+            if e.prevInAEL == e1: prevE = e1.prevInAEL
+            else: prevE = e.prevInAEL
+
+        if prevE is not None and prevE.outIdx >= 0 and \
+            _TopX(prevE, pt.y) == _TopX(e, pt.y) and \
+           _SlopesEqual2(e, prevE): 
+                self._AddJoin(e, prevE)
+        return
+
+    def _AddLocalMaxPoly(self, e1, e2, pt):
+        self._AddOutPt(e1, pt)
+        if e1.outIdx == e2.outIdx:
+            e1.outIdx = -1
+            e2.outIdx = -1
+        elif e1.outIdx < e2.outIdx:
+            self._AppendPolygon(e1, e2)
+        else:
+            self._AppendPolygon(e2, e1)
+
+    def _CreateOutRec(self):
+        outRec = OutRec(len(self._PolyOutList))
+        self._PolyOutList.append(outRec)
+        return outRec
+    
+    def _AddOutPt(self, e, pt):
+        toFront = e.side == EdgeSide.Left
+        if e.outIdx < 0:
+            outRec = self._CreateOutRec();
+            e.outIdx = outRec.idx
+            op = OutPt(outRec.idx, pt)
+            op.nextOp = op
+            op.prevOp = op
+            outRec.pts = op
+            _SetHoleState(e, outRec, self._PolyOutList)
+        else:
+            outRec = self._PolyOutList[e.outIdx]
+            op = outRec.pts
+            if (toFront and _PointsEqual(pt, op.pt)) or \
+                (not toFront and _PointsEqual(pt, op.prevOp.pt)): return
+            op2 = OutPt(outRec.idx, pt)
+            op2.nextOp = op
+            op2.prevOp = op.prevOp
+            op.prevOp.nextOp = op2
+            op.prevOp = op2
+            if toFront: outRec.pts = op2
+        
+    def _AppendPolygon(self, e1, e2):
+        outRec1 = self._PolyOutList[e1.outIdx]
+        outRec2 = self._PolyOutList[e2.outIdx]
+        holeStateRec = None
+        if _Param1RightOfParam2(outRec1, outRec2): holeStateRec = outRec2
+        elif _Param1RightOfParam2(outRec2, outRec1): holeStateRec = outRec1
+        else: holeStateRec = _GetLowermostRec(outRec1, outRec2)
+                
+        p1_lft = outRec1.pts
+        p2_lft = outRec2.pts
+        p1_rt = p1_lft.prevOp
+        p2_rt = p2_lft.prevOp
+        newSide = EdgeSide.Left
+        
+        if e1.side == EdgeSide.Left:
+            if e2.side == EdgeSide.Left:
+                # z y x a b c
+                _ReversePolyPtLinks(p2_lft)
+                p2_lft.nextOp = p1_lft
+                p1_lft.prevOp = p2_lft
+                p1_rt.nextOp = p2_rt
+                p2_rt.prevOp = p1_rt
+                outRec1.pts = p2_rt
+            else:
+                # x y z a b c
+                p2_rt.nextOp = p1_lft
+                p1_lft.prevOp = p2_rt
+                p2_lft.prevOp = p1_rt
+                p1_rt.nextOp = p2_lft
+                outRec1.pts = p2_lft
+        else:
+            newSide = EdgeSide.Right
+            if e2.side == EdgeSide.Right:
+                # a b c z y x
+                _ReversePolyPtLinks(p2_lft)
+                p1_rt.nextOp = p2_rt
+                p2_rt.prevOp = p1_rt
+                p2_lft.nextOp = p1_lft
+                p1_lft.prevOp = p2_lft
+            else:
+                # a b c x y z
+                p1_rt.nextOp = p2_lft
+                p2_lft.prevOp = p1_rt
+                p1_lft.prevOp = p2_rt
+                p2_rt.nextOp = p1_lft
+                
+        outRec1.bottomPt = None                
+        if holeStateRec == outRec2:
+            if outRec2.FirstLeft != outRec1:
+                outRec1.FirstLeft = outRec2.FirstLeft
+            outRec1.isHole = outRec2.isHole
+        outRec2.pts = None
+        outRec2.bottomPt = None
+        outRec2.FirstLeft = outRec1
+        OKIdx = outRec1.idx
+        ObsoleteIdx = outRec2.idx
+
+        e1.outIdx = -1
+        e2.outIdx = -1
+
+        e = self._ActiveEdges
+        while e is not None:
+            if e.outIdx == ObsoleteIdx:
+                e.outIdx = OKIdx
+                e.side = newSide
+                break
+            e = e.nextInAEL
+        outRec2.idx = outRec1.idx    
+        
+    def _FixupIntersectionOrder(self):
+        self._CopyAELToSEL()
+        inode = self._IntersectNodes
+        while inode is not None:
+            if (not _EdgesAdjacent(inode)):
+                nextNode = inode.nextIn
+                while (nextNode and not _EdgesAdjacent(nextNode)):
+                    nextNode = nextNode.nextIn
+                if (nextNode is None): return False
+                e1 = inode.e1
+                e2 = inode.e2
+                p = inode.pt
+                inode.e1 = nextNode.e1
+                inode.e2 = nextNode.e2
+                inode.pt = nextNode.pt
+                nextNode.e1 = e1
+                nextNode.e2 = e2
+                nextNode.pt = p
+        
+            self._SwapPositionsInSEL(inode.e1, inode.e2);
+            inode = inode.nextIn
+        return True
+                                
+    def _ProcessEdgesAtTopOfScanbeam(self, topY):
+        e = self._ActiveEdges
+        while e is not None:
+            if _IsMaxima(e, topY) and _GetMaximaPair(e).dx != horizontal:
+                ePrev = e.prevInAEL
+                self._DoMaxima(e, topY)
+                if ePrev is None: e = self._ActiveEdges
+                else: e = ePrev.nextInAEL
+            else:
+                intermediateVert = _IsIntermediate(e, topY)
+                if intermediateVert and e.nextInLML.dx == horizontal:
+                    if e.outIdx >= 0:
+                        self._AddOutPt(e, e.Top)
+                        hj = self._HorzJoins
+                        if hj is not None:
+                            while True:
+                                _1, _2, overlap = _GetOverlapSegment(
+                                    hj.edge.Bot, hj.edge.Top, e.nextInLML.Bot, e.nextInLML.Top)
+                                if overlap: self._AddJoin(hj.edge, e.nextInLML, hj.savedIdx, e.outIdx)
+                                hj = hj.nextHj
+                            if hj == self._HorzJoins: break
+                            self._AddHorzJoin(e.nextInLML, e.outIdx)                        
+                        
+                    e = self._UpdateEdgeIntoAEL(e)
+                    self._AddEdgeToSEL(e)
+                else:
+                    e.Curr = Point(_TopX(e, topY), topY)
+                    if (self.ForceSimple and e.prevInAEL is not None and
+                      e.prevInAEL.Curr.x == e.Curr.x and
+                      e.outIdx >= 0 and e.prevInAEL.outIdx >= 0):
+                        if (intermediateVert):
+                            self._AddOutPt(e.prevInAEL, Point(e.Curr.x, topY));
+                        else:
+                            self._AddOutPt(e, Point(e.Curr.x, topY))
+                e = e.nextInAEL
+
+        self._ProcessHorizontals()
+
+        e = self._ActiveEdges
+        while e is not None:
+            if _IsIntermediate(e, topY):
+                if (e.outIdx >= 0) :
+                    self._AddOutPt(e, e.Top)
+                e = self._UpdateEdgeIntoAEL(e)
+                
+                ePrev = e.prevInAEL
+                eNext  = e.nextInAEL
+                if ePrev is not None and ePrev.Curr.x == e.Bot.x and \
+                    (ePrev.Curr.y == e.Bot.y) and (e.outIdx >= 0) and \
+                    (ePrev.outIdx >= 0) and (ePrev.Curr.y > ePrev.Top.y) and \
+                    _SlopesEqual2(e, ePrev):
+                        self._AddOutPt(ePrev, e.Bot)
+                        self._AddJoin(e, ePrev)
+                elif eNext is not None and (eNext.Curr.x == e.Bot.x) and \
+                    (eNext.Curr.y == e.Bot.y) and (e.outIdx >= 0) and \
+                    (eNext.outIdx >= 0) and (eNext.Curr.y > eNext.Top.y) and \
+                    _SlopesEqual2(e, eNext):
+                        self._AddOutPt(eNext, e.Bot)
+                        self._AddJoin(e, eNext)
+                
+            e = e.nextInAEL
+                      
+    def _Area(self, pts):
+        # see http://www.mathopenref.com/coordpolygonarea2.html
+        result = 0.0
+        p = pts
+        while True:
+            result += (p.pt.x + p.prevOp.pt.x) * (p.prevOp.pt.y - p.pt.y)
+            p = p.nextOp
+            if p == pts: break
+        return result / 2
+        
+    def _JoinPoints(self, jr):
+        p1, p2 = None, None
+        outRec1 = self._PolyOutList[jr.poly1Idx]
+        outRec2 = self._PolyOutList[jr.poly2Idx]
+        if outRec1 is None or outRec2 is None: return p1, p2, False        
+        pp1a = outRec1.pts; pp2a = outRec2.pts
+        pt1 = jr.pt2a; pt2 = jr.pt2b
+        pt3 = jr.pt1a; pt4 = jr.pt1b
+        pp1a, pt1, pt2, result = _FindSegment(pp1a, pt1, pt2)
+        if not result: return p1, p2, False
+        if (outRec1 == outRec2):
+            pp2a = pp1a.nextOp
+            pp2a, pt3, pt4, result = _FindSegment(pp2a, pt3, pt4) 
+            if not result or pp2a == pp1a: return p1, p2, False
+        else:
+            pp2a, pt3, pt4, result = _FindSegment(pp2a, pt3, pt4)
+            if not result: return p1, p2, False
+        pt1, pt2, result = _GetOverlapSegment(pt1, pt2, pt3, pt4) 
+        if not result: return p1, p2, False
+    
+        prevOp = pp1a.prevOp
+        if _PointsEqual(pp1a.pt, pt1): p1 = pp1a
+        elif _PointsEqual(prevOp.pt, pt1): p1 = prevOp
+        else: p1 = _InsertPolyPtBetween(pp1a, prevOp, pt1)
+        
+        if _PointsEqual(pp1a.pt, pt2): p2 = pp1a
+        elif _PointsEqual(prevOp.pt, pt2): p2 = prevOp
+        elif (p1 == pp1a) or (p1 == prevOp):
+            p2 = _InsertPolyPtBetween(pp1a, prevOp, pt2)
+        elif _Pt3IsBetweenPt1AndPt2(pp1a.pt, p1.pt, pt2):
+            p2 = _InsertPolyPtBetween(pp1a, p1, pt2)
+        else: p2 = _InsertPolyPtBetween(p1, prevOp, pt2)
+    
+        prevOp = pp2a.prevOp
+        if _PointsEqual(pp2a.pt, pt1): p3 = pp2a
+        elif _PointsEqual(prevOp.pt, pt1): p3 = prevOp
+        else: p3 = _InsertPolyPtBetween(pp2a, prevOp, pt1)        
+        if _PointsEqual(pp2a.pt, pt2): p4 = pp2a
+        elif _PointsEqual(prevOp.pt, pt2): p4 = prevOp
+        elif (p3 == pp2a) or (p3 == prevOp):
+            p4 = _InsertPolyPtBetween(pp2a, prevOp, pt2)
+        elif _Pt3IsBetweenPt1AndPt2(pp2a.pt, p3.pt, pt2):
+            p4 = _InsertPolyPtBetween(pp2a, p3, pt2)
+        else: p4 = _InsertPolyPtBetween(p3, prevOp, pt2)
+    
+        if p1.nextOp == p2 and p3.prevOp == p4:
+            p1.nextOp = p3
+            p3.prevOp = p1
+            p2.prevOp = p4
+            p4.nextOp = p2
+            return p1, p2, True
+        elif p1.prevOp == p2 and p3.nextOp == p4:
+            p1.prevOp = p3
+            p3.nextOp = p1
+            p2.nextOp = p4
+            p4.prevOp = p2
+            return p1, p2, True
+        return p1, p2, False
+
+    def _FixupFirstLefts1(self, oldOutRec, newOutRec):
+        for outRec in self._PolyOutList:
+            if outRec.pts is not None and outRec.FirstLeft == oldOutRec:
+                if _Poly2ContainsPoly1(outRec.pts, newOutRec.pts):
+                    outRec.FirstLeft = newOutRec
+
+    def _FixupFirstLefts2(self, oldOutRec, newOutRec):
+        for outRec in self._PolyOutList:
+            if outRec.FirstLeft == oldOutRec: outRec.FirstLeft = newOutRec
+
+    def _GetOutRec(self, idx):
+        outrec = self._PolyOutList[idx]
+        while (outrec != self._PolyOutList[outrec.idx]):
+            outrec = self._PolyOutList[outrec.idx]
+        return outrec
+
+    def _JoinCommonEdges(self):
+        for i in range(len(self._JoinList)):
+            jr = self._JoinList[i]
+            outRec1 = self._GetOutRec(jr.poly1Idx)
+            outRec2 = self._GetOutRec(jr.poly2Idx)
+            if outRec1.pts is None or outRec2.pts is None: continue
+
+            if outRec1 == outRec2: holeStateRec = outRec1
+            elif _Param1RightOfParam2(outRec1, outRec2): holeStateRec = outRec2
+            elif _Param1RightOfParam2(outRec2, outRec1): holeStateRec = outRec1
+            else: holeStateRec = _GetLowermostRec(outRec1, outRec2)
+
+            p1, p2, result = self._JoinPoints(jr)
+            if not result: continue
+
+            if outRec1 == outRec2:
+                outRec1.pts = p1
+                outRec1.bottomPt = None
+                outRec2 = self._CreateOutRec()
+                outRec2.pts = p2
+                jr.poly2Idx = outRec2.idx
+
+                if _Poly2ContainsPoly1(outRec2.pts, outRec1.pts):
+                    outRec2.isHole = not outRec1.isHole
+                    outRec2.FirstLeft = outRec1
+                    
+                    self._FixupJoinRecs(jr, p2, i + 1)
+                    
+                    if self._UsingPolyTree: self._FixupFirstLefts2(outRec2, outRec1)
+                    
+                    _FixupOutPolygon(outRec1)
+                    _FixupOutPolygon(outRec2)
+                    
+                    if (outRec2.isHole ^ self.ReverseSolution) == self._Area(outRec2) > 0.0:
+                        _ReversePolyPtLinks(outRec2.pts)
+                        
+                elif _Poly2ContainsPoly1(outRec1.pts, outRec2.pts):
+                    outRec2.isHole = outRec1.isHole
+                    outRec1.isHole = not outRec2.isHole
+                    outRec2.FirstLeft = outRec1.FirstLeft
+                    outRec1.FirstLeft = outRec2
+                    
+                    self._FixupJoinRecs(jr, p2, i + 1)
+                    
+                    if self._UsingPolyTree: self._FixupFirstLefts2(outRec1, outRec2)
+                    
+                    _FixupOutPolygon(outRec1)
+                    _FixupOutPolygon(outRec2)
+                    
+                    if (outRec1.isHole ^ self.ReverseSolution) == self._Area(outRec1) > 0.0:
+                        _ReversePolyPtLinks(outRec1.pts)
+                else:                  
+                    outRec2.isHole = outRec1.isHole
+                    outRec2.FirstLeft = outRec1.FirstLeft
+                    
+                    self._FixupJoinRecs(jr, p2, i + 1)
+                    if self._UsingPolyTree: self._FixupFirstLefts1(outRec1, outRec2)
+                    
+                    _FixupOutPolygon(outRec1)
+                    _FixupOutPolygon(outRec2)
+            else:
+                _FixupOutPolygon(outRec1)
+                outRec2.pts = None
+                outRec2.bottomPt = None
+                outRec2.idx = outRec1.idx
+                
+                outRec1.isHole = holeStateRec.isHole
+                if holeStateRec == outRec2:
+                    outRec1.FirstLeft = outRec2.FirstLeft
+                outRec2.FirstLeft = outRec1
+                
+                if self._UsingPolyTree: self._FixupFirstLefts2(outRec2, outRec1)
+        return
+    
+    def _DoSimplePolygons(self):
+        i = 0;
+        while i < len(self._PolyOutList):
+            outrec = self._PolyOutList[i]
+            i +=1
+            op = outrec.pts
+            if (op is None): continue
+            while True:
+                op2 = op.nextOp
+                while (op2 != outrec.pts): 
+                    if (_PointsEqual(op.pt, op2.pt) and op2.nextOp != op and op2.prevOp != op): 
+                        #split the polygon into two ...
+                        op3 = op.prevOp
+                        op4 = op2.prevOp
+                        op.prevOp = op4
+                        op4.nextOp = op
+                        op2.prevOp = op3
+                        op3.nextOp = op2
+                        
+                        outrec.pts = op
+                        outrec2 = self._CreateOutRec();
+                        outrec2.pts = op2;
+                        _UpdateOutPtIdxs(outrec2)
+                        if (_Poly2ContainsPoly1(outrec2.pts, outrec.pts)):
+                            #OutRec2 is contained by OutRec1 ...
+                            outrec2.isHole = not outrec.isHole
+                            outrec2.FirstLeft = outrec
+                      
+                        elif (_Poly2ContainsPoly1(outrec.pts, outrec2.pts)):
+                            #OutRec1 is contained by OutRec2 ...
+                            outrec2.isHole = outrec.isHole
+                            outrec.isHole = not outrec2.isHole
+                            outrec2.FirstLeft = outrec.FirstLeft
+                            outrec.FirstLeft = outrec2
+                        else:
+                            #the 2 polygons are separate ...
+                            outrec2.isHole = outrec.isHole;
+                            outrec2.FirstLeft = outrec.FirstLeft;
+                        op2 = op; # ie get ready for the next iteration
+                    op2 = op2.nextOp
+                op = op.nextOp
+                if op == outrec.pts: break
+        return
+                
+    def _ExecuteInternal(self):
+#         try: 
+            try:
+                self._Reset()
+                if self._Scanbeam is None: return True
+                botY = self._PopScanbeam()
+                while True:
+                    self._InsertLocalMinimaIntoAEL(botY)
+                    self._HorzJoins = None
+                    self._ProcessHorizontals()
+                    topY = self._PopScanbeam()
+                    if not self._ProcessIntersections(botY, topY): return False
+                    self._ProcessEdgesAtTopOfScanbeam(topY)
+                    botY = topY
+                    if self._Scanbeam is None and self._CurrentLocMin is None: break
+                    
+                for outRec in self._PolyOutList:
+                    if outRec.pts is None: continue                
+                    _FixupOutPolygon(outRec)
+                    if outRec.pts is None: continue
+                    if ((outRec.isHole ^ self.ReverseSolution) == (self._Area(outRec.pts) > 0.0)):
+                        _ReversePolyPtLinks(outRec.pts)
+                
+                if self._JoinList is not None: self._JoinCommonEdges()
+                if self.ForceSimple: self._DoSimplePolygons()
+                
+                return True
+            finally:
+                self._JoinList = None
+                self._HorzJoins = None
+#         except:
+#             return False
+
+    def Execute(
+            self,
+            clipType,
+            solution,
+            subjFillType = PolyFillType.EvenOdd,
+            clipFillType = PolyFillType.EvenOdd):
+        if self._ExecuteLocked: return False
+        try:
+            self._ExecuteLocked = True
+            self._UsingPolyTree = True
+            del solution[:]
+            self._SubjFillType = subjFillType
+            self._ClipFillType = clipFillType
+            self._ClipType = clipType
+            result = self._ExecuteInternal()
+            if result: self._BuildResult(solution)
+        finally:
+            self._ExecuteLocked = False
+            self._UsingPolyTree = False
+        return result
+
+    def Execute2(
+            self,
+            clipType,
+            solutionTree,
+            subjFillType = PolyFillType.EvenOdd,
+            clipFillType = PolyFillType.EvenOdd):
+        if self._ExecuteLocked: return False
+        try:
+            self._ExecuteLocked = True
+            self._UsingPolyTree = True
+            solutionTree.Clear()
+            self._SubjFillType = subjFillType
+            self._ClipFillType = clipFillType
+            self._ClipType = clipType
+            result = self._ExecuteInternal()
+            if result: self._BuildResult2(solutionTree)
+        finally:
+            self._ExecuteLocked = False
+            self._UsingPolyTree = False
+        return result
+
+    def _BuildResult(self, polygons):
+        for outRec in self._PolyOutList:
+            if outRec is None: continue
+            cnt = _PointCount(outRec.pts)
+            if (cnt < 3): continue
+            poly = []
+            op = outRec.pts
+            for _ in range(cnt):
+                poly.append(op.pt) 
+                op = op.prevOp
+            polygons.append(poly)
+        return
+    
+    def _BuildResult2(self, polyTree):
+        for outRec in self._PolyOutList:
+            if outRec is None: continue
+            cnt = _PointCount(outRec.pts)
+            if (cnt < 3): continue
+            _FixHoleLinkage(outRec)
+            
+            # add nodes to _AllNodes list ...
+            polyNode = PolyNode()
+            polyTree._AllNodes.append(polyNode)
+            outRec.PolyNode = polyNode
+            op = outRec.pts
+            while True:
+                polyNode.Contour.append(op.pt)
+                op = op.prevOp
+                if op == outRec.pts: break
+        # build the tree ...
+        for outRec in self._PolyOutList:
+            if outRec.PolyNode is None: continue
+            if outRec.FirstLeft is None:
+                polyTree._AddChild(outRec.PolyNode)
+            else:
+                outRec.FirstLeft.PolyNode._AddChild(outRec.PolyNode)                 
+        return
+       
+#===============================================================================
+# OffsetPolygons (+ ancilliary functions)
+#===============================================================================
+
+def _GetUnitNormal(pt1, pt2):
+    if pt2.x == pt1.x and pt2.y == pt1.y:
+        return FloatPoint(0.0, 0.0)
+    dx = float(pt2.x - pt1.x)
+    dy = float(pt2.y - pt1.y)
+    f = 1.0 / math.hypot(dx, dy)
+    dx = float(dx) * f
+    dy = float(dy) * f
+    return FloatPoint(dy, -dx)
+
+def _GetBounds(pts):
+    left = None
+    for poly in pts:
+        for pt in poly:
+            left = pt.x
+            top = pt.y
+            right = pt.x
+            bottom = pt.y
+            break
+        break
+    
+    for poly in pts:
+        for pt in poly:
+            if pt.x < left: left = pt.x
+            if pt.x > right: right = pt.x
+            if pt.y < top: top = pt.y
+            if pt.y > bottom: bottom = pt.y
+    if left is None: return Rect(0, 0, 0, 0)
+    else: return Rect(left, top, right, bottom)
+
+def _GetLowestPt(poly):
+    # precondition: poly must not be empty
+    result = poly[0]
+    for pt in poly:
+        if pt.y > result.y or (pt.y == result.y and pt.x < result.x):
+            result = pt
+    return result
+
+def _StripDupPts(poly):
+    if poly == []: return poly
+    for i in range(1, len(poly)):
+        if _PointsEqual(poly[i-1], poly[i]): poly.pop(i)
+    i = len(poly) -1
+    while i > 0 and _PointsEqual(poly[i], poly[0]):
+        poly.pop(i)
+        i -= 1
+    return poly
+
+def _OffsetInternal(polys, isPolygon, delta, jointype = JoinType.Square, endtype = EndType.Square, limit = 0.0): 
+    
+    def _DoSquare(pt):
+        # see offset_triginometry.svg in the documentation folder ...
+        dx = math.tan(math.atan2(sinA, 
+            Normals[k].x * Normals[j].x + Normals[k].y * Normals[j].y)/4)
+        result.append(Point(
+            round(pt.x + delta * (Normals[k].x - Normals[k].y *dx)),
+            round(pt.y + delta * (Normals[k].y + Normals[k].x *dx))))
+        result.append(Point(
+            round(pt.x + delta * (Normals[j].x + Normals[j].y *dx)),
+            round(pt.y + delta * (Normals[j].y - Normals[j].x *dx))))
+        return
+
+    def _DoMiter(pt, r):
+        q = delta / r
+        result.append(Point(
+            round(pt.x + (Normals[k].x + Normals[j].x) * q),
+            round(pt.y + (Normals[k].y + Normals[j].y) * q)))
+        return
+
+
+    def _DoRound(pt):
+        a = math.atan2(sinA, 
+                Normals[k].x * Normals[j].x + Normals[k].y * Normals[j].y)
+        steps = round(step360 * abs(a));
+        X,Y = Normals[k].x, Normals[k].y
+        for _ in range(steps):
+            result.append(Point(
+                round(pt.x + X * delta), round(pt.y + Y * delta)))
+            X2 = X
+            X = X * mcos - msin * Y
+            Y = X2 * msin + Y * mcos
+        result.append(Point(round(pt.x + Normals[j].x * delta), 
+            round(pt.y + Normals[j].y * delta)));
+        return
+
+    def GetSin():
+        result = (Normals[k].x * Normals[j].y - Normals[j].x * Normals[k].y)
+        if (result > 1.0): result = 1.0 
+        elif (result < -1.0): result = -1.0
+        return result
+            
+    def _OffsetPoint(jointype):
+        if (sinA * delta < 0):
+            result.append(Point(round(pts[j].x + Normals[k].x * delta),
+              round(pts[j].y + Normals[k].y * delta)))
+            result.append(pts[j])
+            result.append(Point(round(pts[j].x + Normals[j].x * delta),
+              round(pts[j].y + Normals[j].y * delta)))
+        elif jointype == JoinType.Miter:
+            r = 1.0 + (Normals[j].x * Normals[k].x + Normals[j].y * Normals[k].y)
+            if (r >= miterLim): _DoMiter(pts[j], r) 
+            else: _DoSquare(pts[j])
+        elif jointype == JoinType.Square: _DoSquare(pts[j])
+        else: _DoRound(pts[j])
+        return j
+
+    if delta == 0: return polys
+    if not isPolygon and delta < 0: delta = -delta
+
+    if jointype == JoinType.Miter:  
+        # miterLim: see offset_triginometry3.svg in the documentation folder ...
+        if limit > 2: miterLim = 2 / (limit * limit)
+        else: miterLim = 0.5
+        if endtype == EndType.Round: limit = 0.25
+        
+    if jointype == JoinType.Round or endtype == EndType.Round:
+        if limit <= 0: limit = 0.25
+        elif limit > abs(delta)*0.25: limit = abs(delta)*0.25
+        # step360: see offset_triginometry2.svg in the documentation folder ...
+        step360 = math.pi / math.acos(1 - limit / abs(delta))
+        msin = math.sin(2 * math.pi / step360)
+        mcos = math.cos(2 * math.pi / step360)
+        step360 /= math.pi * 2
+        if delta < 0: msin = -msin
+            
+    res = []
+    ppts = polys[:]    
+    for pts in ppts:    
+        Normals = []
+        result = []
+        cnt = len(pts)
+        
+        if (cnt == 0 or cnt < 3 and delta <= 0): continue
+        
+        if (cnt == 1):
+            if jointype == JoinType.Round:
+                X,Y = 1.0, 0.0
+                for _ in range(round(step360 * 2 * math.pi)):
+                    result.append(Point(round(pts[0].x + X * delta),
+                      round(pts[0].y + Y * delta)))
+                    X2 = X
+                    X = X * mcos - msin * Y
+                    Y = X2 * msin + Y * mcos
+            else:
+                X,Y = -1.0, -1.0
+                for _ in range(4):
+                    result.append(Point(round(pts[0].x + X * delta),
+                      round(pts[0].y + Y * delta)))
+                    if X < 0: X = 1
+                    elif Y < 0: Y = 1
+                    else: X = -1
+            continue
+        
+        forceClose = _PointsEqual(pts[0], pts[cnt - 1])
+        if (forceClose): cnt -=1
+        
+        for j in range(cnt -1):
+            Normals.append(_GetUnitNormal(pts[j], pts[j+1]))
+        if isPolygon or forceClose: 
+            Normals.append(_GetUnitNormal(pts[cnt-1], pts[0]))
+        else:
+            Normals.append(Normals[cnt-2])
+    
+    
+        if (isPolygon or forceClose):
+            k = cnt - 1
+            for j in range(cnt):
+                sinA = GetSin()
+                k = _OffsetPoint(jointype)
+            res.append(result)
+                    
+            if not isPolygon:
+                result = []
+                delta = -delta
+                k = cnt - 1
+                for j in range(cnt):
+                    sinA = GetSin()
+                    k = _OffsetPoint(jointype)
+                delta = -delta
+                res.append(result[::-1])        
+    
+        else: 
+            # offset the polyline going forward ...
+            k = 0;
+            for j in range(1, cnt-1):
+                sinA = GetSin()
+                k = _OffsetPoint(jointype)
+            
+            # handle the end (butt, round or square) ...
+            if (endtype == EndType.Butt):
+                j = cnt - 1
+                pt1 = Point(round(float(pts[j].x) + Normals[j].x * delta), \
+                    round(float(pts[j].y) + Normals[j].y * delta))
+                result.append(pt1)
+                pt1 = Point(round(float(pts[j].x) - Normals[j].x * delta), \
+                    round(float(pts[j].y) - Normals[j].y * delta))
+                result.append(pt1)
+            else:
+                j = cnt - 1;
+                k = cnt - 2;
+                Normals[j] = FloatPoint(-Normals[j].x, -Normals[j].y)
+                if (endtype == EndType.Square): _DoSquare(pts[j])
+                else: _DoRound(pts[j])
+            
+            # re-build Normals ...
+            for j in range(cnt -1, 0, -1):
+                Normals[j] = FloatPoint(-Normals[j -1].x, -Normals[j -1].y)
+            Normals[0] = FloatPoint(-Normals[1].x, -Normals[1].y)
+            
+            # offset the polyline going backward ...
+            k = cnt -1;
+            for j in range(cnt -2, 0, -1):
+                sinA = GetSin()
+                k = _OffsetPoint(jointype)
+            
+            # finally handle the start (butt, round or square) ...
+            if (endtype == EndType.Butt): 
+                pt1 = Point(round(float(pts[0].x) - Normals[0].x * delta), \
+                    round(float(pts[0].y) - Normals[0].y * delta))
+                result.append(pt1)
+                pt1 = Point(round(float(pts[0].x) + Normals[0].x * delta), \
+                    round(float(pts[0].y) + Normals[0].y * delta))
+                result.append(pt1)
+            else:
+                j = 0
+                k = 1
+                if (endtype == EndType.Square): _DoSquare(pts[0]) 
+                else: _DoRound(pts[0])
+            res.append(result)        
+            
+
+    c = Clipper()
+    c.AddPolygons(res, PolyType.Subject)
+    if delta > 0:
+        c.Execute(ClipType.Union, res, PolyFillType.Positive, PolyFillType.Positive)
+    else:
+        bounds = _GetBounds(res)
+        outer = []
+        outer.append(Point(bounds.left-10, bounds.bottom+10))
+        outer.append(Point(bounds.right+10, bounds.bottom+10))
+        outer.append(Point(bounds.right+10, bounds.top-10))
+        outer.append(Point(bounds.left-10, bounds.top-10))
+        c.AddPolygon(outer, PolyType.Subject)
+        c.ReverseSolution = True
+        c.Execute(ClipType.Union, res, PolyFillType.Negative, PolyFillType.Negative)
+        if len(res) > 0: res.pop(0)
+    return res
+
+def OffsetPolygons(polys, delta, jointype = JoinType.Square, limit = 0.0, autoFix = True):
+    if not autoFix: 
+        return _OffsetInternal(polys, True, delta, jointype, EndType.Butt, limit)        
+    pts = polys[:]
+    botPoly = None
+    botPt = None
+    for poly in pts:
+        poly = _StripDupPts(poly)
+        if len(poly) < 3: continue
+        bot = _GetLowestPt(poly)
+        if botPt is None or (bot.y > botPt.y) or \
+            (bot.y == botPt.y and bot.x < botPt.x):
+                botPt = bot
+                botPoly = poly
+    if botPt is None: return []
+    # if the outermost polygon has the wrong orientation,
+    # reverse the orientation of all the polygons ...
+    if Area(botPoly) < 0.0:
+        for i in range(len(pts)):
+            pts[i] = pts[i][::-1]                
+    return _OffsetInternal(pts, True, delta, jointype, EndType.Butt, limit)
+
+def OffsetPolyLines(polys, delta, jointype = JoinType.Square, endtype = EndType.Square, limit = 0.0):
+    polys2 = polys[:]
+    for p in polys2:
+        if p == []: continue            
+        for i in range(1, len(p)):
+            if _PointsEqual(p[i-1], p[i]): p.pop(i)
+
+    if endtype == EndType.Closed:
+        for i in range(len(polys2)):
+            polys2.append(polys2[i][::-1])
+        return _OffsetInternal(polys2, True, delta, jointype, EndType.Butt, limit) 
+    else:    
+        return _OffsetInternal(polys2, False, delta, jointype, endtype, limit) 
+
+def _DistanceSqrd(pt1, pt2):
+    dx = (pt1.x - pt2.x)
+    dy = (pt1.y - pt2.y)
+    return (dx*dx + dy*dy)
+
+def _ClosestPointOnLine(pt, linePt1, linePt2):
+    dx = linePt2.x - linePt1.x
+    dy = linePt2.y - linePt1.y
+    if (dx == 0 and dy == 0): 
+        return FloatPoint(linePt1.x, linePt1.y)
+    q = ((pt.x-linePt1.x)*dx + (pt.Y-linePt1.Y)*dy) / (dx*dx + dy*dy)
+    return FloatPoint(
+      (1-q)*linePt1.X + q*linePt2.X,
+      (1-q)*linePt1.Y + q*linePt2.Y)
+
+def _SlopesNearColinear(pt1, pt2, pt3, distSqrd):
+    if _DistanceSqrd(pt1, pt2) > _DistanceSqrd(pt1, pt3): return False
+    cpol = _ClosestPointOnLine(pt2, pt1, pt3);
+    dx = pt2.x - cpol.x
+    dy = pt2.y - cpol.y
+    return (dx*dx + dy*dy) < distSqrd
+
+def _PointsAreClose(pt1, pt2, distSqrd):
+    dx = pt1.x - pt2.x
+    dy = pt1.y - pt2.y
+    return (dx * dx) + (dy * dy) <= distSqrd
+
+def CleanPolygon(poly, distance = 1.415):
+    distSqrd = distance * distance
+    highI = len(poly) -1
+    while (highI > 0 and _PointsEqual(poly[highI], poly[0])): highI -= 1
+    if (highI < 2): return []
+    pt = poly[highI]
+    result = []
+    i = 0
+    while True:
+        while (i < highI and _PointsAreClose(pt, poly[i+1], distSqrd)): i +=2
+        i2 = i
+        while (i < highI and (_PointsAreClose(poly[i], poly[i+1], distSqrd) or \
+                _SlopesNearColinear(pt, poly[i], poly[i+1], distSqrd))): i +=1
+        if i >= highI: break
+        elif i != i2: continue
+        pt = poly[i]
+        i +=1
+        result.append(pt) 
+               
+    if (i <= highI): result.append(poly[i])
+    j = len(result)
+    if (j > 2 and _SlopesNearColinear(result[j-2], result[j-1], result[0], distSqrd)): 
+        del result[j-1:]
+    if len(result) < 3: return []
+    else: return result
+    
+def CleanPolygons(polys, distance = 1.415):
+    result = []
+    for poly in polys:
+        result.append(CleanPolygon(poly, distance = 1.415))
+    return result
+
+def SimplifyPolygon(poly, fillType):
+    result = []
+    c = Clipper();
+    c.ForceSimple = True    
+    c.AddPolygon(poly, PolyType.Subject);
+    c.Execute(ClipType.Union, result, fillType, fillType)
+    return result
+
+def SimplifyPolygons(polys, fillType):
+    result = []
+    c = Clipper();
+    c.ForceSimple = True    
+    c.AddPolygons(polys, PolyType.Subject);
+    c.Execute(ClipType.Union, result, fillType, fillType)
+    return result
diff --git a/Third Party/python/clipper_demo.py b/Third Party/python/clipper_demo.py
new file mode 100644
index 0000000..14696aa
--- /dev/null
+++ b/Third Party/python/clipper_demo.py	
@@ -0,0 +1,267 @@
+# from clipper import Area, Clipper, Point, ClipType, PolyType, PolyFillType
+from clipper import * 
+import math
+import re
+from random import randint
+# from subprocess import call
+import os
+ 
+#===============================================================================
+#===============================================================================
+
+def LoadFile1(lines):
+    # File type 1: first line is total polygons count and subsequent lines 
+    # contain the polygon vertex count followed by its coords 
+    try:
+        polygons = []
+        poly = []
+        for l in lines:
+            vals = re.split(' |, |,', l.strip())
+            if len(vals) < 2:  
+                if (len(poly)  > 2):
+                    polygons.append(poly)
+                poly = []
+            else: 
+                poly.append(Point(int(vals[0]), int(vals[1])))
+        if (len(poly)  > 2):
+            polygons.append(poly)
+        return polygons
+    except:
+        return None
+#===============================================================================
+
+def LoadFile2(lines):
+    # File type 2: vertex coords on consecutive lines for each polygon 
+    # where each polygon is separated by an empty line 
+    try:
+        polygons = []
+        poly = []
+        for l in lines:
+            l = l.strip()
+            if (l == ''): 
+                if (len(poly)  > 2):
+                    polygons.append(poly)
+                poly = []
+            else: 
+                vals = re.split(' |, |,', l)
+                poly.append(Point(int(vals[0]), int(vals[1])))
+        if (len(poly)  > 2):
+            polygons.append(poly)
+        return polygons
+    except:
+        return None
+#===============================================================================
+
+def LoadFile(filename):
+    try:
+        f = open(filename, 'r')
+        try:
+            lines = f.readlines()
+        finally:
+            f.close()
+        # pick file type from format of first line ...
+        if len(lines) == 0: return []
+        elif not ',' in lines[0]: return LoadFile1(lines)
+        else: return LoadFile2(lines)
+    except:
+        return None
+#===============================================================================
+    
+def SaveToFile(filename, polys, scale = 1.0):
+    invScale = 1.0 / scale
+    try:
+        f = open(filename, 'w')
+        try:
+            if invScale == 1:
+                for poly in polys:
+                    for pt in poly:
+                        f.write("{0}, {1}\n".format(pt.x, pt.y))
+                    f.write("\n")
+            else:
+                for poly in polys:
+                    for pt in poly:
+                        f.write("{0:.4f}, {1:.4f}\n".format(pt.x * invScale, pt.y * invScale))
+                    f.write("\n")
+        finally:
+            f.close()
+    except:
+        return
+#===============================================================================
+    
+def RandomPoly(maxWidth, maxHeight, vertCnt):
+    result = []
+    for _ in range(vertCnt):
+        result.append(Point(randint(0, maxWidth), randint(0, maxHeight)))
+    return result
+
+#===============================================================================
+# SVGBuilder
+#===============================================================================
+class SVGBuilder(object):
+    
+    def HtmlColor(self, val):
+        return "#{0:06x}".format(val & 0xFFFFFF)
+    
+    def AlphaClr(self, val):
+        return "{0:.2f}".format(float(val >> 24)/255)
+
+    class StyleInfo(object):
+        def __init__(self):      
+            self.fillType = PolyFillType.EvenOdd
+            self.brushClr = 0
+            self.penClr = 0
+            self.penWidth = 0.8
+            self.showCoords = False
+    
+    class StyleInfoPlus(StyleInfo):
+        
+        def __init__(self):   
+            SVGBuilder.StyleInfo.__init__(self)   
+            self.polygons = []
+            self.textlines = []
+        
+    def __init__(self):        
+        self.GlobalStyle = SVGBuilder.StyleInfo()
+        self.PolyInfoList = []
+        self.PathHeader = " <path d=\""
+        self.PathFooter = "\"\n style=\"fill:{0}; fill-opacity:{1}; fill-rule:{2}; stroke:{3}; stroke-opacity:{4}; stroke-width:{5:.2f};\" filter=\"url(#Gamma)\"/>\n\n"
+        self.Header = """<?xml version=\"1.0\" standalone=\"no\"?> 
+<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" 
+\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\"> 
+\n<svg width=\"{0}px\" height=\"{1}px\" viewBox=\"0 0 {0} {1}\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">
+  <defs>
+    <filter id="Gamma">
+      <feComponentTransfer>
+        <feFuncR type="gamma" amplitude="1" exponent="0.3" offset="0" />
+        <feFuncG type="gamma" amplitude="1" exponent="0.3" offset="0" />
+        <feFuncB type="gamma" amplitude="1" exponent="0.3" offset="0" />
+      </feComponentTransfer>
+    </filter>
+  </defs>\n\n"""
+    
+    def AddPolygon(self, poly, brushColor, penColor):
+        if poly is None or len(poly) == 0: return
+        pi = self.StyleInfoPlus()
+        pi.penWidth = self.GlobalStyle.penWidth
+        pi.fillType = self.GlobalStyle.fillType
+        pi.showCoords = self.GlobalStyle.showCoords
+        pi.brushClr = brushColor
+        pi.penClr = penColor        
+        pi.polygons.append(poly)
+        self.PolyInfoList.append(pi)
+    
+    def AddPolygons(self, polys, brushColor, penColor):
+        if polys is None or len(polys) == 0: return
+        pi = self.StyleInfoPlus()
+        pi.penWidth = self.GlobalStyle.penWidth
+        pi.fillType = self.GlobalStyle.fillType
+        pi.showCoords = self.GlobalStyle.showCoords
+        pi.brushClr = brushColor
+        pi.penClr = penColor        
+        pi.polygons = polys
+        self.PolyInfoList.append(pi)
+    
+    def SaveToFile(self, filename, invScale = 1.0, margin = 10):
+        if len(self.PolyInfoList) == 0: return False
+        if invScale == 0: invScale = 1.0
+        if margin < 0: margin = 0
+        pi = self.PolyInfoList[0]
+        # get bounding rect ...
+        left = right = pi.polygons[0][0].x
+        top = bottom = pi.polygons[0][0].y
+        for pi in self.PolyInfoList:
+            for p in pi.polygons:
+                for ip in p:
+                    if ip.x < left: left = ip.x
+                    if ip.x > right: right = ip.x
+                    if ip.y < top: top = ip.y
+                    if ip.y > bottom: bottom = ip.y
+        left *= invScale
+        top *= invScale
+        right *= invScale
+        bottom *= invScale
+        offsetX = -left + margin      
+        offsetY = -top + margin      
+                    
+        f = open(filename, 'w')
+        m2 = margin * 2
+        f.write(self.Header.format(right - left + m2, bottom - top + m2))
+        for pi in self.PolyInfoList:
+            f.write(self.PathHeader)
+            for p in pi.polygons:
+                cnt = len(p)
+                if cnt < 3: continue
+                f.write(" M {0:.2f} {1:.2f}".format(p[0].x * invScale + offsetX, p[0].y * invScale + offsetY))
+                for i in range(1,cnt):
+                    f.write(" L {0:.2f} {1:.2f}".format(p[i].x * invScale + offsetX, p[i].y * invScale + offsetY))
+                f.write(" z")
+            fillRule = "evenodd"
+            if pi.fillType != PolyFillType.EvenOdd: fillRule = "nonzero"
+            f.write(self.PathFooter.format(self.HtmlColor(pi.brushClr), 
+                self.AlphaClr(pi.brushClr), fillRule, 
+                self.HtmlColor(pi.penClr), self.AlphaClr(pi.penClr),  pi.penWidth))
+            
+            if (pi.showCoords):
+                f.write("<g font-family=\"Verdana\" font-size=\"11\" fill=\"black\">\n\n")
+                for p in pi.polygons:
+                    cnt = len(p)
+                    if cnt < 3: continue
+                    for pt in p:
+                        x = pt.x * invScale + offsetX
+                        y = pt.y * invScale + offsetY
+                        f.write("<text x=\"{0}\" y=\"{1}\">{2},{3}</text>\n".format(x, y, pt.x, pt.y))
+                    f.write("\n")
+                f.write("</g>\n")
+    
+        f.write("</svg>\n")
+        f.close()
+        return True
+
+#===============================================================================
+# Main entry ...
+#===============================================================================
+         
+scaleExp = 0
+scale = math.pow(10, scaleExp)
+invScale = 1.0 / scale
+
+subj, clip = [], [] 
+#load saved subject and clip polygons ...    
+#subj = LoadFile('./subj.txt')
+#clip = LoadFile('./clip.txt')
+
+# Generate random subject and clip polygons ...
+subj.append(RandomPoly(640 * scale, 480 * scale, 100))
+clip.append(RandomPoly(640 * scale, 480 * scale, 100))
+#SaveToFile('./subj2.txt', subj, scale)
+#SaveToFile('./clip2.txt', clip, scale)
+
+# Load the polygons into Clipper and execute the boolean clip op ...
+c = Clipper()
+solution = []
+pft = PolyFillType.EvenOdd
+
+c.AddPolygons(subj, PolyType.Subject)
+c.AddPolygons(clip, PolyType.Clip)
+result = c.Execute(ClipType.Intersection, solution, pft, pft)
+
+SaveToFile('./solution2.txt', solution, scale)
+
+# Create an SVG file to display what's happened ...
+svgBuilder = SVGBuilder() 
+#svgBuilder.GlobalStyle.showCoords = True
+svgBuilder.GlobalStyle.fillType = pft
+svgBuilder.AddPolygons(subj, 0x402020FF, 0x802020FF)
+#svgBuilder.GlobalStyle.showCoords = False
+svgBuilder.AddPolygons(clip, 0x40FFFF20, 0x80FF2020)
+svgBuilder.GlobalStyle.penWidth = 0.6
+svgBuilder.AddPolygons(solution, 0x60138013, 0xFF003300)
+
+holes = []
+for poly in solution: 
+    if Area(poly) < 0: holes.append(poly)
+svgBuilder.AddPolygons(holes, 0x0, 0xFFFF0000)
+svgBuilder.SaveToFile('./test.svg', invScale, 100)
+
+if result: os.startfile('test.svg') # call(('open', 'test.svg')) # print("finished") # 
+else: print("failed")
\ No newline at end of file
diff --git a/Third Party/python/python_readme.txt b/Third Party/python/python_readme.txt
new file mode 100644
index 0000000..2fe1d3d
--- /dev/null
+++ b/Third Party/python/python_readme.txt	
@@ -0,0 +1,10 @@
+
+The clipper.py file included in this distribution contains a very old version of Clipper.
+
+I found it too onerous maintaining 4 parallel translations of Clipper, and I only wrote clipper.py to teach myself Python. 
+Besides, the Python code is about 100 times slower than compiled versions of Clipper so there are better alternatives.
+
+Maxime Chalon <maxime.chalon at gmail.com> has written a Python extension module for Clipper:
+https://sites.google.com/site/maxelsbackyard/home/pyclipper
+This module provides a Python interface to the C++ compiled Clipper Library.
+
diff --git a/Third Party/ruby/ruby_readme.txt b/Third Party/ruby/ruby_readme.txt
new file mode 100644
index 0000000..c71c1d8
--- /dev/null
+++ b/Third Party/ruby/ruby_readme.txt	
@@ -0,0 +1,4 @@
+A Ruby module written by Mike Owens <http://mike.filespanker.com>
+that wraps the Clipper library can be downloaded from:
+
+http://github.com/mieko/rbclipper
\ No newline at end of file
diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt
new file mode 100644
index 0000000..652221f
--- /dev/null
+++ b/cpp/CMakeLists.txt
@@ -0,0 +1,21 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6.0)
+PROJECT(polyclipping)
+
+SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Release type")
+# The header name clipper.hpp is too generic, so install in a subdirectory
+SET(CMAKE_INSTALL_INCDIR "${CMAKE_INSTALL_PREFIX}/include/polyclipping")
+SET(CMAKE_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}")
+SET(CMAKE_INSTALL_PKGCONFIGDIR "${CMAKE_INSTALL_PREFIX}/share/pkgconfig")
+SET(PCFILE "${CMAKE_CURRENT_BINARY_DIR}/polyclipping.pc")
+
+SET(BUILD_SHARED_LIBS ON CACHE BOOL
+    "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)")
+ADD_LIBRARY(polyclipping clipper.cpp)
+
+CONFIGURE_FILE (polyclipping.pc.cmakein "${PCFILE}" @ONLY)
+
+INSTALL (FILES clipper.hpp DESTINATION "${CMAKE_INSTALL_INCDIR}")
+INSTALL (TARGETS polyclipping LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+INSTALL (FILES "${PCFILE}" DESTINATION "${CMAKE_INSTALL_PKGCONFIGDIR}")
+
+SET_TARGET_PROPERTIES(polyclipping PROPERTIES VERSION 21.0.0 SOVERSION 21 )
diff --git a/cpp/clipper.cpp b/cpp/clipper.cpp
new file mode 100644
index 0000000..d932da0
--- /dev/null
+++ b/cpp/clipper.cpp
@@ -0,0 +1,4577 @@
+/*******************************************************************************
+*                                                                              *
+* Author    :  Angus Johnson                                                   *
+* Version   :  6.2.9                                                           *
+* Date      :  16 February 2015                                                *
+* Website   :  http://www.angusj.com                                           *
+* Copyright :  Angus Johnson 2010-2015                                         *
+*                                                                              *
+* License:                                                                     *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt                                         *
+*                                                                              *
+* Attributions:                                                                *
+* The code in this library is an extension of Bala Vatti's clipping algorithm: *
+* "A generic solution to polygon clipping"                                     *
+* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63.             *
+* http://portal.acm.org/citation.cfm?id=129906                                 *
+*                                                                              *
+* Computer graphics and geometric modeling: implementation and algorithms      *
+* By Max K. Agoston                                                            *
+* Springer; 1 edition (January 4, 2005)                                        *
+* http://books.google.com/books?q=vatti+clipping+agoston                       *
+*                                                                              *
+* See also:                                                                    *
+* "Polygon Offsetting by Computing Winding Numbers"                            *
+* Paper no. DETC2005-85513 pp. 565-575                                         *
+* ASME 2005 International Design Engineering Technical Conferences             *
+* and Computers and Information in Engineering Conference (IDETC/CIE2005)      *
+* September 24-28, 2005 , Long Beach, California, USA                          *
+* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf              *
+*                                                                              *
+*******************************************************************************/
+
+/*******************************************************************************
+*                                                                              *
+* This is a translation of the Delphi Clipper library and the naming style     *
+* used has retained a Delphi flavour.                                          *
+*                                                                              *
+*******************************************************************************/
+
+#include "clipper.hpp"
+#include <cmath>
+#include <vector>
+#include <algorithm>
+#include <stdexcept>
+#include <cstring>
+#include <cstdlib>
+#include <ostream>
+#include <functional>
+#include <sstream>
+
+namespace ClipperLib {
+
+static double const pi = 3.141592653589793238;
+static double const two_pi = pi *2;
+static double const def_arc_tolerance = 0.25;
+
+enum Direction { dRightToLeft, dLeftToRight };
+
+static int const Unassigned = -1;  //edge not currently 'owning' a solution
+static int const Skip = -2;        //edge that would otherwise close a path
+
+#define HORIZONTAL (-1.0E+40)
+#define TOLERANCE (1.0e-20)
+#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE))
+
+struct TEdge {
+  IntPoint Bot;
+  IntPoint Curr;
+  IntPoint Top;
+  IntPoint Delta;
+  double Dx;
+  PolyType PolyTyp;
+  EdgeSide Side;
+  int WindDelta; //1 or -1 depending on winding direction
+  int WindCnt;
+  int WindCnt2; //winding count of the opposite polytype
+  int OutIdx;
+  TEdge *Next;
+  TEdge *Prev;
+  TEdge *NextInLML;
+  TEdge *NextInAEL;
+  TEdge *PrevInAEL;
+  TEdge *NextInSEL;
+  TEdge *PrevInSEL;
+};
+
+struct IntersectNode {
+  TEdge          *Edge1;
+  TEdge          *Edge2;
+  IntPoint        Pt;
+};
+
+struct LocalMinimum {
+  cInt          Y;
+  TEdge        *LeftBound;
+  TEdge        *RightBound;
+};
+
+struct OutPt;
+
+struct OutRec {
+  int       Idx;
+  bool      IsHole;
+  bool      IsOpen;
+  OutRec   *FirstLeft;  //see comments in clipper.pas
+  PolyNode *PolyNd;
+  OutPt    *Pts;
+  OutPt    *BottomPt;
+};
+
+struct OutPt {
+  int       Idx;
+  IntPoint  Pt;
+  OutPt    *Next;
+  OutPt    *Prev;
+};
+
+struct Join {
+  OutPt    *OutPt1;
+  OutPt    *OutPt2;
+  IntPoint  OffPt;
+};
+
+struct LocMinSorter
+{
+  inline bool operator()(const LocalMinimum& locMin1, const LocalMinimum& locMin2)
+  {
+    return locMin2.Y < locMin1.Y;
+  }
+};
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+inline cInt Round(double val)
+{
+  if ((val < 0)) return static_cast<cInt>(val - 0.5); 
+  else return static_cast<cInt>(val + 0.5);
+}
+//------------------------------------------------------------------------------
+
+inline cInt Abs(cInt val)
+{
+  return val < 0 ? -val : val;
+}
+
+//------------------------------------------------------------------------------
+// PolyTree methods ...
+//------------------------------------------------------------------------------
+
+void PolyTree::Clear()
+{
+    for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i)
+      delete AllNodes[i];
+    AllNodes.resize(0); 
+    Childs.resize(0);
+}
+//------------------------------------------------------------------------------
+
+PolyNode* PolyTree::GetFirst() const
+{
+  if (!Childs.empty())
+      return Childs[0];
+  else
+      return 0;
+}
+//------------------------------------------------------------------------------
+
+int PolyTree::Total() const
+{
+  int result = (int)AllNodes.size();
+  //with negative offsets, ignore the hidden outer polygon ...
+  if (result > 0 && Childs[0] != AllNodes[0]) result--;
+  return result;
+}
+
+//------------------------------------------------------------------------------
+// PolyNode methods ...
+//------------------------------------------------------------------------------
+
+PolyNode::PolyNode(): Childs(), Parent(0), Index(0), m_IsOpen(false)
+{
+}
+//------------------------------------------------------------------------------
+
+int PolyNode::ChildCount() const
+{
+  return (int)Childs.size();
+}
+//------------------------------------------------------------------------------
+
+void PolyNode::AddChild(PolyNode& child)
+{
+  unsigned cnt = (unsigned)Childs.size();
+  Childs.push_back(&child);
+  child.Parent = this;
+  child.Index = cnt;
+}
+//------------------------------------------------------------------------------
+
+PolyNode* PolyNode::GetNext() const
+{ 
+  if (!Childs.empty()) 
+      return Childs[0]; 
+  else
+      return GetNextSiblingUp();    
+}  
+//------------------------------------------------------------------------------
+
+PolyNode* PolyNode::GetNextSiblingUp() const
+{ 
+  if (!Parent) //protects against PolyTree.GetNextSiblingUp()
+      return 0;
+  else if (Index == Parent->Childs.size() - 1)
+      return Parent->GetNextSiblingUp();
+  else
+      return Parent->Childs[Index + 1];
+}  
+//------------------------------------------------------------------------------
+
+bool PolyNode::IsHole() const
+{ 
+  bool result = true;
+  PolyNode* node = Parent;
+  while (node)
+  {
+      result = !result;
+      node = node->Parent;
+  }
+  return result;
+}  
+//------------------------------------------------------------------------------
+
+bool PolyNode::IsOpen() const
+{ 
+  return m_IsOpen;
+}  
+//------------------------------------------------------------------------------
+
+#ifndef use_int32
+
+//------------------------------------------------------------------------------
+// Int128 class (enables safe math on signed 64bit integers)
+// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1
+//    Int128 val2((long64)9223372036854775807);
+//    Int128 val3 = val1 * val2;
+//    val3.AsString => "85070591730234615847396907784232501249" (8.5e+37)
+//------------------------------------------------------------------------------
+
+class Int128
+{
+  public:
+    ulong64 lo;
+    long64 hi;
+
+    Int128(long64 _lo = 0)
+    {
+      lo = (ulong64)_lo;   
+      if (_lo < 0)  hi = -1; else hi = 0; 
+    }
+
+
+    Int128(const Int128 &val): lo(val.lo), hi(val.hi){}
+
+    Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){}
+    
+    Int128& operator = (const long64 &val)
+    {
+      lo = (ulong64)val;
+      if (val < 0) hi = -1; else hi = 0;
+      return *this;
+    }
+
+    bool operator == (const Int128 &val) const
+      {return (hi == val.hi && lo == val.lo);}
+
+    bool operator != (const Int128 &val) const
+      { return !(*this == val);}
+
+    bool operator > (const Int128 &val) const
+    {
+      if (hi != val.hi)
+        return hi > val.hi;
+      else
+        return lo > val.lo;
+    }
+
+    bool operator < (const Int128 &val) const
+    {
+      if (hi != val.hi)
+        return hi < val.hi;
+      else
+        return lo < val.lo;
+    }
+
+    bool operator >= (const Int128 &val) const
+      { return !(*this < val);}
+
+    bool operator <= (const Int128 &val) const
+      { return !(*this > val);}
+
+    Int128& operator += (const Int128 &rhs)
+    {
+      hi += rhs.hi;
+      lo += rhs.lo;
+      if (lo < rhs.lo) hi++;
+      return *this;
+    }
+
+    Int128 operator + (const Int128 &rhs) const
+    {
+      Int128 result(*this);
+      result+= rhs;
+      return result;
+    }
+
+    Int128& operator -= (const Int128 &rhs)
+    {
+      *this += -rhs;
+      return *this;
+    }
+
+    Int128 operator - (const Int128 &rhs) const
+    {
+      Int128 result(*this);
+      result -= rhs;
+      return result;
+    }
+
+    Int128 operator-() const //unary negation
+    {
+      if (lo == 0)
+        return Int128(-hi, 0);
+      else
+        return Int128(~hi, ~lo + 1);
+    }
+
+    operator double() const
+    {
+      const double shift64 = 18446744073709551616.0; //2^64
+      if (hi < 0)
+      {
+        if (lo == 0) return (double)hi * shift64;
+        else return -(double)(~lo + ~hi * shift64);
+      }
+      else
+        return (double)(lo + hi * shift64);
+    }
+
+};
+//------------------------------------------------------------------------------
+
+Int128 Int128Mul (long64 lhs, long64 rhs)
+{
+  bool negate = (lhs < 0) != (rhs < 0);
+
+  if (lhs < 0) lhs = -lhs;
+  ulong64 int1Hi = ulong64(lhs) >> 32;
+  ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF);
+
+  if (rhs < 0) rhs = -rhs;
+  ulong64 int2Hi = ulong64(rhs) >> 32;
+  ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF);
+
+  //nb: see comments in clipper.pas
+  ulong64 a = int1Hi * int2Hi;
+  ulong64 b = int1Lo * int2Lo;
+  ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi;
+
+  Int128 tmp;
+  tmp.hi = long64(a + (c >> 32));
+  tmp.lo = long64(c << 32);
+  tmp.lo += long64(b);
+  if (tmp.lo < b) tmp.hi++;
+  if (negate) tmp = -tmp;
+  return tmp;
+};
+#endif
+
+//------------------------------------------------------------------------------
+// Miscellaneous global functions
+//------------------------------------------------------------------------------
+
+bool Orientation(const Path &poly)
+{
+    return Area(poly) >= 0;
+}
+//------------------------------------------------------------------------------
+
+double Area(const Path &poly)
+{
+  int size = (int)poly.size();
+  if (size < 3) return 0;
+
+  double a = 0;
+  for (int i = 0, j = size -1; i < size; ++i)
+  {
+    a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y);
+    j = i;
+  }
+  return -a * 0.5;
+}
+//------------------------------------------------------------------------------
+
+double Area(const OutRec &outRec)
+{
+  OutPt *op = outRec.Pts;
+  if (!op) return 0;
+  double a = 0;
+  do {
+    a +=  (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y);
+    op = op->Next;
+  } while (op != outRec.Pts);
+  return a * 0.5;
+}
+//------------------------------------------------------------------------------
+
+bool PointIsVertex(const IntPoint &Pt, OutPt *pp)
+{
+  OutPt *pp2 = pp;
+  do
+  {
+    if (pp2->Pt == Pt) return true;
+    pp2 = pp2->Next;
+  }
+  while (pp2 != pp);
+  return false;
+}
+//------------------------------------------------------------------------------
+
+//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
+//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
+int PointInPolygon(const IntPoint &pt, const Path &path)
+{
+  //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
+  int result = 0;
+  size_t cnt = path.size();
+  if (cnt < 3) return 0;
+  IntPoint ip = path[0];
+  for(size_t i = 1; i <= cnt; ++i)
+  {
+    IntPoint ipNext = (i == cnt ? path[0] : path[i]);
+    if (ipNext.Y == pt.Y)
+    {
+        if ((ipNext.X == pt.X) || (ip.Y == pt.Y && 
+          ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1;
+    }
+    if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y))
+    {
+      if (ip.X >= pt.X)
+      {
+        if (ipNext.X > pt.X) result = 1 - result;
+        else
+        {
+          double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - 
+            (double)(ipNext.X - pt.X) * (ip.Y - pt.Y);
+          if (!d) return -1;
+          if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result;
+        }
+      } else
+      {
+        if (ipNext.X > pt.X)
+        {
+          double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - 
+            (double)(ipNext.X - pt.X) * (ip.Y - pt.Y);
+          if (!d) return -1;
+          if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result;
+        }
+      }
+    }
+    ip = ipNext;
+  } 
+  return result;
+}
+//------------------------------------------------------------------------------
+
+int PointInPolygon (const IntPoint &pt, OutPt *op)
+{
+  //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
+  int result = 0;
+  OutPt* startOp = op;
+  for(;;)
+  {
+    if (op->Next->Pt.Y == pt.Y)
+    {
+        if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && 
+          ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1;
+    }
+    if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y))
+    {
+      if (op->Pt.X >= pt.X)
+      {
+        if (op->Next->Pt.X > pt.X) result = 1 - result;
+        else
+        {
+          double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - 
+            (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y);
+          if (!d) return -1;
+          if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result;
+        }
+      } else
+      {
+        if (op->Next->Pt.X > pt.X)
+        {
+          double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - 
+            (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y);
+          if (!d) return -1;
+          if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result;
+        }
+      }
+    } 
+    op = op->Next;
+    if (startOp == op) break;
+  } 
+  return result;
+}
+//------------------------------------------------------------------------------
+
+bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2)
+{
+  OutPt* op = OutPt1;
+  do
+  {
+    //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon
+    int res = PointInPolygon(op->Pt, OutPt2);
+    if (res >= 0) return res > 0;
+    op = op->Next; 
+  }
+  while (op != OutPt1);
+  return true; 
+}
+//----------------------------------------------------------------------
+
+bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range)
+{
+#ifndef use_int32
+  if (UseFullInt64Range)
+    return Int128Mul(e1.Delta.Y, e2.Delta.X) == Int128Mul(e1.Delta.X, e2.Delta.Y);
+  else 
+#endif
+    return e1.Delta.Y * e2.Delta.X == e1.Delta.X * e2.Delta.Y;
+}
+//------------------------------------------------------------------------------
+
+bool SlopesEqual(const IntPoint pt1, const IntPoint pt2,
+  const IntPoint pt3, bool UseFullInt64Range)
+{
+#ifndef use_int32
+  if (UseFullInt64Range)
+    return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y);
+  else 
+#endif
+    return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y);
+}
+//------------------------------------------------------------------------------
+
+bool SlopesEqual(const IntPoint pt1, const IntPoint pt2,
+  const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range)
+{
+#ifndef use_int32
+  if (UseFullInt64Range)
+    return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y);
+  else 
+#endif
+    return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y);
+}
+//------------------------------------------------------------------------------
+
+inline bool IsHorizontal(TEdge &e)
+{
+  return e.Delta.Y == 0;
+}
+//------------------------------------------------------------------------------
+
+inline double GetDx(const IntPoint pt1, const IntPoint pt2)
+{
+  return (pt1.Y == pt2.Y) ?
+    HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y);
+}
+//---------------------------------------------------------------------------
+
+inline void SetDx(TEdge &e)
+{
+  e.Delta.X = (e.Top.X - e.Bot.X);
+  e.Delta.Y = (e.Top.Y - e.Bot.Y);
+
+  if (e.Delta.Y == 0) e.Dx = HORIZONTAL;
+  else e.Dx = (double)(e.Delta.X) / e.Delta.Y;
+}
+//---------------------------------------------------------------------------
+
+inline void SwapSides(TEdge &Edge1, TEdge &Edge2)
+{
+  EdgeSide Side =  Edge1.Side;
+  Edge1.Side = Edge2.Side;
+  Edge2.Side = Side;
+}
+//------------------------------------------------------------------------------
+
+inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2)
+{
+  int OutIdx =  Edge1.OutIdx;
+  Edge1.OutIdx = Edge2.OutIdx;
+  Edge2.OutIdx = OutIdx;
+}
+//------------------------------------------------------------------------------
+
+inline cInt TopX(TEdge &edge, const cInt currentY)
+{
+  return ( currentY == edge.Top.Y ) ?
+    edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y));
+}
+//------------------------------------------------------------------------------
+
+void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip)
+{
+#ifdef use_xyz  
+  ip.Z = 0;
+#endif
+
+  double b1, b2;
+  if (Edge1.Dx == Edge2.Dx)
+  {
+    ip.Y = Edge1.Curr.Y;
+    ip.X = TopX(Edge1, ip.Y);
+    return;
+  }
+  else if (Edge1.Delta.X == 0)
+  {
+    ip.X = Edge1.Bot.X;
+    if (IsHorizontal(Edge2))
+      ip.Y = Edge2.Bot.Y;
+    else
+    {
+      b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx);
+      ip.Y = Round(ip.X / Edge2.Dx + b2);
+    }
+  }
+  else if (Edge2.Delta.X == 0)
+  {
+    ip.X = Edge2.Bot.X;
+    if (IsHorizontal(Edge1))
+      ip.Y = Edge1.Bot.Y;
+    else
+    {
+      b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx);
+      ip.Y = Round(ip.X / Edge1.Dx + b1);
+    }
+  } 
+  else 
+  {
+    b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx;
+    b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx;
+    double q = (b2-b1) / (Edge1.Dx - Edge2.Dx);
+    ip.Y = Round(q);
+    if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx))
+      ip.X = Round(Edge1.Dx * q + b1);
+    else 
+      ip.X = Round(Edge2.Dx * q + b2);
+  }
+
+  if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) 
+  {
+    if (Edge1.Top.Y > Edge2.Top.Y)
+      ip.Y = Edge1.Top.Y;
+    else
+      ip.Y = Edge2.Top.Y;
+    if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx))
+      ip.X = TopX(Edge1, ip.Y);
+    else
+      ip.X = TopX(Edge2, ip.Y);
+  } 
+  //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ...
+  if (ip.Y > Edge1.Curr.Y)
+  {
+    ip.Y = Edge1.Curr.Y;
+    //use the more vertical edge to derive X ...
+    if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx))
+      ip.X = TopX(Edge2, ip.Y); else
+      ip.X = TopX(Edge1, ip.Y);
+  }
+}
+//------------------------------------------------------------------------------
+
+void ReversePolyPtLinks(OutPt *pp)
+{
+  if (!pp) return;
+  OutPt *pp1, *pp2;
+  pp1 = pp;
+  do {
+  pp2 = pp1->Next;
+  pp1->Next = pp1->Prev;
+  pp1->Prev = pp2;
+  pp1 = pp2;
+  } while( pp1 != pp );
+}
+//------------------------------------------------------------------------------
+
+void DisposeOutPts(OutPt*& pp)
+{
+  if (pp == 0) return;
+    pp->Prev->Next = 0;
+  while( pp )
+  {
+    OutPt *tmpPp = pp;
+    pp = pp->Next;
+    delete tmpPp;
+  }
+}
+//------------------------------------------------------------------------------
+
+inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt)
+{
+  std::memset(e, 0, sizeof(TEdge));
+  e->Next = eNext;
+  e->Prev = ePrev;
+  e->Curr = Pt;
+  e->OutIdx = Unassigned;
+}
+//------------------------------------------------------------------------------
+
+void InitEdge2(TEdge& e, PolyType Pt)
+{
+  if (e.Curr.Y >= e.Next->Curr.Y)
+  {
+    e.Bot = e.Curr;
+    e.Top = e.Next->Curr;
+  } else
+  {
+    e.Top = e.Curr;
+    e.Bot = e.Next->Curr;
+  }
+  SetDx(e);
+  e.PolyTyp = Pt;
+}
+//------------------------------------------------------------------------------
+
+TEdge* RemoveEdge(TEdge* e)
+{
+  //removes e from double_linked_list (but without removing from memory)
+  e->Prev->Next = e->Next;
+  e->Next->Prev = e->Prev;
+  TEdge* result = e->Next;
+  e->Prev = 0; //flag as removed (see ClipperBase.Clear)
+  return result;
+}
+//------------------------------------------------------------------------------
+
+inline void ReverseHorizontal(TEdge &e)
+{
+  //swap horizontal edges' Top and Bottom x's so they follow the natural
+  //progression of the bounds - ie so their xbots will align with the
+  //adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
+  std::swap(e.Top.X, e.Bot.X);
+#ifdef use_xyz  
+  std::swap(e.Top.Z, e.Bot.Z);
+#endif
+}
+//------------------------------------------------------------------------------
+
+void SwapPoints(IntPoint &pt1, IntPoint &pt2)
+{
+  IntPoint tmp = pt1;
+  pt1 = pt2;
+  pt2 = tmp;
+}
+//------------------------------------------------------------------------------
+
+bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a,
+  IntPoint pt2b, IntPoint &pt1, IntPoint &pt2)
+{
+  //precondition: segments are Collinear.
+  if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y))
+  {
+    if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b);
+    if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b);
+    if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a;
+    if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b;
+    return pt1.X < pt2.X;
+  } else
+  {
+    if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b);
+    if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b);
+    if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a;
+    if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b;
+    return pt1.Y > pt2.Y;
+  }
+}
+//------------------------------------------------------------------------------
+
+bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2)
+{
+  OutPt *p = btmPt1->Prev;
+  while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev;
+  double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt));
+  p = btmPt1->Next;
+  while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next;
+  double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt));
+
+  p = btmPt2->Prev;
+  while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev;
+  double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt));
+  p = btmPt2->Next;
+  while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next;
+  double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt));
+  return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n);
+}
+//------------------------------------------------------------------------------
+
+OutPt* GetBottomPt(OutPt *pp)
+{
+  OutPt* dups = 0;
+  OutPt* p = pp->Next;
+  while (p != pp)
+  {
+    if (p->Pt.Y > pp->Pt.Y)
+    {
+      pp = p;
+      dups = 0;
+    }
+    else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X)
+    {
+      if (p->Pt.X < pp->Pt.X)
+      {
+        dups = 0;
+        pp = p;
+      } else
+      {
+        if (p->Next != pp && p->Prev != pp) dups = p;
+      }
+    }
+    p = p->Next;
+  }
+  if (dups)
+  {
+    //there appears to be at least 2 vertices at BottomPt so ...
+    while (dups != p)
+    {
+      if (!FirstIsBottomPt(p, dups)) pp = dups;
+      dups = dups->Next;
+      while (dups->Pt != pp->Pt) dups = dups->Next;
+    }
+  }
+  return pp;
+}
+//------------------------------------------------------------------------------
+
+bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1,
+  const IntPoint pt2, const IntPoint pt3)
+{
+  if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2))
+    return false;
+  else if (pt1.X != pt3.X)
+    return (pt2.X > pt1.X) == (pt2.X < pt3.X);
+  else
+    return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y);
+}
+//------------------------------------------------------------------------------
+
+bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b)
+{
+  if (seg1a > seg1b) std::swap(seg1a, seg1b);
+  if (seg2a > seg2b) std::swap(seg2a, seg2b);
+  return (seg1a < seg2b) && (seg2a < seg1b);
+}
+
+//------------------------------------------------------------------------------
+// ClipperBase class methods ...
+//------------------------------------------------------------------------------
+
+ClipperBase::ClipperBase() //constructor
+{
+  m_CurrentLM = m_MinimaList.begin(); //begin() == end() here
+  m_UseFullRange = false;
+}
+//------------------------------------------------------------------------------
+
+ClipperBase::~ClipperBase() //destructor
+{
+  Clear();
+}
+//------------------------------------------------------------------------------
+
+void RangeTest(const IntPoint& Pt, bool& useFullRange)
+{
+  if (useFullRange)
+  {
+    if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange)
+    {
+      std::stringstream s;
+      s << "Coordinate outside allowed range: ";
+      s << std::fixed << Pt.X << " " << Pt.Y << " " << -Pt.X << " " << -Pt.Y;
+      throw clipperException(s.str().c_str());
+    }
+  }
+  else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) 
+  {
+    useFullRange = true;
+    RangeTest(Pt, useFullRange);
+  }
+}
+//------------------------------------------------------------------------------
+
+TEdge* FindNextLocMin(TEdge* E)
+{
+  for (;;)
+  {
+    while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next;
+    if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break;
+    while (IsHorizontal(*E->Prev)) E = E->Prev;
+    TEdge* E2 = E;
+    while (IsHorizontal(*E)) E = E->Next;
+    if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz.
+    if (E2->Prev->Bot.X < E->Bot.X) E = E2;
+    break;
+  }
+  return E;
+}
+//------------------------------------------------------------------------------
+
+TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward)
+{
+  TEdge *Result = E;
+  TEdge *Horz = 0;
+
+  if (E->OutIdx == Skip)
+  {
+    //if edges still remain in the current bound beyond the skip edge then
+    //create another LocMin and call ProcessBound once more
+    if (NextIsForward)
+    {
+      while (E->Top.Y == E->Next->Bot.Y) E = E->Next;
+      //don't include top horizontals when parsing a bound a second time,
+      //they will be contained in the opposite bound ...
+      while (E != Result && IsHorizontal(*E)) E = E->Prev;
+    }
+    else
+    {
+      while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev;
+      while (E != Result && IsHorizontal(*E)) E = E->Next;
+    }
+
+    if (E == Result)
+    {
+      if (NextIsForward) Result = E->Next;
+      else Result = E->Prev;
+    }
+    else
+    {
+      //there are more edges in the bound beyond result starting with E
+      if (NextIsForward)
+        E = Result->Next;
+      else
+        E = Result->Prev;
+      MinimaList::value_type locMin;
+      locMin.Y = E->Bot.Y;
+      locMin.LeftBound = 0;
+      locMin.RightBound = E;
+      E->WindDelta = 0;
+      Result = ProcessBound(E, NextIsForward);
+      m_MinimaList.push_back(locMin);
+    }
+    return Result;
+  }
+
+  TEdge *EStart;
+
+  if (IsHorizontal(*E))
+  {
+    //We need to be careful with open paths because this may not be a
+    //true local minima (ie E may be following a skip edge).
+    //Also, consecutive horz. edges may start heading left before going right.
+    if (NextIsForward) 
+      EStart = E->Prev;
+    else 
+      EStart = E->Next;
+    if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge
+      {
+        if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X)
+          ReverseHorizontal(*E);
+      }
+      else if (EStart->Bot.X != E->Bot.X)
+        ReverseHorizontal(*E);
+  }
+  
+  EStart = E;
+  if (NextIsForward)
+  {
+    while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip)
+      Result = Result->Next;
+    if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip)
+    {
+      //nb: at the top of a bound, horizontals are added to the bound
+      //only when the preceding edge attaches to the horizontal's left vertex
+      //unless a Skip edge is encountered when that becomes the top divide
+      Horz = Result;
+      while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev;
+      if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev;
+    }
+    while (E != Result) 
+    {
+      E->NextInLML = E->Next;
+      if (IsHorizontal(*E) && E != EStart &&
+        E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E);
+      E = E->Next;
+    }
+    if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) 
+      ReverseHorizontal(*E);
+    Result = Result->Next; //move to the edge just beyond current bound
+  } else
+  {
+    while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) 
+      Result = Result->Prev;
+    if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip)
+    {
+      Horz = Result;
+      while (IsHorizontal(*Horz->Next)) Horz = Horz->Next;
+      if (Horz->Next->Top.X == Result->Prev->Top.X ||
+          Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next;
+    }
+
+    while (E != Result)
+    {
+      E->NextInLML = E->Prev;
+      if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) 
+        ReverseHorizontal(*E);
+      E = E->Prev;
+    }
+    if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) 
+      ReverseHorizontal(*E);
+    Result = Result->Prev; //move to the edge just beyond current bound
+  }
+
+  return Result;
+}
+//------------------------------------------------------------------------------
+
+bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
+{
+#ifdef use_lines
+  if (!Closed && PolyTyp == ptClip)
+    throw clipperException("AddPath: Open paths must be subject.");
+#else
+  if (!Closed)
+    throw clipperException("AddPath: Open paths have been disabled.");
+#endif
+
+  int highI = (int)pg.size() -1;
+  if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI;
+  while (highI > 0 && (pg[highI] == pg[highI -1])) --highI;
+  if ((Closed && highI < 2) || (!Closed && highI < 1)) return false;
+
+  //create a new edge array ...
+  TEdge *edges = new TEdge [highI +1];
+
+  bool IsFlat = true;
+  //1. Basic (first) edge initialization ...
+  try
+  {
+    edges[1].Curr = pg[1];
+    RangeTest(pg[0], m_UseFullRange);
+    RangeTest(pg[highI], m_UseFullRange);
+    InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]);
+    InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]);
+    for (int i = highI - 1; i >= 1; --i)
+    {
+      RangeTest(pg[i], m_UseFullRange);
+      InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]);
+    }
+  }
+  catch(std::exception const&)
+  {
+    delete [] edges;
+    throw; //range test fails
+  }
+  TEdge *eStart = &edges[0];
+
+  //2. Remove duplicate vertices, and (when closed) collinear edges ...
+  TEdge *E = eStart, *eLoopStop = eStart;
+  for (;;)
+  {
+    //nb: allows matching start and end points when not Closed ...
+    if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart))
+    {
+      if (E == E->Next) break;
+      if (E == eStart) eStart = E->Next;
+      E = RemoveEdge(E);
+      eLoopStop = E;
+      continue;
+    }
+    if (E->Prev == E->Next) 
+      break; //only two vertices
+    else if (Closed &&
+      SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && 
+      (!m_PreserveCollinear ||
+      !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr)))
+    {
+      //Collinear edges are allowed for open paths but in closed paths
+      //the default is to merge adjacent collinear edges into a single edge.
+      //However, if the PreserveCollinear property is enabled, only overlapping
+      //collinear edges (ie spikes) will be removed from closed paths.
+      if (E == eStart) eStart = E->Next;
+      E = RemoveEdge(E);
+      E = E->Prev;
+      eLoopStop = E;
+      continue;
+    }
+    E = E->Next;
+    if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break;
+  }
+
+  if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next)))
+  {
+    delete [] edges;
+    return false;
+  }
+
+  if (!Closed)
+  { 
+    m_HasOpenPaths = true;
+    eStart->Prev->OutIdx = Skip;
+  }
+
+  //3. Do second stage of edge initialization ...
+  E = eStart;
+  do
+  {
+    InitEdge2(*E, PolyTyp);
+    E = E->Next;
+    if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false;
+  }
+  while (E != eStart);
+
+  //4. Finally, add edge bounds to LocalMinima list ...
+
+  //Totally flat paths must be handled differently when adding them
+  //to LocalMinima list to avoid endless loops etc ...
+  if (IsFlat) 
+  {
+    if (Closed) 
+    {
+      delete [] edges;
+      return false;
+    }
+    E->Prev->OutIdx = Skip;
+    MinimaList::value_type locMin;
+    locMin.Y = E->Bot.Y;
+    locMin.LeftBound = 0;
+    locMin.RightBound = E;
+    locMin.RightBound->Side = esRight;
+    locMin.RightBound->WindDelta = 0;
+    for (;;)
+    {
+      if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E);
+      if (E->Next->OutIdx == Skip) break;
+      E->NextInLML = E->Next;
+      E = E->Next;
+    }
+    m_MinimaList.push_back(locMin);
+    m_edges.push_back(edges);
+	  return true;
+  }
+
+  m_edges.push_back(edges);
+  bool leftBoundIsForward;
+  TEdge* EMin = 0;
+
+  //workaround to avoid an endless loop in the while loop below when
+  //open paths have matching start and end points ...
+  if (E->Prev->Bot == E->Prev->Top) E = E->Next;
+
+  for (;;)
+  {
+    E = FindNextLocMin(E);
+    if (E == EMin) break;
+    else if (!EMin) EMin = E;
+
+    //E and E.Prev now share a local minima (left aligned if horizontal).
+    //Compare their slopes to find which starts which bound ...
+    MinimaList::value_type locMin;
+    locMin.Y = E->Bot.Y;
+    if (E->Dx < E->Prev->Dx) 
+    {
+      locMin.LeftBound = E->Prev;
+      locMin.RightBound = E;
+      leftBoundIsForward = false; //Q.nextInLML = Q.prev
+    } else
+    {
+      locMin.LeftBound = E;
+      locMin.RightBound = E->Prev;
+      leftBoundIsForward = true; //Q.nextInLML = Q.next
+    }
+    locMin.LeftBound->Side = esLeft;
+    locMin.RightBound->Side = esRight;
+
+    if (!Closed) locMin.LeftBound->WindDelta = 0;
+    else if (locMin.LeftBound->Next == locMin.RightBound)
+      locMin.LeftBound->WindDelta = -1;
+    else locMin.LeftBound->WindDelta = 1;
+    locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta;
+
+    E = ProcessBound(locMin.LeftBound, leftBoundIsForward);
+    if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward);
+
+    TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward);
+    if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward);
+
+    if (locMin.LeftBound->OutIdx == Skip)
+      locMin.LeftBound = 0;
+    else if (locMin.RightBound->OutIdx == Skip)
+      locMin.RightBound = 0;
+    m_MinimaList.push_back(locMin);
+    if (!leftBoundIsForward) E = E2;
+  }
+  return true;
+}
+//------------------------------------------------------------------------------
+
+bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed)
+{
+  bool result = false;
+  for (Paths::size_type i = 0; i < ppg.size(); ++i)
+    if (AddPath(ppg[i], PolyTyp, Closed)) result = true;
+  return result;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::Clear()
+{
+  DisposeLocalMinimaList();
+  for (EdgeList::size_type i = 0; i < m_edges.size(); ++i)
+  {
+    //for each edge array in turn, find the first used edge and 
+    //check for and remove any hiddenPts in each edge in the array.
+    TEdge* edges = m_edges[i];
+    delete [] edges;
+  }
+  m_edges.clear();
+  m_UseFullRange = false;
+  m_HasOpenPaths = false;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::Reset()
+{
+  m_CurrentLM = m_MinimaList.begin();
+  if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process
+  std::stable_sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter());
+
+  //reset all edges ...
+  for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm)
+  {
+    TEdge* e = lm->LeftBound;
+    if (e)
+    {
+      e->Curr = e->Bot;
+      e->Side = esLeft;
+      e->OutIdx = Unassigned;
+    }
+
+    e = lm->RightBound;
+    if (e)
+    {
+      e->Curr = e->Bot;
+      e->Side = esRight;
+      e->OutIdx = Unassigned;
+    }
+  }
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::DisposeLocalMinimaList()
+{
+  m_MinimaList.clear();
+  m_CurrentLM = m_MinimaList.begin();
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::PopLocalMinima()
+{
+  if (m_CurrentLM == m_MinimaList.end()) return;
+  ++m_CurrentLM;
+}
+//------------------------------------------------------------------------------
+
+IntRect ClipperBase::GetBounds()
+{
+  IntRect result;
+  MinimaList::iterator lm = m_MinimaList.begin();
+  if (lm == m_MinimaList.end())
+  {
+    result.left = result.top = result.right = result.bottom = 0;
+    return result;
+  }
+  result.left = lm->LeftBound->Bot.X;
+  result.top = lm->LeftBound->Bot.Y;
+  result.right = lm->LeftBound->Bot.X;
+  result.bottom = lm->LeftBound->Bot.Y;
+  while (lm != m_MinimaList.end())
+  {
+    result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y);
+    TEdge* e = lm->LeftBound;
+    for (;;) {
+      TEdge* bottomE = e;
+      while (e->NextInLML)
+      {
+        if (e->Bot.X < result.left) result.left = e->Bot.X;
+        if (e->Bot.X > result.right) result.right = e->Bot.X;
+        e = e->NextInLML;
+      }
+      result.left = std::min(result.left, e->Bot.X);
+      result.right = std::max(result.right, e->Bot.X);
+      result.left = std::min(result.left, e->Top.X);
+      result.right = std::max(result.right, e->Top.X);
+      result.top = std::min(result.top, e->Top.Y);
+      if (bottomE == lm->LeftBound) e = lm->RightBound;
+      else break;
+    }
+    ++lm;
+  }
+  return result;
+}
+
+//------------------------------------------------------------------------------
+// TClipper methods ...
+//------------------------------------------------------------------------------
+
+Clipper::Clipper(int initOptions) : ClipperBase() //constructor
+{
+  m_ActiveEdges = 0;
+  m_SortedEdges = 0;
+  m_ExecuteLocked = false;
+  m_UseFullRange = false;
+  m_ReverseOutput = ((initOptions & ioReverseSolution) != 0);
+  m_StrictSimple = ((initOptions & ioStrictlySimple) != 0);
+  m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0);
+  m_HasOpenPaths = false;
+#ifdef use_xyz  
+  m_ZFill = 0;
+#endif
+}
+//------------------------------------------------------------------------------
+
+Clipper::~Clipper() //destructor
+{
+  Clear();
+}
+//------------------------------------------------------------------------------
+
+#ifdef use_xyz  
+void Clipper::ZFillFunction(ZFillCallback zFillFunc)
+{  
+  m_ZFill = zFillFunc;
+}
+//------------------------------------------------------------------------------
+#endif
+
+void Clipper::Reset()
+{
+  ClipperBase::Reset();
+  m_Scanbeam = ScanbeamList();
+  m_Maxima = MaximaList();
+  m_ActiveEdges = 0;
+  m_SortedEdges = 0;
+  for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm)
+    InsertScanbeam(lm->Y);
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType)
+{
+    return Execute(clipType, solution, fillType, fillType);
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType)
+{
+    return Execute(clipType, polytree, fillType, fillType);
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::Execute(ClipType clipType, Paths &solution,
+    PolyFillType subjFillType, PolyFillType clipFillType)
+{
+  if( m_ExecuteLocked ) return false;
+  if (m_HasOpenPaths)
+    throw clipperException("Error: PolyTree struct is needed for open path clipping.");
+  m_ExecuteLocked = true;
+  solution.resize(0);
+  m_SubjFillType = subjFillType;
+  m_ClipFillType = clipFillType;
+  m_ClipType = clipType;
+  m_UsingPolyTree = false;
+  bool succeeded = ExecuteInternal();
+  if (succeeded) BuildResult(solution);
+  DisposeAllOutRecs();
+  m_ExecuteLocked = false;
+  return succeeded;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::Execute(ClipType clipType, PolyTree& polytree,
+    PolyFillType subjFillType, PolyFillType clipFillType)
+{
+  if( m_ExecuteLocked ) return false;
+  m_ExecuteLocked = true;
+  m_SubjFillType = subjFillType;
+  m_ClipFillType = clipFillType;
+  m_ClipType = clipType;
+  m_UsingPolyTree = true;
+  bool succeeded = ExecuteInternal();
+  if (succeeded) BuildResult2(polytree);
+  DisposeAllOutRecs();
+  m_ExecuteLocked = false;
+  return succeeded;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::FixHoleLinkage(OutRec &outrec)
+{
+  //skip OutRecs that (a) contain outermost polygons or
+  //(b) already have the correct owner/child linkage ...
+  if (!outrec.FirstLeft ||                
+      (outrec.IsHole != outrec.FirstLeft->IsHole &&
+      outrec.FirstLeft->Pts)) return;
+
+  OutRec* orfl = outrec.FirstLeft;
+  while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts))
+      orfl = orfl->FirstLeft;
+  outrec.FirstLeft = orfl;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::ExecuteInternal()
+{
+  bool succeeded = true;
+  try {
+    Reset();
+    if (m_CurrentLM == m_MinimaList.end()) return true;
+    cInt botY = PopScanbeam();
+    do {
+      InsertLocalMinimaIntoAEL(botY);
+      ProcessHorizontals();
+	  ClearGhostJoins();
+	  if (m_Scanbeam.empty()) break;
+      cInt topY = PopScanbeam();
+      succeeded = ProcessIntersections(topY);
+      if (!succeeded) break;
+      ProcessEdgesAtTopOfScanbeam(topY);
+      botY = topY;
+    } while (!m_Scanbeam.empty() || m_CurrentLM != m_MinimaList.end());
+  }
+  catch(std::exception const&) 
+  {
+    succeeded = false;
+  }
+
+  if (succeeded)
+  {
+    //fix orientations ...
+    for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+    {
+      OutRec *outRec = m_PolyOuts[i];
+      if (!outRec->Pts || outRec->IsOpen) continue;
+      if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0))
+        ReversePolyPtLinks(outRec->Pts);
+    }
+
+    if (!m_Joins.empty()) JoinCommonEdges();
+
+    //unfortunately FixupOutPolygon() must be done after JoinCommonEdges()
+    for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+    {
+      OutRec *outRec = m_PolyOuts[i];
+      if (!outRec->Pts) continue;
+      if (outRec->IsOpen)
+        FixupOutPolyline(*outRec);
+      else
+        FixupOutPolygon(*outRec);
+    }
+
+    if (m_StrictSimple) DoSimplePolygons();
+  }
+
+  ClearJoins();
+  ClearGhostJoins();
+  return succeeded;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::InsertScanbeam(const cInt Y)
+{
+    m_Scanbeam.push(Y);
+}
+//------------------------------------------------------------------------------
+
+cInt Clipper::PopScanbeam()
+{
+    const cInt Y = m_Scanbeam.top();
+    m_Scanbeam.pop();
+    while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates.
+    return Y;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DisposeAllOutRecs(){
+  for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+    DisposeOutRec(i);
+  m_PolyOuts.clear();
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DisposeOutRec(PolyOutList::size_type index)
+{
+  OutRec *outRec = m_PolyOuts[index];
+  if (outRec->Pts) DisposeOutPts(outRec->Pts);
+  delete outRec;
+  m_PolyOuts[index] = 0;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::SetWindingCount(TEdge &edge)
+{
+  TEdge *e = edge.PrevInAEL;
+  //find the edge of the same polytype that immediately preceeds 'edge' in AEL
+  while (e  && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL;
+  if (!e)
+  {
+    edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta);
+    edge.WindCnt2 = 0;
+    e = m_ActiveEdges; //ie get ready to calc WindCnt2
+  }   
+  else if (edge.WindDelta == 0 && m_ClipType != ctUnion)
+  {
+    edge.WindCnt = 1;
+    edge.WindCnt2 = e->WindCnt2;
+    e = e->NextInAEL; //ie get ready to calc WindCnt2
+  }
+  else if (IsEvenOddFillType(edge))
+  {
+    //EvenOdd filling ...
+    if (edge.WindDelta == 0)
+    {
+      //are we inside a subj polygon ...
+      bool Inside = true;
+      TEdge *e2 = e->PrevInAEL;
+      while (e2)
+      {
+        if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) 
+          Inside = !Inside;
+        e2 = e2->PrevInAEL;
+      }
+      edge.WindCnt = (Inside ? 0 : 1);
+    }
+    else
+    {
+      edge.WindCnt = edge.WindDelta;
+    }
+    edge.WindCnt2 = e->WindCnt2;
+    e = e->NextInAEL; //ie get ready to calc WindCnt2
+  } 
+  else
+  {
+    //nonZero, Positive or Negative filling ...
+    if (e->WindCnt * e->WindDelta < 0)
+    {
+      //prev edge is 'decreasing' WindCount (WC) toward zero
+      //so we're outside the previous polygon ...
+      if (Abs(e->WindCnt) > 1)
+      {
+        //outside prev poly but still inside another.
+        //when reversing direction of prev poly use the same WC 
+        if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt;
+        //otherwise continue to 'decrease' WC ...
+        else edge.WindCnt = e->WindCnt + edge.WindDelta;
+      } 
+      else
+        //now outside all polys of same polytype so set own WC ...
+        edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta);
+    } else
+    {
+      //prev edge is 'increasing' WindCount (WC) away from zero
+      //so we're inside the previous polygon ...
+      if (edge.WindDelta == 0) 
+        edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1);
+      //if wind direction is reversing prev then use same WC
+      else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt;
+      //otherwise add to WC ...
+      else edge.WindCnt = e->WindCnt + edge.WindDelta;
+    }
+    edge.WindCnt2 = e->WindCnt2;
+    e = e->NextInAEL; //ie get ready to calc WindCnt2
+  }
+
+  //update WindCnt2 ...
+  if (IsEvenOddAltFillType(edge))
+  {
+    //EvenOdd filling ...
+    while (e != &edge)
+    {
+      if (e->WindDelta != 0)
+        edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0);
+      e = e->NextInAEL;
+    }
+  } else
+  {
+    //nonZero, Positive or Negative filling ...
+    while ( e != &edge )
+    {
+      edge.WindCnt2 += e->WindDelta;
+      e = e->NextInAEL;
+    }
+  }
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::IsEvenOddFillType(const TEdge& edge) const
+{
+  if (edge.PolyTyp == ptSubject)
+    return m_SubjFillType == pftEvenOdd; else
+    return m_ClipFillType == pftEvenOdd;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const
+{
+  if (edge.PolyTyp == ptSubject)
+    return m_ClipFillType == pftEvenOdd; else
+    return m_SubjFillType == pftEvenOdd;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::IsContributing(const TEdge& edge) const
+{
+  PolyFillType pft, pft2;
+  if (edge.PolyTyp == ptSubject)
+  {
+    pft = m_SubjFillType;
+    pft2 = m_ClipFillType;
+  } else
+  {
+    pft = m_ClipFillType;
+    pft2 = m_SubjFillType;
+  }
+
+  switch(pft)
+  {
+    case pftEvenOdd: 
+      //return false if a subj line has been flagged as inside a subj polygon
+      if (edge.WindDelta == 0 && edge.WindCnt != 1) return false;
+      break;
+    case pftNonZero:
+      if (Abs(edge.WindCnt) != 1) return false;
+      break;
+    case pftPositive: 
+      if (edge.WindCnt != 1) return false;
+      break;
+    default: //pftNegative
+      if (edge.WindCnt != -1) return false;
+  }
+
+  switch(m_ClipType)
+  {
+    case ctIntersection:
+      switch(pft2)
+      {
+        case pftEvenOdd: 
+        case pftNonZero: 
+          return (edge.WindCnt2 != 0);
+        case pftPositive: 
+          return (edge.WindCnt2 > 0);
+        default: 
+          return (edge.WindCnt2 < 0);
+      }
+      break;
+    case ctUnion:
+      switch(pft2)
+      {
+        case pftEvenOdd: 
+        case pftNonZero: 
+          return (edge.WindCnt2 == 0);
+        case pftPositive: 
+          return (edge.WindCnt2 <= 0);
+        default: 
+          return (edge.WindCnt2 >= 0);
+      }
+      break;
+    case ctDifference:
+      if (edge.PolyTyp == ptSubject)
+        switch(pft2)
+        {
+          case pftEvenOdd: 
+          case pftNonZero: 
+            return (edge.WindCnt2 == 0);
+          case pftPositive: 
+            return (edge.WindCnt2 <= 0);
+          default: 
+            return (edge.WindCnt2 >= 0);
+        }
+      else
+        switch(pft2)
+        {
+          case pftEvenOdd: 
+          case pftNonZero: 
+            return (edge.WindCnt2 != 0);
+          case pftPositive: 
+            return (edge.WindCnt2 > 0);
+          default: 
+            return (edge.WindCnt2 < 0);
+        }
+      break;
+    case ctXor:
+      if (edge.WindDelta == 0) //XOr always contributing unless open
+        switch(pft2)
+        {
+          case pftEvenOdd: 
+          case pftNonZero: 
+            return (edge.WindCnt2 == 0);
+          case pftPositive: 
+            return (edge.WindCnt2 <= 0);
+          default: 
+            return (edge.WindCnt2 >= 0);
+        }
+      else 
+        return true;
+      break;
+    default:
+      return true;
+  }
+}
+//------------------------------------------------------------------------------
+
+OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt)
+{
+  OutPt* result;
+  TEdge *e, *prevE;
+  if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx ))
+  {
+    result = AddOutPt(e1, Pt);
+    e2->OutIdx = e1->OutIdx;
+    e1->Side = esLeft;
+    e2->Side = esRight;
+    e = e1;
+    if (e->PrevInAEL == e2)
+      prevE = e2->PrevInAEL; 
+    else
+      prevE = e->PrevInAEL;
+  } else
+  {
+    result = AddOutPt(e2, Pt);
+    e1->OutIdx = e2->OutIdx;
+    e1->Side = esRight;
+    e2->Side = esLeft;
+    e = e2;
+    if (e->PrevInAEL == e1)
+        prevE = e1->PrevInAEL;
+    else
+        prevE = e->PrevInAEL;
+  }
+
+  if (prevE && prevE->OutIdx >= 0 &&
+      (TopX(*prevE, Pt.Y) == TopX(*e, Pt.Y)) &&
+      SlopesEqual(*e, *prevE, m_UseFullRange) &&
+      (e->WindDelta != 0) && (prevE->WindDelta != 0))
+  {
+    OutPt* outPt = AddOutPt(prevE, Pt);
+    AddJoin(result, outPt, e->Top);
+  }
+  return result;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt)
+{
+  AddOutPt( e1, Pt );
+  if (e2->WindDelta == 0) AddOutPt(e2, Pt);
+  if( e1->OutIdx == e2->OutIdx )
+  {
+    e1->OutIdx = Unassigned;
+    e2->OutIdx = Unassigned;
+  }
+  else if (e1->OutIdx < e2->OutIdx) 
+    AppendPolygon(e1, e2); 
+  else 
+    AppendPolygon(e2, e1);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddEdgeToSEL(TEdge *edge)
+{
+  //SEL pointers in PEdge are reused to build a list of horizontal edges.
+  //However, we don't need to worry about order with horizontal edge processing.
+  if( !m_SortedEdges )
+  {
+    m_SortedEdges = edge;
+    edge->PrevInSEL = 0;
+    edge->NextInSEL = 0;
+  }
+  else
+  {
+    edge->NextInSEL = m_SortedEdges;
+    edge->PrevInSEL = 0;
+    m_SortedEdges->PrevInSEL = edge;
+    m_SortedEdges = edge;
+  }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::CopyAELToSEL()
+{
+  TEdge* e = m_ActiveEdges;
+  m_SortedEdges = e;
+  while ( e )
+  {
+    e->PrevInSEL = e->PrevInAEL;
+    e->NextInSEL = e->NextInAEL;
+    e = e->NextInAEL;
+  }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt)
+{
+  Join* j = new Join;
+  j->OutPt1 = op1;
+  j->OutPt2 = op2;
+  j->OffPt = OffPt;
+  m_Joins.push_back(j);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ClearJoins()
+{
+  for (JoinList::size_type i = 0; i < m_Joins.size(); i++)
+    delete m_Joins[i];
+  m_Joins.resize(0);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ClearGhostJoins()
+{
+  for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++)
+    delete m_GhostJoins[i];
+  m_GhostJoins.resize(0);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt)
+{
+  Join* j = new Join;
+  j->OutPt1 = op;
+  j->OutPt2 = 0;
+  j->OffPt = OffPt;
+  m_GhostJoins.push_back(j);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::InsertLocalMinimaIntoAEL(const cInt botY)
+{
+  while (m_CurrentLM != m_MinimaList.end() && (m_CurrentLM->Y == botY))
+  {
+    TEdge* lb = m_CurrentLM->LeftBound;
+    TEdge* rb = m_CurrentLM->RightBound;
+    PopLocalMinima();
+    OutPt *Op1 = 0;
+    if (!lb)
+    {
+      //nb: don't insert LB into either AEL or SEL
+      InsertEdgeIntoAEL(rb, 0);
+      SetWindingCount(*rb);
+      if (IsContributing(*rb))
+        Op1 = AddOutPt(rb, rb->Bot); 
+    } 
+    else if (!rb)
+    {
+      InsertEdgeIntoAEL(lb, 0);
+      SetWindingCount(*lb);
+      if (IsContributing(*lb))
+        Op1 = AddOutPt(lb, lb->Bot);
+      InsertScanbeam(lb->Top.Y);
+    }
+    else
+    {
+      InsertEdgeIntoAEL(lb, 0);
+      InsertEdgeIntoAEL(rb, lb);
+      SetWindingCount( *lb );
+      rb->WindCnt = lb->WindCnt;
+      rb->WindCnt2 = lb->WindCnt2;
+      if (IsContributing(*lb))
+        Op1 = AddLocalMinPoly(lb, rb, lb->Bot);      
+      InsertScanbeam(lb->Top.Y);
+    }
+
+     if (rb)
+     {
+       if(IsHorizontal(*rb)) AddEdgeToSEL(rb);
+       else InsertScanbeam( rb->Top.Y );
+     }
+
+    if (!lb || !rb) continue;
+
+    //if any output polygons share an edge, they'll need joining later ...
+    if (Op1 && IsHorizontal(*rb) && 
+      !m_GhostJoins.empty() && (rb->WindDelta != 0))
+    {
+      for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i)
+      {
+        Join* jr = m_GhostJoins[i];
+        //if the horizontal Rb and a 'ghost' horizontal overlap, then convert
+        //the 'ghost' join to a real join ready for later ...
+        if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X))
+          AddJoin(jr->OutPt1, Op1, jr->OffPt);
+      }
+    }
+
+    if (lb->OutIdx >= 0 && lb->PrevInAEL && 
+      lb->PrevInAEL->Curr.X == lb->Bot.X &&
+      lb->PrevInAEL->OutIdx >= 0 &&
+      SlopesEqual(*lb->PrevInAEL, *lb, m_UseFullRange) &&
+      (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0))
+    {
+        OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot);
+        AddJoin(Op1, Op2, lb->Top);
+    }
+
+    if(lb->NextInAEL != rb)
+    {
+
+      if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 &&
+        SlopesEqual(*rb->PrevInAEL, *rb, m_UseFullRange) &&
+        (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0))
+      {
+          OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot);
+          AddJoin(Op1, Op2, rb->Top);
+      }
+
+      TEdge* e = lb->NextInAEL;
+      if (e)
+      {
+        while( e != rb )
+        {
+          //nb: For calculating winding counts etc, IntersectEdges() assumes
+          //that param1 will be to the Right of param2 ABOVE the intersection ...
+          IntersectEdges(rb , e , lb->Curr); //order important here
+          e = e->NextInAEL;
+        }
+      }
+    }
+    
+  }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DeleteFromAEL(TEdge *e)
+{
+  TEdge* AelPrev = e->PrevInAEL;
+  TEdge* AelNext = e->NextInAEL;
+  if(  !AelPrev &&  !AelNext && (e != m_ActiveEdges) ) return; //already deleted
+  if( AelPrev ) AelPrev->NextInAEL = AelNext;
+  else m_ActiveEdges = AelNext;
+  if( AelNext ) AelNext->PrevInAEL = AelPrev;
+  e->NextInAEL = 0;
+  e->PrevInAEL = 0;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DeleteFromSEL(TEdge *e)
+{
+  TEdge* SelPrev = e->PrevInSEL;
+  TEdge* SelNext = e->NextInSEL;
+  if( !SelPrev &&  !SelNext && (e != m_SortedEdges) ) return; //already deleted
+  if( SelPrev ) SelPrev->NextInSEL = SelNext;
+  else m_SortedEdges = SelNext;
+  if( SelNext ) SelNext->PrevInSEL = SelPrev;
+  e->NextInSEL = 0;
+  e->PrevInSEL = 0;
+}
+//------------------------------------------------------------------------------
+
+#ifdef use_xyz
+void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2)
+{
+  if (pt.Z != 0 || !m_ZFill) return;
+  else if (pt == e1.Bot) pt.Z = e1.Bot.Z;
+  else if (pt == e1.Top) pt.Z = e1.Top.Z;
+  else if (pt == e2.Bot) pt.Z = e2.Bot.Z;
+  else if (pt == e2.Top) pt.Z = e2.Top.Z;
+  else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); 
+}
+//------------------------------------------------------------------------------
+#endif
+
+void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt)
+{
+  bool e1Contributing = ( e1->OutIdx >= 0 );
+  bool e2Contributing = ( e2->OutIdx >= 0 );
+
+#ifdef use_xyz
+        SetZ(Pt, *e1, *e2);
+#endif
+
+#ifdef use_lines
+  //if either edge is on an OPEN path ...
+  if (e1->WindDelta == 0 || e2->WindDelta == 0)
+  {
+    //ignore subject-subject open path intersections UNLESS they
+    //are both open paths, AND they are both 'contributing maximas' ...
+	if (e1->WindDelta == 0 && e2->WindDelta == 0) return;
+
+    //if intersecting a subj line with a subj poly ...
+    else if (e1->PolyTyp == e2->PolyTyp && 
+      e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion)
+    {
+      if (e1->WindDelta == 0)
+      {
+        if (e2Contributing)
+        {
+          AddOutPt(e1, Pt);
+          if (e1Contributing) e1->OutIdx = Unassigned;
+        }
+      }
+      else
+      {
+        if (e1Contributing)
+        {
+          AddOutPt(e2, Pt);
+          if (e2Contributing) e2->OutIdx = Unassigned;
+        }
+      }
+    }
+    else if (e1->PolyTyp != e2->PolyTyp)
+    {
+      //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ...
+      if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && 
+        (m_ClipType != ctUnion || e2->WindCnt2 == 0))
+      {
+        AddOutPt(e1, Pt);
+        if (e1Contributing) e1->OutIdx = Unassigned;
+      }
+      else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && 
+        (m_ClipType != ctUnion || e1->WindCnt2 == 0))
+      {
+        AddOutPt(e2, Pt);
+        if (e2Contributing) e2->OutIdx = Unassigned;
+      }
+    }
+    return;
+  }
+#endif
+
+  //update winding counts...
+  //assumes that e1 will be to the Right of e2 ABOVE the intersection
+  if ( e1->PolyTyp == e2->PolyTyp )
+  {
+    if ( IsEvenOddFillType( *e1) )
+    {
+      int oldE1WindCnt = e1->WindCnt;
+      e1->WindCnt = e2->WindCnt;
+      e2->WindCnt = oldE1WindCnt;
+    } else
+    {
+      if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt;
+      else e1->WindCnt += e2->WindDelta;
+      if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt;
+      else e2->WindCnt -= e1->WindDelta;
+    }
+  } else
+  {
+    if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta;
+    else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0;
+    if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta;
+    else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0;
+  }
+
+  PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2;
+  if (e1->PolyTyp == ptSubject)
+  {
+    e1FillType = m_SubjFillType;
+    e1FillType2 = m_ClipFillType;
+  } else
+  {
+    e1FillType = m_ClipFillType;
+    e1FillType2 = m_SubjFillType;
+  }
+  if (e2->PolyTyp == ptSubject)
+  {
+    e2FillType = m_SubjFillType;
+    e2FillType2 = m_ClipFillType;
+  } else
+  {
+    e2FillType = m_ClipFillType;
+    e2FillType2 = m_SubjFillType;
+  }
+
+  cInt e1Wc, e2Wc;
+  switch (e1FillType)
+  {
+    case pftPositive: e1Wc = e1->WindCnt; break;
+    case pftNegative: e1Wc = -e1->WindCnt; break;
+    default: e1Wc = Abs(e1->WindCnt);
+  }
+  switch(e2FillType)
+  {
+    case pftPositive: e2Wc = e2->WindCnt; break;
+    case pftNegative: e2Wc = -e2->WindCnt; break;
+    default: e2Wc = Abs(e2->WindCnt);
+  }
+
+  if ( e1Contributing && e2Contributing )
+  {
+    if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) ||
+      (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) )
+    {
+      AddLocalMaxPoly(e1, e2, Pt); 
+    }
+    else
+    {
+      AddOutPt(e1, Pt);
+      AddOutPt(e2, Pt);
+      SwapSides( *e1 , *e2 );
+      SwapPolyIndexes( *e1 , *e2 );
+    }
+  }
+  else if ( e1Contributing )
+  {
+    if (e2Wc == 0 || e2Wc == 1) 
+    {
+      AddOutPt(e1, Pt);
+      SwapSides(*e1, *e2);
+      SwapPolyIndexes(*e1, *e2);
+    }
+  }
+  else if ( e2Contributing )
+  {
+    if (e1Wc == 0 || e1Wc == 1) 
+    {
+      AddOutPt(e2, Pt);
+      SwapSides(*e1, *e2);
+      SwapPolyIndexes(*e1, *e2);
+    }
+  } 
+  else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1))
+  {
+    //neither edge is currently contributing ...
+
+    cInt e1Wc2, e2Wc2;
+    switch (e1FillType2)
+    {
+      case pftPositive: e1Wc2 = e1->WindCnt2; break;
+      case pftNegative : e1Wc2 = -e1->WindCnt2; break;
+      default: e1Wc2 = Abs(e1->WindCnt2);
+    }
+    switch (e2FillType2)
+    {
+      case pftPositive: e2Wc2 = e2->WindCnt2; break;
+      case pftNegative: e2Wc2 = -e2->WindCnt2; break;
+      default: e2Wc2 = Abs(e2->WindCnt2);
+    }
+
+    if (e1->PolyTyp != e2->PolyTyp)
+    {
+      AddLocalMinPoly(e1, e2, Pt);
+    }
+    else if (e1Wc == 1 && e2Wc == 1)
+      switch( m_ClipType ) {
+        case ctIntersection:
+          if (e1Wc2 > 0 && e2Wc2 > 0)
+            AddLocalMinPoly(e1, e2, Pt);
+          break;
+        case ctUnion:
+          if ( e1Wc2 <= 0 && e2Wc2 <= 0 )
+            AddLocalMinPoly(e1, e2, Pt);
+          break;
+        case ctDifference:
+          if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
+              ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
+                AddLocalMinPoly(e1, e2, Pt);
+          break;
+        case ctXor:
+          AddLocalMinPoly(e1, e2, Pt);
+      }
+    else
+      SwapSides( *e1, *e2 );
+  }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::SetHoleState(TEdge *e, OutRec *outrec)
+{
+  bool IsHole = false;
+  TEdge *e2 = e->PrevInAEL;
+  while (e2)
+  {
+    if (e2->OutIdx >= 0 && e2->WindDelta != 0)
+    {
+      IsHole = !IsHole;
+      if (! outrec->FirstLeft)
+        outrec->FirstLeft = m_PolyOuts[e2->OutIdx];
+    }
+    e2 = e2->PrevInAEL;
+  }
+  if (IsHole) outrec->IsHole = true;
+}
+//------------------------------------------------------------------------------
+
+OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2)
+{
+  //work out which polygon fragment has the correct hole state ...
+  if (!outRec1->BottomPt) 
+    outRec1->BottomPt = GetBottomPt(outRec1->Pts);
+  if (!outRec2->BottomPt) 
+    outRec2->BottomPt = GetBottomPt(outRec2->Pts);
+  OutPt *OutPt1 = outRec1->BottomPt;
+  OutPt *OutPt2 = outRec2->BottomPt;
+  if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1;
+  else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2;
+  else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1;
+  else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2;
+  else if (OutPt1->Next == OutPt1) return outRec2;
+  else if (OutPt2->Next == OutPt2) return outRec1;
+  else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1;
+  else return outRec2;
+}
+//------------------------------------------------------------------------------
+
+bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2)
+{
+  do
+  {
+    outRec1 = outRec1->FirstLeft;
+    if (outRec1 == outRec2) return true;
+  } while (outRec1);
+  return false;
+}
+//------------------------------------------------------------------------------
+
+OutRec* Clipper::GetOutRec(int Idx)
+{
+  OutRec* outrec = m_PolyOuts[Idx];
+  while (outrec != m_PolyOuts[outrec->Idx])
+    outrec = m_PolyOuts[outrec->Idx];
+  return outrec;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AppendPolygon(TEdge *e1, TEdge *e2)
+{
+  //get the start and ends of both output polygons ...
+  OutRec *outRec1 = m_PolyOuts[e1->OutIdx];
+  OutRec *outRec2 = m_PolyOuts[e2->OutIdx];
+
+  OutRec *holeStateRec;
+  if (Param1RightOfParam2(outRec1, outRec2)) 
+    holeStateRec = outRec2;
+  else if (Param1RightOfParam2(outRec2, outRec1)) 
+    holeStateRec = outRec1;
+  else 
+    holeStateRec = GetLowermostRec(outRec1, outRec2);
+
+  //get the start and ends of both output polygons and
+  //join e2 poly onto e1 poly and delete pointers to e2 ...
+
+  OutPt* p1_lft = outRec1->Pts;
+  OutPt* p1_rt = p1_lft->Prev;
+  OutPt* p2_lft = outRec2->Pts;
+  OutPt* p2_rt = p2_lft->Prev;
+
+  EdgeSide Side;
+  //join e2 poly onto e1 poly and delete pointers to e2 ...
+  if(  e1->Side == esLeft )
+  {
+    if(  e2->Side == esLeft )
+    {
+      //z y x a b c
+      ReversePolyPtLinks(p2_lft);
+      p2_lft->Next = p1_lft;
+      p1_lft->Prev = p2_lft;
+      p1_rt->Next = p2_rt;
+      p2_rt->Prev = p1_rt;
+      outRec1->Pts = p2_rt;
+    } else
+    {
+      //x y z a b c
+      p2_rt->Next = p1_lft;
+      p1_lft->Prev = p2_rt;
+      p2_lft->Prev = p1_rt;
+      p1_rt->Next = p2_lft;
+      outRec1->Pts = p2_lft;
+    }
+    Side = esLeft;
+  } else
+  {
+    if(  e2->Side == esRight )
+    {
+      //a b c z y x
+      ReversePolyPtLinks(p2_lft);
+      p1_rt->Next = p2_rt;
+      p2_rt->Prev = p1_rt;
+      p2_lft->Next = p1_lft;
+      p1_lft->Prev = p2_lft;
+    } else
+    {
+      //a b c x y z
+      p1_rt->Next = p2_lft;
+      p2_lft->Prev = p1_rt;
+      p1_lft->Prev = p2_rt;
+      p2_rt->Next = p1_lft;
+    }
+    Side = esRight;
+  }
+
+  outRec1->BottomPt = 0;
+  if (holeStateRec == outRec2)
+  {
+    if (outRec2->FirstLeft != outRec1)
+      outRec1->FirstLeft = outRec2->FirstLeft;
+    outRec1->IsHole = outRec2->IsHole;
+  }
+  outRec2->Pts = 0;
+  outRec2->BottomPt = 0;
+  outRec2->FirstLeft = outRec1;
+
+  int OKIdx = e1->OutIdx;
+  int ObsoleteIdx = e2->OutIdx;
+
+  e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly
+  e2->OutIdx = Unassigned;
+
+  TEdge* e = m_ActiveEdges;
+  while( e )
+  {
+    if( e->OutIdx == ObsoleteIdx )
+    {
+      e->OutIdx = OKIdx;
+      e->Side = Side;
+      break;
+    }
+    e = e->NextInAEL;
+  }
+
+  outRec2->Idx = outRec1->Idx;
+}
+//------------------------------------------------------------------------------
+
+OutRec* Clipper::CreateOutRec()
+{
+  OutRec* result = new OutRec;
+  result->IsHole = false;
+  result->IsOpen = false;
+  result->FirstLeft = 0;
+  result->Pts = 0;
+  result->BottomPt = 0;
+  result->PolyNd = 0;
+  m_PolyOuts.push_back(result);
+  result->Idx = (int)m_PolyOuts.size()-1;
+  return result;
+}
+//------------------------------------------------------------------------------
+
+OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
+{
+  if(  e->OutIdx < 0 )
+  {
+    OutRec *outRec = CreateOutRec();
+    outRec->IsOpen = (e->WindDelta == 0);
+    OutPt* newOp = new OutPt;
+    outRec->Pts = newOp;
+    newOp->Idx = outRec->Idx;
+    newOp->Pt = pt;
+    newOp->Next = newOp;
+    newOp->Prev = newOp;
+    if (!outRec->IsOpen)
+      SetHoleState(e, outRec);
+    e->OutIdx = outRec->Idx;
+    return newOp;
+  } else
+  {
+    OutRec *outRec = m_PolyOuts[e->OutIdx];
+    //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most'
+    OutPt* op = outRec->Pts;
+
+	bool ToFront = (e->Side == esLeft);
+	if (ToFront && (pt == op->Pt)) return op;
+    else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev;
+
+    OutPt* newOp = new OutPt;
+    newOp->Idx = outRec->Idx;
+    newOp->Pt = pt;
+    newOp->Next = op;
+    newOp->Prev = op->Prev;
+    newOp->Prev->Next = newOp;
+    op->Prev = newOp;
+    if (ToFront) outRec->Pts = newOp;
+    return newOp;
+  }
+}
+//------------------------------------------------------------------------------
+
+OutPt* Clipper::GetLastOutPt(TEdge *e)
+{
+	OutRec *outRec = m_PolyOuts[e->OutIdx];
+	if (e->Side == esLeft)
+		return outRec->Pts;
+	else
+		return outRec->Pts->Prev;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ProcessHorizontals()
+{
+  TEdge* horzEdge = m_SortedEdges;
+  while(horzEdge)
+  {
+    DeleteFromSEL(horzEdge);
+    ProcessHorizontal(horzEdge);
+    horzEdge = m_SortedEdges;
+  }
+}
+//------------------------------------------------------------------------------
+
+inline bool IsMinima(TEdge *e)
+{
+  return e  && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e);
+}
+//------------------------------------------------------------------------------
+
+inline bool IsMaxima(TEdge *e, const cInt Y)
+{
+  return e && e->Top.Y == Y && !e->NextInLML;
+}
+//------------------------------------------------------------------------------
+
+inline bool IsIntermediate(TEdge *e, const cInt Y)
+{
+  return e->Top.Y == Y && e->NextInLML;
+}
+//------------------------------------------------------------------------------
+
+TEdge *GetMaximaPair(TEdge *e)
+{
+  TEdge* result = 0;
+  if ((e->Next->Top == e->Top) && !e->Next->NextInLML)
+    result = e->Next;
+  else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML)
+    result = e->Prev;
+
+  if (result && (result->OutIdx == Skip ||
+    //result is false if both NextInAEL & PrevInAEL are nil & not horizontal ...
+    (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result))))
+      return 0;
+  return result;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2)
+{
+  //check that one or other edge hasn't already been removed from AEL ...
+  if (Edge1->NextInAEL == Edge1->PrevInAEL || 
+    Edge2->NextInAEL == Edge2->PrevInAEL) return;
+
+  if(  Edge1->NextInAEL == Edge2 )
+  {
+    TEdge* Next = Edge2->NextInAEL;
+    if( Next ) Next->PrevInAEL = Edge1;
+    TEdge* Prev = Edge1->PrevInAEL;
+    if( Prev ) Prev->NextInAEL = Edge2;
+    Edge2->PrevInAEL = Prev;
+    Edge2->NextInAEL = Edge1;
+    Edge1->PrevInAEL = Edge2;
+    Edge1->NextInAEL = Next;
+  }
+  else if(  Edge2->NextInAEL == Edge1 )
+  {
+    TEdge* Next = Edge1->NextInAEL;
+    if( Next ) Next->PrevInAEL = Edge2;
+    TEdge* Prev = Edge2->PrevInAEL;
+    if( Prev ) Prev->NextInAEL = Edge1;
+    Edge1->PrevInAEL = Prev;
+    Edge1->NextInAEL = Edge2;
+    Edge2->PrevInAEL = Edge1;
+    Edge2->NextInAEL = Next;
+  }
+  else
+  {
+    TEdge* Next = Edge1->NextInAEL;
+    TEdge* Prev = Edge1->PrevInAEL;
+    Edge1->NextInAEL = Edge2->NextInAEL;
+    if( Edge1->NextInAEL ) Edge1->NextInAEL->PrevInAEL = Edge1;
+    Edge1->PrevInAEL = Edge2->PrevInAEL;
+    if( Edge1->PrevInAEL ) Edge1->PrevInAEL->NextInAEL = Edge1;
+    Edge2->NextInAEL = Next;
+    if( Edge2->NextInAEL ) Edge2->NextInAEL->PrevInAEL = Edge2;
+    Edge2->PrevInAEL = Prev;
+    if( Edge2->PrevInAEL ) Edge2->PrevInAEL->NextInAEL = Edge2;
+  }
+
+  if( !Edge1->PrevInAEL ) m_ActiveEdges = Edge1;
+  else if( !Edge2->PrevInAEL ) m_ActiveEdges = Edge2;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2)
+{
+  if(  !( Edge1->NextInSEL ) &&  !( Edge1->PrevInSEL ) ) return;
+  if(  !( Edge2->NextInSEL ) &&  !( Edge2->PrevInSEL ) ) return;
+
+  if(  Edge1->NextInSEL == Edge2 )
+  {
+    TEdge* Next = Edge2->NextInSEL;
+    if( Next ) Next->PrevInSEL = Edge1;
+    TEdge* Prev = Edge1->PrevInSEL;
+    if( Prev ) Prev->NextInSEL = Edge2;
+    Edge2->PrevInSEL = Prev;
+    Edge2->NextInSEL = Edge1;
+    Edge1->PrevInSEL = Edge2;
+    Edge1->NextInSEL = Next;
+  }
+  else if(  Edge2->NextInSEL == Edge1 )
+  {
+    TEdge* Next = Edge1->NextInSEL;
+    if( Next ) Next->PrevInSEL = Edge2;
+    TEdge* Prev = Edge2->PrevInSEL;
+    if( Prev ) Prev->NextInSEL = Edge1;
+    Edge1->PrevInSEL = Prev;
+    Edge1->NextInSEL = Edge2;
+    Edge2->PrevInSEL = Edge1;
+    Edge2->NextInSEL = Next;
+  }
+  else
+  {
+    TEdge* Next = Edge1->NextInSEL;
+    TEdge* Prev = Edge1->PrevInSEL;
+    Edge1->NextInSEL = Edge2->NextInSEL;
+    if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1;
+    Edge1->PrevInSEL = Edge2->PrevInSEL;
+    if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1;
+    Edge2->NextInSEL = Next;
+    if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2;
+    Edge2->PrevInSEL = Prev;
+    if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2;
+  }
+
+  if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1;
+  else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2;
+}
+//------------------------------------------------------------------------------
+
+TEdge* GetNextInAEL(TEdge *e, Direction dir)
+{
+  return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL;
+}
+//------------------------------------------------------------------------------
+
+void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right)
+{
+  if (HorzEdge.Bot.X < HorzEdge.Top.X)
+  {
+    Left = HorzEdge.Bot.X;
+    Right = HorzEdge.Top.X;
+    Dir = dLeftToRight;
+  } else
+  {
+    Left = HorzEdge.Top.X;
+    Right = HorzEdge.Bot.X;
+    Dir = dRightToLeft;
+  }
+}
+//------------------------------------------------------------------------
+
+/*******************************************************************************
+* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or    *
+* Bottom of a scanbeam) are processed as if layered. The order in which HEs    *
+* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#]    *
+* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs),      *
+* and with other non-horizontal edges [*]. Once these intersections are        *
+* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into   *
+* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs.    *
+*******************************************************************************/
+
+void Clipper::ProcessHorizontal(TEdge *horzEdge)
+{
+  Direction dir;
+  cInt horzLeft, horzRight;
+  bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx]->IsOpen);
+
+  GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
+
+  TEdge* eLastHorz = horzEdge, *eMaxPair = 0;
+  while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) 
+    eLastHorz = eLastHorz->NextInLML;
+  if (!eLastHorz->NextInLML)
+    eMaxPair = GetMaximaPair(eLastHorz);
+
+  MaximaList::const_iterator maxIt;
+  MaximaList::const_reverse_iterator maxRit;
+  if (!m_Maxima.empty())
+  {
+      //get the first maxima in range (X) ...
+      if (dir == dLeftToRight)
+      {
+          maxIt = m_Maxima.begin();
+          while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++;
+          if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X)
+              maxIt = m_Maxima.end();
+      }
+      else
+      {
+          maxRit = m_Maxima.rbegin();
+          while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++;
+          if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X)
+              maxRit = m_Maxima.rend();
+      }
+  }
+
+  OutPt* op1 = 0;
+
+  for (;;) //loop through consec. horizontal edges
+  {
+		  
+    bool IsLastHorz = (horzEdge == eLastHorz);
+    TEdge* e = GetNextInAEL(horzEdge, dir);
+    while(e)
+    {
+
+        //this code block inserts extra coords into horizontal edges (in output
+        //polygons) whereever maxima touch these horizontal edges. This helps
+        //'simplifying' polygons (ie if the Simplify property is set).
+        if (!m_Maxima.empty())
+        {
+            if (dir == dLeftToRight)
+            {
+                while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X) 
+                {
+                  if (horzEdge->OutIdx >= 0 && !IsOpen)
+                    AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y));
+                  maxIt++;
+                }
+            }
+            else
+            {
+                while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X)
+                {
+                  if (horzEdge->OutIdx >= 0 && !IsOpen)
+                    AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y));
+                  maxRit++;
+                }
+            }
+        };
+
+        if ((dir == dLeftToRight && e->Curr.X > horzRight) ||
+			(dir == dRightToLeft && e->Curr.X < horzLeft)) break;
+
+		//Also break if we've got to the end of an intermediate horizontal edge ...
+		//nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal.
+		if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && 
+			e->Dx < horzEdge->NextInLML->Dx) break;
+
+    if (horzEdge->OutIdx >= 0 && !IsOpen)  //note: may be done multiple times
+		{
+            op1 = AddOutPt(horzEdge, e->Curr);
+			TEdge* eNextHorz = m_SortedEdges;
+			while (eNextHorz)
+			{
+				if (eNextHorz->OutIdx >= 0 &&
+					HorzSegmentsOverlap(horzEdge->Bot.X,
+					horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))
+				{
+                    OutPt* op2 = GetLastOutPt(eNextHorz);
+                    AddJoin(op2, op1, eNextHorz->Top);
+				}
+				eNextHorz = eNextHorz->NextInSEL;
+			}
+			AddGhostJoin(op1, horzEdge->Bot);
+		}
+		
+		//OK, so far we're still in range of the horizontal Edge  but make sure
+        //we're at the last of consec. horizontals when matching with eMaxPair
+        if(e == eMaxPair && IsLastHorz)
+        {
+          if (horzEdge->OutIdx >= 0)
+            AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top);
+          DeleteFromAEL(horzEdge);
+          DeleteFromAEL(eMaxPair);
+          return;
+        }
+        
+		if(dir == dLeftToRight)
+        {
+          IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);
+          IntersectEdges(horzEdge, e, Pt);
+        }
+        else
+        {
+          IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);
+          IntersectEdges( e, horzEdge, Pt);
+        }
+        TEdge* eNext = GetNextInAEL(e, dir);
+        SwapPositionsInAEL( horzEdge, e );
+        e = eNext;
+    } //end while(e)
+
+	//Break out of loop if HorzEdge.NextInLML is not also horizontal ...
+	if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break;
+
+	UpdateEdgeIntoAEL(horzEdge);
+    if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot);
+    GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
+
+  } //end for (;;)
+
+  if (horzEdge->OutIdx >= 0 && !op1)
+  {
+      op1 = GetLastOutPt(horzEdge);
+      TEdge* eNextHorz = m_SortedEdges;
+      while (eNextHorz)
+      {
+          if (eNextHorz->OutIdx >= 0 &&
+              HorzSegmentsOverlap(horzEdge->Bot.X,
+              horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))
+          {
+              OutPt* op2 = GetLastOutPt(eNextHorz);
+              AddJoin(op2, op1, eNextHorz->Top);
+          }
+          eNextHorz = eNextHorz->NextInSEL;
+      }
+      AddGhostJoin(op1, horzEdge->Top);
+  }
+
+  if (horzEdge->NextInLML)
+  {
+    if(horzEdge->OutIdx >= 0)
+    {
+      op1 = AddOutPt( horzEdge, horzEdge->Top);
+      UpdateEdgeIntoAEL(horzEdge);
+      if (horzEdge->WindDelta == 0) return;
+      //nb: HorzEdge is no longer horizontal here
+      TEdge* ePrev = horzEdge->PrevInAEL;
+      TEdge* eNext = horzEdge->NextInAEL;
+      if (ePrev && ePrev->Curr.X == horzEdge->Bot.X &&
+        ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 &&
+        (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y &&
+        SlopesEqual(*horzEdge, *ePrev, m_UseFullRange)))
+      {
+        OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot);
+        AddJoin(op1, op2, horzEdge->Top);
+      }
+      else if (eNext && eNext->Curr.X == horzEdge->Bot.X &&
+        eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 &&
+        eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y &&
+        SlopesEqual(*horzEdge, *eNext, m_UseFullRange))
+      {
+        OutPt* op2 = AddOutPt(eNext, horzEdge->Bot);
+        AddJoin(op1, op2, horzEdge->Top);
+      }
+    }
+    else
+      UpdateEdgeIntoAEL(horzEdge); 
+  }
+  else
+  {
+    if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top);
+    DeleteFromAEL(horzEdge);
+  }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::UpdateEdgeIntoAEL(TEdge *&e)
+{
+  if( !e->NextInLML )
+    throw clipperException("UpdateEdgeIntoAEL: invalid call");
+
+  e->NextInLML->OutIdx = e->OutIdx;
+  TEdge* AelPrev = e->PrevInAEL;
+  TEdge* AelNext = e->NextInAEL;
+  if (AelPrev) AelPrev->NextInAEL = e->NextInLML;
+  else m_ActiveEdges = e->NextInLML;
+  if (AelNext) AelNext->PrevInAEL = e->NextInLML;
+  e->NextInLML->Side = e->Side;
+  e->NextInLML->WindDelta = e->WindDelta;
+  e->NextInLML->WindCnt = e->WindCnt;
+  e->NextInLML->WindCnt2 = e->WindCnt2;
+  e = e->NextInLML;
+  e->Curr = e->Bot;
+  e->PrevInAEL = AelPrev;
+  e->NextInAEL = AelNext;
+  if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y);
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::ProcessIntersections(const cInt topY)
+{
+  if( !m_ActiveEdges ) return true;
+  try {
+    BuildIntersectList(topY);
+    size_t IlSize = m_IntersectList.size();
+    if (IlSize == 0) return true;
+    if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList();
+    else return false;
+  }
+  catch(std::exception const& ex) 
+  {
+    m_SortedEdges = 0;
+    DisposeIntersectNodes();
+    throw clipperException((std::string("ProcessIntersections error ") + ex.what()).c_str());
+  }
+  m_SortedEdges = 0;
+  return true;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DisposeIntersectNodes()
+{
+  for (size_t i = 0; i < m_IntersectList.size(); ++i )
+    delete m_IntersectList[i];
+  m_IntersectList.clear();
+}
+//------------------------------------------------------------------------------
+
+void Clipper::BuildIntersectList(const cInt topY)
+{
+  if ( !m_ActiveEdges ) return;
+
+  //prepare for sorting ...
+  TEdge* e = m_ActiveEdges;
+  m_SortedEdges = e;
+  while( e )
+  {
+    e->PrevInSEL = e->PrevInAEL;
+    e->NextInSEL = e->NextInAEL;
+    e->Curr.X = TopX( *e, topY );
+    e = e->NextInAEL;
+  }
+
+  //bubblesort ...
+  bool isModified;
+  do
+  {
+    isModified = false;
+    e = m_SortedEdges;
+    while( e->NextInSEL )
+    {
+      TEdge *eNext = e->NextInSEL;
+      IntPoint Pt;
+      if(e->Curr.X > eNext->Curr.X)
+      {
+        IntersectPoint(*e, *eNext, Pt);
+        IntersectNode * newNode = new IntersectNode;
+        newNode->Edge1 = e;
+        newNode->Edge2 = eNext;
+        newNode->Pt = Pt;
+        m_IntersectList.push_back(newNode);
+
+        SwapPositionsInSEL(e, eNext);
+        isModified = true;
+      }
+      else
+        e = eNext;
+    }
+    if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0;
+    else break;
+  }
+  while ( isModified );
+  m_SortedEdges = 0; //important
+}
+//------------------------------------------------------------------------------
+
+
+void Clipper::ProcessIntersectList()
+{
+  for (size_t i = 0; i < m_IntersectList.size(); ++i)
+  {
+    IntersectNode* iNode = m_IntersectList[i];
+    {
+      IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt);
+      SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 );
+    }
+    delete iNode;
+  }
+  m_IntersectList.clear();
+}
+//------------------------------------------------------------------------------
+
+bool IntersectListSort(IntersectNode* node1, IntersectNode* node2)
+{
+  return node2->Pt.Y < node1->Pt.Y;
+}
+//------------------------------------------------------------------------------
+
+inline bool EdgesAdjacent(const IntersectNode &inode)
+{
+  return (inode.Edge1->NextInSEL == inode.Edge2) ||
+    (inode.Edge1->PrevInSEL == inode.Edge2);
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::FixupIntersectionOrder()
+{
+  //pre-condition: intersections are sorted Bottom-most first.
+  //Now it's crucial that intersections are made only between adjacent edges,
+  //so to ensure this the order of intersections may need adjusting ...
+  CopyAELToSEL();
+  std::stable_sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort);
+  size_t cnt = m_IntersectList.size();
+  for (size_t i = 0; i < cnt; ++i) 
+  {
+    if (!EdgesAdjacent(*m_IntersectList[i]))
+    {
+      size_t j = i + 1;
+      while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++;
+      if (j == cnt)  return false;
+      std::swap(m_IntersectList[i], m_IntersectList[j]);
+    }
+    SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2);
+  }
+  return true;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DoMaxima(TEdge *e)
+{
+  TEdge* eMaxPair = GetMaximaPair(e);
+  if (!eMaxPair)
+  {
+    if (e->OutIdx >= 0)
+      AddOutPt(e, e->Top);
+    DeleteFromAEL(e);
+    return;
+  }
+
+  TEdge* eNext = e->NextInAEL;
+  while(eNext && eNext != eMaxPair)
+  {
+    IntersectEdges(e, eNext, e->Top);
+    SwapPositionsInAEL(e, eNext);
+    eNext = e->NextInAEL;
+  }
+
+  if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned)
+  {
+    DeleteFromAEL(e);
+    DeleteFromAEL(eMaxPair);
+  }
+  else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 )
+  {
+    if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top);
+    DeleteFromAEL(e);
+    DeleteFromAEL(eMaxPair);
+  }
+#ifdef use_lines
+  else if (e->WindDelta == 0)
+  {
+    if (e->OutIdx >= 0) 
+    {
+      AddOutPt(e, e->Top);
+      e->OutIdx = Unassigned;
+    }
+    DeleteFromAEL(e);
+
+    if (eMaxPair->OutIdx >= 0)
+    {
+      AddOutPt(eMaxPair, e->Top);
+      eMaxPair->OutIdx = Unassigned;
+    }
+    DeleteFromAEL(eMaxPair);
+  } 
+#endif
+  else throw clipperException("DoMaxima error");
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
+{
+  TEdge* e = m_ActiveEdges;
+  while( e )
+  {
+    //1. process maxima, treating them as if they're 'bent' horizontal edges,
+    //   but exclude maxima with horizontal edges. nb: e can't be a horizontal.
+    bool IsMaximaEdge = IsMaxima(e, topY);
+
+    if(IsMaximaEdge)
+    {
+      TEdge* eMaxPair = GetMaximaPair(e);
+      IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair));
+    }
+
+    if(IsMaximaEdge)
+    {
+      if (m_StrictSimple) m_Maxima.push_back(e->Top.X);
+      TEdge* ePrev = e->PrevInAEL;
+      DoMaxima(e);
+      if( !ePrev ) e = m_ActiveEdges;
+      else e = ePrev->NextInAEL;
+    }
+    else
+    {
+      //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ...
+      if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML))
+      {
+        UpdateEdgeIntoAEL(e);
+        if (e->OutIdx >= 0)
+          AddOutPt(e, e->Bot);
+        AddEdgeToSEL(e);
+      } 
+      else
+      {
+        e->Curr.X = TopX( *e, topY );
+        e->Curr.Y = topY;
+      }
+
+      //When StrictlySimple and 'e' is being touched by another edge, then
+      //make sure both edges have a vertex here ...
+      if (m_StrictSimple)
+      {  
+        TEdge* ePrev = e->PrevInAEL;
+        if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) &&
+          (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0))
+        {
+          IntPoint pt = e->Curr;
+#ifdef use_xyz
+          SetZ(pt, *ePrev, *e);
+#endif
+          OutPt* op = AddOutPt(ePrev, pt);
+          OutPt* op2 = AddOutPt(e, pt);
+          AddJoin(op, op2, pt); //StrictlySimple (type-3) join
+        }
+      }
+
+      e = e->NextInAEL;
+    }
+  }
+
+  //3. Process horizontals at the Top of the scanbeam ...
+  m_Maxima.sort();
+  ProcessHorizontals();
+  m_Maxima.clear();
+
+  //4. Promote intermediate vertices ...
+  e = m_ActiveEdges;
+  while(e)
+  {
+    if(IsIntermediate(e, topY))
+    {
+      OutPt* op = 0;
+      if( e->OutIdx >= 0 ) 
+        op = AddOutPt(e, e->Top);
+      UpdateEdgeIntoAEL(e);
+
+      //if output polygons share an edge, they'll need joining later ...
+      TEdge* ePrev = e->PrevInAEL;
+      TEdge* eNext = e->NextInAEL;
+      if (ePrev && ePrev->Curr.X == e->Bot.X &&
+        ePrev->Curr.Y == e->Bot.Y && op &&
+        ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y &&
+        SlopesEqual(*e, *ePrev, m_UseFullRange) &&
+        (e->WindDelta != 0) && (ePrev->WindDelta != 0))
+      {
+        OutPt* op2 = AddOutPt(ePrev, e->Bot);
+        AddJoin(op, op2, e->Top);
+      }
+      else if (eNext && eNext->Curr.X == e->Bot.X &&
+        eNext->Curr.Y == e->Bot.Y && op &&
+        eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y &&
+        SlopesEqual(*e, *eNext, m_UseFullRange) &&
+        (e->WindDelta != 0) && (eNext->WindDelta != 0))
+      {
+        OutPt* op2 = AddOutPt(eNext, e->Bot);
+        AddJoin(op, op2, e->Top);
+      }
+    }
+    e = e->NextInAEL;
+  }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::FixupOutPolyline(OutRec &outrec)
+{
+  OutPt *pp = outrec.Pts;
+  OutPt *lastPP = pp->Prev;
+  while (pp != lastPP)
+  {
+    pp = pp->Next;
+    if (pp->Pt == pp->Prev->Pt)
+    {
+      if (pp == lastPP) lastPP = pp->Prev;
+      OutPt *tmpPP = pp->Prev;
+      tmpPP->Next = pp->Next;
+      pp->Next->Prev = tmpPP;
+      delete pp;
+      pp = tmpPP;
+    }
+  }
+
+  if (pp == pp->Prev)
+  {
+    DisposeOutPts(pp);
+    outrec.Pts = 0;
+    return;
+  }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::FixupOutPolygon(OutRec &outrec)
+{
+    //FixupOutPolygon() - removes duplicate points and simplifies consecutive
+    //parallel edges by removing the middle vertex.
+    OutPt *lastOK = 0;
+    outrec.BottomPt = 0;
+    OutPt *pp = outrec.Pts;
+    bool preserveCol = m_PreserveCollinear || m_StrictSimple;
+
+    for (;;)
+    {
+        if (pp->Prev == pp || pp->Prev == pp->Next)
+        {
+            DisposeOutPts(pp);
+            outrec.Pts = 0;
+            return;
+        }
+
+        //test for duplicate points and collinear edges ...
+        if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) ||
+            (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) &&
+            (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt))))
+        {
+            lastOK = 0;
+            OutPt *tmp = pp;
+            pp->Prev->Next = pp->Next;
+            pp->Next->Prev = pp->Prev;
+            pp = pp->Prev;
+            delete tmp;
+        }
+        else if (pp == lastOK) break;
+        else
+        {
+            if (!lastOK) lastOK = pp;
+            pp = pp->Next;
+        }
+    }
+    outrec.Pts = pp;
+}
+//------------------------------------------------------------------------------
+
+int PointCount(OutPt *Pts)
+{
+    if (!Pts) return 0;
+    int result = 0;
+    OutPt* p = Pts;
+    do
+    {
+        result++;
+        p = p->Next;
+    }
+    while (p != Pts);
+    return result;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::BuildResult(Paths &polys)
+{
+  polys.reserve(m_PolyOuts.size());
+  for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+  {
+    if (!m_PolyOuts[i]->Pts) continue;
+    Path pg;
+    OutPt* p = m_PolyOuts[i]->Pts->Prev;
+    int cnt = PointCount(p);
+    if (cnt < 2) continue;
+    pg.reserve(cnt);
+    for (int j = 0; j < cnt; ++j)
+    {
+      pg.push_back(p->Pt);
+      p = p->Prev;
+    }
+    polys.push_back(pg);
+  }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::BuildResult2(PolyTree& polytree)
+{
+    polytree.Clear();
+    polytree.AllNodes.reserve(m_PolyOuts.size());
+    //add each output polygon/contour to polytree ...
+    for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++)
+    {
+        OutRec* outRec = m_PolyOuts[i];
+        int cnt = PointCount(outRec->Pts);
+        if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue;
+        FixHoleLinkage(*outRec);
+        PolyNode* pn = new PolyNode();
+        //nb: polytree takes ownership of all the PolyNodes
+        polytree.AllNodes.push_back(pn);
+        outRec->PolyNd = pn;
+        pn->Parent = 0;
+        pn->Index = 0;
+        pn->Contour.reserve(cnt);
+        OutPt *op = outRec->Pts->Prev;
+        for (int j = 0; j < cnt; j++)
+        {
+            pn->Contour.push_back(op->Pt);
+            op = op->Prev;
+        }
+    }
+
+    //fixup PolyNode links etc ...
+    polytree.Childs.reserve(m_PolyOuts.size());
+    for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++)
+    {
+        OutRec* outRec = m_PolyOuts[i];
+        if (!outRec->PolyNd) continue;
+        if (outRec->IsOpen) 
+        {
+          outRec->PolyNd->m_IsOpen = true;
+          polytree.AddChild(*outRec->PolyNd);
+        }
+        else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) 
+          outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd);
+        else
+          polytree.AddChild(*outRec->PolyNd);
+    }
+}
+//------------------------------------------------------------------------------
+
+void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2)
+{
+  //just swap the contents (because fIntersectNodes is a single-linked-list)
+  IntersectNode inode = int1; //gets a copy of Int1
+  int1.Edge1 = int2.Edge1;
+  int1.Edge2 = int2.Edge2;
+  int1.Pt = int2.Pt;
+  int2.Edge1 = inode.Edge1;
+  int2.Edge2 = inode.Edge2;
+  int2.Pt = inode.Pt;
+}
+//------------------------------------------------------------------------------
+
+inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2)
+{
+  if (e2.Curr.X == e1.Curr.X) 
+  {
+    if (e2.Top.Y > e1.Top.Y)
+      return e2.Top.X < TopX(e1, e2.Top.Y); 
+      else return e1.Top.X > TopX(e2, e1.Top.Y);
+  } 
+  else return e2.Curr.X < e1.Curr.X;
+}
+//------------------------------------------------------------------------------
+
+bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, 
+    cInt& Left, cInt& Right)
+{
+  if (a1 < a2)
+  {
+    if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);}
+    else {Left = std::max(a1,b2); Right = std::min(a2,b1);}
+  } 
+  else
+  {
+    if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);}
+    else {Left = std::max(a2,b2); Right = std::min(a1,b1);}
+  }
+  return Left < Right;
+}
+//------------------------------------------------------------------------------
+
+inline void UpdateOutPtIdxs(OutRec& outrec)
+{  
+  OutPt* op = outrec.Pts;
+  do
+  {
+    op->Idx = outrec.Idx;
+    op = op->Prev;
+  }
+  while(op != outrec.Pts);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge)
+{
+  if(!m_ActiveEdges)
+  {
+    edge->PrevInAEL = 0;
+    edge->NextInAEL = 0;
+    m_ActiveEdges = edge;
+  }
+  else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge))
+  {
+      edge->PrevInAEL = 0;
+      edge->NextInAEL = m_ActiveEdges;
+      m_ActiveEdges->PrevInAEL = edge;
+      m_ActiveEdges = edge;
+  } 
+  else
+  {
+    if(!startEdge) startEdge = m_ActiveEdges;
+    while(startEdge->NextInAEL  && 
+      !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge))
+        startEdge = startEdge->NextInAEL;
+    edge->NextInAEL = startEdge->NextInAEL;
+    if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge;
+    edge->PrevInAEL = startEdge;
+    startEdge->NextInAEL = edge;
+  }
+}
+//----------------------------------------------------------------------
+
+OutPt* DupOutPt(OutPt* outPt, bool InsertAfter)
+{
+  OutPt* result = new OutPt;
+  result->Pt = outPt->Pt;
+  result->Idx = outPt->Idx;
+  if (InsertAfter)
+  {
+    result->Next = outPt->Next;
+    result->Prev = outPt;
+    outPt->Next->Prev = result;
+    outPt->Next = result;
+  } 
+  else
+  {
+    result->Prev = outPt->Prev;
+    result->Next = outPt;
+    outPt->Prev->Next = result;
+    outPt->Prev = result;
+  }
+  return result;
+}
+//------------------------------------------------------------------------------
+
+bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b,
+  const IntPoint Pt, bool DiscardLeft)
+{
+  Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight);
+  Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight);
+  if (Dir1 == Dir2) return false;
+
+  //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we
+  //want Op1b to be on the Right. (And likewise with Op2 and Op2b.)
+  //So, to facilitate this while inserting Op1b and Op2b ...
+  //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b,
+  //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.)
+  if (Dir1 == dLeftToRight) 
+  {
+    while (op1->Next->Pt.X <= Pt.X && 
+      op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y)  
+        op1 = op1->Next;
+    if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next;
+    op1b = DupOutPt(op1, !DiscardLeft);
+    if (op1b->Pt != Pt) 
+    {
+      op1 = op1b;
+      op1->Pt = Pt;
+      op1b = DupOutPt(op1, !DiscardLeft);
+    }
+  } 
+  else
+  {
+    while (op1->Next->Pt.X >= Pt.X && 
+      op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) 
+        op1 = op1->Next;
+    if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next;
+    op1b = DupOutPt(op1, DiscardLeft);
+    if (op1b->Pt != Pt)
+    {
+      op1 = op1b;
+      op1->Pt = Pt;
+      op1b = DupOutPt(op1, DiscardLeft);
+    }
+  }
+
+  if (Dir2 == dLeftToRight)
+  {
+    while (op2->Next->Pt.X <= Pt.X && 
+      op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y)
+        op2 = op2->Next;
+    if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next;
+    op2b = DupOutPt(op2, !DiscardLeft);
+    if (op2b->Pt != Pt)
+    {
+      op2 = op2b;
+      op2->Pt = Pt;
+      op2b = DupOutPt(op2, !DiscardLeft);
+    };
+  } else
+  {
+    while (op2->Next->Pt.X >= Pt.X && 
+      op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) 
+        op2 = op2->Next;
+    if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next;
+    op2b = DupOutPt(op2, DiscardLeft);
+    if (op2b->Pt != Pt)
+    {
+      op2 = op2b;
+      op2->Pt = Pt;
+      op2b = DupOutPt(op2, DiscardLeft);
+    };
+  };
+
+  if ((Dir1 == dLeftToRight) == DiscardLeft)
+  {
+    op1->Prev = op2;
+    op2->Next = op1;
+    op1b->Next = op2b;
+    op2b->Prev = op1b;
+  }
+  else
+  {
+    op1->Next = op2;
+    op2->Prev = op1;
+    op1b->Prev = op2b;
+    op2b->Next = op1b;
+  }
+  return true;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
+{
+  OutPt *op1 = j->OutPt1, *op1b;
+  OutPt *op2 = j->OutPt2, *op2b;
+
+  //There are 3 kinds of joins for output polygons ...
+  //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere
+  //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal).
+  //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same
+  //location at the Bottom of the overlapping segment (& Join.OffPt is above).
+  //3. StrictSimple joins where edges touch but are not collinear and where
+  //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point.
+  bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y);
+
+  if (isHorizontal  && (j->OffPt == j->OutPt1->Pt) &&
+  (j->OffPt == j->OutPt2->Pt))
+  {
+    //Strictly Simple join ...
+    if (outRec1 != outRec2) return false;
+    op1b = j->OutPt1->Next;
+    while (op1b != op1 && (op1b->Pt == j->OffPt)) 
+      op1b = op1b->Next;
+    bool reverse1 = (op1b->Pt.Y > j->OffPt.Y);
+    op2b = j->OutPt2->Next;
+    while (op2b != op2 && (op2b->Pt == j->OffPt)) 
+      op2b = op2b->Next;
+    bool reverse2 = (op2b->Pt.Y > j->OffPt.Y);
+    if (reverse1 == reverse2) return false;
+    if (reverse1)
+    {
+      op1b = DupOutPt(op1, false);
+      op2b = DupOutPt(op2, true);
+      op1->Prev = op2;
+      op2->Next = op1;
+      op1b->Next = op2b;
+      op2b->Prev = op1b;
+      j->OutPt1 = op1;
+      j->OutPt2 = op1b;
+      return true;
+    } else
+    {
+      op1b = DupOutPt(op1, true);
+      op2b = DupOutPt(op2, false);
+      op1->Next = op2;
+      op2->Prev = op1;
+      op1b->Prev = op2b;
+      op2b->Next = op1b;
+      j->OutPt1 = op1;
+      j->OutPt2 = op1b;
+      return true;
+    }
+  } 
+  else if (isHorizontal)
+  {
+    //treat horizontal joins differently to non-horizontal joins since with
+    //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt
+    //may be anywhere along the horizontal edge.
+    op1b = op1;
+    while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2)
+      op1 = op1->Prev;
+    while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2)
+      op1b = op1b->Next;
+    if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon'
+
+    op2b = op2;
+    while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b)
+      op2 = op2->Prev;
+    while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1)
+      op2b = op2b->Next;
+    if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon'
+
+    cInt Left, Right;
+    //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges
+    if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right))
+      return false;
+
+    //DiscardLeftSide: when overlapping edges are joined, a spike will created
+    //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up
+    //on the discard Side as either may still be needed for other joins ...
+    IntPoint Pt;
+    bool DiscardLeftSide;
+    if (op1->Pt.X >= Left && op1->Pt.X <= Right) 
+    {
+      Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X);
+    } 
+    else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) 
+    {
+      Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X);
+    } 
+    else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right)
+    {
+      Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X;
+    } 
+    else
+    {
+      Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X);
+    }
+    j->OutPt1 = op1; j->OutPt2 = op2;
+    return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide);
+  } else
+  {
+    //nb: For non-horizontal joins ...
+    //    1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y
+    //    2. Jr.OutPt1.Pt > Jr.OffPt.Y
+
+    //make sure the polygons are correctly oriented ...
+    op1b = op1->Next;
+    while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next;
+    bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) ||
+      !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange));
+    if (Reverse1)
+    {
+      op1b = op1->Prev;
+      while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev;
+      if ((op1b->Pt.Y > op1->Pt.Y) ||
+        !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false;
+    };
+    op2b = op2->Next;
+    while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next;
+    bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) ||
+      !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange));
+    if (Reverse2)
+    {
+      op2b = op2->Prev;
+      while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev;
+      if ((op2b->Pt.Y > op2->Pt.Y) ||
+        !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false;
+    }
+
+    if ((op1b == op1) || (op2b == op2) || (op1b == op2b) ||
+      ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false;
+
+    if (Reverse1)
+    {
+      op1b = DupOutPt(op1, false);
+      op2b = DupOutPt(op2, true);
+      op1->Prev = op2;
+      op2->Next = op1;
+      op1b->Next = op2b;
+      op2b->Prev = op1b;
+      j->OutPt1 = op1;
+      j->OutPt2 = op1b;
+      return true;
+    } else
+    {
+      op1b = DupOutPt(op1, true);
+      op2b = DupOutPt(op2, false);
+      op1->Next = op2;
+      op2->Prev = op1;
+      op1b->Prev = op2b;
+      op2b->Next = op1b;
+      j->OutPt1 = op1;
+      j->OutPt2 = op1b;
+      return true;
+    }
+  }
+}
+//----------------------------------------------------------------------
+
+static OutRec* ParseFirstLeft(OutRec* FirstLeft)
+{
+  while (FirstLeft && !FirstLeft->Pts)
+    FirstLeft = FirstLeft->FirstLeft;
+  return FirstLeft;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec)
+{ 
+  //tests if NewOutRec contains the polygon before reassigning FirstLeft
+  for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+  {
+    OutRec* outRec = m_PolyOuts[i];
+    if (!outRec->Pts || !outRec->FirstLeft) continue;
+    OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft);
+    if (firstLeft == OldOutRec)
+    {
+      if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts))
+        outRec->FirstLeft = NewOutRec;
+    }
+  }
+}
+//----------------------------------------------------------------------
+
+void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec)
+{ 
+  //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon
+  for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+  {
+    OutRec* outRec = m_PolyOuts[i];
+    if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec;
+  }
+}
+//----------------------------------------------------------------------
+
+void Clipper::JoinCommonEdges()
+{
+  for (JoinList::size_type i = 0; i < m_Joins.size(); i++)
+  {
+    Join* join = m_Joins[i];
+
+    OutRec *outRec1 = GetOutRec(join->OutPt1->Idx);
+    OutRec *outRec2 = GetOutRec(join->OutPt2->Idx);
+
+    if (!outRec1->Pts || !outRec2->Pts) continue;
+    if (outRec1->IsOpen || outRec2->IsOpen) continue;
+
+    //get the polygon fragment with the correct hole state (FirstLeft)
+    //before calling JoinPoints() ...
+    OutRec *holeStateRec;
+    if (outRec1 == outRec2) holeStateRec = outRec1;
+    else if (Param1RightOfParam2(outRec1, outRec2)) holeStateRec = outRec2;
+    else if (Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1;
+    else holeStateRec = GetLowermostRec(outRec1, outRec2);
+
+    if (!JoinPoints(join, outRec1, outRec2)) continue;
+
+    if (outRec1 == outRec2)
+    {
+      //instead of joining two polygons, we've just created a new one by
+      //splitting one polygon into two.
+      outRec1->Pts = join->OutPt1;
+      outRec1->BottomPt = 0;
+      outRec2 = CreateOutRec();
+      outRec2->Pts = join->OutPt2;
+
+      //update all OutRec2.Pts Idx's ...
+      UpdateOutPtIdxs(*outRec2);
+
+      //We now need to check every OutRec.FirstLeft pointer. If it points
+      //to OutRec1 it may need to point to OutRec2 instead ...
+      if (m_UsingPolyTree)
+        for (PolyOutList::size_type j = 0; j < m_PolyOuts.size() - 1; j++)
+        {
+          OutRec* oRec = m_PolyOuts[j];
+          if (!oRec->Pts || ParseFirstLeft(oRec->FirstLeft) != outRec1 ||
+            oRec->IsHole == outRec1->IsHole) continue;
+          if (Poly2ContainsPoly1(oRec->Pts, join->OutPt2))
+            oRec->FirstLeft = outRec2;
+        }
+
+      if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts))
+      {
+        //outRec2 is contained by outRec1 ...
+        outRec2->IsHole = !outRec1->IsHole;
+        outRec2->FirstLeft = outRec1;
+
+        //fixup FirstLeft pointers that may need reassigning to OutRec1
+        if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1);
+
+        if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0))
+          ReversePolyPtLinks(outRec2->Pts);
+            
+      } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts))
+      {
+        //outRec1 is contained by outRec2 ...
+        outRec2->IsHole = outRec1->IsHole;
+        outRec1->IsHole = !outRec2->IsHole;
+        outRec2->FirstLeft = outRec1->FirstLeft;
+        outRec1->FirstLeft = outRec2;
+
+        //fixup FirstLeft pointers that may need reassigning to OutRec1
+        if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2);
+
+        if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0))
+          ReversePolyPtLinks(outRec1->Pts);
+      } 
+      else
+      {
+        //the 2 polygons are completely separate ...
+        outRec2->IsHole = outRec1->IsHole;
+        outRec2->FirstLeft = outRec1->FirstLeft;
+
+        //fixup FirstLeft pointers that may need reassigning to OutRec2
+        if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2);
+      }
+     
+    } else
+    {
+      //joined 2 polygons together ...
+
+      outRec2->Pts = 0;
+      outRec2->BottomPt = 0;
+      outRec2->Idx = outRec1->Idx;
+
+      outRec1->IsHole = holeStateRec->IsHole;
+      if (holeStateRec == outRec2) 
+        outRec1->FirstLeft = outRec2->FirstLeft;
+      outRec2->FirstLeft = outRec1;
+
+      //fixup FirstLeft pointers that may need reassigning to OutRec1
+      if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1);
+    }
+  }
+}
+
+//------------------------------------------------------------------------------
+// ClipperOffset support functions ...
+//------------------------------------------------------------------------------
+
+DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2)
+{
+  if(pt2.X == pt1.X && pt2.Y == pt1.Y) 
+    return DoublePoint(0, 0);
+
+  double Dx = (double)(pt2.X - pt1.X);
+  double dy = (double)(pt2.Y - pt1.Y);
+  double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy );
+  Dx *= f;
+  dy *= f;
+  return DoublePoint(dy, -Dx);
+}
+
+//------------------------------------------------------------------------------
+// ClipperOffset class
+//------------------------------------------------------------------------------
+
+ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance)
+{
+  this->MiterLimit = miterLimit;
+  this->ArcTolerance = arcTolerance;
+  m_lowest.X = -1;
+}
+//------------------------------------------------------------------------------
+
+ClipperOffset::~ClipperOffset()
+{
+  Clear();
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::Clear()
+{
+  for (int i = 0; i < m_polyNodes.ChildCount(); ++i)
+    delete m_polyNodes.Childs[i];
+  m_polyNodes.Childs.clear();
+  m_lowest.X = -1;
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType)
+{
+  int highI = (int)path.size() - 1;
+  if (highI < 0) return;
+  PolyNode* newNode = new PolyNode();
+  newNode->m_jointype = joinType;
+  newNode->m_endtype = endType;
+
+  //strip duplicate points from path and also get index to the lowest point ...
+  if (endType == etClosedLine || endType == etClosedPolygon)
+    while (highI > 0 && path[0] == path[highI]) highI--;
+  newNode->Contour.reserve(highI + 1);
+  newNode->Contour.push_back(path[0]);
+  int j = 0, k = 0;
+  for (int i = 1; i <= highI; i++)
+    if (newNode->Contour[j] != path[i])
+    {
+      j++;
+      newNode->Contour.push_back(path[i]);
+      if (path[i].Y > newNode->Contour[k].Y ||
+        (path[i].Y == newNode->Contour[k].Y &&
+        path[i].X < newNode->Contour[k].X)) k = j;
+    }
+  if (endType == etClosedPolygon && j < 2)
+  {
+    delete newNode;
+    return;
+  }
+  m_polyNodes.AddChild(*newNode);
+
+  //if this path's lowest pt is lower than all the others then update m_lowest
+  if (endType != etClosedPolygon) return;
+  if (m_lowest.X < 0)
+    m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k);
+  else
+  {
+    IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y];
+    if (newNode->Contour[k].Y > ip.Y ||
+      (newNode->Contour[k].Y == ip.Y &&
+      newNode->Contour[k].X < ip.X))
+      m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k);
+  }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType)
+{
+  for (Paths::size_type i = 0; i < paths.size(); ++i)
+    AddPath(paths[i], joinType, endType);
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::FixOrientations()
+{
+  //fixup orientations of all closed paths if the orientation of the
+  //closed path with the lowermost vertex is wrong ...
+  if (m_lowest.X >= 0 && 
+    !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour))
+  {
+    for (int i = 0; i < m_polyNodes.ChildCount(); ++i)
+    {
+      PolyNode& node = *m_polyNodes.Childs[i];
+      if (node.m_endtype == etClosedPolygon ||
+        (node.m_endtype == etClosedLine && Orientation(node.Contour)))
+          ReversePath(node.Contour);
+    }
+  } else
+  {
+    for (int i = 0; i < m_polyNodes.ChildCount(); ++i)
+    {
+      PolyNode& node = *m_polyNodes.Childs[i];
+      if (node.m_endtype == etClosedLine && !Orientation(node.Contour))
+        ReversePath(node.Contour);
+    }
+  }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::Execute(Paths& solution, double delta)
+{
+  solution.clear();
+  FixOrientations();
+  DoOffset(delta);
+  
+  //now clean up 'corners' ...
+  Clipper clpr;
+  clpr.AddPaths(m_destPolys, ptSubject, true);
+  if (delta > 0)
+  {
+    clpr.Execute(ctUnion, solution, pftPositive, pftPositive);
+  }
+  else
+  {
+    IntRect r = clpr.GetBounds();
+    Path outer(4);
+    outer[0] = IntPoint(r.left - 10, r.bottom + 10);
+    outer[1] = IntPoint(r.right + 10, r.bottom + 10);
+    outer[2] = IntPoint(r.right + 10, r.top - 10);
+    outer[3] = IntPoint(r.left - 10, r.top - 10);
+
+    clpr.AddPath(outer, ptSubject, true);
+    clpr.ReverseSolution(true);
+    clpr.Execute(ctUnion, solution, pftNegative, pftNegative);
+    if (!solution.empty()) solution.erase(solution.begin());
+  }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::Execute(PolyTree& solution, double delta)
+{
+  solution.Clear();
+  FixOrientations();
+  DoOffset(delta);
+
+  //now clean up 'corners' ...
+  Clipper clpr;
+  clpr.AddPaths(m_destPolys, ptSubject, true);
+  if (delta > 0)
+  {
+    clpr.Execute(ctUnion, solution, pftPositive, pftPositive);
+  }
+  else
+  {
+    IntRect r = clpr.GetBounds();
+    Path outer(4);
+    outer[0] = IntPoint(r.left - 10, r.bottom + 10);
+    outer[1] = IntPoint(r.right + 10, r.bottom + 10);
+    outer[2] = IntPoint(r.right + 10, r.top - 10);
+    outer[3] = IntPoint(r.left - 10, r.top - 10);
+
+    clpr.AddPath(outer, ptSubject, true);
+    clpr.ReverseSolution(true);
+    clpr.Execute(ctUnion, solution, pftNegative, pftNegative);
+    //remove the outer PolyNode rectangle ...
+    if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0)
+    {
+      PolyNode* outerNode = solution.Childs[0];
+      solution.Childs.reserve(outerNode->ChildCount());
+      solution.Childs[0] = outerNode->Childs[0];
+      solution.Childs[0]->Parent = outerNode->Parent;
+      for (int i = 1; i < outerNode->ChildCount(); ++i)
+        solution.AddChild(*outerNode->Childs[i]);
+    }
+    else
+      solution.Clear();
+  }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::DoOffset(double delta)
+{
+  m_destPolys.clear();
+  m_delta = delta;
+
+  //if Zero offset, just copy any CLOSED polygons to m_p and return ...
+  if (NEAR_ZERO(delta)) 
+  {
+    m_destPolys.reserve(m_polyNodes.ChildCount());
+    for (int i = 0; i < m_polyNodes.ChildCount(); i++)
+    {
+      PolyNode& node = *m_polyNodes.Childs[i];
+      if (node.m_endtype == etClosedPolygon)
+        m_destPolys.push_back(node.Contour);
+    }
+    return;
+  }
+
+  //see offset_triginometry3.svg in the documentation folder ...
+  if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit);
+  else m_miterLim = 0.5;
+
+  double y;
+  if (ArcTolerance <= 0.0) y = def_arc_tolerance;
+  else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) 
+    y = std::fabs(delta) * def_arc_tolerance;
+  else y = ArcTolerance;
+  //see offset_triginometry2.svg in the documentation folder ...
+  double steps = pi / std::acos(1 - y / std::fabs(delta));
+  if (steps > std::fabs(delta) * pi) 
+    steps = std::fabs(delta) * pi;  //ie excessive precision check
+  m_sin = std::sin(two_pi / steps);
+  m_cos = std::cos(two_pi / steps);
+  m_StepsPerRad = steps / two_pi;
+  if (delta < 0.0) m_sin = -m_sin;
+
+  m_destPolys.reserve(m_polyNodes.ChildCount() * 2);
+  for (int i = 0; i < m_polyNodes.ChildCount(); i++)
+  {
+    PolyNode& node = *m_polyNodes.Childs[i];
+    m_srcPoly = node.Contour;
+
+    int len = (int)m_srcPoly.size();
+    if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon)))
+        continue;
+
+    m_destPoly.clear();
+    if (len == 1)
+    {
+      if (node.m_jointype == jtRound)
+      {
+        double X = 1.0, Y = 0.0;
+        for (cInt j = 1; j <= steps; j++)
+        {
+          m_destPoly.push_back(IntPoint(
+            Round(m_srcPoly[0].X + X * delta),
+            Round(m_srcPoly[0].Y + Y * delta)));
+          double X2 = X;
+          X = X * m_cos - m_sin * Y;
+          Y = X2 * m_sin + Y * m_cos;
+        }
+      }
+      else
+      {
+        double X = -1.0, Y = -1.0;
+        for (int j = 0; j < 4; ++j)
+        {
+          m_destPoly.push_back(IntPoint(
+            Round(m_srcPoly[0].X + X * delta),
+            Round(m_srcPoly[0].Y + Y * delta)));
+          if (X < 0) X = 1;
+          else if (Y < 0) Y = 1;
+          else X = -1;
+        }
+      }
+      m_destPolys.push_back(m_destPoly);
+      continue;
+    }
+    //build m_normals ...
+    m_normals.clear();
+    m_normals.reserve(len);
+    for (int j = 0; j < len - 1; ++j)
+      m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1]));
+    if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon)
+      m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0]));
+    else
+      m_normals.push_back(DoublePoint(m_normals[len - 2]));
+
+    if (node.m_endtype == etClosedPolygon)
+    {
+      int k = len - 1;
+      for (int j = 0; j < len; ++j)
+        OffsetPoint(j, k, node.m_jointype);
+      m_destPolys.push_back(m_destPoly);
+    }
+    else if (node.m_endtype == etClosedLine)
+    {
+      int k = len - 1;
+      for (int j = 0; j < len; ++j)
+        OffsetPoint(j, k, node.m_jointype);
+      m_destPolys.push_back(m_destPoly);
+      m_destPoly.clear();
+      //re-build m_normals ...
+      DoublePoint n = m_normals[len -1];
+      for (int j = len - 1; j > 0; j--)
+        m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y);
+      m_normals[0] = DoublePoint(-n.X, -n.Y);
+      k = 0;
+      for (int j = len - 1; j >= 0; j--)
+        OffsetPoint(j, k, node.m_jointype);
+      m_destPolys.push_back(m_destPoly);
+    }
+    else
+    {
+      int k = 0;
+      for (int j = 1; j < len - 1; ++j)
+        OffsetPoint(j, k, node.m_jointype);
+
+      IntPoint pt1;
+      if (node.m_endtype == etOpenButt)
+      {
+        int j = len - 1;
+        pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X *
+          delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta));
+        m_destPoly.push_back(pt1);
+        pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X *
+          delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta));
+        m_destPoly.push_back(pt1);
+      }
+      else
+      {
+        int j = len - 1;
+        k = len - 2;
+        m_sinA = 0;
+        m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y);
+        if (node.m_endtype == etOpenSquare)
+          DoSquare(j, k);
+        else
+          DoRound(j, k);
+      }
+
+      //re-build m_normals ...
+      for (int j = len - 1; j > 0; j--)
+        m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y);
+      m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y);
+
+      k = len - 1;
+      for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype);
+
+      if (node.m_endtype == etOpenButt)
+      {
+        pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta),
+          (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta));
+        m_destPoly.push_back(pt1);
+        pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta),
+          (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta));
+        m_destPoly.push_back(pt1);
+      }
+      else
+      {
+        k = 1;
+        m_sinA = 0;
+        if (node.m_endtype == etOpenSquare)
+          DoSquare(0, 1);
+        else
+          DoRound(0, 1);
+      }
+      m_destPolys.push_back(m_destPoly);
+    }
+  }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype)
+{
+  //cross product ...
+  m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y);
+  if (std::fabs(m_sinA * m_delta) < 1.0) 
+  {
+    //dot product ...
+    double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); 
+    if (cosA > 0) // angle => 0 degrees
+    {
+      m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta),
+        Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta)));
+      return; 
+    }
+    //else angle => 180 degrees   
+  }
+  else if (m_sinA > 1.0) m_sinA = 1.0;
+  else if (m_sinA < -1.0) m_sinA = -1.0;
+
+  if (m_sinA * m_delta < 0)
+  {
+    m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta),
+      Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta)));
+    m_destPoly.push_back(m_srcPoly[j]);
+    m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta),
+      Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta)));
+  }
+  else
+    switch (jointype)
+    {
+      case jtMiter:
+        {
+          double r = 1 + (m_normals[j].X * m_normals[k].X +
+            m_normals[j].Y * m_normals[k].Y);
+          if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k);
+          break;
+        }
+      case jtSquare: DoSquare(j, k); break;
+      case jtRound: DoRound(j, k); break;
+    }
+  k = j;
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::DoSquare(int j, int k)
+{
+  double dx = std::tan(std::atan2(m_sinA,
+      m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4);
+  m_destPoly.push_back(IntPoint(
+      Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)),
+      Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx))));
+  m_destPoly.push_back(IntPoint(
+      Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)),
+      Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx))));
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::DoMiter(int j, int k, double r)
+{
+  double q = m_delta / r;
+  m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q),
+      Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q)));
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::DoRound(int j, int k)
+{
+  double a = std::atan2(m_sinA,
+  m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y);
+  int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1);
+
+  double X = m_normals[k].X, Y = m_normals[k].Y, X2;
+  for (int i = 0; i < steps; ++i)
+  {
+    m_destPoly.push_back(IntPoint(
+        Round(m_srcPoly[j].X + X * m_delta),
+        Round(m_srcPoly[j].Y + Y * m_delta)));
+    X2 = X;
+    X = X * m_cos - m_sin * Y;
+    Y = X2 * m_sin + Y * m_cos;
+  }
+  m_destPoly.push_back(IntPoint(
+  Round(m_srcPoly[j].X + m_normals[j].X * m_delta),
+  Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta)));
+}
+
+//------------------------------------------------------------------------------
+// Miscellaneous public functions
+//------------------------------------------------------------------------------
+
+void Clipper::DoSimplePolygons()
+{
+  PolyOutList::size_type i = 0;
+  while (i < m_PolyOuts.size()) 
+  {
+    OutRec* outrec = m_PolyOuts[i++];
+    OutPt* op = outrec->Pts;
+    if (!op || outrec->IsOpen) continue;
+    do //for each Pt in Polygon until duplicate found do ...
+    {
+      OutPt* op2 = op->Next;
+      while (op2 != outrec->Pts) 
+      {
+        if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) 
+        {
+          //split the polygon into two ...
+          OutPt* op3 = op->Prev;
+          OutPt* op4 = op2->Prev;
+          op->Prev = op4;
+          op4->Next = op;
+          op2->Prev = op3;
+          op3->Next = op2;
+
+          outrec->Pts = op;
+          OutRec* outrec2 = CreateOutRec();
+          outrec2->Pts = op2;
+          UpdateOutPtIdxs(*outrec2);
+          if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts))
+          {
+            //OutRec2 is contained by OutRec1 ...
+            outrec2->IsHole = !outrec->IsHole;
+            outrec2->FirstLeft = outrec;
+            if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec);
+          }
+          else
+            if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts))
+          {
+            //OutRec1 is contained by OutRec2 ...
+            outrec2->IsHole = outrec->IsHole;
+            outrec->IsHole = !outrec2->IsHole;
+            outrec2->FirstLeft = outrec->FirstLeft;
+            outrec->FirstLeft = outrec2;
+            if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2);
+            }
+            else
+          {
+            //the 2 polygons are separate ...
+            outrec2->IsHole = outrec->IsHole;
+            outrec2->FirstLeft = outrec->FirstLeft;
+            if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2);
+            }
+          op2 = op; //ie get ready for the Next iteration
+        }
+        op2 = op2->Next;
+      }
+      op = op->Next;
+    }
+    while (op != outrec->Pts);
+  }
+}
+//------------------------------------------------------------------------------
+
+void ReversePath(Path& p)
+{
+  std::reverse(p.begin(), p.end());
+}
+//------------------------------------------------------------------------------
+
+void ReversePaths(Paths& p)
+{
+  for (Paths::size_type i = 0; i < p.size(); ++i)
+    ReversePath(p[i]);
+}
+//------------------------------------------------------------------------------
+
+void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType)
+{
+  Clipper c;
+  c.StrictlySimple(true);
+  c.AddPath(in_poly, ptSubject, true);
+  c.Execute(ctUnion, out_polys, fillType, fillType);
+}
+//------------------------------------------------------------------------------
+
+void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType)
+{
+  Clipper c;
+  c.StrictlySimple(true);
+  c.AddPaths(in_polys, ptSubject, true);
+  c.Execute(ctUnion, out_polys, fillType, fillType);
+}
+//------------------------------------------------------------------------------
+
+void SimplifyPolygons(Paths &polys, PolyFillType fillType)
+{
+  SimplifyPolygons(polys, polys, fillType);
+}
+//------------------------------------------------------------------------------
+
+inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2)
+{
+  double Dx = ((double)pt1.X - pt2.X);
+  double dy = ((double)pt1.Y - pt2.Y);
+  return (Dx*Dx + dy*dy);
+}
+//------------------------------------------------------------------------------
+
+double DistanceFromLineSqrd(
+  const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2)
+{
+  //The equation of a line in general form (Ax + By + C = 0)
+  //given 2 points (x�,y�) & (x�,y�) is ...
+  //(y� - y�)x + (x� - x�)y + (y� - y�)x� - (x� - x�)y� = 0
+  //A = (y� - y�); B = (x� - x�); C = (y� - y�)x� - (x� - x�)y�
+  //perpendicular distance of point (x�,y�) = (Ax� + By� + C)/Sqrt(A� + B�)
+  //see http://en.wikipedia.org/wiki/Perpendicular_distance
+  double A = double(ln1.Y - ln2.Y);
+  double B = double(ln2.X - ln1.X);
+  double C = A * ln1.X  + B * ln1.Y;
+  C = A * pt.X + B * pt.Y - C;
+  return (C * C) / (A * A + B * B);
+}
+//---------------------------------------------------------------------------
+
+bool SlopesNearCollinear(const IntPoint& pt1, 
+    const IntPoint& pt2, const IntPoint& pt3, double distSqrd)
+{
+  //this function is more accurate when the point that's geometrically
+  //between the other 2 points is the one that's tested for distance.
+  //ie makes it more likely to pick up 'spikes' ...
+	if (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y))
+	{
+    if ((pt1.X > pt2.X) == (pt1.X < pt3.X))
+      return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd;
+    else if ((pt2.X > pt1.X) == (pt2.X < pt3.X))
+      return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;
+		else
+	    return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd;
+	}
+	else
+	{
+    if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y))
+      return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd;
+    else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y))
+      return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;
+		else
+      return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd;
+	}
+}
+//------------------------------------------------------------------------------
+
+bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd)
+{
+    double Dx = (double)pt1.X - pt2.X;
+    double dy = (double)pt1.Y - pt2.Y;
+    return ((Dx * Dx) + (dy * dy) <= distSqrd);
+}
+//------------------------------------------------------------------------------
+
+OutPt* ExcludeOp(OutPt* op)
+{
+  OutPt* result = op->Prev;
+  result->Next = op->Next;
+  op->Next->Prev = result;
+  result->Idx = 0;
+  return result;
+}
+//------------------------------------------------------------------------------
+
+void CleanPolygon(const Path& in_poly, Path& out_poly, double distance)
+{
+  //distance = proximity in units/pixels below which vertices
+  //will be stripped. Default ~= sqrt(2).
+  
+  size_t size = in_poly.size();
+  
+  if (size == 0) 
+  {
+    out_poly.clear();
+    return;
+  }
+
+  OutPt* outPts = new OutPt[size];
+  for (size_t i = 0; i < size; ++i)
+  {
+    outPts[i].Pt = in_poly[i];
+    outPts[i].Next = &outPts[(i + 1) % size];
+    outPts[i].Next->Prev = &outPts[i];
+    outPts[i].Idx = 0;
+  }
+
+  double distSqrd = distance * distance;
+  OutPt* op = &outPts[0];
+  while (op->Idx == 0 && op->Next != op->Prev) 
+  {
+    if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd))
+    {
+      op = ExcludeOp(op);
+      size--;
+    } 
+    else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd))
+    {
+      ExcludeOp(op->Next);
+      op = ExcludeOp(op);
+      size -= 2;
+    }
+    else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd))
+    {
+      op = ExcludeOp(op);
+      size--;
+    }
+    else
+    {
+      op->Idx = 1;
+      op = op->Next;
+    }
+  }
+
+  if (size < 3) size = 0;
+  out_poly.resize(size);
+  for (size_t i = 0; i < size; ++i)
+  {
+    out_poly[i] = op->Pt;
+    op = op->Next;
+  }
+  delete [] outPts;
+}
+//------------------------------------------------------------------------------
+
+void CleanPolygon(Path& poly, double distance)
+{
+  CleanPolygon(poly, poly, distance);
+}
+//------------------------------------------------------------------------------
+
+void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance)
+{
+  for (Paths::size_type i = 0; i < in_polys.size(); ++i)
+    CleanPolygon(in_polys[i], out_polys[i], distance);
+}
+//------------------------------------------------------------------------------
+
+void CleanPolygons(Paths& polys, double distance)
+{
+  CleanPolygons(polys, polys, distance);
+}
+//------------------------------------------------------------------------------
+
+void Minkowski(const Path& poly, const Path& path, 
+  Paths& solution, bool isSum, bool isClosed)
+{
+  int delta = (isClosed ? 1 : 0);
+  size_t polyCnt = poly.size();
+  size_t pathCnt = path.size();
+  Paths pp;
+  pp.reserve(pathCnt);
+  if (isSum)
+    for (size_t i = 0; i < pathCnt; ++i)
+    {
+      Path p;
+      p.reserve(polyCnt);
+      for (size_t j = 0; j < poly.size(); ++j)
+        p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y));
+      pp.push_back(p);
+    }
+  else
+    for (size_t i = 0; i < pathCnt; ++i)
+    {
+      Path p;
+      p.reserve(polyCnt);
+      for (size_t j = 0; j < poly.size(); ++j)
+        p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y));
+      pp.push_back(p);
+    }
+
+  solution.clear();
+  solution.reserve((pathCnt + delta) * (polyCnt + 1));
+  for (size_t i = 0; i < pathCnt - 1 + delta; ++i)
+    for (size_t j = 0; j < polyCnt; ++j)
+    {
+      Path quad;
+      quad.reserve(4);
+      quad.push_back(pp[i % pathCnt][j % polyCnt]);
+      quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]);
+      quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]);
+      quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]);
+      if (!Orientation(quad)) ReversePath(quad);
+      solution.push_back(quad);
+    }
+}
+//------------------------------------------------------------------------------
+
+void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed)
+{
+  Minkowski(pattern, path, solution, true, pathIsClosed);
+  Clipper c;
+  c.AddPaths(solution, ptSubject, true);
+  c.Execute(ctUnion, solution, pftNonZero, pftNonZero);
+}
+//------------------------------------------------------------------------------
+
+void TranslatePath(const Path& input, Path& output, const IntPoint delta)
+{
+  //precondition: input != output
+  output.resize(input.size());
+  for (size_t i = 0; i < input.size(); ++i)
+    output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y);
+}
+//------------------------------------------------------------------------------
+
+void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed)
+{
+  Clipper c;
+  for (size_t i = 0; i < paths.size(); ++i)
+  {
+    Paths tmp;
+    Minkowski(pattern, paths[i], tmp, true, pathIsClosed);
+    c.AddPaths(tmp, ptSubject, true);
+    if (pathIsClosed)
+    {
+      Path tmp2;
+      TranslatePath(paths[i], tmp2, pattern[0]);
+      c.AddPath(tmp2, ptClip, true);
+    }
+  }
+    c.Execute(ctUnion, solution, pftNonZero, pftNonZero);
+}
+//------------------------------------------------------------------------------
+
+void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution)
+{
+  Minkowski(poly1, poly2, solution, false, true);
+  Clipper c;
+  c.AddPaths(solution, ptSubject, true);
+  c.Execute(ctUnion, solution, pftNonZero, pftNonZero);
+}
+//------------------------------------------------------------------------------
+
+enum NodeType {ntAny, ntOpen, ntClosed};
+
+void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths)
+{
+  bool match = true;
+  if (nodetype == ntClosed) match = !polynode.IsOpen();
+  else if (nodetype == ntOpen) return;
+
+  if (!polynode.Contour.empty() && match)
+    paths.push_back(polynode.Contour);
+  for (int i = 0; i < polynode.ChildCount(); ++i)
+    AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths);
+}
+//------------------------------------------------------------------------------
+
+void PolyTreeToPaths(const PolyTree& polytree, Paths& paths)
+{
+  paths.resize(0); 
+  paths.reserve(polytree.Total());
+  AddPolyNodeToPaths(polytree, ntAny, paths);
+}
+//------------------------------------------------------------------------------
+
+void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths)
+{
+  paths.resize(0); 
+  paths.reserve(polytree.Total());
+  AddPolyNodeToPaths(polytree, ntClosed, paths);
+}
+//------------------------------------------------------------------------------
+
+void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths)
+{
+  paths.resize(0); 
+  paths.reserve(polytree.Total());
+  //Open paths are top level only, so ...
+  for (int i = 0; i < polytree.ChildCount(); ++i)
+    if (polytree.Childs[i]->IsOpen())
+      paths.push_back(polytree.Childs[i]->Contour);
+}
+//------------------------------------------------------------------------------
+
+std::ostream& operator <<(std::ostream &s, const IntPoint &p)
+{
+  s << "(" << p.X << "," << p.Y << ")";
+  return s;
+}
+//------------------------------------------------------------------------------
+
+std::ostream& operator <<(std::ostream &s, const Path &p)
+{
+  if (p.empty()) return s;
+  Path::size_type last = p.size() -1;
+  for (Path::size_type i = 0; i < last; i++)
+    s << "(" << p[i].X << "," << p[i].Y << "), ";
+  s << "(" << p[last].X << "," << p[last].Y << ")\n";
+  return s;
+}
+//------------------------------------------------------------------------------
+
+std::ostream& operator <<(std::ostream &s, const Paths &p)
+{
+  for (Paths::size_type i = 0; i < p.size(); i++)
+    s << p[i];
+  s << "\n";
+  return s;
+}
+//------------------------------------------------------------------------------
+
+} //ClipperLib namespace
diff --git a/cpp/clipper.hpp b/cpp/clipper.hpp
new file mode 100644
index 0000000..73cc06c
--- /dev/null
+++ b/cpp/clipper.hpp
@@ -0,0 +1,435 @@
+/*******************************************************************************
+*                                                                              *
+* Author    :  Angus Johnson                                                   *
+* Version   :  6.2.9                                                           *
+* Date      :  16 February 2015                                                *
+* Website   :  http://www.angusj.com                                           *
+* Copyright :  Angus Johnson 2010-2015                                         *
+*                                                                              *
+* License:                                                                     *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt                                         *
+*                                                                              *
+* Attributions:                                                                *
+* The code in this library is an extension of Bala Vatti's clipping algorithm: *
+* "A generic solution to polygon clipping"                                     *
+* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63.             *
+* http://portal.acm.org/citation.cfm?id=129906                                 *
+*                                                                              *
+* Computer graphics and geometric modeling: implementation and algorithms      *
+* By Max K. Agoston                                                            *
+* Springer; 1 edition (January 4, 2005)                                        *
+* http://books.google.com/books?q=vatti+clipping+agoston                       *
+*                                                                              *
+* See also:                                                                    *
+* "Polygon Offsetting by Computing Winding Numbers"                            *
+* Paper no. DETC2005-85513 pp. 565-575                                         *
+* ASME 2005 International Design Engineering Technical Conferences             *
+* and Computers and Information in Engineering Conference (IDETC/CIE2005)      *
+* September 24-28, 2005 , Long Beach, California, USA                          *
+* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf              *
+*                                                                              *
+*******************************************************************************/
+
+#ifndef clipper_hpp
+#define clipper_hpp
+
+#define CLIPPER_VERSION "6.2.6"
+
+//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
+//improve performance but coordinate values are limited to the range +/- 46340
+//#define use_int32
+
+//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance.
+//#define use_xyz
+
+//use_lines: Enables line clipping. Adds a very minor cost to performance.
+//#define use_lines
+  
+//use_deprecated: Enables temporary support for the obsolete functions
+//#define use_deprecated  
+
+#include <vector>
+#include <list>
+#include <set>
+#include <stdexcept>
+#include <cstring>
+#include <cstdlib>
+#include <ostream>
+#include <functional>
+#include <queue>
+#if defined(CLIPPER_IMPL_INCLUDE)
+#include CLIPPER_IMPL_INCLUDE
+#endif
+
+namespace ClipperLib {
+
+enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
+enum PolyType { ptSubject, ptClip };
+//By far the most widely used winding rules for polygon filling are
+//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
+//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
+//see http://glprogramming.com/red/chapter11.html
+enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
+
+#ifdef use_int32
+  typedef int cInt;
+  static cInt const loRange = 0x7FFF;
+  static cInt const hiRange = 0x7FFF;
+#else
+  typedef std::int64_t cInt;
+  static cInt const loRange = 0x3FFFFFFF;
+  static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
+  typedef signed long long long64;     //used by Int128 class
+  typedef unsigned long long ulong64;
+
+#endif
+
+#if defined(CLIPPER_INTPOINT_IMPL)
+
+typedef CLIPPER_INTPOINT_IMPL IntPoint;
+
+#else
+
+struct IntPoint {
+  cInt X;
+  cInt Y;
+#ifdef use_xyz
+  cInt Z;
+  IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {};
+#else
+  IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {};
+#endif
+
+  friend inline bool operator== (const IntPoint& a, const IntPoint& b)
+  {
+    return a.X == b.X && a.Y == b.Y;
+  }
+  friend inline bool operator!= (const IntPoint& a, const IntPoint& b)
+  {
+    return a.X != b.X  || a.Y != b.Y; 
+  }
+};
+#endif
+
+//------------------------------------------------------------------------------
+
+#if defined(CLIPPER_PATH_IMPL)
+
+typedef CLIPPER_PATH_IMPL Path;
+
+#else
+
+typedef std::vector< IntPoint > Path;
+
+#endif
+
+
+#if defined(CLIPPER_PATHS_IMPL)
+
+typedef CLIPPER_PATHS_IMPL Paths;
+
+#else
+
+typedef std::vector< Path > Paths;
+
+#endif
+
+
+
+inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
+inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
+
+std::ostream& operator <<(std::ostream &s, const IntPoint &p);
+std::ostream& operator <<(std::ostream &s, const Path &p);
+std::ostream& operator <<(std::ostream &s, const Paths &p);
+
+struct DoublePoint
+{
+  double X;
+  double Y;
+  DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}
+  DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {}
+};
+//------------------------------------------------------------------------------
+
+#ifdef use_xyz
+typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt);
+#endif
+
+enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
+enum JoinType {jtSquare, jtRound, jtMiter};
+enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
+
+class PolyNode;
+typedef std::vector< PolyNode* > PolyNodes;
+
+class PolyNode 
+{ 
+public:
+    PolyNode();
+    virtual ~PolyNode(){};
+    Path Contour;
+    PolyNodes Childs;
+    PolyNode* Parent;
+    PolyNode* GetNext() const;
+    bool IsHole() const;
+    bool IsOpen() const;
+    int ChildCount() const;
+private:
+    unsigned Index; //node index in Parent.Childs
+    bool m_IsOpen;
+    JoinType m_jointype;
+    EndType m_endtype;
+    PolyNode* GetNextSiblingUp() const;
+    void AddChild(PolyNode& child);
+    friend class Clipper; //to access Index
+    friend class ClipperOffset; 
+};
+
+class PolyTree: public PolyNode
+{ 
+public:
+    ~PolyTree(){Clear();};
+    PolyNode* GetFirst() const;
+    void Clear();
+    int Total() const;
+private:
+    PolyNodes AllNodes;
+    friend class Clipper; //to access AllNodes
+};
+
+bool Orientation(const Path &poly);
+double Area(const Path &poly);
+int PointInPolygon(const IntPoint &pt, const Path &path);
+
+void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
+void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
+void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
+
+void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
+void CleanPolygon(Path& poly, double distance = 1.415);
+void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415);
+void CleanPolygons(Paths& polys, double distance = 1.415);
+
+void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
+void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed);
+void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
+
+void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
+void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths);
+void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths);
+
+void ReversePath(Path& p);
+void ReversePaths(Paths& p);
+
+struct IntRect { cInt left; cInt top; cInt right; cInt bottom; };
+
+//enums that are used internally ...
+enum EdgeSide { esLeft = 1, esRight = 2};
+
+//forward declarations (for stuff used internally) ...
+struct TEdge;
+struct IntersectNode;
+struct LocalMinimum;
+struct OutPt;
+struct OutRec;
+struct Join;
+
+typedef std::vector < OutRec* > PolyOutList;
+typedef std::vector < TEdge* > EdgeList;
+typedef std::vector < Join* > JoinList;
+typedef std::vector < IntersectNode* > IntersectList;
+
+//------------------------------------------------------------------------------
+
+//ClipperBase is the ancestor to the Clipper class. It should not be
+//instantiated directly. This class simply abstracts the conversion of sets of
+//polygon coordinates into edge objects that are stored in a LocalMinima list.
+class ClipperBase
+{
+public:
+  ClipperBase();
+  virtual ~ClipperBase();
+  bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
+  bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);
+  virtual void Clear();
+  IntRect GetBounds();
+  bool PreserveCollinear() {return m_PreserveCollinear;};
+  void PreserveCollinear(bool value) {m_PreserveCollinear = value;};
+protected:
+  void DisposeLocalMinimaList();
+  TEdge* AddBoundsToLML(TEdge *e, bool IsClosed);
+  void PopLocalMinima();
+  virtual void Reset();
+  TEdge* ProcessBound(TEdge* E, bool IsClockwise);
+  TEdge* DescendToMin(TEdge *&E);
+  void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
+
+  typedef std::vector<LocalMinimum> MinimaList;
+  MinimaList::iterator m_CurrentLM;
+  MinimaList           m_MinimaList;
+
+  bool              m_UseFullRange;
+  EdgeList          m_edges;
+  bool             m_PreserveCollinear;
+  bool             m_HasOpenPaths;
+};
+//------------------------------------------------------------------------------
+
+class Clipper : public virtual ClipperBase
+{
+public:
+  Clipper(int initOptions = 0);
+  ~Clipper();
+  bool Execute(ClipType clipType,
+      Paths &solution,
+      PolyFillType fillType = pftEvenOdd);
+  bool Execute(ClipType clipType,
+      Paths &solution,
+      PolyFillType subjFillType,
+      PolyFillType clipFillType);
+  bool Execute(ClipType clipType,
+      PolyTree &polytree,
+      PolyFillType fillType = pftEvenOdd);
+  bool Execute(ClipType clipType,
+      PolyTree &polytree,
+      PolyFillType subjFillType,
+      PolyFillType clipFillType);
+  bool ReverseSolution() { return m_ReverseOutput; };
+  void ReverseSolution(bool value) {m_ReverseOutput = value;};
+  bool StrictlySimple() {return m_StrictSimple;};
+  void StrictlySimple(bool value) {m_StrictSimple = value;};
+  //set the callback function for z value filling on intersections (otherwise Z is 0)
+#ifdef use_xyz
+  void ZFillFunction(ZFillCallback zFillFunc);
+#endif
+protected:
+  void Reset();
+  virtual bool ExecuteInternal();
+private:
+  PolyOutList      m_PolyOuts;
+  JoinList         m_Joins;
+  JoinList         m_GhostJoins;
+  IntersectList    m_IntersectList;
+  ClipType         m_ClipType;
+  typedef std::priority_queue<cInt> ScanbeamList;
+  ScanbeamList     m_Scanbeam;
+  typedef std::list<cInt> MaximaList;
+  MaximaList       m_Maxima;
+  TEdge           *m_ActiveEdges;
+  TEdge           *m_SortedEdges;
+  bool             m_ExecuteLocked;
+  PolyFillType     m_ClipFillType;
+  PolyFillType     m_SubjFillType;
+  bool             m_ReverseOutput;
+  bool             m_UsingPolyTree; 
+  bool             m_StrictSimple;
+#ifdef use_xyz
+  ZFillCallback   m_ZFill; //custom callback 
+#endif
+  void SetWindingCount(TEdge& edge);
+  bool IsEvenOddFillType(const TEdge& edge) const;
+  bool IsEvenOddAltFillType(const TEdge& edge) const;
+  void InsertScanbeam(const cInt Y);
+  cInt PopScanbeam();
+  void InsertLocalMinimaIntoAEL(const cInt botY);
+  void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge);
+  void AddEdgeToSEL(TEdge *edge);
+  void CopyAELToSEL();
+  void DeleteFromSEL(TEdge *e);
+  void DeleteFromAEL(TEdge *e);
+  void UpdateEdgeIntoAEL(TEdge *&e);
+  void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
+  bool IsContributing(const TEdge& edge) const;
+  bool IsTopHorz(const cInt XPos);
+  void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
+  void DoMaxima(TEdge *e);
+  void ProcessHorizontals();
+  void ProcessHorizontal(TEdge *horzEdge);
+  void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
+  OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
+  OutRec* GetOutRec(int idx);
+  void AppendPolygon(TEdge *e1, TEdge *e2);
+  void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
+  OutRec* CreateOutRec();
+  OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
+  OutPt* GetLastOutPt(TEdge *e);
+  void DisposeAllOutRecs();
+  void DisposeOutRec(PolyOutList::size_type index);
+  bool ProcessIntersections(const cInt topY);
+  void BuildIntersectList(const cInt topY);
+  void ProcessIntersectList();
+  void ProcessEdgesAtTopOfScanbeam(const cInt topY);
+  void BuildResult(Paths& polys);
+  void BuildResult2(PolyTree& polytree);
+  void SetHoleState(TEdge *e, OutRec *outrec);
+  void DisposeIntersectNodes();
+  bool FixupIntersectionOrder();
+  void FixupOutPolygon(OutRec &outrec);
+  void FixupOutPolyline(OutRec &outrec);
+  bool IsHole(TEdge *e);
+  bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
+  void FixHoleLinkage(OutRec &outrec);
+  void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt);
+  void ClearJoins();
+  void ClearGhostJoins();
+  void AddGhostJoin(OutPt *op, const IntPoint offPt);
+  bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2);
+  void JoinCommonEdges();
+  void DoSimplePolygons();
+  void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
+  void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
+#ifdef use_xyz
+  void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
+#endif
+};
+//------------------------------------------------------------------------------
+
+class ClipperOffset 
+{
+public:
+  ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25);
+  ~ClipperOffset();
+  void AddPath(const Path& path, JoinType joinType, EndType endType);
+  void AddPaths(const Paths& paths, JoinType joinType, EndType endType);
+  void Execute(Paths& solution, double delta);
+  void Execute(PolyTree& solution, double delta);
+  void Clear();
+  double MiterLimit;
+  double ArcTolerance;
+private:
+  Paths m_destPolys;
+  Path m_srcPoly;
+  Path m_destPoly;
+  std::vector<DoublePoint> m_normals;
+  double m_delta, m_sinA, m_sin, m_cos;
+  double m_miterLim, m_StepsPerRad;
+  IntPoint m_lowest;
+  PolyNode m_polyNodes;
+
+  void FixOrientations();
+  void DoOffset(double delta);
+  void OffsetPoint(int j, int& k, JoinType jointype);
+  void DoSquare(int j, int k);
+  void DoMiter(int j, int k, double r);
+  void DoRound(int j, int k);
+};
+//------------------------------------------------------------------------------
+
+class clipperException : public std::exception
+{
+  public:
+    clipperException(const char* description): m_descr(description) {}
+    virtual ~clipperException() throw() {}
+    virtual const char* what() const throw() {return m_descr.c_str();}
+  private:
+    std::string m_descr;
+};
+//------------------------------------------------------------------------------
+
+} //ClipperLib namespace
+
+#endif //clipper_hpp
+
+
diff --git a/cpp/cpp_agg/agg_conv_clipper.h b/cpp/cpp_agg/agg_conv_clipper.h
new file mode 100644
index 0000000..15f66f9
--- /dev/null
+++ b/cpp/cpp_agg/agg_conv_clipper.h
@@ -0,0 +1,295 @@
+/*******************************************************************************
+*                                                                              *
+* Author    :  Angus Johnson                                                   *
+* Version   :  1.1                                                             *
+* Date      :  4 April 2011                                                    *
+* Website   :  http://www.angusj.com                                           *
+* Copyright :  Angus Johnson 2010-2011                                         *
+*                                                                              *
+* License:                                                                     *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt                                         *
+*                                                                              *
+*******************************************************************************/
+
+#ifndef AGG_CONV_CLIPPER_INCLUDED
+#define AGG_CONV_CLIPPER_INCLUDED
+
+#include <cmath>
+#include "agg_basics.h"
+#include "agg_array.h"
+#include "../clipper.hpp"
+
+namespace agg
+{
+  enum clipper_op_e { clipper_or,
+    clipper_and, clipper_xor, clipper_a_minus_b, clipper_b_minus_a };
+  enum clipper_PolyFillType {clipper_even_odd, clipper_non_zero, clipper_positive, clipper_negative};
+
+  template<class VSA, class VSB> class conv_clipper
+  {
+    enum status { status_move_to, status_line_to, status_stop };
+    typedef VSA source_a_type;
+    typedef VSB source_b_type;
+    typedef conv_clipper<source_a_type, source_b_type> self_type;
+
+  private:
+    source_a_type*							m_src_a;
+    source_b_type*							m_src_b;
+    status									m_status;
+    int										m_vertex;
+    int										m_contour;
+    int										m_scaling_factor;
+    clipper_op_e              m_operation;
+    pod_bvector<ClipperLib::IntPoint, 8>		m_vertex_accumulator;
+    ClipperLib::Paths         m_poly_a;
+    ClipperLib::Paths         m_poly_b;
+    ClipperLib::Paths         m_result;
+    ClipperLib::Clipper       m_clipper;
+    clipper_PolyFillType      m_subjFillType;
+    clipper_PolyFillType      m_clipFillType;
+
+    int Round(double val)
+    {
+    if ((val < 0)) return (int)(val - 0.5); else return (int)(val + 0.5);
+    }
+
+  public:
+    conv_clipper(source_a_type &a, source_b_type &b,
+      clipper_op_e op = clipper_or,
+      clipper_PolyFillType subjFillType = clipper_even_odd,
+      clipper_PolyFillType clipFillType = clipper_even_odd,
+	  int scaling_factor = 2) :
+        m_src_a(&a),
+        m_src_b(&b),
+        m_status(status_move_to),
+        m_vertex(-1),
+        m_contour(-1),
+        m_operation(op),
+        m_subjFillType(subjFillType),
+        m_clipFillType(clipFillType)
+    {
+		m_scaling_factor = std::max(std::min(scaling_factor, 6),0);
+		m_scaling_factor = Round(std::pow((double)10, m_scaling_factor));
+	}
+
+    ~conv_clipper()
+    {
+    }
+
+    void attach1(VSA &source, clipper_PolyFillType subjFillType = clipper_even_odd)
+      { m_src_a = &source; m_subjFillType = subjFillType; }
+    void attach2(VSB &source, clipper_PolyFillType clipFillType = clipper_even_odd)
+      { m_src_b = &source; m_clipFillType = clipFillType; }
+
+    void operation(clipper_op_e v) { m_operation = v; }
+
+    void rewind(unsigned path_id);
+    unsigned vertex(double* x, double* y);
+  
+    bool next_contour();
+    bool next_vertex(double* x, double* y);
+    void start_extracting();
+    void add_vertex_(double &x, double &y);
+    void end_contour(ClipperLib::Paths &p);
+
+	template<class VS> void add(VS &src, ClipperLib::Paths &p){
+		unsigned cmd;
+		double x; double y; double start_x; double start_y;
+		bool starting_first_line;
+
+		start_x = 0.0;
+		start_y = 0.0;
+		starting_first_line = true;
+		p.resize(0);
+
+		cmd = src->vertex( &x , &y );
+		while(!is_stop(cmd))
+		{
+		  if(is_vertex(cmd))
+		  {
+			if(is_move_to(cmd))
+			{
+			  if(!starting_first_line ) end_contour(p);
+			  start_x = x;
+			  start_y = y;
+			}
+			add_vertex_( x, y );
+			starting_first_line = false;
+		  }
+		  else if(is_end_poly(cmd))
+		  {
+			if(!starting_first_line && is_closed(cmd))
+			  add_vertex_( start_x, start_y );
+		  }
+		  cmd = src->vertex( &x, &y );
+		}
+		end_contour(p);
+	}
+  };
+
+  //------------------------------------------------------------------------
+
+  template<class VSA, class VSB> 
+  void conv_clipper<VSA, VSB>::start_extracting()
+  {
+    m_status = status_move_to;
+    m_contour = -1;
+    m_vertex = -1;
+  }
+  //------------------------------------------------------------------------------
+
+  template<class VSA, class VSB>
+  void conv_clipper<VSA, VSB>::rewind(unsigned path_id)
+  {
+    m_src_a->rewind( path_id );
+    m_src_b->rewind( path_id );
+
+    add( m_src_a , m_poly_a );
+    add( m_src_b , m_poly_b );
+    m_result.resize(0);
+
+    ClipperLib::PolyFillType pftSubj, pftClip;
+    switch (m_subjFillType)
+    {
+      case clipper_even_odd: pftSubj = ClipperLib::pftEvenOdd; break;
+      case clipper_non_zero: pftSubj = ClipperLib::pftNonZero; break;
+      case clipper_positive: pftSubj = ClipperLib::pftPositive; break;
+      default: pftSubj = ClipperLib::pftNegative;
+    }
+    switch (m_clipFillType)
+    {
+      case clipper_even_odd: pftClip = ClipperLib::pftEvenOdd; break;
+      case clipper_non_zero: pftClip = ClipperLib::pftNonZero; break;
+      case clipper_positive: pftClip = ClipperLib::pftPositive; break;
+      default: pftClip = ClipperLib::pftNegative;
+    }
+
+    m_clipper.Clear();
+    switch( m_operation ) {
+      case clipper_or:
+        {
+        m_clipper.AddPaths( m_poly_a , ClipperLib::ptSubject, true );
+        m_clipper.AddPaths( m_poly_b , ClipperLib::ptClip, true );
+        m_clipper.Execute( ClipperLib::ctUnion , m_result , pftSubj, pftClip);
+		break;
+        }
+      case clipper_and:
+        {
+        m_clipper.AddPaths( m_poly_a , ClipperLib::ptSubject, true );
+        m_clipper.AddPaths( m_poly_b , ClipperLib::ptClip, true );
+        m_clipper.Execute( ClipperLib::ctIntersection , m_result, pftSubj, pftClip );
+		break;
+        }
+      case clipper_xor:
+        {
+        m_clipper.AddPaths( m_poly_a , ClipperLib::ptSubject, true );
+        m_clipper.AddPaths( m_poly_b , ClipperLib::ptClip, true );
+        m_clipper.Execute( ClipperLib::ctXor , m_result, pftSubj, pftClip );
+		break;
+        }
+      case clipper_a_minus_b:
+        {
+        m_clipper.AddPaths( m_poly_a , ClipperLib::ptSubject, true );
+        m_clipper.AddPaths( m_poly_b , ClipperLib::ptClip, true );
+        m_clipper.Execute( ClipperLib::ctDifference , m_result, pftSubj, pftClip );
+		break;
+        }
+      case clipper_b_minus_a:
+        {
+        m_clipper.AddPaths( m_poly_b , ClipperLib::ptSubject, true );
+        m_clipper.AddPaths( m_poly_a , ClipperLib::ptClip, true );
+        m_clipper.Execute( ClipperLib::ctDifference , m_result, pftSubj, pftClip );
+		break;
+        }
+    }
+    start_extracting();
+  }
+  //------------------------------------------------------------------------------
+
+  template<class VSA, class VSB>
+  void conv_clipper<VSA, VSB>::end_contour( ClipperLib::Paths &p)
+  {
+  unsigned i, len;
+
+  if( m_vertex_accumulator.size() < 3 ) return;
+  len = p.size();
+  p.resize(len+1);
+  p[len].resize(m_vertex_accumulator.size());
+  for( i = 0 ; i < m_vertex_accumulator.size() ; i++ )
+    p[len][i] = m_vertex_accumulator[i];
+  m_vertex_accumulator.remove_all();
+  }
+  //------------------------------------------------------------------------------
+
+  template<class VSA, class VSB> 
+  void conv_clipper<VSA, VSB>::add_vertex_(double &x, double &y)
+  {
+	  ClipperLib::IntPoint v;
+
+	  v.X = Round(x * m_scaling_factor);
+	  v.Y = Round(y * m_scaling_factor);
+	  m_vertex_accumulator.add( v );
+  }
+  //------------------------------------------------------------------------------
+
+  template<class VSA, class VSB> 
+  bool conv_clipper<VSA, VSB>::next_contour()
+  {   
+	m_contour++;
+	if(m_contour >= (int)m_result.size()) return false;
+	m_vertex =-1;
+	return true;
+}
+//------------------------------------------------------------------------------
+
+  template<class VSA, class VSB> 
+  bool conv_clipper<VSA, VSB>::next_vertex(double *x, double *y)
+  {
+    m_vertex++;
+    if(m_vertex >= (int)m_result[m_contour].size()) return false;
+    *x = (double)m_result[ m_contour ][ m_vertex ].X / m_scaling_factor;
+    *y = (double)m_result[ m_contour ][ m_vertex ].Y / m_scaling_factor;
+    return true;
+  }
+  //------------------------------------------------------------------------------
+
+  template<class VSA, class VSB>
+  unsigned conv_clipper<VSA, VSB>::vertex(double *x, double *y)
+{
+  if(  m_status == status_move_to )
+  {
+    if( next_contour() )
+    {
+      if(  next_vertex( x, y ) )
+      {
+        m_status =status_line_to;
+        return path_cmd_move_to;
+      }
+	  else
+	  {
+        m_status = status_stop;
+        return path_cmd_end_poly | path_flags_close;
+      }
+    }
+	else
+      return path_cmd_stop;
+  }
+  else
+  {
+    if(  next_vertex( x, y ) )
+    {
+      return path_cmd_line_to;
+    }
+	else
+    {
+      m_status = status_move_to;
+      return path_cmd_end_poly | path_flags_close;
+    }
+  }
+}
+//------------------------------------------------------------------------------
+
+
+} //namespace agg
+#endif //AGG_CONV_CLIPPER_INCLUDED
diff --git a/cpp/cpp_agg/clipper_test.cpp b/cpp/cpp_agg/clipper_test.cpp
new file mode 100644
index 0000000..ffbaa64
--- /dev/null
+++ b/cpp/cpp_agg/clipper_test.cpp
@@ -0,0 +1,574 @@
+#include <stdio.h>
+#include "agg_basics.h"
+#include "agg_rendering_buffer.h"
+#include "agg_rasterizer_scanline_aa.h"
+#include "agg_scanline_u.h"
+#include "agg_scanline_p.h"
+#include "agg_renderer_scanline.h"
+#include "agg_renderer_primitives.h"
+#include "agg_conv_curve.h"
+#include "agg_conv_stroke.h"
+#include "agg_conv_clip_polygon.h"
+#include "agg_gsv_text.h"
+#include "agg_pixfmt_rgb.h"
+#include "agg_platform_support.h"
+
+#include "agg_slider_ctrl.h"
+#include "agg_cbox_ctrl.h"
+#include "agg_rbox_ctrl.h"
+
+#include "agg_conv_clipper.h"
+#include "windows.h"
+
+enum flip_y_e { flip_y = true };
+
+
+class spiral
+{
+public:
+    spiral(double x, double y, double r1, double r2, double step, double start_angle=0) :
+        m_x(x), 
+        m_y(y), 
+        m_r1(r1), 
+        m_r2(r2), 
+        m_step(step), 
+        m_start_angle(start_angle),
+        m_angle(start_angle),
+        m_da(agg::deg2rad(4.0)),
+        m_dr(m_step / 90.0)
+    {
+    }
+
+    void rewind(unsigned) 
+    { 
+        m_angle = m_start_angle; 
+        m_curr_r = m_r1; 
+        m_start = true; 
+    }
+
+    unsigned vertex(double* x, double* y)
+    {
+        if(m_curr_r > m_r2) return agg::path_cmd_stop;
+
+        *x = m_x + cos(m_angle) * m_curr_r;
+        *y = m_y + sin(m_angle) * m_curr_r;
+        m_curr_r += m_dr;
+        m_angle += m_da;
+        if(m_start) 
+        {
+            m_start = false;
+            return agg::path_cmd_move_to;
+        }
+        return agg::path_cmd_line_to;
+    }
+
+private:
+    double m_x;
+    double m_y;
+    double m_r1;
+    double m_r2;
+    double m_step;
+    double m_start_angle;
+
+    double m_angle;
+    double m_curr_r;
+    double m_da;
+    double m_dr;
+    bool   m_start;
+};
+
+
+
+namespace agg
+{
+    // A simple counter of points and contours
+    template<class Src> struct conv_poly_counter
+    {
+        unsigned m_contours;
+        unsigned m_points;
+    
+        conv_poly_counter(Src& src) : m_src(&src), m_contours(0), m_points(0) {}
+    
+        void rewind(unsigned path_id)
+        {
+            m_contours = 0;
+            m_points = 0;
+            m_src->rewind(path_id);
+        }
+
+        unsigned vertex(double* x, double* y)
+        {
+            unsigned cmd = m_src->vertex(x, y);
+            if(is_vertex(cmd))  ++m_points;
+            if(is_move_to(cmd)) ++m_contours;
+            return cmd;
+        }
+
+    private:
+        Src* m_src;
+    };
+}
+
+
+void make_gb_poly(agg::path_storage& ps);
+void make_arrows(agg::path_storage& ps);
+
+
+class the_application : public agg::platform_support
+{
+    agg::rbox_ctrl<agg::rgba8> m_polygons;
+    agg::rbox_ctrl<agg::rgba8> m_operation;
+    double m_x;
+    double m_y;
+
+	virtual void on_key(int x, int y, unsigned key, unsigned flags)
+	{
+		if(key == agg::key_escape) exit(0);
+		
+	}
+
+public:
+    the_application(agg::pix_format_e format, bool flip_y) :
+        agg::platform_support(format, flip_y),
+        m_polygons (5.0,     5.0, 5.0+205.0,  110.0,  !flip_y),
+        m_operation(555.0,   5.0, 555.0+80.0, 130.0,  !flip_y)
+    {
+        m_operation.add_item("None");
+        m_operation.add_item("OR");
+        m_operation.add_item("AND");
+        m_operation.add_item("XOR");
+        m_operation.add_item("A-B");
+        m_operation.add_item("B-A");
+        m_operation.cur_item(2);
+        add_ctrl(m_operation);
+
+        m_polygons.add_item("Two Simple Paths");
+        m_polygons.add_item("Closed Stroke");
+        m_polygons.add_item("Great Britain and Arrows");
+        m_polygons.add_item("Great Britain and Spiral");
+        m_polygons.add_item("Spiral and Glyph");
+        m_polygons.cur_item(3);
+        add_ctrl(m_polygons);
+    }
+
+
+    template<class Scanline, class Ras, class Ren, class Clp>
+    void perform_rendering(Scanline &sl, Ras &ras, Ren &ren, Clp &clp)
+    {
+        if(m_operation.cur_item() > 0)
+        {
+            ras.reset();
+            switch(m_operation.cur_item())
+            {
+                case 1: clp.operation(agg::clipper_or);           break;
+                case 2: clp.operation(agg::clipper_and);          break;
+                case 3: clp.operation(agg::clipper_xor);          break;
+                case 4: clp.operation(agg::clipper_a_minus_b);    break;
+                case 5: clp.operation(agg::clipper_b_minus_a);    break;
+            }
+            agg::conv_poly_counter<Clp> counter(clp);
+
+
+            start_timer();
+            counter.rewind(0);
+            double t1 = elapsed_time();
+
+			agg::path_storage ps;
+            double x;
+            double y;
+            unsigned cmd;
+            start_timer();
+            while(!agg::is_stop(cmd = counter.vertex(&x, &y)))
+            {
+				if(agg::is_move_to(cmd)) 
+					ps.move_to(x, y);
+				else if(agg::is_line_to(cmd))
+					ps.line_to(x, y);
+				else if(agg::is_close(cmd))
+					ps.close_polygon();
+			}
+			ras.add_path(ps);
+            ren.color(agg::rgba(0.25, 0.9, 0.25, 0.65));
+            agg::render_scanlines(ras, sl, ren);
+            double t2 = elapsed_time();
+
+            agg::conv_stroke<agg::path_storage> stroke(ps);
+            stroke.width(0.4);
+            ras.add_path(stroke);
+            ren.color(agg::rgba(0, 0, 0));
+            agg::render_scanlines(ras, sl, ren);
+
+            char buf[100];
+            sprintf_s(buf, "Contours: %d      Points: %d", counter.m_contours, counter.m_points);
+            agg::gsv_text txt;
+            agg::conv_stroke<agg::gsv_text> txt_stroke(txt);
+            txt_stroke.width(1.5);
+            txt_stroke.line_cap(agg::round_cap);
+            txt.size(10.0);
+            txt.start_point(250, 5);
+            txt.text(buf);
+            ras.add_path(txt_stroke);
+            ren.color(agg::rgba(0.0, 0.0, 0.0));
+            agg::render_scanlines(ras, sl, ren);
+
+            sprintf_s(buf, "Clipper=%.3fms Render=%.3fms", t1, t2);
+            txt.start_point(250, 20);
+            txt.text(buf);
+            ras.add_path(txt_stroke);
+            ren.color(agg::rgba(0.0, 0.0, 0.0));
+            agg::render_scanlines(ras, sl, ren);
+        }
+    }
+
+
+    template<class Scanline, class Ras>
+    unsigned render_clipper(Scanline& sl, Ras& ras)
+    {
+        agg::pixfmt_bgr24 pf(rbuf_window());
+        agg::renderer_base<agg::pixfmt_bgr24> rb(pf);
+        agg::renderer_scanline_aa_solid<agg::renderer_base<agg::pixfmt_bgr24> > ren(rb);
+
+
+        switch(m_polygons.cur_item())
+        {
+            case 0:
+            {
+                //------------------------------------
+                // Two simple paths
+                //
+                agg::path_storage ps1;
+                agg::path_storage ps2;
+
+                agg::conv_clipper<agg::path_storage, agg::path_storage> clp(ps1, ps2, agg::clipper_or, agg::clipper_non_zero, agg::clipper_non_zero);
+
+                double x = m_x - initial_width()/2 + 100;
+                double y = m_y - initial_height()/2 + 100;
+                ps1.move_to(x+140, y+145);
+                ps1.line_to(x+225, y+44);
+                ps1.line_to(x+296, y+219);
+                ps1.close_polygon();
+
+                ps1.line_to(x+226, y+289);
+                ps1.line_to(x+82,  y+292);
+
+                ps1.move_to(x+220, y+222);
+                ps1.line_to(x+363, y+249);
+                ps1.line_to(x+265, y+331);
+
+                ps1.move_to(x+242, y+243);
+                ps1.line_to(x+268, y+309);
+                ps1.line_to(x+325, y+261);
+
+                ps1.move_to(x+259, y+259);
+                ps1.line_to(x+273, y+288);
+                ps1.line_to(x+298, y+266);
+
+                ps2.move_to(100+32,  100+77);
+                ps2.line_to(100+473, 100+263);
+                ps2.line_to(100+351, 100+290);
+                ps2.line_to(100+354, 100+374);
+
+                ras.reset();
+                ras.add_path(ps1);
+                ren.color(agg::rgba(0, 0, 0, 0.1));
+                agg::render_scanlines(ras, sl, ren);
+
+                ras.reset();
+                ras.add_path(ps2);
+                ren.color(agg::rgba(0, 0.6, 0, 0.1));
+                agg::render_scanlines(ras, sl, ren);
+
+                perform_rendering(sl, ras, ren, clp);
+            }
+            break;
+
+            case 1:
+            {
+                //------------------------------------
+                // Closed stroke
+                //
+                agg::path_storage ps1;
+                agg::path_storage ps2;
+                agg::conv_stroke<agg::path_storage> stroke(ps2);
+                stroke.width(10.0);
+
+                agg::conv_clipper<agg::path_storage, 
+                              agg::conv_stroke<agg::path_storage> > clp(ps1, stroke, agg::clipper_or, agg::clipper_non_zero, agg::clipper_non_zero);
+
+
+                double x = m_x - initial_width()/2 + 100;
+                double y = m_y - initial_height()/2 + 100;
+                ps1.move_to(x+140, y+145);
+                ps1.line_to(x+225, y+44);
+                ps1.line_to(x+296, y+219);
+                ps1.close_polygon();
+
+                ps1.line_to(x+226, y+289);
+                ps1.line_to(x+82,  y+292);
+
+                ps1.move_to(x+220-50, y+222);
+                ps1.line_to(x+265-50, y+331);
+                ps1.line_to(x+363-50, y+249);
+                ps1.close_polygon(agg::path_flags_ccw);
+
+                ps2.move_to(100+32,  100+77);
+                ps2.line_to(100+473, 100+263);
+                ps2.line_to(100+351, 100+290);
+                ps2.line_to(100+354, 100+374);
+                ps2.close_polygon();
+
+                ras.reset();
+                ras.add_path(ps1);
+                ren.color(agg::rgba(0, 0, 0, 0.1));
+                agg::render_scanlines(ras, sl, ren);
+
+                ras.reset();
+                ras.add_path(stroke);
+                ren.color(agg::rgba(0, 0.6, 0, 0.1));
+                agg::render_scanlines(ras, sl, ren);
+
+                perform_rendering(sl, ras, ren, clp);
+            }
+            break;
+
+
+            case 2:
+            {
+                //------------------------------------
+                // Great Britain and Arrows
+                //
+                agg::path_storage gb_poly;
+                agg::path_storage arrows;
+                make_gb_poly(gb_poly);
+                make_arrows(arrows);
+
+                agg::trans_affine mtx1;
+                agg::trans_affine mtx2;
+                mtx1 *= agg::trans_affine_translation(-1150, -1150);
+                mtx1 *= agg::trans_affine_scaling(2.0);
+
+                mtx2 = mtx1;
+                mtx2 *= agg::trans_affine_translation(m_x - initial_width()/2, 
+                                                      m_y - initial_height()/2);
+
+                agg::conv_transform<agg::path_storage> trans_gb_poly(gb_poly, mtx1);
+                agg::conv_transform<agg::path_storage> trans_arrows(arrows, mtx2);
+
+                agg::conv_clipper<agg::conv_transform<agg::path_storage>, 
+                              agg::conv_transform<agg::path_storage> > clp(trans_gb_poly, trans_arrows, agg::clipper_or, agg::clipper_non_zero, agg::clipper_non_zero);
+
+                ras.add_path(trans_gb_poly);
+                ren.color(agg::rgba(0.5, 0.5, 0, 0.1));
+                agg::render_scanlines(ras, sl, ren);
+
+                agg::conv_stroke<agg::conv_transform<agg::path_storage> > stroke_gb_poly(trans_gb_poly);
+                stroke_gb_poly.width(0.1);
+                ras.add_path(stroke_gb_poly);
+                ren.color(agg::rgba(0, 0, 0));
+                agg::render_scanlines(ras, sl, ren);
+        
+                ras.add_path(trans_arrows);
+                ren.color(agg::rgba(0.0, 0.5, 0.5, 0.1));
+                agg::render_scanlines(ras, sl, ren);
+
+                perform_rendering(sl, ras, ren, clp);
+            }
+            break;
+
+
+            case 3:
+            {
+                //------------------------------------
+                // Great Britain and a Spiral
+                //
+                spiral sp(m_x, m_y, 10, 150, 30, 0.0);
+                agg::conv_stroke<spiral> stroke(sp);
+                stroke.width(15.0);
+
+                agg::path_storage gb_poly;
+                make_gb_poly(gb_poly);
+
+                agg::trans_affine mtx;
+                mtx *= agg::trans_affine_translation(-1150, -1150);
+                mtx *= agg::trans_affine_scaling(2.0);
+
+                agg::conv_transform<agg::path_storage> trans_gb_poly(gb_poly, mtx);
+
+                agg::conv_clipper<agg::conv_transform<agg::path_storage>, 
+                              agg::conv_stroke<spiral> > clp(trans_gb_poly, stroke, agg::clipper_or, agg::clipper_non_zero, agg::clipper_non_zero);
+
+                ras.add_path(trans_gb_poly);
+                ren.color(agg::rgba(0.5, 0.5, 0, 0.1));
+                agg::render_scanlines(ras, sl, ren);
+
+                agg::conv_stroke<agg::conv_transform<agg::path_storage> > stroke_gb_poly(trans_gb_poly);
+                stroke_gb_poly.width(0.1);
+                ras.add_path(stroke_gb_poly);
+                ren.color(agg::rgba(0, 0, 0));
+                agg::render_scanlines(ras, sl, ren);
+        
+                ras.add_path(stroke);
+                ren.color(agg::rgba(0.0, 0.5, 0.5, 0.1));
+                agg::render_scanlines(ras, sl, ren);
+
+                perform_rendering(sl, ras, ren, clp);
+            }
+            break;
+
+
+            case 4:
+            {
+                //------------------------------------
+                // Spiral and glyph
+                //
+                spiral sp(m_x, m_y, 10, 150, 30, 0.0);
+                agg::conv_stroke<spiral> stroke(sp);
+                stroke.width(15.0);
+
+                agg::path_storage glyph;
+                glyph.move_to(28.47, 6.45);
+                glyph.curve3(21.58, 1.12, 19.82, 0.29);
+                glyph.curve3(17.19, -0.93, 14.21, -0.93);
+                glyph.curve3(9.57, -0.93, 6.57, 2.25);
+                glyph.curve3(3.56, 5.42, 3.56, 10.60);
+                glyph.curve3(3.56, 13.87, 5.03, 16.26);
+                glyph.curve3(7.03, 19.58, 11.99, 22.51);
+                glyph.curve3(16.94, 25.44, 28.47, 29.64);
+                glyph.line_to(28.47, 31.40);
+                glyph.curve3(28.47, 38.09, 26.34, 40.58);
+                glyph.curve3(24.22, 43.07, 20.17, 43.07);
+                glyph.curve3(17.09, 43.07, 15.28, 41.41);
+                glyph.curve3(13.43, 39.75, 13.43, 37.60);
+                glyph.line_to(13.53, 34.77);
+                glyph.curve3(13.53, 32.52, 12.38, 31.30);
+                glyph.curve3(11.23, 30.08, 9.38, 30.08);
+                glyph.curve3(7.57, 30.08, 6.42, 31.35);
+                glyph.curve3(5.27, 32.62, 5.27, 34.81);
+                glyph.curve3(5.27, 39.01, 9.57, 42.53);
+                glyph.curve3(13.87, 46.04, 21.63, 46.04);
+                glyph.curve3(27.59, 46.04, 31.40, 44.04);
+                glyph.curve3(34.28, 42.53, 35.64, 39.31);
+                glyph.curve3(36.52, 37.21, 36.52, 30.71);
+                glyph.line_to(36.52, 15.53);
+                glyph.curve3(36.52, 9.13, 36.77, 7.69);
+                glyph.curve3(37.01, 6.25, 37.57, 5.76);
+                glyph.curve3(38.13, 5.27, 38.87, 5.27);
+                glyph.curve3(39.65, 5.27, 40.23, 5.62);
+                glyph.curve3(41.26, 6.25, 44.19, 9.18);
+                glyph.line_to(44.19, 6.45);
+                glyph.curve3(38.72, -0.88, 33.74, -0.88);
+                glyph.curve3(31.35, -0.88, 29.93, 0.78);
+                glyph.curve3(28.52, 2.44, 28.47, 6.45);
+                glyph.close_polygon();
+
+                glyph.move_to(28.47, 9.62);
+                glyph.line_to(28.47, 26.66);
+                glyph.curve3(21.09, 23.73, 18.95, 22.51);
+                glyph.curve3(15.09, 20.36, 13.43, 18.02);
+                glyph.curve3(11.77, 15.67, 11.77, 12.89);
+                glyph.curve3(11.77, 9.38, 13.87, 7.06);
+                glyph.curve3(15.97, 4.74, 18.70, 4.74);
+                glyph.curve3(22.41, 4.74, 28.47, 9.62);
+                glyph.close_polygon();
+
+                agg::trans_affine mtx;
+                mtx *= agg::trans_affine_scaling(4.0);
+                mtx *= agg::trans_affine_translation(220, 200);
+                agg::conv_transform<agg::path_storage> trans(glyph, mtx);
+                agg::conv_curve<agg::conv_transform<agg::path_storage> > curve(trans);
+
+                agg::conv_clipper<agg::conv_stroke<spiral>, 
+                                 agg::conv_curve<
+                                     agg::conv_transform<
+                                         agg::path_storage> > > clp(stroke, curve, agg::clipper_or, agg::clipper_non_zero, agg::clipper_non_zero);
+
+                ras.reset();
+                ras.add_path(stroke);
+                ren.color(agg::rgba(0, 0, 0, 0.1));
+                agg::render_scanlines(ras, sl, ren);
+
+                ras.reset();
+                ras.add_path(curve);
+                ren.color(agg::rgba(0, 0.6, 0, 0.1));
+                agg::render_scanlines(ras, sl, ren);
+
+                perform_rendering(sl, ras, ren, clp);
+            }
+            break;
+        }
+
+        return 0;
+    }
+
+
+    virtual void on_init()
+    {
+        m_x = width() / 2.0;
+        m_y = height() / 2.0;
+    }
+
+    virtual void on_draw()
+    {
+        typedef agg::renderer_base<agg::pixfmt_bgr24> base_ren_type;
+
+        agg::pixfmt_bgr24 pf(rbuf_window());
+        base_ren_type ren_base(pf);
+        ren_base.clear(agg::rgba(1,1,1));
+
+        agg::scanline_u8 sl;
+        agg::rasterizer_scanline_aa<> ras;
+
+        render_clipper(sl, ras);
+
+        agg::render_ctrl(ras, sl, ren_base, m_polygons);
+        agg::render_ctrl(ras, sl, ren_base, m_operation);
+    }
+
+    virtual void on_mouse_button_down(int x, int y, unsigned flags)
+    {
+        if(flags & agg::mouse_left)
+        {
+            m_x = x;
+            m_y = y;
+            force_redraw();
+        }
+    }
+
+
+    virtual void on_mouse_move(int x, int y, unsigned flags)
+    {
+        if(flags & agg::mouse_left)
+        {
+            m_x = x;
+            m_y = y;
+            force_redraw();
+        }
+    }
+
+
+
+};
+
+
+int agg_main(int argc, char* argv[])
+{
+    the_application app(agg::pix_format_bgr24, flip_y);
+    app.caption("AGG Example. Clipper");
+
+    if(app.init(640, 520, agg::window_resize))
+    {
+		//replace the main window icon with Resource Icon #1 ...
+		HWND w = GetActiveWindow();
+		HMODULE m = GetModuleHandle(0); //hInstance
+		HANDLE small_ico = LoadImage(m, MAKEINTRESOURCE(1), IMAGE_ICON, 16, 16, 0);
+		HANDLE big_ico = LoadImage(m, MAKEINTRESOURCE(1), IMAGE_ICON, 32, 32, 0);
+		SendMessage(w, WM_SETICON, ICON_SMALL, (LPARAM)small_ico);
+		SendMessage(w, WM_SETICON, ICON_BIG, (LPARAM)big_ico);
+
+		//main message loop ...
+		return app.run();
+    }
+    return 0;
+}
+
+
diff --git a/cpp/cpp_agg/clipper_test.sln b/cpp/cpp_agg/clipper_test.sln
new file mode 100644
index 0000000..2c46499
--- /dev/null
+++ b/cpp/cpp_agg/clipper_test.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual C++ Express 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "clipper_test", "clipper_test.vcxproj", "{4DF13A7A-6137-E76C-8C62-F591D0E4B69F}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{4DF13A7A-6137-E76C-8C62-F591D0E4B69F}.Debug|Win32.ActiveCfg = Debug|Win32
+		{4DF13A7A-6137-E76C-8C62-F591D0E4B69F}.Debug|Win32.Build.0 = Debug|Win32
+		{4DF13A7A-6137-E76C-8C62-F591D0E4B69F}.Release|Win32.ActiveCfg = Release|Win32
+		{4DF13A7A-6137-E76C-8C62-F591D0E4B69F}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/cpp/cpp_agg/clipper_test.vcxproj b/cpp/cpp_agg/clipper_test.vcxproj
new file mode 100644
index 0000000..9f4aaf2
--- /dev/null
+++ b/cpp/cpp_agg/clipper_test.vcxproj
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <Keyword>Win32Proj</Keyword>
+    <ProjectName>clipper_test</ProjectName>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <IncludePath>C:\Program Files (x86)\Borland\agg-2.5\include;C:\Program Files (x86)\Borland\agg-2.5\include\platform;C:\Program Files (x86)\Borland\agg-2.5\include\platform\win32;C:\Program Files (x86)\Borland\agg-2.5\include\ctrl;C:\Program Files (x86)\Borland\agg-2.5\include\util;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <IncludePath>C:\Program Files (x86)\Borland\agg-2.5\include;C:\Program Files (x86)\Borland\agg-2.5\include\platform;C:\Program Files (x86)\Borland\agg-2.5\include\platform\win32;C:\Program Files (x86)\Borland\agg-2.5\include\ctrl;C:\Program Files (x86)\Borland\agg-2.5\include\util;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <Optimization>Disabled</Optimization>
+    </ClCompile>
+    <Link>
+      <TargetMachine>MachineX86</TargetMachine>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Windows</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <TargetMachine>MachineX86</TargetMachine>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Windows</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\agg_src\examples\make_arrows.cpp" />
+    <ClCompile Include="..\agg_src\examples\make_gb_poly.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_arc.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_arrowhead.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_bezier_arc.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_bspline.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_curves.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_embedded_raster_fonts.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_gsv_text.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_image_filters.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_line_aa_basics.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_line_profile_aa.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_rounded_rect.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_sqrt_tables.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_trans_affine.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_trans_double_path.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_trans_single_path.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_trans_warp_magnifier.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_vcgen_bspline.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_vcgen_contour.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_vcgen_dash.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_vcgen_markers_term.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_vcgen_smooth_poly1.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_vcgen_stroke.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_vpgen_clip_polygon.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_vpgen_clip_polyline.cpp" />
+    <ClCompile Include="..\agg_src\src\agg_vpgen_segmentator.cpp" />
+    <ClCompile Include="..\agg_src\src\ctrl\agg_rbox_ctrl.cpp" />
+    <ClCompile Include="..\agg_src\src\platform\win32\agg_platform_support.cpp" />
+    <ClCompile Include="..\agg_src\src\platform\win32\agg_win32_bmp.cpp" />
+    <ClCompile Include="..\clipper.cpp" />
+    <ClCompile Include="clipper_test.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\clipper.hpp" />
+    <ClInclude Include="agg_conv_clipper.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/cpp/cpp_agg/clipper_test.vcxproj.filters b/cpp/cpp_agg/clipper_test.vcxproj.filters
new file mode 100644
index 0000000..6a12557
--- /dev/null
+++ b/cpp/cpp_agg/clipper_test.vcxproj.filters
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="clipper_test.cpp" />
+    <ClCompile Include="..\clipper.cpp">
+      <Filter>ClipperLib</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_arc.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_arrowhead.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_bezier_arc.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_bspline.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_curves.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_embedded_raster_fonts.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_gsv_text.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_image_filters.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_line_aa_basics.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_line_profile_aa.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_rounded_rect.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_sqrt_tables.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_trans_affine.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_trans_double_path.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_trans_single_path.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_trans_warp_magnifier.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_vcgen_bspline.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_vcgen_contour.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_vcgen_dash.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_vcgen_markers_term.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_vcgen_smooth_poly1.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_vcgen_stroke.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_vpgen_clip_polygon.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_vpgen_clip_polyline.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\agg_vpgen_segmentator.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\ctrl\agg_rbox_ctrl.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\platform\win32\agg_platform_support.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\src\platform\win32\agg_win32_bmp.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\examples\make_arrows.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\agg_src\examples\make_gb_poly.cpp">
+      <Filter>agg_src</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <Filter Include="agg_src">
+      <UniqueIdentifier>{31703eea-b46a-4b7c-93fe-7b3889404c6f}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="ClipperLib">
+      <UniqueIdentifier>{0d7823fd-c31f-4f5d-b1f5-3f3c91751bf3}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="agg_clipper">
+      <UniqueIdentifier>{0ea31d0f-7e25-4f22-964b-8c74b5ced2fd}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\clipper.hpp">
+      <Filter>ClipperLib</Filter>
+    </ClInclude>
+    <ClInclude Include="agg_conv_clipper.h">
+      <Filter>agg_clipper</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/cpp/cpp_agg/icon.res b/cpp/cpp_agg/icon.res
new file mode 100644
index 0000000..a4fc1a7
Binary files /dev/null and b/cpp/cpp_agg/icon.res differ
diff --git a/cpp/cpp_cairo/Cairo Resources.txt b/cpp/cpp_cairo/Cairo Resources.txt
new file mode 100644
index 0000000..e868eec
--- /dev/null
+++ b/cpp/cpp_cairo/Cairo Resources.txt	
@@ -0,0 +1,6 @@
+http://cairographics.org/
+
+The Windows dynamic linked libraries necessary to run Cairo can be downloaded from
+http://www.gtk.org/download/win32.php
+All the dlls listed under the heading "Required third party dependencies" are 
+required except gettext-runtime.dll.
diff --git a/cpp/cpp_cairo/cairo.sln b/cpp/cpp_cairo/cairo.sln
new file mode 100644
index 0000000..651e81a
--- /dev/null
+++ b/cpp/cpp_cairo/cairo.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual C++ Express 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cairo", "cairo.vcxproj", "{6AFBCA2B-9262-6D28-7506-E13747347388}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{6AFBCA2B-9262-6D28-7506-E13747347388}.Debug|Win32.ActiveCfg = Debug|Win32
+		{6AFBCA2B-9262-6D28-7506-E13747347388}.Debug|Win32.Build.0 = Debug|Win32
+		{6AFBCA2B-9262-6D28-7506-E13747347388}.Release|Win32.ActiveCfg = Release|Win32
+		{6AFBCA2B-9262-6D28-7506-E13747347388}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/cpp/cpp_cairo/cairo.vcxproj b/cpp/cpp_cairo/cairo.vcxproj
new file mode 100644
index 0000000..9816ce1
--- /dev/null
+++ b/cpp/cpp_cairo/cairo.vcxproj
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <Keyword>Win32Proj</Keyword>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <IncludePath>C:\Program Files %28x86%29\Borland\Clipper\PreReleaseTesting\cpp\cairo_src;$(IncludePath)</IncludePath>
+    <SourcePath>C:\Program Files %28x86%29\Borland\Clipper\PreReleaseTesting\cpp\cairo_src;C:\Program Files %28x86%29\Borland\graphics32\Examples\Vcl\Drawing\Clipper\PreReleaseTesting\cpp\cairo_src;$(SourcePath)</SourcePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>C:\Program Files (x86)\Borland\graphics32\Examples\Vcl\Drawing\Clipper\PreReleaseTesting\cpp\cairo_src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <Optimization>Disabled</Optimization>
+      <UseUnicodeForAssemblerListing>
+      </UseUnicodeForAssemblerListing>
+    </ClCompile>
+    <Link>
+      <TargetMachine>MachineX86</TargetMachine>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Windows</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>C:\Program Files (x86)\Borland\graphics32\Examples\Vcl\Drawing\Clipper\PreReleaseTesting\cpp\cairo_src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <TargetMachine>MachineX86</TargetMachine>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Windows</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\clipper.cpp" />
+    <ClCompile Include="cairo_clipper.cpp" />
+    <ClCompile Include="main.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\cairo_src\cairo.h" />
+    <ClInclude Include="..\clipper.hpp" />
+    <ClInclude Include="cairo_clipper.hpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <Library Include="libcairo-2.lib" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/cpp/cpp_cairo/cairo_clipper.cpp b/cpp/cpp_cairo/cairo_clipper.cpp
new file mode 100644
index 0000000..5767b9d
--- /dev/null
+++ b/cpp/cpp_cairo/cairo_clipper.cpp
@@ -0,0 +1,134 @@
+/*******************************************************************************
+*                                                                              *
+* Author    :  Angus Johnson                                                   *
+* Version   :  1.1                                                             *
+* Date      :  4 April 2011                                                    *
+* Copyright :  Angus Johnson 2010-2011                                         *
+*                                                                              *
+* License:                                                                     *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt                                         *
+*                                                                              *
+* Modified by Mike Owens to support coordinate transformation                  *
+*******************************************************************************/
+
+#include <stdexcept>
+#include <cmath>
+#include <cairo.h>
+#include "clipper.hpp"
+#include "cairo_clipper.hpp"
+
+namespace ClipperLib {
+  namespace cairo {
+
+    namespace {
+
+      inline cInt Round(double val)
+      {
+        if ((val < 0)) return (cInt)(val - 0.5); else return (cInt)(val + 0.5);
+      }
+
+      void transform_point(cairo_t* pen, Transform transform, cInt* x, cInt* y)
+      {
+        double _x = (double)*x, _y = (double)*y;
+        switch (transform)
+        {
+          case tDeviceToUser:
+            cairo_device_to_user(pen, &_x, &_y);
+            break;
+          case tUserToDevice:
+            cairo_user_to_device(pen, &_x, &_y);
+            break;
+          default:
+            ;
+        }
+        *x = Round(_x); *y = Round(_y);
+      }
+    }
+
+    void cairo_to_clipper(cairo_t* cr,
+                          Paths &pg,
+                          int scaling_factor,
+                          Transform transform)
+    {
+      if (scaling_factor > 8 || scaling_factor < 0)
+        throw clipperCairoException("cairo_to_clipper: invalid scaling factor");
+      double scaling = std::pow((double)10, scaling_factor);
+
+      pg.clear();
+      cairo_path_t *path = cairo_copy_path_flat(cr);
+
+      int poly_count = 0;
+      for (int i = 0; i < path->num_data; i += path->data[i].header.length) {
+        if( path->data[i].header.type == CAIRO_PATH_CLOSE_PATH) poly_count++;
+      }
+
+      pg.resize(poly_count);
+      int i = 0, pc = 0;
+      while (pc < poly_count)
+      {
+        int vert_count = 1;
+        int j = i;
+        while(j < path->num_data &&
+          path->data[j].header.type != CAIRO_PATH_CLOSE_PATH)
+        {
+          if (path->data[j].header.type == CAIRO_PATH_LINE_TO)
+            vert_count++;
+          j += path->data[j].header.length;
+        }
+        pg[pc].resize(vert_count);
+        if (path->data[i].header.type != CAIRO_PATH_MOVE_TO) {
+          pg.resize(pc);
+          break;
+        }
+        pg[pc][0].X = Round(path->data[i+1].point.x *scaling);
+        pg[pc][0].Y = Round(path->data[i+1].point.y *scaling);
+        if (transform != tNone)
+          transform_point(cr, transform, &pg[pc][0].X, &pg[pc][0].Y);
+
+        i += path->data[i].header.length;
+
+        j = 1;
+        while (j < vert_count && i < path->num_data &&
+          path->data[i].header.type == CAIRO_PATH_LINE_TO) {
+          pg[pc][j].X = Round(path->data[i+1].point.x *scaling);
+          pg[pc][j].Y = Round(path->data[i+1].point.y *scaling);
+          if (transform != tNone)
+            transform_point(cr, transform, &pg[pc][j].X, &pg[pc][j].Y);
+          j++;
+          i += path->data[i].header.length;
+        }
+        pc++;
+        i += path->data[i].header.length;
+      }
+      cairo_path_destroy(path);
+    }
+    //--------------------------------------------------------------------------
+
+    void clipper_to_cairo(const Paths &pg,
+                          cairo_t* cr,
+                          int scaling_factor,
+                          Transform transform)
+    {
+      if (scaling_factor > 8 || scaling_factor < 0)
+        throw clipperCairoException("clipper_to_cairo: invalid scaling factor");
+      double scaling = std::pow((double)10, scaling_factor);
+      for (size_t i = 0; i < pg.size(); ++i)
+      {
+        size_t sz = pg[i].size();
+        if (sz < 3)
+          continue;
+        cairo_new_sub_path(cr);
+        for (size_t j = 0; j < sz; ++j) {
+          cInt x = pg[i][j].X, y = pg[i][j].Y;
+          if (transform != tNone)
+            transform_point(cr, transform, &x, &y);
+          cairo_line_to(cr, (double)x / scaling, (double)y / scaling);
+        }
+        cairo_close_path(cr);
+      }
+    }
+    //--------------------------------------------------------------------------
+
+  }
+}
diff --git a/cpp/cpp_cairo/cairo_clipper.hpp b/cpp/cpp_cairo/cairo_clipper.hpp
new file mode 100644
index 0000000..7c52dfd
--- /dev/null
+++ b/cpp/cpp_cairo/cairo_clipper.hpp
@@ -0,0 +1,59 @@
+/*******************************************************************************
+*                                                                              *
+* Author    :  Angus Johnson                                                   *
+* Version   :  1.1                                                             *
+* Date      :  4 April 2011                                                    *
+* Copyright :  Angus Johnson 2010-2011                                         *
+*                                                                              *
+* License:                                                                     *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt                                         *
+*                                                                              *
+* Modified by Mike Owens to support coordinate transformation                  *
+*******************************************************************************/
+
+#ifndef CLIPPER_CAIRO_CLIPPER_HPP
+#define CLIPPER_CAIRO_CLIPPER_HPP
+
+#include "clipper.hpp"
+
+typedef struct _cairo cairo_t;
+
+namespace ClipperLib {
+  namespace cairo {
+
+    enum Transform {
+      tNone,
+      tUserToDevice,
+      tDeviceToUser
+    };
+
+//nb: Since Clipper only accepts integer coordinates, fractional values have to
+//be scaled up and down when being passed to and from Clipper. This is easily
+//accomplished by setting the scaling factor (10^x) in the following functions.
+//When scaling, remember that on most platforms, integer is only a 32bit value.
+    void cairo_to_clipper(cairo_t* cr,
+                          ClipperLib::Paths &pg,
+                          int scaling_factor = 0,
+                          Transform transform = tNone);
+
+    void clipper_to_cairo(const ClipperLib::Paths &pg,
+                          cairo_t* cr,
+                          int scaling_factor = 0,
+                          Transform transform = tNone);
+  }
+
+  class clipperCairoException : public std::exception
+  {
+    public:
+      clipperCairoException(const char* description)
+        throw(): std::exception(), m_description (description) {}
+      virtual ~clipperCairoException() throw() {}
+      virtual const char* what() const throw() {return m_description.c_str();}
+    private:
+      std::string m_description;
+  };
+}
+
+#endif
+
diff --git a/cpp/cpp_cairo/libcairo-2.lib b/cpp/cpp_cairo/libcairo-2.lib
new file mode 100644
index 0000000..9be9bc1
Binary files /dev/null and b/cpp/cpp_cairo/libcairo-2.lib differ
diff --git a/cpp/cpp_cairo/main.cpp b/cpp/cpp_cairo/main.cpp
new file mode 100644
index 0000000..b196a22
--- /dev/null
+++ b/cpp/cpp_cairo/main.cpp
@@ -0,0 +1,182 @@
+//---------------------------------------------------------------------------
+
+#include <windows.h>
+#include <cstring>
+#include <cmath>
+#include <sstream>
+#pragma hdrstop
+
+#include "clipper.hpp"
+#include "cairo.h"
+#include "cairo-win32.h"
+#include "cairo_clipper.hpp"
+//---------------------------------------------------------------------------
+
+int offsetVal;
+
+LRESULT CALLBACK WndProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+int CALLBACK wWinMain(HINSTANCE hInstance,
+  HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
+{
+  WCHAR* ClsName = L"CairoApp";
+  WCHAR* WndName = L"A Simple Cairo Clipper Demo";
+  offsetVal = 0;
+
+  MSG        Msg;
+  HWND       hWnd;
+  WNDCLASSEX WndClsEx;
+
+  // Create the application window
+  WndClsEx.cbSize        = sizeof(WNDCLASSEX);
+  WndClsEx.style         = CS_HREDRAW | CS_VREDRAW;
+  WndClsEx.lpfnWndProc   = WndProcedure;
+  WndClsEx.cbClsExtra    = 0;
+  WndClsEx.cbWndExtra    = 0;
+  WndClsEx.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
+  WndClsEx.hCursor       = LoadCursor(NULL, IDC_ARROW);
+  WndClsEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
+  WndClsEx.lpszMenuName  = NULL;
+  WndClsEx.lpszClassName = ClsName;
+  WndClsEx.hInstance     = hInstance;
+  WndClsEx.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
+
+  // Register the application
+  RegisterClassEx(&WndClsEx);
+
+  // Create the window object
+  hWnd = CreateWindow(ClsName, WndName, WS_OVERLAPPEDWINDOW,
+    CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
+    NULL, NULL, hInstance, NULL);
+  if( !hWnd ) return 0;
+
+  ShowWindow(hWnd, SW_SHOWNORMAL);
+  UpdateWindow(hWnd);
+
+  while( GetMessage(&Msg, NULL, 0, 0) )
+  {
+     TranslateMessage(&Msg);
+     DispatchMessage(&Msg);
+  }
+  return Msg.wParam;
+}
+//------------------------------------------------------------------------------
+
+
+void OnPaint(HWND hWnd, HDC dc)
+{
+  RECT rec;
+  GetClientRect(hWnd, &rec);
+  cairo_surface_t* surface = cairo_win32_surface_create(dc);
+  cairo_t* cr = cairo_create(surface);
+
+  cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
+  cairo_set_line_width (cr, 2.0);
+
+  //fill background with white ...
+  cairo_rectangle(cr, 0, 0, rec.right, rec.bottom);
+  cairo_set_source_rgba(cr, 1, 1, 1, 1);
+  cairo_fill(cr);
+
+  using namespace ClipperLib;
+
+  const int scaling = 2;
+
+  Clipper clpr;    //clipper class
+  Paths pg; //std::vector for polygon(s) storage
+
+  //create a circular pattern, add the path to clipper and then draw it ...
+  cairo_arc(cr, 170,110,70,0,2*3.1415926);
+  cairo_close_path(cr);
+  cairo::cairo_to_clipper(cr, pg, scaling);
+  clpr.AddPaths(pg, ptSubject, true);
+  cairo_set_source_rgba(cr, 0, 0, 1, 0.25);
+  cairo_fill_preserve (cr);
+  cairo_set_source_rgba(cr, 0, 0, 0, 0.5);
+  cairo_stroke (cr);
+
+  //draw a star and another circle, add them to clipper and draw ...
+  cairo_move_to(cr, 60,110);
+  cairo_line_to (cr, 240,70);
+  cairo_line_to (cr, 110,210);
+  cairo_line_to (cr, 140,25);
+  cairo_line_to (cr, 230,200);
+  cairo_close_path(cr);
+  cairo_new_sub_path(cr);
+  cairo_arc(cr, 190,50,20,0,2*3.1415926);
+  cairo_close_path(cr);
+  cairo::cairo_to_clipper(cr, pg, scaling);
+  clpr.AddPaths(pg, ptClip, true);
+  cairo_set_source_rgba(cr, 1, 0, 0, 0.25);
+  cairo_fill_preserve (cr);
+  cairo_set_source_rgba(cr, 0, 0, 0, 0.5);
+  cairo_stroke (cr);
+
+  clpr.Execute(ctIntersection, pg, pftNonZero, pftNonZero);
+  //now do something fancy with the returned polygons ...
+  OffsetPaths(pg, pg, offsetVal * std::pow((double)10,scaling), jtMiter, etClosed);
+
+  //finally copy the clipped path back to the cairo context and draw it ...
+  cairo::clipper_to_cairo(pg, cr, scaling);
+  cairo_set_source_rgba(cr, 1, 1, 0, 1);
+  cairo_fill_preserve (cr);
+  cairo_set_source_rgba(cr, 0, 0, 0, 1);
+  cairo_stroke (cr);
+
+  cairo_text_extents_t extent;
+  cairo_set_font_size(cr,11);
+  std::stringstream ss;
+  ss << "Polygon offset = " << offsetVal << ".  (Adjust with arrow keys)";
+  std::string s = ss.str();
+  cairo_text_extents(cr, s.c_str(), &extent);
+  cairo_move_to(cr, 10, rec.bottom - extent.height);
+  cairo_show_text(cr, s.c_str());
+
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface);
+}
+//------------------------------------------------------------------------------
+
+LRESULT CALLBACK WndProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
+{
+    PAINTSTRUCT ps;
+    HDC Handle;
+
+    switch(Msg)
+    {
+      case WM_DESTROY:
+        PostQuitMessage(WM_QUIT);
+        return 0;
+
+      case WM_PAINT:
+        Handle = BeginPaint(hWnd, &ps);
+        OnPaint(hWnd, Handle);
+        EndPaint(hWnd, &ps);
+        return 0;
+
+      case WM_KEYDOWN:
+        switch(wParam)
+        {
+          case VK_ESCAPE:
+            PostQuitMessage(0);
+            return 0;
+          case VK_RIGHT:
+          case VK_UP:
+            if (offsetVal < 20) offsetVal++;
+            InvalidateRect(hWnd, 0, false);
+            return 0;
+          case VK_LEFT:
+          case VK_DOWN:
+            if (offsetVal > -20) offsetVal--;
+            InvalidateRect(hWnd, 0, false);
+            return 0;
+          default:
+            return DefWindowProc(hWnd, Msg, wParam, lParam);
+        }
+
+      default:
+        return DefWindowProc(hWnd, Msg, wParam, lParam);
+    }
+}
+//---------------------------------------------------------------------------
+
diff --git a/cpp/cpp_console/clipper_console_demo.cpp b/cpp/cpp_console/clipper_console_demo.cpp
new file mode 100644
index 0000000..a63b78c
--- /dev/null
+++ b/cpp/cpp_console/clipper_console_demo.cpp
@@ -0,0 +1,461 @@
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <cmath>
+#include <algorithm>
+#include <ctime>
+#include <cstdlib>
+#include <cstdio>
+#include <vector>
+#include <iomanip>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include "clipper.hpp"
+
+using namespace std;
+using namespace ClipperLib;
+
+//---------------------------------------------------------------------------
+// SVGBuilder class
+// a very simple class that creates an SVG image file
+//---------------------------------------------------------------------------
+
+class SVGBuilder
+{
+  static string ColorToHtml(unsigned clr)
+  {
+    stringstream ss;
+    ss << '#' << hex << std::setfill('0') << setw(6) << (clr & 0xFFFFFF);
+    return ss.str();
+  }
+  //------------------------------------------------------------------------------
+
+  static float GetAlphaAsFrac(unsigned clr)
+  {
+    return ((float)(clr >> 24) / 255);
+  }
+  //------------------------------------------------------------------------------
+
+  class StyleInfo
+  {
+  public:
+  PolyFillType pft;
+  unsigned brushClr;
+  unsigned penClr;
+  double penWidth;
+  bool showCoords;
+
+  StyleInfo()
+  {
+    pft = pftNonZero;
+    brushClr = 0xFFFFFFCC;
+    penClr = 0xFF000000;
+    penWidth = 0.8;
+    showCoords = false;
+  }
+  };
+
+  class PolyInfo
+  {
+    public:
+      Paths paths;
+    StyleInfo si;
+
+      PolyInfo(Paths paths, StyleInfo style)
+      {
+          this->paths = paths;
+          this->si = style;
+      }
+  };
+
+  typedef std::vector<PolyInfo> PolyInfoList;
+
+private:
+  PolyInfoList polyInfos;
+  static const std::string svg_xml_start[];
+  static const std::string poly_end[];
+
+public:
+  StyleInfo style;
+
+  void AddPaths(Paths& poly)
+  {
+    if (poly.size() == 0) return;
+    polyInfos.push_back(PolyInfo(poly, style));
+  }
+
+  bool SaveToFile(const string& filename, double scale = 1.0, int margin = 10)
+  {
+    //calculate the bounding rect ...
+    PolyInfoList::size_type i = 0;
+    Paths::size_type j;
+    while (i < polyInfos.size())
+    {
+      j = 0;
+      while (j < polyInfos[i].paths.size() &&
+        polyInfos[i].paths[j].size() == 0) j++;
+      if (j < polyInfos[i].paths.size()) break;
+      i++;
+    }
+    if (i == polyInfos.size()) return false;
+
+    IntRect rec;
+    rec.left = polyInfos[i].paths[j][0].X;
+    rec.right = rec.left;
+    rec.top = polyInfos[i].paths[j][0].Y;
+    rec.bottom = rec.top;
+    for ( ; i < polyInfos.size(); ++i)
+      for (Paths::size_type j = 0; j < polyInfos[i].paths.size(); ++j)
+        for (Path::size_type k = 0; k < polyInfos[i].paths[j].size(); ++k)
+        {
+          IntPoint ip = polyInfos[i].paths[j][k];
+          if (ip.X < rec.left) rec.left = ip.X;
+          else if (ip.X > rec.right) rec.right = ip.X;
+          if (ip.Y < rec.top) rec.top = ip.Y;
+          else if (ip.Y > rec.bottom) rec.bottom = ip.Y;
+        }
+
+    if (scale == 0) scale = 1.0;
+    if (margin < 0) margin = 0;
+    rec.left = (cInt)((double)rec.left * scale);
+    rec.top = (cInt)((double)rec.top * scale);
+    rec.right = (cInt)((double)rec.right * scale);
+    rec.bottom = (cInt)((double)rec.bottom * scale);
+    cInt offsetX = -rec.left + margin;
+    cInt offsetY = -rec.top + margin;
+
+    ofstream file;
+    file.open(filename);
+    if (!file.is_open()) return false;
+    file.setf(ios::fixed);
+    file.precision(0);
+    file << svg_xml_start[0] <<
+      ((rec.right - rec.left) + margin*2) << "px" << svg_xml_start[1] <<
+      ((rec.bottom - rec.top) + margin*2) << "px" << svg_xml_start[2] <<
+      ((rec.right - rec.left) + margin*2) << " " <<
+      ((rec.bottom - rec.top) + margin*2) << svg_xml_start[3];
+    setlocale(LC_NUMERIC, "C");
+    file.precision(2);
+
+    for (PolyInfoList::size_type i = 0; i < polyInfos.size(); ++i)
+  {
+      file << " <path d=\"";
+    for (Paths::size_type j = 0; j < polyInfos[i].paths.size(); ++j)
+      {
+        if (polyInfos[i].paths[j].size() < 3) continue;
+        file << " M " << ((double)polyInfos[i].paths[j][0].X * scale + offsetX) <<
+          " " << ((double)polyInfos[i].paths[j][0].Y * scale + offsetY);
+        for (Path::size_type k = 1; k < polyInfos[i].paths[j].size(); ++k)
+        {
+          IntPoint ip = polyInfos[i].paths[j][k];
+          double x = (double)ip.X * scale;
+          double y = (double)ip.Y * scale;
+          file << " L " << (x + offsetX) << " " << (y + offsetY);
+        }
+        file << " z";
+    }
+      file << poly_end[0] << ColorToHtml(polyInfos[i].si.brushClr) <<
+    poly_end[1] << GetAlphaAsFrac(polyInfos[i].si.brushClr) <<
+        poly_end[2] <<
+        (polyInfos[i].si.pft == pftEvenOdd ? "evenodd" : "nonzero") <<
+        poly_end[3] << ColorToHtml(polyInfos[i].si.penClr) <<
+    poly_end[4] << GetAlphaAsFrac(polyInfos[i].si.penClr) <<
+        poly_end[5] << polyInfos[i].si.penWidth << poly_end[6];
+
+        if (polyInfos[i].si.showCoords)
+        {
+      file << "<g font-family=\"Verdana\" font-size=\"11\" fill=\"black\">\n\n";
+      for (Paths::size_type j = 0; j < polyInfos[i].paths.size(); ++j)
+      {
+        if (polyInfos[i].paths[j].size() < 3) continue;
+        for (Path::size_type k = 0; k < polyInfos[i].paths[j].size(); ++k)
+        {
+          IntPoint ip = polyInfos[i].paths[j][k];
+          file << "<text x=\"" << (int)(ip.X * scale + offsetX) <<
+          "\" y=\"" << (int)(ip.Y * scale + offsetY) << "\">" <<
+          ip.X << "," << ip.Y << "</text>\n";
+          file << "\n";
+        }
+      }
+      file << "</g>\n";
+        }
+    }
+    file << "</svg>\n";
+    file.close();
+    setlocale(LC_NUMERIC, "");
+    return true;
+  }
+}; //SVGBuilder
+//------------------------------------------------------------------------------
+
+const std::string SVGBuilder::svg_xml_start [] =
+  {"<?xml version=\"1.0\" standalone=\"no\"?>\n"
+    "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"\n"
+    "\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n\n"
+    "<svg width=\"",
+    "\" height=\"",
+    "\" viewBox=\"0 0 ",
+    "\" version=\"1.0\" xmlns=\"http://www.w3.org/2000/svg\">\n\n"
+  };
+const std::string SVGBuilder::poly_end [] =
+  {"\"\n style=\"fill:",
+    "; fill-opacity:",
+    "; fill-rule:",
+    "; stroke:",
+    "; stroke-opacity:",
+    "; stroke-width:",
+    ";\"/>\n\n"
+  };
+
+//------------------------------------------------------------------------------
+// Miscellaneous function ...
+//------------------------------------------------------------------------------
+
+bool SaveToFile(const string& filename, Paths &ppg, double scale = 1.0, unsigned decimal_places = 0)
+{
+  ofstream ofs(filename);
+  if (!ofs) return false;
+
+  if (decimal_places > 8) decimal_places = 8;
+  ofs << setprecision(decimal_places) << std::fixed;
+
+  Path pg;
+  for (size_t i = 0; i < ppg.size(); ++i)
+  {
+    for (size_t j = 0; j < ppg[i].size(); ++j)
+      ofs << ppg[i][j].X / scale << ", " << ppg[i][j].Y / scale << "," << std::endl;
+    ofs << std::endl;
+  }
+  ofs.close();
+  return true;
+}
+//------------------------------------------------------------------------------
+
+bool LoadFromFile(Paths &ppg, const string& filename, double scale)
+{
+  //file format assumes: 
+  //  1. path coordinates (x,y) are comma separated (+/- spaces) and 
+  //  each coordinate is on a separate line
+  //  2. each path is separated by one or more blank lines
+
+  ppg.clear();
+  ifstream ifs(filename);
+  if (!ifs) return false;
+  string line;
+  Path pg;
+  while (std::getline(ifs, line))
+  {
+    stringstream ss(line);
+    double X = 0.0, Y = 0.0;
+    if (!(ss >> X))
+    {
+      //ie blank lines => flag start of next polygon 
+      if (pg.size() > 0) ppg.push_back(pg);
+      pg.clear();
+      continue;
+    }
+    char c = ss.peek();  
+    while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces before comma
+    if (c == ',') {ss.read(&c, 1); c = ss.peek();} //gobble comma
+    while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces after comma
+    if (!(ss >> Y)) break; //oops!
+    pg.push_back(IntPoint((cInt)(X * scale),(cInt)(Y * scale)));
+  }
+  if (pg.size() > 0) ppg.push_back(pg);
+  ifs.close();
+  return true;
+}
+//------------------------------------------------------------------------------
+
+void MakeRandomPoly(int edgeCount, int width, int height, Paths & poly)
+{
+  poly.resize(1);
+  poly[0].resize(edgeCount);
+  for (int i = 0; i < edgeCount; i++){
+    poly[0][i].X = rand() % width;
+    poly[0][i].Y = rand() % height;
+  }
+}
+//------------------------------------------------------------------------------
+
+bool ASCII_icompare(const char* str1, const char* str2)
+{
+  //case insensitive compare for ASCII chars only
+  while (*str1) 
+  {
+    if (toupper(*str1) != toupper(*str2)) return false;
+    str1++;
+    str2++;
+  }
+  return (!*str2);
+}
+
+//------------------------------------------------------------------------------
+// Main entry point ...
+//------------------------------------------------------------------------------
+
+int main(int argc, char* argv[])
+{
+  if (argc > 1 &&
+    (strcmp(argv[1], "-b") == 0 || strcmp(argv[1], "--benchmark") == 0))
+  {
+    //do a benchmark test that creates a subject and a clip polygon both with
+    //100 vertices randomly placed in a 400 * 400 space. Then perform an
+    //intersection operation based on even-odd filling. Repeat all this X times.
+    int loop_cnt = 1000;
+    char * dummy;
+    if (argc > 2) loop_cnt = strtol(argv[2], &dummy, 10);
+    if (loop_cnt == 0) loop_cnt = 1000;
+    cout << "\nPerforming " << loop_cnt << " random intersection operations ... ";
+    srand((int)time(0));
+    int error_cnt = 0;
+    Paths subject, clip, solution;
+    Clipper clpr;
+
+    time_t time_start = clock();
+    for (int i = 0; i < loop_cnt; i++) {
+      MakeRandomPoly(100, 400, 400, subject);
+      MakeRandomPoly(100, 400, 400, clip);
+      clpr.Clear();
+      clpr.AddPaths(subject, ptSubject, true);
+      clpr.AddPaths(clip, ptClip, true);
+      if (!clpr.Execute(ctIntersection, solution, pftEvenOdd, pftEvenOdd))
+        error_cnt++;
+    }
+    double time_elapsed = double(clock() - time_start)/CLOCKS_PER_SEC;
+
+    cout << "\nFinished in " << time_elapsed << " secs with ";
+    cout << error_cnt << " errors.\n\n";
+    //let's save the very last result ...
+    SaveToFile("Subject.txt", subject);
+    SaveToFile("Clip.txt", clip);
+    SaveToFile("Solution.txt", solution);
+
+    //and see the final clipping op as an image too ...
+    SVGBuilder svg;
+    svg.style.penWidth = 0.8;
+    svg.style.pft = pftEvenOdd;
+    svg.style.brushClr = 0x1200009C;
+    svg.style.penClr = 0xCCD3D3DA;
+    svg.AddPaths(subject);
+    svg.style.brushClr = 0x129C0000;
+    svg.style.penClr = 0xCCFFA07A;
+    svg.AddPaths(clip);
+    svg.style.brushClr = 0x6080ff9C;
+    svg.style.penClr = 0xFF003300;
+    svg.style.pft = pftNonZero;
+    svg.AddPaths(solution);
+    svg.SaveToFile("solution.svg");
+    return 0;
+  }
+
+  if (argc < 3)
+  {
+    cout << "\nUsage:\n"
+      << "  clipper_console_demo S_FILE C_FILE CT [S_FILL C_FILL] [PRECISION] [SVG_SCALE]\n"
+      << "or\n"
+      << "  clipper_console_demo --benchmark [LOOP_COUNT]\n\n"
+      << "Legend: [optional parameters in square braces]; {comments in curly braces}\n\n"
+      << "Parameters:\n"
+      << "  S_FILE & C_FILE are the subject and clip input files (see format below)\n"
+      << "  CT: cliptype, either INTERSECTION or UNION or DIFFERENCE or XOR\n"
+      << "  SUBJECT_FILL & CLIP_FILL: either EVENODD or NONZERO. Default: NONZERO\n"
+      << "  PRECISION (in decimal places) for input data. Default = 0\n"
+      << "  SVG_SCALE: scale of the output svg image. Default = 1.0\n"
+      << "  LOOP_COUNT is the number of random clipping operations. Default = 1000\n\n"
+      << "\nFile format for input and output files:\n"
+      << "  X, Y[,] {first vertex of first path}\n"
+      << "  X, Y[,] {next vertex of first path}\n"
+      << "  {etc.}\n"
+      << "  X, Y[,] {last vertex of first path}\n"
+      << "  {blank line(s) between paths}\n"
+      << "  X, Y[,] {first vertex of second path}\n"
+      << "  X, Y[,] {next vertex of second path}\n"
+      << "  {etc.}\n\n"
+      << "Examples:\n"
+      << "  clipper_console_demo \"subj.txt\" \"clip.txt\" INTERSECTION EVENODD EVENODD\n"
+      << "  clipper_console_demo --benchmark 1000\n";
+    return 1;
+  }
+
+  int scale_log10 = 0;
+  char* dummy;
+  if (argc > 6) scale_log10 = strtol(argv[6], &dummy, 10);
+  double scale = std::pow(double(10), scale_log10);
+
+  double svg_scale = 1.0;
+  if (argc > 7) svg_scale = strtod(argv[7], &dummy);
+  svg_scale /= scale;
+
+  Paths subject, clip;
+
+  if (!LoadFromFile(subject, argv[1], scale))
+  {
+    cerr << "\nCan't open the file " << argv[1]
+      << " or the file format is invalid.\n";
+    return 1;
+  }
+  if (!LoadFromFile(clip, argv[2], scale))
+  {
+    cerr << "\nCan't open the file " << argv[2]
+      << " or the file format is invalid.\n";
+    return 1;
+  }
+
+  ClipType clipType = ctIntersection;
+  const string sClipType[] = {"INTERSECTION", "UNION", "DIFFERENCE", "XOR"};
+
+  if (argc > 3)
+  {
+    if (ASCII_icompare(argv[3], "XOR")) clipType = ctXor;
+    else if (ASCII_icompare(argv[3], "UNION")) clipType = ctUnion;
+    else if (ASCII_icompare(argv[3], "DIFFERENCE")) clipType = ctDifference;
+    else clipType = ctIntersection;
+  }
+
+  PolyFillType subj_pft = pftNonZero, clip_pft = pftNonZero;
+  if (argc > 5)
+  {
+    if (ASCII_icompare(argv[4], "EVENODD")) subj_pft = pftEvenOdd;
+    if (ASCII_icompare(argv[5], "EVENODD")) clip_pft = pftEvenOdd;
+  }
+
+  Clipper c;
+  c.AddPaths(subject, ptSubject, true);
+  c.AddPaths(clip, ptClip, true);
+  Paths solution;
+
+  if (!c.Execute(clipType, solution, subj_pft, clip_pft)) 
+  {
+    cout << (sClipType[clipType] + " failed!\n\n");
+    return 1;
+  }
+
+  cout << "\nFinished!\n\n";
+  SaveToFile("solution.txt", solution, scale);
+
+  //let's see the result too ...
+  SVGBuilder svg;
+  svg.style.penWidth = 0.8;
+  svg.style.brushClr = 0x1200009C;
+  svg.style.penClr = 0xCCD3D3DA;
+  svg.style.pft = subj_pft;
+  svg.AddPaths(subject);
+  svg.style.brushClr = 0x129C0000;
+  svg.style.penClr = 0xCCFFA07A;
+  svg.style.pft = clip_pft;
+  svg.AddPaths(clip);
+  svg.style.brushClr = 0x6080ff9C;
+  svg.style.penClr = 0xFF003300;
+  svg.style.pft = pftNonZero;
+  svg.AddPaths(solution);
+  svg.SaveToFile("solution.svg", svg_scale);
+
+  //finally, show the svg image in the default viewing application
+  system("solution.svg"); 
+  return 0;
+}
+//---------------------------------------------------------------------------
diff --git a/cpp/cpp_console/clipper_console_demo.sln b/cpp/cpp_console/clipper_console_demo.sln
new file mode 100644
index 0000000..147a7b5
--- /dev/null
+++ b/cpp/cpp_console/clipper_console_demo.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual C++ Express 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "clipper_console_demo", "clipper_console_demo.vcxproj", "{0710EFB8-6D8B-4DF1-9D14-907AE3857FE4}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{0710EFB8-6D8B-4DF1-9D14-907AE3857FE4}.Debug|Win32.ActiveCfg = Debug|Win32
+		{0710EFB8-6D8B-4DF1-9D14-907AE3857FE4}.Debug|Win32.Build.0 = Debug|Win32
+		{0710EFB8-6D8B-4DF1-9D14-907AE3857FE4}.Release|Win32.ActiveCfg = Release|Win32
+		{0710EFB8-6D8B-4DF1-9D14-907AE3857FE4}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/cpp/cpp_console/clipper_console_demo.vcxproj b/cpp/cpp_console/clipper_console_demo.vcxproj
new file mode 100644
index 0000000..0f0b5ee
--- /dev/null
+++ b/cpp/cpp_console/clipper_console_demo.vcxproj
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{0710EFB8-6D8B-4DF1-9D14-907AE3857FE4}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>clipper_console_demo</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PrecompiledHeaderFile>
+      </PrecompiledHeaderFile>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PrecompiledHeaderFile>
+      </PrecompiledHeaderFile>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="clipper.hpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="clipper.cpp" />
+    <ClCompile Include="clipper_console_demo.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+      <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+      </PrecompiledHeaderFile>
+    </ClCompile>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/cpp/cpp_opengl/clipper_demo.sln b/cpp/cpp_opengl/clipper_demo.sln
new file mode 100644
index 0000000..56899d2
--- /dev/null
+++ b/cpp/cpp_opengl/clipper_demo.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual C++ Express 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "clipper_demo", "clipper_demo.vcxproj", "{4D48A928-F288-40F9-9FA0-4AC650602636}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{4D48A928-F288-40F9-9FA0-4AC650602636}.Debug|Win32.ActiveCfg = Debug|Win32
+		{4D48A928-F288-40F9-9FA0-4AC650602636}.Debug|Win32.Build.0 = Debug|Win32
+		{4D48A928-F288-40F9-9FA0-4AC650602636}.Release|Win32.ActiveCfg = Release|Win32
+		{4D48A928-F288-40F9-9FA0-4AC650602636}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/cpp/cpp_opengl/clipper_demo.vcxproj b/cpp/cpp_opengl/clipper_demo.vcxproj
new file mode 100644
index 0000000..d697681
--- /dev/null
+++ b/cpp/cpp_opengl/clipper_demo.vcxproj
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{4D48A928-F288-40F9-9FA0-4AC650602636}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>opengl_app</RootNamespace>
+    <ProjectName>clipper_demo</ProjectName>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <SourcePath>$(SourcePath)</SourcePath>
+    <IncludePath>$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>opengl32.lib;glu32.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level4</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>opengl32.lib;glu32.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\clipper.cpp" />
+    <ClCompile Include="main.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\clipper.hpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <Resource Include="icon.res" />
+    <Resource Include="menu.res" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/cpp/cpp_opengl/icon.res b/cpp/cpp_opengl/icon.res
new file mode 100644
index 0000000..a4fc1a7
Binary files /dev/null and b/cpp/cpp_opengl/icon.res differ
diff --git a/cpp/cpp_opengl/main.cpp b/cpp/cpp_opengl/main.cpp
new file mode 100644
index 0000000..204716b
--- /dev/null
+++ b/cpp/cpp_opengl/main.cpp
@@ -0,0 +1,621 @@
+#include <windows.h>
+#include <commctrl.h>
+#include <gl/gl.h>
+#include <gl/glu.h>
+//#include <gl/glut.h>
+#include <ctime>
+#include <cmath>
+#include <sstream>
+#include <fstream>
+#include <iomanip>
+#include "../clipper.hpp"
+
+using namespace std;
+using namespace ClipperLib;
+
+enum poly_color_type { pctSubject, pctClip, pctSolution };
+
+//global vars ...
+HWND		 hWnd;
+HWND     hStatus; 
+HDC			 hDC;
+HGLRC		 hRC;
+ClipType     ct = ctIntersection;
+PolyFillType pft = pftEvenOdd;
+JoinType jt = jtRound;
+bool show_clipping = true;
+Paths sub, clp, sol;
+int VertCount = 5;
+int scale = 10;
+double delta = 0.0;
+
+const LPCWSTR helpText = 
+L"Clipper Demo tips...\n\n"
+L"I - for Intersection operations.\n"
+L"U - for Union operations.\n"
+L"D - for Difference operations.\n"
+L"X - for XOR operations.\n"
+L"------------------------------\n"
+L"Q - Toggle clipping on/off.\n"
+L"------------------------------\n"
+L"E - for EvenOdd fills.\n"
+L"Z - for NonZero fills.\n"
+L"P - for Positive fills.\n"
+L"N - for Negative fills.\n"
+L"------------------------------\n"
+L"nn<ENTER> - number of vertices (3..50).\n"
+L"------------------------------\n"
+L"UP arrow - Expand Solution.\n"
+L"DN arrow - Contract Solution.\n"
+L"LT or RT arrow - Reset Solution.\n"
+L"------------------------------\n"
+L"M - Miter OffsetPolygons.\n"
+L"S - Square OffsetPolygons.\n"
+L"R - Round OffsetPolygons.\n"
+L"------------------------------\n"
+L"SPACE, ENTER or click to refresh.\n"
+L"F1 - to see this help dialog again.\n"
+L"Esc - to quit.\n";
+
+typedef std::vector< GLdouble* > Vectors;
+Vectors vectors;
+
+//------------------------------------------------------------------------------
+// heap memory management for GLUtesselator ...
+//------------------------------------------------------------------------------
+
+GLdouble* NewVector(GLdouble x, GLdouble y)
+{
+  GLdouble *vert = new GLdouble[3];
+  vert[0] = x;
+  vert[1] = y;
+  vert[2] = 0;
+  vectors.push_back(vert);
+  return vert;
+}
+//------------------------------------------------------------------------------
+
+void ClearVectors()
+{
+  for (Vectors::size_type i = 0; i < vectors.size(); ++i)
+    delete[] vectors[i];
+  vectors.clear(); 
+}
+
+//------------------------------------------------------------------------------
+// GLUtesselator callback functions ...
+//------------------------------------------------------------------------------
+
+void CALLBACK BeginCallback(GLenum type)   
+{   
+    glBegin(type);   
+} 
+//------------------------------------------------------------------------------
+
+void CALLBACK EndCallback()   
+{   
+    glEnd();   
+}
+//------------------------------------------------------------------------------
+
+void CALLBACK VertexCallback(GLvoid *vertex)   
+{   
+	glVertex3dv( (const double *)vertex );   
+} 
+//------------------------------------------------------------------------------
+
+void CALLBACK CombineCallback(GLdouble coords[3], 
+  GLdouble*[4], GLfloat[4], GLdouble **dataOut )   
+{   
+  GLdouble *vert = NewVector(coords[0], coords[1]);
+	*dataOut = vert;
+}   
+//------------------------------------------------------------------------------
+
+wstring str2wstr(const std::string &s) {
+	int slength = (int)s.length() + 1;
+	int len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 
+	wchar_t* buf = new wchar_t[len];
+  MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
+  std::wstring r(buf);
+  delete[] buf;
+  return r;
+}
+//------------------------------------------------------------------------------
+
+void CALLBACK ErrorCallback(GLenum errorCode)   
+{   
+	std::wstring s = str2wstr( (char *)gluErrorString(errorCode) );
+	SetWindowText(hWnd, s.c_str());
+}   
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+// Set up pixel format for graphics initialization
+void SetupPixelFormat()
+{
+    PIXELFORMATDESCRIPTOR pfd;
+    ZeroMemory( &pfd, sizeof(pfd));
+    pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
+    pfd.nVersion = 1;
+    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+    pfd.iPixelType = PFD_TYPE_RGBA;
+    pfd.cColorBits = 32;
+    int pfIdx = ChoosePixelFormat(hDC, &pfd);
+    if (pfIdx != 0) SetPixelFormat(hDC, pfIdx, &pfd);
+}
+//------------------------------------------------------------------------------
+
+// Initialize OpenGL graphics
+void InitGraphics()
+{
+  hDC = GetDC(hWnd);
+  SetupPixelFormat();
+  hRC = wglCreateContext(hDC);
+  wglMakeCurrent(hDC, hRC);
+	glDisable(GL_DEPTH_TEST);
+	glEnable(GL_BLEND);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glTranslatef (0.375, 0.375, 0);
+}
+//------------------------------------------------------------------------------
+
+void MakeRandomPoly(Path &p, int width, int height, int edgeCount)
+{
+	p.resize(edgeCount);
+	for (int i = 0; i < edgeCount; i++)
+	{
+		p[i].X = (rand()%(width -20) +10)*scale;
+		p[i].Y = (rand()%(height -20) +10)*scale;
+	}
+}
+//------------------------------------------------------------------------------
+
+void ResizeGraphics(int width, int height)
+{
+  //setup 2D projection with origin at top-left corner ...
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrtho(0, width, height, 0, 0, 1);
+	glViewport(0, 0, width, height);
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+}
+//------------------------------------------------------------------------------
+
+void DrawPolygon(Paths &pgs, poly_color_type pct)
+{
+	switch (pct)
+	{
+		case pctSubject: glColor4f(0.0f, 0.0f, 1.0f, 0.062f); break;
+		case pctClip: glColor4f(1.0f, 1.0f, 0.0f, 0.062f); break;
+		default: glColor4f(0.0f, 1.0f, 0.0f, 0.25f);
+	}
+
+	GLUtesselator* tess = gluNewTess();
+  gluTessCallback(tess, GLU_TESS_BEGIN, (void (CALLBACK*)())&BeginCallback);    
+  gluTessCallback(tess, GLU_TESS_VERTEX, (void (CALLBACK*)())&VertexCallback);    
+  gluTessCallback(tess, GLU_TESS_END, (void (CALLBACK*)())&EndCallback);   
+  gluTessCallback(tess, GLU_TESS_COMBINE, (void (CALLBACK*)())&CombineCallback);   
+  gluTessCallback(tess, GLU_TESS_ERROR, (void (CALLBACK*)())&ErrorCallback);
+  gluTessNormal(tess, 0.0, 0.0, 1.0);
+	
+	switch (pft)
+  {
+    case pftEvenOdd: 
+      gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD); 
+      break;
+    case pftNonZero: 
+      gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO); 
+      break;
+    case pftPositive: 
+      gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE); 
+      break;
+    default: //case pftNegative
+      if (pct == pctSolution)
+        gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
+      else
+        gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NEGATIVE);
+  }
+
+	gluTessProperty(tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE); //GL_FALSE
+	gluTessBeginPolygon(tess, NULL); 
+	for (Paths::size_type i = 0; i < pgs.size(); ++i)
+	{
+		gluTessBeginContour(tess);
+		for (Path::size_type j = 0; j < pgs[i].size(); ++j)
+		{
+      GLdouble *vert = 
+        NewVector((GLdouble)pgs[i][j].X/scale, (GLdouble)pgs[i][j].Y/scale);
+			gluTessVertex(tess, vert, vert); 
+		}
+		gluTessEndContour(tess); 
+	}
+	gluTessEndPolygon(tess);
+  ClearVectors();
+
+	switch (pct)
+	{
+		case pctSubject: 
+      glColor4f(0.0f, 0.6f, 1.0f, 0.5f); 
+      break;
+		case pctClip: 
+      glColor4f(1.0f, 0.6f, 0.0f, 0.5f); 
+      break;
+		default: 
+      glColor4f(0.0f, 0.4f, 0.0f, 1.0f);
+	}
+	if (pct == pctSolution) glLineWidth(1.0f); else glLineWidth(0.8f);
+
+  gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD); 
+	gluTessProperty(tess, GLU_TESS_BOUNDARY_ONLY, GL_TRUE);
+	for (Paths::size_type i = 0; i < pgs.size(); ++i)
+	{
+    gluTessBeginPolygon(tess, NULL); 
+		gluTessBeginContour(tess);
+		for (Path::size_type j = 0; j < pgs[i].size(); ++j)
+		{
+			GLdouble *vert = 
+        NewVector((GLdouble)pgs[i][j].X/scale, (GLdouble)pgs[i][j].Y/scale);
+			gluTessVertex(tess, vert, vert); 
+		}
+
+    switch (pct)
+	  {
+		  case pctSubject: 
+        glColor4f(0.0f, 0.0f, 0.8f, 0.5f); 
+        break;
+		  case pctClip: 
+        glColor4f(0.6f, 0.0f, 0.0f, 0.5f); 
+	  }
+		gluTessEndContour(tess);
+	  gluTessEndPolygon(tess);
+	}
+
+	//final cleanup ...
+	gluDeleteTess(tess);
+  ClearVectors();
+}
+//------------------------------------------------------------------------------
+
+void DrawGraphics()
+{
+	//this can take a few moments ...
+	HCURSOR hWaitCursor = LoadCursor(NULL, IDC_WAIT);
+	SetCursor(hWaitCursor);
+	SetClassLong(hWnd, GCL_HCURSOR, (DWORD)hWaitCursor);
+
+	//fill background with a light off-gray color ...
+	glClearColor(1,1,1,1);
+	glClear(GL_COLOR_BUFFER_BIT);
+
+  //glRasterPos2f(110, 340);
+  //glColor4f(0.0f, 1.0f, 0.0f, 1.0f); 
+  //char * text = "Positive Fills";
+  //for (int i = 0; i < strlen(text); ++i)
+  //  glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, text[i]);
+	
+  DrawPolygon(sub, pctSubject);
+	DrawPolygon(clp, pctClip);
+  if (show_clipping)
+    DrawPolygon(sol, pctSolution);
+  wstringstream ss;
+  if (!show_clipping)
+    ss << L"Clipper Demo - NO CLIPPING"; 
+  else
+	  switch (ct)
+	  {
+		  case ctUnion: 
+        ss << L"Clipper Demo - UNION"; 
+        break;
+		  case ctDifference: 
+        ss << L"Clipper Demo - DIFFERENCE"; 
+        break;
+		  case ctXor: 
+        ss << L"Clipper Demo - XOR"; 
+        break;
+		  default: 
+        ss << L"Clipper Demo - INTERSECTION"; 
+	  }
+
+	switch(pft)
+  {
+    case pftEvenOdd: 
+      ss << L"  (EvenOdd filled polygons with "; 
+      break;
+    case pftNonZero: 
+      ss << L"  (NonZero filled polygons with "; 
+      break;
+    case pftPositive: 
+      ss << L"  (Positive filled polygons with "; 
+      break;
+    default: 
+      ss << L"  (Negative filled polygons with "; 
+  }
+  ss << VertCount << " vertices each.)";
+	SetWindowText(hWnd, ss.str().c_str());
+
+	HCURSOR hArrowCursor = LoadCursor(NULL, IDC_ARROW);
+	SetCursor(hArrowCursor);
+	SetClassLong(hWnd, GCL_HCURSOR, (DWORD)hArrowCursor);
+}
+//------------------------------------------------------------------------------
+
+inline long64 Round(double val)
+{
+  if ((val < 0)) return (long64)(val - 0.5); else return (long64)(val + 0.5);
+}
+//------------------------------------------------------------------------------
+
+//bool LoadFromFile(Polygons &ppg, char * filename, double scale= 1,
+//  int xOffset = 0, int yOffset = 0)
+//{
+//  ppg.clear();
+//  ifstream infile(filename);
+//  if (!infile.is_open()) return false;
+//  int polyCnt, vertCnt;
+//  double X, Y;
+//  
+//  infile >> polyCnt;
+//  infile.ignore(80, '\n');
+//  if (infile.good() && polyCnt > 0)
+//  {
+//    ppg.resize(polyCnt);
+//    for (int i = 0; i < polyCnt; i++) 
+//    {
+//      infile >> vertCnt;
+//      infile.ignore(80, '\n');
+//      if (!infile.good() || vertCnt < 0) break;
+//      ppg[i].resize(vertCnt);
+//      for (int j = 0; infile.good() && j < vertCnt; j++) 
+//      {
+//        infile >> X;
+//        while (infile.peek() == ' ') infile.ignore();
+//        if (infile.peek() == ',') infile.ignore();
+//        while (infile.peek() == ' ') infile.ignore();
+//        infile >> Y;
+//        ppg[i][j].X = Round((X + xOffset) * scale);
+//        ppg[i][j].Y = Round((Y + yOffset) * scale);
+//        infile.ignore(80, '\n');
+//      }
+//    }
+//  }
+//  infile.close();
+//  return true;
+//}
+//------------------------------------------------------------------------------
+
+void SaveToFile(const char *filename, Paths &pp, double scale = 1)
+{
+  ofstream of(filename);
+  if (!of.is_open()) return;
+  of << pp.size() << "\n";
+  for (Paths::size_type i = 0; i < pp.size(); ++i)
+  {
+    of << pp[i].size() << "\n";
+    if (scale > 1.01 || scale < 0.99) 
+      of << fixed << setprecision(6);
+    for (Path::size_type j = 0; j < pp[i].size(); ++j)
+      of << (double)pp[i][j].X /scale << ", " << (double)pp[i][j].Y /scale << ",\n";
+  }
+  of.close();
+}
+//---------------------------------------------------------------------------
+
+void UpdatePolygons(bool updateSolutionOnly)
+{
+	if (VertCount < 0) VertCount = -VertCount;
+  if (VertCount > 50) VertCount = 50;
+  if (VertCount < 3) VertCount = 3;
+
+  Clipper c;
+	if (!updateSolutionOnly)
+	{
+    delta = 0.0;
+
+    RECT r;
+    GetWindowRect(hStatus, &r);
+    int statusHeight = r.bottom - r.top;
+    GetClientRect(hWnd, &r);
+
+    sub.resize(1);
+    clp.resize(1);
+    
+ 
+    MakeRandomPoly(sub[0], r.right, r.bottom - statusHeight, VertCount);
+    MakeRandomPoly(clp[0], r.right, r.bottom - statusHeight, VertCount);
+
+    //SaveToFile("subj.txt", sub);
+    //SaveToFile("clip.txt", clp);
+	}
+
+  c.AddPaths(sub, ptSubject, true);
+  c.AddPaths(clp, ptClip, true);
+	
+  c.Execute(ct, sol, pft, pft);
+  SaveToFile("solution.txt", sol);
+
+  if (delta != 0.0)
+  {
+    ClipperOffset co;
+    co.AddPaths(sol, jt, etClosedPolygon);
+    co.Execute(sol, delta);
+  }
+
+	InvalidateRect(hWnd, NULL, false); 
+}
+//------------------------------------------------------------------------------
+
+void DoNumericKeyPress(int  num)
+{
+  if (VertCount >= 0) VertCount = -num;
+  else if (VertCount > -10) VertCount = VertCount*10 - num;
+  else Beep(1000, 100);
+}
+//------------------------------------------------------------------------------
+
+LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM  wParam, LPARAM  lParam)
+{
+	int clientwidth, clientheight;
+  switch (uMsg)
+  {
+
+	  case WM_SIZE:
+		  clientwidth = LOWORD(lParam);
+		  clientheight = HIWORD(lParam);
+		  ResizeGraphics(clientwidth, clientheight);
+		  SetWindowPos(hStatus, NULL, 0, 
+        clientheight, clientwidth, 0, SWP_NOACTIVATE | SWP_NOZORDER);
+          return 0;
+
+	  case WM_PAINT:
+		  HDC hdc;
+		  PAINTSTRUCT ps;
+		  hdc = BeginPaint(hWnd, &ps);
+		  //do the drawing ...
+		  DrawGraphics();
+		  SwapBuffers(hdc);
+		  EndPaint(hWnd, &ps);		
+		  return 0;
+
+    case WM_CLOSE: 
+        DestroyWindow(hWnd);
+        return 0;
+ 
+    case WM_DESTROY:
+        PostQuitMessage(0);
+        return 0;
+
+	  case WM_HELP:
+      MessageBox(hWnd, helpText, L"Clipper Demo - Help", 0);
+      return 0;
+
+    case WM_COMMAND:
+      switch(LOWORD(wParam))
+      {
+          case 1: case 27: PostQuitMessage(0); break; //escape
+          case 98: MessageBox(hWnd, helpText, L"Clipper Demo - Help", 0); break;
+          case 99: MessageBox(hWnd, L"After closing this dialog,\ntype the required number of vertices (3-50) then <Enter> ...", L"Clipper Demo", 0);
+          case 101: show_clipping = true; ct = ctIntersection; UpdatePolygons(true); break;
+          case 102: show_clipping = true; ct = ctUnion; UpdatePolygons(true); break;
+          case 103: show_clipping = true; ct = ctDifference; UpdatePolygons(true); break;
+          case 104: show_clipping = true; ct = ctXor; UpdatePolygons(true); break;
+			    case 105: pft = pftEvenOdd; UpdatePolygons(true); break;
+			    case 106: pft = pftNonZero; UpdatePolygons(true); break;
+          case 107: pft = pftPositive; UpdatePolygons(true); break;
+          case 108: pft = pftNegative; UpdatePolygons(true); break;
+			    case 109: show_clipping = !show_clipping; UpdatePolygons(true); break;
+          case 110: case 111: case 112: case 113: case 114:
+          case 115: case 116: case 117: case 118: case 119: 
+            DoNumericKeyPress(LOWORD(wParam) - 110); 
+            break;
+          case 120: UpdatePolygons(false); break; //space, return
+          case 131: if (delta < 20*scale) {delta += scale; UpdatePolygons(true);} break;
+          case 132: if (delta > -20*scale) {delta -= scale; UpdatePolygons(true);} break;
+          case 133: if (delta != 0.0) {delta = 0.0; UpdatePolygons(true);} break;
+          case 141: {jt = jtMiter; if (delta != 0.0) UpdatePolygons(true);} break;
+          case 142: {jt = jtSquare; if (delta != 0.0) UpdatePolygons(true);} break;
+          case 143: {jt = jtRound; if (delta != 0.0) UpdatePolygons(true);} break;
+          default: return DefWindowProc (hWnd, uMsg, wParam, lParam); 
+      }
+      return 0; 
+
+    case WM_LBUTTONUP:
+		  UpdatePolygons(false);
+		  return 0;
+
+    // Default event handler
+    default: return DefWindowProc (hWnd, uMsg, wParam, lParam); 
+  }  
+}
+//------------------------------------------------------------------------------
+
+int WINAPI WinMain (HINSTANCE hInstance, 
+  HINSTANCE, LPSTR, int nCmdShow)
+{
+
+    const LPCWSTR appname = TEXT("Clipper Demo");
+
+    WNDCLASS wndclass;
+    MSG      msg;
+ 
+    // Define the window class
+    wndclass.style         = 0;
+    wndclass.lpfnWndProc   = (WNDPROC)MainWndProc;
+    wndclass.cbClsExtra    = 0;
+    wndclass.cbWndExtra    = 0;
+    wndclass.hInstance     = hInstance;
+    wndclass.hIcon         = LoadIcon(hInstance, appname);
+    wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
+    wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
+    wndclass.lpszMenuName  = appname;
+    wndclass.lpszClassName = appname;
+ 
+    // Register the window class
+    if (!RegisterClass(&wndclass)) return FALSE;
+
+    HMENU menu = LoadMenu(hInstance, MAKEINTRESOURCE(1));
+    HACCEL accel = LoadAccelerators(hInstance, MAKEINTRESOURCE(1));
+
+    int windowSizeX = 540, windowSizeY = 400;
+
+    int dx = GetSystemMetrics(SM_XVIRTUALSCREEN);
+    int dy = GetSystemMetrics(SM_YVIRTUALSCREEN);
+    dx += ((GetSystemMetrics(SM_CXSCREEN) -windowSizeX) /2);
+    dy += ((GetSystemMetrics(SM_CYSCREEN) -windowSizeY) /2);
+
+    // Create the window
+    hWnd = CreateWindow(
+            appname,
+            appname,
+            WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
+            dx, dy, windowSizeX, windowSizeY,
+            NULL,
+            menu,
+            hInstance,
+            NULL);
+ 
+    if (!hWnd) return FALSE;
+
+	//replace the main window icon with Resource Icon #1 ...
+  HANDLE small_ico = LoadImage(hInstance, MAKEINTRESOURCE(1), IMAGE_ICON, 16, 16, 0);
+	HANDLE big_ico = LoadImage(hInstance, MAKEINTRESOURCE(1), IMAGE_ICON, 32, 32, 0);
+	SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)small_ico);
+	SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)big_ico);
+
+	InitCommonControls();
+	hStatus = CreateWindowEx(0, L"msctls_statusbar32", NULL, WS_CHILD | WS_VISIBLE,
+		0, 0, 0, 0, hWnd, (HMENU)0, hInstance, NULL);
+	SetWindowText(hStatus, L" Copyright � Angus Johnson 2011");
+
+  // Initialize OpenGL
+  InitGraphics();
+
+	srand((unsigned)time(0)); 
+	UpdatePolygons(false);
+
+  // Display the window
+  ShowWindow(hWnd, nCmdShow);
+  UpdateWindow(hWnd);
+
+  // Event loop
+    for (;;)
+    {
+        if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) == TRUE)
+        {
+            if (!GetMessage(&msg, NULL, 0, 0)) break;
+
+            if (!TranslateAccelerator(hWnd, accel, &msg))
+            {
+              TranslateMessage(&msg);
+              DispatchMessage(&msg);
+            }
+        }
+    }
+	  wglMakeCurrent(NULL, NULL);
+    wglDeleteContext(hRC);
+    ReleaseDC(hWnd, hDC);
+    return TRUE;
+}
+//------------------------------------------------------------------------------
diff --git a/cpp/cpp_opengl/menu.res b/cpp/cpp_opengl/menu.res
new file mode 100644
index 0000000..9f8ff54
Binary files /dev/null and b/cpp/cpp_opengl/menu.res differ
diff --git a/cpp/fix_members.sh b/cpp/fix_members.sh
new file mode 100755
index 0000000..998d45f
--- /dev/null
+++ b/cpp/fix_members.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+export ROOTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+perl -i -p -e "s/\.X/\.x/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/\->X/\->x/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/cInt X;/cInt x;/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/double X;/double x;/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/X\(x\)/x\(_x\)/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/X\(\(/x\(\(/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/double x = 0/double _x = 0/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/cInt x = 0/cInt _x = 0/g;" ${ROOTDIR}/clipper.*
+
+perl -i -p -e "s/\.Y/\.y/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/\->Y/\->y/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/cInt Y;/cInt y;/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/double Y;/double y;/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/Y\(y\)/y\(_y\)/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/Y\(\(/y\(\(/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/cInt          Y;/cInt          y;/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/double y = 0/double _y = 0/g;" ${ROOTDIR}/clipper.*
+perl -i -p -e "s/cInt y = 0/cInt _y = 0/g;" ${ROOTDIR}/clipper.*
+
+
diff --git a/cpp/polyclipping.pc.cmakein b/cpp/polyclipping.pc.cmakein
new file mode 100644
index 0000000..7cf55f2
--- /dev/null
+++ b/cpp/polyclipping.pc.cmakein
@@ -0,0 +1,13 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=@CMAKE_INSTALL_LIBDIR@
+sharedlibdir=@CMAKE_INSTALL_LIBDIR@
+includedir=@CMAKE_INSTALL_INCDIR@
+
+Name: polyclipping
+Description: polygon clipping library
+Version: @VERSION@
+
+Requires:
+Libs: -L${libdir} -L${sharedlibdir} -lpolyclipping
+Cflags: -I${includedir}
diff --git a/mapnik-changes.md b/mapnik-changes.md
new file mode 100644
index 0000000..87114ec
--- /dev/null
+++ b/mapnik-changes.md
@@ -0,0 +1,24 @@
+Mapnik working copy of [clipper](http://sourceforge.net/projects/polyclipping/files/).
+
+Why this fork?
+ 
+  - We want to adapt Mapnik geometries to clipper algorithms in a zero-copy way.
+   - This fork reworks the clipper c++ code to be able to pass in specialized data structures
+   - This fork also provides a shell script to rename clipper `X` and `Y` variables lowercase so they can be the [same as Mapnik](https://github.com/mapnik/mapnik/blob/master/include/mapnik/geometry.hpp#L57-L58)
+  - Once we forked we found it useful to make a few other modifications like:
+   - Using `stable_sort` to ensure test results are stable across Linux and OS X: https://github.com/mapnik/node-mapnik/issues/442#issuecomment-110133213
+   - To avoid abort on uncatchable/invalid exception: https://github.com/mapnik/clipper/commit/7c73dedbed7abcbc3aa0acc3bfbd7a62ff75e5ec
+
+Currently developing and using the `r493-mapnik` branch in:
+
+   - https://github.com/mapbox/mapnik-vector-tile
+   - https://github.com/mapnik/node-mapnik
+
+Details:
+
+ - We are using r493 rather than r494 due this this bug introduced in r494: https://github.com/mapbox/mapnik-vector-tile/issues/115
+ - We have not tested with any commit greater than r494 (http://sourceforge.net/p/polyclipping/code/HEAD/tree/)
+ - See our changes: https://github.com/mapnik/clipper/compare/r493...r493-mapnik
+
+
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/mapnik-clipper.git



More information about the Pkg-grass-devel mailing list