diff --git a/.idea/.idea.Aetheria-Economy/.idea/contentModel.xml b/.idea/.idea.Aetheria-Economy/.idea/contentModel.xml
index 3f7010721..904cce64b 100644
--- a/.idea/.idea.Aetheria-Economy/.idea/contentModel.xml
+++ b/.idea/.idea.Aetheria-Economy/.idea/contentModel.xml
@@ -409,6 +409,620 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1258,12 +1872,12 @@
-
+
diff --git a/Assembly-CSharp-Editor.csproj b/Assembly-CSharp-Editor.csproj
index c4086ad73..06c1863c1 100644
--- a/Assembly-CSharp-Editor.csproj
+++ b/Assembly-CSharp-Editor.csproj
@@ -26,7 +26,7 @@
full
false
Temp\bin\Debug\
- DEBUG;TRACE;UNITY_5_3_OR_NEWER;UNITY_5_4_OR_NEWER;UNITY_5_5_OR_NEWER;UNITY_5_6_OR_NEWER;UNITY_2017_1_OR_NEWER;UNITY_2017_2_OR_NEWER;UNITY_2017_3_OR_NEWER;UNITY_2017_4_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2018_2_OR_NEWER;UNITY_2018_3_OR_NEWER;UNITY_2018_4_OR_NEWER;UNITY_2018_4_17;UNITY_2018_4;UNITY_2018;PLATFORM_ARCH_64;UNITY_64;UNITY_INCLUDE_TESTS;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_DUCK_TYPING;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_PHYSICS;ENABLE_SPRITES;ENABLE_GRID;ENABLE_TILEMAP;ENABLE_TERRAIN;ENABLE_TEXTURE_STREAMING;ENABLE_DIRECTOR;ENABLE_UNET;ENABLE_LZMA;ENABLE_UNITYEVENTS;ENABLE_WEBCAM;ENABLE_WWW;ENABLE_CLOUD_SERVICES_COLLAB;ENABLE_CLOUD_SERVICES_COLLAB_SOFTLOCKS;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_HUB;ENABLE_CLOUD_PROJECT_ID;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CLOUD_SERVICES_UNET;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_LICENSE;ENABLE_EDITOR_HUB;ENABLE_EDITOR_HUB_LICENSE;ENABLE_WEBSOCKET_CLIENT;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_TIMELINE;ENABLE_EDITOR_METRICS;ENABLE_EDITOR_METRICS_CACHING;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_ANIMATION_JOBS;INCLUDE_DYNAMIC_GI;INCLUDE_GI;ENABLE_MONO_BDWGC;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;INCLUDE_PUBNUB;ENABLE_VIDEO;ENABLE_CUSTOM_RENDER_TEXTURE;ENABLE_LOCALIZATION;PLATFORM_STANDALONE_WIN;PLATFORM_STANDALONE;UNITY_STANDALONE_WIN;UNITY_STANDALONE;ENABLE_SUBSTANCE;ENABLE_RUNTIME_GI;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_UNITYWEBREQUEST;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_OUT_OF_PROCESS_CRASH_HANDLER;ENABLE_EVENT_QUEUE;ENABLE_CLUSTER_SYNC;ENABLE_CLUSTERINPUT;ENABLE_VR;ENABLE_AR;ENABLE_WEBSOCKET_HOST;ENABLE_MONO;NET_4_6;ENABLE_PROFILER;UNITY_ASSERTIONS;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_BURST_AOT;UNITY_TEAM_LICENSE;UNITY_PRO_LICENSE;UNITY_POST_PROCESSING_STACK_V2;CSHARP_7_OR_LATER;CSHARP_7_3_OR_NEWER
+ DEBUG;TRACE;UNITY_5_3_OR_NEWER;UNITY_5_4_OR_NEWER;UNITY_5_5_OR_NEWER;UNITY_5_6_OR_NEWER;UNITY_2017_1_OR_NEWER;UNITY_2017_2_OR_NEWER;UNITY_2017_3_OR_NEWER;UNITY_2017_4_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2018_2_OR_NEWER;UNITY_2018_3_OR_NEWER;UNITY_2018_4_OR_NEWER;UNITY_2018_4_17;UNITY_2018_4;UNITY_2018;PLATFORM_ARCH_64;UNITY_64;UNITY_INCLUDE_TESTS;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_DUCK_TYPING;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_PHYSICS;ENABLE_SPRITES;ENABLE_GRID;ENABLE_TILEMAP;ENABLE_TERRAIN;ENABLE_TEXTURE_STREAMING;ENABLE_DIRECTOR;ENABLE_UNET;ENABLE_LZMA;ENABLE_UNITYEVENTS;ENABLE_WEBCAM;ENABLE_WWW;ENABLE_CLOUD_SERVICES_COLLAB;ENABLE_CLOUD_SERVICES_COLLAB_SOFTLOCKS;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_HUB;ENABLE_CLOUD_PROJECT_ID;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CLOUD_SERVICES_UNET;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_LICENSE;ENABLE_EDITOR_HUB;ENABLE_EDITOR_HUB_LICENSE;ENABLE_WEBSOCKET_CLIENT;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_TIMELINE;ENABLE_EDITOR_METRICS;ENABLE_EDITOR_METRICS_CACHING;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_ANIMATION_JOBS;INCLUDE_DYNAMIC_GI;INCLUDE_GI;ENABLE_MONO_BDWGC;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;INCLUDE_PUBNUB;ENABLE_VIDEO;ENABLE_CUSTOM_RENDER_TEXTURE;ENABLE_LOCALIZATION;PLATFORM_STANDALONE_WIN;PLATFORM_STANDALONE;UNITY_STANDALONE_WIN;UNITY_STANDALONE;ENABLE_SUBSTANCE;ENABLE_RUNTIME_GI;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_UNITYWEBREQUEST;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_OUT_OF_PROCESS_CRASH_HANDLER;ENABLE_EVENT_QUEUE;ENABLE_CLUSTER_SYNC;ENABLE_CLUSTERINPUT;ENABLE_VR;ENABLE_AR;ENABLE_WEBSOCKET_HOST;ENABLE_MONO;NET_4_6;ENABLE_PROFILER;UNITY_ASSERTIONS;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_BURST_AOT;UNITY_TEAM_LICENSE;UNITY_PRO_LICENSE;UNITY_POST_PROCESSING_STACK_V2;TEST_MSAGL;REPORTING;PERSISTENCE;CSHARP_7_OR_LATER;CSHARP_7_3_OR_NEWER
prompt
4
0169
@@ -383,6 +383,9 @@
C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/Newtonsoft.Json.dll
+
+ C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/QuickGraph.dll
+
C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/Remotion.Linq.dll
@@ -410,9 +413,6 @@
C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/Grpc.Core.Api/lib/net45/Grpc.Core.Api.dll
-
- C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/QuickGraph.dll
-
C:/Program Files/Unity/Hub/Editor/2018.4.17f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/mscorlib.dll
diff --git a/Assembly-CSharp-firstpass.csproj b/Assembly-CSharp-firstpass.csproj
index c213bfb9b..b5d64bebf 100644
--- a/Assembly-CSharp-firstpass.csproj
+++ b/Assembly-CSharp-firstpass.csproj
@@ -26,7 +26,7 @@
full
false
Temp\bin\Debug\
- DEBUG;TRACE;UNITY_5_3_OR_NEWER;UNITY_5_4_OR_NEWER;UNITY_5_5_OR_NEWER;UNITY_5_6_OR_NEWER;UNITY_2017_1_OR_NEWER;UNITY_2017_2_OR_NEWER;UNITY_2017_3_OR_NEWER;UNITY_2017_4_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2018_2_OR_NEWER;UNITY_2018_3_OR_NEWER;UNITY_2018_4_OR_NEWER;UNITY_2018_4_17;UNITY_2018_4;UNITY_2018;PLATFORM_ARCH_64;UNITY_64;UNITY_INCLUDE_TESTS;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_DUCK_TYPING;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_PHYSICS;ENABLE_SPRITES;ENABLE_GRID;ENABLE_TILEMAP;ENABLE_TERRAIN;ENABLE_TEXTURE_STREAMING;ENABLE_DIRECTOR;ENABLE_UNET;ENABLE_LZMA;ENABLE_UNITYEVENTS;ENABLE_WEBCAM;ENABLE_WWW;ENABLE_CLOUD_SERVICES_COLLAB;ENABLE_CLOUD_SERVICES_COLLAB_SOFTLOCKS;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_HUB;ENABLE_CLOUD_PROJECT_ID;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CLOUD_SERVICES_UNET;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_LICENSE;ENABLE_EDITOR_HUB;ENABLE_EDITOR_HUB_LICENSE;ENABLE_WEBSOCKET_CLIENT;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_TIMELINE;ENABLE_EDITOR_METRICS;ENABLE_EDITOR_METRICS_CACHING;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_ANIMATION_JOBS;INCLUDE_DYNAMIC_GI;INCLUDE_GI;ENABLE_MONO_BDWGC;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;INCLUDE_PUBNUB;ENABLE_VIDEO;ENABLE_CUSTOM_RENDER_TEXTURE;ENABLE_LOCALIZATION;PLATFORM_STANDALONE_WIN;PLATFORM_STANDALONE;UNITY_STANDALONE_WIN;UNITY_STANDALONE;ENABLE_SUBSTANCE;ENABLE_RUNTIME_GI;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_UNITYWEBREQUEST;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_OUT_OF_PROCESS_CRASH_HANDLER;ENABLE_EVENT_QUEUE;ENABLE_CLUSTER_SYNC;ENABLE_CLUSTERINPUT;ENABLE_VR;ENABLE_AR;ENABLE_WEBSOCKET_HOST;ENABLE_MONO;NET_4_6;ENABLE_PROFILER;UNITY_ASSERTIONS;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_BURST_AOT;UNITY_TEAM_LICENSE;UNITY_PRO_LICENSE;UNITY_POST_PROCESSING_STACK_V2;CSHARP_7_OR_LATER;CSHARP_7_3_OR_NEWER
+ DEBUG;TRACE;UNITY_5_3_OR_NEWER;UNITY_5_4_OR_NEWER;UNITY_5_5_OR_NEWER;UNITY_5_6_OR_NEWER;UNITY_2017_1_OR_NEWER;UNITY_2017_2_OR_NEWER;UNITY_2017_3_OR_NEWER;UNITY_2017_4_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2018_2_OR_NEWER;UNITY_2018_3_OR_NEWER;UNITY_2018_4_OR_NEWER;UNITY_2018_4_17;UNITY_2018_4;UNITY_2018;PLATFORM_ARCH_64;UNITY_64;UNITY_INCLUDE_TESTS;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_DUCK_TYPING;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_PHYSICS;ENABLE_SPRITES;ENABLE_GRID;ENABLE_TILEMAP;ENABLE_TERRAIN;ENABLE_TEXTURE_STREAMING;ENABLE_DIRECTOR;ENABLE_UNET;ENABLE_LZMA;ENABLE_UNITYEVENTS;ENABLE_WEBCAM;ENABLE_WWW;ENABLE_CLOUD_SERVICES_COLLAB;ENABLE_CLOUD_SERVICES_COLLAB_SOFTLOCKS;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_HUB;ENABLE_CLOUD_PROJECT_ID;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CLOUD_SERVICES_UNET;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_LICENSE;ENABLE_EDITOR_HUB;ENABLE_EDITOR_HUB_LICENSE;ENABLE_WEBSOCKET_CLIENT;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_TIMELINE;ENABLE_EDITOR_METRICS;ENABLE_EDITOR_METRICS_CACHING;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_ANIMATION_JOBS;INCLUDE_DYNAMIC_GI;INCLUDE_GI;ENABLE_MONO_BDWGC;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;INCLUDE_PUBNUB;ENABLE_VIDEO;ENABLE_CUSTOM_RENDER_TEXTURE;ENABLE_LOCALIZATION;PLATFORM_STANDALONE_WIN;PLATFORM_STANDALONE;UNITY_STANDALONE_WIN;UNITY_STANDALONE;ENABLE_SUBSTANCE;ENABLE_RUNTIME_GI;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_UNITYWEBREQUEST;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_OUT_OF_PROCESS_CRASH_HANDLER;ENABLE_EVENT_QUEUE;ENABLE_CLUSTER_SYNC;ENABLE_CLUSTERINPUT;ENABLE_VR;ENABLE_AR;ENABLE_WEBSOCKET_HOST;ENABLE_MONO;NET_4_6;ENABLE_PROFILER;UNITY_ASSERTIONS;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_BURST_AOT;UNITY_TEAM_LICENSE;UNITY_PRO_LICENSE;UNITY_POST_PROCESSING_STACK_V2;TEST_MSAGL;REPORTING;PERSISTENCE;CSHARP_7_OR_LATER;CSHARP_7_3_OR_NEWER
prompt
4
0169
@@ -297,6 +297,9 @@
C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/Newtonsoft.Json.dll
+
+ C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/QuickGraph.dll
+
C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/Remotion.Linq.dll
@@ -321,9 +324,6 @@
C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/Grpc.Core.Api/lib/net45/Grpc.Core.Api.dll
-
- C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/QuickGraph.dll
-
C:/Program Files/Unity/Hub/Editor/2018.4.17f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/mscorlib.dll
diff --git a/Assembly-CSharp.csproj b/Assembly-CSharp.csproj
index 690530951..f74e7030c 100644
--- a/Assembly-CSharp.csproj
+++ b/Assembly-CSharp.csproj
@@ -26,7 +26,7 @@
full
false
Temp\bin\Debug\
- DEBUG;TRACE;UNITY_5_3_OR_NEWER;UNITY_5_4_OR_NEWER;UNITY_5_5_OR_NEWER;UNITY_5_6_OR_NEWER;UNITY_2017_1_OR_NEWER;UNITY_2017_2_OR_NEWER;UNITY_2017_3_OR_NEWER;UNITY_2017_4_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2018_2_OR_NEWER;UNITY_2018_3_OR_NEWER;UNITY_2018_4_OR_NEWER;UNITY_2018_4_17;UNITY_2018_4;UNITY_2018;PLATFORM_ARCH_64;UNITY_64;UNITY_INCLUDE_TESTS;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_DUCK_TYPING;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_PHYSICS;ENABLE_SPRITES;ENABLE_GRID;ENABLE_TILEMAP;ENABLE_TERRAIN;ENABLE_TEXTURE_STREAMING;ENABLE_DIRECTOR;ENABLE_UNET;ENABLE_LZMA;ENABLE_UNITYEVENTS;ENABLE_WEBCAM;ENABLE_WWW;ENABLE_CLOUD_SERVICES_COLLAB;ENABLE_CLOUD_SERVICES_COLLAB_SOFTLOCKS;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_HUB;ENABLE_CLOUD_PROJECT_ID;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CLOUD_SERVICES_UNET;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_LICENSE;ENABLE_EDITOR_HUB;ENABLE_EDITOR_HUB_LICENSE;ENABLE_WEBSOCKET_CLIENT;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_TIMELINE;ENABLE_EDITOR_METRICS;ENABLE_EDITOR_METRICS_CACHING;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_ANIMATION_JOBS;INCLUDE_DYNAMIC_GI;INCLUDE_GI;ENABLE_MONO_BDWGC;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;INCLUDE_PUBNUB;ENABLE_VIDEO;ENABLE_CUSTOM_RENDER_TEXTURE;ENABLE_LOCALIZATION;PLATFORM_STANDALONE_WIN;PLATFORM_STANDALONE;UNITY_STANDALONE_WIN;UNITY_STANDALONE;ENABLE_SUBSTANCE;ENABLE_RUNTIME_GI;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_UNITYWEBREQUEST;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_OUT_OF_PROCESS_CRASH_HANDLER;ENABLE_EVENT_QUEUE;ENABLE_CLUSTER_SYNC;ENABLE_CLUSTERINPUT;ENABLE_VR;ENABLE_AR;ENABLE_WEBSOCKET_HOST;ENABLE_MONO;NET_4_6;ENABLE_PROFILER;UNITY_ASSERTIONS;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_BURST_AOT;UNITY_TEAM_LICENSE;UNITY_PRO_LICENSE;UNITY_POST_PROCESSING_STACK_V2;CSHARP_7_OR_LATER;CSHARP_7_3_OR_NEWER
+ DEBUG;TRACE;UNITY_5_3_OR_NEWER;UNITY_5_4_OR_NEWER;UNITY_5_5_OR_NEWER;UNITY_5_6_OR_NEWER;UNITY_2017_1_OR_NEWER;UNITY_2017_2_OR_NEWER;UNITY_2017_3_OR_NEWER;UNITY_2017_4_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2018_2_OR_NEWER;UNITY_2018_3_OR_NEWER;UNITY_2018_4_OR_NEWER;UNITY_2018_4_17;UNITY_2018_4;UNITY_2018;PLATFORM_ARCH_64;UNITY_64;UNITY_INCLUDE_TESTS;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_DUCK_TYPING;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_PHYSICS;ENABLE_SPRITES;ENABLE_GRID;ENABLE_TILEMAP;ENABLE_TERRAIN;ENABLE_TEXTURE_STREAMING;ENABLE_DIRECTOR;ENABLE_UNET;ENABLE_LZMA;ENABLE_UNITYEVENTS;ENABLE_WEBCAM;ENABLE_WWW;ENABLE_CLOUD_SERVICES_COLLAB;ENABLE_CLOUD_SERVICES_COLLAB_SOFTLOCKS;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_HUB;ENABLE_CLOUD_PROJECT_ID;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CLOUD_SERVICES_UNET;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_LICENSE;ENABLE_EDITOR_HUB;ENABLE_EDITOR_HUB_LICENSE;ENABLE_WEBSOCKET_CLIENT;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_TIMELINE;ENABLE_EDITOR_METRICS;ENABLE_EDITOR_METRICS_CACHING;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_ANIMATION_JOBS;INCLUDE_DYNAMIC_GI;INCLUDE_GI;ENABLE_MONO_BDWGC;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;INCLUDE_PUBNUB;ENABLE_VIDEO;ENABLE_CUSTOM_RENDER_TEXTURE;ENABLE_LOCALIZATION;PLATFORM_STANDALONE_WIN;PLATFORM_STANDALONE;UNITY_STANDALONE_WIN;UNITY_STANDALONE;ENABLE_SUBSTANCE;ENABLE_RUNTIME_GI;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_UNITYWEBREQUEST;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_OUT_OF_PROCESS_CRASH_HANDLER;ENABLE_EVENT_QUEUE;ENABLE_CLUSTER_SYNC;ENABLE_CLUSTERINPUT;ENABLE_VR;ENABLE_AR;ENABLE_WEBSOCKET_HOST;ENABLE_MONO;NET_4_6;ENABLE_PROFILER;UNITY_ASSERTIONS;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_BURST_AOT;UNITY_TEAM_LICENSE;UNITY_PRO_LICENSE;UNITY_POST_PROCESSING_STACK_V2;TEST_MSAGL;REPORTING;PERSISTENCE;CSHARP_7_OR_LATER;CSHARP_7_3_OR_NEWER
prompt
4
0169
@@ -241,6 +241,516 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -511,6 +1021,7 @@
+
@@ -793,6 +1304,9 @@
C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/Newtonsoft.Json.dll
+
+ C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/QuickGraph.dll
+
C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/Remotion.Linq.dll
@@ -817,9 +1331,6 @@
C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/Grpc.Core.Api/lib/net45/Grpc.Core.Api.dll
-
- C:/Users/rwvens/Documents/GitHub/Aetheria-Economy/Assets/Plugins/QuickGraph.dll
-
C:/Program Files/Unity/Hub/Editor/2018.4.17f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/mscorlib.dll
diff --git a/Assets/Resources/Elysium.asset b/Assets/Resources/Elysium.asset
index f53b5b69b..270b14d10 100644
--- a/Assets/Resources/Elysium.asset
+++ b/Assets/Resources/Elysium.asset
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:30c451432161fff5c202ce8217149ad255f4f3a66cd71aec2ce8722dd3f6d310
-size 7770
+oid sha256:b50f0d89a6d4b0efcc7303858ec2ec24e72ca68c67b45596f58387cfd601f08e
+size 7792
diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity
index a38f4124c..26fb0aba5 100644
--- a/Assets/Scenes/Main.unity
+++ b/Assets/Scenes/Main.unity
@@ -7880,7 +7880,7 @@ MonoBehaviour:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 435654366}
- m_Enabled: 0
+ m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0ae6f9307731b8f469a018c69d95d2ba, type: 3}
m_Name:
@@ -11412,12 +11412,12 @@ PrefabInstance:
- target: {fileID: 3856099931455214244, guid: b93e36d73a071274d8e9225e077ce92a,
type: 3}
propertyPath: m_Name
- value: Zone
+ value: Sector
objectReference: {fileID: 0}
- target: {fileID: 4750078953547169287, guid: b93e36d73a071274d8e9225e077ce92a,
type: 3}
propertyPath: m_text
- value: Zone
+ value: Sector
objectReference: {fileID: 0}
- target: {fileID: 4750078953547169287, guid: b93e36d73a071274d8e9225e077ce92a,
type: 3}
@@ -11427,7 +11427,7 @@ PrefabInstance:
- target: {fileID: 4750078953547169287, guid: b93e36d73a071274d8e9225e077ce92a,
type: 3}
propertyPath: m_textInfo.characterCount
- value: 4
+ value: 6
objectReference: {fileID: 0}
- target: {fileID: 4750078953547169287, guid: b93e36d73a071274d8e9225e077ce92a,
type: 3}
@@ -11449,6 +11449,11 @@ PrefabInstance:
propertyPath: m_textInfo.pageCount
value: 1
objectReference: {fileID: 0}
+ - target: {fileID: 4750078953547169287, guid: b93e36d73a071274d8e9225e077ce92a,
+ type: 3}
+ propertyPath: m_subTextObjects.Array.data[1]
+ value:
+ objectReference: {fileID: 105783976}
- target: {fileID: 5318229203699776728, guid: b93e36d73a071274d8e9225e077ce92a,
type: 3}
propertyPath: m_LocalPosition.x
@@ -11562,7 +11567,7 @@ PrefabInstance:
- target: {fileID: 6493174789768221420, guid: b93e36d73a071274d8e9225e077ce92a,
type: 3}
propertyPath: m_AnchoredPosition.x
- value: 68.3
+ value: 78.765
objectReference: {fileID: 0}
- target: {fileID: 6493174789768221420, guid: b93e36d73a071274d8e9225e077ce92a,
type: 3}
@@ -14296,6 +14301,7 @@ GameObject:
- component: {fileID: 712278634}
- component: {fileID: 712278633}
- component: {fileID: 712278632}
+ - component: {fileID: 712278635}
m_Layer: 0
m_Name: Tech View
m_TagString: Untagged
@@ -14310,7 +14316,7 @@ MonoBehaviour:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 712278631}
- m_Enabled: 1
+ m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6270ad993ac7b944786c936b7b058263, type: 3}
m_Name:
@@ -14322,24 +14328,28 @@ MonoBehaviour:
RadialLink: {fileID: 1417816149}
Arrow: {fileID: 1083812776}
Algorithm: 1
- TechCount: 1000
- SeedTechs: 15
- RollingWindowSize: 50
+ TechCount: 200
+ SeedTechs: 10
+ RollingWindowSize: 20
EliminateIslands: 0
- DisconnectedMultiplier: 2
+ MultipleDependencies: 0
+ MultipleDependencyProbability: 0.5
+ MaxDependencyDistance: 4
+ MaxDependencyTierDifference: 2
+ DisconnectedMultiplier: 4
K: 0.25
- LengthFactor: 1
- Iterations: 100
+ LengthFactor: 2
+ Iterations: 1000
LayerDistance: 1
- VertexDistance: 0.1
+ VertexDistance: 1
WidthPerHeight: 1
PositionMode: -1
Radial: 1
- MinimizeEdgeLength: 0
- OptimizeWidth: 0
+ MinimizeEdgeLength: 1
+ OptimizeWidth: 1
Sections: 360
PaddingRadians: 3.14
- StartTier: 10
+ StartTier: 4
Scale: 2
LinkPoints: 32
LinkDepth: 5
@@ -14347,7 +14357,7 @@ MonoBehaviour:
RingDepth: 6
ArrowDepth: 3
Saturation: 0.95
- HueMin: 0.5
+ HueMin: 0
HueMax: 1
Brightness: 1
RingInnerSaturation: 1
@@ -14394,6 +14404,56 @@ Transform:
m_Father: {fileID: 0}
m_RootOrder: 6
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &712278635
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 712278631}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d47bb1c8e9ce08e41877db523d91a2f6, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ IconsPath: Sprites/Icons/Tech
+ Ring: {fileID: 680383218}
+ Tech: {fileID: 586612708}
+ RadialLink: {fileID: 1417816149}
+ Arrow: {fileID: 1083812776}
+ TechCount: 500
+ SeedTechs: 15
+ RollingWindowSize: 100
+ Radial: 1
+ MultipleDependencyProbability: 0.15
+ DependencyDepth: 2
+ DependencyAncestorDepth: 4
+ PackingMethod: 0
+ Sections: 360
+ PaddingRadians: 3.14
+ StartTier: 12
+ Scale: 2
+ LinkPoints: 16
+ LinkDepth: 5
+ LinkTargetDistance: 0.75
+ RingDepth: 6
+ ArrowDepth: 3
+ LayerDistance: 1.25
+ Saturation: 0.95
+ HueMin: 0
+ HueMax: 1
+ Brightness: 1
+ RingInnerSaturation: 1
+ RingOuterSaturation: 0.25
+ RingInnerBrightness: 1
+ RingOuterBrightness: 0.25
+ RingColorKeys: 8
+ FillOpacity: 0.3
+ GlowOpacity: 0.6
+ TechFillMaterial: {fileID: 2100000, guid: c3c13ba00125fdf4c92bd96210dc7230, type: 2}
+ TechGlowMaterial: {fileID: 2100000, guid: 1426b25e088759647b77658321197be7, type: 2}
+ TechArrowMaterial: {fileID: 2100000, guid: 2a2ec810cb24ddb4d869e73fe8a1510a, type: 2}
+ TechLinkMaterial: {fileID: 2100000, guid: 67e23cda35beab546a57003dd2d33de1, type: 2}
--- !u!1 &716116810
GameObject:
m_ObjectHideFlags: 0
@@ -22522,7 +22582,7 @@ MonoBehaviour:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1126386499}
- m_Enabled: 1
+ m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1c1faf0a46b9cf24498cd5ec93c34c71, type: 3}
m_Name:
@@ -38510,6 +38570,11 @@ PrefabInstance:
propertyPath: m_textInfo.pageCount
value: 1
objectReference: {fileID: 0}
+ - target: {fileID: 4750078953547169287, guid: b93e36d73a071274d8e9225e077ce92a,
+ type: 3}
+ propertyPath: m_subTextObjects.Array.data[1]
+ value:
+ objectReference: {fileID: 426675036}
- target: {fileID: 5318229203699776728, guid: b93e36d73a071274d8e9225e077ce92a,
type: 3}
propertyPath: m_LocalPosition.x
@@ -39972,6 +40037,7 @@ GameObject:
m_Component:
- component: {fileID: 1941419274}
- component: {fileID: 1941419275}
+ - component: {fileID: 1941419276}
m_Layer: 0
m_Name: Link
m_TagString: Untagged
@@ -40006,6 +40072,19 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 1281fe44325f9b548aa42dc331b17266, type: 3}
m_Name:
m_EditorClassIdentifier:
+--- !u!114 &1941419276
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1941419273}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d54298fa75eb6304489e19a84af3413f, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ Icon: {fileID: 1813193646}
--- !u!1 &1944537660
GameObject:
m_ObjectHideFlags: 0
@@ -43547,9 +43626,7 @@ PrefabInstance:
- target: {fileID: 4750078953547169287, guid: b93e36d73a071274d8e9225e077ce92a,
type: 3}
propertyPath: m_text
- value: 'Corporation
-
-'
+ value: Corporation
objectReference: {fileID: 0}
- target: {fileID: 4750078953547169287, guid: b93e36d73a071274d8e9225e077ce92a,
type: 3}
@@ -43559,7 +43636,7 @@ PrefabInstance:
- target: {fileID: 4750078953547169287, guid: b93e36d73a071274d8e9225e077ce92a,
type: 3}
propertyPath: m_textInfo.characterCount
- value: 12
+ value: 11
objectReference: {fileID: 0}
- target: {fileID: 4750078953547169287, guid: b93e36d73a071274d8e9225e077ce92a,
type: 3}
@@ -43581,6 +43658,16 @@ PrefabInstance:
propertyPath: m_subTextObjects.Array.data[1]
value:
objectReference: {fileID: 650897775}
+ - target: {fileID: 4750078953547169287, guid: b93e36d73a071274d8e9225e077ce92a,
+ type: 3}
+ propertyPath: m_textInfo.lineCount
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 4750078953547169287, guid: b93e36d73a071274d8e9225e077ce92a,
+ type: 3}
+ propertyPath: m_textInfo.pageCount
+ value: 1
+ objectReference: {fileID: 0}
- target: {fileID: 5318229203699776728, guid: b93e36d73a071274d8e9225e077ce92a,
type: 3}
propertyPath: m_LocalPosition.x
diff --git a/Assets/Scripts/Editor/DatabaseInspector.cs b/Assets/Scripts/Editor/DatabaseInspector.cs
index 2f2606924..583c173cd 100644
--- a/Assets/Scripts/Editor/DatabaseInspector.cs
+++ b/Assets/Scripts/Editor/DatabaseInspector.cs
@@ -197,7 +197,7 @@ public class DatabaseInspector : EditorWindow
else if (type == typeof(Dictionary))
{
var dict = (Dictionary) field.GetValue(obj);
- Inspect(field.Name.SplitCamelCase(), ref dict);
+ Inspect(field.Name.SplitCamelCase(), ref dict, link.EntryType);
}
else if (type == typeof(Dictionary))
{
@@ -611,7 +611,7 @@ public class DatabaseInspector : EditorWindow
return value;
}
- public void Inspect(string label, ref Dictionary value)
+ public void Inspect(string label, ref Dictionary value, Type referenceType)
{
Space();
LabelField(label, EditorStyles.boldLabel);
@@ -629,7 +629,11 @@ public class DatabaseInspector : EditorWindow
{
using (var h = new HorizontalScope(_list.ListItemStyle))
{
- GUILayout.Label(DatabaseCache.Get(ingredient.Key).Name);
+ var entry = DatabaseCache.Get(ingredient.Key);
+ if(entry is INamedEntry named)
+ GUILayout.Label(named.EntryName);
+ else GUILayout.Label(entry.ID.ToString());
+
value[ingredient.Key] = DelayedIntField(value[ingredient.Key], GUILayout.Width(50));
var rect = GetControlRect(false, GUILayout.Width(EditorGUIUtility.singleLineHeight));
@@ -641,15 +645,18 @@ public class DatabaseInspector : EditorWindow
if (v.rect.Contains(Event.current.mousePosition))
{
+ var dragData = DragAndDrop.GetGenericData("Item");
+ var isId = dragData is Guid guid;
+ var dragEntry = isId ? DatabaseCache.Get(guid) : null;
+ var correctType = referenceType.IsInstanceOfType(dragEntry);
if(Event.current.type == EventType.DragUpdated)
- DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
+ DragAndDrop.visualMode = correctType ? DragAndDropVisualMode.Copy : DragAndDropVisualMode.Rejected;
else if(Event.current.type == EventType.DragPerform)
{
- var guid = DragAndDrop.GetGenericData("Item");
- if (guid is Guid)
+ if (isId && correctType)
{
DragAndDrop.AcceptDrag();
- value[(Guid) guid] = 1;
+ value[guid] = 1;
GUI.changed = true;
}
}
diff --git a/Assets/Scripts/Editor/DatabaseView.cs b/Assets/Scripts/Editor/DatabaseView.cs
index 04f60971a..e21246d96 100644
--- a/Assets/Scripts/Editor/DatabaseView.cs
+++ b/Assets/Scripts/Editor/DatabaseView.cs
@@ -164,7 +164,8 @@ public class DatabaseListView : EditorWindow
var result = await R
.Db("Aetheria")
.Table(table)
- .Update(entry)
+ .Get(entry.ID)
+ .Replace(entry)
.RunAsync(_connection);
Debug.Log($"Uploaded entry to RethinkDB: {entry.ID} result: {result}");
};
diff --git a/Assets/Scripts/NIH/MSAGL.meta b/Assets/Scripts/NIH/MSAGL.meta
new file mode 100644
index 000000000..9f850e3b7
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a80bb5b7ce4059d40a0eddd80b4b4ae5
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/AutomaticGraphLayout.csproj b/Assets/Scripts/NIH/MSAGL/AutomaticGraphLayout.csproj
new file mode 100644
index 000000000..7884fbb28
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/AutomaticGraphLayout.csproj
@@ -0,0 +1,17 @@
+
+
+ false
+
+
+ netstandard2.0
+
+
+ PERSISTENCE;REPORTING
+
+
+ PERSISTENCE;REPORTING;TEST_MSAGL
+
+
+
+
+
\ No newline at end of file
diff --git a/Assets/Scripts/NIH/MSAGL/AutomaticGraphLayout.csproj.meta b/Assets/Scripts/NIH/MSAGL/AutomaticGraphLayout.csproj.meta
new file mode 100644
index 000000000..272a93c90
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/AutomaticGraphLayout.csproj.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 40dcc7efbe87de246a2a5146c5128222
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/AutomaticGraphLayout.nuspec b/Assets/Scripts/NIH/MSAGL/AutomaticGraphLayout.nuspec
new file mode 100644
index 000000000..e39e7a323
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/AutomaticGraphLayout.nuspec
@@ -0,0 +1,24 @@
+
+
+ AutomaticGraphLayout
+ 1.1.4
+ paulovila
+ Microsoft
+ https://github.com/Microsoft/automatic-graph-layout/blob/master/LICENSE
+ https://github.com/Microsoft/automatic-graph-layout
+ true
+ This .NET asssembly contains the core MSAGL layout functionality. Use this library if you just want MSAGL to perform the layout only and afterwards you will use a separate tool to perform the rendering and visalization.
+
+
+ Copyright © Microsoft Corporation
+ en-US
+ MSAGL Graph Layout
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assets/Scripts/NIH/MSAGL/AutomaticGraphLayout.nuspec.meta b/Assets/Scripts/NIH/MSAGL/AutomaticGraphLayout.nuspec.meta
new file mode 100644
index 000000000..958717901
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/AutomaticGraphLayout.nuspec.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 64a9bda5ccebb9e45b4c45a2f2d7d9da
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core.meta b/Assets/Scripts/NIH/MSAGL/Core.meta
new file mode 100644
index 000000000..b3411b557
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f3f9e18906f0e744eb7e5b695467d1cc
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/AlgorithmBase.cs b/Assets/Scripts/NIH/MSAGL/Core/AlgorithmBase.cs
new file mode 100644
index 000000000..dac2cb835
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/AlgorithmBase.cs
@@ -0,0 +1,305 @@
+using System;
+namespace Microsoft.Msagl.Core
+{
+ ///
+ /// Base class for algorithms that are cancelable and report progress
+ ///
+ public abstract class AlgorithmBase
+ {
+ #region Running
+ ///
+ /// Runs the algorithm.
+ /// Inherits any preexisting cancel tokens.
+ ///
+ public void Run()
+ {
+ this.progressRatio = 0;
+ this.localStepCount = 0;
+
+ this.cancelToken = threadStaticCancelToken;
+ this.ThrowIfCanceled();
+
+ this.ProgressSteps(0); // Initial progress
+
+ this.RunInternal();
+
+ if (!this.IsCanceled)
+ {
+ this.ProgressComplete();
+ }
+ }
+
+ ///
+ /// Runs the algorithm, setting up the cancel token and reverting to the old cancel token before finishing
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "cancelToken")]
+ public void Run(CancelToken cancelToken)
+ {
+ this.progressRatio = 0;
+ this.localStepCount = 0;
+
+ CancelToken oldCancelToken = this.SetCancelToken(cancelToken);
+
+ try
+ {
+ this.ProgressSteps(0); // Initial progress
+
+ this.RunInternal();
+
+ if (!this.IsCanceled)
+ {
+ this.ProgressComplete();
+ }
+ }
+ finally
+ {
+ threadStaticCancelToken = oldCancelToken;
+ }
+ }
+
+ ///
+ /// Executes the actual algorithm.
+ ///
+ protected abstract void RunInternal();
+
+ #endregion Running
+
+ #region Cancel
+
+ ///
+ /// Set the cancel token. Returns the old cancel token, in case you need to revert at some stage.
+ ///
+ /// new cancel token
+ /// old cancel token
+ public CancelToken SetCancelToken(CancelToken cancelToken)
+ {
+ this.cancelToken = cancelToken;
+ this.ThrowIfCanceled();
+ CancelToken oldCancelToken = threadStaticCancelToken;
+ threadStaticCancelToken = cancelToken;
+ return oldCancelToken;
+ }
+
+ ///
+ /// Cancels the algorithm that is currently in progress.
+ ///
+ public void Cancel()
+ {
+ if (this.cancelToken != null)
+ {
+ this.cancelToken.Canceled = true;
+ }
+ }
+
+ ///
+ /// True if the algorithm has been canceled.
+ ///
+ public bool IsCanceled
+ {
+ get { return this.cancelToken != null && this.cancelToken.Canceled; }
+ }
+
+ ///
+ /// Throws an OperationCanceledException if the current algorithm has been canceled.
+ ///
+ protected void ThrowIfCanceled()
+ {
+ if (this.cancelToken != null)
+ {
+ this.cancelToken.ThrowIfCanceled();
+ }
+ }
+
+ ///
+ /// The current cancel token - one per thread, cached by cancelToken, below.
+ ///
+ [ThreadStatic]
+ private static CancelToken threadStaticCancelToken;
+
+ ///
+ /// ThreadStatic field above has about 10X the lookup cost of a local field, so we
+ /// cache its value in cancelToken
+ ///
+ private volatile CancelToken cancelToken;
+
+ ///
+ /// the current cancel token
+ ///
+ protected CancelToken CancelToken { get { return cancelToken; } }
+
+ #endregion Cancel
+
+ #region Progress
+
+ ///
+ /// notifies whenever progress is made by the algorithm. ProgressChangedEventArgs report
+ /// 0 < progressRatio <= 1
+ ///
+ public event EventHandler ProgressChanged;
+
+ ///
+ /// Total progress to date
+ ///
+ private double progressRatio = 0;
+
+ ///
+ /// Step size to increment progressRatio when ProgressStep is called
+ ///
+ private double localProgressStepSize = LocalProgressStepSizeDefault;
+
+ ///
+ /// True if the progress step count has been specified using StartListenToLocalProgress.
+ ///
+ private bool localProgressSpecified = false;
+
+ ///
+ /// The number of times ProgressStep has been called.
+ ///
+ private int localStepCount = 0;
+
+ ///
+ /// When running sub-algorithms this gives the progressRatio start point
+ ///
+ private double stageStartRatio = 0;
+
+ ///
+ /// When running sub-algorithms this gives the progressRatio end point
+ ///
+ private double stageEndRatio = 1;
+
+ ///
+ /// if StartListenToLocalProgress is not used to setup step size the following will be the
+ /// (rather arbitrary) default.
+ ///
+ private const double LocalProgressStepSizeDefault = 0.05;
+
+ ///
+ /// setup to start listening to ProgressStep() invocations from this algorithm.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ protected void StartListenToLocalProgress(int expectedSteps, double stageRatio = 1)
+ {
+ localProgressStepSize = stageRatio / Math.Max(1, expectedSteps); // at least 1, to prevent divide by 0
+ localProgressSpecified = true;
+ localStepCount = 0;
+ stageStartRatio = 0;
+ stageEndRatio = 1;
+ }
+
+ ///
+ /// When starting a sub-algorithm start listening to its progress, transforming
+ /// the progressRatio it reports (between 0 and 1) to between progressRatio and progressRatio+stageRatio
+ ///
+ /// The child algorithm.
+ /// The fraction of time the child algorithm will take of the parent algorithm.
+ protected void StartListenToProgress(AlgorithmBase childAlgorithm, double stageRatio)
+ {
+ ValidateArg.IsNotNull(childAlgorithm, "childAlgorithm");
+ childAlgorithm.ProgressChanged += NotifyProgressChanged;
+ stageStartRatio = progressRatio;
+ stageEndRatio = progressRatio + stageRatio;
+ }
+
+ ///
+ /// When the sub-algorithm is completed this reverts us to listening to local progress
+ ///
+ ///
+ protected void StopListenToProgress(AlgorithmBase childAlgorithm)
+ {
+ ValidateArg.IsNotNull(childAlgorithm, "childAlgorithm");
+ childAlgorithm.ProgressChanged -= NotifyProgressChanged;
+ progressRatio = stageEndRatio;
+ stageStartRatio = 0;
+ stageEndRatio = 1;
+ }
+
+ ///
+ /// Runs the child algorithm and listens for progress changes.
+ ///
+ protected void RunChildAlgorithm(AlgorithmBase childAlgorithm, double stageRatio)
+ {
+ ValidateArg.IsNotNull(childAlgorithm, "childAlgorithm");
+ try
+ {
+ StartListenToProgress(childAlgorithm, stageRatio);
+ childAlgorithm.Run();
+ }
+ finally
+ {
+ StopListenToProgress(childAlgorithm);
+ }
+ }
+
+ ///
+ /// Call this to report 100% progress if your algorithm finishes early
+ ///
+ protected void ProgressComplete()
+ {
+ ThrowIfCanceled();
+ if (progressRatio != 1.0)
+ {
+ this.progressRatio = 1.0;
+ this.NotifyProgressChanged(this, new ProgressChangedEventArgs(progressRatio));
+ }
+ }
+
+ ///
+ /// Checks for cancel, increases progress, and notifies any listeners of progress.
+ /// Call whenever the algorithm makes progress.
+ ///
+ protected void ProgressStep()
+ {
+ this.ProgressSteps(1);
+ }
+
+ ///
+ /// Checks for cancel, increases progress, and notifies any listeners of progress.
+ /// Call whenever the algorithm makes progress.
+ ///
+ protected void ProgressSteps(int stepsTaken)
+ {
+ ThrowIfCanceled();
+ localStepCount += stepsTaken;
+
+ if (localProgressSpecified)
+ {
+ progressRatio = progressRatio + (localProgressStepSize * stepsTaken);
+ }
+ else
+ {
+ // If no local step count was specified, use a progress bar
+ // which never reaches 100% and gets slower as time goes on.
+ // It approaches 85% (instead of 100%) so it never appears to be completed.
+ const double Limit = 0.85;
+ const double HalfLife = 50.0; // 50 was chosen as the default since it represents one ProgressStep per percentage at the start (i.e. 50 steps reach 50%)
+ const double Numerator = Limit * HalfLife;
+ progressRatio = Limit - (Numerator / (HalfLife + localStepCount));
+ }
+
+ // Round up to 100% if the sum of steps doesn't quite reach it.
+ if (Math.Round(progressRatio, 6) == 1.0)
+ {
+ progressRatio = 1.0;
+ }
+
+ this.NotifyProgressChanged(this, new ProgressChangedEventArgs(progressRatio));
+ }
+
+ ///
+ /// notifies whenever progress is made by the algorithm. ProgressChangedEventArgs report
+ /// 0 < progressRatio <= 1. Progress from sub-algorithms are converted to the correct ratio
+ /// of the local progress
+ ///
+ private void NotifyProgressChanged(object sender, ProgressChangedEventArgs args)
+ {
+ if (ProgressChanged != null)
+ {
+ double stageRatio = stageEndRatio - stageStartRatio;
+ double stageProgress = stageRatio * args.RatioComplete;
+ ProgressChanged(this, new ProgressChangedEventArgs(stageStartRatio + stageProgress));
+ }
+ }
+
+ #endregion Progress Logic
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/AlgorithmBase.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/AlgorithmBase.cs.meta
new file mode 100644
index 000000000..7c6da9b74
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/AlgorithmBase.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8f0e154226de7c44d93b02694d82946a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/CancelToken.cs b/Assets/Scripts/NIH/MSAGL/Core/CancelToken.cs
new file mode 100644
index 000000000..2404cea13
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/CancelToken.cs
@@ -0,0 +1,57 @@
+using System;
+
+#if PPC
+using System.Threading;
+using System.Threading.Tasks;
+#endif
+
+namespace Microsoft.Msagl.Core {
+
+ ///
+ /// a place holder for the cancelled flag
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
+ public class CancelToken {
+
+ volatile bool canceled;
+
+ ///
+ /// Set this flag to true when you want to cancel the layout.
+ ///
+ public bool Canceled {
+ get { return canceled; }
+ set {
+ canceled = value;
+#if PPC
+ if (canceled)
+ {
+ cancellationTokenSource.Cancel();
+ }
+#endif
+ }
+ }
+ ///
+ /// throws is the layout has been cancelled
+ ///
+ public void ThrowIfCanceled()
+ {
+#if PPC
+ cancellationTokenSource.Token.ThrowIfCancellationRequested();
+#endif
+ if (this.Canceled)
+ throw new OperationCanceledException();
+ }
+
+#if PPC
+ CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
+
+ ///
+ /// Pass this cancelation token for use in cancellable parallel for-each operations
+ ///
+ public CancellationToken CancellationToken
+ {
+ get { return cancellationTokenSource.Token; }
+ }
+#endif
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/CancelToken.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/CancelToken.cs.meta
new file mode 100644
index 000000000..7ef1d5001
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/CancelToken.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 838ad0009928e604e94e5580a15ada26
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures.meta
new file mode 100644
index 000000000..fd0a920ff
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 500f7d7386669de4abbd734ecd9ae3c3
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/BinaryHeapPriorityQueue.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/BinaryHeapPriorityQueue.cs
new file mode 100644
index 000000000..71dd3deeb
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/BinaryHeapPriorityQueue.cs
@@ -0,0 +1,130 @@
+using System;
+
+namespace Microsoft.Msagl.Core.DataStructures {
+
+
+ ///
+ /// A priority queue based on the binary heap algorithm
+ ///
+ internal class BinaryHeapPriorityQueue {
+ //indexing for A starts from 1
+
+ readonly int[] _heap;//array of heap elements
+ readonly int[] _reverse_heap; // the map from [0,..., n-1] to their places in heap
+ ///
+ /// the array of priorities
+ ///
+ readonly double[] _priors;
+ internal int Count { get { return heapSize; } }
+ int heapSize;
+ ///
+ /// the constructor
+ /// we suppose that all integers inserted into the queue will be less then n
+ ///
+ /// it is the number of different integers that will be inserted into the queue
+ internal BinaryHeapPriorityQueue(int n) {
+ _priors = new double[n];
+ _heap = new int[n + 1];//because indexing for A starts from 1
+ _reverse_heap = new int[n];
+ }
+
+
+ void SwapWithParent(int i) {
+ int parent = _heap[i >> 1];
+ PutAtI(i >> 1, _heap[i]);
+ PutAtI(i, parent);
+ }
+
+ internal void Enqueue(int o, double priority) {
+ heapSize++;
+ int i = heapSize;
+ _priors[o] = priority;
+ PutAtI(i, o);
+ while (i > 1 && _priors[_heap[i >> 1]] > priority) {
+ SwapWithParent(i);
+ i >>= 1;
+ }
+ }
+
+ void PutAtI(int i, int h) {
+ _heap[i] = h;
+ _reverse_heap[h] = i;
+ }
+
+ ///
+ /// return the first element of the queue and removes it from the queue
+ ///
+ ///
+ internal int Dequeue() {
+ if (heapSize == 0)
+ throw new InvalidOperationException();
+ int ret = _heap[1];
+ if (heapSize > 1)
+ {
+ PutAtI(1, _heap[heapSize]);
+ int i = 1;
+ while (true)
+ {
+ int smallest = i;
+ int l = i << 1;
+
+ if (l <= heapSize && _priors[_heap[l]] <_priors[ _heap[i]])
+ smallest = l;
+
+ int r = l + 1;
+
+ if (r <= heapSize && _priors[_heap[r]] < _priors[_heap[smallest]])
+ smallest = r;
+
+ if (smallest != i)
+ SwapWithParent(smallest);
+ else
+ break;
+
+ i = smallest;
+
+ }
+ }
+ heapSize--;
+ return ret;
+ }
+
+ ///
+ /// sets the object priority to c
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal void DecreasePriority(int o, double newPriority) {
+
+ //System.Diagnostics.Debug.WriteLine("delcrease "+ o.ToString()+" to "+ newPriority.ToString());
+
+ _priors[o] = newPriority;
+ int i = _reverse_heap[o];
+ while (i > 1) {
+ if (_priors[_heap[i]] < _priors[_heap[i >> 1]])
+ SwapWithParent(i);
+ else
+ break;
+ i >>= 1;
+ }
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ public static void Test() {
+ var q = new BinaryHeapPriorityQueue(10);
+ q.Enqueue(2, 2);
+ q.Enqueue(1, 1);
+ q.Enqueue(9, 9);
+ q.Enqueue(8, 8);
+ q.Enqueue(5, 5);
+ q.Enqueue(3, 3);
+ q.Enqueue(4, 4);
+ q.Enqueue(7, 7);
+ q.Enqueue(6, 6);
+ q.Enqueue(0, 0);
+ for (int i = 0; i < 10; i++)
+ Console.WriteLine(q.Dequeue());
+ }
+ }
+}
+
+
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/BinaryHeapPriorityQueue.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/BinaryHeapPriorityQueue.cs.meta
new file mode 100644
index 000000000..910ef65a8
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/BinaryHeapPriorityQueue.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a62428fef9112304eb0439efc749dc37
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/BinaryHeapWithComparer.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/BinaryHeapWithComparer.cs
new file mode 100644
index 000000000..7a5bcf6e7
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/BinaryHeapWithComparer.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+
+namespace Microsoft.Msagl.Core.DataStructures {
+ ///
+ /// A priority queue based on the binary heap algorithm.
+ /// This class needs a comparer object to compare elements of the queue.
+ ///
+ ///
+ internal class BinaryHeapWithComparer {
+ const int initialHeapCapacity = 16;
+ T[] A; //array of the heap elems starting at A[1]
+
+ int heapSize = 0;
+
+ internal void Enqueue(T element) {
+ if (heapSize == A.Length - 1) {
+ var newA = new T[A.Length*2];
+ Array.Copy(A, 1, newA, 1, heapSize);
+ A = newA;
+ }
+
+ int i = heapSize + 1;
+ A[i] = element;
+ heapSize++;
+ int j = i >> 1;
+ T parent, son;
+ while (i > 1 && Less(son = A[i], parent = A[j])) {
+ A[j] = son;
+ A[i] = parent;
+ i = j;
+ j = i >> 1;
+ }
+ }
+
+
+ internal T Dequeue() {
+ if (heapSize < 1)
+ throw new InvalidOperationException();
+ T ret = A[1];
+
+ T candidate = A[heapSize];
+
+ heapSize--;
+
+ ChangeMinimum(candidate);
+
+ return ret;
+ }
+
+ internal void ChangeMinimum(T candidate) {
+ A[1] = candidate;
+
+ int j = 1;
+ int i = 2;
+ bool done = false;
+ while (i < heapSize && !done) {
+ done = true;
+ //both sons exist
+ T leftSon = A[i];
+ T rigthSon = A[i + 1];
+ int compareResult = comparer.Compare(leftSon, rigthSon);
+ if (compareResult < 0) {
+ //left son is the smallest
+ if (comparer.Compare(leftSon, candidate) < 0) {
+ A[j] = leftSon;
+ A[i] = candidate;
+ done = false;
+ j = i;
+ i = j << 1;
+ }
+ } else {
+ //right son in not the greatest
+ if (comparer.Compare(rigthSon, candidate) < 0) {
+ A[j] = rigthSon;
+ A[i + 1] = candidate;
+ done = false;
+ j = i + 1;
+ i = j << 1;
+ }
+ }
+ }
+
+ if (i == heapSize) {
+ //can we do one more step:
+ T leftSon = A[i];
+ if (comparer.Compare(leftSon, candidate) < 0) {
+ A[j] = leftSon;
+ A[i] = candidate;
+ }
+ }
+ }
+
+ internal int Count {
+ get { return heapSize; }
+ }
+
+ internal bool Less(T a, T b) {
+ return comparer.Compare(a, b) < 0;
+ }
+
+ IComparer comparer;
+
+ internal BinaryHeapWithComparer(IComparer comparer) {
+ A = new T[initialHeapCapacity + 1];
+ this.comparer = comparer;
+ }
+
+#if TEST_MSAGL
+
+ public override string ToString() {
+ int i = 1;
+ return "{" + Print(i) + "}";
+ }
+
+ string Print(int i) {
+ if (2*i + 1 <= heapSize)
+ return String.Format(CultureInfo.InvariantCulture, "({0}->{1},{2})", A[i], A[i*2], A[i*2 + 1]) +
+ Print(i*2) + Print(i*2 + 1);
+ if (2*i == heapSize)
+ return String.Format(CultureInfo.InvariantCulture, "({0}->{1}", A[i], A[i*2]);
+ if (i == heapSize && i == 1)
+ return " " + A[i].ToString() + " ";
+ return "";
+ }
+#endif
+
+
+ public T GetMinimum() {
+ return A[1];
+ }
+
+#if TEST_MSAGL
+ //internal void UpdateMinimum() {
+ // throw new NotImplementedException();
+ //}
+#endif
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/BinaryHeapWithComparer.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/BinaryHeapWithComparer.cs.meta
new file mode 100644
index 000000000..83d424bb8
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/BinaryHeapWithComparer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 78dc14a0e35a20145a57e0314da9ab9b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/CollectionUtilities.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/CollectionUtilities.cs
new file mode 100644
index 000000000..b4942beb0
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/CollectionUtilities.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+namespace Microsoft.Msagl.Core.DataStructures {
+ ///
+ /// various utilities for collections
+ ///
+ public class CollectionUtilities {
+ ///
+ /// Add value to dictionary
+ ///
+ internal static void AddToMap(Dictionary dictionary, T key, TS value)
+ where TC : ICollection, new() {
+ TC tc;
+ if (!dictionary.TryGetValue(key, out tc))
+ dictionary[key] = tc = new TC();
+
+ tc.Add(value);
+ }
+
+ ///
+ /// Remove value from dictionary
+ ///
+ [SuppressMessage("Microsoft.Naming", "CA1715:IdentifiersShouldHaveCorrectPrefix", MessageId = "T"),
+ SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "S"),
+ SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+ internal static void RemoveFromMap(Dictionary dictionary, T key, TS value)
+ where TC : ICollection {
+ var tc = dictionary[key];
+ tc.Remove(value);
+ if (tc.Count == 0)
+ dictionary.Remove(key);
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/CollectionUtilities.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/CollectionUtilities.cs.meta
new file mode 100644
index 000000000..3643eba77
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/CollectionUtilities.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6e8b582a3d0c32a41bc4eb7f0db98f96
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericBinaryHeapPriorityQueue.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericBinaryHeapPriorityQueue.cs
new file mode 100644
index 000000000..8160fcb3a
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericBinaryHeapPriorityQueue.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.Msagl.Core.DataStructures {
+
+
+ ///
+ /// A generic version priority queue based on the binary heap algorithm where
+ /// the priority of each element is passed as a parameter.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
+ public class GenericBinaryHeapPriorityQueue : IEnumerable {
+
+ const int InitialHeapCapacity = 16;
+
+
+ //indexing for A starts from 1
+
+ //[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ //internal void Clear()
+ //{
+ // if(heapSize>0)
+ // {
+ // for(int i=0;i[] A;//array of heap elements
+ // ReSharper restore InconsistentNaming
+
+ ///
+ /// it is a mapping from queue elements and their correspondent HeapElements
+ ///
+ readonly Dictionary> cache;
+ internal int Count { get { return heapSize; } }
+ int heapSize;
+
+ internal bool ContainsElement(T key) {
+ return cache.ContainsKey(key);
+ }
+
+
+ internal GenericBinaryHeapPriorityQueue() {
+ cache = new Dictionary>();
+ A = new GenericHeapElement[InitialHeapCapacity + 1];
+ }
+
+
+ void SwapWithParent(int i) {
+ var parent = A[i >> 1];
+
+ PutAtI(i >> 1, A[i]);
+ PutAtI(i, parent);
+ }
+
+
+
+ internal void Enqueue(T element, double priority) {
+ if (heapSize == A.Length - 1) {
+ var newA = new GenericHeapElement[A.Length * 2];
+ Array.Copy(A, 1, newA, 1, heapSize);
+ A = newA;
+ }
+
+ heapSize++;
+ int i = heapSize;
+ GenericHeapElement h;
+
+ A[i] = cache[element] = h = new GenericHeapElement(i, priority, element);
+ while (i > 1 && A[i >> 1].priority.CompareTo(priority) > 0) {
+ SwapWithParent(i);
+ i >>= 1;
+ }
+ }
+
+ internal bool IsEmpty() {
+ return heapSize == 0;
+ }
+
+ void PutAtI(int i, GenericHeapElement h) {
+ A[i] = h;
+ h.indexToA = i;
+ }
+
+ /////
+ ///// return the first element of the queue without removing it
+ /////
+ /////
+ //[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ //internal int Peek() {
+ // return A[1].v;
+ //}
+
+ internal T Dequeue() {
+ if (heapSize == 0)
+ throw new InvalidOperationException();
+
+ var ret = A[1].v;
+
+ MoveQueueOneStepForward(ret);
+
+ return ret;
+
+ }
+
+ internal T Dequeue(out double priority) {
+ if (heapSize == 0) throw new InvalidOperationException();
+
+ var ret = A[1].v;
+ priority = A[1].priority;
+ MoveQueueOneStepForward(ret);
+ return ret;
+ }
+
+ void MoveQueueOneStepForward(T ret) {
+ cache.Remove(ret);
+ PutAtI(1, A[heapSize]);
+ int i = 1;
+ while (true) {
+ int smallest = i;
+ int l = i << 1;
+
+ if (l <= heapSize && A[l].priority.CompareTo(A[i].priority) < 0)
+ smallest = l;
+
+ int r = l + 1;
+
+ if (r <= heapSize && A[r].priority.CompareTo(A[smallest].priority) < 0)
+ smallest = r;
+
+ if (smallest != i)
+ SwapWithParent(smallest);
+ else
+ break;
+
+ i = smallest;
+
+ }
+
+ heapSize--;
+ }
+
+ ///
+ /// sets the object priority to c
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal void DecreasePriority(T element, double newPriority)
+ {
+ GenericHeapElement h;
+ //ignore the element if it is not in the queue
+ if (!cache.TryGetValue(element, out h)) return;
+
+ //var h = cache[element];
+ h.priority = newPriority;
+ int i = h.indexToA;
+ while (i > 1) {
+ if (A[i].priority.CompareTo(A[i >> 1].priority) < 0)
+ SwapWithParent(i);
+ else
+ break;
+ i >>= 1;
+ }
+ }
+
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ // public static void Test() {
+ // Timer nm = new Timer();
+ // var bound = 1000000;
+ // nm.Start();
+ // {
+ // var q = new BinaryHeapPriorityQueue(bound + 1);
+ // Random random = new Random();
+ // for (int i = 0; i < bound; i++) {
+ // q.Enqueue(i, random.NextDouble());
+ // }
+ // for (int i = 0; i < bound; i++) {
+ // q.DecreasePriority(i, -random.NextDouble());
+ // }
+ //
+ // while (q.IsEmpty() == false) {
+ // q.Dequeue();
+ // }
+ //
+ // }
+ // nm.Stop();
+ // Console.WriteLine(nm.Duration);
+ // nm.Start();
+ // {
+ // var q = new GenericBinaryHeapPriorityQueue();
+ // Random random = new Random();
+ // for (int i = 0; i < bound; i++) {
+ // q.Enqueue(i, random.NextDouble());
+ // }
+ // for (int i = 0; i < bound; i++) {
+ // q.DecreasePriority(i, -random.NextDouble());
+ // }
+ //
+ // while (q.IsEmpty() == false) {
+ // q.Dequeue();
+ // }
+ //
+ // }
+ // nm.Stop();
+ // Console.WriteLine(nm.Duration);
+ //}
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ public static void Test() {
+ var q = new GenericBinaryHeapPriorityQueue();
+ q.Enqueue(2, 2);
+ q.Enqueue(1, 1);
+ q.Enqueue(9, 9);
+ q.Enqueue(8, 8);
+ q.Enqueue(5, 5);
+ q.Enqueue(3, 3);
+ q.Enqueue(4, 4);
+ q.Enqueue(7, 7);
+ q.Enqueue(6, 6);
+ q.Enqueue(0, 0);
+
+ q.DecreasePriority(4, 2.5);
+
+ while (q.IsEmpty() == false)
+ Console.WriteLine(q.Dequeue());
+
+
+ }
+ ///
+ /// enumerator
+ ///
+ ///
+ public IEnumerator GetEnumerator() {
+ for (int i = 1; i <= heapSize; i++)
+ yield return A[i].v;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public T Peek(out double priority) {
+ if (Count == 0) {
+ priority = 0.0;
+ return default(T);
+ }
+ priority = A[1].priority;
+ return A[1].v;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ for (int i = 1; i <= heapSize; i++)
+ yield return A[i].v;
+ }
+#if TEST_MSAGL
+ ///
+ ///
+ ///
+ ///
+ public override string ToString() {
+ StringBuilder sb=new StringBuilder();
+ foreach (var i in this)
+ sb.Append(i + ",");
+ return sb.ToString();
+ }
+
+#endif
+ }
+}
+
+
+
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericBinaryHeapPriorityQueue.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericBinaryHeapPriorityQueue.cs.meta
new file mode 100644
index 000000000..ab47c1f2f
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericBinaryHeapPriorityQueue.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7e122f1c5e2b7ce4e91aa5792a19a862
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericBinaryHeapPriorityQueueWithTimestamp.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericBinaryHeapPriorityQueueWithTimestamp.cs
new file mode 100644
index 000000000..b454b293f
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericBinaryHeapPriorityQueueWithTimestamp.cs
@@ -0,0 +1,270 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.Msagl.Core.DataStructures {
+
+ // TODO: Move and expand the commented tests below into PriorityQueueTests unit tests for all 3 PQ classes.
+ // TODO: Reduce duplication with GenericBinaryHeapPriorityQueue by breaking most functionality out into a
+ // GenericBinaryHeapPriorityQueue class and using THeapElement.ComparePriority, keeping only Enqueue
+ // and DecreasePriority in the derived classes as these must know about timestamp.
+
+ ///
+ /// A generic version priority queue based on the binary heap algorithm where
+ /// the priority of each element is passed as a parameter and priority ties are broken by timestamp.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
+ public class GenericBinaryHeapPriorityQueueWithTimestamp : IEnumerable {
+
+ const int InitialHeapCapacity = 16;
+
+
+ //indexing for A starts from 1
+
+ //[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ //internal void Clear()
+ //{
+ // if(heapSize>0)
+ // {
+ // for(int i=0;i[] A;//array of heap elements
+ // ReSharper restore InconsistentNaming
+
+ UInt64 timestamp;
+ UInt64 NextTimestamp { get { return ++this.timestamp; } }
+
+ ///
+ /// it is a mapping from queue elements and their correspondent HeapElements
+ ///
+ readonly Dictionary> cache;
+ internal int Count { get { return heapSize; } }
+ int heapSize;
+
+ internal GenericBinaryHeapPriorityQueueWithTimestamp() {
+ cache = new Dictionary>();
+ A = new GenericHeapElementWithTimestamp[InitialHeapCapacity + 1];
+ }
+
+
+ void SwapWithParent(int i) {
+ var parent = A[i >> 1];
+
+ PutAtI(i >> 1, A[i]);
+ PutAtI(i, parent);
+ }
+
+ internal void Enqueue(T element, double priority) {
+ if (heapSize == A.Length - 1) {
+ var newA = new GenericHeapElementWithTimestamp[A.Length * 2];
+ Array.Copy(A, 1, newA, 1, heapSize);
+ A = newA;
+ }
+
+ heapSize++;
+ int i = heapSize;
+ GenericHeapElementWithTimestamp h;
+
+ A[i] = cache[element] = h = new GenericHeapElementWithTimestamp(i, priority, element, this.NextTimestamp);
+ while (i > 1 && A[i >> 1].ComparePriority(A[i]) > 0) {
+ SwapWithParent(i);
+ i >>= 1;
+ }
+ System.Diagnostics.Debug.Assert(A[i] == h);
+ A[i] = h;
+
+ }
+
+ void PutAtI(int i, GenericHeapElementWithTimestamp h) {
+ A[i] = h;
+ h.indexToA = i;
+ }
+
+#if TEST_MSAGL
+ ///
+ /// Gets the next element to be dequeued, without dequeueing it.
+ ///
+ internal T Peek() {
+ if (heapSize == 0) {
+ throw new InvalidOperationException();
+ }
+ return A[1].v;
+ }
+
+ ///
+ /// Gets the timestamp of the next element to be dequeued, without dequeueing it.
+ ///
+ internal UInt64 PeekTimestamp() {
+ if (heapSize == 0) {
+ throw new InvalidOperationException();
+ }
+ return A[1].Timestamp;
+ }
+#endif // TEST_MSAGL
+
+ internal T Dequeue() {
+ if (heapSize == 0)
+ throw new InvalidOperationException();
+
+ var ret = A[1].v;
+
+ MoveQueueOneStepForward(ret);
+
+ return ret;
+
+ }
+
+ void MoveQueueOneStepForward(T ret) {
+ cache.Remove(ret);
+ PutAtI(1, A[heapSize]);
+ int i = 1;
+ while (true) {
+ int smallest = i;
+ int l = i << 1;
+
+ if (l <= heapSize && A[l].ComparePriority(A[i]) < 0)
+ smallest = l;
+
+ int r = l + 1;
+
+ if (r <= heapSize && A[r].ComparePriority(A[smallest]) < 0)
+ smallest = r;
+
+ if (smallest != i)
+ SwapWithParent(smallest);
+ else
+ break;
+
+ i = smallest;
+
+ }
+
+ heapSize--;
+ }
+
+ ///
+ /// sets the object priority to c
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal void DecreasePriority(T element, double newPriority)
+ {
+ GenericHeapElementWithTimestamp h;
+ //ignore the element if it is not in the queue
+ if (!cache.TryGetValue(element, out h)) return;
+
+ //var h = cache[element];
+ h.priority = newPriority;
+ int i = h.indexToA;
+ while (i > 1) {
+ if (A[i].ComparePriority(A[i >> 1]) < 0)
+ SwapWithParent(i);
+ else
+ break;
+ i >>= 1;
+ }
+ }
+
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ // public static void Test() {
+ // Timer nm = new Timer();
+ // var bound = 1000000;
+ // nm.Start();
+ // {
+ // var q = new BinaryHeapPriorityQueue(bound + 1);
+ // Random random = new Random();
+ // for (int i = 0; i < bound; i++) {
+ // q.Enqueue(i, random.NextDouble());
+ // }
+ // for (int i = 0; i < bound; i++) {
+ // q.DecreasePriority(i, -random.NextDouble());
+ // }
+ //
+ // while (q.IsEmpty() == false) {
+ // q.Dequeue();
+ // }
+ //
+ // }
+ // nm.Stop();
+ // Console.WriteLine(nm.Duration);
+ // nm.Start();
+ // {
+ // var q = new GenericBinaryHeapPriorityQueue();
+ // Random random = new Random();
+ // for (int i = 0; i < bound; i++) {
+ // q.Enqueue(i, random.NextDouble());
+ // }
+ // for (int i = 0; i < bound; i++) {
+ // q.DecreasePriority(i, -random.NextDouble());
+ // }
+ //
+ // while (q.IsEmpty() == false) {
+ // q.Dequeue();
+ // }
+ //
+ // }
+ // nm.Stop();
+ // Console.WriteLine(nm.Duration);
+ //}
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ public static void Test() {
+ var q = new GenericBinaryHeapPriorityQueue();
+ q.Enqueue(2, 2);
+ q.Enqueue(1, 1);
+ q.Enqueue(9, 9);
+ q.Enqueue(8, 8);
+ q.Enqueue(5, 5);
+ q.Enqueue(3, 3);
+ q.Enqueue(4, 4);
+ q.Enqueue(7, 7);
+ q.Enqueue(6, 6);
+ q.Enqueue(0, 0);
+
+ q.DecreasePriority(4, 2.5);
+
+ while (q.IsEmpty() == false)
+ Console.WriteLine(q.Dequeue());
+
+
+ }
+ ///
+ /// enumerator
+ ///
+ ///
+ public IEnumerator GetEnumerator() {
+ for (int i = 1; i <= heapSize; i++)
+ yield return A[i].v;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ for (int i = 1; i <= heapSize; i++)
+ yield return A[i].v;
+ }
+#if TEST_MSAGL
+ ///
+ ///
+ ///
+ ///
+ public override string ToString() {
+ var sb=new StringBuilder();
+ foreach (var i in this)
+ sb.Append(i + ",");
+ return sb.ToString();
+ }
+
+#endif
+ }
+}
+
+
+
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericBinaryHeapPriorityQueueWithTimestamp.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericBinaryHeapPriorityQueueWithTimestamp.cs.meta
new file mode 100644
index 000000000..b25b29163
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericBinaryHeapPriorityQueueWithTimestamp.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a84290ac5fe1bdb4f9bd404eb9dfc396
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericHeapElement.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericHeapElement.cs
new file mode 100644
index 000000000..914a55aaa
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericHeapElement.cs
@@ -0,0 +1,12 @@
+namespace Microsoft.Msagl.Core.DataStructures {
+ class GenericHeapElement {
+ internal int indexToA;
+ internal double priority;
+ internal T v;//value
+ internal GenericHeapElement(int index, double priority, T v) {
+ indexToA = index;
+ this.priority = priority;
+ this.v = v;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericHeapElement.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericHeapElement.cs.meta
new file mode 100644
index 000000000..64e340d06
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericHeapElement.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: bc99aab509d5d5846b25d73984a920c1
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericHeapElementWithTimestamp.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericHeapElementWithTimestamp.cs
new file mode 100644
index 000000000..59d4bf378
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericHeapElementWithTimestamp.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Microsoft.Msagl.Core.DataStructures {
+ ///
+ /// A priority queue element that is compared by priority and timestamp
+ ///
+ internal class GenericHeapElementWithTimestamp : GenericHeapElement {
+ // Timestamp operates as a tiebreaker when priorities are equal.
+ internal UInt64 Timestamp { get; set; }
+
+ internal GenericHeapElementWithTimestamp(int index, double priority, T v, UInt64 timestamp)
+ : base(index, priority, v) {
+ this.Timestamp = timestamp;
+ }
+
+ internal int ComparePriority(GenericHeapElementWithTimestamp other) {
+ var cmp = base.priority.CompareTo(other.priority);
+
+ // Consider a more recent sequence to have a lower priority value (== higher priority)
+ // so reverse the direction of comparison. Note: Timestamp only applies in the case
+ // of tied priorities, so if it wraps, we'll have suboptimal processing and likely a different
+ // path in the event of ties, but will not fail.
+ return (cmp != 0) ? cmp : other.Timestamp.CompareTo(this.Timestamp);
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericHeapElementWithTimestamp.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericHeapElementWithTimestamp.cs.meta
new file mode 100644
index 000000000..3955945e1
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/GenericHeapElementWithTimestamp.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 91406a9d3ae7e79408f53eaac222784e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree.meta
new file mode 100644
index 000000000..75248d3cb
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2db8c0017da4d1e42a8df84b52235c5b
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/ComparerOnDelegate.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/ComparerOnDelegate.cs
new file mode 100644
index 000000000..3ef163d2d
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/ComparerOnDelegate.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Msagl.Core.DataStructures {
+ internal class ComparerOnDelegate : IComparer {
+ readonly Func comparer;
+
+ public ComparerOnDelegate(Func compare) {
+ comparer= compare;
+ }
+
+ public int Compare(T x, T y) {
+ return comparer(x, y);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/ComparerOnDelegate.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/ComparerOnDelegate.cs.meta
new file mode 100644
index 000000000..ae0f54347
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/ComparerOnDelegate.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 808dfa443622ca94d9f1a9e8cd9c13be
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/DefaultComperer.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/DefaultComperer.cs
new file mode 100644
index 000000000..52ad0d112
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/DefaultComperer.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Msagl.Core.DataStructures {
+ internal class DefaultComperer: IComparer {
+ public int Compare(T x, T y) {
+ return ((IComparable) x).CompareTo(y);
+ }
+
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/DefaultComperer.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/DefaultComperer.cs.meta
new file mode 100644
index 000000000..384cdaaac
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/DefaultComperer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9bcd9db072a6903479507b3a95b5d657
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBColor.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBColor.cs
new file mode 100644
index 000000000..06e0ed33b
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBColor.cs
@@ -0,0 +1,8 @@
+namespace Microsoft.Msagl.Core.DataStructures {
+
+ internal enum RBColor {
+ Red,
+ Black
+ }
+
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBColor.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBColor.cs.meta
new file mode 100644
index 000000000..c8b2d27c6
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBColor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ca5c296797071684880042932e6df5ee
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBNode.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBNode.cs
new file mode 100644
index 000000000..d2e441059
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBNode.cs
@@ -0,0 +1,29 @@
+using System;
+namespace Microsoft.Msagl.Core.DataStructures {
+
+
+#if TEST_MSAGL
+ [Serializable]
+#endif
+ internal class RBNode {
+
+ internal RBColor color;
+ internal T Item;
+ internal RBNode parent, left, right;
+ internal RBNode(RBColor color) { this.color = color; }
+ internal RBNode(RBColor color, T item, RBNode p, RBNode left, RBNode right) {
+ this.color = color;
+ this.parent = p;
+ this.left = left;
+ this.right = right;
+ this.Item = item;
+ }
+
+ ///
+ ///
+ ///
+ public override string ToString() {
+ return Item.ToString();
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBNode.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBNode.cs.meta
new file mode 100644
index 000000000..9c56b9b25
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBNode.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: eb455e0993b23bc45814a25cd54d4a50
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBTree.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBTree.cs
new file mode 100644
index 000000000..c8d7430a6
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBTree.cs
@@ -0,0 +1,483 @@
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Msagl.Core.DataStructures {
+
+ //public class TestRB :IComparer{
+ // public static void Test() {
+ // var tree = new RBTree(new TestRB());
+ // tree.insert(1);
+ // tree.insert(6);
+ // tree.insert(8);
+ // tree.insert(11);
+ // tree.insert(13);
+ // tree.insert(17);
+ // tree.insert(15);
+ // tree.insert(25);
+ // tree.insert(22);
+ // tree.insert(27);
+ // tree.insert(16);
+
+ // var t = tree.findLast(delegate(int i) { return i <= 15.5; });
+
+ // }
+
+
+ // #region IComparer Members
+
+ // public int Compare(int x, int y) {
+ // return x - y;
+ // }
+
+ // #endregion
+ //}
+
+#if TEST_MSAGL
+ [Serializable]
+#endif
+ internal class RbTree : IEnumerable {
+
+ ///
+ /// find the first, minimal, node in the tree such that predicate holds
+ ///
+ /// Has to be monotone in the sense that if it holds for t then it holds for any t' greater or equal than t
+ /// so the predicate values have a form (false, false, ..., false, true, true, ..., true)
+ ///
+ /// the first node where predicate holds or null
+ internal RBNode FindFirst(Func predicate) {
+ return FindFirst(root, predicate);
+ }
+
+ internal RbTree() {
+ comparer = new DefaultComperer();
+ root = nil = new RBNode(RBColor.Black);
+ }
+
+ RBNode FindFirst(RBNode n, Func p) {
+ if (n == nil)
+ return null;
+ RBNode good = null;
+ while (n != nil)
+ n = p(n.Item) ? (good = n).left : n.right;
+
+ return good;
+ }
+
+ ///
+ /// find the last, maximal, node in the tree such that predicate holds
+ ///
+ /// Has to be monotone in the sense that if it holds for t then it holds for any t' less or equal than t
+ /// so the predicate values on the tree have a form (true, true, ..., true, false, false, ..., false)
+ ///
+ /// the last node where predicate holds or null
+ internal RBNode FindLast(Func predicate) {
+ return FindLast(root, predicate);
+ }
+
+ RBNode FindLast(RBNode n, Func p) {
+ if (n == nil)
+ return null;
+ RBNode good = null;
+ while (n != nil)
+ n = p(n.Item) ? (good = n).right : n.left;
+
+ return good;
+ }
+
+
+ readonly IComparer comparer;
+
+ IComparer Comparer {
+ get { return comparer; }
+ }
+
+ public IEnumerator GetEnumerator() { return new RBTreeEnumerator(this); }
+
+ RBNode nil;
+
+ internal RBNode Nil { get { return nil; } }
+
+ RBNode root;
+ internal RBNode Root { get { return root; } }
+
+ internal RBNode Next(RBNode x) {
+ if (x.right != nil)
+ return TreeMinimum(x.right);
+ RBNode y = x.parent;
+ while (y != nil && x == y.right) {
+ x = y;
+ y = y.parent;
+ }
+ return ToNull(y);
+ }
+
+ RBNode ToNull(RBNode y) {
+ return y != nil ? y : null;
+ }
+
+ internal RBNode Previous(RBNode x) {
+ if (x.left != nil)
+ return TreeMaximum(x.left);
+ RBNode y = x.parent;
+ while (y != nil && x == y.left) {
+ x = y;
+ y = y.parent;
+ }
+ return ToNull(y);
+ }
+
+ RBNode TreeMinimum(RBNode x) {
+ while (x.left != nil)
+ x = x.left;
+ return ToNull(x);
+ }
+
+ internal RBNode TreeMinimum() {
+ return TreeMinimum(root);
+ }
+
+
+ RBNode TreeMaximum(RBNode x) {
+ while (x.right != nil)
+ x = x.right;
+ return ToNull(x);
+ }
+
+ internal RBNode TreeMaximum() {
+ return TreeMaximum(root);
+ }
+
+
+ //RBTree Clone() {
+ // RBTree clone = new RBTree(this.Comparer);
+ // foreach (T n in this) {
+ // clone.insert(n);
+ // }
+ // return clone;
+ //}
+
+
+ public override string ToString() {
+ string ret = "{";
+ int i = 0;
+ foreach (T p in this) {
+ ret += p.ToString();
+ if (i != count - 1) {
+ ret += ",";
+ }
+
+ i++;
+ }
+
+ return ret + "}";
+ }
+
+
+ internal RBNode DeleteSubtree(RBNode z) {
+ System.Diagnostics.Debug.Assert(z != nil);
+
+ RBNode y;
+ if (z.left == nil || z.right == nil) {
+ /* y has a nil node as a child */
+ y = z;
+ } else {
+ /* find tree successor with a nil node as a child */
+ y = z.right;
+ while (y.left != nil) y = y.left;
+ }
+
+ /* x is y's only child */
+ RBNode x = y.left != nil ? y.left : y.right;
+
+ x.parent = y.parent;
+ if (y.parent == nil)
+ root = x;
+ else {
+ if (y == y.parent.left)
+ y.parent.left = x;
+ else
+ y.parent.right = x;
+ }
+ if (y != z)
+ z.Item = y.Item;
+ if (y.color == RBColor.Black)
+ DeleteFixup(x);
+
+ // checkTheTree();
+
+ return ToNull(z);
+
+ }
+
+ int count;
+ public int Count { get { return count; } }
+
+ internal RBNode Remove(T i) {
+ RBNode n = Find(i);
+ if (n != null) {
+ count--;
+ return DeleteSubtree(n);
+ }
+ return null;
+ }
+
+ internal void DeleteNodeInternal(RBNode x) {
+ count--;
+ DeleteSubtree(x);
+ }
+
+ RBNode Find(RBNode x, T i) {
+ int compareResult;
+ while (x != nil && (compareResult = Comparer.Compare(i, x.Item)) != 0)
+ x = compareResult < 0 ? x.left : x.right;
+
+ return ToNull(x);
+ }
+
+ internal RBNode Find(T i) {
+ return Find(root, i);
+ }
+
+ void DeleteFixup(RBNode x) {
+ while (x != root && x.color == RBColor.Black) {
+ if (x == x.parent.left) {
+ RBNode w = x.parent.right;
+ if (w.color == RBColor.Red) {
+ w.color = RBColor.Black;
+ x.parent.color = RBColor.Red;
+ LeftRotate(x.parent);
+ w = x.parent.right;
+ }
+ if (w.left.color == RBColor.Black && w.right.color == RBColor.Black) {
+ w.color = RBColor.Red;
+ x = x.parent;
+ } else {
+ if (w.right.color == RBColor.Black) {
+ w.left.color = RBColor.Black;
+ w.color = RBColor.Red;
+ RightRotate(w);
+ w = x.parent.right;
+ }
+ w.color = x.parent.color;
+ x.parent.color = RBColor.Black;
+ w.right.color = RBColor.Black;
+ LeftRotate(x.parent);
+ x = root;
+ }
+ } else {
+ RBNode w = x.parent.left;
+ if (w.color == RBColor.Red) {
+ w.color = RBColor.Black;
+ x.parent.color = RBColor.Red;
+ RightRotate(x.parent);
+ w = x.parent.left;
+ }
+ if (w.right.color == RBColor.Black && w.left.color == RBColor.Black) {
+ w.color = RBColor.Red;
+ x = x.parent;
+ } else {
+ if (w.left.color == RBColor.Black) {
+ w.right.color = RBColor.Black;
+ w.color = RBColor.Red;
+ LeftRotate(w);
+ w = x.parent.left;
+ }
+ w.color = x.parent.color;
+ x.parent.color = RBColor.Black;
+ w.left.color = RBColor.Black;
+ RightRotate(x.parent);
+ x = root;
+ }
+ }
+ }
+ x.color = RBColor.Black;
+ }
+
+ internal bool IsEmpty() { return root == nil; }
+
+ RBNode TreeInsert(T z) {
+ var y = nil;
+ var x = root;
+ var compareRes = 0;
+ while (x != nil) {
+ y = x;
+#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=368
+ compareRes = Comparer.Compare(z, x.Item);
+ x = compareRes < 0 ? x.left : x.right;
+#else
+ x = (compareRes = Comparer.Compare(z, x.Item)) < 0 ? x.left : x.right;
+#endif
+ }
+
+ var nz = new RBNode(RBColor.Black, z, y, nil, nil);
+
+ if (y == nil)
+ root = nz;
+ else if (compareRes < 0)
+ y.left = nz;
+ else
+ y.right = nz;
+
+ return ToNull(nz);
+ }
+
+ void InsertPrivate(RBNode x) {
+ count++;
+ x.color = RBColor.Red;
+ while (x != root && x.parent.color == RBColor.Red) {
+ if (x.parent == x.parent.parent.left) {
+ RBNode y = x.parent.parent.right;
+ if (y.color == RBColor.Red) {
+ x.parent.color = RBColor.Black;
+ y.color = RBColor.Black;
+ x.parent.parent.color = RBColor.Red;
+ x = x.parent.parent;
+ } else {
+ if (x == x.parent.right) {
+ x = x.parent;
+ LeftRotate(x);
+ }
+ x.parent.color = RBColor.Black;
+ x.parent.parent.color = RBColor.Red;
+ RightRotate(x.parent.parent);
+ }
+ } else {
+ RBNode y = x.parent.parent.left;
+ if (y.color == RBColor.Red) {
+ x.parent.color = RBColor.Black;
+ y.color = RBColor.Black;
+ x.parent.parent.color = RBColor.Red;
+ x = x.parent.parent;
+ } else {
+ if (x == x.parent.left) {
+ x = x.parent;
+ RightRotate(x);
+ }
+ x.parent.color = RBColor.Black;
+ x.parent.parent.color = RBColor.Red;
+ LeftRotate(x.parent.parent);
+ }
+ }
+
+ }
+
+ root.color = RBColor.Black;
+
+ //checkTheTree();
+ }
+
+ internal RBNode Insert(T v) {
+ RBNode x = TreeInsert(v);
+ InsertPrivate(x);
+ return ToNull(x);
+ }
+
+ void LeftRotate(RBNode x) {
+ RBNode y = x.right;
+ x.right = y.left;
+ if (y.left != nil)
+ y.left.parent = x;
+ y.parent = x.parent;
+ if (x.parent == nil)
+ root = y;
+ else if (x == x.parent.left)
+ x.parent.left = y;
+ else
+ x.parent.right = y;
+
+ y.left = x;
+ x.parent = y;
+ }
+
+ void RightRotate(RBNode x) {
+ RBNode y = x.left;
+ x.left = y.right;
+ if (y.right != nil)
+ y.right.parent = x;
+ y.parent = x.parent;
+ if (x.parent == nil)
+ root = y;
+ else if (x == x.parent.right)
+ x.parent.right = y;
+ else
+ x.parent.left = y;
+
+ y.right = x;
+ x.parent = y;
+
+ }
+
+ internal RbTree(Func func) : this(new ComparerOnDelegate(func)) {}
+
+
+ internal RbTree(IComparer comparer) {
+ Clear();
+ this.comparer = comparer;
+ }
+
+ internal void Clear() {
+ root = nil = new RBNode(RBColor.Black);
+ }
+
+ //void checkTheTree() {
+ // int blacks = -1;
+ // checkBlacks(root, 0, ref blacks);
+ // checkColors(root);
+
+ //}
+
+
+
+ //void checkBlacks(RBNode node, int nnow, ref int blacks) {
+ // if (node != nil) {
+ // RBNode l = node.left, r = node.right;
+
+ // if (l != nil)
+ // if (Comparer.Compare(l.item,node.item) >= 0)
+ // throw new Exception("order");
+
+ // if (r != nil)
+ // if (Comparer.Compare(r.item,node.item) <= 0)
+ // throw new Exception("order");
+
+
+ // }
+
+ // if (node == nil) {
+ // if (blacks != nnow + 1 && blacks != -1)
+ // throw new Exception("blacks");
+
+ // blacks = nnow + 1;
+
+ // } else if (node.color == RBColor.Black) {
+ // checkBlacks(node.left, nnow + 1, ref blacks);
+ // checkBlacks(node.right, nnow + 1, ref blacks);
+ // } else {
+ // checkBlacks(node.left, nnow, ref blacks);
+ // checkBlacks(node.right, nnow, ref blacks);
+ // }
+
+
+ //}
+
+ //void checkColors(RBNode x) {
+ // if (x == nil)
+ // return;
+ // //property 3
+ // if (x.color == RBColor.Red)
+ // if (x.left.color == RBColor.Red || x.right.color == RBColor.Red)
+ // throw new Exception("colors");
+
+ // checkColors(x.left);
+ // checkColors(x.right);
+ //}
+
+ #region IEnumerable Members
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ return new RBTreeEnumerator(this);
+ }
+
+ #endregion
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBTree.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBTree.cs.meta
new file mode 100644
index 000000000..f99d755e5
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBTree.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a151171447e096b488f0330e292d99bb
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBTreeEnumerator.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBTreeEnumerator.cs
new file mode 100644
index 000000000..6f7f51544
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBTreeEnumerator.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Msagl.Core.DataStructures {
+ internal class RBTreeEnumerator : IEnumerator {
+ bool initialState;
+ RbTree tree;
+ RBNode c;
+ public T Current {
+ get { return c.Item; }
+ }
+ public void Reset() {
+ initialState = true;
+ }
+
+ public bool MoveNext() {
+ if (tree.IsEmpty())
+ return false;
+
+ if (initialState == true) {
+ initialState = false;
+ c = tree.TreeMinimum();
+ } else {
+ c = tree.Next(c);
+ }
+ return c != null;
+ }
+
+ internal RBTreeEnumerator(RbTree tree) {
+ this.tree = tree;
+ Reset();
+
+ }
+
+ #region IDisposable Members
+
+ public void Dispose() {
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
+
+ #region IEnumerator Members
+
+ object System.Collections.IEnumerator.Current {
+ get { return c.Item; }
+ }
+
+ #endregion
+ }
+
+
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBTreeEnumerator.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBTreeEnumerator.cs.meta
new file mode 100644
index 000000000..e4c607353
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RBTree/RBTreeEnumerator.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 395191aa1bdd41c49a6404fa3ab9b293
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RealNumberSpan.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RealNumberSpan.cs
new file mode 100644
index 000000000..5a4614bd2
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RealNumberSpan.cs
@@ -0,0 +1,43 @@
+using System;
+
+namespace Microsoft.Msagl.Core.DataStructures {
+ ///
+ /// this class behaves like one dimensional bounding box
+ ///
+ public class RealNumberSpan{
+ internal RealNumberSpan(){
+ IsEmpty = true;
+ }
+
+ internal bool IsEmpty { get; set; }
+
+ internal void AddValue(double x){
+ if(IsEmpty){
+ Min = Max = x;
+ IsEmpty = false;
+ } else if(x < Min)
+ Min = x;
+ else if(x > Max)
+ Max = x;
+ }
+
+ internal double Min { get; set; }
+ internal double Max { get; set; }
+ ///
+ ///
+ ///
+ public double Length{
+ get { return Max-Min; }
+ }
+#if TEST_MSAGL
+ ///
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object)")]
+ public override string ToString() {
+ return IsEmpty ? "empty" : String.Format("{0},{1}", Min, Max);
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RealNumberSpan.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RealNumberSpan.cs.meta
new file mode 100644
index 000000000..5471d3267
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/RealNumberSpan.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 02eb261364f08ad44855f1343b1c9b8b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/Set.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/Set.cs
new file mode 100644
index 000000000..96d4f6ede
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/Set.cs
@@ -0,0 +1,300 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.Msagl.Core.DataStructures {
+ ///
+ /// Implementation of Set.
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Set")]
+ public class Set : MarshalByRefObject, ICollection {
+ HashSet hashSet = new HashSet();
+ ///
+ /// inserts an element into the set
+ ///
+ ///
+ public void Insert(T element) {
+ hashSet.Add(element);
+#if SHARPKIT
+ UpdateHashKey();
+#endif
+ }
+ void System.Collections.Generic.ICollection.Add(T t) { Insert(t); }
+ ///
+ /// returns true when the set contains the element
+ ///
+ ///
+ ///
+ public bool Contains(T item) { return hashSet.Contains(item); }
+ ///
+ /// deletes the element from the set
+ ///
+ ///
+ public void Delete(T item) {
+ hashSet.Remove(item);
+#if SHARPKIT
+ UpdateHashKey();
+#endif
+ }
+ ///
+ /// deletes the element from the set
+ ///
+ ///
+ ///
+ public bool Remove(T item) {
+ var ret = hashSet.Remove(item);
+#if SHARPKIT
+ UpdateHashKey();
+#endif
+ return ret;
+ }
+ ///
+ /// returns the number elements in the set
+ ///
+ public int Count { get { return hashSet.Count; } }
+ ///
+ /// returns the set entities enumerator
+ ///
+ ///
+ public IEnumerator GetEnumerator() { return hashSet.GetEnumerator(); }
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ return hashSet.GetEnumerator();
+ }
+
+
+
+ ///
+ /// the read only flag
+ ///
+ public bool IsReadOnly { get { return false; } }
+
+ // public bool IsSynchronized {get {return table.IsSynchronized; }}
+
+ //public T SyncRoot {get {return table.SyncRoot;}}
+
+ ///
+ /// copies to an array
+ ///
+ ///
+ ///
+ public void CopyTo(T[] array, int arrayIndex) {
+ hashSet.CopyTo(array, arrayIndex);
+ }
+
+ ///
+ /// ToString
+ ///
+ ///
+ public override string ToString() {
+ string r = "{";
+ int i = 0;
+ foreach (T o in this) {
+ r += o.ToString();
+ i++;
+ if (i < Count)
+ r += ",";
+ }
+ r += "}";
+ return r;
+ }
+
+ ///
+ /// cleans the set
+ ///
+ public void Clear() {
+ this.hashSet.Clear();
+#if SHARPKIT
+ UpdateHashKey();
+#endif
+ }
+ ///
+ /// clones the set
+ ///
+ ///
+ public Set Clone() {
+ Set ret = new Set();
+ foreach (T i in this) {
+ ret.Insert(i);
+ }
+ return ret;
+ }
+
+ ///
+ /// constructor
+ ///
+ ///
+ public Set(IEnumerable enumerableCollection) {
+ ValidateArg.IsNotNull(enumerableCollection, "enumerableCollection");
+ foreach (T j in enumerableCollection)
+ this.Insert(j);
+#if SHARPKIT
+ UpdateHashKey();
+#endif
+ }
+
+ ///
+ /// an empty constructor
+ ///
+ public Set() {
+ }
+
+ ///
+ /// creates an array from the set elements
+ ///
+ ///
+ ///
+ public System.Array ToArray(System.Type type) {
+ System.Array ret = System.Array.CreateInstance(type, this.Count);
+ int i = 0;
+ foreach (T o in this) {
+ ret.SetValue(o, i++);
+ }
+ return ret;
+ }
+ ///
+ /// overloading plus operator
+ ///
+ ///
+ ///
+ ///
+ static public Set operator +(Set set0, Set set1) {
+ ValidateArg.IsNotNull(set1, "set1");
+ Set ret = new Set(set0);
+ foreach (T t in set1)
+ ret.Insert(t);
+ return ret;
+ }
+
+ ///
+ /// overloading plus operator
+ ///
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates")]
+ static public Set operator -(Set set0, Set set1) {
+ ValidateArg.IsNotNull(set1, "set1");
+ Set ret = new Set(set0);
+ foreach (T t in set1)
+ ret.Remove(t);
+ return ret;
+ }
+ ///
+ /// overloading the == operator
+ ///
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
+ static public bool operator ==(Set set0, Set set1) {
+ if ((object)set0 == null && (object)set1 == null)
+ return true;
+ if ((object)set0 == null && (object)set1 != null)
+ return false;
+
+ if ((object)set1 == null && (object)set0 != null)
+ return false;
+
+ foreach (T t in set0)
+ if (!set1.Contains(t))
+ return false;
+
+ foreach (T t in set1)
+ if (!set0.Contains(t))
+ return false;
+
+ return true;
+ }
+
+ ///
+ /// overloading the == operator
+ ///
+ ///
+ ///
+ ///
+ static public bool operator !=(Set set0, Set set1) {
+ return !(set0 == set1);
+ }
+ ///
+ /// intersection
+ ///
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates")]
+ static public Set operator *(Set set0, Set set1) {
+ ValidateArg.IsNotNull(set0, "set0");
+ ValidateArg.IsNotNull(set1, "set1");
+ return
+ new Set(set0.Count < set1.Count
+ ? set0.Where(a => set1.Contains(a))
+ : set1.Where(a => set0.Contains(a)));
+ }
+ ///
+ /// the equality
+ ///
+ /// the object to compare to
+ ///
+ public override bool Equals(object obj) {
+ Set set = obj as Set;
+ if (set == null)
+ return false;
+ return set == this;
+ }
+
+ ///
+ /// hash code
+ ///
+ ///
+ public override int GetHashCode() {
+ int ret = 0;
+ foreach (T t in this)
+ ret |= t.GetHashCode();
+ return ret;
+ }
+
+ ///
+ /// a friendly add, union, operator
+ ///
+ ///
+ ///
+ ///
+ public Set Add(Set set0, Set set1) {
+ return set0 + set1;
+ }
+
+ ///
+ /// inserts a range of elements into the set
+ ///
+ /// elements to insert
+ public void InsertRange(IEnumerable elements )
+ {
+ ValidateArg.IsNotNull(elements,"elements");
+ foreach (var element in elements)
+ Insert(element);
+ }
+
+ ///
+ /// checks the set is contained in the "otherSet"
+ ///
+ ///
+ ///
+ public bool IsContained(Set otherSet) { return this.All(p => otherSet.Contains(p)); }
+
+ ///
+ /// checks that this set conains the "otherSet"
+ ///
+ ///
+ ///
+ public bool Contains(Set otherSet) { return otherSet.All(p => Contains(p)); }
+
+#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=289
+ private SharpKit.JavaScript.JsString _hashKey;
+ private void UpdateHashKey()
+ {
+ _hashKey = GetHashCode().ToString();
+ }
+#endif
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/Set.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/Set.cs.meta
new file mode 100644
index 000000000..9e3b76d16
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/Set.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 23411223e702a51449131ae3cb7deeb5
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/Size.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/Size.cs
new file mode 100644
index 000000000..3fd718d2e
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/Size.cs
@@ -0,0 +1,72 @@
+namespace Microsoft.Msagl.Core.DataStructures
+{
+
+
+ ///
+ /// Size structure
+ ///
+ public struct Size
+ {
+ double width;
+ ///
+ /// width
+ ///
+ public double Width
+ {
+ get { return width; }
+ set { width = value; }
+ }
+ double height;
+ ///
+ /// Height
+ ///
+ public double Height
+ {
+ get { return height; }
+ set { height = value; }
+ }
+
+ ///
+ /// constructor
+ ///
+ ///
+ ///
+ public Size(double width, double height)
+ {
+ this.width = width;
+
+
+ this.height = height;
+
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Size operator /(Size s, double d) { return new Size(s.Width / d, s.Height / d); }
+
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Size operator *(Size s, double d) { return new Size(s.Width * d, s.Height * d); }
+
+
+ ///
+ /// padding the size ( from both sides!)
+ ///
+ ///
+ public void Pad(double padding) {
+ width += 2*padding;
+ height += 2*padding;
+ }
+ }
+
+
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/Size.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/Size.cs.meta
new file mode 100644
index 000000000..4a7175771
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/Size.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 10ae915a97e51564585fa058c476f9cf
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/SymmetricTuple.cs b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/SymmetricTuple.cs
new file mode 100644
index 000000000..86c03a705
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/SymmetricTuple.cs
@@ -0,0 +1,63 @@
+using System;
+
+namespace Microsoft.Msagl.Core.DataStructures {
+ ///
+ /// a tuple such that (a,b)==(b,a) for any a and b
+ ///
+ ///
+ public class SymmetricTuple {
+ bool Equals(SymmetricTuple other) {
+ return (A.Equals(other.A) && B.Equals(other.B)) ||
+ (A.Equals(other.B) && B.Equals(other.A));
+ }
+
+ ///
+ /// Serves as a hash function for a particular type.
+ ///
+ ///
+ /// A hash code for the current .
+ ///
+ /// 2
+ public override int GetHashCode() {
+
+#if !SHARPKIT
+ unchecked {
+#endif
+ return A.GetHashCode() ^ B.GetHashCode(); // we need a symmetric hash code
+#if !SHARPKIT
+ }
+#endif
+ }
+
+ public T A { get; private set; }
+ public T B { get; private set; }
+ ///
+ /// constructor
+ ///
+ ///
+ ///
+ public SymmetricTuple(T a, T b) {
+ A = a;
+ B = b;
+ }
+
+ ///
+ /// Determines whether the specified is equal to the current .
+ ///
+ ///
+ /// true if the specified is equal to the current ; otherwise, false.
+ ///
+ /// The object to compare with the current object. 2
+ public override bool Equals(object obj) {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((SymmetricTuple)obj);
+ }
+
+ public override string ToString() {
+ return String.Format("({0},{1})", A, B);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/Scripts/NIH/MSAGL/Core/DataStructures/SymmetricTuple.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/SymmetricTuple.cs.meta
new file mode 100644
index 000000000..e41968d53
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/DataStructures/SymmetricTuple.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4a850709a43d60748b3dd2674e1d902a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry.meta
new file mode 100644
index 000000000..7c646e35e
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 90b057800aaf56a4c9d635fac7c02c89
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/ApproximateComparer.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ApproximateComparer.cs
new file mode 100644
index 000000000..0471b5ed2
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ApproximateComparer.cs
@@ -0,0 +1,198 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.Msagl.Core.Geometry {
+ ///
+ /// Class that provides methods for doing approximate comparisons.
+ ///
+ public static class ApproximateComparer {
+ ///
+ /// return true if the points are close enough
+ ///
+ public static bool Close(Point pointA, Point pointB, double tolerance) {
+ return (pointA - pointB).Length <= tolerance;
+ }
+
+ ///
+ /// return true if the points are close enough
+ ///
+ public static bool Close(Point pointA, Point pointB) {
+ return Close(pointA, pointB, DistanceEpsilon);
+ }
+
+ ///
+ /// return true if the numbers are close enough
+ ///
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "a"), SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "b")]
+ public static bool Close(double a, double b) {
+ return Math.Abs(a - b) <= DistanceEpsilon;
+ }
+
+ ///
+ /// return true if the two rects are nearly identical (ignoring precision errors smaller than DistanceEpsilon)
+ ///
+ public static bool Close(Rectangle rect1, Rectangle rect2, double tolerance) {
+ return Close(rect1.LeftBottom, rect2.LeftBottom, tolerance) && Close(rect1.RightTop, rect2.RightTop, tolerance);
+ }
+
+ ///
+ /// return true if the two rects are nearly identical (ignoring precision errors smaller than DistanceEpsilon)
+ ///
+ /// first Rectangle
+ /// second Rectangle
+ ///
+ public static bool Close(Rectangle rect1, Rectangle rect2) {
+ return Close(rect1, rect2, DistanceEpsilon);
+ }
+
+ ///
+ /// The usual Compare operation, but within Curve.DistanceEpsilon.
+ ///
+ ///
+ ///
+ ///
+ public static int Compare(double numberA, double numberB) {
+ double c = numberA - numberB;
+ // The <= and >= here complement the < and > in Close(double, double).
+ if (c <= -DistanceEpsilon)
+ return -1;
+ if (c >= DistanceEpsilon)
+ return 1;
+ return 0;
+ }
+
+
+ ///
+ /// The usual > operation, but within Curve.DistanceEpsilon.
+ ///
+ ///
+ ///
+ ///
+ public static bool Greater(double numberA, double numberB) {
+ return Compare(numberA, numberB) > 0;
+ }
+
+ ///
+ /// The usual >= operation, but within Curve.DistanceEpsilon.
+ ///
+ ///
+ ///
+ ///
+ public static bool GreaterOrEqual(double numberA, double numberB) {
+ return Compare(numberA, numberB) >= 0;
+ }
+
+ ///
+ /// The usual less operation, but within Curve.DistanceEpsilon.
+ ///
+ ///
+ ///
+ ///
+ public static bool Less(double numberA, double numberB) {
+ return Compare(numberA, numberB) < 0;
+ }
+
+ ///
+ /// The usual less operation, but within Curve.DistanceEpsilon.
+ ///
+ ///
+ ///
+ ///
+ public static bool LessOrEqual(double numberA, double numberB) {
+ return Compare(numberA, numberB) <= 0;
+ }
+
+ ///
+ /// returns true if two intersections points are close enough
+ ///
+ ///
+ ///
+ ///
+ public static bool CloseIntersections(Point intersectionPoint0, Point intersectionPoint1) {
+ Point c = intersectionPoint0 - intersectionPoint1;
+ return c * c < IntersectionEpsilon * IntersectionEpsilon;
+ }
+
+ ///
+ /// 0 iff value is close to zero;
+ /// 1 iff value is strictly greater than zero;
+ /// -1 iff value is strictly lower than zero;
+ ///
+ public static int Sign(double value) {
+ if (value > DistanceEpsilon) return 1;
+ if (value < -DistanceEpsilon) return -1;
+ return 0;
+ }
+
+ static readonly double squareOfDistanceEpsilon = Math.Pow(10.0, -DistanceEpsilonPrecision * 2);
+
+ ///
+ /// The distance for two points considered to be the same
+ ///
+ internal static double DistanceEpsilon = Math.Pow(10.0, -DistanceEpsilonPrecision);
+
+ ///
+ /// The square of the distance epsilon for two points considered to be the same
+ ///
+ internal static double SquareOfDistanceEpsilon {
+ get { return squareOfDistanceEpsilon; }
+ }
+
+ ///
+ /// The digits of precision for the distance for two points considered to be the same
+ ///
+ internal static int DistanceEpsilonPrecision {
+ get { return 6; }
+ }
+
+ static double distXEps = 0.0001;
+
+ ///
+ /// The distance for two intersection points considered to be the same
+ ///
+ public static double IntersectionEpsilon {
+ get { return distXEps; }
+ set { distXEps = value; }
+ }
+
+ static double tolerance = 1.0E-8;
+
+ ///
+ /// The distance where to real numbers are considered the same
+ ///
+ public static double Tolerance {
+ get { return tolerance; }
+ set { tolerance = value; }
+ }
+
+ static double userDefinedTolerance = tolerance;
+
+ ///
+ /// A tolerance that is settable by users of MSAGL to adjust performance.
+ ///
+ public static double UserDefinedTolerance {
+ get { return userDefinedTolerance; }
+ set { userDefinedTolerance = value; }
+ }
+
+ ///
+ /// point coordinates will be rounded to NumberOfDigitsToRound for comparison
+ ///
+ static internal int NumberOfDigitsToRound {
+ get { return DistanceEpsilonPrecision; }
+ }
+
+ static internal Point Round(Point point) {
+ return new Point(Round(point.X), Round(point.Y));
+ }
+
+ static internal Point Round(Point point, int numberDigitsToRound)
+ {
+ return new Point(Math.Round(point.X, numberDigitsToRound), Math.Round(point.Y, numberDigitsToRound));
+ }
+
+ static internal double Round(double value) {
+ return Math.Round(value, NumberOfDigitsToRound);
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/ApproximateComparer.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ApproximateComparer.cs.meta
new file mode 100644
index 000000000..2f1c8b1eb
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ApproximateComparer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 380260d63cdb0a0418bb345f925c6291
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/CompassVector.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/CompassVector.cs
new file mode 100644
index 000000000..0206ee2b2
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/CompassVector.cs
@@ -0,0 +1,254 @@
+using System;
+using System.Diagnostics;
+
+namespace Microsoft.Msagl.Core.Geometry {
+ internal struct CompassVector {
+
+ ///
+ /// Override op==
+ ///
+ ///
+ ///
+ ///
+ public static bool operator ==(CompassVector a, CompassVector b) {
+ return a.Dir == b.Dir;
+ }
+
+ ///
+ /// Return the hash code.
+ ///
+ ///
+ public override int GetHashCode() {
+ return (int)Dir;
+ }
+
+ ///
+ /// Override op!=
+ ///
+ ///
+ ///
+ ///
+ public static bool operator !=(CompassVector a, CompassVector b) {
+ return a.Dir != b.Dir;
+ }
+
+ internal CompassVector(Directions direction)
+ : this() {
+ Dir = direction;
+ }
+
+ // Warning: do not use for VisibilityGraph generation. See VectorDirection(a) comments.
+ internal CompassVector(Point a)
+ : this() {
+ Dir = VectorDirection(a);
+ }
+
+ internal CompassVector(Point a, Point b)
+ : this() {
+ Dir = VectorDirection(a, b);
+ }
+
+
+ internal Directions Dir { get; set; }
+ internal CompassVector Right {
+ get { return new CompassVector(RotateRight(Dir)); }
+ }
+
+ internal static Directions RotateRight(Directions direction) {
+ switch (direction) {
+ case Directions.North:
+ return Directions.East;
+ case Directions.East:
+ return Directions.South;
+ case Directions.South:
+ return Directions.West;
+ case Directions.West:
+ return Directions.North;
+ default:
+ throw new InvalidOperationException();
+ }
+ }
+
+ internal static Directions RotateLeft(Directions direction) {
+ switch (direction) {
+ case Directions.North:
+ return Directions.West;
+ case Directions.West:
+ return Directions.South;
+ case Directions.South:
+ return Directions.East;
+ case Directions.East:
+ return Directions.North;
+ default:
+ throw new InvalidOperationException();
+ }
+ }
+
+ internal static int ToIndex(Directions direction) {
+ switch (direction) {
+ case Directions.North:
+ return 0;
+ case Directions.East:
+ return 1;
+ case Directions.South:
+ return 2;
+ case Directions.West:
+ return 3;
+ default:
+ throw new InvalidOperationException();
+ }
+ }
+
+ internal static Directions VectorDirection(Point d){
+ Directions r = Directions.None;
+ if(d.X > ApproximateComparer.DistanceEpsilon)
+ r = Directions.East;
+ else if(d.X < -ApproximateComparer.DistanceEpsilon)
+ r = Directions.West;
+ if(d.Y > ApproximateComparer.DistanceEpsilon)
+ r |= Directions.North;
+ else if(d.Y < -ApproximateComparer.DistanceEpsilon)
+ r |= Directions.South;
+ return r;
+ }
+
+ internal static Directions VectorDirection(Point a, Point b) {
+ Directions r = Directions.None;
+
+ // This method is called a lot as part of rectilinear layout.
+ // Try to keep it quick.
+ double horizontalDiff = b.X - a.X;
+ double verticalDiff = b.Y - a.Y;
+ double halfEpsilon = ApproximateComparer.DistanceEpsilon / 2;
+
+ if (horizontalDiff > halfEpsilon)
+ r = Directions.East;
+ else if (-horizontalDiff > halfEpsilon)
+ r = Directions.West;
+ if (verticalDiff > halfEpsilon)
+ r |= Directions.North;
+ else if (-verticalDiff > halfEpsilon)
+ r |= Directions.South;
+ return r;
+ }
+
+ internal static Directions DirectionsFromPointToPoint(Point a, Point b) {
+ return VectorDirection(a, b);
+ }
+
+ internal static Directions PureDirectionFromPointToPoint(Point a, Point b) {
+ Directions dir = VectorDirection(a, b);
+ Debug.Assert(IsPureDirection(dir), "Impure direction found");
+ return dir;
+ }
+
+ internal static Directions OppositeDir(Directions direction) {
+ switch (direction) {
+ case Directions.North:
+ return Directions.South;
+ case Directions.West:
+ return Directions.East;
+ case Directions.South:
+ return Directions.North;
+ case Directions.East:
+ return Directions.West;
+ default:
+ return Directions.None;
+ }
+ }
+
+ internal static bool IsPureDirection(Directions direction) {
+ switch (direction) {
+ case Directions.North:
+ return true;
+ case Directions.East:
+ return true;
+ case Directions.South:
+ return true;
+ case Directions.West:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ internal static bool IsPureDirection(Point a, Point b) {
+ return IsPureDirection(DirectionsFromPointToPoint(a, b));
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public bool Equals(CompassVector other) {
+ return Equals(other.Dir, Dir);
+ }
+
+
+ internal static bool DirectionsAreParallel(Directions a, Directions b){
+ return a == b || a == OppositeDir(b);
+ }
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override bool Equals(object obj) {
+ if (ReferenceEquals(null, obj)) return false;
+ if (obj.GetType() != typeof(CompassVector)) return false;
+ return Equals((CompassVector)obj);
+ }
+
+ ///
+ /// Returns a string representing the direction.
+ ///
+ ///
+ public override string ToString() {
+ return Dir.ToString();
+ }
+
+ ///
+ /// Translates the CompassVector's direction into a new Point.
+ ///
+ ///
+ public Point ToPoint() {
+ var p = new Point();
+ if ((Dir & Directions.East) == Directions.East)
+ p.X += 1;
+ if ((Dir & Directions.North) == Directions.North)
+ p.Y += 1;
+ if ((Dir & Directions.West) == Directions.West)
+ p.X -= 1;
+ if ((Dir & Directions.South) == Directions.South)
+ p.Y -= 1;
+ return p;
+ }
+
+ ///
+ /// Translates a direction into a Point.
+ ///
+ ///
+ public static Point ToPoint(Directions dir) {
+ return (new CompassVector(dir)).ToPoint();
+ }
+
+ ///
+ /// the negation operator
+ ///
+ ///
+ ///
+ public static CompassVector operator -(CompassVector directionVector) {
+ return new CompassVector(OppositeDir(directionVector.Dir));
+ }
+ ///
+ /// the negation operator
+ ///
+ ///
+ public CompassVector Negate() {
+ return -this;
+ }
+
+
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/CompassVector.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/CompassVector.cs.meta
new file mode 100644
index 000000000..e94ce31fe
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/CompassVector.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5f1d55eb0ba264f45943033b4518baf5
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull.meta
new file mode 100644
index 000000000..d5cbeec3d
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6c814fa42e9363c49ac9b234daeea29b
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/ConvexHull.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/ConvexHull.cs
new file mode 100644
index 000000000..d89202871
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/ConvexHull.cs
@@ -0,0 +1,196 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Microsoft.Msagl.Core.Geometry.Curves;
+using Microsoft.Msagl.Core.Layout;
+using Microsoft.Msagl.DebugHelpers;
+using Microsoft.Msagl.Routing;
+
+namespace Microsoft.Msagl.Core.Geometry {
+ ///
+ /// Creates the convex hull of a set of points following "Computational Geometry, second edition" of O'Rourke
+ ///
+ public sealed class ConvexHull {
+ HullPoint[] hullPoints;
+ Point pivot;
+ HullStack stack;
+ HullPointComparer comparer;
+
+ ConvexHull(IEnumerable bodyPoints) {
+ SetPivotAndAllocateHullPointsArray(bodyPoints);
+ }
+
+ void SetPivotAndAllocateHullPointsArray(IEnumerable bodyPoints) {
+ pivot = new Point(0, Double.MaxValue); //set Y to a very big value
+ int pivotIndex = -1;
+ int n = 0;
+ foreach (Point point in bodyPoints) {
+ if (point.Y < pivot.Y) {
+ pivot = point;
+ pivotIndex = n;
+ }
+ else if (point.Y == pivot.Y)
+ if (point.X > pivot.X) {
+ pivot = point;
+ pivotIndex = n;
+ }
+ n++;
+ }
+ if (n >= 1) {
+ hullPoints = new HullPoint[n - 1]; //we will not copy the pivot into the hull points
+ n = 0;
+ foreach (Point point in bodyPoints)
+ if (n != pivotIndex)
+ hullPoints[n++] = new HullPoint(point);
+ else
+ pivotIndex = -1; //forget where the pivot was
+ }
+ }
+
+ Point StackTopPoint {
+ get { return stack.Point; }
+ }
+
+ Point StackSecondPoint {
+ get { return stack.Next.Point; }
+ }
+
+ ///
+ /// calculates the convex hull of the given set of points
+ ///
+ /// Point of the convex hull.
+ /// The list of extreme points of the hull boundaries in the clockwise order
+ public static IEnumerable CalculateConvexHull(IEnumerable pointsOfTheBody) {
+ var convexHull = new ConvexHull(pointsOfTheBody);
+ return convexHull.Calculate();
+ }
+
+ IEnumerable Calculate() {
+ if (pivot.Y == Double.MaxValue)
+ return new Point[0];
+ if (hullPoints.Length == 0)
+ return new[] {pivot};
+ SortAllPointsWithoutPivot();
+ Scan();
+ return EnumerateStack();
+ }
+
+ IEnumerable EnumerateStack() {
+ HullStack stackCell = stack;
+ while (stackCell != null) {
+ yield return stackCell.Point;
+ stackCell = stackCell.Next;
+ }
+ }
+
+ void Scan() {
+ int i = 0;
+ while (hullPoints[i].Deleted)
+ i++;
+
+ stack = new HullStack(pivot);
+ Push(i++);
+ if (i < hullPoints.Length) {
+ if (!hullPoints[i].Deleted)
+ Push(i++);
+ else
+ i++;
+ }
+
+ while (i < hullPoints.Length) {
+ if (!hullPoints[i].Deleted) {
+ if (LeftTurn(i))
+ Push(i++);
+ else
+ Pop();
+ }
+ else
+ i++;
+ }
+
+ //cleanup the end
+ while (StackHasMoreThanTwoPoints() && !LeftTurnToPivot())
+ Pop();
+ }
+
+ bool LeftTurnToPivot() {
+ return Point.GetTriangleOrientation(StackSecondPoint, StackTopPoint, pivot) ==
+ TriangleOrientation.Counterclockwise;
+ }
+
+ bool StackHasMoreThanTwoPoints() {
+ return stack.Next != null && stack.Next.Next != null;
+ }
+
+ void Pop() {
+ stack = stack.Next;
+ }
+
+
+ bool LeftTurn(int i) {
+ if (stack.Next == null)
+ return true; //there is only one point in the stack
+ var orientation = Point.GetTriangleOrientationWithIntersectionEpsilon(StackSecondPoint, StackTopPoint, hullPoints[i].Point);
+ if (orientation == TriangleOrientation.Counterclockwise)
+ return true;
+ if (orientation == TriangleOrientation.Clockwise)
+ return false;
+ return BackSwitchOverPivot(hullPoints[i].Point);
+ }
+ bool BackSwitchOverPivot(Point point) {
+ //we know here that there at least two points in the stack but it has to be exaclty two
+ if (stack.Next.Next != null)
+ return false;
+ Debug.Assert(StackSecondPoint == pivot);
+ return StackTopPoint.X > pivot.X + ApproximateComparer.DistanceEpsilon &&
+ point.X < pivot.X - ApproximateComparer.DistanceEpsilon;
+ }
+
+ void Push(int p) {
+ var t = new HullStack(hullPoints[p].Point) {Next = stack};
+ stack = t;
+ }
+
+ void SortAllPointsWithoutPivot() {
+ comparer = new HullPointComparer(pivot);
+ Array.Sort(hullPoints, comparer);
+// if (true) {
+// var list = new List();
+// var d = 3.0;
+// list.Add(new DebugCurve(100, 0.001, "magenta", El(ref d, pivot)));
+// foreach (var hullPoint in hullPoints) {
+// list.Add(new DebugCurve(100,0.001, hullPoint.Deleted ? "blue" : "green", El(ref d, hullPoint.Point)));
+// }
+// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(list);
+// }
+ }
+
+
+/*
+ Ellipse El(ref double d, Point p) {
+ var ret= new Ellipse(d, d, p);
+ d -= 0.01;
+ return ret;
+ }
+*/
+
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object)")]
+ internal static Polyline CreateConvexHullAsClosedPolyline(IEnumerable points) {
+ var convexHull = new Polyline(CalculateConvexHull(points)) { Closed = true };
+#if TEST_MSAGL
+ foreach (var point in points) {
+ if (Curve.PointRelativeToCurveLocation(point, convexHull) == PointLocation.Outside) {
+ var hullPoint = convexHull[convexHull.ClosestParameter(point)];
+
+ // This can be too restrictive if very close points are put into the hull. It is probably
+ // better to clean up in the caller before doing this, but this assert can also be relaxed.
+ Debug.Assert(ApproximateComparer.Close(point, hullPoint, ApproximateComparer.IntersectionEpsilon * 20), String.Format("not CloseIntersections: initial point {0}, hull point {1}", point, hullPoint));
+
+ }
+ }
+#endif // TEST_MSAGL
+ return convexHull;
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/ConvexHull.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/ConvexHull.cs.meta
new file mode 100644
index 000000000..c34d4e380
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/ConvexHull.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 848192ed3ea85614cb33c20793d74157
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullPoint.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullPoint.cs
new file mode 100644
index 000000000..b95ec6449
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullPoint.cs
@@ -0,0 +1,27 @@
+
+namespace Microsoft.Msagl.Core.Geometry {
+ internal class HullPoint {
+ Point point;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Point Point {
+ get { return point; }
+ set { point = value; }
+ }
+ bool deleted;
+
+ internal bool Deleted {
+ get { return deleted; }
+ set { deleted = value; }
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal HullPoint(Point point) {
+ this.Point = point;
+ }
+
+ public override string ToString() {
+ return point + (Deleted ? "X" : "");
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullPoint.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullPoint.cs.meta
new file mode 100644
index 000000000..035c343d8
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullPoint.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a24e6188dd050ce46850d17ef181852b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullPointComparer.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullPointComparer.cs
new file mode 100644
index 000000000..bb04b603f
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullPointComparer.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Msagl.Core.Geometry {
+ internal class HullPointComparer:IComparer {
+ Point pivot;
+
+ ///
+ /// note that this function can change "deleted" member for collinear points
+ ///
+ ///
+ ///
+ ///
+#if SHARPKIT //http://code.google.com/p/sharpkit/issues/detail?id=203
+ //SharpKit/Colin - https://code.google.com/p/sharpkit/issues/detail?id=332
+ public int Compare(HullPoint i, HullPoint j) {
+#else
+ int IComparer.Compare(HullPoint i, HullPoint j) {
+#endif
+ if (i == j)
+ return 0;
+ if (i == null)
+ return -1;
+ if (j == null)
+ return 1;
+
+ switch (Point.GetTriangleOrientationWithIntersectionEpsilon(pivot, i.Point, j.Point)) {
+ case TriangleOrientation.Counterclockwise:
+ return -1;
+ case TriangleOrientation.Clockwise:
+ return 1;
+ case TriangleOrientation.Collinear: {
+ //because of the double point error pi and pj can be on different sizes of the pivot on the horizontal line passing through the pivot, or rather just above it
+ var piDelX = i.Point.X - pivot.X;
+ var pjDelX = j.Point.X - pivot.X;
+ if (piDelX > ApproximateComparer.DistanceEpsilon && pjDelX < -ApproximateComparer.DistanceEpsilon) {
+ return -1;
+ }
+ if (piDelX < -ApproximateComparer.DistanceEpsilon && pjDelX > ApproximateComparer.DistanceEpsilon) {
+ return 1;
+ }
+
+ //here i and j cannot be on the different sides of the pivot because of the choice of the pivot
+ //delete the one that is closer to the pivot.
+ var pi = i.Point - pivot;
+ var pj = j.Point - pivot;
+ var iMinJ = pi.L1 - pj.L1;
+ if (iMinJ < 0) {
+ i.Deleted = true;
+ return -1;
+ }
+ if (iMinJ > 0) {
+ j.Deleted = true;
+ return 1;
+ }
+
+ //points are the same, leave the one with the greatest hash code
+ if (i.GetHashCode() < j.GetHashCode())
+ i.Deleted = true;
+ else
+ j.Deleted = true;
+
+ return 0;
+ }
+
+ }
+ throw new InvalidOperationException();
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal HullPointComparer(Point pivotPoint) {
+ this.pivot = pivotPoint;
+ }
+
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullPointComparer.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullPointComparer.cs.meta
new file mode 100644
index 000000000..4fd9c1679
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullPointComparer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5544c1d609815f24db41713b5cee93c4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullStack.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullStack.cs
new file mode 100644
index 000000000..e63068e44
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullStack.cs
@@ -0,0 +1,25 @@
+namespace Microsoft.Msagl.Core.Geometry {
+ internal class HullStack {
+ Point hullPoint;
+
+ internal Point Point {
+ get { return hullPoint; }
+ set { hullPoint = value; }
+ }
+ HullStack next;
+
+ internal HullStack Next {
+ get { return next; }
+ set { next = value; }
+ }
+
+ internal HullStack(Point hullPoint) {
+ this.Point = hullPoint;
+ }
+
+ public override string ToString() {
+ return hullPoint.ToString();
+ }
+
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullStack.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullStack.cs.meta
new file mode 100644
index 000000000..7629add4a
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/ConvexHull/HullStack.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d5d2bc29fc3a11c408e92a50c141d04c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves.meta
new file mode 100644
index 000000000..6a930dca8
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f26b9b4baf6941a4d8b4c04ce6bfc6fc
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ClosestPointOnCurve.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ClosestPointOnCurve.cs
new file mode 100644
index 000000000..d695cc0f3
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ClosestPointOnCurve.cs
@@ -0,0 +1,63 @@
+using System;
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ internal class ClosestPointOnCurve {
+ ClosestPointOnCurve() { }
+ /////
+ ///// Gets the closest point on the curve to the point
+ /////
+ /////
+ /////
+ ///// will return Double.MaxVal if Newton iterations fail
+ /////
+ internal static double ClosestPoint(ICurve curve, Point a, double hint, double low, double high) {
+ /*
+ * Let F=(c(t)-a)^2. We try to bring to zero the first derivative of F, Ft. Denote it by f(t).
+ * Applying the Newton method we see that dt=-f(t)/der(f(t)
+ * The first derivative of F, f, has the form (c-a)*ct. We discarded a multiplier here.
+ * The second derivative has the form ct*ct+(c-a)*ctt
+ * The new t becomes t-dt
+ */
+ const int numberOfIterationsMax = 5;
+ const int numberOfOverShootsMax = 5;
+ double t = hint;
+
+ int numberOfIteration = 0;
+ int numberOfOvershoots = 0;
+ double dt;
+ bool abort = false;
+ do {
+ Point c = curve[t];
+ Point ct = curve.Derivative(t);
+ Point ctt = curve.SecondDerivative(t);
+
+ double secondDerivative = ct * ct + (c - a) * ctt;
+
+ if (Math.Abs(secondDerivative) < ApproximateComparer.Tolerance)
+ return t;
+
+ dt = (c - a) * ct / secondDerivative;
+
+ t -= dt;
+
+ if (t > high + ApproximateComparer.Tolerance) {
+ t = high;
+ numberOfOvershoots++;
+ } else if (t < low - ApproximateComparer.Tolerance) {
+ t = low;
+ numberOfOvershoots++;
+ }
+ numberOfIteration++;
+ } while (Math.Abs(dt) > ApproximateComparer.Tolerance &&!
+ (abort = (numberOfIteration >= numberOfIterationsMax || numberOfOvershoots >= numberOfOverShootsMax)));
+
+ //may be the initial value was just fine
+ if (abort && (curve[hint] - a).Length < ApproximateComparer.DistanceEpsilon)
+ t = hint;
+
+ return t;
+
+ }
+
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ClosestPointOnCurve.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ClosestPointOnCurve.cs.meta
new file mode 100644
index 000000000..f74c97165
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ClosestPointOnCurve.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c70b0860b9cf2454a852dff5c58a0ba7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CubicBezierSegment.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CubicBezierSegment.cs
new file mode 100644
index 000000000..6d86d942c
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CubicBezierSegment.cs
@@ -0,0 +1,630 @@
+// #region Using directives
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using Microsoft.Msagl.Core.DataStructures;
+using Microsoft.Msagl.Core.Layout;
+using Microsoft.Msagl.DebugHelpers;
+//#endregion
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ ///
+ /// Cubic Bezier Segment
+ ///
+#if TEST_MSAGL
+ [Serializable]
+#endif
+ public class CubicBezierSegment : ICurve {
+
+ ///
+ /// left derivative at t
+ ///
+ /// the parameter where the derivative is calculated
+ ///
+ public Point LeftDerivative(double t) {
+ return Derivative(t);
+ }
+
+ ///
+ /// right derivative at t
+ ///
+ /// the parameter where the derivative is calculated
+ ///
+ public Point RightDerivative(double t) {
+ return Derivative(t);
+ }
+
+
+ ///
+ /// control points
+ ///
+ Point[] b = new Point[4];
+
+
+ ///
+ /// coefficients
+ ///
+ Point l, e, c;
+
+ ///
+ /// get a control point
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "B")]
+ public Point B(int controlPointIndex) {
+ return b[controlPointIndex];
+ }
+ ///
+ /// ToString
+ ///
+ ///
+ override public string ToString() {
+ return String.Format(CultureInfo.InvariantCulture, "(Bezie{0},{1},{2},{3})", b[0], b[1], b[2], b[3]);
+ }
+ ParallelogramNodeOverICurve pBoxNode;
+ ///
+ /// A tree of ParallelogramNodes covering the curve.
+ /// This tree is used in curve intersections routines.
+ ///
+ ///
+ public ParallelogramNodeOverICurve ParallelogramNodeOverICurve {
+ get {
+#if PPC
+ lock(this){
+#endif
+ if (pBoxNode != null)
+ return pBoxNode;
+ return pBoxNode = ParallelogramNodeOverICurve.CreateParallelogramNodeForCurveSegment(this);
+#if PPC
+ }
+#endif
+ }
+ }
+ ///
+ /// Returns the point on the curve corresponding to parameter t
+ ///
+ ///
+ ///
+ public Point this[double t] {
+ get {
+ double t2 = t * t;
+ double t3 = t2 * t;
+ return l * t3 + e * t2 + c * t + b[0];
+ }
+ }
+
+ static void AdjustParamTo01(ref double u) {
+ if (u > 1)
+ u = 1;
+ else if (u < 0)
+ u = 0;
+ }
+ //throw away the segments [0,u] and [v,1] of the segment
+
+ ///
+ /// Returns the trimmed curve
+ ///
+ ///
+ ///
+ ///
+ public ICurve Trim(double u, double v) {
+
+ AdjustParamTo01(ref u);
+ AdjustParamTo01(ref v);
+
+ if (u > v)
+ return Trim(v, u);
+
+ if (u > 1.0 - ApproximateComparer.Tolerance)
+ return new CubicBezierSegment(b[3], b[3], b[3], b[3]);
+
+ Point[] b1 = new Point[3];
+ Point[] b2 = new Point[2];
+ Point pv = Casteljau(u, b1, b2);
+
+ //this will be the trim to [v,1]
+ CubicBezierSegment trimByU = new CubicBezierSegment(pv, b2[1], b1[2], b[3]);
+
+
+ //1-v is not zero here because we have handled already the case v=1
+ Point pu = trimByU.Casteljau((v - u) / (1.0 - u), b1, b2);
+
+ return new CubicBezierSegment(trimByU.b[0], b1[0], b2[0], pu);
+
+ }
+
+ ///
+ /// Not Implemented: Returns the trimmed curve, wrapping around the end if start is greater than end.
+ ///
+ /// The starting parameter
+ /// The ending parameter
+ /// The trimmed curve
+ public ICurve TrimWithWrap(double start, double end) {
+ throw new NotImplementedException();
+ }
+
+ //array for Casteljau method
+ internal Point Casteljau(double t, Point[] b1, Point[] b2) {
+
+ double f = 1.0 - t;
+ for (int i = 0; i < 3; i++)
+ b1[i] = b[i] * f + b[i + 1] * t;
+
+ for (int i = 0; i < 2; i++)
+ b2[i] = b1[i] * f + b1[i + 1] * t;
+
+ return b2[0] * f + b2[1] * t;
+
+ }
+ ///
+ /// first derivative
+ ///
+ ///
+ ///
+ public Point Derivative(double t) {
+ return 3 * l * t * t + 2 * e * t + c;
+ }
+ ///
+ /// second derivative
+ ///
+ ///
+ ///
+ public Point SecondDerivative(double t) { return 6 * l * t + 2 * e; }
+
+ ///
+ /// third derivative
+ ///
+ ///
+ ///
+ public Point ThirdDerivative(double t) { return 6 * l; }
+ ///
+ /// the constructor
+ ///
+ ///
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1025:ReplaceRepetitiveArgumentsWithParamsArray"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "b")]
+ public CubicBezierSegment(Point b0, Point b1, Point b2, Point b3) {
+
+ this.b[0] = b0;
+ this.b[1] = b1;
+ this.b[2] = b2;
+ this.b[3] = b3;
+ c = 3 * (b[1] - b[0]);
+ e = 3 * (b[2] - b[1]) - c;
+ l = b[3] - b[0] - c - e;
+ }
+
+ ///
+ /// this[ParStart]
+ ///
+ public Point Start {
+ get { return b[0]; }
+ }
+
+ ///
+ /// this[ParEnd]
+ ///
+ public Point End {
+ get { return b[3]; }
+ }
+
+
+ double parStart;
+
+ ///
+ /// the start of the parameter domain
+ ///
+ public double ParStart {
+ get { return parStart; }
+ set { parStart = value; }
+ }
+
+ double parEnd = 1;
+
+ ///
+ /// the end of the parameter domain
+ ///
+ public double ParEnd {
+ get { return parEnd; }
+ set { parEnd = value; }
+ }
+
+ ///
+ /// this[Reverse[t]]=this[ParEnd+ParStart-t]
+ ///
+ ///
+ public ICurve Reverse() {
+ return new CubicBezierSegment(b[3], b[2], b[1], b[0]);
+ }
+
+ ///
+ /// Returns the curved moved by delta
+ ///
+ public void Translate(Point delta) {
+ this.b[0] += delta;
+ this.b[1] += delta;
+ this.b[2] += delta;
+ this.b[3] += delta;
+ c = 3 * (b[1] - b[0]);
+ e = 3 * (b[2] - b[1]) - c;
+ l = b[3] - b[0] - c - e;
+
+ pBoxNode = null;
+ }
+
+ ///
+ /// Returns the curved scaled by x and y
+ ///
+ ///
+ ///
+ ///
+ public ICurve ScaleFromOrigin(double xScale, double yScale)
+ {
+ return new CubicBezierSegment(Point.Scale(xScale, yScale, b[0]), Point.Scale(xScale, yScale, b[1]), Point.Scale(xScale, yScale, b[2]), Point.Scale(xScale, yScale, b[3]));
+ }
+
+ ///
+ /// Offsets the curve in the direction of dir
+ ///
+ ///
+ ///
+ ///
+ public ICurve OffsetCurve(double offset, Point dir) { return null; }
+
+ ///
+ /// return length of the curve segment [start,end]
+ ///
+ ///
+ ///
+ ///
+ public double LengthPartial(double start, double end) {
+ return Trim(start, end).Length;
+ }
+ ///
+ /// Get the length of the curve
+ ///
+ public double Length {
+ get { return LengthOnControlPolygon(b[0],b[1],b[2],b[3]); }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ static double LengthOnControlPolygon(Point b0, Point b1, Point b2, Point b3) {
+ var innerCordLength = (b3- b0).Length;
+ var controlPointPolygonLength = (b1 - b0).Length + (b2 - b1).Length + (b3 - b2).Length;
+ if (controlPointPolygonLength - innerCordLength > Curve.LineSegmentThreshold) {
+ var mb0 = (b0 + b1)/2;
+ var mb1 = (b1 + b2)/2;
+ var mb2=(b2 + b3)/2;
+ var mmb0 = (mb0 + mb1)/2;
+ var mmb1 = (mb2 + mb1)/2;
+ var mmmb0 = (mmb0 + mmb1)/2;
+// LayoutAlgorithmSettings.ShowDebugCurves(new DebugCurve(100, 2, "blue", new CubicBezierSegment(b0, b1, b2, b3)), new DebugCurve(100, 1, "red", new CubicBezierSegment(b0, mb0, mmb0, mmmb0)), new DebugCurve(100, 1, "green", new CubicBezierSegment(mmmb0, mmb1, mb2, b3)));
+ return LengthOnControlPolygon(b0, mb0, mmb0, mmmb0) + LengthOnControlPolygon(mmmb0, mmb1, mb2, b3);
+ }
+
+ return (controlPointPolygonLength + innerCordLength) / 2;
+ }
+
+ ///
+ /// the segment bounding box
+ ///
+ public Rectangle BoundingBox {
+ get {
+ var ret = new Rectangle(this.b[0], this.b[1]);
+ ret.Add(b[2]);
+ ret.Add(b[3]);
+ return ret;
+ }
+ }
+
+
+ ///
+ /// Return the transformed curve
+ ///
+ ///
+ /// the transformed curve
+ public ICurve Transform(PlaneTransformation transformation) {
+ return new CubicBezierSegment(transformation * b[0], transformation * b[1], transformation * b[2], transformation * b[3]);
+ }
+
+ ///
+ /// returns a parameter t such that the distance between curve[t] and targetPoint is minimal
+ /// and t belongs to the closed segment [low,high]
+ ///
+ /// the point to find the closest point
+ /// the upper bound of the parameter
+ /// the low bound of the parameter
+ ///
+ public double ClosestParameterWithinBounds(Point targetPoint, double low, double high) {
+ System.Diagnostics.Debug.Assert(high <= 1 && low >= 0);
+ System.Diagnostics.Debug.Assert(low <= high);
+ double t = (high-low) / 8;
+ double closest = 0;
+ double minDist = Double.MaxValue;
+ for (int i = 0; i < 9; i++) {
+ Point p = targetPoint - this[i * t + low];
+ double d = p * p;
+ if (d < minDist) {
+ minDist = d;
+ closest = i * t + low;
+ }
+ }
+ return ClosestPointOnCurve.ClosestPoint(this, targetPoint, closest, low, high);
+ }
+
+
+ ///
+ /// clones the curve.
+ ///
+ /// the cloned curve
+ public ICurve Clone() {
+ return new CubicBezierSegment(this.b[0], this.b[1], this.b[2], this.b[3]);
+ }
+
+ ///
+ /// the signed curvature of the segment at t
+ ///
+ ///
+ ///
+ public double Curvature(double t) {
+ System.Diagnostics.Debug.Assert(t >= ParStart && t <= ParEnd);
+
+ double den = G(t);
+
+ System.Diagnostics.Debug.Assert(Math.Abs(den) > 0.00001);
+
+ return F(t) / den;
+ }
+
+
+ double F(double t) {
+ return Xp(t) * Ypp(t) - Yp(t) * Xpp(t);
+ }
+
+ ///
+ /// G(t) is the denomenator of the curvature
+ ///
+ ///
+ ///
+ double G(double t) {
+ double xp = Xp(t);
+ double yp = Yp(t);
+ double den = (xp * xp + yp * yp);
+ return Math.Sqrt(den * den * den);
+ }
+
+ ///
+ /// the first derivative of x-coord
+ ///
+ ///
+ ///
+ double Xp(double t) {
+ return 3 * l.X * t * t + 2 * e.X * t + c.X;
+ }
+
+ ///
+ /// the second derivativ of y-coordinate
+ ///
+ ///
+ ///
+ double Ypp(double t) {
+ return 6 * l.Y * t + 2 * e.Y;
+ }
+
+ ///
+ /// the first derivative of y-coord
+ ///
+ ///
+ ///
+ double Yp(double t) {
+ return 3 * l.Y * t * t + 2 * e.Y * t + c.Y;
+ }
+
+ ///
+ /// the seconde derivative of x coord
+ ///
+ ///
+ ///
+ double Xpp(double t) {
+ return 6 * l.X * t + 2 * e.X;
+ }
+ ///
+ /// the third derivative of x coordinate
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "t")]
+ double Xppp(double t) {
+ return 6 * l.X;
+ }
+
+ ///
+ /// the third derivative of y coordinate
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "t")]
+ double Yppp(double t) {
+ return 6 * l.Y;
+ }
+
+
+ ///
+ /// the derivative of the curvature at t
+ ///
+ ///
+ ///
+ public double CurvatureDerivative(double t) {
+ // we need to calculate the derivative of f/g where f=xp* ypp-yp*xpp and g=(xp*xp+yp*yp)^(3/2)
+ double h = G(t);
+ return (Fp(t) * h - Gp(t) * F(t)) / (h * h);
+ }
+
+
+
+
+ double Fp(double t) {
+ return Xp(t) * Yppp(t) - Yp(t) * Xppp(t);
+ }
+
+ double Fpp(double t) {
+ return Xpp(t) * Yppp(t) // + Xp(t) * Ypppp(t)=0
+ - Ypp(t) * Xppp(t);//- Yp(t) * Xpppp(t)=0
+ }
+
+
+ ///
+ /// returns a parameter t such that the distance between curve[t] and a is minimal
+ ///
+ ///
+ ///
+ public double ClosestParameter(Point targetPoint) {
+ double t = 1.0 / 8;
+ double closest = 0;
+ double minDist = Double.MaxValue;
+ for (int i = 0; i < 9; i++) {
+ Point p = targetPoint - this[i * t];
+ double d = p * p;
+ if (d < minDist) {
+ minDist = d;
+ closest = i * t;
+ }
+ }
+ return ClosestPointOnCurve.ClosestPoint(this, targetPoint, closest, 0, 1);
+ }
+
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+ public IEnumerable> MaximalCurvaturePoints {
+ get {
+
+ List> maxCurveCandidates = new List>();
+
+
+ int n = 8;
+ double d = 1.0 / n;
+ double prev = -1;//out of the domain
+ for (int i = 0; i < n; i++) {
+ double start = i * d;
+ double end = start + d;
+ double x;
+ if (RootFinder.TryToFindRoot(new CurvatureDerivative(this, start, end), start, end, (start + end) / 2, out x)) {
+ if (x != prev) {
+ prev = x;
+ maxCurveCandidates.Add(new Tuple(x, Curvature(x)));
+ }
+ }
+ }
+ maxCurveCandidates.Add(new Tuple(ParStart, Curvature(ParStart)));
+ maxCurveCandidates.Add(new Tuple(ParEnd, Curvature(ParEnd)));
+
+ var maxCur = maxCurveCandidates.Max(l => Math.Abs(l.Item2));
+
+ return from v in maxCurveCandidates where Math.Abs(v.Item2) == maxCur select v;
+ }
+
+ }
+
+ #region ICurve Members
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double CurvatureSecondDerivative(double t) {
+ double g = G(t);
+ return (Qp(t) * g - 2 * Q(t) * Gp(t)) / (g * g * g);
+ }
+
+ double Q(double t) {
+ return Fp(t) * G(t) - Gp(t) * F(t);
+ }
+
+ double Qp(double t) {
+ return Fpp(t) * G(t) - Gpp(t) * F(t);
+ }
+
+ double Gpp(double t) {
+ var xp = Xp(t);
+ var yp = Yp(t);
+ var xpp = Xpp(t);
+ var ypp = Ypp(t);
+ var xppp = Xppp(t);
+ var yppp = Yppp(t);
+ var u = Math.Sqrt(xp * xp + yp * yp);
+ var v = xp * xpp + yp * ypp;
+ return 3 * ((v * v) / u + u * (xpp * xpp + xp * xppp + ypp * ypp + yp * yppp));
+
+ }
+
+ double Gp(double t) {
+ var xp = Xp(t);
+ var yp = Yp(t);
+ var xpp = Xpp(t);
+ var ypp = Ypp(t);
+ return 3 * Math.Sqrt(xp * xp + yp * yp) * (xp * xpp + yp * ypp);
+ }
+
+
+ #endregion
+
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double GetParameterAtLength(double length) {
+ double low = 0;
+ double upper = 1;
+ while (upper - low > ApproximateComparer.Tolerance) {
+ var middle = (upper + low)/2;
+ int err = EvaluateError(length, middle);
+ if (err > 0)
+ upper = middle;
+ else if (err < 0)
+ low = middle;
+ else
+ return middle;
+ }
+
+ return (low + upper)/2;
+ }
+
+ int EvaluateError(double length, double t) {
+ //todo: this is a slow version!
+ var f = 1 - t;
+ var mb0 = f*b[0] + t*b[1];
+ var mb1 = f*b[1] + t*b[2];
+ var mb2 = f*b[2] + t*b[3];
+ var mmb0 = f * mb0 + t * mb1;
+ var mmb1 = f * mb1 + t * mb2;
+ var mmmb0 = f * mmb0 + t * mmb1;
+
+ var lengthAtT = LengthOnControlPolygon(b[0], mb0, mmb0, mmmb0);
+
+ if (lengthAtT > length + ApproximateComparer.DistanceEpsilon)
+ return 1;
+
+ if (lengthAtT < length - ApproximateComparer.DistanceEpsilon)
+ return -1;
+
+ return 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CubicBezierSegment.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CubicBezierSegment.cs.meta
new file mode 100644
index 000000000..a45e1284e
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CubicBezierSegment.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0bcfc220e0631264ba44cc8179e030cb
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurvatureDerivative.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurvatureDerivative.cs
new file mode 100644
index 000000000..e07af6eb7
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurvatureDerivative.cs
@@ -0,0 +1,69 @@
+using System;
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ ///
+ ///
+ ///
+ public class CurvatureDerivative : IFunction {
+ ICurve curve;
+ double start;
+ double end;
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public CurvatureDerivative(ICurve curve, double start, double end) {
+ this.curve = curve;
+ this.start = start;
+ this.end = end;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double this[double t] {
+ get { return curve.CurvatureDerivative(t); }
+ }
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double Derivative(double t) {
+ return curve.CurvatureSecondDerivative(t);
+ }
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double SecondDerivative(double t) {
+ throw new NotImplementedException();
+ }
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double ThirdDerivative(double t) {
+ throw new NotImplementedException();
+ }
+ ///
+ ///
+ ///
+ public double ParStart {
+ get {return start; }
+ }
+///
+///
+///
+ public double ParEnd {
+ get { return end; }
+ }
+
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurvatureDerivative.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurvatureDerivative.cs.meta
new file mode 100644
index 000000000..144595a67
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurvatureDerivative.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: bb98496b3d0bc244bb7bd7746d7136d6
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Curve.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Curve.cs
new file mode 100644
index 000000000..838b82364
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Curve.cs
@@ -0,0 +1,2109 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+#pragma warning disable 1587
+ ///
+ /// Curve: keeps a sequence of connected ICurves
+ ///
+#pragma warning restore 1587
+#if TEST_MSAGL
+ [Serializable]
+#endif
+#pragma warning disable 1591
+ public partial class Curve : ICurve {
+#pragma warning restore 1591
+ ParallelogramInternalTreeNode pBNode;
+ //the parameter domain is [0,n] where n is the number of segments
+ readonly List segs;
+
+ static bool IsCloseToLineSeg(double a, ref Point ap, double b, ref Point bp, ICurve s, double e) {
+ const double x = 1.0/3;
+ double p = a*x + b*(1 - x);
+
+ Point delta = s[p] - (ap*x + bp*(1 - x));
+ if (delta*delta > e)
+ return false;
+
+ p = a*(1 - x) + b*x;
+
+ delta = s[p] - (ap*(1 - x) + bp*x);
+ if (delta*delta > e)
+ return false;
+
+ p = a*0.5 + b*0.5;
+
+ delta = s[p] - (ap*0.5 + bp*0.5);
+ if (delta*delta > e)
+ return false;
+
+ return true;
+ }
+
+ ///
+ /// interpolates the curve between parameters a and side1 as by a sequence of line segments
+ ///
+ /// start parameter of the interpolation
+ /// start point of the interpolation
+ /// end parameter of the interpolation
+ /// end point of the interpolation
+ /// the interpolated curve
+ /// the maximal allowed distance between the curve and its inerpolation
+ ///
+ public static IList Interpolate(double startParameter, Point start, double endParameter, Point end,
+ ICurve curve, double epsilon) {
+ return Interpolate(startParameter, ref start, endParameter, ref end, curve, epsilon);
+ }
+
+ internal static List Interpolate(double a, ref Point ap, double b, ref Point bp, ICurve s,
+ double eps) {
+ var r = new List();
+ if (IsCloseToLineSeg(a, ref ap, b, ref bp, s, eps))
+ r.Add(new LineSegment(ap, bp));
+ else {
+ double m = 0.5*(a + b);
+ Point mp = s[m];
+ r.AddRange(Interpolate(a, ref ap, m, ref mp, s, eps));
+ r.AddRange(Interpolate(m, ref mp, b, ref bp, s, eps));
+ }
+
+ return r;
+ }
+
+ ///
+ /// this function always produces at least two segments
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static List Interpolate(double eps, double a, ref Point ap, double b, ref Point bp,
+ ICurve s) {
+ double m = (a + b)/2;
+ Point mp = s[m];
+ List ret = Interpolate(a, ref ap, m, ref mp, s, eps*eps);
+ ret.AddRange(Interpolate(m, ref mp, b, ref bp, s, eps*eps));
+ return ret;
+ }
+
+ internal static List Interpolate(ICurve s, double eps) {
+ Point start = s.Start;
+ Point end = s.End;
+ return Interpolate(eps, s.ParStart, ref start, s.ParEnd, ref end, s);
+ }
+
+
+ ///
+ /// this[Reverse[t]]=this[ParEnd+ParStart-t]
+ ///
+ ///
+ public ICurve Reverse() {
+ var ret = new Curve(segs.Count);
+ for (int i = segs.Count - 1; i >= 0; i--)
+ ret.AddSegment(segs[i].Reverse());
+ return ret;
+ }
+
+ ///
+ /// Constructs the curve
+ ///
+ public Curve() {
+ segs = new List();
+ }
+
+ ///
+ /// Constructs a curve with an initial segment capacity.
+ ///
+ public Curve(int segmentCapacity) {
+ segs = new List(segmentCapacity);
+ }
+
+ internal Curve(List segs) {
+ this.segs = segs;
+ ParStart = 0;
+
+ foreach (ICurve s in segs)
+ ParEnd += s.ParEnd - s.ParStart;
+ }
+
+ ///
+ /// this[ParStart]
+ ///
+ public Point Start {
+ get { return segs[0].Start; }
+ }
+
+ ///
+ /// this[ParEnd]
+ ///
+ public Point End {
+ get { return segs[segs.Count - 1].End; }
+ }
+
+ ///
+ /// Returns the trim curve
+ ///
+ ///
+ ///
+ ///
+ public ICurve Trim(double start, double end) {
+ AdjustStartEndEndParametersToDomain(ref start, ref end);
+
+ int sseg;
+ double spar;
+
+ GetSegmentAndParameter(start, out spar, out sseg);
+
+ int eseg;
+ double epar;
+
+ GetSegmentAndParameter(end, out epar, out eseg);
+
+ if (sseg == eseg)
+ return Segments[sseg].Trim(spar, epar);
+
+ var c = new Curve(eseg-sseg+1);
+
+ if (spar < Segments[sseg].ParEnd)
+ c = c.AddSegment(Segments[sseg].Trim(spar, Segments[sseg].ParEnd));
+
+ for (int i = sseg + 1; i < eseg; i++)
+ c = c.AddSegment(Segments[i]);
+
+ if (Segments[eseg].ParStart < epar)
+ c = c.AddSegment(Segments[eseg].Trim(Segments[eseg].ParStart, epar));
+
+ return c;
+ }
+
+ void AdjustStartEndEndParametersToDomain(ref double start, ref double end) {
+ if (start > end) {
+ double t = start;
+ start = end;
+ end = t;
+ }
+
+ if (start < ParStart)
+ start = ParStart;
+
+ if (end > ParEnd)
+ end = ParEnd;
+
+ }
+
+ ///
+ /// Returns the trimmed curve, wrapping around the end if start is greater than end.
+ ///
+ /// The starting parameter
+ /// The ending parameter
+ /// The trimmed curve
+ public ICurve TrimWithWrap(double start, double end) {
+ Debug.Assert(start >= ParStart && start <= ParEnd);
+ Debug.Assert(end >= ParStart && end <= ParEnd);
+ if (start < end)
+ return Trim(start, end) as Curve;
+
+ Debug.Assert(ApproximateComparer.Close(Start, End), "Curve must be closed to wrap");
+ var c = new Curve();
+ c.AddSegment(Trim(start, ParEnd) as Curve);
+ c.AddSegment(Trim(ParStart, end) as Curve);
+ return c;
+ }
+
+ ///
+ /// Adds a segment to the curve
+ ///
+ /// the curve that we add a segment to
+ /// the new curve extended with the segment
+ public Curve AddSegment(ICurve curve) {
+ if (curve == null)
+ return this; //nothing happens
+#if TEST_MSAGL
+ if (segs.Count > 0 && !ApproximateComparer.Close(End, curve.Start, 0.001)) //Layout.Show(this, s);
+ //Console.WriteLine((segs[segs.Count - 1].End - s.Sta rt).Length);
+ throw new InvalidOperationException(); //discontinuous curve
+#endif
+ ParStart = 0;
+ var c = curve as Curve;
+ if (c == null) {
+ segs.Add(curve);
+ ParEnd += curve.ParEnd - curve.ParStart;
+ }
+ else {
+ IncreaseSegmentCapacity(c.Segments.Count);
+ foreach (ICurve cc in c.Segments) {
+ segs.Add(cc);
+ ParEnd += cc.ParEnd - cc.ParStart;
+ }
+ }
+ return this;
+ }
+
+ ///
+ /// Increases the capacity of the segment list.
+ /// Call this before adding a bunch of segments to prevent the list from having to resize multiple times.
+ ///
+ public void IncreaseSegmentCapacity(int additionalAmount) {
+ int capacityNeeded = segs.Count + additionalAmount;
+ if (segs.Capacity < capacityNeeded) {
+ int capacity = Math.Max(segs.Capacity, 1);
+ while (capacity < capacityNeeded) capacity *= 2;
+ segs.Capacity = capacity;
+ }
+ }
+
+ internal Curve AddSegs(ICurve a, ICurve b) {
+ Curve c = AddSegment(a);
+ return c.AddSegment(b);
+ }
+
+ internal Curve AddSegs(ICurve a, ICurve b, ICurve c, ICurve d) {
+ IncreaseSegmentCapacity(4);
+ Curve r = AddSegment(a);
+ r = r.AddSegment(b);
+ r = r.AddSegment(c);
+ return r.AddSegment(d);
+ }
+
+ //internal Curve AddSegs(IEnumerable segments) {
+ // Curve r = new Curve(this.Segs);
+ // foreach (ICurve segment in segments)
+ // r = r.AddSeg(segment);
+
+ // return r;
+ //}
+ ///
+ /// Returns the list of the curve segments
+ ///
+ public IList Segments {
+ get { return segs; }
+ }
+
+ ///
+ /// A tree of ParallelogramNodes covering the curve.
+ /// This tree is used in curve intersections routines.
+ ///
+ ///
+ public ParallelogramNodeOverICurve ParallelogramNodeOverICurve {
+ get {
+ lock (this) {
+ if (pBNode != null)
+ return pBNode;
+
+ pBNode = new ParallelogramInternalTreeNode(this, ParallelogramNodeOverICurve.DefaultLeafBoxesOffset);
+ var parallelograms = new List();
+ foreach (ICurve curveSeg in segs) {
+ ParallelogramNodeOverICurve pBoxNode = curveSeg.ParallelogramNodeOverICurve;
+
+ parallelograms.Add(pBoxNode.Parallelogram);
+
+ pBNode.AddChild(pBoxNode);
+ }
+ pBNode.Parallelogram = Parallelogram.GetParallelogramOfAGroup(parallelograms);
+
+ return pBNode;
+ }
+ }
+ }
+
+ ///
+ /// finds an intersection between to curves,
+ ///
+ ///
+ ///
+ /// if set to true parameters of the intersection point will be given in the curve parametrization
+ /// InterseciontInfo or null if there are no intersections
+ public static IntersectionInfo CurveCurveIntersectionOne(ICurve curve0, ICurve curve1, bool liftIntersection) {
+ ValidateArg.IsNotNull(curve0, "curve0");
+ ValidateArg.IsNotNull(curve1, "curve1");
+ Debug.Assert(curve0 != curve1, "curve0 == curve1");
+#if DEBUGGLEE
+// double c0S = curve0.ParStart, c1S = curve1.ParStart;
+// if (CurvesAreCloseAtParams(curve0, curve1, c0S, c1S)) {
+// double mc0 = 0.5 * (curve0.ParStart + curve0.ParEnd);
+// double mc1 = 0.5 * (curve1.ParStart + curve1.ParEnd);
+// double c0E = curve0.ParEnd;
+// if (CurvesAreCloseAtParams(curve0, curve1, mc0, mc1)) {
+// double c1E = curve1.ParEnd;
+// CurvesAreCloseAtParams(curve0, curve1, c0E, c1E);
+// throw new InvalidOperationException();
+// }
+// }
+#endif
+ //recurse down to find all PBLeaf pairs which intesect and try to cross their segments
+
+ IntersectionInfo ret = CurveCurveXWithParallelogramNodesOne(curve0.ParallelogramNodeOverICurve,
+ curve1.ParallelogramNodeOverICurve);
+
+ if (liftIntersection && ret != null)
+ ret = LiftIntersectionToCurves(curve0, curve1, ret);
+
+
+ return ret;
+ }
+
+ ///
+ /// calculates all intersections between curve0 and curve1
+ ///
+ ///
+ ///
+ /// if set to true parameters of the intersection point will be given in the curve parametrization
+ ///
+ public static IList GetAllIntersections(ICurve curve0, ICurve curve1, bool liftIntersections) {
+ ValidateArg.IsNotNull(curve0, "curve0");
+ ValidateArg.IsNotNull(curve1, "curve1");
+ Debug.Assert(curve0 != curve1);
+#if DEBUGGLEE
+// var c0S = curve0.ParStart;
+// var c1S = curve1.ParStart;
+// var c0E = curve0.ParEnd;
+// var c1E = curve1.ParEnd;
+// if (CurvesAreCloseAtParams(curve0, curve1, c0S, c1S)) {
+// if (CurvesAreCloseAtParams(curve0, curve1, c0E, c1E)) {
+// var mc0 = 0.5*(curve0.ParStart + curve0.ParEnd);
+// var mc1 = 0.5*(curve1.ParStart + curve1.ParEnd);
+// if (CurvesAreCloseAtParams(curve0, curve1, mc0, mc1))
+// throw new InvalidOperationException();
+// }
+// }
+#endif
+
+ var lineSeg = curve0 as LineSegment;
+ if (lineSeg != null)
+ return GetAllIntersectionsOfLineAndICurve(lineSeg, curve1, liftIntersections);
+
+ return GetAllIntersectionsInternal(curve0, curve1, liftIntersections);
+ }
+
+
+ internal static IList GetAllIntersectionsInternal(ICurve curve0, ICurve curve1,
+ bool liftIntersections) {
+ //recurse down to find all PBLeaf pairs which intesect and try to cross their segments
+
+ var intersections = new List();
+ CurveCurveXWithParallelogramNodes(curve0.ParallelogramNodeOverICurve, curve1.ParallelogramNodeOverICurve,
+ ref intersections);
+
+ if (liftIntersections)
+ for (int i = 0; i < intersections.Count; i++)
+ intersections[i] = LiftIntersectionToCurves(curve0, curve1, intersections[i]);
+
+
+ //fix the parameters - adjust them to the curve
+ return intersections;
+ }
+
+ static IList GetAllIntersectionsOfLineAndICurve(LineSegment lineSeg, ICurve iCurve,
+ bool liftIntersections) {
+ var poly = iCurve as Polyline;
+ if (poly != null)
+ return GetAllIntersectionsOfLineAndPolyline(lineSeg, poly);
+
+ var curve = iCurve as Curve;
+ if (curve != null)
+ return GetAllIntersectionsOfLineAndCurve(lineSeg, curve, liftIntersections);
+
+ var roundedRect = iCurve as RoundedRect;
+ if (roundedRect != null)
+ return GetAllIntersectionsOfLineAndRoundedRect(lineSeg, roundedRect, liftIntersections);
+
+ var ellipse = iCurve as Ellipse;
+ if (ellipse != null && ellipse.IsArc())
+ return GetAllIntersectionsOfLineAndArc(lineSeg, ellipse);
+
+ return GetAllIntersectionsInternal(lineSeg, iCurve, liftIntersections);
+ }
+
+ static IList GetAllIntersectionsOfLineAndRoundedRect(LineSegment lineSeg, RoundedRect roundedRect, bool liftIntersections) {
+ var ret = GetAllIntersectionsOfLineAndCurve(lineSeg, roundedRect.Curve, liftIntersections);
+ if(liftIntersections)
+ foreach (var intersectionInfo in ret) {
+ intersectionInfo.Segment1 = roundedRect;
+ }
+ return ret;
+ }
+
+ static IList GetAllIntersectionsOfLineAndCurve(LineSegment lineSeg, Curve curve, bool liftIntersections) {
+ var ret = new List();
+ var lineParallelogram = lineSeg.ParallelogramNodeOverICurve;
+ var curveParallelogramRoot = curve.ParallelogramNodeOverICurve;
+ if (Parallelogram.Intersect(lineParallelogram.Parallelogram, curveParallelogramRoot.Parallelogram) == false)
+ return ret;
+ var parOffset = 0.0;
+ foreach (var seg in curve.Segments) {
+ var iiList = GetAllIntersections(lineSeg, seg, false);
+ if(liftIntersections) {
+ foreach (var intersectionInfo in iiList) {
+ intersectionInfo.Par1 += parOffset-seg.ParStart;
+ intersectionInfo.Segment1 = curve;
+ }
+ parOffset += seg.ParEnd - seg.ParStart;
+ }
+ foreach (var intersectionInfo in iiList) {
+ if(! AlreadyInside(ret, intersectionInfo))
+ ret.Add(intersectionInfo);
+ }
+ }
+
+ return ret;
+ }
+
+ static bool AlreadyInside(List ret, IntersectionInfo intersectionInfo) {
+ for(int i=0;i GetAllIntersectionsOfLineAndArc(LineSegment lineSeg, Ellipse ellipse) {
+ Point lineDir = lineSeg.End - lineSeg.Start;
+ var ret = new List();
+ double segLength = lineDir.Length;
+ if (segLength < ApproximateComparer.DistanceEpsilon) {
+ if (ApproximateComparer.Close((lineSeg.Start - ellipse.Center).Length, ellipse.AxisA.Length)) {
+ double angle = Point.Angle(ellipse.AxisA, lineSeg.Start - ellipse.Center);
+ if (ellipse.ParStart - ApproximateComparer.Tolerance <= angle) {
+ angle = Math.Max(angle, ellipse.ParStart);
+ if (angle <= ellipse.ParEnd + ApproximateComparer.Tolerance) {
+ angle = Math.Min(ellipse.ParEnd, angle);
+ ret.Add(new IntersectionInfo(0, angle, lineSeg.Start, lineSeg, ellipse));
+ }
+ }
+ }
+ return ret;
+ }
+
+ Point perp = lineDir.Rotate90Ccw()/segLength;
+ double segProjection = (lineSeg.Start - ellipse.Center)*perp;
+ Point closestPointOnLine = ellipse.Center + perp*segProjection;
+
+ double rad = ellipse.AxisA.Length;
+ double absSegProj = Math.Abs(segProjection);
+ if (rad < absSegProj - ApproximateComparer.DistanceEpsilon)
+ return ret; //we don't have an intersection
+ lineDir = perp.Rotate90Cw();
+ if (ApproximateComparer.Close(rad, absSegProj))
+ TryToAddPointToLineCircleCrossing(lineSeg, ellipse, ret, closestPointOnLine, segLength, lineDir);
+ else {
+ Debug.Assert(rad > absSegProj);
+ double otherLeg = Math.Sqrt(rad*rad - segProjection*segProjection);
+ TryToAddPointToLineCircleCrossing(lineSeg, ellipse, ret, closestPointOnLine + otherLeg*lineDir,
+ segLength, lineDir);
+ TryToAddPointToLineCircleCrossing(lineSeg, ellipse, ret, closestPointOnLine - otherLeg*lineDir,
+ segLength, lineDir);
+ }
+
+ return ret;
+ }
+
+ static void TryToAddPointToLineCircleCrossing(LineSegment lineSeg,
+ Ellipse ellipse, List ret, Point point, double segLength, Point lineDir) {
+ Point ds = point - lineSeg.Start;
+ Point de = point - lineSeg.End;
+ double t = ds*lineDir;
+ if (t < -ApproximateComparer.DistanceEpsilon)
+ return;
+ t = Math.Max(t, 0);
+ if (t > segLength + ApproximateComparer.DistanceEpsilon)
+ return;
+ t = Math.Min(t, segLength);
+ t /= segLength;
+
+ double angle = Point.Angle(ellipse.AxisA, point - ellipse.Center);
+ if (ellipse.ParStart - ApproximateComparer.Tolerance <= angle) {
+ angle = Math.Max(angle, ellipse.ParStart);
+ if (angle <= ellipse.ParEnd + ApproximateComparer.Tolerance) {
+ angle = Math.Min(ellipse.ParEnd, angle);
+ ret.Add(new IntersectionInfo(t, angle, point, lineSeg, ellipse));
+ }
+ }
+ }
+
+ static IList GetAllIntersectionsOfLineAndPolyline(LineSegment lineSeg, Polyline poly) {
+ var ret = new List();
+ double offset = 0.0;
+ double par0, par1;
+ Point x;
+ PolylinePoint polyPoint = poly.StartPoint;
+ for (; polyPoint != null && polyPoint.Next != null; polyPoint = polyPoint.Next) {
+ if (CrossTwoLineSegs(lineSeg.Start, lineSeg.End, polyPoint.Point, polyPoint.Next.Point, 0, 1, 0, 1,
+ out par0, out par1, out x)) {
+ AdjustSolution(lineSeg.Start, lineSeg.End, polyPoint.Point, polyPoint.Next.Point, ref par0, ref par1,
+ ref x);
+ if (!OldIntersection(ret, ref x))
+ ret.Add(new IntersectionInfo(par0, offset + par1, x, lineSeg, poly));
+ }
+ offset++;
+ }
+ if (poly.Closed)
+ if (CrossTwoLineSegs(lineSeg.Start, lineSeg.End, polyPoint.Point, poly.Start, 0, 1, 0, 1, out par0,
+ out par1, out x)) {
+ AdjustSolution(lineSeg.Start, lineSeg.End, polyPoint.Point, poly.Start, ref par0, ref par1, ref x);
+ if (!OldIntersection(ret, ref x))
+ ret.Add(new IntersectionInfo(par0, offset + par1, x, lineSeg, poly));
+ }
+
+ return ret;
+ }
+
+ static void AdjustSolution(Point aStart, Point aEnd, Point bStart, Point bEnd, ref double par0, ref double par1,
+ ref Point x) {
+ //adjust the intersection if it is close to the ends of the segs
+ if (ApproximateComparer.CloseIntersections(x, aStart)) {
+ x = aStart;
+ par0 = 0;
+ }
+ else if (ApproximateComparer.CloseIntersections(x, aEnd)) {
+ x = aEnd;
+ par0 = 1;
+ }
+
+ if (ApproximateComparer.CloseIntersections(x, bStart)) {
+ x = bStart;
+ par1 = Math.Floor(par1);
+ }
+ else if (ApproximateComparer.CloseIntersections(x, bEnd)) {
+ x = bEnd;
+ par1 = Math.Ceiling(par1);
+ }
+ }
+
+#if DEBUGGLEE
+ static bool CurvesAreCloseAtParams(ICurve c0, ICurve c1, double c0S, double c1S) {
+ return Close(c0[c0S], c1[c1S]) && Close(c0.Derivative(c0S), c1.Derivative(c1S));
+ }
+#endif
+
+
+ static IntersectionInfo CurveCurveXWithParallelogramNodesOne(ParallelogramNodeOverICurve n0,
+ ParallelogramNodeOverICurve n1) {
+ if (!Parallelogram.Intersect(n0.Parallelogram, n1.Parallelogram))
+ //Console.WriteLine("Boxes {0} and {1} do not intersect", n0.Box, n1.Box);
+ return null;
+ var n0Pb = n0 as ParallelogramInternalTreeNode;
+ var n1Pb = n1 as ParallelogramInternalTreeNode;
+ if (n0Pb != null && n1Pb != null)
+ foreach (ParallelogramNodeOverICurve n00 in n0Pb.Children)
+ foreach (ParallelogramNodeOverICurve n11 in n1Pb.Children) {
+ IntersectionInfo x = CurveCurveXWithParallelogramNodesOne(n00, n11);
+ if (x != null) return x;
+ }
+ else if (n1Pb != null)
+ foreach (ParallelogramNodeOverICurve n in n1Pb.Children) {
+ IntersectionInfo x = CurveCurveXWithParallelogramNodesOne(n0, n);
+ if (x != null) return x;
+ }
+ else if (n0Pb != null)
+ foreach (ParallelogramNodeOverICurve n in n0Pb.Children) {
+ IntersectionInfo x = CurveCurveXWithParallelogramNodesOne(n, n1);
+ if (x != null) return x;
+ }
+ else
+ return CrossOverIntervalsOne(n0, n1);
+
+ return null;
+ }
+
+
+ static void CurveCurveXWithParallelogramNodes(ParallelogramNodeOverICurve n0, ParallelogramNodeOverICurve n1,
+ ref List intersections) {
+ if (!Parallelogram.Intersect(n0.Parallelogram, n1.Parallelogram))
+ //Console.WriteLine("Boxes {0} and {1} do not intersect", n0.Box, n1.Box);
+ return;
+ //if(Routing.debug)
+ // Testing.InternalTest.Show(n0, n1, n0.Seg, n1.Seg);
+ var n0Pb = n0 as ParallelogramInternalTreeNode;
+ var n1Pb = n1 as ParallelogramInternalTreeNode;
+ if (n0Pb != null && n1Pb != null)
+ foreach (ParallelogramNodeOverICurve n00 in n0Pb.Children)
+ foreach (ParallelogramNodeOverICurve n11 in n1Pb.Children)
+ CurveCurveXWithParallelogramNodes(n00, n11, ref intersections);
+ else if (n1Pb != null)
+ foreach (ParallelogramNodeOverICurve n in n1Pb.Children)
+ CurveCurveXWithParallelogramNodes(n0, n, ref intersections);
+ else if (n0Pb != null)
+ foreach (ParallelogramNodeOverICurve n in n0Pb.Children)
+ CurveCurveXWithParallelogramNodes(n, n1, ref intersections);
+ else intersections = CrossOverIntervals(n0, n1, intersections);
+ }
+
+ static IntersectionInfo CrossOverIntervalsOne(ParallelogramNodeOverICurve n0, ParallelogramNodeOverICurve n1) {
+ //both are leafs
+ var l0 = n0 as ParallelogramLeaf;
+ var l1 = n1 as ParallelogramLeaf;
+ double d0 = (l0.High - l0.Low)/2;
+ double d1 = (l1.High - l1.Low)/2;
+
+ for (int i = 1; i < 2; i++) {
+ double p0 = i*d0 + l0.Low;
+ for (int j = 1; j < 2; j++) {
+ double p1 = j*d1 + l1.Low;
+ double aSol, bSol;
+ Point x;
+ bool r;
+ if (l0.Chord == null && l1.Chord == null)
+ r = CrossWithinIntervalsWithGuess(n0.Seg, n1.Seg, l0.Low, l0.High, l1.Low, l1.High,
+ p0, p1, out aSol, out bSol, out x);
+ else if (l0.Chord != null && l1.Chord == null) {
+ r = CrossWithinIntervalsWithGuess(l0.Chord, n1.Seg, 0, 1, l1.Low, l1.High,
+ 0.5*i,
+ p1, out aSol, out bSol, out x);
+ if (r)
+ aSol = l0.Low + aSol*(l0.High - l0.Low);
+ }
+ else if (l0.Chord == null) {
+ r = CrossWithinIntervalsWithGuess(n0.Seg, l1.Chord,
+ l0.Low, l0.High, 0, 1, p0,
+ 0.5*j, out aSol, out bSol, out x);
+ if (r)
+ bSol = l1.Low + bSol*(l1.High - l1.Low);
+ }
+ else //if (l0.Chord != null && l1.Chord != null)
+ {
+ r = CrossWithinIntervalsWithGuess(l0.Chord, l1.Chord,
+ 0, 1, 0, 1, 0.5*i,
+ 0.5*j, out aSol, out bSol, out x);
+ if (r) {
+ bSol = l1.Low + bSol*(l1.High - l1.Low);
+ aSol = l0.Low + aSol*(l0.High - l0.Low);
+ }
+ }
+
+ if (r)
+ return CreateIntersectionOne(l0, l1, aSol, bSol, x);
+ }
+ }
+
+ return GoDeeperOne(l0, l1);
+ }
+
+ static List CrossOverIntervals(ParallelogramNodeOverICurve n0, ParallelogramNodeOverICurve n1,
+ List intersections) {
+ //both are leafs
+ var l0 = n0 as ParallelogramLeaf;
+ var l1 = n1 as ParallelogramLeaf;
+ double d0 = (l0.High - l0.Low)/2;
+ double d1 = (l1.High - l1.Low)/2;
+ bool found = false;
+
+ for (int i = 1; i < 2; i++) {
+ double p0 = i*d0 + l0.Low;
+ for (int j = 1; j < 2; j++) {
+ double p1 = j*d1 + l1.Low;
+
+
+ double aSol, bSol;
+ Point x;
+
+
+ bool r;
+ if (l0.Chord == null && l1.Chord == null)
+ r = CrossWithinIntervalsWithGuess(n0.Seg, n1.Seg, l0.Low, l0.High, l1.Low, l1.High,
+ p0, p1, out aSol, out bSol, out x);
+ else if (l0.Chord != null && l1.Chord == null) {
+ r = CrossWithinIntervalsWithGuess(l0.Chord, n1.Seg, 0, 1, l1.Low, l1.High,
+ 0.5*i,
+ p1, out aSol, out bSol, out x);
+ if (r)
+ aSol = l0.Low + aSol*(l0.High - l0.Low);
+ }
+ else if (l0.Chord == null) {
+//&& l1.Chord != null)
+ r = CrossWithinIntervalsWithGuess(n0.Seg, l1.Chord,
+ l0.Low, l0.High, 0, 1, p0,
+ 0.5*j, out aSol, out bSol, out x);
+ if (r)
+ bSol = l1.Low + bSol*(l1.High - l1.Low);
+ }
+ else //if (l0.Chord != null && l1.Chord != null)
+ {
+ r = CrossWithinIntervalsWithGuess(l0.Chord, l1.Chord,
+ 0, 1, 0, 1, 0.5*i,
+ 0.5*j, out aSol, out bSol, out x);
+ if (r) {
+ bSol = l1.Low + bSol*(l1.High - l1.Low);
+ aSol = l0.Low + aSol*(l0.High - l0.Low);
+ }
+ }
+
+ if (r) {
+ AddIntersection(l0, l1, intersections, aSol, bSol, x);
+ found = true;
+ }
+ }
+ }
+
+ if (!found)
+ GoDeeper(ref intersections, l0, l1);
+ return intersections;
+ }
+
+ static void AddIntersection(ParallelogramLeaf n0, ParallelogramLeaf n1, List intersections,
+ double aSol, double bSol,
+ Point x) {
+ //adjust the intersection if it is close to the ends of the segs
+ if (ApproximateComparer.CloseIntersections(x, n0.Seg[n0.Low])) {
+ x = n0.Seg[n0.Low];
+ aSol = n0.Low;
+ }
+ else if (ApproximateComparer.CloseIntersections(x, n0.Seg[n0.High])) {
+ x = n0.Seg[n0.High];
+ aSol = n0.High;
+ }
+
+ if (ApproximateComparer.CloseIntersections(x, n1.Seg[n1.Low])) {
+ x = n1.Seg[n1.Low];
+ bSol = n1.Low;
+ }
+ else if (ApproximateComparer.CloseIntersections(x, n1.Seg[n1.High])) {
+ x = n1.Seg[n1.High];
+ bSol = n1.High;
+ }
+
+ bool oldIntersection = OldIntersection(intersections, ref x);
+ if (!oldIntersection) {
+ var xx = new IntersectionInfo(aSol, bSol, x, n0.Seg, n1.Seg);
+ intersections.Add(xx);
+ }
+
+ return;
+ }
+
+ ///
+ /// returns true if the intersection exists already
+ ///
+ ///
+ ///
+ ///
+ static bool OldIntersection(List intersections, ref Point x) {
+ bool oldIntersection = false;
+ //we don't expect many intersections so it's ok just go through all of them
+ foreach (IntersectionInfo ii in intersections)
+ if ((x - ii.IntersectionPoint).Length < ApproximateComparer.DistanceEpsilon*100)
+ //please no close intersections
+ {
+ oldIntersection = true;
+ break;
+ }
+ return oldIntersection;
+ }
+
+ static IntersectionInfo CreateIntersectionOne(ParallelogramLeaf n0, ParallelogramLeaf n1,
+ double aSol, double bSol, Point x) {
+ //adjust the intersection if it is close to the ends of the segs
+ if (ApproximateComparer.CloseIntersections(x, n0.Seg[n0.Low])) {
+ x = n0.Seg[n0.Low];
+ aSol = n0.Low;
+ }
+ else if (ApproximateComparer.CloseIntersections(x, n0.Seg[n0.High])) {
+ x = n0.Seg[n0.High];
+ aSol = n0.High;
+ }
+
+ if (ApproximateComparer.CloseIntersections(x, n1.Seg[n1.Low])) {
+ x = n1.Seg[n1.Low];
+ bSol = n1.Low;
+ }
+ else if (ApproximateComparer.CloseIntersections(x, n1.Seg[n1.High])) {
+ x = n1.Seg[n1.High];
+ bSol = n1.High;
+ }
+
+ return new IntersectionInfo(aSol, bSol, x, n0.Seg, n1.Seg);
+ }
+
+ static IntersectionInfo LiftIntersectionToCurves(
+ ICurve c0, ICurve c1, double aSol,
+ double bSol, Point x, ICurve seg0, ICurve seg1) {
+ double a = LiftParameterToCurve(c0, aSol - seg0.ParStart, seg0);
+ double b = LiftParameterToCurve(c1, bSol - seg1.ParStart, seg1);
+
+ return new IntersectionInfo(a, b, x, c0, c1);
+ }
+
+ static IntersectionInfo DropIntersectionToSegs(IntersectionInfo xx) {
+ ICurve seg0;
+ double par0;
+
+ if (xx.Segment0 is Curve)
+ (xx.Segment0 as Curve).GetSegmentAndParameter(xx.Par0, out par0, out seg0);
+ else {
+ par0 = xx.Par0;
+ seg0 = xx.Segment0;
+ }
+
+ ICurve seg1;
+ double par1;
+
+ if (xx.Segment1 is Curve)
+ (xx.Segment1 as Curve).GetSegmentAndParameter(xx.Par1, out par1, out seg1);
+ else {
+ par1 = xx.Par1;
+ seg1 = xx.Segment1;
+ }
+
+ return new IntersectionInfo(par0, par1, xx.IntersectionPoint, seg0, seg1);
+ }
+
+ internal static IntersectionInfo LiftIntersectionToCurves(ICurve c0, ICurve c1, IntersectionInfo xx) {
+ return LiftIntersectionToCurves(c0, c1, xx.Par0, xx.Par1, xx.IntersectionPoint, xx.Segment0, xx.Segment1);
+ }
+
+ internal static double LiftParameterToCurve(ICurve curve, double par, ICurve seg) {
+ if (curve == seg)
+ return par;
+
+ var c = curve as Curve;
+
+ if (c != null) {
+ double offset = 0;
+ foreach (ICurve s in c.Segments) {
+ if (s == seg)
+ return par + offset;
+ offset += ParamSpan(s);
+ }
+ }
+ var roundedRect = curve as RoundedRect;
+ if (roundedRect != null)
+ return LiftParameterToCurve(roundedRect.Curve, par, seg);
+
+ throw new InvalidOperationException(); //"bug in LiftParameterToCurve");
+ }
+
+ static double ParamSpan(ICurve s) {
+ return s.ParEnd - s.ParStart;
+ }
+
+ static IntersectionInfo GoDeeperOne(ParallelogramLeaf l0, ParallelogramLeaf l1) {
+ double eps = ApproximateComparer.DistanceEpsilon;
+ // Console.WriteLine("did not find an intersection");
+ if (l0.LeafBoxesOffset > eps && l1.LeafBoxesOffset > eps) {
+ // Console.WriteLine("going deeper on both with offset {0} {1}", l0.LeafBoxesOffset / 2, l1.LeafBoxesOffset / 2);
+
+
+ ParallelogramNodeOverICurve nn0 = ParallelogramNodeOverICurve.CreateParallelogramNodeForCurveSeg(
+ l0.Low, l0.High, l0.Seg, l0.LeafBoxesOffset/2);
+ ParallelogramNodeOverICurve nn1 = ParallelogramNodeOverICurve.CreateParallelogramNodeForCurveSeg(
+ l1.Low, l1.High, l1.Seg, l1.LeafBoxesOffset/2);
+ return CurveCurveXWithParallelogramNodesOne(nn0, nn1);
+ }
+ if (l0.LeafBoxesOffset > eps) {
+ // Console.WriteLine("go deeper on the left");
+ ParallelogramNodeOverICurve nn0 = ParallelogramNodeOverICurve.CreateParallelogramNodeForCurveSeg(
+ l0.Low, l0.High, l0.Seg, l0.LeafBoxesOffset/2);
+ return CurveCurveXWithParallelogramNodesOne(nn0, l1);
+ }
+ if (l1.LeafBoxesOffset > eps) {
+ // Console.WriteLine("go deeper on the right");
+ ParallelogramNodeOverICurve nn1 = ParallelogramNodeOverICurve.CreateParallelogramNodeForCurveSeg(
+ l1.Low, l1.High, l1.Seg, l1.LeafBoxesOffset/2);
+ return CurveCurveXWithParallelogramNodesOne(l0, nn1);
+ }
+ //just cross LineSegs and adjust the solutions if the segments are not straight lines
+ Point l0Low = l0.Seg[l0.Low];
+ Point l0High = l0.Seg[l0.High];
+ if (!ApproximateComparer.Close(l0Low, l0High)) {
+ Point l1Low = l1.Seg[l1.Low];
+ Point l1High = l1.Seg[l1.High];
+ if (!ApproximateComparer.Close(l1Low, l1High)) {
+ LineSegment ls0 = l0.Seg is LineSegment ? l0.Seg as LineSegment : new LineSegment(l0Low, l0High);
+ LineSegment ls1 = l1.Seg is LineSegment ? l1.Seg as LineSegment : new LineSegment(l1Low, l1High);
+
+ double asol, bsol;
+ Point x;
+ bool r = CrossWithinIntervalsWithGuess(ls0, ls1, 0, 1, 0, 1, 0.5, 0.5, out asol, out bsol, out x);
+ if (r) {
+ AdjustParameters(l0, ls0, l1, ls1, x, ref asol, ref bsol);
+ return CreateIntersectionOne(l0, l1, asol, bsol, x);
+ }
+ }
+ }
+ return null;
+ }
+
+ static void GoDeeper(ref List intersections, ParallelogramLeaf l0, ParallelogramLeaf l1) {
+ double eps = ApproximateComparer.DistanceEpsilon;
+ // Console.WriteLine("did not find an intersection");
+ if (l0.LeafBoxesOffset > eps && l1.LeafBoxesOffset > eps) {
+ // Console.WriteLine("going deeper on both with offset {0} {1}", l0.LeafBoxesOffset / 2, l1.LeafBoxesOffset / 2);
+
+
+ ParallelogramNodeOverICurve nn0 = ParallelogramNodeOverICurve.CreateParallelogramNodeForCurveSeg(
+ l0.Low, l0.High, l0.Seg, l0.LeafBoxesOffset/2);
+ ParallelogramNodeOverICurve nn1 = ParallelogramNodeOverICurve.CreateParallelogramNodeForCurveSeg(
+ l1.Low, l1.High, l1.Seg, l1.LeafBoxesOffset/2);
+ CurveCurveXWithParallelogramNodes(nn0, nn1, ref intersections);
+ }
+ else if (l0.LeafBoxesOffset > eps) {
+ // Console.WriteLine("go deeper on the left");
+ ParallelogramNodeOverICurve nn0 = ParallelogramNodeOverICurve.CreateParallelogramNodeForCurveSeg(
+ l0.Low, l0.High, l0.Seg, l0.LeafBoxesOffset/2);
+ CurveCurveXWithParallelogramNodes(nn0, l1, ref intersections);
+ }
+ else if (l1.LeafBoxesOffset > eps) {
+ // Console.WriteLine("go deeper on the right");
+ ParallelogramNodeOverICurve nn1 = ParallelogramNodeOverICurve.CreateParallelogramNodeForCurveSeg(
+ l1.Low, l1.High, l1.Seg, l1.LeafBoxesOffset/2);
+ CurveCurveXWithParallelogramNodes(l0, nn1, ref intersections);
+ }
+ else {
+ //just cross LineSegs since the polylogramms are so thin
+ Point l0Low = l0.Seg[l0.Low];
+ Point l0High = l0.Seg[l0.High];
+ if (!ApproximateComparer.Close(l0Low, l0High)) {
+ Point l1Low = l1.Seg[l1.Low];
+ Point l1High = l1.Seg[l1.High];
+ if (!ApproximateComparer.Close(l1Low, l1High)) {
+ LineSegment ls0 = l0.Seg is LineSegment ? l0.Seg as LineSegment : new LineSegment(l0Low, l0High);
+ LineSegment ls1 = l1.Seg is LineSegment ? l1.Seg as LineSegment : new LineSegment(l1Low, l1High);
+
+ double asol, bsol;
+ Point x;
+ bool r = CrossWithinIntervalsWithGuess(ls0, ls1, 0, 1, 0, 1, 0.5, 0.5, out asol, out bsol, out x);
+ if (r) {
+ AdjustParameters(l0, ls0, l1, ls1, x, ref asol, ref bsol);
+ AddIntersection(l0, l1, intersections, asol, bsol, x);
+ }
+ }
+ }
+ }
+ }
+
+
+ static void AdjustParameters(ParallelogramLeaf l0, LineSegment ls0, ParallelogramLeaf l1, LineSegment ls1,
+ Point x, ref double asol, ref double bsol) {
+ if (ls0 != l0.Seg && l0.Seg is Polyline == false) //l0.Seg is not a LineSegment and not a polyline
+ asol = l0.Seg.ClosestParameter(x); //we need to find the correct parameter
+ else
+ asol = l0.Low + asol*(l0.High - l0.Low);
+ if (ls1 != l1.Seg && l1.Seg is Polyline == false) //l1.Seg is not a LineSegment and not a polyline
+ bsol = l1.Seg.ClosestParameter(x); //we need to find the correct parameter
+ else
+ bsol = l1.Low + bsol*(l1.High - l1.Low);
+ }
+
+ // static void ShowNodesWithCurves(Curve c0, Curve point0, ParallelogramNodeOverICurve n0, ParallelogramNodeOverICurve n1)
+ //{
+ // System.Windows.Forms.Form f = new System.Windows.Forms.Form();
+ // GViewer v = new GViewer();
+ // f.SuspendLayout();
+ // f.Controls.Add(v);
+ // Graph g = new Graph("", "");
+
+ // foreach (ICurve cs in c0.Segs)
+ // if (cs is CubicBezierSeg)
+ // {
+ // g.bezierCurves.Add(cs as CubicBezierSeg);
+ // }
+
+ // foreach (ICurve cs in point0.Segs)
+ // if (cs is CubicBezierSeg)
+ // {
+ // g.bezierCurves.Add(cs as CubicBezierSeg);
+ // }
+ // else if (cs is Ellipse)
+ // {
+ // Ellipse el = cs as Ellipse;
+ // int n = 64;
+ // double delta = (el.ParEnd - el.ParStart) / n;
+ // for (int i = 0; i < n; i++)
+ // {
+ // Point s = el[el.ParStart + i * delta];
+ // Point e = el[el.ParStart + (i + 1) * delta];
+ // g.bezierCurves.Add(new CubicBezierSeg(s, 0.5 * (s + e), 0.5 * (s + e), e));
+ // }
+
+
+ // }
+
+ // // g.ellipses.Add(cs as Ellipse);
+
+ // g.pBoxes.Add(n0.Box);
+ // g.pBoxes.Add(n1.Box);
+ // v.Graph = g;
+ // Rectangle side1 = new Rectangle();
+ // for (int i = 0; i < 4; i++)
+ // side1.Add(n0.Box.Vertex(i));
+ // for (int i = 0; i < 4; i++)
+ // side1.Add(n0.Box.Vertex(i));
+ // g.GraphAttr.BoundingBox = side1;
+
+ // v.Dock = System.Windows.Forms.DockStyle.Fill;
+ // f.Size = new Size(System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Size.Width / 2, System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Size.Height / 2);
+ // f.ResumeLayout();
+ // f.ShowDialog();
+
+ //}
+
+ //internal static void ShowBezierSegs(params CubicBezierSeg[] segs)
+ //{
+ // System.Windows.Forms.Form f = new System.Windows.Forms.Form();
+ // GViewer v = new GViewer();
+ // f.SuspendLayout();
+ // f.Controls.Add(v);
+ // Graph g = new Graph("", "");
+
+ // foreach (CubicBezierSeg cs in segs)
+ // g.bezierCurves.Add(cs);
+
+
+ // v.Graph = g;
+ // v.Dock = System.Windows.Forms.DockStyle.Fill;
+ // f.Size = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Size;
+ // f.ResumeLayout();
+ // f.ShowDialog();
+
+ //}
+
+ static double lineSegThreshold = 0.05;
+
+ ///
+ /// The distance between the start and end point of a curve segment for which we consider the segment as a line segment
+ ///
+ public static double LineSegmentThreshold {
+ get { return lineSegThreshold; }
+ set { lineSegThreshold = value; }
+ }
+
+ ///
+ /// returns the segment correspoinding to t and the segment parameter
+ ///
+ ///
+ ///
+ ///
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t"),
+ SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#"),
+ SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")]
+ public void GetSegmentAndParameter(double t, out double par, out ICurve segment) {
+ double u = ParStart; //u is the sum of param domains
+ foreach (ICurve sg in segs) {
+ double domLen = sg.ParEnd - sg.ParStart;
+ if (t >= u && t <= u + domLen) {
+ par = t - u + sg.ParStart;
+ segment = sg;
+ return;
+ }
+ u += domLen;
+ }
+ segment = segs.Last();
+ par = segment.ParEnd;
+ // throw new InvalidOperationException(); // parameter is out of domain of the curve
+ // //remove later
+ // par = 0;
+ // segment = null;
+ /*g = Math.Max(t, Math.Min(t, nOfSegments));
+
+ if (t == nOfSegments)
+ {
+ seg = nOfSegments - 1;
+ g = 1;
+ }
+ else
+ {
+ seg = (int)t;
+ g = t - seg;
+ }*/
+ }
+
+ internal void GetSegmentAndParameter(double t, out double par, out int segIndex) {
+ double u = 0; //u is the sum of param domains
+ segIndex = 0;
+ par = -1;
+
+ for (int i = 0; i < segs.Count; i++)
+ {
+ var sg = segs[i];
+ double domLen = sg.ParEnd - sg.ParStart;
+ if (t >= u && (t <= u + domLen || (i == segs.Count-1 && ApproximateComparer.Compare(t,u+domLen)<=0) ))
+ {
+ par = t - u + sg.ParStart;
+ return;
+ }
+ segIndex++;
+ u += domLen;
+ }
+
+ throw new InvalidOperationException(string.Format("Check, args t:{0}, par:{1}, segIndex:{2} and u:{3}", t, par, segIndex, u)); // parameter is out of domain of the curve*/
+ /*g = Math.Max(t, Math.Min(t, nOfSegments));
+
+ if (t == nOfSegments)
+ {
+ seg = nOfSegments - 1;
+ g = 1;
+ }
+ else
+ {
+ seg = (int)t;
+ g = t - seg;
+ }*/
+ }
+
+
+ ///
+ /// Returns the point on the curve corresponding to parameter t
+ ///
+ ///
+ ///
+ public Point this[double t] {
+ get {
+ double par;
+ ICurve seg;
+
+ GetSegmentAndParameter(t, out par, out seg);
+
+ return seg[par];
+ }
+ }
+
+ ///
+ /// first derivative at t
+ ///
+ /// the parameter where the derivative is calculated
+ ///
+ public Point Derivative(double t) {
+ double par;
+ ICurve seg;
+
+ GetSegmentAndParameter(t, out par, out seg);
+ return seg.Derivative(par);
+ }
+
+ ///
+ /// second derivative
+ ///
+ ///
+ ///
+ public Point SecondDerivative(double t) {
+ double par;
+ ICurve seg;
+
+ GetSegmentAndParameter(t, out par, out seg);
+ return seg.SecondDerivative(par);
+ }
+
+ ///
+ /// third derivative
+ ///
+ /// the parameter of the derivative
+ ///
+ public Point ThirdDerivative(double t) {
+ double par;
+ ICurve seg;
+
+ GetSegmentAndParameter(t, out par, out seg);
+ return seg.ThirdDerivative(par);
+ }
+
+ // For curves A(s) and B(t), if you have some evidence that
+ // there is at most one intersection point, and you have some guess for the parameters (s0, t0)...
+ // You are trying to bring to (0,0) the vector( F^2 s , F^2 t ).
+ //F(s,t) = A(s) - B(t). To minimize F^2,
+ //You get the system of equations to solve for ds and dt:
+ //F*Fs + (F*Fss + Fs*Fs)ds + (F*Fst + Fs*Ft)dt = 0
+ //F*Ft + (F*Fst + Fs*Ft)ds + (F*Ftt + Ft*Ft)dt = 0
+ //
+ //Where F = F(si,ti), Fs and Ft are the first partials at si, ti, Fxx are the second partials,
+ // and s(i+1) = si+ds, t(i+1) = ti+dt.
+ //Of course you have to make sure that ds and dt do not take you out of your domain. This will converge if the curves have 2nd order continuity and your starting parameters are reasonable. It is not a good method for situations that are not well behaved, but it is really simple.
+
+ internal static bool CrossWithinIntervalsWithGuess(
+ ICurve a, ICurve b,
+ double amin, double amax,
+ double bmin, double bmax,
+ double aGuess,
+ double bGuess,
+ out double aSolution,
+ out double bSolution, out Point x) {
+ bool r;
+ if (a is LineSegment && b is LineSegment)
+ if (CrossTwoLineSegs(a.Start, a.End, b.Start, b.End, amin, amax, bmin, bmax, out aSolution,
+ out bSolution, out x))
+ return true;
+ //it also handles the case of almost parallel segments
+ Point aPoint;
+ Point bPoint;
+ r = MinDistWithinIntervals(a,
+ b,
+ amin,
+ amax, bmin,
+ bmax,
+ aGuess, bGuess,
+ out aSolution,
+ out bSolution, out aPoint, out bPoint);
+
+
+ x = 0.5*(aPoint + bPoint);
+ Point aMinusB = aPoint - bPoint;
+ //if side1 is false tnen the values a and side1 are meaningless
+ bool ret = r && aMinusB*aMinusB < ApproximateComparer.DistanceEpsilon*ApproximateComparer.DistanceEpsilon;
+ return ret;
+ }
+
+ static bool CrossTwoLineSegs(Point aStart, Point aEnd, Point bStart, Point bEnd, double amin, double amax,
+ double bmin, double bmax, out double aSolution, out double bSolution, out Point x) {
+ Point u = aEnd - aStart;
+ Point v = bStart - bEnd;
+ Point w = bStart - aStart;
+ bool r = LinearSystem2.Solve(u.X, v.X, w.X, u.Y, v.Y, w.Y, out aSolution, out bSolution);
+ x = aStart + aSolution*u;
+
+ if (r) {
+ if (aSolution < amin - ApproximateComparer.Tolerance)
+ return false;
+
+ aSolution = Math.Max(aSolution, amin);
+
+ if (aSolution > amax + ApproximateComparer.Tolerance)
+ return false;
+
+ aSolution = Math.Min(aSolution, amax);
+
+ if (bSolution < bmin - ApproximateComparer.Tolerance)
+ return false;
+
+ bSolution = Math.Max(bSolution, bmin);
+
+ if (bSolution > bmax + ApproximateComparer.Tolerance)
+ return false;
+
+ bSolution = Math.Min(bSolution, bmax);
+
+ // if(!ApproximateComparer.Close(x,B[bSolution]))
+ // throw new InvalidOperationException();// ("segs");
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Decides if the point lies inside, outside or on the curve
+ ///
+ /// the point under test
+ /// a closed curve
+ /// the point position characteristic
+ public static PointLocation PointRelativeToCurveLocation(Point point, ICurve curve) {
+ System.Diagnostics.Debug.Assert(!Double.IsNaN(point.X) && !Double.IsNaN(point.Y));
+ ValidateArg.IsNotNull(curve, "curve");
+ if (!curve.BoundingBox.Contains(point))
+ return PointLocation.Outside;
+
+ double l = 2*curve.BoundingBox.Diagonal; //l should be big enough for the line to exit outside of the curve
+
+ const double degree = Math.PI/180.0;
+ int inside = 0;
+ for (int i = 13; i < 360; i += 13) {
+ var lineDir = new Point(Math.Cos(i*degree), Math.Sin(i*degree));
+ var ls = new LineSegment(point, point + l*lineDir);
+
+ IList intersections = GetAllIntersections(ls, curve, true);
+
+
+ //SugiyamaLayoutSettings.Show(ls, curve);
+ // CurveSerializer.Serialize("cornerC:\\tmp\\ls",ls);
+ // CurveSerializer.Serialize("cornerC:\\tmp\\pol",curve);
+ if (AllIntersectionsAreGood(intersections, curve)) {
+ foreach (IntersectionInfo xx in intersections)
+ if (ApproximateComparer.Close(xx.IntersectionPoint, point))
+ return PointLocation.Boundary;
+ bool insideThisTime = intersections.Count%2 == 1;
+ //to be on the safe side we need to get the same result at least twice
+ if (insideThisTime)
+ inside++;
+ else
+ inside--;
+
+ if (inside >= 2)
+ return PointLocation.Inside;
+ if (inside <= -2)
+ return PointLocation.Outside;
+ }
+ }
+ //if all intersections are not good then we probably have the point on the boundaryCurve
+
+ return PointLocation.Boundary;
+ }
+
+
+ // static bool debug;
+ static bool AllIntersectionsAreGood(IList intersections, ICurve polygon) {
+ // If this isn't a Curve, try a Polyline.
+ //TODO: fix this to avoid the cast
+ var polyCurve = polygon as Curve;
+ if (null == polyCurve) {
+ var polyLine = polygon as Polyline;
+ if (null != polyLine)
+ polyCurve = polyLine.ToCurve();
+ }
+ if (null != polyCurve)
+ foreach (IntersectionInfo xx in intersections)
+ if (!RealCut(DropIntersectionToSegs(xx), polyCurve, false))
+ return false;
+ return true;
+ }
+
+
+ ///
+ /// Returns true if curves do not touch in the intersection point
+ ///
+ ///
+ ///
+ /// if set to true and first curve is closed will return true
+ /// only when the second curve cuts the first one from the inside
+ ///
+ public static bool RealCutWithClosedCurve(IntersectionInfo xx, Curve polygon, bool onlyFromInsideCuts) {
+ ValidateArg.IsNotNull(xx, "xx");
+ ValidateArg.IsNotNull(polygon, "polygon");
+ ICurve sseg = xx.Segment0;
+ ICurve pseg = xx.Segment1;
+ double spar = xx.Par0;
+ double ppar = xx.Par1;
+ Point x = xx.IntersectionPoint;
+
+ //normalised tangent to spline
+ Point ts = sseg.Derivative(spar).Normalize();
+ Point pn = pseg.Derivative(ppar).Normalize().Rotate(Math.PI/2);
+
+ if (ApproximateComparer.Close(x, pseg.End)) {
+ //so pseg enters the spline
+ ICurve exitSeg = null;
+ for (int i = 0; i < polygon.Segments.Count; i++)
+ if (polygon.Segments[i] == pseg) {
+ exitSeg = polygon.Segments[(i + 1)%polygon.Segments.Count];
+ break;
+ }
+
+ if (exitSeg == null)
+ throw new InvalidOperationException(); //"exitSeg==null");
+
+ Point tsn = ts.Rotate((Math.PI/2));
+
+ bool touch = (tsn*pseg.Derivative(pseg.ParEnd))*(tsn*exitSeg.Derivative(exitSeg.ParStart)) <
+ ApproximateComparer.Tolerance;
+
+ return !touch;
+ }
+
+ if (ApproximateComparer.Close(x, pseg.Start)) {
+ //so pseg exits the spline
+ ICurve enterSeg = null;
+ for (int i = 0; i < polygon.Segments.Count; i++)
+ if (polygon.Segments[i] == pseg) {
+ enterSeg = polygon.Segments[i > 0 ? (i - 1) : polygon.Segments.Count - 1];
+ break;
+ }
+
+ Point tsn = ts.Rotate((Math.PI/2));
+ bool touch = (tsn*pseg.Derivative(pseg.ParStart))*
+ (tsn*enterSeg.Derivative(enterSeg.ParEnd)) < ApproximateComparer.Tolerance;
+
+ return !touch;
+ }
+
+ double d = ts*pn;
+ if (onlyFromInsideCuts)
+ return d > ApproximateComparer.DistanceEpsilon;
+ return Math.Abs(d) > ApproximateComparer.DistanceEpsilon;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// consider a cut good only if the segment cuts the polygon from inside
+ ///
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "polyline")]
+ public static bool RealCut(IntersectionInfo xx, Curve polyline, bool onlyFromInsideCuts) {
+ ValidateArg.IsNotNull(xx, "xx");
+ ValidateArg.IsNotNull(polyline, "polyline");
+ ICurve sseg = xx.Segment0;
+ ICurve pseg = xx.Segment1;
+ double spar = xx.Par0;
+ double ppar = xx.Par1;
+ Point x = xx.IntersectionPoint;
+
+
+ //normalised tangent to spline
+ Point ts = sseg.Derivative(spar).Normalize();
+ Point pn = pseg.Derivative(ppar).Normalize().Rotate(Math.PI/2);
+
+ if (ApproximateComparer.Close(x, pseg.End)) {
+ //so pseg enters the spline
+ ICurve exitSeg = null;
+ for (int i = 0; i < polyline.Segments.Count - 1; i++)
+ if (polyline.Segments[i] == pseg) {
+ exitSeg = polyline.Segments[i + 1];
+ break;
+ }
+
+ if (exitSeg == null)
+ return false; //hit the end of the polyline
+
+ Point tsn = ts.Rotate((Math.PI/2));
+
+ bool touch = (tsn*pseg.Derivative(pseg.ParEnd))*(tsn*exitSeg.Derivative(exitSeg.ParStart)) <
+ ApproximateComparer.Tolerance;
+
+ return !touch;
+ }
+
+ if (ApproximateComparer.Close(x, pseg.Start)) {
+ //so pseg exits the spline
+ ICurve enterSeg = null;
+ for (int i = polyline.segs.Count - 1; i > 0; i--)
+ if (polyline.Segments[i] == pseg) {
+ enterSeg = polyline.Segments[i - 1];
+ break;
+ }
+ if (enterSeg == null)
+ return false;
+ Point tsn = ts.Rotate((Math.PI/2));
+ bool touch = (tsn*pseg.Derivative(pseg.ParStart))*
+ (tsn*enterSeg.Derivative(enterSeg.ParEnd)) < ApproximateComparer.Tolerance;
+
+ return !touch;
+ }
+
+ double d = ts*pn;
+ if (onlyFromInsideCuts)
+ return d > ApproximateComparer.DistanceEpsilon;
+ return Math.Abs(d) > ApproximateComparer.DistanceEpsilon;
+ }
+
+ internal static bool MinDistWithinIntervals(
+ ICurve a, ICurve b, double aMin, double aMax, double bMin, double bMax,
+ double aGuess, double bGuess, out double aSolution, out double bSolution, out Point aPoint, out Point bPoint) {
+ var md = new MinDistCurveCurve(a, b, aMin, aMax, bMin, bMax, aGuess, bGuess);
+ md.Solve();
+ aSolution = md.ASolution;
+ aPoint = md.APoint;
+ bSolution = md.BSolution;
+ bPoint = md.BPoint;
+
+ return md.Status;
+ }
+
+#if DEBUGCURVES
+ public override string ToString()
+ {
+ bool poly = true;
+ foreach (ICurve s in segs)
+ if (s is LineSeg == false)
+ {
+ poly = false;
+ break;
+ }
+
+ string ret;
+ if (!poly)
+ {
+ ret = "{";
+
+ foreach (ICurve seg in Segs)
+ {
+ ret += seg + ",";
+ }
+
+
+ return ret + "}";
+ }
+ ret = "{";
+ if (segs.Count > 0)
+ ret += segs[0].Start.X.ToString() + "," + segs[0].Start.Y.ToString()+" ";
+ foreach(LineSeg s in segs)
+ ret += s.End.X.ToString() + "," + s.End.Y.ToString() + " ";
+ return ret + "}";
+ }
+#endif
+
+ ///
+ /// Offsets the curve in the direction of dir
+ ///
+ ///
+ ///
+ ///
+ public ICurve OffsetCurve(double offset, Point dir) {
+ return null;
+ }
+
+ ///
+ /// The bounding rectangle of the curve
+ ///
+ public Rectangle BoundingBox {
+ get {
+ if (Segments.Count == 0)
+ return new Rectangle(0, 0, -1, -1);
+#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=369 there are no structs in js
+ Rectangle b = Segments[0].BoundingBox.Clone();
+#else
+ Rectangle b = Segments[0].BoundingBox;
+#endif
+ for (int i = 1; i < Segments.Count; i++)
+ b.Add(Segments[i].BoundingBox);
+
+ return b;
+ }
+ }
+
+ /////
+ ///// //sort intersections by the a parameter
+ /////
+ /////
+ /////
+ /////
+ //internal static void AddRealIntersectionsToDictionaryForClosedCurve(SortedDictionary dictionary, ICurve coeff, Curve side1) {
+ // StopIntersect si = new StopIntersect((new XExaminer(dictionary, coeff, side1)).IntersectionExaminerForClosedCurveWithIntersectionLifting);
+ // Curve.GetAllIntersections(coeff, side1, si, true);
+ //}
+
+ //internal static void AddRealIntersectionsWithTreeToDictionaryForClosedCurves(SortedDictionary dictionary, ICurve coeff, ParallelogramNode bound) {
+ // if( Parallelogram.Intersect( coeff.ParallelogramNodeOverICurve.Parallelogram, bound.Parallelogram)){
+ // ParallelogramBinaryTreeNode treeNode = bound as ParallelogramBinaryTreeNode;
+ // if (treeNode != null) {
+ // AddRealIntersectionsWithTreeToDictionaryForClosedCurves(dictionary, coeff, treeNode.LeftSon);
+ // AddRealIntersectionsWithTreeToDictionaryForClosedCurves(dictionary, coeff, treeNode.RightSon);
+ // } else
+ // AddRealIntersectionsToDictionaryForClosedCurve(dictionary, coeff, (Curve)((ParallelogramNodeOverICurve)bound).Seg);
+ // }
+ //}
+
+ #region ICurve Members
+
+ ///
+ /// clones the curve.
+ ///
+ /// the cloned curve
+ public ICurve Clone() {
+ var c = new Curve(Segments.Count);
+ foreach (ICurve seg in Segments)
+ c.AddSegment(seg.Clone());
+ return c;
+ }
+
+ #endregion
+
+ #region ICurve Members
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double GetParameterAtLength(double length) {
+ var parSpan = 0.0;
+ foreach (var seg in Segments) {
+ var segL = seg.Length;
+ if (segL >= length)
+ return parSpan+seg.GetParameterAtLength(length);
+
+ length -= segL;
+ parSpan += seg.ParEnd - seg.ParStart;
+ }
+ return ParEnd;
+ }
+
+ ///
+ /// returns a parameter t such that the distance between curve[t] and targetPoint is minimal
+ /// and t belongs to the closed segment [low,high]
+ ///
+ /// the point to find the closest point
+ /// the upper bound of the parameter
+ /// the low bound of the parameter
+ ///
+ public double ClosestParameterWithinBounds(Point targetPoint, double low, double high) {
+ double par = 0;
+ double dist = Double.MaxValue;
+ double offset = 0;
+ foreach (ICurve seg in Segments) {
+ if (offset > high)
+ break; //we are out of the [low, high] segment
+ double segParamSpan = ParamSpan(seg);
+ double segEnd = offset + segParamSpan;
+ if (segEnd >= low) {
+ //we are in business
+ double segLow = Math.Max(seg.ParStart, seg.ParStart + (low - offset));
+ double segHigh = Math.Min(seg.ParEnd, seg.ParStart + (high - offset));
+ Debug.Assert(segHigh >= segLow);
+ double t = seg.ClosestParameterWithinBounds(targetPoint, segLow, segHigh);
+ Point d = targetPoint - seg[t];
+ double dd = d*d;
+ if (dd < dist) {
+ par = offset + t - seg.ParStart;
+ dist = dd;
+ }
+ }
+ offset += segParamSpan;
+ }
+ return par;
+ }
+
+ ///
+ /// returns a parameter t such that the distance between curve[t] and a is minimal
+ ///
+ ///
+ ///
+ public double ClosestParameter(Point targetPoint) {
+ double par = 0;
+ double dist = Double.MaxValue;
+ double offset = 0;
+ foreach (ICurve c in Segments) {
+ double t = c.ClosestParameter(targetPoint);
+ Point d = targetPoint - c[t];
+ double dd = d*d;
+ if (dd < dist) {
+ par = offset + t - c.ParStart;
+ dist = dd;
+ }
+ offset += ParamSpan(c);
+ }
+ return par;
+ }
+
+ #endregion
+
+ #region Curve concatenations
+
+ ///
+ ///adds a line segment to the curve
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Curve AddLineSegment(Curve curve, Point pointA, Point pointB) {
+ ValidateArg.IsNotNull(curve, "curve");
+ return curve.AddSegment(new LineSegment(pointA, pointB));
+ }
+
+
+ //static internal void AddLineSegment(Curve c, double x, double y, Point b) {
+ // AddLineSegment(c, new Point(x, y), b);
+ //}
+
+ ///
+ /// adds a line segment to the curve
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x"),
+ SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "y")]
+ public static void AddLineSegment(Curve curve, double x0, double y0, double x1, double y1) {
+ AddLineSegment(curve, new Point(x0, y0), new Point(x1, y1));
+ }
+
+ ///
+ /// adds a line segment to the curve
+ ///
+ ///
+ ///
+ ///
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "y"),
+ SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x"),
+ SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "c")]
+ public static void ContinueWithLineSegment(Curve c, double x, double y) {
+ ValidateArg.IsNotNull(c, "c");
+ AddLineSegment(c, c.End, new Point(x, y));
+ }
+
+ ///
+ /// adds a line segment to the curve
+ ///
+ ///
+ ///
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x"),
+ SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "c"),
+ SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Seg")]
+ public static void ContinueWithLineSegment(Curve c, Point x) {
+ ValidateArg.IsNotNull(c, "c");
+ AddLineSegment(c, c.End, x);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public static void CloseCurve(Curve curve) {
+ ValidateArg.IsNotNull(curve, "curve");
+ ContinueWithLineSegment(curve, curve.Start);
+ }
+
+ #endregion
+
+ ///
+ /// left derivative at t
+ ///
+ /// the parameter where the derivative is calculated
+ ///
+ public Point LeftDerivative(double t) {
+ ICurve seg = TryToGetLeftSegment(t);
+ if (seg != null)
+ return seg.Derivative(seg.ParEnd);
+ return Derivative(t);
+ }
+
+ ///
+ /// right derivative at t
+ ///
+ /// the parameter where the derivative is calculated
+ ///
+ public Point RightDerivative(double t) {
+ ICurve seg = TryToGetRightSegment(t);
+ if (seg != null)
+ return seg.Derivative(seg.ParStart);
+ return Derivative(t);
+ }
+
+
+ ICurve TryToGetLeftSegment(double t) {
+ if (Math.Abs(t - ParStart) < ApproximateComparer.Tolerance) {
+ if (Start == End)
+ return Segments[Segments.Count - 1];
+ return null;
+ }
+ foreach (ICurve seg in Segments) {
+ t -= ParamSpan(seg);
+ if (Math.Abs(t) < ApproximateComparer.Tolerance)
+ return seg;
+ }
+ return null;
+ }
+
+ ICurve TryToGetRightSegment(double t) {
+ if (Math.Abs(t - ParEnd) < ApproximateComparer.Tolerance) {
+ if (Start == End)
+ return Segments[0];
+ return null;
+ }
+
+ foreach (ICurve seg in Segments) {
+ if (Math.Abs(t) < ApproximateComparer.Tolerance)
+ return seg;
+
+ t -= ParamSpan(seg);
+ }
+ return null;
+ }
+
+ ///
+ /// gets the closest point together with its parameter
+ ///
+ ///
+ ///
+ ///
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#")]
+ public static double ClosestParameterWithPoint(ICurve curve, Point location, out Point pointOnCurve) {
+ ValidateArg.IsNotNull(curve, "curve");
+ double t = curve.ClosestParameter(location);
+ pointOnCurve = curve[t];
+ return t;
+ }
+
+ ///
+ /// gets the point on the curve that is closest to the given point
+ ///
+ /// the curve to examine
+ /// the target point
+ /// the closest point
+ public static Point ClosestPoint(ICurve curve, Point location) {
+ ValidateArg.IsNotNull(curve, "curve");
+ return curve[curve.ClosestParameter(location)];
+ }
+
+ ///
+ /// Tests whether the first curve is inside the second.
+ /// We suppose that the curves are convex and they are
+ /// not degenerated into a point
+ ///
+ ///
+ ///
+ ///
+ public static bool CurveIsInsideOther(ICurve innerCurve, ICurve outerCurve) {
+ ValidateArg.IsNotNull(innerCurve, "innerCurve");
+ ValidateArg.IsNotNull(outerCurve, "outerCurve");
+ if (!outerCurve.BoundingBox.Contains(innerCurve.BoundingBox)) return false;
+ IList xx = GetAllIntersections(innerCurve, outerCurve, true);
+ if (xx.Count == 0) return NonIntersectingCurveIsInsideOther(innerCurve, outerCurve);
+ if (xx.Count == 1) //it has to be a touch
+ return innerCurve.Start != xx[0].IntersectionPoint
+ ? PointRelativeToCurveLocation(innerCurve.Start, outerCurve) == PointLocation.Inside
+ : PointRelativeToCurveLocation(innerCurve[(innerCurve.ParStart + innerCurve.ParEnd)/2],
+ outerCurve) == PointLocation.Inside;
+ return
+ PointsBetweenIntersections(innerCurve, xx).All(
+ p => PointRelativeToCurveLocation(p, outerCurve) != PointLocation.Outside);
+ }
+
+ // Return points between but not including the intersections.
+ internal static IEnumerable PointsBetweenIntersections(ICurve a, IList xx) {
+ xx.OrderBy(x => x.Par0);
+ for (int i = 0; i < xx.Count - 1; i++)
+ yield return a[(xx[i].Par0 + xx[i + 1].Par0)/2];
+ //take care of the last interval
+ double start = xx[xx.Count - 1].Par0;
+ double end = xx[0].Par0;
+ double len = a.ParEnd - start + end - a.ParStart;
+ double middle = start + len/2;
+ if (middle > a.ParEnd)
+ middle = a.ParStart + middle - a.ParEnd;
+ yield return a[middle];
+ }
+
+ static bool NonIntersectingCurveIsInsideOther(ICurve a, ICurve b) {
+ ValidateArg.IsNotNull(a, "a");
+ ValidateArg.IsNotNull(b, "b");
+ // Due to rounding, even curves with 0 intersections may return Boundary.
+ for (double par = a.ParStart; par < a.ParEnd; par += 0.5) {
+ // continue as long as we have boundary points.
+ PointLocation parLoc = PointRelativeToCurveLocation(a[par], b);
+ if (PointLocation.Boundary != parLoc) return PointLocation.Inside == parLoc;
+ }
+
+ // All points so far were on border so it is not considered inside; test the End.
+ return PointLocation.Outside != PointRelativeToCurveLocation(a.End, b);
+ }
+
+ ///
+ /// Tests whether the interiors of two closed convex curves intersect
+ ///
+ /// convex closed curve
+ /// convex closed curve
+ ///
+ public static bool ClosedCurveInteriorsIntersect(ICurve curve1, ICurve curve2) {
+ ValidateArg.IsNotNull(curve1, "curve1");
+ ValidateArg.IsNotNull(curve2, "curve2");
+ if (!curve2.BoundingBox.Intersects(curve1.BoundingBox))
+ return false;
+ IList xx = GetAllIntersections(curve1, curve2, true);
+ if (xx.Count == 0)
+ return NonIntersectingCurveIsInsideOther(curve1, curve2) ||
+ NonIntersectingCurveIsInsideOther(curve2, curve1);
+ if (xx.Count == 1) //it is a touch
+ return curve1.Start != xx[0].IntersectionPoint
+ ? PointRelativeToCurveLocation(curve1.Start, curve2) == PointLocation.Inside
+ : PointRelativeToCurveLocation(curve1[(curve1.ParStart + curve1.ParEnd)/2], curve2) ==
+ PointLocation.Inside ||
+ curve2.Start != xx[0].IntersectionPoint
+ ? PointRelativeToCurveLocation(curve2.Start, curve1) == PointLocation.Inside
+ : PointRelativeToCurveLocation(curve2[(curve2.ParStart + curve2.ParEnd)/2], curve1) ==
+ PointLocation.Inside;
+ return
+ PointsBetweenIntersections(curve1, xx).Any(
+ p => PointRelativeToCurveLocation(p, curve2) == PointLocation.Inside);
+ }
+
+ #region ICurve Members
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double Curvature(double t) {
+ ICurve seg;
+ double par;
+ GetSegmentAndParameter(t, out par, out seg);
+ return seg.Curvature(par);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double CurvatureDerivative(double t) {
+ throw new NotImplementedException();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double CurvatureSecondDerivative(double t) {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ ///
+ ///
+ ///True if the curves intersect each other.
+ public static bool CurvesIntersect(ICurve curve1, ICurve curve2) {
+ return curve1 == curve2 || (CurveCurveIntersectionOne(curve1, curve2, false) != null);
+ }
+
+ internal static CubicBezierSegment CreateBezierSeg(double kPrev, double kNext, Site a, Site b, Site c) {
+ Point s = kPrev*a.Point + (1 - kPrev)*b.Point;
+ Point e = kNext*c.Point + (1 - kNext)*b.Point;
+ Point t = (2.0/3.0)*b.Point;
+ return new CubicBezierSegment(s, s/3.0 + t, t + e/3.0, e);
+ }
+
+ internal static CubicBezierSegment CreateBezierSeg(Point a, Point b, Point perp, int i) {
+ Point d = perp*i;
+ return new CubicBezierSegment(a, a + d, b + d, b);
+ }
+
+ internal static bool FindCorner(Site a, out Site b, out Site c) {
+ c = null; // to silence the compiler
+ b = a.Next;
+ if (b.Next == null)
+ return false; //no corner has been found
+ c = b.Next;
+ return c != null;
+ }
+
+ internal static ICurve TrimEdgeSplineWithNodeBoundaries(ICurve sourceBoundary,
+ ICurve targetBoundary, ICurve spline,
+ bool narrowestInterval) {
+
+ var start = spline.ParStart;
+ var end = spline.ParEnd;
+ if (sourceBoundary != null)
+ FindNewStart(spline, ref start, sourceBoundary, narrowestInterval);
+ if (targetBoundary != null)
+ FindNewEnd(spline, targetBoundary, narrowestInterval, ref end);
+
+ double st = Math.Min(start, end);
+ double en = Math.Max(start, end);
+ return st < en ? spline.Trim(st, en) : spline;
+ }
+
+ static void FindNewEnd(ICurve spline, ICurve targetBoundary, bool narrowestInterval, ref double end) {
+ //SugiyamaLayoutSettings.Show(c, spline);
+ IList intersections = GetAllIntersections(spline, targetBoundary, true);
+ if (intersections.Count == 0) {
+ end = spline.ParEnd;
+ return;
+ }
+ if (narrowestInterval) {
+ end = spline.ParEnd;
+ foreach (IntersectionInfo xx in intersections)
+ if (xx.Par0 < end)
+ end = xx.Par0;
+ }
+ else {
+ //looking for the last intersection
+ end = spline.ParStart;
+ foreach (IntersectionInfo xx in intersections)
+ if (xx.Par0 > end)
+ end = xx.Par0;
+ }
+ }
+
+ static void FindNewStart(ICurve spline, ref double start, ICurve sourceBoundary, bool narrowestInterval) {
+ IList intersections = GetAllIntersections(spline, sourceBoundary, true);
+ if (intersections.Count == 0) {
+ start = spline.ParStart;
+ return;
+ }
+ if (narrowestInterval) {
+ start = spline.ParStart;
+ foreach (IntersectionInfo xx in intersections)
+ if (xx.Par0 > start)
+ start = xx.Par0;
+ }
+ else {
+ start = spline.ParEnd;
+ foreach (IntersectionInfo xx in intersections)
+ if (xx.Par0 < start)
+ start = xx.Par0;
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Polyline PolylineAroundClosedCurve(ICurve curve) {
+ Polyline ret;
+ var ellipse = curve as Ellipse;
+ if (ellipse != null)
+ ret = RefineEllipse(ellipse);
+ else {
+ var poly = curve as Polyline;
+ if (poly != null)
+ return poly;
+ var c = curve as Curve;
+ if (c != null && AllSegsAreLines(c)) {
+ ret = new Polyline();
+ foreach (LineSegment ls in c.Segments)
+ ret.AddPoint(ls.Start);
+ ret.Closed = true;
+ if (!ret.IsClockwise())
+ ret = (Polyline) ret.Reverse();
+ }
+ else
+ ret = StandardRectBoundary(curve);
+ }
+ return ret;
+ }
+
+ static bool AllSegsAreLines(Curve c) {
+ foreach (ICurve s in c.Segments)
+ if (!(s is LineSegment))
+ return false;
+ return true;
+ }
+
+ ///
+ /// this code only works for the standard ellipse
+ ///
+ ///
+ ///
+ static Polyline RefineEllipse(Ellipse ellipse) {
+ Polyline rect = StandardRectBoundary(ellipse);
+ var dict = new SortedDictionary();
+ double a = Math.PI/4;
+ double w = ellipse.BoundingBox.Width;
+ double h = ellipse.BoundingBox.Height;
+ double l = Math.Sqrt(w*w + h*h);
+ for (int i = 0; i < 4; i++) {
+ double t = a + i*Math.PI/2; // parameter
+ Point p = ellipse[t]; //point on the ellipse
+ Point tan = l*(ellipse.Derivative(t).Normalize()); //make it long enough
+
+ var ls = new LineSegment(p - tan, p + tan);
+ //System.Diagnostics.Debug.Assert(ls.Start.X > -10000);
+ foreach (IntersectionInfo ix in GetAllIntersections(rect, ls, true))
+ dict[ix.Par0] = ix.IntersectionPoint;
+ }
+
+ Debug.Assert(dict.Count > 0);
+ return new Polyline(dict.Values) {Closed = true};
+ }
+
+ internal static Polyline StandardRectBoundary(ICurve curve) {
+ Rectangle bbox = curve.BoundingBox;
+ return bbox.Perimeter();
+ }
+
+ ///
+ /// Create a closed Polyline from a rectangle
+ ///
+ ///
+ public static Polyline PolyFromBox(Rectangle rectangle) {
+ var p = new Polyline();
+ p.AddPoint(rectangle.LeftTop);
+ p.AddPoint(rectangle.RightTop);
+ p.AddPoint(rectangle.RightBottom);
+ p.AddPoint(rectangle.LeftBottom);
+ p.Closed = true;
+ return p;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Curve.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Curve.cs.meta
new file mode 100644
index 000000000..462e62121
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Curve.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 57f594541b890cb47891b3a90e1b5045
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveFactory.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveFactory.cs
new file mode 100644
index 000000000..f8504c445
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveFactory.cs
@@ -0,0 +1,397 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Msagl.Core.Layout;
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ ///
+ /// the helper class to create curves
+ ///
+ public sealed class CurveFactory {
+ CurveFactory() { }
+ ///
+ /// Creates an ellipse by the length of axes and the center
+ ///
+ ///
+ ///
+ ///
+ ///
+ static public ICurve CreateEllipse(double radiusInXDirection, double radiusInYDirection, Point center) {
+ return new Ellipse(radiusInXDirection, radiusInYDirection, center);
+ }
+
+ ///
+ /// Creates an ellipse by the length of axes and the center
+ ///
+ ///
+ ///
+ ///
+ static public ICurve CreateCircle(double radius, Point center) {
+ return new Ellipse(radius, radius, center);
+ }
+
+ ///
+ /// Create a rectangle with smoothed corners
+ ///
+ /// the rectangle width
+ /// the rectangle height
+ /// the length of the x axis of the corner smoothing ellipse
+ /// the length of the y axis of the corner smoothing ellipse
+ /// the rectangle center
+ ///
+ static public ICurve CreateRectangleWithRoundedCorners(double width, double height, double radiusInXDirection, double radiusInYDirection, Point center) {
+ var box = new Rectangle(center.X - width / 2, center.Y - height / 2, center.X + width / 2, center.Y + height / 2);
+ return new RoundedRect(box, radiusInXDirection, radiusInYDirection);
+ }
+
+ ///
+ /// Create in the specified curve, a rectangle with smoothed corners
+ ///
+ ///
+ /// the rectangle width
+ /// the rectangle height
+ /// the length of the x axis of the corner smoothing ellipse
+ /// the length of the y axis of the corner smoothing ellipse
+ /// the rectangle center
+ ///
+ static internal void CreateRectangleWithRoundedCorners(Curve c, double width, double height, double radiusInXDirection, double radiusInYDirection, Point center) {
+ if (radiusInXDirection == 0 || radiusInYDirection == 0) {
+ CreateRectangle(c, width, height, center);
+ return;
+ }
+ double w = width / 2;
+ if (radiusInXDirection > w / 2)
+ radiusInXDirection = w / 2;
+ double h = height / 2;
+ if (radiusInYDirection > h / 2)
+ radiusInYDirection = h / 2;
+ double x = center.X;
+ double y = center.Y;
+ double ox = w - radiusInXDirection;
+ double oy = h - radiusInYDirection;
+ double top = y + h;
+ double bottom = y - h;
+ double left = x - w;
+ double right = x + w;
+ //ellipse's axises
+ Point a = new Point(radiusInXDirection, 0);
+ Point b = new Point(0, radiusInYDirection);
+
+ c.IncreaseSegmentCapacity(8);
+
+ if (ox > 0)
+ c.AddSegment(new LineSegment(new Point(x - ox, bottom), new Point(x + ox, bottom)));
+ c.AddSegment(new Ellipse(1.5 * Math.PI, 2 * Math.PI, a, b, x + ox, y - oy));
+ if (oy > 0)
+ c.AddSegment(new LineSegment(new Point(right, y - oy), new Point(right, y + oy)));
+ c.AddSegment(new Ellipse(0, 0.5 * Math.PI, a, b, x + ox, y + oy));
+ if (ox > 0)
+ c.AddSegment(new LineSegment(new Point(x + ox, top), new Point(x - ox, top)));
+ c.AddSegment(new Ellipse(0.5 * Math.PI, Math.PI, a, b, x - ox, y + oy));
+ if (oy > 0)
+ c.AddSegment(new LineSegment(new Point(left, y + oy), new Point(left, y - oy)));
+ c.AddSegment(new Ellipse(Math.PI, 1.5 * Math.PI, a, b, x - ox, y - oy));
+ }
+
+ ///
+ /// create a box of the given width and height at center.
+ ///
+ ///
+ ///
+ ///
+ ///
+ static public ICurve CreateRectangle(double width, double height, Point center) {
+ double w = width / 2;
+ double h = height / 2;
+ double x = center.X;
+ double y = center.Y;
+ Curve c = new Curve();
+ Point[] p = new Point[] { new Point(x - w, y - h), new Point(x + w, y - h), new Point(x + w, y + h), new Point(x - w, y + h) };
+ c.AddSegs(new LineSegment(p[0], p[1]), new LineSegment(p[1], p[2]), new LineSegment(p[2], p[3]), new LineSegment(p[3], p[0]));
+ return c;
+ }
+
+ ///
+ /// create a box of the given width and height at center.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ static internal void CreateRectangle(Curve c, double width, double height, Point center) {
+ double w = width / 2;
+ double h = height / 2;
+ double x = center.X;
+ double y = center.Y;
+ Point[] p = new Point[] { new Point(x - w, y - h), new Point(x + w, y - h), new Point(x + w, y + h), new Point(x - w, y + h) };
+ c.AddSegs(new LineSegment(p[0], p[1]), new LineSegment(p[1], p[2]), new LineSegment(p[2], p[3]), new LineSegment(p[3], p[0]));
+ }
+
+ ///
+ /// Create a polyline curve for the given rectangle
+ ///
+ ///
+ ///
+ static public ICurve CreateRectangle(Rectangle rectangle) {
+ return CreateRectangle(rectangle.Width, rectangle.Height, rectangle.Center);
+ }
+
+ ///
+ /// Creates a curve resembling a house large enough to inscribe within it a rectangle of the
+ /// given width and height at center.
+ ///
+ /// the house width - the rectangle width
+ /// the height of the inscribed rectangle; the house will be half-again higher
+ /// the center of the inscribed rectangle
+ ///
+ static public ICurve CreateHouse(double width, double height, Point center) {
+ double w = width / 2;
+ double h = height / 2;
+ double x = center.X;
+ double y = center.Y;
+ Curve c = new Curve(4);
+ Curve.AddLineSegment(c, x - w, y - h, x + w, y - h);
+ Curve.ContinueWithLineSegment(c, x + w, y + h);
+ Curve.ContinueWithLineSegment(c, x, y + 2 * h);
+ Curve.ContinueWithLineSegment(c, x - w, y + h);
+ Curve.CloseCurve(c);
+ return c;
+ }
+
+ ///
+ /// Creates curve resembling a house within the rectangle formed by width and height at center
+ /// (if the rectangle is a square, the house has the shape of home plate in baseball).
+ ///
+ /// the bounding rectangle width
+ /// the bounding rectangle height
+ /// the bounding rectangle center
+ ///
+ static public ICurve CreateInteriorHouse(double width, double height, Point center) {
+ double w = width / 2;
+ double h = height / 2;
+ double x = center.X;
+ double y = center.Y;
+ Curve c = new Curve(4);
+ Curve.AddLineSegment(c, x - w, y - h, x + w, y - h);
+ Curve.ContinueWithLineSegment(c, x + w, y);
+ Curve.ContinueWithLineSegment(c, x, y + h);
+ Curve.ContinueWithLineSegment(c, x - w, y);
+ Curve.CloseCurve(c);
+ return c;
+ }
+
+ ///
+ /// Creates a curve resembling an inverted house
+ ///
+ /// the house width
+ /// the house heigth
+ /// the house center
+ ///
+ static public ICurve CreateInvertedHouse(double width, double height, Point center) {
+ ICurve shape = CreateHouse(width, height, center);
+ return RotateCurveAroundCenterByDegree(shape, center, 180.0);
+ }
+
+ ///
+ /// Creates a curve resembling a diamond large enough to inscribe within it a rectangle of the
+ /// given width and height at center.
+ ///
+ /// the width of the inscribed rectangle
+ /// the height of the inscribed rectangle
+ /// the diamond center
+ ///
+ static public ICurve CreateDiamond(double width, double height, Point center) {
+ double w = width;
+ double h = height;
+ double x = center.X;
+ double y = center.Y;
+ Curve c = new Curve();
+ Point[] p = new Point[] { new Point(x, y - h), new Point(x + w, y), new Point(x, y + h), new Point(x - w, y) };
+ c.AddSegs(new LineSegment(p[0], p[1]), new LineSegment(p[1], p[2]), new LineSegment(p[2], p[3]), new LineSegment(p[3], p[0]));
+ return c;
+ }
+
+ ///
+ /// Creates a curve resembling a diamond within the rectangle formed by width and height at center.
+ ///
+ /// the bounding rectangle width
+ /// the bounding rectangle height
+ /// the bounding rectangle center
+ ///
+ static public ICurve CreateInteriorDiamond(double width, double height, Point center) {
+ return CreateDiamond(width / 2, height / 2, center);
+ }
+
+ // This adds the padding to the edges around the inscribed rectangle of an octagon.
+ static double octagonPad = 1.0 / 4;
+
+ ///
+ /// Creates a curve of the form of an octagon large enough to inscribe a rectangle
+ /// of width and height around center.
+ ///
+ /// the inscribed rectangle width
+ /// the inscribed rectangle height
+ /// the inscribed rectangle (and octagon) center
+ ///
+ public static Polyline CreateOctagon(double width, double height, Point center) {
+ double w = width / 2;
+ double h = height / 2;
+ Point[] ps = new Point[8];
+
+ // Pad out horizontally
+ ps[0] = new Point(w + (octagonPad * w), h - (h * octagonPad));
+ ps[3] = new Point(-ps[0].X, ps[0].Y);
+ ps[4] = new Point(ps[3].X, -ps[3].Y);
+ ps[7] = new Point(ps[0].X, -ps[0].Y);
+
+ // Pad out vertically
+ ps[1] = new Point(w - (w * octagonPad), h + (h * octagonPad));
+ ps[2] = new Point(-ps[1].X, ps[1].Y);
+ ps[6] = new Point(ps[1].X, -ps[1].Y);
+ ps[5] = new Point(ps[2].X, -ps[2].Y);
+
+ for (int i = 0; i < 8; i++) {
+ ps[i] += center;
+ }
+
+ Polyline polyline = new Polyline(ps) { Closed = true };
+ return polyline;
+ }
+
+ ///
+ /// Creates a curve of the form of an hexagon large enough to inscribe a rectangle
+ /// of width and height around center.
+ ///
+ /// the inscribed rectangle width
+ /// the inscribed rectangle height
+ /// the inscribed rectangle (and hexagon) center
+ ///
+ public static ICurve CreateHexagon(double width, double height, Point center) {
+ var h = height / 2;
+ var w = width / 2;
+ var x = center.X;
+ var y = center.Y;
+ return new Polyline(new[]{
+ new Point(-w + x, - h + y),
+ new Point( w + x, - h + y),
+ new Point( w + h + x, 0 + y),
+ new Point( w + x, h + y),
+ new Point(-w + x, h + y),
+ new Point(-w - h + x, 0 + y),
+ }) { Closed = true };
+ }
+
+ ///
+ /// Creates a curve in the form of an octagon within the rectangle formed by width and height at center.
+ ///
+ /// the bounding rectangle width
+ /// the bounding rectangle height
+ /// the bounding rectangle center
+ ///
+ public static ICurve CreateInteriorOctagon(double width, double height, Point center) {
+ return CreateOctagon(width / (1.0 + octagonPad), height / (1.0 + octagonPad), center);
+ }
+
+ ///
+ /// testing, don't use
+ ///
+ ///
+ ///
+ ///
+ public static ICurve CreateTestShape(double width, double height) {
+ int mult = 1;
+ double w = width * 3;
+ double h = height * 3;
+ Curve curve = new Curve(9);
+ Curve.AddLineSegment(curve, -w, -h, 0, -h / 2);
+ Curve.ContinueWithLineSegment(curve, w / 2, -0.75 * h);
+ Curve.ContinueWithLineSegment(curve, w, -h);
+ Curve.ContinueWithLineSegment(curve, 0.75 * w, -h / 2);
+ Curve.ContinueWithLineSegment(curve, w / 2, 0);
+ Curve.ContinueWithLineSegment(curve, w, h);
+
+ Curve.ContinueWithLineSegment(curve, 0, h / 2);
+ Curve.ContinueWithLineSegment(curve, -w, mult * h);
+ Curve.ContinueWithLineSegment(curve, -w / 3, 0);
+ Curve.CloseCurve(curve);
+ return curve;
+ }
+
+ ///
+ /// create a triangle inside the box formed by width and height.
+ ///
+ ///
+ ///
+ ///
+ ///
+ static public ICurve CreateInteriorTriangle(double width, double height, Point center) {
+ double w = width / 2;
+ double h = height / 2;
+ double x = center.X;
+ double y = center.Y;
+ Curve c = new Curve(3);
+ Point[] p = new Point[] { new Point(x - w, y - h), new Point(x + w, y - h), new Point(x, y + h) };
+ c.AddSegment(new LineSegment(p[0], p[1]));
+ c.AddSegment(new LineSegment(p[1], p[2]));
+ c.AddSegment(new LineSegment(p[2], p[0]));
+ return c;
+ }
+
+ ///
+ /// Rotate a curve around a given point using radians
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static ICurve RotateCurveAroundCenterByRadian(ICurve curve, Point center, double angle) {
+ ValidateArg.IsNotNull(curve, "curve");
+ var c = Math.Cos(angle);
+ var s = Math.Sin(angle);
+ var transform = new PlaneTransformation(1, 0, center.X, 0, 1, center.Y) * new PlaneTransformation(c, -s, 0, s, c, 0) * new PlaneTransformation(1, 0, -center.X, 0, 1, -center.Y);
+ return curve.Transform(transform);
+ }
+
+ ///
+ /// Rotate a curve around a given point using degrees
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static ICurve RotateCurveAroundCenterByDegree(ICurve curve, Point center, double degree) {
+ return RotateCurveAroundCenterByRadian(curve, center, degree * (Math.PI / 180.0));
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static ICurve CreateStar(double width, Point center) {
+ const double a = Math.PI * 2 / 5;
+ var r2 = width / (2 * Math.Sin(a));
+ var r = r2 / 2;
+ return new Polyline(StarPoints(r, r2, center, a)) { Closed = true };
+
+ }
+
+ static IEnumerable StarPoints(double r, double r2, Point center, double a) {
+ var ang = Math.PI / 2;
+ var anghalf = a / 2;
+ for (int i = 0; i < 5; i++) {
+ yield return center + r2 * new Point(Math.Cos(ang), Math.Sin(ang));
+ yield return center + r * new Point(Math.Cos(ang + anghalf), Math.Sin(ang + anghalf));
+ ang += a;
+ }
+ }
+
+ internal static Polyline CreateRegularPolygon(int n, Point center, double rad) {
+ var pt = new Point[n];
+ double a = 2 * Math.PI / n;
+ for (int i = 0; i < n; i++)
+ pt[i] = rad * (new Point(Math.Cos(i * a), Math.Sin(i * a))) + center;
+ return new Polyline(pt) { Closed = true };
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveFactory.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveFactory.cs.meta
new file mode 100644
index 000000000..2ea39d739
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveFactory.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0683753b5ef800a419a9cff7fda0aa34
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveLengthAndParams.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveLengthAndParams.cs
new file mode 100644
index 000000000..3768d46c5
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveLengthAndParams.cs
@@ -0,0 +1,128 @@
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ public partial class Curve {
+
+ double parStart;
+
+ ///
+ /// the start of the parameter domain
+ ///
+ public double ParStart {
+ get { return parStart; }
+ set { parStart = value; }
+ }
+
+ double parEnd;
+
+ ///
+ /// the end of the parameter domain
+ ///
+ public double ParEnd {
+ get { return parEnd; }
+ set { parEnd = value; }
+ }
+
+ ///
+ /// Returns the curved moved by delta
+ ///
+ public void Translate(Point delta)
+ {
+ lock (this)
+ {
+ foreach (ICurve s in segs)
+ s.Translate(delta);
+
+ pBNode = null;
+ }
+ }
+
+ ///
+ /// Returns the curved scaled by x and y
+ ///
+ ///
+ ///
+ ///
+ public ICurve ScaleFromOrigin(double xScale, double yScale)
+ {
+ Curve c = new Curve(segs.Count);
+ foreach (ICurve s in segs)
+ c.AddSegment(s.ScaleFromOrigin(xScale, yScale));
+
+ return c;
+ }
+
+ ///
+ /// Return the transformed curve
+ ///
+ ///
+ /// the transformed curve
+ public ICurve Transform(PlaneTransformation transformation) {
+ Curve c = new Curve(segs.Count);
+ foreach (ICurve s in segs)
+ c.AddSegment(s.Transform(transformation));
+
+ return c;
+ }
+
+ ///
+ /// return length of the curve segment [start,end]
+ ///
+ ///
+ ///
+ ///
+ public double LengthPartial(double start, double end) {
+ double s, e;
+ int i, j;
+
+ AdjustStartEndEndParametersToDomain(ref start, ref end);
+ this.GetSegmentAndParameter(start, out s, out i);
+ this.GetSegmentAndParameter(end, out e, out j);
+
+ ICurve seg = this.segs[i];
+ double ret = seg.LengthPartial(s, seg.ParEnd);
+ for (int k = i + 1; k < j; k++)
+ ret += segs[k].Length;
+
+ seg = segs[j];
+ return ret + seg.LengthPartial(seg.ParStart, e);
+
+ }
+
+ ///
+ /// An approximate length of the curve calculated using the interpolation
+ ///
+ ///
+ ///
+ static public double LengthWithInterpolation(ICurve curve) {
+ double ret = 0;
+ foreach (LineSegment ls in Interpolate(curve, lineSegThreshold))
+ ret += ls.Length;
+ return ret;
+ }
+
+ ///
+ /// An approximate length of the curve calculated using the interpolation
+ ///
+ ///
+ ///
+ ///
+ static public double LengthWithInterpolationAndThreshold(ICurve curve, double threshold)
+ {
+ double ret = 0;
+ foreach (LineSegment ls in Interpolate(curve, threshold))
+ ret += ls.Length;
+ return ret;
+ }
+
+ ///
+ /// Get the length of the curve
+ ///
+ public double Length {
+ get {
+ double ret = 0;
+ foreach (ICurve ic in segs)
+ ret += ic.Length;
+ return ret;
+ }
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveLengthAndParams.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveLengthAndParams.cs.meta
new file mode 100644
index 000000000..ef31cedfd
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveLengthAndParams.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a9cdecaa81a099a428117c0723547bda
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveTangent.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveTangent.cs
new file mode 100644
index 000000000..f304e1d19
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveTangent.cs
@@ -0,0 +1,21 @@
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ ///
+ /// used to calculate the around curve
+ ///
+ internal class CurveTangent {
+ internal Point touchPoint;
+
+ internal Point direction;
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal CurveTangent(Point touch, Point direction) {
+ this.touchPoint = touch;
+ this.direction = direction;
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveTangent.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveTangent.cs.meta
new file mode 100644
index 000000000..4419bf61e
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/CurveTangent.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8165017438605044f82f595067f26f18
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Ellipse.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Ellipse.cs
new file mode 100644
index 000000000..90b157f2f
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Ellipse.cs
@@ -0,0 +1,621 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+
+namespace Microsoft.Msagl.Core.Geometry.Curves{
+ ///
+ /// A class representing an ellipse.
+ ///
+#if TEST_MSAGL
+ [Serializable]
+#endif
+ public class Ellipse : ICurve{
+#if TEST_MSAGL
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object[])")]
+ public override string ToString() {
+ return String.Format("{0} {1} from {2} to {3} a0={4} a1={5}", Start, End, ParStart, ParEnd, AxisA, AxisB);
+ }
+#endif
+ Rectangle box;
+
+ ParallelogramNodeOverICurve parallelogramNodeOverICurve;
+
+ ///
+ /// offsets the curve in the given direction
+ ///
+ /// the width of the offset
+ /// the direction of the offset
+ ///
+ public ICurve OffsetCurve(double offset, Point dir){
+ //is dir inside or outside
+ Point d = dir - center;
+ double angle = Point.Angle(aAxis, d);
+ Point s = aAxis*Math.Cos(angle) + bAxis*Math.Sin(angle);
+ if(s.Length < d.Length){
+ double al = aAxis.Length;
+ double bl = bAxis.Length;
+ return new Ellipse((al + offset)*aAxis.Normalize(), (bl + offset)*bAxis.Normalize(), center);
+ }
+ {
+ double al = aAxis.Length;
+ double bl = bAxis.Length;
+#if DEBUGCURVES
+ if (al < offset || bl < offset)
+ throw new Exception("wrong parameter for ellipse offset");
+#endif
+ return new Ellipse((al - offset)*aAxis.Normalize(), (bl - offset)*bAxis.Normalize(), center);
+ }
+ }
+
+ ///
+ /// Reverse the ellipe: not implemented.
+ ///
+ /// returns the reversed curve
+ public ICurve Reverse(){
+ return null; // throw new Exception("not implemented");
+ }
+
+ ///
+ /// Returns the start point of the curve
+ ///
+ public Point Start{
+ get { return this[ParStart]; }
+ }
+
+ ///
+ /// Returns the end point of the curve
+ ///
+ public Point End{
+ get { return this[ParEnd]; }
+ }
+
+ ///
+ /// Trims the curve
+ ///
+ /// the trim start parameter
+ /// the trim end parameter
+ ///
+ public ICurve Trim(double start, double end){
+ Debug.Assert(start<=end);
+ Debug.Assert(start>=ParStart-ApproximateComparer.Tolerance);
+ Debug.Assert(end<=ParEnd+ApproximateComparer.Tolerance);
+
+ return new Ellipse(Math.Max(start, ParStart), Math.Min(end,ParEnd), AxisA, AxisB, center);
+ }
+
+ ///
+ /// Not Implemented: Returns the trimmed curve, wrapping around the end if start is greater than end.
+ ///
+ /// The starting parameter
+ /// The ending parameter
+ /// The trimmed curve
+ public ICurve TrimWithWrap(double start, double end)
+ {
+ throw new NotImplementedException();
+ }
+
+ //half x axes
+ Point aAxis;
+
+ ///
+ /// the X axis of the ellipse
+ ///
+ public Point AxisA{
+ get { return aAxis; }
+ set { aAxis = value; }
+ }
+
+ Point bAxis;
+
+ ///
+ /// the Y axis of the ellipse
+ ///
+ public Point AxisB{
+ get { return bAxis; }
+ set { bAxis = value; }
+ }
+
+ Point center;
+
+ ///
+ /// the center of the ellipse
+ ///
+ public Point Center{
+ get { return center; }
+ set { center = value; }
+ }
+
+ ///
+ /// The bounding box of the ellipse
+ ///
+ public Rectangle BoundingBox{
+ get { return box; }
+ }
+
+ ///
+ /// Returns the point on the curve corresponding to parameter t
+ ///
+ /// the parameter of the derivative
+ ///
+ public Point this[double t]{
+ get { return center + Math.Cos(t)*aAxis + Math.Sin(t)*bAxis; }
+ }
+
+ ///
+ /// first derivative
+ ///
+ /// the p
+ ///
+ public Point Derivative(double t){
+ return -Math.Sin(t)*aAxis + Math.Cos(t)*bAxis;
+ }
+
+ ///
+ /// second derivative
+ ///
+ ///
+ ///
+ public Point SecondDerivative(double t){
+ return -Math.Cos(t)*aAxis - Math.Sin(t)*bAxis;
+ }
+
+ ///
+ /// third derivative
+ ///
+ ///
+ ///
+ public Point ThirdDerivative(double t){
+ return Math.Sin(t)*aAxis - Math.Cos(t)*bAxis;
+ }
+
+ ///
+ /// a tree of ParallelogramNodes covering the edge
+ ///
+ ///
+ public ParallelogramNodeOverICurve ParallelogramNodeOverICurve{
+ get{
+#if PPC
+ lock(this){
+#endif
+ if(parallelogramNodeOverICurve != null)
+
+ return parallelogramNodeOverICurve;
+ return parallelogramNodeOverICurve = CreateParallelogramNodeForCurveSeg(this);
+#if PPC
+ }
+#endif
+ }
+ }
+
+
+ static ParallelogramNodeOverICurve CreateNodeWithSegmentSplit(double start, double end, Ellipse seg, double eps){
+ var pBNode = new ParallelogramInternalTreeNode(seg, eps);
+ pBNode.AddChild(CreateParallelogramNodeForCurveSeg(start, 0.5*(start + end), seg, eps));
+ pBNode.AddChild(CreateParallelogramNodeForCurveSeg(0.5*(start + end), end, seg, eps));
+ var boxes = new List();
+ boxes.Add(pBNode.Children[0].Parallelogram);
+ boxes.Add(pBNode.Children[1].Parallelogram);
+ pBNode.Parallelogram = Parallelogram.GetParallelogramOfAGroup(boxes);
+ return pBNode;
+ }
+
+ internal static ParallelogramNodeOverICurve CreateParallelogramNodeForCurveSeg(double start, double end,
+ Ellipse seg, double eps){
+ bool closedSeg = (start == seg.ParStart && end == seg.ParEnd && ApproximateComparer.Close(seg.Start, seg.End));
+ if(closedSeg)
+ return CreateNodeWithSegmentSplit(start, end, seg, eps);
+
+ Point s = seg[start];
+ Point e = seg[end];
+ Point w = e - s;
+ Point middle = seg[(start + end) / 2];
+
+ if (ParallelogramNodeOverICurve.DistToSegm(middle, s, e) <= ApproximateComparer.IntersectionEpsilon &&
+ w * w < Curve.LineSegmentThreshold * Curve.LineSegmentThreshold && end - start < Curve.LineSegmentThreshold) {
+ var ls = new LineSegment(s, e);
+ var leaf = ls.ParallelogramNodeOverICurve as ParallelogramLeaf;
+ leaf.Low = start;
+ leaf.High = end;
+ leaf.Seg = seg;
+ leaf.Chord = ls;
+ return leaf;
+ }
+
+ bool we = WithinEpsilon(seg, start, end, eps);
+ var box = new Parallelogram();
+
+ if(we && CreateParallelogramOnSubSeg(start, end, seg, ref box)){
+ return new ParallelogramLeaf(start, end, box, seg, eps);
+ } else{
+ return CreateNodeWithSegmentSplit(start, end, seg, eps);
+ }
+ }
+
+ internal static bool CreateParallelogramOnSubSeg(double start, double end, Ellipse seg, ref Parallelogram box){
+ Point tan1 = seg.Derivative(start);
+
+ Point tan2 = seg.Derivative(end);
+ Point tan2Perp = Point.P(-tan2.Y, tan2.X);
+
+ Point corner = seg[start];
+
+ Point e = seg[end];
+
+ Point p = e - corner;
+
+ double numerator = p*tan2Perp;
+ double denumerator = (tan1*tan2Perp);
+ double x; // = (p * tan2Perp) / (tan1 * tan2Perp);
+ if(Math.Abs(numerator) < ApproximateComparer.DistanceEpsilon)
+ x = 0;
+ else if(Math.Abs(denumerator) < ApproximateComparer.DistanceEpsilon){
+ //it is degenerated; adjacent sides are parallel, but
+ //since p * tan2Perp is big it does not contain e
+ return false;
+ } else x = numerator/denumerator;
+
+ tan1 *= x;
+
+ box = new Parallelogram(corner, tan1, e - corner - tan1);
+#if DEBUGCURVES
+ if (!box.Contains(seg[end]))
+ {
+
+ throw new InvalidOperationException();//"the box does not contain the end of the segment");
+ }
+#endif
+
+ return true;
+ }
+
+ static bool WithinEpsilon(Ellipse seg, double start, double end, double eps){
+ int n = 3; //hack !!!! but maybe can be proven for Bezier curves and other regular curves
+ double d = (end - start)/n;
+ Point s = seg[start];
+ Point e = seg[end];
+
+ double d0 = ParallelogramNodeOverICurve.DistToSegm(seg[start + d], s, e);
+ if(d0 > eps)
+ return false;
+
+ double d1 = ParallelogramNodeOverICurve.DistToSegm(seg[start + d*(n - 1)], s, e);
+ //double d1d1 = seg.d1(start) * seg.d1(end);
+
+ return d1 <= eps; // && d1d1 > 0;
+ }
+
+ static ParallelogramNodeOverICurve CreateParallelogramNodeForCurveSeg(Ellipse seg) {
+ return CreateParallelogramNodeForCurveSeg(seg.ParStart, seg.ParEnd, seg,
+ ParallelogramNodeOverICurve.DefaultLeafBoxesOffset);
+ }
+
+ double parStart;
+
+ ///
+ /// the start of the parameter domain
+ ///
+ public double ParStart{
+ get { return parStart; }
+ set { parStart = value; }
+ }
+
+ double parEnd;
+
+ ///
+ /// the end of the parameter domain
+ ///
+ public double ParEnd{
+ get { return parEnd; }
+ set { parEnd = value; }
+ }
+
+ ///
+ /// The point on the ellipse corresponding to the parameter t is calculated by
+ /// the formula center + cos(t)*axis0 + sin(t) * axis1.
+ /// To get an ellipse rotating clockwise use, for example,
+ /// axis0=(-1,0) and axis1=(0,1)
+ /// start angle in radians
+ /// end angle in radians
+ /// x radius
+ /// y radius
+ /// the ellipse center
+ ///
+ public Ellipse(double parStart, double parEnd, Point axis0, Point axis1, Point center){
+ Debug.Assert(parStart<=parEnd);
+ ParStart = parStart;
+ ParEnd = parEnd;
+ AxisA = axis0;
+ AxisB = axis1;
+ this.center = center;
+ SetBoundingBox();
+ }
+
+ void SetBoundingBox() {
+ if (ApproximateComparer.Close(ParStart, 0) && ApproximateComparer.Close(ParEnd, Math.PI * 2))
+ box = FullBox();
+ else {
+ //the idea is that the box of an arc staying in one quadrant is just the box of the start and the end point of the arc
+ box = new Rectangle(Start, End);
+ //now Start and End are in the box, we need just add all k*P/2 that are in between
+ double t;
+ for (int i = (int)Math.Ceiling(ParStart / (Math.PI / 2)); (t = i * Math.PI / 2) < ParEnd; i++)
+ if (t > parStart)
+ box.Add(this[t]);
+
+ }
+ }
+
+
+ ///
+ /// The point on the ellipse corresponding to the parameter t is calculated by
+ /// the formula center + cos(t)*axis0 + sin(t) * axis1.
+ /// To get an ellipse rotating clockwise use, for example,
+ /// axis0=(-1,0) and axis1=(0,1)
+ ///
+ /// start angle in radians
+ /// end angle in radians
+ /// the x axis
+ /// the y axis
+ /// x coordinate of the center
+ /// y coordinate of the center
+ public Ellipse(double parStart, double parEnd, Point axis0, Point axis1, double centerX, double centerY)
+ : this(parStart, parEnd, axis0, axis1, new Point(centerX, centerY)){
+ }
+
+ ///
+ /// Construct a full ellipse by two axes
+ ///
+ /// an axis
+ /// an axis
+ ///
+ public Ellipse(Point axis0, Point axis1, Point center)
+ : this(0, Math.PI*2, axis0, axis1, center){
+ }
+
+
+ ///
+ /// Constructs a full ellipse with axes aligned to X and Y directions
+ ///
+ /// the length of the X axis
+ /// the length of the Y axis
+ ///
+ public Ellipse(double axisA, double axisB, Point center)
+ : this(0, Math.PI*2, new Point(axisA, 0), new Point(0, axisB), center){
+ }
+
+ ///
+ /// Moves the ellipse to the delta vector
+ ///
+ public void Translate(Point delta)
+ {
+ this.center += delta;
+ this.box = new Rectangle(center + bAxis + aAxis, center - bAxis - aAxis);
+ parallelogramNodeOverICurve = null;
+ }
+
+ ///
+ /// Scales the ellipse by x and by y
+ ///
+ ///
+ ///
+ /// the moved ellipse
+ public ICurve ScaleFromOrigin(double xScale, double yScale)
+ {
+ return new Ellipse(parStart, parEnd, aAxis * xScale, bAxis * yScale, Point.Scale(xScale, yScale, center));
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double GetParameterAtLength(double length) {
+ //todo: slow version!
+ const double eps = 0.001;
+
+ var l = ParStart;
+ var u = ParEnd;
+ var lenplus = length + eps;
+ var lenminsu = length - eps;
+ while (u - l > ApproximateComparer.DistanceEpsilon) {
+ var m = 0.5*(u + l);
+ var len = LengthPartial(ParStart, m);
+ if(len>lenplus)
+ u=m;
+ else if (len < lenminsu)
+ l = m;
+ else return m;
+ }
+ return (u + l)/2;
+ }
+
+ ///
+ /// Transforms the ellipse
+ ///
+ ///
+ /// the transformed ellipse
+ public ICurve Transform(PlaneTransformation transformation){
+ if(transformation != null){
+ Point ap = transformation*aAxis - transformation.Offset;
+ Point bp = transformation*bAxis - transformation.Offset;
+ return new Ellipse(parStart, parEnd, ap, bp, transformation*center);
+ }
+ return this;
+ }
+
+ ///
+ /// returns a parameter t such that the distance between curve[t] and targetPoint is minimal
+ /// and t belongs to the closed segment [low,high]
+ ///
+ /// the point to find the closest point
+ /// the upper bound of the parameter
+ /// the low bound of the parameter
+ ///
+ public double ClosestParameterWithinBounds(Point targetPoint, double low, double high) {
+ const int numberOfTestPoints = 8;
+ double t = (high - low) / (numberOfTestPoints + 1);
+ double closest = low;
+ double minDist = Double.MaxValue;
+ for (int i = 0; i <= numberOfTestPoints; i++) {
+ double par = low + i * t;
+ Point p = targetPoint - this[par];
+ double d = p * p;
+ if (d < minDist) {
+ minDist = d;
+ closest = par;
+ }
+ }
+ if (closest == 0 && high == Math.PI*2)
+ low = -Math.PI;
+ double ret = ClosestPointOnCurve.ClosestPoint(this, targetPoint, closest, low, high);
+ if (ret < 0)
+ ret += 2 * Math.PI;
+ return ret;
+ }
+
+ ///
+ /// return length of the curve segment [start,end] : not implemented
+ ///
+ ///
+ ///
+ ///
+ public double LengthPartial(double start, double end) {
+ return Curve.LengthWithInterpolationAndThreshold(Trim(start, end), Curve.LineSegmentThreshold/100);
+ }
+
+ ///
+ /// Return the length of the ellipse curve: not implemented
+ ///
+ public double Length{
+ get {
+ return Curve.LengthWithInterpolation(this);
+ }
+ }
+
+
+ ///
+ /// clones the curve.
+ ///
+ /// the cloned curve
+ public ICurve Clone(){
+ return new Ellipse(parStart, parEnd, aAxis, bAxis, center);
+ }
+
+ #region ICurve Members
+
+ ///
+ /// returns a parameter t such that the distance between curve[t] and a is minimal
+ ///
+ ///
+ /// the parameter of the closest point
+ public double ClosestParameter(Point targetPoint){
+ double savedParStart = 0;
+ const int numberOfTestPoints = 8;
+ double t = (ParEnd - ParStart)/(numberOfTestPoints + 1);
+ double closest = ParStart;
+ double minDist = Double.MaxValue;
+ for (int i = 0; i <= numberOfTestPoints; i++){
+ double par = ParStart + i*t;
+ Point p = targetPoint - this[par];
+ double d = p*p;
+ if(d < minDist){
+ minDist = d;
+ closest = par;
+ }
+ }
+ bool parStartWasChanged = false;
+ if(closest == 0 && ParEnd == Math.PI*2){
+ parStartWasChanged = true;
+ savedParStart = ParStart;
+ ParStart = -Math.PI;
+ }
+ double ret = ClosestPointOnCurve.ClosestPoint(this, targetPoint, closest, ParStart, ParEnd);
+ if(ret<0)
+ ret += 2*Math.PI;
+ if (parStartWasChanged)
+ ParStart = savedParStart;
+ return ret;
+ }
+
+ ///
+ /// left derivative at t
+ ///
+ /// the parameter where the derivative is calculated
+ ///
+ public Point LeftDerivative(double t){
+ return Derivative(t);
+ }
+
+ ///
+ /// right derivative at t
+ ///
+ /// the parameter where the derivative is calculated
+ ///
+ public Point RightDerivative(double t){
+ return Derivative(t);
+ }
+
+ #endregion
+
+ #region ICurve Members
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double Curvature(double t){
+ throw new NotImplementedException();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double CurvatureDerivative(double t){
+ throw new NotImplementedException();
+ }
+
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double CurvatureSecondDerivative(double t){
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ ///
+ /// returns true if the ellipse goes counterclockwise
+ ///
+ ///
+ public bool OrientedCounterclockwise(){
+ return AxisA.X*AxisB.Y - AxisB.X*AxisA.Y > 0;
+ }
+
+
+ ///
+ ///returns the box of the ellipse that this ellipse is a part of
+ ///
+ ///
+ public Rectangle FullBox() {
+ var del=AxisA + AxisB;
+ return new Rectangle(center + del, center - del);
+ }
+
+ ///
+ ///is it a proper circle?
+ ///
+ public bool IsArc() {
+ return AxisA.X == AxisB.Y && AxisA.Y == -AxisB.X;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Ellipse.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Ellipse.cs.meta
new file mode 100644
index 000000000..7ccfaebc2
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Ellipse.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c5c6926f69ff27d44ac54fa374927c6f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ICurve.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ICurve.cs
new file mode 100644
index 000000000..7e49cb7f6
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ICurve.cs
@@ -0,0 +1,217 @@
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ ///
+ /// The interface for curves: instances of ICurve inside of GLEE
+ /// are BSpline,Curve,LineSeg, Ellipse,CubicBezierSeg and ArrowTipCurve.
+ ///
+ public interface ICurve {
+ ///
+ /// Returns the point on the curve corresponding to parameter t
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1043:UseIntegralOrStringArgumentForIndexers")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t")]
+ Point this[double t] { get; }
+ ///
+ /// first derivative at t
+ ///
+ /// the parameter where the derivative is calculated
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t")]
+ Point Derivative(double t);
+ ///
+ /// second derivative
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t")]
+ Point SecondDerivative(double t);
+ ///
+ /// third derivative
+ ///
+ /// the parameter of the derivative
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t")]
+ Point ThirdDerivative(double t);
+
+ ///
+ /// A tree of ParallelogramNodes covering the curve.
+ /// This tree is used in curve intersections routines.
+ ///
+ ///
+ ParallelogramNodeOverICurve ParallelogramNodeOverICurve { get; }
+
+ ///
+ /// XY bounding box of the curve
+ ///
+ Rectangle BoundingBox { get;}
+
+ ///
+ /// the start of the parameter domain
+ ///
+ double ParStart { get;}
+
+ ///
+ /// the end of the parameter domain
+ ///
+ double ParEnd { get;}
+
+ ///
+ /// Returns the trim curve between start and end, without wrap
+ ///
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "End")]
+ ICurve Trim(double start, double end);
+
+ ///
+ /// Returns the trim curve between start and end, with wrap, if supported by the implementing class.
+ ///
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "End")]
+ ICurve TrimWithWrap(double start, double end);
+
+ ///
+ /// Moves the curve by the delta.
+ ///
+ void Translate(Point delta);
+
+ ///
+ /// Returns the curved with all points scaled from the original by x and y
+ ///
+ ///
+ ICurve ScaleFromOrigin(double xScale, double yScale);
+
+ ///
+ /// this[ParStart]
+ ///
+ Point Start { get;}
+
+ ///
+ /// this[ParEnd]
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "End")]
+ Point End { get; }
+
+ ///
+ /// this[Reverse[t]]=this[ParEnd+ParStart-t]
+ ///
+ ///
+ ICurve Reverse();
+
+
+
+ ///
+ /// Offsets the curve in the direction of dir
+ ///
+ ///
+ ///
+ ///
+
+
+
+ ICurve OffsetCurve(double offset, Point dir);
+
+ ///
+ /// return length of the curve segment [start,end]
+ ///
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "End")]
+ double LengthPartial(double start, double end);
+
+ ///
+ /// Get the length of the curve
+ ///
+ double Length { get;}
+
+
+ ///
+ /// gets the parameter at a specific length from the start along the curve
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming",
+ "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t")]
+ double GetParameterAtLength(double length);
+
+ ///
+ /// Return the transformed curve
+ ///
+ ///
+ /// the transformed curve
+ ICurve Transform(PlaneTransformation transformation);
+
+
+ ///
+ /// returns a parameter t such that the distance between curve[t] and targetPoint is minimal
+ /// and t belongs to the closed segment [low,high]
+ ///
+ /// the point to find the closest point
+ /// the upper bound of the parameter
+ /// the low bound of the parameter
+ ///
+ double ClosestParameterWithinBounds(Point targetPoint, double low, double high);
+
+ ///
+ /// returns a parameter t such that the distance between curve[t] and a is minimal
+ ///
+ ///
+ ///
+ double ClosestParameter(Point targetPoint);
+///
+/// clones the curve.
+///
+/// the cloned curve
+ ICurve Clone();
+
+
+ ///
+ /// The left derivative at t.
+ ///
+ /// the parameter where the derivative is calculated
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t")]
+ Point LeftDerivative(double t);
+
+
+ ///
+ /// the right derivative at t
+ ///
+ /// the parameter where the derivative is calculated
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t")]
+ Point RightDerivative(double t);
+
+
+ ///
+ /// the signed curvature of the segment at t
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t")]
+ double Curvature(double t);
+ ///
+ /// the derivative of the curvature at t
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t")]
+ double CurvatureDerivative(double t);
+
+
+ ///
+ /// the derivative of CurvatureDerivative
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t")]
+ double CurvatureSecondDerivative(double t);
+
+
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ICurve.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ICurve.cs.meta
new file mode 100644
index 000000000..62fa842d3
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ICurve.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 87007c1367b6da648814f9b9bd5f761c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/IFunction.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/IFunction.cs
new file mode 100644
index 000000000..a126c4477
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/IFunction.cs
@@ -0,0 +1,46 @@
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ ///
+ ///
+ ///
+ public interface IFunction {
+ ///
+ /// Returns the falue on the curve corresponding to parameter t
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1043:UseIntegralOrStringArgumentForIndexers")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t")]
+ double this[double t] { get; }
+ ///
+ /// first derivative at t
+ ///
+ /// the parameter where the derivative is calculated
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t")]
+ double Derivative(double t);
+ ///
+ /// second derivative
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t")]
+ double SecondDerivative(double t);
+ ///
+ /// third derivative
+ ///
+ /// the parameter of the derivative
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "t")]
+ double ThirdDerivative(double t);
+
+ ///
+ /// the domain of the function is the [ParStart, ParEnd] segment
+ ///
+ double ParStart { get; }
+ ///
+ /// the domain of the function is the [ParStart, ParEnd] segment
+ ///
+ double ParEnd { get; }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/IFunction.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/IFunction.cs.meta
new file mode 100644
index 000000000..503b987bd
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/IFunction.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 65427237fd9e6174988919a03f663052
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/IntersectionInfo.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/IntersectionInfo.cs
new file mode 100644
index 000000000..49580b92b
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/IntersectionInfo.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Globalization;
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ ///
+ /// Contains the result of the intersection of two ICurves.
+ ///
+ public class IntersectionInfo {
+
+
+ /* The following conditions should hold:
+ * X=seg0[par0]=seg1[par1]
+ */
+
+ double par0, par1;
+ Point x;
+
+ ICurve seg0, seg1;
+ ///
+ /// the parameter on the first curve
+ ///
+ public double Par0 {
+ get { return par0; }
+ set { par0 = value; }
+ }
+ ///
+ /// the parameter on the second curve
+ ///
+ public double Par1 {
+ get { return par1; }
+ set { par1 = value; }
+ }
+
+ ///
+ /// the intersection point
+ ///
+ public Point IntersectionPoint {
+ get { return x; }
+ set { x = value; }
+ }
+
+///
+/// the segment of the first curve where the intersection point belongs
+///
+ public ICurve Segment0 {
+ get { return seg0; }
+ set { seg0=value; }
+ }
+
+ ///
+ /// the segment of the second curve where the intersection point belongs
+ ///
+ public ICurve Segment1 {
+ get { return seg1; }
+ set { seg1 = value; }
+ }
+
+ ///
+ /// the constructor
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object,System.Object)")]
+ internal IntersectionInfo(double pr0, double pr1, Point x, ICurve s0, ICurve s1) {
+ par0 = pr0;
+ par1 = pr1;
+ this.x = x;
+ seg0 = s0;
+ seg1 = s1;
+#if DETAILED_DEBUG
+ System.Diagnostics.Debug.Assert(ApproximateComparer.Close(x, s0[pr0], ApproximateComparer.IntersectionEpsilon*10),
+ string.Format("intersection not at curve[param]; x = {0}, s0[pr0] = {1}, diff = {2}", x, s0[pr0], x - s0[pr0]));
+ System.Diagnostics.Debug.Assert(ApproximateComparer.Close(x, s1[pr1], ApproximateComparer.IntersectionEpsilon*10),
+ string.Format("intersection not at curve[param]; x = {0}, s1[pr1] = {1}, diff = {2}", x, s1[pr1], x - s1[pr1]));
+#endif
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public override string ToString() {
+ return String.Format(CultureInfo.InvariantCulture, "XX({0} {1} {2})", par0, par1, x);
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/IntersectionInfo.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/IntersectionInfo.cs.meta
new file mode 100644
index 000000000..b96a3fbb7
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/IntersectionInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ab381fde1cfb1ba44abc25ef2f5d0888
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/LineSegment.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/LineSegment.cs
new file mode 100644
index 000000000..818c76cce
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/LineSegment.cs
@@ -0,0 +1,518 @@
+using System;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ ///
+ /// Line segment
+ ///
+#if TEST_MSAGL
+ [Serializable]
+#endif
+ [DebuggerDisplay("[({a.X} {a.Y}),({b.X} {b.Y})]")]
+ public class LineSegment : ICurve {
+
+ Point a;//the line goes from a to side1
+
+ ///
+ /// Offsets the curve in the direction of dir
+ ///
+ ///
+ ///
+ ///
+ public ICurve OffsetCurve(double offset, Point dir) { return null; }
+ ///
+ /// the line start point
+ ///
+ public Point Start {
+ get { return a; }
+ set { a = value; }
+ }
+ Point b;
+ ///
+ /// the line end point
+ ///
+ public Point End {
+ get { return b; }
+ set { b = value; }
+ }
+
+
+ ///
+ /// the start parameter
+ ///
+ public double ParStart {
+ get { return 0; }
+ }
+
+ ///
+ /// the end parameter
+ ///
+ public double ParEnd {
+ get { return 1; }
+ }
+
+ ///
+ /// Returns the trim curve
+ ///
+ ///
+ ///
+ ///
+ public ICurve Trim(double start, double end) {
+
+ if (start > end)
+ throw new InvalidOperationException();//"wrong params in trimming");
+
+ Point p1 = start <= ParStart ? Start : (1 - start) * a + start * b;
+ Point p2 = end >= ParEnd ? End : (1 - end) * a + end * b;
+ if (ApproximateComparer.Close(start, end)) {
+ return null;
+ }
+ return new LineSegment(p1, p2);
+ }
+
+ ///
+ /// Not Implemented: Returns the trimmed curve, wrapping around the end if start is greater than end.
+ ///
+ /// The starting parameter
+ /// The ending parameter
+ /// The trimmed curve
+ public ICurve TrimWithWrap(double start, double end) {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// A tree of ParallelogramNodes covering the curve.
+ /// This tree is used in curve intersections routines.
+ ///
+ ///
+ public ParallelogramNodeOverICurve ParallelogramNodeOverICurve {
+ get {
+ Point side = 0.5 * (b - a);
+
+ return new ParallelogramLeaf(0, 1, new Parallelogram(a, side, side), this, 0);
+ }
+ }
+
+ internal Point Normal {
+ get {
+ Point t = a - b;
+ t /= t.Length;
+ return new Point(-t.Y, t.X);
+ }
+ }
+ ///
+ /// construct a line segment
+ ///
+ ///
+ ///
+ public LineSegment(Point start, Point end) {
+#if SHARPKIT
+ a = start.Clone();
+ b = end.Clone();
+#else
+ a = start;
+ b = end;
+#endif
+ }
+
+ ///
+ /// constructs a line segment
+ ///
+ /// the first point
+ /// x-coordinate of the second point
+ /// y-coordinate of the second point
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "a"), SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x"), SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "y")]
+ public LineSegment(Point a, double x, double y) : this(a, new Point(x, y)) { }
+
+ ///
+ /// constructs a line segment
+ ///
+ /// x-coordinate of the first point
+ /// y-coordinate of the first point
+ /// x-coordinate of the second point
+ /// y-coordinate of the second point
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "y"), SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x")]
+ public LineSegment(double x0, double y0, double x1, double y1) : this(new Point(x0, y0), new Point(x1, y1)) { }
+
+ ///
+ /// Returns the point on the curve corresponding to parameter t
+ ///
+ ///
+ ///
+ public Point this[double t] {
+ get { return a + (b - a) * t; }
+ }
+
+ ///
+ /// first derivative at t
+ ///
+ /// the parameter where the derivative is calculated
+ ///
+ public Point Derivative(double t) {
+ return b - a;
+ }
+
+ ///
+ /// second derivative
+ ///
+ ///
+ ///
+ public Point SecondDerivative(double t) {
+ return new Point(0, 0);
+ }
+
+ ///
+ /// third derivative
+ ///
+ /// the parameter of the derivative
+ ///
+ public Point ThirdDerivative(double t) {
+ return new Point(0, 0);
+ }
+
+ ///
+ /// This is the rounded form; DebuggerDisplay shows the unrounded form.
+ ///
+ ///
+ public override string ToString() {
+ return "{" + a + "," + b + "}";
+ }
+
+ ///
+ /// this[Reverse[t]]=this[ParEnd+ParStart-t]
+ ///
+ ///
+
+ public ICurve Reverse() {
+ return new LineSegment(b, a);
+ }
+
+ /*
+ static internal IntersectionInfo Cross(LineSeg coeff, LineSeg side1){
+ IntersectionInfo xx=CrossTwoLines(coeff.Start, coeff.End-coeff.Start,side1.Start, side1.End-side1.Start);
+ if (xx == null)
+ {
+ //parallel segs
+ Point adir=coeff.d1(0);
+ Point bdir=side1.d1(0);
+
+ if (adir.Length > bdir.Length)
+ {
+ if (adir.Length > Curve.DistEps)
+ {
+ adir = adir.Normalize();
+ if(Math.Abs((coeff-side1)*adir1){
+ if (ApproximateComparer.Close(coeff.End, xx.X))
+ {
+ xx.X = coeff.End;
+ xx.Par0 = 1;
+ }
+ else
+ return null;
+ }
+ else if(xx.Par0<0){
+ if(ApproximateComparer.Close(coeff.Start,xx.X)){
+ xx.X=coeff.Start;
+ xx.Par0=1;
+ }
+ else
+ return null;
+ }
+
+ if (xx.Par1 > 1)
+ {
+ if (ApproximateComparer.Close(side1.End, xx.X))
+ {
+ xx.X = coeff.End;
+ xx.Par1 = 1;
+ }
+ else
+ return null;
+ }
+ else if (xx.Par1 < 0)
+ {
+ if (ApproximateComparer.Close(side1.Start, xx.X))
+ {
+ xx.X = coeff.Start;
+ xx.Par1 = 1;
+ }
+ else
+ return null;
+ }
+
+ return xx;
+ }
+ * */
+
+ ///
+ /// Returns the curved moved by delta
+ ///
+ public void Translate(Point delta)
+ {
+ a += delta;
+ b += delta;
+ }
+
+ ///
+ /// Scale (multiply) from zero by x and y
+ ///
+ ///
+ ///
+ /// scaled copy
+ public ICurve ScaleFromOrigin(double xScale, double yScale)
+ {
+ return new LineSegment(Point.Scale(xScale, yScale, a), Point.Scale(xScale, yScale, b));
+ }
+
+ ///
+ /// gets the parameter at a specific length from the start along the curve
+ ///
+ ///
+ ///
+ public double GetParameterAtLength(double length) {
+ var len = (b - a).Length;
+ if (len < ApproximateComparer.Tolerance)
+ return 0;
+ var t = length/len;
+ return t > 1 ? 1 : (t < 0 ? 0 : t);
+ }
+
+ ///
+ /// Return the transformed curve
+ ///
+ ///
+ /// the transformed curve
+ public ICurve Transform(PlaneTransformation transformation) {
+ return new LineSegment(transformation * a, transformation * b);
+ }
+
+ ///
+ /// returns a parameter t such that the distance between curve[t] and targetPoint is minimal
+ /// and t belongs to the closed segment [low,high]
+ ///
+ /// the point to find the closest point
+ /// the upper bound of the parameter
+ /// the low bound of the parameter
+ ///
+ public double ClosestParameterWithinBounds(Point targetPoint, double low, double high) {
+ var t = ClosestParameter(targetPoint);
+ if (t < low)
+ t = low;
+ if(t>high)
+ t=high;
+ return t;
+ }
+
+ ///
+ /// return length of the curve segment [start,end]
+ ///
+ ///
+ ///
+ ///
+ public double LengthPartial(double start, double end) {
+ return (this[end] - this[start]).Length;
+ }
+
+ ///
+ /// Get the length of the curve
+ ///
+ public double Length {
+ get {
+ return (a - b).Length;
+ }
+ }
+ ///
+ /// The bounding box of the line
+ ///
+ public Rectangle BoundingBox {
+ get {
+ return new Rectangle(Start, End);
+ }
+ }
+
+
+
+ ///
+ /// clones the curve.
+ ///
+ /// the cloned curve
+
+ public ICurve Clone() {
+ return new LineSegment(this.a, this.b);
+ }
+
+ ///
+ /// returns a parameter t such that the distance between curve[t] and a is minimal
+ ///
+ ///
+ ///
+ public double ClosestParameter(Point targetPoint) {
+ return Point.ClosestParameterOnLineSegment(targetPoint, Start, End);
+ }
+ ///
+ /// left derivative at t
+ ///
+ /// the parameter where the derivative is calculated
+ ///
+ public Point LeftDerivative(double t) {
+ return Derivative(t);
+ }
+
+ ///
+ /// right derivative at t
+ ///
+ /// the parameter where the derivative is calculated
+ ///
+ public Point RightDerivative(double t) {
+ return Derivative(t);
+ }
+ ///
+ /// returns true if segments are not parallel and are intesecting
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x"), SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "d"), SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "c"), SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "b"), SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "a"), SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "4#")]
+ public static bool Intersect(Point a, Point b, Point c, Point d, out Point x) {
+ if (!Point.LineLineIntersection(a, b, c, d, out x))
+ return false;
+ return XIsBetweenPoints(ref a, ref b, ref x) && XIsBetweenPoints(ref c, ref d, ref x);
+ }
+
+ static bool XIsBetweenPoints(ref Point a, ref Point b, ref Point x) {
+ return (a - x) * (b - x) <= ApproximateComparer.DistanceEpsilon;
+ }
+
+#region ICurve Members
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double Curvature(double t) {
+ return 0;
+ }
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double CurvatureDerivative(double t) {
+ return 0;
+ }
+
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public double CurvatureSecondDerivative(double t) {
+ throw new NotImplementedException();
+ }
+
+#endregion
+
+ ///
+ /// [a,b] and [c,d] are the segments. u and v are the corresponding closest point params
+ /// see http://www.geometrictools.com/Documentation/DistanceLine3Line3.pdf
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static double MinDistBetweenLineSegments(Point a, Point b, Point c, Point d, out double parab, out double parcd) {
+ Point u = b - a;
+ Point v = d - c;
+ Point w = a - c;
+
+ double D = Point.CrossProduct(u, v);
+
+ double uu = u * u; // always >= 0
+ double uv = u * v;
+ double vv = v * v; // always >= 0
+ double uw = u * w;
+ double vw = v * w;
+ double sN, tN;
+ double absD = Math.Abs(D);
+ double sD = absD, tD = absD;
+
+ // compute the line parameters of the two closest points
+ if (absD < ApproximateComparer.Tolerance) { // the lines are almost parallel
+ sN = 0.0; // force using point a on segment [a..b]
+ sD = 1.0; // to prevent possible division by 0.0 later
+ tN = vw;
+ tD = vv;
+ }
+ else { // get the closest points on the infinite lines
+ sN = Point.CrossProduct(v, w);
+ tN = Point.CrossProduct(u, w);
+ if (D < 0) {
+ sN = -sN;
+ tN = -tN;
+ }
+
+ if (sN < 0.0) { // parab < 0 => the s=0 edge is visible
+ sN = 0.0;
+ tN = vw;
+ tD = vv;
+ }
+ else if (sN > sD) { // parab > 1 => the s=1 edge is visible
+ sN = sD = 1;
+ tN = vw + uv;
+ tD = vv;
+ }
+ }
+
+ if (tN < 0.0) { // tc < 0 => the t=0 edge is visible
+ tN = 0.0;
+ // recompute parab for this edge
+ if (-uw < 0.0)
+ sN = 0.0;
+ else if (-uw > uu)
+ sN = sD;
+ else {
+ sN = -uw;
+ sD = uu;
+ }
+ }
+ else if (tN > tD) { // tc > 1 => the t=1 edge is visible
+ tN = tD = 1;
+ // recompute parab for this edge
+ if ((-uw + uv) < 0.0)
+ sN = 0;
+ else if ((-uw + uv) > uu)
+ sN = sD;
+ else {
+ sN = (-uw + uv);
+ sD = uu;
+ }
+ }
+
+ // finally do the division to get parameters
+ parab = (Math.Abs(sN) < ApproximateComparer.Tolerance ? 0.0 : sN / sD);
+ parcd = (Math.Abs(tN) < ApproximateComparer.Tolerance ? 0.0 : tN / tD);
+
+ // get the difference of the two closest points
+ Point dP = w + (parab * u) - (parcd * v);
+
+ return dP.Length; // return the closest distance
+ }
+ }
+}
+
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/LineSegment.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/LineSegment.cs.meta
new file mode 100644
index 000000000..1bcd7689e
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/LineSegment.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 58ccf7a3701aeea4d9dbc54d63e11731
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/LinearSystem2.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/LinearSystem2.cs
new file mode 100644
index 000000000..eeb5decff
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/LinearSystem2.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ ///
+ /// solves a linear system of two equations with to unknown variables
+ ///
+ internal class LinearSystem2 {
+ LinearSystem2() { }
+ static double eps = 1.0E-8;
+
+ internal static double Eps {
+ get { return LinearSystem2.eps; }
+ // set { LinearSystem2.eps = value; }
+ }
+
+ internal static bool Solve(double a00, double a01, double b0, double a10, double a11, double b1, out double x, out double y) {
+ double d = a00 * a11 - a10 * a01;
+
+ if (Math.Abs(d) < Eps) {
+ x = y = 0; //to avoid the compiler bug
+ return false;
+ }
+
+ x = (b0 * a11 - b1 * a01) / d;
+ y = (a00 * b1 - a10 * b0) / d;
+
+ return true;
+
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/LinearSystem2.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/LinearSystem2.cs.meta
new file mode 100644
index 000000000..d8ebdb40d
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/LinearSystem2.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b5c55970eec189a468d9c4b82fdc5a50
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/MinDistCurveCurve.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/MinDistCurveCurve.cs
new file mode 100644
index 000000000..8fec1efc4
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/MinDistCurveCurve.cs
@@ -0,0 +1,357 @@
+using System;
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ //For curves A(s) and B(t) with some guess for the parameters (s0, t0) we
+ //are trying to bring to (0,0) the vector(Fs,Ft).
+ //where F(s,t) = (A(s) - B(t))^2. To minimize F^2,
+ //you get the system of equations to solve for ds and dt:
+ //Fs + Fss*ds + Fst*dt = 0
+ //Ft + Fst*ds + Ftt*dt = 0
+ //
+ //Where F = F(si,ti), Fs and Ft are the first partials at si, ti, Fxx are the second partials,
+ // and s(i+1) = si+ds, t(i+1) = ti+dt.
+ //Of course you have to make sure that ds and dt do not take you out of your domain.
+ //This will converge if the curves have 2nd order continuity
+ //and we are starting parameters are reasonable.
+ //It is not a good method for situations that are not well behaved, but it is really simple.
+///
+/// Implements the minimal distance between curves functionality
+///
+#if TEST_MSAGL
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Dist")]
+ public
+#else
+ internal
+#endif
+ class MinDistCurveCurve {
+
+ ICurve curveA;
+ ICurve curveB;
+ double aMin;
+ double aMax;
+ double bMin; double bMax;
+ double aGuess; double bGuess;
+ double aSolution;
+ internal double ASolution {
+ get { return aSolution; }
+ }
+ double bSolution;
+ internal double BSolution {
+ get { return bSolution; }
+ }
+
+ Point aPoint;
+ internal Point APoint { get { return aPoint; } }
+
+ Point bPoint;
+ internal Point BPoint { get { return bPoint; } }
+ bool status;
+ internal bool Status { get { return status; } }
+ double si;
+ double ti;
+
+ Point a, b, a_b, ad, bd, add, bdd;
+
+ void InitValues() {
+ a = curveA[si];
+ b = curveB[ti];
+ a_b = a - b;
+ ad = curveA.Derivative(si);
+ add = curveA.SecondDerivative(si);
+ bd = curveB.Derivative(ti);
+ bdd = curveB.SecondDerivative(ti);
+ }
+///
+/// constructor
+///
+/// first curve
+/// second curve
+/// the first curve minimal parameter
+ /// the first curve maximal parameter
+ /// the second curve minimal parameter
+ /// the first curve maximal parameter
+///
+///
+ public MinDistCurveCurve(ICurve curveAPar,
+ ICurve curveBPar,
+ double lowBound0,
+ double upperBound0,
+ double lowBound1,
+ double upperBound1,
+ double guess0,
+ double guess1) {
+ this.curveA = curveAPar;
+ this.curveB = curveBPar;
+ this.aMin = lowBound0;
+ this.bMin = lowBound1;
+ this.aMax = upperBound0;
+ this.bMax = upperBound1;
+ this.aGuess = guess0;
+ this.bGuess = guess1;
+ this.si = guess0;
+ this.ti = guess1;
+ }
+
+
+ //we ignore the mulitplier 2 here fore efficiency reasons
+ double Fs {
+ get {
+ return /*2**/a_b * ad;
+ }
+ }
+
+ double Fss {
+ get {
+ return /*2**/(a_b * add + ad * ad);
+ }
+ }
+
+ double Fst //equals to Fts
+ {
+ get {
+ return - /*2**/bd * ad;
+ }
+ }
+
+ double Ftt {
+ get {
+ return /*2**/(-a_b * bdd + bd * bd);
+ }
+ }
+
+
+ double Ft {
+ get {
+ return -/*2**/a_b * bd;
+ }
+ }
+
+
+
+ ///
+ /// xy - the first row
+ /// uw - the second row
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static double Delta(double x, double y, double u, double w) {
+ return x * w - u * y;
+ }
+ //Fs + Fss*ds + Fst*dt = 0
+ //Ft + Fst*ds + Ftt*dt = 0
+ internal void Solve() {
+
+ int numberOfBoundaryCrossings = 0;
+ const int maxNumberOfBoundaryCrossings = 10;
+ int numberOfTotalReps = 0;
+ const int maxNumberOfTotalReps = 100;
+
+ bool abort = false;
+
+ InitValues();
+
+ if (curveA is LineSegment && curveB is LineSegment ) {
+ Point bd1 = curveB.Derivative(0);
+ bd1 /= bd1.Length;
+ Point an = (curveA as LineSegment).Normal;
+
+ double del = Math.Abs(an * bd1);
+
+
+ if (Math.Abs(del) < ApproximateComparer.DistanceEpsilon || Delta(Fss, Fst, Fst, Ftt) < ApproximateComparer.Tolerance) {
+ status = true;
+ ParallelLineSegLineSegMinDist();
+ return;
+ }
+ }
+
+ double ds, dt;
+ do {
+
+ //hopefully it will be inlined by the compiler
+ double delta = Delta(Fss, Fst, Fst, Ftt);
+ if (Math.Abs(delta) < ApproximateComparer.Tolerance) {
+ status = false;
+ abort = true;
+ break;
+ }
+
+ ds = Delta(-Fs, Fst, -Ft, Ftt) / delta;
+ dt = Delta(Fss, -Fs, Fst, -Ft) / delta;
+
+
+ double nsi = si + ds;
+ double nti = ti + dt;
+
+ bool bc;
+
+ if (nsi > aMax + ApproximateComparer.DistanceEpsilon || nsi < aMin - ApproximateComparer.DistanceEpsilon || nti > bMax + ApproximateComparer.DistanceEpsilon || nti < bMin - ApproximateComparer.DistanceEpsilon) {
+ numberOfBoundaryCrossings++;
+ ChopDsDt(ref ds, ref dt);
+ si += ds;
+ ti += dt;
+ bc = true;
+ } else {
+ bc = false;
+ si = nsi;
+ ti = nti;
+ if (si > aMax)
+ si = aMax;
+ else if (si < aMin)
+ si = aMin;
+
+ if (ti > bMax)
+ ti = bMax;
+ else if (ti < bMin)
+ ti = bMin;
+ }
+
+ InitValues();
+
+ numberOfTotalReps++;
+
+ abort = numberOfBoundaryCrossings >= maxNumberOfBoundaryCrossings ||
+ numberOfTotalReps >= maxNumberOfTotalReps || (ds == 0 && dt == 0 && bc);
+
+ } while ((Math.Abs(ds) >= ApproximateComparer.Tolerance || Math.Abs(dt) >= ApproximateComparer.Tolerance) && !abort);
+
+ if (abort) {
+ //may be the initial values were just OK
+ Point t = curveA[aGuess] - curveB[bGuess];
+ if (t * t < ApproximateComparer.DistanceEpsilon * ApproximateComparer.DistanceEpsilon) {
+ aSolution = aGuess;
+ bSolution = bGuess;
+ aPoint = curveA[aGuess];
+ bPoint = curveB[bGuess];
+ status = true;
+ return;
+
+ }
+ }
+
+
+ aSolution = si;
+ bSolution = ti;
+ aPoint = a;
+ bPoint = b;
+ status = !abort;
+
+ }
+
+
+
+ void ChopDsDt(ref double ds, ref double dt) {
+ if (ds != 0 && dt != 0) {
+ double k1 = 1; //we are looking for a chopped vector of the form k(ds,dt)
+
+ if (si + ds > aMax) //we have si+k*ds=aMax
+ k1 = (aMax - si) / ds;
+ else if (si + ds < aMin)
+ k1 = (aMin - si) / ds;
+
+ double k2 = 1;
+
+ if (ti + dt > bMax) //we need to have ti+k*dt=bMax or ti+k*dt=bMin
+ k2 = (bMax - ti) / dt;
+ else if (ti + dt < bMin)
+ k2 = (bMin - ti) / dt;
+
+ double k = Math.Min(k1, k2);
+ ds *= k;
+ dt *= k;
+
+ } else if (ds == 0) {
+ if (ti + dt > bMax)
+ dt = bMax - ti;
+ else if (ti + dt < bMin)
+ dt = bMin - ti;
+ } else { //dt==0)
+ if (si + ds > aMax)
+ ds = aMax - si;
+ else if (si + ds < aMin)
+ ds = aMin - si;
+ }
+ }
+
+ void ParallelLineSegLineSegMinDist() {
+ LineSegment l0 = curveA as LineSegment;
+ LineSegment l1 = curveB as LineSegment;
+
+ Point v0 = l0.Start;
+ Point v1 = l0.End;
+ Point v2 = l1.Start;
+ Point v3 = l1.End;
+
+ Point d0 = v1 - v0;
+
+ double nd0 = d0.Length;
+
+ double r0 = 0, r1, r2, r3;
+
+ if (nd0 > ApproximateComparer.DistanceEpsilon) {
+ //v0 becomes the zero point
+ d0 /= nd0;
+ r1 = d0 * (v1 - v0);
+ r2 = d0 * (v2 - v0);
+ r3 = d0 * (v3 - v0);
+
+ bool swapped = false;
+ if (r2 > r3) {
+ swapped = true;
+ double t = r2;
+ r2 = r3;
+ r3 = t;
+ }
+
+ if (r3 < r0) {
+ aSolution = 0;
+ bSolution = swapped ? 0 : 1;
+ } else if (r2 > r1) {
+ aSolution = 1;
+ bSolution = swapped ? 1 : 0;
+
+ } else {
+ double r = Math.Min(r1, r3);
+ aSolution = r / (r1 - r0);
+ bSolution = (r - r2) / (r3 - r2);
+ if (swapped)
+ bSolution = 1 - bSolution;
+
+ }
+ } else {
+ Point d1 = v3 - v2;
+ double nd1 = d1.Length;
+ if (nd1 > ApproximateComparer.DistanceEpsilon) {
+ //v2 becomes the zero point
+ d1 /= nd1;
+ r0 = 0; //v2 position
+ r1 = d1 * (v3 - v2);//v3 position
+ r2 = d1 * (v0 - v2);//v0 position - here v0 and v1 are indistinguishable
+
+
+ if (r2 < r0) {
+ bSolution = 0;
+ aSolution = 1;
+ } else if (r2 > r1) {
+ bSolution = 1;
+ aSolution = 0;
+
+ } else {
+ double r = Math.Min(r1, r2);
+ bSolution = r / (r1 - r0);
+ aSolution = 0;
+ }
+
+ } else {
+ aSolution = 0;
+ bSolution = 0;
+ }
+ }
+ aPoint = curveA[aSolution];
+ bPoint = curveB[bSolution];
+ }
+ }
+
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/MinDistCurveCurve.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/MinDistCurveCurve.cs.meta
new file mode 100644
index 000000000..2aebbafd4
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/MinDistCurveCurve.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 769a03cab788c094b9cd2195e07c0e8c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Parallelogram.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Parallelogram.cs
new file mode 100644
index 000000000..642ba080c
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Parallelogram.cs
@@ -0,0 +1,461 @@
+// #region Using directives
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+//#endregion
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+
+ ///
+ /// Parallelogram vertex type.
+ /// The clockwise order of vertices is Corner, A, OtherCorner,B
+ ///
+
+
+ public enum VertexId {
+ ///
+ /// the basic corner vertex
+ ///
+ Corner = 0,
+ ///
+ /// a vertex adjacent to the basic corner
+ ///
+ VertexA,
+ ///
+ /// the corner opposite to the basic corner
+ ///
+ OtherCorner,
+ ///
+ /// another vertex adjacent to the basic corner
+ ///
+ VertexB
+ }
+
+ ///
+ /// It is a parallelogram with the vertices corner, corner+coeff,corner+coeff+side1 and corner+side1.
+ /// Parallelograms are used by GLEE in curve intersections routines.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")
+#if TEST_MSAGL
+ , Serializable
+#endif
+ ]
+ //parallelograms are not stored in dictionaries
+ public struct Parallelogram {
+
+ bool isSeg;
+
+ Point corner;
+ Point a;//a side exiting from the corner
+ Point b;//another side exiting from the corner
+ Point otherCorner;
+ Point aPlusCorner;
+ Point bPlusCorner;
+
+#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=371
+ // SharpKit/Colin: this to support the lack of int casting to enums
+ private IEnumerable AllVertices
+ {
+ get
+ {
+ yield return corner;
+ yield return aPlusCorner;
+ yield return otherCorner;
+ yield return bPlusCorner;
+ }
+ }
+
+ private IEnumerable OtherVertices
+ {
+ get
+ {
+ yield return aPlusCorner;
+ yield return otherCorner;
+ yield return bPlusCorner;
+ }
+ }
+#endif
+
+ internal Point Corner {
+ get { return corner; }
+ }
+
+ internal Point OtherCorner {
+ get { return otherCorner; }
+ }
+
+ Point aRot;//a rotated on 90 degrees towards side1
+ Point bRot; //side1 rotated on 90 degrees towards coeff
+
+ double abRot; //the scalar product coeff*bRot
+ double baRot; //the scalar product side1*aRot;
+///
+/// to string
+///
+///
+ override public string ToString() {
+ return String.Format(CultureInfo.InvariantCulture, "({0},{1},{2},{3})", Vertex(0), Vertex(VertexId.VertexA),
+ Vertex(VertexId.OtherCorner), Vertex(VertexId.VertexB));
+ }
+
+ ///
+ /// Constructs the parallelogram by the corner and two sides.
+ ///
+ /// the corner
+ /// a side
+ /// another side
+ public Parallelogram(Point corner, Point sideA, Point sideB) {
+
+ this.corner = corner;
+ this.a = sideA;
+ this.b = sideB;
+
+ this.aRot = new Point(-sideA.Y, sideA.X);
+ if (aRot.Length > 0.5)
+ aRot = aRot.Normalize();
+
+ this.bRot = new Point(-sideB.Y, sideB.X);
+ if (bRot.Length > 0.5)
+ bRot = bRot.Normalize();
+
+ abRot = sideA * bRot;
+
+ baRot = sideB * aRot;
+
+
+ if (abRot < 0) {
+ abRot = -abRot;
+ bRot = -bRot;
+ }
+
+ if (baRot < 0) {
+ baRot = -baRot;
+ aRot = -aRot;
+ }
+
+
+ isSeg = (sideA - sideB).Length < ApproximateComparer.DistanceEpsilon;
+
+ aPlusCorner = sideA + corner;
+ otherCorner = sideB +aPlusCorner;
+ bPlusCorner = sideB + corner;
+ }
+ ///
+ /// Return true if the parallelogram contains the point
+ ///
+ ///
+ ///
+ public bool Contains(Point point) {
+ Point g = point - corner;
+ double e = ApproximateComparer.DistanceEpsilon;
+
+ double gbRot = g * bRot;
+ if (gbRot > abRot + e || gbRot < -e)
+ return false;
+
+ double gaRot = g * aRot;
+ return gaRot <= baRot + e && gaRot >= -e;
+ }
+
+
+///
+/// return the area of the parallelogram
+///
+ public double Area { get { return Math.Abs(a.X * b.Y - a.Y * b.X); } }
+
+ ///
+ /// Return the correspoingin vertex of the parallelogram
+ ///
+ /// vertex kind
+ /// vertex value
+ public Point Vertex(VertexId vertexPar) {
+ switch (vertexPar) {
+ case VertexId.Corner: return corner;
+ case VertexId.VertexA: return aPlusCorner;
+ case VertexId.OtherCorner: return OtherCorner;
+ case VertexId.VertexB: return bPlusCorner;
+ default:
+ throw new InvalidOperationException();
+ }
+ }
+
+///
+/// returns true if parallelograms intersect
+///
+ ///
+ ///
+///
+ static public bool Intersect(Parallelogram parallelogram0, Parallelogram parallelogram1) {
+ //my hunch is that p0 and p1 do not intersect if and only if
+ //they can be separated with one of the parallelogram sides in case when at least one of them
+
+
+ bool ret = !(separByA(ref parallelogram0, ref parallelogram1) || separByA(ref parallelogram1, ref parallelogram0) ||
+ separByB(ref parallelogram0, ref parallelogram1) || separByB(ref parallelogram1, ref parallelogram0));
+
+ if (ret == false)
+ return false;
+
+ if (!(parallelogram0.isSeg && parallelogram1.isSeg))
+ return true;
+
+
+ if (!Point.ParallelWithinEpsilon(parallelogram0.OtherCorner - parallelogram0.Corner,
+ parallelogram1.OtherCorner - parallelogram1.Corner, 1.0E-5))
+ return true;
+
+ //here we know that the segs are parallel
+ return ParallelSegsIntersect(ref parallelogram1, ref parallelogram0);
+
+ }
+
+
+ static bool ParallelSegsIntersect(ref Parallelogram p0, ref Parallelogram p1) {
+
+ Point v0 = p0.Corner;
+ Point v1 = p0.OtherCorner;
+
+ Point v2 = p1.Corner;
+ Point v3 = p1.OtherCorner;
+
+
+ Point d = v1 - v0;
+
+ //let us imagine that v0 is at zero
+
+ double r0 = 0; // position of v0
+
+ //offset of v1
+ double r1 = d * d;
+
+ //offset of v2
+ double r2 = (v2 - v0) * d;
+
+ //offset of v3
+ double r3 = (v3 - v0) * d;
+
+ // we need to check if [r0,r1] intersects [r2,r3]
+
+ if (r2 > r3) {
+ double t = r2;
+ r2 = r3;
+ r3 = t;
+ }
+
+ return !(r3 < r0 - ApproximateComparer.DistanceEpsilon || r2 > r1 + ApproximateComparer.DistanceEpsilon);
+
+ }
+
+ static double mult(ref Point a, ref Point b) {
+ return a.X * b.X + a.Y * b.Y;
+ }
+
+ static bool separByA(ref Parallelogram p0, ref Parallelogram p1) {
+
+ double eps = ApproximateComparer.DistanceEpsilon;
+ Point t = new Point(p1.corner.X - p0.corner.X, p1.corner.Y - p0.corner.Y);
+ double p1a = mult(ref t , ref p0.aRot);
+
+ if (p1a > p0.baRot + eps) {
+ t.X = p1.aPlusCorner.X - p0.corner.X;
+ t.Y = p1.aPlusCorner.Y - p0.corner.Y;
+ if (mult(ref t, ref p0.aRot) <= p0.baRot + eps)
+ return false;
+
+ t.X = p1.bPlusCorner.X - p0.corner.X;
+ t.Y = p1.bPlusCorner.Y - p0.corner.Y;
+ if (mult(ref t, ref p0.aRot) <= p0.baRot + eps)
+ return false;
+
+ t.X = p1.otherCorner.X - p0.corner.X;
+ t.Y = p1.otherCorner.Y - p0.corner.Y;
+ if (mult(ref t, ref p0.aRot) <= p0.baRot + eps)
+ return false;
+
+ return true;
+ } else if (p1a < -eps) {
+ t.X = p1.aPlusCorner.X - p0.corner.X;
+ t.Y = p1.aPlusCorner.Y - p0.corner.Y;
+ if (mult(ref t, ref p0.aRot) >= - eps)
+ return false;
+
+ t.X = p1.bPlusCorner.X - p0.corner.X;
+ t.Y = p1.bPlusCorner.Y - p0.corner.Y;
+ if (mult(ref t, ref p0.aRot) >= -eps)
+ return false;
+
+ t.X = p1.otherCorner.X - p0.corner.X;
+ t.Y = p1.otherCorner.Y - p0.corner.Y;
+ if (mult(ref t, ref p0.aRot) >= -eps)
+ return false;
+
+ return true;
+ }
+
+ return false;/*
+ double eps = Curve.DistEps;
+
+ double p1a = (p1.Corner - p0.Corner) * p0.aRot;
+
+ if (p1a > p0.baRot + eps) {
+ for (int i = 1; i < 4; i++) {
+ if ((p1.Vertex((Parallelogram.VertexId)i) - p0.corner) * p0.aRot <= p0.baRot + eps)
+ return false;
+ }
+ return true;
+ } else if (p1a < -eps) {
+ for (int i = 1; i < 4; i++) {
+
+ double delta = (p1.Vertex((Parallelogram.VertexId)i) - p0.corner) * p0.aRot;
+
+ if (delta >= -eps) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return false;*/
+ }
+
+
+ static bool separByB(ref Parallelogram p0, ref Parallelogram p1) {
+ double eps = ApproximateComparer.DistanceEpsilon;
+ double p1a = (p1.Vertex(0) - p0.corner) * p0.bRot;
+
+ if (p1a > p0.abRot + eps) {
+#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=371
+ //SharpKit/Colin - can't cast ints to enum
+ foreach (var i in p1.OtherVertices)
+ if ((i - p0.corner)*p0.bRot <= p0.abRot + eps)
+ return false;
+#else
+ for (int i = 1; i < 4; i++) {
+ if ((p1.Vertex((VertexId)i) - p0.corner) * p0.bRot <= p0.abRot + eps)
+ return false;
+ }
+#endif
+
+ return true;
+ } else if (p1a < -eps) {
+#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=371
+ //SharpKit/Colin - can't cast ints to enum
+ foreach (var i in p1.OtherVertices)
+ if ((i - p0.corner) * p0.bRot >= -eps)
+ return false;
+#else
+ for (int i = 1; i < 4; i++) {
+ if ((p1.Vertex((VertexId)i) - p0.corner) * p0.bRot >= -eps)
+ return false;
+ }
+#endif
+ return true;
+ }
+ return false;
+ }
+ ///
+ /// create a Parallelogram over a group
+ ///
+ /// group of boxes
+ /// xy box
+ internal static Parallelogram GetParallelogramOfAGroup(List boxes) {
+
+ double minX = 0, maxX = 0, minY = 0, maxY = 0;//have to int - getting compiler error - bug
+ bool firstTime = true;
+ foreach (Parallelogram b in boxes) {
+#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=371
+ // SharpKit/Colin: can't cast ints to enums
+ foreach (Point v in b.AllVertices) {
+#else
+ for (int i = 0; i < 4; i++) {
+ Point v = b.Vertex((VertexId)i);
+#endif
+ double x = v.X;
+ double y = v.Y;
+ if (firstTime) {
+ firstTime = false;
+ minX = maxX = x;
+ minY = maxY = y;
+ } else {
+ if (x < minX) {
+ minX = x;
+ } else if (x > maxX) {
+ maxX = x;
+ }
+ if (y < minY) {
+ minY = y;
+ } else if (y > maxY) {
+ maxY = y;
+ }
+ }
+ }
+
+ }
+ return new Parallelogram(new Point(minX, minY), new Point(0, maxY - minY), new Point(maxX - minX, 0));
+ }
+
+ internal Parallelogram(Parallelogram box0, Parallelogram box1) {
+ Point v = box0.Corner;
+ double minX, maxX, minY, maxY;
+ minX = maxX = v.X;
+ minY = maxY = v.Y;
+
+
+ PumpMinMax(ref minX, ref maxX, ref minY, ref maxY, ref box0.aPlusCorner);
+ PumpMinMax(ref minX, ref maxX, ref minY, ref maxY, ref box0.otherCorner);
+ PumpMinMax(ref minX, ref maxX, ref minY, ref maxY, ref box0.bPlusCorner);
+
+ PumpMinMax(ref minX, ref maxX, ref minY, ref maxY, ref box1.corner);
+ PumpMinMax(ref minX, ref maxX, ref minY, ref maxY, ref box1.aPlusCorner);
+ PumpMinMax(ref minX, ref maxX, ref minY, ref maxY, ref box1.otherCorner);
+ PumpMinMax(ref minX, ref maxX, ref minY, ref maxY, ref box1.bPlusCorner);
+
+ this.corner = new Point(minX, minY);
+ this.a = new Point(0, maxY - minY);
+ this.b = new Point(maxX - minX, 0);
+
+ aPlusCorner = a + corner;
+ otherCorner = b + aPlusCorner;
+ bPlusCorner = b + corner;
+
+ this.aRot = new Point(-this.a.Y, this.a.X);
+ if (aRot.Length > 0.5)
+ aRot = aRot.Normalize();
+
+ this.bRot = new Point(-this.b.Y, this.b.X);
+ if (bRot.Length > 0.5)
+ bRot = bRot.Normalize();
+
+ abRot = this.a * bRot;
+ baRot = this.b * aRot;
+
+
+ if (abRot < 0) {
+ abRot = -abRot;
+ bRot = -bRot;
+ }
+
+ if (baRot < 0) {
+ baRot = -baRot;
+ aRot = -aRot;
+ }
+
+ isSeg = (this.a - this.b).Length < ApproximateComparer.DistanceEpsilon;
+ }
+
+ static void PumpMinMax(ref double minX, ref double maxX, ref double minY, ref double maxY,
+ ref Point p) {
+ if (p.X < minX) {
+ minX = p.X;
+ } else if (p.X > maxX) {
+ maxX = p.X;
+ }
+ if (p.Y < minY) {
+ minY = p.Y;
+ } else if (p.Y > maxY) {
+ maxY = p.Y;
+ }
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Parallelogram.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Parallelogram.cs.meta
new file mode 100644
index 000000000..2b532a52e
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/Parallelogram.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8f080cd400c38514bbbece3bca6c2bb7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramBinaryTreeNode.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramBinaryTreeNode.cs
new file mode 100644
index 000000000..8921bc327
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramBinaryTreeNode.cs
@@ -0,0 +1,27 @@
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ ///
+ /// Keeps left and right sons of the node. Is used in curve intersections routines.
+ ///
+ internal class ParallelogramBinaryTreeNode:ParallelogramNode {
+
+ ParallelogramNode leftSon;
+ public ParallelogramNode LeftSon {
+ get {
+ return leftSon;
+ }
+ set {
+ leftSon = value;
+ }
+ }
+ ParallelogramNode rightSon;
+
+ public ParallelogramNode RightSon {
+ get {
+ return rightSon;
+ }
+ set {
+ rightSon = value;
+ }
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramBinaryTreeNode.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramBinaryTreeNode.cs.meta
new file mode 100644
index 000000000..25b9e1484
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramBinaryTreeNode.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 32e489400e6548f48b2083d3fb5089b0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramInternalTreeNode.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramInternalTreeNode.cs
new file mode 100644
index 000000000..123c161ee
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramInternalTreeNode.cs
@@ -0,0 +1,41 @@
+#region Using directives
+
+using System;
+using System.Collections.Generic;
+
+#endregion
+
+namespace Microsoft.Msagl.Core.Geometry.Curves
+{
+
+ ///
+ /// An internal node of ParallelogramNodeOverICurve hierarchy.
+ /// Is used in curve intersections routines.
+ ///
+#if TEST_MSAGL
+ [Serializable]
+#endif
+ internal class ParallelogramInternalTreeNode : ParallelogramNodeOverICurve
+ {
+ List children = new List();
+
+ internal ParallelogramInternalTreeNode(ICurve seg, double leafBoxesOffset):base(seg,leafBoxesOffset)
+ {
+ }
+
+ ///
+ /// children of the node
+ ///
+ ///
+ internal List Children
+ {
+ get { return children; }
+ }
+
+ internal void AddChild(ParallelogramNodeOverICurve node)
+ {
+ children.Add(node);
+ }
+ }
+
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramInternalTreeNode.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramInternalTreeNode.cs.meta
new file mode 100644
index 000000000..93bb13e53
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramInternalTreeNode.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3aa68f74e4ef5794f93ff1829db71dc0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramLeaf.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramLeaf.cs
new file mode 100644
index 000000000..39386df51
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramLeaf.cs
@@ -0,0 +1,54 @@
+using System;
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+
+ ///
+ /// A leaf of the ParallelogramNodeOverICurve hierarchy.
+ /// Is used in curve intersectons routine.
+ ///
+#if TEST_MSAGL
+ [Serializable]
+#endif
+ internal class ParallelogramLeaf : ParallelogramNodeOverICurve {
+ double low;
+
+ internal double Low {
+ get {
+ return low;
+ }
+ set {
+ low = value;
+ }
+ }
+ double high;
+
+ internal double High {
+ get {
+ return high;
+ }
+ set {
+ high = value;
+ }
+ }
+
+ internal ParallelogramLeaf(double low, double high, Parallelogram box, ICurve seg, double leafBoxesOffset)
+ : base(seg, leafBoxesOffset) {
+ this.low = low;
+ this.high = high;
+ this.Parallelogram = box;
+ }
+
+
+
+ LineSegment chord;
+
+ internal LineSegment Chord {
+ get { return chord; }
+ set {
+ chord = value;
+ if (!ApproximateComparer.Close(Seg[low], chord.Start))
+ throw new InvalidOperationException();
+ }
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramLeaf.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramLeaf.cs.meta
new file mode 100644
index 000000000..89c77f11d
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramLeaf.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4c3631e140b119c42a768dcdad7318a8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramNode.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramNode.cs
new file mode 100644
index 000000000..04713fd24
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramNode.cs
@@ -0,0 +1,31 @@
+#region Using directives
+
+
+
+#endregion
+
+using System;
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ ///
+ /// Represents a node containing a parallelogram.
+ /// Is used in curve intersections routines.
+ ///
+#if TEST_MSAGL
+ [Serializable]
+#endif
+ abstract public class ParallelogramNode {
+ Parallelogram parallelogram;
+ ///
+ /// gets or sets the parallelogram of the node
+ ///
+ public Parallelogram Parallelogram {
+ get {
+ return parallelogram;
+ }
+ set {
+ parallelogram = value;
+ }
+ }
+ }
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramNode.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramNode.cs.meta
new file mode 100644
index 000000000..bc81bb6f8
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramNode.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9258e849b5418a249ac14eb2cbe49c62
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramNodeOverICurve.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramNodeOverICurve.cs
new file mode 100644
index 000000000..3038c2109
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramNodeOverICurve.cs
@@ -0,0 +1,218 @@
+// //#region Using directives
+
+using System;
+using System.Collections.Generic;
+
+//#endregion
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+
+ ///
+ /// Serves to hold a Parallelogram and a ICurve,
+ /// and is used in curve intersections routines.
+ /// The node can be a top of the hierarchy if its sons are non-nulls.
+ /// The sons are either both nulls or both non-nulls
+ ///
+
+#if TEST_MSAGL
+ [Serializable]
+#endif
+ abstract public class ParallelogramNodeOverICurve : ParallelogramNode {
+
+ ICurve seg;
+ ///
+ /// The segment bounded by the parallelogram
+ ///
+ internal ICurve Seg {
+ get {
+ return seg;
+ }
+ set {
+ seg = value;
+ }
+ }
+
+ double leafBoxesOffset = DefaultLeafBoxesOffset;
+
+ static internal double DefaultLeafBoxesOffset = 0.5;
+ ///
+ /// The leafs of this node are as tight as the offset
+ ///
+ ///
+ internal double LeafBoxesOffset {
+ get {
+ return leafBoxesOffset;
+ }
+ }
+
+
+
+
+ internal ParallelogramNodeOverICurve(ICurve s, double leafBoxesOffset) {
+ seg = s;
+ this.leafBoxesOffset = leafBoxesOffset;
+ }
+
+///
+///
+///
+///
+///
+ static public ParallelogramNodeOverICurve CreateParallelogramNodeForCurveSegment(ICurve segment) {
+ ValidateArg.IsNotNull(segment, "segment");
+ return CreateParallelogramNodeForCurveSeg(segment.ParStart, segment.ParEnd, segment, DefaultLeafBoxesOffset);
+ }
+
+ static bool WithinEpsilon(ICurve seg, double start, double end, double eps) {
+ if (seg is LineSegment)
+ return true;
+
+ int n = 3; //hack !!!! but maybe can be proven for Bezier curves and other regular curves
+ double d = (end - start) / n;
+ Point s = seg[start];
+ Point e = seg[end];
+
+ double d0 = DistToSegm(seg[start + d], s, e);
+ double d1 = DistToSegm(seg[start + d * (n-1)], s, e);
+ //double d1d1 = seg.d1(start) * seg.d1(end);
+
+ return d0 < eps
+ &&
+ d1 < eps;// && d1d1 > 0;
+
+ }
+
+ internal static double DistToSegm(Point p, Point s, Point e) {
+
+ Point l = e - s;
+ if (l.Length < ApproximateComparer.IntersectionEpsilon)
+ return (p - (0.5f * (s + e))).Length;
+ Point perp = new Point(-l.Y, l.X);
+ perp = perp * (1.0f / perp.Length);
+ return Math.Abs((p - s) * perp);
+
+ }
+
+
+
+ ///
+ /// Creates a bounding parallelogram on a curve segment
+ /// We suppose here that the segment is convex or concave from start to end,
+ /// that is the region bounded by the straight segment seg[start], seg[end] and the curve seg is convex
+ ///
+ internal static bool CreateParallelogramOnSubSeg(double start, double end, ICurve seg, ref Parallelogram box,
+ Point startPoint, Point endPoint) {
+
+ if (seg is CubicBezierSegment)
+ return CreateParallelogramOnSubSegOnBezierSeg(start, end, seg, ref box);
+
+ Point tan1 = seg.Derivative(start);
+
+ Point tan2 = seg.Derivative(end);
+ Point tan2Perp = Point.P(-tan2.Y, tan2.X);
+
+
+
+ Point p = endPoint - startPoint;
+
+ double numerator = p * tan2Perp;
+ double denumerator = (tan1 * tan2Perp);
+ double x;// = (p * tan2Perp) / (tan1 * tan2Perp);
+ if (Math.Abs(numerator) < ApproximateComparer.DistanceEpsilon)
+ x = 0;
+ else if (Math.Abs(denumerator) < ApproximateComparer.DistanceEpsilon) {
+ //it is degenerated; adjacent sides are parallel, but
+ //since p * tan2Perp is big it does not contain e
+ return false;
+ } else x = numerator / denumerator;
+
+ tan1 *= x;
+
+ box = new Parallelogram(startPoint, tan1, endPoint - startPoint - tan1);
+#if DEBUGCURVES
+ if (!box.Contains(seg[end]))
+ {
+
+ throw new InvalidOperationException();//"the box does not contain the end of the segment");
+ }
+#endif
+
+ double delta = (end - start) / 64;
+ for (int i = 1; i < 64; i++) {
+ if (!box.Contains(seg[start + delta * i]))
+ return false;
+ }
+ return true;
+
+
+ }
+ delegate Point B(int i);
+ internal static bool CreateParallelogramOnSubSegOnBezierSeg(double start, double end, ICurve seg, ref Parallelogram box) {
+
+ CubicBezierSegment trimSeg = seg.Trim(start, end) as CubicBezierSegment;
+
+
+ B b = trimSeg.B;
+ Point a = b(1) - b(0);
+
+ box = new Parallelogram(b(0), a, b(3) - b(0));
+
+ if (box.Contains(b(2)))
+ return true;
+
+ box = new Parallelogram(b(3), b(2) - b(3), b(0) - b(3));
+
+ if (box.Contains(b(1)))
+ return true;
+
+ return false;
+ }
+
+
+ internal static ParallelogramNodeOverICurve CreateParallelogramNodeForCurveSeg(double start, double end, ICurve seg, double eps) {
+
+ bool closedSeg = (start == seg.ParStart && end == seg.ParEnd && ApproximateComparer.Close(seg.Start, seg.End));
+ if (closedSeg)
+ return CreateNodeWithSegmentSplit(start, end, seg, eps);
+
+ Point s = seg[start];
+ Point e = seg[end];
+ Point w = e - s;
+ Point middle = seg[(start + end)/2];
+ if (
+ DistToSegm(middle, s, e) <= ApproximateComparer.IntersectionEpsilon
+ && w*w < Curve.LineSegmentThreshold*Curve.LineSegmentThreshold
+ && end - start < Curve.LineSegmentThreshold) {
+ var ls = new LineSegment(s, e);
+ var leaf = ls.ParallelogramNodeOverICurve as ParallelogramLeaf;
+ leaf.Low = start;
+ leaf.High = end;
+ leaf.Seg = seg;
+ leaf.Chord = ls;
+ return leaf;
+ }
+
+ bool we = WithinEpsilon(seg, start, end, eps);
+ Parallelogram box = new Parallelogram();
+
+ if (we && CreateParallelogramOnSubSeg(start, end, seg, ref box, s, e))
+ return new ParallelogramLeaf(start, end, box, seg, eps);
+
+ return CreateNodeWithSegmentSplit(start, end, seg, eps);
+
+ }
+
+ static ParallelogramNodeOverICurve CreateNodeWithSegmentSplit(double start, double end, ICurve seg, double eps) {
+ ParallelogramInternalTreeNode pBNode = new ParallelogramInternalTreeNode(seg, eps);
+ pBNode.AddChild(CreateParallelogramNodeForCurveSeg(start, 0.5 * (start + end), seg, eps));
+ pBNode.AddChild(CreateParallelogramNodeForCurveSeg(0.5 * (start + end), end, seg, eps));
+ List boxes = new List();
+ boxes.Add(pBNode.Children[0].Parallelogram);
+ boxes.Add(pBNode.Children[1].Parallelogram);
+ pBNode.Parallelogram = Parallelogram.GetParallelogramOfAGroup(boxes);
+ return pBNode;
+
+ }
+ }
+
+}
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramNodeOverICurve.cs.meta b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramNodeOverICurve.cs.meta
new file mode 100644
index 000000000..3d0c6f968
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/ParallelogramNodeOverICurve.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cf0f7ac8cff442e40bec6be58bdbecc0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/PenetrationDepth.cs b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/PenetrationDepth.cs
new file mode 100644
index 000000000..a01cba03e
--- /dev/null
+++ b/Assets/Scripts/NIH/MSAGL/Core/Geometry/Curves/PenetrationDepth.cs
@@ -0,0 +1,202 @@
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Msagl.Core.Geometry.Curves {
+ ///
+ /// Works for convex curves by calculating a value which might be slightly greater than the exact penetration depth
+ /// It is presize on convex polygons.
+ ///
+ static public class PenetrationDepth {
+
+ ///
+ /// Calculates a vector of minimal length by which to move the first curve to avoid intersections of interiors.
+ /// It is presize on polylines and will be slightly longer on curves.
+ ///
+ ///
+ ///
+ /// the shortest vector d such that d+curve0 does not intersect the interiou curve1
+ static public Point PenetrationVector(ICurve curve0, ICurve curve1) {
+ return PenetrationDepthForPolylines(PolylineAroundClosedCurve(curve0), PolylineAroundClosedCurve(curve1));
+ }
+ ///
+ /// returns a polyline around the curve
+ ///
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline")]
+ public static Polyline PolylineAroundClosedCurve(ICurve curve) {
+ Polyline poly = new Polyline();
+ foreach (Point point in PointsOnAroundPolyline(curve))
+ poly.AddPoint(point);
+ if (Point.GetTriangleOrientation(poly.StartPoint.Point, poly.StartPoint.Next.Point, poly.StartPoint.Next.Next.Point) == TriangleOrientation.Counterclockwise)
+ poly = (Polyline)poly.Reverse();
+ poly.Closed = true;
+ return poly;
+ }
+
+
+ static IEnumerable PointsOnAroundPolyline(ICurve curve) {
+ bool firstSide = true;
+ CurveTangent prevTangent = null;
+ CurveTangent firstCurveTangent = null;
+ foreach (CurveTangent curveTangent in TangentsAroundCurve(curve)) {
+ if (firstSide) {
+ firstSide = false;
+ firstCurveTangent = prevTangent = curveTangent;
+ } else {
+ if (!TangentsAreParallel(prevTangent, curveTangent))
+ yield return TangentIntersection(prevTangent, curveTangent);
+ prevTangent = curveTangent;
+ }
+ }
+ yield return TangentIntersection(firstCurveTangent, prevTangent);
+
+ }
+
+
+
+ static IEnumerable