Initial commit
diff --git a/Bin/bg.svg b/Bin/bg.svg
new file mode 100644
index 0000000..5f94b5a
--- /dev/null
+++ b/Bin/bg.svg
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<svg version="1.1" baseProfile="tiny">
+<defs>
+</defs>
+<path d="M13.526,0v68.009H0V0H13.526z"/>
+<path d="M24.574,11.145V0H38.1v11.145H24.574z M38.1,18.765v49.244H24.574V18.765H38.1z"/>
+<path d="M62.293,0v24.766h0.19c1.651-2.539,3.889-4.396,6.715-5.572c2.825-1.175,5.763-1.763,8.811-1.763
+ c2.477,0,4.92,0.509,7.334,1.524c2.413,1.017,4.587,2.571,6.524,4.667c1.937,2.096,3.508,4.779,4.715,8.049
+ c1.206,3.271,1.81,7.16,1.81,11.668c0,4.51-0.604,8.398-1.81,11.668c-1.207,3.271-2.778,5.953-4.715,8.049
+ c-1.938,2.096-4.112,3.652-6.524,4.667c-2.414,1.016-4.858,1.524-7.334,1.524c-3.62,0-6.858-0.571-9.715-1.715
+ c-2.857-1.143-5.017-3.079-6.477-5.81h-0.19v6.286H48.768V0H62.293z M84.201,37.434c-0.445-1.905-1.143-3.587-2.096-5.049
+ c-0.952-1.46-2.143-2.619-3.572-3.477c-1.429-0.856-3.16-1.285-5.191-1.285c-1.969,0-3.684,0.429-5.144,1.285
+ c-1.461,0.857-2.667,2.017-3.62,3.477c-0.953,1.462-1.652,3.144-2.096,5.049c-0.445,1.905-0.667,3.905-0.667,6.001
+ c0,2.032,0.222,4,0.667,5.905c0.444,1.905,1.143,3.588,2.096,5.048c0.953,1.462,2.158,2.62,3.62,3.477
+ c1.46,0.857,3.174,1.286,5.144,1.286c2.031,0,3.762-0.429,5.191-1.286c1.429-0.856,2.62-2.015,3.572-3.477
+ c0.953-1.46,1.651-3.143,2.096-5.048c0.443-1.905,0.667-3.873,0.667-5.905C84.867,41.339,84.644,39.339,84.201,37.434z"/>
+<path d="M134.016,18.765v9.049h-9.906v24.384c0,2.286,0.381,3.81,1.143,4.572c0.762,0.762,2.286,1.143,4.572,1.143
+ c0.762,0,1.491-0.031,2.191-0.095c0.698-0.062,1.365-0.158,2-0.286v10.478c-1.143,0.19-2.414,0.317-3.81,0.381
+ c-1.397,0.062-2.762,0.096-4.096,0.096c-2.095,0-4.081-0.144-5.953-0.429c-1.874-0.286-3.524-0.841-4.953-1.667
+ c-1.429-0.824-2.557-2-3.381-3.524c-0.826-1.524-1.238-3.524-1.238-6.001V27.813h-8.192v-9.049h8.192V4.001h13.525v14.764H134.016z"
+ />
+<path d="M154.97,56.198c2.031,1.969,4.953,2.952,8.763,2.952c2.729,0,5.079-0.682,7.048-2.048c1.967-1.364,3.174-2.81,3.62-4.334
+ h11.906c-1.905,5.905-4.827,10.13-8.763,12.669c-3.938,2.54-8.701,3.81-14.288,3.81c-3.874,0-7.367-0.619-10.478-1.857
+ c-3.112-1.238-5.748-3-7.906-5.286c-2.159-2.286-3.826-5.016-5-8.191c-1.176-3.175-1.762-6.668-1.762-10.478
+ c0-3.683,0.603-7.111,1.81-10.287c1.206-3.175,2.92-5.921,5.144-8.239c2.222-2.317,4.873-4.144,7.954-5.477
+ c3.079-1.334,6.492-2.001,10.239-2.001c4.191,0,7.842,0.81,10.954,2.429c3.11,1.62,5.667,3.796,7.667,6.525
+ c2,2.73,3.444,5.843,4.334,9.334c0.888,3.493,1.206,7.144,0.952,10.954h-35.528C151.827,51.055,152.937,54.23,154.97,56.198z
+ M170.257,30.29c-1.619-1.777-4.081-2.667-7.382-2.667c-2.16,0-3.953,0.366-5.382,1.095c-1.429,0.731-2.572,1.636-3.429,2.715
+ c-0.857,1.081-1.461,2.224-1.81,3.429c-0.35,1.207-0.557,2.286-0.619,3.239h22.003C173.003,34.671,171.876,32.068,170.257,30.29z"/>
+<path d="M206.547,55.77c0.603,1.047,1.381,1.904,2.333,2.571c0.953,0.667,2.048,1.159,3.286,1.477
+ c1.238,0.318,2.524,0.477,3.858,0.477c0.953,0,1.953-0.11,3-0.334c1.047-0.222,2-0.571,2.857-1.048
+ c0.857-0.476,1.571-1.11,2.143-1.904c0.571-0.794,0.857-1.794,0.857-3.001c0-2.031-1.35-3.556-4.048-4.572
+ c-2.7-1.015-6.462-2.031-11.287-3.048c-1.969-0.443-3.891-0.967-5.763-1.571c-1.874-0.603-3.541-1.396-5-2.381
+ c-1.461-0.984-2.636-2.223-3.524-3.715c-0.89-1.491-1.333-3.317-1.333-5.478c0-3.174,0.619-5.777,1.857-7.811
+ c1.238-2.031,2.873-3.634,4.905-4.81c2.032-1.175,4.317-2.001,6.858-2.477c2.539-0.477,5.144-0.715,7.811-0.715
+ s5.254,0.255,7.763,0.763c2.508,0.509,4.746,1.366,6.715,2.571c1.968,1.207,3.604,2.81,4.906,4.811c1.301,2,2.079,4.524,2.333,7.572
+ h-12.859c-0.19-2.604-1.176-4.365-2.953-5.287c-1.779-0.919-3.874-1.381-6.287-1.381c-0.762,0-1.588,0.048-2.477,0.143
+ c-0.89,0.096-1.7,0.303-2.429,0.619c-0.731,0.319-1.35,0.779-1.857,1.382c-0.509,0.604-0.762,1.414-0.762,2.429
+ c0,1.207,0.444,2.19,1.333,2.953c0.889,0.762,2.048,1.381,3.477,1.857c1.429,0.476,3.063,0.904,4.906,1.285
+ c1.841,0.381,3.715,0.795,5.62,1.238c1.967,0.445,3.889,0.985,5.763,1.619c1.872,0.636,3.539,1.477,5,2.524
+ c1.46,1.048,2.634,2.35,3.524,3.905c0.888,1.557,1.333,3.477,1.333,5.763c0,3.238-0.652,5.953-1.953,8.144
+ c-1.302,2.191-3,3.953-5.096,5.287c-2.095,1.333-4.493,2.271-7.191,2.81c-2.7,0.539-5.446,0.81-8.239,0.81
+ c-2.857,0-5.653-0.286-8.382-0.857c-2.731-0.571-5.16-1.523-7.287-2.857c-2.128-1.333-3.874-3.096-5.239-5.286
+ c-1.366-2.191-2.112-4.937-2.238-8.239h12.859C205.642,53.468,205.943,54.722,206.547,55.77z"/>
+<path d="M257.695,55.77c0.603,1.047,1.381,1.904,2.333,2.571c0.953,0.667,2.048,1.159,3.286,1.477
+ c1.238,0.318,2.524,0.477,3.857,0.477c0.953,0,1.953-0.11,3.001-0.334c1.048-0.222,2-0.571,2.857-1.048
+ c0.857-0.476,1.571-1.11,2.143-1.904c0.572-0.794,0.857-1.794,0.857-3.001c0-2.031-1.35-3.556-4.048-4.572
+ c-2.7-1.015-6.462-2.031-11.287-3.048c-1.969-0.443-3.891-0.967-5.763-1.571c-1.874-0.603-3.541-1.396-5.001-2.381
+ c-1.461-0.984-2.635-2.223-3.524-3.715c-0.89-1.491-1.333-3.317-1.333-5.478c0-3.174,0.619-5.777,1.857-7.811
+ c1.238-2.031,2.872-3.634,4.905-4.81c2.031-1.175,4.317-2.001,6.858-2.477c2.539-0.477,5.144-0.715,7.811-0.715
+ s5.253,0.255,7.763,0.763c2.508,0.509,4.746,1.366,6.715,2.571c1.968,1.207,3.604,2.81,4.905,4.811c1.301,2,2.079,4.524,2.334,7.572
+ h-12.858c-0.191-2.604-1.176-4.365-2.953-5.287c-1.778-0.919-3.874-1.381-6.287-1.381c-0.762,0-1.588,0.048-2.476,0.143
+ c-0.891,0.096-1.7,0.303-2.43,0.619c-0.73,0.319-1.35,0.779-1.857,1.382c-0.509,0.604-0.762,1.414-0.762,2.429
+ c0,1.207,0.443,2.19,1.334,2.953c0.888,0.762,2.048,1.381,3.477,1.857c1.429,0.476,3.062,0.904,4.905,1.285
+ c1.841,0.381,3.715,0.795,5.619,1.238c1.968,0.445,3.89,0.985,5.763,1.619c1.873,0.636,3.54,1.477,5.001,2.524
+ c1.46,1.048,2.635,2.35,3.524,3.905c0.889,1.557,1.333,3.477,1.333,5.763c0,3.238-0.651,5.953-1.952,8.144
+ c-1.303,2.191-3,3.953-5.096,5.287c-2.096,1.333-4.493,2.271-7.191,2.81c-2.7,0.539-5.446,0.81-8.239,0.81
+ c-2.857,0-5.652-0.286-8.382-0.857c-2.731-0.571-5.16-1.523-7.287-2.857c-2.128-1.333-3.874-3.096-5.239-5.286
+ c-1.366-2.191-2.112-4.937-2.238-8.239h12.859C256.79,53.468,257.091,54.722,257.695,55.77z"/>
+<path d="M297.175,16.336c1.016-3.271,2.539-6.128,4.573-8.573c2.031-2.443,4.586-4.349,7.668-5.715
+ C312.494,0.684,316.035,0,320.035,0c3.049,0,5.953,0.477,8.717,1.429c2.762,0.953,5.189,2.319,7.285,4.096
+ c2.096,1.778,3.764,3.97,5.002,6.572c1.238,2.604,1.857,5.524,1.857,8.764c0,3.366-0.541,6.255-1.619,8.667
+ c-1.082,2.414-2.51,4.558-4.287,6.43c-1.779,1.874-3.795,3.572-6.049,5.096c-2.254,1.524-4.523,3.033-6.811,4.524
+ c-2.285,1.493-4.51,3.096-6.666,4.811c-2.16,1.714-4.064,3.715-5.717,6.001h31.529v11.62h-48.959c0-3.872,0.556-7.239,1.667-10.097
+ c1.11-2.857,2.619-5.413,4.525-7.668c1.904-2.253,4.143-4.333,6.715-6.238s5.287-3.842,8.145-5.811
+ c1.459-1.015,3.014-2.048,4.666-3.096c1.65-1.048,3.158-2.205,4.525-3.477c1.365-1.27,2.508-2.698,3.428-4.286
+ c0.92-1.587,1.383-3.396,1.383-5.43c0-3.238-0.939-5.762-2.811-7.572c-1.873-1.81-4.271-2.714-7.191-2.714
+ c-1.969,0-3.637,0.461-5,1.381c-1.367,0.921-2.463,2.128-3.287,3.619c-0.826,1.493-1.414,3.144-1.762,4.953
+ c-0.35,1.81-0.523,3.604-0.523,5.382h-12.955C295.714,23.146,296.158,19.606,297.175,16.336z"/>
+</svg>
diff --git a/Bin/fg.svg b/Bin/fg.svg
new file mode 100644
index 0000000..ae998cd
--- /dev/null
+++ b/Bin/fg.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<svg version="1.1" baseProfile="tiny">
+<defs>
+</defs>
+<polygon points="57.25,0 74.941,35.848 114.5,41.595 85.875,69.498 92.633,108.897 57.25,90.296 21.868,108.897 28.625,69.498
+ 0,41.595 39.559,35.848 "/>
+</svg>
diff --git a/Build/VC9/libtess2.sln b/Build/VC9/libtess2.sln
new file mode 100644
index 0000000..c6aa33b
--- /dev/null
+++ b/Build/VC9/libtess2.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual C++ Express 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libtess2", "libtess2.vcproj", "{54B96EEE-1E45-4A06-9A01-C3218F0A8358}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {54B96EEE-1E45-4A06-9A01-C3218F0A8358}.Debug|Win32.ActiveCfg = Debug|Win32
+ {54B96EEE-1E45-4A06-9A01-C3218F0A8358}.Debug|Win32.Build.0 = Debug|Win32
+ {54B96EEE-1E45-4A06-9A01-C3218F0A8358}.Release|Win32.ActiveCfg = Release|Win32
+ {54B96EEE-1E45-4A06-9A01-C3218F0A8358}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Build/VC9/libtess2.suo b/Build/VC9/libtess2.suo
new file mode 100644
index 0000000..36963e6
--- /dev/null
+++ b/Build/VC9/libtess2.suo
Binary files differ
diff --git a/Build/VC9/libtess2.vcproj b/Build/VC9/libtess2.vcproj
new file mode 100644
index 0000000..66f0b97
--- /dev/null
+++ b/Build/VC9/libtess2.vcproj
@@ -0,0 +1,267 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="libtess2"
+ ProjectGUID="{54B96EEE-1E45-4A06-9A01-C3218F0A8358}"
+ RootNamespace="libtess2"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="..\..\Bin"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\Include;..\..\Example;..\..\Contrib\SDL\include;..\..\Contrib"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="SDL.lib SDLmain.lib opengl32.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="..\..\Contrib\SDL\lib"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="..\..\Bin"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="..\..\Include;..\..\Example;..\..\Contrib\SDL\include;..\..\Contrib"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="SDL.lib SDLmain.lib opengl32.lib"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="..\..\Contrib\SDL\lib"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\..\Source\bucketalloc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Source\dict.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Source\geom.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Example\main.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Source\mesh.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Contrib\nanosvg.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Source\priorityq.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Source\sweep.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Source\tess.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath="..\..\Source\bucketalloc.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Source\dict.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Source\geom.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Source\mesh.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Contrib\nanosvg.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Source\priorityq.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Source\sweep.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Source\tess.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\Include\tesselator.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Build/Xcode/English.lproj/InfoPlist.strings b/Build/Xcode/English.lproj/InfoPlist.strings
new file mode 100755
index 0000000..b416943
--- /dev/null
+++ b/Build/Xcode/English.lproj/InfoPlist.strings
Binary files differ
diff --git a/Build/Xcode/English.lproj/SDLMain.nib/classes.nib b/Build/Xcode/English.lproj/SDLMain.nib/classes.nib
new file mode 100644
index 0000000..799eaad
--- /dev/null
+++ b/Build/Xcode/English.lproj/SDLMain.nib/classes.nib
@@ -0,0 +1,19 @@
+{
+ IBClasses = (
+ {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
+ {
+ ACTIONS = {
+ help = id;
+ newGame = id;
+ openGame = id;
+ prefsMenu = id;
+ saveGame = id;
+ saveGameAs = id;
+ };
+ CLASS = SDLMain;
+ LANGUAGE = ObjC;
+ SUPERCLASS = NSObject;
+ }
+ );
+ IBVersion = 1;
+}
\ No newline at end of file
diff --git a/Build/Xcode/English.lproj/SDLMain.nib/info.nib b/Build/Xcode/English.lproj/SDLMain.nib/info.nib
new file mode 100644
index 0000000..1d6fb7e
--- /dev/null
+++ b/Build/Xcode/English.lproj/SDLMain.nib/info.nib
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBDocumentLocation</key>
+ <string>62 117 356 240 0 0 1152 848 </string>
+ <key>IBEditorPositions</key>
+ <dict>
+ <key>29</key>
+ <string>62 362 195 44 0 0 1152 848 </string>
+ </dict>
+ <key>IBFramework Version</key>
+ <string>291.0</string>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>29</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>6L60</string>
+</dict>
+</plist>
diff --git a/Build/Xcode/English.lproj/SDLMain.nib/objects.nib b/Build/Xcode/English.lproj/SDLMain.nib/objects.nib
new file mode 100644
index 0000000..6378015
--- /dev/null
+++ b/Build/Xcode/English.lproj/SDLMain.nib/objects.nib
Binary files differ
diff --git a/Build/Xcode/Info.plist b/Build/Xcode/Info.plist
new file mode 100644
index 0000000..21de94c
--- /dev/null
+++ b/Build/Xcode/Info.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.yourcompany.libtess2</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>NSMainNibFile</key>
+ <string>SDLMain</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/Build/Xcode/libtess2.xcodeproj/memon.pbxuser b/Build/Xcode/libtess2.xcodeproj/memon.pbxuser
new file mode 100644
index 0000000..7b987c1
--- /dev/null
+++ b/Build/Xcode/libtess2.xcodeproj/memon.pbxuser
@@ -0,0 +1,348 @@
+// !$*UTF8*$!
+{
+ 29B97313FDCFA39411CA2CEA /* Project object */ = {
+ activeBuildConfigurationName = Debug;
+ activeExecutable = 6B58CAF4101992A200956BA2 /* libtess2 */;
+ activeTarget = 8D1107260486CEB800E47090 /* libtess2 */;
+ addToTargets = (
+ 8D1107260486CEB800E47090 /* libtess2 */,
+ );
+ breakpoints = (
+ );
+ codeSenseManager = 6B58CB07101992A800956BA2 /* Code sense */;
+ executables = (
+ 6B58CAF4101992A200956BA2 /* libtess2 */,
+ );
+ perUserDictionary = {
+ PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = {
+ PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
+ PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
+ PBXFileTableDataSourceColumnWidthsKey = (
+ 20,
+ 646,
+ 20,
+ 48,
+ 43,
+ 43,
+ 20,
+ );
+ PBXFileTableDataSourceColumnsKey = (
+ PBXFileDataSource_FiletypeID,
+ PBXFileDataSource_Filename_ColumnID,
+ PBXFileDataSource_Built_ColumnID,
+ PBXFileDataSource_ObjectSize_ColumnID,
+ PBXFileDataSource_Errors_ColumnID,
+ PBXFileDataSource_Warnings_ColumnID,
+ PBXFileDataSource_Target_ColumnID,
+ );
+ };
+ PBXPerProjectTemplateStateSaveDate = 305475119;
+ PBXWorkspaceStateSaveDate = 305475119;
+ };
+ perUserProjectItems = {
+ 6B33E35F1235077800141A9B = 6B33E35F1235077800141A9B /* PBXTextBookmark */;
+ 6B33E3621235299900141A9B = 6B33E3621235299900141A9B /* PBXTextBookmark */;
+ 6B33E3641235299900141A9B = 6B33E3641235299900141A9B /* PBXTextBookmark */;
+ 6B33E36C12352A0A00141A9B = 6B33E36C12352A0A00141A9B /* PBXTextBookmark */;
+ 6B33E36E12352BF000141A9B = 6B33E36E12352BF000141A9B /* PBXTextBookmark */;
+ 6B33E36F12352BF000141A9B = 6B33E36F12352BF000141A9B /* PBXTextBookmark */;
+ 6B33E37012352BF000141A9B = 6B33E37012352BF000141A9B /* PBXTextBookmark */;
+ 6B33E37412352C6900141A9B = 6B33E37412352C6900141A9B /* PBXTextBookmark */;
+ 6B33E37512352C6900141A9B = 6B33E37512352C6900141A9B /* PBXTextBookmark */;
+ 6B33E38312352D0F00141A9B = 6B33E38312352D0F00141A9B /* PBXTextBookmark */;
+ };
+ sourceControlManager = 6B58CB06101992A800956BA2 /* Source Control */;
+ userBuildSettings = {
+ };
+ };
+ 6B33E35F1235077800141A9B /* PBXTextBookmark */ = {
+ isa = PBXTextBookmark;
+ fRef = 6B58CB091019935200956BA2 /* main.c */;
+ name = "main.c: 13";
+ rLen = 0;
+ rLoc = 217;
+ rType = 0;
+ vrLen = 658;
+ vrLoc = 746;
+ };
+ 6B33E3621235299900141A9B /* PBXTextBookmark */ = {
+ isa = PBXTextBookmark;
+ fRef = 6B33E3631235299900141A9B /* isomesher_dc.h */;
+ name = "isomesher_dc.h: 54";
+ rLen = 0;
+ rLoc = 1157;
+ rType = 0;
+ vrLen = 774;
+ vrLoc = 655;
+ };
+ 6B33E3631235299900141A9B /* isomesher_dc.h */ = {
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ name = isomesher_dc.h;
+ path = /Users/memon/Downloads/isosurf/threed/isomesher_dc.h;
+ sourceTree = "<absolute>";
+ };
+ 6B33E3641235299900141A9B /* PBXTextBookmark */ = {
+ isa = PBXTextBookmark;
+ fRef = 6B33E3651235299900141A9B /* isomesher_dc.cpp */;
+ name = "isomesher_dc.cpp: 22";
+ rLen = 0;
+ rLoc = 644;
+ rType = 0;
+ vrLen = 1385;
+ vrLoc = 0;
+ };
+ 6B33E3651235299900141A9B /* isomesher_dc.cpp */ = {
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.cpp.cpp;
+ name = isomesher_dc.cpp;
+ path = /Users/memon/Downloads/isosurf/threed/isomesher_dc.cpp;
+ sourceTree = "<absolute>";
+ };
+ 6B33E36C12352A0A00141A9B /* PBXTextBookmark */ = {
+ isa = PBXTextBookmark;
+ fRef = 6B58CB4B1019948500956BA2 /* geom.h */;
+ name = "geom.h: 30";
+ rLen = 0;
+ rLoc = 1649;
+ rType = 0;
+ vrLen = 1567;
+ vrLoc = 1806;
+ };
+ 6B33E36E12352BF000141A9B /* PBXTextBookmark */ = {
+ isa = PBXTextBookmark;
+ fRef = 6B58CB4D1019948500956BA2 /* mesh.h */;
+ name = "mesh.h: 118";
+ rLen = 0;
+ rLoc = 5865;
+ rType = 0;
+ vrLen = 1596;
+ vrLoc = 5310;
+ };
+ 6B33E36F12352BF000141A9B /* PBXTextBookmark */ = {
+ isa = PBXTextBookmark;
+ fRef = 6B58CB501019948500956BA2 /* sweep.c */;
+ name = "sweep.c: 440";
+ rLen = 0;
+ rLoc = 14876;
+ rType = 0;
+ vrLen = 1366;
+ vrLoc = 13947;
+ };
+ 6B33E37012352BF000141A9B /* PBXTextBookmark */ = {
+ isa = PBXTextBookmark;
+ fRef = 6B58CB531019948500956BA2 /* tess.h */;
+ name = "tess.h: 73";
+ rLen = 0;
+ rLoc = 2671;
+ rType = 0;
+ vrLen = 1071;
+ vrLoc = 1824;
+ };
+ 6B33E37412352C6900141A9B /* PBXTextBookmark */ = {
+ isa = PBXTextBookmark;
+ fRef = 6B58CB521019948500956BA2 /* tess.c */;
+ name = "tess.c: 959";
+ rLen = 0;
+ rLoc = 25081;
+ rType = 0;
+ vrLen = 643;
+ vrLoc = 24599;
+ };
+ 6B33E37512352C6900141A9B /* PBXTextBookmark */ = {
+ isa = PBXTextBookmark;
+ fRef = 6B58CB451019947700956BA2 /* tesselator.h */;
+ name = "tesselator.h: 147";
+ rLen = 0;
+ rLoc = 6694;
+ rType = 0;
+ vrLen = 1977;
+ vrLoc = 4911;
+ };
+ 6B33E38312352D0F00141A9B /* PBXTextBookmark */ = {
+ isa = PBXTextBookmark;
+ fRef = 6B58CB451019947700956BA2 /* tesselator.h */;
+ name = "tesselator.h: 137";
+ rLen = 0;
+ rLoc = 6164;
+ rType = 0;
+ vrLen = 1837;
+ vrLoc = 5164;
+ };
+ 6B58CAF4101992A200956BA2 /* libtess2 */ = {
+ isa = PBXExecutable;
+ activeArgIndices = (
+ );
+ argumentStrings = (
+ );
+ autoAttachOnCrash = 1;
+ breakpointsEnabled = 0;
+ configStateDict = {
+ };
+ customDataFormattersEnabled = 1;
+ dataTipCustomDataFormattersEnabled = 1;
+ dataTipShowTypeColumn = 1;
+ dataTipSortType = 0;
+ debuggerPlugin = GDBDebugging;
+ disassemblyDisplayState = 0;
+ dylibVariantSuffix = "";
+ enableDebugStr = 1;
+ environmentEntries = (
+ );
+ executableSystemSymbolLevel = 0;
+ executableUserSymbolLevel = 0;
+ libgmallocEnabled = 1;
+ name = libtess2;
+ savedGlobals = {
+ };
+ showTypeColumn = 0;
+ sourceDirectories = (
+ );
+ };
+ 6B58CB06101992A800956BA2 /* Source Control */ = {
+ isa = PBXSourceControlManager;
+ fallbackIsa = XCSourceControlManager;
+ isSCMEnabled = 0;
+ scmConfiguration = {
+ repositoryNamesForRoots = {
+ "" = "";
+ };
+ };
+ };
+ 6B58CB07101992A800956BA2 /* Code sense */ = {
+ isa = PBXCodeSenseManager;
+ indexTemplatePath = "";
+ };
+ 6B58CB091019935200956BA2 /* main.c */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {824, 4888}}";
+ sepNavSelRange = "{217, 0}";
+ sepNavVisRange = "{746, 658}";
+ };
+ };
+ 6B58CB0A1019935200956BA2 /* SDLMain.h */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {742, 495}}";
+ sepNavSelRange = "{503, 0}";
+ sepNavVisRange = "{0, 503}";
+ };
+ };
+ 6B58CB0B1019935200956BA2 /* SDLMain.m */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {901, 5811}}";
+ sepNavSelRange = "{2860, 0}";
+ sepNavVisRange = "{2560, 353}";
+ };
+ };
+ 6B58CB451019947700956BA2 /* tesselator.h */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {922, 2054}}";
+ sepNavSelRange = "{6164, 0}";
+ sepNavVisRange = "{5307, 1694}";
+ };
+ };
+ 6B58CB461019948500956BA2 /* bucketalloc.c */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {742, 2720}}";
+ sepNavSelRange = "{1626, 0}";
+ sepNavVisRange = "{402, 1384}";
+ };
+ };
+ 6B58CB471019948500956BA2 /* bucketalloc.h */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {742, 768}}";
+ sepNavSelRange = "{1626, 0}";
+ sepNavVisRange = "{322, 1384}";
+ };
+ };
+ 6B58CB481019948500956BA2 /* dict.c */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {742, 1504}}";
+ sepNavSelRange = "{1620, 0}";
+ sepNavVisRange = "{616, 1359}";
+ };
+ };
+ 6B58CB491019948500956BA2 /* dict.h */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {742, 1056}}";
+ sepNavSelRange = "{1620, 0}";
+ sepNavVisRange = "{241, 1508}";
+ };
+ };
+ 6B58CB4A1019948500956BA2 /* geom.c */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {742, 4000}}";
+ sepNavSelRange = "{1649, 0}";
+ sepNavVisRange = "{582, 1291}";
+ };
+ };
+ 6B58CB4B1019948500956BA2 /* geom.h */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {824, 1001}}";
+ sepNavSelRange = "{1649, 0}";
+ sepNavVisRange = "{1806, 1567}";
+ };
+ };
+ 6B58CB4C1019948500956BA2 /* mesh.c */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {742, 13376}}";
+ sepNavSelRange = "{1620, 0}";
+ sepNavVisRange = "{241, 1505}";
+ };
+ };
+ 6B58CB4D1019948500956BA2 /* mesh.h */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {824, 3432}}";
+ sepNavSelRange = "{5865, 0}";
+ sepNavVisRange = "{5310, 1596}";
+ };
+ };
+ 6B58CB4E1019948500956BA2 /* priorityq.c */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {742, 7808}}";
+ sepNavSelRange = "{1620, 0}";
+ sepNavVisRange = "{572, 1199}";
+ };
+ };
+ 6B58CB4F1019948500956BA2 /* priorityq.h */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {742, 1456}}";
+ sepNavSelRange = "{1620, 0}";
+ sepNavVisRange = "{322, 1617}";
+ };
+ };
+ 6B58CB501019948500956BA2 /* sweep.c */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {824, 17264}}";
+ sepNavSelRange = "{14876, 0}";
+ sepNavVisRange = "{13947, 1366}";
+ };
+ };
+ 6B58CB511019948500956BA2 /* sweep.h */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {742, 1152}}";
+ sepNavSelRange = "{1620, 0}";
+ sepNavVisRange = "{161, 1588}";
+ };
+ };
+ 6B58CB521019948500956BA2 /* tess.c */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {824, 12805}}";
+ sepNavSelRange = "{25081, 0}";
+ sepNavVisRange = "{24599, 643}";
+ };
+ };
+ 6B58CB531019948500956BA2 /* tess.h */ = {
+ uiCtxt = {
+ sepNavIntBoundsRect = "{{0, 0}, {824, 1183}}";
+ sepNavSelRange = "{2671, 0}";
+ sepNavVisRange = "{1824, 1071}";
+ };
+ };
+ 8D1107260486CEB800E47090 /* libtess2 */ = {
+ activeExec = 0;
+ executables = (
+ 6B58CAF4101992A200956BA2 /* libtess2 */,
+ );
+ };
+}
diff --git a/Build/Xcode/libtess2.xcodeproj/memon.perspectivev3 b/Build/Xcode/libtess2.xcodeproj/memon.perspectivev3
new file mode 100644
index 0000000..761e8b6
--- /dev/null
+++ b/Build/Xcode/libtess2.xcodeproj/memon.perspectivev3
@@ -0,0 +1,1538 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>ActivePerspectiveName</key>
+ <string>Project</string>
+ <key>AllowedModules</key>
+ <array>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>PBXSmartGroupTreeModule</string>
+ <key>Name</key>
+ <string>Groups and Files Outline View</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>PBXNavigatorGroup</string>
+ <key>Name</key>
+ <string>Editor</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>XCTaskListModule</string>
+ <key>Name</key>
+ <string>Task List</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>XCDetailModule</string>
+ <key>Name</key>
+ <string>File and Smart Group Detail Viewer</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>1</string>
+ <key>Module</key>
+ <string>PBXBuildResultsModule</string>
+ <key>Name</key>
+ <string>Detailed Build Results Viewer</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>1</string>
+ <key>Module</key>
+ <string>PBXProjectFindModule</string>
+ <key>Name</key>
+ <string>Project Batch Find Tool</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>XCProjectFormatConflictsModule</string>
+ <key>Name</key>
+ <string>Project Format Conflicts List</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>PBXBookmarksModule</string>
+ <key>Name</key>
+ <string>Bookmarks Tool</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>PBXClassBrowserModule</string>
+ <key>Name</key>
+ <string>Class Browser</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>PBXCVSModule</string>
+ <key>Name</key>
+ <string>Source Code Control Tool</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>PBXDebugBreakpointsModule</string>
+ <key>Name</key>
+ <string>Debug Breakpoints Tool</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>XCDockableInspector</string>
+ <key>Name</key>
+ <string>Inspector</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>PBXOpenQuicklyModule</string>
+ <key>Name</key>
+ <string>Open Quickly Tool</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>1</string>
+ <key>Module</key>
+ <string>PBXDebugSessionModule</string>
+ <key>Name</key>
+ <string>Debugger</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>1</string>
+ <key>Module</key>
+ <string>PBXDebugCLIModule</string>
+ <key>Name</key>
+ <string>Debug Console</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>XCSnapshotModule</string>
+ <key>Name</key>
+ <string>Snapshots Tool</string>
+ </dict>
+ </array>
+ <key>BundlePath</key>
+ <string>/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources</string>
+ <key>Description</key>
+ <string>AIODescriptionKey</string>
+ <key>DockingSystemVisible</key>
+ <false/>
+ <key>Extension</key>
+ <string>perspectivev3</string>
+ <key>FavBarConfig</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>6B58CB05101992A800956BA2</string>
+ <key>XCBarModuleItemNames</key>
+ <dict/>
+ <key>XCBarModuleItems</key>
+ <array/>
+ </dict>
+ <key>FirstTimeWindowDisplayed</key>
+ <false/>
+ <key>Identifier</key>
+ <string>com.apple.perspectives.project.defaultV3</string>
+ <key>MajorVersion</key>
+ <integer>34</integer>
+ <key>MinorVersion</key>
+ <integer>0</integer>
+ <key>Name</key>
+ <string>All-In-One</string>
+ <key>Notifications</key>
+ <array/>
+ <key>OpenEditors</key>
+ <array/>
+ <key>PerspectiveWidths</key>
+ <array>
+ <integer>1010</integer>
+ <integer>1010</integer>
+ </array>
+ <key>Perspectives</key>
+ <array>
+ <dict>
+ <key>ChosenToolbarItems</key>
+ <array>
+ <string>XCToolbarPerspectiveControl</string>
+ <string>NSToolbarSeparatorItem</string>
+ <string>active-combo-popup</string>
+ <string>action</string>
+ <string>NSToolbarFlexibleSpaceItem</string>
+ <string>build-and-go</string>
+ <string>com.apple.ide.PBXToolbarStopButton</string>
+ <string>NSToolbarFlexibleSpaceItem</string>
+ <string>get-info</string>
+ <string>com.apple.pbx.toolbar.searchfield</string>
+ </array>
+ <key>ControllerClassBaseName</key>
+ <string></string>
+ <key>IconName</key>
+ <string>WindowOfProject</string>
+ <key>Identifier</key>
+ <string>perspective.project</string>
+ <key>IsVertical</key>
+ <false/>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXBottomSmartGroupGIDs</key>
+ <array>
+ <string>1C37FBAC04509CD000000102</string>
+ <string>1C37FAAC04509CD000000102</string>
+ <string>1C08E77C0454961000C914BD</string>
+ <string>1C37FABC05509CD000000102</string>
+ <string>1C37FABC05539CD112110102</string>
+ <string>E2644B35053B69B200211256</string>
+ <string>1C37FABC04509CD000100104</string>
+ <string>1CC0EA4004350EF90044410B</string>
+ <string>1CC0EA4004350EF90041110B</string>
+ <string>1C77FABC04509CD000000102</string>
+ </array>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CA23ED40692098700951B8B</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Files</string>
+ <key>PBXProjectStructureProvided</key>
+ <string>yes</string>
+ <key>PBXSmartGroupTreeModuleColumnData</key>
+ <dict>
+ <key>PBXSmartGroupTreeModuleColumnWidthsKey</key>
+ <array>
+ <real>185</real>
+ </array>
+ <key>PBXSmartGroupTreeModuleColumnsKey_v4</key>
+ <array>
+ <string>MainColumn</string>
+ </array>
+ </dict>
+ <key>PBXSmartGroupTreeModuleOutlineStateKey_v7</key>
+ <dict>
+ <key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
+ <array>
+ <string>29B97314FDCFA39411CA2CEA</string>
+ <string>6B58CB3D1019942B00956BA2</string>
+ </array>
+ <key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
+ <array>
+ <array>
+ <integer>16</integer>
+ <integer>1</integer>
+ <integer>0</integer>
+ </array>
+ </array>
+ <key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
+ <string>{{0, 0}, {185, 604}}</string>
+ </dict>
+ <key>PBXTopSmartGroupGIDs</key>
+ <array/>
+ <key>XCIncludePerspectivesSwitch</key>
+ <false/>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{0, 0}, {202, 622}}</string>
+ <key>GroupTreeTableConfiguration</key>
+ <array>
+ <string>MainColumn</string>
+ <real>185</real>
+ </array>
+ <key>RubberWindowFrame</key>
+ <string>72 115 1010 663 0 0 1280 778 </string>
+ </dict>
+ <key>Module</key>
+ <string>PBXSmartGroupTreeModule</string>
+ <key>Proportion</key>
+ <string>202pt</string>
+ </dict>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>BecomeActive</key>
+ <true/>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>6B58CB00101992A800956BA2</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>SDLMain.m</string>
+ <key>PBXSplitModuleInNavigatorKey</key>
+ <dict>
+ <key>Split0</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>6B58CB01101992A800956BA2</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>SDLMain.m</string>
+ <key>_historyCapacity</key>
+ <integer>0</integer>
+ <key>bookmark</key>
+ <string>6B58CC4B1019AD0500956BA2</string>
+ <key>history</key>
+ <array>
+ <string>6B58CB3E1019943B00956BA2</string>
+ <string>6B58CB5C101999C500956BA2</string>
+ <string>6B58CB5D101999C500956BA2</string>
+ <string>6B58CB5E101999C500956BA2</string>
+ <string>6B58CB5F101999C500956BA2</string>
+ <string>6B58CB60101999C500956BA2</string>
+ <string>6B58CB61101999C500956BA2</string>
+ <string>6B58CB62101999C500956BA2</string>
+ <string>6B58CB63101999C500956BA2</string>
+ <string>6B58CB64101999C500956BA2</string>
+ <string>6B58CB65101999C500956BA2</string>
+ <string>6B58CB67101999C500956BA2</string>
+ <string>6B58CBA210199C0400956BA2</string>
+ <string>6B58CBCA1019A1D300956BA2</string>
+ <string>6B58CBE81019A65C00956BA2</string>
+ <string>6B58CBE91019A65C00956BA2</string>
+ <string>6B58CBF21019A6AF00956BA2</string>
+ <string>6B58CC141019A94D00956BA2</string>
+ <string>6B58CC391019AC4200956BA2</string>
+ <string>6B58CC481019AD0500956BA2</string>
+ <string>6B58CC491019AD0500956BA2</string>
+ </array>
+ <key>prevStack</key>
+ <array>
+ <string>6B58CB401019943B00956BA2</string>
+ <string>6B58CB6C101999C500956BA2</string>
+ <string>6B58CB6D101999C500956BA2</string>
+ <string>6B58CB6E101999C500956BA2</string>
+ <string>6B58CB6F101999C500956BA2</string>
+ <string>6B58CB70101999C500956BA2</string>
+ <string>6B58CB71101999C500956BA2</string>
+ <string>6B58CB72101999C500956BA2</string>
+ <string>6B58CB73101999C500956BA2</string>
+ <string>6B58CB74101999C500956BA2</string>
+ <string>6B58CB75101999C500956BA2</string>
+ <string>6B58CB76101999C500956BA2</string>
+ <string>6B58CB77101999C500956BA2</string>
+ <string>6B58CB78101999C500956BA2</string>
+ <string>6B58CB79101999C500956BA2</string>
+ <string>6B58CB7A101999C500956BA2</string>
+ <string>6B58CB7B101999C500956BA2</string>
+ <string>6B58CB7C101999C500956BA2</string>
+ <string>6B58CB7D101999C500956BA2</string>
+ <string>6B58CB7E101999C500956BA2</string>
+ <string>6B58CB7F101999C500956BA2</string>
+ <string>6B58CB81101999C500956BA2</string>
+ <string>6B58CB83101999C500956BA2</string>
+ <string>6B58CB84101999C500956BA2</string>
+ <string>6B58CB85101999C500956BA2</string>
+ <string>6B58CB86101999C500956BA2</string>
+ <string>6B58CB87101999C500956BA2</string>
+ <string>6B58CB88101999C500956BA2</string>
+ <string>6B58CB89101999C500956BA2</string>
+ <string>6B58CB8A101999C500956BA2</string>
+ <string>6B58CB8B101999C500956BA2</string>
+ <string>6B58CB8C101999C500956BA2</string>
+ <string>6B58CB8D101999C500956BA2</string>
+ <string>6B58CB8E101999C500956BA2</string>
+ <string>6B58CB8F101999C500956BA2</string>
+ <string>6B58CB90101999C500956BA2</string>
+ <string>6B58CBEC1019A65C00956BA2</string>
+ <string>6B58CBED1019A65C00956BA2</string>
+ <string>6B58CBEE1019A65C00956BA2</string>
+ <string>6B58CBEF1019A65C00956BA2</string>
+ <string>6B58CBF41019A6AF00956BA2</string>
+ <string>6B58CB80101999C500956BA2</string>
+ <string>6B58CC4A1019AD0500956BA2</string>
+ </array>
+ </dict>
+ <key>SplitCount</key>
+ <string>1</string>
+ </dict>
+ <key>StatusBarVisibility</key>
+ <true/>
+ <key>XCSharingToken</key>
+ <string>com.apple.Xcode.CommonNavigatorGroupSharingToken</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{0, 0}, {803, 498}}</string>
+ <key>RubberWindowFrame</key>
+ <string>72 115 1010 663 0 0 1280 778 </string>
+ </dict>
+ <key>Module</key>
+ <string>PBXNavigatorGroup</string>
+ <key>Proportion</key>
+ <string>498pt</string>
+ </dict>
+ <dict>
+ <key>Proportion</key>
+ <string>119pt</string>
+ <key>Tabs</key>
+ <array>
+ <dict>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CA23EDF0692099D00951B8B</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Detail</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{10, 27}, {803, 63}}</string>
+ </dict>
+ <key>Module</key>
+ <string>XCDetailModule</string>
+ </dict>
+ <dict>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CA23EE00692099D00951B8B</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Project Find</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{10, 31}, {603, 297}}</string>
+ </dict>
+ <key>Module</key>
+ <string>PBXProjectFindModule</string>
+ </dict>
+ <dict>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXCVSModuleFilterTypeKey</key>
+ <integer>1032</integer>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CA23EE10692099D00951B8B</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>SCM Results</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{10, 31}, {603, 297}}</string>
+ </dict>
+ <key>Module</key>
+ <string>PBXCVSModule</string>
+ </dict>
+ <dict>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>XCMainBuildResultsModuleGUID</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Build</string>
+ <key>XCBuildResultsTrigger_Collapse</key>
+ <integer>1021</integer>
+ <key>XCBuildResultsTrigger_Open</key>
+ <integer>1011</integer>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{10, 27}, {803, 92}}</string>
+ <key>RubberWindowFrame</key>
+ <string>72 115 1010 663 0 0 1280 778 </string>
+ </dict>
+ <key>Module</key>
+ <string>PBXBuildResultsModule</string>
+ </dict>
+ </array>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>803pt</string>
+ </dict>
+ </array>
+ <key>Name</key>
+ <string>Project</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>XCModuleDock</string>
+ <string>PBXSmartGroupTreeModule</string>
+ <string>XCModuleDock</string>
+ <string>PBXNavigatorGroup</string>
+ <string>XCDockableTabModule</string>
+ <string>XCDetailModule</string>
+ <string>PBXProjectFindModule</string>
+ <string>PBXCVSModule</string>
+ <string>PBXBuildResultsModule</string>
+ </array>
+ <key>TableOfContents</key>
+ <array>
+ <string>6B58CB421019943B00956BA2</string>
+ <string>1CA23ED40692098700951B8B</string>
+ <string>6B58CB431019943B00956BA2</string>
+ <string>6B58CB00101992A800956BA2</string>
+ <string>6B58CB441019943B00956BA2</string>
+ <string>1CA23EDF0692099D00951B8B</string>
+ <string>1CA23EE00692099D00951B8B</string>
+ <string>1CA23EE10692099D00951B8B</string>
+ <string>XCMainBuildResultsModuleGUID</string>
+ </array>
+ <key>ToolbarConfiguration</key>
+ <string>xcode.toolbar.config.defaultV3</string>
+ </dict>
+ <dict>
+ <key>ChosenToolbarItems</key>
+ <array>
+ <string>XCToolbarPerspectiveControl</string>
+ <string>NSToolbarSeparatorItem</string>
+ <string>active-combo-popup</string>
+ <string>NSToolbarFlexibleSpaceItem</string>
+ <string>build-and-go</string>
+ <string>com.apple.ide.PBXToolbarStopButton</string>
+ <string>debugger-restart-executable</string>
+ <string>debugger-pause</string>
+ <string>debugger-step-over</string>
+ <string>debugger-step-into</string>
+ <string>debugger-step-out</string>
+ <string>debugger-enable-breakpoints</string>
+ <string>NSToolbarFlexibleSpaceItem</string>
+ <string>com.apple.ide.XCBreakpointsToolbarItem</string>
+ <string>clear-log</string>
+ </array>
+ <key>ControllerClassBaseName</key>
+ <string>PBXDebugSessionModule</string>
+ <key>IconName</key>
+ <string>DebugTabIcon</string>
+ <key>Identifier</key>
+ <string>perspective.debug</string>
+ <key>IsVertical</key>
+ <true/>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CCC7628064C1048000F2A68</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Debugger Console</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{0, 0}, {1010, 174}}</string>
+ </dict>
+ <key>Module</key>
+ <string>PBXDebugCLIModule</string>
+ <key>Proportion</key>
+ <string>174pt</string>
+ </dict>
+ <dict>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>Debugger</key>
+ <dict>
+ <key>HorizontalSplitView</key>
+ <dict>
+ <key>_collapsingFrameDimension</key>
+ <real>0.0</real>
+ <key>_indexOfCollapsedView</key>
+ <integer>0</integer>
+ <key>_percentageOfCollapsedView</key>
+ <real>0.0</real>
+ <key>isCollapsed</key>
+ <string>yes</string>
+ <key>sizes</key>
+ <array>
+ <string>{{0, 0}, {493, 215}}</string>
+ <string>{{493, 0}, {517, 215}}</string>
+ </array>
+ </dict>
+ <key>VerticalSplitView</key>
+ <dict>
+ <key>_collapsingFrameDimension</key>
+ <real>0.0</real>
+ <key>_indexOfCollapsedView</key>
+ <integer>0</integer>
+ <key>_percentageOfCollapsedView</key>
+ <real>0.0</real>
+ <key>isCollapsed</key>
+ <string>yes</string>
+ <key>sizes</key>
+ <array>
+ <string>{{0, 0}, {1010, 215}}</string>
+ <string>{{0, 215}, {1010, 228}}</string>
+ </array>
+ </dict>
+ </dict>
+ <key>LauncherConfigVersion</key>
+ <string>8</string>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CCC7629064C1048000F2A68</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Debug</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>DebugConsoleVisible</key>
+ <string>None</string>
+ <key>DebugConsoleWindowFrame</key>
+ <string>{{200, 200}, {500, 300}}</string>
+ <key>DebugSTDIOWindowFrame</key>
+ <string>{{200, 200}, {500, 300}}</string>
+ <key>Frame</key>
+ <string>{{0, 179}, {1010, 443}}</string>
+ <key>PBXDebugSessionStackFrameViewKey</key>
+ <dict>
+ <key>DebugVariablesTableConfiguration</key>
+ <array>
+ <string>Name</string>
+ <real>120</real>
+ <string>Value</string>
+ <real>85</real>
+ <string>Summary</string>
+ <real>287</real>
+ </array>
+ <key>Frame</key>
+ <string>{{493, 0}, {517, 215}}</string>
+ </dict>
+ </dict>
+ <key>Module</key>
+ <string>PBXDebugSessionModule</string>
+ <key>Proportion</key>
+ <string>443pt</string>
+ </dict>
+ </array>
+ <key>Name</key>
+ <string>Debug</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>XCModuleDock</string>
+ <string>PBXDebugCLIModule</string>
+ <string>PBXDebugSessionModule</string>
+ <string>PBXDebugProcessAndThreadModule</string>
+ <string>PBXDebugProcessViewModule</string>
+ <string>PBXDebugThreadViewModule</string>
+ <string>PBXDebugStackFrameViewModule</string>
+ <string>PBXNavigatorGroup</string>
+ </array>
+ <key>TableOfContents</key>
+ <array>
+ <string>6B58CB9610199A0C00956BA2</string>
+ <string>1CCC7628064C1048000F2A68</string>
+ <string>1CCC7629064C1048000F2A68</string>
+ <string>6B58CB9710199A0C00956BA2</string>
+ <string>6B58CB9810199A0C00956BA2</string>
+ <string>6B58CB9910199A0C00956BA2</string>
+ <string>6B58CB9A10199A0C00956BA2</string>
+ <string>6B58CB9B10199A0C00956BA2</string>
+ </array>
+ <key>ToolbarConfiguration</key>
+ <string>xcode.toolbar.config.debugV3</string>
+ </dict>
+ </array>
+ <key>PerspectivesBarVisible</key>
+ <true/>
+ <key>ShelfIsVisible</key>
+ <false/>
+ <key>SourceDescription</key>
+ <string>file at '/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources/XCPerspectivesSpecification.xcperspec'</string>
+ <key>StatusbarIsVisible</key>
+ <true/>
+ <key>TimeStamp</key>
+ <real>0.0</real>
+ <key>ToolbarDisplayMode</key>
+ <integer>1</integer>
+ <key>ToolbarIsVisible</key>
+ <true/>
+ <key>ToolbarSizeMode</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Perspectives</string>
+ <key>UpdateMessage</key>
+ <string></string>
+ <key>WindowJustification</key>
+ <integer>5</integer>
+ <key>WindowOrderList</key>
+ <array>
+ <string>6B58CBB610199FFF00956BA2</string>
+ <string>6B58CBB710199FFF00956BA2</string>
+ <string>/Users/memon/Code/libtess2/Build/Xcode/libtess2.xcodeproj</string>
+ </array>
+ <key>WindowString</key>
+ <string>72 115 1010 663 0 0 1280 778 </string>
+ <key>WindowToolsV3</key>
+ <array>
+ <dict>
+ <key>Identifier</key>
+ <string>windowTool.debugger</string>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>Debugger</key>
+ <dict>
+ <key>HorizontalSplitView</key>
+ <dict>
+ <key>_collapsingFrameDimension</key>
+ <real>0.0</real>
+ <key>_indexOfCollapsedView</key>
+ <integer>0</integer>
+ <key>_percentageOfCollapsedView</key>
+ <real>0.0</real>
+ <key>isCollapsed</key>
+ <string>yes</string>
+ <key>sizes</key>
+ <array>
+ <string>{{0, 0}, {317, 164}}</string>
+ <string>{{317, 0}, {377, 164}}</string>
+ </array>
+ </dict>
+ <key>VerticalSplitView</key>
+ <dict>
+ <key>_collapsingFrameDimension</key>
+ <real>0.0</real>
+ <key>_indexOfCollapsedView</key>
+ <integer>0</integer>
+ <key>_percentageOfCollapsedView</key>
+ <real>0.0</real>
+ <key>isCollapsed</key>
+ <string>yes</string>
+ <key>sizes</key>
+ <array>
+ <string>{{0, 0}, {694, 164}}</string>
+ <string>{{0, 164}, {694, 216}}</string>
+ </array>
+ </dict>
+ </dict>
+ <key>LauncherConfigVersion</key>
+ <string>8</string>
+ <key>PBXProjectModuleGUID</key>
+ <string>1C162984064C10D400B95A72</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Debug - GLUTExamples (Underwater)</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>DebugConsoleDrawerSize</key>
+ <string>{100, 120}</string>
+ <key>DebugConsoleVisible</key>
+ <string>None</string>
+ <key>DebugConsoleWindowFrame</key>
+ <string>{{200, 200}, {500, 300}}</string>
+ <key>DebugSTDIOWindowFrame</key>
+ <string>{{200, 200}, {500, 300}}</string>
+ <key>Frame</key>
+ <string>{{0, 0}, {694, 380}}</string>
+ <key>RubberWindowFrame</key>
+ <string>321 238 694 422 0 0 1440 878 </string>
+ </dict>
+ <key>Module</key>
+ <string>PBXDebugSessionModule</string>
+ <key>Proportion</key>
+ <string>100%</string>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>100%</string>
+ </dict>
+ </array>
+ <key>Name</key>
+ <string>Debugger</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>PBXDebugSessionModule</string>
+ </array>
+ <key>StatusbarIsVisible</key>
+ <integer>1</integer>
+ <key>TableOfContents</key>
+ <array>
+ <string>1CD10A99069EF8BA00B06720</string>
+ <string>1C0AD2AB069F1E9B00FABCE6</string>
+ <string>1C162984064C10D400B95A72</string>
+ <string>1C0AD2AC069F1E9B00FABCE6</string>
+ </array>
+ <key>ToolbarConfiguration</key>
+ <string>xcode.toolbar.config.debugV3</string>
+ <key>WindowString</key>
+ <string>321 238 694 422 0 0 1440 878 </string>
+ <key>WindowToolGUID</key>
+ <string>1CD10A99069EF8BA00B06720</string>
+ <key>WindowToolIsVisible</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Identifier</key>
+ <string>windowTool.build</string>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CD0528F0623707200166675</string>
+ <key>PBXProjectModuleLabel</key>
+ <string><No Editor></string>
+ <key>PBXSplitModuleInNavigatorKey</key>
+ <dict>
+ <key>Split0</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CD052900623707200166675</string>
+ </dict>
+ <key>SplitCount</key>
+ <string>1</string>
+ </dict>
+ <key>StatusBarVisibility</key>
+ <integer>1</integer>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{0, 0}, {500, 215}}</string>
+ <key>RubberWindowFrame</key>
+ <string>192 257 500 500 0 0 1280 1002 </string>
+ </dict>
+ <key>Module</key>
+ <string>PBXNavigatorGroup</string>
+ <key>Proportion</key>
+ <string>218pt</string>
+ </dict>
+ <dict>
+ <key>BecomeActive</key>
+ <integer>1</integer>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>XCMainBuildResultsModuleGUID</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Build</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{0, 222}, {500, 236}}</string>
+ <key>RubberWindowFrame</key>
+ <string>192 257 500 500 0 0 1280 1002 </string>
+ </dict>
+ <key>Module</key>
+ <string>PBXBuildResultsModule</string>
+ <key>Proportion</key>
+ <string>236pt</string>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>458pt</string>
+ </dict>
+ </array>
+ <key>Name</key>
+ <string>Build Results</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>PBXBuildResultsModule</string>
+ </array>
+ <key>StatusbarIsVisible</key>
+ <integer>1</integer>
+ <key>TableOfContents</key>
+ <array>
+ <string>1C78EAA5065D492600B07095</string>
+ <string>1C78EAA6065D492600B07095</string>
+ <string>1CD0528F0623707200166675</string>
+ <string>XCMainBuildResultsModuleGUID</string>
+ </array>
+ <key>ToolbarConfiguration</key>
+ <string>xcode.toolbar.config.buildV3</string>
+ <key>WindowString</key>
+ <string>192 257 500 500 0 0 1280 1002 </string>
+ </dict>
+ <dict>
+ <key>Identifier</key>
+ <string>windowTool.find</string>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CDD528C0622207200134675</string>
+ <key>PBXProjectModuleLabel</key>
+ <string><No Editor></string>
+ <key>PBXSplitModuleInNavigatorKey</key>
+ <dict>
+ <key>Split0</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CD0528D0623707200166675</string>
+ </dict>
+ <key>SplitCount</key>
+ <string>1</string>
+ </dict>
+ <key>StatusBarVisibility</key>
+ <integer>1</integer>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{0, 0}, {781, 167}}</string>
+ <key>RubberWindowFrame</key>
+ <string>62 385 781 470 0 0 1440 878 </string>
+ </dict>
+ <key>Module</key>
+ <string>PBXNavigatorGroup</string>
+ <key>Proportion</key>
+ <string>781pt</string>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>50%</string>
+ </dict>
+ <dict>
+ <key>BecomeActive</key>
+ <integer>1</integer>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CD0528E0623707200166675</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Project Find</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{8, 0}, {773, 254}}</string>
+ <key>RubberWindowFrame</key>
+ <string>62 385 781 470 0 0 1440 878 </string>
+ </dict>
+ <key>Module</key>
+ <string>PBXProjectFindModule</string>
+ <key>Proportion</key>
+ <string>50%</string>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>428pt</string>
+ </dict>
+ </array>
+ <key>Name</key>
+ <string>Project Find</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>PBXProjectFindModule</string>
+ </array>
+ <key>StatusbarIsVisible</key>
+ <integer>1</integer>
+ <key>TableOfContents</key>
+ <array>
+ <string>1C530D57069F1CE1000CFCEE</string>
+ <string>1C530D58069F1CE1000CFCEE</string>
+ <string>1C530D59069F1CE1000CFCEE</string>
+ <string>1CDD528C0622207200134675</string>
+ <string>1C530D5A069F1CE1000CFCEE</string>
+ <string>1CE0B1FE06471DED0097A5F4</string>
+ <string>1CD0528E0623707200166675</string>
+ </array>
+ <key>WindowString</key>
+ <string>62 385 781 470 0 0 1440 878 </string>
+ <key>WindowToolGUID</key>
+ <string>1C530D57069F1CE1000CFCEE</string>
+ <key>WindowToolIsVisible</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Identifier</key>
+ <string>windowTool.snapshots</string>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>Module</key>
+ <string>XCSnapshotModule</string>
+ <key>Proportion</key>
+ <string>100%</string>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>100%</string>
+ </dict>
+ </array>
+ <key>Name</key>
+ <string>Snapshots</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>XCSnapshotModule</string>
+ </array>
+ <key>StatusbarIsVisible</key>
+ <string>Yes</string>
+ <key>ToolbarConfiguration</key>
+ <string>xcode.toolbar.config.snapshots</string>
+ <key>WindowString</key>
+ <string>315 824 300 550 0 0 1440 878 </string>
+ <key>WindowToolIsVisible</key>
+ <string>Yes</string>
+ </dict>
+ <dict>
+ <key>Identifier</key>
+ <string>windowTool.debuggerConsole</string>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>BecomeActive</key>
+ <integer>1</integer>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1C78EAAC065D492600B07095</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Debugger Console</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{0, 0}, {700, 358}}</string>
+ <key>RubberWindowFrame</key>
+ <string>149 87 700 400 0 0 1440 878 </string>
+ </dict>
+ <key>Module</key>
+ <string>PBXDebugCLIModule</string>
+ <key>Proportion</key>
+ <string>358pt</string>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>358pt</string>
+ </dict>
+ </array>
+ <key>Name</key>
+ <string>Debugger Console</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>PBXDebugCLIModule</string>
+ </array>
+ <key>StatusbarIsVisible</key>
+ <integer>1</integer>
+ <key>TableOfContents</key>
+ <array>
+ <string>1C530D5B069F1CE1000CFCEE</string>
+ <string>1C530D5C069F1CE1000CFCEE</string>
+ <string>1C78EAAC065D492600B07095</string>
+ </array>
+ <key>ToolbarConfiguration</key>
+ <string>xcode.toolbar.config.consoleV3</string>
+ <key>WindowString</key>
+ <string>149 87 440 400 0 0 1440 878 </string>
+ <key>WindowToolGUID</key>
+ <string>1C530D5B069F1CE1000CFCEE</string>
+ <key>WindowToolIsVisible</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Identifier</key>
+ <string>windowTool.scm</string>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1C78EAB2065D492600B07095</string>
+ <key>PBXProjectModuleLabel</key>
+ <string><No Editor></string>
+ <key>PBXSplitModuleInNavigatorKey</key>
+ <dict>
+ <key>Split0</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1C78EAB3065D492600B07095</string>
+ </dict>
+ <key>SplitCount</key>
+ <string>1</string>
+ </dict>
+ <key>StatusBarVisibility</key>
+ <integer>1</integer>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{0, 0}, {452, 0}}</string>
+ <key>RubberWindowFrame</key>
+ <string>743 379 452 308 0 0 1280 1002 </string>
+ </dict>
+ <key>Module</key>
+ <string>PBXNavigatorGroup</string>
+ <key>Proportion</key>
+ <string>0pt</string>
+ </dict>
+ <dict>
+ <key>BecomeActive</key>
+ <integer>1</integer>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CD052920623707200166675</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>SCM</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>ConsoleFrame</key>
+ <string>{{0, 259}, {452, 0}}</string>
+ <key>Frame</key>
+ <string>{{0, 7}, {452, 259}}</string>
+ <key>RubberWindowFrame</key>
+ <string>743 379 452 308 0 0 1280 1002 </string>
+ <key>TableConfiguration</key>
+ <array>
+ <string>Status</string>
+ <real>30</real>
+ <string>FileName</string>
+ <real>199</real>
+ <string>Path</string>
+ <real>197.09500122070312</real>
+ </array>
+ <key>TableFrame</key>
+ <string>{{0, 0}, {452, 250}}</string>
+ </dict>
+ <key>Module</key>
+ <string>PBXCVSModule</string>
+ <key>Proportion</key>
+ <string>262pt</string>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>266pt</string>
+ </dict>
+ </array>
+ <key>Name</key>
+ <string>SCM</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>PBXCVSModule</string>
+ </array>
+ <key>StatusbarIsVisible</key>
+ <integer>1</integer>
+ <key>TableOfContents</key>
+ <array>
+ <string>1C78EAB4065D492600B07095</string>
+ <string>1C78EAB5065D492600B07095</string>
+ <string>1C78EAB2065D492600B07095</string>
+ <string>1CD052920623707200166675</string>
+ </array>
+ <key>ToolbarConfiguration</key>
+ <string>xcode.toolbar.config.scmV3</string>
+ <key>WindowString</key>
+ <string>743 379 452 308 0 0 1280 1002 </string>
+ </dict>
+ <dict>
+ <key>Identifier</key>
+ <string>windowTool.breakpoints</string>
+ <key>IsVertical</key>
+ <integer>0</integer>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>BecomeActive</key>
+ <integer>1</integer>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXBottomSmartGroupGIDs</key>
+ <array>
+ <string>1C77FABC04509CD000000102</string>
+ </array>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CE0B1FE06471DED0097A5F4</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Files</string>
+ <key>PBXProjectStructureProvided</key>
+ <string>no</string>
+ <key>PBXSmartGroupTreeModuleColumnData</key>
+ <dict>
+ <key>PBXSmartGroupTreeModuleColumnWidthsKey</key>
+ <array>
+ <real>168</real>
+ </array>
+ <key>PBXSmartGroupTreeModuleColumnsKey_v4</key>
+ <array>
+ <string>MainColumn</string>
+ </array>
+ </dict>
+ <key>PBXSmartGroupTreeModuleOutlineStateKey_v7</key>
+ <dict>
+ <key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
+ <array>
+ <string>1C77FABC04509CD000000102</string>
+ </array>
+ <key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
+ <array>
+ <array>
+ <integer>0</integer>
+ </array>
+ </array>
+ <key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
+ <string>{{0, 0}, {168, 350}}</string>
+ </dict>
+ <key>PBXTopSmartGroupGIDs</key>
+ <array/>
+ <key>XCIncludePerspectivesSwitch</key>
+ <integer>0</integer>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{0, 0}, {185, 368}}</string>
+ <key>GroupTreeTableConfiguration</key>
+ <array>
+ <string>MainColumn</string>
+ <real>168</real>
+ </array>
+ <key>RubberWindowFrame</key>
+ <string>315 424 744 409 0 0 1440 878 </string>
+ </dict>
+ <key>Module</key>
+ <string>PBXSmartGroupTreeModule</string>
+ <key>Proportion</key>
+ <string>185pt</string>
+ </dict>
+ <dict>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CA1AED706398EBD00589147</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Detail</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{190, 0}, {554, 368}}</string>
+ <key>RubberWindowFrame</key>
+ <string>315 424 744 409 0 0 1440 878 </string>
+ </dict>
+ <key>Module</key>
+ <string>XCDetailModule</string>
+ <key>Proportion</key>
+ <string>554pt</string>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>368pt</string>
+ </dict>
+ </array>
+ <key>MajorVersion</key>
+ <integer>3</integer>
+ <key>MinorVersion</key>
+ <integer>0</integer>
+ <key>Name</key>
+ <string>Breakpoints</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>PBXSmartGroupTreeModule</string>
+ <string>XCDetailModule</string>
+ </array>
+ <key>StatusbarIsVisible</key>
+ <integer>1</integer>
+ <key>TableOfContents</key>
+ <array>
+ <string>1CDDB66807F98D9800BB5817</string>
+ <string>1CDDB66907F98D9800BB5817</string>
+ <string>1CE0B1FE06471DED0097A5F4</string>
+ <string>1CA1AED706398EBD00589147</string>
+ </array>
+ <key>ToolbarConfiguration</key>
+ <string>xcode.toolbar.config.breakpointsV3</string>
+ <key>WindowString</key>
+ <string>315 424 744 409 0 0 1440 878 </string>
+ <key>WindowToolGUID</key>
+ <string>1CDDB66807F98D9800BB5817</string>
+ <key>WindowToolIsVisible</key>
+ <integer>1</integer>
+ </dict>
+ <dict>
+ <key>Identifier</key>
+ <string>windowTool.debugAnimator</string>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>Module</key>
+ <string>PBXNavigatorGroup</string>
+ <key>Proportion</key>
+ <string>100%</string>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>100%</string>
+ </dict>
+ </array>
+ <key>Name</key>
+ <string>Debug Visualizer</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>PBXNavigatorGroup</string>
+ </array>
+ <key>StatusbarIsVisible</key>
+ <integer>1</integer>
+ <key>ToolbarConfiguration</key>
+ <string>xcode.toolbar.config.debugAnimatorV3</string>
+ <key>WindowString</key>
+ <string>100 100 700 500 0 0 1280 1002 </string>
+ </dict>
+ <dict>
+ <key>Identifier</key>
+ <string>windowTool.bookmarks</string>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>Module</key>
+ <string>PBXBookmarksModule</string>
+ <key>Proportion</key>
+ <string>166pt</string>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>166pt</string>
+ </dict>
+ </array>
+ <key>Name</key>
+ <string>Bookmarks</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>PBXBookmarksModule</string>
+ </array>
+ <key>StatusbarIsVisible</key>
+ <integer>0</integer>
+ <key>WindowString</key>
+ <string>538 42 401 187 0 0 1280 1002 </string>
+ </dict>
+ <dict>
+ <key>Identifier</key>
+ <string>windowTool.projectFormatConflicts</string>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>Module</key>
+ <string>XCProjectFormatConflictsModule</string>
+ <key>Proportion</key>
+ <string>100%</string>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>100%</string>
+ </dict>
+ </array>
+ <key>Name</key>
+ <string>Project Format Conflicts</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>XCProjectFormatConflictsModule</string>
+ </array>
+ <key>StatusbarIsVisible</key>
+ <integer>0</integer>
+ <key>WindowContentMinSize</key>
+ <string>450 300</string>
+ <key>WindowString</key>
+ <string>50 850 472 307 0 0 1440 877</string>
+ </dict>
+ <dict>
+ <key>Identifier</key>
+ <string>windowTool.classBrowser</string>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>BecomeActive</key>
+ <integer>1</integer>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>OptionsSetName</key>
+ <string>Hierarchy, all classes</string>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CA6456E063B45B4001379D8</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Class Browser - NSObject</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>ClassesFrame</key>
+ <string>{{0, 0}, {369, 96}}</string>
+ <key>ClassesTreeTableConfiguration</key>
+ <array>
+ <string>PBXClassNameColumnIdentifier</string>
+ <real>208</real>
+ <string>PBXClassBookColumnIdentifier</string>
+ <real>22</real>
+ </array>
+ <key>Frame</key>
+ <string>{{0, 0}, {616, 353}}</string>
+ <key>MembersFrame</key>
+ <string>{{0, 105}, {369, 395}}</string>
+ <key>MembersTreeTableConfiguration</key>
+ <array>
+ <string>PBXMemberTypeIconColumnIdentifier</string>
+ <real>22</real>
+ <string>PBXMemberNameColumnIdentifier</string>
+ <real>216</real>
+ <string>PBXMemberTypeColumnIdentifier</string>
+ <real>94</real>
+ <string>PBXMemberBookColumnIdentifier</string>
+ <real>22</real>
+ </array>
+ <key>PBXModuleWindowStatusBarHidden2</key>
+ <integer>1</integer>
+ <key>RubberWindowFrame</key>
+ <string>597 125 616 374 0 0 1280 1002 </string>
+ </dict>
+ <key>Module</key>
+ <string>PBXClassBrowserModule</string>
+ <key>Proportion</key>
+ <string>354pt</string>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>354pt</string>
+ </dict>
+ </array>
+ <key>Name</key>
+ <string>Class Browser</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>PBXClassBrowserModule</string>
+ </array>
+ <key>StatusbarIsVisible</key>
+ <integer>0</integer>
+ <key>TableOfContents</key>
+ <array>
+ <string>1C78EABA065D492600B07095</string>
+ <string>1C78EABB065D492600B07095</string>
+ <string>1CA6456E063B45B4001379D8</string>
+ </array>
+ <key>ToolbarConfiguration</key>
+ <string>xcode.toolbar.config.classbrowser</string>
+ <key>WindowString</key>
+ <string>597 125 616 374 0 0 1280 1002 </string>
+ </dict>
+ <dict>
+ <key>Identifier</key>
+ <string>windowTool.refactoring</string>
+ <key>IncludeInToolsMenu</key>
+ <integer>0</integer>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>BecomeActive</key>
+ <integer>1</integer>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{0, 0}, {500, 335}</string>
+ <key>RubberWindowFrame</key>
+ <string>{0, 0}, {500, 335}</string>
+ </dict>
+ <key>Module</key>
+ <string>XCRefactoringModule</string>
+ <key>Proportion</key>
+ <string>100%</string>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>100%</string>
+ </dict>
+ </array>
+ <key>Name</key>
+ <string>Refactoring</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>XCRefactoringModule</string>
+ </array>
+ <key>WindowString</key>
+ <string>200 200 500 356 0 0 1920 1200 </string>
+ </dict>
+ </array>
+</dict>
+</plist>
diff --git a/Build/Xcode/libtess2.xcodeproj/project.pbxproj b/Build/Xcode/libtess2.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..b75f3a3
--- /dev/null
+++ b/Build/Xcode/libtess2.xcodeproj/project.pbxproj
@@ -0,0 +1,385 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 002F39FA09D0881F00EBEB88 /* SDL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 002F39F909D0881F00EBEB88 /* SDL.framework */; };
+ 002F3A0009D0884600EBEB88 /* SDL.framework in Copy Frameworks into .app bundle */ = {isa = PBXBuildFile; fileRef = 002F39F909D0881F00EBEB88 /* SDL.framework */; };
+ 002F3AF109D08F1000EBEB88 /* SDLMain.nib in Resources */ = {isa = PBXBuildFile; fileRef = 002F3AEF09D08F1000EBEB88 /* SDLMain.nib */; };
+ 6B33E3331235065700141A9B /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B33E3321235065700141A9B /* OpenGL.framework */; };
+ 6B58CB0C1019935200956BA2 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 6B58CB091019935200956BA2 /* main.c */; };
+ 6B58CB0D1019935200956BA2 /* SDLMain.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B58CB0B1019935200956BA2 /* SDLMain.m */; };
+ 6B58CB121019938400956BA2 /* nanosvg.c in Sources */ = {isa = PBXBuildFile; fileRef = 6B58CB101019938400956BA2 /* nanosvg.c */; };
+ 6B58CB541019948500956BA2 /* bucketalloc.c in Sources */ = {isa = PBXBuildFile; fileRef = 6B58CB461019948500956BA2 /* bucketalloc.c */; };
+ 6B58CB551019948500956BA2 /* dict.c in Sources */ = {isa = PBXBuildFile; fileRef = 6B58CB481019948500956BA2 /* dict.c */; };
+ 6B58CB561019948500956BA2 /* geom.c in Sources */ = {isa = PBXBuildFile; fileRef = 6B58CB4A1019948500956BA2 /* geom.c */; };
+ 6B58CB571019948500956BA2 /* mesh.c in Sources */ = {isa = PBXBuildFile; fileRef = 6B58CB4C1019948500956BA2 /* mesh.c */; };
+ 6B58CB581019948500956BA2 /* priorityq.c in Sources */ = {isa = PBXBuildFile; fileRef = 6B58CB4E1019948500956BA2 /* priorityq.c */; };
+ 6B58CB591019948500956BA2 /* sweep.c in Sources */ = {isa = PBXBuildFile; fileRef = 6B58CB501019948500956BA2 /* sweep.c */; };
+ 6B58CB5A1019948500956BA2 /* tess.c in Sources */ = {isa = PBXBuildFile; fileRef = 6B58CB521019948500956BA2 /* tess.c */; };
+ 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
+ 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 002F39FD09D0883400EBEB88 /* Copy Frameworks into .app bundle */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 002F3A0009D0884600EBEB88 /* SDL.framework in Copy Frameworks into .app bundle */,
+ );
+ name = "Copy Frameworks into .app bundle";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 002F39F909D0881F00EBEB88 /* SDL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL.framework; path = /Library/Frameworks/SDL.framework; sourceTree = "<absolute>"; };
+ 002F3AF009D08F1000EBEB88 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/SDLMain.nib; sourceTree = "<group>"; };
+ 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
+ 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
+ 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+ 32CA4F630368D1EE00C91783 /* libtess2_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libtess2_Prefix.pch; sourceTree = "<group>"; };
+ 6B33E3321235065700141A9B /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = "<absolute>"; };
+ 6B58CB091019935200956BA2 /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = main.c; path = ../../Example/main.c; sourceTree = SOURCE_ROOT; };
+ 6B58CB0A1019935200956BA2 /* SDLMain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLMain.h; path = ../../Example/SDLMain.h; sourceTree = SOURCE_ROOT; };
+ 6B58CB0B1019935200956BA2 /* SDLMain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLMain.m; path = ../../Example/SDLMain.m; sourceTree = SOURCE_ROOT; };
+ 6B58CB101019938400956BA2 /* nanosvg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nanosvg.c; path = ../../Contrib/nanosvg.c; sourceTree = SOURCE_ROOT; };
+ 6B58CB111019938400956BA2 /* nanosvg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nanosvg.h; path = ../../Contrib/nanosvg.h; sourceTree = SOURCE_ROOT; };
+ 6B58CB451019947700956BA2 /* tesselator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tesselator.h; path = ../../Include/tesselator.h; sourceTree = SOURCE_ROOT; };
+ 6B58CB461019948500956BA2 /* bucketalloc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = bucketalloc.c; path = ../../Source/bucketalloc.c; sourceTree = SOURCE_ROOT; };
+ 6B58CB471019948500956BA2 /* bucketalloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bucketalloc.h; path = ../../Source/bucketalloc.h; sourceTree = SOURCE_ROOT; };
+ 6B58CB481019948500956BA2 /* dict.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dict.c; path = ../../Source/dict.c; sourceTree = SOURCE_ROOT; };
+ 6B58CB491019948500956BA2 /* dict.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dict.h; path = ../../Source/dict.h; sourceTree = SOURCE_ROOT; };
+ 6B58CB4A1019948500956BA2 /* geom.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = geom.c; path = ../../Source/geom.c; sourceTree = SOURCE_ROOT; };
+ 6B58CB4B1019948500956BA2 /* geom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = geom.h; path = ../../Source/geom.h; sourceTree = SOURCE_ROOT; };
+ 6B58CB4C1019948500956BA2 /* mesh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mesh.c; path = ../../Source/mesh.c; sourceTree = SOURCE_ROOT; };
+ 6B58CB4D1019948500956BA2 /* mesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mesh.h; path = ../../Source/mesh.h; sourceTree = SOURCE_ROOT; };
+ 6B58CB4E1019948500956BA2 /* priorityq.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = priorityq.c; path = ../../Source/priorityq.c; sourceTree = SOURCE_ROOT; };
+ 6B58CB4F1019948500956BA2 /* priorityq.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = priorityq.h; path = ../../Source/priorityq.h; sourceTree = SOURCE_ROOT; };
+ 6B58CB501019948500956BA2 /* sweep.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sweep.c; path = ../../Source/sweep.c; sourceTree = SOURCE_ROOT; };
+ 6B58CB511019948500956BA2 /* sweep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sweep.h; path = ../../Source/sweep.h; sourceTree = SOURCE_ROOT; };
+ 6B58CB521019948500956BA2 /* tess.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = tess.c; path = ../../Source/tess.c; sourceTree = SOURCE_ROOT; };
+ 6B58CB531019948500956BA2 /* tess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tess.h; path = ../../Source/tess.h; sourceTree = SOURCE_ROOT; };
+ 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
+ 8D1107320486CEB800E47090 /* libtess2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = libtess2.app; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8D11072E0486CEB800E47090 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 002F39FA09D0881F00EBEB88 /* SDL.framework in Frameworks */,
+ 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
+ 6B33E3331235065700141A9B /* OpenGL.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 080E96DDFE201D6D7F000001 /* Classes */ = {
+ isa = PBXGroup;
+ children = (
+ 6B58CB0A1019935200956BA2 /* SDLMain.h */,
+ 6B58CB0B1019935200956BA2 /* SDLMain.m */,
+ );
+ name = Classes;
+ sourceTree = "<group>";
+ };
+ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 002F39F909D0881F00EBEB88 /* SDL.framework */,
+ 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
+ 6B33E3321235065700141A9B /* OpenGL.framework */,
+ );
+ name = "Linked Frameworks";
+ sourceTree = "<group>";
+ };
+ 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 29B97324FDCFA39411CA2CEA /* AppKit.framework */,
+ 29B97325FDCFA39411CA2CEA /* Foundation.framework */,
+ );
+ name = "Other Frameworks";
+ sourceTree = "<group>";
+ };
+ 19C28FACFE9D520D11CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8D1107320486CEB800E47090 /* libtess2.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 29B97314FDCFA39411CA2CEA /* libtess2 */ = {
+ isa = PBXGroup;
+ children = (
+ 6B58CB3D1019942B00956BA2 /* Libtess2 */,
+ 6B58CB101019938400956BA2 /* nanosvg.c */,
+ 6B58CB111019938400956BA2 /* nanosvg.h */,
+ 6B58CB091019935200956BA2 /* main.c */,
+ 080E96DDFE201D6D7F000001 /* Classes */,
+ 29B97315FDCFA39411CA2CEA /* Other Sources */,
+ 29B97317FDCFA39411CA2CEA /* Resources */,
+ 29B97323FDCFA39411CA2CEA /* Frameworks */,
+ 19C28FACFE9D520D11CA2CBB /* Products */,
+ );
+ name = libtess2;
+ sourceTree = "<group>";
+ };
+ 29B97315FDCFA39411CA2CEA /* Other Sources */ = {
+ isa = PBXGroup;
+ children = (
+ 32CA4F630368D1EE00C91783 /* libtess2_Prefix.pch */,
+ );
+ name = "Other Sources";
+ sourceTree = "<group>";
+ };
+ 29B97317FDCFA39411CA2CEA /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 8D1107310486CEB800E47090 /* Info.plist */,
+ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
+ 002F3AEF09D08F1000EBEB88 /* SDLMain.nib */,
+ );
+ name = Resources;
+ sourceTree = "<group>";
+ };
+ 29B97323FDCFA39411CA2CEA /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
+ 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 6B58CB3D1019942B00956BA2 /* Libtess2 */ = {
+ isa = PBXGroup;
+ children = (
+ 6B58CB461019948500956BA2 /* bucketalloc.c */,
+ 6B58CB471019948500956BA2 /* bucketalloc.h */,
+ 6B58CB481019948500956BA2 /* dict.c */,
+ 6B58CB491019948500956BA2 /* dict.h */,
+ 6B58CB4A1019948500956BA2 /* geom.c */,
+ 6B58CB4B1019948500956BA2 /* geom.h */,
+ 6B58CB4C1019948500956BA2 /* mesh.c */,
+ 6B58CB4D1019948500956BA2 /* mesh.h */,
+ 6B58CB4E1019948500956BA2 /* priorityq.c */,
+ 6B58CB4F1019948500956BA2 /* priorityq.h */,
+ 6B58CB501019948500956BA2 /* sweep.c */,
+ 6B58CB511019948500956BA2 /* sweep.h */,
+ 6B58CB521019948500956BA2 /* tess.c */,
+ 6B58CB531019948500956BA2 /* tess.h */,
+ 6B58CB451019947700956BA2 /* tesselator.h */,
+ );
+ name = Libtess2;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 8D1107260486CEB800E47090 /* libtess2 */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "libtess2" */;
+ buildPhases = (
+ 8D1107290486CEB800E47090 /* Resources */,
+ 8D11072C0486CEB800E47090 /* Sources */,
+ 8D11072E0486CEB800E47090 /* Frameworks */,
+ 002F39FD09D0883400EBEB88 /* Copy Frameworks into .app bundle */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = libtess2;
+ productInstallPath = "$(HOME)/Applications";
+ productName = libtess2;
+ productReference = 8D1107320486CEB800E47090 /* libtess2.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 29B97313FDCFA39411CA2CEA /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0410;
+ };
+ buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "libtess2" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 1;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 29B97314FDCFA39411CA2CEA /* libtess2 */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 8D1107260486CEB800E47090 /* libtess2 */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 8D1107290486CEB800E47090 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
+ 002F3AF109D08F1000EBEB88 /* SDLMain.nib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8D11072C0486CEB800E47090 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 6B58CB0C1019935200956BA2 /* main.c in Sources */,
+ 6B58CB0D1019935200956BA2 /* SDLMain.m in Sources */,
+ 6B58CB121019938400956BA2 /* nanosvg.c in Sources */,
+ 6B58CB541019948500956BA2 /* bucketalloc.c in Sources */,
+ 6B58CB551019948500956BA2 /* dict.c in Sources */,
+ 6B58CB561019948500956BA2 /* geom.c in Sources */,
+ 6B58CB571019948500956BA2 /* mesh.c in Sources */,
+ 6B58CB581019948500956BA2 /* priorityq.c in Sources */,
+ 6B58CB591019948500956BA2 /* sweep.c in Sources */,
+ 6B58CB5A1019948500956BA2 /* tess.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 002F3AEF09D08F1000EBEB88 /* SDLMain.nib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 002F3AF009D08F1000EBEB88 /* English */,
+ );
+ name = SDLMain.nib;
+ sourceTree = "<group>";
+ };
+ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 089C165DFE840E0CC02AAC07 /* English */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ C01FCF4B08A954540054247B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CONFIGURATION_BUILD_DIR = ../../Bin;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ PRODUCT_NAME = libtess2;
+ WRAPPER_EXTENSION = app;
+ ZERO_LINK = YES;
+ };
+ name = Debug;
+ };
+ C01FCF4C08A954540054247B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = (
+ ppc,
+ i386,
+ );
+ CONFIGURATION_BUILD_DIR = ../../Bin;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
+ GCC_MODEL_TUNING = G5;
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ PRODUCT_NAME = libtess2;
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+ C01FCF4F08A954540054247B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(HOME)/Library/Frameworks",
+ /Library/Frameworks,
+ "$(FRAMEWORK_SEARCH_PATHS)",
+ );
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = (
+ "$(HOME)/Library/Frameworks/SDL.framework/Headers",
+ /Library/Frameworks/SDL.framework/Headers,
+ "$(HEADER_SEARCH_PATHS)",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ SDKROOT = macosx10.6;
+ };
+ name = Debug;
+ };
+ C01FCF5008A954540054247B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(HOME)/Library/Frameworks",
+ /Library/Frameworks,
+ "$(FRAMEWORK_SEARCH_PATHS)",
+ );
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = (
+ "$(HOME)/Library/Frameworks/SDL.framework/Headers",
+ /Library/Frameworks/SDL.framework/Headers,
+ "$(HEADER_SEARCH_PATHS)",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ SDKROOT = macosx10.6;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "libtess2" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C01FCF4B08A954540054247B /* Debug */,
+ C01FCF4C08A954540054247B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ C01FCF4E08A954540054247B /* Build configuration list for PBXProject "libtess2" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C01FCF4F08A954540054247B /* Debug */,
+ C01FCF5008A954540054247B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}
diff --git a/Build/Xcode/libtess2.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Build/Xcode/libtess2.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..752d81d
--- /dev/null
+++ b/Build/Xcode/libtess2.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:libtess2.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/Build/Xcode/libtess2.xcodeproj/project.xcworkspace/xcuserdata/memon.xcuserdatad/UserInterfaceState.xcuserstate b/Build/Xcode/libtess2.xcodeproj/project.xcworkspace/xcuserdata/memon.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000..72981f3
--- /dev/null
+++ b/Build/Xcode/libtess2.xcodeproj/project.xcworkspace/xcuserdata/memon.xcuserdatad/UserInterfaceState.xcuserstate
@@ -0,0 +1,2073 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>$archiver</key>
+ <string>NSKeyedArchiver</string>
+ <key>$objects</key>
+ <array>
+ <string>$null</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>31</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>2</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>3</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>4</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>147</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>29D63372-45A9-453A-893B-7B4BB1455209</string>
+ <string>IDEWorkspaceDocument</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>5</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>6</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>7</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>8</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>9</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>10</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>11</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>12</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>13</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>145</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>146</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>50</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>2</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>5</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>22</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>50</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>IDEWorkspaceTabController_02CAE73A-C833-4DE0-A43B-9EFC376AD5B5</string>
+ <string>IDEWindowFrame</string>
+ <string>IDEOrderedWorkspaceTabControllers</string>
+ <string>IDEWindowInFullscreenMode</string>
+ <string>IDEWorkspaceWindowControllerUniqueIdentifier</string>
+ <string>IDEActiveWorkspaceTabController</string>
+ <string>IDEWindowToolbarIsVisible</string>
+ <string>IDEWindowTabBarIsVisible</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>14</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>15</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>16</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>17</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>18</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>19</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>20</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>21</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>22</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>23</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>36</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>74</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>50</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>83</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>AssistantEditorsLayout</string>
+ <string>IDEShowNavigator</string>
+ <string>IDEWorkspaceTabControllerUtilityAreaSplitView</string>
+ <string>IDENavigatorArea</string>
+ <string>IDEWorkspaceTabControllerDesignAreaSplitView</string>
+ <string>IDEShowUtilities</string>
+ <string>IDEEditorArea</string>
+ <integer>0</integer>
+ <true/>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>24</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>25</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>DVTSplitViewItems</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>34</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>26</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>32</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>31</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>27</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>28</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>29</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>30</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>DVTIdentifier</string>
+ <string>DVTViewMagnitude</string>
+ <string></string>
+ <real>442</real>
+ <dict>
+ <key>$classes</key>
+ <array>
+ <string>NSDictionary</string>
+ <string>NSObject</string>
+ </array>
+ <key>$classname</key>
+ <string>NSDictionary</string>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>31</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>27</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>28</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>29</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>33</integer>
+ </dict>
+ </array>
+ </dict>
+ <real>224</real>
+ <dict>
+ <key>$classes</key>
+ <array>
+ <string>NSMutableArray</string>
+ <string>NSArray</string>
+ <string>NSObject</string>
+ </array>
+ <key>$classname</key>
+ <string>NSMutableArray</string>
+ </dict>
+ <dict>
+ <key>$classes</key>
+ <array>
+ <string>NSMutableDictionary</string>
+ <string>NSDictionary</string>
+ <string>NSObject</string>
+ </array>
+ <key>$classname</key>
+ <string>NSMutableDictionary</string>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>37</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>38</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>39</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>40</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>37</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>58</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>Xcode.IDEKit.Navigator.Issues</string>
+ <string>SelectedNavigator</string>
+ <string>Xcode.IDEKit.Navigator.Structure</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>41</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>42</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>43</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>44</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>45</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>46</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>47</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>48</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>49</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>50</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>51</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>52</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>54</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>55</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>50</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>56</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>50</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>57</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>IDEErrorFilteringEnabled</string>
+ <string>IDEVisibleRect</string>
+ <string>IDECollapsedFiles</string>
+ <string>IDEExpandedIssues</string>
+ <string>IDESelectedNavigables</string>
+ <string>IDEShowsByType</string>
+ <string>IDECollapsedTypes</string>
+ <string>IDERecentFilteringEnabled</string>
+ <string>IDECollapsedGroups</string>
+ <false/>
+ <string>{{0, 0}, {259, 600}}</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>53</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array/>
+ </dict>
+ <dict>
+ <key>$classes</key>
+ <array>
+ <string>NSMutableSet</string>
+ <string>NSSet</string>
+ <string>NSObject</string>
+ </array>
+ <key>$classname</key>
+ <string>NSMutableSet</string>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>53</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array/>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>34</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array/>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>53</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array/>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>53</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array/>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>59</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>60</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>61</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>62</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>63</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>64</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>65</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>66</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>50</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>67</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>50</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>50</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>69</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>71</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>IDEVisibleRect</string>
+ <string>IDEUnsavedDocumentFilteringEnabled</string>
+ <string>IDENavigatorExpandedItemsBeforeFilteringSet</string>
+ <string>IDERecentDocumentFilteringEnabled</string>
+ <string>IDESCMStatusFilteringEnabled</string>
+ <string>IDESelectedObjects</string>
+ <string>IDEExpandedItemsSet</string>
+ <string>{{0, 0}, {259, 622}}</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>68</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array/>
+ </dict>
+ <dict>
+ <key>$classes</key>
+ <array>
+ <string>NSSet</string>
+ <string>NSObject</string>
+ </array>
+ <key>$classname</key>
+ <string>NSSet</string>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>70</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array/>
+ </dict>
+ <dict>
+ <key>$classes</key>
+ <array>
+ <string>NSArray</string>
+ <string>NSObject</string>
+ </array>
+ <key>$classname</key>
+ <string>NSArray</string>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>68</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>72</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>34</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>73</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>libtess2</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>24</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>75</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>34</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>76</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>78</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>80</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>31</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>27</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>28</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>17</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>77</integer>
+ </dict>
+ </array>
+ </dict>
+ <real>260</real>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>31</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>27</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>28</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>20</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>79</integer>
+ </dict>
+ </array>
+ </dict>
+ <real>1106</real>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>31</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>27</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>28</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>81</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>82</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>IDEUtilitiesArea</string>
+ <real>260</real>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>84</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>85</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>86</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>87</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>88</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>89</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>90</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>91</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>92</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>98</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>110</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>22</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>21</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>136</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>144</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>50</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>layoutTree</string>
+ <string>IDEEditorMode_Standard</string>
+ <string>IDEEDitorArea_DebugArea</string>
+ <string>IDEShowEditor</string>
+ <string>EditorMode</string>
+ <string>DebuggerSplitView</string>
+ <string>DefaultPersistentRepresentations</string>
+ <string>ShowDebuggerArea</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>97</integer>
+ </dict>
+ <key>geniusEditorContextNode</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>0</integer>
+ </dict>
+ <key>primaryEditorContextNode</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>93</integer>
+ </dict>
+ <key>rootLayoutTreeNode</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>94</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>96</integer>
+ </dict>
+ <key>children</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>0</integer>
+ </dict>
+ <key>contentType</key>
+ <integer>1</integer>
+ <key>documentArchivableRepresentation</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>0</integer>
+ </dict>
+ <key>orientation</key>
+ <integer>0</integer>
+ <key>parent</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>94</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>96</integer>
+ </dict>
+ <key>children</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>95</integer>
+ </dict>
+ <key>contentType</key>
+ <integer>0</integer>
+ <key>documentArchivableRepresentation</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>0</integer>
+ </dict>
+ <key>orientation</key>
+ <integer>0</integer>
+ <key>parent</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>70</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>93</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$classes</key>
+ <array>
+ <string>IDEWorkspaceTabControllerLayoutTreeNode</string>
+ <string>NSObject</string>
+ </array>
+ <key>$classname</key>
+ <string>IDEWorkspaceTabControllerLayoutTreeNode</string>
+ </dict>
+ <dict>
+ <key>$classes</key>
+ <array>
+ <string>IDEWorkspaceTabControllerLayoutTree</string>
+ <string>NSObject</string>
+ </array>
+ <key>$classname</key>
+ <string>IDEWorkspaceTabControllerLayoutTree</string>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>99</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>100</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>EditorLayout_PersistentRepresentation</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>101</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>102</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>Main</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>31</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>103</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>104</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>105</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>106</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>21</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>108</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>EditorLayout_StateSavingStateDictionaries</string>
+ <string>EditorLayout_Selected</string>
+ <string>EditorLayout_Geometry</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>70</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>107</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array/>
+ <key>NS.objects</key>
+ <array/>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>70</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>109</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>{{0, 0}, {1106, 666}}</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>111</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>112</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>113</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>114</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>115</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>116</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>117</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>118</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>120</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>117</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>122</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>130</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>LayoutFocusMode</string>
+ <string>console</string>
+ <string>variables</string>
+ <string>LayoutMode</string>
+ <string>IDEDebugArea_SplitView</string>
+ <string>IDEDebuggerAreaSplitView</string>
+ <integer>1</integer>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>119</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>21</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>ConsoleFilterMode</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>121</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>117</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>VariablesViewSelectedScope</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>24</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>123</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>34</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>124</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>127</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>31</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>27</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>28</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>125</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>126</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>VariablesView</string>
+ <real>555</real>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>31</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>27</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>28</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>128</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>129</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>ConsoleArea</string>
+ <real>550</real>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>24</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>131</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>34</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>132</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>134</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>31</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>27</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>28</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>125</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>133</integer>
+ </dict>
+ </array>
+ </dict>
+ <real>555</real>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>31</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>27</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>28</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>128</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>135</integer>
+ </dict>
+ </array>
+ </dict>
+ <real>550</real>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>24</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>137</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>34</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>138</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>141</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>31</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>27</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>28</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>139</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>140</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>IDEEditor</string>
+ <real>203</real>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>31</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>27</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>28</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>142</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>143</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>IDEDebuggerArea</string>
+ <real>115</real>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array/>
+ <key>NS.objects</key>
+ <array/>
+ </dict>
+ <string>{{0, 4}, {1366, 742}}</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>70</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>5</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>148</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>149</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>150</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>151</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>152</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>153</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>154</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>155</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>156</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>157</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>50</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>158</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>21</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>159</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>164</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>167</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>199</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>200</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>50</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>50</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>BreakpointsActivated</string>
+ <string>DefaultEditorStatesForURLs</string>
+ <string>DebuggingWindowBehavior</string>
+ <string>ActiveRunDestination</string>
+ <string>ActiveScheme</string>
+ <string>LastCompletedPersistentSchemeBasedActivityReport</string>
+ <string>DocumentWindows</string>
+ <string>RecentEditorDocumentURLs</string>
+ <string>AppFocusInMiniDebugging</string>
+ <string>MiniDebuggingConsole</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array/>
+ <key>NS.objects</key>
+ <array/>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>160</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>161</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>162</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>163</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>IDEDeviceLocation</string>
+ <string>IDEDeviceArchitecture</string>
+ <string>dvtdevice-local-computer:localhost</string>
+ <string>i386</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>165</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>166</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>IDENameString</string>
+ <string>libtess2</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>168</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>169</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>170</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>171</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>197</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>198</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>IDEActivityReportCompletionSummaryStringSegments</string>
+ <string>IDEActivityReportOptions</string>
+ <string>IDEActivityReportTitle</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>34</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>172</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>179</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>183</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>188</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>173</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>174</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>175</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>176</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>177</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>178</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>IDEActivityReportStringSegmentPriority</string>
+ <string>IDEActivityReportStringSegmentBackSeparator</string>
+ <string>IDEActivityReportStringSegmentStringValue</string>
+ <real>2</real>
+ <string> </string>
+ <string>Build</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>173</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>174</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>175</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>180</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>181</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>182</integer>
+ </dict>
+ </array>
+ </dict>
+ <real>4</real>
+ <string>: </string>
+ <string>libtess2</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>173</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>174</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>175</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>184</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>185</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>186</integer>
+ </dict>
+ </array>
+ </dict>
+ <real>1</real>
+ <string> │ </string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>187</integer>
+ </dict>
+ <key>NS.data</key>
+ <data>
+ YnBsaXN0MDDUAQIDBAUGOzxYJHZlcnNpb25YJG9iamVjdHNZJGFy
+ Y2hpdmVyVCR0b3ASAAGGoK0HCA8QGhscJCUrMTQ3VSRudWxs0wkK
+ CwwNDlxOU0F0dHJpYnV0ZXNWJGNsYXNzWE5TU3RyaW5ngAOADIAC
+ WVN1Y2NlZWRlZNMKERITFBdXTlMua2V5c1pOUy5vYmplY3RzgAui
+ FRaABIAFohgZgAaACVZOU0ZvbnRXTlNDb2xvctQKHR4fICEiI1ZO
+ U05hbWVWTlNTaXplWE5TZkZsYWdzgAiAByNAJgAAAAAAABENEF8Q
+ EUx1Y2lkYUdyYW5kZS1Cb2xk0iYnKClaJGNsYXNzbmFtZVgkY2xh
+ c3Nlc1ZOU0ZvbnSiKCpYTlNPYmplY3TTCiwtLi8wXE5TQ29sb3JT
+ cGFjZVdOU1doaXRlgAoQA0IwANImJzIzV05TQ29sb3KiMirSJic1
+ NlxOU0RpY3Rpb25hcnmiNSrSJic4OV8QEk5TQXR0cmlidXRlZFN0
+ cmluZ6I6Kl8QEk5TQXR0cmlidXRlZFN0cmluZ18QD05TS2V5ZWRB
+ cmNoaXZlctE9PlRyb290gAEACAARABoAIwAtADIANwBFAEsAUgBf
+ AGYAbwBxAHMAdQB/AIYAjgCZAJsAngCgAKIApQCnAKkAsAC4AMEA
+ yADPANgA2gDcAOUA6AD8AQEBDAEVARwBHwEoAS8BPAFEAUYBSAFL
+ AVABWAFbAWABbQFwAXUBigGNAaIBtAG3AbwAAAAAAAACAQAAAAAA
+ AAA/AAAAAAAAAAAAAAAAAAABvg==
+ </data>
+ </dict>
+ <dict>
+ <key>$classes</key>
+ <array>
+ <string>NSMutableData</string>
+ <string>NSData</string>
+ <string>NSObject</string>
+ </array>
+ <key>$classname</key>
+ <string>NSMutableData</string>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>35</integer>
+ </dict>
+ <key>NS.keys</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>173</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>189</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>190</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>175</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>191</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>192</integer>
+ </dict>
+ </array>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>193</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>117</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>194</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>196</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>117</integer>
+ </dict>
+ <dict>
+ <key>CF$UID</key>
+ <integer>117</integer>
+ </dict>
+ </array>
+ </dict>
+ <string>IDEActivityReportStringSegmentType</string>
+ <string>IDEActivityReportStringSegmentDate</string>
+ <string>IDEActivityReportStringSegmentDateStyle</string>
+ <string>IDEActivityReportStringSegmentTimeStyle</string>
+ <real>3</real>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>195</integer>
+ </dict>
+ <key>NS.time</key>
+ <real>400540032.95346999</real>
+ </dict>
+ <dict>
+ <key>$classes</key>
+ <array>
+ <string>NSDate</string>
+ <string>NSObject</string>
+ </array>
+ <key>$classname</key>
+ <string>NSDate</string>
+ </dict>
+ <string>Today at 23:07</string>
+ <integer>234</integer>
+ <string>libtess2</string>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>34</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array>
+ <dict>
+ <key>CF$UID</key>
+ <integer>2</integer>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>$class</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>34</integer>
+ </dict>
+ <key>NS.objects</key>
+ <array/>
+ </dict>
+ </array>
+ <key>$top</key>
+ <dict>
+ <key>State</key>
+ <dict>
+ <key>CF$UID</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ <key>$version</key>
+ <integer>100000</integer>
+</dict>
+</plist>
diff --git a/Build/Xcode/libtess2.xcodeproj/xcuserdata/memon.xcuserdatad/xcschemes/libtess2.xcscheme b/Build/Xcode/libtess2.xcodeproj/xcuserdata/memon.xcuserdatad/xcschemes/libtess2.xcscheme
new file mode 100644
index 0000000..b6cf18d
--- /dev/null
+++ b/Build/Xcode/libtess2.xcodeproj/xcuserdata/memon.xcuserdatad/xcschemes/libtess2.xcscheme
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "8D1107260486CEB800E47090"
+ BuildableName = "libtess2.app"
+ BlueprintName = "libtess2"
+ ReferencedContainer = "container:libtess2.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ buildConfiguration = "Debug">
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Debug">
+ <BuildableProductRunnable>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "8D1107260486CEB800E47090"
+ BuildableName = "libtess2.app"
+ BlueprintName = "libtess2"
+ ReferencedContainer = "container:libtess2.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Release">
+ <BuildableProductRunnable>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "8D1107260486CEB800E47090"
+ BuildableName = "libtess2.app"
+ BlueprintName = "libtess2"
+ ReferencedContainer = "container:libtess2.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/Build/Xcode/libtess2.xcodeproj/xcuserdata/memon.xcuserdatad/xcschemes/xcschememanagement.plist b/Build/Xcode/libtess2.xcodeproj/xcuserdata/memon.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..7ce5e7c
--- /dev/null
+++ b/Build/Xcode/libtess2.xcodeproj/xcuserdata/memon.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SchemeUserState</key>
+ <dict>
+ <key>libtess2.xcscheme</key>
+ <dict>
+ <key>orderHint</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>8D1107260486CEB800E47090</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/Build/Xcode/libtess2_Prefix.pch b/Build/Xcode/libtess2_Prefix.pch
new file mode 100644
index 0000000..9ebeccb
--- /dev/null
+++ b/Build/Xcode/libtess2_Prefix.pch
@@ -0,0 +1,9 @@
+//
+// Prefix header for all source files of the 'libtess2' target in the 'libtess2' project
+//
+
+#include "SDL.h"
+
+#ifdef __OBJC__
+ #import <Cocoa/Cocoa.h>
+#endif
diff --git a/Contrib/SDL/readme.txt b/Contrib/SDL/readme.txt
new file mode 100644
index 0000000..732aca3
--- /dev/null
+++ b/Contrib/SDL/readme.txt
@@ -0,0 +1,6 @@
+
+Windows
+Download SDL Developer Libraries from http://www.libsdl.org and unzip them here.
+
+OSX
+Download and install OSX SDL Developer Libraries from http://www.libsdl.org
\ No newline at end of file
diff --git a/Contrib/nanosvg.c b/Contrib/nanosvg.c
new file mode 100644
index 0000000..d3abba6
--- /dev/null
+++ b/Contrib/nanosvg.c
@@ -0,0 +1,1335 @@
+//
+// Copyright (c) 2009 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+// The SVG parser is based on Anti-Graim Geometry SVG example
+// Copyright (C) 2002-2004 Maxim Shemanarev (McSeem)
+
+#include "nanosvg.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <ctype.h>
+
+#ifndef M_PI
+ #define M_PI 3.14159265358979323846264338327
+#endif
+
+#ifdef _MSC_VER
+ #pragma warning (disable: 4996) // Switch off security warnings
+#endif
+
+// Simple XML parser
+
+#define TAG 1
+#define CONTENT 2
+#define MAX_ATTRIBS 256
+
+static void parseContent(char* s,
+ void (*contentCb)(void* ud, const char* s),
+ void* ud)
+{
+ // Trim start white spaces
+ while (*s && isspace(*s)) s++;
+ if (!*s) return;
+
+ if (contentCb)
+ (*contentCb)(ud, s);
+}
+
+static void parseElement(char* s,
+ void (*startelCb)(void* ud, const char* el, const char** attr),
+ void (*endelCb)(void* ud, const char* el),
+ void* ud)
+{
+ const char* attr[MAX_ATTRIBS];
+ int nattr = 0;
+ char* name;
+ int start = 0;
+ int end = 0;
+
+ // Skip white space after the '<'
+ while (*s && isspace(*s)) s++;
+
+ // Check if the tag is end tag
+ if (*s == '/')
+ {
+ s++;
+ end = 1;
+ }
+ else
+ {
+ start = 1;
+ }
+
+ // Skip comments, data and preprocessor stuff.
+ if (!*s || *s == '?' || *s == '!')
+ return;
+
+ // Get tag name
+ name = s;
+ while (*s && !isspace(*s)) s++;
+ if (*s) { *s++ = '\0'; }
+
+ // Get attribs
+ while (!end && *s && nattr < MAX_ATTRIBS-1)
+ {
+ // Skip white space before the attrib name
+ while (*s && isspace(*s)) s++;
+ if (!*s) break;
+ if (*s == '/')
+ {
+ end = 1;
+ break;
+ }
+ attr[nattr++] = s;
+ // Find end of the attrib name.
+ while (*s && !isspace(*s) && *s != '=') s++;
+ if (*s) { *s++ = '\0'; }
+ // Skip until the beginning of the value.
+ while (*s && *s != '\"') s++;
+ if (!*s) break;
+ s++;
+ // Store value and find the end of it.
+ attr[nattr++] = s;
+ while (*s && *s != '\"') s++;
+ if (*s) { *s++ = '\0'; }
+ }
+
+ // List terminator
+ attr[nattr++] = 0;
+ attr[nattr++] = 0;
+
+ // Call callbacks.
+ if (start && startelCb)
+ (*startelCb)(ud, name, attr);
+ if (end && endelCb)
+ (*endelCb)(ud, name);
+}
+
+int parsexml(char* input,
+ void (*startelCb)(void* ud, const char* el, const char** attr),
+ void (*endelCb)(void* ud, const char* el),
+ void (*contentCb)(void* ud, const char* s),
+ void* ud)
+{
+ char* s = input;
+ char* mark = s;
+ int state = CONTENT;
+ while (*s)
+ {
+ if (*s == '<' && state == CONTENT)
+ {
+ // Start of a tag
+ *s++ = '\0';
+ parseContent(mark, contentCb, ud);
+ mark = s;
+ state = TAG;
+ }
+ else if (*s == '>' && state == TAG)
+ {
+ // Start of a content or new tag.
+ *s++ = '\0';
+ parseElement(mark, startelCb, endelCb, ud);
+ mark = s;
+ state = CONTENT;
+ }
+ else
+ s++;
+ }
+
+ return 1;
+}
+
+
+/* Simple SVG parser. */
+
+#define SVG_MAX_ATTR 128
+
+struct SVGAttrib
+{
+ float xform[6];
+ unsigned int fillColor;
+ unsigned int strokeColor;
+ float fillOpacity;
+ float strokeOpacity;
+ float strokeWidth;
+ char hasFill;
+ char hasStroke;
+ char visible;
+};
+
+struct SVGParser
+{
+ struct SVGAttrib attr[SVG_MAX_ATTR];
+ int attrHead;
+ float* buf;
+ int nbuf;
+ int cbuf;
+ struct SVGPath* plist;
+ char pathFlag;
+ char defsFlag;
+ float tol;
+};
+
+static void xformSetIdentity(float* t)
+{
+ t[0] = 1.0f; t[1] = 0.0f;
+ t[2] = 0.0f; t[3] = 1.0f;
+ t[4] = 0.0f; t[5] = 0.0f;
+}
+
+static void xformSetTranslation(float* t, float tx, float ty)
+{
+ t[0] = 1.0f; t[1] = 0.0f;
+ t[2] = 0.0f; t[3] = 1.0f;
+ t[4] = tx; t[5] = ty;
+}
+
+static void xformSetScale(float* t, float sx, float sy)
+{
+ t[0] = sx; t[1] = 0.0f;
+ t[2] = 0.0f; t[3] = sy;
+ t[4] = 0.0f; t[5] = 0.0f;
+}
+
+static void xformMultiply(float* t, float* s)
+{
+ float t0 = t[0] * s[0] + t[1] * s[2];
+ float t2 = t[2] * s[0] + t[3] * s[2];
+ float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
+ t[1] = t[0] * s[1] + t[1] * s[3];
+ t[3] = t[2] * s[1] + t[3] * s[3];
+ t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
+ t[0] = t0;
+ t[2] = t2;
+ t[4] = t4;
+}
+
+static void xformPremultiply(float* t, float* s)
+{
+ float s2[6];
+ memcpy(s2, s, sizeof(float)*6);
+ xformMultiply(s2, t);
+ memcpy(t, s2, sizeof(float)*6);
+}
+
+static struct SVGParser* svgCreateParser()
+{
+ struct SVGParser* p;
+ p = (struct SVGParser*)malloc(sizeof(struct SVGParser));
+ if (!p)
+ return NULL;
+ memset(p, 0, sizeof(struct SVGParser));
+
+ // Init style
+ xformSetIdentity(p->attr[0].xform);
+ p->attr[0].fillColor = 0;
+ p->attr[0].strokeColor = 0;
+ p->attr[0].fillOpacity = 1;
+ p->attr[0].strokeOpacity = 1;
+ p->attr[0].strokeWidth = 1;
+ p->attr[0].hasFill = 0;
+ p->attr[0].hasStroke = 0;
+ p->attr[0].visible = 1;
+
+ return p;
+}
+
+static void svgDeleteParser(struct SVGParser* p)
+{
+ struct SVGPath* path;
+ struct SVGPath* next;
+ path = p->plist;
+ while (path)
+ {
+ next = path->next;
+ if (path->pts)
+ free(path->pts);
+ free(path);
+ path = next;
+ }
+ if (p->buf)
+ free(p->buf);
+ free(p);
+}
+
+static void svgResetPath(struct SVGParser* p)
+{
+ p->nbuf = 0;
+}
+
+static void svgPathPoint(struct SVGParser* p, float x, float y)
+{
+ int cap;
+ float* buf;
+ if (p->nbuf+1 > p->cbuf)
+ {
+ cap = p->cbuf ? p->cbuf*2 : 8;
+ buf = (float*)malloc(cap*2*sizeof(float));
+ if (!buf) return;
+ if (p->nbuf)
+ memcpy(buf, p->buf, p->nbuf*2*sizeof(float));
+ if (p->buf)
+ free(p->buf);
+ p->buf = buf;
+ p->cbuf = cap;
+ }
+ p->buf[p->nbuf*2+0] = x;
+ p->buf[p->nbuf*2+1] = y;
+ p->nbuf++;
+}
+
+static struct SVGAttrib* svgGetAttr(struct SVGParser* p)
+{
+ return &p->attr[p->attrHead];
+}
+
+static void svgPushAttr(struct SVGParser* p)
+{
+ if (p->attrHead < SVG_MAX_ATTR-1)
+ {
+ p->attrHead++;
+ memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(struct SVGAttrib));
+ }
+}
+
+static void svgPopAttr(struct SVGParser* p)
+{
+ if (p->attrHead > 0)
+ p->attrHead--;
+}
+
+static void svgCreatePath(struct SVGParser* p, char closed)
+{
+ float* t;
+ float* pt;
+ struct SVGAttrib* attr;
+ struct SVGPath* path;
+ int i;
+
+ if (!p)
+ return;
+
+ if (!p->nbuf)
+ {
+ return;
+ }
+
+ attr = svgGetAttr(p);
+
+ path = (struct SVGPath*)malloc(sizeof(struct SVGPath));
+ if (!path)
+ return;
+ memset(path, 0, sizeof(struct SVGPath));
+ path->pts = (float*)malloc(p->nbuf*2*sizeof(float));
+ if (!path->pts)
+ {
+ free(path);
+ return;
+ }
+ path->closed = closed;
+ path->npts = p->nbuf;
+
+ path->next = p->plist;
+ p->plist = path;
+
+ // Transform path.
+ t = attr->xform;
+ for (i = 0; i < p->nbuf; ++i)
+ {
+ pt = &p->buf[i*2];
+ path->pts[i*2+0] = pt[0]*t[0] + pt[1]*t[2] + t[4];
+ path->pts[i*2+1] = pt[0]*t[1] + pt[1]*t[3] + t[5];
+ }
+
+ path->hasFill = attr->hasFill;
+ path->hasStroke = attr->hasStroke;
+ path->strokeWidth = attr->strokeWidth * t[0];
+
+ path->fillColor = attr->fillColor;
+ if (path->hasFill)
+ path->fillColor |= (unsigned int)(attr->fillOpacity*255) << 24;
+
+ path->strokeColor = attr->strokeColor;
+ if (path->hasStroke)
+ path->strokeColor |= (unsigned int)(attr->strokeOpacity*255) << 24;
+}
+
+static int isnum(char c)
+{
+ return strchr("0123456789+-.eE", c) != 0;
+}
+
+/*static const char* parsePathFloats(const char* s, float* arg, int n)
+{
+ char num[64];
+ const char* start;
+ int nnum;
+ int i = 0;
+ while (*s && i < n)
+ {
+ // Skip white spaces and commas
+ while (*s && (isspace(*s) || *s == ',')) s++;
+ if (!*s) break;
+ start = s;
+ nnum = 0;
+ while (*s && isnum(*s))
+ {
+ if (nnum < 63) num[nnum++] = *s;
+ s++;
+ }
+ num[nnum] = '\0';
+ arg[i++] = (float)atof(num);
+ }
+ return s;
+}*/
+
+
+static const char* getNextPathItem(const char* s, char* it)
+{
+ int i = 0;
+ it[0] = '\0';
+ // Skip white spaces and commas
+ while (*s && (isspace(*s) || *s == ',')) s++;
+ if (!*s) return s;
+ if (*s == '-' || *s == '+' || isnum(*s))
+ {
+ while (*s == '-' || *s == '+')
+ {
+ if (i < 63) it[i++] = *s;
+ s++;
+ }
+ while (*s && *s != '-' && *s != '+' && isnum(*s))
+ {
+ if (i < 63) it[i++] = *s;
+ s++;
+ }
+ it[i] = '\0';
+ }
+ else
+ {
+ it[0] = *s++;
+ it[1] = '\0';
+ return s;
+ }
+ return s;
+}
+
+
+static unsigned int parseColor(const char* str)
+{
+ unsigned c = 0;
+ while(*str == ' ') ++str;
+ if (*str == '#')
+ sscanf(str + 1, "%x", &c);
+ return c;
+}
+
+static float parseFloat(const char* str)
+{
+ while (*str == ' ') ++str;
+ return (float)atof(str);
+}
+
+static int parseTransformArgs(const char* str, float* args, int maxNa, int* na)
+{
+ const char* end;
+ const char* ptr;
+
+ *na = 0;
+ ptr = str;
+ while (*ptr && *ptr != '(') ++ptr;
+ if (*ptr == 0)
+ return 1;
+ end = ptr;
+ while (*end && *end != ')') ++end;
+ if (*end == 0)
+ return 1;
+
+ while (ptr < end)
+ {
+ if (isnum(*ptr))
+ {
+ if (*na >= maxNa) return 0;
+ args[(*na)++] = (float)atof(ptr);
+ while (ptr < end && isnum(*ptr)) ++ptr;
+ }
+ else
+ {
+ ++ptr;
+ }
+ }
+ return (int)(end - str);
+}
+
+static int svgParseMatrix(struct SVGParser* p, const char* str)
+{
+ float t[6];
+ int na = 0;
+ int len = parseTransformArgs(str, t, 6, &na);
+ if (na != 6) return len;
+ xformPremultiply(svgGetAttr(p)->xform, t);
+ return len;
+}
+
+static int svgParseTranslate(struct SVGParser* p, const char* str)
+{
+ float args[2];
+ float t[6];
+ int na = 0;
+ int len = parseTransformArgs(str, args, 2, &na);
+ if (na == 1) args[1] = 0.0;
+ xformSetTranslation(t, args[0], args[1]);
+ xformPremultiply(svgGetAttr(p)->xform, t);
+ return len;
+}
+
+static int svgParseScale(struct SVGParser* p, const char* str)
+{
+ float args[2];
+ int na = 0;
+ float t[6];
+ int len = parseTransformArgs(str, args, 2, &na);
+ if (na == 1) args[1] = args[0];
+ xformSetScale(t, args[0], args[1]);
+ xformPremultiply(svgGetAttr(p)->xform, t);
+ return len;
+}
+
+static void svgParseTransform(struct SVGParser* p, const char* str)
+{
+ while (*str)
+ {
+ if (strncmp(str, "matrix", 6) == 0)
+ str += svgParseMatrix(p, str);
+ else if (strncmp(str, "translate", 9) == 0)
+ str += svgParseTranslate(p, str);
+ else if (strncmp(str, "scale", 5) == 0)
+ str += svgParseScale(p, str);
+ else
+ ++str;
+ }
+}
+
+static void svgParseStyle(struct SVGParser* p, const char* str);
+
+static int svgParseAttr(struct SVGParser* p, const char* name, const char* value)
+{
+ struct SVGAttrib* attr = svgGetAttr(p);
+ if (!attr) return 0;
+
+ if (strcmp(name, "style") == 0)
+ {
+ svgParseStyle(p, value);
+ }
+ else if (strcmp(name, "display") == 0)
+ {
+ if (strcmp(value, "none") == 0)
+ attr->visible = 0;
+ else
+ attr->visible = 1;
+ }
+ else if (strcmp(name, "fill") == 0)
+ {
+ if (strcmp(value, "none") == 0)
+ {
+ attr->hasFill = 0;
+ }
+ else
+ {
+ attr->hasFill = 1;
+ attr->fillColor = parseColor(value);
+ }
+ }
+ else if (strcmp(name, "fill-opacity") == 0)
+ {
+ attr->fillOpacity = parseFloat(value);
+ }
+ else if (strcmp(name, "stroke") == 0)
+ {
+ if (strcmp(value, "none") == 0)
+ {
+ attr->hasStroke = 0;
+ }
+ else
+ {
+ attr->hasStroke = 1;
+ attr->strokeColor = parseColor(value);
+ }
+ }
+ else if (strcmp(name, "stroke-width") == 0)
+ {
+ attr->strokeWidth = parseFloat(value);
+ }
+ else if (strcmp(name, "stroke-opacity") == 0)
+ {
+ attr->strokeOpacity = parseFloat(value);
+ }
+ else if (strcmp(name, "transform") == 0)
+ {
+ svgParseTransform(p, value);
+ }
+ else
+ {
+ return 0;
+ }
+ return 1;
+}
+
+static int svgParseNameValue(struct SVGParser* p, const char* start, const char* end)
+{
+ const char* str;
+ const char* val;
+ char name[512];
+ char value[512];
+ int n;
+
+ str = start;
+ while (str < end && *str != ':') ++str;
+
+ val = str;
+
+ // Right Trim
+ while (str > start && (*str == ':' || isspace(*str))) --str;
+ ++str;
+
+ n = (int)(str - start);
+ if (n > 511) n = 511;
+ if (n) memcpy(name, start, n);
+ name[n] = 0;
+
+ while (val < end && (*val == ':' || isspace(*val))) ++val;
+
+ n = (int)(end - val);
+ if (n > 511) n = 511;
+ if (n) memcpy(value, val, n);
+ value[n] = 0;
+
+ return svgParseAttr(p, name, value);
+}
+
+static void svgParseStyle(struct SVGParser* p, const char* str)
+{
+ const char* start;
+ const char* end;
+
+ while (*str)
+ {
+ // Left Trim
+ while(*str && isspace(*str)) ++str;
+ start = str;
+ while(*str && *str != ';') ++str;
+ end = str;
+
+ // Right Trim
+ while (end > start && (*end == ';' || isspace(*end))) --end;
+ ++end;
+
+ svgParseNameValue(p, start, end);
+ if (*str) ++str;
+ }
+}
+
+static void svgParseAttribs(struct SVGParser* p, const char** attr)
+{
+ int i;
+ for (i = 0; attr[i]; i += 2)
+ {
+ if (strcmp(attr[i], "style") == 0)
+ svgParseStyle(p, attr[i + 1]);
+ else
+ svgParseAttr(p, attr[i], attr[i + 1]);
+ }
+}
+
+static int getArgsPerElement(char cmd)
+{
+ switch (tolower(cmd))
+ {
+ case 'v':
+ case 'h':
+ return 1;
+ case 'm':
+ case 'l':
+ case 't':
+ return 2;
+ case 'q':
+ case 's':
+ return 4;
+ case 'c':
+ return 6;
+ case 'a':
+ return 7;
+ }
+ return 0;
+}
+
+static float distPtSeg(float x, float y, float px, float py, float qx, float qy)
+{
+ float pqx, pqy, dx, dy, d, t;
+ pqx = qx-px;
+ pqy = qy-py;
+ dx = x-px;
+ dy = y-py;
+ d = pqx*pqx + pqy*pqy;
+ t = pqx*dx + pqy*dy;
+ if (d > 0) t /= d;
+ if (t < 0) t = 0;
+ else if (t > 1) t = 1;
+ dx = px + t*pqx - x;
+ dy = py + t*pqy - y;
+ return dx*dx + dy*dy;
+}
+
+static void cubicBezRec(struct SVGParser* p,
+ float x1, float y1, float x2, float y2,
+ float x3, float y3, float x4, float y4,
+ int level)
+{
+ float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
+ float d;
+
+ if (level > 12) return;
+
+ x12 = (x1+x2)*0.5f;
+ y12 = (y1+y2)*0.5f;
+ x23 = (x2+x3)*0.5f;
+ y23 = (y2+y3)*0.5f;
+ x34 = (x3+x4)*0.5f;
+ y34 = (y3+y4)*0.5f;
+ x123 = (x12+x23)*0.5f;
+ y123 = (y12+y23)*0.5f;
+ x234 = (x23+x34)*0.5f;
+ y234 = (y23+y34)*0.5f;
+ x1234 = (x123+x234)*0.5f;
+ y1234 = (y123+y234)*0.5f;
+
+ d = distPtSeg(x1234, y1234, x1,y1, x4,y4);
+ if (level > 0 && d < p->tol*p->tol)
+ {
+ svgPathPoint(p, x1234, y1234);
+ return;
+ }
+
+ cubicBezRec(p, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1);
+ cubicBezRec(p, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1);
+}
+
+static void cubicBez(struct SVGParser* p,
+ float x1, float y1, float cx1, float cy1,
+ float cx2, float cy2, float x2, float y2)
+{
+ cubicBezRec(p, x1,y1, cx1,cy1, cx2,cy2, x2,y2, 0);
+ svgPathPoint(p, x2, y2);
+}
+
+static void quadBezRec(struct SVGParser* p,
+ float x1, float y1, float x2, float y2, float x3, float y3,
+ int level)
+{
+ float x12,y12,x23,y23,x123,y123,d;
+
+ if (level > 12) return;
+
+ x12 = (x1+x2)*0.5f;
+ y12 = (y1+y2)*0.5f;
+ x23 = (x2+x3)*0.5f;
+ y23 = (y2+y3)*0.5f;
+ x123 = (x12+x23)*0.5f;
+ y123 = (y12+y23)*0.5f;
+
+ d = distPtSeg(x123, y123, x1,y1, x3,y3);
+ if (level > 0 && d < p->tol*p->tol)
+ {
+ svgPathPoint(p, x123, y123);
+ return;
+ }
+
+ quadBezRec(p, x1,y1, x12,y12, x123,y123, level+1);
+ quadBezRec(p, x123,y123, x23,y23, x3,y3, level+1);
+}
+
+static void quadBez(struct SVGParser* p,
+ float x1, float y1, float cx, float cy, float x2, float y2)
+{
+ quadBezRec(p, x1,y1, cx,cy, x2,y2, 0);
+ svgPathPoint(p, x2, y2);
+}
+
+static void pathLineTo(struct SVGParser* p, float* cpx, float* cpy, float* args, int rel)
+{
+ if (rel)
+ {
+ *cpx += args[0];
+ *cpy += args[1];
+ }
+ else
+ {
+ *cpx = args[0];
+ *cpy = args[1];
+ }
+ svgPathPoint(p, *cpx, *cpy);
+}
+
+static void pathHLineTo(struct SVGParser* p, float* cpx, float* cpy, float* args, int rel)
+{
+ if (rel)
+ *cpx += args[0];
+ else
+ *cpx = args[0];
+ svgPathPoint(p, *cpx, *cpy);
+}
+
+static void pathVLineTo(struct SVGParser* p, float* cpx, float* cpy, float* args, int rel)
+{
+ if (rel)
+ *cpy += args[0];
+ else
+ *cpy = args[0];
+ svgPathPoint(p, *cpx, *cpy);
+}
+
+static void pathCubicBezTo(struct SVGParser* p, float* cpx, float* cpy,
+ float* cpx2, float* cpy2, float* args, int rel)
+{
+ float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
+
+ x1 = *cpx;
+ y1 = *cpy;
+ if (rel)
+ {
+ cx1 = *cpx + args[0];
+ cy1 = *cpy + args[1];
+ cx2 = *cpx + args[2];
+ cy2 = *cpy + args[3];
+ x2 = *cpx + args[4];
+ y2 = *cpy + args[5];
+ }
+ else
+ {
+ cx1 = args[0];
+ cy1 = args[1];
+ cx2 = args[2];
+ cy2 = args[3];
+ x2 = args[4];
+ y2 = args[5];
+ }
+
+ cubicBez(p, x1,y1, cx1,cy1, cx2,cy2, x2,y2);
+
+ *cpx2 = cx2;
+ *cpy2 = cy2;
+ *cpx = x2;
+ *cpy = y2;
+}
+
+static void pathCubicBezShortTo(struct SVGParser* p, float* cpx, float* cpy,
+ float* cpx2, float* cpy2, float* args, int rel)
+{
+ float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
+
+ x1 = *cpx;
+ y1 = *cpy;
+ if (rel)
+ {
+ cx2 = *cpx + args[0];
+ cy2 = *cpy + args[1];
+ x2 = *cpx + args[2];
+ y2 = *cpy + args[3];
+ }
+ else
+ {
+ cx2 = args[0];
+ cy2 = args[1];
+ x2 = args[2];
+ y2 = args[3];
+ }
+
+ cx1 = 2*x1 - *cpx2;
+ cy1 = 2*y1 - *cpy2;
+
+ cubicBez(p, x1,y1, cx1,cy1, cx2,cy2, x2,y2);
+
+ *cpx2 = cx2;
+ *cpy2 = cy2;
+ *cpx = x2;
+ *cpy = y2;
+}
+
+static void pathQuadBezTo(struct SVGParser* p, float* cpx, float* cpy,
+ float* cpx2, float* cpy2, float* args, int rel)
+{
+ float x1, y1, x2, y2, cx, cy;
+
+ x1 = *cpx;
+ y1 = *cpy;
+ if (rel)
+ {
+ cx = *cpx + args[0];
+ cy = *cpy + args[1];
+ x2 = *cpx + args[2];
+ y2 = *cpy + args[3];
+ }
+ else
+ {
+ cx = args[0];
+ cy = args[1];
+ x2 = args[2];
+ y2 = args[3];
+ }
+
+ quadBez(p, x1,y1, cx,cy, x2,y2);
+
+ *cpx2 = cx;
+ *cpy2 = cy;
+ *cpx = x2;
+ *cpy = y2;
+}
+
+static void pathQuadBezShortTo(struct SVGParser* p, float* cpx, float* cpy,
+ float* cpx2, float* cpy2, float* args, int rel)
+{
+ float x1, y1, x2, y2, cx, cy;
+
+ x1 = *cpx;
+ y1 = *cpy;
+ if (rel)
+ {
+ x2 = *cpx + args[0];
+ y2 = *cpy + args[1];
+ }
+ else
+ {
+ x2 = args[0];
+ y2 = args[1];
+ }
+
+ cx = 2*x1 - *cpx2;
+ cy = 2*y1 - *cpy2;
+
+ quadBez(p, x1,y1, cx,cy, x2,y2);
+
+ *cpx2 = cx;
+ *cpy2 = cy;
+ *cpx = x2;
+ *cpy = y2;
+}
+
+static void svgParsePath(struct SVGParser* p, const char** attr)
+{
+ const char* s;
+ char cmd;
+ float args[10];
+ int nargs;
+ int rargs;
+ float cpx, cpy, cpx2, cpy2;
+ const char* tmp[4];
+ char closedFlag;
+ int i;
+ char item[64];
+
+ for (i = 0; attr[i]; i += 2)
+ {
+ if (strcmp(attr[i], "d") == 0)
+ {
+ s = attr[i + 1];
+
+ svgResetPath(p);
+ closedFlag = 0;
+ nargs = 0;
+
+ while (*s)
+ {
+ s = getNextPathItem(s, item);
+ if (!*item) break;
+
+ if (isnum(item[0]))
+ {
+ if (nargs < 10)
+ args[nargs++] = (float)atof(item);
+ if (nargs >= rargs)
+ {
+ switch (cmd)
+ {
+ case 'm':
+ case 'M':
+ case 'l':
+ case 'L':
+ pathLineTo(p, &cpx, &cpy, args, (cmd == 'm' || cmd == 'l') ? 1 : 0);
+ break;
+ case 'H':
+ case 'h':
+ pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
+ break;
+ case 'V':
+ case 'v':
+ pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
+ break;
+ case 'C':
+ case 'c':
+ pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0);
+ break;
+ case 'S':
+ case 's':
+ pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
+ break;
+ case 'Q':
+ case 'q':
+ pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0);
+ break;
+ case 'T':
+ case 't':
+ pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
+ break;
+ default:
+ if (nargs >= 2)
+ {
+ cpx = args[nargs-2];
+ cpy = args[nargs-1];
+ }
+ break;
+ }
+ nargs = 0;
+ }
+ }
+ else
+ {
+ cmd = item[0];
+ rargs = getArgsPerElement(cmd);
+ if (cmd == 'M' || cmd == 'm')
+ {
+ // Commit path.
+ if (p->nbuf)
+ svgCreatePath(p, closedFlag);
+ // Start new subpath.
+ svgResetPath(p);
+ closedFlag = 0;
+ nargs = 0;
+ cpx = 0; cpy = 0;
+ }
+ else if (cmd == 'Z' || cmd == 'z')
+ {
+ closedFlag = 1;
+ // Commit path.
+ if (p->nbuf)
+ svgCreatePath(p, closedFlag);
+ // Start new subpath.
+ svgResetPath(p);
+ closedFlag = 0;
+ nargs = 0;
+ }
+ }
+ }
+
+ // Commit path.
+ if (p->nbuf)
+ svgCreatePath(p, closedFlag);
+
+ }
+ else
+ {
+ tmp[0] = attr[i];
+ tmp[1] = attr[i + 1];
+ tmp[2] = 0;
+ tmp[3] = 0;
+ svgParseAttribs(p, tmp);
+ }
+ }
+}
+
+static void svgParseRect(struct SVGParser* p, const char** attr)
+{
+ float x = 0.0f;
+ float y = 0.0f;
+ float w = 0.0f;
+ float h = 0.0f;
+ int i;
+
+ for (i = 0; attr[i]; i += 2)
+ {
+ if (!svgParseAttr(p, attr[i], attr[i + 1]))
+ {
+ if (strcmp(attr[i], "x") == 0) x = parseFloat(attr[i+1]);
+ if (strcmp(attr[i], "y") == 0) y = parseFloat(attr[i+1]);
+ if (strcmp(attr[i], "width") == 0) w = parseFloat(attr[i+1]);
+ if (strcmp(attr[i], "height") == 0) h = parseFloat(attr[i+1]);
+ }
+ }
+
+ if (w != 0.0f && h != 0.0f)
+ {
+ svgResetPath(p);
+
+ svgPathPoint(p, x, y);
+ svgPathPoint(p, x+w, y);
+ svgPathPoint(p, x+w, y+h);
+ svgPathPoint(p, x, y+h);
+
+ svgCreatePath(p, 1);
+ }
+}
+
+static void svgParseCircle(struct SVGParser* p, const char** attr)
+{
+ float cx = 0.0f;
+ float cy = 0.0f;
+ float r = 0.0f;
+ float da;
+ int i,n;
+ float x,y,u;
+
+ for (i = 0; attr[i]; i += 2)
+ {
+ if (!svgParseAttr(p, attr[i], attr[i + 1]))
+ {
+ if (strcmp(attr[i], "cx") == 0) cx = parseFloat(attr[i+1]);
+ if (strcmp(attr[i], "cy") == 0) cy = parseFloat(attr[i+1]);
+ if (strcmp(attr[i], "r") == 0) r = fabsf(parseFloat(attr[i+1]));
+ }
+ }
+
+ if (r != 0.0f)
+ {
+ svgResetPath(p);
+
+ da = acosf(r/(r+p->tol))*2;
+ n = (int)ceilf(M_PI*2/da);
+
+ da = (float)(M_PI*2)/n;
+ for (i = 0; i < n; ++i)
+ {
+ u = i*da;
+ x = cx + cosf(u)*r;
+ y = cy + sinf(u)*r;
+ svgPathPoint(p, x, y);
+ }
+
+ svgCreatePath(p, 1);
+ }
+}
+
+static void svgParseLine(struct SVGParser* p, const char** attr)
+{
+ float x1 = 0.0;
+ float y1 = 0.0;
+ float x2 = 0.0;
+ float y2 = 0.0;
+ int i;
+
+ for (i = 0; attr[i]; i += 2)
+ {
+ if (!svgParseAttr(p, attr[i], attr[i + 1]))
+ {
+ if (strcmp(attr[i], "x1") == 0) x1 = parseFloat(attr[i + 1]);
+ if (strcmp(attr[i], "y1") == 0) y1 = parseFloat(attr[i + 1]);
+ if (strcmp(attr[i], "x2") == 0) x2 = parseFloat(attr[i + 1]);
+ if (strcmp(attr[i], "y2") == 0) y2 = parseFloat(attr[i + 1]);
+ }
+ }
+
+ svgResetPath(p);
+
+ svgPathPoint(p, x1, y1);
+ svgPathPoint(p, x2, y2);
+
+ svgCreatePath(p, 0);
+}
+
+static void svgParsePoly(struct SVGParser* p, const char** attr, int closeFlag)
+{
+ int i;
+ const char* s;
+ float args[2];
+ int nargs;
+ char item[64];
+
+ svgResetPath(p);
+
+ for (i = 0; attr[i]; i += 2)
+ {
+ if (!svgParseAttr(p, attr[i], attr[i + 1]))
+ {
+ if (strcmp(attr[i], "points") == 0)
+ {
+ s = attr[i + 1];
+ nargs = 0;
+ while (*s)
+ {
+ s = getNextPathItem(s, item);
+ args[nargs++] = (float)atof(item);
+ if (nargs >= 2)
+ {
+ svgPathPoint(p, args[0], args[1]);
+ nargs = 0;
+ }
+ }
+ }
+ }
+ }
+
+ svgCreatePath(p, closeFlag);
+}
+
+static void svgStartElement(void* ud, const char* el, const char** attr)
+{
+ struct SVGParser* p = (struct SVGParser*)ud;
+
+ // Skip everything in defs
+ if (p->defsFlag)
+ return;
+
+ if (strcmp(el, "g") == 0)
+ {
+ svgPushAttr(p);
+ svgParseAttribs(p, attr);
+ }
+ else if (strcmp(el, "path") == 0)
+ {
+ if (p->pathFlag) // Do not allow nested paths.
+ return;
+ svgPushAttr(p);
+ svgParsePath(p, attr);
+ p->pathFlag = 1;
+ svgPopAttr(p);
+ }
+ else if (strcmp(el, "rect") == 0)
+ {
+ svgPushAttr(p);
+ svgParseRect(p, attr);
+ svgPopAttr(p);
+ }
+ else if (strcmp(el, "circle") == 0)
+ {
+ svgPushAttr(p);
+ svgParseCircle(p, attr);
+ svgPopAttr(p);
+ }
+ else if (strcmp(el, "line") == 0)
+ {
+ svgPushAttr(p);
+ svgParseLine(p, attr);
+ svgPopAttr(p);
+ }
+ else if (strcmp(el, "polyline") == 0)
+ {
+ svgPushAttr(p);
+ svgParsePoly(p, attr, 0);
+ svgPopAttr(p);
+ }
+ else if (strcmp(el, "polygon") == 0)
+ {
+ svgPushAttr(p);
+ svgParsePoly(p, attr, 1);
+ svgPopAttr(p);
+ }
+ else if (strcmp(el, "defs") == 0)
+ {
+ p->defsFlag = 1;
+ }
+}
+
+static void svgEndElement(void* ud, const char* el)
+{
+ struct SVGParser* p = (struct SVGParser*)ud;
+
+ if (strcmp(el, "g") == 0)
+ {
+ svgPopAttr(p);
+ }
+ else if (strcmp(el, "path") == 0)
+ {
+ p->pathFlag = 0;
+ }
+ else if (strcmp(el, "defs") == 0)
+ {
+ p->defsFlag = 0;
+ }
+}
+
+static void svgContent(void* ud, const char* s)
+{
+ // empty
+}
+
+struct SVGPath* svgParse(char* input)
+{
+ struct SVGParser* p;
+ struct SVGPath* ret = 0;
+
+ p = svgCreateParser();
+ if (!p)
+ return 0;
+
+ p->tol = 1.0f;
+
+ parsexml(input, svgStartElement, svgEndElement, svgContent, p);
+
+ if (p->buf)
+ {
+ free(p->buf);
+ p->buf = NULL;
+ p->nbuf = 0;
+ p->cbuf = 0;
+ }
+
+ ret = p->plist;
+ p->plist = 0;
+
+ svgDeleteParser(p);
+
+ return ret;
+}
+
+struct SVGPath* svgParseFromFile(const char* filename)
+{
+ FILE* fp;
+ int size;
+ char* data;
+ struct SVGPath* plist;
+
+ fp = fopen(filename, "rb");
+ if (!fp) return 0;
+ fseek(fp, 0, SEEK_END);
+ size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ data = (char*)malloc(size+1);
+ fread(data, size, 1, fp);
+ data[size] = '\0'; // Must be null terminated.
+ fclose(fp);
+ plist = svgParse(data);
+ free(data);
+ return plist;
+}
+
+void svgDelete(struct SVGPath* plist)
+{
+ struct SVGPath* path;
+ struct SVGPath* next;
+ if (!plist)
+ return;
+ path = plist;
+ while (path)
+ {
+ next = path->next;
+ if (path->pts)
+ free(path->pts);
+ free(path);
+ path = next;
+ }
+}
diff --git a/Contrib/nanosvg.h b/Contrib/nanosvg.h
new file mode 100644
index 0000000..a6bc857
--- /dev/null
+++ b/Contrib/nanosvg.h
@@ -0,0 +1,66 @@
+//
+// Copyright (c) 2009 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+// Version 1.0 - Initial version
+// Version 1.1 - Fixed path parsing, implemented curves, implemented circle.
+
+#ifndef NANOSVG_H
+#define NANOSVG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Example Usage:
+ // Load
+ struct SVGPath* plist;
+ plist = svgParseFromFile("test.svg.");
+ // Use...
+ for (SVGPath* it = plist; it; it = it->next)
+ ...
+ // Delete
+ svgDelete(plist);
+*/
+
+struct SVGPath
+{
+ float* pts;
+ int npts;
+ unsigned int fillColor;
+ unsigned int strokeColor;
+ float strokeWidth;
+ char hasFill;
+ char hasStroke;
+ char closed;
+ struct SVGPath* next;
+};
+
+// Parses SVG file from a file, returns linked list of paths.
+struct SVGPath* svgParseFromFile(const char* filename);
+
+// Parses SVG file from a null terminated string, returns linked list of paths.
+struct SVGPath* svgParse(char* input);
+
+// Deletes list of paths.
+void svgDelete(struct SVGPath* plist);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // NANOSVG_H
diff --git a/Example/SDLMain.h b/Example/SDLMain.h
new file mode 100644
index 0000000..d18cac7
--- /dev/null
+++ b/Example/SDLMain.h
@@ -0,0 +1,17 @@
+/* SDLMain.m - main entry point for our Cocoa-ized SDL app
+ Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
+ Non-NIB-Code & other changes: Max Horn <max@quendi.de>
+
+ Feel free to customize this file to suit your needs
+*/
+
+#import <Cocoa/Cocoa.h>
+
+@interface SDLMain : NSObject
+- (IBAction)prefsMenu:(id)sender;
+- (IBAction)newGame:(id)sender;
+- (IBAction)openGame:(id)sender;
+- (IBAction)saveGame:(id)sender;
+- (IBAction)saveGameAs:(id)sender;
+- (IBAction)help:(id)sender;
+@end
diff --git a/Example/SDLMain.m b/Example/SDLMain.m
new file mode 100644
index 0000000..b1007e3
--- /dev/null
+++ b/Example/SDLMain.m
@@ -0,0 +1,436 @@
+/* SDLMain.m - main entry point for our Cocoa-ized SDL app
+ Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
+ Non-NIB-Code & other changes: Max Horn <max@quendi.de>
+
+ Feel free to customize this file to suit your needs
+*/
+
+#import "SDL.h"
+#import "SDLMain.h"
+#import <sys/param.h> /* for MAXPATHLEN */
+#import <unistd.h>
+
+/* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
+ but the method still is there and works. To avoid warnings, we declare
+ it ourselves here. */
+@interface NSApplication(SDL_Missing_Methods)
+- (void)setAppleMenu:(NSMenu *)menu;
+@end
+
+/* Use this flag to determine whether we use SDLMain.nib or not */
+#define SDL_USE_NIB_FILE 1
+
+/* Use this flag to determine whether we use CPS (docking) or not */
+#define SDL_USE_CPS 1
+#ifdef SDL_USE_CPS
+/* Portions of CPS.h */
+typedef struct CPSProcessSerNum
+{
+ UInt32 lo;
+ UInt32 hi;
+} CPSProcessSerNum;
+
+extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
+extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
+extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
+
+#endif /* SDL_USE_CPS */
+
+static int gArgc;
+static char **gArgv;
+static BOOL gFinderLaunch;
+static BOOL gCalledAppMainline = FALSE;
+
+static NSString *getApplicationName(void)
+{
+ NSDictionary *dict;
+ NSString *appName = 0;
+
+ /* Determine the application name */
+ dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
+ if (dict)
+ appName = [dict objectForKey: @"CFBundleName"];
+
+ if (![appName length])
+ appName = [[NSProcessInfo processInfo] processName];
+
+ return appName;
+}
+
+#if SDL_USE_NIB_FILE
+/* A helper category for NSString */
+@interface NSString (ReplaceSubString)
+- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
+@end
+#endif
+
+@interface SDLApplication : NSApplication
+@end
+
+@implementation SDLApplication
+/* Invoked from the Quit menu item */
+- (void)terminate:(id)sender
+{
+ /* Post a SDL_QUIT event */
+ SDL_Event event;
+ event.type = SDL_QUIT;
+ SDL_PushEvent(&event);
+}
+@end
+
+/* The main class of the application, the application's delegate */
+@implementation SDLMain
+
+- (IBAction)prefsMenu:(id)sender
+{
+ printf ("prefs menu\n");
+}
+
+- (IBAction)newGame:(id)sender
+{
+ printf ("new game\n");
+
+ NSRunAlertPanel (@"Get ready to blow up some... stuff!",
+ @"Click OK to begin total carnage. Click Cancel to prevent total carnage.", @"OK", @"Cancel", nil);
+}
+
+- (IBAction)openGame:(id)sender
+{
+ NSString *path = nil;
+ NSOpenPanel *openPanel = [ NSOpenPanel openPanel ];
+
+ if ( [ openPanel runModalForDirectory:nil
+ file:@"SavedGame" types:nil ] ) {
+
+ path = [ [ openPanel filenames ] objectAtIndex:0 ];
+ }
+
+ printf ("open game: %s\n", [ path cString ]);
+}
+
+- (IBAction)saveGame:(id)sender
+{
+ NSString *path = nil;
+ NSSavePanel *savePanel = [ NSSavePanel savePanel ];
+
+ if ( [ savePanel runModalForDirectory:nil
+ file:@"SaveGameFile" ] ) {
+
+ path = [ savePanel filename ];
+ }
+
+ printf ("save game: %s\n", [ path cString ]);
+}
+
+- (IBAction)saveGameAs:(id)sender
+{
+ printf ("save game as\n");
+}
+
+- (IBAction)help:(id)sender
+{
+ NSRunAlertPanel (@"Oh help, where have ye gone?",
+ @"Sorry, there is no help available.\n\nThis message brought to you by We Don't Document, Inc.\n\n", @"Rats", @"Good, I never read it anyway", nil);
+}
+
+
+/* Set the working directory to the .app's parent directory */
+- (void) setupWorkingDirectory:(BOOL)shouldChdir
+{
+ if (shouldChdir)
+ {
+ char parentdir[MAXPATHLEN];
+ CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
+ CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
+ if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN)) {
+ assert ( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */
+ }
+ CFRelease(url);
+ CFRelease(url2);
+ }
+
+}
+
+#if SDL_USE_NIB_FILE
+
+/* Fix menu to contain the real app name instead of "SDL App" */
+- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
+{
+ NSRange aRange;
+ NSEnumerator *enumerator;
+ NSMenuItem *menuItem;
+
+ aRange = [[aMenu title] rangeOfString:@"SDL App"];
+ if (aRange.length != 0)
+ [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
+
+ enumerator = [[aMenu itemArray] objectEnumerator];
+ while ((menuItem = [enumerator nextObject]))
+ {
+ aRange = [[menuItem title] rangeOfString:@"SDL App"];
+ if (aRange.length != 0)
+ [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
+ if ([menuItem hasSubmenu])
+ [self fixMenu:[menuItem submenu] withAppName:appName];
+ }
+ [ aMenu sizeToFit ];
+}
+
+#else
+
+static void setApplicationMenu(void)
+{
+ /* warning: this code is very odd */
+ NSMenu *appleMenu;
+ NSMenuItem *menuItem;
+ NSString *title;
+ NSString *appName;
+
+ appName = getApplicationName();
+ appleMenu = [[NSMenu alloc] initWithTitle:@""];
+
+ /* Add menu items */
+ title = [@"About " stringByAppendingString:appName];
+ [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
+
+ [appleMenu addItem:[NSMenuItem separatorItem]];
+
+ title = [@"Hide " stringByAppendingString:appName];
+ [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
+
+ menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
+ [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
+
+ [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
+
+ [appleMenu addItem:[NSMenuItem separatorItem]];
+
+ title = [@"Quit " stringByAppendingString:appName];
+ [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
+
+
+ /* Put menu into the menubar */
+ menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
+ [menuItem setSubmenu:appleMenu];
+ [[NSApp mainMenu] addItem:menuItem];
+
+ /* Tell the application object that this is now the application menu */
+ [NSApp setAppleMenu:appleMenu];
+
+ /* Finally give up our references to the objects */
+ [appleMenu release];
+ [menuItem release];
+}
+
+/* Create a window menu */
+static void setupWindowMenu(void)
+{
+ NSMenu *windowMenu;
+ NSMenuItem *windowMenuItem;
+ NSMenuItem *menuItem;
+
+ windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
+
+ /* "Minimize" item */
+ menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
+ [windowMenu addItem:menuItem];
+ [menuItem release];
+
+ /* Put menu into the menubar */
+ windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
+ [windowMenuItem setSubmenu:windowMenu];
+ [[NSApp mainMenu] addItem:windowMenuItem];
+
+ /* Tell the application object that this is now the window menu */
+ [NSApp setWindowsMenu:windowMenu];
+
+ /* Finally give up our references to the objects */
+ [windowMenu release];
+ [windowMenuItem release];
+}
+
+/* Replacement for NSApplicationMain */
+static void CustomApplicationMain (int argc, char **argv)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ SDLMain *sdlMain;
+
+ /* Ensure the application object is initialised */
+ [SDLApplication sharedApplication];
+
+#ifdef SDL_USE_CPS
+ {
+ CPSProcessSerNum PSN;
+ /* Tell the dock about us */
+ if (!CPSGetCurrentProcess(&PSN))
+ if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
+ if (!CPSSetFrontProcess(&PSN))
+ [SDLApplication sharedApplication];
+ }
+#endif /* SDL_USE_CPS */
+
+ /* Set up the menubar */
+ [NSApp setMainMenu:[[NSMenu alloc] init]];
+ setApplicationMenu();
+ setupWindowMenu();
+
+ /* Create SDLMain and make it the app delegate */
+ sdlMain = [[SDLMain alloc] init];
+ [NSApp setDelegate:sdlMain];
+
+ /* Start the main event loop */
+ [NSApp run];
+
+ [sdlMain release];
+ [pool release];
+}
+
+#endif
+
+
+/*
+ * Catch document open requests...this lets us notice files when the app
+ * was launched by double-clicking a document, or when a document was
+ * dragged/dropped on the app's icon. You need to have a
+ * CFBundleDocumentsType section in your Info.plist to get this message,
+ * apparently.
+ *
+ * Files are added to gArgv, so to the app, they'll look like command line
+ * arguments. Previously, apps launched from the finder had nothing but
+ * an argv[0].
+ *
+ * This message may be received multiple times to open several docs on launch.
+ *
+ * This message is ignored once the app's mainline has been called.
+ */
+- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
+{
+ const char *temparg;
+ size_t arglen;
+ char *arg;
+ char **newargv;
+
+ if (!gFinderLaunch) /* MacOS is passing command line args. */
+ return FALSE;
+
+ if (gCalledAppMainline) /* app has started, ignore this document. */
+ return FALSE;
+
+ temparg = [filename UTF8String];
+ arglen = SDL_strlen(temparg) + 1;
+ arg = (char *) SDL_malloc(arglen);
+ if (arg == NULL)
+ return FALSE;
+
+ newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
+ if (newargv == NULL)
+ {
+ SDL_free(arg);
+ return FALSE;
+ }
+ gArgv = newargv;
+
+ SDL_strlcpy(arg, temparg, arglen);
+ gArgv[gArgc++] = arg;
+ gArgv[gArgc] = NULL;
+ return TRUE;
+}
+
+
+/* Called when the internal event loop has just started running */
+- (void) applicationDidFinishLaunching: (NSNotification *) note
+{
+ int status;
+
+ /* Set the working directory to the .app's parent directory */
+ [self setupWorkingDirectory:gFinderLaunch];
+
+#if SDL_USE_NIB_FILE
+ /* Set the main menu to contain the real app name instead of "SDL App" */
+ [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
+#endif
+
+ /* Hand off to main application code */
+ gCalledAppMainline = TRUE;
+ status = SDL_main (gArgc, gArgv);
+
+ /* We're done, thank you for playing */
+ exit(status);
+}
+@end
+
+
+@implementation NSString (ReplaceSubString)
+
+- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
+{
+ unsigned int bufferSize;
+ unsigned int selfLen = [self length];
+ unsigned int aStringLen = [aString length];
+ unichar *buffer;
+ NSRange localRange;
+ NSString *result;
+
+ bufferSize = selfLen + aStringLen - aRange.length;
+ buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar));
+
+ /* Get first part into buffer */
+ localRange.location = 0;
+ localRange.length = aRange.location;
+ [self getCharacters:buffer range:localRange];
+
+ /* Get middle part into buffer */
+ localRange.location = 0;
+ localRange.length = aStringLen;
+ [aString getCharacters:(buffer+aRange.location) range:localRange];
+
+ /* Get last part into buffer */
+ localRange.location = aRange.location + aRange.length;
+ localRange.length = selfLen - localRange.location;
+ [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
+
+ /* Build output string */
+ result = [NSString stringWithCharacters:buffer length:bufferSize];
+
+ NSDeallocateMemoryPages(buffer, bufferSize);
+
+ return result;
+}
+
+@end
+
+
+
+#ifdef main
+# undef main
+#endif
+
+
+/* Main entry point to executable - should *not* be SDL_main! */
+int main (int argc, char **argv)
+{
+ /* Copy the arguments into a global variable */
+ /* This is passed if we are launched by double-clicking */
+ if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
+ gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
+ gArgv[0] = argv[0];
+ gArgv[1] = NULL;
+ gArgc = 1;
+ gFinderLaunch = YES;
+ } else {
+ int i;
+ gArgc = argc;
+ gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
+ for (i = 0; i <= argc; i++)
+ gArgv[i] = argv[i];
+ gFinderLaunch = NO;
+ }
+
+#if SDL_USE_NIB_FILE
+ [SDLApplication poseAsClass:[NSApplication class]];
+ NSApplicationMain (argc, argv);
+#else
+ CustomApplicationMain (argc, argv);
+#endif
+ return 0;
+}
diff --git a/Example/main.c b/Example/main.c
new file mode 100644
index 0000000..659c3db
--- /dev/null
+++ b/Example/main.c
@@ -0,0 +1,391 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "SDL.h"
+#include "SDL_Opengl.h"
+#include "nanosvg.h"
+#include "tesselator.h"
+
+
+void* stdAlloc(void* userData, unsigned int size)
+{
+ int* allocated = ( int*)userData;
+ *allocated += (int)size;
+ return malloc(size);
+}
+
+void stdFree(void* userData, void* ptr)
+{
+ free(ptr);
+}
+
+struct MemPool
+{
+ unsigned char* buf;
+ unsigned int cap;
+ unsigned int size;
+};
+
+void* poolAlloc( void* userData, unsigned int size )
+{
+ struct MemPool* pool = (struct MemPool*)userData;
+ if (pool->size + size < pool->cap)
+ {
+ unsigned char* ptr = pool->buf + pool->size;
+ pool->size += size;
+ return ptr;
+ }
+ return 0;
+}
+
+void poolFree( void* userData, void* ptr )
+{
+ // empty
+}
+
+
+// Undefine this to see non-interactive heap allocator version.
+#define USE_POOL 1
+
+
+int main(int argc, char *argv[])
+{
+ int done;
+ SDL_Event event;
+ SDL_Surface* screen;
+ const SDL_VideoInfo* vi;
+ int width,height,i,j;
+ struct SVGPath* bg;
+ struct SVGPath* fg;
+ struct SVGPath* it;
+ float bounds[4],view[4],cx,cy,w,offx,offy;
+ float t = 0.0f;
+ Uint32 lastTime,time;
+ TESSalloc ma;
+ TESStesselator* tess = 0;
+ const int nvp = 6;
+ unsigned char* vflags = 0;
+ int nvflags = 0;
+ int run = 1;
+#ifdef USE_POOL
+ struct MemPool pool;
+ unsigned char mem[1024*512];
+#else
+ int allocated = 0;
+#endif
+
+ // Load assets
+ bg = svgParseFromFile("bg.svg");
+ if (!bg) return -1;
+ fg = svgParseFromFile("fg.svg");
+ if (!fg) return -1;
+
+ // Flip y
+ for (it = bg; it != NULL; it = it->next)
+ for (i = 0; i < it->npts; ++i)
+ it->pts[i*2+1] = -it->pts[i*2+1];
+ for (it = fg; it != NULL; it = it->next)
+ for (i = 0; i < it->npts; ++i)
+ it->pts[i*2+1] = -it->pts[i*2+1];
+
+ // Find FG bounds and center.
+ bounds[0] = bounds[2] = fg->pts[0];
+ bounds[1] = bounds[3] = fg->pts[1];
+ for (it = fg; it != NULL; it = it->next)
+ {
+ for (i = 0; i < it->npts; ++i)
+ {
+ const float x = it->pts[i*2];
+ const float y = it->pts[i*2+1];
+ if (x < bounds[0]) bounds[0] = x;
+ if (y < bounds[1]) bounds[1] = y;
+ if (x > bounds[2]) bounds[2] = x;
+ if (y > bounds[3]) bounds[3] = y;
+ }
+ }
+ cx = (bounds[0]+bounds[2])/2;
+ cy = (bounds[1]+bounds[3])/2;
+ for (it = fg; it != NULL; it = it->next)
+ {
+ for (i = 0; i < it->npts; ++i)
+ {
+ it->pts[i*2] -= cx;
+ it->pts[i*2+1] -= cy;
+ }
+ }
+
+ // Find BG bounds.
+ bounds[0] = bounds[2] = bg->pts[0];
+ bounds[1] = bounds[3] = bg->pts[1];
+ for (it = bg; it != NULL; it = it->next)
+ {
+ for (i = 0; i < it->npts; ++i)
+ {
+ const float x = it->pts[i*2];
+ const float y = it->pts[i*2+1];
+ if (x < bounds[0]) bounds[0] = x;
+ if (y < bounds[1]) bounds[1] = y;
+ if (x > bounds[2]) bounds[2] = x;
+ if (y > bounds[3]) bounds[3] = y;
+ }
+ }
+
+#ifdef USE_POOL
+
+ pool.size = 0;
+ pool.cap = sizeof(mem);
+ pool.buf = mem;
+ memset(&ma, 0, sizeof(ma));
+ ma.memalloc = poolAlloc;
+ ma.memfree = poolFree;
+ ma.userData = (void*)&pool;
+ ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices.
+
+#else
+
+ memset(&ma, 0, sizeof(ma));
+ ma.memalloc = stdAlloc;
+ ma.memfree = stdFree;
+ ma.userData = (void*)&allocated;
+ ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices.
+
+ tess = tessNewTess(&ma);
+ if (!tess)
+ return -1;
+
+ // Offset the foreground shape to center of the bg.
+ offx = (bounds[2]+bounds[0])/2;
+ offy = (bounds[3]+bounds[1])/2;
+ for (it = fg; it != NULL; it = it->next)
+ {
+ for (i = 0; i < it->npts; ++i)
+ {
+ it->pts[i*2] += offx;
+ it->pts[i*2+1] += offy;
+ }
+ }
+
+ // Add contours.
+ for (it = bg; it != NULL; it = it->next)
+ tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts);
+ for (it = fg; it != NULL; it = it->next)
+ tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts);
+ if (!tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, nvp, 2, 0))
+ return -1;
+ printf("Memory used: %.1f kB\n", allocated/1024.0f);
+
+#endif
+
+
+ if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
+ {
+ fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
+ return -1;
+ }
+
+ // Init OpenGL
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
+
+ vi = SDL_GetVideoInfo();
+ width = vi->current_w - 20;
+ height = vi->current_h - 80;
+ screen = SDL_SetVideoMode(width, height, 0, SDL_OPENGL);
+ if (!screen) return -1;
+
+ SDL_WM_SetCaption("libtess2 Demo", 0);
+
+ // Adjust bounds so that we get nice view of the bg.
+ cx = (bounds[0]+bounds[2])/2;
+ cy = (bounds[3]+bounds[1])/2;
+ w = (bounds[2]-bounds[0])/2;
+ view[0] = cx - w*1.2f;
+ view[2] = cx + w*1.2f;
+ view[1] = cy - w*1.2f*height/width;
+ view[3] = cy + w*1.2f*height/width;
+
+ lastTime = SDL_GetTicks();
+ done = 0;
+ while (!done)
+ {
+ while (SDL_PollEvent(&event))
+ {
+ switch (event.type)
+ {
+ case SDL_MOUSEMOTION:
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ break;
+ case SDL_KEYDOWN:
+ if (event.key.keysym.sym == SDLK_ESCAPE)
+ done = 1;
+ else if (event.key.keysym.sym == SDLK_SPACE)
+ run ^= 1;
+ break;
+ case SDL_QUIT:
+ done = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ time = SDL_GetTicks();
+ if (run)
+ t += (time-lastTime) / 1000.0f;
+ lastTime = time;
+
+ // Update and render
+ glViewport(0, 0, width, height);
+ glClearColor(0.3f, 0.3f, 0.32f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_TEXTURE_2D);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(view[0],view[2],view[1],view[3],-1,1);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glDisable(GL_DEPTH_TEST);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+
+#ifdef USE_POOL
+ pool.size = 0; // reset pool
+ tess = tessNewTess(&ma);
+ if (tess)
+ {
+ offx = (view[2]+view[0])/2 + sinf(t) * (view[2]-view[0])/2;
+ offy = (view[3]+view[1])/2 + cosf(t*3.13f) * (view[3]-view[1])/6;
+
+ for (it = fg; it != NULL; it = it->next)
+ {
+ for (i = 0; i < it->npts; ++i)
+ {
+ it->pts[i*2] += offx;
+ it->pts[i*2+1] += offy;
+ }
+ }
+
+ for (it = bg; it != NULL; it = it->next)
+ tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts);
+ for (it = fg; it != NULL; it = it->next)
+ tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts);
+
+ for (it = fg; it != NULL; it = it->next)
+ {
+ for (i = 0; i < it->npts; ++i)
+ {
+ it->pts[i*2] -= offx;
+ it->pts[i*2+1] -= offy;
+ }
+ }
+
+ // First combine contours and then triangulate, this removes unnecessary inner vertices.
+ if (tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_BOUNDARY_CONTOURS, 0, 0, 0))
+ {
+ const float* verts = tessGetVertices(tess);
+ const int* vinds = tessGetVertexIndices(tess);
+ const int nverts = tessGetVertexCount(tess);
+ const int* elems = tessGetElements(tess);
+ const int nelems = tessGetElementCount(tess);
+
+ if (nverts > nvflags)
+ {
+ if (vflags)
+ free(vflags);
+ nvflags = nverts;
+ vflags = (unsigned char*)malloc(sizeof(unsigned char)*nvflags);
+ }
+
+ if (vflags)
+ {
+ // Vertex indices describe the order the indices were added and can be used
+ // to map the tesselator output to input. Vertices marked as TESS_UNDEF
+ // are the ones that were created at the intersection of segments.
+ // That is, if vflags is set it means that the vertex comes from intersegment.
+ for (i = 0; i < nverts; ++i)
+ vflags[i] = vinds[i] == TESS_UNDEF ? 1 : 0;
+ }
+
+ for (i = 0; i < nelems; ++i)
+ {
+ int b = elems[i*2];
+ int n = elems[i*2+1];
+ tessAddContour(tess, 2, &verts[b*2], sizeof(float)*2, n);
+ }
+ if (!tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, nvp, 2, 0))
+ tess = 0;
+ }
+ else
+ tess = 0;
+ }
+#endif
+
+ // Draw tesselated pieces.
+ if (tess)
+ {
+ const float* verts = tessGetVertices(tess);
+ const int* vinds = tessGetVertexIndices(tess);
+ const int* elems = tessGetElements(tess);
+ const int nverts = tessGetVertexCount(tess);
+ const int nelems = tessGetElementCount(tess);
+
+ // Draw polygons.
+ glColor4ub(255,255,255,128);
+ for (i = 0; i < nelems; ++i)
+ {
+ const int* p = &elems[i*nvp];
+ glBegin(GL_TRIANGLE_FAN);
+ for (j = 0; j < nvp && p[j] != TESS_UNDEF; ++j)
+ glVertex2f(verts[p[j]*2], verts[p[j]*2+1]);
+ glEnd();
+ }
+
+ glColor4ub(0,0,0,16);
+ for (i = 0; i < nelems; ++i)
+ {
+ const int* p = &elems[i*nvp];
+ glBegin(GL_LINE_LOOP);
+ for (j = 0; j < nvp && p[j] != TESS_UNDEF; ++j)
+ glVertex2f(verts[p[j]*2], verts[p[j]*2+1]);
+ glEnd();
+ }
+
+ glColor4ub(0,0,0,128);
+ glPointSize(3.0f);
+ glBegin(GL_POINTS);
+ for (i = 0; i < nverts; ++i)
+ {
+ if (vflags && vflags[vinds[i]])
+ glColor4ub(255,0,0,192);
+ else
+ glColor4ub(0,0,0,128);
+ glVertex2f(verts[i*2], verts[i*2+1]);
+ }
+ glEnd();
+ glPointSize(1.0f);
+ }
+
+ glEnable(GL_DEPTH_TEST);
+ SDL_GL_SwapBuffers();
+ }
+
+ if (tess) tessDeleteTess(tess);
+
+ if (vflags)
+ free(vflags);
+
+ svgDelete(bg);
+ svgDelete(fg);
+
+ SDL_Quit();
+ return 0;
+}
diff --git a/Include/tesselator.h b/Include/tesselator.h
new file mode 100755
index 0000000..60e0e9b
--- /dev/null
+++ b/Include/tesselator.h
@@ -0,0 +1,209 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Mikko Mononen, July 2009.
+*/
+
+#ifndef TESSELATOR_H
+#define TESSELATOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// See OpenGL Red Book for description of the winding rules
+// http://www.glprogramming.com/red/chapter11.html
+enum TessWindingRule
+{
+ TESS_WINDING_ODD,
+ TESS_WINDING_NONZERO,
+ TESS_WINDING_POSITIVE,
+ TESS_WINDING_NEGATIVE,
+ TESS_WINDING_ABS_GEQ_TWO,
+};
+
+// The contents of the tessGetElements() depends on element type being passed to tessTesselate().
+// Tesselation result element types:
+// TESS_POLYGONS
+// Each element in the element array is polygon defined as 'polySize' number of vertex indices.
+// If a polygon has than 'polySize' vertices, the remaining indices are stored as TESS_UNDEF.
+// Example, drawing a polygon:
+// const int nelems = tessGetElementCount(tess);
+// const TESSindex* elems = tessGetElements(tess);
+// for (int i = 0; i < nelems; i++) {
+// const TESSindex* poly = &elems[i * polySize];
+// glBegin(GL_POLYGON);
+// for (int j = 0; j < polySize; j++) {
+// if (poly[j] == TESS_UNDEF) break;
+// glVertex2fv(&verts[poly[j]*vertexSize]);
+// }
+// glEnd();
+// }
+//
+// TESS_CONNECTED_POLYGONS
+// Each element in the element array is polygon defined as 'polySize' number of vertex indices,
+// followed by 'polySize' indices to neighour polygons, that is each element is 'polySize' * 2 indices.
+// If a polygon has than 'polySize' vertices, the remaining indices are stored as TESS_UNDEF.
+// If a polygon edge is a boundary, that is, not connected to another polygon, the neighbour index is TESS_UNDEF.
+// Example, flood fill based on seed polygon:
+// const int nelems = tessGetElementCount(tess);
+// const TESSindex* elems = tessGetElements(tess);
+// unsigned char* visited = (unsigned char*)calloc(nelems);
+// TESSindex stack[50];
+// int nstack = 0;
+// stack[nstack++] = seedPoly;
+// visited[startPoly] = 1;
+// while (nstack > 0) {
+// TESSindex idx = stack[--nstack];
+// const TESSindex* poly = &elems[idx * polySize * 2];
+// const TESSindex* nei = &poly[polySize];
+// for (int i = 0; i < polySize; i++) {
+// if (poly[i] == TESS_UNDEF) break;
+// if (nei[i] != TESS_UNDEF && !visited[nei[i]])
+// stack[nstack++] = nei[i];
+// visited[nei[i]] = 1;
+// }
+// }
+// }
+//
+// TESS_BOUNDARY_CONTOURS
+// Each element in the element array is [base index, count] pair defining a range of vertices for a contour.
+// The first value is index to first vertex in contour and the second value is number of vertices in the contour.
+// Example, drawing contours:
+// const int nelems = tessGetElementCount(tess);
+// const TESSindex* elems = tessGetElements(tess);
+// for (int i = 0; i < nelems; i++) {
+// const TESSindex base = elems[i * 2];
+// const TESSindex count = elems[i * 2 + 1];
+// glBegin(GL_LINE_LOOP);
+// for (int j = 0; j < count; j++) {
+// glVertex2fv(&verts[(base+count) * vertexSize]);
+// }
+// glEnd();
+// }
+//
+enum TessElementType
+{
+ TESS_POLYGONS,
+ TESS_CONNECTED_POLYGONS,
+ TESS_BOUNDARY_CONTOURS,
+};
+
+typedef float TESSreal;
+typedef int TESSindex;
+typedef struct TESStesselator TESStesselator;
+typedef struct TESSalloc TESSalloc;
+
+#define TESS_UNDEF (~(TESSindex)0)
+
+// Custom memory allocator interface.
+// The internal memory allocator allocates mesh edges, vertices and faces
+// as well as dictionary nodes and active regions in buckets and uses simple
+// freelist to speed up the allocation. The bucket size should roughly match your
+// expected input data. For example if you process only hundreds of vertices,
+// a bucket size of 128 might be ok, where as when processing thousands of vertices
+// bucket size of 1024 might be approproate. The bucket size is a compromise between
+// how often to allocate memory from the system versus how much extra space the system
+// should allocate. Reasonable defaults are show in commects below, they will be used if
+// the bucket sizes are zero.
+//
+// The use may left the memrealloc to be null. In that case, the tesselator will not try to
+// dynamically grow int's internal arrays. The tesselator only needs the reallocation when it
+// has found intersecting segments and needs to add new vertex. This defency can be cured by
+// allocating some extra vertices beforehand. The 'extraVertices' variable allows to specify
+// number of expected extra vertices.
+struct TESSalloc
+{
+ void *(*memalloc)( void *userData, unsigned int size );
+ void *(*memrealloc)( void *userData, void* ptr, unsigned int size );
+ void (*memfree)( void *userData, void *ptr );
+ void* userData; // User data passed to the allocator functions.
+ int meshEdgeBucketSize; // 512
+ int meshVertexBucketSize; // 512
+ int meshFaceBucketSize; // 256
+ int dictNodeBucketSize; // 512
+ int regionBucketSize; // 256
+ int extraVertices; // Number of extra vertices allocated for the priority queue.
+};
+
+// tessNewTess() - Creates a new tesselator.
+// Use tessDeleteTess() to delete the tesselator.
+// Returns:
+// new tesselator object.
+TESStesselator* tessNewTess( TESSalloc* alloc );
+
+// tessDeleteTess() - Deletes a tesselator.
+// Parameters:
+// tess - pointer to tesselator object to be deleted.
+void tessDeleteTess( TESStesselator *tess );
+
+// tessAddContour() - Adds a contour to be tesselated.
+// The type of the vertex coordinates is assumed to be TESSreal.
+// Parameters:
+// tess - pointer to tesselator object.
+// size - number of coordinates per vertex. Must be 2 or 3.
+// pointer - pointer to the first coordinate of the first vertex in the array.
+// stride - defines offset in bytes between consecutive vertices.
+// count - number of vertices in contour.
+void tessAddContour( TESStesselator *tess, int size, const void* pointer, int stride, int count );
+
+// tessTesselate() - tesselate contours.
+// Parameters:
+// tess - pointer to tesselator object.
+// windingRule - winding rules used for tesselation, must be one of TessWindingRule.
+// elementType - defines the tesselation result element type, must be one of TessElementType.
+// polySize - defines maximum vertices per polygons if output is polygons.
+// vertexSize - defines the number of coordinates in tesselation result vertex, must be 2 or 3.
+// normal - defines the normal of the input contours, of null the normal is calculated automatically.
+// Returns:
+// 1 if succeed, 0 if failed.
+int tessTesselate( TESStesselator *tess, int windingRule, int elementType, int polySize, int vertexSize, const TESSreal* normal );
+
+// tessGetVertexCount() - Returns number of vertices in the tesselated output.
+int tessGetVertexCount( TESStesselator *tess );
+
+// tessGetVertices() - Returns pointer to first coordinate of first vertex.
+const TESSreal* tessGetVertices( TESStesselator *tess );
+
+// tessGetVertexIndices() - Returns pointer to first vertex index.
+// Vertex indices can be used to map the generated vertices to the original vertices.
+// Every point added using tessAddContour() will get a new index starting at 0.
+// New vertices generated at the intersections of segments are assigned value TESS_UNDEF.
+const TESSindex* tessGetVertexIndices( TESStesselator *tess );
+
+// tessGetElementCount() - Returns number of elements in the the tesselated output.
+int tessGetElementCount( TESStesselator *tess );
+
+// tessGetElements() - Returns pointer to the first element.
+const TESSindex* tessGetElements( TESStesselator *tess );
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // TESSELATOR_H
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..619c7a2
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,25 @@
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
diff --git a/README.md b/README.md
index e5ed584..b437ce3 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,17 @@
-libtess2
+Libtess2
========
+Version 1.0
-Game and tools oriented refactored version of GLU tesselator.
+
+This is refactored version of the original libtess which comes with the GLU reference implementation. The code is good quality polygon tesselator and triangulator. The original code comes with rather horrible interface and its' performance suffers from lots of small memory allocations. The main point of the refactoring has been the interface and memory allocation scheme.
+
+Simple bucketed memory allocator (see Graphics Gems III for reference) was added which speeds up the code by order of magnitude (tests showed 15 to 50 times improvement depending on data). The API allows the user to pass his own allocator to the library. It is possible to configure the library so that the library runs on predefined chunk of memory.
+
+The API was changed to loosely resemble the OpenGL vertex array API. The processed data can be accessed via getter functions. The code is able to output contours, polygons and connected polygons. The output of the tesselator can be also used as input for new run. I.e. the user may first want to calculate an union all the input contours and the triangulate them.
+
+The code is released under SGI FREE SOFTWARE LICENSE B Version 2.0.
+http://oss.sgi.com/projects/FreeB/
+
+
+Mikko Mononen
+memon@inside.org
\ No newline at end of file
diff --git a/Source/bucketalloc.c b/Source/bucketalloc.c
new file mode 100755
index 0000000..420ebab
--- /dev/null
+++ b/Source/bucketalloc.c
@@ -0,0 +1,191 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Mikko Mononen, July 2009.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "../Include/tesselator.h"
+
+//#define CHECK_BOUNDS
+
+typedef struct BucketAlloc BucketAlloc;
+typedef struct Bucket Bucket;
+
+struct Bucket
+{
+ Bucket *next;
+};
+
+struct BucketAlloc
+{
+ void *freelist;
+ Bucket *buckets;
+ unsigned int itemSize;
+ unsigned int bucketSize;
+ const char *name;
+ TESSalloc* alloc;
+};
+
+static int CreateBucket( struct BucketAlloc* ba )
+{
+ size_t size;
+ Bucket* bucket;
+ void* freelist;
+ unsigned char* head;
+ unsigned char* it;
+
+ // Allocate memory for the bucket
+ size = sizeof(Bucket) + ba->itemSize * ba->bucketSize;
+ bucket = (Bucket*)ba->alloc->memalloc( ba->alloc->userData, size );
+ if ( !bucket )
+ return 0;
+ bucket->next = 0;
+
+ // Add the bucket into the list of buckets.
+ bucket->next = ba->buckets;
+ ba->buckets = bucket;
+
+ // Add new items to the free list.
+ freelist = ba->freelist;
+ head = (unsigned char*)bucket + sizeof(Bucket);
+ it = head + ba->itemSize * ba->bucketSize;
+ do
+ {
+ it -= ba->itemSize;
+ // Store pointer to next free item.
+ *((void**)it) = freelist;
+ // Pointer to next location containing a free item.
+ freelist = (void*)it;
+ }
+ while ( it != head );
+ // Update pointer to next location containing a free item.
+ ba->freelist = (void*)it;
+
+ return 1;
+}
+
+static void *NextFreeItem( struct BucketAlloc *ba )
+{
+ return *(void**)ba->freelist;
+}
+
+struct BucketAlloc* createBucketAlloc( TESSalloc* alloc, const char* name,
+ unsigned int itemSize, unsigned int bucketSize )
+{
+ BucketAlloc* ba = (BucketAlloc*)alloc->memalloc( alloc->userData, sizeof(BucketAlloc) );
+
+ ba->alloc = alloc;
+ ba->name = name;
+ ba->itemSize = itemSize;
+ if ( ba->itemSize < sizeof(void*) )
+ ba->itemSize = sizeof(void*);
+ ba->bucketSize = bucketSize;
+ ba->freelist = 0;
+ ba->buckets = 0;
+
+ if ( !CreateBucket( ba ) )
+ {
+ alloc->memfree( alloc->userData, ba );
+ return 0;
+ }
+
+ return ba;
+}
+
+void* bucketAlloc( struct BucketAlloc *ba )
+{
+ void *it;
+
+ // If running out of memory, allocate new bucket and update the freelist.
+ if ( !ba->freelist || !NextFreeItem( ba ) )
+ {
+ if ( !CreateBucket( ba ) )
+ return 0;
+ }
+
+ // Pop item from in front of the free list.
+ it = ba->freelist;
+ ba->freelist = NextFreeItem( ba );
+
+ return it;
+}
+
+void bucketFree( struct BucketAlloc *ba, void *ptr )
+{
+#ifdef CHECK_BOUNDS
+ int inBounds = 0;
+ Bucket *bucket;
+
+ // Check that the pointer is allocated with this allocator.
+ bucket = ba->buckets;
+ while ( bucket )
+ {
+ void *bucketMin = (void*)((unsigned char*)bucket + sizeof(Bucket));
+ void *bucketMax = (void*)((unsigned char*)bucket + sizeof(Bucket) + ba->itemSize * ba->bucketSize);
+ if ( ptr >= bucketMin && ptr < bucketMax )
+ {
+ inBounds = 1;
+ break;
+ }
+ bucket = bucket->next;
+ }
+
+ if ( inBounds )
+ {
+ // Add the node in front of the free list.
+ *(void**)ptr = ba->freelist;
+ ba->freelist = ptr;
+ }
+ else
+ {
+ printf("ERROR! pointer 0x%p does not belong to allocator '%s'\n", ba->name);
+ }
+#else
+ // Add the node in front of the free list.
+ *(void**)ptr = ba->freelist;
+ ba->freelist = ptr;
+#endif
+}
+
+void deleteBucketAlloc( struct BucketAlloc *ba )
+{
+ TESSalloc* alloc = ba->alloc;
+ Bucket *bucket = ba->buckets;
+ Bucket *next;
+ while ( bucket )
+ {
+ next = bucket->next;
+ alloc->memfree( alloc->userData, bucket );
+ bucket = next;
+ }
+ ba->freelist = 0;
+ ba->buckets = 0;
+ alloc->memfree( alloc->userData, ba );
+}
diff --git a/Source/bucketalloc.h b/Source/bucketalloc.h
new file mode 100755
index 0000000..c540951
--- /dev/null
+++ b/Source/bucketalloc.h
@@ -0,0 +1,51 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Mikko Mononen, July 2009.
+*/
+
+#ifndef MEMALLOC_H
+#define MEMALLOC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "tesselator.h"
+
+struct BucketAlloc *createBucketAlloc( TESSalloc* alloc, const char *name,
+ unsigned int itemSize, unsigned int bucketSize );
+void *bucketAlloc( struct BucketAlloc *ba);
+void bucketFree( struct BucketAlloc *ba, void *ptr );
+void deleteBucketAlloc( struct BucketAlloc *ba );
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
diff --git a/Source/dict.c b/Source/dict.c
new file mode 100755
index 0000000..650adda
--- /dev/null
+++ b/Source/dict.c
@@ -0,0 +1,109 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#include <stddef.h>
+#include "../Include/tesselator.h"
+#include "bucketalloc.h"
+#include "dict.h"
+
+/* really tessDictListNewDict */
+Dict *dictNewDict( TESSalloc* alloc, void *frame, int (*leq)(void *frame, DictKey key1, DictKey key2) )
+{
+ Dict *dict = (Dict *)alloc->memalloc( alloc->userData, sizeof( Dict ));
+ DictNode *head;
+
+ if (dict == NULL) return NULL;
+
+ head = &dict->head;
+
+ head->key = NULL;
+ head->next = head;
+ head->prev = head;
+
+ dict->frame = frame;
+ dict->leq = leq;
+
+ if (alloc->dictNodeBucketSize < 16)
+ alloc->dictNodeBucketSize = 16;
+ if (alloc->dictNodeBucketSize > 4096)
+ alloc->dictNodeBucketSize = 4096;
+ dict->nodePool = createBucketAlloc( alloc, "Dict", sizeof(DictNode), alloc->dictNodeBucketSize );
+
+ return dict;
+}
+
+/* really tessDictListDeleteDict */
+void dictDeleteDict( TESSalloc* alloc, Dict *dict )
+{
+ deleteBucketAlloc( dict->nodePool );
+ alloc->memfree( alloc->userData, dict );
+}
+
+/* really tessDictListInsertBefore */
+DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key )
+{
+ DictNode *newNode;
+
+ do {
+ node = node->prev;
+ } while( node->key != NULL && ! (*dict->leq)(dict->frame, node->key, key));
+
+ newNode = (DictNode *)bucketAlloc( dict->nodePool );
+ if (newNode == NULL) return NULL;
+
+ newNode->key = key;
+ newNode->next = node->next;
+ node->next->prev = newNode;
+ newNode->prev = node;
+ node->next = newNode;
+
+ return newNode;
+}
+
+/* really tessDictListDelete */
+void dictDelete( Dict *dict, DictNode *node ) /*ARGSUSED*/
+{
+ node->next->prev = node->prev;
+ node->prev->next = node->next;
+ bucketFree( dict->nodePool, node );
+}
+
+/* really tessDictListSearch */
+DictNode *dictSearch( Dict *dict, DictKey key )
+{
+ DictNode *node = &dict->head;
+
+ do {
+ node = node->next;
+ } while( node->key != NULL && ! (*dict->leq)(dict->frame, key, node->key));
+
+ return node;
+}
diff --git a/Source/dict.h b/Source/dict.h
new file mode 100755
index 0000000..4cf3226
--- /dev/null
+++ b/Source/dict.h
@@ -0,0 +1,74 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#ifndef DICT_LIST_H
+#define DICT_LIST_H
+
+typedef void *DictKey;
+typedef struct Dict Dict;
+typedef struct DictNode DictNode;
+
+Dict *dictNewDict( TESSalloc* alloc, void *frame, int (*leq)(void *frame, DictKey key1, DictKey key2) );
+
+void dictDeleteDict( TESSalloc* alloc, Dict *dict );
+
+/* Search returns the node with the smallest key greater than or equal
+* to the given key. If there is no such key, returns a node whose
+* key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc.
+*/
+DictNode *dictSearch( Dict *dict, DictKey key );
+DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key );
+void dictDelete( Dict *dict, DictNode *node );
+
+#define dictKey(n) ((n)->key)
+#define dictSucc(n) ((n)->next)
+#define dictPred(n) ((n)->prev)
+#define dictMin(d) ((d)->head.next)
+#define dictMax(d) ((d)->head.prev)
+#define dictInsert(d,k) (dictInsertBefore((d),&(d)->head,(k)))
+
+
+/*** Private data structures ***/
+
+struct DictNode {
+ DictKey key;
+ DictNode *next;
+ DictNode *prev;
+};
+
+struct Dict {
+ DictNode head;
+ void *frame;
+ struct BucketAlloc *nodePool;
+ int (*leq)(void *frame, DictKey key1, DictKey key2);
+};
+
+#endif
diff --git a/Source/geom.c b/Source/geom.c
new file mode 100755
index 0000000..a49ea17
--- /dev/null
+++ b/Source/geom.c
@@ -0,0 +1,261 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+//#include "tesos.h"
+#include <assert.h>
+#include "mesh.h"
+#include "geom.h"
+
+int tesvertLeq( TESSvertex *u, TESSvertex *v )
+{
+ /* Returns TRUE if u is lexicographically <= v. */
+
+ return VertLeq( u, v );
+}
+
+TESSreal tesedgeEval( TESSvertex *u, TESSvertex *v, TESSvertex *w )
+{
+ /* Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w),
+ * evaluates the t-coord of the edge uw at the s-coord of the vertex v.
+ * Returns v->t - (uw)(v->s), ie. the signed distance from uw to v.
+ * If uw is vertical (and thus passes thru v), the result is zero.
+ *
+ * The calculation is extremely accurate and stable, even when v
+ * is very close to u or w. In particular if we set v->t = 0 and
+ * let r be the negated result (this evaluates (uw)(v->s)), then
+ * r is guaranteed to satisfy MIN(u->t,w->t) <= r <= MAX(u->t,w->t).
+ */
+ TESSreal gapL, gapR;
+
+ assert( VertLeq( u, v ) && VertLeq( v, w ));
+
+ gapL = v->s - u->s;
+ gapR = w->s - v->s;
+
+ if( gapL + gapR > 0 ) {
+ if( gapL < gapR ) {
+ return (v->t - u->t) + (u->t - w->t) * (gapL / (gapL + gapR));
+ } else {
+ return (v->t - w->t) + (w->t - u->t) * (gapR / (gapL + gapR));
+ }
+ }
+ /* vertical line */
+ return 0;
+}
+
+TESSreal tesedgeSign( TESSvertex *u, TESSvertex *v, TESSvertex *w )
+{
+ /* Returns a number whose sign matches EdgeEval(u,v,w) but which
+ * is cheaper to evaluate. Returns > 0, == 0 , or < 0
+ * as v is above, on, or below the edge uw.
+ */
+ TESSreal gapL, gapR;
+
+ assert( VertLeq( u, v ) && VertLeq( v, w ));
+
+ gapL = v->s - u->s;
+ gapR = w->s - v->s;
+
+ if( gapL + gapR > 0 ) {
+ return (v->t - w->t) * gapL + (v->t - u->t) * gapR;
+ }
+ /* vertical line */
+ return 0;
+}
+
+
+/***********************************************************************
+* Define versions of EdgeSign, EdgeEval with s and t transposed.
+*/
+
+TESSreal testransEval( TESSvertex *u, TESSvertex *v, TESSvertex *w )
+{
+ /* Given three vertices u,v,w such that TransLeq(u,v) && TransLeq(v,w),
+ * evaluates the t-coord of the edge uw at the s-coord of the vertex v.
+ * Returns v->s - (uw)(v->t), ie. the signed distance from uw to v.
+ * If uw is vertical (and thus passes thru v), the result is zero.
+ *
+ * The calculation is extremely accurate and stable, even when v
+ * is very close to u or w. In particular if we set v->s = 0 and
+ * let r be the negated result (this evaluates (uw)(v->t)), then
+ * r is guaranteed to satisfy MIN(u->s,w->s) <= r <= MAX(u->s,w->s).
+ */
+ TESSreal gapL, gapR;
+
+ assert( TransLeq( u, v ) && TransLeq( v, w ));
+
+ gapL = v->t - u->t;
+ gapR = w->t - v->t;
+
+ if( gapL + gapR > 0 ) {
+ if( gapL < gapR ) {
+ return (v->s - u->s) + (u->s - w->s) * (gapL / (gapL + gapR));
+ } else {
+ return (v->s - w->s) + (w->s - u->s) * (gapR / (gapL + gapR));
+ }
+ }
+ /* vertical line */
+ return 0;
+}
+
+TESSreal testransSign( TESSvertex *u, TESSvertex *v, TESSvertex *w )
+{
+ /* Returns a number whose sign matches TransEval(u,v,w) but which
+ * is cheaper to evaluate. Returns > 0, == 0 , or < 0
+ * as v is above, on, or below the edge uw.
+ */
+ TESSreal gapL, gapR;
+
+ assert( TransLeq( u, v ) && TransLeq( v, w ));
+
+ gapL = v->t - u->t;
+ gapR = w->t - v->t;
+
+ if( gapL + gapR > 0 ) {
+ return (v->s - w->s) * gapL + (v->s - u->s) * gapR;
+ }
+ /* vertical line */
+ return 0;
+}
+
+
+int tesvertCCW( TESSvertex *u, TESSvertex *v, TESSvertex *w )
+{
+ /* For almost-degenerate situations, the results are not reliable.
+ * Unless the floating-point arithmetic can be performed without
+ * rounding errors, *any* implementation will give incorrect results
+ * on some degenerate inputs, so the client must have some way to
+ * handle this situation.
+ */
+ return (u->s*(v->t - w->t) + v->s*(w->t - u->t) + w->s*(u->t - v->t)) >= 0;
+}
+
+/* Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b),
+* or (x+y)/2 if a==b==0. It requires that a,b >= 0, and enforces
+* this in the rare case that one argument is slightly negative.
+* The implementation is extremely stable numerically.
+* In particular it guarantees that the result r satisfies
+* MIN(x,y) <= r <= MAX(x,y), and the results are very accurate
+* even when a and b differ greatly in magnitude.
+*/
+#define RealInterpolate(a,x,b,y) \
+ (a = (a < 0) ? 0 : a, b = (b < 0) ? 0 : b, \
+ ((a <= b) ? ((b == 0) ? ((x+y) / 2) \
+ : (x + (y-x) * (a/(a+b)))) \
+ : (y + (x-y) * (b/(a+b)))))
+
+#ifndef FOR_TRITE_TEST_PROGRAM
+#define Interpolate(a,x,b,y) RealInterpolate(a,x,b,y)
+#else
+
+/* Claim: the ONLY property the sweep algorithm relies on is that
+* MIN(x,y) <= r <= MAX(x,y). This is a nasty way to test that.
+*/
+#include <stdlib.h>
+extern int RandomInterpolate;
+
+double Interpolate( double a, double x, double b, double y)
+{
+ printf("*********************%d\n",RandomInterpolate);
+ if( RandomInterpolate ) {
+ a = 1.2 * drand48() - 0.1;
+ a = (a < 0) ? 0 : ((a > 1) ? 1 : a);
+ b = 1.0 - a;
+ }
+ return RealInterpolate(a,x,b,y);
+}
+
+#endif
+
+#define Swap(a,b) if (1) { TESSvertex *t = a; a = b; b = t; } else
+
+void tesedgeIntersect( TESSvertex *o1, TESSvertex *d1,
+ TESSvertex *o2, TESSvertex *d2,
+ TESSvertex *v )
+ /* Given edges (o1,d1) and (o2,d2), compute their point of intersection.
+ * The computed point is guaranteed to lie in the intersection of the
+ * bounding rectangles defined by each edge.
+ */
+{
+ TESSreal z1, z2;
+
+ /* This is certainly not the most efficient way to find the intersection
+ * of two line segments, but it is very numerically stable.
+ *
+ * Strategy: find the two middle vertices in the VertLeq ordering,
+ * and interpolate the intersection s-value from these. Then repeat
+ * using the TransLeq ordering to find the intersection t-value.
+ */
+
+ if( ! VertLeq( o1, d1 )) { Swap( o1, d1 ); }
+ if( ! VertLeq( o2, d2 )) { Swap( o2, d2 ); }
+ if( ! VertLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); }
+
+ if( ! VertLeq( o2, d1 )) {
+ /* Technically, no intersection -- do our best */
+ v->s = (o2->s + d1->s) / 2;
+ } else if( VertLeq( d1, d2 )) {
+ /* Interpolate between o2 and d1 */
+ z1 = EdgeEval( o1, o2, d1 );
+ z2 = EdgeEval( o2, d1, d2 );
+ if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
+ v->s = Interpolate( z1, o2->s, z2, d1->s );
+ } else {
+ /* Interpolate between o2 and d2 */
+ z1 = EdgeSign( o1, o2, d1 );
+ z2 = -EdgeSign( o1, d2, d1 );
+ if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
+ v->s = Interpolate( z1, o2->s, z2, d2->s );
+ }
+
+ /* Now repeat the process for t */
+
+ if( ! TransLeq( o1, d1 )) { Swap( o1, d1 ); }
+ if( ! TransLeq( o2, d2 )) { Swap( o2, d2 ); }
+ if( ! TransLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); }
+
+ if( ! TransLeq( o2, d1 )) {
+ /* Technically, no intersection -- do our best */
+ v->t = (o2->t + d1->t) / 2;
+ } else if( TransLeq( d1, d2 )) {
+ /* Interpolate between o2 and d1 */
+ z1 = TransEval( o1, o2, d1 );
+ z2 = TransEval( o2, d1, d2 );
+ if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
+ v->t = Interpolate( z1, o2->t, z2, d1->t );
+ } else {
+ /* Interpolate between o2 and d2 */
+ z1 = TransSign( o1, o2, d1 );
+ z2 = -TransSign( o1, d2, d1 );
+ if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
+ v->t = Interpolate( z1, o2->t, z2, d2->t );
+ }
+}
diff --git a/Source/geom.h b/Source/geom.h
new file mode 100755
index 0000000..cf83897
--- /dev/null
+++ b/Source/geom.h
@@ -0,0 +1,76 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#ifndef GEOM_H
+#define GEOM_H
+
+#include "mesh.h"
+
+#ifdef NO_BRANCH_CONDITIONS
+/* MIPS architecture has special instructions to evaluate boolean
+* conditions -- more efficient than branching, IF you can get the
+* compiler to generate the right instructions (SGI compiler doesn't)
+*/
+#define VertEq(u,v) (((u)->s == (v)->s) & ((u)->t == (v)->t))
+#define VertLeq(u,v) (((u)->s < (v)->s) | \
+ ((u)->s == (v)->s & (u)->t <= (v)->t))
+#else
+#define VertEq(u,v) ((u)->s == (v)->s && (u)->t == (v)->t)
+#define VertLeq(u,v) (((u)->s < (v)->s) || ((u)->s == (v)->s && (u)->t <= (v)->t))
+#endif
+
+#define EdgeEval(u,v,w) tesedgeEval(u,v,w)
+#define EdgeSign(u,v,w) tesedgeSign(u,v,w)
+
+/* Versions of VertLeq, EdgeSign, EdgeEval with s and t transposed. */
+
+#define TransLeq(u,v) (((u)->t < (v)->t) || ((u)->t == (v)->t && (u)->s <= (v)->s))
+#define TransEval(u,v,w) testransEval(u,v,w)
+#define TransSign(u,v,w) testransSign(u,v,w)
+
+
+#define EdgeGoesLeft(e) VertLeq( (e)->Dst, (e)->Org )
+#define EdgeGoesRight(e) VertLeq( (e)->Org, (e)->Dst )
+
+#define ABS(x) ((x) < 0 ? -(x) : (x))
+#define VertL1dist(u,v) (ABS(u->s - v->s) + ABS(u->t - v->t))
+
+#define VertCCW(u,v,w) tesvertCCW(u,v,w)
+
+int tesvertLeq( TESSvertex *u, TESSvertex *v );
+TESSreal tesedgeEval( TESSvertex *u, TESSvertex *v, TESSvertex *w );
+TESSreal tesedgeSign( TESSvertex *u, TESSvertex *v, TESSvertex *w );
+TESSreal testransEval( TESSvertex *u, TESSvertex *v, TESSvertex *w );
+TESSreal testransSign( TESSvertex *u, TESSvertex *v, TESSvertex *w );
+int tesvertCCW( TESSvertex *u, TESSvertex *v, TESSvertex *w );
+void tesedgeIntersect( TESSvertex *o1, TESSvertex *d1, TESSvertex *o2, TESSvertex *d2, TESSvertex *v );
+
+#endif
diff --git a/Source/mesh.c b/Source/mesh.c
new file mode 100755
index 0000000..06a8ced
--- /dev/null
+++ b/Source/mesh.c
@@ -0,0 +1,843 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+//#include "tesos.h"
+#include <stddef.h>
+#include <assert.h>
+#include "mesh.h"
+#include "geom.h"
+#include "bucketalloc.h"
+
+#define TRUE 1
+#define FALSE 0
+
+/************************ Utility Routines ************************/
+
+/* Allocate and free half-edges in pairs for efficiency.
+* The *only* place that should use this fact is allocation/free.
+*/
+typedef struct { TESShalfEdge e, eSym; } EdgePair;
+
+/* MakeEdge creates a new pair of half-edges which form their own loop.
+* No vertex or face structures are allocated, but these must be assigned
+* before the current edge operation is completed.
+*/
+static TESShalfEdge *MakeEdge( TESSmesh* mesh, TESShalfEdge *eNext )
+{
+ TESShalfEdge *e;
+ TESShalfEdge *eSym;
+ TESShalfEdge *ePrev;
+ EdgePair *pair = (EdgePair *)bucketAlloc( mesh->edgeBucket );
+ if (pair == NULL) return NULL;
+
+ e = &pair->e;
+ eSym = &pair->eSym;
+
+ /* Make sure eNext points to the first edge of the edge pair */
+ if( eNext->Sym < eNext ) { eNext = eNext->Sym; }
+
+ /* Insert in circular doubly-linked list before eNext.
+ * Note that the prev pointer is stored in Sym->next.
+ */
+ ePrev = eNext->Sym->next;
+ eSym->next = ePrev;
+ ePrev->Sym->next = e;
+ e->next = eNext;
+ eNext->Sym->next = eSym;
+
+ e->Sym = eSym;
+ e->Onext = e;
+ e->Lnext = eSym;
+ e->Org = NULL;
+ e->Lface = NULL;
+ e->winding = 0;
+ e->activeRegion = NULL;
+
+ eSym->Sym = e;
+ eSym->Onext = eSym;
+ eSym->Lnext = e;
+ eSym->Org = NULL;
+ eSym->Lface = NULL;
+ eSym->winding = 0;
+ eSym->activeRegion = NULL;
+
+ return e;
+}
+
+/* Splice( a, b ) is best described by the Guibas/Stolfi paper or the
+* CS348a notes (see mesh.h). Basically it modifies the mesh so that
+* a->Onext and b->Onext are exchanged. This can have various effects
+* depending on whether a and b belong to different face or vertex rings.
+* For more explanation see tessMeshSplice() below.
+*/
+static void Splice( TESShalfEdge *a, TESShalfEdge *b )
+{
+ TESShalfEdge *aOnext = a->Onext;
+ TESShalfEdge *bOnext = b->Onext;
+
+ aOnext->Sym->Lnext = b;
+ bOnext->Sym->Lnext = a;
+ a->Onext = bOnext;
+ b->Onext = aOnext;
+}
+
+/* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the
+* origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives
+* a place to insert the new vertex in the global vertex list. We insert
+* the new vertex *before* vNext so that algorithms which walk the vertex
+* list will not see the newly created vertices.
+*/
+static void MakeVertex( TESSvertex *newVertex,
+ TESShalfEdge *eOrig, TESSvertex *vNext )
+{
+ TESShalfEdge *e;
+ TESSvertex *vPrev;
+ TESSvertex *vNew = newVertex;
+
+ assert(vNew != NULL);
+
+ /* insert in circular doubly-linked list before vNext */
+ vPrev = vNext->prev;
+ vNew->prev = vPrev;
+ vPrev->next = vNew;
+ vNew->next = vNext;
+ vNext->prev = vNew;
+
+ vNew->anEdge = eOrig;
+ /* leave coords, s, t undefined */
+
+ /* fix other edges on this vertex loop */
+ e = eOrig;
+ do {
+ e->Org = vNew;
+ e = e->Onext;
+ } while( e != eOrig );
+}
+
+/* MakeFace( newFace, eOrig, fNext ) attaches a new face and makes it the left
+* face of all edges in the face loop to which eOrig belongs. "fNext" gives
+* a place to insert the new face in the global face list. We insert
+* the new face *before* fNext so that algorithms which walk the face
+* list will not see the newly created faces.
+*/
+static void MakeFace( TESSface *newFace, TESShalfEdge *eOrig, TESSface *fNext )
+{
+ TESShalfEdge *e;
+ TESSface *fPrev;
+ TESSface *fNew = newFace;
+
+ assert(fNew != NULL);
+
+ /* insert in circular doubly-linked list before fNext */
+ fPrev = fNext->prev;
+ fNew->prev = fPrev;
+ fPrev->next = fNew;
+ fNew->next = fNext;
+ fNext->prev = fNew;
+
+ fNew->anEdge = eOrig;
+ fNew->trail = NULL;
+ fNew->marked = FALSE;
+
+ /* The new face is marked "inside" if the old one was. This is a
+ * convenience for the common case where a face has been split in two.
+ */
+ fNew->inside = fNext->inside;
+
+ /* fix other edges on this face loop */
+ e = eOrig;
+ do {
+ e->Lface = fNew;
+ e = e->Lnext;
+ } while( e != eOrig );
+}
+
+/* KillEdge( eDel ) destroys an edge (the half-edges eDel and eDel->Sym),
+* and removes from the global edge list.
+*/
+static void KillEdge( TESSmesh *mesh, TESShalfEdge *eDel )
+{
+ TESShalfEdge *ePrev, *eNext;
+
+ /* Half-edges are allocated in pairs, see EdgePair above */
+ if( eDel->Sym < eDel ) { eDel = eDel->Sym; }
+
+ /* delete from circular doubly-linked list */
+ eNext = eDel->next;
+ ePrev = eDel->Sym->next;
+ eNext->Sym->next = ePrev;
+ ePrev->Sym->next = eNext;
+
+ bucketFree( mesh->edgeBucket, eDel );
+}
+
+
+/* KillVertex( vDel ) destroys a vertex and removes it from the global
+* vertex list. It updates the vertex loop to point to a given new vertex.
+*/
+static void KillVertex( TESSmesh *mesh, TESSvertex *vDel, TESSvertex *newOrg )
+{
+ TESShalfEdge *e, *eStart = vDel->anEdge;
+ TESSvertex *vPrev, *vNext;
+
+ /* change the origin of all affected edges */
+ e = eStart;
+ do {
+ e->Org = newOrg;
+ e = e->Onext;
+ } while( e != eStart );
+
+ /* delete from circular doubly-linked list */
+ vPrev = vDel->prev;
+ vNext = vDel->next;
+ vNext->prev = vPrev;
+ vPrev->next = vNext;
+
+ bucketFree( mesh->vertexBucket, vDel );
+}
+
+/* KillFace( fDel ) destroys a face and removes it from the global face
+* list. It updates the face loop to point to a given new face.
+*/
+static void KillFace( TESSmesh *mesh, TESSface *fDel, TESSface *newLface )
+{
+ TESShalfEdge *e, *eStart = fDel->anEdge;
+ TESSface *fPrev, *fNext;
+
+ /* change the left face of all affected edges */
+ e = eStart;
+ do {
+ e->Lface = newLface;
+ e = e->Lnext;
+ } while( e != eStart );
+
+ /* delete from circular doubly-linked list */
+ fPrev = fDel->prev;
+ fNext = fDel->next;
+ fNext->prev = fPrev;
+ fPrev->next = fNext;
+
+ bucketFree( mesh->faceBucket, fDel );
+}
+
+
+/****************** Basic Edge Operations **********************/
+
+/* tessMeshMakeEdge creates one edge, two vertices, and a loop (face).
+* The loop consists of the two new half-edges.
+*/
+TESShalfEdge *tessMeshMakeEdge( TESSmesh *mesh )
+{
+ TESSvertex *newVertex1 = (TESSvertex*)bucketAlloc(mesh->vertexBucket);
+ TESSvertex *newVertex2 = (TESSvertex*)bucketAlloc(mesh->vertexBucket);
+ TESSface *newFace = (TESSface*)bucketAlloc(mesh->faceBucket);
+ TESShalfEdge *e;
+
+ /* if any one is null then all get freed */
+ if (newVertex1 == NULL || newVertex2 == NULL || newFace == NULL) {
+ if (newVertex1 != NULL) bucketFree( mesh->vertexBucket, newVertex1 );
+ if (newVertex2 != NULL) bucketFree( mesh->vertexBucket, newVertex2 );
+ if (newFace != NULL) bucketFree( mesh->faceBucket, newFace );
+ return NULL;
+ }
+
+ e = MakeEdge( mesh, &mesh->eHead );
+ if (e == NULL) return NULL;
+
+ MakeVertex( newVertex1, e, &mesh->vHead );
+ MakeVertex( newVertex2, e->Sym, &mesh->vHead );
+ MakeFace( newFace, e, &mesh->fHead );
+ return e;
+}
+
+
+/* tessMeshSplice( eOrg, eDst ) is the basic operation for changing the
+* mesh connectivity and topology. It changes the mesh so that
+* eOrg->Onext <- OLD( eDst->Onext )
+* eDst->Onext <- OLD( eOrg->Onext )
+* where OLD(...) means the value before the meshSplice operation.
+*
+* This can have two effects on the vertex structure:
+* - if eOrg->Org != eDst->Org, the two vertices are merged together
+* - if eOrg->Org == eDst->Org, the origin is split into two vertices
+* In both cases, eDst->Org is changed and eOrg->Org is untouched.
+*
+* Similarly (and independently) for the face structure,
+* - if eOrg->Lface == eDst->Lface, one loop is split into two
+* - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one
+* In both cases, eDst->Lface is changed and eOrg->Lface is unaffected.
+*
+* Some special cases:
+* If eDst == eOrg, the operation has no effect.
+* If eDst == eOrg->Lnext, the new face will have a single edge.
+* If eDst == eOrg->Lprev, the old face will have a single edge.
+* If eDst == eOrg->Onext, the new vertex will have a single edge.
+* If eDst == eOrg->Oprev, the old vertex will have a single edge.
+*/
+int tessMeshSplice( TESSmesh* mesh, TESShalfEdge *eOrg, TESShalfEdge *eDst )
+{
+ int joiningLoops = FALSE;
+ int joiningVertices = FALSE;
+
+ if( eOrg == eDst ) return 1;
+
+ if( eDst->Org != eOrg->Org ) {
+ /* We are merging two disjoint vertices -- destroy eDst->Org */
+ joiningVertices = TRUE;
+ KillVertex( mesh, eDst->Org, eOrg->Org );
+ }
+ if( eDst->Lface != eOrg->Lface ) {
+ /* We are connecting two disjoint loops -- destroy eDst->Lface */
+ joiningLoops = TRUE;
+ KillFace( mesh, eDst->Lface, eOrg->Lface );
+ }
+
+ /* Change the edge structure */
+ Splice( eDst, eOrg );
+
+ if( ! joiningVertices ) {
+ TESSvertex *newVertex = (TESSvertex*)bucketAlloc( mesh->vertexBucket );
+ if (newVertex == NULL) return 0;
+
+ /* We split one vertex into two -- the new vertex is eDst->Org.
+ * Make sure the old vertex points to a valid half-edge.
+ */
+ MakeVertex( newVertex, eDst, eOrg->Org );
+ eOrg->Org->anEdge = eOrg;
+ }
+ if( ! joiningLoops ) {
+ TESSface *newFace = (TESSface*)bucketAlloc( mesh->faceBucket );
+ if (newFace == NULL) return 0;
+
+ /* We split one loop into two -- the new loop is eDst->Lface.
+ * Make sure the old face points to a valid half-edge.
+ */
+ MakeFace( newFace, eDst, eOrg->Lface );
+ eOrg->Lface->anEdge = eOrg;
+ }
+
+ return 1;
+}
+
+
+/* tessMeshDelete( eDel ) removes the edge eDel. There are several cases:
+* if (eDel->Lface != eDel->Rface), we join two loops into one; the loop
+* eDel->Lface is deleted. Otherwise, we are splitting one loop into two;
+* the newly created loop will contain eDel->Dst. If the deletion of eDel
+* would create isolated vertices, those are deleted as well.
+*
+* This function could be implemented as two calls to tessMeshSplice
+* plus a few calls to memFree, but this would allocate and delete
+* unnecessary vertices and faces.
+*/
+int tessMeshDelete( TESSmesh *mesh, TESShalfEdge *eDel )
+{
+ TESShalfEdge *eDelSym = eDel->Sym;
+ int joiningLoops = FALSE;
+
+ /* First step: disconnect the origin vertex eDel->Org. We make all
+ * changes to get a consistent mesh in this "intermediate" state.
+ */
+ if( eDel->Lface != eDel->Rface ) {
+ /* We are joining two loops into one -- remove the left face */
+ joiningLoops = TRUE;
+ KillFace( mesh, eDel->Lface, eDel->Rface );
+ }
+
+ if( eDel->Onext == eDel ) {
+ KillVertex( mesh, eDel->Org, NULL );
+ } else {
+ /* Make sure that eDel->Org and eDel->Rface point to valid half-edges */
+ eDel->Rface->anEdge = eDel->Oprev;
+ eDel->Org->anEdge = eDel->Onext;
+
+ Splice( eDel, eDel->Oprev );
+ if( ! joiningLoops ) {
+ TESSface *newFace= (TESSface*)bucketAlloc( mesh->faceBucket );
+ if (newFace == NULL) return 0;
+
+ /* We are splitting one loop into two -- create a new loop for eDel. */
+ MakeFace( newFace, eDel, eDel->Lface );
+ }
+ }
+
+ /* Claim: the mesh is now in a consistent state, except that eDel->Org
+ * may have been deleted. Now we disconnect eDel->Dst.
+ */
+ if( eDelSym->Onext == eDelSym ) {
+ KillVertex( mesh, eDelSym->Org, NULL );
+ KillFace( mesh, eDelSym->Lface, NULL );
+ } else {
+ /* Make sure that eDel->Dst and eDel->Lface point to valid half-edges */
+ eDel->Lface->anEdge = eDelSym->Oprev;
+ eDelSym->Org->anEdge = eDelSym->Onext;
+ Splice( eDelSym, eDelSym->Oprev );
+ }
+
+ /* Any isolated vertices or faces have already been freed. */
+ KillEdge( mesh, eDel );
+
+ return 1;
+}
+
+
+/******************** Other Edge Operations **********************/
+
+/* All these routines can be implemented with the basic edge
+* operations above. They are provided for convenience and efficiency.
+*/
+
+
+/* tessMeshAddEdgeVertex( eOrg ) creates a new edge eNew such that
+* eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex.
+* eOrg and eNew will have the same left face.
+*/
+TESShalfEdge *tessMeshAddEdgeVertex( TESSmesh *mesh, TESShalfEdge *eOrg )
+{
+ TESShalfEdge *eNewSym;
+ TESShalfEdge *eNew = MakeEdge( mesh, eOrg );
+ if (eNew == NULL) return NULL;
+
+ eNewSym = eNew->Sym;
+
+ /* Connect the new edge appropriately */
+ Splice( eNew, eOrg->Lnext );
+
+ /* Set the vertex and face information */
+ eNew->Org = eOrg->Dst;
+ {
+ TESSvertex *newVertex= (TESSvertex*)bucketAlloc( mesh->vertexBucket );
+ if (newVertex == NULL) return NULL;
+
+ MakeVertex( newVertex, eNewSym, eNew->Org );
+ }
+ eNew->Lface = eNewSym->Lface = eOrg->Lface;
+
+ return eNew;
+}
+
+
+/* tessMeshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
+* such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org.
+* eOrg and eNew will have the same left face.
+*/
+TESShalfEdge *tessMeshSplitEdge( TESSmesh *mesh, TESShalfEdge *eOrg )
+{
+ TESShalfEdge *eNew;
+ TESShalfEdge *tempHalfEdge= tessMeshAddEdgeVertex( mesh, eOrg );
+ if (tempHalfEdge == NULL) return NULL;
+
+ eNew = tempHalfEdge->Sym;
+
+ /* Disconnect eOrg from eOrg->Dst and connect it to eNew->Org */
+ Splice( eOrg->Sym, eOrg->Sym->Oprev );
+ Splice( eOrg->Sym, eNew );
+
+ /* Set the vertex and face information */
+ eOrg->Dst = eNew->Org;
+ eNew->Dst->anEdge = eNew->Sym; /* may have pointed to eOrg->Sym */
+ eNew->Rface = eOrg->Rface;
+ eNew->winding = eOrg->winding; /* copy old winding information */
+ eNew->Sym->winding = eOrg->Sym->winding;
+
+ return eNew;
+}
+
+
+/* tessMeshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst
+* to eDst->Org, and returns the corresponding half-edge eNew.
+* If eOrg->Lface == eDst->Lface, this splits one loop into two,
+* and the newly created loop is eNew->Lface. Otherwise, two disjoint
+* loops are merged into one, and the loop eDst->Lface is destroyed.
+*
+* If (eOrg == eDst), the new face will have only two edges.
+* If (eOrg->Lnext == eDst), the old face is reduced to a single edge.
+* If (eOrg->Lnext->Lnext == eDst), the old face is reduced to two edges.
+*/
+TESShalfEdge *tessMeshConnect( TESSmesh *mesh, TESShalfEdge *eOrg, TESShalfEdge *eDst )
+{
+ TESShalfEdge *eNewSym;
+ int joiningLoops = FALSE;
+ TESShalfEdge *eNew = MakeEdge( mesh, eOrg );
+ if (eNew == NULL) return NULL;
+
+ eNewSym = eNew->Sym;
+
+ if( eDst->Lface != eOrg->Lface ) {
+ /* We are connecting two disjoint loops -- destroy eDst->Lface */
+ joiningLoops = TRUE;
+ KillFace( mesh, eDst->Lface, eOrg->Lface );
+ }
+
+ /* Connect the new edge appropriately */
+ Splice( eNew, eOrg->Lnext );
+ Splice( eNewSym, eDst );
+
+ /* Set the vertex and face information */
+ eNew->Org = eOrg->Dst;
+ eNewSym->Org = eDst->Org;
+ eNew->Lface = eNewSym->Lface = eOrg->Lface;
+
+ /* Make sure the old face points to a valid half-edge */
+ eOrg->Lface->anEdge = eNewSym;
+
+ if( ! joiningLoops ) {
+ TESSface *newFace= (TESSface*)bucketAlloc( mesh->faceBucket );
+ if (newFace == NULL) return NULL;
+
+ /* We split one loop into two -- the new loop is eNew->Lface */
+ MakeFace( newFace, eNew, eOrg->Lface );
+ }
+ return eNew;
+}
+
+
+/******************** Other Operations **********************/
+
+/* tessMeshZapFace( fZap ) destroys a face and removes it from the
+* global face list. All edges of fZap will have a NULL pointer as their
+* left face. Any edges which also have a NULL pointer as their right face
+* are deleted entirely (along with any isolated vertices this produces).
+* An entire mesh can be deleted by zapping its faces, one at a time,
+* in any order. Zapped faces cannot be used in further mesh operations!
+*/
+void tessMeshZapFace( TESSmesh *mesh, TESSface *fZap )
+{
+ TESShalfEdge *eStart = fZap->anEdge;
+ TESShalfEdge *e, *eNext, *eSym;
+ TESSface *fPrev, *fNext;
+
+ /* walk around face, deleting edges whose right face is also NULL */
+ eNext = eStart->Lnext;
+ do {
+ e = eNext;
+ eNext = e->Lnext;
+
+ e->Lface = NULL;
+ if( e->Rface == NULL ) {
+ /* delete the edge -- see TESSmeshDelete above */
+
+ if( e->Onext == e ) {
+ KillVertex( mesh, e->Org, NULL );
+ } else {
+ /* Make sure that e->Org points to a valid half-edge */
+ e->Org->anEdge = e->Onext;
+ Splice( e, e->Oprev );
+ }
+ eSym = e->Sym;
+ if( eSym->Onext == eSym ) {
+ KillVertex( mesh, eSym->Org, NULL );
+ } else {
+ /* Make sure that eSym->Org points to a valid half-edge */
+ eSym->Org->anEdge = eSym->Onext;
+ Splice( eSym, eSym->Oprev );
+ }
+ KillEdge( mesh, e );
+ }
+ } while( e != eStart );
+
+ /* delete from circular doubly-linked list */
+ fPrev = fZap->prev;
+ fNext = fZap->next;
+ fNext->prev = fPrev;
+ fPrev->next = fNext;
+
+ bucketFree( mesh->faceBucket, fZap );
+}
+
+
+/* tessMeshNewMesh() creates a new mesh with no edges, no vertices,
+* and no loops (what we usually call a "face").
+*/
+TESSmesh *tessMeshNewMesh( TESSalloc* alloc )
+{
+ TESSvertex *v;
+ TESSface *f;
+ TESShalfEdge *e;
+ TESShalfEdge *eSym;
+ TESSmesh *mesh = (TESSmesh *)alloc->memalloc( alloc->userData, sizeof( TESSmesh ));
+ if (mesh == NULL) {
+ return NULL;
+ }
+
+ if (alloc->meshEdgeBucketSize < 16)
+ alloc->meshEdgeBucketSize = 16;
+ if (alloc->meshEdgeBucketSize > 4096)
+ alloc->meshEdgeBucketSize = 4096;
+
+ if (alloc->meshVertexBucketSize < 16)
+ alloc->meshVertexBucketSize = 16;
+ if (alloc->meshVertexBucketSize > 4096)
+ alloc->meshVertexBucketSize = 4096;
+
+ if (alloc->meshFaceBucketSize < 16)
+ alloc->meshFaceBucketSize = 16;
+ if (alloc->meshFaceBucketSize > 4096)
+ alloc->meshFaceBucketSize = 4096;
+
+ mesh->edgeBucket = createBucketAlloc( alloc, "Mesh Edges", sizeof(EdgePair), alloc->meshEdgeBucketSize );
+ mesh->vertexBucket = createBucketAlloc( alloc, "Mesh Vertices", sizeof(TESSvertex), alloc->meshVertexBucketSize );
+ mesh->faceBucket = createBucketAlloc( alloc, "Mesh Faces", sizeof(TESSface), alloc->meshFaceBucketSize );
+
+ v = &mesh->vHead;
+ f = &mesh->fHead;
+ e = &mesh->eHead;
+ eSym = &mesh->eHeadSym;
+
+ v->next = v->prev = v;
+ v->anEdge = NULL;
+
+ f->next = f->prev = f;
+ f->anEdge = NULL;
+ f->trail = NULL;
+ f->marked = FALSE;
+ f->inside = FALSE;
+
+ e->next = e;
+ e->Sym = eSym;
+ e->Onext = NULL;
+ e->Lnext = NULL;
+ e->Org = NULL;
+ e->Lface = NULL;
+ e->winding = 0;
+ e->activeRegion = NULL;
+
+ eSym->next = eSym;
+ eSym->Sym = e;
+ eSym->Onext = NULL;
+ eSym->Lnext = NULL;
+ eSym->Org = NULL;
+ eSym->Lface = NULL;
+ eSym->winding = 0;
+ eSym->activeRegion = NULL;
+
+ return mesh;
+}
+
+
+/* tessMeshUnion( mesh1, mesh2 ) forms the union of all structures in
+* both meshes, and returns the new mesh (the old meshes are destroyed).
+*/
+TESSmesh *tessMeshUnion( TESSalloc* alloc, TESSmesh *mesh1, TESSmesh *mesh2 )
+{
+ TESSface *f1 = &mesh1->fHead;
+ TESSvertex *v1 = &mesh1->vHead;
+ TESShalfEdge *e1 = &mesh1->eHead;
+ TESSface *f2 = &mesh2->fHead;
+ TESSvertex *v2 = &mesh2->vHead;
+ TESShalfEdge *e2 = &mesh2->eHead;
+
+ /* Add the faces, vertices, and edges of mesh2 to those of mesh1 */
+ if( f2->next != f2 ) {
+ f1->prev->next = f2->next;
+ f2->next->prev = f1->prev;
+ f2->prev->next = f1;
+ f1->prev = f2->prev;
+ }
+
+ if( v2->next != v2 ) {
+ v1->prev->next = v2->next;
+ v2->next->prev = v1->prev;
+ v2->prev->next = v1;
+ v1->prev = v2->prev;
+ }
+
+ if( e2->next != e2 ) {
+ e1->Sym->next->Sym->next = e2->next;
+ e2->next->Sym->next = e1->Sym->next;
+ e2->Sym->next->Sym->next = e1;
+ e1->Sym->next = e2->Sym->next;
+ }
+
+ alloc->memfree( alloc->userData, mesh2 );
+ return mesh1;
+}
+
+
+static int CountFaceVerts( TESSface *f )
+{
+ TESShalfEdge *eCur = f->anEdge;
+ int n = 0;
+ do
+ {
+ n++;
+ eCur = eCur->Lnext;
+ }
+ while (eCur != f->anEdge);
+ return n;
+}
+
+int tessMeshMergeConvexFaces( TESSmesh *mesh, int maxVertsPerFace )
+{
+ TESSface *f;
+ TESShalfEdge *eCur, *eNext, *eSym;
+ TESSvertex *vStart;
+ int curNv, symNv;
+
+ for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next )
+ {
+ // Skip faces which are outside the result.
+ if( !f->inside )
+ continue;
+
+ eCur = f->anEdge;
+ vStart = eCur->Org;
+
+ while (1)
+ {
+ eNext = eCur->Lnext;
+ eSym = eCur->Sym;
+
+ // Try to merge if the neighbour face is valid.
+ if( eSym && eSym->Lface && eSym->Lface->inside )
+ {
+ // Try to merge the neighbour faces if the resulting polygons
+ // does not exceed maximum number of vertices.
+ curNv = CountFaceVerts( f );
+ symNv = CountFaceVerts( eSym->Lface );
+ if( (curNv+symNv-2) <= maxVertsPerFace )
+ {
+ // Merge if the resulting poly is convex.
+ if( VertCCW( eCur->Lprev->Org, eCur->Org, eSym->Lnext->Lnext->Org ) &&
+ VertCCW( eSym->Lprev->Org, eSym->Org, eCur->Lnext->Lnext->Org ) )
+ {
+ eNext = eSym->Lnext;
+ if( !tessMeshDelete( mesh, eSym ) )
+ return 0;
+ eCur = 0;
+ }
+ }
+ }
+
+ if( eCur && eCur->Lnext->Org == vStart )
+ break;
+
+ // Continue to next edge.
+ eCur = eNext;
+ }
+ }
+
+ return 1;
+}
+
+
+#ifdef DELETE_BY_ZAPPING
+
+/* tessMeshDeleteMesh( mesh ) will free all storage for any valid mesh.
+*/
+void tessMeshDeleteMesh( TESSalloc* alloc, TESSmesh *mesh )
+{
+ TESSface *fHead = &mesh->fHead;
+
+ while( fHead->next != fHead ) {
+ tessMeshZapFace( fHead->next );
+ }
+ assert( mesh->vHead.next == &mesh->vHead );
+
+ alloc->memfree( alloc->userData, mesh );
+}
+
+#else
+
+/* tessMeshDeleteMesh( mesh ) will free all storage for any valid mesh.
+*/
+void tessMeshDeleteMesh( TESSalloc* alloc, TESSmesh *mesh )
+{
+ deleteBucketAlloc(mesh->edgeBucket);
+ deleteBucketAlloc(mesh->vertexBucket);
+ deleteBucketAlloc(mesh->faceBucket);
+
+ alloc->memfree( alloc->userData, mesh );
+}
+
+#endif
+
+#ifndef NDEBUG
+
+/* tessMeshCheckMesh( mesh ) checks a mesh for self-consistency.
+*/
+void tessMeshCheckMesh( TESSmesh *mesh )
+{
+ TESSface *fHead = &mesh->fHead;
+ TESSvertex *vHead = &mesh->vHead;
+ TESShalfEdge *eHead = &mesh->eHead;
+ TESSface *f, *fPrev;
+ TESSvertex *v, *vPrev;
+ TESShalfEdge *e, *ePrev;
+
+ fPrev = fHead;
+ for( fPrev = fHead ; (f = fPrev->next) != fHead; fPrev = f) {
+ assert( f->prev == fPrev );
+ e = f->anEdge;
+ do {
+ assert( e->Sym != e );
+ assert( e->Sym->Sym == e );
+ assert( e->Lnext->Onext->Sym == e );
+ assert( e->Onext->Sym->Lnext == e );
+ assert( e->Lface == f );
+ e = e->Lnext;
+ } while( e != f->anEdge );
+ }
+ assert( f->prev == fPrev && f->anEdge == NULL );
+
+ vPrev = vHead;
+ for( vPrev = vHead ; (v = vPrev->next) != vHead; vPrev = v) {
+ assert( v->prev == vPrev );
+ e = v->anEdge;
+ do {
+ assert( e->Sym != e );
+ assert( e->Sym->Sym == e );
+ assert( e->Lnext->Onext->Sym == e );
+ assert( e->Onext->Sym->Lnext == e );
+ assert( e->Org == v );
+ e = e->Onext;
+ } while( e != v->anEdge );
+ }
+ assert( v->prev == vPrev && v->anEdge == NULL );
+
+ ePrev = eHead;
+ for( ePrev = eHead ; (e = ePrev->next) != eHead; ePrev = e) {
+ assert( e->Sym->next == ePrev->Sym );
+ assert( e->Sym != e );
+ assert( e->Sym->Sym == e );
+ assert( e->Org != NULL );
+ assert( e->Dst != NULL );
+ assert( e->Lnext->Onext->Sym == e );
+ assert( e->Onext->Sym->Lnext == e );
+ }
+ assert( e->Sym->next == ePrev->Sym
+ && e->Sym == &mesh->eHeadSym
+ && e->Sym->Sym == e
+ && e->Org == NULL && e->Dst == NULL
+ && e->Lface == NULL && e->Rface == NULL );
+}
+
+#endif
diff --git a/Source/mesh.h b/Source/mesh.h
new file mode 100755
index 0000000..c492916
--- /dev/null
+++ b/Source/mesh.h
@@ -0,0 +1,267 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#ifndef MESH_H
+#define MESH_H
+
+#include "../Include/tesselator.h"
+
+typedef struct TESSmesh TESSmesh;
+typedef struct TESSvertex TESSvertex;
+typedef struct TESSface TESSface;
+typedef struct TESShalfEdge TESShalfEdge;
+typedef struct ActiveRegion ActiveRegion;
+
+/* The mesh structure is similar in spirit, notation, and operations
+* to the "quad-edge" structure (see L. Guibas and J. Stolfi, Primitives
+* for the manipulation of general subdivisions and the computation of
+* Voronoi diagrams, ACM Transactions on Graphics, 4(2):74-123, April 1985).
+* For a simplified description, see the course notes for CS348a,
+* "Mathematical Foundations of Computer Graphics", available at the
+* Stanford bookstore (and taught during the fall quarter).
+* The implementation also borrows a tiny subset of the graph-based approach
+* use in Mantyla's Geometric Work Bench (see M. Mantyla, An Introduction
+* to Sold Modeling, Computer Science Press, Rockville, Maryland, 1988).
+*
+* The fundamental data structure is the "half-edge". Two half-edges
+* go together to make an edge, but they point in opposite directions.
+* Each half-edge has a pointer to its mate (the "symmetric" half-edge Sym),
+* its origin vertex (Org), the face on its left side (Lface), and the
+* adjacent half-edges in the CCW direction around the origin vertex
+* (Onext) and around the left face (Lnext). There is also a "next"
+* pointer for the global edge list (see below).
+*
+* The notation used for mesh navigation:
+* Sym = the mate of a half-edge (same edge, but opposite direction)
+* Onext = edge CCW around origin vertex (keep same origin)
+* Dnext = edge CCW around destination vertex (keep same dest)
+* Lnext = edge CCW around left face (dest becomes new origin)
+* Rnext = edge CCW around right face (origin becomes new dest)
+*
+* "prev" means to substitute CW for CCW in the definitions above.
+*
+* The mesh keeps global lists of all vertices, faces, and edges,
+* stored as doubly-linked circular lists with a dummy header node.
+* The mesh stores pointers to these dummy headers (vHead, fHead, eHead).
+*
+* The circular edge list is special; since half-edges always occur
+* in pairs (e and e->Sym), each half-edge stores a pointer in only
+* one direction. Starting at eHead and following the e->next pointers
+* will visit each *edge* once (ie. e or e->Sym, but not both).
+* e->Sym stores a pointer in the opposite direction, thus it is
+* always true that e->Sym->next->Sym->next == e.
+*
+* Each vertex has a pointer to next and previous vertices in the
+* circular list, and a pointer to a half-edge with this vertex as
+* the origin (NULL if this is the dummy header). There is also a
+* field "data" for client data.
+*
+* Each face has a pointer to the next and previous faces in the
+* circular list, and a pointer to a half-edge with this face as
+* the left face (NULL if this is the dummy header). There is also
+* a field "data" for client data.
+*
+* Note that what we call a "face" is really a loop; faces may consist
+* of more than one loop (ie. not simply connected), but there is no
+* record of this in the data structure. The mesh may consist of
+* several disconnected regions, so it may not be possible to visit
+* the entire mesh by starting at a half-edge and traversing the edge
+* structure.
+*
+* The mesh does NOT support isolated vertices; a vertex is deleted along
+* with its last edge. Similarly when two faces are merged, one of the
+* faces is deleted (see tessMeshDelete below). For mesh operations,
+* all face (loop) and vertex pointers must not be NULL. However, once
+* mesh manipulation is finished, TESSmeshZapFace can be used to delete
+* faces of the mesh, one at a time. All external faces can be "zapped"
+* before the mesh is returned to the client; then a NULL face indicates
+* a region which is not part of the output polygon.
+*/
+
+struct TESSvertex {
+ TESSvertex *next; /* next vertex (never NULL) */
+ TESSvertex *prev; /* previous vertex (never NULL) */
+ TESShalfEdge *anEdge; /* a half-edge with this origin */
+
+ /* Internal data (keep hidden) */
+ TESSreal coords[3]; /* vertex location in 3D */
+ TESSreal s, t; /* projection onto the sweep plane */
+ int pqHandle; /* to allow deletion from priority queue */
+ TESSindex n; /* to allow identify unique vertices */
+ TESSindex idx; /* to allow map result to original verts */
+};
+
+struct TESSface {
+ TESSface *next; /* next face (never NULL) */
+ TESSface *prev; /* previous face (never NULL) */
+ TESShalfEdge *anEdge; /* a half edge with this left face */
+
+ /* Internal data (keep hidden) */
+ TESSface *trail; /* "stack" for conversion to strips */
+ TESSindex n; /* to allow identiy unique faces */
+ char marked; /* flag for conversion to strips */
+ char inside; /* this face is in the polygon interior */
+};
+
+struct TESShalfEdge {
+ TESShalfEdge *next; /* doubly-linked list (prev==Sym->next) */
+ TESShalfEdge *Sym; /* same edge, opposite direction */
+ TESShalfEdge *Onext; /* next edge CCW around origin */
+ TESShalfEdge *Lnext; /* next edge CCW around left face */
+ TESSvertex *Org; /* origin vertex (Overtex too long) */
+ TESSface *Lface; /* left face */
+
+ /* Internal data (keep hidden) */
+ ActiveRegion *activeRegion; /* a region with this upper edge (sweep.c) */
+ int winding; /* change in winding number when crossing
+ from the right face to the left face */
+};
+
+#define Rface Sym->Lface
+#define Dst Sym->Org
+
+#define Oprev Sym->Lnext
+#define Lprev Onext->Sym
+#define Dprev Lnext->Sym
+#define Rprev Sym->Onext
+#define Dnext Rprev->Sym /* 3 pointers */
+#define Rnext Oprev->Sym /* 3 pointers */
+
+
+struct TESSmesh {
+ TESSvertex vHead; /* dummy header for vertex list */
+ TESSface fHead; /* dummy header for face list */
+ TESShalfEdge eHead; /* dummy header for edge list */
+ TESShalfEdge eHeadSym; /* and its symmetric counterpart */
+
+ struct BucketAlloc* edgeBucket;
+ struct BucketAlloc* vertexBucket;
+ struct BucketAlloc* faceBucket;
+};
+
+/* The mesh operations below have three motivations: completeness,
+* convenience, and efficiency. The basic mesh operations are MakeEdge,
+* Splice, and Delete. All the other edge operations can be implemented
+* in terms of these. The other operations are provided for convenience
+* and/or efficiency.
+*
+* When a face is split or a vertex is added, they are inserted into the
+* global list *before* the existing vertex or face (ie. e->Org or e->Lface).
+* This makes it easier to process all vertices or faces in the global lists
+* without worrying about processing the same data twice. As a convenience,
+* when a face is split, the "inside" flag is copied from the old face.
+* Other internal data (v->data, v->activeRegion, f->data, f->marked,
+* f->trail, e->winding) is set to zero.
+*
+* ********************** Basic Edge Operations **************************
+*
+* tessMeshMakeEdge( mesh ) creates one edge, two vertices, and a loop.
+* The loop (face) consists of the two new half-edges.
+*
+* tessMeshSplice( eOrg, eDst ) is the basic operation for changing the
+* mesh connectivity and topology. It changes the mesh so that
+* eOrg->Onext <- OLD( eDst->Onext )
+* eDst->Onext <- OLD( eOrg->Onext )
+* where OLD(...) means the value before the meshSplice operation.
+*
+* This can have two effects on the vertex structure:
+* - if eOrg->Org != eDst->Org, the two vertices are merged together
+* - if eOrg->Org == eDst->Org, the origin is split into two vertices
+* In both cases, eDst->Org is changed and eOrg->Org is untouched.
+*
+* Similarly (and independently) for the face structure,
+* - if eOrg->Lface == eDst->Lface, one loop is split into two
+* - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one
+* In both cases, eDst->Lface is changed and eOrg->Lface is unaffected.
+*
+* tessMeshDelete( eDel ) removes the edge eDel. There are several cases:
+* if (eDel->Lface != eDel->Rface), we join two loops into one; the loop
+* eDel->Lface is deleted. Otherwise, we are splitting one loop into two;
+* the newly created loop will contain eDel->Dst. If the deletion of eDel
+* would create isolated vertices, those are deleted as well.
+*
+* ********************** Other Edge Operations **************************
+*
+* tessMeshAddEdgeVertex( eOrg ) creates a new edge eNew such that
+* eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex.
+* eOrg and eNew will have the same left face.
+*
+* tessMeshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
+* such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org.
+* eOrg and eNew will have the same left face.
+*
+* tessMeshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst
+* to eDst->Org, and returns the corresponding half-edge eNew.
+* If eOrg->Lface == eDst->Lface, this splits one loop into two,
+* and the newly created loop is eNew->Lface. Otherwise, two disjoint
+* loops are merged into one, and the loop eDst->Lface is destroyed.
+*
+* ************************ Other Operations *****************************
+*
+* tessMeshNewMesh() creates a new mesh with no edges, no vertices,
+* and no loops (what we usually call a "face").
+*
+* tessMeshUnion( mesh1, mesh2 ) forms the union of all structures in
+* both meshes, and returns the new mesh (the old meshes are destroyed).
+*
+* tessMeshDeleteMesh( mesh ) will free all storage for any valid mesh.
+*
+* tessMeshZapFace( fZap ) destroys a face and removes it from the
+* global face list. All edges of fZap will have a NULL pointer as their
+* left face. Any edges which also have a NULL pointer as their right face
+* are deleted entirely (along with any isolated vertices this produces).
+* An entire mesh can be deleted by zapping its faces, one at a time,
+* in any order. Zapped faces cannot be used in further mesh operations!
+*
+* tessMeshCheckMesh( mesh ) checks a mesh for self-consistency.
+*/
+
+TESShalfEdge *tessMeshMakeEdge( TESSmesh *mesh );
+int tessMeshSplice( TESSmesh *mesh, TESShalfEdge *eOrg, TESShalfEdge *eDst );
+int tessMeshDelete( TESSmesh *mesh, TESShalfEdge *eDel );
+
+TESShalfEdge *tessMeshAddEdgeVertex( TESSmesh *mesh, TESShalfEdge *eOrg );
+TESShalfEdge *tessMeshSplitEdge( TESSmesh *mesh, TESShalfEdge *eOrg );
+TESShalfEdge *tessMeshConnect( TESSmesh *mesh, TESShalfEdge *eOrg, TESShalfEdge *eDst );
+
+TESSmesh *tessMeshNewMesh( TESSalloc* alloc );
+TESSmesh *tessMeshUnion( TESSalloc* alloc, TESSmesh *mesh1, TESSmesh *mesh2 );
+int tessMeshMergeConvexFaces( TESSmesh *mesh, int maxVertsPerFace );
+void tessMeshDeleteMesh( TESSalloc* alloc, TESSmesh *mesh );
+void tessMeshZapFace( TESSmesh *mesh, TESSface *fZap );
+
+#ifdef NDEBUG
+#define tessMeshCheckMesh( mesh )
+#else
+void tessMeshCheckMesh( TESSmesh *mesh );
+#endif
+
+#endif
diff --git a/Source/priorityq.c b/Source/priorityq.c
new file mode 100755
index 0000000..62a6654
--- /dev/null
+++ b/Source/priorityq.c
@@ -0,0 +1,514 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+//#include "tesos.h"
+#include <stddef.h>
+#include <assert.h>
+#include "../Include/tesselator.h"
+#include "priorityq.h"
+
+
+#define INIT_SIZE 32
+
+#define TRUE 1
+#define FALSE 0
+
+#ifdef FOR_TRITE_TEST_PROGRAM
+#define LEQ(x,y) (*pq->leq)(x,y)
+#else
+/* Violates modularity, but a little faster */
+#include "geom.h"
+#define LEQ(x,y) VertLeq((TESSvertex *)x, (TESSvertex *)y)
+#endif
+
+
+/* Include all the code for the regular heap-based queue here. */
+
+/* The basic operations are insertion of a new key (pqInsert),
+* and examination/extraction of a key whose value is minimum
+* (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete);
+* for this purpose pqInsert returns a "handle" which is supplied
+* as the argument.
+*
+* An initial heap may be created efficiently by calling pqInsert
+* repeatedly, then calling pqInit. In any case pqInit must be called
+* before any operations other than pqInsert are used.
+*
+* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
+* This may also be tested with pqIsEmpty.
+*/
+
+
+/* Since we support deletion the data structure is a little more
+* complicated than an ordinary heap. "nodes" is the heap itself;
+* active nodes are stored in the range 1..pq->size. When the
+* heap exceeds its allocated size (pq->max), its size doubles.
+* The children of node i are nodes 2i and 2i+1.
+*
+* Each node stores an index into an array "handles". Each handle
+* stores a key, plus a pointer back to the node which currently
+* represents that key (ie. nodes[handles[i].node].handle == i).
+*/
+
+
+#define pqHeapMinimum(pq) ((pq)->handles[(pq)->nodes[1].handle].key)
+#define pqHeapIsEmpty(pq) ((pq)->size == 0)
+
+
+
+/* really pqHeapNewPriorityQHeap */
+PriorityQHeap *pqHeapNewPriorityQ( TESSalloc* alloc, int size, int (*leq)(PQkey key1, PQkey key2) )
+{
+ PriorityQHeap *pq = (PriorityQHeap *)alloc->memalloc( alloc->userData, sizeof( PriorityQHeap ));
+ if (pq == NULL) return NULL;
+
+ pq->size = 0;
+ pq->max = size;
+ pq->nodes = (PQnode *)alloc->memalloc( alloc->userData, (size + 1) * sizeof(pq->nodes[0]) );
+ if (pq->nodes == NULL) {
+ alloc->memfree( alloc->userData, pq );
+ return NULL;
+ }
+
+ pq->handles = (PQhandleElem *)alloc->memalloc( alloc->userData, (size + 1) * sizeof(pq->handles[0]) );
+ if (pq->handles == NULL) {
+ alloc->memfree( alloc->userData, pq->nodes );
+ alloc->memfree( alloc->userData, pq );
+ return NULL;
+ }
+
+ pq->initialized = FALSE;
+ pq->freeList = 0;
+ pq->leq = leq;
+
+ pq->nodes[1].handle = 1; /* so that Minimum() returns NULL */
+ pq->handles[1].key = NULL;
+ return pq;
+}
+
+/* really pqHeapDeletePriorityQHeap */
+void pqHeapDeletePriorityQ( TESSalloc* alloc, PriorityQHeap *pq )
+{
+ alloc->memfree( alloc->userData, pq->handles );
+ alloc->memfree( alloc->userData, pq->nodes );
+ alloc->memfree( alloc->userData, pq );
+}
+
+
+static void FloatDown( PriorityQHeap *pq, int curr )
+{
+ PQnode *n = pq->nodes;
+ PQhandleElem *h = pq->handles;
+ PQhandle hCurr, hChild;
+ int child;
+
+ hCurr = n[curr].handle;
+ for( ;; ) {
+ child = curr << 1;
+ if( child < pq->size && LEQ( h[n[child+1].handle].key,
+ h[n[child].handle].key )) {
+ ++child;
+ }
+
+ assert(child <= pq->max);
+
+ hChild = n[child].handle;
+ if( child > pq->size || LEQ( h[hCurr].key, h[hChild].key )) {
+ n[curr].handle = hCurr;
+ h[hCurr].node = curr;
+ break;
+ }
+ n[curr].handle = hChild;
+ h[hChild].node = curr;
+ curr = child;
+ }
+}
+
+
+static void FloatUp( PriorityQHeap *pq, int curr )
+{
+ PQnode *n = pq->nodes;
+ PQhandleElem *h = pq->handles;
+ PQhandle hCurr, hParent;
+ int parent;
+
+ hCurr = n[curr].handle;
+ for( ;; ) {
+ parent = curr >> 1;
+ hParent = n[parent].handle;
+ if( parent == 0 || LEQ( h[hParent].key, h[hCurr].key )) {
+ n[curr].handle = hCurr;
+ h[hCurr].node = curr;
+ break;
+ }
+ n[curr].handle = hParent;
+ h[hParent].node = curr;
+ curr = parent;
+ }
+}
+
+/* really pqHeapInit */
+void pqHeapInit( PriorityQHeap *pq )
+{
+ int i;
+
+ /* This method of building a heap is O(n), rather than O(n lg n). */
+
+ for( i = pq->size; i >= 1; --i ) {
+ FloatDown( pq, i );
+ }
+ pq->initialized = TRUE;
+}
+
+/* really pqHeapInsert */
+/* returns INV_HANDLE iff out of memory */
+PQhandle pqHeapInsert( TESSalloc* alloc, PriorityQHeap *pq, PQkey keyNew )
+{
+ int curr;
+ PQhandle free;
+
+ curr = ++ pq->size;
+ if( (curr*2) > pq->max ) {
+ if (!alloc->memrealloc)
+ {
+ return INV_HANDLE;
+ }
+ else
+ {
+ PQnode *saveNodes= pq->nodes;
+ PQhandleElem *saveHandles= pq->handles;
+
+ // If the heap overflows, double its size.
+ pq->max <<= 1;
+ pq->nodes = (PQnode *)alloc->memrealloc( alloc->userData, pq->nodes,
+ (size_t)((pq->max + 1) * sizeof( pq->nodes[0] )));
+ if (pq->nodes == NULL) {
+ pq->nodes = saveNodes; // restore ptr to free upon return
+ return INV_HANDLE;
+ }
+ pq->handles = (PQhandleElem *)alloc->memrealloc( alloc->userData, pq->handles,
+ (size_t) ((pq->max + 1) * sizeof( pq->handles[0] )));
+ if (pq->handles == NULL) {
+ pq->handles = saveHandles; // restore ptr to free upon return
+ return INV_HANDLE;
+ }
+ }
+ }
+
+ if( pq->freeList == 0 ) {
+ free = curr;
+ } else {
+ free = pq->freeList;
+ pq->freeList = pq->handles[free].node;
+ }
+
+ pq->nodes[curr].handle = free;
+ pq->handles[free].node = curr;
+ pq->handles[free].key = keyNew;
+
+ if( pq->initialized ) {
+ FloatUp( pq, curr );
+ }
+ assert(free != INV_HANDLE);
+ return free;
+}
+
+/* really pqHeapExtractMin */
+PQkey pqHeapExtractMin( PriorityQHeap *pq )
+{
+ PQnode *n = pq->nodes;
+ PQhandleElem *h = pq->handles;
+ PQhandle hMin = n[1].handle;
+ PQkey min = h[hMin].key;
+
+ if( pq->size > 0 ) {
+ n[1].handle = n[pq->size].handle;
+ h[n[1].handle].node = 1;
+
+ h[hMin].key = NULL;
+ h[hMin].node = pq->freeList;
+ pq->freeList = hMin;
+
+ if( -- pq->size > 0 ) {
+ FloatDown( pq, 1 );
+ }
+ }
+ return min;
+}
+
+/* really pqHeapDelete */
+void pqHeapDelete( PriorityQHeap *pq, PQhandle hCurr )
+{
+ PQnode *n = pq->nodes;
+ PQhandleElem *h = pq->handles;
+ int curr;
+
+ assert( hCurr >= 1 && hCurr <= pq->max && h[hCurr].key != NULL );
+
+ curr = h[hCurr].node;
+ n[curr].handle = n[pq->size].handle;
+ h[n[curr].handle].node = curr;
+
+ if( curr <= -- pq->size ) {
+ if( curr <= 1 || LEQ( h[n[curr>>1].handle].key, h[n[curr].handle].key )) {
+ FloatDown( pq, curr );
+ } else {
+ FloatUp( pq, curr );
+ }
+ }
+ h[hCurr].key = NULL;
+ h[hCurr].node = pq->freeList;
+ pq->freeList = hCurr;
+}
+
+
+
+/* Now redefine all the function names to map to their "Sort" versions. */
+
+/* really tessPqSortNewPriorityQ */
+PriorityQ *pqNewPriorityQ( TESSalloc* alloc, int size, int (*leq)(PQkey key1, PQkey key2) )
+{
+ PriorityQ *pq = (PriorityQ *)alloc->memalloc( alloc->userData, sizeof( PriorityQ ));
+ if (pq == NULL) return NULL;
+
+ pq->heap = pqHeapNewPriorityQ( alloc, size, leq );
+ if (pq->heap == NULL) {
+ alloc->memfree( alloc->userData, pq );
+ return NULL;
+ }
+
+// pq->keys = (PQkey *)memAlloc( INIT_SIZE * sizeof(pq->keys[0]) );
+ pq->keys = (PQkey *)alloc->memalloc( alloc->userData, size * sizeof(pq->keys[0]) );
+ if (pq->keys == NULL) {
+ pqHeapDeletePriorityQ( alloc, pq->heap );
+ alloc->memfree( alloc->userData, pq );
+ return NULL;
+ }
+
+ pq->size = 0;
+ pq->max = size; //INIT_SIZE;
+ pq->initialized = FALSE;
+ pq->leq = leq;
+
+ return pq;
+}
+
+/* really tessPqSortDeletePriorityQ */
+void pqDeletePriorityQ( TESSalloc* alloc, PriorityQ *pq )
+{
+ assert(pq != NULL);
+ if (pq->heap != NULL) pqHeapDeletePriorityQ( alloc, pq->heap );
+ if (pq->order != NULL) alloc->memfree( alloc->userData, pq->order );
+ if (pq->keys != NULL) alloc->memfree( alloc->userData, pq->keys );
+ alloc->memfree( alloc->userData, pq );
+}
+
+
+#define LT(x,y) (! LEQ(y,x))
+#define GT(x,y) (! LEQ(x,y))
+#define Swap(a,b) if(1){PQkey *tmp = *a; *a = *b; *b = tmp;}else
+
+/* really tessPqSortInit */
+int pqInit( TESSalloc* alloc, PriorityQ *pq )
+{
+ PQkey **p, **r, **i, **j, *piv;
+ struct { PQkey **p, **r; } Stack[50], *top = Stack;
+ unsigned int seed = 2016473283;
+
+ /* Create an array of indirect pointers to the keys, so that we
+ * the handles we have returned are still valid.
+ */
+ /*
+ pq->order = (PQkey **)memAlloc( (size_t)
+ (pq->size * sizeof(pq->order[0])) );
+ */
+ pq->order = (PQkey **)alloc->memalloc( alloc->userData,
+ (size_t)((pq->size+1) * sizeof(pq->order[0])) );
+ /* the previous line is a patch to compensate for the fact that IBM */
+ /* machines return a null on a malloc of zero bytes (unlike SGI), */
+ /* so we have to put in this defense to guard against a memory */
+ /* fault four lines down. from fossum@austin.ibm.com. */
+ if (pq->order == NULL) return 0;
+
+ p = pq->order;
+ r = p + pq->size - 1;
+ for( piv = pq->keys, i = p; i <= r; ++piv, ++i ) {
+ *i = piv;
+ }
+
+ /* Sort the indirect pointers in descending order,
+ * using randomized Quicksort
+ */
+ top->p = p; top->r = r; ++top;
+ while( --top >= Stack ) {
+ p = top->p;
+ r = top->r;
+ while( r > p + 10 ) {
+ seed = seed * 1539415821 + 1;
+ i = p + seed % (r - p + 1);
+ piv = *i;
+ *i = *p;
+ *p = piv;
+ i = p - 1;
+ j = r + 1;
+ do {
+ do { ++i; } while( GT( **i, *piv ));
+ do { --j; } while( LT( **j, *piv ));
+ Swap( i, j );
+ } while( i < j );
+ Swap( i, j ); /* Undo last swap */
+ if( i - p < r - j ) {
+ top->p = j+1; top->r = r; ++top;
+ r = i-1;
+ } else {
+ top->p = p; top->r = i-1; ++top;
+ p = j+1;
+ }
+ }
+ /* Insertion sort small lists */
+ for( i = p+1; i <= r; ++i ) {
+ piv = *i;
+ for( j = i; j > p && LT( **(j-1), *piv ); --j ) {
+ *j = *(j-1);
+ }
+ *j = piv;
+ }
+ }
+ pq->max = pq->size;
+ pq->initialized = TRUE;
+ pqHeapInit( pq->heap ); /* always succeeds */
+
+#ifndef NDEBUG
+ p = pq->order;
+ r = p + pq->size - 1;
+ for( i = p; i < r; ++i ) {
+ assert( LEQ( **(i+1), **i ));
+ }
+#endif
+
+ return 1;
+}
+
+/* really tessPqSortInsert */
+/* returns INV_HANDLE iff out of memory */
+PQhandle pqInsert( TESSalloc* alloc, PriorityQ *pq, PQkey keyNew )
+{
+ int curr;
+
+ if( pq->initialized ) {
+ return pqHeapInsert( alloc, pq->heap, keyNew );
+ }
+ curr = pq->size;
+ if( ++ pq->size >= pq->max ) {
+ if (!alloc->memrealloc)
+ {
+ return INV_HANDLE;
+ }
+ else
+ {
+ PQkey *saveKey= pq->keys;
+ // If the heap overflows, double its size.
+ pq->max <<= 1;
+ pq->keys = (PQkey *)alloc->memrealloc( alloc->userData, pq->keys,
+ (size_t)(pq->max * sizeof( pq->keys[0] )));
+ if (pq->keys == NULL) {
+ pq->keys = saveKey; // restore ptr to free upon return
+ return INV_HANDLE;
+ }
+ }
+ }
+ assert(curr != INV_HANDLE);
+ pq->keys[curr] = keyNew;
+
+ /* Negative handles index the sorted array. */
+ return -(curr+1);
+}
+
+/* really tessPqSortExtractMin */
+PQkey pqExtractMin( PriorityQ *pq )
+{
+ PQkey sortMin, heapMin;
+
+ if( pq->size == 0 ) {
+ return pqHeapExtractMin( pq->heap );
+ }
+ sortMin = *(pq->order[pq->size-1]);
+ if( ! pqHeapIsEmpty( pq->heap )) {
+ heapMin = pqHeapMinimum( pq->heap );
+ if( LEQ( heapMin, sortMin )) {
+ return pqHeapExtractMin( pq->heap );
+ }
+ }
+ do {
+ -- pq->size;
+ } while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL );
+ return sortMin;
+}
+
+/* really tessPqSortMinimum */
+PQkey pqMinimum( PriorityQ *pq )
+{
+ PQkey sortMin, heapMin;
+
+ if( pq->size == 0 ) {
+ return pqHeapMinimum( pq->heap );
+ }
+ sortMin = *(pq->order[pq->size-1]);
+ if( ! pqHeapIsEmpty( pq->heap )) {
+ heapMin = pqHeapMinimum( pq->heap );
+ if( LEQ( heapMin, sortMin )) {
+ return heapMin;
+ }
+ }
+ return sortMin;
+}
+
+/* really tessPqSortIsEmpty */
+int pqIsEmpty( PriorityQ *pq )
+{
+ return (pq->size == 0) && pqHeapIsEmpty( pq->heap );
+}
+
+/* really tessPqSortDelete */
+void pqDelete( PriorityQ *pq, PQhandle curr )
+{
+ if( curr >= 0 ) {
+ pqHeapDelete( pq->heap, curr );
+ return;
+ }
+ curr = -(curr+1);
+ assert( curr < pq->max && pq->keys[curr] != NULL );
+
+ pq->keys[curr] = NULL;
+ while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ) {
+ -- pq->size;
+ }
+}
diff --git a/Source/priorityq.h b/Source/priorityq.h
new file mode 100755
index 0000000..6ef1c05
--- /dev/null
+++ b/Source/priorityq.h
@@ -0,0 +1,104 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#ifndef PRIORITYQ_H
+#define PRIORITYQ_H
+
+/* The basic operations are insertion of a new key (pqInsert),
+* and examination/extraction of a key whose value is minimum
+* (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete);
+* for this purpose pqInsert returns a "handle" which is supplied
+* as the argument.
+*
+* An initial heap may be created efficiently by calling pqInsert
+* repeatedly, then calling pqInit. In any case pqInit must be called
+* before any operations other than pqInsert are used.
+*
+* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
+* This may also be tested with pqIsEmpty.
+*/
+
+/* Since we support deletion the data structure is a little more
+* complicated than an ordinary heap. "nodes" is the heap itself;
+* active nodes are stored in the range 1..pq->size. When the
+* heap exceeds its allocated size (pq->max), its size doubles.
+* The children of node i are nodes 2i and 2i+1.
+*
+* Each node stores an index into an array "handles". Each handle
+* stores a key, plus a pointer back to the node which currently
+* represents that key (ie. nodes[handles[i].node].handle == i).
+*/
+
+typedef void *PQkey;
+typedef int PQhandle;
+typedef struct PriorityQHeap PriorityQHeap;
+
+#define INV_HANDLE 0x0fffffff
+
+typedef struct { PQhandle handle; } PQnode;
+typedef struct { PQkey key; PQhandle node; } PQhandleElem;
+
+struct PriorityQHeap {
+
+ PQnode *nodes;
+ PQhandleElem *handles;
+ int size, max;
+ PQhandle freeList;
+ int initialized;
+
+ int (*leq)(PQkey key1, PQkey key2);
+};
+
+typedef struct PriorityQ PriorityQ;
+
+struct PriorityQ {
+ PriorityQHeap *heap;
+
+ PQkey *keys;
+ PQkey **order;
+ PQhandle size, max;
+ int initialized;
+
+ int (*leq)(PQkey key1, PQkey key2);
+};
+
+PriorityQ *pqNewPriorityQ( TESSalloc* alloc, int size, int (*leq)(PQkey key1, PQkey key2) );
+void pqDeletePriorityQ( TESSalloc* alloc, PriorityQ *pq );
+
+int pqInit( TESSalloc* alloc, PriorityQ *pq );
+PQhandle pqInsert( TESSalloc* alloc, PriorityQ *pq, PQkey key );
+PQkey pqExtractMin( PriorityQ *pq );
+void pqDelete( PriorityQ *pq, PQhandle handle );
+
+PQkey pqMinimum( PriorityQ *pq );
+int pqIsEmpty( PriorityQ *pq );
+
+#endif
diff --git a/Source/sweep.c b/Source/sweep.c
new file mode 100755
index 0000000..816ab06
--- /dev/null
+++ b/Source/sweep.c
@@ -0,0 +1,1322 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#include <assert.h>
+#include <stddef.h>
+#include <setjmp.h> /* longjmp */
+
+#include "mesh.h"
+#include "geom.h"
+#include "tess.h"
+#include "dict.h"
+#include "priorityq.h"
+#include "bucketalloc.h"
+#include "sweep.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#ifdef FOR_TRITE_TEST_PROGRAM
+extern void DebugEvent( TESStesselator *tess );
+#else
+#define DebugEvent( tess )
+#endif
+
+/*
+* Invariants for the Edge Dictionary.
+* - each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2)
+* at any valid location of the sweep event
+* - if EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2
+* share a common endpoint
+* - for each e, e->Dst has been processed, but not e->Org
+* - each edge e satisfies VertLeq(e->Dst,event) && VertLeq(event,e->Org)
+* where "event" is the current sweep line event.
+* - no edge e has zero length
+*
+* Invariants for the Mesh (the processed portion).
+* - the portion of the mesh left of the sweep line is a planar graph,
+* ie. there is *some* way to embed it in the plane
+* - no processed edge has zero length
+* - no two processed vertices have identical coordinates
+* - each "inside" region is monotone, ie. can be broken into two chains
+* of monotonically increasing vertices according to VertLeq(v1,v2)
+* - a non-invariant: these chains may intersect (very slightly)
+*
+* Invariants for the Sweep.
+* - if none of the edges incident to the event vertex have an activeRegion
+* (ie. none of these edges are in the edge dictionary), then the vertex
+* has only right-going edges.
+* - if an edge is marked "fixUpperEdge" (it is a temporary edge introduced
+* by ConnectRightVertex), then it is the only right-going edge from
+* its associated vertex. (This says that these edges exist only
+* when it is necessary.)
+*/
+
+#define MAX(x,y) ((x) >= (y) ? (x) : (y))
+#define MIN(x,y) ((x) <= (y) ? (x) : (y))
+
+/* When we merge two edges into one, we need to compute the combined
+* winding of the new edge.
+*/
+#define AddWinding(eDst,eSrc) (eDst->winding += eSrc->winding, \
+ eDst->Sym->winding += eSrc->Sym->winding)
+
+static void SweepEvent( TESStesselator *tess, TESSvertex *vEvent );
+static void WalkDirtyRegions( TESStesselator *tess, ActiveRegion *regUp );
+static int CheckForRightSplice( TESStesselator *tess, ActiveRegion *regUp );
+
+static int EdgeLeq( TESStesselator *tess, ActiveRegion *reg1, ActiveRegion *reg2 )
+/*
+* Both edges must be directed from right to left (this is the canonical
+* direction for the upper edge of each region).
+*
+* The strategy is to evaluate a "t" value for each edge at the
+* current sweep line position, given by tess->event. The calculations
+* are designed to be very stable, but of course they are not perfect.
+*
+* Special case: if both edge destinations are at the sweep event,
+* we sort the edges by slope (they would otherwise compare equally).
+*/
+{
+ TESSvertex *event = tess->event;
+ TESShalfEdge *e1, *e2;
+ TESSreal t1, t2;
+
+ e1 = reg1->eUp;
+ e2 = reg2->eUp;
+
+ if( e1->Dst == event ) {
+ if( e2->Dst == event ) {
+ /* Two edges right of the sweep line which meet at the sweep event.
+ * Sort them by slope.
+ */
+ if( VertLeq( e1->Org, e2->Org )) {
+ return EdgeSign( e2->Dst, e1->Org, e2->Org ) <= 0;
+ }
+ return EdgeSign( e1->Dst, e2->Org, e1->Org ) >= 0;
+ }
+ return EdgeSign( e2->Dst, event, e2->Org ) <= 0;
+ }
+ if( e2->Dst == event ) {
+ return EdgeSign( e1->Dst, event, e1->Org ) >= 0;
+ }
+
+ /* General case - compute signed distance *from* e1, e2 to event */
+ t1 = EdgeEval( e1->Dst, event, e1->Org );
+ t2 = EdgeEval( e2->Dst, event, e2->Org );
+ return (t1 >= t2);
+}
+
+
+static void DeleteRegion( TESStesselator *tess, ActiveRegion *reg )
+{
+ if( reg->fixUpperEdge ) {
+ /* It was created with zero winding number, so it better be
+ * deleted with zero winding number (ie. it better not get merged
+ * with a real edge).
+ */
+ assert( reg->eUp->winding == 0 );
+ }
+ reg->eUp->activeRegion = NULL;
+ dictDelete( tess->dict, reg->nodeUp );
+ bucketFree( tess->regionPool, reg );
+}
+
+
+static int FixUpperEdge( TESStesselator *tess, ActiveRegion *reg, TESShalfEdge *newEdge )
+/*
+* Replace an upper edge which needs fixing (see ConnectRightVertex).
+*/
+{
+ assert( reg->fixUpperEdge );
+ if ( !tessMeshDelete( tess->mesh, reg->eUp ) ) return 0;
+ reg->fixUpperEdge = FALSE;
+ reg->eUp = newEdge;
+ newEdge->activeRegion = reg;
+
+ return 1;
+}
+
+static ActiveRegion *TopLeftRegion( TESStesselator *tess, ActiveRegion *reg )
+{
+ TESSvertex *org = reg->eUp->Org;
+ TESShalfEdge *e;
+
+ /* Find the region above the uppermost edge with the same origin */
+ do {
+ reg = RegionAbove( reg );
+ } while( reg->eUp->Org == org );
+
+ /* If the edge above was a temporary edge introduced by ConnectRightVertex,
+ * now is the time to fix it.
+ */
+ if( reg->fixUpperEdge ) {
+ e = tessMeshConnect( tess->mesh, RegionBelow(reg)->eUp->Sym, reg->eUp->Lnext );
+ if (e == NULL) return NULL;
+ if ( !FixUpperEdge( tess, reg, e ) ) return NULL;
+ reg = RegionAbove( reg );
+ }
+ return reg;
+}
+
+static ActiveRegion *TopRightRegion( ActiveRegion *reg )
+{
+ TESSvertex *dst = reg->eUp->Dst;
+
+ /* Find the region above the uppermost edge with the same destination */
+ do {
+ reg = RegionAbove( reg );
+ } while( reg->eUp->Dst == dst );
+ return reg;
+}
+
+static ActiveRegion *AddRegionBelow( TESStesselator *tess,
+ ActiveRegion *regAbove,
+ TESShalfEdge *eNewUp )
+/*
+* Add a new active region to the sweep line, *somewhere* below "regAbove"
+* (according to where the new edge belongs in the sweep-line dictionary).
+* The upper edge of the new region will be "eNewUp".
+* Winding number and "inside" flag are not updated.
+*/
+{
+ ActiveRegion *regNew = (ActiveRegion *)bucketAlloc( tess->regionPool );
+ if (regNew == NULL) longjmp(tess->env,1);
+
+ regNew->eUp = eNewUp;
+ regNew->nodeUp = dictInsertBefore( tess->dict, regAbove->nodeUp, regNew );
+ if (regNew->nodeUp == NULL) longjmp(tess->env,1);
+ regNew->fixUpperEdge = FALSE;
+ regNew->sentinel = FALSE;
+ regNew->dirty = FALSE;
+
+ eNewUp->activeRegion = regNew;
+ return regNew;
+}
+
+static int IsWindingInside( TESStesselator *tess, int n )
+{
+ switch( tess->windingRule ) {
+ case TESS_WINDING_ODD:
+ return (n & 1);
+ case TESS_WINDING_NONZERO:
+ return (n != 0);
+ case TESS_WINDING_POSITIVE:
+ return (n > 0);
+ case TESS_WINDING_NEGATIVE:
+ return (n < 0);
+ case TESS_WINDING_ABS_GEQ_TWO:
+ return (n >= 2) || (n <= -2);
+ }
+ /*LINTED*/
+ assert( FALSE );
+ /*NOTREACHED*/
+
+ return( FALSE );
+}
+
+
+static void ComputeWinding( TESStesselator *tess, ActiveRegion *reg )
+{
+ reg->windingNumber = RegionAbove(reg)->windingNumber + reg->eUp->winding;
+ reg->inside = IsWindingInside( tess, reg->windingNumber );
+}
+
+
+static void FinishRegion( TESStesselator *tess, ActiveRegion *reg )
+/*
+* Delete a region from the sweep line. This happens when the upper
+* and lower chains of a region meet (at a vertex on the sweep line).
+* The "inside" flag is copied to the appropriate mesh face (we could
+* not do this before -- since the structure of the mesh is always
+* changing, this face may not have even existed until now).
+*/
+{
+ TESShalfEdge *e = reg->eUp;
+ TESSface *f = e->Lface;
+
+ f->inside = reg->inside;
+ f->anEdge = e; /* optimization for tessMeshTessellateMonoRegion() */
+ DeleteRegion( tess, reg );
+}
+
+
+static TESShalfEdge *FinishLeftRegions( TESStesselator *tess,
+ ActiveRegion *regFirst, ActiveRegion *regLast )
+/*
+* We are given a vertex with one or more left-going edges. All affected
+* edges should be in the edge dictionary. Starting at regFirst->eUp,
+* we walk down deleting all regions where both edges have the same
+* origin vOrg. At the same time we copy the "inside" flag from the
+* active region to the face, since at this point each face will belong
+* to at most one region (this was not necessarily true until this point
+* in the sweep). The walk stops at the region above regLast; if regLast
+* is NULL we walk as far as possible. At the same time we relink the
+* mesh if necessary, so that the ordering of edges around vOrg is the
+* same as in the dictionary.
+*/
+{
+ ActiveRegion *reg, *regPrev;
+ TESShalfEdge *e, *ePrev;
+
+ regPrev = regFirst;
+ ePrev = regFirst->eUp;
+ while( regPrev != regLast ) {
+ regPrev->fixUpperEdge = FALSE; /* placement was OK */
+ reg = RegionBelow( regPrev );
+ e = reg->eUp;
+ if( e->Org != ePrev->Org ) {
+ if( ! reg->fixUpperEdge ) {
+ /* Remove the last left-going edge. Even though there are no further
+ * edges in the dictionary with this origin, there may be further
+ * such edges in the mesh (if we are adding left edges to a vertex
+ * that has already been processed). Thus it is important to call
+ * FinishRegion rather than just DeleteRegion.
+ */
+ FinishRegion( tess, regPrev );
+ break;
+ }
+ /* If the edge below was a temporary edge introduced by
+ * ConnectRightVertex, now is the time to fix it.
+ */
+ e = tessMeshConnect( tess->mesh, ePrev->Lprev, e->Sym );
+ if (e == NULL) longjmp(tess->env,1);
+ if ( !FixUpperEdge( tess, reg, e ) ) longjmp(tess->env,1);
+ }
+
+ /* Relink edges so that ePrev->Onext == e */
+ if( ePrev->Onext != e ) {
+ if ( !tessMeshSplice( tess->mesh, e->Oprev, e ) ) longjmp(tess->env,1);
+ if ( !tessMeshSplice( tess->mesh, ePrev, e ) ) longjmp(tess->env,1);
+ }
+ FinishRegion( tess, regPrev ); /* may change reg->eUp */
+ ePrev = reg->eUp;
+ regPrev = reg;
+ }
+ return ePrev;
+}
+
+
+static void AddRightEdges( TESStesselator *tess, ActiveRegion *regUp,
+ TESShalfEdge *eFirst, TESShalfEdge *eLast, TESShalfEdge *eTopLeft,
+ int cleanUp )
+/*
+* Purpose: insert right-going edges into the edge dictionary, and update
+* winding numbers and mesh connectivity appropriately. All right-going
+* edges share a common origin vOrg. Edges are inserted CCW starting at
+* eFirst; the last edge inserted is eLast->Oprev. If vOrg has any
+* left-going edges already processed, then eTopLeft must be the edge
+* such that an imaginary upward vertical segment from vOrg would be
+* contained between eTopLeft->Oprev and eTopLeft; otherwise eTopLeft
+* should be NULL.
+*/
+{
+ ActiveRegion *reg, *regPrev;
+ TESShalfEdge *e, *ePrev;
+ int firstTime = TRUE;
+
+ /* Insert the new right-going edges in the dictionary */
+ e = eFirst;
+ do {
+ assert( VertLeq( e->Org, e->Dst ));
+ AddRegionBelow( tess, regUp, e->Sym );
+ e = e->Onext;
+ } while ( e != eLast );
+
+ /* Walk *all* right-going edges from e->Org, in the dictionary order,
+ * updating the winding numbers of each region, and re-linking the mesh
+ * edges to match the dictionary ordering (if necessary).
+ */
+ if( eTopLeft == NULL ) {
+ eTopLeft = RegionBelow( regUp )->eUp->Rprev;
+ }
+ regPrev = regUp;
+ ePrev = eTopLeft;
+ for( ;; ) {
+ reg = RegionBelow( regPrev );
+ e = reg->eUp->Sym;
+ if( e->Org != ePrev->Org ) break;
+
+ if( e->Onext != ePrev ) {
+ /* Unlink e from its current position, and relink below ePrev */
+ if ( !tessMeshSplice( tess->mesh, e->Oprev, e ) ) longjmp(tess->env,1);
+ if ( !tessMeshSplice( tess->mesh, ePrev->Oprev, e ) ) longjmp(tess->env,1);
+ }
+ /* Compute the winding number and "inside" flag for the new regions */
+ reg->windingNumber = regPrev->windingNumber - e->winding;
+ reg->inside = IsWindingInside( tess, reg->windingNumber );
+
+ /* Check for two outgoing edges with same slope -- process these
+ * before any intersection tests (see example in tessComputeInterior).
+ */
+ regPrev->dirty = TRUE;
+ if( ! firstTime && CheckForRightSplice( tess, regPrev )) {
+ AddWinding( e, ePrev );
+ DeleteRegion( tess, regPrev );
+ if ( !tessMeshDelete( tess->mesh, ePrev ) ) longjmp(tess->env,1);
+ }
+ firstTime = FALSE;
+ regPrev = reg;
+ ePrev = e;
+ }
+ regPrev->dirty = TRUE;
+ assert( regPrev->windingNumber - e->winding == reg->windingNumber );
+
+ if( cleanUp ) {
+ /* Check for intersections between newly adjacent edges. */
+ WalkDirtyRegions( tess, regPrev );
+ }
+}
+
+
+static void SpliceMergeVertices( TESStesselator *tess, TESShalfEdge *e1,
+ TESShalfEdge *e2 )
+/*
+* Two vertices with idential coordinates are combined into one.
+* e1->Org is kept, while e2->Org is discarded.
+*/
+{
+ if ( !tessMeshSplice( tess->mesh, e1, e2 ) ) longjmp(tess->env,1);
+}
+
+static void VertexWeights( TESSvertex *isect, TESSvertex *org, TESSvertex *dst,
+ TESSreal *weights )
+/*
+* Find some weights which describe how the intersection vertex is
+* a linear combination of "org" and "dest". Each of the two edges
+* which generated "isect" is allocated 50% of the weight; each edge
+* splits the weight between its org and dst according to the
+* relative distance to "isect".
+*/
+{
+ TESSreal t1 = VertL1dist( org, isect );
+ TESSreal t2 = VertL1dist( dst, isect );
+
+ weights[0] = (TESSreal)0.5 * t2 / (t1 + t2);
+ weights[1] = (TESSreal)0.5 * t1 / (t1 + t2);
+ isect->coords[0] += weights[0]*org->coords[0] + weights[1]*dst->coords[0];
+ isect->coords[1] += weights[0]*org->coords[1] + weights[1]*dst->coords[1];
+ isect->coords[2] += weights[0]*org->coords[2] + weights[1]*dst->coords[2];
+}
+
+
+static void GetIntersectData( TESStesselator *tess, TESSvertex *isect,
+ TESSvertex *orgUp, TESSvertex *dstUp,
+ TESSvertex *orgLo, TESSvertex *dstLo )
+ /*
+ * We've computed a new intersection point, now we need a "data" pointer
+ * from the user so that we can refer to this new vertex in the
+ * rendering callbacks.
+ */
+{
+ TESSreal weights[4];
+
+ isect->coords[0] = isect->coords[1] = isect->coords[2] = 0;
+ isect->idx = TESS_UNDEF;
+ VertexWeights( isect, orgUp, dstUp, &weights[0] );
+ VertexWeights( isect, orgLo, dstLo, &weights[2] );
+}
+
+static int CheckForRightSplice( TESStesselator *tess, ActiveRegion *regUp )
+/*
+* Check the upper and lower edge of "regUp", to make sure that the
+* eUp->Org is above eLo, or eLo->Org is below eUp (depending on which
+* origin is leftmost).
+*
+* The main purpose is to splice right-going edges with the same
+* dest vertex and nearly identical slopes (ie. we can't distinguish
+* the slopes numerically). However the splicing can also help us
+* to recover from numerical errors. For example, suppose at one
+* point we checked eUp and eLo, and decided that eUp->Org is barely
+* above eLo. Then later, we split eLo into two edges (eg. from
+* a splice operation like this one). This can change the result of
+* our test so that now eUp->Org is incident to eLo, or barely below it.
+* We must correct this condition to maintain the dictionary invariants.
+*
+* One possibility is to check these edges for intersection again
+* (ie. CheckForIntersect). This is what we do if possible. However
+* CheckForIntersect requires that tess->event lies between eUp and eLo,
+* so that it has something to fall back on when the intersection
+* calculation gives us an unusable answer. So, for those cases where
+* we can't check for intersection, this routine fixes the problem
+* by just splicing the offending vertex into the other edge.
+* This is a guaranteed solution, no matter how degenerate things get.
+* Basically this is a combinatorial solution to a numerical problem.
+*/
+{
+ ActiveRegion *regLo = RegionBelow(regUp);
+ TESShalfEdge *eUp = regUp->eUp;
+ TESShalfEdge *eLo = regLo->eUp;
+
+ if( VertLeq( eUp->Org, eLo->Org )) {
+ if( EdgeSign( eLo->Dst, eUp->Org, eLo->Org ) > 0 ) return FALSE;
+
+ /* eUp->Org appears to be below eLo */
+ if( ! VertEq( eUp->Org, eLo->Org )) {
+ /* Splice eUp->Org into eLo */
+ if ( tessMeshSplitEdge( tess->mesh, eLo->Sym ) == NULL) longjmp(tess->env,1);
+ if ( !tessMeshSplice( tess->mesh, eUp, eLo->Oprev ) ) longjmp(tess->env,1);
+ regUp->dirty = regLo->dirty = TRUE;
+
+ } else if( eUp->Org != eLo->Org ) {
+ /* merge the two vertices, discarding eUp->Org */
+ pqDelete( tess->pq, eUp->Org->pqHandle );
+ SpliceMergeVertices( tess, eLo->Oprev, eUp );
+ }
+ } else {
+ if( EdgeSign( eUp->Dst, eLo->Org, eUp->Org ) < 0 ) return FALSE;
+
+ /* eLo->Org appears to be above eUp, so splice eLo->Org into eUp */
+ RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
+ if (tessMeshSplitEdge( tess->mesh, eUp->Sym ) == NULL) longjmp(tess->env,1);
+ if ( !tessMeshSplice( tess->mesh, eLo->Oprev, eUp ) ) longjmp(tess->env,1);
+ }
+ return TRUE;
+}
+
+static int CheckForLeftSplice( TESStesselator *tess, ActiveRegion *regUp )
+/*
+* Check the upper and lower edge of "regUp", to make sure that the
+* eUp->Dst is above eLo, or eLo->Dst is below eUp (depending on which
+* destination is rightmost).
+*
+* Theoretically, this should always be true. However, splitting an edge
+* into two pieces can change the results of previous tests. For example,
+* suppose at one point we checked eUp and eLo, and decided that eUp->Dst
+* is barely above eLo. Then later, we split eLo into two edges (eg. from
+* a splice operation like this one). This can change the result of
+* the test so that now eUp->Dst is incident to eLo, or barely below it.
+* We must correct this condition to maintain the dictionary invariants
+* (otherwise new edges might get inserted in the wrong place in the
+* dictionary, and bad stuff will happen).
+*
+* We fix the problem by just splicing the offending vertex into the
+* other edge.
+*/
+{
+ ActiveRegion *regLo = RegionBelow(regUp);
+ TESShalfEdge *eUp = regUp->eUp;
+ TESShalfEdge *eLo = regLo->eUp;
+ TESShalfEdge *e;
+
+ assert( ! VertEq( eUp->Dst, eLo->Dst ));
+
+ if( VertLeq( eUp->Dst, eLo->Dst )) {
+ if( EdgeSign( eUp->Dst, eLo->Dst, eUp->Org ) < 0 ) return FALSE;
+
+ /* eLo->Dst is above eUp, so splice eLo->Dst into eUp */
+ RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
+ e = tessMeshSplitEdge( tess->mesh, eUp );
+ if (e == NULL) longjmp(tess->env,1);
+ if ( !tessMeshSplice( tess->mesh, eLo->Sym, e ) ) longjmp(tess->env,1);
+ e->Lface->inside = regUp->inside;
+ } else {
+ if( EdgeSign( eLo->Dst, eUp->Dst, eLo->Org ) > 0 ) return FALSE;
+
+ /* eUp->Dst is below eLo, so splice eUp->Dst into eLo */
+ regUp->dirty = regLo->dirty = TRUE;
+ e = tessMeshSplitEdge( tess->mesh, eLo );
+ if (e == NULL) longjmp(tess->env,1);
+ if ( !tessMeshSplice( tess->mesh, eUp->Lnext, eLo->Sym ) ) longjmp(tess->env,1);
+ e->Rface->inside = regUp->inside;
+ }
+ return TRUE;
+}
+
+
+static int CheckForIntersect( TESStesselator *tess, ActiveRegion *regUp )
+/*
+* Check the upper and lower edges of the given region to see if
+* they intersect. If so, create the intersection and add it
+* to the data structures.
+*
+* Returns TRUE if adding the new intersection resulted in a recursive
+* call to AddRightEdges(); in this case all "dirty" regions have been
+* checked for intersections, and possibly regUp has been deleted.
+*/
+{
+ ActiveRegion *regLo = RegionBelow(regUp);
+ TESShalfEdge *eUp = regUp->eUp;
+ TESShalfEdge *eLo = regLo->eUp;
+ TESSvertex *orgUp = eUp->Org;
+ TESSvertex *orgLo = eLo->Org;
+ TESSvertex *dstUp = eUp->Dst;
+ TESSvertex *dstLo = eLo->Dst;
+ TESSreal tMinUp, tMaxLo;
+ TESSvertex isect, *orgMin;
+ TESShalfEdge *e;
+
+ assert( ! VertEq( dstLo, dstUp ));
+ assert( EdgeSign( dstUp, tess->event, orgUp ) <= 0 );
+ assert( EdgeSign( dstLo, tess->event, orgLo ) >= 0 );
+ assert( orgUp != tess->event && orgLo != tess->event );
+ assert( ! regUp->fixUpperEdge && ! regLo->fixUpperEdge );
+
+ if( orgUp == orgLo ) return FALSE; /* right endpoints are the same */
+
+ tMinUp = MIN( orgUp->t, dstUp->t );
+ tMaxLo = MAX( orgLo->t, dstLo->t );
+ if( tMinUp > tMaxLo ) return FALSE; /* t ranges do not overlap */
+
+ if( VertLeq( orgUp, orgLo )) {
+ if( EdgeSign( dstLo, orgUp, orgLo ) > 0 ) return FALSE;
+ } else {
+ if( EdgeSign( dstUp, orgLo, orgUp ) < 0 ) return FALSE;
+ }
+
+ /* At this point the edges intersect, at least marginally */
+ DebugEvent( tess );
+
+ tesedgeIntersect( dstUp, orgUp, dstLo, orgLo, &isect );
+ /* The following properties are guaranteed: */
+ assert( MIN( orgUp->t, dstUp->t ) <= isect.t );
+ assert( isect.t <= MAX( orgLo->t, dstLo->t ));
+ assert( MIN( dstLo->s, dstUp->s ) <= isect.s );
+ assert( isect.s <= MAX( orgLo->s, orgUp->s ));
+
+ if( VertLeq( &isect, tess->event )) {
+ /* The intersection point lies slightly to the left of the sweep line,
+ * so move it until it''s slightly to the right of the sweep line.
+ * (If we had perfect numerical precision, this would never happen
+ * in the first place). The easiest and safest thing to do is
+ * replace the intersection by tess->event.
+ */
+ isect.s = tess->event->s;
+ isect.t = tess->event->t;
+ }
+ /* Similarly, if the computed intersection lies to the right of the
+ * rightmost origin (which should rarely happen), it can cause
+ * unbelievable inefficiency on sufficiently degenerate inputs.
+ * (If you have the test program, try running test54.d with the
+ * "X zoom" option turned on).
+ */
+ orgMin = VertLeq( orgUp, orgLo ) ? orgUp : orgLo;
+ if( VertLeq( orgMin, &isect )) {
+ isect.s = orgMin->s;
+ isect.t = orgMin->t;
+ }
+
+ if( VertEq( &isect, orgUp ) || VertEq( &isect, orgLo )) {
+ /* Easy case -- intersection at one of the right endpoints */
+ (void) CheckForRightSplice( tess, regUp );
+ return FALSE;
+ }
+
+ if( (! VertEq( dstUp, tess->event )
+ && EdgeSign( dstUp, tess->event, &isect ) >= 0)
+ || (! VertEq( dstLo, tess->event )
+ && EdgeSign( dstLo, tess->event, &isect ) <= 0 ))
+ {
+ /* Very unusual -- the new upper or lower edge would pass on the
+ * wrong side of the sweep event, or through it. This can happen
+ * due to very small numerical errors in the intersection calculation.
+ */
+ if( dstLo == tess->event ) {
+ /* Splice dstLo into eUp, and process the new region(s) */
+ if (tessMeshSplitEdge( tess->mesh, eUp->Sym ) == NULL) longjmp(tess->env,1);
+ if ( !tessMeshSplice( tess->mesh, eLo->Sym, eUp ) ) longjmp(tess->env,1);
+ regUp = TopLeftRegion( tess, regUp );
+ if (regUp == NULL) longjmp(tess->env,1);
+ eUp = RegionBelow(regUp)->eUp;
+ FinishLeftRegions( tess, RegionBelow(regUp), regLo );
+ AddRightEdges( tess, regUp, eUp->Oprev, eUp, eUp, TRUE );
+ return TRUE;
+ }
+ if( dstUp == tess->event ) {
+ /* Splice dstUp into eLo, and process the new region(s) */
+ if (tessMeshSplitEdge( tess->mesh, eLo->Sym ) == NULL) longjmp(tess->env,1);
+ if ( !tessMeshSplice( tess->mesh, eUp->Lnext, eLo->Oprev ) ) longjmp(tess->env,1);
+ regLo = regUp;
+ regUp = TopRightRegion( regUp );
+ e = RegionBelow(regUp)->eUp->Rprev;
+ regLo->eUp = eLo->Oprev;
+ eLo = FinishLeftRegions( tess, regLo, NULL );
+ AddRightEdges( tess, regUp, eLo->Onext, eUp->Rprev, e, TRUE );
+ return TRUE;
+ }
+ /* Special case: called from ConnectRightVertex. If either
+ * edge passes on the wrong side of tess->event, split it
+ * (and wait for ConnectRightVertex to splice it appropriately).
+ */
+ if( EdgeSign( dstUp, tess->event, &isect ) >= 0 ) {
+ RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
+ if (tessMeshSplitEdge( tess->mesh, eUp->Sym ) == NULL) longjmp(tess->env,1);
+ eUp->Org->s = tess->event->s;
+ eUp->Org->t = tess->event->t;
+ }
+ if( EdgeSign( dstLo, tess->event, &isect ) <= 0 ) {
+ regUp->dirty = regLo->dirty = TRUE;
+ if (tessMeshSplitEdge( tess->mesh, eLo->Sym ) == NULL) longjmp(tess->env,1);
+ eLo->Org->s = tess->event->s;
+ eLo->Org->t = tess->event->t;
+ }
+ /* leave the rest for ConnectRightVertex */
+ return FALSE;
+ }
+
+ /* General case -- split both edges, splice into new vertex.
+ * When we do the splice operation, the order of the arguments is
+ * arbitrary as far as correctness goes. However, when the operation
+ * creates a new face, the work done is proportional to the size of
+ * the new face. We expect the faces in the processed part of
+ * the mesh (ie. eUp->Lface) to be smaller than the faces in the
+ * unprocessed original contours (which will be eLo->Oprev->Lface).
+ */
+ if (tessMeshSplitEdge( tess->mesh, eUp->Sym ) == NULL) longjmp(tess->env,1);
+ if (tessMeshSplitEdge( tess->mesh, eLo->Sym ) == NULL) longjmp(tess->env,1);
+ if ( !tessMeshSplice( tess->mesh, eLo->Oprev, eUp ) ) longjmp(tess->env,1);
+ eUp->Org->s = isect.s;
+ eUp->Org->t = isect.t;
+ eUp->Org->pqHandle = pqInsert( &tess->alloc, tess->pq, eUp->Org );
+ if (eUp->Org->pqHandle == INV_HANDLE) {
+ pqDeletePriorityQ( &tess->alloc, tess->pq );
+ tess->pq = NULL;
+ longjmp(tess->env,1);
+ }
+ GetIntersectData( tess, eUp->Org, orgUp, dstUp, orgLo, dstLo );
+ RegionAbove(regUp)->dirty = regUp->dirty = regLo->dirty = TRUE;
+ return FALSE;
+}
+
+static void WalkDirtyRegions( TESStesselator *tess, ActiveRegion *regUp )
+/*
+* When the upper or lower edge of any region changes, the region is
+* marked "dirty". This routine walks through all the dirty regions
+* and makes sure that the dictionary invariants are satisfied
+* (see the comments at the beginning of this file). Of course
+* new dirty regions can be created as we make changes to restore
+* the invariants.
+*/
+{
+ ActiveRegion *regLo = RegionBelow(regUp);
+ TESShalfEdge *eUp, *eLo;
+
+ for( ;; ) {
+ /* Find the lowest dirty region (we walk from the bottom up). */
+ while( regLo->dirty ) {
+ regUp = regLo;
+ regLo = RegionBelow(regLo);
+ }
+ if( ! regUp->dirty ) {
+ regLo = regUp;
+ regUp = RegionAbove( regUp );
+ if( regUp == NULL || ! regUp->dirty ) {
+ /* We've walked all the dirty regions */
+ return;
+ }
+ }
+ regUp->dirty = FALSE;
+ eUp = regUp->eUp;
+ eLo = regLo->eUp;
+
+ if( eUp->Dst != eLo->Dst ) {
+ /* Check that the edge ordering is obeyed at the Dst vertices. */
+ if( CheckForLeftSplice( tess, regUp )) {
+
+ /* If the upper or lower edge was marked fixUpperEdge, then
+ * we no longer need it (since these edges are needed only for
+ * vertices which otherwise have no right-going edges).
+ */
+ if( regLo->fixUpperEdge ) {
+ DeleteRegion( tess, regLo );
+ if ( !tessMeshDelete( tess->mesh, eLo ) ) longjmp(tess->env,1);
+ regLo = RegionBelow( regUp );
+ eLo = regLo->eUp;
+ } else if( regUp->fixUpperEdge ) {
+ DeleteRegion( tess, regUp );
+ if ( !tessMeshDelete( tess->mesh, eUp ) ) longjmp(tess->env,1);
+ regUp = RegionAbove( regLo );
+ eUp = regUp->eUp;
+ }
+ }
+ }
+ if( eUp->Org != eLo->Org ) {
+ if( eUp->Dst != eLo->Dst
+ && ! regUp->fixUpperEdge && ! regLo->fixUpperEdge
+ && (eUp->Dst == tess->event || eLo->Dst == tess->event) )
+ {
+ /* When all else fails in CheckForIntersect(), it uses tess->event
+ * as the intersection location. To make this possible, it requires
+ * that tess->event lie between the upper and lower edges, and also
+ * that neither of these is marked fixUpperEdge (since in the worst
+ * case it might splice one of these edges into tess->event, and
+ * violate the invariant that fixable edges are the only right-going
+ * edge from their associated vertex).
+ */
+ if( CheckForIntersect( tess, regUp )) {
+ /* WalkDirtyRegions() was called recursively; we're done */
+ return;
+ }
+ } else {
+ /* Even though we can't use CheckForIntersect(), the Org vertices
+ * may violate the dictionary edge ordering. Check and correct this.
+ */
+ (void) CheckForRightSplice( tess, regUp );
+ }
+ }
+ if( eUp->Org == eLo->Org && eUp->Dst == eLo->Dst ) {
+ /* A degenerate loop consisting of only two edges -- delete it. */
+ AddWinding( eLo, eUp );
+ DeleteRegion( tess, regUp );
+ if ( !tessMeshDelete( tess->mesh, eUp ) ) longjmp(tess->env,1);
+ regUp = RegionAbove( regLo );
+ }
+ }
+}
+
+
+static void ConnectRightVertex( TESStesselator *tess, ActiveRegion *regUp,
+ TESShalfEdge *eBottomLeft )
+/*
+* Purpose: connect a "right" vertex vEvent (one where all edges go left)
+* to the unprocessed portion of the mesh. Since there are no right-going
+* edges, two regions (one above vEvent and one below) are being merged
+* into one. "regUp" is the upper of these two regions.
+*
+* There are two reasons for doing this (adding a right-going edge):
+* - if the two regions being merged are "inside", we must add an edge
+* to keep them separated (the combined region would not be monotone).
+* - in any case, we must leave some record of vEvent in the dictionary,
+* so that we can merge vEvent with features that we have not seen yet.
+* For example, maybe there is a vertical edge which passes just to
+* the right of vEvent; we would like to splice vEvent into this edge.
+*
+* However, we don't want to connect vEvent to just any vertex. We don''t
+* want the new edge to cross any other edges; otherwise we will create
+* intersection vertices even when the input data had no self-intersections.
+* (This is a bad thing; if the user's input data has no intersections,
+* we don't want to generate any false intersections ourselves.)
+*
+* Our eventual goal is to connect vEvent to the leftmost unprocessed
+* vertex of the combined region (the union of regUp and regLo).
+* But because of unseen vertices with all right-going edges, and also
+* new vertices which may be created by edge intersections, we don''t
+* know where that leftmost unprocessed vertex is. In the meantime, we
+* connect vEvent to the closest vertex of either chain, and mark the region
+* as "fixUpperEdge". This flag says to delete and reconnect this edge
+* to the next processed vertex on the boundary of the combined region.
+* Quite possibly the vertex we connected to will turn out to be the
+* closest one, in which case we won''t need to make any changes.
+*/
+{
+ TESShalfEdge *eNew;
+ TESShalfEdge *eTopLeft = eBottomLeft->Onext;
+ ActiveRegion *regLo = RegionBelow(regUp);
+ TESShalfEdge *eUp = regUp->eUp;
+ TESShalfEdge *eLo = regLo->eUp;
+ int degenerate = FALSE;
+
+ if( eUp->Dst != eLo->Dst ) {
+ (void) CheckForIntersect( tess, regUp );
+ }
+
+ /* Possible new degeneracies: upper or lower edge of regUp may pass
+ * through vEvent, or may coincide with new intersection vertex
+ */
+ if( VertEq( eUp->Org, tess->event )) {
+ if ( !tessMeshSplice( tess->mesh, eTopLeft->Oprev, eUp ) ) longjmp(tess->env,1);
+ regUp = TopLeftRegion( tess, regUp );
+ if (regUp == NULL) longjmp(tess->env,1);
+ eTopLeft = RegionBelow( regUp )->eUp;
+ FinishLeftRegions( tess, RegionBelow(regUp), regLo );
+ degenerate = TRUE;
+ }
+ if( VertEq( eLo->Org, tess->event )) {
+ if ( !tessMeshSplice( tess->mesh, eBottomLeft, eLo->Oprev ) ) longjmp(tess->env,1);
+ eBottomLeft = FinishLeftRegions( tess, regLo, NULL );
+ degenerate = TRUE;
+ }
+ if( degenerate ) {
+ AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE );
+ return;
+ }
+
+ /* Non-degenerate situation -- need to add a temporary, fixable edge.
+ * Connect to the closer of eLo->Org, eUp->Org.
+ */
+ if( VertLeq( eLo->Org, eUp->Org )) {
+ eNew = eLo->Oprev;
+ } else {
+ eNew = eUp;
+ }
+ eNew = tessMeshConnect( tess->mesh, eBottomLeft->Lprev, eNew );
+ if (eNew == NULL) longjmp(tess->env,1);
+
+ /* Prevent cleanup, otherwise eNew might disappear before we've even
+ * had a chance to mark it as a temporary edge.
+ */
+ AddRightEdges( tess, regUp, eNew, eNew->Onext, eNew->Onext, FALSE );
+ eNew->Sym->activeRegion->fixUpperEdge = TRUE;
+ WalkDirtyRegions( tess, regUp );
+}
+
+/* Because vertices at exactly the same location are merged together
+* before we process the sweep event, some degenerate cases can't occur.
+* However if someone eventually makes the modifications required to
+* merge features which are close together, the cases below marked
+* TOLERANCE_NONZERO will be useful. They were debugged before the
+* code to merge identical vertices in the main loop was added.
+*/
+#define TOLERANCE_NONZERO FALSE
+
+static void ConnectLeftDegenerate( TESStesselator *tess,
+ ActiveRegion *regUp, TESSvertex *vEvent )
+/*
+* The event vertex lies exacty on an already-processed edge or vertex.
+* Adding the new vertex involves splicing it into the already-processed
+* part of the mesh.
+*/
+{
+ TESShalfEdge *e, *eTopLeft, *eTopRight, *eLast;
+ ActiveRegion *reg;
+
+ e = regUp->eUp;
+ if( VertEq( e->Org, vEvent )) {
+ /* e->Org is an unprocessed vertex - just combine them, and wait
+ * for e->Org to be pulled from the queue
+ */
+ assert( TOLERANCE_NONZERO );
+ SpliceMergeVertices( tess, e, vEvent->anEdge );
+ return;
+ }
+
+ if( ! VertEq( e->Dst, vEvent )) {
+ /* General case -- splice vEvent into edge e which passes through it */
+ if (tessMeshSplitEdge( tess->mesh, e->Sym ) == NULL) longjmp(tess->env,1);
+ if( regUp->fixUpperEdge ) {
+ /* This edge was fixable -- delete unused portion of original edge */
+ if ( !tessMeshDelete( tess->mesh, e->Onext ) ) longjmp(tess->env,1);
+ regUp->fixUpperEdge = FALSE;
+ }
+ if ( !tessMeshSplice( tess->mesh, vEvent->anEdge, e ) ) longjmp(tess->env,1);
+ SweepEvent( tess, vEvent ); /* recurse */
+ return;
+ }
+
+ /* vEvent coincides with e->Dst, which has already been processed.
+ * Splice in the additional right-going edges.
+ */
+ assert( TOLERANCE_NONZERO );
+ regUp = TopRightRegion( regUp );
+ reg = RegionBelow( regUp );
+ eTopRight = reg->eUp->Sym;
+ eTopLeft = eLast = eTopRight->Onext;
+ if( reg->fixUpperEdge ) {
+ /* Here e->Dst has only a single fixable edge going right.
+ * We can delete it since now we have some real right-going edges.
+ */
+ assert( eTopLeft != eTopRight ); /* there are some left edges too */
+ DeleteRegion( tess, reg );
+ if ( !tessMeshDelete( tess->mesh, eTopRight ) ) longjmp(tess->env,1);
+ eTopRight = eTopLeft->Oprev;
+ }
+ if ( !tessMeshSplice( tess->mesh, vEvent->anEdge, eTopRight ) ) longjmp(tess->env,1);
+ if( ! EdgeGoesLeft( eTopLeft )) {
+ /* e->Dst had no left-going edges -- indicate this to AddRightEdges() */
+ eTopLeft = NULL;
+ }
+ AddRightEdges( tess, regUp, eTopRight->Onext, eLast, eTopLeft, TRUE );
+}
+
+
+static void ConnectLeftVertex( TESStesselator *tess, TESSvertex *vEvent )
+/*
+* Purpose: connect a "left" vertex (one where both edges go right)
+* to the processed portion of the mesh. Let R be the active region
+* containing vEvent, and let U and L be the upper and lower edge
+* chains of R. There are two possibilities:
+*
+* - the normal case: split R into two regions, by connecting vEvent to
+* the rightmost vertex of U or L lying to the left of the sweep line
+*
+* - the degenerate case: if vEvent is close enough to U or L, we
+* merge vEvent into that edge chain. The subcases are:
+* - merging with the rightmost vertex of U or L
+* - merging with the active edge of U or L
+* - merging with an already-processed portion of U or L
+*/
+{
+ ActiveRegion *regUp, *regLo, *reg;
+ TESShalfEdge *eUp, *eLo, *eNew;
+ ActiveRegion tmp;
+
+ /* assert( vEvent->anEdge->Onext->Onext == vEvent->anEdge ); */
+
+ /* Get a pointer to the active region containing vEvent */
+ tmp.eUp = vEvent->anEdge->Sym;
+ /* __GL_DICTLISTKEY */ /* tessDictListSearch */
+ regUp = (ActiveRegion *)dictKey( dictSearch( tess->dict, &tmp ));
+ regLo = RegionBelow( regUp );
+ if( !regLo ) {
+ // This may happen if the input polygon is coplanar.
+ return;
+ }
+ eUp = regUp->eUp;
+ eLo = regLo->eUp;
+
+ /* Try merging with U or L first */
+ if( EdgeSign( eUp->Dst, vEvent, eUp->Org ) == 0 ) {
+ ConnectLeftDegenerate( tess, regUp, vEvent );
+ return;
+ }
+
+ /* Connect vEvent to rightmost processed vertex of either chain.
+ * e->Dst is the vertex that we will connect to vEvent.
+ */
+ reg = VertLeq( eLo->Dst, eUp->Dst ) ? regUp : regLo;
+
+ if( regUp->inside || reg->fixUpperEdge) {
+ if( reg == regUp ) {
+ eNew = tessMeshConnect( tess->mesh, vEvent->anEdge->Sym, eUp->Lnext );
+ if (eNew == NULL) longjmp(tess->env,1);
+ } else {
+ TESShalfEdge *tempHalfEdge= tessMeshConnect( tess->mesh, eLo->Dnext, vEvent->anEdge);
+ if (tempHalfEdge == NULL) longjmp(tess->env,1);
+
+ eNew = tempHalfEdge->Sym;
+ }
+ if( reg->fixUpperEdge ) {
+ if ( !FixUpperEdge( tess, reg, eNew ) ) longjmp(tess->env,1);
+ } else {
+ ComputeWinding( tess, AddRegionBelow( tess, regUp, eNew ));
+ }
+ SweepEvent( tess, vEvent );
+ } else {
+ /* The new vertex is in a region which does not belong to the polygon.
+ * We don''t need to connect this vertex to the rest of the mesh.
+ */
+ AddRightEdges( tess, regUp, vEvent->anEdge, vEvent->anEdge, NULL, TRUE );
+ }
+}
+
+
+static void SweepEvent( TESStesselator *tess, TESSvertex *vEvent )
+/*
+* Does everything necessary when the sweep line crosses a vertex.
+* Updates the mesh and the edge dictionary.
+*/
+{
+ ActiveRegion *regUp, *reg;
+ TESShalfEdge *e, *eTopLeft, *eBottomLeft;
+
+ tess->event = vEvent; /* for access in EdgeLeq() */
+ DebugEvent( tess );
+
+ /* Check if this vertex is the right endpoint of an edge that is
+ * already in the dictionary. In this case we don't need to waste
+ * time searching for the location to insert new edges.
+ */
+ e = vEvent->anEdge;
+ while( e->activeRegion == NULL ) {
+ e = e->Onext;
+ if( e == vEvent->anEdge ) {
+ /* All edges go right -- not incident to any processed edges */
+ ConnectLeftVertex( tess, vEvent );
+ return;
+ }
+ }
+
+ /* Processing consists of two phases: first we "finish" all the
+ * active regions where both the upper and lower edges terminate
+ * at vEvent (ie. vEvent is closing off these regions).
+ * We mark these faces "inside" or "outside" the polygon according
+ * to their winding number, and delete the edges from the dictionary.
+ * This takes care of all the left-going edges from vEvent.
+ */
+ regUp = TopLeftRegion( tess, e->activeRegion );
+ if (regUp == NULL) longjmp(tess->env,1);
+ reg = RegionBelow( regUp );
+ eTopLeft = reg->eUp;
+ eBottomLeft = FinishLeftRegions( tess, reg, NULL );
+
+ /* Next we process all the right-going edges from vEvent. This
+ * involves adding the edges to the dictionary, and creating the
+ * associated "active regions" which record information about the
+ * regions between adjacent dictionary edges.
+ */
+ if( eBottomLeft->Onext == eTopLeft ) {
+ /* No right-going edges -- add a temporary "fixable" edge */
+ ConnectRightVertex( tess, regUp, eBottomLeft );
+ } else {
+ AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE );
+ }
+}
+
+
+/* Make the sentinel coordinates big enough that they will never be
+* merged with real input features.
+*/
+
+static void AddSentinel( TESStesselator *tess, TESSreal smin, TESSreal smax, TESSreal t )
+/*
+* We add two sentinel edges above and below all other edges,
+* to avoid special cases at the top and bottom.
+*/
+{
+ TESShalfEdge *e;
+ ActiveRegion *reg = (ActiveRegion *)bucketAlloc( tess->regionPool );
+ if (reg == NULL) longjmp(tess->env,1);
+
+ e = tessMeshMakeEdge( tess->mesh );
+ if (e == NULL) longjmp(tess->env,1);
+
+ e->Org->s = smax;
+ e->Org->t = t;
+ e->Dst->s = smin;
+ e->Dst->t = t;
+ tess->event = e->Dst; /* initialize it */
+
+ reg->eUp = e;
+ reg->windingNumber = 0;
+ reg->inside = FALSE;
+ reg->fixUpperEdge = FALSE;
+ reg->sentinel = TRUE;
+ reg->dirty = FALSE;
+ reg->nodeUp = dictInsert( tess->dict, reg );
+ if (reg->nodeUp == NULL) longjmp(tess->env,1);
+}
+
+
+static void InitEdgeDict( TESStesselator *tess )
+/*
+* We maintain an ordering of edge intersections with the sweep line.
+* This order is maintained in a dynamic dictionary.
+*/
+{
+ TESSreal w, h;
+ TESSreal smin, smax, tmin, tmax;
+
+ tess->dict = dictNewDict( &tess->alloc, tess, (int (*)(void *, DictKey, DictKey)) EdgeLeq );
+ if (tess->dict == NULL) longjmp(tess->env,1);
+
+ w = (tess->bmax[0] - tess->bmin[0]);
+ h = (tess->bmax[1] - tess->bmin[1]);
+
+ smin = tess->bmin[0] - w;
+ smax = tess->bmax[0] + w;
+ tmin = tess->bmin[1] - h;
+ tmax = tess->bmax[1] + h;
+
+ AddSentinel( tess, smin, smax, tmin );
+ AddSentinel( tess, smin, smax, tmax );
+}
+
+
+static void DoneEdgeDict( TESStesselator *tess )
+{
+ ActiveRegion *reg;
+ int fixedEdges = 0;
+
+ while( (reg = (ActiveRegion *)dictKey( dictMin( tess->dict ))) != NULL ) {
+ /*
+ * At the end of all processing, the dictionary should contain
+ * only the two sentinel edges, plus at most one "fixable" edge
+ * created by ConnectRightVertex().
+ */
+ if( ! reg->sentinel ) {
+ assert( reg->fixUpperEdge );
+ assert( ++fixedEdges == 1 );
+ }
+ assert( reg->windingNumber == 0 );
+ DeleteRegion( tess, reg );
+ /* tessMeshDelete( reg->eUp );*/
+ }
+ dictDeleteDict( &tess->alloc, tess->dict );
+}
+
+
+static void RemoveDegenerateEdges( TESStesselator *tess )
+/*
+* Remove zero-length edges, and contours with fewer than 3 vertices.
+*/
+{
+ TESShalfEdge *e, *eNext, *eLnext;
+ TESShalfEdge *eHead = &tess->mesh->eHead;
+
+ /*LINTED*/
+ for( e = eHead->next; e != eHead; e = eNext ) {
+ eNext = e->next;
+ eLnext = e->Lnext;
+
+ if( VertEq( e->Org, e->Dst ) && e->Lnext->Lnext != e ) {
+ /* Zero-length edge, contour has at least 3 edges */
+
+ SpliceMergeVertices( tess, eLnext, e ); /* deletes e->Org */
+ if ( !tessMeshDelete( tess->mesh, e ) ) longjmp(tess->env,1); /* e is a self-loop */
+ e = eLnext;
+ eLnext = e->Lnext;
+ }
+ if( eLnext->Lnext == e ) {
+ /* Degenerate contour (one or two edges) */
+
+ if( eLnext != e ) {
+ if( eLnext == eNext || eLnext == eNext->Sym ) { eNext = eNext->next; }
+ if ( !tessMeshDelete( tess->mesh, eLnext ) ) longjmp(tess->env,1);
+ }
+ if( e == eNext || e == eNext->Sym ) { eNext = eNext->next; }
+ if ( !tessMeshDelete( tess->mesh, e ) ) longjmp(tess->env,1);
+ }
+ }
+}
+
+static int InitPriorityQ( TESStesselator *tess )
+/*
+* Insert all vertices into the priority queue which determines the
+* order in which vertices cross the sweep line.
+*/
+{
+ PriorityQ *pq;
+ TESSvertex *v, *vHead;
+ int vertexCount = 0;
+
+ vHead = &tess->mesh->vHead;
+ for( v = vHead->next; v != vHead; v = v->next ) {
+ vertexCount++;
+ }
+ /* Make sure there is enough space for sentinels. */
+ vertexCount += MAX( 8, tess->alloc.extraVertices );
+
+ pq = tess->pq = pqNewPriorityQ( &tess->alloc, vertexCount, (int (*)(PQkey, PQkey)) tesvertLeq );
+ if (pq == NULL) return 0;
+
+ vHead = &tess->mesh->vHead;
+ for( v = vHead->next; v != vHead; v = v->next ) {
+ v->pqHandle = pqInsert( &tess->alloc, pq, v );
+ if (v->pqHandle == INV_HANDLE)
+ break;
+ }
+ if (v != vHead || !pqInit( &tess->alloc, pq ) ) {
+ pqDeletePriorityQ( &tess->alloc, tess->pq );
+ tess->pq = NULL;
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void DonePriorityQ( TESStesselator *tess )
+{
+ pqDeletePriorityQ( &tess->alloc, tess->pq );
+}
+
+
+static int RemoveDegenerateFaces( TESStesselator *tess, TESSmesh *mesh )
+/*
+* Delete any degenerate faces with only two edges. WalkDirtyRegions()
+* will catch almost all of these, but it won't catch degenerate faces
+* produced by splice operations on already-processed edges.
+* The two places this can happen are in FinishLeftRegions(), when
+* we splice in a "temporary" edge produced by ConnectRightVertex(),
+* and in CheckForLeftSplice(), where we splice already-processed
+* edges to ensure that our dictionary invariants are not violated
+* by numerical errors.
+*
+* In both these cases it is *very* dangerous to delete the offending
+* edge at the time, since one of the routines further up the stack
+* will sometimes be keeping a pointer to that edge.
+*/
+{
+ TESSface *f, *fNext;
+ TESShalfEdge *e;
+
+ /*LINTED*/
+ for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) {
+ fNext = f->next;
+ e = f->anEdge;
+ assert( e->Lnext != e );
+
+ if( e->Lnext->Lnext == e ) {
+ /* A face with only two edges */
+ AddWinding( e->Onext, e );
+ if ( !tessMeshDelete( tess->mesh, e ) ) return 0;
+ }
+ }
+ return 1;
+}
+
+int tessComputeInterior( TESStesselator *tess )
+/*
+* tessComputeInterior( tess ) computes the planar arrangement specified
+* by the given contours, and further subdivides this arrangement
+* into regions. Each region is marked "inside" if it belongs
+* to the polygon, according to the rule given by tess->windingRule.
+* Each interior region is guaranteed be monotone.
+*/
+{
+ TESSvertex *v, *vNext;
+
+ /* Each vertex defines an event for our sweep line. Start by inserting
+ * all the vertices in a priority queue. Events are processed in
+ * lexicographic order, ie.
+ *
+ * e1 < e2 iff e1.x < e2.x || (e1.x == e2.x && e1.y < e2.y)
+ */
+ RemoveDegenerateEdges( tess );
+ if ( !InitPriorityQ( tess ) ) return 0; /* if error */
+ InitEdgeDict( tess );
+
+ while( (v = (TESSvertex *)pqExtractMin( tess->pq )) != NULL ) {
+ for( ;; ) {
+ vNext = (TESSvertex *)pqMinimum( tess->pq );
+ if( vNext == NULL || ! VertEq( vNext, v )) break;
+
+ /* Merge together all vertices at exactly the same location.
+ * This is more efficient than processing them one at a time,
+ * simplifies the code (see ConnectLeftDegenerate), and is also
+ * important for correct handling of certain degenerate cases.
+ * For example, suppose there are two identical edges A and B
+ * that belong to different contours (so without this code they would
+ * be processed by separate sweep events). Suppose another edge C
+ * crosses A and B from above. When A is processed, we split it
+ * at its intersection point with C. However this also splits C,
+ * so when we insert B we may compute a slightly different
+ * intersection point. This might leave two edges with a small
+ * gap between them. This kind of error is especially obvious
+ * when using boundary extraction (TESS_BOUNDARY_ONLY).
+ */
+ vNext = (TESSvertex *)pqExtractMin( tess->pq );
+ SpliceMergeVertices( tess, v->anEdge, vNext->anEdge );
+ }
+ SweepEvent( tess, v );
+ }
+
+ /* Set tess->event for debugging purposes */
+ tess->event = ((ActiveRegion *) dictKey( dictMin( tess->dict )))->eUp->Org;
+ DebugEvent( tess );
+ DoneEdgeDict( tess );
+ DonePriorityQ( tess );
+
+ if ( !RemoveDegenerateFaces( tess, tess->mesh ) ) return 0;
+ tessMeshCheckMesh( tess->mesh );
+
+ return 1;
+}
diff --git a/Source/sweep.h b/Source/sweep.h
new file mode 100755
index 0000000..32f0f86
--- /dev/null
+++ b/Source/sweep.h
@@ -0,0 +1,74 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#ifndef SWEEP_H
+#define SWEEP_H
+
+#include "mesh.h"
+
+/* tessComputeInterior( tess ) computes the planar arrangement specified
+* by the given contours, and further subdivides this arrangement
+* into regions. Each region is marked "inside" if it belongs
+* to the polygon, according to the rule given by tess->windingRule.
+* Each interior region is guaranteed be monotone.
+*/
+int tessComputeInterior( TESStesselator *tess );
+
+
+/* The following is here *only* for access by debugging routines */
+
+#include "dict.h"
+
+/* For each pair of adjacent edges crossing the sweep line, there is
+* an ActiveRegion to represent the region between them. The active
+* regions are kept in sorted order in a dynamic dictionary. As the
+* sweep line crosses each vertex, we update the affected regions.
+*/
+
+struct ActiveRegion {
+ TESShalfEdge *eUp; /* upper edge, directed right to left */
+ DictNode *nodeUp; /* dictionary node corresponding to eUp */
+ int windingNumber; /* used to determine which regions are
+ * inside the polygon */
+ int inside; /* is this region inside the polygon? */
+ int sentinel; /* marks fake edges at t = +/-infinity */
+ int dirty; /* marks regions where the upper or lower
+ * edge has changed, but we haven't checked
+ * whether they intersect yet */
+ int fixUpperEdge; /* marks temporary edges introduced when
+ * we process a "right vertex" (one without
+ * any edges leaving to the right) */
+};
+
+#define RegionBelow(r) ((ActiveRegion *) dictKey(dictPred((r)->nodeUp)))
+#define RegionAbove(r) ((ActiveRegion *) dictKey(dictSucc((r)->nodeUp)))
+
+#endif
diff --git a/Source/tess.c b/Source/tess.c
new file mode 100755
index 0000000..ea39ee6
--- /dev/null
+++ b/Source/tess.c
@@ -0,0 +1,968 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#include <stddef.h>
+#include <assert.h>
+#include <setjmp.h>
+#include "bucketalloc.h"
+#include "tess.h"
+#include "mesh.h"
+#include "sweep.h"
+#include "geom.h"
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define TRUE 1
+#define FALSE 0
+
+#define Dot(u,v) (u[0]*v[0] + u[1]*v[1] + u[2]*v[2])
+
+static void Normalize( TESSreal v[3] )
+{
+ TESSreal len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
+
+ assert( len > 0 );
+ len = sqrtf( len );
+ v[0] /= len;
+ v[1] /= len;
+ v[2] /= len;
+}
+
+#define ABS(x) ((x) < 0 ? -(x) : (x))
+
+static int LongAxis( TESSreal v[3] )
+{
+ int i = 0;
+
+ if( ABS(v[1]) > ABS(v[0]) ) { i = 1; }
+ if( ABS(v[2]) > ABS(v[i]) ) { i = 2; }
+ return i;
+}
+
+static void ComputeNormal( TESStesselator *tess, TESSreal norm[3] )
+{
+ TESSvertex *v, *v1, *v2;
+ TESSreal c, tLen2, maxLen2;
+ TESSreal maxVal[3], minVal[3], d1[3], d2[3], tNorm[3];
+ TESSvertex *maxVert[3], *minVert[3];
+ TESSvertex *vHead = &tess->mesh->vHead;
+ int i;
+
+ v = vHead->next;
+ for( i = 0; i < 3; ++i ) {
+ c = v->coords[i];
+ minVal[i] = c;
+ minVert[i] = v;
+ maxVal[i] = c;
+ maxVert[i] = v;
+ }
+
+ for( v = vHead->next; v != vHead; v = v->next ) {
+ for( i = 0; i < 3; ++i ) {
+ c = v->coords[i];
+ if( c < minVal[i] ) { minVal[i] = c; minVert[i] = v; }
+ if( c > maxVal[i] ) { maxVal[i] = c; maxVert[i] = v; }
+ }
+ }
+
+ /* Find two vertices separated by at least 1/sqrt(3) of the maximum
+ * distance between any two vertices
+ */
+ i = 0;
+ if( maxVal[1] - minVal[1] > maxVal[0] - minVal[0] ) { i = 1; }
+ if( maxVal[2] - minVal[2] > maxVal[i] - minVal[i] ) { i = 2; }
+ if( minVal[i] >= maxVal[i] ) {
+ /* All vertices are the same -- normal doesn't matter */
+ norm[0] = 0; norm[1] = 0; norm[2] = 1;
+ return;
+ }
+
+ /* Look for a third vertex which forms the triangle with maximum area
+ * (Length of normal == twice the triangle area)
+ */
+ maxLen2 = 0;
+ v1 = minVert[i];
+ v2 = maxVert[i];
+ d1[0] = v1->coords[0] - v2->coords[0];
+ d1[1] = v1->coords[1] - v2->coords[1];
+ d1[2] = v1->coords[2] - v2->coords[2];
+ for( v = vHead->next; v != vHead; v = v->next ) {
+ d2[0] = v->coords[0] - v2->coords[0];
+ d2[1] = v->coords[1] - v2->coords[1];
+ d2[2] = v->coords[2] - v2->coords[2];
+ tNorm[0] = d1[1]*d2[2] - d1[2]*d2[1];
+ tNorm[1] = d1[2]*d2[0] - d1[0]*d2[2];
+ tNorm[2] = d1[0]*d2[1] - d1[1]*d2[0];
+ tLen2 = tNorm[0]*tNorm[0] + tNorm[1]*tNorm[1] + tNorm[2]*tNorm[2];
+ if( tLen2 > maxLen2 ) {
+ maxLen2 = tLen2;
+ norm[0] = tNorm[0];
+ norm[1] = tNorm[1];
+ norm[2] = tNorm[2];
+ }
+ }
+
+ if( maxLen2 <= 0 ) {
+ /* All points lie on a single line -- any decent normal will do */
+ norm[0] = norm[1] = norm[2] = 0;
+ norm[LongAxis(d1)] = 1;
+ }
+}
+
+
+static void CheckOrientation( TESStesselator *tess )
+{
+ TESSreal area;
+ TESSface *f, *fHead = &tess->mesh->fHead;
+ TESSvertex *v, *vHead = &tess->mesh->vHead;
+ TESShalfEdge *e;
+
+ /* When we compute the normal automatically, we choose the orientation
+ * so that the the sum of the signed areas of all contours is non-negative.
+ */
+ area = 0;
+ for( f = fHead->next; f != fHead; f = f->next ) {
+ e = f->anEdge;
+ if( e->winding <= 0 ) continue;
+ do {
+ area += (e->Org->s - e->Dst->s) * (e->Org->t + e->Dst->t);
+ e = e->Lnext;
+ } while( e != f->anEdge );
+ }
+ if( area < 0 ) {
+ /* Reverse the orientation by flipping all the t-coordinates */
+ for( v = vHead->next; v != vHead; v = v->next ) {
+ v->t = - v->t;
+ }
+ tess->tUnit[0] = - tess->tUnit[0];
+ tess->tUnit[1] = - tess->tUnit[1];
+ tess->tUnit[2] = - tess->tUnit[2];
+ }
+}
+
+#ifdef FOR_TRITE_TEST_PROGRAM
+#include <stdlib.h>
+extern int RandomSweep;
+#define S_UNIT_X (RandomSweep ? (2*drand48()-1) : 1.0)
+#define S_UNIT_Y (RandomSweep ? (2*drand48()-1) : 0.0)
+#else
+#if defined(SLANTED_SWEEP)
+/* The "feature merging" is not intended to be complete. There are
+* special cases where edges are nearly parallel to the sweep line
+* which are not implemented. The algorithm should still behave
+* robustly (ie. produce a reasonable tesselation) in the presence
+* of such edges, however it may miss features which could have been
+* merged. We could minimize this effect by choosing the sweep line
+* direction to be something unusual (ie. not parallel to one of the
+* coordinate axes).
+*/
+#define S_UNIT_X (TESSreal)0.50941539564955385 /* Pre-normalized */
+#define S_UNIT_Y (TESSreal)0.86052074622010633
+#else
+#define S_UNIT_X (TESSreal)1.0
+#define S_UNIT_Y (TESSreal)0.0
+#endif
+#endif
+
+/* Determine the polygon normal and project vertices onto the plane
+* of the polygon.
+*/
+void tessProjectPolygon( TESStesselator *tess )
+{
+ TESSvertex *v, *vHead = &tess->mesh->vHead;
+ TESSreal norm[3];
+ TESSreal *sUnit, *tUnit;
+ int i, first, computedNormal = FALSE;
+
+ norm[0] = tess->normal[0];
+ norm[1] = tess->normal[1];
+ norm[2] = tess->normal[2];
+ if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) {
+ ComputeNormal( tess, norm );
+ computedNormal = TRUE;
+ }
+ sUnit = tess->sUnit;
+ tUnit = tess->tUnit;
+ i = LongAxis( norm );
+
+#if defined(FOR_TRITE_TEST_PROGRAM) || defined(TRUE_PROJECT)
+ /* Choose the initial sUnit vector to be approximately perpendicular
+ * to the normal.
+ */
+ Normalize( norm );
+
+ sUnit[i] = 0;
+ sUnit[(i+1)%3] = S_UNIT_X;
+ sUnit[(i+2)%3] = S_UNIT_Y;
+
+ /* Now make it exactly perpendicular */
+ w = Dot( sUnit, norm );
+ sUnit[0] -= w * norm[0];
+ sUnit[1] -= w * norm[1];
+ sUnit[2] -= w * norm[2];
+ Normalize( sUnit );
+
+ /* Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame */
+ tUnit[0] = norm[1]*sUnit[2] - norm[2]*sUnit[1];
+ tUnit[1] = norm[2]*sUnit[0] - norm[0]*sUnit[2];
+ tUnit[2] = norm[0]*sUnit[1] - norm[1]*sUnit[0];
+ Normalize( tUnit );
+#else
+ /* Project perpendicular to a coordinate axis -- better numerically */
+ sUnit[i] = 0;
+ sUnit[(i+1)%3] = S_UNIT_X;
+ sUnit[(i+2)%3] = S_UNIT_Y;
+
+ tUnit[i] = 0;
+ tUnit[(i+1)%3] = (norm[i] > 0) ? -S_UNIT_Y : S_UNIT_Y;
+ tUnit[(i+2)%3] = (norm[i] > 0) ? S_UNIT_X : -S_UNIT_X;
+#endif
+
+ /* Project the vertices onto the sweep plane */
+ for( v = vHead->next; v != vHead; v = v->next )
+ {
+ v->s = Dot( v->coords, sUnit );
+ v->t = Dot( v->coords, tUnit );
+ }
+ if( computedNormal ) {
+ CheckOrientation( tess );
+ }
+
+ /* Compute ST bounds. */
+ first = 1;
+ for( v = vHead->next; v != vHead; v = v->next )
+ {
+ if (first)
+ {
+ tess->bmin[0] = tess->bmax[0] = v->s;
+ tess->bmin[1] = tess->bmax[1] = v->t;
+ first = 0;
+ }
+ else
+ {
+ if (v->s < tess->bmin[0]) tess->bmin[0] = v->s;
+ if (v->s > tess->bmax[0]) tess->bmax[0] = v->s;
+ if (v->t < tess->bmin[1]) tess->bmin[1] = v->t;
+ if (v->t > tess->bmax[1]) tess->bmax[1] = v->t;
+ }
+ }
+}
+
+#define AddWinding(eDst,eSrc) (eDst->winding += eSrc->winding, \
+ eDst->Sym->winding += eSrc->Sym->winding)
+
+/* tessMeshTessellateMonoRegion( face ) tessellates a monotone region
+* (what else would it do??) The region must consist of a single
+* loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this
+* case means that any vertical line intersects the interior of the
+* region in a single interval.
+*
+* Tessellation consists of adding interior edges (actually pairs of
+* half-edges), to split the region into non-overlapping triangles.
+*
+* The basic idea is explained in Preparata and Shamos (which I don''t
+* have handy right now), although their implementation is more
+* complicated than this one. The are two edge chains, an upper chain
+* and a lower chain. We process all vertices from both chains in order,
+* from right to left.
+*
+* The algorithm ensures that the following invariant holds after each
+* vertex is processed: the untessellated region consists of two
+* chains, where one chain (say the upper) is a single edge, and
+* the other chain is concave. The left vertex of the single edge
+* is always to the left of all vertices in the concave chain.
+*
+* Each step consists of adding the rightmost unprocessed vertex to one
+* of the two chains, and forming a fan of triangles from the rightmost
+* of two chain endpoints. Determining whether we can add each triangle
+* to the fan is a simple orientation test. By making the fan as large
+* as possible, we restore the invariant (check it yourself).
+*/
+int tessMeshTessellateMonoRegion( TESSmesh *mesh, TESSface *face )
+{
+ TESShalfEdge *up, *lo;
+
+ /* All edges are oriented CCW around the boundary of the region.
+ * First, find the half-edge whose origin vertex is rightmost.
+ * Since the sweep goes from left to right, face->anEdge should
+ * be close to the edge we want.
+ */
+ up = face->anEdge;
+ assert( up->Lnext != up && up->Lnext->Lnext != up );
+
+ for( ; VertLeq( up->Dst, up->Org ); up = up->Lprev )
+ ;
+ for( ; VertLeq( up->Org, up->Dst ); up = up->Lnext )
+ ;
+ lo = up->Lprev;
+
+ while( up->Lnext != lo ) {
+ if( VertLeq( up->Dst, lo->Org )) {
+ /* up->Dst is on the left. It is safe to form triangles from lo->Org.
+ * The EdgeGoesLeft test guarantees progress even when some triangles
+ * are CW, given that the upper and lower chains are truly monotone.
+ */
+ while( lo->Lnext != up && (EdgeGoesLeft( lo->Lnext )
+ || EdgeSign( lo->Org, lo->Dst, lo->Lnext->Dst ) <= 0 )) {
+ TESShalfEdge *tempHalfEdge= tessMeshConnect( mesh, lo->Lnext, lo );
+ if (tempHalfEdge == NULL) return 0;
+ lo = tempHalfEdge->Sym;
+ }
+ lo = lo->Lprev;
+ } else {
+ /* lo->Org is on the left. We can make CCW triangles from up->Dst. */
+ while( lo->Lnext != up && (EdgeGoesRight( up->Lprev )
+ || EdgeSign( up->Dst, up->Org, up->Lprev->Org ) >= 0 )) {
+ TESShalfEdge *tempHalfEdge= tessMeshConnect( mesh, up, up->Lprev );
+ if (tempHalfEdge == NULL) return 0;
+ up = tempHalfEdge->Sym;
+ }
+ up = up->Lnext;
+ }
+ }
+
+ /* Now lo->Org == up->Dst == the leftmost vertex. The remaining region
+ * can be tessellated in a fan from this leftmost vertex.
+ */
+ assert( lo->Lnext != up );
+ while( lo->Lnext->Lnext != up ) {
+ TESShalfEdge *tempHalfEdge= tessMeshConnect( mesh, lo->Lnext, lo );
+ if (tempHalfEdge == NULL) return 0;
+ lo = tempHalfEdge->Sym;
+ }
+
+ return 1;
+}
+
+
+/* tessMeshTessellateInterior( mesh ) tessellates each region of
+* the mesh which is marked "inside" the polygon. Each such region
+* must be monotone.
+*/
+int tessMeshTessellateInterior( TESSmesh *mesh )
+{
+ TESSface *f, *next;
+
+ /*LINTED*/
+ for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) {
+ /* Make sure we don''t try to tessellate the new triangles. */
+ next = f->next;
+ if( f->inside ) {
+ if ( !tessMeshTessellateMonoRegion( mesh, f ) ) return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+/* tessMeshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces
+* which are not marked "inside" the polygon. Since further mesh operations
+* on NULL faces are not allowed, the main purpose is to clean up the
+* mesh so that exterior loops are not represented in the data structure.
+*/
+void tessMeshDiscardExterior( TESSmesh *mesh )
+{
+ TESSface *f, *next;
+
+ /*LINTED*/
+ for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) {
+ /* Since f will be destroyed, save its next pointer. */
+ next = f->next;
+ if( ! f->inside ) {
+ tessMeshZapFace( mesh, f );
+ }
+ }
+}
+
+/* tessMeshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the
+* winding numbers on all edges so that regions marked "inside" the
+* polygon have a winding number of "value", and regions outside
+* have a winding number of 0.
+*
+* If keepOnlyBoundary is TRUE, it also deletes all edges which do not
+* separate an interior region from an exterior one.
+*/
+int tessMeshSetWindingNumber( TESSmesh *mesh, int value,
+ int keepOnlyBoundary )
+{
+ TESShalfEdge *e, *eNext;
+
+ for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) {
+ eNext = e->next;
+ if( e->Rface->inside != e->Lface->inside ) {
+
+ /* This is a boundary edge (one side is interior, one is exterior). */
+ e->winding = (e->Lface->inside) ? value : -value;
+ } else {
+
+ /* Both regions are interior, or both are exterior. */
+ if( ! keepOnlyBoundary ) {
+ e->winding = 0;
+ } else {
+ if ( !tessMeshDelete( mesh, e ) ) return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+void* heapAlloc( void* userData, unsigned int size )
+{
+ return malloc( size );
+}
+
+void* heapRealloc( void *userData, void* ptr, unsigned int size )
+{
+ return realloc( ptr, size );
+}
+
+void heapFree( void* userData, void* ptr )
+{
+ free( ptr );
+}
+
+static TESSalloc defaulAlloc =
+{
+ heapAlloc,
+ heapRealloc,
+ heapFree,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+};
+
+TESStesselator* tessNewTess( TESSalloc* alloc )
+{
+ TESStesselator* tess;
+
+ if (alloc == NULL)
+ alloc = &defaulAlloc;
+
+ /* Only initialize fields which can be changed by the api. Other fields
+ * are initialized where they are used.
+ */
+
+ tess = (TESStesselator *)alloc->memalloc( alloc->userData, sizeof( TESStesselator ));
+ if ( tess == NULL ) {
+ return 0; /* out of memory */
+ }
+ tess->alloc = *alloc;
+ /* Check and set defaults. */
+ if (tess->alloc.meshEdgeBucketSize == 0)
+ tess->alloc.meshEdgeBucketSize = 512;
+ if (tess->alloc.meshVertexBucketSize == 0)
+ tess->alloc.meshVertexBucketSize = 512;
+ if (tess->alloc.meshFaceBucketSize == 0)
+ tess->alloc.meshFaceBucketSize = 256;
+ if (tess->alloc.dictNodeBucketSize == 0)
+ tess->alloc.dictNodeBucketSize = 512;
+ if (tess->alloc.regionBucketSize == 0)
+ tess->alloc.regionBucketSize = 256;
+
+ tess->normal[0] = 0;
+ tess->normal[1] = 0;
+ tess->normal[2] = 0;
+
+ tess->bmin[0] = 0;
+ tess->bmin[1] = 0;
+ tess->bmax[0] = 0;
+ tess->bmax[1] = 0;
+
+ tess->windingRule = TESS_WINDING_ODD;
+
+ if (tess->alloc.regionBucketSize < 16)
+ tess->alloc.regionBucketSize = 16;
+ if (tess->alloc.regionBucketSize > 4096)
+ tess->alloc.regionBucketSize = 4096;
+ tess->regionPool = createBucketAlloc( &tess->alloc, "Regions",
+ sizeof(ActiveRegion), tess->alloc.regionBucketSize );
+
+ // Initialize to begin polygon.
+ tess->mesh = NULL;
+
+ tess->outOfMemory = 0;
+ tess->vertexIndexCounter = 0;
+
+ tess->vertices = 0;
+ tess->vertexIndices = 0;
+ tess->vertexCount = 0;
+ tess->elements = 0;
+ tess->elementCount = 0;
+
+ return tess;
+}
+
+void tessDeleteTess( TESStesselator *tess )
+{
+
+ struct TESSalloc alloc = tess->alloc;
+
+ deleteBucketAlloc( tess->regionPool );
+
+ if( tess->mesh != NULL ) {
+ tessMeshDeleteMesh( &alloc, tess->mesh );
+ tess->mesh = NULL;
+ }
+ if (tess->vertices != NULL) {
+ alloc.memfree( alloc.userData, tess->vertices );
+ tess->vertices = 0;
+ }
+ if (tess->vertexIndices != NULL) {
+ alloc.memfree( alloc.userData, tess->vertexIndices );
+ tess->vertexIndices = 0;
+ }
+ if (tess->elements != NULL) {
+ alloc.memfree( alloc.userData, tess->elements );
+ tess->elements = 0;
+ }
+
+ alloc.memfree( alloc.userData, tess );
+}
+
+
+static TESSindex GetNeighbourFace(TESShalfEdge* edge)
+{
+ if (!edge->Rface)
+ return TESS_UNDEF;
+ if (!edge->Rface->inside)
+ return TESS_UNDEF;
+ return edge->Rface->n;
+}
+
+void OutputPolymesh( TESStesselator *tess, TESSmesh *mesh, int elementType, int polySize, int vertexSize )
+{
+ TESSvertex* v = 0;
+ TESSface* f = 0;
+ TESShalfEdge* edge = 0;
+ int maxFaceCount = 0;
+ int maxVertexCount = 0;
+ int faceVerts, i;
+ TESSindex *elements = 0;
+ TESSreal *vert;
+
+ // Assume that the input data is triangles now.
+ // Try to merge as many polygons as possible
+ if (polySize > 3)
+ {
+ if (!tessMeshMergeConvexFaces( mesh, polySize ))
+ {
+ tess->outOfMemory = 1;
+ return;
+ }
+ }
+
+ // Mark unused
+ for ( v = mesh->vHead.next; v != &mesh->vHead; v = v->next )
+ v->n = TESS_UNDEF;
+
+ // Create unique IDs for all vertices and faces.
+ for ( f = mesh->fHead.next; f != &mesh->fHead; f = f->next )
+ {
+ f->n = TESS_UNDEF;
+ if( !f->inside ) continue;
+
+ edge = f->anEdge;
+ faceVerts = 0;
+ do
+ {
+ v = edge->Org;
+ if ( v->n == TESS_UNDEF )
+ {
+ v->n = maxVertexCount;
+ maxVertexCount++;
+ }
+ faceVerts++;
+ edge = edge->Lnext;
+ }
+ while (edge != f->anEdge);
+
+ assert( faceVerts <= polySize );
+
+ f->n = maxFaceCount;
+ ++maxFaceCount;
+ }
+
+ tess->elementCount = maxFaceCount;
+ if (elementType == TESS_CONNECTED_POLYGONS)
+ maxFaceCount *= 2;
+ tess->elements = (TESSindex*)tess->alloc.memalloc( tess->alloc.userData,
+ sizeof(TESSindex) * maxFaceCount * polySize );
+ if (!tess->elements)
+ {
+ tess->outOfMemory = 1;
+ return;
+ }
+
+ tess->vertexCount = maxVertexCount;
+ tess->vertices = (TESSreal*)tess->alloc.memalloc( tess->alloc.userData,
+ sizeof(TESSreal) * tess->vertexCount * vertexSize );
+ if (!tess->vertices)
+ {
+ tess->outOfMemory = 1;
+ return;
+ }
+
+ tess->vertexIndices = (TESSindex*)tess->alloc.memalloc( tess->alloc.userData,
+ sizeof(TESSindex) * tess->vertexCount );
+ if (!tess->vertexIndices)
+ {
+ tess->outOfMemory = 1;
+ return;
+ }
+
+ // Output vertices.
+ for ( v = mesh->vHead.next; v != &mesh->vHead; v = v->next )
+ {
+ if ( v->n != TESS_UNDEF )
+ {
+ // Store coordinate
+ vert = &tess->vertices[v->n*vertexSize];
+ vert[0] = v->coords[0];
+ vert[1] = v->coords[1];
+ if ( vertexSize > 2 )
+ vert[2] = v->coords[2];
+ // Store vertex index.
+ tess->vertexIndices[v->n] = v->idx;
+ }
+ }
+
+ // Output indices.
+ elements = tess->elements;
+ for ( f = mesh->fHead.next; f != &mesh->fHead; f = f->next )
+ {
+ if ( !f->inside ) continue;
+
+ // Store polygon
+ edge = f->anEdge;
+ faceVerts = 0;
+ do
+ {
+ v = edge->Org;
+ *elements++ = v->n;
+ faceVerts++;
+ edge = edge->Lnext;
+ }
+ while (edge != f->anEdge);
+ // Fill unused.
+ for (i = faceVerts; i < polySize; ++i)
+ *elements++ = TESS_UNDEF;
+
+ // Store polygon connectivity
+ if ( elementType == TESS_CONNECTED_POLYGONS )
+ {
+ edge = f->anEdge;
+ do
+ {
+ *elements++ = GetNeighbourFace( edge );
+ edge = edge->Lnext;
+ }
+ while (edge != f->anEdge);
+ // Fill unused.
+ for (i = faceVerts; i < polySize; ++i)
+ *elements++ = TESS_UNDEF;
+ }
+ }
+}
+
+void OutputContours( TESStesselator *tess, TESSmesh *mesh, int vertexSize )
+{
+ TESSface *f = 0;
+ TESShalfEdge *edge = 0;
+ TESShalfEdge *start = 0;
+ TESSreal *verts = 0;
+ TESSindex *elements = 0;
+ TESSindex *vertInds = 0;
+ int startVert = 0;
+ int vertCount = 0;
+
+ tess->vertexCount = 0;
+ tess->elementCount = 0;
+
+ for ( f = mesh->fHead.next; f != &mesh->fHead; f = f->next )
+ {
+ if ( !f->inside ) continue;
+
+ start = edge = f->anEdge;
+ do
+ {
+ ++tess->vertexCount;
+ edge = edge->Lnext;
+ }
+ while ( edge != start );
+
+ ++tess->elementCount;
+ }
+
+ tess->elements = (TESSindex*)tess->alloc.memalloc( tess->alloc.userData,
+ sizeof(TESSindex) * tess->elementCount * 2 );
+ if (!tess->elements)
+ {
+ tess->outOfMemory = 1;
+ return;
+ }
+
+ tess->vertices = (TESSreal*)tess->alloc.memalloc( tess->alloc.userData,
+ sizeof(TESSreal) * tess->vertexCount * vertexSize );
+ if (!tess->vertices)
+ {
+ tess->outOfMemory = 1;
+ return;
+ }
+
+ tess->vertexIndices = (TESSindex*)tess->alloc.memalloc( tess->alloc.userData,
+ sizeof(TESSindex) * tess->vertexCount );
+ if (!tess->vertexIndices)
+ {
+ tess->outOfMemory = 1;
+ return;
+ }
+
+ verts = tess->vertices;
+ elements = tess->elements;
+ vertInds = tess->vertexIndices;
+
+ startVert = 0;
+
+ for ( f = mesh->fHead.next; f != &mesh->fHead; f = f->next )
+ {
+ if ( !f->inside ) continue;
+
+ vertCount = 0;
+ start = edge = f->anEdge;
+ do
+ {
+ *verts++ = edge->Org->coords[0];
+ *verts++ = edge->Org->coords[1];
+ if ( vertexSize > 2 )
+ *verts++ = edge->Org->coords[2];
+ *vertInds++ = edge->Org->idx;
+ ++vertCount;
+ edge = edge->Lnext;
+ }
+ while ( edge != start );
+
+ elements[0] = startVert;
+ elements[1] = vertCount;
+ elements += 2;
+
+ startVert += vertCount;
+ }
+}
+
+void tessAddContour( TESStesselator *tess, int size, const void* vertices,
+ int stride, int numVertices )
+{
+ const unsigned char *src = (const unsigned char*)vertices;
+ TESShalfEdge *e;
+ int i;
+
+ if ( tess->mesh == NULL )
+ tess->mesh = tessMeshNewMesh( &tess->alloc );
+ if ( tess->mesh == NULL ) {
+ tess->outOfMemory = 1;
+ return;
+ }
+
+ if ( size < 2 )
+ size = 2;
+ if ( size > 3 )
+ size = 3;
+
+ e = NULL;
+
+ for( i = 0; i < numVertices; ++i )
+ {
+ const TESSreal* coords = (const TESSreal*)src;
+ src += stride;
+
+ if( e == NULL ) {
+ /* Make a self-loop (one vertex, one edge). */
+ e = tessMeshMakeEdge( tess->mesh );
+ if ( e == NULL ) {
+ tess->outOfMemory = 1;
+ return;
+ }
+ if ( !tessMeshSplice( tess->mesh, e, e->Sym ) ) {
+ tess->outOfMemory = 1;
+ return;
+ }
+ } else {
+ /* Create a new vertex and edge which immediately follow e
+ * in the ordering around the left face.
+ */
+ if ( tessMeshSplitEdge( tess->mesh, e ) == NULL ) {
+ tess->outOfMemory = 1;
+ return;
+ }
+ e = e->Lnext;
+ }
+
+ /* The new vertex is now e->Org. */
+ e->Org->coords[0] = coords[0];
+ e->Org->coords[1] = coords[1];
+ if ( size > 2 )
+ e->Org->coords[2] = coords[2];
+ else
+ e->Org->coords[2] = 0;
+ /* Store the insertion number so that the vertex can be later recognized. */
+ e->Org->idx = tess->vertexIndexCounter++;
+
+ /* The winding of an edge says how the winding number changes as we
+ * cross from the edge''s right face to its left face. We add the
+ * vertices in such an order that a CCW contour will add +1 to
+ * the winding number of the region inside the contour.
+ */
+ e->winding = 1;
+ e->Sym->winding = -1;
+ }
+}
+
+int tessTesselate( TESStesselator *tess, int windingRule, int elementType,
+ int polySize, int vertexSize, const TESSreal* normal )
+{
+ TESSmesh *mesh;
+ int rc = 1;
+
+ if (tess->vertices != NULL) {
+ tess->alloc.memfree( tess->alloc.userData, tess->vertices );
+ tess->vertices = 0;
+ }
+ if (tess->elements != NULL) {
+ tess->alloc.memfree( tess->alloc.userData, tess->elements );
+ tess->elements = 0;
+ }
+ if (tess->vertexIndices != NULL) {
+ tess->alloc.memfree( tess->alloc.userData, tess->vertexIndices );
+ tess->vertexIndices = 0;
+ }
+
+ tess->vertexIndexCounter = 0;
+
+ if (normal)
+ {
+ tess->normal[0] = normal[0];
+ tess->normal[1] = normal[1];
+ tess->normal[2] = normal[2];
+ }
+
+ tess->windingRule = windingRule;
+
+ if (vertexSize < 2)
+ vertexSize = 2;
+ if (vertexSize > 3)
+ vertexSize = 3;
+
+ if (setjmp(tess->env) != 0) {
+ /* come back here if out of memory */
+ return 0;
+ }
+
+ if (!tess->mesh)
+ {
+ return 0;
+ }
+
+ /* Determine the polygon normal and project vertices onto the plane
+ * of the polygon.
+ */
+ tessProjectPolygon( tess );
+
+ /* tessComputeInterior( tess ) computes the planar arrangement specified
+ * by the given contours, and further subdivides this arrangement
+ * into regions. Each region is marked "inside" if it belongs
+ * to the polygon, according to the rule given by tess->windingRule.
+ * Each interior region is guaranteed be monotone.
+ */
+ if ( !tessComputeInterior( tess ) ) {
+ longjmp(tess->env,1); /* could've used a label */
+ }
+
+ mesh = tess->mesh;
+
+ /* If the user wants only the boundary contours, we throw away all edges
+ * except those which separate the interior from the exterior.
+ * Otherwise we tessellate all the regions marked "inside".
+ */
+ if (elementType == TESS_BOUNDARY_CONTOURS) {
+ rc = tessMeshSetWindingNumber( mesh, 1, TRUE );
+ } else {
+ rc = tessMeshTessellateInterior( mesh );
+ }
+ if (rc == 0) longjmp(tess->env,1); /* could've used a label */
+
+ tessMeshCheckMesh( mesh );
+
+ if (elementType == TESS_BOUNDARY_CONTOURS) {
+ OutputContours( tess, mesh, vertexSize ); /* output contours */
+ }
+ else
+ {
+ OutputPolymesh( tess, mesh, elementType, polySize, vertexSize ); /* output polygons */
+ }
+
+ tessMeshDeleteMesh( &tess->alloc, mesh );
+ tess->mesh = NULL;
+
+ if (tess->outOfMemory)
+ return 0;
+ return 1;
+}
+
+int tessGetVertexCount( TESStesselator *tess )
+{
+ return tess->vertexCount;
+}
+
+const TESSreal* tessGetVertices( TESStesselator *tess )
+{
+ return tess->vertices;
+}
+
+const TESSindex* tessGetVertexIndices( TESStesselator *tess )
+{
+ return tess->vertexIndices;
+}
+
+int tessGetElementCount( TESStesselator *tess )
+{
+ return tess->elementCount;
+}
+
+const int* tessGetElements( TESStesselator *tess )
+{
+ return tess->elements;
+}
diff --git a/Source/tess.h b/Source/tess.h
new file mode 100755
index 0000000..c8c747b
--- /dev/null
+++ b/Source/tess.h
@@ -0,0 +1,90 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+** of the Software, and to permit persons to whom the Software is furnished to do so,
+** subject to the following conditions:
+**
+** The above copyright notice including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be
+** included in all copies or substantial portions of the Software.
+**
+** 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 AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC.
+** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+** OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#ifndef TESS_H
+#define TESS_H
+
+#include <setjmp.h>
+#include "bucketalloc.h"
+#include "mesh.h"
+#include "dict.h"
+#include "priorityq.h"
+#include "../Include/tesselator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//typedef struct TESStesselator TESStesselator;
+
+struct TESStesselator {
+
+ /*** state needed for collecting the input data ***/
+ TESSmesh *mesh; /* stores the input contours, and eventually
+ the tessellation itself */
+ int outOfMemory;
+
+ /*** state needed for projecting onto the sweep plane ***/
+
+ TESSreal normal[3]; /* user-specified normal (if provided) */
+ TESSreal sUnit[3]; /* unit vector in s-direction (debugging) */
+ TESSreal tUnit[3]; /* unit vector in t-direction (debugging) */
+
+ TESSreal bmin[2];
+ TESSreal bmax[2];
+
+ /*** state needed for the line sweep ***/
+ int windingRule; /* rule for determining polygon interior */
+
+ Dict *dict; /* edge dictionary for sweep line */
+ PriorityQ *pq; /* priority queue of vertex events */
+ TESSvertex *event; /* current sweep event being processed */
+
+ struct BucketAlloc* regionPool;
+
+ TESSindex vertexIndexCounter;
+
+ TESSreal *vertices;
+ TESSindex *vertexIndices;
+ int vertexCount;
+ TESSindex *elements;
+ int elementCount;
+
+ TESSalloc alloc;
+
+ jmp_buf env; /* place to jump to when memAllocs fail */
+};
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
diff --git a/alg_outline.md b/alg_outline.md
new file mode 100644
index 0000000..449ca2a
--- /dev/null
+++ b/alg_outline.md
@@ -0,0 +1,225 @@
+This is only a very brief overview. There is quite a bit of
+additional documentation in the source code itself.
+
+
+Goals of robust tesselation
+---------------------------
+
+The tesselation algorithm is fundamentally a 2D algorithm. We
+initially project all data into a plane; our goal is to robustly
+tesselate the projected data. The same topological tesselation is
+then applied to the input data.
+
+Topologically, the output should always be a tesselation. If the
+input is even slightly non-planar, then some triangles will
+necessarily be back-facing when viewed from some angles, but the goal
+is to minimize this effect.
+
+The algorithm needs some capability of cleaning up the input data as
+well as the numerical errors in its own calculations. One way to do
+this is to specify a tolerance as defined above, and clean up the
+input and output during the line sweep process. At the very least,
+the algorithm must handle coincident vertices, vertices incident to an
+edge, and coincident edges.
+
+
+Phases of the algorithm
+-----------------------
+
+1. Find the polygon normal N.
+2. Project the vertex data onto a plane. It does not need to be
+ perpendicular to the normal, eg. we can project onto the plane
+ perpendicular to the coordinate axis whose dot product with N
+ is largest.
+3. Using a line-sweep algorithm, partition the plane into x-monotone
+ regions. Any vertical line intersects an x-monotone region in
+ at most one interval.
+4. Triangulate the x-monotone regions.
+5. Group the triangles into strips and fans.
+
+
+Finding the normal vector
+-------------------------
+
+A common way to find a polygon normal is to compute the signed area
+when the polygon is projected along the three coordinate axes. We
+can't do this, since contours can have zero area without being
+degenerate (eg. a bowtie).
+
+We fit a plane to the vertex data, ignoring how they are connected
+into contours. Ideally this would be a least-squares fit; however for
+our purpose the accuracy of the normal is not important. Instead we
+find three vertices which are widely separated, and compute the normal
+to the triangle they form. The vertices are chosen so that the
+triangle has an area at least 1/sqrt(3) times the largest area of any
+triangle formed using the input vertices.
+
+The contours do affect the orientation of the normal; after computing
+the normal, we check that the sum of the signed contour areas is
+non-negative, and reverse the normal if necessary.
+
+
+Projecting the vertices
+-----------------------
+
+We project the vertices onto a plane perpendicular to one of the three
+coordinate axes. This helps numerical accuracy by removing a
+transformation step between the original input data and the data
+processed by the algorithm. The projection also compresses the input
+data; the 2D distance between vertices after projection may be smaller
+than the original 2D distance. However by choosing the coordinate
+axis whose dot product with the normal is greatest, the compression
+factor is at most 1/sqrt(3).
+
+Even though the *accuracy* of the normal is not that important (since
+we are projecting perpendicular to a coordinate axis anyway), the
+*robustness* of the computation is important. For example, if there
+are many vertices which lie almost along a line, and one vertex V
+which is well-separated from the line, then our normal computation
+should involve V otherwise the results will be garbage.
+
+The advantage of projecting perpendicular to the polygon normal is
+that computed intersection points will be as close as possible to
+their ideal locations. To get this behavior, define TRUE_PROJECT.
+
+
+The Line Sweep
+--------------
+
+There are three data structures: the mesh, the event queue, and the
+edge dictionary.
+
+The mesh is a "quad-edge" data structure which records the topology of
+the current decomposition; for details see the include file "mesh.h".
+
+The event queue simply holds all vertices (both original and computed
+ones), organized so that we can quickly extract the vertex with the
+minimum x-coord (and among those, the one with the minimum y-coord).
+
+The edge dictionary describes the current intersection of the sweep
+line with the regions of the polygon. This is just an ordering of the
+edges which intersect the sweep line, sorted by their current order of
+intersection. For each pair of edges, we store some information about
+the monotone region between them -- these are call "active regions"
+(since they are crossed by the current sweep line).
+
+The basic algorithm is to sweep from left to right, processing each
+vertex. The processed portion of the mesh (left of the sweep line) is
+a planar decomposition. As we cross each vertex, we update the mesh
+and the edge dictionary, then we check any newly adjacent pairs of
+edges to see if they intersect.
+
+A vertex can have any number of edges. Vertices with many edges can
+be created as vertices are merged and intersection points are
+computed. For unprocessed vertices (right of the sweep line), these
+edges are in no particular order around the vertex; for processed
+vertices, the topological ordering should match the geometric ordering.
+
+The vertex processing happens in two phases: first we process are the
+left-going edges (all these edges are currently in the edge
+dictionary). This involves:
+
+ - deleting the left-going edges from the dictionary;
+ - relinking the mesh if necessary, so that the order of these edges around
+ the event vertex matches the order in the dictionary;
+ - marking any terminated regions (regions which lie between two left-going
+ edges) as either "inside" or "outside" according to their winding number.
+
+When there are no left-going edges, and the event vertex is in an
+"interior" region, we need to add an edge (to split the region into
+monotone pieces). To do this we simply join the event vertex to the
+rightmost left endpoint of the upper or lower edge of the containing
+region.
+
+Then we process the right-going edges. This involves:
+
+ - inserting the edges in the edge dictionary;
+ - computing the winding number of any newly created active regions.
+ We can compute this incrementally using the winding of each edge
+ that we cross as we walk through the dictionary.
+ - relinking the mesh if necessary, so that the order of these edges around
+ the event vertex matches the order in the dictionary;
+ - checking any newly adjacent edges for intersection and/or merging.
+
+If there are no right-going edges, again we need to add one to split
+the containing region into monotone pieces. In our case it is most
+convenient to add an edge to the leftmost right endpoint of either
+containing edge; however we may need to change this later (see the
+code for details).
+
+
+Invariants
+----------
+
+These are the most important invariants maintained during the sweep.
+We define a function VertLeq(v1,v2) which defines the order in which
+vertices cross the sweep line, and a function EdgeLeq(e1,e2; loc)
+which says whether e1 is below e2 at the sweep event location "loc".
+This function is defined only at sweep event locations which lie
+between the rightmost left endpoint of {e1,e2}, and the leftmost right
+endpoint of {e1,e2}.
+
+Invariants for the Edge Dictionary.
+
+ - Each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2)
+ at any valid location of the sweep event.
+ - If EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2
+ share a common endpoint.
+ - For each e in the dictionary, e->Dst has been processed but not e->Org.
+ - Each edge e satisfies VertLeq(e->Dst,event) && VertLeq(event,e->Org)
+ where "event" is the current sweep line event.
+ - No edge e has zero length.
+ - No two edges have identical left and right endpoints.
+
+Invariants for the Mesh (the processed portion).
+
+ - The portion of the mesh left of the sweep line is a planar graph,
+ ie. there is *some* way to embed it in the plane.
+ - No processed edge has zero length.
+ - No two processed vertices have identical coordinates.
+ - Each "inside" region is monotone, ie. can be broken into two chains
+ of monotonically increasing vertices according to VertLeq(v1,v2)
+ - a non-invariant: these chains may intersect (slightly) due to
+ numerical errors, but this does not affect the algorithm's operation.
+
+Invariants for the Sweep.
+
+ - If a vertex has any left-going edges, then these must be in the edge
+ dictionary at the time the vertex is processed.
+ - If an edge is marked "fixUpperEdge" (it is a temporary edge introduced
+ by ConnectRightVertex), then it is the only right-going edge from
+ its associated vertex. (This says that these edges exist only
+ when it is necessary.)
+
+
+Robustness
+----------
+
+The key to the robustness of the algorithm is maintaining the
+invariants above, especially the correct ordering of the edge
+dictionary. We achieve this by:
+
+ 1. Writing the numerical computations for maximum precision rather
+ than maximum speed.
+
+ 2. Making no assumptions at all about the results of the edge
+ intersection calculations -- for sufficiently degenerate inputs,
+ the computed location is not much better than a random number.
+
+ 3. When numerical errors violate the invariants, restore them
+ by making *topological* changes when necessary (ie. relinking
+ the mesh structure).
+
+
+Triangulation and Grouping
+--------------------------
+
+We finish the line sweep before doing any triangulation. This is
+because even after a monotone region is complete, there can be further
+changes to its vertex data because of further vertex merging.
+
+After triangulating all monotone regions, we want to group the
+triangles into fans and strips. We do this using a greedy approach.
+The triangulation itself is not optimized to reduce the number of
+primitives; we just try to get a reasonable decomposition of the
+computed triangulation.