From 977629ae42725bfa486105ac3f830dfea3443ac3 Mon Sep 17 00:00:00 2001 From: Devin Miller Date: Thu, 9 Jan 2020 08:09:15 -0800 Subject: [PATCH 01/12] Fixed distance computation for placing new 3d points (#47) When placing a new point on a non-closed path, the expected behavior is that the point would be placed a distance from the scene camera equal to the distance from the scene camera and the last point on the spline. However, distance was being computed by comparing the scene camera's global coordinates and the end point's local coordinates, meaning the computation would only be correct when the parent game object is located at the scene origin. Modified the distance computation to compare distances using the end point's global position. Also added a check to pick the "end point" based on whether the new point is getting appended or prepended. Tested manually in scene with a PathCreator component. --- .../PathCreator/Core/Editor/PathEditor.cs | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/PathEditor.cs b/Path Creator Project/Assets/PathCreator/Core/Editor/PathEditor.cs index 7c52cc6..e4dbd8b 100755 --- a/Path Creator Project/Assets/PathCreator/Core/Editor/PathEditor.cs +++ b/Path Creator Project/Assets/PathCreator/Core/Editor/PathEditor.cs @@ -371,17 +371,26 @@ void ProcessBezierPathInput (Event e) { bezierPath.SplitSegment (newPathPoint, selectedSegmentIndex, pathMouseInfo.timeOnBezierSegment); } // If path is not a closed loop, add new point on to the end of the path - else if (!bezierPath.IsClosed) { + else if (!bezierPath.IsClosed) + { + // If control/command are held down, the point gets pre-pended, so we want to check distance + // to the endpoint we are adding to + var pointIdx = e.control || e.command ? 0 : bezierPath.NumPoints - 1; // insert new point at same dst from scene camera as the point that comes before it (for a 3d path) - float dstCamToEndpoint = (Camera.current.transform.position - bezierPath[bezierPath.NumPoints - 1]).magnitude; - Vector3 newPathPoint = MouseUtility.GetMouseWorldPosition (bezierPath.Space, dstCamToEndpoint); - newPathPoint = MathUtility.InverseTransformPoint (newPathPoint, creator.transform, bezierPath.Space); + var endPointLocal = bezierPath[pointIdx]; + var endPointGlobal = + MathUtility.TransformPoint(endPointLocal, creator.transform, bezierPath.Space); + var distanceCameraToEndpoint = (Camera.current.transform.position - endPointGlobal).magnitude; + var newPointGlobal = + MouseUtility.GetMouseWorldPosition (bezierPath.Space, distanceCameraToEndpoint); + var newPointLocal = + MathUtility.InverseTransformPoint (newPointGlobal, creator.transform, bezierPath.Space); Undo.RecordObject (creator, "Add segment"); if (e.control || e.command) { - bezierPath.AddSegmentToStart (newPathPoint); + bezierPath.AddSegmentToStart (newPointLocal); } else { - bezierPath.AddSegmentToEnd (newPathPoint); + bezierPath.AddSegmentToEnd (newPointLocal); } } From a12b510654b61c1856e9408431a1ecbfd4ccb96e Mon Sep 17 00:00:00 2001 From: Devin Miller Date: Mon, 17 Feb 2020 08:46:40 -0800 Subject: [PATCH 02/12] Add raycast check to GetMouseWorldPosition (#50) When building a path on top of a given scene, may intuitively expect to click on points in the scene geometry to add path segments. However, the path creator always creates points a pre-specified distance from the editor camera. Added a raycast check that looks for potential geometry the user may be clicking on. If there is geometry close to the pre-specified distance, a point will be generated at the ray intersection. Otherwise, we fall back to the original behavior. Tested manually in editor. --- .../Core/Editor/Helper/MouseUtility.cs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs b/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs index 0ae6b33..1f7aa53 100755 --- a/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs +++ b/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs @@ -7,31 +7,33 @@ namespace PathCreationEditor public static class MouseUtility { /// - /// Determines mouse position in world. If PathSpace is xy/xz, the position will be locked to that plane. - /// If PathSpace is xyz, then depthFor3DSpace will be used as distance from scene camera. - /// + /// Determines mouse position in world. If PathSpace is xy/xz, the position will be locked to that plane. + /// If PathSpace is xyz, will attempt to raycast to a reasonably close object, or return the position + /// at depthFor3DSpace distance from the current view + /// public static Vector3 GetMouseWorldPosition(PathSpace space, float depthFor3DSpace = 10) { - Ray mouseRay = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); - Vector3 worldMouse = mouseRay.GetPoint(depthFor3DSpace); + var mouseRay = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); + var worldMouse = Physics.Raycast(mouseRay, out var hitInfo, depthFor3DSpace * 2f) ? + hitInfo.point : mouseRay.GetPoint(depthFor3DSpace); // Mouse can only move on XY plane if (space == PathSpace.xy) { - float zDir = mouseRay.direction.z; + var zDir = mouseRay.direction.z; if (zDir != 0) { - float dstToXYPlane = Mathf.Abs(mouseRay.origin.z / zDir); + var dstToXYPlane = Mathf.Abs(mouseRay.origin.z / zDir); worldMouse = mouseRay.GetPoint(dstToXYPlane); } } // Mouse can only move on XZ plane else if (space == PathSpace.xz) { - float yDir = mouseRay.direction.y; + var yDir = mouseRay.direction.y; if (yDir != 0) { - float dstToXZPlane = Mathf.Abs(mouseRay.origin.y / yDir); + var dstToXZPlane = Mathf.Abs(mouseRay.origin.y / yDir); worldMouse = mouseRay.GetPoint(dstToXZPlane); } } @@ -40,4 +42,4 @@ public static Vector3 GetMouseWorldPosition(PathSpace space, float depthFor3DSpa } } -} \ No newline at end of file +} From 8e093006d196f00771a8da9a68f8fdf36ca1d6d4 Mon Sep 17 00:00:00 2001 From: Peter <6738908+pschraut@users.noreply.github.com> Date: Mon, 17 Feb 2020 17:48:21 +0100 Subject: [PATCH 03/12] VertexPath.CalculateClosestPointOnPathData optimization (#53) * added test scene/script that checks if the position returned by GetClosestPointOnPath is almost identical to the input position if the input position is on the path already. * added functionality to the test the old GetClosestPointOnPath, GetClosestTimeOnPath and GetClosestDistanceAlongPath results with the upcoming optimized implementations. added functionality to profile the old and new implementations. added scene that visualizes a position near the path and its actual old and new GetClosestPointOnPath results. * Optimized VertexPath.CalculateClosestPointOnPathData method. On my PC, with the provided test scene "GetClosestPointOnPath Test" the profiling results are: - Old implementation: 5.58ms - 14.62ms - New implementation: 0.25ms - 0.40ms The idea for the optimization is to do the math in "VertexPath local-space" rather than world-space. This optimization thus avoids to transform hundrets or thousands of path positions to world-space. It only applies two transformations instead: One to transform the incoming worldPoint into local-space and then a transform of the computed result from local- to world-space. --- .../Core/Runtime/Objects/VertexPath.cs | 34 +- .../Scenes/GetClosestPointOnPath Test.unity | 644 ++++++++++++++++++ .../GetClosestPointOnPath Test.unity.meta | 7 + .../Scripts/GetClosestPointOnPathTest.cs | 121 ++++ .../Scripts/GetClosestPointOnPathTest.cs.meta | 11 + 5 files changed, 805 insertions(+), 12 deletions(-) create mode 100644 Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity create mode 100644 Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity.meta create mode 100644 Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs create mode 100644 Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs b/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs index 5fd6e6b..0f2ac7b 100755 --- a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs +++ b/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs @@ -32,7 +32,7 @@ public class VertexPath { /// Equal to (0,0,-1) for 2D paths, and (0,1,0) for XZ paths public readonly Vector3 up; - // Default values and constants: + // Default values and constants: const int accuracy = 10; // A scalar for how many times bezier path is divided when determining vertex positions const float minVertexSpacing = .01f; @@ -228,19 +228,29 @@ public Quaternion GetRotation (float t, EndOfPathInstruction endOfPathInstructio /// Finds the closest point on the path from any point in the world public Vector3 GetClosestPointOnPath (Vector3 worldPoint) { - TimeOnPathData data = CalculateClosestPointOnPathData (worldPoint); - return Vector3.Lerp (GetPoint (data.previousIndex), GetPoint (data.nextIndex), data.percentBetweenIndices); + // Transform the provided worldPoint into VertexPath local-space. + // This allows to do math on the localPoint's, thus avoiding the need to + // transform each local vertexpath point into world space via GetPoint. + Vector3 localPoint = MathUtility.InverseTransformPoint(worldPoint, transform, space); + + TimeOnPathData data = CalculateClosestPointOnPathData (localPoint); + Vector3 localResult = Vector3.Lerp (localPoints[data.previousIndex], localPoints[data.nextIndex], data.percentBetweenIndices); + + // Transform local result into world space + return MathUtility.TransformPoint(localResult, transform, space); } /// Finds the 'time' (0=start of path, 1=end of path) along the path that is closest to the given point public float GetClosestTimeOnPath (Vector3 worldPoint) { - TimeOnPathData data = CalculateClosestPointOnPathData (worldPoint); + Vector3 localPoint = MathUtility.InverseTransformPoint(worldPoint, transform, space); + TimeOnPathData data = CalculateClosestPointOnPathData (localPoint); return Mathf.Lerp (times[data.previousIndex], times[data.nextIndex], data.percentBetweenIndices); } /// Finds the distance along the path that is closest to the given point public float GetClosestDistanceAlongPath (Vector3 worldPoint) { - TimeOnPathData data = CalculateClosestPointOnPathData (worldPoint); + Vector3 localPoint = MathUtility.InverseTransformPoint(worldPoint, transform, space); + TimeOnPathData data = CalculateClosestPointOnPathData(localPoint); return Mathf.Lerp (cumulativeLengthAtEachVertex[data.previousIndex], cumulativeLengthAtEachVertex[data.nextIndex], data.percentBetweenIndices); } @@ -248,7 +258,7 @@ public float GetClosestDistanceAlongPath (Vector3 worldPoint) { #region Internal methods - /// For a given value 't' between 0 and 1, calculate the indices of the two vertices before and after t. + /// For a given value 't' between 0 and 1, calculate the indices of the two vertices before and after t. /// Also calculate how far t is between those two vertices as a percentage between 0 and 1. TimeOnPathData CalculatePercentOnPathData (float t, EndOfPathInstruction endOfPathInstruction) { // Constrain t based on the end of path instruction @@ -295,7 +305,7 @@ TimeOnPathData CalculatePercentOnPathData (float t, EndOfPathInstruction endOfPa } /// Calculate time data for closest point on the path from given world point - TimeOnPathData CalculateClosestPointOnPathData (Vector3 worldPoint) { + TimeOnPathData CalculateClosestPointOnPathData (Vector3 localPoint) { float minSqrDst = float.MaxValue; Vector3 closestPoint = Vector3.zero; int closestSegmentIndexA = 0; @@ -311,8 +321,8 @@ TimeOnPathData CalculateClosestPointOnPathData (Vector3 worldPoint) { } } - Vector3 closestPointOnSegment = MathUtility.ClosestPointOnLineSegment (worldPoint, GetPoint (i), GetPoint (nextI)); - float sqrDst = (worldPoint - closestPointOnSegment).sqrMagnitude; + Vector3 closestPointOnSegment = MathUtility.ClosestPointOnLineSegment (localPoint, localPoints[i], localPoints[nextI]); + float sqrDst = (localPoint - closestPointOnSegment).sqrMagnitude; if (sqrDst < minSqrDst) { minSqrDst = sqrDst; closestPoint = closestPointOnSegment; @@ -321,8 +331,8 @@ TimeOnPathData CalculateClosestPointOnPathData (Vector3 worldPoint) { } } - float closestSegmentLength = (GetPoint (closestSegmentIndexA) - GetPoint (closestSegmentIndexB)).magnitude; - float t = (closestPoint - GetPoint (closestSegmentIndexA)).magnitude / closestSegmentLength; + float closestSegmentLength = (localPoints[closestSegmentIndexA] - localPoints[closestSegmentIndexB]).magnitude; + float t = (closestPoint - localPoints[closestSegmentIndexA]).magnitude / closestSegmentLength; return new TimeOnPathData (closestSegmentIndexA, closestSegmentIndexB, t); } @@ -342,4 +352,4 @@ public TimeOnPathData (int prev, int next, float percentBetweenIndices) { } -} \ No newline at end of file +} diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity b/Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity new file mode 100644 index 0000000..e3c27be --- /dev/null +++ b/Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity @@ -0,0 +1,644 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657844, g: 0.49641222, b: 0.57481694, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_TemporalCoherenceThreshold: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 10 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringMode: 2 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ShowResolutionOverlay: 1 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &184727957 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 184727959} + - component: {fileID: 184727958} + m_Layer: 0 + m_Name: Path + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &184727958 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 184727957} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8e5ac92bc18f545cc84cd886ece82b4d, type: 3} + m_Name: + m_EditorClassIdentifier: + editorData: + _bezierPath: + points: + - {x: -4.350084, y: -0.6192361, z: 0.36958665} + - {x: -4.3476486, y: -0.597047, z: 0.52262044} + - {x: -2.7572303, y: -0.70872635, z: 1.6174023} + - {x: -2.3782218, y: -0.38763738, z: 1.8379713} + - {x: -1.9992132, y: -0.06654839, z: 2.0585403} + - {x: -2.4454098, y: 0.8219146, z: 1.9317083} + - {x: -2.0614424, y: 1.2668917, z: 1.9732294} + - {x: -1.677475, y: 1.7118688, z: 2.0147505} + - {x: -0.46440378, y: 1.7154288, z: 2.2258382} + - {x: 0.14839771, y: 1.7105651, z: 2.2219813} + - {x: 0.76119924, y: 1.7057014, z: 2.2181244} + - {x: 4.2510085, y: -0.53037375, z: 1.282062} + - {x: 4.3446193, y: -0.6192361, z: 0.36958665} + - {x: 4.43823, y: -0.7080985, z: -0.54288876} + - {x: 6.1194963, y: 0.44442305, z: -2.4886756} + - {x: 3.7941601, y: -0.07067474, z: -2.6234398} + - {x: 1.4688243, y: -0.5857725, z: -2.758204} + - {x: 5.180417, y: 0.88676715, z: -3.9433024} + - {x: 4.939216, y: 1.0569758, z: -4.3701224} + - {x: 4.698015, y: 1.2271845, z: -4.7969427} + - {x: 2.5704806, y: 0.8330983, z: -4.9625225} + - {x: 2.446082, y: 0.83640313, z: -4.9686565} + - {x: 2.3216836, y: 0.839708, z: -4.9747906} + - {x: 0.31069377, y: 0.7397387, z: -2.727452} + - {x: -0.3484243, y: 0.69917965, z: -2.494286} + - {x: -1.0075424, y: 0.6586206, z: -2.26112} + - {x: -1.4713835, y: -1.2655108, z: -2.326689} + - {x: -2.1617332, y: -1.2623534, z: -2.510853} + - {x: -2.8520832, y: -1.259196, z: -2.695017} + - {x: -3.6936672, y: 0.7034443, z: -3.0868487} + - {x: -3.5478704, y: 0.910876, z: -3.260935} + - {x: -3.4020736, y: 1.1183076, z: -3.4350214} + - {x: -4.3403153, y: 1.11183, z: -3.5472014} + - {x: -4.5300093, y: 1.5598526, z: -4.0236} + - {x: -4.719703, y: 2.0078752, z: -4.4999995} + - {x: -4.878911, y: 0.7921399, z: -2.921095} + - {x: -4.9720774, y: 0.74950266, z: -2.794322} + - {x: -5.0652437, y: 0.70686543, z: -2.6675491} + - {x: -6.667046, y: 0.38763303, z: -1.8212851} + - {x: -6.4901414, y: 0.058772564, z: -1.1090026} + - {x: -6.3132367, y: -0.2700879, z: -0.39672005} + - {x: -4.352519, y: -0.64142525, z: 0.21655288} + isClosed: 1 + space: 0 + controlMode: 1 + autoControlLength: 0.3 + boundsUpToDate: 0 + bounds: + m_Center: {x: 0.005727291, y: 0.5250117, z: -0.39030027} + m_Extent: {x: 4.3562183, y: 1.1872373, z: 2.6691647} + perAnchorNormalsAngle: + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + globalNormalsAngle: 69 + flipNormals: 0 + vertexPathUpToDate: 1 + vertexPathMaxAngleError: 0.3 + vertexPathMinVertexSpacing: 0 + showTransformTool: 1 + showPathBounds: 0 + showPerSegmentBounds: 0 + displayAnchorPoints: 1 + displayControlPoints: 1 + bezierHandleScale: 1 + globalDisplaySettingsFoldout: 0 + keepConstantHandleSize: 0 + showNormalsInVertexMode: 0 + showBezierPathInVertexMode: 0 + showDisplayOptions: 0 + showPathOptions: 1 + showVertexPathDisplayOptions: 0 + showVertexPathOptions: 1 + showNormals: 0 + showNormalsHelpInfo: 0 + tabIndex: 0 + initialized: 1 +--- !u!4 &184727959 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 184727957} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1308570505 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1308570508} + - component: {fileID: 1308570507} + - component: {fileID: 1308570506} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1308570506 +AudioListener: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1308570505} + m_Enabled: 1 +--- !u!20 &1308570507 +Camera: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1308570505} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1308570508 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1308570505} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1312361968 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1312361971} + - component: {fileID: 1312361970} + - component: {fileID: 1312361969} + m_Layer: 0 + m_Name: New Position Result + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!23 &1312361969 +MeshRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1312361968} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: 9ca80467a033e954a8b9ad8601d6f4c7, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1312361970 +MeshFilter: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1312361968} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1312361971 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1312361968} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 1.1773397, y: -2.0806398, z: -0.9905968} + m_LocalScale: {x: 0.15, y: 2, z: 0.15} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1336900834 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1336900837} + - component: {fileID: 1336900836} + - component: {fileID: 1336900835} + m_Layer: 0 + m_Name: Old Position Result + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!23 &1336900835 +MeshRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1336900834} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: 01763a1c279a5cd4d8f897f6b6cdbae5, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1336900836 +MeshFilter: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1336900834} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1336900837 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1336900834} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 1.1773397, y: -2.0806398, z: -0.9905968} + m_LocalScale: {x: 0.2, y: 1, z: 0.2} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1414926773 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1414926775} + - component: {fileID: 1414926774} + m_Layer: 0 + m_Name: GetClosestPointOnPathTest + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1414926774 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1414926773} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2353903649ab3c34eb2a2bffc094de57, type: 3} + m_Name: + m_EditorClassIdentifier: + pathCreator: {fileID: 184727958} + inputPositionObject: {fileID: 1625705262} + oldMethodObject: {fileID: 1336900837} + newMethodObject: {fileID: 1312361971} +--- !u!4 &1414926775 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1414926773} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 1.1773397, y: -2.0806398, z: -0.9905968} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1625705259 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1625705262} + - component: {fileID: 1625705261} + - component: {fileID: 1625705260} + m_Layer: 0 + m_Name: Input Position + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!23 &1625705260 +MeshRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1625705259} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: 5c270a73328a99e439fec44a0cf3a17d, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1625705261 +MeshFilter: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1625705259} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1625705262 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1625705259} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 2.81, y: -2.0806398, z: -0.78} + m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1844901808 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1844901810} + - component: {fileID: 1844901809} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &1844901809 +Light: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1844901808} + m_Enabled: 1 + serializedVersion: 8 + m_Type: 1 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_Lightmapping: 4 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1844901810 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1844901808} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity.meta b/Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity.meta new file mode 100644 index 0000000..f45f774 --- /dev/null +++ b/Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: fbb1f45e5065be44badcc1594a52435a +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs b/Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs new file mode 100644 index 0000000..91e7190 --- /dev/null +++ b/Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs @@ -0,0 +1,121 @@ +using System; +using UnityEngine; +using PathCreation; +using PathCreation.Utility; + +namespace PathCreation.Examples +{ + public class GetClosestPointOnPathTest : MonoBehaviour + { + public PathCreator pathCreator; + + public Transform inputPositionObject; + public Transform oldMethodObject; + public Transform newMethodObject; + + void Update() + { + // Get a point around the path + Vector3 worldPoint = pathCreator.path.GetPointAtDistance(Time.time * 2) + new Vector3(Mathf.Sin(Time.time)*2, Mathf.Sin(1+Time.time*0.5f)*2.5f, Mathf.Sin(2+Time.time*0.3f)*3); + + UnityEngine.Profiling.Profiler.BeginSample("Before Optimization"); + Vector3 oldClosestPointOnPath = OldGetClosestPointOnPath(pathCreator.path, worldPoint); + float oldClosestTimeOnPath = OldGetClosestTimeOnPath(pathCreator.path, worldPoint); + float oldClosestDistanceAlongPath = OldGetClosestDistanceAlongPath(pathCreator.path, worldPoint); + UnityEngine.Profiling.Profiler.EndSample(); + + UnityEngine.Profiling.Profiler.BeginSample("After Optimization"); + Vector3 newClosestPointOnPath = pathCreator.path.GetClosestPointOnPath(worldPoint); + float newClosestTimeOnPath = pathCreator.path.GetClosestTimeOnPath(worldPoint); + float newClosestDistanceAlongPath = pathCreator.path.GetClosestDistanceAlongPath(worldPoint); + UnityEngine.Profiling.Profiler.EndSample(); + + // This much error we allow between the new and old implementations + float errorThreshold = 0.001f; + + // Check if the GetClosestPointOnPath returned value is the same for the old and new implementation + if (Vector3.Distance(newClosestPointOnPath, oldClosestPointOnPath) > errorThreshold) + Debug.LogErrorFormat(this, "GetClosestPointOnPath() failed (distance={0})", Vector3.Distance(newClosestPointOnPath, oldClosestPointOnPath)); + + // Check if the GetClosestTimeOnPath returned value is the same for the old and new implementation + if (Mathf.Abs(newClosestTimeOnPath - oldClosestTimeOnPath) > errorThreshold) + Debug.LogErrorFormat(this, "GetClosestTimeOnPath() failed. new={0}, old={1}", newClosestTimeOnPath, oldClosestTimeOnPath); + + // Check if the GetClosestDistanceAlongPath returned value is the same for the old and new implementation + if (Mathf.Abs(newClosestDistanceAlongPath - oldClosestDistanceAlongPath) > errorThreshold) + Debug.LogErrorFormat(this, "GetClosestDistanceAlongPath() failed. new={0}, old={1}", newClosestDistanceAlongPath, oldClosestDistanceAlongPath); + + // Visualize the input position + inputPositionObject.position = worldPoint; + + // Visualize the computed path position + oldMethodObject.position = oldClosestPointOnPath; + newMethodObject.position = newClosestPointOnPath; + + // Draw lines to connect the input position and the calculated output positions + Debug.DrawLine(worldPoint, oldClosestPointOnPath, Color.red); + Debug.DrawLine(worldPoint, newClosestPointOnPath, Color.green); + } + + // This is the old VertexPath code before the optimization. I copied it here to compare its + // results to the new implementation, to make sure it still returns the same values after the optimization. + Vector3 OldGetClosestPointOnPath(VertexPath path, Vector3 worldPoint) + { + VertexPath.TimeOnPathData data = OldCalculateClosestPointOnPathData(path, worldPoint); + return Vector3.Lerp(path.GetPoint(data.previousIndex), path.GetPoint(data.nextIndex), data.percentBetweenIndices); + } + + float OldGetClosestTimeOnPath(VertexPath path, Vector3 worldPoint) + { + VertexPath.TimeOnPathData data = OldCalculateClosestPointOnPathData(path, worldPoint); + return Mathf.Lerp(path.times[data.previousIndex], path.times[data.nextIndex], data.percentBetweenIndices); + } + + float OldGetClosestDistanceAlongPath(VertexPath path, Vector3 worldPoint) + { + VertexPath.TimeOnPathData data = OldCalculateClosestPointOnPathData(path, worldPoint); + return Mathf.Lerp(path.cumulativeLengthAtEachVertex[data.previousIndex], path.cumulativeLengthAtEachVertex[data.nextIndex], data.percentBetweenIndices); + } + + VertexPath.TimeOnPathData OldCalculateClosestPointOnPathData(VertexPath path, Vector3 worldPoint) + { + Vector3[] localPoints = path.localNormals; + bool isClosedLoop = path.isClosedLoop; + + float minSqrDst = float.MaxValue; + Vector3 closestPoint = Vector3.zero; + int closestSegmentIndexA = 0; + int closestSegmentIndexB = 0; + + for (int i = 0; i < localPoints.Length; i++) + { + int nextI = i + 1; + if (nextI >= localPoints.Length) + { + if (isClosedLoop) + { + nextI %= localPoints.Length; + } + else + { + break; + } + } + + Vector3 closestPointOnSegment = MathUtility.ClosestPointOnLineSegment(worldPoint, path.GetPoint(i), path.GetPoint(nextI)); + float sqrDst = (worldPoint - closestPointOnSegment).sqrMagnitude; + if (sqrDst < minSqrDst) + { + minSqrDst = sqrDst; + closestPoint = closestPointOnSegment; + closestSegmentIndexA = i; + closestSegmentIndexB = nextI; + } + + } + float closestSegmentLength = (path.GetPoint(closestSegmentIndexA) - path.GetPoint(closestSegmentIndexB)).magnitude; + float t = (closestPoint - path.GetPoint(closestSegmentIndexA)).magnitude / closestSegmentLength; + return new VertexPath.TimeOnPathData(closestSegmentIndexA, closestSegmentIndexB, t); + } + } +} diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs.meta b/Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs.meta new file mode 100644 index 0000000..592a266 --- /dev/null +++ b/Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2353903649ab3c34eb2a2bffc094de57 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 0f2ad19952a0d76e070c897b4d30809a7ee77a85 Mon Sep 17 00:00:00 2001 From: Sebastian Lague Date: Mon, 17 Feb 2020 18:54:28 +0200 Subject: [PATCH 04/12] Removed test scripts --- .../Scenes/GetClosestPointOnPath Test.unity | 644 ------------------ .../GetClosestPointOnPath Test.unity.meta | 7 - .../Scripts/GetClosestPointOnPathTest.cs | 121 ---- .../Scripts/GetClosestPointOnPathTest.cs.meta | 11 - 4 files changed, 783 deletions(-) delete mode 100644 Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity delete mode 100644 Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity.meta delete mode 100644 Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs delete mode 100644 Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity b/Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity deleted file mode 100644 index e3c27be..0000000 --- a/Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity +++ /dev/null @@ -1,644 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!29 &1 -OcclusionCullingSettings: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_OcclusionBakeSettings: - smallestOccluder: 5 - smallestHole: 0.25 - backfaceThreshold: 100 - m_SceneGUID: 00000000000000000000000000000000 - m_OcclusionCullingData: {fileID: 0} ---- !u!104 &2 -RenderSettings: - m_ObjectHideFlags: 0 - serializedVersion: 9 - m_Fog: 0 - m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} - m_FogMode: 3 - m_FogDensity: 0.01 - m_LinearFogStart: 0 - m_LinearFogEnd: 300 - m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} - m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} - m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} - m_AmbientIntensity: 1 - m_AmbientMode: 0 - m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} - m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} - m_HaloStrength: 0.5 - m_FlareStrength: 1 - m_FlareFadeSpeed: 3 - m_HaloTexture: {fileID: 0} - m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} - m_DefaultReflectionMode: 0 - m_DefaultReflectionResolution: 128 - m_ReflectionBounces: 1 - m_ReflectionIntensity: 1 - m_CustomReflection: {fileID: 0} - m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0.44657844, g: 0.49641222, b: 0.57481694, a: 1} - m_UseRadianceAmbientProbe: 0 ---- !u!157 &3 -LightmapSettings: - m_ObjectHideFlags: 0 - serializedVersion: 11 - m_GIWorkflowMode: 0 - m_GISettings: - serializedVersion: 2 - m_BounceScale: 1 - m_IndirectOutputScale: 1 - m_AlbedoBoost: 1 - m_TemporalCoherenceThreshold: 1 - m_EnvironmentLightingMode: 0 - m_EnableBakedLightmaps: 1 - m_EnableRealtimeLightmaps: 1 - m_LightmapEditorSettings: - serializedVersion: 10 - m_Resolution: 2 - m_BakeResolution: 40 - m_AtlasSize: 1024 - m_AO: 0 - m_AOMaxDistance: 1 - m_CompAOExponent: 1 - m_CompAOExponentDirect: 0 - m_Padding: 2 - m_LightmapParameters: {fileID: 0} - m_LightmapsBakeMode: 1 - m_TextureCompression: 1 - m_FinalGather: 0 - m_FinalGatherFiltering: 1 - m_FinalGatherRayCount: 256 - m_ReflectionCompression: 2 - m_MixedBakeMode: 2 - m_BakeBackend: 1 - m_PVRSampling: 1 - m_PVRDirectSampleCount: 32 - m_PVRSampleCount: 500 - m_PVRBounces: 2 - m_PVRFilterTypeDirect: 0 - m_PVRFilterTypeIndirect: 0 - m_PVRFilterTypeAO: 0 - m_PVRFilteringMode: 2 - m_PVRCulling: 1 - m_PVRFilteringGaussRadiusDirect: 1 - m_PVRFilteringGaussRadiusIndirect: 5 - m_PVRFilteringGaussRadiusAO: 2 - m_PVRFilteringAtrousPositionSigmaDirect: 0.5 - m_PVRFilteringAtrousPositionSigmaIndirect: 2 - m_PVRFilteringAtrousPositionSigmaAO: 1 - m_ShowResolutionOverlay: 1 - m_LightingDataAsset: {fileID: 0} - m_UseShadowmask: 1 ---- !u!196 &4 -NavMeshSettings: - serializedVersion: 2 - m_ObjectHideFlags: 0 - m_BuildSettings: - serializedVersion: 2 - agentTypeID: 0 - agentRadius: 0.5 - agentHeight: 2 - agentSlope: 45 - agentClimb: 0.4 - ledgeDropHeight: 0 - maxJumpAcrossDistance: 0 - minRegionArea: 2 - manualCellSize: 0 - cellSize: 0.16666667 - manualTileSize: 0 - tileSize: 256 - accuratePlacement: 0 - debug: - m_Flags: 0 - m_NavMeshData: {fileID: 0} ---- !u!1 &184727957 -GameObject: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - serializedVersion: 5 - m_Component: - - component: {fileID: 184727959} - - component: {fileID: 184727958} - m_Layer: 0 - m_Name: Path - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &184727958 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 184727957} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 8e5ac92bc18f545cc84cd886ece82b4d, type: 3} - m_Name: - m_EditorClassIdentifier: - editorData: - _bezierPath: - points: - - {x: -4.350084, y: -0.6192361, z: 0.36958665} - - {x: -4.3476486, y: -0.597047, z: 0.52262044} - - {x: -2.7572303, y: -0.70872635, z: 1.6174023} - - {x: -2.3782218, y: -0.38763738, z: 1.8379713} - - {x: -1.9992132, y: -0.06654839, z: 2.0585403} - - {x: -2.4454098, y: 0.8219146, z: 1.9317083} - - {x: -2.0614424, y: 1.2668917, z: 1.9732294} - - {x: -1.677475, y: 1.7118688, z: 2.0147505} - - {x: -0.46440378, y: 1.7154288, z: 2.2258382} - - {x: 0.14839771, y: 1.7105651, z: 2.2219813} - - {x: 0.76119924, y: 1.7057014, z: 2.2181244} - - {x: 4.2510085, y: -0.53037375, z: 1.282062} - - {x: 4.3446193, y: -0.6192361, z: 0.36958665} - - {x: 4.43823, y: -0.7080985, z: -0.54288876} - - {x: 6.1194963, y: 0.44442305, z: -2.4886756} - - {x: 3.7941601, y: -0.07067474, z: -2.6234398} - - {x: 1.4688243, y: -0.5857725, z: -2.758204} - - {x: 5.180417, y: 0.88676715, z: -3.9433024} - - {x: 4.939216, y: 1.0569758, z: -4.3701224} - - {x: 4.698015, y: 1.2271845, z: -4.7969427} - - {x: 2.5704806, y: 0.8330983, z: -4.9625225} - - {x: 2.446082, y: 0.83640313, z: -4.9686565} - - {x: 2.3216836, y: 0.839708, z: -4.9747906} - - {x: 0.31069377, y: 0.7397387, z: -2.727452} - - {x: -0.3484243, y: 0.69917965, z: -2.494286} - - {x: -1.0075424, y: 0.6586206, z: -2.26112} - - {x: -1.4713835, y: -1.2655108, z: -2.326689} - - {x: -2.1617332, y: -1.2623534, z: -2.510853} - - {x: -2.8520832, y: -1.259196, z: -2.695017} - - {x: -3.6936672, y: 0.7034443, z: -3.0868487} - - {x: -3.5478704, y: 0.910876, z: -3.260935} - - {x: -3.4020736, y: 1.1183076, z: -3.4350214} - - {x: -4.3403153, y: 1.11183, z: -3.5472014} - - {x: -4.5300093, y: 1.5598526, z: -4.0236} - - {x: -4.719703, y: 2.0078752, z: -4.4999995} - - {x: -4.878911, y: 0.7921399, z: -2.921095} - - {x: -4.9720774, y: 0.74950266, z: -2.794322} - - {x: -5.0652437, y: 0.70686543, z: -2.6675491} - - {x: -6.667046, y: 0.38763303, z: -1.8212851} - - {x: -6.4901414, y: 0.058772564, z: -1.1090026} - - {x: -6.3132367, y: -0.2700879, z: -0.39672005} - - {x: -4.352519, y: -0.64142525, z: 0.21655288} - isClosed: 1 - space: 0 - controlMode: 1 - autoControlLength: 0.3 - boundsUpToDate: 0 - bounds: - m_Center: {x: 0.005727291, y: 0.5250117, z: -0.39030027} - m_Extent: {x: 4.3562183, y: 1.1872373, z: 2.6691647} - perAnchorNormalsAngle: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - globalNormalsAngle: 69 - flipNormals: 0 - vertexPathUpToDate: 1 - vertexPathMaxAngleError: 0.3 - vertexPathMinVertexSpacing: 0 - showTransformTool: 1 - showPathBounds: 0 - showPerSegmentBounds: 0 - displayAnchorPoints: 1 - displayControlPoints: 1 - bezierHandleScale: 1 - globalDisplaySettingsFoldout: 0 - keepConstantHandleSize: 0 - showNormalsInVertexMode: 0 - showBezierPathInVertexMode: 0 - showDisplayOptions: 0 - showPathOptions: 1 - showVertexPathDisplayOptions: 0 - showVertexPathOptions: 1 - showNormals: 0 - showNormalsHelpInfo: 0 - tabIndex: 0 - initialized: 1 ---- !u!4 &184727959 -Transform: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 184727957} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 2 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1308570505 -GameObject: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - serializedVersion: 5 - m_Component: - - component: {fileID: 1308570508} - - component: {fileID: 1308570507} - - component: {fileID: 1308570506} - m_Layer: 0 - m_Name: Main Camera - m_TagString: MainCamera - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!81 &1308570506 -AudioListener: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1308570505} - m_Enabled: 1 ---- !u!20 &1308570507 -Camera: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1308570505} - m_Enabled: 1 - serializedVersion: 2 - m_ClearFlags: 1 - m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} - m_NormalizedViewPortRect: - serializedVersion: 2 - x: 0 - y: 0 - width: 1 - height: 1 - near clip plane: 0.3 - far clip plane: 1000 - field of view: 60 - orthographic: 0 - orthographic size: 5 - m_Depth: -1 - m_CullingMask: - serializedVersion: 2 - m_Bits: 4294967295 - m_RenderingPath: -1 - m_TargetTexture: {fileID: 0} - m_TargetDisplay: 0 - m_TargetEye: 3 - m_HDR: 1 - m_AllowMSAA: 1 - m_AllowDynamicResolution: 0 - m_ForceIntoRT: 0 - m_OcclusionCulling: 1 - m_StereoConvergence: 10 - m_StereoSeparation: 0.022 ---- !u!4 &1308570508 -Transform: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1308570505} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 1, z: -10} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1312361968 -GameObject: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - serializedVersion: 5 - m_Component: - - component: {fileID: 1312361971} - - component: {fileID: 1312361970} - - component: {fileID: 1312361969} - m_Layer: 0 - m_Name: New Position Result - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!23 &1312361969 -MeshRenderer: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1312361968} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RenderingLayerMask: 4294967295 - m_Materials: - - {fileID: 2100000, guid: 9ca80467a033e954a8b9ad8601d6f4c7, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 0 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 ---- !u!33 &1312361970 -MeshFilter: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1312361968} - m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} ---- !u!4 &1312361971 -Transform: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1312361968} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 1.1773397, y: -2.0806398, z: -0.9905968} - m_LocalScale: {x: 0.15, y: 2, z: 0.15} - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 5 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1336900834 -GameObject: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - serializedVersion: 5 - m_Component: - - component: {fileID: 1336900837} - - component: {fileID: 1336900836} - - component: {fileID: 1336900835} - m_Layer: 0 - m_Name: Old Position Result - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!23 &1336900835 -MeshRenderer: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1336900834} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RenderingLayerMask: 4294967295 - m_Materials: - - {fileID: 2100000, guid: 01763a1c279a5cd4d8f897f6b6cdbae5, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 0 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 ---- !u!33 &1336900836 -MeshFilter: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1336900834} - m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} ---- !u!4 &1336900837 -Transform: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1336900834} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 1.1773397, y: -2.0806398, z: -0.9905968} - m_LocalScale: {x: 0.2, y: 1, z: 0.2} - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 4 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1414926773 -GameObject: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - serializedVersion: 5 - m_Component: - - component: {fileID: 1414926775} - - component: {fileID: 1414926774} - m_Layer: 0 - m_Name: GetClosestPointOnPathTest - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &1414926774 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1414926773} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 2353903649ab3c34eb2a2bffc094de57, type: 3} - m_Name: - m_EditorClassIdentifier: - pathCreator: {fileID: 184727958} - inputPositionObject: {fileID: 1625705262} - oldMethodObject: {fileID: 1336900837} - newMethodObject: {fileID: 1312361971} ---- !u!4 &1414926775 -Transform: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1414926773} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 1.1773397, y: -2.0806398, z: -0.9905968} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 3 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1625705259 -GameObject: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - serializedVersion: 5 - m_Component: - - component: {fileID: 1625705262} - - component: {fileID: 1625705261} - - component: {fileID: 1625705260} - m_Layer: 0 - m_Name: Input Position - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!23 &1625705260 -MeshRenderer: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1625705259} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RenderingLayerMask: 4294967295 - m_Materials: - - {fileID: 2100000, guid: 5c270a73328a99e439fec44a0cf3a17d, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 0 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 ---- !u!33 &1625705261 -MeshFilter: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1625705259} - m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} ---- !u!4 &1625705262 -Transform: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1625705259} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 2.81, y: -2.0806398, z: -0.78} - m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 6 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1844901808 -GameObject: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - serializedVersion: 5 - m_Component: - - component: {fileID: 1844901810} - - component: {fileID: 1844901809} - m_Layer: 0 - m_Name: Directional Light - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!108 &1844901809 -Light: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1844901808} - m_Enabled: 1 - serializedVersion: 8 - m_Type: 1 - m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} - m_Intensity: 1 - m_Range: 10 - m_SpotAngle: 30 - m_CookieSize: 10 - m_Shadows: - m_Type: 2 - m_Resolution: -1 - m_CustomResolution: -1 - m_Strength: 1 - m_Bias: 0.05 - m_NormalBias: 0.4 - m_NearPlane: 0.2 - m_Cookie: {fileID: 0} - m_DrawHalo: 0 - m_Flare: {fileID: 0} - m_RenderMode: 0 - m_CullingMask: - serializedVersion: 2 - m_Bits: 4294967295 - m_Lightmapping: 4 - m_AreaSize: {x: 1, y: 1} - m_BounceIntensity: 1 - m_ColorTemperature: 6570 - m_UseColorTemperature: 0 - m_ShadowRadius: 0 - m_ShadowAngle: 0 ---- !u!4 &1844901810 -Transform: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 1844901808} - m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} - m_LocalPosition: {x: 0, y: 3, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity.meta b/Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity.meta deleted file mode 100644 index f45f774..0000000 --- a/Path Creator Project/Assets/PathCreator/Examples/Scenes/GetClosestPointOnPath Test.unity.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: fbb1f45e5065be44badcc1594a52435a -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs b/Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs deleted file mode 100644 index 91e7190..0000000 --- a/Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using UnityEngine; -using PathCreation; -using PathCreation.Utility; - -namespace PathCreation.Examples -{ - public class GetClosestPointOnPathTest : MonoBehaviour - { - public PathCreator pathCreator; - - public Transform inputPositionObject; - public Transform oldMethodObject; - public Transform newMethodObject; - - void Update() - { - // Get a point around the path - Vector3 worldPoint = pathCreator.path.GetPointAtDistance(Time.time * 2) + new Vector3(Mathf.Sin(Time.time)*2, Mathf.Sin(1+Time.time*0.5f)*2.5f, Mathf.Sin(2+Time.time*0.3f)*3); - - UnityEngine.Profiling.Profiler.BeginSample("Before Optimization"); - Vector3 oldClosestPointOnPath = OldGetClosestPointOnPath(pathCreator.path, worldPoint); - float oldClosestTimeOnPath = OldGetClosestTimeOnPath(pathCreator.path, worldPoint); - float oldClosestDistanceAlongPath = OldGetClosestDistanceAlongPath(pathCreator.path, worldPoint); - UnityEngine.Profiling.Profiler.EndSample(); - - UnityEngine.Profiling.Profiler.BeginSample("After Optimization"); - Vector3 newClosestPointOnPath = pathCreator.path.GetClosestPointOnPath(worldPoint); - float newClosestTimeOnPath = pathCreator.path.GetClosestTimeOnPath(worldPoint); - float newClosestDistanceAlongPath = pathCreator.path.GetClosestDistanceAlongPath(worldPoint); - UnityEngine.Profiling.Profiler.EndSample(); - - // This much error we allow between the new and old implementations - float errorThreshold = 0.001f; - - // Check if the GetClosestPointOnPath returned value is the same for the old and new implementation - if (Vector3.Distance(newClosestPointOnPath, oldClosestPointOnPath) > errorThreshold) - Debug.LogErrorFormat(this, "GetClosestPointOnPath() failed (distance={0})", Vector3.Distance(newClosestPointOnPath, oldClosestPointOnPath)); - - // Check if the GetClosestTimeOnPath returned value is the same for the old and new implementation - if (Mathf.Abs(newClosestTimeOnPath - oldClosestTimeOnPath) > errorThreshold) - Debug.LogErrorFormat(this, "GetClosestTimeOnPath() failed. new={0}, old={1}", newClosestTimeOnPath, oldClosestTimeOnPath); - - // Check if the GetClosestDistanceAlongPath returned value is the same for the old and new implementation - if (Mathf.Abs(newClosestDistanceAlongPath - oldClosestDistanceAlongPath) > errorThreshold) - Debug.LogErrorFormat(this, "GetClosestDistanceAlongPath() failed. new={0}, old={1}", newClosestDistanceAlongPath, oldClosestDistanceAlongPath); - - // Visualize the input position - inputPositionObject.position = worldPoint; - - // Visualize the computed path position - oldMethodObject.position = oldClosestPointOnPath; - newMethodObject.position = newClosestPointOnPath; - - // Draw lines to connect the input position and the calculated output positions - Debug.DrawLine(worldPoint, oldClosestPointOnPath, Color.red); - Debug.DrawLine(worldPoint, newClosestPointOnPath, Color.green); - } - - // This is the old VertexPath code before the optimization. I copied it here to compare its - // results to the new implementation, to make sure it still returns the same values after the optimization. - Vector3 OldGetClosestPointOnPath(VertexPath path, Vector3 worldPoint) - { - VertexPath.TimeOnPathData data = OldCalculateClosestPointOnPathData(path, worldPoint); - return Vector3.Lerp(path.GetPoint(data.previousIndex), path.GetPoint(data.nextIndex), data.percentBetweenIndices); - } - - float OldGetClosestTimeOnPath(VertexPath path, Vector3 worldPoint) - { - VertexPath.TimeOnPathData data = OldCalculateClosestPointOnPathData(path, worldPoint); - return Mathf.Lerp(path.times[data.previousIndex], path.times[data.nextIndex], data.percentBetweenIndices); - } - - float OldGetClosestDistanceAlongPath(VertexPath path, Vector3 worldPoint) - { - VertexPath.TimeOnPathData data = OldCalculateClosestPointOnPathData(path, worldPoint); - return Mathf.Lerp(path.cumulativeLengthAtEachVertex[data.previousIndex], path.cumulativeLengthAtEachVertex[data.nextIndex], data.percentBetweenIndices); - } - - VertexPath.TimeOnPathData OldCalculateClosestPointOnPathData(VertexPath path, Vector3 worldPoint) - { - Vector3[] localPoints = path.localNormals; - bool isClosedLoop = path.isClosedLoop; - - float minSqrDst = float.MaxValue; - Vector3 closestPoint = Vector3.zero; - int closestSegmentIndexA = 0; - int closestSegmentIndexB = 0; - - for (int i = 0; i < localPoints.Length; i++) - { - int nextI = i + 1; - if (nextI >= localPoints.Length) - { - if (isClosedLoop) - { - nextI %= localPoints.Length; - } - else - { - break; - } - } - - Vector3 closestPointOnSegment = MathUtility.ClosestPointOnLineSegment(worldPoint, path.GetPoint(i), path.GetPoint(nextI)); - float sqrDst = (worldPoint - closestPointOnSegment).sqrMagnitude; - if (sqrDst < minSqrDst) - { - minSqrDst = sqrDst; - closestPoint = closestPointOnSegment; - closestSegmentIndexA = i; - closestSegmentIndexB = nextI; - } - - } - float closestSegmentLength = (path.GetPoint(closestSegmentIndexA) - path.GetPoint(closestSegmentIndexB)).magnitude; - float t = (closestPoint - path.GetPoint(closestSegmentIndexA)).magnitude / closestSegmentLength; - return new VertexPath.TimeOnPathData(closestSegmentIndexA, closestSegmentIndexB, t); - } - } -} diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs.meta b/Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs.meta deleted file mode 100644 index 592a266..0000000 --- a/Path Creator Project/Assets/PathCreator/Examples/Scripts/GetClosestPointOnPathTest.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2353903649ab3c34eb2a2bffc094de57 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From f9b37fa87ba352727df5842b6d7f605d0c24700e Mon Sep 17 00:00:00 2001 From: Sebastian Lague Date: Mon, 17 Feb 2020 22:07:21 +0200 Subject: [PATCH 05/12] Rewrote MathUtil constrained transformation functions for better performance --- .../Core/Runtime/Utility/MathUtility.cs | 118 +++++++++--------- 1 file changed, 62 insertions(+), 56 deletions(-) diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs b/Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs index af862ba..a476689 100755 --- a/Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs +++ b/Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs @@ -4,64 +4,59 @@ namespace PathCreation.Utility { public static class MathUtility { - static PosRotScale LockTransformToSpace (Transform t, PathSpace space) { - var original = new PosRotScale (t); - if (space == PathSpace.xy) { - t.eulerAngles = new Vector3 (0, 0, t.eulerAngles.z); - t.position = new Vector3 (t.position.x, t.position.y, 0); - } else if (space == PathSpace.xz) { - t.eulerAngles = new Vector3 (0, t.eulerAngles.y, 0); - t.position = new Vector3 (t.position.x, 0, t.position.z); - } - - //float maxScale = Mathf.Max (t.localScale.x * t.parent.localScale.x, t.localScale.y * t.parent.localScale.y, t.localScale.z * t.parent.localScale.z); - float maxScale = Mathf.Max (t.lossyScale.x, t.lossyScale.y, t.lossyScale.z); - - t.localScale = Vector3.one * maxScale; - - return original; - } - + // Transform point from local to world space public static Vector3 TransformPoint (Vector3 p, Transform t, PathSpace space) { - var original = LockTransformToSpace (t, space); - Vector3 transformedPoint = t.TransformPoint (p); - original.SetTransform (t); - return transformedPoint; + // path only works correctly for uniform scales, so average out xyz global scale + float scale = Vector3.Dot (t.lossyScale, Vector3.one) / 3; + Vector3 constrainedPos = t.position; + Quaternion constrainedRot = t.rotation; + ConstrainPosRot (ref constrainedPos, ref constrainedRot, space); + return constrainedRot * p * scale + constrainedPos; } + // Transform point from world to local space public static Vector3 InverseTransformPoint (Vector3 p, Transform t, PathSpace space) { - var original = LockTransformToSpace (t, space); - Vector3 transformedPoint = t.InverseTransformPoint (p); - original.SetTransform (t); - return transformedPoint; + Vector3 constrainedPos = t.position; + Quaternion constrainedRot = t.rotation; + ConstrainPosRot (ref constrainedPos, ref constrainedRot, space); + + // path only works correctly for uniform scales, so average out xyz global scale + float scale = Vector3.Dot (t.lossyScale, Vector3.one) / 3; + var offset = p - constrainedPos; + + return Quaternion.Inverse (constrainedRot) * offset / scale; } + // Transform vector from local to world space (affected by rotation and scale, but not position) public static Vector3 TransformVector (Vector3 p, Transform t, PathSpace space) { - var original = LockTransformToSpace (t, space); - Vector3 transformedPoint = t.TransformVector (p); - original.SetTransform (t); - return transformedPoint; + // path only works correctly for uniform scales, so average out xyz global scale + float scale = Vector3.Dot (t.lossyScale, Vector3.one) / 3; + Quaternion constrainedRot = t.rotation; + ConstrainRot (ref constrainedRot, space); + return constrainedRot * p * scale; } + // Transform vector from world to local space (affected by rotation and scale, but not position) public static Vector3 InverseTransformVector (Vector3 p, Transform t, PathSpace space) { - var original = LockTransformToSpace (t, space); - Vector3 transformedPoint = t.InverseTransformVector (p); - original.SetTransform (t); - return transformedPoint; + Quaternion constrainedRot = t.rotation; + ConstrainRot (ref constrainedRot, space); + // path only works correctly for uniform scales, so average out xyz global scale + float scale = Vector3.Dot (t.lossyScale, Vector3.one) / 3; + return Quaternion.Inverse (constrainedRot) * p / scale; } + // Transform vector from local to world space (affected by rotation, but not position or scale) public static Vector3 TransformDirection (Vector3 p, Transform t, PathSpace space) { - var original = LockTransformToSpace (t, space); - Vector3 transformedPoint = t.TransformDirection (p); - original.SetTransform (t); - return transformedPoint; + Quaternion constrainedRot = t.rotation; + ConstrainRot (ref constrainedRot, space); + return constrainedRot * p; } + // Transform vector from world to local space (affected by rotation, but not position or scale) public static Vector3 InverseTransformDirection (Vector3 p, Transform t, PathSpace space) { - var original = LockTransformToSpace (t, space); - Vector3 transformedPoint = t.InverseTransformDirection (p); - original.SetTransform (t); - return transformedPoint; + Quaternion constrainedRot = t.rotation; + ConstrainRot (ref constrainedRot, space); + return Quaternion.Inverse (constrainedRot) * p; } public static bool LineSegmentsIntersect (Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2) { @@ -140,22 +135,33 @@ public static bool PointsAreClockwise (Vector2[] points) { return signedArea >= 0; } - class PosRotScale { - public readonly Vector3 position; - public readonly Quaternion rotation; - public readonly Vector3 scale; - - public PosRotScale (Transform t) { - this.position = t.position; - this.rotation = t.rotation; - this.scale = t.localScale; + static void ConstrainPosRot (ref Vector3 pos, ref Quaternion rot, PathSpace space) { + if (space == PathSpace.xy) { + var eulerAngles = rot.eulerAngles; + if (eulerAngles.x != 0 || eulerAngles.y != 0) { + rot = Quaternion.AngleAxis (eulerAngles.z, Vector3.forward); + } + pos = new Vector3 (pos.x, pos.y, 0); + } else if (space == PathSpace.xz) { + var eulerAngles = rot.eulerAngles; + if (eulerAngles.x != 0 || eulerAngles.z != 0) { + rot = Quaternion.AngleAxis (eulerAngles.y, Vector3.up); + } + pos = new Vector3 (pos.x, 0, pos.z); } + } - public void SetTransform (Transform t) { - t.position = position; - t.rotation = rotation; - t.localScale = scale; - + static void ConstrainRot (ref Quaternion rot, PathSpace space) { + if (space == PathSpace.xy) { + var eulerAngles = rot.eulerAngles; + if (eulerAngles.x != 0 || eulerAngles.y != 0) { + rot = Quaternion.AngleAxis (eulerAngles.z, Vector3.forward); + } + } else if (space == PathSpace.xz) { + var eulerAngles = rot.eulerAngles; + if (eulerAngles.x != 0 || eulerAngles.z != 0) { + rot = Quaternion.AngleAxis (eulerAngles.y, Vector3.up); + } } } } From dbbaeeeb53da52abcf3246dd72507c277928c06d Mon Sep 17 00:00:00 2001 From: Sebastian Lague Date: Fri, 2 Oct 2020 00:20:52 +0200 Subject: [PATCH 06/12] Upgrade to 2019.4 LTS --- Path Creator Project/Logs/Packages-Update.log | 52 +++ Path Creator Project/Packages/manifest.json | 49 ++- .../Packages/packages-lock.json | 371 ++++++++++++++++++ .../ProjectSettings/EditorSettings.asset | 14 +- .../ProjectSettings/ProjectVersion.txt | 3 +- 5 files changed, 484 insertions(+), 5 deletions(-) create mode 100644 Path Creator Project/Packages/packages-lock.json diff --git a/Path Creator Project/Logs/Packages-Update.log b/Path Creator Project/Logs/Packages-Update.log index 6c678fb..0e6b9db 100644 --- a/Path Creator Project/Logs/Packages-Update.log +++ b/Path Creator Project/Logs/Packages-Update.log @@ -88,3 +88,55 @@ The following packages were added: com.unity.modules.xr@1.0.0 com.unity.multiplayer-hlapi@1.0.2 com.unity.xr.legacyinputhelpers@2.0.2 + +=== Fri Oct 2 00:17:46 2020 + +Packages were changed. +Update Mode: updateDependencies + +The following packages were added: + com.unity.2d.sprite@1.0.0 + com.unity.2d.tilemap@1.0.0 + com.unity.ads@3.4.7 + com.unity.analytics@3.3.5 + com.unity.collab-proxy@1.2.16 + com.unity.ide.rider@1.1.4 + com.unity.ide.vscode@1.2.1 + com.unity.modules.ai@1.0.0 + com.unity.modules.androidjni@1.0.0 + com.unity.modules.animation@1.0.0 + com.unity.modules.assetbundle@1.0.0 + com.unity.modules.audio@1.0.0 + com.unity.modules.cloth@1.0.0 + com.unity.modules.director@1.0.0 + com.unity.modules.imageconversion@1.0.0 + com.unity.modules.imgui@1.0.0 + com.unity.modules.jsonserialize@1.0.0 + com.unity.modules.particlesystem@1.0.0 + com.unity.modules.physics@1.0.0 + com.unity.modules.physics2d@1.0.0 + com.unity.modules.screencapture@1.0.0 + com.unity.modules.terrain@1.0.0 + com.unity.modules.terrainphysics@1.0.0 + com.unity.modules.tilemap@1.0.0 + com.unity.modules.ui@1.0.0 + com.unity.modules.uielements@1.0.0 + com.unity.modules.umbra@1.0.0 + com.unity.modules.unityanalytics@1.0.0 + com.unity.modules.unitywebrequest@1.0.0 + com.unity.modules.unitywebrequestassetbundle@1.0.0 + com.unity.modules.unitywebrequestaudio@1.0.0 + com.unity.modules.unitywebrequesttexture@1.0.0 + com.unity.modules.unitywebrequestwww@1.0.0 + com.unity.modules.vehicles@1.0.0 + com.unity.modules.video@1.0.0 + com.unity.modules.vr@1.0.0 + com.unity.modules.wind@1.0.0 + com.unity.modules.xr@1.0.0 + com.unity.multiplayer-hlapi@1.0.6 + com.unity.purchasing@2.0.6 + com.unity.test-framework@1.1.16 + com.unity.textmeshpro@2.0.1 + com.unity.timeline@1.2.6 + com.unity.ugui@1.0.0 + com.unity.xr.legacyinputhelpers@2.1.4 diff --git a/Path Creator Project/Packages/manifest.json b/Path Creator Project/Packages/manifest.json index 526aca6..ab244fb 100644 --- a/Path Creator Project/Packages/manifest.json +++ b/Path Creator Project/Packages/manifest.json @@ -1,4 +1,49 @@ { - "dependencies": { - } + "dependencies": { + "com.unity.2d.sprite": "1.0.0", + "com.unity.2d.tilemap": "1.0.0", + "com.unity.ads": "3.4.7", + "com.unity.analytics": "3.3.5", + "com.unity.collab-proxy": "1.2.16", + "com.unity.ide.rider": "1.1.4", + "com.unity.ide.vscode": "1.2.1", + "com.unity.multiplayer-hlapi": "1.0.6", + "com.unity.purchasing": "2.0.6", + "com.unity.test-framework": "1.1.16", + "com.unity.textmeshpro": "2.0.1", + "com.unity.timeline": "1.2.6", + "com.unity.ugui": "1.0.0", + "com.unity.xr.legacyinputhelpers": "2.1.4", + "com.unity.modules.ai": "1.0.0", + "com.unity.modules.androidjni": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.cloth": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.screencapture": "1.0.0", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.uielements": "1.0.0", + "com.unity.modules.umbra": "1.0.0", + "com.unity.modules.unityanalytics": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0", + "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vehicles": "1.0.0", + "com.unity.modules.video": "1.0.0", + "com.unity.modules.vr": "1.0.0", + "com.unity.modules.wind": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } } diff --git a/Path Creator Project/Packages/packages-lock.json b/Path Creator Project/Packages/packages-lock.json new file mode 100644 index 0000000..f71a07a --- /dev/null +++ b/Path Creator Project/Packages/packages-lock.json @@ -0,0 +1,371 @@ +{ + "dependencies": { + "com.unity.2d.sprite": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.2d.tilemap": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.ads": { + "version": "3.4.7", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.analytics": { + "version": "3.3.5", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.collab-proxy": { + "version": "1.2.16", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.ext.nunit": { + "version": "1.0.0", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.ide.rider": { + "version": "1.1.4", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.1.1" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ide.vscode": { + "version": "1.2.1", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.multiplayer-hlapi": { + "version": "1.0.6", + "depth": 0, + "source": "registry", + "dependencies": { + "nuget.mono-cecil": "0.1.6-preview" + }, + "url": "https://packages.unity.com" + }, + "com.unity.purchasing": { + "version": "2.0.6", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.test-framework": { + "version": "1.1.16", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.textmeshpro": { + "version": "2.0.1", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.timeline": { + "version": "1.2.6", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.ugui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0" + } + }, + "com.unity.xr.legacyinputhelpers": { + "version": "2.1.4", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "nuget.mono-cecil": { + "version": "0.1.6-preview", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.modules.ai": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.androidjni": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.animation": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.assetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.audio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.cloth": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.director": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.animation": "1.0.0" + } + }, + "com.unity.modules.imageconversion": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.imgui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.jsonserialize": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.particlesystem": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics2d": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.screencapture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.subsystems": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.terrain": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.terrainphysics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.terrain": "1.0.0" + } + }, + "com.unity.modules.tilemap": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics2d": "1.0.0" + } + }, + "com.unity.modules.ui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.uielements": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.umbra": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unityanalytics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.unitywebrequest": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unitywebrequestassetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestaudio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.audio": "1.0.0" + } + }, + "com.unity.modules.unitywebrequesttexture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestwww": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.vehicles": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.video": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.vr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } + }, + "com.unity.modules.wind": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.xr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.subsystems": "1.0.0" + } + } + } +} diff --git a/Path Creator Project/ProjectSettings/EditorSettings.asset b/Path Creator Project/ProjectSettings/EditorSettings.asset index a01cbc8..ab517c4 100644 --- a/Path Creator Project/ProjectSettings/EditorSettings.asset +++ b/Path Creator Project/ProjectSettings/EditorSettings.asset @@ -3,7 +3,7 @@ --- !u!159 &1 EditorSettings: m_ObjectHideFlags: 0 - serializedVersion: 7 + serializedVersion: 9 m_ExternalVersionControlSupport: Visible Meta Files m_SerializationMode: 2 m_LineEndingsForNewScripts: 1 @@ -16,10 +16,20 @@ EditorSettings: m_EtcTextureFastCompressor: 1 m_EtcTextureNormalCompressor: 2 m_EtcTextureBestCompressor: 4 - m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp;asmref m_ProjectGenerationRootNamespace: m_CollabEditorSettings: inProgressEnabled: 1 m_EnableTextureStreamingInEditMode: 1 m_EnableTextureStreamingInPlayMode: 1 m_AsyncShaderCompilation: 1 + m_EnterPlayModeOptionsEnabled: 0 + m_EnterPlayModeOptions: 3 + m_ShowLightmapResolutionOverlay: 1 + m_UseLegacyProbeSampleCount: 1 + m_AssetPipelineMode: 1 + m_CacheServerMode: 0 + m_CacheServerEndpoint: + m_CacheServerNamespacePrefix: default + m_CacheServerEnableDownload: 1 + m_CacheServerEnableUpload: 1 diff --git a/Path Creator Project/ProjectSettings/ProjectVersion.txt b/Path Creator Project/ProjectSettings/ProjectVersion.txt index 8f3d813..cb55a75 100644 --- a/Path Creator Project/ProjectSettings/ProjectVersion.txt +++ b/Path Creator Project/ProjectSettings/ProjectVersion.txt @@ -1 +1,2 @@ -m_EditorVersion: 2018.1.9f2 +m_EditorVersion: 2019.4.8f1 +m_EditorVersionWithRevision: 2019.4.8f1 (60781d942082) From 34be42aee280ae7adc9616b6989b9a377c15ece3 Mon Sep 17 00:00:00 2001 From: Sebastian Lague Date: Fri, 2 Oct 2020 00:28:35 +0200 Subject: [PATCH 07/12] Procedural cylinder example --- .../Examples/Scenes/Mesh Examples.meta | 8 + .../Scenes/Mesh Examples/Cylinder.unity | 656 ++++++++++++++++++ .../Scenes/Mesh Examples/Cylinder.unity.meta | 7 + .../Scenes/{ => Mesh Examples}/Road.unity | 0 .../{ => Mesh Examples}/Road.unity.meta | 0 .../Examples/Scripts/CylinderMeshCreator.cs | 115 +++ .../Scripts/CylinderMeshCreator.cs.meta | 11 + 7 files changed, 797 insertions(+) create mode 100644 Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples.meta create mode 100644 Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity create mode 100755 Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity.meta rename Path Creator Project/Assets/PathCreator/Examples/Scenes/{ => Mesh Examples}/Road.unity (100%) rename Path Creator Project/Assets/PathCreator/Examples/Scenes/{ => Mesh Examples}/Road.unity.meta (100%) create mode 100644 Path Creator Project/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs create mode 100644 Path Creator Project/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples.meta b/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples.meta new file mode 100644 index 0000000..192a200 --- /dev/null +++ b/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0510b8afe74db46d28644a2bafb7e6f2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity b/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity new file mode 100644 index 0000000..e51a845 --- /dev/null +++ b/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity @@ -0,0 +1,656 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657874, g: 0.49641258, b: 0.574817, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 0 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!43 &392264963 +Mesh: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + serializedVersion: 10 + m_SubMeshes: + - serializedVersion: 2 + firstByte: 0 + indexCount: 24300 + topology: 0 + baseVertex: 0 + firstVertex: 0 + vertexCount: 4060 + localAABB: + m_Center: {x: 1.9145843, y: 0.566661, z: 1.37708} + m_Extent: {x: 4.354134, y: 1.5391792, z: 4.676765} + m_Shapes: + vertices: [] + shapes: [] + channels: [] + fullWeights: [] + m_BindPose: [] + m_BoneNameHashes: + m_RootBoneNameHash: 0 + m_BonesAABB: [] + m_VariableBoneCountWeights: + m_Data: + m_MeshCompression: 0 + m_IsReadable: 1 + m_KeepVertices: 0 + m_KeepIndices: 0 + m_IndexFormat: 0 + m_IndexBuffer: 000001000a0001000b000a00010002000b0002000c000b00020003000c0003000d000c00030004000d0004000e000d00040005000e0005000f000e00050006000f00060010000f0006000700100007001100100007000800110008001200110008000900120009001300120009000000130000000a0013000a000b0014000b00150014000b000c0015000c00160015000c000d0016000d00170016000d000e0017000e00180017000e000f0018000f00190018000f001000190010001a001900100011001a0011001b001a00110012001b0012001c001b00120013001c0013001d001c0013000a001d000a0014001d00140015001e0015001f001e00150016001f00160020001f0016001700200017002100200017001800210018002200210018001900220019002300220019001a0023001a00240023001a001b0024001b00250024001b001c0025001c00260025001c001d0026001d00270026001d001400270014001e0027001e001f0028001f00290028001f002000290020002a002900200021002a0021002b002a00210022002b0022002c002b00220023002c0023002d002c00230024002d0024002e002d00240025002e0025002f002e00250026002f00260030002f0026002700300027003100300027001e0031001e002800310028002900320029003300320029002a0033002a00340033002a002b0034002b00350034002b002c0035002c00360035002c002d0036002d00370036002d002e0037002e00380037002e002f0038002f00390038002f003000390030003a003900300031003a0031003b003a00310028003b00280032003b00320033003c0033003d003c00330034003d0034003e003d00340035003e0035003f003e00350036003f00360040003f0036003700400037004100400037003800410038004200410038003900420039004300420039003a0043003a00440043003a003b0044003b00450044003b003200450032003c0045003c003d0046003d00470046003d003e0047003e00480047003e003f0048003f00490048003f004000490040004a004900400041004a0041004b004a00410042004b0042004c004b00420043004c0043004d004c00430044004d0044004e004d00440045004e0045004f004e0045003c004f003c0046004f0046004700500047005100500047004800510048005200510048004900520049005300520049004a0053004a00540053004a004b0054004b00550054004b004c0055004c00560055004c004d0056004d00570056004d004e0057004e00580057004e004f0058004f00590058004f0046005900460050005900500051005a0051005b005a00510052005b0052005c005b00520053005c0053005d005c00530054005d0054005e005d00540055005e0055005f005e00550056005f00560060005f0056005700600057006100600057005800610058006200610058005900620059006300620059005000630050005a0063005a005b0064005b00650064005b005c0065005c00660065005c005d0066005d00670066005d005e0067005e00680067005e005f0068005f00690068005f006000690060006a006900600061006a0061006b006a00610062006b0062006c006b00620063006c0063006d006c0063005a006d005a0064006d00640065006e0065006f006e00650066006f00660070006f0066006700700067007100700067006800710068007200710068006900720069007300720069006a0073006a00740073006a006b0074006b00750074006b006c0075006c00760075006c006d0076006d00770076006d006400770064006e0077006e006f0078006f00790078006f007000790070007a007900700071007a0071007b007a00710072007b0072007c007b00720073007c0073007d007c00730074007d0074007e007d00740075007e0075007f007e00750076007f00760080007f0076007700800077008100800077006e0081006e007800810078007900820079008300820079007a0083007a00840083007a007b0084007b00850084007b007c0085007c00860085007c007d0086007d00870086007d007e0087007e00880087007e007f0088007f00890088007f008000890080008a008900800081008a0081008b008a00810078008b00780082008b00820083008c0083008d008c00830084008d0084008e008d00840085008e0085008f008e00850086008f00860090008f0086008700900087009100900087008800910088009200910088008900920089009300920089008a0093008a00940093008a008b0094008b00950094008b008200950082008c0095008c008d0096008d00970096008d008e0097008e00980097008e008f0098008f00990098008f009000990090009a009900900091009a0091009b009a00910092009b0092009c009b00920093009c0093009d009c00930094009d0094009e009d00940095009e0095009f009e0095008c009f008c0096009f0096009700a0009700a100a00097009800a1009800a200a10098009900a2009900a300a20099009a00a3009a00a400a3009a009b00a4009b00a500a4009b009c00a5009c00a600a5009c009d00a6009d00a700a6009d009e00a7009e00a800a7009e009f00a8009f00a900a8009f009600a9009600a000a900a000a100aa00a100ab00aa00a100a200ab00a200ac00ab00a200a300ac00a300ad00ac00a300a400ad00a400ae00ad00a400a500ae00a500af00ae00a500a600af00a600b000af00a600a700b000a700b100b000a700a800b100a800b200b100a800a900b200a900b300b200a900a000b300a000aa00b300aa00ab00b400ab00b500b400ab00ac00b500ac00b600b500ac00ad00b600ad00b700b600ad00ae00b700ae00b800b700ae00af00b800af00b900b800af00b000b900b000ba00b900b000b100ba00b100bb00ba00b100b200bb00b200bc00bb00b200b300bc00b300bd00bc00b300aa00bd00aa00b400bd00b400b500be00b500bf00be00b500b600bf00b600c000bf00b600b700c000b700c100c000b700b800c100b800c200c100b800b900c200b900c300c200b900ba00c300ba00c400c300ba00bb00c400bb00c500c400bb00bc00c500bc00c600c500bc00bd00c600bd00c700c600bd00b400c700b400be00c700be00bf00c800bf00c900c800bf00c000c900c000ca00c900c000c100ca00c100cb00ca00c100c200cb00c200cc00cb00c200c300cc00c300cd00cc00c300c400cd00c400ce00cd00c400c500ce00c500cf00ce00c500c600cf00c600d000cf00c600c700d000c700d100d000c700be00d100be00c800d100c800c900d200c900d300d200c900ca00d300ca00d400d300ca00cb00d400cb00d500d400cb00cc00d500cc00d600d500cc00cd00d600cd00d700d600cd00ce00d700ce00d800d700ce00cf00d800cf00d900d800cf00d000d900d000da00d900d000d100da00d100db00da00d100c800db00c800d200db00d200d300dc00d300dd00dc00d300d400dd00d400de00dd00d400d500de00d500df00de00d500d600df00d600e000df00d600d700e000d700e100e000d700d800e100d800e200e100d800d900e200d900e300e200d900da00e300da00e400e300da00db00e400db00e500e400db00d200e500d200dc00e500dc00dd00e600dd00e700e600dd00de00e700de00e800e700de00df00e800df00e900e800df00e000e900e000ea00e900e000e100ea00e100eb00ea00e100e200eb00e200ec00eb00e200e300ec00e300ed00ec00e300e400ed00e400ee00ed00e400e500ee00e500ef00ee00e500dc00ef00dc00e600ef00e600e700f000e700f100f000e700e800f100e800f200f100e800e900f200e900f300f200e900ea00f300ea00f400f300ea00eb00f400eb00f500f400eb00ec00f500ec00f600f500ec00ed00f600ed00f700f600ed00ee00f700ee00f800f700ee00ef00f800ef00f900f800ef00e600f900e600f000f900f000f100fa00f100fb00fa00f100f200fb00f200fc00fb00f200f300fc00f300fd00fc00f300f400fd00f400fe00fd00f400f500fe00f500ff00fe00f500f600ff00f6000001ff00f600f7000001f70001010001f700f8000101f80002010101f800f9000201f90003010201f900f0000301f000fa000301fa00fb000401fb0005010401fb00fc000501fc0006010501fc00fd000601fd0007010601fd00fe000701fe0008010701fe00ff000801ff0009010801ff000001090100010a010901000101010a0101010b010a01010102010b0102010c010b01020103010c0103010d010c010301fa000d01fa0004010d01040105010e0105010f010e01050106010f01060110010f0106010701100107011101100107010801110108011201110108010901120109011301120109010a0113010a01140113010a010b0114010b01150114010b010c0115010c01160115010c010d0116010d01170116010d010401170104010e0117010e010f0118010f01190118010f011001190110011a011901100111011a0111011b011a01110112011b0112011c011b01120113011c0113011d011c01130114011d0114011e011d01140115011e0115011f011e01150116011f01160120011f0116011701200117012101200117010e0121010e011801210118011901220119012301220119011a0123011a01240123011a011b0124011b01250124011b011c0125011c01260125011c011d0126011d01270126011d011e0127011e01280127011e011f0128011f01290128011f012001290120012a012901200121012a0121012b012a01210118012b01180122012b01220123012c0123012d012c01230124012d0124012e012d01240125012e0125012f012e01250126012f01260130012f0126012701300127013101300127012801310128013201310128012901320129013301320129012a0133012a01340133012a012b0134012b01350134012b012201350122012c0135012c012d0136012d01370136012d012e0137012e01380137012e012f0138012f01390138012f013001390130013a013901300131013a0131013b013a01310132013b0132013c013b01320133013c0133013d013c01330134013d0134013e013d01340135013e0135013f013e0135012c013f012c0136013f0136013701400137014101400137013801410138014201410138013901420139014301420139013a0143013a01440143013a013b0144013b01450144013b013c0145013c01460145013c013d0146013d01470146013d013e0147013e01480147013e013f0148013f01490148013f0136014901360140014901400141014a0141014b014a01410142014b0142014c014b01420143014c0143014d014c01430144014d0144014e014d01440145014e0145014f014e01450146014f01460150014f0146014701500147015101500147014801510148015201510148014901520149015301520149014001530140014a0153014a014b0154014b01550154014b014c0155014c01560155014c014d0156014d01570156014d014e0157014e01580157014e014f0158014f01590158014f015001590150015a015901500151015a0151015b015a01510152015b0152015c015b01520153015c0153015d015c0153014a015d014a0154015d01540155015e0155015f015e01550156015f01560160015f0156015701600157016101600157015801610158016201610158015901620159016301620159015a0163015a01640163015a015b0164015b01650164015b015c0165015c01660165015c015d0166015d01670166015d015401670154015e0167015e015f0168015f01690168015f016001690160016a016901600161016a0161016b016a01610162016b0162016c016b01620163016c0163016d016c01630164016d0164016e016d01640165016e0165016f016e01650166016f01660170016f0166016701700167017101700167015e0171015e016801710168016901720169017301720169016a0173016a01740173016a016b0174016b01750174016b016c0175016c01760175016c016d0176016d01770176016d016e0177016e01780177016e016f0178016f01790178016f017001790170017a017901700171017a0171017b017a01710168017b01680172017b01720173017c0173017d017c01730174017d0174017e017d01740175017e0175017f017e01750176017f01760180017f0176017701800177018101800177017801810178018201810178017901820179018301820179017a0183017a01840183017a017b0184017b01850184017b017201850172017c0185017c017d0186017d01870186017d017e0187017e01880187017e017f0188017f01890188017f018001890180018a018901800181018a0181018b018a01810182018b0182018c018b01820183018c0183018d018c01830184018d0184018e018d01840185018e0185018f018e0185017c018f017c0186018f0186018701900187019101900187018801910188019201910188018901920189019301920189018a0193018a01940193018a018b0194018b01950194018b018c0195018c01960195018c018d0196018d01970196018d018e0197018e01980197018e018f0198018f01990198018f0186019901860190019901900191019a0191019b019a01910192019b0192019c019b01920193019c0193019d019c01930194019d0194019e019d01940195019e0195019f019e01950196019f019601a0019f0196019701a0019701a101a00197019801a1019801a201a10198019901a2019901a301a20199019001a30190019a01a3019a019b01a4019b01a501a4019b019c01a5019c01a601a5019c019d01a6019d01a701a6019d019e01a7019e01a801a7019e019f01a8019f01a901a8019f01a001a901a001aa01a901a001a101aa01a101ab01aa01a101a201ab01a201ac01ab01a201a301ac01a301ad01ac01a3019a01ad019a01a401ad01a401a501ae01a501af01ae01a501a601af01a601b001af01a601a701b001a701b101b001a701a801b101a801b201b101a801a901b201a901b301b201a901aa01b301aa01b401b301aa01ab01b401ab01b501b401ab01ac01b501ac01b601b501ac01ad01b601ad01b701b601ad01a401b701a401ae01b701ae01af01b801af01b901b801af01b001b901b001ba01b901b001b101ba01b101bb01ba01b101b201bb01b201bc01bb01b201b301bc01b301bd01bc01b301b401bd01b401be01bd01b401b501be01b501bf01be01b501b601bf01b601c001bf01b601b701c001b701c101c001b701ae01c101ae01b801c101b801b901c201b901c301c201b901ba01c301ba01c401c301ba01bb01c401bb01c501c401bb01bc01c501bc01c601c501bc01bd01c601bd01c701c601bd01be01c701be01c801c701be01bf01c801bf01c901c801bf01c001c901c001ca01c901c001c101ca01c101cb01ca01c101b801cb01b801c201cb01c201c301cc01c301cd01cc01c301c401cd01c401ce01cd01c401c501ce01c501cf01ce01c501c601cf01c601d001cf01c601c701d001c701d101d001c701c801d101c801d201d101c801c901d201c901d301d201c901ca01d301ca01d401d301ca01cb01d401cb01d501d401cb01c201d501c201cc01d501cc01cd01d601cd01d701d601cd01ce01d701ce01d801d701ce01cf01d801cf01d901d801cf01d001d901d001da01d901d001d101da01d101db01da01d101d201db01d201dc01db01d201d301dc01d301dd01dc01d301d401dd01d401de01dd01d401d501de01d501df01de01d501cc01df01cc01d601df01d601d701e001d701e101e001d701d801e101d801e201e101d801d901e201d901e301e201d901da01e301da01e401e301da01db01e401db01e501e401db01dc01e501dc01e601e501dc01dd01e601dd01e701e601dd01de01e701de01e801e701de01df01e801df01e901e801df01d601e901d601e001e901e001e101ea01e101eb01ea01e101e201eb01e201ec01eb01e201e301ec01e301ed01ec01e301e401ed01e401ee01ed01e401e501ee01e501ef01ee01e501e601ef01e601f001ef01e601e701f001e701f101f001e701e801f101e801f201f101e801e901f201e901f301f201e901e001f301e001ea01f301ea01eb01f401eb01f501f401eb01ec01f501ec01f601f501ec01ed01f601ed01f701f601ed01ee01f701ee01f801f701ee01ef01f801ef01f901f801ef01f001f901f001fa01f901f001f101fa01f101fb01fa01f101f201fb01f201fc01fb01f201f301fc01f301fd01fc01f301ea01fd01ea01f401fd01f401f501fe01f501ff01fe01f501f601ff01f6010002ff01f601f7010002f70101020002f701f8010102f80102020102f801f9010202f90103020202f901fa010302fa0104020302fa01fb010402fb0105020402fb01fc010502fc0106020502fc01fd010602fd0107020602fd01f4010702f401fe010702fe01ff010802ff0109020802ff010002090200020a020902000201020a0201020b020a02010202020b0202020c020b02020203020c0203020d020c02030204020d0204020e020d02040205020e0205020f020e02050206020f02060210020f020602070210020702110210020702fe011102fe010802110208020902120209021302120209020a0213020a02140213020a020b0214020b02150214020b020c0215020c02160215020c020d0216020d02170216020d020e0217020e02180217020e020f0218020f02190218020f021002190210021a021902100211021a0211021b021a02110208021b02080212021b02120213021c0213021d021c02130214021d0214021e021d02140215021e0215021f021e02150216021f02160220021f0216021702200217022102200217021802210218022202210218021902220219022302220219021a0223021a02240223021a021b0224021b02250224021b021202250212021c0225021c021d0226021d02270226021d021e0227021e02280227021e021f0228021f02290228021f022002290220022a022902200221022a0221022b022a02210222022b0222022c022b02220223022c0223022d022c02230224022d0224022e022d02240225022e0225022f022e0225021c022f021c0226022f0226022702300227023102300227022802310228023202310228022902320229023302320229022a0233022a02340233022a022b0234022b02350234022b022c0235022c02360235022c022d0236022d02370236022d022e0237022e02380237022e022f0238022f02390238022f0226023902260230023902300231023a0231023b023a02310232023b0232023c023b02320233023c0233023d023c02330234023d0234023e023d02340235023e0235023f023e02350236023f02360240023f0236023702400237024102400237023802410238024202410238023902420239024302420239023002430230023a0243023a023b0244023b02450244023b023c0245023c02460245023c023d0246023d02470246023d023e0247023e02480247023e023f0248023f02490248023f024002490240024a024902400241024a0241024b024a02410242024b0242024c024b02420243024c0243024d024c0243023a024d023a0244024d02440245024e0245024f024e02450246024f02460250024f0246024702500247025102500247024802510248025202510248024902520249025302520249024a0253024a02540253024a024b0254024b02550254024b024c0255024c02560255024c024d0256024d02570256024d024402570244024e0257024e024f0258024f02590258024f025002590250025a025902500251025a0251025b025a02510252025b0252025c025b02520253025c0253025d025c02530254025d0254025e025d02540255025e0255025f025e02550256025f02560260025f0256025702600257026102600257024e0261024e025802610258025902620259026302620259025a0263025a02640263025a025b0264025b02650264025b025c0265025c02660265025c025d0266025d02670266025d025e0267025e02680267025e025f0268025f02690268025f026002690260026a026902600261026a0261026b026a02610258026b02580262026b02620263026c0263026d026c02630264026d0264026e026d02640265026e0265026f026e02650266026f02660270026f0266026702700267027102700267026802710268027202710268026902720269027302720269026a0273026a02740273026a026b0274026b02750274026b026202750262026c0275026c026d0276026d02770276026d026e0277026e02780277026e026f0278026f02790278026f027002790270027a027902700271027a0271027b027a02710272027b0272027c027b02720273027c0273027d027c02730274027d0274027e027d02740275027e0275027f027e0275026c027f026c0276027f0276027702800277028102800277027802810278028202810278027902820279028302820279027a0283027a02840283027a027b0284027b02850284027b027c0285027c02860285027c027d0286027d02870286027d027e0287027e02880287027e027f0288027f02890288027f0276028902760280028902800281028a0281028b028a02810282028b0282028c028b02820283028c0283028d028c02830284028d0284028e028d02840285028e0285028f028e02850286028f02860290028f0286028702900287029102900287028802910288029202910288028902920289029302920289028002930280028a0293028a028b0294028b02950294028b028c0295028c02960295028c028d0296028d02970296028d028e0297028e02980297028e028f0298028f02990298028f029002990290029a029902900291029a0291029b029a02910292029b0292029c029b02920293029c0293029d029c0293028a029d028a0294029d02940295029e0295029f029e02950296029f029602a0029f0296029702a0029702a102a00297029802a1029802a202a10298029902a2029902a302a20299029a02a3029a02a402a3029a029b02a4029b02a502a4029b029c02a5029c02a602a5029c029d02a6029d02a702a6029d029402a70294029e02a7029e029f02a8029f02a902a8029f02a002a902a002aa02a902a002a102aa02a102ab02aa02a102a202ab02a202ac02ab02a202a302ac02a302ad02ac02a302a402ad02a402ae02ad02a402a502ae02a502af02ae02a502a602af02a602b002af02a602a702b002a702b102b002a7029e02b1029e02a802b102a802a902b202a902b302b202a902aa02b302aa02b402b302aa02ab02b402ab02b502b402ab02ac02b502ac02b602b502ac02ad02b602ad02b702b602ad02ae02b702ae02b802b702ae02af02b802af02b902b802af02b002b902b002ba02b902b002b102ba02b102bb02ba02b102a802bb02a802b202bb02b202b302bc02b302bd02bc02b302b402bd02b402be02bd02b402b502be02b502bf02be02b502b602bf02b602c002bf02b602b702c002b702c102c002b702b802c102b802c202c102b802b902c202b902c302c202b902ba02c302ba02c402c302ba02bb02c402bb02c502c402bb02b202c502b202bc02c502bc02bd02c602bd02c702c602bd02be02c702be02c802c702be02bf02c802bf02c902c802bf02c002c902c002ca02c902c002c102ca02c102cb02ca02c102c202cb02c202cc02cb02c202c302cc02c302cd02cc02c302c402cd02c402ce02cd02c402c502ce02c502cf02ce02c502bc02cf02bc02c602cf02c602c702d002c702d102d002c702c802d102c802d202d102c802c902d202c902d302d202c902ca02d302ca02d402d302ca02cb02d402cb02d502d402cb02cc02d502cc02d602d502cc02cd02d602cd02d702d602cd02ce02d702ce02d802d702ce02cf02d802cf02d902d802cf02c602d902c602d002d902d002d102da02d102db02da02d102d202db02d202dc02db02d202d302dc02d302dd02dc02d302d402dd02d402de02dd02d402d502de02d502df02de02d502d602df02d602e002df02d602d702e002d702e102e002d702d802e102d802e202e102d802d902e202d902e302e202d902d002e302d002da02e302da02db02e402db02e502e402db02dc02e502dc02e602e502dc02dd02e602dd02e702e602dd02de02e702de02e802e702de02df02e802df02e902e802df02e002e902e002ea02e902e002e102ea02e102eb02ea02e102e202eb02e202ec02eb02e202e302ec02e302ed02ec02e302da02ed02da02e402ed02e402e502ee02e502ef02ee02e502e602ef02e602f002ef02e602e702f002e702f102f002e702e802f102e802f202f102e802e902f202e902f302f202e902ea02f302ea02f402f302ea02eb02f402eb02f502f402eb02ec02f502ec02f602f502ec02ed02f602ed02f702f602ed02e402f702e402ee02f702ee02ef02f802ef02f902f802ef02f002f902f002fa02f902f002f102fa02f102fb02fa02f102f202fb02f202fc02fb02f202f302fc02f302fd02fc02f302f402fd02f402fe02fd02f402f502fe02f502ff02fe02f502f602ff02f6020003ff02f602f7020003f70201030003f702ee020103ee02f8020103f802f9020203f90203030203f902fa020303fa0204030303fa02fb020403fb0205030403fb02fc020503fc0206030503fc02fd020603fd0207030603fd02fe020703fe0208030703fe02ff020803ff0209030803ff020003090300030a030903000301030a0301030b030a030103f8020b03f80202030b03020303030c0303030d030c03030304030d0304030e030d03040305030e0305030f030e03050306030f03060310030f0306030703100307031103100307030803110308031203110308030903120309031303120309030a0313030a03140313030a030b0314030b03150314030b030203150302030c0315030c030d0316030d03170316030d030e0317030e03180317030e030f0318030f03190318030f031003190310031a031903100311031a0311031b031a03110312031b0312031c031b03120313031c0313031d031c03130314031d0314031e031d03140315031e0315031f031e0315030c031f030c0316031f0316031703200317032103200317031803210318032203210318031903220319032303220319031a0323031a03240323031a031b0324031b03250324031b031c0325031c03260325031c031d0326031d03270326031d031e0327031e03280327031e031f0328031f03290328031f0316032903160320032903200321032a0321032b032a03210322032b0322032c032b03220323032c0323032d032c03230324032d0324032e032d03240325032e0325032f032e03250326032f03260330032f0326032703300327033103300327032803310328033203310328032903320329033303320329032003330320032a0333032a032b0334032b03350334032b032c0335032c03360335032c032d0336032d03370336032d032e0337032e03380337032e032f0338032f03390338032f033003390330033a033903300331033a0331033b033a03310332033b0332033c033b03320333033c0333033d033c0333032a033d032a0334033d03340335033e0335033f033e03350336033f03360340033f0336033703400337034103400337033803410338034203410338033903420339034303420339033a0343033a03440343033a033b0344033b03450344033b033c0345033c03460345033c033d0346033d03470346033d033403470334033e0347033e033f0348033f03490348033f034003490340034a034903400341034a0341034b034a03410342034b0342034c034b03420343034c0343034d034c03430344034d0344034e034d03440345034e0345034f034e03450346034f03460350034f0346034703500347035103500347033e0351033e034803510348034903520349035303520349034a0353034a03540353034a034b0354034b03550354034b034c0355034c03560355034c034d0356034d03570356034d034e0357034e03580357034e034f0358034f03590358034f035003590350035a035903500351035a0351035b035a03510348035b03480352035b03520353035c0353035d035c03530354035d0354035e035d03540355035e0355035f035e03550356035f03560360035f0356035703600357036103600357035803610358036203610358035903620359036303620359035a0363035a03640363035a035b0364035b03650364035b035203650352035c0365035c035d0366035d03670366035d035e0367035e03680367035e035f0368035f03690368035f036003690360036a036903600361036a0361036b036a03610362036b0362036c036b03620363036c0363036d036c03630364036d0364036e036d03640365036e0365036f036e0365035c036f035c0366036f0366036703700367037103700367036803710368037203710368036903720369037303720369036a0373036a03740373036a036b0374036b03750374036b036c0375036c03760375036c036d0376036d03770376036d036e0377036e03780377036e036f0378036f03790378036f0366037903660370037903700371037a0371037b037a03710372037b0372037c037b03720373037c0373037d037c03730374037d0374037e037d03740375037e0375037f037e03750376037f03760380037f0376037703800377038103800377037803810378038203810378037903820379038303820379037003830370037a0383037a037b0384037b03850384037b037c0385037c03860385037c037d0386037d03870386037d037e0387037e03880387037e037f0388037f03890388037f038003890380038a038903800381038a0381038b038a03810382038b0382038c038b03820383038c0383038d038c0383037a038d037a0384038d03840385038e0385038f038e03850386038f03860390038f0386038703900387039103900387038803910388039203910388038903920389039303920389038a0393038a03940393038a038b0394038b03950394038b038c0395038c03960395038c038d0396038d03970396038d038403970384038e0397038e038f0398038f03990398038f039003990390039a039903900391039a0391039b039a03910392039b0392039c039b03920393039c0393039d039c03930394039d0394039e039d03940395039e0395039f039e03950396039f039603a0039f0396039703a0039703a103a00397038e03a1038e039803a10398039903a2039903a303a20399039a03a3039a03a403a3039a039b03a4039b03a503a4039b039c03a5039c03a603a5039c039d03a6039d03a703a6039d039e03a7039e03a803a7039e039f03a8039f03a903a8039f03a003a903a003aa03a903a003a103aa03a103ab03aa03a1039803ab039803a203ab03a203a303ac03a303ad03ac03a303a403ad03a403ae03ad03a403a503ae03a503af03ae03a503a603af03a603b003af03a603a703b003a703b103b003a703a803b103a803b203b103a803a903b203a903b303b203a903aa03b303aa03b403b303aa03ab03b403ab03b503b403ab03a203b503a203ac03b503ac03ad03b603ad03b703b603ad03ae03b703ae03b803b703ae03af03b803af03b903b803af03b003b903b003ba03b903b003b103ba03b103bb03ba03b103b203bb03b203bc03bb03b203b303bc03b303bd03bc03b303b403bd03b403be03bd03b403b503be03b503bf03be03b503ac03bf03ac03b603bf03b603b703c003b703c103c003b703b803c103b803c203c103b803b903c203b903c303c203b903ba03c303ba03c403c303ba03bb03c403bb03c503c403bb03bc03c503bc03c603c503bc03bd03c603bd03c703c603bd03be03c703be03c803c703be03bf03c803bf03c903c803bf03b603c903b603c003c903c003c103ca03c103cb03ca03c103c203cb03c203cc03cb03c203c303cc03c303cd03cc03c303c403cd03c403ce03cd03c403c503ce03c503cf03ce03c503c603cf03c603d003cf03c603c703d003c703d103d003c703c803d103c803d203d103c803c903d203c903d303d203c903c003d303c003ca03d303ca03cb03d403cb03d503d403cb03cc03d503cc03d603d503cc03cd03d603cd03d703d603cd03ce03d703ce03d803d703ce03cf03d803cf03d903d803cf03d003d903d003da03d903d003d103da03d103db03da03d103d203db03d203dc03db03d203d303dc03d303dd03dc03d303ca03dd03ca03d403dd03d403d503de03d503df03de03d503d603df03d603e003df03d603d703e003d703e103e003d703d803e103d803e203e103d803d903e203d903e303e203d903da03e303da03e403e303da03db03e403db03e503e403db03dc03e503dc03e603e503dc03dd03e603dd03e703e603dd03d403e703d403de03e703de03df03e803df03e903e803df03e003e903e003ea03e903e003e103ea03e103eb03ea03e103e203eb03e203ec03eb03e203e303ec03e303ed03ec03e303e403ed03e403ee03ed03e403e503ee03e503ef03ee03e503e603ef03e603f003ef03e603e703f003e703f103f003e703de03f103de03e803f103e803e903f203e903f303f203e903ea03f303ea03f403f303ea03eb03f403eb03f503f403eb03ec03f503ec03f603f503ec03ed03f603ed03f703f603ed03ee03f703ee03f803f703ee03ef03f803ef03f903f803ef03f003f903f003fa03f903f003f103fa03f103fb03fa03f103e803fb03e803f203fb03f203f303fc03f303fd03fc03f303f403fd03f403fe03fd03f403f503fe03f503ff03fe03f503f603ff03f6030004ff03f603f7030004f70301040004f703f8030104f80302040104f803f9030204f90303040204f903fa030304fa0304040304fa03fb030404fb0305040404fb03f2030504f203fc030504fc03fd030604fd0307040604fd03fe030704fe0308040704fe03ff030804ff0309040804ff030004090400040a040904000401040a0401040b040a04010402040b0402040c040b04020403040c0403040d040c04030404040d0404040e040d04040405040e0405040f040e040504fc030f04fc0306040f0406040704100407041104100407040804110408041204110408040904120409041304120409040a0413040a04140413040a040b0414040b04150414040b040c0415040c04160415040c040d0416040d04170416040d040e0417040e04180417040e040f0418040f04190418040f0406041904060410041904100411041a0411041b041a04110412041b0412041c041b04120413041c0413041d041c04130414041d0414041e041d04140415041e0415041f041e04150416041f04160420041f0416041704200417042104200417041804210418042204210418041904220419042304220419041004230410041a0423041a041b0424041b04250424041b041c0425041c04260425041c041d0426041d04270426041d041e0427041e04280427041e041f0428041f04290428041f042004290420042a042904200421042a0421042b042a04210422042b0422042c042b04220423042c0423042d042c0423041a042d041a0424042d04240425042e0425042f042e04250426042f04260430042f0426042704300427043104300427042804310428043204310428042904320429043304320429042a0433042a04340433042a042b0434042b04350434042b042c0435042c04360435042c042d0436042d04370436042d042404370424042e0437042e042f0438042f04390438042f043004390430043a043904300431043a0431043b043a04310432043b0432043c043b04320433043c0433043d043c04330434043d0434043e043d04340435043e0435043f043e04350436043f04360440043f0436043704400437044104400437042e0441042e043804410438043904420439044304420439043a0443043a04440443043a043b0444043b04450444043b043c0445043c04460445043c043d0446043d04470446043d043e0447043e04480447043e043f0448043f04490448043f044004490440044a044904400441044a0441044b044a04410438044b04380442044b04420443044c0443044d044c04430444044d0444044e044d04440445044e0445044f044e04450446044f04460450044f0446044704500447045104500447044804510448045204510448044904520449045304520449044a0453044a04540453044a044b0454044b04550454044b044204550442044c0455044c044d0456044d04570456044d044e0457044e04580457044e044f0458044f04590458044f045004590450045a045904500451045a0451045b045a04510452045b0452045c045b04520453045c0453045d045c04530454045d0454045e045d04540455045e0455045f045e0455044c045f044c0456045f0456045704600457046104600457045804610458046204610458045904620459046304620459045a0463045a04640463045a045b0464045b04650464045b045c0465045c04660465045c045d0466045d04670466045d045e0467045e04680467045e045f0468045f04690468045f0456046904560460046904600461046a0461046b046a04610462046b0462046c046b04620463046c0463046d046c04630464046d0464046e046d04640465046e0465046f046e04650466046f04660470046f0466046704700467047104700467046804710468047204710468046904720469047304720469046004730460046a0473046a046b0474046b04750474046b046c0475046c04760475046c046d0476046d04770476046d046e0477046e04780477046e046f0478046f04790478046f047004790470047a047904700471047a0471047b047a04710472047b0472047c047b04720473047c0473047d047c0473046a047d046a0474047d04740475047e0475047f047e04750476047f04760480047f0476047704800477048104800477047804810478048204810478047904820479048304820479047a0483047a04840483047a047b0484047b04850484047b047c0485047c04860485047c047d0486047d04870486047d047404870474047e0487047e047f0488047f04890488047f048004890480048a048904800481048a0481048b048a04810482048b0482048c048b04820483048c0483048d048c04830484048d0484048e048d04840485048e0485048f048e04850486048f04860490048f0486048704900487049104900487047e0491047e048804910488048904920489049304920489048a0493048a04940493048a048b0494048b04950494048b048c0495048c04960495048c048d0496048d04970496048d048e0497048e04980497048e048f0498048f04990498048f049004990490049a049904900491049a0491049b049a04910488049b04880492049b04920493049c0493049d049c04930494049d0494049e049d04940495049e0495049f049e04950496049f049604a0049f0496049704a0049704a104a00497049804a1049804a204a10498049904a2049904a304a20499049a04a3049a04a404a3049a049b04a4049b04a504a4049b049204a50492049c04a5049c049d04a6049d04a704a6049d049e04a7049e04a804a7049e049f04a8049f04a904a8049f04a004a904a004aa04a904a004a104aa04a104ab04aa04a104a204ab04a204ac04ab04a204a304ac04a304ad04ac04a304a404ad04a404ae04ad04a404a504ae04a504af04ae04a5049c04af049c04a604af04a604a704b004a704b104b004a704a804b104a804b204b104a804a904b204a904b304b204a904aa04b304aa04b404b304aa04ab04b404ab04b504b404ab04ac04b504ac04b604b504ac04ad04b604ad04b704b604ad04ae04b704ae04b804b704ae04af04b804af04b904b804af04a604b904a604b004b904b004b104ba04b104bb04ba04b104b204bb04b204bc04bb04b204b304bc04b304bd04bc04b304b404bd04b404be04bd04b404b504be04b504bf04be04b504b604bf04b604c004bf04b604b704c004b704c104c004b704b804c104b804c204c104b804b904c204b904c304c204b904b004c304b004ba04c304ba04bb04c404bb04c504c404bb04bc04c504bc04c604c504bc04bd04c604bd04c704c604bd04be04c704be04c804c704be04bf04c804bf04c904c804bf04c004c904c004ca04c904c004c104ca04c104cb04ca04c104c204cb04c204cc04cb04c204c304cc04c304cd04cc04c304ba04cd04ba04c404cd04c404c504ce04c504cf04ce04c504c604cf04c604d004cf04c604c704d004c704d104d004c704c804d104c804d204d104c804c904d204c904d304d204c904ca04d304ca04d404d304ca04cb04d404cb04d504d404cb04cc04d504cc04d604d504cc04cd04d604cd04d704d604cd04c404d704c404ce04d704ce04cf04d804cf04d904d804cf04d004d904d004da04d904d004d104da04d104db04da04d104d204db04d204dc04db04d204d304dc04d304dd04dc04d304d404dd04d404de04dd04d404d504de04d504df04de04d504d604df04d604e004df04d604d704e004d704e104e004d704ce04e104ce04d804e104d804d904e204d904e304e204d904da04e304da04e404e304da04db04e404db04e504e404db04dc04e504dc04e604e504dc04dd04e604dd04e704e604dd04de04e704de04e804e704de04df04e804df04e904e804df04e004e904e004ea04e904e004e104ea04e104eb04ea04e104d804eb04d804e204eb04e204e304ec04e304ed04ec04e304e404ed04e404ee04ed04e404e504ee04e504ef04ee04e504e604ef04e604f004ef04e604e704f004e704f104f004e704e804f104e804f204f104e804e904f204e904f304f204e904ea04f304ea04f404f304ea04eb04f404eb04f504f404eb04e204f504e204ec04f504ec04ed04f604ed04f704f604ed04ee04f704ee04f804f704ee04ef04f804ef04f904f804ef04f004f904f004fa04f904f004f104fa04f104fb04fa04f104f204fb04f204fc04fb04f204f304fc04f304fd04fc04f304f404fd04f404fe04fd04f404f504fe04f504ff04fe04f504ec04ff04ec04f604ff04f604f7040005f70401050005f704f8040105f80402050105f804f9040205f90403050205f904fa040305fa0404050305fa04fb040405fb0405050405fb04fc040505fc0406050505fc04fd040605fd0407050605fd04fe040705fe0408050705fe04ff040805ff0409050805ff04f6040905f60400050905000501050a0501050b050a05010502050b0502050c050b05020503050c0503050d050c05030504050d0504050e050d05040505050e0505050f050e05050506050f05060510050f0506050705100507051105100507050805110508051205110508050905120509051305120509050005130500050a0513050a050b0514050b05150514050b050c0515050c05160515050c050d0516050d05170516050d050e0517050e05180517050e050f0518050f05190518050f051005190510051a051905100511051a0511051b051a05110512051b0512051c051b05120513051c0513051d051c0513050a051d050a0514051d05140515051e0515051f051e05150516051f05160520051f0516051705200517052105200517051805210518052205210518051905220519052305220519051a0523051a05240523051a051b0524051b05250524051b051c0525051c05260525051c051d0526051d05270526051d051405270514051e0527051e051f0528051f05290528051f052005290520052a052905200521052a0521052b052a05210522052b0522052c052b05220523052c0523052d052c05230524052d0524052e052d05240525052e0525052f052e05250526052f05260530052f0526052705300527053105300527051e0531051e052805310528052905320529053305320529052a0533052a05340533052a052b0534052b05350534052b052c0535052c05360535052c052d0536052d05370536052d052e0537052e05380537052e052f0538052f05390538052f053005390530053a053905300531053a0531053b053a05310528053b05280532053b05320533053c0533053d053c05330534053d0534053e053d05340535053e0535053f053e05350536053f05360540053f0536053705400537054105400537053805410538054205410538053905420539054305420539053a0543053a05440543053a053b0544053b05450544053b053205450532053c0545053c053d0546053d05470546053d053e0547053e05480547053e053f0548053f05490548053f054005490540054a054905400541054a0541054b054a05410542054b0542054c054b05420543054c0543054d054c05430544054d0544054e054d05440545054e0545054f054e0545053c054f053c0546054f0546054705500547055105500547054805510548055205510548054905520549055305520549054a0553054a05540553054a054b0554054b05550554054b054c0555054c05560555054c054d0556054d05570556054d054e0557054e05580557054e054f0558054f05590558054f0546055905460550055905500551055a0551055b055a05510552055b0552055c055b05520553055c0553055d055c05530554055d0554055e055d05540555055e0555055f055e05550556055f05560560055f0556055705600557056105600557055805610558056205610558055905620559056305620559055005630550055a0563055a055b0564055b05650564055b055c0565055c05660565055c055d0566055d05670566055d055e0567055e05680567055e055f0568055f05690568055f056005690560056a056905600561056a0561056b056a05610562056b0562056c056b05620563056c0563056d056c0563055a056d055a0564056d05640565056e0565056f056e05650566056f05660570056f0566056705700567057105700567056805710568057205710568056905720569057305720569056a0573056a05740573056a056b0574056b05750574056b056c0575056c05760575056c056d0576056d05770576056d056405770564056e0577056e056f0578056f05790578056f057005790570057a057905700571057a0571057b057a05710572057b0572057c057b05720573057c0573057d057c05730574057d0574057e057d05740575057e0575057f057e05750576057f05760580057f0576057705800577058105800577056e0581056e057805810578057905820579058305820579057a0583057a05840583057a057b0584057b05850584057b057c0585057c05860585057c057d0586057d05870586057d057e0587057e05880587057e057f0588057f05890588057f058005890580058a058905800581058a0581058b058a05810578058b05780582058b05820583058c0583058d058c05830584058d0584058e058d05840585058e0585058f058e05850586058f05860590058f0586058705900587059105900587058805910588059205910588058905920589059305920589058a0593058a05940593058a058b0594058b05950594058b058205950582058c0595058c058d0596058d05970596058d058e0597058e05980597058e058f0598058f05990598058f059005990590059a059905900591059a0591059b059a05910592059b0592059c059b05920593059c0593059d059c05930594059d0594059e059d05940595059e0595059f059e0595058c059f058c0596059f0596059705a0059705a105a00597059805a1059805a205a10598059905a2059905a305a20599059a05a3059a05a405a3059a059b05a4059b05a505a4059b059c05a5059c05a605a5059c059d05a6059d05a705a6059d059e05a7059e05a805a7059e059f05a8059f05a905a8059f059605a9059605a005a905a005a105aa05a105ab05aa05a105a205ab05a205ac05ab05a205a305ac05a305ad05ac05a305a405ad05a405ae05ad05a405a505ae05a505af05ae05a505a605af05a605b005af05a605a705b005a705b105b005a705a805b105a805b205b105a805a905b205a905b305b205a905a005b305a005aa05b305aa05ab05b405ab05b505b405ab05ac05b505ac05b605b505ac05ad05b605ad05b705b605ad05ae05b705ae05b805b705ae05af05b805af05b905b805af05b005b905b005ba05b905b005b105ba05b105bb05ba05b105b205bb05b205bc05bb05b205b305bc05b305bd05bc05b305aa05bd05aa05b405bd05b405b505be05b505bf05be05b505b605bf05b605c005bf05b605b705c005b705c105c005b705b805c105b805c205c105b805b905c205b905c305c205b905ba05c305ba05c405c305ba05bb05c405bb05c505c405bb05bc05c505bc05c605c505bc05bd05c605bd05c705c605bd05b405c705b405be05c705be05bf05c805bf05c905c805bf05c005c905c005ca05c905c005c105ca05c105cb05ca05c105c205cb05c205cc05cb05c205c305cc05c305cd05cc05c305c405cd05c405ce05cd05c405c505ce05c505cf05ce05c505c605cf05c605d005cf05c605c705d005c705d105d005c705be05d105be05c805d105c805c905d205c905d305d205c905ca05d305ca05d405d305ca05cb05d405cb05d505d405cb05cc05d505cc05d605d505cc05cd05d605cd05d705d605cd05ce05d705ce05d805d705ce05cf05d805cf05d905d805cf05d005d905d005da05d905d005d105da05d105db05da05d105c805db05c805d205db05d205d305dc05d305dd05dc05d305d405dd05d405de05dd05d405d505de05d505df05de05d505d605df05d605e005df05d605d705e005d705e105e005d705d805e105d805e205e105d805d905e205d905e305e205d905da05e305da05e405e305da05db05e405db05e505e405db05d205e505d205dc05e505dc05dd05e605dd05e705e605dd05de05e705de05e805e705de05df05e805df05e905e805df05e005e905e005ea05e905e005e105ea05e105eb05ea05e105e205eb05e205ec05eb05e205e305ec05e305ed05ec05e305e405ed05e405ee05ed05e405e505ee05e505ef05ee05e505dc05ef05dc05e605ef05e605e705f005e705f105f005e705e805f105e805f205f105e805e905f205e905f305f205e905ea05f305ea05f405f305ea05eb05f405eb05f505f405eb05ec05f505ec05f605f505ec05ed05f605ed05f705f605ed05ee05f705ee05f805f705ee05ef05f805ef05f905f805ef05e605f905e605f005f905f005f105fa05f105fb05fa05f105f205fb05f205fc05fb05f205f305fc05f305fd05fc05f305f405fd05f405fe05fd05f405f505fe05f505ff05fe05f505f605ff05f6050006ff05f605f7050006f70501060006f705f8050106f80502060106f805f9050206f90503060206f905f0050306f005fa050306fa05fb050406fb0505060406fb05fc050506fc0506060506fc05fd050606fd0507060606fd05fe050706fe0508060706fe05ff050806ff0509060806ff050006090600060a060906000601060a0601060b060a06010602060b0602060c060b06020603060c0603060d060c060306fa050d06fa0504060d06040605060e0605060f060e06050606060f06060610060f0606060706100607061106100607060806110608061206110608060906120609061306120609060a0613060a06140613060a060b0614060b06150614060b060c0615060c06160615060c060d0616060d06170616060d060406170604060e0617060e060f0618060f06190618060f061006190610061a061906100611061a0611061b061a06110612061b0612061c061b06120613061c0613061d061c06130614061d0614061e061d06140615061e0615061f061e06150616061f06160620061f0616061706200617062106200617060e0621060e061806210618061906220619062306220619061a0623061a06240623061a061b0624061b06250624061b061c0625061c06260625061c061d0626061d06270626061d061e0627061e06280627061e061f0628061f06290628061f062006290620062a062906200621062a0621062b062a06210618062b06180622062b06220623062c0623062d062c06230624062d0624062e062d06240625062e0625062f062e06250626062f06260630062f0626062706300627063106300627062806310628063206310628062906320629063306320629062a0633062a06340633062a062b0634062b06350634062b062206350622062c0635062c062d0636062d06370636062d062e0637062e06380637062e062f0638062f06390638062f063006390630063a063906300631063a0631063b063a06310632063b0632063c063b06320633063c0633063d063c06330634063d0634063e063d06340635063e0635063f063e0635062c063f062c0636063f0636063706400637064106400637063806410638064206410638063906420639064306420639063a0643063a06440643063a063b0644063b06450644063b063c0645063c06460645063c063d0646063d06470646063d063e0647063e06480647063e063f0648063f06490648063f0636064906360640064906400641064a0641064b064a06410642064b0642064c064b06420643064c0643064d064c06430644064d0644064e064d06440645064e0645064f064e06450646064f06460650064f0646064706500647065106500647064806510648065206510648064906520649065306520649064006530640064a0653064a064b0654064b06550654064b064c0655064c06560655064c064d0656064d06570656064d064e0657064e06580657064e064f0658064f06590658064f065006590650065a065906500651065a0651065b065a06510652065b0652065c065b06520653065c0653065d065c0653064a065d064a0654065d06540655065e0655065f065e06550656065f06560660065f0656065706600657066106600657065806610658066206610658065906620659066306620659065a0663065a06640663065a065b0664065b06650664065b065c0665065c06660665065c065d0666065d06670666065d065406670654065e0667065e065f0668065f06690668065f066006690660066a066906600661066a0661066b066a06610662066b0662066c066b06620663066c0663066d066c06630664066d0664066e066d06640665066e0665066f066e06650666066f06660670066f0666066706700667067106700667065e0671065e066806710668066906720669067306720669066a0673066a06740673066a066b0674066b06750674066b066c0675066c06760675066c066d0676066d06770676066d066e0677066e06780677066e066f0678066f06790678066f067006790670067a067906700671067a0671067b067a06710668067b06680672067b06720673067c0673067d067c06730674067d0674067e067d06740675067e0675067f067e06750676067f06760680067f0676067706800677068106800677067806810678068206810678067906820679068306820679067a0683067a06840683067a067b0684067b06850684067b067206850672067c0685067c067d0686067d06870686067d067e0687067e06880687067e067f0688067f06890688067f068006890680068a068906800681068a0681068b068a06810682068b0682068c068b06820683068c0683068d068c06830684068d0684068e068d06840685068e0685068f068e0685067c068f067c0686068f0686068706900687069106900687068806910688069206910688068906920689069306920689068a0693068a06940693068a068b0694068b06950694068b068c0695068c06960695068c068d0696068d06970696068d068e0697068e06980697068e068f0698068f06990698068f0686069906860690069906900691069a0691069b069a06910692069b0692069c069b06920693069c0693069d069c06930694069d0694069e069d06940695069e0695069f069e06950696069f069606a0069f0696069706a0069706a106a00697069806a1069806a206a10698069906a2069906a306a20699069006a30690069a06a3069a069b06a4069b06a506a4069b069c06a5069c06a606a5069c069d06a6069d06a706a6069d069e06a7069e06a806a7069e069f06a8069f06a906a8069f06a006a906a006aa06a906a006a106aa06a106ab06aa06a106a206ab06a206ac06ab06a206a306ac06a306ad06ac06a3069a06ad069a06a406ad06a406a506ae06a506af06ae06a506a606af06a606b006af06a606a706b006a706b106b006a706a806b106a806b206b106a806a906b206a906b306b206a906aa06b306aa06b406b306aa06ab06b406ab06b506b406ab06ac06b506ac06b606b506ac06ad06b606ad06b706b606ad06a406b706a406ae06b706ae06af06b806af06b906b806af06b006b906b006ba06b906b006b106ba06b106bb06ba06b106b206bb06b206bc06bb06b206b306bc06b306bd06bc06b306b406bd06b406be06bd06b406b506be06b506bf06be06b506b606bf06b606c006bf06b606b706c006b706c106c006b706ae06c106ae06b806c106b806b906c206b906c306c206b906ba06c306ba06c406c306ba06bb06c406bb06c506c406bb06bc06c506bc06c606c506bc06bd06c606bd06c706c606bd06be06c706be06c806c706be06bf06c806bf06c906c806bf06c006c906c006ca06c906c006c106ca06c106cb06ca06c106b806cb06b806c206cb06c206c306cc06c306cd06cc06c306c406cd06c406ce06cd06c406c506ce06c506cf06ce06c506c606cf06c606d006cf06c606c706d006c706d106d006c706c806d106c806d206d106c806c906d206c906d306d206c906ca06d306ca06d406d306ca06cb06d406cb06d506d406cb06c206d506c206cc06d506cc06cd06d606cd06d706d606cd06ce06d706ce06d806d706ce06cf06d806cf06d906d806cf06d006d906d006da06d906d006d106da06d106db06da06d106d206db06d206dc06db06d206d306dc06d306dd06dc06d306d406dd06d406de06dd06d406d506de06d506df06de06d506cc06df06cc06d606df06d606d706e006d706e106e006d706d806e106d806e206e106d806d906e206d906e306e206d906da06e306da06e406e306da06db06e406db06e506e406db06dc06e506dc06e606e506dc06dd06e606dd06e706e606dd06de06e706de06e806e706de06df06e806df06e906e806df06d606e906d606e006e906e006e106ea06e106eb06ea06e106e206eb06e206ec06eb06e206e306ec06e306ed06ec06e306e406ed06e406ee06ed06e406e506ee06e506ef06ee06e506e606ef06e606f006ef06e606e706f006e706f106f006e706e806f106e806f206f106e806e906f206e906f306f206e906e006f306e006ea06f306ea06eb06f406eb06f506f406eb06ec06f506ec06f606f506ec06ed06f606ed06f706f606ed06ee06f706ee06f806f706ee06ef06f806ef06f906f806ef06f006f906f006fa06f906f006f106fa06f106fb06fa06f106f206fb06f206fc06fb06f206f306fc06f306fd06fc06f306ea06fd06ea06f406fd06f406f506fe06f506ff06fe06f506f606ff06f6060007ff06f606f7060007f70601070007f706f8060107f80602070107f806f9060207f90603070207f906fa060307fa0604070307fa06fb060407fb0605070407fb06fc060507fc0606070507fc06fd060607fd0607070607fd06f4060707f406fe060707fe06ff060807ff0609070807ff060007090700070a070907000701070a0701070b070a07010702070b0702070c070b07020703070c0703070d070c07030704070d0704070e070d07040705070e0705070f070e07050706070f07060710070f070607070710070707110710070707fe061107fe060807110708070907120709071307120709070a0713070a07140713070a070b0714070b07150714070b070c0715070c07160715070c070d0716070d07170716070d070e0717070e07180717070e070f0718070f07190718070f071007190710071a071907100711071a0711071b071a07110708071b07080712071b07120713071c0713071d071c07130714071d0714071e071d07140715071e0715071f071e07150716071f07160720071f0716071707200717072107200717071807210718072207210718071907220719072307220719071a0723071a07240723071a071b0724071b07250724071b071207250712071c0725071c071d0726071d07270726071d071e0727071e07280727071e071f0728071f07290728071f072007290720072a072907200721072a0721072b072a07210722072b0722072c072b07220723072c0723072d072c07230724072d0724072e072d07240725072e0725072f072e0725071c072f071c0726072f0726072707300727073107300727072807310728073207310728072907320729073307320729072a0733072a07340733072a072b0734072b07350734072b072c0735072c07360735072c072d0736072d07370736072d072e0737072e07380737072e072f0738072f07390738072f0726073907260730073907300731073a0731073b073a07310732073b0732073c073b07320733073c0733073d073c07330734073d0734073e073d07340735073e0735073f073e07350736073f07360740073f0736073707400737074107400737073807410738074207410738073907420739074307420739073007430730073a0743073a073b0744073b07450744073b073c0745073c07460745073c073d0746073d07470746073d073e0747073e07480747073e073f0748073f07490748073f074007490740074a074907400741074a0741074b074a07410742074b0742074c074b07420743074c0743074d074c0743073a074d073a0744074d07440745074e0745074f074e07450746074f07460750074f0746074707500747075107500747074807510748075207510748074907520749075307520749074a0753074a07540753074a074b0754074b07550754074b074c0755074c07560755074c074d0756074d07570756074d074407570744074e0757074e074f0758074f07590758074f075007590750075a075907500751075a0751075b075a07510752075b0752075c075b07520753075c0753075d075c07530754075d0754075e075d07540755075e0755075f075e07550756075f07560760075f0756075707600757076107600757074e0761074e075807610758075907620759076307620759075a0763075a07640763075a075b0764075b07650764075b075c0765075c07660765075c075d0766075d07670766075d075e0767075e07680767075e075f0768075f07690768075f076007690760076a076907600761076a0761076b076a07610758076b07580762076b07620763076c0763076d076c07630764076d0764076e076d07640765076e0765076f076e07650766076f07660770076f0766076707700767077107700767076807710768077207710768076907720769077307720769076a0773076a07740773076a076b0774076b07750774076b076207750762076c0775076c076d0776076d07770776076d076e0777076e07780777076e076f0778076f07790778076f077007790770077a077907700771077a0771077b077a07710772077b0772077c077b07720773077c0773077d077c07730774077d0774077e077d07740775077e0775077f077e0775076c077f076c0776077f0776077707800777078107800777077807810778078207810778077907820779078307820779077a0783077a07840783077a077b0784077b07850784077b077c0785077c07860785077c077d0786077d07870786077d077e0787077e07880787077e077f0788077f07890788077f0776078907760780078907800781078a0781078b078a07810782078b0782078c078b07820783078c0783078d078c07830784078d0784078e078d07840785078e0785078f078e07850786078f07860790078f0786078707900787079107900787078807910788079207910788078907920789079307920789078007930780078a0793078a078b0794078b07950794078b078c0795078c07960795078c078d0796078d07970796078d078e0797078e07980797078e078f0798078f07990798078f079007990790079a079907900791079a0791079b079a07910792079b0792079c079b07920793079c0793079d079c0793078a079d078a0794079d07940795079e0795079f079e07950796079f079607a0079f0796079707a0079707a107a00797079807a1079807a207a10798079907a2079907a307a20799079a07a3079a07a407a3079a079b07a4079b07a507a4079b079c07a5079c07a607a5079c079d07a6079d07a707a6079d079407a70794079e07a7079e079f07a8079f07a907a8079f07a007a907a007aa07a907a007a107aa07a107ab07aa07a107a207ab07a207ac07ab07a207a307ac07a307ad07ac07a307a407ad07a407ae07ad07a407a507ae07a507af07ae07a507a607af07a607b007af07a607a707b007a707b107b007a7079e07b1079e07a807b107a807a907b207a907b307b207a907aa07b307aa07b407b307aa07ab07b407ab07b507b407ab07ac07b507ac07b607b507ac07ad07b607ad07b707b607ad07ae07b707ae07b807b707ae07af07b807af07b907b807af07b007b907b007ba07b907b007b107ba07b107bb07ba07b107a807bb07a807b207bb07b207b307bc07b307bd07bc07b307b407bd07b407be07bd07b407b507be07b507bf07be07b507b607bf07b607c007bf07b607b707c007b707c107c007b707b807c107b807c207c107b807b907c207b907c307c207b907ba07c307ba07c407c307ba07bb07c407bb07c507c407bb07b207c507b207bc07c507bc07bd07c607bd07c707c607bd07be07c707be07c807c707be07bf07c807bf07c907c807bf07c007c907c007ca07c907c007c107ca07c107cb07ca07c107c207cb07c207cc07cb07c207c307cc07c307cd07cc07c307c407cd07c407ce07cd07c407c507ce07c507cf07ce07c507bc07cf07bc07c607cf07c607c707d007c707d107d007c707c807d107c807d207d107c807c907d207c907d307d207c907ca07d307ca07d407d307ca07cb07d407cb07d507d407cb07cc07d507cc07d607d507cc07cd07d607cd07d707d607cd07ce07d707ce07d807d707ce07cf07d807cf07d907d807cf07c607d907c607d007d907d007d107da07d107db07da07d107d207db07d207dc07db07d207d307dc07d307dd07dc07d307d407dd07d407de07dd07d407d507de07d507df07de07d507d607df07d607e007df07d607d707e007d707e107e007d707d807e107d807e207e107d807d907e207d907e307e207d907d007e307d007da07e307da07db07e407db07e507e407db07dc07e507dc07e607e507dc07dd07e607dd07e707e607dd07de07e707de07e807e707de07df07e807df07e907e807df07e007e907e007ea07e907e007e107ea07e107eb07ea07e107e207eb07e207ec07eb07e207e307ec07e307ed07ec07e307da07ed07da07e407ed07e407e507ee07e507ef07ee07e507e607ef07e607f007ef07e607e707f007e707f107f007e707e807f107e807f207f107e807e907f207e907f307f207e907ea07f307ea07f407f307ea07eb07f407eb07f507f407eb07ec07f507ec07f607f507ec07ed07f607ed07f707f607ed07e407f707e407ee07f707ee07ef07f807ef07f907f807ef07f007f907f007fa07f907f007f107fa07f107fb07fa07f107f207fb07f207fc07fb07f207f307fc07f307fd07fc07f307f407fd07f407fe07fd07f407f507fe07f507ff07fe07f507f607ff07f6070008ff07f607f7070008f70701080008f707ee070108ee07f8070108f807f9070208f90703080208f907fa070308fa0704080308fa07fb070408fb0705080408fb07fc070508fc0706080508fc07fd070608fd0707080608fd07fe070708fe0708080708fe07ff070808ff0709080808ff070008090800080a080908000801080a0801080b080a080108f8070b08f80702080b08020803080c0803080d080c08030804080d0804080e080d08040805080e0805080f080e08050806080f08060810080f0806080708100807081108100807080808110808081208110808080908120809081308120809080a0813080a08140813080a080b0814080b08150814080b080208150802080c0815080c080d0816080d08170816080d080e0817080e08180817080e080f0818080f08190818080f081008190810081a081908100811081a0811081b081a08110812081b0812081c081b08120813081c0813081d081c08130814081d0814081e081d08140815081e0815081f081e0815080c081f080c0816081f0816081708200817082108200817081808210818082208210818081908220819082308220819081a0823081a08240823081a081b0824081b08250824081b081c0825081c08260825081c081d0826081d08270826081d081e0827081e08280827081e081f0828081f08290828081f0816082908160820082908200821082a0821082b082a08210822082b0822082c082b08220823082c0823082d082c08230824082d0824082e082d08240825082e0825082f082e08250826082f08260830082f0826082708300827083108300827082808310828083208310828082908320829083308320829082008330820082a0833082a082b0834082b08350834082b082c0835082c08360835082c082d0836082d08370836082d082e0837082e08380837082e082f0838082f08390838082f083008390830083a083908300831083a0831083b083a08310832083b0832083c083b08320833083c0833083d083c0833082a083d082a0834083d08340835083e0835083f083e08350836083f08360840083f0836083708400837084108400837083808410838084208410838083908420839084308420839083a0843083a08440843083a083b0844083b08450844083b083c0845083c08460845083c083d0846083d08470846083d083408470834083e0847083e083f0848083f08490848083f084008490840084a084908400841084a0841084b084a08410842084b0842084c084b08420843084c0843084d084c08430844084d0844084e084d08440845084e0845084f084e08450846084f08460850084f0846084708500847085108500847083e0851083e084808510848084908520849085308520849084a0853084a08540853084a084b0854084b08550854084b084c0855084c08560855084c084d0856084d08570856084d084e0857084e08580857084e084f0858084f08590858084f085008590850085a085908500851085a0851085b085a08510848085b08480852085b08520853085c0853085d085c08530854085d0854085e085d08540855085e0855085f085e08550856085f08560860085f0856085708600857086108600857085808610858086208610858085908620859086308620859085a0863085a08640863085a085b0864085b08650864085b085208650852085c0865085c085d0866085d08670866085d085e0867085e08680867085e085f0868085f08690868085f086008690860086a086908600861086a0861086b086a08610862086b0862086c086b08620863086c0863086d086c08630864086d0864086e086d08640865086e0865086f086e0865085c086f085c0866086f0866086708700867087108700867086808710868087208710868086908720869087308720869086a0873086a08740873086a086b0874086b08750874086b086c0875086c08760875086c086d0876086d08770876086d086e0877086e08780877086e086f0878086f08790878086f0866087908660870087908700871087a0871087b087a08710872087b0872087c087b08720873087c0873087d087c08730874087d0874087e087d08740875087e0875087f087e08750876087f08760880087f0876087708800877088108800877087808810878088208810878087908820879088308820879087008830870087a0883087a087b0884087b08850884087b087c0885087c08860885087c087d0886087d08870886087d087e0887087e08880887087e087f0888087f08890888087f088008890880088a088908800881088a0881088b088a08810882088b0882088c088b08820883088c0883088d088c0883087a088d087a0884088d08840885088e0885088f088e08850886088f08860890088f0886088708900887089108900887088808910888089208910888088908920889089308920889088a0893088a08940893088a088b0894088b08950894088b088c0895088c08960895088c088d0896088d08970896088d088408970884088e0897088e088f0898088f08990898088f089008990890089a089908900891089a0891089b089a08910892089b0892089c089b08920893089c0893089d089c08930894089d0894089e089d08940895089e0895089f089e08950896089f089608a0089f0896089708a0089708a108a00897088e08a1088e089808a10898089908a2089908a308a20899089a08a3089a08a408a3089a089b08a4089b08a508a4089b089c08a5089c08a608a5089c089d08a6089d08a708a6089d089e08a7089e08a808a7089e089f08a8089f08a908a8089f08a008a908a008aa08a908a008a108aa08a108ab08aa08a1089808ab089808a208ab08a208a308ac08a308ad08ac08a308a408ad08a408ae08ad08a408a508ae08a508af08ae08a508a608af08a608b008af08a608a708b008a708b108b008a708a808b108a808b208b108a808a908b208a908b308b208a908aa08b308aa08b408b308aa08ab08b408ab08b508b408ab08a208b508a208ac08b508ac08ad08b608ad08b708b608ad08ae08b708ae08b808b708ae08af08b808af08b908b808af08b008b908b008ba08b908b008b108ba08b108bb08ba08b108b208bb08b208bc08bb08b208b308bc08b308bd08bc08b308b408bd08b408be08bd08b408b508be08b508bf08be08b508ac08bf08ac08b608bf08b608b708c008b708c108c008b708b808c108b808c208c108b808b908c208b908c308c208b908ba08c308ba08c408c308ba08bb08c408bb08c508c408bb08bc08c508bc08c608c508bc08bd08c608bd08c708c608bd08be08c708be08c808c708be08bf08c808bf08c908c808bf08b608c908b608c008c908c008c108ca08c108cb08ca08c108c208cb08c208cc08cb08c208c308cc08c308cd08cc08c308c408cd08c408ce08cd08c408c508ce08c508cf08ce08c508c608cf08c608d008cf08c608c708d008c708d108d008c708c808d108c808d208d108c808c908d208c908d308d208c908c008d308c008ca08d308ca08cb08d408cb08d508d408cb08cc08d508cc08d608d508cc08cd08d608cd08d708d608cd08ce08d708ce08d808d708ce08cf08d808cf08d908d808cf08d008d908d008da08d908d008d108da08d108db08da08d108d208db08d208dc08db08d208d308dc08d308dd08dc08d308ca08dd08ca08d408dd08d408d508de08d508df08de08d508d608df08d608e008df08d608d708e008d708e108e008d708d808e108d808e208e108d808d908e208d908e308e208d908da08e308da08e408e308da08db08e408db08e508e408db08dc08e508dc08e608e508dc08dd08e608dd08e708e608dd08d408e708d408de08e708de08df08e808df08e908e808df08e008e908e008ea08e908e008e108ea08e108eb08ea08e108e208eb08e208ec08eb08e208e308ec08e308ed08ec08e308e408ed08e408ee08ed08e408e508ee08e508ef08ee08e508e608ef08e608f008ef08e608e708f008e708f108f008e708de08f108de08e808f108e808e908f208e908f308f208e908ea08f308ea08f408f308ea08eb08f408eb08f508f408eb08ec08f508ec08f608f508ec08ed08f608ed08f708f608ed08ee08f708ee08f808f708ee08ef08f808ef08f908f808ef08f008f908f008fa08f908f008f108fa08f108fb08fa08f108e808fb08e808f208fb08f208f308fc08f308fd08fc08f308f408fd08f408fe08fd08f408f508fe08f508ff08fe08f508f608ff08f6080009ff08f608f7080009f70801090009f708f8080109f80802090109f808f9080209f90803090209f908fa080309fa0804090309fa08fb080409fb0805090409fb08f2080509f208fc080509fc08fd080609fd0807090609fd08fe080709fe0808090709fe08ff080809ff0809090809ff080009090900090a090909000901090a0901090b090a09010902090b0902090c090b09020903090c0903090d090c09030904090d0904090e090d09040905090e0905090f090e090509fc080f09fc0806090f0906090709100907091109100907090809110908091209110908090909120909091309120909090a0913090a09140913090a090b0914090b09150914090b090c0915090c09160915090c090d0916090d09170916090d090e0917090e09180917090e090f0918090f09190918090f0906091909060910091909100911091a0911091b091a09110912091b0912091c091b09120913091c0913091d091c09130914091d0914091e091d09140915091e0915091f091e09150916091f09160920091f0916091709200917092109200917091809210918092209210918091909220919092309220919091009230910091a0923091a091b0924091b09250924091b091c0925091c09260925091c091d0926091d09270926091d091e0927091e09280927091e091f0928091f09290928091f092009290920092a092909200921092a0921092b092a09210922092b0922092c092b09220923092c0923092d092c0923091a092d091a0924092d09240925092e0925092f092e09250926092f09260930092f0926092709300927093109300927092809310928093209310928092909320929093309320929092a0933092a09340933092a092b0934092b09350934092b092c0935092c09360935092c092d0936092d09370936092d092409370924092e0937092e092f0938092f09390938092f093009390930093a093909300931093a0931093b093a09310932093b0932093c093b09320933093c0933093d093c09330934093d0934093e093d09340935093e0935093f093e09350936093f09360940093f0936093709400937094109400937092e0941092e093809410938093909420939094309420939093a0943093a09440943093a093b0944093b09450944093b093c0945093c09460945093c093d0946093d09470946093d093e0947093e09480947093e093f0948093f09490948093f094009490940094a094909400941094a0941094b094a09410938094b09380942094b09420943094c0943094d094c09430944094d0944094e094d09440945094e0945094f094e09450946094f09460950094f0946094709500947095109500947094809510948095209510948094909520949095309520949094a0953094a09540953094a094b0954094b09550954094b094209550942094c0955094c094d0956094d09570956094d094e0957094e09580957094e094f0958094f09590958094f095009590950095a095909500951095a0951095b095a09510952095b0952095c095b09520953095c0953095d095c09530954095d0954095e095d09540955095e0955095f095e0955094c095f094c0956095f0956095709600957096109600957095809610958096209610958095909620959096309620959095a0963095a09640963095a095b0964095b09650964095b095c0965095c09660965095c095d0966095d09670966095d095e0967095e09680967095e095f0968095f09690968095f0956096909560960096909600961096a0961096b096a09610962096b0962096c096b09620963096c0963096d096c09630964096d0964096e096d09640965096e0965096f096e09650966096f09660970096f0966096709700967097109700967096809710968097209710968096909720969097309720969096009730960096a0973096a096b0974096b09750974096b096c0975096c09760975096c096d0976096d09770976096d096e0977096e09780977096e096f0978096f09790978096f097009790970097a097909700971097a0971097b097a09710972097b0972097c097b09720973097c0973097d097c0973096a097d096a0974097d09740975097e0975097f097e09750976097f09760980097f0976097709800977098109800977097809810978098209810978097909820979098309820979097a0983097a09840983097a097b0984097b09850984097b097c0985097c09860985097c097d0986097d09870986097d097409870974097e0987097e097f0988097f09890988097f098009890980098a098909800981098a0981098b098a09810982098b0982098c098b09820983098c0983098d098c09830984098d0984098e098d09840985098e0985098f098e09850986098f09860990098f0986098709900987099109900987097e0991097e098809910988098909920989099309920989098a0993098a09940993098a098b0994098b09950994098b098c0995098c09960995098c098d0996098d09970996098d098e0997098e09980997098e098f0998098f09990998098f099009990990099a099909900991099a0991099b099a09910988099b09880992099b09920993099c0993099d099c09930994099d0994099e099d09940995099e0995099f099e09950996099f099609a0099f0996099709a0099709a109a00997099809a1099809a209a10998099909a2099909a309a20999099a09a3099a09a409a3099a099b09a4099b09a509a4099b099209a50992099c09a5099c099d09a6099d09a709a6099d099e09a7099e09a809a7099e099f09a8099f09a909a8099f09a009a909a009aa09a909a009a109aa09a109ab09aa09a109a209ab09a209ac09ab09a209a309ac09a309ad09ac09a309a409ad09a409ae09ad09a409a509ae09a509af09ae09a5099c09af099c09a609af09a609a709b009a709b109b009a709a809b109a809b209b109a809a909b209a909b309b209a909aa09b309aa09b409b309aa09ab09b409ab09b509b409ab09ac09b509ac09b609b509ac09ad09b609ad09b709b609ad09ae09b709ae09b809b709ae09af09b809af09b909b809af09a609b909a609b009b909b009b109ba09b109bb09ba09b109b209bb09b209bc09bb09b209b309bc09b309bd09bc09b309b409bd09b409be09bd09b409b509be09b509bf09be09b509b609bf09b609c009bf09b609b709c009b709c109c009b709b809c109b809c209c109b809b909c209b909c309c209b909b009c309b009ba09c309ba09bb09c409bb09c509c409bb09bc09c509bc09c609c509bc09bd09c609bd09c709c609bd09be09c709be09c809c709be09bf09c809bf09c909c809bf09c009c909c009ca09c909c009c109ca09c109cb09ca09c109c209cb09c209cc09cb09c209c309cc09c309cd09cc09c309ba09cd09ba09c409cd09c409c509ce09c509cf09ce09c509c609cf09c609d009cf09c609c709d009c709d109d009c709c809d109c809d209d109c809c909d209c909d309d209c909ca09d309ca09d409d309ca09cb09d409cb09d509d409cb09cc09d509cc09d609d509cc09cd09d609cd09d709d609cd09c409d709c409ce09d709ce09cf09d809cf09d909d809cf09d009d909d009da09d909d009d109da09d109db09da09d109d209db09d209dc09db09d209d309dc09d309dd09dc09d309d409dd09d409de09dd09d409d509de09d509df09de09d509d609df09d609e009df09d609d709e009d709e109e009d709ce09e109ce09d809e109d809d909e209d909e309e209d909da09e309da09e409e309da09db09e409db09e509e409db09dc09e509dc09e609e509dc09dd09e609dd09e709e609dd09de09e709de09e809e709de09df09e809df09e909e809df09e009e909e009ea09e909e009e109ea09e109eb09ea09e109d809eb09d809e209eb09e209e309ec09e309ed09ec09e309e409ed09e409ee09ed09e409e509ee09e509ef09ee09e509e609ef09e609f009ef09e609e709f009e709f109f009e709e809f109e809f209f109e809e909f209e909f309f209e909ea09f309ea09f409f309ea09eb09f409eb09f509f409eb09e209f509e209ec09f509ec09ed09f609ed09f709f609ed09ee09f709ee09f809f709ee09ef09f809ef09f909f809ef09f009f909f009fa09f909f009f109fa09f109fb09fa09f109f209fb09f209fc09fb09f209f309fc09f309fd09fc09f309f409fd09f409fe09fd09f409f509fe09f509ff09fe09f509ec09ff09ec09f609ff09f609f709000af709010a000af709f809010af809020a010af809f909020af909030a020af909fa09030afa09040a030afa09fb09040afb09050a040afb09fc09050afc09060a050afc09fd09060afd09070a060afd09fe09070afe09080a070afe09ff09080aff09090a080aff09f609090af609000a090a000a010a0a0a010a0b0a0a0a010a020a0b0a020a0c0a0b0a020a030a0c0a030a0d0a0c0a030a040a0d0a040a0e0a0d0a040a050a0e0a050a0f0a0e0a050a060a0f0a060a100a0f0a060a070a100a070a110a100a070a080a110a080a120a110a080a090a120a090a130a120a090a000a130a000a0a0a130a0a0a0b0a140a0b0a150a140a0b0a0c0a150a0c0a160a150a0c0a0d0a160a0d0a170a160a0d0a0e0a170a0e0a180a170a0e0a0f0a180a0f0a190a180a0f0a100a190a100a1a0a190a100a110a1a0a110a1b0a1a0a110a120a1b0a120a1c0a1b0a120a130a1c0a130a1d0a1c0a130a0a0a1d0a0a0a140a1d0a140a150a1e0a150a1f0a1e0a150a160a1f0a160a200a1f0a160a170a200a170a210a200a170a180a210a180a220a210a180a190a220a190a230a220a190a1a0a230a1a0a240a230a1a0a1b0a240a1b0a250a240a1b0a1c0a250a1c0a260a250a1c0a1d0a260a1d0a270a260a1d0a140a270a140a1e0a270a1e0a1f0a280a1f0a290a280a1f0a200a290a200a2a0a290a200a210a2a0a210a2b0a2a0a210a220a2b0a220a2c0a2b0a220a230a2c0a230a2d0a2c0a230a240a2d0a240a2e0a2d0a240a250a2e0a250a2f0a2e0a250a260a2f0a260a300a2f0a260a270a300a270a310a300a270a1e0a310a1e0a280a310a280a290a320a290a330a320a290a2a0a330a2a0a340a330a2a0a2b0a340a2b0a350a340a2b0a2c0a350a2c0a360a350a2c0a2d0a360a2d0a370a360a2d0a2e0a370a2e0a380a370a2e0a2f0a380a2f0a390a380a2f0a300a390a300a3a0a390a300a310a3a0a310a3b0a3a0a310a280a3b0a280a320a3b0a320a330a3c0a330a3d0a3c0a330a340a3d0a340a3e0a3d0a340a350a3e0a350a3f0a3e0a350a360a3f0a360a400a3f0a360a370a400a370a410a400a370a380a410a380a420a410a380a390a420a390a430a420a390a3a0a430a3a0a440a430a3a0a3b0a440a3b0a450a440a3b0a320a450a320a3c0a450a3c0a3d0a460a3d0a470a460a3d0a3e0a470a3e0a480a470a3e0a3f0a480a3f0a490a480a3f0a400a490a400a4a0a490a400a410a4a0a410a4b0a4a0a410a420a4b0a420a4c0a4b0a420a430a4c0a430a4d0a4c0a430a440a4d0a440a4e0a4d0a440a450a4e0a450a4f0a4e0a450a3c0a4f0a3c0a460a4f0a460a470a500a470a510a500a470a480a510a480a520a510a480a490a520a490a530a520a490a4a0a530a4a0a540a530a4a0a4b0a540a4b0a550a540a4b0a4c0a550a4c0a560a550a4c0a4d0a560a4d0a570a560a4d0a4e0a570a4e0a580a570a4e0a4f0a580a4f0a590a580a4f0a460a590a460a500a590a500a510a5a0a510a5b0a5a0a510a520a5b0a520a5c0a5b0a520a530a5c0a530a5d0a5c0a530a540a5d0a540a5e0a5d0a540a550a5e0a550a5f0a5e0a550a560a5f0a560a600a5f0a560a570a600a570a610a600a570a580a610a580a620a610a580a590a620a590a630a620a590a500a630a500a5a0a630a5a0a5b0a640a5b0a650a640a5b0a5c0a650a5c0a660a650a5c0a5d0a660a5d0a670a660a5d0a5e0a670a5e0a680a670a5e0a5f0a680a5f0a690a680a5f0a600a690a600a6a0a690a600a610a6a0a610a6b0a6a0a610a620a6b0a620a6c0a6b0a620a630a6c0a630a6d0a6c0a630a5a0a6d0a5a0a640a6d0a640a650a6e0a650a6f0a6e0a650a660a6f0a660a700a6f0a660a670a700a670a710a700a670a680a710a680a720a710a680a690a720a690a730a720a690a6a0a730a6a0a740a730a6a0a6b0a740a6b0a750a740a6b0a6c0a750a6c0a760a750a6c0a6d0a760a6d0a770a760a6d0a640a770a640a6e0a770a6e0a6f0a780a6f0a790a780a6f0a700a790a700a7a0a790a700a710a7a0a710a7b0a7a0a710a720a7b0a720a7c0a7b0a720a730a7c0a730a7d0a7c0a730a740a7d0a740a7e0a7d0a740a750a7e0a750a7f0a7e0a750a760a7f0a760a800a7f0a760a770a800a770a810a800a770a6e0a810a6e0a780a810a780a790a820a790a830a820a790a7a0a830a7a0a840a830a7a0a7b0a840a7b0a850a840a7b0a7c0a850a7c0a860a850a7c0a7d0a860a7d0a870a860a7d0a7e0a870a7e0a880a870a7e0a7f0a880a7f0a890a880a7f0a800a890a800a8a0a890a800a810a8a0a810a8b0a8a0a810a780a8b0a780a820a8b0a820a830a8c0a830a8d0a8c0a830a840a8d0a840a8e0a8d0a840a850a8e0a850a8f0a8e0a850a860a8f0a860a900a8f0a860a870a900a870a910a900a870a880a910a880a920a910a880a890a920a890a930a920a890a8a0a930a8a0a940a930a8a0a8b0a940a8b0a950a940a8b0a820a950a820a8c0a950a8c0a8d0a960a8d0a970a960a8d0a8e0a970a8e0a980a970a8e0a8f0a980a8f0a990a980a8f0a900a990a900a9a0a990a900a910a9a0a910a9b0a9a0a910a920a9b0a920a9c0a9b0a920a930a9c0a930a9d0a9c0a930a940a9d0a940a9e0a9d0a940a950a9e0a950a9f0a9e0a950a8c0a9f0a8c0a960a9f0a960a970aa00a970aa10aa00a970a980aa10a980aa20aa10a980a990aa20a990aa30aa20a990a9a0aa30a9a0aa40aa30a9a0a9b0aa40a9b0aa50aa40a9b0a9c0aa50a9c0aa60aa50a9c0a9d0aa60a9d0aa70aa60a9d0a9e0aa70a9e0aa80aa70a9e0a9f0aa80a9f0aa90aa80a9f0a960aa90a960aa00aa90aa00aa10aaa0aa10aab0aaa0aa10aa20aab0aa20aac0aab0aa20aa30aac0aa30aad0aac0aa30aa40aad0aa40aae0aad0aa40aa50aae0aa50aaf0aae0aa50aa60aaf0aa60ab00aaf0aa60aa70ab00aa70ab10ab00aa70aa80ab10aa80ab20ab10aa80aa90ab20aa90ab30ab20aa90aa00ab30aa00aaa0ab30aaa0aab0ab40aab0ab50ab40aab0aac0ab50aac0ab60ab50aac0aad0ab60aad0ab70ab60aad0aae0ab70aae0ab80ab70aae0aaf0ab80aaf0ab90ab80aaf0ab00ab90ab00aba0ab90ab00ab10aba0ab10abb0aba0ab10ab20abb0ab20abc0abb0ab20ab30abc0ab30abd0abc0ab30aaa0abd0aaa0ab40abd0ab40ab50abe0ab50abf0abe0ab50ab60abf0ab60ac00abf0ab60ab70ac00ab70ac10ac00ab70ab80ac10ab80ac20ac10ab80ab90ac20ab90ac30ac20ab90aba0ac30aba0ac40ac30aba0abb0ac40abb0ac50ac40abb0abc0ac50abc0ac60ac50abc0abd0ac60abd0ac70ac60abd0ab40ac70ab40abe0ac70abe0abf0ac80abf0ac90ac80abf0ac00ac90ac00aca0ac90ac00ac10aca0ac10acb0aca0ac10ac20acb0ac20acc0acb0ac20ac30acc0ac30acd0acc0ac30ac40acd0ac40ace0acd0ac40ac50ace0ac50acf0ace0ac50ac60acf0ac60ad00acf0ac60ac70ad00ac70ad10ad00ac70abe0ad10abe0ac80ad10ac80ac90ad20ac90ad30ad20ac90aca0ad30aca0ad40ad30aca0acb0ad40acb0ad50ad40acb0acc0ad50acc0ad60ad50acc0acd0ad60acd0ad70ad60acd0ace0ad70ace0ad80ad70ace0acf0ad80acf0ad90ad80acf0ad00ad90ad00ada0ad90ad00ad10ada0ad10adb0ada0ad10ac80adb0ac80ad20adb0ad20ad30adc0ad30add0adc0ad30ad40add0ad40ade0add0ad40ad50ade0ad50adf0ade0ad50ad60adf0ad60ae00adf0ad60ad70ae00ad70ae10ae00ad70ad80ae10ad80ae20ae10ad80ad90ae20ad90ae30ae20ad90ada0ae30ada0ae40ae30ada0adb0ae40adb0ae50ae40adb0ad20ae50ad20adc0ae50adc0add0ae60add0ae70ae60add0ade0ae70ade0ae80ae70ade0adf0ae80adf0ae90ae80adf0ae00ae90ae00aea0ae90ae00ae10aea0ae10aeb0aea0ae10ae20aeb0ae20aec0aeb0ae20ae30aec0ae30aed0aec0ae30ae40aed0ae40aee0aed0ae40ae50aee0ae50aef0aee0ae50adc0aef0adc0ae60aef0ae60ae70af00ae70af10af00ae70ae80af10ae80af20af10ae80ae90af20ae90af30af20ae90aea0af30aea0af40af30aea0aeb0af40aeb0af50af40aeb0aec0af50aec0af60af50aec0aed0af60aed0af70af60aed0aee0af70aee0af80af70aee0aef0af80aef0af90af80aef0ae60af90ae60af00af90af00af10afa0af10afb0afa0af10af20afb0af20afc0afb0af20af30afc0af30afd0afc0af30af40afd0af40afe0afd0af40af50afe0af50aff0afe0af50af60aff0af60a000bff0af60af70a000bf70a010b000bf70af80a010bf80a020b010bf80af90a020bf90a030b020bf90af00a030bf00afa0a030bfa0afb0a040bfb0a050b040bfb0afc0a050bfc0a060b050bfc0afd0a060bfd0a070b060bfd0afe0a070bfe0a080b070bfe0aff0a080bff0a090b080bff0a000b090b000b0a0b090b000b010b0a0b010b0b0b0a0b010b020b0b0b020b0c0b0b0b020b030b0c0b030b0d0b0c0b030bfa0a0d0bfa0a040b0d0b040b050b0e0b050b0f0b0e0b050b060b0f0b060b100b0f0b060b070b100b070b110b100b070b080b110b080b120b110b080b090b120b090b130b120b090b0a0b130b0a0b140b130b0a0b0b0b140b0b0b150b140b0b0b0c0b150b0c0b160b150b0c0b0d0b160b0d0b170b160b0d0b040b170b040b0e0b170b0e0b0f0b180b0f0b190b180b0f0b100b190b100b1a0b190b100b110b1a0b110b1b0b1a0b110b120b1b0b120b1c0b1b0b120b130b1c0b130b1d0b1c0b130b140b1d0b140b1e0b1d0b140b150b1e0b150b1f0b1e0b150b160b1f0b160b200b1f0b160b170b200b170b210b200b170b0e0b210b0e0b180b210b180b190b220b190b230b220b190b1a0b230b1a0b240b230b1a0b1b0b240b1b0b250b240b1b0b1c0b250b1c0b260b250b1c0b1d0b260b1d0b270b260b1d0b1e0b270b1e0b280b270b1e0b1f0b280b1f0b290b280b1f0b200b290b200b2a0b290b200b210b2a0b210b2b0b2a0b210b180b2b0b180b220b2b0b220b230b2c0b230b2d0b2c0b230b240b2d0b240b2e0b2d0b240b250b2e0b250b2f0b2e0b250b260b2f0b260b300b2f0b260b270b300b270b310b300b270b280b310b280b320b310b280b290b320b290b330b320b290b2a0b330b2a0b340b330b2a0b2b0b340b2b0b350b340b2b0b220b350b220b2c0b350b2c0b2d0b360b2d0b370b360b2d0b2e0b370b2e0b380b370b2e0b2f0b380b2f0b390b380b2f0b300b390b300b3a0b390b300b310b3a0b310b3b0b3a0b310b320b3b0b320b3c0b3b0b320b330b3c0b330b3d0b3c0b330b340b3d0b340b3e0b3d0b340b350b3e0b350b3f0b3e0b350b2c0b3f0b2c0b360b3f0b360b370b400b370b410b400b370b380b410b380b420b410b380b390b420b390b430b420b390b3a0b430b3a0b440b430b3a0b3b0b440b3b0b450b440b3b0b3c0b450b3c0b460b450b3c0b3d0b460b3d0b470b460b3d0b3e0b470b3e0b480b470b3e0b3f0b480b3f0b490b480b3f0b360b490b360b400b490b400b410b4a0b410b4b0b4a0b410b420b4b0b420b4c0b4b0b420b430b4c0b430b4d0b4c0b430b440b4d0b440b4e0b4d0b440b450b4e0b450b4f0b4e0b450b460b4f0b460b500b4f0b460b470b500b470b510b500b470b480b510b480b520b510b480b490b520b490b530b520b490b400b530b400b4a0b530b4a0b4b0b540b4b0b550b540b4b0b4c0b550b4c0b560b550b4c0b4d0b560b4d0b570b560b4d0b4e0b570b4e0b580b570b4e0b4f0b580b4f0b590b580b4f0b500b590b500b5a0b590b500b510b5a0b510b5b0b5a0b510b520b5b0b520b5c0b5b0b520b530b5c0b530b5d0b5c0b530b4a0b5d0b4a0b540b5d0b540b550b5e0b550b5f0b5e0b550b560b5f0b560b600b5f0b560b570b600b570b610b600b570b580b610b580b620b610b580b590b620b590b630b620b590b5a0b630b5a0b640b630b5a0b5b0b640b5b0b650b640b5b0b5c0b650b5c0b660b650b5c0b5d0b660b5d0b670b660b5d0b540b670b540b5e0b670b5e0b5f0b680b5f0b690b680b5f0b600b690b600b6a0b690b600b610b6a0b610b6b0b6a0b610b620b6b0b620b6c0b6b0b620b630b6c0b630b6d0b6c0b630b640b6d0b640b6e0b6d0b640b650b6e0b650b6f0b6e0b650b660b6f0b660b700b6f0b660b670b700b670b710b700b670b5e0b710b5e0b680b710b680b690b720b690b730b720b690b6a0b730b6a0b740b730b6a0b6b0b740b6b0b750b740b6b0b6c0b750b6c0b760b750b6c0b6d0b760b6d0b770b760b6d0b6e0b770b6e0b780b770b6e0b6f0b780b6f0b790b780b6f0b700b790b700b7a0b790b700b710b7a0b710b7b0b7a0b710b680b7b0b680b720b7b0b720b730b7c0b730b7d0b7c0b730b740b7d0b740b7e0b7d0b740b750b7e0b750b7f0b7e0b750b760b7f0b760b800b7f0b760b770b800b770b810b800b770b780b810b780b820b810b780b790b820b790b830b820b790b7a0b830b7a0b840b830b7a0b7b0b840b7b0b850b840b7b0b720b850b720b7c0b850b7c0b7d0b860b7d0b870b860b7d0b7e0b870b7e0b880b870b7e0b7f0b880b7f0b890b880b7f0b800b890b800b8a0b890b800b810b8a0b810b8b0b8a0b810b820b8b0b820b8c0b8b0b820b830b8c0b830b8d0b8c0b830b840b8d0b840b8e0b8d0b840b850b8e0b850b8f0b8e0b850b7c0b8f0b7c0b860b8f0b860b870b900b870b910b900b870b880b910b880b920b910b880b890b920b890b930b920b890b8a0b930b8a0b940b930b8a0b8b0b940b8b0b950b940b8b0b8c0b950b8c0b960b950b8c0b8d0b960b8d0b970b960b8d0b8e0b970b8e0b980b970b8e0b8f0b980b8f0b990b980b8f0b860b990b860b900b990b900b910b9a0b910b9b0b9a0b910b920b9b0b920b9c0b9b0b920b930b9c0b930b9d0b9c0b930b940b9d0b940b9e0b9d0b940b950b9e0b950b9f0b9e0b950b960b9f0b960ba00b9f0b960b970ba00b970ba10ba00b970b980ba10b980ba20ba10b980b990ba20b990ba30ba20b990b900ba30b900b9a0ba30b9a0b9b0ba40b9b0ba50ba40b9b0b9c0ba50b9c0ba60ba50b9c0b9d0ba60b9d0ba70ba60b9d0b9e0ba70b9e0ba80ba70b9e0b9f0ba80b9f0ba90ba80b9f0ba00ba90ba00baa0ba90ba00ba10baa0ba10bab0baa0ba10ba20bab0ba20bac0bab0ba20ba30bac0ba30bad0bac0ba30b9a0bad0b9a0ba40bad0ba40ba50bae0ba50baf0bae0ba50ba60baf0ba60bb00baf0ba60ba70bb00ba70bb10bb00ba70ba80bb10ba80bb20bb10ba80ba90bb20ba90bb30bb20ba90baa0bb30baa0bb40bb30baa0bab0bb40bab0bb50bb40bab0bac0bb50bac0bb60bb50bac0bad0bb60bad0bb70bb60bad0ba40bb70ba40bae0bb70bae0baf0bb80baf0bb90bb80baf0bb00bb90bb00bba0bb90bb00bb10bba0bb10bbb0bba0bb10bb20bbb0bb20bbc0bbb0bb20bb30bbc0bb30bbd0bbc0bb30bb40bbd0bb40bbe0bbd0bb40bb50bbe0bb50bbf0bbe0bb50bb60bbf0bb60bc00bbf0bb60bb70bc00bb70bc10bc00bb70bae0bc10bae0bb80bc10bb80bb90bc20bb90bc30bc20bb90bba0bc30bba0bc40bc30bba0bbb0bc40bbb0bc50bc40bbb0bbc0bc50bbc0bc60bc50bbc0bbd0bc60bbd0bc70bc60bbd0bbe0bc70bbe0bc80bc70bbe0bbf0bc80bbf0bc90bc80bbf0bc00bc90bc00bca0bc90bc00bc10bca0bc10bcb0bca0bc10bb80bcb0bb80bc20bcb0bc20bc30bcc0bc30bcd0bcc0bc30bc40bcd0bc40bce0bcd0bc40bc50bce0bc50bcf0bce0bc50bc60bcf0bc60bd00bcf0bc60bc70bd00bc70bd10bd00bc70bc80bd10bc80bd20bd10bc80bc90bd20bc90bd30bd20bc90bca0bd30bca0bd40bd30bca0bcb0bd40bcb0bd50bd40bcb0bc20bd50bc20bcc0bd50bcc0bcd0bd60bcd0bd70bd60bcd0bce0bd70bce0bd80bd70bce0bcf0bd80bcf0bd90bd80bcf0bd00bd90bd00bda0bd90bd00bd10bda0bd10bdb0bda0bd10bd20bdb0bd20bdc0bdb0bd20bd30bdc0bd30bdd0bdc0bd30bd40bdd0bd40bde0bdd0bd40bd50bde0bd50bdf0bde0bd50bcc0bdf0bcc0bd60bdf0bd60bd70be00bd70be10be00bd70bd80be10bd80be20be10bd80bd90be20bd90be30be20bd90bda0be30bda0be40be30bda0bdb0be40bdb0be50be40bdb0bdc0be50bdc0be60be50bdc0bdd0be60bdd0be70be60bdd0bde0be70bde0be80be70bde0bdf0be80bdf0be90be80bdf0bd60be90bd60be00be90be00be10bea0be10beb0bea0be10be20beb0be20bec0beb0be20be30bec0be30bed0bec0be30be40bed0be40bee0bed0be40be50bee0be50bef0bee0be50be60bef0be60bf00bef0be60be70bf00be70bf10bf00be70be80bf10be80bf20bf10be80be90bf20be90bf30bf20be90be00bf30be00bea0bf30bea0beb0bf40beb0bf50bf40beb0bec0bf50bec0bf60bf50bec0bed0bf60bed0bf70bf60bed0bee0bf70bee0bf80bf70bee0bef0bf80bef0bf90bf80bef0bf00bf90bf00bfa0bf90bf00bf10bfa0bf10bfb0bfa0bf10bf20bfb0bf20bfc0bfb0bf20bf30bfc0bf30bfd0bfc0bf30bea0bfd0bea0bf40bfd0bf40bf50bfe0bf50bff0bfe0bf50bf60bff0bf60b000cff0bf60bf70b000cf70b010c000cf70bf80b010cf80b020c010cf80bf90b020cf90b030c020cf90bfa0b030cfa0b040c030cfa0bfb0b040cfb0b050c040cfb0bfc0b050cfc0b060c050cfc0bfd0b060cfd0b070c060cfd0bf40b070cf40bfe0b070cfe0bff0b080cff0b090c080cff0b000c090c000c0a0c090c000c010c0a0c010c0b0c0a0c010c020c0b0c020c0c0c0b0c020c030c0c0c030c0d0c0c0c030c040c0d0c040c0e0c0d0c040c050c0e0c050c0f0c0e0c050c060c0f0c060c100c0f0c060c070c100c070c110c100c070cfe0b110cfe0b080c110c080c090c120c090c130c120c090c0a0c130c0a0c140c130c0a0c0b0c140c0b0c150c140c0b0c0c0c150c0c0c160c150c0c0c0d0c160c0d0c170c160c0d0c0e0c170c0e0c180c170c0e0c0f0c180c0f0c190c180c0f0c100c190c100c1a0c190c100c110c1a0c110c1b0c1a0c110c080c1b0c080c120c1b0c120c130c1c0c130c1d0c1c0c130c140c1d0c140c1e0c1d0c140c150c1e0c150c1f0c1e0c150c160c1f0c160c200c1f0c160c170c200c170c210c200c170c180c210c180c220c210c180c190c220c190c230c220c190c1a0c230c1a0c240c230c1a0c1b0c240c1b0c250c240c1b0c120c250c120c1c0c250c1c0c1d0c260c1d0c270c260c1d0c1e0c270c1e0c280c270c1e0c1f0c280c1f0c290c280c1f0c200c290c200c2a0c290c200c210c2a0c210c2b0c2a0c210c220c2b0c220c2c0c2b0c220c230c2c0c230c2d0c2c0c230c240c2d0c240c2e0c2d0c240c250c2e0c250c2f0c2e0c250c1c0c2f0c1c0c260c2f0c260c270c300c270c310c300c270c280c310c280c320c310c280c290c320c290c330c320c290c2a0c330c2a0c340c330c2a0c2b0c340c2b0c350c340c2b0c2c0c350c2c0c360c350c2c0c2d0c360c2d0c370c360c2d0c2e0c370c2e0c380c370c2e0c2f0c380c2f0c390c380c2f0c260c390c260c300c390c300c310c3a0c310c3b0c3a0c310c320c3b0c320c3c0c3b0c320c330c3c0c330c3d0c3c0c330c340c3d0c340c3e0c3d0c340c350c3e0c350c3f0c3e0c350c360c3f0c360c400c3f0c360c370c400c370c410c400c370c380c410c380c420c410c380c390c420c390c430c420c390c300c430c300c3a0c430c3a0c3b0c440c3b0c450c440c3b0c3c0c450c3c0c460c450c3c0c3d0c460c3d0c470c460c3d0c3e0c470c3e0c480c470c3e0c3f0c480c3f0c490c480c3f0c400c490c400c4a0c490c400c410c4a0c410c4b0c4a0c410c420c4b0c420c4c0c4b0c420c430c4c0c430c4d0c4c0c430c3a0c4d0c3a0c440c4d0c440c450c4e0c450c4f0c4e0c450c460c4f0c460c500c4f0c460c470c500c470c510c500c470c480c510c480c520c510c480c490c520c490c530c520c490c4a0c530c4a0c540c530c4a0c4b0c540c4b0c550c540c4b0c4c0c550c4c0c560c550c4c0c4d0c560c4d0c570c560c4d0c440c570c440c4e0c570c4e0c4f0c580c4f0c590c580c4f0c500c590c500c5a0c590c500c510c5a0c510c5b0c5a0c510c520c5b0c520c5c0c5b0c520c530c5c0c530c5d0c5c0c530c540c5d0c540c5e0c5d0c540c550c5e0c550c5f0c5e0c550c560c5f0c560c600c5f0c560c570c600c570c610c600c570c4e0c610c4e0c580c610c580c590c620c590c630c620c590c5a0c630c5a0c640c630c5a0c5b0c640c5b0c650c640c5b0c5c0c650c5c0c660c650c5c0c5d0c660c5d0c670c660c5d0c5e0c670c5e0c680c670c5e0c5f0c680c5f0c690c680c5f0c600c690c600c6a0c690c600c610c6a0c610c6b0c6a0c610c580c6b0c580c620c6b0c620c630c6c0c630c6d0c6c0c630c640c6d0c640c6e0c6d0c640c650c6e0c650c6f0c6e0c650c660c6f0c660c700c6f0c660c670c700c670c710c700c670c680c710c680c720c710c680c690c720c690c730c720c690c6a0c730c6a0c740c730c6a0c6b0c740c6b0c750c740c6b0c620c750c620c6c0c750c6c0c6d0c760c6d0c770c760c6d0c6e0c770c6e0c780c770c6e0c6f0c780c6f0c790c780c6f0c700c790c700c7a0c790c700c710c7a0c710c7b0c7a0c710c720c7b0c720c7c0c7b0c720c730c7c0c730c7d0c7c0c730c740c7d0c740c7e0c7d0c740c750c7e0c750c7f0c7e0c750c6c0c7f0c6c0c760c7f0c760c770c800c770c810c800c770c780c810c780c820c810c780c790c820c790c830c820c790c7a0c830c7a0c840c830c7a0c7b0c840c7b0c850c840c7b0c7c0c850c7c0c860c850c7c0c7d0c860c7d0c870c860c7d0c7e0c870c7e0c880c870c7e0c7f0c880c7f0c890c880c7f0c760c890c760c800c890c800c810c8a0c810c8b0c8a0c810c820c8b0c820c8c0c8b0c820c830c8c0c830c8d0c8c0c830c840c8d0c840c8e0c8d0c840c850c8e0c850c8f0c8e0c850c860c8f0c860c900c8f0c860c870c900c870c910c900c870c880c910c880c920c910c880c890c920c890c930c920c890c800c930c800c8a0c930c8a0c8b0c940c8b0c950c940c8b0c8c0c950c8c0c960c950c8c0c8d0c960c8d0c970c960c8d0c8e0c970c8e0c980c970c8e0c8f0c980c8f0c990c980c8f0c900c990c900c9a0c990c900c910c9a0c910c9b0c9a0c910c920c9b0c920c9c0c9b0c920c930c9c0c930c9d0c9c0c930c8a0c9d0c8a0c940c9d0c940c950c9e0c950c9f0c9e0c950c960c9f0c960ca00c9f0c960c970ca00c970ca10ca00c970c980ca10c980ca20ca10c980c990ca20c990ca30ca20c990c9a0ca30c9a0ca40ca30c9a0c9b0ca40c9b0ca50ca40c9b0c9c0ca50c9c0ca60ca50c9c0c9d0ca60c9d0ca70ca60c9d0c940ca70c940c9e0ca70c9e0c9f0ca80c9f0ca90ca80c9f0ca00ca90ca00caa0ca90ca00ca10caa0ca10cab0caa0ca10ca20cab0ca20cac0cab0ca20ca30cac0ca30cad0cac0ca30ca40cad0ca40cae0cad0ca40ca50cae0ca50caf0cae0ca50ca60caf0ca60cb00caf0ca60ca70cb00ca70cb10cb00ca70c9e0cb10c9e0ca80cb10ca80ca90cb20ca90cb30cb20ca90caa0cb30caa0cb40cb30caa0cab0cb40cab0cb50cb40cab0cac0cb50cac0cb60cb50cac0cad0cb60cad0cb70cb60cad0cae0cb70cae0cb80cb70cae0caf0cb80caf0cb90cb80caf0cb00cb90cb00cba0cb90cb00cb10cba0cb10cbb0cba0cb10ca80cbb0ca80cb20cbb0cb20cb30cbc0cb30cbd0cbc0cb30cb40cbd0cb40cbe0cbd0cb40cb50cbe0cb50cbf0cbe0cb50cb60cbf0cb60cc00cbf0cb60cb70cc00cb70cc10cc00cb70cb80cc10cb80cc20cc10cb80cb90cc20cb90cc30cc20cb90cba0cc30cba0cc40cc30cba0cbb0cc40cbb0cc50cc40cbb0cb20cc50cb20cbc0cc50cbc0cbd0cc60cbd0cc70cc60cbd0cbe0cc70cbe0cc80cc70cbe0cbf0cc80cbf0cc90cc80cbf0cc00cc90cc00cca0cc90cc00cc10cca0cc10ccb0cca0cc10cc20ccb0cc20ccc0ccb0cc20cc30ccc0cc30ccd0ccc0cc30cc40ccd0cc40cce0ccd0cc40cc50cce0cc50ccf0cce0cc50cbc0ccf0cbc0cc60ccf0cc60cc70cd00cc70cd10cd00cc70cc80cd10cc80cd20cd10cc80cc90cd20cc90cd30cd20cc90cca0cd30cca0cd40cd30cca0ccb0cd40ccb0cd50cd40ccb0ccc0cd50ccc0cd60cd50ccc0ccd0cd60ccd0cd70cd60ccd0cce0cd70cce0cd80cd70cce0ccf0cd80ccf0cd90cd80ccf0cc60cd90cc60cd00cd90cd00cd10cda0cd10cdb0cda0cd10cd20cdb0cd20cdc0cdb0cd20cd30cdc0cd30cdd0cdc0cd30cd40cdd0cd40cde0cdd0cd40cd50cde0cd50cdf0cde0cd50cd60cdf0cd60ce00cdf0cd60cd70ce00cd70ce10ce00cd70cd80ce10cd80ce20ce10cd80cd90ce20cd90ce30ce20cd90cd00ce30cd00cda0ce30cda0cdb0ce40cdb0ce50ce40cdb0cdc0ce50cdc0ce60ce50cdc0cdd0ce60cdd0ce70ce60cdd0cde0ce70cde0ce80ce70cde0cdf0ce80cdf0ce90ce80cdf0ce00ce90ce00cea0ce90ce00ce10cea0ce10ceb0cea0ce10ce20ceb0ce20cec0ceb0ce20ce30cec0ce30ced0cec0ce30cda0ced0cda0ce40ced0ce40ce50cee0ce50cef0cee0ce50ce60cef0ce60cf00cef0ce60ce70cf00ce70cf10cf00ce70ce80cf10ce80cf20cf10ce80ce90cf20ce90cf30cf20ce90cea0cf30cea0cf40cf30cea0ceb0cf40ceb0cf50cf40ceb0cec0cf50cec0cf60cf50cec0ced0cf60ced0cf70cf60ced0ce40cf70ce40cee0cf70cee0cef0cf80cef0cf90cf80cef0cf00cf90cf00cfa0cf90cf00cf10cfa0cf10cfb0cfa0cf10cf20cfb0cf20cfc0cfb0cf20cf30cfc0cf30cfd0cfc0cf30cf40cfd0cf40cfe0cfd0cf40cf50cfe0cf50cff0cfe0cf50cf60cff0cf60c000dff0cf60cf70c000df70c010d000df70cee0c010dee0cf80c010df80cf90c020df90c030d020df90cfa0c030dfa0c040d030dfa0cfb0c040dfb0c050d040dfb0cfc0c050dfc0c060d050dfc0cfd0c060dfd0c070d060dfd0cfe0c070dfe0c080d070dfe0cff0c080dff0c090d080dff0c000d090d000d0a0d090d000d010d0a0d010d0b0d0a0d010df80c0b0df80c020d0b0d020d030d0c0d030d0d0d0c0d030d040d0d0d040d0e0d0d0d040d050d0e0d050d0f0d0e0d050d060d0f0d060d100d0f0d060d070d100d070d110d100d070d080d110d080d120d110d080d090d120d090d130d120d090d0a0d130d0a0d140d130d0a0d0b0d140d0b0d150d140d0b0d020d150d020d0c0d150d0c0d0d0d160d0d0d170d160d0d0d0e0d170d0e0d180d170d0e0d0f0d180d0f0d190d180d0f0d100d190d100d1a0d190d100d110d1a0d110d1b0d1a0d110d120d1b0d120d1c0d1b0d120d130d1c0d130d1d0d1c0d130d140d1d0d140d1e0d1d0d140d150d1e0d150d1f0d1e0d150d0c0d1f0d0c0d160d1f0d160d170d200d170d210d200d170d180d210d180d220d210d180d190d220d190d230d220d190d1a0d230d1a0d240d230d1a0d1b0d240d1b0d250d240d1b0d1c0d250d1c0d260d250d1c0d1d0d260d1d0d270d260d1d0d1e0d270d1e0d280d270d1e0d1f0d280d1f0d290d280d1f0d160d290d160d200d290d200d210d2a0d210d2b0d2a0d210d220d2b0d220d2c0d2b0d220d230d2c0d230d2d0d2c0d230d240d2d0d240d2e0d2d0d240d250d2e0d250d2f0d2e0d250d260d2f0d260d300d2f0d260d270d300d270d310d300d270d280d310d280d320d310d280d290d320d290d330d320d290d200d330d200d2a0d330d2a0d2b0d340d2b0d350d340d2b0d2c0d350d2c0d360d350d2c0d2d0d360d2d0d370d360d2d0d2e0d370d2e0d380d370d2e0d2f0d380d2f0d390d380d2f0d300d390d300d3a0d390d300d310d3a0d310d3b0d3a0d310d320d3b0d320d3c0d3b0d320d330d3c0d330d3d0d3c0d330d2a0d3d0d2a0d340d3d0d340d350d3e0d350d3f0d3e0d350d360d3f0d360d400d3f0d360d370d400d370d410d400d370d380d410d380d420d410d380d390d420d390d430d420d390d3a0d430d3a0d440d430d3a0d3b0d440d3b0d450d440d3b0d3c0d450d3c0d460d450d3c0d3d0d460d3d0d470d460d3d0d340d470d340d3e0d470d3e0d3f0d480d3f0d490d480d3f0d400d490d400d4a0d490d400d410d4a0d410d4b0d4a0d410d420d4b0d420d4c0d4b0d420d430d4c0d430d4d0d4c0d430d440d4d0d440d4e0d4d0d440d450d4e0d450d4f0d4e0d450d460d4f0d460d500d4f0d460d470d500d470d510d500d470d3e0d510d3e0d480d510d480d490d520d490d530d520d490d4a0d530d4a0d540d530d4a0d4b0d540d4b0d550d540d4b0d4c0d550d4c0d560d550d4c0d4d0d560d4d0d570d560d4d0d4e0d570d4e0d580d570d4e0d4f0d580d4f0d590d580d4f0d500d590d500d5a0d590d500d510d5a0d510d5b0d5a0d510d480d5b0d480d520d5b0d520d530d5c0d530d5d0d5c0d530d540d5d0d540d5e0d5d0d540d550d5e0d550d5f0d5e0d550d560d5f0d560d600d5f0d560d570d600d570d610d600d570d580d610d580d620d610d580d590d620d590d630d620d590d5a0d630d5a0d640d630d5a0d5b0d640d5b0d650d640d5b0d520d650d520d5c0d650d5c0d5d0d660d5d0d670d660d5d0d5e0d670d5e0d680d670d5e0d5f0d680d5f0d690d680d5f0d600d690d600d6a0d690d600d610d6a0d610d6b0d6a0d610d620d6b0d620d6c0d6b0d620d630d6c0d630d6d0d6c0d630d640d6d0d640d6e0d6d0d640d650d6e0d650d6f0d6e0d650d5c0d6f0d5c0d660d6f0d660d670d700d670d710d700d670d680d710d680d720d710d680d690d720d690d730d720d690d6a0d730d6a0d740d730d6a0d6b0d740d6b0d750d740d6b0d6c0d750d6c0d760d750d6c0d6d0d760d6d0d770d760d6d0d6e0d770d6e0d780d770d6e0d6f0d780d6f0d790d780d6f0d660d790d660d700d790d700d710d7a0d710d7b0d7a0d710d720d7b0d720d7c0d7b0d720d730d7c0d730d7d0d7c0d730d740d7d0d740d7e0d7d0d740d750d7e0d750d7f0d7e0d750d760d7f0d760d800d7f0d760d770d800d770d810d800d770d780d810d780d820d810d780d790d820d790d830d820d790d700d830d700d7a0d830d7a0d7b0d840d7b0d850d840d7b0d7c0d850d7c0d860d850d7c0d7d0d860d7d0d870d860d7d0d7e0d870d7e0d880d870d7e0d7f0d880d7f0d890d880d7f0d800d890d800d8a0d890d800d810d8a0d810d8b0d8a0d810d820d8b0d820d8c0d8b0d820d830d8c0d830d8d0d8c0d830d7a0d8d0d7a0d840d8d0d840d850d8e0d850d8f0d8e0d850d860d8f0d860d900d8f0d860d870d900d870d910d900d870d880d910d880d920d910d880d890d920d890d930d920d890d8a0d930d8a0d940d930d8a0d8b0d940d8b0d950d940d8b0d8c0d950d8c0d960d950d8c0d8d0d960d8d0d970d960d8d0d840d970d840d8e0d970d8e0d8f0d980d8f0d990d980d8f0d900d990d900d9a0d990d900d910d9a0d910d9b0d9a0d910d920d9b0d920d9c0d9b0d920d930d9c0d930d9d0d9c0d930d940d9d0d940d9e0d9d0d940d950d9e0d950d9f0d9e0d950d960d9f0d960da00d9f0d960d970da00d970da10da00d970d8e0da10d8e0d980da10d980d990da20d990da30da20d990d9a0da30d9a0da40da30d9a0d9b0da40d9b0da50da40d9b0d9c0da50d9c0da60da50d9c0d9d0da60d9d0da70da60d9d0d9e0da70d9e0da80da70d9e0d9f0da80d9f0da90da80d9f0da00da90da00daa0da90da00da10daa0da10dab0daa0da10d980dab0d980da20dab0da20da30dac0da30dad0dac0da30da40dad0da40dae0dad0da40da50dae0da50daf0dae0da50da60daf0da60db00daf0da60da70db00da70db10db00da70da80db10da80db20db10da80da90db20da90db30db20da90daa0db30daa0db40db30daa0dab0db40dab0db50db40dab0da20db50da20dac0db50dac0dad0db60dad0db70db60dad0dae0db70dae0db80db70dae0daf0db80daf0db90db80daf0db00db90db00dba0db90db00db10dba0db10dbb0dba0db10db20dbb0db20dbc0dbb0db20db30dbc0db30dbd0dbc0db30db40dbd0db40dbe0dbd0db40db50dbe0db50dbf0dbe0db50dac0dbf0dac0db60dbf0db60db70dc00db70dc10dc00db70db80dc10db80dc20dc10db80db90dc20db90dc30dc20db90dba0dc30dba0dc40dc30dba0dbb0dc40dbb0dc50dc40dbb0dbc0dc50dbc0dc60dc50dbc0dbd0dc60dbd0dc70dc60dbd0dbe0dc70dbe0dc80dc70dbe0dbf0dc80dbf0dc90dc80dbf0db60dc90db60dc00dc90dc00dc10dca0dc10dcb0dca0dc10dc20dcb0dc20dcc0dcb0dc20dc30dcc0dc30dcd0dcc0dc30dc40dcd0dc40dce0dcd0dc40dc50dce0dc50dcf0dce0dc50dc60dcf0dc60dd00dcf0dc60dc70dd00dc70dd10dd00dc70dc80dd10dc80dd20dd10dc80dc90dd20dc90dd30dd20dc90dc00dd30dc00dca0dd30dca0dcb0dd40dcb0dd50dd40dcb0dcc0dd50dcc0dd60dd50dcc0dcd0dd60dcd0dd70dd60dcd0dce0dd70dce0dd80dd70dce0dcf0dd80dcf0dd90dd80dcf0dd00dd90dd00dda0dd90dd00dd10dda0dd10ddb0dda0dd10dd20ddb0dd20ddc0ddb0dd20dd30ddc0dd30ddd0ddc0dd30dca0ddd0dca0dd40ddd0dd40dd50dde0dd50ddf0dde0dd50dd60ddf0dd60de00ddf0dd60dd70de00dd70de10de00dd70dd80de10dd80de20de10dd80dd90de20dd90de30de20dd90dda0de30dda0de40de30dda0ddb0de40ddb0de50de40ddb0ddc0de50ddc0de60de50ddc0ddd0de60ddd0de70de60ddd0dd40de70dd40dde0de70dde0ddf0de80ddf0de90de80ddf0de00de90de00dea0de90de00de10dea0de10deb0dea0de10de20deb0de20dec0deb0de20de30dec0de30ded0dec0de30de40ded0de40dee0ded0de40de50dee0de50def0dee0de50de60def0de60df00def0de60de70df00de70df10df00de70dde0df10dde0de80df10de80de90df20de90df30df20de90dea0df30dea0df40df30dea0deb0df40deb0df50df40deb0dec0df50dec0df60df50dec0ded0df60ded0df70df60ded0dee0df70dee0df80df70dee0def0df80def0df90df80def0df00df90df00dfa0df90df00df10dfa0df10dfb0dfa0df10de80dfb0de80df20dfb0df20df30dfc0df30dfd0dfc0df30df40dfd0df40dfe0dfd0df40df50dfe0df50dff0dfe0df50df60dff0df60d000eff0df60df70d000ef70d010e000ef70df80d010ef80d020e010ef80df90d020ef90d030e020ef90dfa0d030efa0d040e030efa0dfb0d040efb0d050e040efb0df20d050ef20dfc0d050efc0dfd0d060efd0d070e060efd0dfe0d070efe0d080e070efe0dff0d080eff0d090e080eff0d000e090e000e0a0e090e000e010e0a0e010e0b0e0a0e010e020e0b0e020e0c0e0b0e020e030e0c0e030e0d0e0c0e030e040e0d0e040e0e0e0d0e040e050e0e0e050e0f0e0e0e050efc0d0f0efc0d060e0f0e060e070e100e070e110e100e070e080e110e080e120e110e080e090e120e090e130e120e090e0a0e130e0a0e140e130e0a0e0b0e140e0b0e150e140e0b0e0c0e150e0c0e160e150e0c0e0d0e160e0d0e170e160e0d0e0e0e170e0e0e180e170e0e0e0f0e180e0f0e190e180e0f0e060e190e060e100e190e100e110e1a0e110e1b0e1a0e110e120e1b0e120e1c0e1b0e120e130e1c0e130e1d0e1c0e130e140e1d0e140e1e0e1d0e140e150e1e0e150e1f0e1e0e150e160e1f0e160e200e1f0e160e170e200e170e210e200e170e180e210e180e220e210e180e190e220e190e230e220e190e100e230e100e1a0e230e1a0e1b0e240e1b0e250e240e1b0e1c0e250e1c0e260e250e1c0e1d0e260e1d0e270e260e1d0e1e0e270e1e0e280e270e1e0e1f0e280e1f0e290e280e1f0e200e290e200e2a0e290e200e210e2a0e210e2b0e2a0e210e220e2b0e220e2c0e2b0e220e230e2c0e230e2d0e2c0e230e1a0e2d0e1a0e240e2d0e240e250e2e0e250e2f0e2e0e250e260e2f0e260e300e2f0e260e270e300e270e310e300e270e280e310e280e320e310e280e290e320e290e330e320e290e2a0e330e2a0e340e330e2a0e2b0e340e2b0e350e340e2b0e2c0e350e2c0e360e350e2c0e2d0e360e2d0e370e360e2d0e240e370e240e2e0e370e2e0e2f0e380e2f0e390e380e2f0e300e390e300e3a0e390e300e310e3a0e310e3b0e3a0e310e320e3b0e320e3c0e3b0e320e330e3c0e330e3d0e3c0e330e340e3d0e340e3e0e3d0e340e350e3e0e350e3f0e3e0e350e360e3f0e360e400e3f0e360e370e400e370e410e400e370e2e0e410e2e0e380e410e380e390e420e390e430e420e390e3a0e430e3a0e440e430e3a0e3b0e440e3b0e450e440e3b0e3c0e450e3c0e460e450e3c0e3d0e460e3d0e470e460e3d0e3e0e470e3e0e480e470e3e0e3f0e480e3f0e490e480e3f0e400e490e400e4a0e490e400e410e4a0e410e4b0e4a0e410e380e4b0e380e420e4b0e420e430e4c0e430e4d0e4c0e430e440e4d0e440e4e0e4d0e440e450e4e0e450e4f0e4e0e450e460e4f0e460e500e4f0e460e470e500e470e510e500e470e480e510e480e520e510e480e490e520e490e530e520e490e4a0e530e4a0e540e530e4a0e4b0e540e4b0e550e540e4b0e420e550e420e4c0e550e4c0e4d0e560e4d0e570e560e4d0e4e0e570e4e0e580e570e4e0e4f0e580e4f0e590e580e4f0e500e590e500e5a0e590e500e510e5a0e510e5b0e5a0e510e520e5b0e520e5c0e5b0e520e530e5c0e530e5d0e5c0e530e540e5d0e540e5e0e5d0e540e550e5e0e550e5f0e5e0e550e4c0e5f0e4c0e560e5f0e560e570e600e570e610e600e570e580e610e580e620e610e580e590e620e590e630e620e590e5a0e630e5a0e640e630e5a0e5b0e640e5b0e650e640e5b0e5c0e650e5c0e660e650e5c0e5d0e660e5d0e670e660e5d0e5e0e670e5e0e680e670e5e0e5f0e680e5f0e690e680e5f0e560e690e560e600e690e600e610e6a0e610e6b0e6a0e610e620e6b0e620e6c0e6b0e620e630e6c0e630e6d0e6c0e630e640e6d0e640e6e0e6d0e640e650e6e0e650e6f0e6e0e650e660e6f0e660e700e6f0e660e670e700e670e710e700e670e680e710e680e720e710e680e690e720e690e730e720e690e600e730e600e6a0e730e6a0e6b0e740e6b0e750e740e6b0e6c0e750e6c0e760e750e6c0e6d0e760e6d0e770e760e6d0e6e0e770e6e0e780e770e6e0e6f0e780e6f0e790e780e6f0e700e790e700e7a0e790e700e710e7a0e710e7b0e7a0e710e720e7b0e720e7c0e7b0e720e730e7c0e730e7d0e7c0e730e6a0e7d0e6a0e740e7d0e740e750e7e0e750e7f0e7e0e750e760e7f0e760e800e7f0e760e770e800e770e810e800e770e780e810e780e820e810e780e790e820e790e830e820e790e7a0e830e7a0e840e830e7a0e7b0e840e7b0e850e840e7b0e7c0e850e7c0e860e850e7c0e7d0e860e7d0e870e860e7d0e740e870e740e7e0e870e7e0e7f0e880e7f0e890e880e7f0e800e890e800e8a0e890e800e810e8a0e810e8b0e8a0e810e820e8b0e820e8c0e8b0e820e830e8c0e830e8d0e8c0e830e840e8d0e840e8e0e8d0e840e850e8e0e850e8f0e8e0e850e860e8f0e860e900e8f0e860e870e900e870e910e900e870e7e0e910e7e0e880e910e880e890e920e890e930e920e890e8a0e930e8a0e940e930e8a0e8b0e940e8b0e950e940e8b0e8c0e950e8c0e960e950e8c0e8d0e960e8d0e970e960e8d0e8e0e970e8e0e980e970e8e0e8f0e980e8f0e990e980e8f0e900e990e900e9a0e990e900e910e9a0e910e9b0e9a0e910e880e9b0e880e920e9b0e920e930e9c0e930e9d0e9c0e930e940e9d0e940e9e0e9d0e940e950e9e0e950e9f0e9e0e950e960e9f0e960ea00e9f0e960e970ea00e970ea10ea00e970e980ea10e980ea20ea10e980e990ea20e990ea30ea20e990e9a0ea30e9a0ea40ea30e9a0e9b0ea40e9b0ea50ea40e9b0e920ea50e920e9c0ea50e9c0e9d0ea60e9d0ea70ea60e9d0e9e0ea70e9e0ea80ea70e9e0e9f0ea80e9f0ea90ea80e9f0ea00ea90ea00eaa0ea90ea00ea10eaa0ea10eab0eaa0ea10ea20eab0ea20eac0eab0ea20ea30eac0ea30ead0eac0ea30ea40ead0ea40eae0ead0ea40ea50eae0ea50eaf0eae0ea50e9c0eaf0e9c0ea60eaf0ea60ea70eb00ea70eb10eb00ea70ea80eb10ea80eb20eb10ea80ea90eb20ea90eb30eb20ea90eaa0eb30eaa0eb40eb30eaa0eab0eb40eab0eb50eb40eab0eac0eb50eac0eb60eb50eac0ead0eb60ead0eb70eb60ead0eae0eb70eae0eb80eb70eae0eaf0eb80eaf0eb90eb80eaf0ea60eb90ea60eb00eb90eb00eb10eba0eb10ebb0eba0eb10eb20ebb0eb20ebc0ebb0eb20eb30ebc0eb30ebd0ebc0eb30eb40ebd0eb40ebe0ebd0eb40eb50ebe0eb50ebf0ebe0eb50eb60ebf0eb60ec00ebf0eb60eb70ec00eb70ec10ec00eb70eb80ec10eb80ec20ec10eb80eb90ec20eb90ec30ec20eb90eb00ec30eb00eba0ec30eba0ebb0ec40ebb0ec50ec40ebb0ebc0ec50ebc0ec60ec50ebc0ebd0ec60ebd0ec70ec60ebd0ebe0ec70ebe0ec80ec70ebe0ebf0ec80ebf0ec90ec80ebf0ec00ec90ec00eca0ec90ec00ec10eca0ec10ecb0eca0ec10ec20ecb0ec20ecc0ecb0ec20ec30ecc0ec30ecd0ecc0ec30eba0ecd0eba0ec40ecd0ec40ec50ece0ec50ecf0ece0ec50ec60ecf0ec60ed00ecf0ec60ec70ed00ec70ed10ed00ec70ec80ed10ec80ed20ed10ec80ec90ed20ec90ed30ed20ec90eca0ed30eca0ed40ed30eca0ecb0ed40ecb0ed50ed40ecb0ecc0ed50ecc0ed60ed50ecc0ecd0ed60ecd0ed70ed60ecd0ec40ed70ec40ece0ed70ece0ecf0ed80ecf0ed90ed80ecf0ed00ed90ed00eda0ed90ed00ed10eda0ed10edb0eda0ed10ed20edb0ed20edc0edb0ed20ed30edc0ed30edd0edc0ed30ed40edd0ed40ede0edd0ed40ed50ede0ed50edf0ede0ed50ed60edf0ed60ee00edf0ed60ed70ee00ed70ee10ee00ed70ece0ee10ece0ed80ee10ed80ed90ee20ed90ee30ee20ed90eda0ee30eda0ee40ee30eda0edb0ee40edb0ee50ee40edb0edc0ee50edc0ee60ee50edc0edd0ee60edd0ee70ee60edd0ede0ee70ede0ee80ee70ede0edf0ee80edf0ee90ee80edf0ee00ee90ee00eea0ee90ee00ee10eea0ee10eeb0eea0ee10ed80eeb0ed80ee20eeb0ee20ee30eec0ee30eed0eec0ee30ee40eed0ee40eee0eed0ee40ee50eee0ee50eef0eee0ee50ee60eef0ee60ef00eef0ee60ee70ef00ee70ef10ef00ee70ee80ef10ee80ef20ef10ee80ee90ef20ee90ef30ef20ee90eea0ef30eea0ef40ef30eea0eeb0ef40eeb0ef50ef40eeb0ee20ef50ee20eec0ef50eec0eed0ef60eed0ef70ef60eed0eee0ef70eee0ef80ef70eee0eef0ef80eef0ef90ef80eef0ef00ef90ef00efa0ef90ef00ef10efa0ef10efb0efa0ef10ef20efb0ef20efc0efb0ef20ef30efc0ef30efd0efc0ef30ef40efd0ef40efe0efd0ef40ef50efe0ef50eff0efe0ef50eec0eff0eec0ef60eff0ef60ef70e000ff70e010f000ff70ef80e010ff80e020f010ff80ef90e020ff90e030f020ff90efa0e030ffa0e040f030ffa0efb0e040ffb0e050f040ffb0efc0e050ffc0e060f050ffc0efd0e060ffd0e070f060ffd0efe0e070ffe0e080f070ffe0eff0e080fff0e090f080fff0ef60e090ff60e000f090f000f010f0a0f010f0b0f0a0f010f020f0b0f020f0c0f0b0f020f030f0c0f030f0d0f0c0f030f040f0d0f040f0e0f0d0f040f050f0e0f050f0f0f0e0f050f060f0f0f060f100f0f0f060f070f100f070f110f100f070f080f110f080f120f110f080f090f120f090f130f120f090f000f130f000f0a0f130f0a0f0b0f140f0b0f150f140f0b0f0c0f150f0c0f160f150f0c0f0d0f160f0d0f170f160f0d0f0e0f170f0e0f180f170f0e0f0f0f180f0f0f190f180f0f0f100f190f100f1a0f190f100f110f1a0f110f1b0f1a0f110f120f1b0f120f1c0f1b0f120f130f1c0f130f1d0f1c0f130f0a0f1d0f0a0f140f1d0f140f150f1e0f150f1f0f1e0f150f160f1f0f160f200f1f0f160f170f200f170f210f200f170f180f210f180f220f210f180f190f220f190f230f220f190f1a0f230f1a0f240f230f1a0f1b0f240f1b0f250f240f1b0f1c0f250f1c0f260f250f1c0f1d0f260f1d0f270f260f1d0f140f270f140f1e0f270f1e0f1f0f280f1f0f290f280f1f0f200f290f200f2a0f290f200f210f2a0f210f2b0f2a0f210f220f2b0f220f2c0f2b0f220f230f2c0f230f2d0f2c0f230f240f2d0f240f2e0f2d0f240f250f2e0f250f2f0f2e0f250f260f2f0f260f300f2f0f260f270f300f270f310f300f270f1e0f310f1e0f280f310f280f290f320f290f330f320f290f2a0f330f2a0f340f330f2a0f2b0f340f2b0f350f340f2b0f2c0f350f2c0f360f350f2c0f2d0f360f2d0f370f360f2d0f2e0f370f2e0f380f370f2e0f2f0f380f2f0f390f380f2f0f300f390f300f3a0f390f300f310f3a0f310f3b0f3a0f310f280f3b0f280f320f3b0f320f330f3c0f330f3d0f3c0f330f340f3d0f340f3e0f3d0f340f350f3e0f350f3f0f3e0f350f360f3f0f360f400f3f0f360f370f400f370f410f400f370f380f410f380f420f410f380f390f420f390f430f420f390f3a0f430f3a0f440f430f3a0f3b0f440f3b0f450f440f3b0f320f450f320f3c0f450f3c0f3d0f460f3d0f470f460f3d0f3e0f470f3e0f480f470f3e0f3f0f480f3f0f490f480f3f0f400f490f400f4a0f490f400f410f4a0f410f4b0f4a0f410f420f4b0f420f4c0f4b0f420f430f4c0f430f4d0f4c0f430f440f4d0f440f4e0f4d0f440f450f4e0f450f4f0f4e0f450f3c0f4f0f3c0f460f4f0f460f470f500f470f510f500f470f480f510f480f520f510f480f490f520f490f530f520f490f4a0f530f4a0f540f530f4a0f4b0f540f4b0f550f540f4b0f4c0f550f4c0f560f550f4c0f4d0f560f4d0f570f560f4d0f4e0f570f4e0f580f570f4e0f4f0f580f4f0f590f580f4f0f460f590f460f500f590f500f510f5a0f510f5b0f5a0f510f520f5b0f520f5c0f5b0f520f530f5c0f530f5d0f5c0f530f540f5d0f540f5e0f5d0f540f550f5e0f550f5f0f5e0f550f560f5f0f560f600f5f0f560f570f600f570f610f600f570f580f610f580f620f610f580f590f620f590f630f620f590f500f630f500f5a0f630f5a0f5b0f640f5b0f650f640f5b0f5c0f650f5c0f660f650f5c0f5d0f660f5d0f670f660f5d0f5e0f670f5e0f680f670f5e0f5f0f680f5f0f690f680f5f0f600f690f600f6a0f690f600f610f6a0f610f6b0f6a0f610f620f6b0f620f6c0f6b0f620f630f6c0f630f6d0f6c0f630f5a0f6d0f5a0f640f6d0f640f650f6e0f650f6f0f6e0f650f660f6f0f660f700f6f0f660f670f700f670f710f700f670f680f710f680f720f710f680f690f720f690f730f720f690f6a0f730f6a0f740f730f6a0f6b0f740f6b0f750f740f6b0f6c0f750f6c0f760f750f6c0f6d0f760f6d0f770f760f6d0f640f770f640f6e0f770f6e0f6f0f780f6f0f790f780f6f0f700f790f700f7a0f790f700f710f7a0f710f7b0f7a0f710f720f7b0f720f7c0f7b0f720f730f7c0f730f7d0f7c0f730f740f7d0f740f7e0f7d0f740f750f7e0f750f7f0f7e0f750f760f7f0f760f800f7f0f760f770f800f770f810f800f770f6e0f810f6e0f780f810f780f790f820f790f830f820f790f7a0f830f7a0f840f830f7a0f7b0f840f7b0f850f840f7b0f7c0f850f7c0f860f850f7c0f7d0f860f7d0f870f860f7d0f7e0f870f7e0f880f870f7e0f7f0f880f7f0f890f880f7f0f800f890f800f8a0f890f800f810f8a0f810f8b0f8a0f810f780f8b0f780f820f8b0f820f830f8c0f830f8d0f8c0f830f840f8d0f840f8e0f8d0f840f850f8e0f850f8f0f8e0f850f860f8f0f860f900f8f0f860f870f900f870f910f900f870f880f910f880f920f910f880f890f920f890f930f920f890f8a0f930f8a0f940f930f8a0f8b0f940f8b0f950f940f8b0f820f950f820f8c0f950f8c0f8d0f960f8d0f970f960f8d0f8e0f970f8e0f980f970f8e0f8f0f980f8f0f990f980f8f0f900f990f900f9a0f990f900f910f9a0f910f9b0f9a0f910f920f9b0f920f9c0f9b0f920f930f9c0f930f9d0f9c0f930f940f9d0f940f9e0f9d0f940f950f9e0f950f9f0f9e0f950f8c0f9f0f8c0f960f9f0f960f970fa00f970fa10fa00f970f980fa10f980fa20fa10f980f990fa20f990fa30fa20f990f9a0fa30f9a0fa40fa30f9a0f9b0fa40f9b0fa50fa40f9b0f9c0fa50f9c0fa60fa50f9c0f9d0fa60f9d0fa70fa60f9d0f9e0fa70f9e0fa80fa70f9e0f9f0fa80f9f0fa90fa80f9f0f960fa90f960fa00fa90fa00fa10faa0fa10fab0faa0fa10fa20fab0fa20fac0fab0fa20fa30fac0fa30fad0fac0fa30fa40fad0fa40fae0fad0fa40fa50fae0fa50faf0fae0fa50fa60faf0fa60fb00faf0fa60fa70fb00fa70fb10fb00fa70fa80fb10fa80fb20fb10fa80fa90fb20fa90fb30fb20fa90fa00fb30fa00faa0fb30faa0fab0fb40fab0fb50fb40fab0fac0fb50fac0fb60fb50fac0fad0fb60fad0fb70fb60fad0fae0fb70fae0fb80fb70fae0faf0fb80faf0fb90fb80faf0fb00fb90fb00fba0fb90fb00fb10fba0fb10fbb0fba0fb10fb20fbb0fb20fbc0fbb0fb20fb30fbc0fb30fbd0fbc0fb30faa0fbd0faa0fb40fbd0fb40fb50fbe0fb50fbf0fbe0fb50fb60fbf0fb60fc00fbf0fb60fb70fc00fb70fc10fc00fb70fb80fc10fb80fc20fc10fb80fb90fc20fb90fc30fc20fb90fba0fc30fba0fc40fc30fba0fbb0fc40fbb0fc50fc40fbb0fbc0fc50fbc0fc60fc50fbc0fbd0fc60fbd0fc70fc60fbd0fb40fc70fb40fbe0fc70fbe0fbf0fc80fbf0fc90fc80fbf0fc00fc90fc00fca0fc90fc00fc10fca0fc10fcb0fca0fc10fc20fcb0fc20fcc0fcb0fc20fc30fcc0fc30fcd0fcc0fc30fc40fcd0fc40fce0fcd0fc40fc50fce0fc50fcf0fce0fc50fc60fcf0fc60fd00fcf0fc60fc70fd00fc70fd10fd00fc70fbe0fd10fbe0fc80fd10fc80fc90fd20fc90fd30fd20fc90fca0fd30fca0fd40fd30fca0fcb0fd40fcb0fd50fd40fcb0fcc0fd50fcc0fd60fd50fcc0fcd0fd60fcd0fd70fd60fcd0fce0fd70fce0fd80fd70fce0fcf0fd80fcf0fd90fd80fcf0fd00fd90fd00fda0fd90fd00fd10fda0fd10fdb0fda0fd10fc80fdb0fc80fd20fdb0f + m_VertexData: + serializedVersion: 3 + m_VertexCount: 4060 + m_Channels: + - stream: 0 + offset: 0 + format: 0 + dimension: 3 + - stream: 0 + offset: 12 + format: 0 + dimension: 3 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + m_DataSize: 97440 + _typelessdata: 065c1bc0f579f33e2b78a14016829cbe2a4e65bfe64da53e328c19c012f2f33e0ba59e401ac107bee95274bfb1f788be446317c04ccb073f0b7d9c403ab4ba3d860426bf8d7741bf6fb415c044ec1d3f04d39b40a3648f3eaf53c2bd408d74bf432415c01fe8333fe7e79c402354b93e906ffd3eaa374abfd2e915c03659413ff2519f4097759c3ebc51653fe145a5bea6b917c0271d413f1125a24081a1073e5952743f8803893e94e219c0e44a333f114da4408bf2babda100263ff479413f69911bc0ec291d3f18f7a440bb718fbedd45c23d818b743f95211cc0102e073f35e2a340175fb9beb969fdbefe364a3fef6018c02d0dee3e79f0a1405ace90bee0c06ebf9274653eba9116c05488ee3e411d9f406a57c3bd711a6dbffec7babe5f6914c0ab17053f3df59c409f96033ecae110bf517a50bfdaba12c021391b3f474b9c40624b9b3e51212c3cf1ec73bf9c2a12c08934313f4b609d407e78b93ee33b153fb92f3abfbdef12c068a43e3f79ca9f4037cb903ed9c36e3fe54a65bef2be14c0d4663e3fb19da240133dc33db2166d3fc6dcba3e4ce716c05393303fb5c5a44041a003bea7db103f337e503fd29518c0dd711a3fab6fa540994b9bbed25b2cbce5ec733f102619c07576043fa75aa4406877b9be293715bfca333a3fda6515c064a0e83ec868a24008ca90becbc36ebfe34e653e449713c0961ee93e76959f40b73cc3bd4b176dbfbfd9babe7c6f11c00a64023f6e6d9d4072a4033ec7d910bf567f50bf46c10fc0fe85183f8ac39c408f4f9b3ed89a2e3c2aec73bff5300fc0f3802e3fafd89d400178b93ed643153f7a293abfa8f50fc09aef3b3f0043a04085c6903ed4c66e3f312565be3ec411c081b03b3f5216a3403c20c33d97136d3f60eeba3e06ec13c0c2db2d3f5a3ea540bbad03be94d3103f4783503f3c9a15c0ceb9173f3ee8a5407c4f9bbe1ad72ebc2bec733f8d2a16c0d8be013f19d3a4406377b9be123f15bf732d3a3fc46a12c09533e33e16e1a2407ec590bec4c66ebfd928653ecc9c10c0d3b4e33eac0da040ac1fc3bd25146dbf99ebbabe98750ec0d060ff3ea0e59d400fb2033ebad110bf648450bfb1c70cc0dbd2153fcd3b9d4072539b3e4517313c70eb73bf4d370cc05ecd2b3f13519e40b377b93ee04b153f1c233abf93fb0cc0ce3a393f88bba040d3c1903ecdc96e3f6bff64be8ac90ec02efa383ff28ea3407904c33d67106d3f5e00bb3ebef010c030242b3ffeb6a54075bb03be8dcb103f4e88503fa59e12c0bd01153fd160a640ad539bbe635031bc65eb733f092f13c0740efe3e8b4ba540fe76b9be134715bf20273a3fae6f0fc0c8c6dd3e6559a340f2c090beb3c96ebf5a03653e56a20dc0104bde3ee285a0406d04c3bdfc106dbf67fdbabeb57b0bc08df9f93ed15d9e40b6bf033ec0c910bf658950bf1ece09c0b91f133f10b49d40a5579b3ef38e333ca7ea73bfa63d09c0ca19293f77c99e401c77b93ed453153fe01c3abf7e010ac00286363f0f34a140f4bc903ec6cc6e3f0fda64bed6ce0bc0de43363f9207a440c7e6c23d520d6d3fef11bb3e77f50dc0a06c283fa32fa6403cc903be8dc3103f518d503f0ea30fc0ad49123f64d9a64089579bbe0fcc33bcaaea733f863310c0379ff83efdc3a540bf76b9be0a4f15bfce203a3f98740cc0f859d83eb3d1a3407cbc90bea4cc6ebf8cdd643edfa70ac04ce1d83e18fea040bce7c2bdcd0d6dbf650fbbbed18108c04992f43e03d69e404bcd033eadc110bf768e50bf89d406c0976c103f532c9e40cb5b9b3eed0b363ce2e973bffe4306c03766263fdb419f40f476b93ec75b153f88163abf680707c038d1333f97aca1404cb8903ebdcf6e3f60b464be21d408c08e8d333f3280a440bccac23d1f0a6d3fef23bb3e2ffa0ac010b5253f47a8a640efd603be81bb103f5c92503f77a70cc09d910f3ff751a740735b9bbe854936bcece9733f02380dc0fa2ff33e6f3ca6403a76b9be0a5715bf861a3a3f837909c026edd23e014aa44033b890be8bcf6ebfe9b7643e69ad07c08877d33e4e76a1409eccc2bd9f0a6dbf4e21bbbeee8705c0052bef3e344e9f4044db033e9ab910bf829350bff5da03c075b90d3f97a49e40fe5f9b3e9c83383c19e973bf574a03c0a4b2233f3fba9f405a76b93ebc63153f4c103abf530d04c06f1c313f1f25a2407eb3903eb3d26e3f188f64be6dd905c03ed7303fd2f8a440cdadc23dfb066d3fb935bb3ee8fe07c080fd223fec20a7404be403be6cb3103f7497503fe1ab09c08dd90c3f89caa740515f9bbe31c538bc31e9733f7f3c0ac0bac0ed3ee1b4a6402876b9beea5e15bf37143a3f6e7e06c05080cd3e50c2a440f0b390be76d26ebff791643ef2b204c0bf0dce3e84eea14049b0c2bd73076dbf3733bbbe0a8e02c0bec3e93e66c69f4050e9033e79b110bf989850bf60e100c053060b3fda1c9f40fc639b3e72fa3a3c57e873bfaf5000c012ff203fa332a0404076b93ea56b153ff9093abf3c1301c0a6672e3fa69da24001af903ea5d56e3f396964beb8de02c0ee202e3f7271a5404390c23dd9036d3f8747bb3ea00305c0ef45203f9099a740c8f303be5dab103f6b9c503f4ab006c07b210a3f1c43a84075649bbe86323bbc42e8733ffb4007c07851e83e532da7407c76b9bec66615bfd30d3a3f598303c07b13c83e9f3aa5401baf90be71d56ebf666c643e7cb801c0f6a3c83eba66a240fa93c2bd48046dbf1d45bbbe4f28ffbf795ce43e983ea0409cf7033e60a910bfa59d50bf98cffbbf3253083f1e959f402c689b3e66723d3c8ee773bf10aefabf814b1e3f08aba040db75b93e9873153faf033abf4e32fcbfdeb22b3f2f16a3401faa903ea0d86e3fb44364be08c8ffbfa16a2b3f14eaa540cd71c23dbb006d3f4859bb3e590802c0608e1d3f3612a840970104be3ba3103f83a1503fb4b403c06a69073fb0bba8404b689bbebaae3dbc86e7733f784504c035e2e23ec6a5a740eb75b9bec06e15bf90073a3f438800c0a4a6c23eedb2a540b0aa90be5ed86ebf9246643e0b7cfdbf2d3ac33ef0dea240ab77c2bd0f016dbf3d57bbbe8734f9bf34f5de3ecab6a0401904043e3ea110bfcaa250bf6edcf5bf11a0053f610da040536b9b3edaf13f3ceee673bfbfbaf4bff0971b3f6c23a140df74b93ea67b153f73fd39bf213ef6bf18fe283fb78ea3402ba5903e9bdb6e3f501e64be9dd2f9bf54b4283fb462a6405857c23d76fd6c3f8b6bbb3e211afebfd0d61a3fda8aa8404d0e04be1a9b103fa5a6503f1db900c059b1043f4334a940f56b9bbe7e2940bcd2e6733ff44901c0f272dd3e381ea8404775b9bec87615bf43013a3f5d1afbbfcb39bd3e3c2ba64031a690be4bdb6ebfe820643e1f87f7bf62d0bd3e2657a340315dc2bdd6fd6cbf3e69bbbec040f3bfed8dd93efb2ea140d410043e1d9910bfeca750bf44e9efbff0ec023fa485a040cf6f9b3e4170423c18e673bf6fc7eebf61e4183fd09ba140de74b93e8083153f23f739bff549f0bf5249263f3e07a4409da0903e89de6e3fbdf863be33ddf3bf07fe253f54dba6405839c23d56fa6c3f447dbb3e9223f8bf421f183f7f03a9407a1c04be0b93103faaab503f0e7bfbbf48f9013fd6aca940846f9bbe6da942bc1fe6733fe39cfcbfac03d83eaa96a840c274b9beb17e15bf09fb393f3324f5bfefccb73e8aa3a640d0a190be35de6ebf30fb633e3392f1bf9466b83e5bcfa3405841c2bda2fa6cbf467bbbbefa4cedbfa526d43e2ca7a1401620043ee99010bf02ad50bf1bf6e9bfd039003fe8fda0403e749b3e75ee443c43e573bf1fd4e8bfd230163f3514a2405e74b93e648b153fecf039bfc955eabf8e94233fc67fa440cb9b903e82e16e3f20d363bec9e7edbfbb47233ff553a740db1cc23d20f76c3f538fbb3e022df2bfb367153f237ca940d42904bec18a103fe2b0503fe183f5bf6b82fe3e6825aa408d739bbe1b2745bc5ce5733fdda5f6bf6594d23e1b0fa9408674b9be898615bfcaf4393f0a2eefbf1160b23ed81ba7404b9d90be26e16ebf60d5633e479debbfc4fcb23e9147a440a023c2bd6ff76cbf5e8dbbbe3359e7bf5cbfce3e5e1fa240812d043ec28810bf1eb250bff202e4bf5e0dfb3e2b76a14079779b3e286b473c9fe473bfcfe0e2bf447d133f998ca2409873b93e5a93153fb8ea39bf9c61e4bfcadf203f4ef8a4401e97903e74e46e3f86ad63be5ff2e7bf7091203f95cca740d1ffc13decf36c3f64a1bb3e7336ecbf24b0123fc8f4a940bc3804beb582103fdeb5503fb48cefbf4612f93efb9daa4031789bbec8a047bc7ee4733fd7aef0bf1b25cd3e8d87a9409274b9be678e15bf73ee393fe137e9bf33f3ac3e2694a740db9890be0fe46ebfb3af633e5ba8e5bff592ad3ec7bfa4407208c2bd35f46cbf699fbbbe6d65e1bf1458c93e8f97a2400c3b043e958010bf3eb750bfc80fdebf1fa7f53e6eeea140a37b9b3e04e3493cd6e373bf7eeddcbfb6c9103ffd04a3402c73b93e509b153f68e439bf6f6ddebf062b1e3fd670a5403e92903e6ce76e3f1f8863bef5fce1bf26db1d3f3545a84027e2c13dc4f06c3f4bb3bb3ee33fe6bf96f80f3f6d6daa405c4604be8a7a103ffbba503f8895e9bf21a2f33e8e16ab40117c9bbe281c4abcbfe3733fd2b7eabfd2b5c73effffa9403974b9be589615bf27e8393fb841e3bf5286a73e750ca840d2f58fbe50f86ebf02f9633e70b3dfbf2429a83efc37a54011a0c2bd38ec6cbff0bdbbbea671dbbfcbf0c33ec10fa340357b023e6d5c10bfdde150bf9f1cd8bfe040f03eb166a24012369a3e66f5563cc91674bf2efad6bf2a160e3f617da3405f42b83ea8ca153f0b0a3abf4279d8bf45761b3f5de9a54000ea8f3e46fe6e3fdbb263be8a07dcbfdc241b3fd6bda8401669c23dcae76c3fded7bb3e5449e0bf09410d3f11e6aa403a8802be8058103f11e4503f5b9ee3bffc31ee3e218fab4033369abe434055bcdb16743fccc0e4bf8646c23e7178aa40cd45b8bebdbd15bf98133a3fd247ddbf6a1ba23e9882a84066368fbe03116fbfd83c643e0abcd9bfa5c3a23eefada54084a1c4bd3ad96cbf4ffcbbbe897cd5bfbc8fbe3ea685a3402bbefe3dfc2410bfd22651bfb228d2bf92e1ea3eb1dca2403434983e9323693c556674bfe405d1bfb8650b3f99f3a3409990b63e2309163f95423abf3483d2bfa8c3183fd65fa6406d2c8f3e71186f3f4ad963befc0ed6bf8a6f183f7f34a9408c61c43d3ad16c3fd028bc3e7d4edabf7f890a3fc85cab405ae5febdec19103fb22d513f54a2ddbf27c1e83ebd05ac40ee3398be0f8568bc6966743f22c5debf49d7bc3ed5eeaa40a190b6be3afb15bfc44d3a3fd74bd7bf92b19c3e86f7a8400bf98ebe7c1c6fbf6716643e44c3d3bf8a609d3ea022a640076cc5bd9bcc6cbf9d2ebcbeac86cfbf2232b93e43faa3401f82fc3d050610bffa4651bf5634ccbf2786e53e6d51a3407b80973e76aa723caa8174bf0f11cbbf04b7083f9a68a440effcb53e6d28163f7c4d3abf198cccbf4312163f28d5a64040f08e3e14246f3f01ad63beac14d0bfc7ba153f0eaaa940ed27c53d0fc36c3f1363bc3e4451d4bffbd1073f6bd2ab40b7b1fcbd5bf60f3fdc50513f9aa3d7bff14fe33e417bac40cc8097bec13973bc9381743fe1c6d8bf0e68b73e1464ab4083fbb5be541c16bf96573a3fdf4fd1bfae47973e756ca940dff08ebe44226fbf03ca633e80cacdbf64fd973e5197a640b337c5bd20c66cbfa452bcbed090c9bf85d4b33ee16ea440adb7fc3dcff50fbf205151bffb3fc6bfbf2ae03e2ac6a3406988973eafa5773c1f8074bf391cc5bf5508063f9cdda44000fcb53e7c38163fc6403abffd94c6bfe460133f7b4aa74060e68e3e082a6f3fa66163be5c1acabf0906133f9f1faa4075edc43dbfbc6c3fa486bc3e0c54cebf791a053f0e48ac40d1e8fcbd2de60f3ff05a513fe1a4d1bfb7dedd3ec6f0ac40258997be342878bcf97f743fa3c8d2bfcaf8b13e54d9ab403cfbb5be512c16bfc44a3a3fe953cbbfbedd913e63e1a940bfe88ebe04286fbfa87d633ebdd1c7bf349a923e010ca7400e04c5bd95bf6cbfe976bcbef59ac3bfe376ae3e7ee3a440fbeafc3d78e50fbf625b51bf9f4bc0bf59cfda3ee63aa440fa8f973e87a77c3c9f7e74bf6227bfbfab59033f9e52a54081fab53e8248163f35343abfdf9dc0bf8aaf103fcdbfa7406bdc8e3efd2f6f3f461663be0b20c4bf4f51103f2f95aa405ab3c43d5eb66c3f74aabc3ed356c8bff862023fb1bdac40f11efdbdfad50f3f0e65513f29a6cbbf786dd83e4a66ad40979097bef1287dbc7f7e743f66caccbf7b89ac3e924eac40cdfab5be363c16bf0e3e3a3ff457c5bfc2738c3e5056aa40c7e08ebebe2d6fbf3231633efcd8c1bffa368d3eb180a740dfcec4bd0eb96cbf319bbcbe1ba5bdbf3e19a93e1b58a540d521fd3d24d50fbf936551bf4357babff973d53ea2afa4409f98973ed5ce803cf67c74bf8b32b9bf06ab003f9fc7a54011fab53e5f58163f81273abfc0a6babf37fe0d3f2035a840b9d28e3ee8356f3ffdca62beb825bebf9b9c0d3fbf0aab405577c43d01b06c3f47cebc3e9959c2bff256ff3e5433ad40b259fdbdccc50f3f0c6f513f71a7c5bf37fcd23ecedbad404c9997bebf1281bcd37c743f29ccc6bf221aa73ed1c3ac40e4fab5be024c16bf48313a3fff5bbfbfb809873e3fcbaa408dd88ebe84336fbface4623e3ae0bbbfb6d3873e61f5a740ba98c4bd77b26cbfc3bfbcbe3fafb7bf94bba33eb9cca540c759fd3d97c40fbfe06f51bfe662b4bf9b18d03e6024a540b9a0973eaa4e833c607b74bfb13db3bfcbf8fb3ea23ca640caf8b53e4468163ffa1a3abf9fafb4bfea4c0b3f73aaa840b9c88e3edf3b6f3f847f62be642bb8bfebe70a3f5180ab40823bc43d9ba96c3f45f2bc3e5f5cbcbff8e7f93ef9a8ad40b590fdbd3bb50f3f6279513fb8a8bfbff08acd3e5251ae4024a197be959583bc467b743fedcdc0bfbfaaa13e1039ad40d2fab5beca5b16bf8f243a3f0c60b9bfa49f813e2c40ab40a14b8ebee8476fbfc9ee623e7be7b5bf6a70823e116aa840d2f3c4bdfaa76cbf73eebcbe65b9b1bfe85d9e3e5641a640a191fa3dc79d0fbfe29751bf896eaebf42bdca3e1c99a5404597963e21f1893c77a374bfd748adbf959bf63ea4b1a64047fdb43ec996163fa9323abf7eb8aebfa39b083fc61fa940e93a8e3e52516f3fd07962be0f31b2bf4033083fe1f5ab408895c43d6f9e6c3f5824bd3e245fb6bf0279f43e9c1eae4011c8fabd5e8f0f3fbba0513f01aab9bfa719c83ed6c6ae40099896be0fbe89bc60a3743fb3cfbabf533b9c3e4eaead40c9ffb4be868616bf353f3a3f6761b3bf976f783e55b3ab406f6d8dbebb656fbf3123633ea8ecafbfb7207a3ef2dca840ebc3c6bdf5926cbf4939bdbe07c2abbf4004993e20b4a640272cf43d325d0fbf3ee251bfed78a8bf2c66c53e080ca6402676943eec86943c28f574bfa152a7bf4142f13edf24a7402525b33e18df163f3f6a3abf8bbfa8bfdfeb053f5f93a940b15f8d3e0e706f3f1f9762be4a34acbf977f053fc269ac403169c63d1d876c3f6e7abd3eeb5eb0bf4a0bef3e9492ae409364f4bdad4b0f3f2ded513f05a8b3bf5da9c23eac3aaf40267694be306294bc2ff5743f51ceb4bf48cd963ed521ae40d324b3bebccc16bf2d793a3f0760adbf67a46d3eb624ac40e5138dbe93756fbfa9f6623ebeefa9bf28676f3eff4da9408d02c8bdde806cbfb97ebdbe21c9a5bfacae933e1125a740c1c4f03de0300fbf331052bf1182a2bf6d13c03e1e7da6404266933ef1489b3c271d75bf0c5ba1bfe4eceb3e4d96a7403d47b23ec10a173f147c3abfc3c4a2bfa83d033f3605aa4021088d3ef47f6f3f9f6462be0c35a6bff8cc023feddbac400fa6c73d97736c3f04c7bd3ea95baabfd89ee93edb04af40b703f1bd411b0f3fcf1d523fb9a2adbf163abd3eceacaf40786693be5cab9bbc0e1d753fbec9aebfa060913e9f93ae405845b2be0bfa16bf0f8a3a3fa95ea7bf06d9623e1596ac40970a8dbeaa7c6fbf0396623ed6f2a3bf72ad643e0cbfa940d2c3c7bd8f786cbf4facbdbe3bd09fbf10598e3e0296a7403a04f13d6e1c0fbfff1c52bf338b9cbfb4c0ba3e34eea640da6f933e0c699e3c351b75bf76639bbf9897e63ebb07a8402446b23edf1e173f096c3abff9c99cbf7c8f003f0f77aa40a0fb8c3e5d876f3f230662becc35a0bf621a003f184ead40b55cc73d936b6c3fcbf3bd3e6758a4bf6c32e43e2277af402e49f1bdfb060f3f5e2a523f6f9da7bfc7cab73ef01eb040e97093be1ec89ebcfc1a753f2cc5a8bfe2f38b3e6905af406445b2be000e17bfe3793a3f4e5da1bf750d583e7507ad4077018dbeb6836fbf6e35623eeff59dbf95f3593e1930aa40f985c7bd2f706cbf1edabdbe56d799bf6c03893ef206a8406845f13dcc070fbfe22952bf559496bf036eb53e4a5fa7400a7a933e458ba13c2a1975bfdd6b95bf6142e13e2a79a8409e44b23ee132173f2c5c3abf2ccf96bfbac2fb3ee7e8aa40f8ee8c3ec98e6f3fb0a761be8b369abfaacffa3e43c0ad400112c73d8b636c3fa620be3e24559ebf08c6de3e6ae9af40748ef1bd7ef20e3f0f37523f2598a1bf715bb23e1291b040d27a93be88eaa1bcfc18753f9dc0a2bf1287863e3277af40c445b2bed32117bfbb693a3ff65b9bbfb6414d3ed478ad401ff88cbec78a6fbfe8d4613e0bf997bf93394f3e25a1aa405746c7bdc0676cbf4c08bebe72de93bfc1ad833ee377a8408e87f13dfef20ebfd73652bf769d90bf581bb03e60d0a7409983933edbada43c341775bf42748fbf3ceddb3e99eaa8400f43b23ece46173f5f4c3abf5cd490bf9166f63ec05aab4070e28c3e36966f3fd54861be473794bfa26af53e6f32ae4041c7c63d6c5b6c3fe74dbe3ee05198bfac59d93eb15bb040ded3f1bdc9dd0e3fe343523fdc929bbf13ecac3e3403b1403d8593be1909a5bce516753f10bc9cbf2f1a813efbe8af402446b2be843517bfab593a3f9f5a95bfc775423e32eaad40ea168cbec0a96fbfc8f7613e28fc91bf6a7f443e3112ab40c8f2c7bdae586cbfec47bebe8ee58dbf1eb07c3ed3e8a840c1e7ec3dbeb90ebfc17252bf95a68abfb6c8aa3e7641a840c8cf913e5085ae3cab5675bfa47c89bf2a98d63e085ca94079a9b03e2d8c173f82753abf89d98abf800af13e9accab408cff8b3e9db66f3f205761be00388ebfaf05f03e9ba4ae40cf70c73d784b6c3f0292be3e9a4e92bf56edd33ef9cdb0408e38edbd05a60e3fb57e523f938d95bfae7ca73e5675b14035d391be1e3aaebc3556753f84b796bf715a773ec45ab040e6aeb0beaf7517bf80863a3f16558fbf48b1373ea158ae4038568bbe5ac66fbfc7ee613ee1fb8bbf96d0393e4280ab400941cabdc03c6cbf87abbebe04ea87bff912723ec956a940b92de63d8c700ebf26c252bf74ad84bf9c7da53ea0afa840b0ad8f3eedeab93c11a575bfad8283bfd649d13ea0caa94039e6ae3ed6d6173f1aa33abfcedb84bf7cb3eb3eb13bac40ee468b3ef4d26f3f473e61be033588bfd5a3ea3e1014af4076cec93df02c6c3f6801bf3ee0468cbfa482ce3e893db140a57ee6bd9c570e3f9ed1523f70838fbf840ea23eb2e4b1401daf8fbef631babccfa4753f37ae90bf91846c3eb2c9b040a5e4aebed4c117bf8db43a3f8f4e89bf50ee2c3e5cc6ae40f12b8bbefcd26fbf6880613ecbfa85bf44242f3e9bedab401682cabdc02d6cbf7ff1bebed6ed81bf2479673e09c4a940351ee53de04b0ebf8adf52bf8b677dbf5434a03e181da940a84f8f3e9580bf3cbcb175bf41107bbf33fdcb3e8c38aa40df90ae3e93fa173ffd993abfb7ba7dbfc65de63e20aaac40441a8b3eebdf6f3faccf60be1f3182bfcc42e53ee182af405305ca3dbd1d6c3fe348bf3e143e86bf5c18c93e73acb1407871e5bdab310e3fccef523f247889bf99a09c3e6453b2405e508fbe24f2bfbc8bb1753fc9a38abf73af613ef037b140888faebef8e517bf15ab3a3f0b4883bf152b223e1834af402d208bbe51dc6fbf7cfe603e70f37fbfba77243ef65aac401a30cabd68226cbf002fbfbe54e377bf3bdf5c3e4a31aa402a74e53d25300ebfc7f052bf2d7471bf19eb9a3e918aa940ab5c8f3eacb4c33c02af75bf231b6fbfadb0c63e79a6aa40ad8eae3e5115183fb5843abfcabd71bf3408e13e9018ad4078098b3ec8e96f3fbd5060be705a78bfe1e1df3eb2f1af40f2a2c93dd5126c3f2b85bf3e463580bf21aec33e5e1bb2404bcde5bd21160e3fca00533fda6c83bfa532973e17c2b240175e8fbebd23c4bcb6ae753f5e9984bf1fda563e2fa6b1408690aebe5f0018bf59953a3f16837abf9467173ed2a1af40037a8abe7af66fbf3dda603e50f173bff8ca193e4fc8ac406d83cabdad126cbf2677bfbefeea6bbf3d45523e8a9eaa40a055e23d4efb0dbfd42153bfcc8065bfe8a1953e09f8a940002e8e3e917ccc3c2cd975bfff2563bf4364c13e6514ab401b6dad3ebe51183fef963abfd6c065bfc4b2db3e0087ad400b648a3e9c04703fb51e60be9c526cbf1281da3e8360b04079fcc93d57026c3f90d0bf3eee5874bff043be3e488ab24079a5e2bde6e10d3f9131533f20c37abfa6c4913ec930b3401e2e8ebed48cccbc25d9753fed1d7dbf94044c3e6d14b240606eadbecc3918bf2daa3a3f89706ebf5faa0c3e8c0db040fb9a89bed11670bf88d5603eb9ea67bf5d250f3ea033ad40d569ccbd56f86bbf9cd8bfbe1aef5fbfeeb2473ebc09ab40f5c5db3d0ab00dbf107053bf468a59bf905c903e7063aa4046028c3ea985d83c482676bf722d57bf701bbc3e4280ab40458fab3e02a2183fc1c33abfa5bf59bf8060d63e66f3ad408f8a893e2525703f870860be764560bf0223d53e52cdb04027f0cb3df6e56b3f043bc03e144168bf3adcb83e36f7b2402816dcbdcc930d3fad81533fe8a56ebf1f598c3e829db3400f028cbe66b3d8bc4526763fbc0271bf7c34413eb080b240f78cabbe398918bf86d83a3fa85962bffbf1013eb377b040604989bec42770bf7d7b603ea4e05bbf4285043e5a9dad403b75cdbdcae36bbfc92bc0be71f053bf9c263d3e5173ab4088d1d83d587e0dbf869d53bf49914dbf441a8b3e39cdaa4047138b3e110de03c764676bf39324bbf7fd5b63e82eaab4002c9aa3e40d2183fbbc93abf20bb4dbfcc10d13e355eae40ae37893e5f36703f45ac5fbe243454bf29c7cf3e8e38b14073efcc3dbed06b3f1f92c03e57245cbf7d76b33e9662b3405b2dd9bdcb5f0d3f78b0533f7f8362bf86ef863eaf08b44039148bbefe89e0bc3746763f8fe264bf9468363e66ebb240a1c6aabe53ba18bfd6dd3a3fce4256bf6c72ee3ddbe1b040313f89bee13070bf3ef85f3e96d64fbfb4c9f33d1307ae40df28cdbd2fd86bbfce69c0bec9f147bf2b9a323ee7dcab403f25d93d6f620dbfd4ae53bf489841bf05d8853e0237ab4011208b3e5842e43cb14376bff6363fbfb28fb13ec354ac40bfc6aa3e1bed183f44b43abf90b641bf47c1cb3e03c9ae407826893e3b40703fd82c5fbec82248bf756bca3ecba3b140f48bcc3dc9c56b3f60cec03e950750bfcd10ae3ef7cdb340be88d9bd25440d3f75c1533f166156bfdd85813edc73b440d0218bbeacbee4bc5443763f69c258bf5c9c2b3e1b56b3405dc8aabec8d418bfcdc73a3fff2b4abf2200d93d014cb14031df88be864370bf00a35f3e90cc43bf4688de3dcc70ae40b93dcdbd86c96bbf3fb0c0be24f33bbf9c0d283e7c46ac401581d73d68370dbf42d253bf439f35bfd595803ecba0ab407c7a8a3e4d2ceb3c635976bfaa3b33bf0e4aac3e04bfac40e621aa3e831b193fdbb33abff5b135bff271c63ed333af4062c0883eed53703f20d45ebe64113cbfe80fc53e080fb240ac8acc3d67b76b3fb414c13ed0ea43bf2caba83e5839b44049f3d7bdf3190d3f11e4533fb13e4abf4938783e09dfb440bd7e8abe1b6cebbcbb58763f4aa24cbfd7cf203ed0c0b340a927aabe260119bf26c83a3f07123ebfe692c33decb4b140c3f387be9f6670bf0b865f3ed6bf37bf0451c93d47d9ae40089acebd01b06bbfbf15c1be43f22fbf20881d3edcaeac407e9ed13dfbe90cbf621d54bf50a429bf08af763e6f09ac404470883e78baf73c219f76bf753e27bfd307a73e3028ad40534ea83e5570193f26d83abf2dab29bff624c13e989daf40e5d5873ec777703fefa65ebe5efd2fbf6eb5bf3e3d79b240d1e7cd3d9f9c6b3f2680c13ef1ca37bf9f45a33ea8a3b440e415d2bdcfcb0c3f932f543fe4183ebf55646d3e1549b5407e7688bebbc3f7bc419e763fbf7e40bfb603163e542ab440a854a8be605319bf80ee3a3fbcf131bf6630ae3d611bb240a87b87be3d7f70bfd8015f3eb2ad2bbf922eb43d483faf404c0bd0bd7d926bbfea8cc1bee9ec23bfe210133ed214ad40dc6ccd3d53a20cbf515d54bf82a51dbfcb416c3ec66fac40831c873e7248013d15cb76bf743d1bbf4fcca13e308fad408736a73e1eb5193f6cde3abf10a01dbf86dcbb3e4705b040ea64873ece8f703fcd1a5ebe1be423bffa5cba3e60e1b240f46ccf3d457d6b3faafec13ee2a42bbf2ee09d3ed60bb54007d8cdbd41800c3f3e72543f4aec31bf728f623ee2b0b540411e87be3b8c01bdb5ca763f585434bf9e380b3e7891b4409935a7be1d9a19bfd3f43a3f82d125bffecc983dd681b240a4f286be009a70bf2b805e3e9c9b1fbf640b9f3d4aa5af40941ad0bd777d6bbf1cf2c1be97e717bf8199083ec87aad401431cb3dba640cbfa68e54bfb3a611bfb4d4613e1ed6ac403538863ed33a063d97e776bf6a3c0fbffb909c3e31f6ad40e852a63e49f7193fbeda3abfe89411bf5094b63ef86cb04069d9863e4cab703f96915dbececa17bfb604b53e8449b3405176cf3dff676b3f3d65c23ed27e1fbfd07a983e0674b540f099cbbd3d430c3f30a3543fb8bf25bf6cba573eb018b6404c3986bee15d06bd5ce7763f002a28bf266d003e9df8b440bc53a6be07da19bfa3f23a3f3cad19bfd073833db6e6b240502f86be07bb70bf001d5e3e378613bfa0f3893db209b0403879d1bd965f6bbf736bc2be93df0bbf6350fc3d23dfad400aeec53d07130cbf5cd854bf73a505bfb16d573ed93aad40ae6f843ec3a70c3d9d2177bfbd3803bf8658973e965bae403ac2a43edf4b1a3fb4ed3abf8a8605bfb04eb13e10d3b0407719863e85cc703fe0215dbe8fad0bbfbcaeaf3e14b0b34046ddd03db1486b3fa8e4c23e335413bf8c17933ea3dab5405154c6bda8ef0b3f22ee543f538e19bf96e94c3eed7eb6404b7084be6fd10cbd7021773f09fb1bbf744ceb3d305eb5404fc0a4bee22d1abfdd063b3f5d850dbf2c475c3d2a4ab3404be185be62cf70bf02785d3ee06d07bfc4cb693dac6cb040a442d2bd8b456bbfdadbc2be4faaffbe9378e73d0c42ae40dd6fc33de9d40bbf630a55bf0c44f3be2a0c4d3e229ead400da0833e6d55113da23a77bf6a65eebeac22923e8abfae401c10a43e2b871a3fe1e33abf97eaf2be6c0bac3eba37b1407ac7853e66e1703f867c5cbe9119ffbeda5aaa3e3815b4402693d13d632e6b3f4b57c33e802507bf2eb68d3ed83fb64075e5c3bde5af0b3ffb20553fa2580dbf791c423ec2e3b640b6a183bee79e11bd3f3a773ff3c70fbf91c6d53d59c2b540850fa4beb1691abf5ffc3a3f8d5d01bf80a4313d9dadb340981c85be6af070bf5d135d3e2aabf6be84ae3f3da5cfb0402e9bd2bd652e6bbf8845c3be8295e7be6da0d23df4a4ae40bccebf3d328d0bbf934655bf263ddbbed0aa423e6b01ae40b63d823e5a2c173d086677bf3d59d6be0eed8c3e7f23af402bbba23e2ed61a3f01ed3abff7c7dabe71c8a63e659cb140d102853e1803713f260b5cbee6d7e6be3007a53e5d7ab44029f0d13d87166b3fd9c3c33e8eedf5bee654883e0ea5b640783bc0bdd8680b3fd15c553ff52201bf314f373e9748b7404a3e82bef84717bde465773fea9403bfc83fc03d8226b64017bba2bea9b51abff2073b3fb35feabe5422073daf0eb4406b6184be661071bffda65c3eee70debed0b4153d3730b1406d82d4bdd30c6bbf0bc6c3bed678cfbed0dabd3d7305af40ffceb93d09370bbf139455bf262fc3be9352383e4862ae40b74b803ea8d21d3de6a277bf6a45bebeadbb873e0a85af40a916a13e232c1b3fcb003bbf0b9cc2be4489a13ea9feb140a04b843e1123713f3b945bbed08acebef5b69f3e21ddb44035ded33d58f36a3f524bc43ee782ddbedcf6823ee507b740763ebabdb80f0b3f28ac553f98cce9be8a882c3e10abb7406a4c80be060e1ebda9a2773f54b6eebe84c7aa3d4e88b6403313a1befb0b1bbf321c3b3f0d00d2be8054b93ce16eb440be2d84bed02171bf49f25b3e3533c6be588dd73ce98fb1409ddad4bd8ef56abfa82fc4be4959b7beb21ba93d0f65af405073b83d31000bbf7dbc55bf7e1eabbeccfd2d3e43c2ae408fa77f3e1bf0213dc0af77bfae2ea6be068c823eb3e5af40ffa7a03e1b601b3f6eed3abf976caabeb44b9c3e0d60b240df0f843e5f35713f29e25abe6f39b6be26689a3e053fb5403417d43d71dc6a3ffcb4c43e5b13c5be20347b3edf69b74085f1b8bd19d80a3fd6d4553f264ed1be2bc4213eab0cb84052aa7fbec63b22bd60af773ff63dd6bed553953d3be9b640e4a8a0bee73f1bbffc073b3f8ea0b9be40be483c12cfb440192283be394a71bfe2ad5b3e9bf5adbed8ac833c99efb140ca89d5bdf2db6abf4c9ec4bec8399fbe2c5c943dabc4af406e27b33dc2ac0abfaa0456bfc90d93be39a9233e3e22af404ab67b3edfd0283dbceb77bfd4178ebe43b97a3e5c46b04068cc9e3effbd1b3fe2043bbffd3c92be770e973e72c1b2401a08833e455e713fdd8a5abeefe79dbe9c19953eeba0b5405edbd43d7fc16a3f4a28c53ec2a3acbebd7a703ed9cbb740eb96b3bd74850a3fa51c563fc1cfb8be98ff163e466eb840a7b77bbe55dc28bd9deb773fb6c5bdbe32be7f3d284ab740cfcb9ebe149a1bbfe7223b3f1531a1be807ffc3aee2bb540b87382bead6871bfdb355b3ee4aa95be40c9c03bef4bb2403013d8bd0cb66abfc426c5be940f87beb86d7f3de720b040e254ac3da3510abffa5556bfe0e675be5c61193ed87eaf406c7a773e19b02f3d212b78bf3ced6bbe3566703ea6a3b0408c159d3e25151c3fe2183bbfa30174be7cd6913e7c1fb340775e823e807c713f4e0a5abe028785bed7cf8f3e7bffb540d065d73de0996a3f91b8c53e522294be8bca653e832ab84086d1acbd49260a3f5b70563f763ea0be9b440c3e92ccb840a97d77be26fd2fbdb72a783f483ba5be02ff543dc3a7b740f4109dbed0f21bbf78363b3fe1c088be609f09bc9a88b540c22d82beea7b71bfd1885a3e24bf7abee01c8dbb15a8b2402511d8bdf3a06abf4a8bc5bea8c95dbe1c25563df47cb040f742ab3d101c0abff17b56bf02b145be6d1a0f3e42dbaf402997763eecd1333d4d3678bf67a93bbe6314663ec200b140189f9c3eb04a1c3f03053bbf958743be299f8c3e587db3401b0f823edf90713f765e59be324a5abea2868a3edd5db6401b45d73d57856a3f291cc63ead3f77be161b5b3efe88b84017c7abbdeaf0093f0e96563f2aac87beee89013eb02ab940519b76be1c1434bddc35783ff8af8cbeda3f2a3d3005b8400c9f9cbe30271cbfa9223b3f8c9b60bef45899bc9be4b540ed1681be41a571bf4b435a3ebf234abe08606dbc9003b340f349d9bda1836abffe00c6be32702dbe48e62c3d56d8b0405feaa43d0dc209bfacc956bf687715be38d6043e0237b040a513723ec5253b3df27778bf6e610bbe66c55b3e345db140ad8f9a3e7fad1c3f07203bbf7e0813be3169873e8bdab3406cfd803e82ba713f2d0659be4c8029bea23e853e96bbb640d396d83d54666a3fcb97c63ed83346beb26d503ed0e6b840c05ea5bdb096093f0de4563fa22c5ebe14a2ed3d2488b9409f1372be903b3bbde277783f9c4268bed40eff3cf261b8408b8d9abeff861cbfab403b3f78982fbeac88edbc7c3db6403a8280becdc071bffeb9593ea47019be6cb8c9bce65bb3400ca2dbbde95e6abf5585c6beb806fabdead8033d8f30b140cfcf9e3db86b09bf511357bf9c58cabd0f3cf53d988fb0408f526e3e9d9a413d0bad78bf420db6bd8a81513e7cb6b140a20c993ebdfe1c3f752b3bbf20e5c4bd3f38823e9834b440426a803e16d6713f6e7758bec934f1bd76f67f3e2e16b7403ce4da3d60406a3f5622c73eab0715be30c9453e8441b94082569fbdd83c093fae2f573fbade2cbec242d83d7ce2b940d7556ebedbe941bd9bac783f670437bee0eea93c98bbb840a00899beb9d91cbf4d4b3b3f882bfdbdbedf20bd5c96b640801f80bec3d771bf7d0a593eae7bd1bd42630ebd3ab4b34080c3dbbd2c476abff8f2c6be4b2d99bde894b53cc888b1407e089d3d732d09bf444057bf5e8453bd38cce03d2fe8b0402ae76c3e9a73463dedbe78bf25ae2abd663e473ec60fb2404b53983e783e1d3fc11b3bbf157147bd7e0f7a3ea68eb4408be17f3ebbef713f78cc57be64688fbd6070753ec870b74038b4da3d832a6a3f638cc73ec6b6c7bdf2243b3e399cb9401dc79dbd9f00093fa55a573fe321f7bde4e2c23dd33cba4086046dbe929546bd12bd783f80c605be6892293c3c15b9403b6198be30161dbfbe3a3b3f3f0c9bbd54f84abd97edb640239c7ebec2fe71bf9b3f583e32fa5fbd08c637bdf00ab4408ef3dcbd4d206abfaf94c7be00f7e0bcf0e9473c8cdfb14075d0973debc608bf709057bfe89591bb1680cc3d8e3fb1406845693eae6c4e3d57ef78bf50e5b63b2a0a3d3e1468b24013b0963e4da61d3f3f193bbf60899fba69b66f3ed9e7b44071347e3ebb17723f5bf956be2e35b6bcd6e96a3e80cab740d3cfdb3d36036a3f0f31c83e46994abdb679303ee4f5b94074a698bdd499083fabaa573f067194bd9070ad3de295ba404b6d69be0c7d4ebdf2ec783fb9f8a8bd00c08eb85c6db94032c396befb7b1dbf06393b3fd61be3bc500a75bd6842b74026857cbe1c3172bf312c573eb2a0e5bbf4f260bd435fb4409756debd32ee69bfa066c8be1048a33c404c183b2834b2402b71913d634308bf38f557bf9d542f3dee66b83d1e95b14044d9643e279b583d562879bf5d94583d0beb323ee5beb2403bb9943e9e291e3f030f3bbfd0a13d3d7268653ec03fb540054d7c3e1548723f55cf55bef48fd13c9b62603ee522b840f48cdd3d63cd693f750dc93e2004b2baaec4253e004eba4020f991bd9f12083f8712583f6e81c6bccde4973d0aedba40e9db64beeec858bd0828793f77800cbd00532cbc43c3b940f0b694be23001ebf81323b3f5e82a63ca57f8fbd3495b740cbc87abea45f72bfd4ec553ed8c9263d2f0085bd90b1b44044cfdfbd4fb969bfd442c9be9ef8893d3081f6bbbc86b240ed978b3d01bf07bff25758bff682b83df25da43da7e8b140fadb603e37b8623d705979bf163dcd3d83d3283eb113b340bcf5923ef8aa1e3fabfa3abf3a3cc03d5e215b3ea295b54019957a3ebe76723f538554bee577963da2e1553e4679b8402309df3d1897693f22efc93e6ac83f3d96151b3e1aa4ba4069218cbd528c073f5476583f6c67c53c2265823d2f42bb4034de60be73f162bd1d59793fdcfd643cd48fabbc2517ba40d5f192bee8801ebf181f3b3fc41f8c3d4a70a4bd8be6b740d84a79beb38a72bf599d543e1937b53d167c99bd6702b540e4cce0bd598769bfa918cabe0f2feb3d061c8ebcdcd7b240b802873dc74207bf4fb158bf36b50c3eaf60903dbb3ab24042a45d3e15316c3dba7e79bf3e20173ea4c11e3e0967b3404a80913ef6231f3fd8dc3abfe7dd103e87df503e11eab5409b12793e58a2723f5e2e53be78a4f83d6d654b3e35ceb840d2fcdf3d6664693f50c8ca3e84acc23de46a103ec0f8ba404a9187bd000f073f34d0583f2671943d29dc593de195bb4051a85dbe26696cbd4a7e793f2d367f3d116900bd9369ba40587d91bee5f81ebf11023b3f3cb5ee3d8857b9bd7b36b84036b877beaab672bf2650533eaf8d0b3ea4edadbdd751b540c4d4e1bde25469bffeeecabe373a263e706bdebc9527b3405e33823dc6c406bf840b59bff02f3d3e91dd783d6a8bb24015445a3e48d1753d08a579bfc4a9473e4cb5143efdb8b340d2f78f3e009f1f3fe4bf3abf54a7413ed6a2463e1d3db6409c7b773ee0ce723fefd851be42742d3ee4ed403ec121b940e7fae03d2831693f14a2cb3ebcc7123e81c4053e034cbb4026c782bd0f90063fd52a593f04a4f73db4fe2e3d2ee8bb409c475abec50576bda4a4793f5cb0e23df4f82abd9bbaba405cf58fbec3721fbf24e63a3fc6b1283ec933cebdd484b84039c175bea2e772bf4b16523ede893c3e3353c2bdb09fb540863ee3bdc61f69bf71c9cbbe21e5563eea4317bdb775b34053fa773d3b3d06bffb6d59bf56b26d3ea113513d83dab24057f3553e901e803d4fd679bfde3b783e33af0a3e5c09b4405d088e3ef224203f11ac3abf5e7b723ef06b3c3e948eb640258a753e0600733f259250be45a35e3ea47b363eb873b940c16de23d98fa683fb781cc3e0348443e0d46f63db19dbb40641679bd4507063ffe8d593fce7a2d3e8834043de538bc40e3f455be803480bd0cd6793f46f1223eaa7455bd0c0abb4049048ebe41f71fbf00d43a3f041a5a3e1e00e3bd03d1b840d2fb73bedb1573bf61ce503ee3936d3ea0a7d6bd5febb5408af7e4bd70e868bf37a7ccbea9cd833eb72e3fbdb1c1b34055536b3d2db505bfcbcf59bf8e1f8f3e8a6c293d7427b34018b3513ee748853d27057abfbb6c943e4db1003e9457b440672d8c3e37a8203f02953abfcaae913e8b3c323ee5ddb64074c8733e652e733fa53f4fbedaf1873e4c102c3e89c3b9406829e43dd8c1683fde64cd3e46dc753e5610e13d37edbb406e726cbd457d053fdbf0593f7c385f3eca0ab33c7487bc402eb451be5f6485bdde047a3f229e543e4dd37fbd5457bb4078278cbef77920bfebbd3a3fd2c6853ee8c1f7bdab1bb9401d6d72be094173bf07794f3e8a538f3e88f0eabd8735b640602ee6bd34b468bfa57ecdbe832c9c3eea0067bd240cb440282a613d1e3505bfec285abf7669a73e9ade013de072b34013324e3edf258a3d35297abf7ebfac3e0e73ed3d47a4b440369b8a3ec123213f96753abfd424aa3ee012283eb12bb7404d33723e355a733fbde24dbe1c98a03e30aa213ed511ba409f53e53de48c683f9a3fce3e23bf933e4fe4cb3d383bbc40d25862bd3bfc043f654a5a3f3082883e4aa43b3c7cd4bc4001344ebe41408abde1287a3f282c833e3b0f95bd15a3bb4016978abe50f420bf519f3a3f5c869e3e793c06bec264b94015a370be156f73bf1a2d4e3ebee1a73eaf2dffbd207eb6402997e7bd7e7e68bf0a58cebe228fb43eff5c87bd0855b4408875553d95af04bffd855abfe7b6bf3ed4d4b43cbfbcb340ec284a3e873c8f3d73527abf4016c53e1190d93d6eefb440f9cd883e9fa5213f565a3abfd69fc23e15ef1d3ef277b7408e67703eb888733f918c4cbe7444b93e7449173e945eba40f8b7e63d2256683f251dcf3e1197ac3e37c2b63dac87bc4068a556bda675043ff6a75a3f4b6fa13e18008c3af51fbd40a92b4abeaf528fbd1d527a3ff20f9c3ee12aaabd46edbb406cca88beb57421bf5e853a3f154db73edb9010bee4abb94095986ebe3fa073bf41ea4c3eb475c03eb4ad09bec3c4b6409474e9bda14568bf1e36cfbe6cf6cc3e60299bbdf99bb440ba41473dd82104bf32e95abfac08d83e395a4c3caa04b440df5a453e54a4943d2d837abfdd71dd3e8fbcc53da338b54002b3863e912f223fa0443abfe720db3e75d2133e41c2b74034646e3ef2b9733f803c4bbe48f8d13e4eef0c3e61a9ba4039a0e83da31b683fe400d03e9077c53e96aca13d2bd2bc409d6d48bd64e6033ff70b5b3f5065ba3eab4018bc7a69bd40525d45be70b894bdde827a3f1ffcb43e6039bfbd8135bc4098ac86be78fd21bf5d713a3f8e1bd03e09dd1abed6f0b94099c86cbe2fce73bf38994b3edc0fd93ecebb13be3809b740ed60ebbd3f0c68bf0014d0beb262e53eaee3aebdbde0b440c0cc393dc99603bfb5485bbf075ff03e64673e3b6b4ab44046e1403edbe6993d38ae7abfa3d2f53e08fab13dae7fb540efc1843e81b4223fe6293abf74a8f33eb3bd093e640ab840fa956c3e1fe8733f11e149be26b4ea3e789c023e02f2ba40dd8bea3df0e0673fc0e3d03e5061de3e04a58c3d7d1abd4020fa3abd8759033f6d6c5b3ffb64d33ea6c7a0bccfb0bd40d5e040beacff99bd01ae7a3f5ef1cd3eba38d4bd8c7bbc404eba84beaa8122bfa4573a3fcfefe83e2d2325be1634ba40fd216bbeccf973bffc3d4a3e9baef13e5dc31dbefd4bb74046e9ecbd29d567bf57edd0beb0d2fd3e1290c2bdd223b5402b3b2e3d951103bfe9a15bbf6f5c043fb469d9bb7e8eb4402bf63c3e43ef9e3d65d17abfb21b073f63459e3d0bc5b5408107833eb933233fe4083abf821a063fef5eff3dda50b840fbe76a3e6814743fa07d48be1bbb013f4d9ff03df338bb40eb06ec3d0ea9673fa1c0d13e2352f73e54516f3d1e61bd40e7772fbd49d3023f0ec65b3ff56bec3e4343f5bc72f6bd40fbf73cbe2e069fbd17d17a3f6fede63ed22ce9bde5bfbc40050283be9fff22bf83373a3fede4003f29632fbe9c75ba40d02669be802974bfdfee483efa28053f3dc427be088db740a8b6eebd229c67bf0cc9d1be32230b3f422ed6bd2f65b540d079203df38402bf10005cbf178b103f984784bcdbd0b4405b4c383e1b48a43d21fb7abf1050133ffc9e8a3db308b640dbf9803ea3bb233f4aed39bf4f63123fe24feb3d9a95b84075ed683e7d44743f9d2247be421f0e3f0a12dc3d2e7ebb40ccd4ed3dc56e673fe9a0d23e0a25083fa76f453d07a6bd4047b621bd8445023fbe245c3f25bd023f19c924bd5b3abe40424e38be9359a4bddbfa7a3f58f0ff3e7215febd8302bd40aaf480bee98523bf731d3a3fcc550d3f5b9a39bed4b4ba40830667bee25a74bf1ca2473eb17d113fbdbb31bec8cbb740020bf1bd8e5f67bf5ba9d2be785f173f12b9e9bd43a4b5407b6f103d11f101bfa2625cbffabb1c3f04e8d1bcf010b540fdfa323ecde1a93dc6297bbff6861f3fb4156e3d134ab64028607d3e8949243fced439bf52af1e3fce51d73d12d8b840a2d6663eca75743f0bc845be6d871a3f9094c73d1fc1bb40c536f03d5b30673f5187d33ea6a5143f58ac1b3da3e8bd4030a411bdc2af013f4a885c3f24490f3fd1d14ebdf67bbe40eafd32bed0f2a9bd77297b3f287e0c3fe47609bed342bd4011507dbec41224bf89063a3f40ca193fedc843becbf1ba40912765beb58874bf1547463e38d51d3f0caa3bbe4908b840303bf3bdbe2367bf4c87d3bef89d233fe030fdbd1be1b540dce1013d1e6201bfb8bf5cbfe9ee283f6f9e0fbdca4eb540f9312e3ee842af3dcd507bbf34c02b3f9b11473d3a89b6404c44793ef2d0243fcbb539bf58fe2a3f9e64c33d4f18b9400df8643eeca3743f886244be60f3263fda26b33dd101bc400a63f23d41f3663f2f6ad43ea02a213f960ee43cff28be40ec2003bd4a1f013f17e65c3fafd91b3f0ebc78bd50bbbe40ff322ebedb56afbd8b507b3f6408193f08db13bee080bd40b63079be589924bfb7e8393f8d41263f91f04dbee12cbb40cd6763bebcb474bf46e4443ef52e2a3fe89045beeb42b840e220f5bd92e966bff461d4be39de2f3f794c08be171cb6403285e93c7dd700bf11185dbf7a23353fc32836bdcb8ab5407ad3293e6479b43de3717bbf5bfb373fef2c203d87c6b6403d77753e1354253f3b9239bfd54f373f4a86af3db156b940c22f633eacd0743febf642be6d62333ff6c69e3da740bc407f39f43d3fb8663f8848d53e29b32d3f6ef8903c7b67be40f910ecbc6c93003ffd3e5d3fe86d283f794691bdc7f8be40dad429bed58bb4bd9f717b3f0796253fab381ebe0bbdbd409c6875be141b25bf28c6393fbcbb323f301158be0e66bb40104561be85e574bf2b8d433ef38a363f36704fbea67bb840cd57f7bd24ad66bf193fd5be41203c3f82f811be2e55b640ff0cca3c374400bf5d755dbfb659413f5b925cbde8c4b540ff92243e650dba3d7d997bbf7438443fdcd0f23cf101b740dce7703e34e1253fe47339bfd6a3433f35b79b3d2e93b940e60f613eb501753fc79241be9fd43f3f41758a3d967dbc402577f63d4c7a663fc02ad63e513f3a3fd05df83b0ea4be404a9cccbc1efeff3ec59c5d3fdc05353ffe21a6bd5434bf40cc9524be0219babd3e997b3f1e27323fb38f28be4bf7bd400ed970be7ea625bf8aa9393fc1393f3f7b2762bea39cbb40bc0a5fbe391775bf7235423ee6e9423f9b4459becdb1b840de22fabdf96c66bffe1fd6be9564483f64991bbeb48bb6408733a63c6e54ffbeb7d55dbf09924d3f036881bd77fcb540e6ba1e3ec6d9bf3d22c47bbff977503f649ba53ccc3ab740c1f16b3e1973263f365739bf01fb4f3fa2fb873d1bcdb94075e15e3e3633753fb12c40bedc4a4c3fc46b6c3df1b7bc404450f93d2f38663ff111d73e2dd0463f601124bb0adebe4032ada8bc17c6fe3e26fe5d3fb9a2413f52ebbabd476dbf4032bb1ebe26e7bfbdf7c37b3fc9bc3e3f19dd32bef22ebe40cdda6bbe763726bf888e393f25bb4b3f4c346cbed2d0bb40b9165dbe2b4575bfcacf403e6d4b4f3ff50e63be92e5b840cbaafcbd482e66bfb4fdd6bee5aa543fe62f25beddbfb6402c2f863cc22cfebef52f5ebf2ccc593f347294bdaa31b6404886193e4b63c53da7e67bbfa1b95c3fa86a313c4c71b740a185673eb7fd263fbf3339bfff545c3f70a5683daa04ba4094ec5c3e8561753f0ebc3ebeb7c4583f1410443deaefbc4084d5fb3d1df8653faaf4d73e3f65533f90b04dbc9f15bf4085b888bc189bfd3e29595e3ff8434e3fada3cfbdd2a3bf403c8619be0a72c5bd79e67b3f83564b3f9d213dbe3064be40ea6c67be12c126bf496c393f4d3f583f3a3976beef02bc40433d5bbe937175bff7623f3e0daf5b3fe2d06cbe4817b940caf7febdd6f065bfd4d8d7bed2f2603f8ebd2ebef9f1b6403b13513c100cfdbe40865ebfcf07663f586aa7bdd464b64015ac143e6dc9ca3d10047cbf15fd683fc062c13ac2a5b740025c633ebc84273f4e0c39bf63b1683f5275413d2d3aba40ba0a5b3e9f8e753f9d453dbea341653ff6d31b3dd425bd402411fe3dc7b9653fc8d3d83edefd5f3fb4f2b8bc234bbf40845b56bc0078fc3ef7af5e3fe1e85a3f734de4bd48d8bf4020ac14bec4d6cabde5037c3f9bf3573fac5e47be5a97be409b4863be9c4627bff645393f36c6643f0c1b80beef32bc40ac0159be92a275bf60ff3d3ec014683f388a76bee546b940eccb00be20b165bfddb5d8be553c6d3f284238be0122b74024550b3c54dafbbe4fe05ebfea44723f0450babdeb95b64043ee0e3e338ad03d6c267cbf4f42753f988000bc25d8b740be705e3e8315283fa9e838bf2810753fd6671a3d9b6dba40e1d2583ed1bf753f6bd43bbe9ec1713fa470e73ca559bd40fe5c003e6b78653f09b6d93e099a6c3fe66705bd897ebf40fa7910bcab43fb3e990a5f3f7491673f44e8f8bd9f0ac04060ef0ebe9590d0bd4d267c3f0f94643f1c9451be65c8be40ab5b5ebeb3d527bf2b24393fb650713fbd1385be2560bc40c6a756bec9d475bfa99a3c3e237d743fbc1b80bebd73b9409f6a02be806d65bff495d9bed887793f73ba41be484fb740b5026f3bea99fabebf3c5fbfd4837e3f741dcdbd44c4b6409c85083ec18bd63d144b7cbfdac4803f443a8cbcca07b840dd0e593ecaab283f28c638bff0b8803fa00be73c479eba408086563ec2f1753fae603abe72457e3f988b973caf8abd40c504023ea532653fa69cda3ebe3a793f482e2ebd24afbf40a5b581bb60fff93ef5675f3fc23e743f4db706be283ac040558508be9f92d6bdfe4a7c3fe338713fc0be5bbea2f6be4030f158bef26a28bf7103393f67de7d3ffa068abeb38abc40859654be2a0376bf27293b3eef73803fa4ec84bef39db94076ec03be2e2b65bfec72dabe8aea823f04274bbef279b740c87244ba8c65f9be7e935fbf2462853fa2d3dfbd03f0b64011bf023e734adc3d36687cbf7fe9863fc4dad7bcd434b8403637543ec43a293f679d38bf1deb863f209c993c55ccba403a76543e6520763f48e338be6166853f70ed0f3c15b9bd40bb85033ed0ee643ff07edb3ec6ef823faecd56bd16ddbf40d4aae4394fc7f83e8fbf5f3f2c78803fa0f010be0567c040d7bd02be1053dcbd22687c3fa3e17d3f1adf65be3422bf400d1754bee4f828bf02dc383f5537853fb6f58ebefbb2bc4053a352bed52f76bf60b0393e3caa863fc4b889bee7c5b940344a05be62ea64bf2b4ddbbed611893f978954be5fa2b7401fa79dbb5339f8be1de65fbfff828b3f7f75f2bd8719b7405dbefa3d25e0e13d18807cbfed0e8d3fa09511bda35fb840e0aa4f3ebfc5293f887038bf651e8d3f60f2183c23f8ba40b379523ec94d763f426037be7eab8b3f00be6aba37e5bd40fddb043ef9ac643f7f5ddc3ee443893fb84a7fbdbf08c040f418933b7d98f73eba12603fbbd2863f87211bbe9791c04063bffabd41e6e1bdff7f7c3fcd46853fe0f66fbe7b4bbf40fa904fbe448229bf5fb0383fbe808b3fd8df93beefd8bc403f5f50be286076bfc83b383e74e18c3f06808ebe8debb94051cf06be74a764bf9a28dcbecc398f3ff5e15dbe83c8b740637b1abcc0fef6be8d3a60bf75a4913f478102bec440b740397eee3debc1e73d3e9a7cbf2235933fd41437bd2a88b8404f7d4a3e9d582a3f364538bfc652933f003036b8a721bb404238503e577e763f18de35be10f2913fa0b02cbc090fbe40b062063e7768643f0c3edd3eb8998f3f3cd293bd1332c040241d153c795bf63eac67603f0f2f8d3fce4925bed2b9c040b181eebd9bc1e7bd329a7c3f629e8b3fe2057abe6c72bf40bf614abe84132abfe386383faecb913fdac398be00fcbc40bfdf4dbe0a9376bf40c8363ec119933fe04093be560eba4048a408beac6064bfc405ddbe8162953f482d67bed0ebb74063d671bc7fb2f5beec9060bf95c6973febba0bbe2f65b7408185e03d6bf6ed3d60b67cbf305c993f78625cbddeadb840eda8443e34f32a3f0e1b38bf6688993fd0a219bc5248bb4042c84d3edab0763fc45a34be533a983f1801a5bcfc35be400f43083e621f643fb721de3e93f1953f32e9a7bd8258c040a5bd6c3c410bf53eedbe603f7f8d933ff8662fbe23dfc0401a81e0bd4ff5edbd74b67c3fe4f7913fa30482be7496bf40398444beddac2abfb65e383f2218983f5aa19dbe091cbd4006a94bbe21c276bfc048353e1b53993ff2fa97be202eba4026740abe081a64bfebe0ddbeed8b9b3fdb6a70be250cb8406311a2bca96ef4bebce260bf53e99d3f09e714bea486b7403380d33da9f5f33d6fcc7cbf0f849f3f24be80bd9ad0b8403c493f3ea8872b3f8aeb37bf3ebf9f3f88e598bc016cbb4034974b3efadf763f3cce32be45849e3ff84bf3bcea59be4027160a3ee2d6633fca02df3e734b9c3f10e9bbbde57bc0407b889f3c39c3f33eb011613f0dee993f5b7839be6601c1400780d3bdb1f5f3bd6fcc7c3f5153983f2a0087be70b7bf4072203fbe4c402bbfa130383fba659e3fb579a2be9539bd40539d49bee3ee76bf5fc1333e3a8d9f3f9daf9cbe734bba40260d0cbed0d563bfbbb8debedab5a13f2c9d79be092ab8408b21c7bc5137f3bea22f61bf840ca43fba071ebeaaa5b740f7b3c73de8b9f93d49dc7cbf8daca53f823493bde8f0b840cd5b3a3e4e162c3f33b737bf0cf7a53ff0a2e4bc3b8dbb403c82493e6c0d773fc63b31be8ccfa43f3ca220bd5d7bbe4094a70b3e8691633f28dfdf3eeca6a23f36d5cfbdc79cc0402678c43c4c89f23e1f5f613f4250a03f0f8043be2621c1400fb3c7bdf1b7f9bd54dc7c3f38b09e3fc4f68bbee8d5bf40f7353abe7fcd2bbfb1fd373f6cb4a43fd14ca7be9754bd40826447be841d77bfd538323e13c8a53fc25ea1be4366ba4076c40dbeef8f63bf9590dfbe3ee0a73fff6181be7045b8404e19efbcb0f6f1be357c61bf2030aa3fba1c27be38c2b74049f6ba3d30acff3d7beb7cbfa4d5ab3fc693a5bdbb0eb9400609353e68a92c3f4e8237bfca2fac3fa80318bdf5abbb400446473e833c773f7da62fbe231cab3f247447bd499abe404b5b0d3e444a633ff0bbe03ef803a93f1aade3bd1cbbc0403968ec3cf745f13e40ac613f16b4a63fd37d4dbe543ec140a3f4babd36a6ffbd97eb7c3f920ea53f56e890bed1f1bf4034e734bed55e2cbf72ca373f4f04ab3fc819acbeb86cbd40f7b744be495177bf10b3303eb203ac3f7e07a6be3b7eba4092cb0fbe364663bf4469e0be1a0bae3fd5ee85be075eb840b0640fbde39ef0be3cca61bf2254b03f922430bef9dbb740eec1ab3dd609033e63fc7cbf53ffb13f5fd9b7bdbf29b94029ba2e3e8b482d3f1f4e37bf8169b23f9c843dbddcc7bb4056ac443ec56f773f0e102ebe1e6ab13fec166ebd59b6be40a3720f3e04fe623ffa9ae13eb662af3f186ef7bd8dd6c0401b1f0e3db0eaef3ef4fa613fae19ad3f257057be9b58c140c2c1abbd550303be99fc7c3f7d6eab3f04d495bed50ac040c08e2ebe68fc2cbf8f98373f9355b13fabdeb0be5281bd40e15242be578177bff9222f3e2d40b23fe9a7aabeb692ba4056fe11be8ffa62bf4a40e1be6f36b43f5b738abe2973b8402c3227bde348efbeac1462bf8278b63f491c39be49f2b74092f09c3d8d30063e77087dbf9729b83f4a00cabd5041b940abb3283e58e32d3fcc1537bf43a4b83f58cb62bd46e0bb408f54423e7f9f773fcf712cbea9b9b73fb4408abde2cebe4039ad113e16b0623f9278e23e67c3b53f748905be6feec04093ec253d0090ee3e7046623f5481b33fe45361be4f6fc1400deb9cbd692c06bea6087d3f3fd0b13f05b89abe4820c040ac7d28be82962dbfc161373fc3a7b73fb89db5be3393bd40f72d40be1aae77bf3a8a2d3e307db83f3a42afbe81a4ba4004da13bebeb262bf9f13e2be0962ba3f90f18ebea385b840abc53bbdaf05eebec65962bf1a9dbc3f340742bef505b8405efd8f3d802a093eb90d7dbf4454be3feb0ddcbd3b56b940f45a233e95752e3f36d836bfc7dfbe3f96f083bd03f6bb404227403ee3cc773f1fcd2abe5a0abe3f8a5e9dbdb5e4be404481133e0d67623fa550e33e8125bc3f98500fbe9303c140617c3a3dcd49ed3e208c623f70eab93f872c6bbe4183c140d1f58fbd322509bef90d7d3f4633b83f64969fbefb32c040062923be2c272ebfaf25373fd1fabd3fcc56babe4ca2bd40e3053ebebcda77bfb3ec2b3eafbabe3f50d6b3be8eb3ba402bc615bee76962bf39e6e2bed98dc03f516993be6895b8402cd750bd5dbfecbed59c62bfddc1c23f03e54abeef16b84002c4823d57290c3e2f107dbf4d7fc43f9c01eebd7268b94049ea1d3e97082f3f239836bf031cc53f626296bd0609bc40fbf43d3e37fa773f652329be255cc43f5264b0bdc4f7be409964153e051d623f1a28e43efb88c23f250c19beea15c140587d4f3dcc00ec3ebfcf623ff754c03fc6f974be6394c140f0c382bd16220cbe6f107d3f8797be3ffe6ea4bee042c040fcbe1dbe73b82ebf2be7363fad4ec43fc409bfbe92aebd40893f3bbe530e78bfc44f2a3e99f8c43f0664b8bed1bfba4059f917beae1d62bf42b8e3bed0b9c63f78da97be6ba2b8408b356abdba5febbe92df62bfbbe6c83f68b553be2b25b8407f57653db96c0f3e26127dbfa2aaca3fb0daffbde977b940d243173e18aa2f3f155736bfe958cb3f5ebaa8bd4219bc40da403b3e4e2d783fea7527befdaeca3f5651c3bd0308bf4068a9173e39ce613f0700e53ec6edc83fc6bb22be6925c140cff6683d169eea3ef912633fdbc0c63f4fbb7ebea9a2c140615465bdc7600fbe94127d3ff4fcc43fb041a9beeb4fc040141017be25582fbf92a8363f9aa3ca3f42b3c3bee8b6bd4039a338be933f78bf13ab283e0637cb3f10e8bcbe32c8ba40a48e1abe2dcd61bf2388e4bee1e5cc3f0a429cbe97abb840e2af82bda3f7e9befd1e63bf980bcf3f59735cbe942fb84038d7433d81ba123efd0f7dbf32d6d03f60c808be8983b940f287103e734b303fb31236bf8496d13ffaf0babd9a25bc403abc383ec65d783f10c225be1803d13fc21dd6bd5014bf40234f1a3eea7a613fc9d6e53e3d54cf3fea5a2cbeeb30c1404116823dc830e93e7753633f862ecd3fd43584beeeacc14083c743bda9b012be63107d3fec63cb3f510baebef958c0408b4410bee6f82fbfd565363f2ff9d03fc055c8be25bcbd40665e36be866c78bf34fe263eb275d13fdd64c1be86cdba40f4c01cbe0e8161bfb454e5bee811d33f38a2a0bec0b1b840a4528ebd30a6e8bea45963bf5d30d53f722265beff36b84062f6263dafd1153eb6077dbfdf01d73fd19411be288cb940e8a70a3e43e2303f7bc935bf9fd4d73f4e0bcdbde92ebc40b772363e248b783f670824be1c58d73fdccee8bd881dbf40c47b1c3e572d613f90a8e63ee6bbd53fb8ec35be4e39c14037b48d3d32dce73ec08e633f719dd33f5b0789be0fb4c140fbe526bde6c615be28087d3fefcbd13f2dceb2bee65ec04097660abe3c8e30bf321e363f4f4fd73f7af1ccbe60bebd40ea2034bece9878bfff4b253e83b4d73fa8dac5bee4cfba4031f21ebec33461bfa21fe6bed03dd93f2cfba4befdb4b84018e699bd7455e7be5a9163bffa54db3fecc26dbe823bb8409d3b0a3d1ae5183e9bfb7cbf982ddd3fc0521abedb91b94056d0043e3f78313f077d35bf2313de3f5e09dfbd4435bc40a52b343e0cb8783f414922beefaddd3fac64fbbdc023bf40efa41e3ec3df603f7778e73ea224dc3f4a713fbea73ec1401541993da588e63e05c7633f780dda3f5cd28dbe22b8c1402c320abdf2d818be15fc7c3fda34d83f738ab7bec861c0403c9404be822231bf73d3353fe7a5dd3f4686d1be8cbdbd40999531be42c878bf3094233e69f3dd3f4649cabe3fcfba406e5021be4be660bfcae8e6be8869df3fbf4ca9be41b5b84056a8a6bd15f7e5be2ac663bf5d79e13f715476be113db84055e5d43cac1b1c3e93eb7cbf4c59e33fd40123be9894b940bcadfc3d4a15323f172d35bf0352e43f70eaf0bd9e38bc40e1a4313e88e7783ffd8220be8104e43f39ef06beeb26bf40a707213e588f603ff946e83e628ee23f44e848bee940c1404a05a63d5827e53e56fc633f8d7ee03faa9692be19b9c14090d1d4bc300c1cbe30ec7c3f9e9ede3ffa3fbcbe9261c0405032fcbdc5bd31bfa285353ff8fce33f5a12d6be17b9bd408bbd2ebeb7fa78bf8bd5213e4f32e43ff8aecebe08cbba40190d24beba9360bf82aee7beec94e53f5b95adbeffb1b8407c34b5bd2a85e4bec7f663bf5b9de73f4bd47ebe1f3bb84051d28c3c2c801f3e4dd67cbfd484e93fc09f2bbecf93b9405b24ee3ddeba323fb9d834bf2491ea3f2c5501be6738bc4093e72e3e1419793fc4b41ebecd5bea3ff01b10be7626bf40c8d9233ed939603fb712e93e30f9e83f284f52be7f3fc1409ca0b43d27b1e33e9f2d643fc1f0e63fcb5297be5fb6c14091a58cbcc56e1fbe03d77c3f4809e53f11edc0beaf5dc040098bedbd396232bf5033353f6754ea3fc194dabeb8b0bd4043382cbe192979bfa210203e1471ea3fd40ad3bef6c2ba4099d026bef44060bf9e70e8bed5bfeb3f30d4b1bef1aab840952fc3bd191be3be0e2364bfccc0ed3f8ca083be6535b8404312113cd7ca223eefbb7cbf0ab0ef3f542b34be398fb940dd89e03d2d5a333f868034bf65d0f03f6a230abe5434bc40266f2c3e1a47793f11e21cbeb8b3f03f433719be1622bf4022a6263eb4e45f3fd4dae93ef864ef3f8ba45bbe1b3ac140aaa0c23d8842e23ebb5a643f0064ed3fea059cbea7afc1408ca810bc23ba22be9ebc7c3fc274eb3fcd90c5bed355c04046dfdfbd900033bfb4dc343ff7abf03f860fdfbe19a5bd40fae729bec85479bf89461e3e95aff03fd65ed7beb5b7ba40df4b29be35f15fbffe30e9be37eaf13ffc0ab6bebea0b84043d9cfbd76c0e1be4e4c64bfade3f33fd5ce87be8b2cb8401287a63a68f1253eb09d7cbfe7daf53fc9a63cbe7f87b940dc0ad43dacf2333f1b2534bfad0ff73fdde112be132dbc4049162a3e4f73793fcb0a1bbe0f0cf73f3b4322be771abf402c1a293e92935f3f45a0ea3e6ed1f53ff0ea64be6e31c1402041cf3d22e5e03e9184643ff7d7f33fa0b1a0bea1a5c1403e51a4babade25be749e7c3fbde0f13f112dcabead4ac0407f6ad3bd6d9733bf0583343f8f03f73f7a82e3be3096bd40729527be3d8079bf3c761c3ebbedf63fd2aadbbe37a9ba406dd92bbe89a05fbfd7eee9befb13f83f9039babe5b93b840b3bfdcbd0e63e0bef97164bfeb05fa3fd0f48bbe8620b8408f03d3bb7e1b293ec77a7cbf5805fc3fb91145be957cb940cd5fc73d9c8b343fdbc533bfe94efd3f1d901bbe9622bc40deb9273e549f793fe82c19bebd64fd3f6c3f2bbe8f0fbf40cd9d2b3e99415f3f5663eb3e7d3efc3fee216ebe6b25c140d31edc3d1085df3ecaaa643f8d4cfa3fb855a5be4098c140dc3ed33b130729bea17b7c3f204df83fadc1cebe303cc04047cbc6bd912e34bfab25343f155bfd3f72ede7beed83bd4018a824befdb179bf49991a3e6b2bfd3f9eeedfbe6f97ba4031b42ebe6a4c5fbfa3a8eabe083dfe3fc55fbebebb82b840e2daebbd9beddebefd9064bfb5130040521290be4911b8401b077fbcce852c3e204f7cbfa1170140c26b4dbe706eb940ed4cb83dba31353ff35e33bf00c70140c02d24bed114bc40dae3243e4bd0793fdf4017bed5de0140692b34be4f01bf40fb8e2e3ea8ea5e3f2922ec3e06560140184977be0316c140df4ceb3d340cde3e18ca643fd560004001f2a9be7587c140ce577f3c0b6d2cbe2a507c3fd1b9fe3f724ed3be4e2ac04002a1b7bd14d334bf1ec1333f36d90140ae4cecbe326dbd40b1db21bed8e179bfa8b5183e33b401409826e4be4581ba405af831be0cf45ebf6d5bebbe81320240607ac2bec96db8401a08fcbd856eddbe04a964bfe1230340ab2494bec0fdb74057cacdbc42fe2f3ed41b7cbf212c044078b055bef65bb9405ce4a83d8ed8353fadf232bf52e60440c7b62cbea602bc403937223efafe793fe14f15be550b0540f6023dbe93eebe40c1ea313e278f5e3fbddaec3e078d0440b12d80be0f02c1403883fb3d2a88dc3eefe2643fa79b03406783aebe1872c1406a06ce3c27e62fbed41c7c3f67930240d7cfd7bee213c0402512a8bd567935bf5156333faf0405409ea2f0bec452bd40456c1fbe4d0d7abf2cd0163e49d204401b55e8be7967ba40acdc34bee09f5ebfc30cecbef3450540738bc6be4355b8401a2005beab05dcbea4bf64bf7f330640852d98bea9e6b740ee5609bd6940333ea9e67bbf3040074077e25dbee945b940a63a9b3df974363fd28432bf65050840742d35bed8ecbb4065c61f3e9b2a7a3f0d5d13becb3708407ac845be23d8be4024cc343e4d395e3f4c91ed3e21c40740e5ad84be59eac0408bdc043ef41bdb3e24fa643f95d60640d30bb3bef358c140f678093d4a2733beb6e77b3fe4c905401e48dcbeb3f9bf404e699abd5a1436bf30ea323fe72f0840cceff4bed034bd40750c1dbebc377abfb9e4143eefef0740b27aecbe394aba4027b537be084c5ebff2bbecbed65808407293cabe5939b840ef1a0cbef49fdabeaed264bf8c420940392d9cbe32ccb740c2302bbddc78363ee8ac7bbfca530a40360266be752cb94003cd8d3d780f373f311332bf33240b401f923dbe96d3bb40be5d1d3e7e557a3f026411be2b640b40537c4ebe2dbebe40109e373effe35d3f9145ee3e44fb0a40692589be0dcfc04036d50b3e6db3d93eb10d653f8e110a40a38bb7be343cc14055432b3dbc5d36be17ae7b3f50000940c2b7e0bef0dbbf4085078dbd1cad36bf8b7a323fcf5a0b400b34f9be4913bd40ed911abee6627abf74f0123e150d0b403197f0be7929ba402cac3abed1f65dbf1e67edbe1b6b0b403292cebefc19b840c76113be8834d9be11e064bff9500c409623a0be4eaeb740b4694ebd73be393e286c7bbfe1660d404e0f6ebe900fb94016ac7f3d39ac373feb9b31bfad420e405ae445bed3b6bb4032df1a3ee7807a3fe2610fbe67900e400e1e57bea3a0be4088903a3e2e8d5d3ff4f5ee3e61320e4006948dbe20b0c0402e18133e6845d83e9b1b653f834c0d40a202bcbece1bc14024794e3daea039be7b6d7b3f9b360c40921ee5be8cbabf40c7287ebd414837bf3d05323f53850e408a6efdbef4edbc40fa7e17beef937abf21e7103ea4290e40cea9f4befe04ba4075033ebe9c9d5dbf980aeebeaa7c0e40f986d2bef5f6b7409bde1bbee5afd7befbe164bfae5e0f40fb0fa4bec68cb74054e477bddd443d3e921c7bbf5d7910409f0876beffeeb840febb5e3d0456383f2d1831bfbb60114014234ebe5596bb4090ef173ea1b07a3f7f4b0dbe6abc11408dac5fbe4a7fbe4083083e3ebb305d3f589def3e646911401bf991be538dc0404ca31b3effbcd63e941d653f608710401a70c0be82f7c0403900783d42233dbe0d1e7b3fb16c0f40c67be9be4895bf4017f35cbdbbf037bf9983313f41af1140c8cd00bfcac3bc409ac114bee9c07abf3cdd0e3e5f451140f5aef8bec9dbb940f2a641beaf415dbfc6a4eebe398d1140a66ed6be47cfb7402f8024be5b2bd6be8cdc64bf5c6b1240d3efa7be9c66b740d18390bde7c0403e21c67abfef8a1340eee97dbec4c9b840f2d33e3d99fb383f929030bf1b7e1440724a56be1471bb402d51153e69dc7a3fdf360bbefde71440a62368be1559be4058c1413edad15c3f6b3cf03e23a01440225296be9765c0401747243e9633d53eea18653f00c21340f5d0c4be42cec0401397903da3a040be80c77a3f6da21240d2cbedbe1a6bbf4025bd3cbdd69538bf30fd303f9cd814406adf02bfe195bc40205912befae97abf87d40c3e5a60144040aafcbeeaaeb94058cf44bed9ea5cbf0c41efbeec9c1440594cdabeffa3b740ac032cbeccbed4be2ad964bf2e771540a2c5abbedf3cb740bc87a2bd2f05443e70717abfc39b164097db82beeda0b840ae89223de896393fb50930bfe89a1740e65d5ebe2948bb40bfe0123ed4057b3fbb2009be2a131840118770be212fbe4024e2443e9a795c3fa5ddf03e98d617406ea19abe0b3ac04030c82b3e3ac4d33e0916653f56fc16402628c9be2ba1c040f198a23d4fe243bef8727a3fc1d715403212f2be1d3dbf40c48420bdaf2f39bf2278303f4f01184016ec04bf2e64bc4039f30fbe88127bbfe6c40a3e827a1740bf4d00bf547eb940840848be3d935cbf20d9efbeb1ab1740e31fdebe1275b740eb9f33be8950d3be74d064bf148218403491afbe820fb740e6bbb4bd9e49473e37167abfc6ab1940f8b786be7074b8402e04063dfa313a3fc37d2fbf11b71a40005d66be841bbb400d71103ec72e7b3f4d0307bede3d1b405dd678be5e01be40db13483e91205c3fb77af13eaf0c1b40c8e69ebea00ac0400263333e8d53d23eae0d653f4c361a407975cdbe3070c04078cdb43df02447bed7177a3f9a0c1940b44ef6be420bbf40041504bd24c939bf33ee2f3f48291b40b3f306bfa72ebc409a490dbe5c3d7bbfe2a4083ec5931a40424102bf024ab9402b6d4bbec9395cbf0f6bf0be79b91a4017e9e1be7a42b74040c93bbeffd7d1be3dbe64bffd8b1b405952b3be80deb640e658c8bdfea74a3e0baf79bfe8ba1c40e8898abe4544b8404c7fce3c1ad23a3f25e82ebf86d21d4053476ebe21ebba40a0ce0d3e54597b3fadd604be09681e408c8880bec6cfbd407b804b3e14c55b3fb810f23e55421e40f921a3be4ed7bf403d8e3b3e3ed8d03ec1fb643fd16f1d40b7b8d1be483bc0407766c83d0c804abee6b0793fe6401c402881fabe83d5be403393cabcc1673abf995a2f3f5e501e4041f508bfc7f4bb4079410abe6f6b7bbf726e063ef5ab1d40b62e04bf6e11b940003d4fbe52dc5bbfbef0f0be10c61d404aa6e5beb20bb74017fe44bebb4bd0bee49c64bfb4941e40b107b7be55a9b6402672debd84354e3e8c3579bff5c81f403e508ebee80fb840e592893ce97a3b3f28442ebf14ed2040b01a76be75b6ba4004ef0a3ed5857b3f5d9702be7d9121406e9a84bece99bd40e7714f3e6a645b3f2199f23e627721409051a7be8a9fbf401bce443e3848cf3e69da643fbea8204029f0d5bee701c0407c84de3db40a4ebe8037793f7d741f409da7febe549bbe408c0185bcbd0f3bbf10b82e3f56762140ccef0abf13b6bb405c9807be31957bbfa43d043ed5c22040331506bf24d4b8401c2053be967e5bbf836ef1be35d12040e755e9be4ad0b640aef14dbe8cc7cebec27664bff89b2140eaafbabe8e6fb6403b93f3bd89a8513e59b878bfabd52240de0992bee5d6b740104f113c2e1d3c3fc59e2dbf7c06244007d57dbe0b7dba403d5e083e9eae7b3f815d00befdb92440b79f88befa5ebd40e365533e14045b3fbf1af33e9dab24403474abbed462bf408cc44d3e1ec0cd3eb9b4643fdae02340321adabe90c3bf40f3acf33d367e51be30ba783f27a72240206001bf395cbe40611c07bc7ab13bbfb2132e3f459b2440e1e40cbf6673bb40d13205bee0bb7bbfde0e023e86d8234032f607bff892b840eca156bea1245bbf4aeff1be16db23407afaecbe1391b6406f0356becd55cdbe735364bffba12440094dbebe0032b640355903be49f0543ecd3d78bf3ce125405bb895be109ab7406a01d63a82b73c3fa8fa2cbfe71e27408ebc82bebc3fba4020f0053e8ed57b3f8f43fcbda6e12740eb998cbe2a20bd40bae0563ecba85a3f06a0f33e16df2740d58bafbe0f22bf40bad4553ee84bcc3eca91643f311827404739debe2281bf403d64033eb1c354bed43f783ff0d82540fb6603bf1219be40057a87ba5b4a3cbf88712d3f13bf274064d40ebfb82cbb403ad002befbe17bbf81b2ff3df2ec26409cd109bfe44db84020355abe10ca5abf696af2be9fe32640d093f0be054eb640cf2f5ebe2be3cbbe8e2964bfaaa62740dadec1bea0f0b5406d000dbe7137583e9abb77bf92eb28407e5b99be6259b7400143b9bb55513d3fd5502cbf3f362a403e8386be7efeb9406583033ef4fb7b3fe2bdf7bd60082b40cd8890be52ddbc40de6b5a3ee84c5a3fd41ff43eb3112b403698b3be31ddbe40d7fb5d3e46d7ca3e6168643fa84e2a402d4de2be963abf404a090d3e7b0858bed9bd773fbf092940456805bfd4d1bd400b29cc3bb6e23cbfbac92c3faee12a4043be10bffee1ba40ed0a00be610b7cbfe910fb3d07002a405ea70bbfdc04b8409c055ebe226d5abfd8dbf2bebeea2940c221f4be1707b640572267be1b62cabe06f163bff4a92a403165c5be67abb5400aa817becfa25b3e9c2677bf9ff42b4017f39cbed014b740c6da60bc41f23d3fbb972bbf724c2d405e3e8abe48b9b940b3cf003e98247c3f5a09f3bd192e2e40286c94be6a96bc40f54c5e3ebded593fc393f43e62432e402199b7be2f94be407ef5663e9453c93eb72f643f2c842d40b355e6bedfefbe406bb0173e25705bbe1a29773f81392c40e76307bf7686bd401caf6a3c67823dbf69122c3fc2022e40eaa012bf7692ba403d2efabdef357cbf624af63d6d112d40fb750dbf23b7b740ce4362be7b0c5abf893cf3be16f02c40c1a1f7be8ebbb5405df570becdd2c8be7da663bf78ab2d40f9ddc8be9861b540ef4323bec12c5f3e507d76bf01fc2e406e7da0be9dcbb640c597b7bc27983e3ff8cf2abf2461304054ec8dbe566fb9404113fc3d5d4d7c3f873aeebd79523140334298bea94abc4095af623ef689593f61f5f43ed0733140668cbbbe3e46be4051d3703e8dc0c73e07e5633f6eb830402f50eabe34a0be40e24f233e93f85ebec77f763fe5672f405e5809bf2f36bd409b55bd3cf5273ebf464b2b3f2a223140437c14bf1c3eba403d14f5bd185c7cbf639af13d042130405e3d0fbfb764b740036466be61ad59bf5899f3be89f32f40a213fbbe696bb54015307abe7c51c7becd5b63bf1aab30400649ccbe3313b540500c2ebe6d91623e84d575bf9901324051faa3bec87db6407f2ff9bcf6353f3f650a2abf34743340ea8c91bea820b940781cf73db8727c3f747ce9bd5a753440b40a9cbe0dfabb40ebdc663eae28593fe354f53ed5a23440cd71bfbe5bf3bd40cc107a3e453cc63e839a633f44eb33406b3ceebe914bbe40901b2e3e255d62bedcd7753fc594324090450bbffce0bc404b4eff3c30c53ebf9c862a3f014034408e5116bf9de5b9403066f0bdc17f7cbfe3f1ec3deb2e3340b8fe10bf410eb740653a6abe345159bf44f8f3be41f532407079febe4c17b5401c5e81bea6dec5beab1363bf08a93340efa7cfbeddc0b440430d38bea4d3653e543075bf96053540fc6aa7bef62bb64099341bbd62cd3f3f3b4629bfc7853640342195bee7cdb840425bf23d8f967c3f6fbbe4bddd963740dfc69fbe43a5bb400eab6a3e61cb583fd1b7f53e87d03740df4ac3be389cbd40ff4c813e84c7c43eb952633fc01c3740611cf2bea7f2bd40f319383e949c65bef632753f32c03540aa2c0dbf8e87bc404d241e3d585b3fbf63c4293f2e5c3740b82018bff188b940fbc5ebbdb3a27cbfce3be83d103b3640f8b912bfb7b3b6407e1f6ebe89f458bfc650f4be2af5354083e900bf31bfb440fbac85be816bc4be7bc462bf30a536408ffad2be8d6ab4408a1c42be5912693ef38274bfe507384044cfaabe20d6b5400de039bde163403f107c28bfcc95394002a998be0b77b84028a4ed3db6b97c3f90ecdfbdeab63a408176a3be454cbb4018876e3e986d583fa514f63ed0fc3a406c17c7becb40bd40e399853ecd52c33ee003633fca4c3a40e3eff5be6f95bd40bb25423e3ed968bee685743f15ea3840980d0fbfdb29bc40cbb63c3d90f03fbf3efc283f9e763a40aee919bf1428b940f61be6bdebc97cbf5f31e33d5f4539400c6f14bf1755b640b95072be1e9558bf8f9bf4be35f33840209002bf1363b440fe7c8abe17e7c2be595f62bf829f3940be40d6be4110b44018774dbe847e6c3e9db973bf75083b40fc26aebe447cb54028ba5cbd3503413f329b27bf30a43c4024249cbe101cb840c033e83db3df7c3fdbd9dabd6fd53d406819a7be0defba40abd3723ec80b583f465ff63e99273e4040d7cabe11e1bc40586f8a3e11ccc13e269e623f4c7b3d40c3b6f9bee333bd4048844d3e0c426cbe96bc733f59123c4043e810bfe0c7bb40d3c35f3d098f40bf981c283fc28e3d4037aa1bbfffc1b840e27ae0bd4df07cbf4516de3d464d3c40dc1b16bf61f1b540cdf476be4c3258bf2bd0f4bec8ee3b40c12e04bff801b4401fa78fbe4f59c1be86e661bf60973c409077d9befeb0b340fc7d59be3efa6f3ec7db72bfa8063e40bf6fb1be601db540ed7b80bd07a4413f65ad26bf56b03f4062909fbef1bbb74060f6e23d20047d3f11c3d5bdd2f1404018adaabe8f8cba409d9e773e05a6573f8f92f63e505041404e87cebef87bbc40209f8f3eee3ac03edb24623fb8a74040416dfdbef2ccbc40108e593ec5bc6fbeaade723f70383f4089ba12bf9060bb405139823df32f41bf7e2e273faca4404042631dbf2f57b84096b4dbbd48127dbf9620d93dde523f404ac117bf0f89b54026467bbed7d257bfde07f5be04e83e4028c605bf569cb3406b5794be40dfbfbe0e7561bfef8c3f4034a0dcbe394db340bd5464be7d45733ecd0772bfa002414078aab4beefb9b440d8c890bd6b3b423f2dc725bf5cba424084eea2be2957b740d541de3d8c257d3f91c1d0bd2a0c44407432aebe4925ba40d4f57b3e0345573f2eccf63e04774440b728d2be0212bc40144f943e11bfbe3e88b3613f19d243406a8a00bf1f61bc404b65643ed50673bec30a723f685c4240498514bf69f4ba400d93923dc4c641bf3349263f72b84340ba151fbf1de8b740c442d7bd49327dbf9332d43d41564240366019bf961cb5405a637fbe727557bf5b40f5be09df4140135707bfa232b34094c798befd6fbebe1d0561bf52804240cebbdfbe67e5b24040986ebe6176763e613571bf82fc434000d8b7be6452b4406c46a0bdc3cd423fd3e124bf60c245404c3fa6be2feeb6409eb8d93da8457d3ff5bccbbd9124474054aab1beb6b9b940ff04803ebce6563f2708f73ec99b47409abcd5beaaa3bb4087be983e704ebd3eb243613f80fa4640795702bfe5f0bb4056a76e3e1c3576be9e38713f507e4540604916bfe883ba403900a23d0f5842bfc565253fffc9464092c120bfc774b74059e3d2bd7f517dbf5a37cf3d5f57454093f81abff5abb44046c581bea81757bfa672f5bec7d3444073e108bfdbc4b240823b9dbe2a01bdbeff8d60bf7a71454042cae2be8779b240fddd78be15a2793ebf5a70bf3cf4464039f8babebfe6b3404bbbafbdd75e433f86f623bf53c848409282a9be0381b640aa3ed53dfe647d3fe2aac6bdf33a4a409014b5bed549b9405713823e1d88563f233ef73e8bbe4a40cf42d9beef30bb4054309d3e97debb3ee1cc603fd8204a40ba1d04bf437cbb4046e9783e7e5e79be675e703f159e4840bf0618bf0b0fba40fa63b13d21e842bf717c243f34d94940b56622bf2efdb640341ecdbd1e767dbf29bdc93d1a5648404f8a1cbf2c37b440200b84bed7b656bf608ff5be24c6474038650abf0053b240e25ca2bed57dbbbe52f55fbf4e60484066cbe5be9809b240e27082bec5047d3eb4556fbfb6e94940f40abebe0077b340c2f7c1bdcffa433f4ae922bf18cc4b4028b8acbea40fb64074d1cf3dd9877d3f353ac1bd324f4d40f470b8bea6d5b840e46d843ec624563fba56f73e28df4d4022bbdcbed2b9ba406358a23e8959ba3e1633603ffe444d4016dd05bf3a03bb408d77823ee3bd7cbe7a596f3f96bb4b4050bd19bfd295b94051c9c33dc88343bf636f233f3fe54c40090224bfee7fb64079efc7bd9b977dbff159c43d9e514b4080121ebfe1bcb340738c86beb65256bf2591f5be45b54a40e2df0bbfbbdbb14001b6a7bedef5b9be894a5fbfed4b4b403dbbe8be4694b140cc8f88befe33803ee3406ebf0adc4c40f60cc1becb01b340c80fd4bd9694443fe7d421bfc9cc4e400cddafbeae98b5400c0ccb3d49a77d3f80e9bbbd6a6050401ebcbbbebb5bb840da01873e2abe553ff654f73ec3fc50405921e0bee13cba4067b6a73e02cfb83ea1875f3f1b665040f09207bf5684ba409899883ea31080be3e446e3ffdd54e40156a1bbfd116b940e321d63d1a1e44bf8959223f8dee4f40029625bf38feb540b8a5c3bdb9b47dbf8632bf3d5f4a4e4076931fbf3c3eb34086bd88be96f455bf83a3f5beaaa14d406c530dbf3260b1408154acbee689b8bebab45ebfdd344e40e49cebbeb21ab14055d88dbe77c5813e78446dbfc0cb4f40ae00c4be4988b2401ecbe3bdc522453f9ed120bfe7ca51406ff3b2be501db540a4b6c63d3cc47d3f2db3b6bd156f534086f8bebe4cddb7408b30893e3e5f553f6069f73eca1754409978e3be56bbb940f953ac3e3c62b73edcf15e3f97845340474109bfd600ba40e0e18d3e24a181be01486d3fb4ed5140620f1dbf3f93b84038d8e53dafab44bf8957213f22f55240fb2227bf3878b540988bbfbd94d07dbff609ba3d67405140880d21bf69bbb240a8e68abe179755bfa3b2f5be5d8b50401cc00ebf8fe0b04008dab0bed822b7bec41b5ebf2b1b5140c470eebe0a9db040420293beee4f833e81446cbfe3b852406be6c6bea40ab240db29f3bd0cae453fcbcb1fbf7ec6544092fbb5beb89db440327fc23d29e07d3f9776b1bd397b56407726c2be875ab7405a548b3e2c01553f757bf73e433057404fc1e6be6135b940f5d7b03e9ffab53e03595e3f75a0564062e80abfe678b940520a933e7e2a83be73486c3fbd0255408fad1ebf4c0bb8404b22f53d313645bf9f53203fecf85540eea828bff3edb4407c37bbbdd9ec7dbf39bab43da5335440b08022bf6c34b240e61c8dbedb3855bf61b7f5be51725340ec2510bfd45cb0407683b5be9bb8b5be34755dbfc9fe5340ca36f1be4e1bb040c85798be1ddd843e1d346bbf65a3554014bec9bee088b140498501be353a463f71b91ebf7cbf57405df5b8bee719b4401a26be3d17fc7d3f8a1aacbdc3845940d845c5be6ed3b640498a8d3e24a2543f3a81f73e17465a4060fbe9be06abb8407b81b53ef88fb43e3ab25d3f9fb9594037880cbf8cecb840265f983e6eb684be61386b3f03155840924420bffa7eb7406f7e023ecac145bfaa421f3f9cf9584017272abf1f5fb440fbc0b5bdff0c7ebff3e9ae3dcc23574038ec23bffda8b140a78d8fbe6dd754bf5a9ff5be3a5656403f8411bfbdd4af4028d6babe723cb4be2ea75cbf6bdf5640faedf3be3995af40237e9ebee180863e8af369bff88a5840d586ccbeb502b14005b30abeefce463f29821dbf92b55a4002e0bbbe9591b340f219b93d311a7e3f5767a6bd628b5c40c155c8beb747b6400b13903e5f3e543f6661f73ef4585d40b225edbef71bb84090dcba3e5512b33e79e25c3fc3cf5c401b200ebf7b5bb840e7869e3e435986bebff7693f36245b40aed321bfffedb640c2c50b3ed05646bf2f0a1e3f61f65b40099b2bbfbacab340bb3ab1bdc9287ebf5960a93d0e105a40db4d25bf2018b140a81b92be067554bf7d72f5be4936594027d912bf4e47af408419c0bed4c5b2bebad15bbf3ebc59403893f6bed109af401d6ea4becc19883e4cb168bfc46e5b40223ecfbe2477b040b55713be9e5d473f104f1cbfe1a75d401ab9bebebe03b34045e3b43d5d347e3f1df5a0bd348e5f407753cbbe58b6b540d6ad923e74da533f2530f73ef9676040dd3cf0be2a87b7401f22c03ea49ab13e710c5c3f04e25f40faad0fbfa7c4b7407179a43ee0f187be21b5683f7e2f5e40865823bf5457b6405984143e1fe646bf48d51c3ff2ef5e40b6072dbf1a32b3409d78adbd0a417ebf9315a43d28f95c405aa826bf2183b040d25194beaa1854bf685ef5be41135c40fb2614bfd1b5ae40839ec4be8269b1be39185bbf0e965c40362af9be5d7aae40538aa9beda94893e288e67bf984f5e40eee6d1be7be7af406eda1abefce1473fd5301bbf389760405d83c1beb671b24064ffb03daa4c7e3f23989bbd028e62401342cebeaf20b540f8dd943ed67d533f831ef73ee9736340d144f3beffedb64031a5c43e8e3eb03e04535b3f1cf16240493411bf7329b7409d93a93ed96b89be8b92673f92376140eed524bf55bcb54059fd1b3efd6947bfc1b81b3f42e661401d6d2ebf4395b24048d4a9bd5e587ebf62c59e3d0edf5f40b9fb27bf05eaaf40178a96be38bc53bfb243f5be19ed5e40bb6d15bf4720ae40091fc9be600fb0be2c585abfcf6c5f40f2b2fbbee0e6ad40be9caebe520c8b3e1c6466bf672d61403081d4bebe53af401f4b22be5a64483fff0d1abf88836340be3ec4be81dbb140eb34ad3d12647e3ff63396bdbc8a65408621d1bebf86b440280f973e4d21533f7506f73eb17c6640813df6be7d50b6406c23c93ecfe4ae3e1c935a3ffbfc654003b312bfe489b640b9a5ae3e67e28abeba68663f633c6440e44b26bf061db540d662233ee4eb47bfcb971a3f3ad964403ccb2fbf3df4b1403c27a5bdf5727ebfa0f1983dacc16240f44729bfd44caf4099e998bed85d53bf7711f5bebec3614065ad16bfba86ad40be22cebe86a8aebe757459bf70406240632dfebe634fad405958b4be8f938c3e1f0d65bf1f086440dc0cd7bef2bbae40ccc52abe09ed483ff9c718bfbe6c66402cebc6be2741b1406cc0a83d5b7d7e3f2f7490bd4c846840bcf1d3be90e8b340b47c993e9fc1523f1ccef63e3a826940d926f9beaaaeb540ef2cce3e797dad3eddad593f88056940202a14bf01e6b5400e61b43ea2688cbefe11653fd93d674065ba27bf7279b440c8e62b3ebc7448bf7f51193fe8c76740601f31bff64db140378ea0bd9c8c7ebffc0e933d13a06540868a2abf80aaae408c859bbea1fc52bf2bbcf4be43966440d3e317bf1fe8ac403682d3be4b3aadbee37258bf001065400c4b00bfdab2ac40e066babe7f208e3e5e9863bfc9de66401f87d9be0a1fae404f8e33be7876493f5a7017bfda5169400186c9be94a1b0403292a43d03957e3f78be8abdaf796b40b3afd6be0a45b3406f2c9c3ee85e523f7b6ff63e7f836c4019fdfbbe6b07b5408091d33ecd0eac3ed6aa583fc2096c40549715bfb03cb5401972ba3e33f58dbed39c633ff93a6a40d11e29bf80d0b34020d0343e2bff48bfb7f6173f7eb26a40ab6a32bffaa2b0401beb9cbd26a27ebf198e8d3d7c7a684082c42bbf9203ae4097ea9dbe989f52bf1e74f4bee3646740ef1119bffc44ac400b4bd8be96e1abbe7c8857bfbedb6740407701bfcb11ac408cbebfbe14948f3e004162bfa4b169401df1dbbe8e7dad4064453bbe3df6493f542f16bf1a336c404210ccbe52fdaf40ccf7a03df3a97e3f534285bd1d6b6e40945cd9beba9cb240b8929e3e8701523fb825f63eb5806f40bac1febe505bb440535ad83e9bb6aa3e06c0573fda096f408cfb16bf818eb44045cabf3e26688fbe7c45623ff4336d403e7a2abfbe22b340de8a3c3e177f49bfaeb5163f68996d408eae33bfe4f3af4017b299bdbdb57ebf902d883d51516b403ff72cbfa258ad400e27a0be164552bf5737f4be0d306a40db381abfe59dab40d8b7dcbe4a95aabeefaa56bf19a46a40339c02bfca6cab4043adc4be14f9903e2ff860bf22816c40464cdebe13d8ac40776942be93704a3f30f814bff2106f40498bcebefc54af403b939d3d9ebd7e3f6a9c7fbd09597140e6f9dbbe3ff0b140dfc6a03e3aa7513fa7ebf53e4d7a724057bb00bffbaab34017c4dc3e236ba93ec9e2563f41067240ff5718bf16dcb34038b7c43ef6cb90be46fd603f382970400fce2bbfcd70b2404ea1433e2df949bf7d80153f957c70400ceb34bfc240af40871396bd2aca7ebfc28b823d83246e40c1222ebfbba9ac40f274a2bef6e951bf36ebf3beb0f76c409a581bbfe7f2aa40ce59e1be4f45a9bed7b855bf03696d40e8b903bfe5c3aa4074dfc9be6661923e34965fbf324d6f409a98e0bea82eac40bff649be18ec4a3ff2ad13bf4eeb714010f7d0bea0a8ae4075f1993d89d17e3f1a5b74bd5f437440a687debea73fb1408015a33edc4b513f039ef53e32707540f90d02bf7af6b2401067e13e2a1ca83ef3ef553fdffe7440acac19bf7d25b34004e9c93e863392be8c9b5f3faf1a7340481a2dbfbabab140c72a4b3ed6744abff836143f7e5b7340e41e36bf1a89ae40b49391bdece07ebf30c7783d91f37040dd452fbf68f6ab40be00a5befc8b51bfd178f3be4fbb6f402e701cbf8d43aa40829ae6bef2e7a7be5f9654bffc29704091cf04bfa416aa40e5cdcfbeb0d9933e0cfb5dbf53157240c6d4e2bed380ab401a9352be5d6d4b3fd63912bfa6c174405752d3bec4f7ad4061d9953d69e67e3f427a68bd932977406704e1be768ab04025b8a53ebeec503f3b1ef53ed5617840e15703bf513db2407fafe63ef0bea63e17cb543f28f377407ff81abf3a6ab240a1d8cf3e8dab93be37005e3fd1077640ae5d2ebf0b00b14014e1533e31f74abfa7bf123f51357640244837bf3eccad40d1188ebda7f37ebf735f6d3db1bd7340c25e30bffb3dab40ee92a7bec52e51bfc7f7f2be227a72400c7e1dbf298fa940e1a1ebbea494a6be6c7753bf3ce67240f0db05bf5964a940a659d5be7643953e6c6c5cbfb4d87440bcfee4bee4cdaa40336f5abed4e74b3f15d510bf23937740289bd5beb241ad405698923de2f77e3f47595dbdc30a7a40ec6de3bef5cfaf404553a83e278f503f4e96f43e524e7b40ac9704bfc77eb14016b8eb3ed66ca53e41ab533f38e27a40c8391cbf97a9b140bf66d53ec11495be26715c3fc0ef78405a962fbf0c40b040dad15b3e80724bbf5058113f1b0b7940f66938bf770bad4030578bbd81037fbfcaa2623dea837640677031bfb681aa4079cba9bef4d650bf289bf2be33357540b8841ebffad6a840bee9efbe5758a5bedf8052bfd09e754009e106bf44aea8401712dabe2f93963e930a5bbf69987740c519e7be1f17aa40072e61be4a594c3f738e0fbfdf607a4098d4d7beb787ac40f2a18f3dda077f3fa47252bd10e87c40b6c7e5be7811af403081aa3ef137503ffd3cf43ec7367e408acf05bf34bcb0400efdef3e1032a43eedb4523f2acd7d4039731dbfeae4b040291dda3ec96396bef50f5b3f91d37b4060c730bf0e7caf401b80623eede34bbfd513103fd9dc7b406b8439bfd046ac409e7f88bd29137fbfeeac573d3b467940db7a32bfa4c1a9402809acbee47e50bfe335f2be80ec774040841fbf0b1ba840e83ff4be371ca4beec7e51bfb5537840e7de07bf6ff4a74043dbdebe67e1973e699b59bf6f547a40f425e9be905ca940b00368beefc94c3f443e0ebfd52a7d40bafed9bee0c9ab4012a68c3d62177f3ffa6447bd73c17f40da11e8be0c4fae40e1b8ac3e5ee04f3f6cd8f33e978d804087ff06bfa5f5af40b251f43e9bf7a23ed0b2513ffd598040e1a41ebf411cb04027e5de3e56b197be42a1593f3fb37e40cef031bf1fb4ae40f14a693ec8544cbf08c50e3f42aa7e40f9963abf237eab402f4b84bd66267fbf3d084b3d5f047c409e7d33bf9ffda8400788aebe382450bf9aa4f1bec59f7a40387c20bf345ba7406359f9be24d1a2be113e50bfa9047b4033d508bfb336a7407197e4bef542993eecde57bf810c7d40ba22ebbe0e9ea8405a4d70bef9414d3ff0b20cbfbef07f400619dcbe0108ab4083d5883ddd287f3f57463bbd504b8140bc4beabe8588ad40254faf3ee4844f3f4437f33e9efd8140432708bff02aaf409d73f93eedaca13e1a6f503f2ccb814049ce1fbf714faf406da2e43e0b1399be83e4573f3fc780401f1233bf16e8ad4079aa713e13ce4cbf39360d3fe3b88040f79d3bbf1bb0aa404a2c81bdb5357fbf46803f3dd4bc7e40487534bf5834a8408f2cb1bed9c84fbf8df2f0be874d7d40b86921bf2796a640c466febee08aa1be7bf54ebf2cb07d4094c109bfbf73a6401719eabe1d9b9a3e232656bf15bf7f40330cedbe44daa74087fa77be85b44d3f80340bbf7f588140c61fdebec340aa407603863dc2367f3f692c30bdf8b282402471ecbe87bcac40fcfeb13e7d294f3f137bf23e9e6a8340224409bfb75aae407082fe3eab68a03e62254f3f4c39834046ec20bf1f7dae405127ea3e246a9abe152b563fd7318240c12734bf9a16ad409173793ecb414dbf86b30b3f7e1a8240a69d3cbf61dea940d8d57dbdf5417fbf3fe9343d93b88040cf6535bf6d67a740e958b3be15744fbffb7af0be50f77f40205022bf82cda540933d01bfc05fa0be72eb4dbfe72b8040c3a60abf32ada540238beebef3d49b3e0bb254bfe1368140dbe6eebed612a740c93d7ebef41c4e3fb0e309bf9eb682403917e0bed175a9409983833d2d437f3f286525bd89188440e786eebec5ecab401e20b43e88d54e3f1c07f23e74d5844022590abfb086ad40274a013fcb3f9f3e661b4e3f35a584407f0222bf00a7ad40a097ee3e67a39bbe9bb7543f3b9a8340d53535bf5c41ac408aa37f3e5daa4dbf31650a3ff67983401a963dbf0409a940710179bd344e7fbf23042a3dae108240494f36bfef96a640348cb5be161f4fbf3af9efbe934e8140862f23bf5101a5408a5203bfb2349fbe85d34cbfc97d8140d4840bbf19e3a4405917f3bebd0d9d3e982d53bf478c8240d5b2f0bed247a640cb5382bec3844e3f0a8708bfbd12844083ffe1be3aa7a840bbf2803d494f7f3f056b1abd047c8540268df0be4f19ab40464cb63e57814e3f2786f13e1f3e864056660bbfedaeac40e85d033fbf169e3e97034d3fe90e8640081123bf25cdac40c921f33e56db9cbef733533f6b008540723c36bf6c68ab40f9ff823e9e124ebf140a093f1bd784409a863ebfca2fa840213470bd835e7fbf7fbc1d3d8e668340083137bfa2c2a540c2d9b7bec7d24ebf593fefbe589f8240530724bf5b31a44021b805bfd71e9ebe377b4bbf91cd82404d5b0cbf3915a440cc7bf8be9a349e3ef76251bf90df83405c6ff2befd78a540ec2e86bed6e74e3fcbff06bfad6c8540e8d7e3bec2d4a74019907b3dcf5b7f3f67ca0fbd3add86400d83f2beea41aa4012e7b83e45324e3f0799f03e70a487403a6b0cbf31d3ab4046e3053fa6fc9c3e03974b3f37768740411724bf53efab4058bff83ec9079ebe6057513f38648640e03a37bf8f8baa40e41d873e74794ebf086d073f21328640a66b3fbf1753a7408faa62bdd36e7fbf4638173d2cba8440bd1038bfaaeaa44046a9b9be55cf4ebf59e4edbe67ed834039e324bffa5ca340490608bfdddc9dbe79ff49bf091a844060360dbfee41a3401a04febe89ae9e3e23a14fbf052f8540762af4bedaa3a44089648abeb5214f3f2a9505bf8fc28640ac9ce5be8ffba640f38c713d3f6a7f3fe00c07bd843a88407f52f4befc63a940780bbb3ef30a4e3f3077ef3e49078940c3560dbfacf1aa40d341083fed779c3ec31c4a3fa7da88409c0325bfb80cab406b63fe3e327d9ebe618d4f3fabc58740c12438bfccaaa940b9a08b3e8e9d4ebf570f063f258f8740304540bfed72a6408f0d4fbda0817fbf248b133d560e864051f038bfb60fa440e663babe78164fbf9859ecbe8039854058c525bfd585a24068e309bf61b39ebea59048bfed61854090160ebfbb6ba240ffb001bf7ef49d3e861a4ebf2d78864081dff5be62cba3406f538fbe94fd4e3f1e7f04bff71188403c43e7be771ea64022e35c3d647f7f3f7b5902bdc6928940faecf5beae81a840a7c3bb3e683b4e3f633fee3e9c678a4076210ebf8f0baa402a160a3ff0d89c3e01cb483f2f3f8a403ed025bfa925aa40cbc3013fe4329ebeb8024e3fef2889400ef738bf02c6a840934c903e948b4ebf3eed043f86ec8840070f41bfd38ea540cdad34bdb0977fbf4cf10f3df7618740d6c739bf2f31a340a2afbabe10824fbfc7a2eabe6d83864096a326bf56aba140ead10bbfdffe9fbed2f646bfeba5864002f20ebf3092a140dfba04bf1db39c3ed7664cbf42bc8740e37ff7be59efa2400c5c95be39a94e3ffe5603bf225c8940cec4e8be713da54047793f3dd2987f3f25f5fdbcb2e68a403053f7be159ba74045d4bb3ecaab4e3f6aabec3e3ac58b40d7cd0ebfee20a94011ee0b3f87f99d3e504a473fbda28b406c7f26bf143aa9408fa8043fcc3f9dbeb6574c3f668c8a407db139bfebdca7402eea953ef14e4ebf4cbc033fff468a40c2c141bf5ba6a44099cc11bd4cb27fbf6402083deab28840e3883abf514ea2408fadbabeb1f64fbff605e9bee6ca8740fa6b27bf86cca0400c000ebf6045a1be872745bf92e78740f5b70fbf58b4a040404208bf4f899b3e804a4abffbfd884033f5f8be020fa24008619cbe7b5c4e3f58c201bfc9a38a40741aeabe1a58a4407d4f1d3d7eb37f3fe608f0bcde378c40328cf8be24b0a640e3eebb3ea3244f3f0deeea3ee21f8d4001630fbfee31a84034240e3f854a9f3e6e74453f36038d40071727bf1c4aa8400031083f99129cbeae3b4a3fcdec8b4063543abf72efa640caf59c3eab034ebf6c22023fa39d8b40b65642bf7ab8a340d23ae4bc8ac57fbf94fb013d50008a401a2d3bbf0d66a14061ddbabe556b50bf0c3de7be140f8940ba1828bf50e89f408a2110bfc896a2be3f5443bf13268940426310bf13d19f40709a0bbf4d4e9a3e583c48bf863c8a400936fabe3529a14039eca2bedb054e3f8d4600bf0fe88b40f93aebbe446da340524dfc3c3dc77f3f3e3ee5bc62858d40318ef9beb1bfa540fe2abc3e6d9d4f3f8d11e93e9e768e4077db0fbf6e3da7400448103feca8a03ef49d433f9f5f8e40f19027bfab54a740108b0b3fe7ce9abe3f2e483f2c498d402ed93abf89fca540ee8ba33e8cac4dbfeba2003f86f18c4030d842bfcdc6a24044e4adbcddd17fbf016efd3c284b8b404abe3bbf037aa0407fd4babeabe150bf9f97e5bee3508a40cbb228bf5a009f4087cc11bf65fda3bedbca41bf51628a40f9fb10bf12ea9e4028540ebf70f5983e879146bfc8788b405951fbbeae3fa040426fa8bec0a24d3f2f39febeec298d403335ecbeb37ea2402ea9c43cf5d47f3f305cdebc4ad08e40ff68fabe7dcba4408f11bc3ef417503f876fe73e8eca8f40fe3f10bf2645a6400cf1113f2219a23e2d15423f21b98f40d1f627bf6e5ba640ec440e3f157299be6884463faaa28e401e4a3bbfd105a540fd04a93e58494dbf92f6fe3ea5428e40af4643bf6ad1a14022bd6bbcf2db7fbfd6c8f43c6c938c40ec3c3cbf498a9f40adc4babe3a5751bfb8f6e3be4e908b40a13a29bfb9149e40377f13bfc866a5be773340bf459c8b408f8211bf6cff9d40801d11bf8495973eafce44bfbdb28c401348fcbe84529f405412aebecd3b4d3fafb6fbbe5b698e401a0aedbe7c8ca1403aee8b3c1ee07f3f46cdd5bc94189040a01dfbbe9dd3a3408efabb3e8691503ffccae53eb21b91401b9110bf2d49a54053a4133f3b8ba33ea87c403fbb0f91402d4928bf7a5ea540f40e113f8a0e98be12c2443f43f98f40b3a73bbf620ba440aba1ae3ea4e24cbfa775fc3eb7908f407ca043bf0dd8a040a620d7bb01e57fbf11d9e43cd8d88d405fa73cbf97969e40aebababee8cb51bf5050e2be11cd8c40c6ae29bf25259d40897915bf1bd0a6befa5b3ebfabd38c40c0f511bfd3109d40a75414bfdc33963ed0a942bf20ea8d40f917fdbe66619e40bb7cb4be59d34c3f0b81f8be15a68f4088b7edbe4d96a0403a321a3ca1e97f3fc8aac7bcf45d9140c2a9fbbec3d7a240fb08bc3e500a513f6806e43ebb69924079cd10bf3549a44001a5153f6ffea43e409f3e3f21639240808628bf875da440f646143f39a996be919d423fac4c914044f03bbff40ca340d412b53e287b4cbf1d36f93ed8da9040f1df43bfc7d99f4030cf053afee87fbf4108d93c901a8f403af83cbfff9d9d4098dbbabe194152bf4380e0be5b068e404e0a2abfa7309c408f6317bf0642a8be12853cbfb9078e402a5112bf4a1d9c40af5a17bf46c6943e639940bf241e8f400eb9fdbe4e6b9d40986ebabef5634c3fd587f5be44df9040aa35eebe1d9b9f406be41f3b5fee7f3f20f1bcbc8c9f92401905fcbee5d6a140da32bc3e3183513f2126e23ec1b3934077f010bf3d44a3405e90173fa57ba63ee0c53c3f63b293409da928bf9a57a340d74e173fdb3395be7e8d403ff89b9240401e3cbf9609a240570ebb3ef70a4cbf2e36f63e29229240620e44bf16d89e40b0e3d83be2e87fbf37b3d23ca459904067383dbf02a29c409fc2babe33b752bf8dd8debe2a3d8f4065552abfc8389b400edf18bf8fc0a9be7dfb3abf64398f402b9c12bf63269b407bcf19bfab46933e19ef3ebfc24f9040fc38febed9719c400371bfbe21ed4b3f6b35f3bef11592400892eebe909c9e4086217abb54ef7f3f3e1ab6bc76de9340ff3dfcbea4d2a040d309bc3eb2fc513ffe83e03ef0fa9440010211bfde3ba240d709193ff201a83e4b3d3b3fb6fe94403bbb28bf434ea24088c3193f4ab093be52e43e3f58e89340e93a3cbfcd02a1408607c03ef8934bbf45e9f33eac669340362c44bf0fd39d40ae4d573cabe67fbfbeb6c83c1496914049683dbfb1a29b4097a4babe702c53bf4234ddbe7c71904069902abf9b3d9a4042721abf5c41abbe945639bfa868904025d712bf312c9a40516c1cbf7cc0913e50183dbff67e91408998febe1b759b40f3c0c4bebc724b3f698ef0be184a93406ccdeebeb59a9d400c2b2abcf2ed7f3fcc65acbcb01a95404655fcbe13cb9f401eeabb3e6275523fecd8de3e483f9640830211bf2930a140479e1a3fb08aa93ea796393f1c489640c7bb28bf9341a140a8601c3f1c2792be270e3d3fce319540a8463cbfa9f89f409b52c53ee4194bbf2543f13ed5a794404e3644bf28ca9c40c846a63cb6e17fbfa36bb93c59cf9240e4843dbf859f9a409f9dbabe20a153bf5e7adbbed1a29140a8b82abf933e99407c421cbf11c6acbed07437bf08959140b5ff12bf222e9940845d1fbf5d33903e51ec3abf41ab924067d3febe7b749a40af9ecabe7ef44a3fd654edbe387b9440a5e3eebef6949c409d888ebcc6e97f3f12b79ebcb35396407846fcbe99bf9e40ccf6bb3edded523fae04dd3e3b80974078ef10bf8b20a0402a731c3fab18ab3ee6af373f048e97406ca828bffc30a040e8521f3fae9590be61e23a3fcb7796406d3e3cbfa3ea9e402938cb3ed59b4abf6300ee3e26e59540712a44bffebc9b40c691da3cb9d97fbfdfecae3cfa0494401a8c3dbf1798994091a8babeda1654bf8ca8d9beb3d0924037cc2abf473b984034ed1dbfa651aebe9aa735bf10be92402e1413bfca2b98402a0922bf739d8e3eaeeb38bf2fd493409be6febe8a6f99404feccfbec2714a3fd478eabed6a89540d6d1eebede8a9b40f769c2bcb5e27f3f86e894bc02899740850efcbec5af9d40fc04bc3ec466533f8d28db3e49bd984025c710bf950c9f409a1e1e3f63adac3ef0e0353feccf98402e7f28bf121c9f401b00223f2ef98ebee8e1383fcdb997400f203cbf52d89d40248cd03e1d184abff81feb3ea01f9740bc0f44bfbbac9a40d379043dc7cf7fbf96e0a83ce9379540b8843dbf958d9840d481babe768c54bf3efdd7be05fc934055d12abfec34974069401fbf30e2afbe441d34bf9ce49340331a13bf65269740854324bf95ff8c3ee64137bf9ffa944093dcfebe8d6798400582d4be6eeb493fef28e8beded3964048a2eebeb77d9a40ccf4f1bc85d97f3f503b8ebc95bb984051b8fbbedd9c9c404ccfbb3efdde533fa184d93e79f799408b8f10bf86f59d40b76f1f3f7a44ae3eeb57343fe20e9a40ad4628bf0d049e402a3a243fec578dbe4639373fdff8984097f23bbfe5c29c40c418d53e9e9149bfadd6e83e4357984083e643bf6c999940309d1e3ddcc27fbf439f9c3c25689640106f3dbf0d809740315bbabe0d0155bf8551d6bec624954054c82abf8e2b9640ccc320bfe676b1beb95f32bfaa089540181213bf001e964031ca26bfc1588b3eee4735bf8f1e9640f8b5febe8f5c974056a6d9be7360493fb743e5be4ffc9740a655eebe8e6d9940bd0713bd6acd7f3f8fb182bc6deb99408d44fbbeed869b40e1aebb3ee356543f39cbd73ecc2e9b40024910bf6cdb9c408ff5203f5ee0af3e7997323fe84a9b403eff27bffae89c4031c1263f36ae8bbec43f353f03359a405ab63bbf6baa9b402f3cda3e0b0749bfdceee53e128b99408ea943bf2a829840645a393df0b27fbf34858e3cb994974025463dbf936e96403a5dbabe597655bfe97ad4be0b4a9640aaac2abf3e1e95408e7122bfe611b3be027130bf55299640d7f712bfa21195407a7e29bf21a9893ed61433bf173f9740826bfebe914d96406e05dfbefdd0483fb70be2be38219940d8e4edbe5c59984077352dbd91be7f3f6c086cbc91179b40aaabfabef36c9a408fc0bb3e7acf543f7aded53e3f629c404fef0fbf48bd9b40bea6223f0b84b13e7ba4303ff5829c4023a427bfe4c99b402677293f01f989be670c333f336d9b403a663bbff58d9a402fa5df3ec77648bfefade23e58bb9a407c5b43bf5e6797407bbf503d0ca27fbfd214863ceebd98407b0c3dbf945995408b4bbabe36ec55bf50aed2be156c9740ad802abf6a0d944064d923bf47b0b4be87b82ebfda46974092cd12bfbe0194405ccd2bbfcbf5873e633231bf755c98402e01febe063b95407eafe3bed13f483f7764dfbede429a40d453edbe984197404eb244bd83ae7f3f645b5bbc49409c40d6f1f9be624f994041abbb3ebd47553f0a10d43e21929d40b9840fbf8c9b9a409c0d243f3129b33e2cec2e3f5cb79d40d53727bf38a79a4031c62b3ff24088bee72a313fc1a19c40d0043bbff06d9940fc4ee43ee1e447bf4007e03ebfe89b40760043bfbb499640e76b663d29907fbfdc53803c61e499400fc63cbfc44194400619babe4c6156bf29fdd0be7c8b984005482abfc8f99240770a25bf904eb6be0a2c2dbfce619840979612bf0eef924045d62dbf0a40863eca872fbf407799401e7dfdbeaf25944034e9e7be3dad473f8715ddbedb619b40a4a8ecbe0527964027e65abd259d7f3f65584ebc39669d40721df9befc2e9840226abb3ed8be553f4768d23e1ebf9e40c20c0fbff8769940983c253f22cdb43e3b612d3fcce89e4031be26bfb281994001cf2d3f318886be2c812f3f5ad39d403a963abf104b9840b080e83efa5147bf8dbfdd3e49139d40c29842bf4c299540dc35803d35797fbf9c09623c16089b4024733cbf2d279340a8eeb9be5bd556bf5d44cfbe43a89940f7022abf64e39140378926bf05f6b7be0d4b2bbf307a99402b5312bf9fd991407f5630bf4d7a843eee5b2dbf788f9a40d8dffcbe980d9340e7fdecbebb12473f77d9d9be317e9c40d0e3ebbeac0995400e8074bd3f877f3f8f6a32bc64899e400b2ff8becb0b97404950bb3e5336563f4297d03e37e99f40b3870ebf944f984026c0263f467bb63edd7a2b3f4a17a0407f3726bf59599840e64f303fb0bf84be5f552d3f02029f40be1a3abf60259740c398ed3ebcb746bfdb7cda3e54399e40d51c42bfb104944018658c3de1607fbf1411493c7a279c40880c3cbf6f08924006feb9befc4b57bfbd47cdbee1c09a40faaa29bfd7c89040581b28bfdea4b9be0e4b29bf838e9a408bfd11bffcbf9040c4d632bfa0ad823ec11f2bbf9ba39b40f61efcbe40f1914026ecf1be8375463fc6a5d6be54969d403afbeabe09e89340c52686bd46707f3f310b1cbc2ea89f40d61bf7be4be49540036cbb3e98af563f1189ce3ec70ea14077ef0dbfe3239740e153283f3132b83e1378293f2541a140e89c25bfbe2c9740a1d1323f77ec82be1f192b3f0d2ca040f78a39bf7afb95405093f23ecc1846bf7c3fd73e555c9f40b19441bf4fdd924084d2963da9497fbf1d453d3cf0439d40e3993bbff0e6904041cab9be57c157bfdc87cbbeaed69b40144729bf8dab8f40cf3e29bfec4ebbbe63b127bf12a09b40ff9b11bf9da38f4092c934bf30e7803e946729bffab49c400246fbbe29d29040fbf7f5bea3da453f1a45d4be9fab9e402efae9bea1c39240edca90bd82597f3f67dc0ebc04c4a040caeff5be00ba9440492cbb3e9c26573f9cd1cc3e4631a240b44a0dbf63f595404275293fd0e0b93e39e0273fe267a240c9f524bf53fd95402bc4343fab2281be0562293ffa52a140c8ee38bfc7ce9440ec98f63e7a7d45bf21e5d43e6e7ca040420141bf4ab391406bbca13db32f7fbf28eb2b3c995d9e401b1c3bbfd5c28f406b92b9bed13558bf86cac9bec7e99c4017d828bfac8b8e40ab6e2abff8fbbcbe220326bffeae9c404b2f11bfaa848e4020d236bf3c327e3e9e8d27bfb2c39d406756fabe7db08f40d42ffabe7e3b453f4ca3d1be32be9f4018e2e8be9e9c914095cf9bbd23407f3fcf67fabb07dda14066acf4be138d934037f0ba3e129d573f5514cb3ed950a340369a0cbf3cc494400ca52a3ffa92bb3e9a31263fa28ba340034324bf3ecb9440f4cc363f90a37ebe8188273fee76a2401b4738bf6b9f934064ccfa3e18de44bf2647d23e1199a140526040bf3986904011d8ad3df9107fbf47b20e3cea739f4013913abfb49b8e404b77b9bef9aa58bf95eac7bea5f99d40205c28bfc9688d40d2dc2bbfcbb4bebe9e0824bfbfba9d40cbb510bfb3628d40d42e39bfcc727a3ef74b25bf3dcf9e40404df9bec68b8e4051f2febe8294443ffc53cebe83cda0402cb0e7be89729040b4aba7bdcc227f3f8d83c5bbaaf2a240aa4ef3be0e5d92408ee4ba3e3a15583fca1dc93eef6ca44048dc0bbff98f9340b0162c3f7852bd3e8a32243fd5aba4409e8223bf0f969340642a393f42dc7abef246253f5797a340c89137bffc6c92401296ff3e5a3644bfb0efce3e7eb1a2406aaf3fbf98558f4088bcb83df8f27ebf9c79f63b2986a04076f639bf0b718d40386cb9bed22159bf88eec5be93059f401cd127bf5e428c401d302dbfe76fc0be8a1f22bfa2c29e40ba2d10bf2e3d8c40de573bbfffb3763e7f3323bfe7d69f407827f8be76638d40bba301bf23ee433f7561cbbedcd8a1407461e6bece448f402571b2bdfa057f3f30d8a0bb3104a4405cd3f1be5b299140a2ddba3ef58d583f341ac73ec784a540070f0bbf085892406a6a2d3f0413bf3e5b48223fb8c7a5406ab222bf385d924082543b3f3c1377be5a2e233f73b3a44068cc36bff0369140e3f8013f538e43bfb4f8cb3ef8c6a3407df43ebf7c228e403a7cc23d44d67ebfabb8e13b8d95a140f55139bfef438c402d28b9be859659bf9d2bc4bebc0ea0403b3c27bf87198b40ae302ebfa222c2be1a8920bfccc79f40b59b0fbf3f158b40e11a3dbff506733e538121bfd6dba0406fedf6beb9388c406f7f03bfc74a433fed0cc9be6ae1a2401efee4bea0148e40156ebcbd73e97e3f374b88bbd512a5402e43f0be2df38f40098dba3eb503593f1563c53ea699a64051370abf951d9140e5682e3f47c9c03e18b4203f96e0a640d8d721bfdd219140ad163d3fb76073beca7d213f8ccca540d5fc35bf63fe8f4074d0033fa3ea42bfabadc93e81d9a440c22f3ebfeaec8c408ab9cd3d97b37ebf0e09ae3b17a2a240c4a338bf66148b4083f2b8bede0a5abf5457c2be2015a140af9d26bf49ee8940826d2fbf53e1c3be9aa51ebf3ecaa040eeff0ebfebea8940f9353fbf61326f3e795e1fbf0bdea1408d9ff5be950b8b4089a905bf0f9e423f14efc5be2fe7a3409186e3be04e28c40e18fc7bde3c77e3fc3962fbb991ea6408c9eeebe88ba8e400360ba3e807a593f4780c33e90aba7405b5509bfa5e08f40eaa72f3f748dc23e83cd1e3f72f6a7401cf320bf03e48f4058323f3f37866fbef15a1f3fa5e2a640442335bf59c38e40b6fb053f693d42bf598cc63ea9e7a540225c3dbfddb38b40e2c8d83d578f7ebfc3e4773b5faaa34010e737bf6ce18940eeeab8be4e825abf1243c0be6217a2402af125bf9dbf88403dc930bf07abc5be34921cbfa0c8a140ae560ebf23bd8840266a41bf3a476b3eb30e1dbf2ddca2404337f4beefda89402bdc07bf27ed413f56a2c2bec9e8a4407bf4e1bed9ab8b40805bd2bd53a57e3f0b92aaba1326a740a0deecbe4a7e8d401e65ba3e33f4593f515ac13e10b9a840356508bf19a08e40df05313fbc5cc43ed9b61c3fd207a940b2ff1fbf93a28e40ed67413f93906bbe910a1d3f45f4a740bf3a34bfc7848d407f33083fad8a41bf0e36c33e52f2a640b47d3cbf13788a409980e23dac6d7ebf7660423b42afa440c01f37bfbfab88408cb5b8be12f85abf145cbebe5516a3403b3a25bf438e8740dad431bf596ac7bed9d21abfbfc3a2401fa30dbfac8c8740463643bf2e76673e482d1bbf09d7a34052baf2be94a7884000ba09bf9941413f2309c0be0ee7a540794de0beed728a403c2adcbd33847e3fc63fe3b91e2aa8406009ebbe413f8c40d729ba3efb6a5a3fef78bf3e0bc3a940356a07bfbd5c8d404a10323f861fc63eb0f81a3fa115aa4051011fbf545e8d40ce33433f13b967be262a1b3f5702a940484733bf6c438c402b100a3f19de40bf81a1c03ef8f9a7409b963bbff43989409432ec3d8a4a7ebfcdf9083b36b1a540e24f36bfc77387407e6db8be746c5bbfa087bcbe6e12a440bd7a24bfa75a86408dd032bfed28c9be1a1e19bf0ebca340e6e60cbff5598640c1f244bf26a1633e825319bf14cfa440b52bf1bef5718740cd8e0bbf4594403f4f73bdbe74e2a6407294debeb437894087fde5bd70617e3f0c46063a362ba940e421e9bee1fd8a40c6dbb93e5fe05a3f5faabd3efec9aa40176606bf01178c405a0b333f5ee1c73e9d44193f5e20ab40eef91dbfb3178c4009f0443f89de63be5151193f580daa40fa4a32bfb3ff8a40cee20b3f303040bfcc11be3e35fea840b8a53abf3ff987401438f73dc4207ebf6b4ca039d8afa640647635bf45398640b644b8be36e25bbff987babe490ba540c2b123bf87248540e01034bf0efacabea30917bf2cb1a4403d210cbfbb2485401d0c47bf15985f3ef8f716bfecc3a540198aefbecc39864014b10dbf7edb3f3f4e03babe97daa74024c8dcbee7f987405ec5f0bd62397e3f1ac80b3bf428aa40cb26e7bee1b98940eac1b93ec4585b3f6c94bb3e83cdab40085805bf9fce8a40b74e343fe4b7c93ec22b173fa027ac408de81cbf6bce8a40070a473fcfcd5fbebdf5163fe014ab40be4431bf5ab9894070080e3f2a763fbf2c9aba3ecbfda94085a739bf28b58640689f003eecf87dbf813a6dbaf0a9a740f78f34bf6efb84402335b8bed05a5cbfd75ab8beb6ffa54067dc22bf17eb8340e54435bf3dceccbe8ff714bfeaa1a540bb4f0bbf2aec8340380449bf06915b3e4bb614bf61b4a64067d1edbe3efe844028ab0fbf51243f3f0fdeb6be43cea840aae4dabea2b88640879dfabd1d137e3f6e21553b1e22ab40c613e5be5c72884017b9b93ebbd25b3fc05eb93e58ccac40733d04bfb38289402b83353fd68fcb3ed018153f242aad401fca1bbfa08189402103493f51bd5bbeaeb3143fad17ac40273130bf8c6f8840cb06103fc9bc3ebfea6db73e4cfaaa40bda138bfd76e8540cd1c053ed9d37dbf6c9fc6ba06a1a8400da233bf6abb8340a2e1b7be62cf5cbfe87db6be32f1a6408bff21bf84af8240712036bfea90cebed24d13bfc18fa6409b760abf79b18240ad924abf1eb1573ea2f112bfeca1a7401b09ecbe8ac083409e5611bffa733e3fcf72b4befabea9403ef1d8be2d75854049e601be06ee7d3fec0b833b4018ac409ef0e2be9a288740015bb93e8f475c3fee8fb73e14c8ad40d01a03bf80348840135d363f7854cd3e6671133f8529ae40c2a31abf8b32884023914a3f92d857be25f0123f5a17ad40cf152fbf7a238740baae113fe20b3ebf490db53eb7f3ab40879437bf50268440a4300a3e32a87dbf3eb743bb1895a940cdac32bf3c798240c2a0b7be80445dbf1485b4bebadfa740551b21bfd07181404c3137bf1b63d0beef5311bfae7aa740009609bfaa748140746c4cbf3fa0533eddbb10bf8b8ca8407e31eabeb3808240094313bfb2b83d3fd143b1beb7acaa402aeed6be8a2f8440faed06be9bc37d3f890db13b560bad409ebde0be9edc85403721b93e94be5c3f7f8bb53eb4c0ae4046f001bf0ae48640d96f373f252bcf3e8574113fc025af409c7519bf30e18640176b4c3fb6c053bed0ba103fe313ae40ddf22dbf27d58540049c133f774f3dbf8dddb13ea0e8ac401a7c36bfb0da824089400f3ee07a7dbfec8c93bbc184aa40a8ac31bf06348140f98ab7be6dbd5dbf2846b2bef2c9a840af2c20bf1c318040e16338bfd543d2becd1e0fbf5661a84061ab08bfd634804012654ebfc0714f3e494b0ebfe372a9404c46e8bec93d8140063e15bf7ff83c3f5dd0adbe1c96ab4063d7d4bec0e682408cdb0bbe5b987d3faba3dd3bfbf9ad404776debe6a8d8440ac18b93e41395d3f273bb33ecab4af401dbb00bf5490854049a4383fa50fd13ea33c0f3f661db0406b3c18bf9a8c8540b1644e3f92894fbeaa490e3fd90baf40a7c42cbfa78384405e9c153fdd8c3cbf465fae3eb4d9ad40205b35bf7c8c814095c7133e00517dbfcb2cafbba970ab4029a430bf9ad87f403d4cb7be2a345ebf3934b0be7cb0a940e4351fbfdcdb7d40bf5239bfd116d4bed43a0dbf5b44a940bfb807bf11e57d40d00650bf37684b3e86440cbf9555aa40194be6beb5f07f40b7f316bf313f3c3f1707abbecc7bac4060b0d2be629b8140f06610be2a6f7d3fc1eefa3bd7e4ae404e1edcbe913b834001d7b83e6cb05d3fe82eb13e04a5b040d8fafebef03984405a92393f4ee4d23e0a5a0d3f2511b14091fa16bf553584408606503f3a794bbe6c430c3febffaf40c48d2bbf842f8340b751173f3dd23bbfb699ab3e99c7ae40bb3334bf2a3c8040c73f183e52267dbf344dcabb7459ac4056952fbf09457d403ef5b6be00a95ebfe03daebef693aa40bc381ebf78517b403a2a3abf51e5d5be1e6e0bbf5924aa40a0bf06bf745c7b40079051bfb561473e42540abf3d35ab408b42e4becb617d40c19918bfa7853b3fd756a8be675ead40aa7bd0bef04d804034ee14beb9447d3ffaa30c3c8cccaf4076b8d9be96e781403d7ab83e90255e3f1342af3e0a92b140a671fcbe5ee1824001693a3ff3b4d43ea28e0b3fa701b240f1b115bfe0db8240388f513fc36d47be67540a3fc3f0b0404b502abf35d981407af5183fc9173bbf8ef2a83ef5b1af40360533bf09d37d40d1511d3edaf37cbf309202bcc73ead40857f2ebff1ac7a40c8c1b6be23205fbfca0eacbe0b74ab40ad341dbfa5c27840bb453bbfaccbd7be853209bff800ab4096bf05bf6bcf78401c7353bf1a1a433ee9d007bf8511ac40f92be2be63ce7a40ad881abf60be3a3fedb4a4be953dae40a838cebe60fc7d40f2df19be66147d3fc05d273cc3b0b0400a44d7be3c9180400856b83e0b9f5e3fc7fbac3e7f7bb240b9d9f9be628681409d873b3fcf9fd63e1d4e093f92eeb240f56114bffe7f8140d272533f441d43be14d1073f05deb1408e0b29bf838080402ee81a3f7c4e3abfb048a53e4297b04068cc31bf72277b4097f8213e2ec47cbf578116bc241fae40ca5f2dbfbb0e784070a6b6be0e9b5fbf41a9a9be3f4fac4046271cbfc92d764043593cbf1bb7d9be88f206bfc4d8ab40c2b604bf4f3c7640323c55bf3bd33e3eec6105bff5e8ac407804e0bec2347840df561cbf03f9393fb458a1beda17af40a8e4cbbe72567b40bd6a1ebec0e67c3f9986393cf88fb140e4bdd4be296f7e409e43b83ecf1a5f3f328daa3edd5fb340ec2ef7be0e288040af9b3c3f188dd83ea00d073f58d6b340fb0713bfcb208040f13c553faece3ebe2161053f27c6b24080bc27bf22497e401ebb1c3f698639bf2fe3a13e4579b140248e30bfc17778401e30263ef6977cbfc1ef20bc47fcae40af3a2cbf8e6c75401444b6be0a1060bfa7a6a7be4727ad4075141bbf109573407b173dbf9e88dbbef22805bf6badac4061a803bf5ea5734045a256bf9fc43a3e067c03bf3ebdad407ed1ddbe3e977540d8dd1dbf353d393fbbc09ebeedeeaf40d884c9be83ac7840ceb722be6dba7c3f658b463ceb6bb240c22bd2beb6b77b407bd8b73e668f5f3f539ba83eeb40b4403578f4be348f7d409b583d3f875fda3e8346053fc7bab4402fa811bfe67e7d4099a2563f3dbb3abe537c033ff4aab340d26726bf068d7b40303f1e3fb9c938bf26579f3ef957b240934a2fbfffc3754071e02a3edf647cbf39b138bc29d6af405b102bbf75c672407ef6b5be608660bf167ea5be1dfcad4061fc19bf86f8704029053ebf486dddbea40903bfe67ead40979402bfa30a71406b4958bf5f7e363e402101bf588eae404a93dbbee1f57240589b1fbf5a75383fab649bbec5c2b0407619c7be9dfe7540e75a27bec2887c3f717b5d3c9544b340e68dcfbe27fc78400f92b73e2b07603f7868a63ea11eb540d9b5f1be16ca7a40e2473e3f9a47dc3e8e24033fd89bb540b74210bff9b77a409f49583f246d36be7122013f668cb440aa0d25bfbacc7840c2fd1f3f1b0038bf8cfb9b3ee831b340f0fe2dbfa20a734050952f3eef2f7cbf27f051bc5dabb04044de29bff21a7040ecd0b5be900161bf7205a3be56ccae40f6dc18bfae566e4044143fbf6664dfbe52a400bfce4bae40d97901bf956a6e405a105abfa712323e0d00fdbedc5aaf409647d9be0f4f70406e6921bf77a7373fb1b397bef891b14074a0c4be0c4b734065eb2bbe64567c3f5ffd733c8318b440cee1ccbebc3a76407a7ab73ebf83603ff2dda33e8af7b54067e4eebe00ff7740d4583f3f1141de3e8fbc003f1278b64051d50ebf19eb77408c115a3f03f931be7400fd3e0469b54060ab23bf9f06764031d1213f2f2f37bff33e983ea007b4400cad2cbf674c7040dedb333ee7fe7bbf8ec960bc6b7cb1401fa628bfc16a6d402c85b5bee97961bf9ebca0be7c98af40b9b717bf46b06b4096e83fbf694ae1be2b20fdbeaa14af40725900bffac56b40808d5bbfccd12d3ec98cf8be5023b0409ff0d6be92a36d4085fd22bfbce1363f1ca794be0e5db240f81bc2bea3927040b42f30be4f267c3fc9ca813c43e8b440d229cabe49747340b82eb73e0ffc603fa49aa13e32ccb6409d06ecbec42e7540052d403f7027e03ec952fd3e0450b74097610dbf10197540ab8e5b3f38b22dbe338ef83e5e41b640b94222bf783b7340cf65233fa16736bf0a35953ee5d9b440cb562bbf3e8a6d40bf0a383ecbcd7bbffd246ebc164ab240b36927bfcbb66a40fb1cb5be7cef61bf219a9ebe4961b040288e16bf39066940ff9f40bfdf27e3be9a40f9be34daaf402069febec21d69401ceb5cbf119f293e6a66f4be6fe8b0404c90d4be69f46a401c7d24bf731d363fdfc991bec324b340c68dbfbe68d66d40796c34be50f57b3fda9a893c92b4b540f667c7bedba9704096c1b63e3e71613f8b859f3e5f9db7400a1fe9be6d5a724084e3403fc005e23e7777f93e7424b8401ee90bbfe4427240cfeb5c3f347a29be486af43e3916b74088d520bf3d6c70403ee3243f23a235bf6563923e74a8b540e7fb29bfebc36a40d2c83c3ea8937bbf5c7286bc1914b340c02826bfd9fe6740cad9b4bead6862bfc82e9cbe7d26b140186015bf50586640d49941bfa723e5be475df4be2a9cb0403716fcbeb57166404f9f5ebf961b253e4fedeebef6a9b1408926d2be55416840bc4126bf9d48353fa0db8dbed4e8b340d2f5bcbe19166b406a0739bedcbd7b3f8c7f973c2f7db6401f9cc4be2bdb6d401e8eb63e98ec613f5c019d3ecb6ab8406f2de6beb4816f4026e0413f9205e43edf89f43e1ef5b840b56b0abf4f686f4068a05e3f88ec24be56f1ee3e52e7b7408c631fbfaf986d400aac263f5cca34bfe26c8e3e6671b6409a9928bfa5f767404e2b413ee75c7bbfb30291bc97d8b340cae024bf36416540fbafb4be9fe662bf2c7e99be40e6b140a52b14bfdaa46340a59042bf6326e7be415befbeba58b140c8b7f9be15c06340e24060bf5496203eea88e9be1366b24004b2cfbe7e88654006ec27bff175343f5b298abe6aa7b4401853babec14f68404c473dbed5897b3f53e9a03c3940b740b8c4c1be30066b40f86fb63e066b623fce459a3e9032b940022fe3be8ca26c402fd8423f3708e63e2586ef3e16c0b94042e708bf51876c400643603f246020befd89e93ebdb2b84025ea1dbfe8be6a40d85b283f36f433bf95ad8a3ebd36b740db3327bf8d2765401433453ef0297bbf4efa96bc8999b440769523bff27f6240ee3bb4be9c5c63bf264897be84a2b240c8f312bfe9ed6040f43443bfb508e9be1568ebbecf11b2403852f7be030b6140a88561bf15571c3ea754e5beb11eb340f635cdbe22cc6240255629bfc5ae333f144887be7762b5407aa8b7beb9856540ea5e41beca567b3fa154a83cabffb74044e5bebe542d684034f6b53e24e0623f9121983eb0f6b9409e28e0be5dbf6940897b433f42eae73eb098eb3e6587ba40fc5e07bf43a269400c87613f3f1c1cbe3159e53e837ab9401e6d1cbf24e16740a5c3293fc92b33bfbad9873e70f8b740d5ca25bfac536240e08e493ec0f07abf7f8fa2bce556b540ef4622bf17bb5f40fdd9b3be9dd463bfc7e594be3c5bb340acb811bf86335e40f2fc43bf53feeabe56d1e6be5ec7b240d1e5f4be8a525e40bef962bf58e2173ec749e0bec5d3b340a2b2cabe4a0c604047e72abf0ddc323f3fb783beec19b6403af6b4be0ab86240bdae45be1d1f7b3f68cab33c77bbb84006febbbe9f5065403c9ab53ee458633f8db9953e20b7ba408c1addbe30d86640a244443f16e2e93e57fee63efe4abb4009d305bf2cb9664035fb623f089f17be424fe03e973eba40a2ec1abf6cff6440b6552b3fcf5632bfb74b843e2cb5b840e95c24bfd27a5f408d014e3e9cb47abf4c46b0bc5f0fb640c9f320bf85f15c407e9cb3bec75164bfa52a92be240fb4404b7910bf95745b4044e844bf040aedbe3b89e1be2378b3408371f2be84955b40ee9264bfd13c133eb37edabe0984b440af27c8bebe475d40698e2cbf7801323fff517fbe84ccb640343cb2be66e55f4007fa49be2de67a3fe2f1bf3c5172b940740eb9beb36e6240576cb53e62d7633f42e9923e8c72bb407003dabea3eb63402832453f57efeb3e69afe13e8d09bc40424204bfb4ca6340ee94643ffef012be1a83da3ea6fdba402c6719bf7a18624057022d3f677831bf7130803e066db940e3ea22bf499d5c40fe1e523e5e7c7abf5a06b9bc09c3b640c89c1fbf87235a40b142b3be2acd64bf368f8fbe4cbeb44060360fbf64b158403fa745bfe407efbe03c6dcbe2f24b440a7f6efbe42d45840abf365bf6dbd0e3e0b66d5be902fb5406696c5becf7e5a4015092ebf3e2e313f225878be4e7ab740a87bafbe1d0e5d40e70a4ebe86af7a3f88c5c83c4b24ba40e017b6bedf875f40d216b53e6752643ffd50903e0829bc40ade4d6be02fa604066f1453f31eced3ebceedc3e25c3bc4064ad02bf24d76040e8f5653fe06b0ebefd69d53ec3b7bb4085dd17bf972c5f40f67e2e3f70a230bf7a65793e0921ba408b7621bf15bc59400814563e79457abfd9b6bfbcec72b74088431ebf18525740fec1b2bec54365bfdc358dbeb969b54029f10dbfedea55405d3c46bf9df3f0be5d8ed8be85ccb440d976edbec60f5640112667bf67610a3e8de5d0be5cd7b540bdffc2be8eb25740e5642fbf0a60303fb82572be5124b84060b5acbe51335a40880c52be9b787a3f7110d13c6ed2ba40681bb3be4e9d5c40b991b43ed1c7643f650b8e3ea1dbbc4024c0d3be78045e40d485463f35d7ef3eddbcd83ed578bd40ce1501bfa0df5d40a627673f830a0abef1ecd03efe6dbc405d5116bfd83c5c4091d82f3fc6d22fbf7952733e2dd1ba400a0020bf35d7564010895a3e8e047abfbb01d0bcfe1eb84032e81cbf387d54404f63b2be94be65bfc0888abe6111b640c9a90cbf31215340b40d47bfcb01f3be5931d3be1971b5405ef2eabe1148534079a668bf91aa053e23e9cabe607bb640f063c0befde2544047fe30bf727f2f3fc49369be7fcab84096e9a9be015557409e5e56bece3a7a3fe2dfdf3cae7cbb404619b0befeae59407b42b43e9b44653f41448b3e4b8abd401896d0be050b5b40c659473f02e9f13eb054d33e932abe404df7febe25e45a4074a8683f384705be60f0ca3e4c20bd40dec214bf394959401576313f6cee2ebf3cb16a3e0f7bbb4028851ebfd4ec534006bf5e3e6ec679bf3551dcbce9c4b840e8881bbf31a351404422b2bef73f66bf827887bef6b2b640295f0bbf85525040dce647bf661ef5be5178cdbea40fb640ce68e8be697b504053236abf96e1003e02bfc4be5319b740f4c3bdbe3e0e5240d18932bfb29e2e3f592361be896ab940a319a7be28715440eb685abe74007a3f9075ea3cb020bc402412adbecbba5640ac11b43e7cc6653f3322883ea232be40a165cdbe770b5840f534483f0d04f43e3d98cd3ef4d5be4026bbfbbe93e2574035266a3fd67700be93c2c43e45ccbd40003013bfbe4f56400408333f37092ebf561b623ebb20bc40bf081dbfc7fe504027a9623e5a8c79bf13e6e3bcae66b94031281abfc1c54e40f19ab1be49b866bf51f184be7350b7401c130abfa0804d40737448bfc415f7beade8c8be1aaab6402fdce5be95ab4d40404c6bbf82d1f83dd1debfbe2cb3b7409420bbbe38364f40b2dc33bfd3ca2d3f226d5abe6b06ba40f645a4becb895140de585ebec0c6793fe16df33c78c0bc401207aabed1c253409c88b33e983d663f89ac853eb3d6be403a31cabef207554025c2483f4efaf53e300ec93e0c7dbf40457bf8befddc5440994e6b3fadf3f7bd43e5bf3efa73be40719b11bf5a525340d459343f57332dbfa47e5b3e4cc2bc40328b1bbf390d4e40a8b0663ed44e79bf0a45eebc6704ba4068c618bf13e54b407415b1be0a3167bfbd5582bef1e9b740ecc508bfb0ab4a408a0a49bff415f9be3a09c4be9440b740ea4ce3bec4d84a40cf816cbfbda9ef3d3a94babe0249b840187ab8be185b4c40fc3a35bfa6f02c3fe50653be3a9eba40cf6ea1be1b9f4e40b75b62be4f8a793fb689fe3c1f5cbd4062f8a6be41c75040e905b33eefb5663f7e17833e9576bf4059f9c6bea4005240a858493f72faf73eec2fc43ef21fc0404838f5be90d35140a9836c3fbabceebdd49dba3e8417bf408e0510bf3c51504020b8353fb5562cbf822b543efe5ebd401b0c1abfa4174b40a4f96a3eb50a79bf53affebc579dba40436317bfac00494045b0b0be60af67bfbfa97ebeb37eb840877707bf3ed3474089cb49bf3035fbbe2624bebe58d2b74062bbe0be7c0248402fea6dbf63eee53d7012b4be1bdab84049d1b5be5b7c49407bbd36bfcd082c3ff7d649be3c31bb4012959ebe86b04b40c47a66be1b4a793fce86063de3f2bd40c2e6a3be7ec74d400db2b23ede35673fa6f57f3e8711c0403abec3beecf44e402c1c4a3fbe1bfa3e9540be3ee2bdc040e6f1f1beaec54e40d4ec6d3f74e9e4bd3c19b43e1fb6bf40006e0ebfcf4b4d402e40373f156a2bbfe4de4a3e93f5bd40088b18bf511d4840d1fe6e3e75ca78bfd11405bd4330bb407bfe15bfe91746404747b0bea72f68bfe66b78be880db9400d2806bfaef644405a784abf3752fdbe2561b8be365eb840ce28debe1928454025346fbfea4fdc3d25e3adbe4465b9401a28b3be4b994640ba2338bf9b252b3fa76241be39bebb40f4ba9bbe3fbd4840f25f6abe140d793f71c80b3d8983be400ed4a0bea7c24a407455b23efeb5673f58a6793e44a6c040e980c0bee2e34b40b7ca4a3f5f36fc3e317cb83e9655c14037a8eebe77b24b4042376f3fdf3fdbbd81e7ad3e884ec04076d40cbf45414a40daaa383faf822abf2c53423ecb87be40da0917bfa31f45408ad5723eae8c78bf5a4409bde5bebb40aa9914bf162c43406fa8afbee0a768bfa11773be1f98b94079d804bf46174240f3ed4abfe950ffbe5c8bb3bed9e5b840a695dbbef04a42402f4270bfa82fd33d55b3a8be2becb940e47db0be56b34340136339bfb64b2a3f57343abee346bc4070df98bed1c64540ae3b6ebe97cf783f61d5103dc90fbf40d2bf9dbe5eba47407db5b13eae2c683fc47c743e8f36c1403242bdbe2ecf4840f03f4b3f1633fe3ef7adb33ed5e8c1407f5debbe849b48406f44703f1415d2bd92bca83e83e2c040a03a0bbf1e334740e1e8393fbda629bfa7463b3e9515bf40cc8815bfa41e4240dae6763e7c4878bf9b6710bd2c49bc40083513bf403d40408c18afbe182369bf28446dbe681eba40fe8803bf16353f404d7e4bbf0eb300bf12eeadbe3169b9404a02d9be0e6b3f409b7271bf3698c93db787a2bebf6eba40ffd2adbe8aca404059bd3abfbf65293f357c31be29cbbc40dc0296be4acd4240aa3772bed98d783f26d0173d9297bf4064aa9abeaeae4440092fb13e4fa8683f7b986e3e56c2c1407702babed8b645409fd14b3fab24003f5b0cae3e8d77c2402b12e8bee0804540ce74713f6368c8bd2192a23eff71c140bba009bf642144407b453b3ff0bc28bf3691323e6a9dbf404c0714bf68193f4064007b3ede0178bf3b7018bd98cdbc403dd011bf914a3d4098a1aebe65a469bfc69766beec9eba40c23902bf514f3c4010254cbfadc901bf0886a7becae6b9402e70d6bea1873c402bb972bf5f9cbf3dc4889bbe88ebba40c529abbefddd3d40f4223cbf2278283ff7bf27be8e49bd40dd2793bea2cf3f40fd2376bece4b783f46c41e3d6019c040fc9597be799e414054cab03eb82a693fe9b4673e0c48c240f1c2b6beb99942400a7b4c3f213b013f699ba73e2e00c34048c6e4be6961424014bc723f7f58bebd738f9b3e70fbc140590608bf0d0b414036b13c3fe3c927bf19ad283e701fc040138612bf46103c4058e17e3efebe77bf881b1ebd504cbd40f56b10bf61543a40580baebe90226abfa44860becf19bb405feb00bf506639408da44cbf62d702bfe4b6a1bec95eba405ce0d3be02a13940fcd073bf3cf9b53d434b95bead62bb402283a8be0cee3a4046663dbf7a92273fe42a1fbe36c2bd405a4f90be38ce3c401af079be670b783f566a243d5695c040968394be1d8a3e40f73cb03e14a8693f6f69613ed7c7c240c284b3be2e783f40b4fb4c3f6847023f47cea13edd82c340247be1be7c3d3f400cd4733fcea9b4bdbd50953ef97ec240306c06bf72f03d4011f73d3f5ee026bf7313203ebd9cc0401b0611bf06043940e554813e137d77bfd0f522bd65c6bd40fc080fbf695b37400254adbe479b6abf538b5abe1f90bb40663cffbec87a3640b7024dbf61db03bf41779cbe37d2ba409452d1bef0b7364033c574bfc09dac3d31a38fbe36d5bb4014dea5be85fb374007933ebf9db1263f264b17be2f36be4012788dbeeec93940d7b87dbe02ca773f40302a3d870cc140517291be8b723b409d86af3e111f6a3f67d95b3ecd42c340e247b0be2c533c40085a4d3f334a033f96959c3eb500c440b431debe04163c405bc7743fe843abbd58ae8f3eb6fdc2401bd304bf6fd23a40e8223f3f17fd25bf9b59183e3d15c140a8870fbfadf435405659833e0c3377bfd4d12bbdc43bbe4092a70dbfb05f344077afacbe3f186bbf711754bec901bc40f2a4fcbec28c33405c804dbf95ee04bfa21f96be0141bb403ec7cebe74cc33400fe175bff1a8a23de39d88be1143bc40f63aa3be710635407edd3fbfd4c1253f73530dbe65a5be405ea28abecbc236403fd080beb983773fcd65323dde7ec1408b628ebec85738403ff0ae3e379d6a3faa37553ed9b8c340bc0cadbeb62a39409bd94d3f485e043f8a34963ea179c44071eadabe04eb384051e3753f9c33a1bd49a9883e9177c3405d3b03bf07b137407d71403f270825bf9f530e3ecd86c140650a0ebf39e132401753853e80e976bf3ffe33bd56aabe40b2470cbf57603140f419acbe989b6bbfd6c04cbec06cbc40ce11fabe6d9b304002054ebf630b06bf1e2a8fbe21a9bb408341ccbeb1dd304079ff76bfb573983d501281be33aabc40509ea0bed40d3240272441bf84ce243f1feb02bec30dbf403ad387beabb7334038b282be6d3f773f9a55393d3aeac140a1588bbe8d383540206eae3e42216b3f77a84d3ed027c44035d6a9be77fd3540fc604e3fd179053f40388f3e6febc44082a6d7be33bb35407702773f21ee96bdfc17813e5deac340dba401bf108b344062be413fce0e24bfefb8033ea6f2c1405a8f0cbf8bca2f40cf35873e66a376bf87fa39bd4f13bf402dea0abf2d5e2e407357abbed0176cbfa24146be33d2bc40ac83f7be92a72d40fb594ebf2b1907bf162a89bec20bbc40f8c0c9be7bec2d4032e977bfa7c08e3dd03375bec80bbd40de069ebe95122f40844742bf7de5233f93e6f3bd7a70bf40220985be8ba930404c8d84be57fb763f6ba83f3dd14fc2407c5388bee9153240aeb2ad3e3c9c6b3f993d473eed90c4402aa4a6be84cc324008b74e3ff585063f583c893e5e57c540de66d4be9b873240eaeb773fa12f8dbde641753e5857c4407c1000bf816131402ce3423f1d2223bf2398f53d5b59c24011170bbf0cb12c40fe11893e525d76bf49a03fbd3f77bf40718f09bf91592b40c680aabe9d906cbf1a0d40beb032bd40dcfaf4be90b12a40db9a4ebf452108bf5f7083be7069bc405245c7be33f92a4041bd78bf6e31853dbacf68be5d68bd40db739bbe1f152c40555d43bf35fe223f698de2bd17cebf40284382bee0982d40246786be0bb6763f914a463d33b0c240685285be5bf02e4073dfac3e25136c3fff38413ec2f4c4406d76a3be5c982f406df84e3fa58c073fee89833e02bec540f82bd1beb9502f4042bf783f0d9583bd13e8683e15bfc44070fdfcbecd342e40b4f8433ffe3722bf0390e43dd7bac240daa109bfc6942940280a8b3e740e76bf15e549bd12d6bf40c93708bf9252284013baa9be3e0e6dbf4ffd38be248ebd40ea77f2be75b92740d8f64ebf2e3a09bf55da78be17c2bc400bcfc4beea03284065b379bf56f3753d293a59bedcbfbd40b6e598be81152940dc8b44bf0407223ff360ccbd8526c04071037fbeba852a40a34b88bef66b763fa8854f3d4a0bc340da5582beeec72b400a29ac3e6f926c3f18e6393e3853c540824da0be0b612c40a0564f3f94a6083f3af2783e451fc64062f6cdbe96162c4081b5793f6f7c72bdc251593e8021c540b8dff9befe042b40292c453f973a21bf7f2fce3d9714c340dc2f08bfdc7426404af38c3e81c175bf2aa052bd562dc040b8e306bf7a48254094f8a8be16926dbfad0331be2be2bd40a3fdefbe9ebe2440d34f4fbf095b0abf3ec569be5b13bd401b63c2bef00b25401ea17abf8e2d613d65e948bee60fbe401e6396bee81226403baf45bfbc0e213f15f4b5bd5577c040489879be146f2740bb1c8abef024763f08ce563d965ec340d7c87ebe769b2840bb7bab3ed0166d3ffaad313ec1a9c540382e9dbe52252940aab24f3f59c5093fe4cf693e9178c640c2c8cabe00d82840e7a37a3f1f995dbd9bf1483e067cc540bfc8f6be08d12740a855463fa33b20bf9343b73d7168c340e7c106bf4d5223402fca8e3eb87775bf1b4459bdd87ec040c09305bf2b3c2240c607a8bea10c6ebfaf2c2abe8730be40958bedbee2c12140bf794fbfaa690bbf0a0d5dbefa5ebd4056ffbfbe28122240645c7bbf93974d3d17213bbe395abe4091e893be530e234047b246bfaa21203f25b4a2bd4fc2c040e53c74be115624402eec8bbe12dd753f99015e3de8abc34082f578be336c2540a492aa3ed58f6d3f76f42a3e38fac540aa169abe7ce62540cfdd4f3f37d20a3f00225d3ec6cbc640eba2c7be36962540a95e7b3f3ceb49bd4b303b3e87d0c540b0b9f3be0b9a2440d259473fd24a1fbfe82fa43db5b6c3405f5805bf522d20406a9c903e892d75bf74d85fbde6cac040304804bfda2d1f405808a7be8d846ebf3f7a23be8779be400822ebbe76c31e4091944fbf7f740cbf339150be3ea5bd4098a3bdbec9161f4013077cbf362a3a3d648f2dbe219fbe40987591beff07204099aa47bf2b351f3f9c9b8fbdbd07c14027f06ebef63a21401db98dbe0c94753f459a653d8cf3c340e63073be6e3a22403f99a93eda056e3f9172243eeb44c640c90697bed1a4224086f94f3f70db0b3fb7b5503e3419c7403a85c4be7f512240c1087c3fa76636bd5da82d3e511fc6403bb3f0be496021409a52483f095b1ebf1367913d48ffc340a4f303bf08061d402f82923ec9da74bf172a6bbd6711c140620103bfa71d1c405610a6be45016fbfe6dc1bbe12bdbe409ec1e8be7bc31b4063bf4fbfc18f0dbf7f6141be12e6bd407350bbbef5191c4055c67cbf9677253d0eaa1cbe85debe40bb0a8fbe0e001d4099b148bf4c391e3fe50f6fbd8647c1401db369bee21d1e403c8b8fbeac46753f9db46f3d6735c440267c6dbe43061f404cb1a83e4f846e3f9e881c3ebc89c64037ff93be6f601f407626503f7bf70c3fe967413ebc60c7406370c1bef5091f4010c87c3fc57321bd67c01c3e4968c6401cb6edbedc231e40725e493f56581dbfab2b723d913fc440609402bfd2db19403d5a943e1a8a74bf7eee74bdd54fc1405dc001bf1b0b19401e15a5be86836fbf054d13beb4f8be404c6ee6be8bc1184067e14fbfacb10ebfdf1731be081fbe40360cb9be3c1b194047787dbfb67e103dcafe0abef415bf402fb08cbeebf5194019ab49bf583d1d3f4e613ebd297fc140ac9764be10fe1a40594c91be7dfc743f11c8773de56ec440b8e767bec7ce1b4041caa73e26076f3fc7b0133e06c6c6404a0691be57181c40b04b503ffd160e3f950f313eb29fc7406168bebea6be1b404b7a7d3fae600cbd7e070b3ec6a8c64068c4eabef7e31a40625d4a3f3a551cbf955a403d7479c4400b3b01bf8eaf1640c024963e473c74bfb99c7cbd0a88c140468500bff7f615404cf0a3be59fc6fbfa2ef0bbe3b2ebf40c826e4be5dbe154041db4fbf89c10fbff94223bee851be40b7d3b6be5d1b16408dff7dbf5a65f93ce1e3f7bd3847bf4054618abe74ea164097894abf214c1c3f631e14bd78b0c1408d935fbe86dc17406a0d93be02b1743fb0ff7f3de2a1c440a06a62be1d95184022afa63e887e6f3f096f0c3eb1fbc64014198ebeb7cd18402d47503fef240f3f0a44233e04d8c740256cbbbeb77018402f017e3f1bf9f0bc11fef73db4e2c6408adee7bea0a11740653d4b3fdc5f1bbfc865163d44adc440fecfffbe73811340e6ea973efeed73bf452182bd56bac140c4a0febe6ce112408cbba2be5e7270bf17b804bef85dbf403cebe1be1eba124011c54fbf51cd10bfe9ab15be027fbe40aba6b4be8c1a134048757ebf2023d23c5e33dabd9f72bf40811d88bee1dd1340b65c4bbf735b1b3f982fd4bcc2dbc14034a55abe81b9144044cb94be5964743fda56843db0cec440a7035dbe885915400383a53ebbf26f3f4368053e0e2bc7405b378bbed68015408132503fd12e103fd7ba153e040ac840ed7bb8be6820154068767e3faa87c9bcb961da3d6616c7401805e5be135d1440a1114c3f776b1abf7711da3ce4dac4404037fdbe93511040e4b6993edf9873bf1f1888bda0e6c1401e44fcbe8eca0f40d784a1bea2eb70bfc751f9bdd087bf4053bcdfbee6b40f4055b04fbf57e511bf909c05be3ca6be40ac85b2bedf18104060ed7ebfc5f7a83cb377b6bd0f98bf4048e585be4bd0104081304cbf4d5f1a3fc7735cbcea00c240c5cd55be199511404b8696be8014743fe5c0893d2ef5c44009b457be1e1c1240c95aa43e4f6d703f2f27fa3dfe53c740cf6188bec6311240871f503f1947113ff792053e9235c8407798b5becdcd114050ee7e3f87e49fbc88a6b63dbf43c740dc38e2be6116114083e94c3f756819bf6588663cfeffc44012aefabed41f0d40cf7b9b3e0c4473bf00c08dbda50ac2406cf7f9be64b20c406040a0bed96971bf1d3ae7bd94a9bf409b9eddbec8ae0c40998d4fbfd50413bf4c4ce8bd6fc5be407c77b0be60160d4026557fbf92127e3cb75490bd5bb5bf4040c183be9bc10d4026f74cbf0f61193f555e0abab41dc240f31f51be126f0e40cf3698beabc6733f466d8e3d0d13c540418d52be82dc0e40dc28a33e7cec703f3660e73d1e74c740709f85be1ee00e40e4ff4f3f7564123fce0fe83d4358c84090c6b2be86780e4028567f3ffc6a6bbcd761903d5768c740ce7cdfbe4bcd0d408cb44d3fa86218bf0a4d593ad91dc5400635f8beb7ec09402a369d3e1af272bfc55092bda527c240c5baf7be579909405edc9ebe55e071bf4c0fd7bd7cc4bf400c91dbbe24a80940804c4fbffc1414bfdf94c9bdceddbe40b879aebe77130a40579c7fbf254b2e3c988e5dbdb8cbbf40f3ad81be50b20a4061a84dbf766c183f361f343c5933c24011944cbe03480b40e2e499be1578733f5c12933d8d29c54092884dbe639b0b405dd1a13eee61713f4e4ad73db68cc74002ee82be968c0b4009c14f3f6572133f2362c93d6473c8405605b0be43210b40fd9c7f3fdc451bbc9db05d3d7a85c7401cd1dcbe6a820a40da674e3f2c6917bf8ce82ebc1e35c54091ccf5be82b806404aeb9e3e1fa072bf84be96bd463ec2405a8ef5bea57f064007659dbe8e5272bf909bc7bd29d9bf402493d9be36a1064054f84ebf631e15bf9424acbdf7efbe40098bacbe62100740c2ce7fbf9ce1bf3b8fed1cbdc4dbbf40c4527fbeb4a20740f34b4ebf1e7a173f3a1cb63c7c42c240ae2548be46200840598f9bbe2028733fca03983d5439c5401da248be235908402065a03e81d2713f2c37c83d719ec740444c80be92370840ce6e4f3fc279143f1511ac3da387c8406054adbe66c80740fcce7f3fd41d99bb583a1d3dd69bc7400736dabe14360740130d4f3fe67216bfa928b2bcb345c5409675f3be55830340a09ba03eac4972bf9fbf9cbd6e4ec240fa72f3be6b65034046e19bbed7c572bf50e7b6bd84e7bf409ea5d7be1b9a03405f974ebff62d16bf0d0f8bbdd5fbbe4016acaabe420d044068f27fbf78ef753adeb3a6bc66e5bf4056687bbee792044015e44ebf1081163ffbad0f3d014bc2400dd643befbf70440d6319dbe3bd6723f9cba9d3d4642c54043db43bee5150540d8ed9e3e4b46723f1733b73d30a9c740fc757bbe35e10440920f4f3fcb88153f7fdc8a3ddf94c84086b4aabe0e6e04404ff27f3fdf5297390375a73c4eabc74072acd7be69e8034037a84f3fc67315bf0bd40dbde54dc5407a32f1be6a4d00400245a23e0cf171bff271a3bd7d56c240726bf1bef94a004081479abedb3c73bfba1fa4bdfbedbf4088ccd5be27930040b1214ebfc14517bfede44bbddbffbe4093e2a8be640a014072ff7fbfae7086bb135cb4b911e7bf40d3aa77be23830140776b4fbfe083153f3947493d4d4bc240a3b43fbe46cf014098cb9ebecf84723f144ca33db542c540b3423fbeb7d10140bc629d3ee1be723f128fa33d37abc740888076be89890140589c4e3f409f163fb8054b3d5799c840392aa8be4c1201400dff7f3f82f3af3b7b52c83921b2c7406437d5be8d9900409b32503fc16e14bf77c848bda44dc5408604efbe442efa3f49e8a33e499b71bfb2eba8bd6656c240f278efbe4f61fa3f4a9298be7baf73bf7e2e92bd85ecbf400409d4be6919fb3f6b934dbfb05318bf61e606bd00fcbe40a22fa7be4010fc3fa3f27fbf4de612bca337943cb9e0bf40981c74be8be7fc3fa2e14fbf368e143fa1497e3d5043c240fcc33bbe0e4dfd3fef61a0bed633723ffe70a83d8e3ac54023db3abe041afd3fc4bb9b3e2631733f816b913d6fa4c740feba71bee961fc3fde104e3fa0aa173f2efd053df494c840e2b6a5be126bfb3fd6f17f3fd3f0273cd52894bc3bb0c74039d8d2bec793fa3fa6aa503f507313bf834f7ebd5346c54076ebecbe76c1f33f3b86a53e4e4671bf5ffcadbd824fc240b89aedbe422df43f7dca96be561b74bfddd581bd6de4bf400059d2bec60df53fe6f24cbfeb5519bf59ba8fbc8df1be408d8fa5be400df63f8cd17fbf6ace5fbc43ae0f3da7d3bf4093b370be1acaf63fc84950bfd89d133fec13983d5d34c240fcf837be32fcf63f6af3a1be3fe1713f8b02ae3d2e2bc540789a36be6690f63fbe009a3eb59b733fdf71813d4396c740e81d6dbee2aff53fb1724d3fd7aa183f5d5e8e3c2389c8406758a3be68b0f43f6ed07f3f852f753cc57d0fbd09a7c7402c8ed0be8ef3f33fe514513ffa7e12bf43bf97bddb37c54038e8eabed054ed3fe91ba73e59ef70bf71c6b3bdba41c2409fd1ebbe2dfaed3f33ef94bed38574bf1edc61bda1d5bf403dbdd0bec603ef3f9b3d4cbfb5571abf19dcb0ba6de0be400003a4be280cf03f8f987fbf489896bc3a5f593dc6bfbf4008716dbe58aef03fc5a050bf94ab123f8890b23d5d1ec240f55434be62acf03ff07ba3be9a8d713fee02b43d7e14c540268232be0507f03f4332983ea805743fca4d613d9780c740eaaa68be6cfdee3f91bf4c3f28ab193f2a8d9c3acb75c840b20fa1be0af5ed3f34977f3fa180a13cf10059bd7296c740305acebeda52ed3f5e6e513ffd8711bf030ab2bd8521c54070fce8bedae8e63f1c9da83eed9370bf51babbbd5f2cc240651feabe9dc8e73fa9f692be5df274bfe5a33bbd79bfbf40ee37cfbef8fbe83fb3664bbfb9611bbf3fce903cfbc7be40d28ca2be840dea3fbf387fbf9771bebc7c50983d6fa4bf40c45b6abec594ea3f9cda50bf5cb3113f3ebdd13da000c24045df30be125eea3f29f5a4be2339713f1ab2ba3dc5f5c4405a992ebe4f7ee93f7241963e7a74743f91e9383dab62c740466864bef44ae83fbde94b3ffdb41a3f5d7193bc295ac84041df9ebe6839e73fd1367f3fa3bfc93cb33598bdb57dc740323eccbe27b2e63fa0a9513f1a8810bf27ded1bd4001c540ca2be7be317fe03f131faa3e223b70bf5a47c2bd7c0dc2402888e8be459ae13f2ed890be545e75bf67cb13bd14a0bf40dccecdbe0df8e23fcd724abff4671cbf8ae7153d63a6be400d35a1beed12e43f14bd7ebf9588e5bcccc7c23dc87fbf40e08767beda7ee43fee0351bf51c0103fc96bef3d3ad9c140f7ad2dbe9912e43f4e6fa6bea6e6703f7b43c03dfeccc4403df52abe85f7e23fce2e943ef8e0743f97da0f3d663ac740d26760bebd99e13fd1f84a3f66b81b3f026e17bd1734c840bacd9cbedd7ee03f42ba7e3f7cf0f03c90d7c2bdb25ac740d73ecabef012e03f8dd2513f9d8e0fbfcf40f0bd81d9c4405e74e5bea417da3fd59eab3e07e46fbf2502c8bd6ce7c1403609e7beb86edb3f6fb28ebeebc075bf9e91e3bcb879bf40147dccbe85f7dc3fc77749bf6d5d1dbfc16c583ddd7dbe40d9f39fbe001cde3f2b387ebfc24605bd0bbde73d0e54bf40d0e064be726cde3f922451bf12d60f3f050d053e7baac1406daa2abe22cadd3fafe1a7be1f92703f4c7fc63d909cc440bb8027be0e73dc3fbd16923eb942753f3c17dd3c440ac74000995cbe41eada3f69004a3f0bac1c3f79c159bd1f06c840bcd59abec6c5d93f34357e3f260b0b3d52b4e7bdee2fc7402d59c8be5475d93f8af5513fb5a00ebfc94b05be39aac44015d7e3beb9b2d33f0717ad3e818c6fbfa7e2cdbd22bac1406aa3e5be7146d53f99788cbe5b2076bf6a489fbc584cbf405043cbbed4fad63f486748bf314e1ebfb9b98d3d5d4ebe40d9c99ebe3129d83fb79a7dbfc99717bd4b86063e3321bf40c76762be0c5ed83fd23351bff8ec0e3fcb90123e5374c140ead527be3385d73f954aa9be363d703f8ef1cc3d6a64c440403d24be7bf1d53f2eec8f3e5ea1753f2d749a3c34d2c64074fd58be183dd43fe0f2483ff39a1d3fc3408ebd2fd0c74031f898bebb0ed33fda977d3f806b1d3dc77106be59fdc640288ec6bee0d9d23fd307523ff4b30dbfc89c12be5873c440d454e2beec50cd3f2d6bae3eee306fbf2762d6bd8e85c1408e57e4bee221cf3fff188abe957f76bf5d2527bce717bf404122cabe6702d13fe22547bf24431fbf63f3b43dd717be40a7b79dbeee3ad23ff2c27cbf5d452abd37af1c3e2be7be40e01d60be1954d23f941751bfe7ff0d3fb89b223eb436c140983125be4a44d13fa49daabe4de76f3fe664d43d7e24c440252c21be5473cf3f1d8e8d3ea202763f88c5153c2492c640bd9655becf92cd3f85b1473ff98f1e3f30d0b5bd3592c740f93597be485acc3f81bf7c3f4d44303df29d1cbee1c2c640b2dec4be1d41cc3f68eb513fc3c00cbfade422be1732c440b3f2e0bec6f4c63f51bcaf3ec2d66ebfe02adebd0e47c140562be3be9f03c93f108487bec2de76bf7950cab7e0d9be400022c9bec310cb3f47b945bf713620bfe674de3dd4d7bd409dc89cbe9c53cc3f22c17bbf4bba3cbd23a0333e79a3be40961f5ebed850cc3f96e250bfb3160d3f49c0323e07efc040c2dc22be8609cb3f12f3abbe8c936f3fb7b9da3d10dac3407b6b1ebeadfac83ff6fb8a3e2463763f1a78c7ba3e47c640287e52be89edc63fbc46463f34811f3f4089dfbd4a49c740779895beb0aac53f1ebc7b3f3ac4423d57a933bea57dc6404a51c3be74adc53f57b2513f79d10bbfa78433be7ee8c340d4aedfbef59dc03f9513b13e717e6ebff8c0e4bd9100c140451ce2be26ebc23f96ef84bedd3577bf69930f3c2094be40213ec8be5825c53fc85044bfe11a21bf1215013e2790bd40d5f59bbebe72c63f00c37abf9c384ebd7889473ef357be40ec5a5cbe0054c63fe8ac50bf28360c3f2122413e349fc04099c320beddd4c43f8640adbe6b3e6f3f1c78e13d2187c340b6e81bbeac87c23f6073883e39ba763f6cce27bc92f3c540fda44fbe7b4dc03ff8e0443ff063203f409801be8bf7c640cc1a94be1400bf3fc2bd7a3fd64b543dfa8c47bebf2fc6402ce3c1bed21ebf3fc07d513f65ed0abfb9d941be1e97c3402889debea54cba3fbc64b23e5c266ebf653bebbda5b2c040102ae1be8ed8bc3fd94d82be108877bfd7c98c3c2d47be40b675c7be3240bf3f17d942bf28f721bfcf48123e5441bd40903d9bbe6a98c03f61b279bf82455fbd4ad35a3e1f05be4011cb5abebc5dc03f7d6850bf2a590b3fae3b4f3ec447c040c8e01ebe91a6be3fce81aebee8e86e3fc68ae83d3e2cc340f79e19bea81abc3f3de1853e4a0c773f333997bcb597c540ac074dbe04b3b93f6e6c433fbf3e213f92bb12be8e9dc640fdbb92becc5ab83f68ad793f8560653d01c95abec3d9c5400594c0be7a95b83f993c513f6f0d0abf06c04fbef43dc3407682ddbe5c01b43fca93b33e0bcd6dbf8045f3bd445dc0406755e0be4eccb63fd10f7fbe6bd777bf22a4d53c04f3bd4050c9c6bec261b93f0f3241bf61d022bffd12253e58ebbc4048a09abe11c5ba3f0e6978bf2c3670bda718703ef8aabd40e57059be846eba3f79fa4fbff97c0a3f3bc85e3eb2e8bf4039351dbe2a7fb83f60aeafbe52936e3f8c3cf03d62c9c240568f17be38b4b53fd21e833ec75c773f1295e1bca233c54083a74abec41eb33f51c6413f7817223f099625be4e3bc640ca7c91be75bbb13f1964783f5562763da30670beae7bc540a164bfbe0312b23fdcce503f882d09bf10565fbe6bdbc240489edcbe54bead3fa2a3b43ea8736dbf135afcbdf1febf4022a2dfbe8dc8b03f090979beb32478bf9143133d3796bd40293ec6be128cb33f604a3fbf9ba623bf0d013a3ecf8cbc4022259abea5fab43f0bd976bf977380bdc2d2833e1948bd40295e58be4188b43f1c5d4fbf59a4093ff9b26f3e8b80bf4077d41bbe9660b23f60d0b0bede3e6e3f3ac6f73d055dc240c4cc15be5d56af3fc310803ee8ab773f5da41bbdbfc5c440b69448bed892ac3fecdd3f3f09ed223fddaf3abe27cfc540626390be4524ab3f5cd2763f3491833da8d383bedd13c5407159bebeaa96ab3fad2a503f7c5008bf16b370be2f6fc24054dedbbe2685a73f00c4b53ebf1b6dbff9fa01be5a97bf40f411dfbec0ceaa3fd8d672be056d78bffcd7393d7c30bd403dd6c5be7ec0ad3fe85a3dbf127324bf38684d3e7025bc4094ce99be793aaf3f414575bf147688bdf7a68e3e37dcbc40ba9857be52acae3fbebe4ebfacd2083f753f7f3e010fbf40f3c41abe544cac3fa5f5b1be96ea6d3f60ccfe3dd6e6c140b55d14beba02a93f32f4793e0bf5773f66ed42bdb44dc44022d546befc10a63f52f03d3f28b8233fa01d4ebec058c5403a728fbe0197a43f593d753fd1938b3d20ad8ebef9a1c4407274bdbe2825a53f3f884f3faf7b07bfb03a80be23fbc1400640dbbe8354a13fd5dcb63e5fc86cbf414805be4c28bf40c2a1debe84dda43f98dc6cbe10ad78bf57685c3d89c3bc40988cc5bea4fda73f3e7c3bbfc82d25bff3c05e3ee8b6bb40349599be4583a93f71ba73bfbedb8fbd8f6f983e0569bc40840d57be94d9a83f3d1a4ebf3b0e083f87db863ed795be40a0f119be6141a63f7bf3b2beed986d3f0b4f033eae68c140292e13be60b8a23fbb2d743e6235783fe67263bd71cdc3407b5845be40989f3f441a3c3fef71243f211d5fbe12dac440a3a38ebe9f129e3fbeb8733ff6f7923de64a98bef527c44015b2bcbe50bc9e3f12ed4e3fd6b506bfe93687be6a7ec14040cadabe2a2e9b3ff171b73e188e6cbf8e8208bedcb0be40154edebea1f69e3f698e67beaed978bffc97803dc64ebc40e354c5bedb44a23f879e39bffaaf25bf7af8703e3041bb40a56899be9cd5a33fbd0572bf281095bd13a7a23e12efbb408ca056bed30fa33fab324dbf7d83073f8c598e3e0216be403c4e19be0a3fa03f4169b3bebc5f6d3f5334073e90e3c040924612be93769c3f251b6f3e7d62783f89ea82bda645c340f53844be5a28993f44493a3f03f4243fb0cb70be3c53c440ba088ebe9897973f6010723f652d983d7939a2be5aa5c3401a21bcbe615d983fbe134e3f3f2a06bf18618ebeabf6c040238bdabe5616953f5286b63eeb6b6cbf57e310bea42ebe404111debe541e993f42d262be83e878bf58e1983d6ed0bb40fa18c5be47999c3fa43c37bfd7aa25bf9861863e98c3ba40ef2b99bedf329e3f9a446fbf316593bdc84bb23ed06ebb40b42256bea94e9d3f362b4bbf3fb4073f80e0983eb190bd40d6ca18bed0439a3fa1ccb2be5a686d3f2c7b093eb858c0409abe11bed23b963f306b683e6276783fae2ba5bdeeb6c24028af43bedfc0923fac57373f9b4b253fafa187bec4c3c3409fc48dbe4727913fb7096f3f26539c3dac0db3be8c18c34035dfbbbe7d0b923fa8074c3f4cc005bf2c289bbe165ec040567cdabe421a8f3f3c34b73ebd436cbf718d11beea9ebd402b0edebeeb77933fd2385bbe860d79bf4ec9b33d8d46bb40b229c5be702c973fe6b534bfc31626bfb285913e523aba40fd5099be84cd983f42156dbf340d96bd527bbd3eabe0ba40028756bed7bb973f06dd49bfcea2073f30e09f3e10fabc40d83019bef45f943f2a84b2be54706d3f25170a3e3cb9bf402e0d12be4b02903f20fe633e7d6d783fb225bfbd9911c2401ed643bec64d8c3f524f353f6a32253f039992bed41dc340c5c38dbeb2ac8a3fc2d86c3f9b0f9c3dc35bbebe7b77c24042d1bbbe5fbe8b3fbdb54a3fda9b05bf3a66a2be25c5bf40bc94dabe981e893fc3cfb73e6d236cbf2cc411bee70ebd40ea2bdebe7cd38d3f2be356befb1779bfab55c43d2ebcba403c4ac5bef8c2913f3d4233bff32326bfa64f983e23b0b940b17099be396c933ff5876bbf4ce695bd2d14c53e2951ba4022be56bed02c923fbf9548bfcba5073f3426a63ebd61bc403f5c19bebe7e8e3f4da6b1beca696d3f982b0f3efb17bf40e42d12bedac9893ff11a613e4e77783f565fc9bdb46ac14040f143be5eda853f00c4333f7d64253fbe2c99bebf76c2402cd28dbe1d31843faf4d6b3f28a19e3dfdbcc5beb9d5c140cce3bbbe8670853f4ef4493f894f05bfd213a7be4122bf4024eddabe0035833fc9d2b73e71f16bbfc6ae16bef076bc40f487debe0d4c883f93a24cbea82079bf83d1e93df52aba400ca7c5be687d8c3f8c272fbf0b3a26bfbe01aa3ee71eb94020cb99be142f8e3f465567bf935e95bde013d83e29b9b940bd6957be6abb8c3f60bd45bff4c8073f55dbb23ecfbebb40a1fd19be62b0883fdbdeb0bebe706d3f1e46123e206abe40ffc712be5599833f3ee4573e4f6b783fee70f1bd1bb6c040ce8944bef4cf7e3f75a72f3f6476253fb1ecaabe29c2c140d5208ebe9d6c7b3ff811673f1ddda03dacafd8bee727c1401737bcbef0537e3f2a55473fb5f504bfb844b4bea66cbe402aaedbbef1db7a3fc00eb83eb9c06bbf1c431abea2cfbb40de4adfbe83f7823f6a653ebeb91579bf331e0c3e668cb9404a6dc6be0d75873f009229bfac6126bf44c7be3e3180b840de949abe812f893faaee61bf1cbb96bd5ac9ed3e7611b940e60159bedd7d873f717842bfedbc073f71cbc03eb608bb4059961bbec605833f3eabb0bef4686d3f6a07143ebaa5bd40f15c14be75f87a3fe1ba4b3e3e3f783f621111bef6e8bf40171846be62fd713ff68b2a3f1236253f435dbfbe2bf5c04078e48ebe7a886e3f99d0613f94fb9f3ddddaedbee663c04064f8bcbec1eb713f850d443feed704bf1871c2be34a8bd407acddcbe9f896f3f5f8eb73e5aa76bbfb9001fbef31abb404e64e0bea1817b3f672a31bedde878bf26e9203e72e1b8401484c7beff8a823f5af523bf4b3326bfd00ed23e3ad5b7403dad9bbe004f843f403c5cbf17a795bd1c29013fbe5cb8400f3d5bbe2c60823fe7ee3ebf85c7073f1758ce3e3a44ba4035df1dbe0bf77a3ff998b0beeb516d3f8da7163e7bd1bc408cb116be09ff6e3f93f23d3e0f0b783f059927befc0abf40ff7148beac6a653fd4eb243f1afb243f06e3d2be3417c040d80f90beaae2613f461a5c3ffac39e3d823701bfb08fbf408e1ebebe52c0653fcf60403ffedd04bf0f8cd0beffd1bc40934edebece88643fe60fb73e82916bbf3a3f23be3256ba40dcd8e1be8f62713fe06822bee8a078bfb90b363eb127b840e3f5c8bedd8e7b3fa5c21dbf8ed725bfa64be53ed01bb74027279dbef42a7f3f2c0356bfe13693bd0f450b3fe398b7409b505ebeded57a3f67233bbfb2ea073f9678db3e216fb940de1521be3c37703f28b2b0be05386d3f5dba183eeeeabb404c011abe7b5d633f859d2e3eb5ba773f132e3ebe6f19be403dc74bbe2e31593f68bc1e3f6492243fbf42e6be5025bf405cb291be1595553f51d5553f3df19b3d7c650bbf3da8be4036b1bfbe2cea593f14583c3f670705bf4967debe35edbb40ea2ce0bed5d3593f51acb63e907c6bbfedd626be1f84b94003a3e3be538a673f15ce13be504a78bf4fe6483e8361b740b7b7cabe344c723f5bac17bf066425bf1964f63e2a56b640b2ef9ebe96fd753fb4d64fbf7af68fbd1960143f32c8b640140862be5e35713f323d37bf6d1f083fafc6e73e0d8cb84003fe24be27c7653ffe68b0be4d226d3fa21e1c3e23f5ba40d2111ebea910583fc7fc1f3edc62773f7b3d51bebf17bd406ae84fbec84e4d3fddad183f7117243fe860f7be1823be403abc93be669d493fa5aa4f3f4f5d983d367c14bf10b1bd40e4a7c1be9e654e3fe066383f834205bf3ab9eabe68faba40dc66e2be6e6f4f3f6f25b63e736c6bbf37862abe22a5b8405ec1e5bed5fb5d3fd5a704bec9df77bfe5e35a3e358fb640e5c7ccbeba4f693f624811bf85cc24bf936a033f9184b5408704a1be74176d3f324f49bfbfa38bbd572e1d3f10ebb540245d66be5ee1673f791433bf5969083ffdc9f33e8c9bb740009029be0eab5b3fcefdafbe7b0f6d3f0ec11f3ed2f0b940fcda22bea71e4d3f06c5103e0df9763fc45963bebf06bc40edcd54bec2ca413ff550123ff27a233f6eea03bf6311bd40562a96be08033e3fab27493f1caa933d9c431dbfe4aabc404b00c4be1e39433f6635343f0d9705bfd5b1f6be03fab94020fae4beb960453ffd73b53e37626bbf26522ebe79b9b7403732e8be7eba543fb315eabd5f6277bf9afe6b3eeab0b5402725cfbea49b603f269c0abfb51124bfe44c0b3f2ca7b4408164a3be8d7a643f737242bfa33f86bd20aa253fc101b540164d6bbebcdc5e3f74ae2ebf2bc8083f0c72ff3e0f9eb64027c82ebe62e7513ff376afbea0ff6c3fa57f233e99deb840f95728be9e8d423f1ef9003ea07d763f5c9474be28e7ba401a725abe79ac363f41a90b3f67bd223f0ecf0bbfe6f0bb40b3f998be8fcd323f7d4f423f14d98d3dcfb925bf5196bb40aab7c6be606b383fe2c32f3f260506bfe02701bf9becb8406ae3e7be2aac3b3f1199b43eda5d6bbfcc3232be86c1b64001f3eabe40c94b3f3e46cabd05d576bf340d7c3ee6c6b44080cdd1bed431583fd3ba03bf2e3823bf36c6123f40beb340ce0da6be8c285c3f52533bbf08db7fbd4ac02d3fa80cb440a8d370bed429563fe2162abf7339093fb752053f2d94b540e5a034bee07f483f35d5aebeb2f26c3f0055273e42bfb740b6812ebeca62383f0d8ae13d3ff3753f2d6182bee2b9b940b8cc60be37fa2b3f2dc9043f43e3213feb4b13bf89c2ba400e269cbe7e03283f0a353b3f090f873d56cb2dbf2074ba4009cac9be37022e3f341e2b3f2a8a06bf55bc06bfd9d2b740a01eebbe0f55323f1397b33e5e5f6bbf9d1a36bebcbdb5408a00eebe3c2a433f483eaabdb43a76bf027b853e7bd1b34068bed4be8b13503f1c71f9bea74422bfe2c8193f1ccab24014fea8be7922543f780734bf989a71bd6a60353f3a0cb34064eb76be2eca4d3f3c5b25bf39bb093ff4a50a3f937eb44034133bbe19773f3fe918aebedde86c3f5c392b3eb093b640614f35beeca12e3f71b2c03dd35c753f62e289bef17fb840a2d367be9db8213fed89fb3e43f1203fb2521abf5087b94026aa9fbeafa91d3fe7ec333f19dc7e3da36835bf3245b9408a32cdbefa01243f5451263fa32307bfd00a0cbf85adb6400aa7eebea35d293f6d71b23e6a666bbff5003abea9aeb4401457f1bed6de3a3fa9548abdf49675bf0a538c3e12d1b240def4d7be8941483f4e53ebbe113d21bfd44a203f2ccbb1408032acbed9684c3fc3a32cbf991062bdc77f3c3f0001b240ce8d7dbea0be453fc68720bf544a0a3f2aac0f3fff5db340d81642be97ce363f9842adbe29e26c3fe1272f3edb5cb540c4b63cbe634d253f93c99f3d9fbd743f35c390be723ab7402f7b6fbeb1ea173f4c60ed3eeaec1f3fced920bf5840b840f67fa3be61c3133f6e8b2c3f48476e3d01873cbf840ab84090ebd0be9a6d1a3f066b213fb6cd07bf1f0b11bf6b7db5407877f2be08c7203fb5bcb03ee37e6bbf02873ebee594b3407af2f4beb5e7323fa01954bdd5e774bfbbb7923e20c6b1407c6ddbbe07bc403fcc78dcbe451420bf029c263fdfc1b040dea7afbeb5fb443f23c224bfe70f50bde180433f8aebb040cc5982be4a073e3f845b1bbf86f40a3fd3a1143f3733b240afa249bed6862e3fb852acbec4de6c3f7c18333ebd1bb440acac44be29661c3f04fe793d0e0b743f027597be82eab540a8b677bed8910e3f1c5fde3e90c41e3f5d3b27bfc3eeb640f2a0a7be29520a3f06aa243f52e65a3d5b8943bf18c5b64005efd4be9446113f990d1c3f469f08bf310f16bf1842b440668bf6beb39e183f9a13af3e33986bbfd5a842bec36fb24071d0f8be64502b3fd5b012bd2f2e74bf4cae983ee2afb040152adfbe148d393f6f88cdbe5ad81ebf5f762c3f87adaf407264b3be9de43d3f39df1cbf7edc3cbdc6f5493f60cbaf40b53786be6fae363f383616bf73a80b3f6131193f09feb04068ca51beb6ab263fd977abbea1d96c3fe6c3363e5dd0b24051404dbe06fa133fd7b8333d8050733f9f949dbe3e90b440844680be56bd053fc94acf3ee6901d3fb31b2dbf9992b540280cacbecc65013fefc01c3f69a2463de9034abfc074b540e638d9befb9b083f99ac163fc37b09bfcab21abfb1fdb24054dcfabe26d9103fe29ead3ed8ac6bbfae4746be3341b14074eafcbe080f243f643aacbc298073bff5a99d3e1390af409e21e3bef0ab323f30c7bfbeaab31dbfe466313fc38fae401d5bb7bef61a373f3e9015bf41e82abdce7d4f3f29a2ae40724f8abe91aa2f3f965311bfa8550c3fcd3b1d3f3fc0af40a3665abe2a321f3fa24faabec9dc6c3fdeca3a3ebd7cb140634a56be47fc0b3f0b05ea3c4baa723fd26ea2bedd2db34008ee84bec0befa3ea77bc13e35761c3f330932bf2d2eb4408ab4b0beb3e0f13e8477153feec1333d23884fbfc71bb44036c0ddbebf60003fcebb113f98480abfe0aa1ebf47b1b1407b64ffbefc71093f8e22ac3e71c36bbf7bc049be200ab040ac9d00bf0e201d3ff23ce4bb3dd772bf8c00a23e8667ae40e04ee7be80152c3f6198b2be2b951cbf8ec7353f5e69ad40fa85bbbe989b303f89830ebf9e4419bda172543fbb70ad40109a8ebefef7283f0d900cbfed010d3f52eb203fcf7aae406e6763beaa15183f1808a9be34e46c3f74cf3e3ef621b040b2b95fbe9767043fbff4663cdd0a723f2f98a6be90c4b14051c989be4de4ea3eac3fb43e26621b3f496636bfb8c2b2403992b5be1cd8e13ed6710e3fd639213d9b7854bf5bbbb240237ee2be521ff13ed3f20c3fe7120bbf4f4222bf9e5db0402e0f02bfb666023f8496aa3ec3dc6bbf2d214dbe33cbae402edf02bf9381163f2a19d33b1c3372bf68cda53ece36ad40abadebbe34c8253f47e3a5be7f7b1bbf2bb1393fe73aac400be1bfbede642a3f69ae07bfd1e107bd9feb583fb537ac40521393be9794223fc5e907bf05ad0d3f834a243f722ead401ac36cbe6a53113fa4afa7be61ee6c3f95ba423eddc0ae401e8369be1871fa3edc1f8e395c70713ff536aabe4255b0403ed28ebed8e3db3e287aa73e6e531a3f944c3abf2951b140e09ebabe85aad23e0da2073f56fc0e3dc9ee58bf5b54b140996ce7be134be23e8a46083fb7db0bbf208b25bf6e03af405a8204bf8568f73e83fca83e8ff86bbf886850be0e85ad40673705bf4231103ff5d29c3c9b9571bf5e1aa93e7ffeab40d239f0be20c21f3f52b699bee2691abfb82b3d3fef04ab405868c4bec974243f071901bfc6dfedbca8f05c3fb1f7aa4014b797bedc7d1c3f3c6603bf0f550e3ff95d273fd4dbab40637076be25e80a3fc748a6be14fb6c3fcb88463e345aad402f9c73be4cd6ec3e599452bc38dc703f9256adbec3e0ae40140394be90b4cd3e49399b3eb34c193f6cc43dbf53daaf408fd4bfbe3e4fc43ec50f013f9269fa3c98f25cbf91e7af40d485ecbe1a3dd43e62bc033ff6a00cbfd88928bf62a3ad402f0907bf9eaeea3e4357a73e4d166cbfd09653be4e38ac400aa407bf8b2c0a3fade4fe3c120071bf58f2ab3e2abfaa4024eff4be2a011a3fca198ebefe6219bf3540403f05c8a940e417c9be33c91e3f6d90f5be7633cdbcf28a603f44b1a9403b819cbe1eb1163f1613febe35f80e3f122b2a3f9a83aa402c3380be61d0043f7bd6a4bed9096d3f14394a3eaeeeab40eefa7dbe49f6df3ebbd6cebcc24f703fb001b0bed267ad40665699be0e4dc03efc858f3e8250183f91d640bff75eae40a62dc5befabcb63e0e81f53e1d23d83c938c60bfb875ae4050c4f1be25edc63e90b0fe3edb600dbfa8432bbf173eac404ea109bf6398de3efb55a53ef83f6cbf04f656be84e5aa40ea220abf9370043fbcbe2e3d946e70bf1876ae3e5879a940abc9f9beec82143fd87682befa5718bff41e433fad84a840ecebcdbeb25f193f36bce8beb153abbcd8f5633ff864a840f86da1be702b113f1722f5be24a50f3f5fdb2c3f5526a940634e85bebd10fe3e8f41a3be3a1d6d3f04e74d3ee87eaa402a4b84befac7d33eeeef19bd8bc16f3f7171b2be14ebab4052c79ebe49a3b33e35c0833e824c173f85b743bfbfdfac4012a5cabebce9a93e9aaee83e192cb43c9ef763bf74ffac400723f7be4252ba3e5193f53e6f300ebf8be62dbf7fd3aa4098480cbfeb32d33e1241a33e9d6c6cbf7b3b5abe818ca9408db20cbf4807fe3e3c4a5d3dcfdd6fbf1ebcb03eca2ca84093c8febe6f4d0f3f08d76dbe064a17bf8fc4453fb13aa740c2e5d2be1e3e143f50fedbbe01cd88bcd51f673faf12a740f87fa6bea1f20b3f2642ecbefa54103f2e5b2f3f0dc4a7408e8c8abe232cf33ec3b1a1be6e306d3f416b513e0b0ba940a4b889bec657c83eb21b4cbd84336f3f209fb4bec26aaa402a55a4be31c4a73e551f703e9649163fba5b46bfdb5cab40fc37d0bed2e29d3e92ebdb3e0719903c2c2367bfdd84ab40c79dfcbecc79ae3eae72ec3e31030fbfd35e30bf6964a940e2fc0ebfe46fc83e6577a13e13906cbf97215dbe152ea840ba500fbfb5bff33e0f82833df15e6fbf2e85b23e49daa6405ef301bfa65b0a3f385159be806016bf80ed473fd0eaa54050ffd7be845f0f3fa5a4d0be23b855bc88c1693f22bba5403fb0abbe0e01073f083ee4be51ef103f0280313f755da64036e68fbe3de5e83eed1ca0bed1446d3ff2ce543ec993a740873e8fbe6c95bd3e298077bd81bd6e3fe434b6be94e7a8403cf9a9bed69d9c3e62755b3ecd6d153fd87d48bf0ed7a940abe0d5be1a96923ee495d03e6663623c13c469bfbc06aa40de1701bf0653a33ee05ce43eefb60fbf707332bfc4f1a7401ebc11bf9537be3e8ac99f3e10b06cbfb6da5fbe45cba64013fb11bfd1f5e93e6edd953d01f36ebfcbdeb33ee582a540168f04bf50a4053f6b0b47be0b9915bf90b1493f1296a4402931ddbec4ba0a3f8f6fc6befe3e22bc3dfa6b3f405fa44055f6b0bec64c023f97e5dcbe1e77113f2461333f62f3a440a05295be3b25df3e77779ebec65b6d3fb21c583ee119a640b6d494befe66b33ef06e8ebd455c6e3fdb56b7be4162a740b0acafbe3014923ef01b493e62b1143fea3b4abf144fa840b399dbbe48e7873eec6ac63e89c42c3cbffa6bbfe685a84044ea03bf45c3983e3d0fdd3ecc5310bf193f34bfef7ba640918414bf2382b43e6d179e3eb4d06cbf497f62be7464a540eaaf14bf12a3e03e08c4a63d22916ebf98f7b43e0227a440d83507bf9124013f39eb35bed9e014bf233b4b3fda3ca3405978e2be074d063fe7ccbcbe7026e5bbd9f36d3f6cffa2406e4fb6be16a5fb3e53e8d5bee8f5113f3316353f2f86a34006cf9abe55e4d53e8ad29cbe33736d3f31475b3eaa9da44055789abe66c3a93e89b69fbdef036e3f883db8be1cdba540776cb5be561d883e2be2373e5903143f77c04bbf44c5a640cf5fe1bed4987b3e53cdbc3ee4e9f53b7ff36dbfb202a7405ec406bf63c18e3e7116d63e10e710bfa6e135bf3d03a540a35417bf1048ab3ed65b9c3e9cf26cbfc91365befcf9a340ad6d17bf28c1d73e5559b63da6386ebfd2d8b53effc6a2402ce609bf44b3f93e14cb25be813614bf3b934c3f88dfa14021d2e7be8813023fdba6b3be23718cbb8cb86f3fff9ba140f6b8bbbe7d1ef33eed36cfbee46c123f7ea6363f3116a240ec58a0be211bcd3e522c9bbe338b6d3f6f535e3e721fa340d826a0be0aa2a03e77c2afbda5b36d3f07f2b8be6f52a440d935bbbede5f7d3ed6a1273ef661133fa5144dbfe739a5401130e7be4478683e68a8b33ec7c5983b23b86fbf6f7da5409ea409bfb644853e0563cf3e997211bf976137bff187a340e72a1abfce83a23eb6149a3e13266dbfc0e767be248ca240fd321abf784bcf3e4e94c53d58e26dbfb99db63e2463a140ce9e0cbf0083f13e68d114be1c8013bfcce64d3f617ea040433cedbe8e18fc3ea7c9a9bef03ba6ba8983713f3e35a040ec30c1be2d01eb3e68e0c7be4df7123fe140383fa9a3a04062eea5be1cc4c43ea13799be88ac6d3fa680613e769fa14036dea5be71fc973e2642c0bd165f6d3f7f9db9be76c8a2409406c1bed4896b3e4170163e4aaf123ff9684ebf39ada340ee07edbeb85e563e18cfa93e5176b93a928271bf5cf6a340a3890cbf7c8d783e64eec73e771912bf30ed38bfab09a24062051dbfc64d9a3e541b983ea54f6dbf5d726abe5b1aa14092fe1cbf3659c73e7a60d43d858e6dbf2648b73ec5fa9f40835f0fbfa4cee93e91db04bedcd312bfbe0f4f3fc1189f404db8f2be9284f43ea7a0a0be4785ca3a3f13733fa5ca9e4000bac6beb063e33e2a20c1be2574133f8ba8393f452e9f40f091abbedcf6bc3e867d97befbc66d3f6e68643e951da040919fabbe6ceb8f3e01f7cfbd93116d3fa31ababe2b3da140adddc6befeeb5a3ebf4d063e4e11123f478a4fbf2f1fa24067e4f2be1e80453e7ca0a03ef957bcba4a1373bf4b6da2405b710fbfe6c1673e3e0fc13eecaa12bf0c4c3abf7889a0403ae31fbf687b923e1e6f963e0e6f6dbfa7c36cbef1a59f40b8ce1fbf8fc3bf3e43d1df3d75566dbfcc90b73e568f9e40522512bff871e23ea26ff0bd205f12bf94dd4f3f13b09d40d63df8be6047ed3e833d99be7c67643b6743743f6e5d9d40624accbe7120dc3ec97ebbbec9ce133fead03a3ff8b69d40f439b1be598ab53e76c995be29e16d3f512e673e7f9a9e40f862b1be3242883e3f77dbbda6e26c3f5d36babe1ab19f40c4b5ccbe95274b3eec44f33d2fa5113fe05250bf5d90a04093c2f8bec37c353e6745993e154d63bb2b4274bf02e3a040045b12bfa4ca573e667dbb3e341413bf4c643bbf85079f405bc322bf7d068b3eb5c0943e9b8e6dbf56066fbe1b2f9e4059a222bf0285b83ea127ea3d82266dbf57bab73e13219d4031ef14bff067db3e83ebd8bd1cf711bfea8d503f91449c4003cbfdbef45be63e1a4c92beff29ab3bc052753fcfed9b4071e0d1be0632d53e4827b6be0c21143f39e13b3ff13d9c40f4e4b6be7b78ae3ee21a94beddfa6d3febd8693e5b169d40f926b7bef5f9803e80f2e5bd9bba6c3fad37babe63249e40478dd2be112e3c3e77a3db3dfd44113fc7fe50bfe5009f40a7a0febe0746263edf57923e14b0adbbf75075bfa7579f409d4515bfe599483e062eb63e1a7513bfa7663cbffb839d40caa425bf4de9833e22d6923e6bb56dbf755771be0ab69c40757825bf7a98b13e56b9f33d40fc6cbf4ecdb73e2fb09b402ebc17bfe0abd43ed0acc1bde39111bff02f513f70d69a400faf01bfaabddf3ea64b8bbe266de43b5156763ff97b9a40ab7ad7be7193ce3edbadb0bebc76143f6beb3c3f57c39a409491bcbea5bba73ed24692bee8186e3f1d866c3e48919b403feabcbeee18743ef0f6efbd76956c3f3e2ababe23979c40cc62d8be25f22d3e9330c43dbbe4103faf9e51bfe2709d40863e02bf8fce173ebe598b3e407aebbb375476bf59cb9d403f3018bf04233a3e13b1b03e52dc13bf9e633dbfc3fe9b40158628bfda557a3e83ad903eb3e36dbfdab173be953a9b40d94f28bfe504ab3e6ff2fc3d27d46cbfacd4b73e7b3c9a40748b1abf9844ce3e5b04aabd8f2811bf2dcb513f826599401a7b04bf4673d93e7e0f84becddc103cd653773fc90799408618ddbe7c4bc83ecaf4aabe3ed4143f20f23d3f1b4799403e3fc2bebf5aa13e484990be933b6e3f7f376f3e490b9a40b6abc2be8c01673e95e1f9bd42716c3f7b11babe63099b407e34debe2982203e2543ac3d7581103f1a3752bf5ce09b409b2a05bfcd240a3eab1e843e546e16bc9b5177bf153e9c4072191bbf62742c3e4de9aa3ed64c14bf7f5e3ebffa779a40b2652bbfda946d3e7ecf8e3e18086ebfa7de75bed6bc99400c272bbfc1c9a43ec477023ee9b56cbfd0bdb73e0bc698409b5b1dbfe231c83e739195bd14d710bfaa41523fdcf19740444807bf9f7cd33e6e957bbe75b1283caf23783f569197404cb7e2bece59c23e99f4a5be211d153f45d43e3f58c9974028ebc7be0c559b3eea7b8ebe61586e3f26bb713e7c8498407568c8be70ab5a3ef51801beab596c3fb0d3b9be477b994056ffe3be30db133e69a7973ded39103f9fa752bf764f9a40021308bf6c8bfa3df0b47b3ef2d52ebc6b2178bffcaf9a40a1ff1dbf5a8b1f3e7ee5a53eeba214bfcb363fbf12f09840be432ebf445d613e13268d3e9e256ebfb1e977be6b3d9840b0fd2dbf87cf9e3e0980053e20a76cbf887eb73e984d9740b62b20bf5a5dc23e2e0f85bdc6a510bfed8f523f347c964035150abf82c3cd3e9b3b71bef3d3373c42c7783f3a1996400054e8be5ea7bc3e9ab5a1be3c4f153f04963f3f784a9640c392cdbefe91953ef6cc8cbed3716e3fec18743e1ffd9640e01ecebe30e24e3ef72504be75516c3f2374b9bef2ec9740d3c2e9be8ec6073e8d18873dab0e103f3ef252bf56be9840ebf70abf78f4e13dc662713e8b353fbc89c478bf5021994021e320bf8632133ea9afa13e3de014bf92ed3fbf21679740911f31bfa0a6553eca5c8b3eab466ebf2ff679be70bc964018d330bf5212993e362c083ea99d6cbfa131b73e45d3954022fb22bf54c3bc3e73706abdee7910bfa5d3523fae0495405ce10cbf4b44c83eb11567be6ced453c8860793f959f9440b3ededbe5a30b73ef2799dbe1480153f3d51403f97ca94404235d3be6f0d903ea90a8bbe368d6e3f3572763e4875954034ced3bed89c433e5fea06be0b4d6c3ff90ab9be735e96401f7eefbeb175f83dc0496e3d63e70f3f783353bf0a2d9740d6d80dbfce71ca3dca3f673eeaf44ebca25d79bf2392974059c323bfcb60073eab759d3ef11d15bf3d9e40bf23dd95400af833bffe734a3e3d2a893ed9736ebf331f7cbedd3995403ea633bfb793933e798b0a3e74986cbf9edab63e0357944002c925bf6c65b73e9a5549bde04910bfbb16533f3c8b934006ac0fbf9b00c33e3e175cbea5a7563c99fe793f5e2493402d83f3be5ff6b13ea1c398be60bd153f6914413fb349934067d1d8bef2c88a3e72f088be12b26e3f72e9783ef9ec9340ff74d9be73de383e8d8209be4d4b6c3f8b99b8bed3cf9440772ff5be1476e23d1ed44c3d2eba0f3f4e7553bf9a9b9540b9b410bf5809b43dd8475c3ef2ff61bc4cfb79bf78029640289f26bf4c32f83d63ba983e726b15bfac5541bff2519440baca36bff0d73f3e7a34873e21996ebfdf247ebe6cb59340fa7436bfb95c8e3e90a20c3ef6966cbf1d7cb63e7dd89240789328bf814cb23ee0092cbd312510bfac49533f8a0f9240867312bf5001be3e145752bee4ca633c08847a3f53a791402811f9be7602ad3ea58f94be8bed153fa2c0413fa6c79140a463debed7cd853e7a0887bec5d06e3fe8397b3e2c649240240fdfbe2bba2e3e42bd0bbe8b4f6c3f8018b8be1b41934027d2fabe3ab5cd3dab3c2f3db99e0f3f8ba253bf0e0a9440068913bffee19e3df786523ed25c6fbcda807abf45729440f97329bf66dde23d8883943e56a615bff9f941bffec59240e99839bfe69d353e148b853e9fb46ebfd50480bec52f92400c4039bf6a54893edfed0d3e71a26cbf4b00b63e7c589140ba5a2bbf8d60ad3e095016bdc51f10bff05d533f5e929040b83715bf862eb93e94d34abe3ff0673cdbe67a3f192990404497febe0c3ca83ec53291be3104163f0c52423fe04490402eece3be4302813e5b5b85beafe86e3fab607d3e19db9040e89de4be98f9243e410b0dbe28606c3f4683b7be62b29140463400bfa9c2b93d4a71193df09d0f3f00b453bf80789240485716bfc28a8a3d8a084b3e33a774bc6de37abfc5e192405f432cbfb254ce3df22c913e86c615bfac8242bf5039914026623cbfafbf2b3e26b6833eb6d46ebf55fa80bef5a8904002073cbfdf77843ec0e20e3e8fb26cbf5d7cb53e0ed78f405d1e2ebfc69ea83e866401bdec1d10bf0a6d533fca138f4040f817bf7b85b43e305143bee1bd6b3ce5457b3fbea98e40770a02bf44a0a33e15c08dbe1b1c163f4de2423f6cc18e40906ae9be68c6783e608b83be03046f3f258c7f3ec7518f40d820eabe58961b3e41120ebe69746c3fefe7b6beae23904011f902bf1d91a63d6e5d043d149f0f3f71c153bff2e690402e1f19bf88ec6d3d1b89433ecc0a7abc50427bbffe509140f80c2fbf258bba3ddebb8d3ebee915bfd10943bfdaab8f40f0243fbf4b43223e4a84813ed9fe6ebf24fa81bedf208f407cc83ebf09947f3ea0670f3e8dc86cbf2defb43e11548e4026dd30bf0e0aa43e19b6d8bcf61b10bf337a533faa938d4001b41abf1809b03e36553bbe88e0703cc9a67b3f28298d4001c404bf1c329f3e5af289be9f3c163fb477433f3a3d8d4088dceebe7ee76f3e546681beea276f3fb5e8803e35c88d407195efbebf96123e83b90ebef78d6c3fe542b6be03958e400eb605bf5e2d943d692ede3cdb9f0f3f26cd53bf6a558f4033df1bbf6462483ddd923b3e6e8e80bceaa27bbfecbf8f4034cf31bf268da73d83ed893e731716bf1d9543bf861d8e40c6de41bf802e193e52137f3ec4216fbf50ed82be5d978d401b8241bf529b763e9ea90f3ec7e16cbfc95db43e52cf8c40d99433bf32a59f3e6e92b5bca82410bf8c7c533fce118c40ce681dbf36bcab3eb27b34becb9a723cb9f67b3f35a78b40097607bf85f49a3e1d9d86be9f50163fdefc433f3eb88b406c3df4bede6d673ef9e37ebebe456f3f89fc813e673e8c40c2f6f4be0a010a3ecb100fbe56ac6c3fc693b5be72068d40a26808bff7a3823d3fa3ba3ceaaf0f3fd6ca53bff6c38d40ae941ebfc88f243d9db9343e4f9b81bcecf27bbf8f2e8e40748734bfab66953d9898863ea43416bf191344bfa18e8c40ba9144bf0c5c103eb8b67b3e363b6fbff9d283bee60c8c40933544bff8e16d3e05820f3e93016dbf53beb33e64498b40d24636bfe75e9b3e679c9cbcd04110bfa96d533fc88e8a40d71720bf988da73ec3442fbe18fc6d3ce0317c3f59248a40d1210abfd3d5963e2df583bec550163f3b70443fbf328a405890f9bea5345f3eb9857bbebd5b6f3f83fd823e7ab48a40a648fabeb9ae013eeaed0ebe31d06c3f4bdfb4befc778b4014130bbf964b633df088a13c91d00f3fd4b953bf98328c400f4221bf08d6013d63852f3e19ac7fbcfd2d7cbf079d8c40153837bf1bca833ddef3833e723d16bf397f44bf2fff8a40783d47bf56c7073ed5ba773ed55c6fbf09c184be82818a408ae246bf8d63653edcd80e3e00286dbff814b33e50c28940baf238bf0d35973e952285bc896210bf555b533fa00a8940d3c022bf267ba33ee2db29becf76693c576d7c3f9da088401ec70cbfe4d3923e580f81bea155163f95e7443fc3ac8840e0d4febe6a37573e2a9377bed5786f3f0e09843e702a8940bd8affbe6236f33d46550ebed8f96c3fc622b4bea2e989402db50dbf9c52423db7d1893ca6f20f3fdca653bf52a18a4015e723bfa843c03cc7202a3e55157dbc41697cbf550b8b40cbe039bfe45b653da00f813e8b4d16bfb5ed44bf0f6f8940e9de49bfb8f4fe3d774d733eb0836fbfdeb385bef5f488401d8649bf9c295d3ee6b40d3e14546dbf0d65b23ecb398840df953bbf482c933ed7a960bcc48610bf9745533f0f858740216125bf8c899f3e478224be11dc643c44a67c3fc91b874044630fbf96f38e3e1f177cbeb25c163f075f453f2f268740b30202bf67804f3e733273be929a6f3fce1b853e49a087407f5b02bf4ca2e33de9450dbef9296d3f5a5ab3be735b8840bc4b10bfd488223d2155693ccc1a103fb48e53bf2f1089407b8026bfb07a7e3c47ca243ec8c379bc19a27cbf75798940587e3cbf6c4e443d7c1a7c3e825f16bf9e5c45bf4ede87406c754cbf9ccfee3de36b6f3ee5a16fbfee9a86be53678740991f4cbf4227553e2d620c3e49826dbfadb1b13eeeaf8640782f3ebf523e8f3e0fa244bc16b610bfef26533f2cfe8540f2f727bf92b29b3e7d5820beaf655c3c8dd17c3ff495854075f511bf962e8b3ea11e77bec955163fc3c8453f149f8540329004bf9d02483e33556fbeafb46f3f071f863e0f16864005e604bf5086d43da1fe0bbe795c6d3fda8eb2be74cd864025d612bf24b7033d70cd4c3c524f103fbc6c53bf367f8740ad0d29bf7054003c95a1203e54fb71bc6bcd7cbf6ee7874029103fbf0835243db824773efd6016bfc6bf45bf154d8640e9024fbf3bfade3de2f76b3e6eb96fbfe27887beded88540afb04ebfb14a4d3ebcf00a3e12b26dbf4bfab03e0b25854012c140bf82628b3e259b32bce7ee10bff500533f4c768440c6862abf97ed973e98131dbed7a7503c00f37c3f5f0f84404c7f14bf127c873e30f772be5843163f0929463f95178440bb1407bfacab403e93e46bbeadc86f3f3b15873ecc8b8440f56607bf2ebcc53d12950abe878f6d3f51c5b1be9f3f8540925615bf301ecb3c3b773a3cc18a103f2e4553bf5eee8540de902bbf00389b39d35d1d3e6e5d67bce2ee7cbf4b558640589841bfa4c2043dbbff723ee15616bf8f1946bf6bbb8440078751bf946dcf3d0c82673e57dd6fbf596688bea1498440033951bf8290453e468a083ed2ed6dbf4530b03e2e998340514a43bf3497873e311c29bc563011bf71d4523f77ed82404c0d2dbffe38943ebab619be4947443cab147d3f148882407e0017bf6eda833e67236ebe5636163f8b90463fbd8f8240079009bf5278393e0d7667be11e86f3fec1f883e870183400bde09bf303db73d573b08be08ce6d3fcfeab0befab18340bdcc17bfa405903c37ae303cb8cb103f361953bfb15d8440c3092ebf205ce8bbf4061a3e58755dbc51107dbf14c38440911644bf18d2cb3c22306e3e305616bf7c7746bf18298340befb53bf8e37c03d68ea623ebd0170bf5d5389be36b98240d8b253bfe8fe3d3e6fc5053e642c6ebf9865af3ed80b8240adc545bf50df833e5d2727bc2c7411bfc4a5523f3663814004862fbfc197903ef0bd16beff89373cda317d3fb9ff8040687319bfef4c803e2a7769bef727163f1bf4463f6207814032fc0bbfec6f323ea4e462beb607703fea2b893e4477814018450cbf9419a93d198405be4f116e3fd004b0bea224824042321abf60d12c3cd2152e3c9e15113f94e652bf44cd8240ec7130bfe03c6abcb00d173ebf5451bca32d7dbfc1308340888446bfc08e8f3c3286693e045116bfffd346bf54968140b06456bf682db13d54315f3e941970bf6a328abefe278140822156bf6681363e692f033ead686ebf849aae3e807d8040443648bfde30803e9e642ebcdac011bf5d70523ff8af7f4001f431bfeaff8c3e77e214be26e4263c27447d3f51ed7e4043db1bbfba92793e670466be8e09163f3d4b473f67fd7e40ba5c0ebf297d2b3e8e2e5fbeeb1b703f46238a3e15da7f40e89f0ebfec249b3de7f502bedf506e3f1427afbe88978040268b1cbfc0886c3be801353c2665113f6baf52bf0c3d814069cd32bfb85fafbc7233153e3fc441bcfc3f7dbf5f9e814028e648bf5012283ca715663ef93a16bfbf2447bf280380405fc258bfc443a23d0e535b3e3c3270bfb2128bbe0d2c7f40748558bf7e122f3eb33c003ea1a86ebfb9c8ad3e76dc7d40829c4abf6d12793ea0a63cbc1d1312bf8736523fbb977c40b25734bfde6e893e6e6013be55e0143cf8527d3fe4d97b4088381ebf2f9a723ea3b262be3ae8153f19a1473f76eb7b4027b210bfa59a243e7e545bbec630703f3b1c8b3eb9c57c4012ef10bf10548d3d940b00beae936e3fa644aebe50157e4004d81ebf007955bbb604433cc0b8113fd87452bf0b5a7f40d51c35bfa009e9bc37b4133e3d4f31bcc54e7dbff10b8040ff3b4bbf8096483b8ac6623e162316bf707347bf18df7c40ae125bbf5a75933d2089563ed75470bfc9008cbe7b067c40a2dc5abf62af273ea936f83d56f56ebf6ae2ac3eddbb7a4066f64cbfdacd713e20cc5abc2c7112bf39f3513f857d794019af36bf3de3853e3f4b12bea525013caf5d7d3f0dc57840338920bfb7ad6b3e5e135fbe10c8153f74fa473fead878406afa12bf16c61d3e3f9056beba4e703f08288c3e87b17940763013bf81457f3d7ee6f7bd80e36e3f0d4cadbe25fc7a40b21621bf60d124bce1f2603c2717123f7d3152bf7d3a7c40ff5d37bfde1611bd8fa2123e64e41fbc6f597dbff5f27c40e6834dbf409e85bb52295f3e240f16bf9dc347bfceb679406e505dbf36b2843d59e3513e197470bf36ec8cbedbde7840fe215dbf914f203e91a2ef3dc6416fbf9efcab3eab987740fa3e4fbf388b6a3ea4657fbcadd012bfe2ad513fd760764045f538bfa858823e9db411be6b78d93bb6637d3f7aae754043c822bff0c4643e8ad85bbebaa3153fd04e483fb4c57540603015bfa8f7163ea1f051be4669703f16318d3ea79d7640d05e15bfc704643db167efbd5d346f3f414cacbed7e37740d44123bf9cd389bc0584823c2d7c123f86e851bfab1b7940898b39bf34822dbd7f09123e68310cbcb05f7dbf08ce79408cb84fbfa04237bc88ed5b3e0df415bf441148bfcd8d7640c0805fbffbd06b3d0b174e3ee38770bf30cb8dbed5b575406a5a5fbfe0ea183ee206e83d2c886fbffe1aab3eb8737440f57a51bfe442633e427e92bcf03113bf9466513f7e427340e92e3bbfb4967d3eeeb211be4c99ac3b4f647d3fbf96724090fa24bff0d75d3ebb7e59be9574153f059b483f13b27240225917bfbe26103e51274ebe5579703fdf278e3e0b8a7340787f17bf74c0483d64dae7bda17d6f3fba59abbe28cc7440ed5e25bf243fc1bcc22c953c4edf123f19a051bf62fd7540f9aa3bbfd8ee49bdc309123e4737eebb5b607dbf21a9764053df51bf80e795bc6196593eb8cd15bfe25648bf1a6473403ea361bf9e224e3ded99493e13a270bfabb58ebe6e8b72407a8561bfde7d113e004dde3dd2d86fbf0725aa3e044d7140e9a953bf80f15b3ec773adbc9f9e13bfdc14513f7d227040985b3dbf6973763e084a12be2bee763b515f7d3fe07d6f40ab1f27bf9ee3563e1f1757be9443153feee8483f0c9e6f40447419bf5450093e7eae49be318f703f902d8f3eb8767040089219bf706c2d3d002edebdf9d06f3fbd53aabe22b57140986d27bf18c4f8bcb915b03c904b133fdb4e51bfa9df7240ebbb3dbfb46966bd8fa4123e28b9c1bb5b5b7dbf46847340d7f753bf0055d0bc2431573e75a815bf169c48bf7f3970402ab263bf6b2f303d7875443ec6c170bf04aa8fbe2f5f6f40949d63bf6bfd093e2f79d23dd43170bf9f1ea93efc236e4056c655bf8b8b543e69dad0bc3c1314bf3cba503f4b006d40d7753fbff83b6f3ec08a13bec7730f3b00547d3f87636c40113229bff6dc4f3ecbb454beb411153f8236493f91896c40247c1bbf126a023ee99344be93a9703f8041903ee1636d40ba901bbf02e2113d7c6dd2bdd72d703f2d36a9be149f6e40f86729bf745618bd8255d33ce5c2133f84f250bfc5c26f4078b83fbf198c81bd93e3133e6b9091bb45507dbf895f70403efc55bf1a9c05bda5c9543eb68215bf38e148bf250e6d406fad65bff6d2113d07dd3f3e80d970bfa79790be4c316c408ea265bf6960023e0f4bc73d628570bf8e1ba83ed8f86a400ad057bff3074d3ead6bf4bc428614bfc85e503f22dc69407b7d41bf64e7673e432615bee410163a16457d3fea476940a5312bbf06bb483e15df52bee6d9143fab7e493fcd746940bb701dbf2ad6f63d010340be80bc703fd04b913ea6516a409c7b1dbf40fbeb3cf352c7bd2485703f561ca8be1a8a6b40204e2bbf7aa034bd8cb4f63c8839143fb59450bfd0a66c40afa041bf210f90bdc87c153e29393ebba1417dbf083b6d4086ec57bfc66c23bd02f1523e1c5515bf402249bf37e26940439867bfe012e63c35c33b3e57ea70bf437f91be19026940809767bf9a4cf53d24c3bc3d23d470bf3b1aa73e03cc674003ca59bf8966453ee0d80bbd5ff814bf2502503f66b666407a7543bfaa75603e3a0217be7e9c8eba6d337d3f4c2b66406a212dbfc27d413e867951bed09c143f08c3493fd75f66402b551fbf08a6e83d75e73bbe54c9703f944d923ef53f6740ee551fbf9078b33ccad3bcbdf4d6703fd408a7be0b7668406b232dbf9a4451bd02ef0c3d4bad143f1b3750bfa88b6940f47743bf91c09ebd245c173e0e62afba0e307dbfc2166a4005cc59bf7ea141bd1a92513ed22115bf315f49bfbcb566401b7269bf7884a73ca89f363e7c0371bf707a92be9ad16540d67b69bfc895e53da696ae3d003271bf4bf2a53e7e9d6440a7b35bbf2ea33d3e702325bd897c15bfa9904f3f188f6340345d45bfdae2583e282a1abec4e842bbbe147d3fb10d6340bf002fbf6f213a3ec94650bea85a143f83074a3fb64a6340d32821bf9a3cda3dfecf36be11dd703f6767933ed82e6440181f21bf783f743c71b9aebd5337713fffd0a5bef462654047e72ebf46516ebd903a263d6a2f153f41c74fbf5a716640ba3d45bffba7adbdb7861a3e34e2b8397f117dbfc1f26640309a5bbf404a60bd345a503e87ee14bf589949bf7688634015316bbfbc574e3cd9f2303e881f71bfb17e93be2b9f6240d8456bbfcf65d53d1ea69e3d119371bf73bca43e766c61406e835dbfaaa1353ee51042bd1f0216bf1f174f3f7e656040482b47bf9712513e40ec1dbeb3349fbb2aef7c3fb5ee5f404cc630bf698a323edd244fbef918143f4b4a4a3f7a35604049e222bf4465cb3d333931be82f2703feb8e943ec51e614086cd22bfb0a6fc3bcae49ebd139d713ff47da4be7a516240f08f30bf141386bd530f433df5b9153f614a4fbf7258634017e846bff2f4bcbd4a3f1e3e217b0c3b8dec7cbf3bcf6340134d5dbf21c97fbd2d254f3e20ba14bfe7d349bfc25a6040e5da6cbf40b6943b94492c3e8a2f71bf5f7694be6e6b5f400afb6cbf24c7c43dfcb4903debe871bfdb90a33eae395e40b33e5fbfc7672d3e73865cbd208116bf58a04e3f4c3a5d40fee448bff60a493e3fa721be82b5dcbbb9c87c3fd5ce5c406c7732bfa5be2a3e9d924ebebbd1133fb6874a3f54205d40118724bff42abc3d43942cbe77fd703fc1a4953ea80f5e40ec6624bf0032af39b00291bd65f6713fb23ca3be68415f40432332bf345995bd646e5d3d1d3b163f4fd24ebfca406040f87c48bf949fccbd6af9213eb223803b70c67cbf41ac60408bea5ebfee0690bde1924e3e827e14bf26094abfb22c5d4067706ebfd02474bbacc3273e2b3c71bf0d6f95be7e365c40369c6ebf63b6b33d3aa7823d613c72bfef5ea23e46055b402de660bf0cf4243e39db77bdb8ff16bfde244e3fa10d5a40048b4abfbdca403ef0a925bead890dbc339e7c3f26ae5940c91434bf20bd223eae394ebe9287133f6bc34a3f4e0b5a40df1726bfca8bac3d660428be7f05713f47bc963e82015b4010ec25bf10bcecbbcbf382bd554d723fc7f5a1beba325c4019a233bf70fda4bdf5bb783d2bbb163ff5554ebf5f2a5d4042fd49bfd6aadcbdf701263ea1a0ba3bfd9b7cbfda895d407e7360bf988fa0bdfa464e3ee33f14bff43b4abf52fe5940d1f06fbf802b48bc6a01223e535071bf828296be5f005940872870bf7c28a23df0455f3d679f72bfecf0a03e3fcf5740ff7862bf00411c3e39308fbd619217bf17874d3f7edf5640731c4cbfb54c383e71c62bbe543331bc725b7c3fb08c5640799d35bff7801a3ef1844ebe3b35133f72fa4a3f76f65640cd9327bf9e7e9c3d996122bea012713f4ff2973e69f45740175c27bf747a75bce82760bdceb2723fd476a0be892559409f0b35bfce08b5bd6ba18f3daa4a173faaba4dbf4a155a402b684bbf3a20edbd001d2c3e46e3f93bb8597cbf18685a4026e761bfba88b1bd3d7d4e3e030014bf43674abf93cf5640d64e71bff4dbacbcdba81b3ef56571bf3ea397be86c85540d19271bff7c28f3d60c6353d710173bf59709f3ed596544047ea63bf961f133ec721a4bdd22018bff2de4c3f42af5340c78c4dbf56612f3ece6632beb79f53bc31107c3f416a5340440537bfb2db113e90cd4ebe56e5123fac2f4b3f2fe25340bcee28bfdaaa8b3d38341cbeee1f713ff93a993e3ce95440c1aa28bf723cbdbca4fe36bd381a733f62d39ebeed1a56404b5336bf4bcbc5bd9c84a43d6edf173f340e4dbf81025740cbb04cbfcf4efebda2a9323e05a81d3cb30f7cbf814757404f3863bf8343c3bd8a9b4e3e08c013bf0f944abfcda05340429472bfb42af8bc16a6163e3a6e71bf98b198bead8f5240a2e472bf9267793de97c123d035573bf2f029e3eed5c5140474365bf13a8093e9fa5b6bdb5a418bfeb3d4c3fb87d504001e54ebf9821263e3d8d38be3ffe74bc68c77b3f4f4750400e5538bf1be6083e847f4fbe2092123f68604b3f7bce5040a4312abf2f80743df13017be5522713f756c9a3e9bdf514044e129bfc0fc00bdbaca13bd8371733f564d9dbe5a1253409e8237bfb81ad7bd1bfdb63dc264183f6f6c4cbf90f15340e5e04dbfe20608beedcf383e4aa23c3c62c77bbff9275440d97064bfc696d5bd68514f3ed97a13bfd7ba4abf1372504043c073bfcc1923bdba8f113e277471bff0c699bee0554f401c1d74bf01dc513d4d23db3c7ba673bfd9819c3e91214e40118366bf14a8ff3d7450cabdc32719bf64914b3fea4a4d40292450bf5f871c3e3f353fbe02568bbcb3757b3fe8234d40de8b39bf2835ff3d256c50be713c123ff18e4b3f71bb4d40995b2bbf1f68503d100a12be6a22713f05a89b3ea4d74e40c0fe2abfb08d24bd3790ddbc00c7733f55b39bbef30b5040cb9838bfe500e9bd89a7ca3d39e9183f06bf4bbf9ae25040b4f74ebfc93311beb27e3f3e9ccf5b3cda757bbf9c095140fe8f65bff88de8bd154f503eac3313bf4dde4abf81434d40b2d174bffb984bbded4c0b3ed87d71bff9fb9abe2d1b4c40033b75bf2fc4283d9c47783ce90074bfdeb39a3ec8e44a405aa867bf7137eb3d4c3ee4bde4b719bf9db64a3fe1164a40e84851bf458b123ecf5e48bec6d59dbcb7007b3f1d004a4060a83abf8de4eb3d905652bee9de113f82b24b3f2fa94a404e6b2cbf6cf42a3d7ef90bbe8a23713f1e049d3e83d14b40fd012cbfc26849bde3e57ebc5024743f1cd199bee8074d40a69439bfb489fbbd2294e43d4576193fcce64abfcfd54d4018f44fbf6ab41abedfa2483eae407c3ccf017bbf93ec4d40a09466bfd136fcbd8815523eb9eb12bf29f54abf64154a40b4ba75bf075976bdad3d043ea78771bf33499cbe69df48407f3076bf1499fa3cd650173bcd5774bfb9bc983e33a64740a3a568bf568fd53dde6500beab411abfc1c4493f63e14640574652bf14f5073e056c52beb29bafbc057a7a3f28dc46403f9e3bbf9b6cd73d366354be5983113ff7d14b3f80984740ca542dbf0a55033df03705be0623713fb37d9e3e7bce4840ffde2cbf8b5070bdffdb3abbdf80743f4eb497beb1074a40db693abfa88807be9289003ea6051a3f28f149bf81cc4a4027c950bf12b624be7191523eaa958e3c4c7d7abfbcd14a40407167bf4a7708beddd7533e71a412bf750b4bbfe8e74640688376bf248491bd59b9fc3dd38671bf66849dbe09a345407b0577bf72979f3ca16b15bcaca074bf07d6963e65664440838269bfd1ddbe3dd4760dbeb6be1abfa6d8483fecaa4340b52353bf84b9f93d17df5bbee75bc0bcf0f4793f3bb8434083743cbf9afdc13da29e56be5428113f73ed4b3f3c894440141f2ebfcacfb33c1cc7febd2f1a713f9ae49f3e1bce4540019d2dbf10768cbde4e20b3c51ce743ff1ae95bebf0a4740f91f3bbf00b711be53930d3ecb841a3ffb0349bf38c64740c77e51bfda242fbef1fc5b3e33d59d3c57f979bfe9b84740fa2d68bfe44613be7708563ea55b12bf3b1b4bbf4dbb4340d62b77bf18dba8bd3d0ff13d7e8171bf53c89ebe50664240deb977bfb0c4003c38e2aabc2fe374bf94d4943e9f254140be3e6abfe81aa73df6131bbe2e361bbf0adb473fb9734040ade053bfb27de23df9db65bebbd7d0bcb361793f92944040d42a3dbf0692ab3d912759be94cb103f70044c3f9d7b4140e4c92ebf80913a3c3f04f3bddc0c713f3b58a13e9ad04240dc3b2ebf80a1a1bd5447a63c7f15753f1a8d93be4b114440fbb63bbfe6511cbea52f1b3ed6fd1a3f690548bf31c344400d1552bf4d033abe50fc653e04b2ac3cbf6679bf58a24440e7ca68bf758d1ebe099c583ec61112bfb6244bbfcf8f40405ab277bf4645c1bd3e22e43dd57971bffe26a0be68293f40e54b78bff06d8dbb52530cbde42175bfc08c923e04e43d4078d86abf00318e3d8dd22abe1aab1bbfdab1463fee3b3d405a7b54bf0a22ca3d0da571be326ae1bc92ab783f5a713d4057bf3dbffc16943dda815cbea66b103fe60e4c3fdf6f3e407c532fbf0058b439254ae6bdaefb703f9be7a23e46d63f40f1b92ebf10bab7bdead1093d8258753fe82691beaa1b41405e2d3cbff46027beecef2a3e36721b3fc9dc46bfc0c341407d8a52bf7b5945be77c3713ec919bb3cabb178bf548e4140804669bff2532abeb6e35b3ea4c811bfde204bbfea653d40030f78bfd702dbbd87dad43de56f71bf97b1a1be8bec3b4076b378bfe4b78cbcd8634dbda75975bf31de8f3eb1a13a40bd476bbf1fae673dd68f3dbe70181cbfe44a453fbc033a402cec54bf0a5db03da9db7fbe7b6bf1bce7c3773f014f3a40f92a3ebfa18b763d8be160be4a0b103fa3064c3fc0663b402bb52fbffc3939bc16fbd7bde5e5703fc29fa43e1fe03c40b8102fbf1ffccebd1fe4493d5c95753f6c588ebef92a3e40707c3cbf910033be43a63d3e38e01b3ffa7545bfeec83e4002d852bf904351be0bd97f3e39b9c83c28cd77bfa97d3e40369969bff1b736bef0d95f3ef78211bf960d4bbf373e3a40c93b78bf9e47f6bdc4abc53d3a5f71bf9347a3be27b0384054ea78bf6a97fcbc4d4c87bd5b8275bf03128d3e015f374072866bbfa8a02f3dc36b50be5e731cbf1dd1433f8ccb3640762d55bf62f1943dcd1987be3087ffbc3fcc763f1a2e37409b683ebfd4c7413d634e65be3bb00f3f09f84b3f076138404dea2fbfced8c1bc3792c9bd78c9703f0a68a63e17ef3940c23b2fbff897e7bd1730853daac4753fb3618bbe3d403b40a49f3cbf11473fbe0078503e8d401c3fdcf843bfb2d33b40a0f852bf99d75dbe3404873e8f1dd53c45d976bf24713b407cbd69bfdcd043be24e7633e354011bf42f54abff41837405b3f78bf0d7709be8c2cb83d574671bf41d5a4beb5743540f4f678bfdf2c39bdbc33a6bda49d75bf553c8a3e801c34408f9a6bbf1821e93c4cb162be7fc01cbf224d423fcf933340a44455bfe01a703dc8298ebe583706bd68ca753fd80e3440407d3ebffe410a3d02e569befd560f3f77e34b3f9c5e3540c7f72fbf2f1316bda52ebcbdd6a5703fa12ea83edc0237402e402fbfa1b000be8906a43d65e5753f826488be105b3840939c3cbffa1f4cbe24b7623eb88f1c3f047442bfc1e338407ff252bf91026bbed60e8e3eb5f8df3c6ed975bfb8683840e3b969bf568c51be876e683e17ff10bfa2d14abf92f63340801778bfd68718be6a4eaa3d232771bf6e78a6be953a3240e2d678bf603777bd8ec1c6bd94aa75bf0128873e85da30407a816bbf5810593c6a1b76be24fd1cbf62a0403fde5c3040172f55bf102b333d65bb95bef0f90bbde1a6743f9df130406c663ebf78909f3c7cf76ebe82ff0e3fc6c24b3ff25f32405adb2fbf1cee4dbd114faebdea7a703fc110aa3eef1b3440f81b2fbf86350ebebb9ec43de6f7753fab2385beff7b35405f713cbf609459beca23763e8cce1c3fa7c540bfa6f93540c3c352bf20ce78bea09e953e8a59e93c82b774bfe76435406f8c69bf69f55fbe687c6d3e45c110bf16a04abf9dd73040e2c177bf716328bed7729b3d700071bfc53ea8be43022f40828778bf06539cbdeb67eabd61a575bf68b0833e83992d406c386bbfe01838bb29c585beca241dbf5fb43e3f29272d4006ea54bffcb1e53c922f9ebee6d210bda24e733fe2d62d407d213ebfb032913b69ed74be4eab0e3f878c4b3f8f652f40a4922fbf0e5584bd7ec09fbd6c47703f9215ac3ee93a314004cd2ebf75641cbe0629e83d3cf8753fd48081bea9a33240191c3cbf92ad67beddcb853e3df71c3fb2d83ebf03163340806a52bf1ba283bef70e9e3e5986f03c4f6173bf4a6632400a3369bf8b176fbec14f733e968910bf35594abf04bd2d40d03777bfc82539becbfc893dedce70bfc24aaabe87cc2b40cc0178bfc6fabebdeb1d0abef48375bfc30e7f3e2e5a2a406ab86abf5c8aa2bcd35f92be222b1dbff25a3c3f6ff32940e06e54bf507e3a3cdd41a8bedf0914bdc49a713f8abf2a4082a83dbf70843cbcab847cbe87600e3f642c4b3f88702c4050182fbfe55ba3bd2dfd8fbd4a09703f604bae3e05612e40544e2ebf58562bbe288c083e01de753f6e707abe5ed32f40b6973bbf6d8276be4b60923efefd1c3f7e803cbf1d3a304040e151bfd03d8bbe4606a83e2cf9f33cb0b371bf026e2f409ea768bf720b7fbe85327a3e6b5e10bf91f149bf58a82a40c96d76bf38fa4abedebb6f3dfa9170bf4b7bacbeb39a2840c63977bf1bf5e3bd739b1fbecf4575bf2f18763ec01d2740d9f569bf8edc1bbd1d4e9fbe8a0d1dbfb6cf393f01c3264024b353bf40e1d2bb6e92b2be445a15bd4ebe6f3f1fad27405df23cbf28c0e9bcd51982bebd210e3f1fbd4a3fae82294061642ebfb86ac4bd65037fbdb8bf6f3fa5a9b03e53902b4064982dbf08353bbe64aa1d3e35a9753f421971be460d2d4050dc3abf381c83be36489f3e08e81c3fa5f039bf05682d40061f51bf454c93be7330b23e97abf43cd3df6fbfe77d2c40cedf67bfc7fb87bea08d803e7a3b10bf8e7f49bfc3992740f56c75bfa0c55dbeda04503d8b4d70bf289aaebe446d25404d3876bf768305be4cd933bec6f474bfce026d3ecfe42340c3f868bf60c56abd6bbfabbe58d81cbf8b32373f4b962340c9bd52bf081ccfbcaf9ebcbe376c15bdcdcf6d3fb79f244023053cbf7e863ebdc2ef85be91ea0d3fc7434a3faf9b2640937c2dbf463be7bd7c4b60bde26d6f3f3908b33e2ec828403bb12cbfcedf4bbeffc8313e165f753f47a267bea3502a40c5f039bff4588bbe53b4ab3e93b61c3f085237bf279f2a40bf2b50bfe0bf9bbe4d2dbc3e6076f23c4af66dbfbb95294066e466bfd0e090bec243843e602110bf53f848bf3d922440a73274bfee9571becda62f3da7fd6fbf3adcb0be334522404cfa74bf16281abecff748beab8474bf2d12633e51b02040d2bd67bf84219fbdf7d0b8be747c1cbf524d343f3e6e2040818b51bfe83639bd6d3dc7be149813bd24a46b3f37982140cedd3abfdc2686bd21128abe08be0d3fe2b0493f6bbc23404d5e2cbfb2f205be88d040bd9d0f6f3f408eb53e75092640a8962bbf8c605dbe45d1463eadf5743f12475dbe579e274022d338bfeefb93be8fc4b83ea25e1c3f5c6a34bf6ae0274073054fbf749da4be4dbfc63e196fec3c52cf6bbf71b6264026b365bf983a9abea948883ee81210bf965648bfdc92214031bc72bf643c83be06b60e3d2da16fbf2e44b3be9d231f40a07c73bf2af82fbe93ee5ebe8cf173bffa32583e62811d40a34166bf174ccbbd167ec6be89f41bbf6919313feb4b1d40f11850bf86cb87bd496cd2be8c960fbd4435693fa5971e404f7939bf3437afbdbc818ebe289e0d3f8001493fdae52040f5062bbf3a4019beccaa20bdaaa36e3f6d3eb83e1955234086462abfd8c06fbe53bb5c3ec768743f38f351be54f72440828137bf7a099dbe3f75c63ed6da1b3f803231bfcb2c254036aa4dbf9fe9adbe39e4d13eac0ae23cb86469bf11e12340d74964bfb20ea4bea49c8c3ed91110bf909747bfd29c1e40f00671bfaf3d8ebef8a7da3c3f376fbf48d3b5bec3091c4030bc71bf030347bed0ae75be4f3773bfc7544c3e46591a40d48064bff602fabdf8bed4bea83a1bbf90902d3f8d301a40b7624ebfa876b5bdcd25debef81309bd347d663f269f1b4094d437bff48fdabdf63d93be698d0d3f7332483f0a191e40f47329bf9b912dbe9ffbffbc01296e3ffa19bb3e19ac2040b4be28bffb8481be8777733efeb3733f3e9645be965c224010fa35bfbe85a6beb8bdd43e20251b3f33a42dbf4f8522402e184cbfd2a8b7be6296dd3eaba8d23c62b066bfb616214050a662bf7e62aebe163f913ea72010bffdb746bf83b11b4076106fbfb2d499bed9438e3cffb26ebf06d0b8be23f9184011b66fbf5e565fbe70cd87be103672bf6a1f3e3e82391740307862bfc4b115be1502e5be4f281abf244b293f991d1740a2654cbf52b9e5bd70b2ebbe579ffbbc1320633f11b01840bcec35bf102504bed51299be9ba20d3fac0a473f31571b40e6a227bf04f142be131cbebc509f6d3f8c1ebe3e910f1e404bfd26bf06a28bbe6d54863e41bf723f623f37be32cf1f402c3b34bf5174b0bea7f6e43ea7151a3ffb5f29bf1beb1f40ba4d4abfe0dec1bef6ffea3e2c66b93c355e63bfa2581e40a1c660bfab3ab9be0a8f963e0a5010bfbb9745bfb4d4184023c66cbf4e1ea6be8f35ee3bf2136ebf122abcbe80f51540e1566dbfd43579be215395be3df570bf86642e3ede251440651560bf5b0330be6bd9f5becbc718bf3792243fe5161440e0114abfa79a0cbe92aef9bea8afdcbce2635f3f4fce1540e8b433bf20821cbe3c079fbe6fd50d3f95ba453f42a41840398925bf9ca659be13ff6fbcffff6c3fd86bc13e76831b407bf824bfb25696be397c933e278e713f9c4927be18531d40f73932bfeeefbabeedc6f53e44c0183f1da024bf11621d407c3d48bf48a4ccbe36bdf83e1d7b993c36b55fbfa7aa1b40759a5ebf8cb0c4bec7cc9b3e979110bfd66244bf5506164060346abf2d08b3bef13fa5ba0b746dbf9154bfbe3cff1240daaa6abfb93b8abed0c4a1bee69f6fbfceae1e3ee41e114065635dbfcdc64bbeffd602bf3f4617bf5dca1f3fc81c11407b7047bf4cc227be9d7703bf2fbbb9bc69965b3fb5f91240583431bfd82b36be51b2a4be30170e3f3b61443f81ff1540302d23bf2b8271be451adbbb96576c3f58bac43e9a061940b6b622bf8a8da1be60ca9f3ebb41703f3e3b17bef2e61a402afe2fbfdbe5c5be42cb023f4046173ff6d31fbf0ee91a4015f145bf1ce8d7be0df2023fe2486a3cf5f15bbf210c1940392d5cbf56b3d0be4033a13e05e810bf030b43bff5471340d85a67bf4f93c0bec7cc1cbc67c46cbf1da4c2be2a18104050b167bfc08e98be9b3caebeeb176ebf4bde0d3e81260e402c615abf6afe68be1cd30abf838115bf54a21a3f11310e40368044bfa05544be9f2f0abf369a8fbc4274573fd2331040d0692ebfce2351be9483aabe90720e3fcadf423f256a1340c28d20bf674285be406e993aed9e6b3ffb2fc83ef09916404a3720bff646adbea027ac3e0cc16e3fff0d06be998b18406e872dbf8056d1be14c70a3fd788153f0ba61abf09811840646843bfe6aae3bed6a0093f6aab133c5cd857bf487e1640cb7e59bfce43ddbed3cda63e665611bfd58941bf3f9b10409a3864bf39c1cebe4afa90bc15046cbf501cc6be41420d400b6964bfa395a7be72b7babe68596cbfc6c6f73dcc3e0b40480d57bfdcd683be6fdc12bf2a7413bfca14153fc1550b408b3f41bf5d5862bef5fa10bf635c3bbc33f8523f5d7e0d40de532bbfc86d6dbe4278b0be3fea0e3ffa32413f8be51040b8a91dbf175992bec9c4113cccd56a3fe7cbcb3e893e144047791dbfad84b9bef98cb83e5d086d3fd66ae7bdfe4116400ad52abf7343ddbeeed1123f9a82133fda1015bf092b1640c7a240bf22eeefbe3965103f0f34383bad6353bf6d021440758e56bf6c63eabe9392ac3e9cde11bfd3dd3fbff0010e4062cd60bf8291ddbe6c60d1bc7e336bbf8abac9be957f0a409ed160bfb34fb7bec317c7beb0646abf8e93d13dfb6908401c6753bfa6e993be06e01abf321e11bf1d250f3ff48c0840b9ad3dbfc1e480be9bc917bf38da8fbbd2244e3f24db0a40b9f127bfc38485be8d82b6be6d7e0f3fc35b3f3f1c730e405e801abf6d05a0be2088863c70fc693fc68bcf3e77f51140227c1abf3b47c6be03ddc43e98176b3f407bc0bd110b1440a4e627bf47ade9be8ed81a3f4f33113fc8170fbf18e8134007a03dbf2db2fcbe692f173f520c8dbb0b964ebfe8991140075c53bf2a12f8be4f75b23ea88012bf85073ebfa77d0b4088195dbf4602edbe8b8107bdd3536abf7079cdbe1dd2074053eb5cbf68bac7be9f3fd3be503b68bfd151a93d30aa0540d06e4fbfd7b4a4be07ca22bfd7800ebf22da083fb1d80540bdca39bf1c5291be508a1ebf6f7f6b3b72ff483fdc4b0840384324bfd1f994becc92bcbe362f103fed5b3d3f23140c407a1117bf8c46aebecdf8c13cdf13693f3c6ad33eadbf0f40af3f17bf6a8ed3bee9f8d03e0ff0683f968197bd9ae7114032bc24bffb93f6be16c6223f459c0e3f3ec208bf19b9114046603abf5bfb04bfe7ed1d3f08cb49bca87449bfee450f40cae74fbf802703bf0067b83e873c13bf6e083cbfe40f0940061e59bffe0ffdbef76d27bdbb5469bfd999d1beb83b05403eb758bf2fd1d8bedbd2dfbefeb665bf9455793d7301034065254bbf7633b6be65fb2abfec6c0bbf41da013fe73a0340619735bfb46fa2be479625bf4bbf583ccb34433f24d20540e34820bf9212a5be2723c3be1f1e113feff73a3fd2c909405a5d13bf481abdbe767ef93c1f23683f794ad73efe9d0d4022c413bf1859e1be233ddd3e567a663f563d56bd43d80f40fb5521bf68fb01bf33f42a3f03910b3ff4bc01bfcf9e0f40ffe336bf49dd0bbf9bf0243f234ab3bcd3b343bf92070d407d324cbfda8b0abf9896be3e1d2314bf11c539bfcabc06406ad154bfc1e006bfa21549bd812d68bfe635d6be16c10240342c54bf9a9beabec999ecbe3cd462bfa677173dc07400400b8346bf796fc8be664333bfa3e307bf816af43e84b80040b90d31bf9d4ab4be6fb82cbf78a4c83c9bd93c3f7e72034083fe1bbfc4deb5beb7dac9be0748123f5c42383f14980740aa600fbf9091ccbeefed173d3423673f8648db3ec9930b40e00510bf79b7efbe2993e93e40b0633f7194ecbc1ee00d4009af1dbfccf108bf8836333f0413083fc826f4be5a9c0d405b2433bf3b0413bfb8fc2b3f47ac06bd536f3dbf60e20a40923348bf273a12bf10a9c43e332a15bf765937bfa0850440883850bf80850fbf108965bd8a0c67bfcc99dabeed630040ae4f4fbfe10afdbeba0df8be33ec5fbf678d5d3c090cfc3f408d41bf9858dbbecfc83abf154b04bf0c50e53eada6fc3faf322cbf16d3c6bed33633bf9988133df092363f5a2e0140076817bf0751c7be40e5cfbe9e77133f9f9d353fd67f05405a1e0bbf4da2dcbe1b96313dc817663fc356df3e89a1094034070cbf6ca2febef1dbf43e73d2603fbec1b6bb71ff0b40a2c919bf5a2a10bfa8bb3a3fff81043ffcfbe4be20b20b4033242fbf1b6d1abfcb70323f34ce34bdb33637bf1cd70840dcee43bf222e1abfcd67ca3e164016bf2de234bf5b6902401b5f4bbf376d18bf90547cbd5dfd65bf98a3debec546fc3f042e4abf6e0408bffc1a01bf4c0c5dbf4fe016bce968f73f894f3cbf52d5eebe449441bfdfae00bfdc82d63e0315f83fd50f27bf5beed9be221e39bfe635433d9567303f5509fe3fae8c12bfd84fd9be6965d5bebaa9143f0907333fd17f0340d99b06bf5636edbe20b1483d2f09653fe457e33ecac50740f0cc07bff40307bfb009ff3e82ef5d3fe9658d3cb7340a406aab15bfb89d17bfdf89413f07ea003f3f1ad6beabde094020eb2abf351122bff35c383f1fb863bd9f0a31bf81e40640466e3fbf766022bf92f4cf3e386817bfb95332bf8a680040fe4846bf239321bf274288bd34f064bf2e91e2be8000f83fb9cb44bfc6c411bf61ce05bf2b185abfcfb502bdf501f33f96ce36bf3c6c01bf79e847bf60e0f9beaca1c73ecfbdf33f73a921bfbe8fedbe29a23ebf6be1743da22d2a3f4eecf93ff46f0dbf98d0ebbee38ddabe42ec153fe566303f7e980140b8db01bfd245febee4355e3dd4f4633fdf55e73ec8000640fd5803bf46f10ebf4546043ff5ef5a3f78a7243d0e800840205611bfd0491fbf42e7473f9c53fa3eff15c7be20220840437b26bf2dee29bfa6ee3d3fc3698abd22c52abfe10a0540c3b43abfc0cd2abf6047d53e7ba518bfd3ad2fbfe604fd3f68fb40bfabef2abfaf7b91bd49e963bfba51e6be61f4f33f142c3fbfd8bc1bbf2a220abf881757bf62e65cbdbad7ee3fdd0b31bf3ca70bbfddb14dbf211df2be7f28b93ecfa2ef3f08001cbfa1d300bf0e9a43bfaa8f943d631b243f0d08f63f8e1208bf5bc9febe63fbdebe6e49173f58d52d3f1696ff3f0cc0f9be8de607bf1f7c743d54df623fd536eb3e4e530440b45efdbe601917bf2bba083fabd5573f2245803da1e1064091cf0cbffb2e27bf73bf4d3f6b95f23e244eb8be167c064066db21bf980232bfd608433f6a8aa3bd148f24bf77490340e0c835bf8a7133bfc032da3e2dee19bf44072dbfac69f93f757b3bbff77b34bf25f99abdcc8b62bf4b44ebbe2d21f03f6e5639bf04dc25bfed580ebfecdd53bfa61a9dbd23ecea3faa0f2bbf0c0516bfd51053bf78ade9bedf4cab3ebdc7eb3f271b16bfec030bbf26cf47bf9680b23db0791e3f1860f23ff67902bfb00c09bf16bbe1be040b193f5d642b3f5030fc3f1257efbe8fdf10bfb725893d8bdc613f7591ee3e68bc02401fa1f3be837f1fbf8f050d3f7293543f02a5ab3dec560540541708bf7a562fbf7c07533fb7e5ea3e32cea9be1fe90440d70b1dbf9a573abf5ea7473f394eb9bd978c1ebff29c014009ad30bfd64e3cbff6b6de3e50af1abf68e72abf20fdf53fcdb335bfc43a3ebfdc99a7bd6ef360bf9bc8f0be8f99ec3f216133bf0ae82fbfe5ba11bf68f150bfb3a2cabd4c5fe73fae1125bf522020bf193657bfb086e3bedf779e3e844de83fc33c10bfa6ea14bf5a2c4bbf66aec43df2ca193f3909ef3fbfaef9be238f12bfafd1e3be05cd193f74042a3f0400f93f3e94e4be50f419bf3f7d983d4597603f65bff23ecb3101409539e9be0a4728bf810a113fa056513f4b15d03decce03403eec02bfc20e38bfb649573f6dd0e23e53129fbed057034029c117bf6e4443bfa3844a3fa745d1bd5e651abfebf3ff3f8d262bbff19f45bfd215e03ec15f1bbfd2d329bfb4b2f23fdbb82fbf160c48bfbbbcb3bd189b5fbf1135f5beab34e93f24362dbf9b083abf468d14bf13644ebfd31eecbd54f5e33fa4dd1ebfa0502abfa1465abfeaf6ddbe7553953ecbf5e43f212a0abf2ce51ebf1c524dbf6345d43d5595163f1ad4eb3fac07eebee8221cbf6aa4e4be912b1a3fd367293fa4f0f53f8a76d9be981723bf8f5aa43d6e3d5f3f1135f73ead6eff3ff87bdebe131b31bf06db133fb9c94e3fedc9f13d02570240f72cfbbe0ed340bf1a5a5a3fe140dd3e73ef95bec7d60140ff4912bf823e4cbf71a44c3fbfece0bdec3717bf3ecffc3f4b7025bfc6004fbf20d2e03e52c11bbff73b29bfcd87ef3f649129bf83ed51bff096bebdcf4e5ebf6461f9bee3efe53f5ddc26bfc83a44bf462417bf4ce14bbfa82b06be80abe03f0c7a18bf879234bf6d1c5dbf4774d8bec1778c3eaebde13ff6e803bfc5ef28bf97504fbf6fb4e33d7a77133fb2bde83f6408e2be4ac425bf076be5be26871a3ffbd0283f11fff23f7406cebe0b462cbf424caf3d7ee55d3fb189fb3efb96fc3f8370d3bec6f839bfc974163fc9424c3f2a32093eafed00402435f0be06a149bf45305d3f8cbcd73e1f158dbe98640040a8ab0cbfc94355bff5a14e3f3f8ef0bd9b1a14bf2bc9f93f6d901fbf446f58bf259ae13ed6291cbfce9828bfb87dec3fd93c23bf36de5bbf8ae9c9bdd5d95cbfaaf5fdbe9bcce23f3e5320bfa47d4ebf86d619bfbd1249bf7ac217be3a83dd3f5ae611bf19e53ebf690f60bf534bd2be9acc823e88a6de3f7cf1fabe970933bf7a6751bf9c0ef53d460a103f3fc7e53fe4afd5be96722fbf434de6be55f61a3faf1d283f662cf03ff642c2be1c7f35bf8c5bba3de16f5c3fde10003f83ddf93f2d16c8beaedf42bf3624193f5075493f8ad41a3ee426ff3ff3efe4be397852bfa123603f8c91d13ee16b83be9603fe3f96e506bfbb535ebf7bb5503ff70301be98b110bfdee2f63f628619bfbcea61bff56ae23ea39c1cbff9e727bf7599e93f24b51cbf5cdd65bfbc8dd6bd09185bbfcfab01bf13d0df3fae9419bf84d058bfc3c61cbfe6c145bff1012cbeea81da3fe41c0bbf174849bfa74063bf280ecbbed87a6f3ec0b5db3f01a9edbef1323dbf0fac53bf16d0043e031a0c3f00f6e23fa8f5c8be8d2e39bf614fe7be12871b3fdc3e273fa37ded3f7124b6bebac33ebfdb0dc63d39c75a3f619c023f0647f73f5f65bcbe92d04bbf82071c3f2231463ff0d92e3e2e95fc3ff154d9beff585bbf9b54633fbb54ca3ec9be70be5861fb3fdcf200bf256e67bf14f0523fcd380bbe83d10cbf1821f43f8a4c13bf89726bbf2c34e33ee1181dbf912f27bfd4e0e63f09f415bff9e86fbffe61e2bd604f59bfcd5404bf6f00dd3f889a12bf753163bf3d761fbff97342bfeaa93fbee6add73f0f1804bf11ba53bfd92466bffee6c3becd2f5a3ea9f1d83fd7eedfbe266b47bf52b155bf33dc0e3ee05d083f0e50e03f7ed1bbbe40f842bff314e8be44061c3f8d83263f8af8ea3f7ca3a9be631448bf1546d13df10e593f822e053fefd8f43f7e56b0bee7cb54bfd9b01e3fd5e8423fb276423e782bfa3f705bcdbe4b4364bfb838663f7e2fc33e59715bbeb5e7f83fb89cf5be369270bfb8eb543f5b2115befc2609bf5089f13f08dd0cbf1c0575bf6ccee33edc851dbf419426bf7a4ce43fb1070fbf0ffe79bf6ddfebbd12bd57bfb09f06bf1d56da3f3b730bbf989c6dbf1db721bf7f6f3fbf164051be55ffd43fdbcaf9be1f365ebf959b68bf4148bdbea3e7463e3452d63fd6dad1be89ac51bf796b57bf212f183e07fa043f48cddd3fa157aebec9c94cbf33bce8be9b791c3fa0dc253f1495e83fc4d19cbe9f6b51bf94d5da3d0775573f6485073f728bf23fb1faa3be16cd5dbf7bf3203fa7e03f3fe52d543e39e2f73f4a16c1be8f336dbf7caf683f5891bc3e222948be5a8ff63f5006e9be25bd79bf56a5563f0d791ebe4ec405bf4514ef3fc34406bfe59f7ebf7877e43e2cff1dbf23e725bf53dde13f97f007bf890d82bf32ecf4bd181b56bf2af508bf1cd2d73f5d1f04bf2b1078bf9ede23bf504e3cbfb81b63be3e77d23f440bebbe7bba68bf03f66abfbe6bb6be0a55333e5bd8d33f2a6ec3be7ef55bbf951259bf0ad4213e5e88013f916edb3ffa88a0bed8a156bf3f60e9be44f01c3f8f32253f0554e63fe2af8fbe66c85abf3eeee33d52cc553fb8e4093f3c5ff03f565297be4ed366bf531c233fc2bb3c3fe526663e1abaf53fcc85b4befd2876bffd096b3f97b5b53ebd9434befd58f43fe722dcbefd7681bfe54b583ffb1d28bef55302bfc7c2ec3f1608ffbed02084bf2c1ce53e427a1ebf983825bf2b94df3f5caf00bf301f87bfaacdfdbd925a54bf28690bbf4b75d53f6c3ff9be2c4581bfa4fb25bf17f538bf76cf75be8516d03f00f3dbbe4a4573bf77446dbf5c14afbee5d71e3ef784d13f5aaab4be584466bf0db35abf6d2f2c3e6dd2fb3ea834d93fc86692be0d7f60bfaf0aeabe09761d3f9176243f0236e43fba3e82beae2964bf4ab0ec3d580e543fd6540c3fe154ee3f065e8abeb7dd6fbfa837253f3a63393fa9db783ea7b3f33f72aaa7bec3227fbf45586d3f1f60ae3e3d1620be3545f23f18f3cebedb1186bfe8ea593fad6d32bef670fdbe8395ea3fab36f1be81f488bf33bfe53e1bfc1ebfdd8224bf4574dd3f4483f2bed3328cbfc59103beb25352bf722f0ebf2143d33f35e4e9be548486bf262b28bf472335bf565585bebce0cd3f647eccbe56d47dbfa0a46fbfd4baa6be7a16083e975bcf3f6c8ca5be6c9770bf51615cbf3e13383e6fbaf33efb22d73f7eee83be5e606abf7dc3eabec11e1e3f3992233f433ee23f8cf768bee68e6dbf2980f53da024523fb7f20e3f676fec3faa357abee5eb78bff35e273f3f9e353f1cba863eccd1f13fa6809abe1b1084bfdfb76f3fe40aa63e1e5309bef156f03f9e72c1be90ae8abf8c945b3fe5213ebe8972f5be8d8fe83f8d10e3be17ca8dbfd25ae63e36881fbf66c423bfa783db3f7744e3bea64691bf68d907be684550bff5ee10bff841d13f6823dabeabc38bbf431e2abfcb5731bf07768fbe73dccb3f0da5bcbe2d3284bf3cbe71bfcc829ebe465de43dca62cd3f610d96be74ec7abf69d75dbf15a6433ef20cec3ee43fd53f2e346abea04474bfde4aebbeb8b31e3fe4d0223fd172e03f5ac24cbeacf776bfe7c8fd3dff2b503f0095113f80b4ea3f78045fbed1fe80bfa34f293fe5d7313fedcb903e051af03f96008dbe4f9088bf65d1713f08d59d3e3bcde6bdae93ee3f4398b3be424c8fbfd5055d3f367849be24e4edbe94b6e63f8e8bd4be2ca092bf82cde63e800120bf672523bf54bad93fa2bbd3be305a96bfca2a0bbef9784ebf794a13bf7869cf3fa516cabe6c0291bf14b32bbffbe82dbf417498be0901ca3ff17eacbe6f7989bfe17873bfa0fb96bebe08bd3dd691cb3f304286be0aa082bfdb0b5fbfe9464e3e5725e53ec782d33fb2f64bbeef277ebf43b8ebbe83391f3f5b26223f58cbde3f1cfd2fbe143080bf8d4d023e125b4e3fb8f5133f341ce93f164743bed88785bff1e62a3fae642e3fe9d2993ea384ee3f7c767ebed5108dbfeb8b733f2d50963eb56cbfbdd6f3ec3f0178a5be3aea93bffa3b5e3f110754be6ffbe6bee502e53fd8bec5be4c7697bfd745e73e458620bf027822bf7118d83f85ebc3be976c9bbfef2d0ebe60a54cbf3aa415bfcdb9cd3f16c1b9be9e3f96bf01242dbf666d2abf8367a1bead4ec83f590f9cbef7be8ebfd90b75bf3c598fbe8ae7953de9e8c93fd05b6cbe41c887bfdb2460bf8b0a593ed445de3ecdebd13fb8292dbe6c0484bf791decbeb2c01f3f397c213ffb47dd3fe6ab12be9de384bf4768053e00844c3fb352163f9fa6e73fc40027be96108abf245a2c3f06e52a3f3bcca23ebe11ed3f3e6462be3d9191bfa51e753fbab08e3e144098bd8377eb3f901397bef38798bfe2565f3fadb45ebe9d1ae0be9f74e33f9cacb6bec94b9cbf65b6e73e100b21bf25cc21bf089ed63f69d7b3bee57ca0bf30df10be58cc4abf37fa17bf0233cc3f7426a9be327a9bbfea6f2ebfb6e826bf5d48aabe6dc5c63f1e5a8bbeb10194bf3e7676bf29a487be72375e3d0f68c83f58a84bbee5ed8cbffe2161bfdce4633e9674d73e017bd03f84d30dbedaf288bfb27aecbeaf48203f06d3203fc2e8db3faca7e9bdcd9589bf8031083ea9a84a3fd4a9183fc853e63fc2350abe80988ebf20a82d3fb85c273f44b0ab3e5dc1eb3f6bce45be011196bfa488763f7fff863e50d462bdbb1eea3f286d88becd249dbf1356603f667469be5948d9bec90be23f9257a7bed81fa1bf971fe83e108f21bf822221bff24ad53f8482a3be448aa5bf967113be47c848bf997a1abff0d4ca3f624a98be34b1a0bf50ad2fbf5a1423bf41c3b3be2265c53f0ec674bea84099bfc6d077bfa97b7ebefa5b0b3d270fc73f0e712abe181092bf141562bf2cd76f3e761cd03e4430cf3f6af4dbbd8cde8dbf93daecbed4ed203f580a203f90adda3fcdf3acbd14468ebf60c80a3efcb5483f75121b3f9223e53f53d4d9bd241f93bf2be42e3f018f233fdc14b53e6093ea3fe0b828beb08f9abf5de2773fab407d3ec0de0fbd5be9e83fe00d73be40c0a1bf134b613f273575be50f6d1be3dc8e03f5dc297beccf1a5bfc785e83e451f22bf706c20bff124d43f94e492be7d92aabffed715bee89b46bf141f1dbf9fa5c93f9d2587be4fe2a5bf36cc30bf340c1fbf1a8dbdbe0634c43f524852bec5799ebf4c0779bf11f46cbe3d325a3c75e4c53f8cac08be492d97bf4aec62bf43417c3ee6a5c83ebe11ce3f2e2b9bbd8ec692bf3724edbe9c95213f89451f3f539cd93fb4555ebdf3f392bf71350d3eeaa5463f3c921d3fa51be43fb5269ebd21a497bf4103303fcc8e1f3f36c1be3e3e8de93f41160bbeab0c9fbf7018793f2ac26b3e79d16bbccfdce73f08b254be2759a6bf0e23623f79a980be5694cabe85afdf3f80e487bee2bfaabf5cd3e83ee7a322bfbac91fbffb28d33ff50782be0795afbf17ad17be6fa144bf6d7b1fbfd7a1c83fbe856bbed50cabbf8caa31bf90571bbfa563c6bec82ec33f984f2fbe35aca3bf67f979bf29cc5cbe1488b1bbabe4c43f4cdaccbd8b449cbf339263bfacd3833eb6efc13e3f1ccd3f34d532bdeba997bf4653edbea726223f46a01e3f05b2d83f5c9fc1bc879e97bf0f2e0f3e37b3443f0be31f3f2939e33f62f842bdb9269cbf2de5303f5bd81b3f3690c73e38ace83f7ae8d9bd5987a3bf530a7a3fee9e5b3e85488f3b55f6e63fb0d635be03efaabfcecb623f494386befde2c3bec1bede3f8a8e6fbea389afbf2d13e93e5a2423bf3b2f1fbfc352d23f4af061bee591b4bf890f19be93cc42bff0a121bf14c5c73f7c5b48beb930b0bfec5832bf65db17bf9b91cebec350c23fb7f10bbebed7a8bf13b97abf10844dbec427bbbc2a0bc43fc48c87bd6d55a1bf6a1564bf9e38893e69afbb3e4e4bcc3f00a736bc08889cbfba77edbe5eae223f66071e3f69ead73f5064f33b25459cbf0cbe103e97da423f830b223f1878e23f68cd8fbc51a6a0bf8e97313fac56183f0fc0cf3e69ece73fe1c69cbd4cffa7bf9dc97a3ff35d4c3e34e0b23c0232e63fc68e16be9d81afbf6053633f24988bbe899abdbedef1dd3fbaea4ebe034fb4bfce4ee93e90a723bf6a921ebf97a1d13fec703fbe8588b9bf87291abe7dfb40bf9abb23bf950ec73fccd324be594db5bf53e632bff36614bf448cd6be3799c13f816cd0bdbefbadbf88547bbf655f3ebef23023bd3c57c33fd7fd02bd5b5fa6bfb37f64bf618f8e3e3196b53e479ecb3fb424b23c6760a1bfd594edbe7f34233fdf711d3ff144d73f0e491f3d67e7a0bfe205123ea806413fad25243ff3d7e13f3052533c9322a5bfd928323f5ddd143ff0b9d73e514de73f9ea13dbd2e74acbf92647b3f51423d3ebd291f3d4c8fe53f68beedbd9110b4bf19c2633fc0dd90be4279b7be4048dd3f40e32dbe850fb9bf7a83e93e4c2824bfaef91dbfb214d13f98981cbe5778bebf96fe1abe7a2f3fbf6dc725bf887dc63f24f600be1b62babf4a5433bfbefc10bf0350debe4e07c13fc24988bd9a17b3bf84cd7bbf4f692fbe46e966bd18c8c23fe010283bc761abbf44d264bfb7d4933e9fa6af3e7914cb3f9f44613d9032a6bfcbaaedbeeeb8233fd8df1c3f00c1d63f6cd7903de984a5bf1a09133ed3383f3f8130263f2a58e13f0925333d269ba9bfa69a323f126f113f497adf3e64cee63ffc3f80bca5e5b0bffddc7b3f63572e3e08f6623d9a0de53f4b9aadbd799bb8bf3419643f721096be1082b1be39c1dc3f0a7e0cbeb0cabdbf63b1e93e4fa624bf4c651dbf3babd03f2fdcf2bdd960c3bfbca51bbeb6313dbf9c0228bf0411c63fd693b9bd706ebfbf40ad33bfc3330dbf3dc6e6be1e9ac03f9c29febcc62ab8bf6d2f7cbf82c71ebe0b6798bde05cc23fe445193d305cb0bfb71365bff8c5993ef617a93e1badca3f3969b53d13feaabf26b9edbee562243f3b281c3fe75dd63f3bc3d23d4b1daabf1bdb133e36513d3faa4f283f1ef8e03fe07a993db40faebf82f5323fd2ae0d3f07d3e73e046fe63fa78b7b3c5e53b5bfdc3d7c3ffbc91d3ea188963d42ace43fce7759bdf421bdbf8c60643f42e19bbe00f1aabe065cdc3f3082d5bd1180c2bf06dae93e773525bfdbbf1cbf976cd03f9dc1abbdc53ec8bfcb0f1cbe16223bbf74472abfabd0c53fde0561bddd6ec4bf47e333bf5d6b09bfc819efbe8159c03fc8659e3b0b32bdbff06a7cbfa2670ebe4c45bcbd871dc23f7bb8943d074cb5bf4b3965bfbc899f3e82d7a23e1570ca3f12edfa3d27c1afbff6b2edbe6cf6243fb78e1b3f5f23d63f14bd0a3e61afaebff57f143eba573b3fa7782a3f4bbfe03f773bda3d497fb2bfd531333fc5e8093f7a0df03e7536e63f60a43f3d1abcb9bf39797c3f336d0d3e9b6cba3d6f72e43fc5ffabbc1fa2c1bf9c8a643ff37da1beaabda4bee01fdc3f8b3491bd002dc7bff4eee93e58a825bf9a3e1cbfcd4fd03f5d4448bd1714cdbfce271cbe686d39bf39212cbf1db3c53fe2dc9bbc7b65c9bf7af633bfe92e06bfb61ff6bed63bc03f9ea1273d392fc2bfec817cbfc33900be21d9dabd7200c23fee4fdd3d8532babf934565bf6b8aa43e88819d3e0f54ca3fa480203e627cb4bf60a6edbe3e73253fc10e1b3f7908d63f49682c3e4b3bb3bf95d0143e289f393f9d532c3f29a5e03fcdd20d3ee7e9b6bf104a333f1da6063f3312f73e701ce63f96dda03d2920bebfc38f7c3ff689fe3d1214d93dd457e43f9cf33a3cdd1cc6bf429c643fbf6da6be675b9fbe3704dc3fcda518bd00d3cbbff0fee93e132026bf45b91bbf1d53d03f406760bcd0e0d1bfdd0c1cbe2bc937bf28e32dbf80b6c53fdcff8b3c4e52cebf95f233bf150f03bfc6cffcbe3d3fc03fd8259e3d4e22c7bf887f7cbff00be5bd50fef7bdcc03c23f742d133ea20fbfbf994065bf775fa93e306a983e5257ca3f2fcc433eaf2fb9bfbb95edbeb4eb253f3b941a3fa10bd63fe15c4e3eecc0b7bf48ec143edbf5373fdf172e3f3ea8e03f70d62e3e6e4fbbbfe64a333f2580033f04c1fd3e811fe63f0487e23d6e7fc2bfe48c7c3f6231e33da24af63df25ae43fe0a3343d1a92cabf949c643f0c33abbe88369abe6c07dc3fe0705dbb0d72d0bf5b0aea3ef99426bfe3371bbf7975d03fc0cdb13cbba4d6bfc2c31bbef82e36bfcb942fbfb5d9c53f74ae5a3d2035d3bfebd933bfc40200bf1da001bf9562c03fc7d9e83d190bccbf60667cbf8152cabd7c060abe8526c23ff0e6373e2ee3c3bf3b2c65bfb114ae3e8f82933ee478ca3fa853673ee2dabdbfa681edbe0d61263fa21d1a3ff92bd63fb695703e1540bcbfd5d8143ea557363f8eca2f3fbec7e03fd023503eb0afbfbfdf36333fb06e003ffa16023fdd3ee63f8c62123eb7d9c6bf46737c3f168cc83d4c32093eed7ae43ffcd09d3da201cfbf798d643f96d7afbe654295be8d28dc3f28defb3cee09d5bfe211ea3e130627bf53bb1abfd1b5d03f30aa6a3db05fdbbfc1491bbe2e7d34bf495931bf991bc63f4c04b83dcf0dd8bf7eaa33bf8290f9bea6fc04bfb9a4c03f0af1193e79e9d0bfd4337cbf62f2adbdd0b018be8467c23f31cf5c3e0badc8bfbc0465bf7720b33e32568e3ec9b7ca3f0689853ede7dc2bfde62edbec0ea263f5994193f9b68d63f0687893eaab8c0bf2a99143e03af343f3982313fd302e13f72b6713e8b0ac4bfb10b333ff96cfa3e096b053fb279e63f8fc7333ee12ecbbfeb3f7c3f1f4eac3ddee7173ee8b6e43fcdd2e13d4f6bd3bf766c643fc9cab4be4e0b90bea366dc3f144d853d7c9ad9bf0513ea3e6b8027bf6a361abf0418d13fe4a1be3d740fe0bf61991abecfb132bfa33133bf2080c63f9086013efcd9dcbf646133bfa9e1f2bee16b08bfb609c13f90a03f3e33bbd5bf65e67bbf8ad690bd748d27beedcac23f3bf3803e596bcdbf6aca64bfa846b83e3622893e2f18cb3fee83973e3117c7bf2a39edbee172273f1010193fb4c5d63fdfe39a3e9429c5bfac2f143e19f4323f2846333f985de13f10c9893e0d5fc8bf26c9323f1bc3f33e12cf083f01d4e63f2178553ed57dcfbf0bf27b3f66438f3d52cb263ecb12e53f3a32133eafcdd7bf9e38643f57d1b9beafd68abe89c5dc3fa821cc3dd721debf930cea3eeaef27bf69bf19bf5699d13ffa22043e83b4e4bf2bd219be201c31bf42cd34bf6704c73f322f273e2f9ae1bf5c0c33bfc8f3ecbe726e0bbf9b8ec13f6c72653ec380dabf638c7bbf6dff6dbd569534beda4dc33f3592933e701ed2bf0f8864bffcd1bc3e209b843e5197cb3fa296a93e0aa7cbbfca0eedbeb9e2273fcea5183faa40d73f925dac3edd92c9bf94a7133e2d60313fcedc343f99d5e13f76d79a3e31adccbf517a323f1fcced3e7bcd0b3f654be73fb36b773e9dc6d3bfc4977b3ff2e26a3d71d8333e268ce53fb4b9353ef028dcbfa8fb633f5948bebe144886beaf42dd3fd8b0093e57a0e2bfba00ea3eee5428bf515519bff634d23fa014293e5450e9bfc4fd18be64b62fbf2f3436bf73a3c73f3ef14c3e0350e6bf67b032bf14a0e7beb61a0ebf522ec23f80ae853eac3bdfbf292a7bbfe53f3fbde53940be3cebc33f1640a63e8ec7d6bf374064bf4cf0c03e8c8c803e4030cc3fb4bcbb3e492ed0bfd3e5ecbe0348283f0f46183fbcd4d73f4eefbd3e1af5cdbfa206133eb8f32f3f9347363f3f66e23fff00ac3e6bf5d0bfd422323f9f6be83e42790e3f60dbe73f1fcb8c3ec209d8bf15357b3f8d3d3c3db8843f3e761ee63f1073583ee07de0bfc1b8633f0f5ac2be002a82be72d9dd3fd4792d3e2617e7bf09f2e93e31b928bf97ec18bfd8e9d23f99224e3ef6e2edbfb81018be165c2ebfff8b37bf2a5cc83f5fc9723e8ffbeabfa24932bf6778e2beada910bfc3e7c23f6fae983e08ece3bf05bc7abf1a0712bd875e4bbe05a2c43f18fbb83ecb66dbbfa3f063bf29ebc43ece52793e01e2cc3f50f4cd3e00add4bf31bcecbe25aa283f90e9173f0481d83f3e97cf3e5250d2bfb44b123ee8932e3ff7a1373fb20ee33fda43bd3eb937d5bf77c0313f4639e33ebb06113f1983e83f1cfa9d3e3f47dcbf97c67a3fb0280f3d4bae4a3ed7c8e63fe45a7b3e7dcce4bf016e633fc447c6be50717cbeda88de3f7268513e4886ebbffbe1e93e791929bf3f8818bffab6d33f184a733e856cf2bf2af516be2fe62cbfe6fa38bf7d2dc93f625a8c3ef89cefbf4ccc31bf47e0dcbede6613bfddb9c33f91b7ab3e0292e8bfee337abf3981c1bcc15457be2e71c53fb8c1cb3e4ffcdfbf4a8c63bfb24fc93e5ae0703ea3abcd3fe43be03e5023d9bf3e83ecbee223293f3978173fa244d93fc253e13e9ba4d6bf0d6c113e72252d3fab06393f1fcee33f6b9ece3e2874d9bf8647313fa6a5dd3ea0bc133fbf41e93f3d41af3e1d7fe0bfb33d7a3ff34abc3c68b1563e6e8ae73f16378f3ed114e9bf3210633ff696cabea3e373bef94fdf3fd379753ed0edefbf9ac9e93e828629bf631818bff8a0d43f63478c3e46eaf6bfe1a815be0f5d2bbf0c783abf171cca3f755a9f3e5931f4bf2c3b31bf912ed7be0d2a16bf61a9c43fc3c9be3ed52aedbf459979bf9e0f3dbcdc4063be925dc63f7f93de3eb285e4bfdd1c63bf8cabcd3ec193683e0e92ce3f5493f23e498fddbf8d47ecbeb092293f7f13173f8424da3fa725f33e6af0dabfd868103e28a72b3f24763a3f65a9e43f9412e03e57a9ddbfbebd303fa8f3d73e1577163f1b1cea3f48a3c03edbafe4bfd5a2793fbbe3323cc8a0623eea67e83f8bd9a03efe54edbff3a6623f67d9cebe5a906bbe6e33e03fb6d98c3e684bf4bff4b0e93ea3e229bff6ba17bf41a3d53fd8f39e3efa5dfbbf747614be63142abf3cb33bbf4223cb3f1c61b23e9abaf8bf3cb330bfb859d2becb7b18bf85b1c53fcee1d13e54b8f1bfe00779bf4c48a2bae24d6dbe6862c73f7b6df13e8304e9bf90b462bf2660d13e7a9b613e9190cf3fc27b023f19f2e1bf9e12ecbe2fec293f8bc3163f1b1cdb3fa584023f9c34dfbf326c0f3e925b2a3f61b13b3f1a9ce53f059cf13efcd7e1bf003b303fda13d33e8ec6183fd70deb3f551bd23e42dae8bf2c11793f3b60243a4fb26c3ef45ce93fa78fb23e148ef1bf4343623f4c80d2bef18164beca2ee13f9f059f3e7da0f8bf2396e93e70372abf276617bf61bad63f74a7b13ef0c8ffbf934d13be40ef28bfc5c93cbf743fcc3fe76bc53e273afdbf6f2e30bf59facdbed98f1abfb4cec63f9cfde43ee33bf6bfea7878bf1f3d053cd75a76be1c7cc83fd226023ff679edbf1d4e62bfc9c0d43e1b4f5b3ea8a3d03f12b30b3fb64ce6bfe7e0ebbe873e2a3f077a163ff927dc3fef7d0b3fe871e3bf656e0e3e8a2f293f03cc3c3fe6a2e63fb59b013fb100e6bffdb92f3f11a9ce3e0ada1a3fa613ec3fb6a5e33ef5feecbfd481783f43e40ebc7ac5753e3e66ea3fae55c43ee2c0f5bfe4e0613f94d7d5be9b175ebeb13ee23f2e3db13e22eefcbf6f7ae93e3a8a2abf941317bf76e5d73f5d61c43eab1502c00f1012be90c827bf34df3dbfc46fcd3f077ad83e1ed800c0d59f2fbf8b92c9be51a21cbf0200c83f591cf83ec1b5fabf91de77bfa916903cbd637fbecca9c93f84990b3f47e6f1bfecde61bf8a2dd83e3afc543e7ecad13f12ef143f529feabf29aaebbe43962a3f092c163f5747dd3f1c7e143f76a8e7bf2d5f0d3e7106283f49e13d3f09bde73fc6710a3f9123eabf402f2f3f963bca3edbe91c3fca2ced3f3c41f53e0b1ef1bf10e7773fe99894bc30d57e3e0083eb3f8b2ad63e85edf9bf6176613fa737d9be17a957be4e62e33f6f7fc33e3d9a00c0c05be93ec0de2abfd5bf16bf5b25d93f3b21d73e324204c04f9910be7e8026bff4103fbf0ab5ce3fd68aeb3e030e03c010f72ebf55b0c4be52e81ebf5046c93f8b9e053f2925ffbf992777bf2efce53c0ba684be63ecca3f500e153fcb48f6bfe95961bf2001dc3ef5004e3e0b06d33f472f1e3f5de9eebf4564ebbefcff2a3f14cf153f377bde3fcd841d3fced7ebbff4250c3e48c6263f29093f3f88ebe83fff4f133f2c40eebf718b2e3f895dc53ef1281f3f425aee3fdf76033f0937f5bf6e2f773f5f10eabc7a64843e2fb4ec3f350ee83e6713febf9cf7603f44f7dcbe1e9650be879ae43f46ccd53e6ab902c02036e93eb53b2bbfd06416bf1d7dda3f89e7e93e1d6906c03e1c0fbe404625bfdc3240bf5012d03f0a9efe3ec53d05c0094f2ebf742ac0bea4ff20bfb1a4ca3f872f0f3f16c401c0077576bf26251a3dae2489be0847cc3fc8841e3fca9ffabfb4db60bf596cdf3ecccf473e8c59d43f7973273f6a29f3bf4329ebbee74f2b3fe58a153fe5c6df3f2a92263fc0feefbfe2e60a3ed38e253fcc25403fb231ea3fe9361c3f7155f2bf40ea2d3fe7cfc03e203b213f519fef3f68560c3fce48f9bfea7c763f8c381cbd7ae2883efafced3f4c02fa3e981801c0087e603f1552e0be4d5b4abe76eae53fe924e83ec8d304c06217e93e55812bbf532116bfd3e5db3f85b0fc3e128c08c099e20dbe4f5224bf0f1241bf9680d13f9ad8083f2b6907c0c3c02dbfe884bcbe20ab22bf0c14cc3f51c0183f22f103c082db75bfa22e3a3d8ec78cbe97b2cd3f28fc273f56eefebf236e60bff13de23eabc2423edebdd53f83ba303ffb61f7bf5bf6eabeff932b3fc850153f4f23e13f67a42f3f4c1ff4bf96d0093e9c93243f9b09413f8c88eb3f1024253f1b65f6bf035f2d3f491fbd3e95e6223f16f5f03f593c153f2c55fdbf01e3753fe7303cbd63888c3e8b56ef3f8200063f8d2403c0ca13603fbc1ce3bef13145be444be73f4e84fa3ebaea06c07ef6e83e29c72bbf33de15bfc55edd3fdcbd073f2bab0ac083a10cbe5b6323bf0deb41bf1dffd23ff361123f539009c05f2f2dbf44f2b8be974a24bfa693cd3fb350223fd81906c0553e75bf7f93593dae5090be582ecf3f2874313f579a01c012fe5fbf0202e53eaed43d3e5232d73f09043a3f4793fbbfe4c2eabe77d82b3f3c16153fcc8fe23f1cbb383f9f39f8bfd2b3083ed99f233ffee4413f73efec3f04172e3f4f6ffabfced02c3f2b85b93eb884243fea5af23f46281e3f21ae00c09d45753f60775bbdaf13903e38c0f03fd0040f3fa32d05c043a75f3f70d8e5be0f2a40be3ebce83fee74063f57fe08c0bbd5e83e9d0b2cbf579c15bfcfe7de3f7924113f5ec60cc006250bbe105022bfc8e242bfc08dd43ff4ea1b3f36b30bc07a802cbf12ccb4be762726bf5b23cf3f80e02b3f363e08c0217f74bfd7407e3dd66394be2dbad03f92ec3a3f67b903c082725fbf844ae83ee710383ed3b6d83fce4f433f4fbdffbfa879eabeaf352c3f67c7143f4f0ce43f09d6413fbb4dfcbf5e6b073ed791223ff2d5423f5e66ee3f8e0f373f0c74febfa4252c3ff162b53e8a5c263fc3d0f33f021a273f06af02c09685743f34ea7fbdce2d943ef139f23ff00d183fd53307c056215f3fec10e9bec3493abe4b3dea3fb3aa0f3f950e0bc0d1a8e83ec15f2cbfe34c15bfeb85e03f2b8c1a3f1adc0ec08b9209be973b21bf87d943bf7531d63f1773253f28d00dc0abcf2bbfb1d8b0be5eec27bf2cc8d03fdc6e353f955c0ac0edc373bf4c2b903d212e98be345bd23f8f64443fe0d205c075ef5ebf2d41eb3e89de323e9950da3f609d4c3fc5ee01c07240eabe92782c3f6b90143f259ee53f57f54a3fbe2c00c0d813063ebf7f213f25c8433f9cf2ef3f6b0e403fb03801c0aa7b2b3fc167b13e7d1c283fe45bf53fa612303f43ac04c09eca733f140791bd29f6973edcc8f33ff31c213ff83509c071a25e3f2df9ebbe521035be76d3eb3f22e4183f131a0dc0cb89e83e69992cbf511615bfb331e23fddf3233f5eee10c0166308bee87220bf498b44bfc9e2d73f07fa2e3f4ee90fc0f8442bbf02d7adbea14129bf977ad23fdffb3e3f13770cc0f12d73bf15629d3d65159bbed309d43f28dc4d3fa9e807c0b1845ebf7c94ed3eb8d62e3e00f8db3f40ec553f97fb03c03210eabe9eb02c3f4a62143fa73de73fbd17543fc42f02c02401053e69b0203f207e443f918cf13f9311493fd43403c077f32a3fc35cae3e9b71293fc3f4f63fbb0f393f0fa706c04734733f95369ebd11e09a3e8765f53f722f2a3f79350bc0523a5e3f6947eebefbee30be5a77ed3f5a1f223f8b220fc02168e83ea2d32cbff3df14bf95eae33f715b2d3f40fd12c0c22807be4fa91fbfb63c45bf2aa1d93fae7f383fc3fe11c058b52abfc0d0aabe76962abf063ad43f6f87483fce8d0ec0339272bf3bb2aa3dbffc9dbe7bc5d53f3553573fdffa09c069155ebfb9eeef3ed4c82a3e7aacdd3f343c5f3f380506c0ecdce9be07ec2c3f4731143f4deae83ff13c5d3f083004c0d2e6033e17e31f3f3131453fb833f33fb418523f852e05c071662a3f7550ab3e4ec52a3fdc9af83ff410423f7a9f08c03a98723f0e79abbd36ca9d3e670ff73f2d45333f69320dc03cce5d3f219af0be1dc92cbe6728ef3f2d5c2b3f102811c08145e83ebc0e2dbfc4a814bf27b1e53fe2c2363f860815c0e7b605be35c11ebf680746bf2a6ddb3fed03423f491014c01a0b2abfe752a7be381c2cbf1107d63f5a11523f88a010c07ed871bf3c1cba3d1a50a1bec98ed73f7ec9603f4c090cc0c08e5dbf4baff23ea418263eb16edf3f0e8d683f7b0b08c0d698e9be71382d3fd4f2133fc7a4ea3fda64663f622d06c064a2023ee3fd1e3f9ef7453fc5e8f43fcf235b3f9f2507c0b1bf293f44d4a73e28472c3fdc4efa3f63164b3f60950ac0e8dd713f28cbbabdf922a13e25c7f83f3e5e3c3f9c2c0fc0524c5d3fc94df3be4dff27be3de7f03fae9a343f6d2a13c0ae19e83eaa542dbf576814bfab88e73fed29403f1d0f17c0624104be93df1dbf18cb46bf014add3f1a864b3fbc1c16c0396629bf3417a4be90842dbff6e4d73fbf985b3f23ae12c0ab2971bf4212c83d7054a4be0f69d93f353e6a3feb120ec0b0145dbf151cf53e91f7213e0d42e13f50de713f7e0d0ac03165e9be626e2d3ffcc7133f9370ec3f6d8f6f3f132708c0765d013e8a1c1e3f11b9463f3eaff63f4033643f741909c07820293fb290a43ed4ab2d3f4814fc3f9b20543f0d880cc0432f713fb6c7c8bdbf25a43e2f90fa3f257b453f452311c08bd55c3f5db0f5be7dd423be30b7f23f0adb3d3fb22815c07cfbe73ed1842dbfdc3b14bf286be93f3f90493fb71219c0402903be3e3b1dbfbd5847bfb831df3f8506553fee2518c00de628bfdda0a1be96942ebfb3cdd93f401e653f6cb814c0109f70bf3ce4d23d85a4a6be374edb3fd7b1733f5f1910c047b25cbf3103f73eabc21e3e6620e33ff32f7b3faa0c0cc0c739e9bef09b2d3f9ea3133f7a47ee3fedbb783f491e0ac0235d003e61721d3f5b4a473fea80f83fa7456d3f120b0bc058a2283f7212a23eafbb2e3fefe4fd3fec2d5d3f94780ec063a4703fd492d3bdea77a63e6b64fc3f559a4e3fa11713c05d755c3f2193f7be8b8920be3c92f43f391c473f562417c02adce73e4ab42dbf7c1014bf2d58eb3fd1f5523f6b131bc04cf601be5e8a1cbf47f047bfde23e13f2c855e3ff82b1ac0325828bfe1f39ebedeb92fbfd5c0db3fd6a16e3f7ebf16c05a0470bf10b8de3dee25a9bed23ddd3f55247d3fc51c12c0cb425cbf241ef93e743d1b3e4e09e53fec40823f17090ec01c04e9be4bd42d3f7776133f1329f03f16f5803f1b130cc00292fe3d08c01c3fc9e1473f625dfa3fd25a763f8efa0cc0bd16283f1f639f3e4fdf2f3f6ac0ff3f283e663f086710c03109703f3555dfbd8dfda83e6e43fe3fa9bb573fc10915c02e095c3f22a6f9be80ec1cbef277f63f265e503f6f1d19c027b7e73e17eb2dbf9bde13bff051ed3f865a5c3f81101dc02c9800befac41bbf509848bfa122e33fa801683f172e1cc067b927bfa7099cbecaf730bf91c0dd3ff622783f98c218c0f5586fbf567ceb3d42d7abbe213adf3f974a833f6c1c14c06cc85bbf975ffb3e8e7d173e19ffe63fdce9863f2f0210c09acae8be110e2e3ffe48133fc117f23f1b8d853f09050ec05629fc3db9fb1b3f4d87483f1047fc3f13737f3f73e70ec0d77b273f50769c3e191a313f8fd40040c6516f3ff25212c0865d6f3ff40eecbd35b1ab3ec71700408edf603f1ef916c089925b3f55ddfbbe5d1a19be976af83f03a1593f5b131bc07092e73ec71f2ebff3ae13bffc57ef3f17be653f080a1fc0f9c4febd3b1b1bbf002849bf882de53fb87b713f5b2c1ec0152f27bffc9199be040432bf6dccdf3fb0d0803fcac11ac069c56ebfd231f63d6718aebead42e13f1202883f631816c00e615bbff73cfd3e7f66143e5601e93faa928b3ffdf711c01e9fe8bede372e3fbb28133f1813f43fe2258a3f1af40fc00902fa3d6f4f1b3f9e17493f8c3dfe3f1247843fc7d110c051f5263f52f7993e5624323f54cf01407c68783f583c14c0fdc96e3fd2c6f6bd10f2ad3e33140140b8056a3fbfe518c0862d5b3f91b4fdbe5df515bebe69fa3f89e4623f25061dc03076e73ed7472ebfc28a13bfc766f13f82206f3f070121c053d3fcbdcc951abf679849bf0d41e73fbbf37a3fde2720c02abd26bf0b9297be09dc32bfdde0e13fce8e853f2ebe1cc0344a6ebfb2fcfe3da8eeafbedf53e33fcfb88c3fad1118c0ac095bbf18c9fe3ef5d9113e5d0ceb3f593b903f63eb13c0f078e8be375d2e3f930b133f6317f63f37bf8e3f11e111c08e3af83d9fc51a3f8e8a493f8f1e00409ad5883f3aba12c04f85263f5af1973ed8fb323fa7ce0240aac0803fea2316c09e4e6e3fa78affbdd1c9af3e26150240522d733f6bd01ac00dd85a3fce3cffbe725613becd71fc3f3e286c3fb5f61ec0695ae73e8d6e2ebfe16713bf017ef33fdb81783f92f522c0357cfabd30f619bf0e1e4abfe15ce93fe434823fb82022c0d93026bf652095be5ce133bf94fde33fe04b8a3fdcb71ec0ddaf6dbffced043e972cb2be6a6de53fd36e913f64081ac00b995abf955e003f0cad0e3ee41fed3febe3943f78dc15c01a3fe8be4f952e3fd7df123f5924f83f1259933f02cc13c04e21f63d78261a3fa70e4a3fbc2201401b658d3fdca014c0c3fa253f9c80953e53ff333f63d20340214e853fb80918c096b36d3f5b2805bed20db23e781a034059567c3f30b91cc02c6b5a3fce9300bf4f1010be7682fe3f2b6c753f1ce520c08332e73ea8a32ebf9e3813bfb2a1f53fcdf0803f51e624c078eff7bdb24d19bf84aa4abffb84eb3f69ee863f771524c053a125bf73b792bed9e334bf8c26e63f41078f3f64ad20c00b186dbf05200a3e7255b4be6193e73f9223963f33fb1bc0892f5abf0c45013fbdbb0b3e2040ef3f118c993f15ca17c03d14e8be54bc2e3f5ec2123f503efa3f79f3973fefb315c042e5f33d797d193ff5994a3f832d0240ddf5913fc98416c01770253f1211933eb3fe343fbbdc044005dd893fdcec19c0fc1b6d3f855d0abef134b43e50260440b4c0823f0d9f1ec040045a3fa07601bffb140dbef14f00406ab07e3f2bd022c00b19e73eddc62ebfd31813bf1dcdf73f10a0853fc1d426c04c27f6bd71d918bfec0a4bbfabb4ed3fe3a68b3fb40726c0343925bfebfb90be1c9c35bf1157e83f86c1933f5fa022c0aba76cbfa9ec0d3e53e5b5befac0e93f87d79a3f94eb1dc0fadf59bfceef013fdc93093e2b68f13f04349e3f83b519c078f1e7bec7db2e3fa4aa123f4960fc3f4e8e9c3ff59917c0de44f23d4105193f9cfc4a3f5e3c03407b87963f026718c0c909253f5f4f913e89b6353f2beb0540d86c8e3f57ce1bc064ab6c3fd7280ebe29c6b53e36360540d756873f228320c04fb6593fc31f02bf5edc0abe9e6201405afa833f33b924c0f4ffe63e5be72ebf0dfc12bf7bfff93fb34e8a3f14c128c03a43f4bde45f18bf516f4bbf2bebef3f585e903fa5f727c00cc924bf1b238fbe5d5f36bf5d8eea3fb67a983f049124c03d2d6cbf5504123e9690b7be70f5eb3fb38a9f3fbcd91fc0af8759bf8caa023f183c073e3c97f33fb8dba23ff29e1bc0fec6e7bee6022f3fc48c123f7989fe3f7929a13f3c7e19c0a98ef03de089183f84614b3fe54e0440d4199b3fab471ac03a9b243f58748f3ec778363f4bfd064076fd923f4cae1dc08e306c3f0e3912be0775b73ec249064079ed8b3f946522c09c60593f80d702bfad7008bedc780240749c883f5ea026c023e2e63eda0d2fbfead912bf8a3afc3f9efc8e3fb6aa2ac04a19f2bd51d617bf69e04bbf2e2af23f8f14953face429c0294b24bf25198dbe253637bf26ceec3f8c329d3fb37e26c050a56bbf077d163ee661b9be8432ee3fda3ca43f18c521c0b82659bf9e74033f6fb6043e29cff53f1083a73fe3851dc0d999e7bec92a2f3fed6e123fe25d004002c5a53f5a601bc099a3ee3dbdff173fdbd14b3f1066054010ad9f3f64261cc03f20243f68678d3e814d373f14140840148f973f5d8c1fc063a86b3f12ad16be8248b93ee5610740c684903ff84524c06c02593f0f9e03bfe9d905be93930340903e8d3f2d8528c0a2c4e63e2d332fbffbb812bff37dfe3fb2a9933fb3912cc09c37f0bdf96117bfbd3f4cbf5a71f43f6ac9993fd3ce2bc04be023bfd4688bbe40e837bf0f16ef3fe8e8a13f796928c0c8336bbf79261a3e7ce0babeda77f03fe1eda83fb5ad23c08ed758bf9717043f22ac023e990ff83ff329ac3f616a1fc07379e7beb3462f3f535a123f6d7b0140d260aa3f57401dc013f8ec3d0589173ff6314c3fb98106401a41a43f37031ec08ab8233f04b18b3ef2fd373f5f2f09409c219c3f916821c0df366b3f23581abea8c6ba3e797e0840a31c953f552426c0f5b4583ffa3e04bff9c303be99b2044091e0913fa9672ac0f2afe63e4c4e2fbfb6a012bf856300402e56983feb762ec02cbbeebd690617bf708a4cbf11bef63f517d9e3f0db72dc0da8823bfbb078abe727838bf7a63f13f439ea63f49522ac0e4d46abf81311d3e951abcbec9c2f23f2a9ead3f749425c0c69358bf4da1043f1ff8003ece55fa3f94d0b03f354d21c0495ae7be91612f3f7646123ff39b0240e6fcae3fe31e1fc0079feb3daa2a173f157e4c3f6fa00740c3d5a83fc1de1fc09062233f0f4c8a3e928d383fbb4d0a40d1b4a03f854323c0d8d76a3f225f1dbe4002bc3e139e0940eab4993f5a0128c0e072583fd8c604bf6c0002be91d405408082963f99482cc0ed99e63ea2692fbfad8812bfff8a014003029d3f445a30c01ce9ecbd149716bfe9e44cbf7f10f93f2f30a33f3d9d2fc05a1c23bfa15388be3f2939bf94b6f33f8352ab3f05392cc0dc5d6abff7fa203ed99dbdbe8013f53f9c4db23f3d7927c0103d58bf384f053ff0acfd3dfda1fc3fdf76b53f482e23c01d2ee7becd872f3f0c2a123f91bf03402f99b33feafb20c00dfee93da3ba163f2dd84c3f51c20840026bad3ff1b821c067f7223f7697883e483d393f466f0b40af48a53f291d25c025606a3fc21f21beb98abd3ed0c00a40964d9e3ff1dc29c01f1f583f507105bf5f91ffbd91f9064053249b3fe6272ec0fa7be63e378e2fbfa26812bfa1b60240e4aca13fe13a32c0580debbd822816bf8b3e4dbf016bfb3f85e1a73f748031c0bfb422bf94c086bee1cd39bfb811f63f1905b03fbf1c2ec0f0f069bf1b5e243e9bfcbebe6e6cf73fc2fbb63f315b29c0e2f157bfb0e4053fc6faf93db4f6fe3f981cba3fda0c25c0d310e7be579f2f3f5819123f9fe70440b635b83fc9d622c07e5fe83d274a163f1f324d3fc0e809401401b23f369123c0c493223fa0fd863ea9df393f64950c4082dda93febf426c083f3693f518624be60e7be3e09e80b40d8e6a23f7ab62bc0aad5573f820406bf94cdfbbde622084002c69f3fd00430c02a6be63e3aa42fbfd55412bf87e403402957a63f021a34c079cee9bddfde15bf057a4dbfe5c9fd3fec91ac3f0a6233c0ca6b22bfc1a485bed3403abf3771f83fb1b6b43fd0fe2fc0fba169bffdd0263eadf6bfbecec9f93f29a9bb3f923b2bc08ab957bf6c53063f3546f73d05a8004003c2be3f07ea26c0e7f6e6beccb42f3fcc09123f1512064067d2bc3f76b024c0d840e73d09fe153fd86e4d3faa110b40a497b63f6e6825c02d4c223f34de853e0e523a3f00be0d40df72ae3fa8cb28c05da4693fe1f526be14e3bf3eb5110d406780a73fe68e2dc0c69e573fc07106bff9fbf8bd974e09408d67a43f71e031c06159e63e37ba2fbf6f4112bfbf140540ce00ab3f9bf735c090f6e7bda67115bfdcd14dbfa31600405a41b13ff44135c087fb21bf7cec83beb9f03abf2bd5fa3f4267b93f2cdf31c0f12469bfdfb02a3e4c7bc1bebc2bfc3fc655c03f581a2dc04f5c57bf3708073febeff23d10d701401967c33fc8c528c02ec2e6bebbe02f3fbbe9113f073f0740426fc13feb8826c0e39ce53dec90153faec54d3f233d0c40b52ebb3f923e27c03adc213f9d28843e38013b3f30e90e40ce08b33f5aa12ac01626693f21c52abe4871c13ee83d0e404a1aac3f2e662fc0a245573fc42107bfd067f4bdb57c0a40f708a93fbeba33c06e36e63edbe32fbf1e1d12bfbf4906402ba9af3feed137c04f4be6bd891315bf8d1d4ebff14c0140b8eeb53f471e37c00fa621bff4b482be40713bbf4b42fd3f9215be3fe6bb33c0b9d068bfc42e2d3e7982c2be1a97fe3f9f00c53fb8f52ec0332757bf306f073f1c5bf03d100b0340640bc83f939e2ac0f0b7e6beede52f3f85e7113f2f710840690cc63fe25e28c05b3be43dbf2f153f52124e3ffd6d0d40dcc6bf3f891229c0f68d213f56e3823eee7d3b3fc819104002a0b73fea742cc043d3683f9b562dbe766dc23e616f0f40f5b4b03f183b31c04c10573fb78807bfe9e0f1bddeaf0b4030aaad3f3d9235c07b35e63ed1e72fbfb81812bfc37e07407051b43f47ac39c03b09e6bd9a0415bf81294ebf41830240009cba3f9efa38c02a9321bf7d6982beac8e3bbf6cafff3fd6c3c23fa09835c0d6b968bf10e52d3e5bc7c2be3981004076abc93f14d130c0111557bfa291073ffc8eef3d0b3f0440b4afcc3f55772cc0bface6be30ef2f3fc7e0113f51a3094098a9ca3fcd342ac00deee33d2a1d153f1a214e3fd39e0e40085fc43f76e62ac01e79213fee96823e309d3b3f5e4a11403337bc3f74482ec0adbb683fa9052ebe42b7c23edba01040924fb53f001033c0dffe563fd3aa07bfedf7f0bd08e30c40544bb23fbf6937c0c92ae63e27f42fbf150e12bf0ab5084040f9b83fc1853bc0b14de4bd15a014bfb7794ebfc5ba03409748bf3ffcd53ac06f2521bf7ebe80be87363cbf760f01405f71c73f5a7437c02b3c68bf5fbd313ecb3fc4be1fb80140c255ce3f7fab32c0eeb356bf3249083fa943eb3d52740540d053d13f464f2ec0806ee6bec121303f55bc113fd4d60a40ee46cf3f0b0a2cc0c961e23d1eb9143f25704e3f19d10f4097f7c83fd0b92cc06309213fe4f2803e90453c3f687c1240cfcec03f721b30c0ee3b683f58c031be3540c43ebfd311406ceab93f4de434c06aa3563ff85b08bf6a4fecbd8c170e405eecb63f864039c0e001e63e932430bfcde311bfe8f00940409fbd3f3e5b3dc0fa3be2bd473014bf1bd34ebf9df7044041f2c33feeac3cc0c5bc20bff29b7ebe74ce3cbf814c0240bb1bcc3f974b39c0cbd567bfe8ae343ef176c5be80f4024091fdd23fc28134c0897356bf53c3083ffc35e83d70af0640ccf6d53fa82330c02061e6bea628303f4ab9113f8e100c40aae4d33f5cdc2dc095bde03d7c46143fb4c94e3fd9091140a891cd3fac8a2ec052ab203f7fe27e3e5cd73c3ff5b413402f68c53f03ec31c0a0d7673f47cc34be9567c53ef60c13405986be3fd8b536c00063563f86d508bfda57e9bd06520f401e8dbb3ff3133bc0d802e63e812830bfadde11bfc82c0b403f45c23fc1303fc04841e2bd5e3114bf3bd24ebf78340640ee9bc83fe5833ec017bd20bfe79d7ebe05ce3cbf8d8903401dc6d03fd8223bc0dad567bf71af343e8b76c5bee23004406ba5d73f055836c0977356bf19c3083f0b3be83d8cea0740d999da3f08f831c0f361e6be6827303f78ba113f484a0d407982d83fabae2fc0e3b8e03d2845143fbcca4e3f98421240ca2bd23f875b30c012ab203feddf7e3ec9d73c3f83ed14409b01ca3f94bc33c0a8d7673f1ecd34be4c67c53e2e4614404d22c33f678738c0aa62563f07d608bfb058e9bd838c1040df2dc03f65e73cc03101e63e9d2930bffbdd11bfaa680c402cebc63f450641c0d344e2bd9d3214bf47d14ebf537107408b45cd3fdd5a40c040bd20bf7b9f7ebec1cd3cbf99c604407370d53f17fa3cc0c1d567bfccaf343eee76c5be426d05403f4ddc3f442e38c0e37356bfa6c2083fa53ae83da6250940e43cdf3f61cc33c08d63e6be6326303f0fbb113ffe830e404820dd3ff38031c033b3e03d0b44143fa0cb4e3f557b1340e9c5d63f5c2c32c0b6aa203fc2dd7e3e44d83c3f0f261640019bce3f218d35c08fd7673f58cd34beac67c53e667f154035bec73ff4583ac08262563f6fd608bfc552e9bd02c7114090cec43fd7ba3ec08700e63eb92a30bfe7dc11bf8ea40d401291cb3fcddb42c035f0e0bdbae613bf5f0d4fbf31ae084021efd13fd73142c0706520bfd5df7bbe3b533dbfa5030640c81ada3f57d13ec01b6c67bf09f5373e7ca4c6bea1a9064016f5e03f82043ac06b1c56bfeb62093fc9bae43dbe600a40f6dfe33fb7a035c07121e6beb357303f8399113fb2bd0f4020bee13f375333c0027adf3d8cf7133fb0074f3f0fb414401160db3f2dfd33c0ae4a203f383c7c3e34623d3f9b5e17406a34d33fad5d37c03e69673f47da37be08b8c63e9fb816401c5acc3f822a3cc06114563f7a6c09bf042de5bd810113403c6fc93f4d8e40c00edbe53e0d5b30bf41b111bfdfe50e403234d03ff5ac44c0e45adebd406513bfc2744fbf02f009401e94d63fbb0344c0f9e01fbf265b78be480d3ebf8f45074022c0de3f60a340c049e866bfd8ad3b3ec429c8be33eb07402999e53f0ad63bc051c555bf0d040a3fd2cfe03da9a10b409a81e83f4b7137c0d100e6be436a303fea8f113fe5fd10401a5de63fbf2235c0a69cdd3dae6f133f88704f3fc2f315402dfddf3ff9cb35c0dad51f3fb183783e54133e3f359e18402ad1d73f542c39c0efe7663fdfb03bbeb22ac83e91f8174023f8d03faaf93dc0f2be553f360b0abfff39e1bd1b421440b20fce3f695e42c01dd7e53e7f6a30bf1aa011bf74281040abd6d43f157d46c012d1ddbd004b13bfb5894fbffc320b400838db3f6cd445c065c61fbf12ae77bebe313ebf9d8808405264e33f2e7442c0f2ce66bf40603c3eb974c8befe2d0940593cea3f77a63dc086b555bf34210a3f7016e03df2e30c40e622ed3ffc4039c0dafce5bebf6b303fb08f113fa03f124059fcea3fa3f136c0dd3bdd3dd753133fec854f3f18351740fc9ae43f4c9a37c07bbf1f3f0bc9773e5a353e3f77df1940b26edc3f8afa3ac082cf663fd06c3cbe286fc83e163a1940ab96d53f41c83fc013af553f4c280abf3888e0bd228415401db0d23fbc2d44c026d8e53e856b30bf739e11bf0e6b11402a79d93f384d48c06dd2ddbd144b13bfa3894fbff9750c40f8dbdf3f20a547c06bc61fbf84ad77bec3313ebfaecb09408808e83ffe4444c0e2ce66bfc6613c3ea774c8becb700a4090dfee3fe5763fc068b555bf54210a3fc218e03d3d260e403cc4f13faf103bc0a8fce5be8a6b303f0390113f5c811340a09bef3f88c038c0a83bdd3d8753133f26864f3f71761840d238e93fa06839c057bf1f3feec7773e91353e3fbc201b40420ce13fc1c83cc083cf663f126e3cbed96ec83e9f7b1a403a35da3fdb9641c0d9ae553fa0280abf3d89e0bd2dc616408e50d73f11fd45c068d7e53ebc6b30bf7a9e11bfa5ad12409f1bde3f581d4ac05cd2ddbd5f4b13bf6c894fbff5b80d40de7fe43fd17549c065c61fbfa5ae77beb3313ebfbe0e0b40b5acec3fcc1546c0f0ce66bf0f613c3e8d74c8be96b30b40c082f33f504741c075b555bf3e210a3f1a19e03d86680f408a65f63f5ee03cc0c4fce5be746b303f1490113f17c31440e13af43f6a8f3ac01b3add3d7c53133f33864f3fc8b71940a2d6ed3ff1363bc049bf1f3f76c8773e90353e3ffe611c40cba9e53ff6963ec08acf663fc46d3cbed46ec83e26bd1b40c0d3de3f726543c0cdae553fb4280abfba88e0bd36081840f6f0db3f64cc47c046d7e53ef66b30bf429e11bf3df0134014bee23f7aed4bc08bd3ddbdad4b13bf30894fbff0fb0e40c423e93f84464bc075c61fbf6eaf77be95313ebfcd510c40e350f13f9be647c0f9ce66bfe9603c3e6c74c8be61f60c40f125f83fbc1743c081b555bf23210a3f541ae03dceaa1040da06fb3f0eb03ec0fbfce5be3e6b303f4090113fd104164024daf83f4c5e3cc0ae38dd3d5953133f54864f3f1ef91a407474f23f42053dc041bf1f3f46c8773e9a353e3f41a31d405547ea3f2b6540c08ecf663fbc6d3cbec56ec83eadfe1c404772e33f0a3445c0bbae553fcf280abf7488e0bd404a19405e91e03fb99b49c0f3d6e53e346c30bf189e11bfd53215408860e73f9dbd4dc0fdd2ddbdc34b13bf24894fbfed3e1040a9c7ed3f37174dc06dc61fbfbcaf77be92313ebfdc940d4011f5f53f6ab749c0ffce66bf49603c3e7c74c8be2b390e4023c9fc3f28e844c09bb555bff9200a3f5c1ae03d16ed11402ca8ff3fbd7f40c064fde5beff6a303f6090113f8b4617406879fd3f2d2d3ec05537dd3d2d53133f77864f3f743a1c404712f73f93d33ec036bf1f3f5fc8773ea3353e3f83e41e40dfe4ee3f603342c086cf663f6d6d3cbef96ec83e35401e40cd10e83fa20247c0ccae553fbc280abfec87e0bd498c1a40c431e53f0e6b4bc014d7e53e2f6c30bf109e11bf6e7516400103ec3fc28d4fc05fd4ddbded4b13bffe884fbfea811140946bf23fede74ec08ec61fbf62af77be80313ebfedd70e404599fa3f3b884bc0f0ce66bf5f613c3e7b74c8bef77b0f402eb6004095b846c091b555bf0b210a3f441ae03d5f2f1340c22402406d4f42c071fde5bedc6a303f8890113f468818405a0c014010fc3fc0fa36dd3de952133faa864f3fca7b1d4022b0fb3fe5a140c036bf1f3f5ec7773eb9353e3fc72520407182f33f960144c08ccf663fac6d3cbece6ec83ebd811f405aafec3f3dd148c0c4ae553fc0280abf0389e0bd54ce1b4031d2e93f653a4dc0d2d6e53e5d6c30bff59d11bf07b8174071a5f03fe55d51c013d4ddbd2c4c13bfd2884fbfe7c41240760ff73fa0b850c092c61fbfd0af77be73313ebffd1a1040713dff3f0a594dc0ebce66bff8603c3eae74c8bec2be1040c7070340008948c0b1b555bfe5200a3fb018e03da77114406c7504401a1f44c0eefde5bec26a303f7490113fffc91940fd5b0340efca41c01e35dd3dc852133fc8864f3f1fbd1e40fb260040347042c01fbf1f3f2cc7773ed0353e3f09672140fb1ff83fcacf45c083cf663f6e6d3cbe036fc83e44c32040de4df13fd49f4ac0c5ae553fd6280abf5d85e0bd5f101d409472ee3fba094fc0fad6e53e9e6c30bf959d11bfa0fa1840e147f53f0a2e53c0afda52be0cf607bfa66852bfe407144058b3fb3f558952c0dd7031bfe90e2fbe754333bf0d5e1140cff00140da294fc02d686abfdc48823e6e529fbe8c011240605905406b594ac068db49bf6b2c153f6479493eefb3154016c60640c8ee45c0a06ab8beb33b303f4e2b213fb80b1b40a2ab0540ce9943c01fae523e66f9073f4469523f74fe1f40e6750240833e44c0156f313f9f112f3e0d45333f4ba8224087bdfc3ffe9d47c099686a3fa85382be1b479f3ecc04224063ecf53f6d6e4cc0f9d3493fb03215bf2ea649be69521e40f812f33f10d950c0a74db83e643c30bfd63221bf + m_CompressedMesh: + m_Vertices: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_UV: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Normals: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Tangents: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Weights: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_NormalSigns: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_TangentSigns: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_FloatColors: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_BoneIndices: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_Triangles: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_UVInfo: 0 + m_LocalAABB: + m_Center: {x: 1.9145843, y: 0.566661, z: 1.37708} + m_Extent: {x: 4.354134, y: 1.5391792, z: 4.676765} + m_MeshUsageFlags: 0 + m_BakedConvexCollisionMesh: + m_BakedTriangleCollisionMesh: + m_MeshMetrics[0]: 1 + m_MeshMetrics[1]: 1 + m_MeshOptimizationFlags: 1 + m_StreamData: + offset: 0 + size: 0 + path: +--- !u!1 &1073702040 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1073702042} + - component: {fileID: 1073702041} + - component: {fileID: 1073702043} + m_Layer: 0 + m_Name: Cylinder Creator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1073702041 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1073702040} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8e5ac92bc18f545cc84cd886ece82b4d, type: 3} + m_Name: + m_EditorClassIdentifier: + editorData: + _bezierPath: + points: + - {x: -4.244944, y: 0.414352, z: 2.5396578} + - {x: -1.9272205, y: -0.009238139, z: 3.3419437} + - {x: 0.39050317, y: -0.43282828, z: 4.1442294} + - {x: 2.3595712, y: -0.7624575, z: 2.901955} + - {x: 3.6651654, y: -0.9810188, z: 2.0782626} + - {x: 4.668234, y: -0.80789363, z: 0.24100709} + - {x: 4.092701, y: -1.0053874, z: -1.1944623} + - {x: 3.5280938, y: -1.199132, z: -2.6026812} + - {x: 0.91258705, y: -2.048873, z: -1.6903156} + - {x: 0.10191786, y: -1.5530716, z: -2.8888125} + - {x: -0.5787585, y: -1.1367731, z: -3.8951278} + - {x: -0.016157448, y: -0.22624412, z: -4.8290043} + - {x: 0.5464436, y: 0.68428487, z: -5.7628803} + isClosed: 0 + space: 0 + controlMode: 3 + autoControlLength: 0.35 + boundsUpToDate: 0 + bounds: + m_Center: {x: 1.2616136, y: 0.54719853, z: -2.224142} + m_Extent: {x: 3.2442577, y: 0.8264315, z: 3.3909774} + perAnchorNormalsAngle: + - 335.08722 + - 345.02103 + - 327.71606 + - 320.15445 + - 320.15445 + globalNormalsAngle: 0 + flipNormals: 0 + vertexPathUpToDate: 1 + vertexPathMaxAngleError: 0.3 + vertexPathMinVertexSpacing: 0 + showTransformTool: 1 + showPathBounds: 0 + showPerSegmentBounds: 0 + displayAnchorPoints: 1 + displayControlPoints: 1 + bezierHandleScale: 1 + globalDisplaySettingsFoldout: 1 + keepConstantHandleSize: 1 + showNormalsInVertexMode: 0 + showBezierPathInVertexMode: 0 + showDisplayOptions: 0 + showPathOptions: 1 + showVertexPathDisplayOptions: 0 + showVertexPathOptions: 1 + showNormals: 1 + showNormalsHelpInfo: 0 + tabIndex: 0 + initialized: 1 +--- !u!4 &1073702042 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1073702040} + m_LocalRotation: {x: 0.067577064, y: -0, z: -0, w: 0.99771404} + m_LocalPosition: {x: 1.86, y: 0.54729664, z: 2.44} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 7.7500005, y: 0, z: 0} +--- !u!114 &1073702043 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1073702040} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 977f1d9610a354cb99a2bf6640f03e8b, type: 3} + m_Name: + m_EditorClassIdentifier: + pathCreator: {fileID: 1073702041} + autoUpdate: 1 + thickness: 0.15 + resolutionU: 10 + resolutionV: 20 + material: {fileID: 2100000, guid: 5c270a73328a99e439fec44a0cf3a17d, type: 2} + meshHolder: {fileID: 1362720713} +--- !u!1 &1362720713 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1362720716} + - component: {fileID: 1362720715} + - component: {fileID: 1362720714} + m_Layer: 0 + m_Name: Mesh Holder + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!23 &1362720714 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1362720713} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 5c270a73328a99e439fec44a0cf3a17d, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1362720715 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1362720713} + m_Mesh: {fileID: 392264963} +--- !u!4 &1362720716 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1362720713} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1777349640 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1777349643} + - component: {fileID: 1777349642} + - component: {fileID: 1777349641} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1777349641 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1777349640} + m_Enabled: 1 +--- !u!20 &1777349642 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1777349640} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1777349643 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1777349640} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 12.48, y: -0.52, z: -31.62} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2092520649 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2092520651} + - component: {fileID: 2092520650} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &2092520650 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2092520649} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.802082 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &2092520651 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2092520649} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 12.48, y: 1.48, z: -21.62} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity.meta b/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity.meta new file mode 100755 index 0000000..db9155f --- /dev/null +++ b/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 64a23c84aa67047f38f5c81b747e5354 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Road.unity b/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Road.unity similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Road.unity rename to Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Road.unity diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Road.unity.meta b/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Road.unity.meta similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Road.unity.meta rename to Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Road.unity.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs b/Path Creator Project/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs new file mode 100644 index 0000000..1d4e484 --- /dev/null +++ b/Path Creator Project/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs @@ -0,0 +1,115 @@ +using System.Collections.Generic; +using PathCreation.Utility; +using UnityEngine; + +namespace PathCreation.Examples { + public class CylinderMeshCreator : PathSceneTool { + + public float thickness = .15f; + [Range (3, 30)] + public int resolutionU = 10; + [Min (0)] + public float resolutionV = 20; + + public Material material; + + [SerializeField, HideInInspector] + GameObject meshHolder; + + MeshFilter meshFilter; + MeshRenderer meshRenderer; + Mesh mesh; + + protected override void PathUpdated () { + if (pathCreator != null) { + AssignMeshComponents (); + AssignMaterials (); + CreateMesh (); + } + } + + void CreateMesh () { + List verts = new List (); + List triangles = new List (); + + int numCircles = Mathf.Max (2, Mathf.RoundToInt (path.length * resolutionV) + 1); + var pathInstruction = PathCreation.EndOfPathInstruction.Stop; + + for (int s = 0; s < numCircles; s++) { + float segmentPercent = s / (numCircles - 1f); + Vector3 centerPos = path.GetPointAtTime (segmentPercent, pathInstruction); + Vector3 norm = path.GetNormal (segmentPercent, pathInstruction); + Vector3 forward = path.GetDirection (segmentPercent, pathInstruction); + Vector3 tangentOrWhatEver = Vector3.Cross (norm, forward); + + for (int currentRes = 0; currentRes < resolutionU; currentRes++) { + var angle = ((float) currentRes / resolutionU) * (Mathf.PI * 2.0f); + + var xVal = Mathf.Sin (angle) * thickness; + var yVal = Mathf.Cos (angle) * thickness; + + var point = (norm * xVal) + (tangentOrWhatEver * yVal) + centerPos; + verts.Add (point); + + //! Adding the triangles + if (s < numCircles - 1) { + int startIndex = resolutionU * s; + triangles.Add (startIndex + currentRes); + triangles.Add (startIndex + (currentRes + 1) % resolutionU); + triangles.Add (startIndex + currentRes + resolutionU); + + triangles.Add (startIndex + (currentRes + 1) % resolutionU); + triangles.Add (startIndex + (currentRes + 1) % resolutionU + resolutionU); + triangles.Add (startIndex + currentRes + resolutionU); + } + + } + } + + if (mesh == null) { + mesh = new Mesh (); + } else { + mesh.Clear (); + } + + mesh.SetVertices (verts); + mesh.SetTriangles (triangles, 0); + mesh.RecalculateNormals (); + + } + + // Add MeshRenderer and MeshFilter components to this gameobject if not already attached + void AssignMeshComponents () { + + if (meshHolder == null) { + meshHolder = new GameObject ("Mesh Holder"); + } + + meshHolder.transform.rotation = Quaternion.identity; + meshHolder.transform.position = Vector3.zero; + meshHolder.transform.localScale = Vector3.one; + + // Ensure mesh renderer and filter components are assigned + if (!meshHolder.gameObject.GetComponent ()) { + meshHolder.gameObject.AddComponent (); + } + if (!meshHolder.GetComponent ()) { + meshHolder.gameObject.AddComponent (); + } + + meshRenderer = meshHolder.GetComponent (); + meshFilter = meshHolder.GetComponent (); + if (mesh == null) { + mesh = new Mesh (); + } + meshFilter.sharedMesh = mesh; + } + + void AssignMaterials () { + if (material != null) { + meshRenderer.sharedMaterial = material; + } + } + + } +} \ No newline at end of file diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs.meta b/Path Creator Project/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs.meta new file mode 100644 index 0000000..a62a487 --- /dev/null +++ b/Path Creator Project/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 977f1d9610a354cb99a2bf6640f03e8b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 90b1733806fbe54887a550d8b9e642a0ae09b27f Mon Sep 17 00:00:00 2001 From: SirGiygas <50772474+SirGiygas@users.noreply.github.com> Date: Sat, 14 Nov 2020 13:08:56 +0100 Subject: [PATCH 08/12] Fix NaN normals (#64) Sometimes the num variable in the method for rotating the normals by user angle was equal to 1 -> 1 - 1f is 0 so you'd get NaN due to dividing by 0. --- .../Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs b/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs index 0f2ac7b..fb63fb8 100755 --- a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs +++ b/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs @@ -140,7 +140,7 @@ public VertexPath (BezierPath bezierPath, Transform transform, float vertexSpaci } for (int i = 0; i < num; i++) { int vertIndex = startVertIndex + i; - float t = i / (num - 1f); + float t = num == 1 ? 1f : i / (num - 1f); float angle = startAngle + deltaAngle * t; Quaternion rot = Quaternion.AngleAxis (angle, localTangents[vertIndex]); localNormals[vertIndex] = (rot * localNormals[vertIndex]) * ((bezierPath.FlipNormals) ? -1 : 1); From 555d9c0f74f4dea40647e6b374da0e21aadb919a Mon Sep 17 00:00:00 2001 From: Sebastian Lague Date: Tue, 17 May 2022 09:13:12 +0200 Subject: [PATCH 09/12] Update folder structure --- .gitignore | 49 ++++- .../Assets => Assets}/PathCreator.meta | 0 .../Assets => Assets}/PathCreator/Core.meta | 0 .../PathCreator/Core/Editor.meta | 0 .../PathCreator/Core/Editor/Helper.meta | 0 .../Core/Editor/Helper/MouseUtility.cs | 0 .../Core/Editor/Helper/MouseUtility.cs.meta | 0 .../Core/Editor/Helper/PathHandle.cs | 0 .../Core/Editor/Helper/PathHandle.cs.meta | 0 .../Core/Editor/Helper/ScreenSpacePolyLine.cs | 0 .../Editor/Helper/ScreenSpacePolyLine.cs.meta | 0 .../Core/Editor/PathCreatorEditor.asmdef | 0 .../Core/Editor/PathCreatorEditor.asmdef.meta | 0 .../PathCreator/Core/Editor/PathEditor.cs | 0 .../Core/Editor/PathEditor.cs.meta | 0 .../PathCreator/Core/Runtime.meta | 0 .../PathCreator/Core/Runtime/Objects.meta | 0 .../Core/Runtime/Objects/BezierPath.cs | 0 .../Core/Runtime/Objects/BezierPath.cs.meta | 0 .../Runtime/Objects/EndOfPathInstruction.cs | 0 .../Objects/EndOfPathInstruction.cs.meta | 0 .../Runtime/Objects/GlobalDisplaySettings.cs | 0 .../Objects/GlobalDisplaySettings.cs.meta | 0 .../Core/Runtime/Objects/MinMax3D.cs | 0 .../Core/Runtime/Objects/MinMax3D.cs.meta | 0 .../Core/Runtime/Objects/PathCreator.cs | 0 .../Core/Runtime/Objects/PathCreator.cs.meta | 0 .../Core/Runtime/Objects/PathCreatorData.cs | 0 .../Runtime/Objects/PathCreatorData.cs.meta | 0 .../Core/Runtime/Objects/PathSpace.cs | 0 .../Core/Runtime/Objects/PathSpace.cs.meta | 0 .../Core/Runtime/Objects/VertexPath.cs | 0 .../Core/Runtime/Objects/VertexPath.cs.meta | 0 .../Core/Runtime/PathCreator.asmdef | 0 .../Core/Runtime/PathCreator.asmdef.meta | 0 .../PathCreator/Core/Runtime/Utility.meta | 0 .../Runtime/Utility/CubicBezierUtility.cs | 0 .../Utility/CubicBezierUtility.cs.meta | 0 .../Core/Runtime/Utility/MathUtility.cs | 0 .../Core/Runtime/Utility/MathUtility.cs.meta | 0 .../Core/Runtime/Utility/VertexPathUtility.cs | 0 .../Runtime/Utility/VertexPathUtility.cs.meta | 0 .../PathCreator/Core/Settings.meta | 0 .../Core/Settings/GlobalDisplaySettings.asset | 0 .../Settings/GlobalDisplaySettings.asset.meta | 0 .../PathCreator/Documentation.meta | 0 .../Documentation/Documentation.pdf | Bin .../Documentation/Documentation.pdf.meta | 0 .../PathCreator/Examples.meta | 0 .../PathCreator/Examples/Materials.meta | 0 .../PathCreator/Examples/Materials/Black.mat | 0 .../Examples/Materials/Black.mat.meta | 0 .../PathCreator/Examples/Materials/Blue.mat | 0 .../Examples/Materials/Blue.mat.meta | 0 .../PathCreator/Examples/Materials/Brown.mat | 0 .../Examples/Materials/Brown.mat.meta | 0 .../Examples/Materials/Dark Grey.mat | 0 .../Examples/Materials/Dark Grey.mat.meta | 0 .../PathCreator/Examples/Materials/Green.mat | 0 .../Examples/Materials/Green.mat.meta | 0 .../PathCreator/Examples/Materials/Red.mat | 0 .../Examples/Materials/Red.mat.meta | 0 .../Examples/Materials/Road Texture.png | Bin .../Examples/Materials/Road Texture.png.meta | 0 .../Examples/Materials/Road Underside.mat | 0 .../Materials/Road Underside.mat.meta | 0 .../PathCreator/Examples/Materials/Road.mat | 0 .../Examples/Materials/Road.mat.meta | 0 .../PathCreator/Examples/Prefabs.meta | 0 .../PathCreator/Examples/Prefabs/Cube.prefab | 0 .../Examples/Prefabs/Cube.prefab.meta | 0 .../Examples/Prefabs/Follower.prefab | 0 .../Examples/Prefabs/Follower.prefab.meta | 0 .../PathCreator/Examples/Prefabs/Path.prefab | 0 .../Examples/Prefabs/Path.prefab.meta | 0 .../Examples/Prefabs/Sphere.prefab | 0 .../Examples/Prefabs/Sphere.prefab.meta | 0 .../PathCreator/Examples/Scenes.meta | 0 .../Examples/Scenes/Follow Path.unity | 0 .../Examples/Scenes/Follow Path.unity.meta | 0 .../Examples/Scenes/Generate Path.unity | 0 .../Examples/Scenes/Generate Path.unity.meta | 0 .../Examples/Scenes/Mesh Examples.meta | 0 .../Scenes/Mesh Examples/Cylinder.unity | 0 .../Scenes/Mesh Examples/Cylinder.unity.meta | 0 .../Examples/Scenes/Mesh Examples/Road.unity | 0 .../Scenes/Mesh Examples/Road.unity.meta | 0 .../Examples/Scenes/Object Placement.unity | 0 .../Scenes/Object Placement.unity.meta | 0 .../Examples/Scenes/Path as Prefab.unity | 0 .../Examples/Scenes/Path as Prefab.unity.meta | 0 .../PathCreator/Examples/Scripts.meta | 0 .../Examples/Scripts/CylinderMeshCreator.cs | 0 .../Scripts/CylinderMeshCreator.cs.meta | 0 .../PathCreator/Examples/Scripts/Editor.meta | 0 .../Scripts/Editor/PathSceneToolEditor.cs | 0 .../Editor/PathSceneToolEditor.cs.meta | 0 .../Examples/Scripts/GeneratePathExample.cs | 0 .../Scripts/GeneratePathExample.cs.meta | 0 .../Examples/Scripts/PathFollower.cs | 0 .../Examples/Scripts/PathFollower.cs.meta | 0 .../Examples/Scripts/PathPlacer.cs | 0 .../Examples/Scripts/PathPlacer.cs.meta | 0 .../Examples/Scripts/PathSceneTool.cs | 0 .../Examples/Scripts/PathSceneTool.cs.meta | 0 .../Examples/Scripts/PathSpawner.cs | 0 .../Examples/Scripts/PathSpawner.cs.meta | 0 .../Examples/Scripts/RoadMeshCreator.cs | 0 .../Examples/Scripts/RoadMeshCreator.cs.meta | 0 .../Packages => Packages}/manifest.json | 20 +-- .../Packages => Packages}/packages-lock.json | 104 +++++------ Path Creator Project/.vscode/settings.json | 56 ------ Path Creator Project/Logs/Packages-Update.log | 142 --------------- .../ProjectSettings/ProjectVersion.txt | 2 - .../AudioManager.asset | 0 .../ClusterInputManager.asset | 0 .../DynamicsManager.asset | 0 .../EditorBuildSettings.asset | 0 .../EditorSettings.asset | 0 .../GraphicsSettings.asset | 0 .../InputManager.asset | 0 .../NavMeshAreas.asset | 0 .../NetworkManager.asset | 0 ProjectSettings/PackageManagerSettings.asset | 43 +++++ .../Physics2DSettings.asset | 0 .../PresetManager.asset | 0 .../ProjectSettings.asset | 0 ProjectSettings/ProjectVersion.txt | 2 + .../QualitySettings.asset | 0 ProjectSettings/SceneTemplateSettings.json | 167 ++++++++++++++++++ .../TagManager.asset | 0 .../TimeManager.asset | 0 .../UnityConnectSettings.asset | 0 .../VFXManager.asset | 0 ProjectSettings/VersionControlSettings.asset | 8 + .../XRSettings.asset | 0 136 files changed, 309 insertions(+), 284 deletions(-) rename {Path Creator Project/Assets => Assets}/PathCreator.meta (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Core.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Editor.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Editor/Helper.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Editor/Helper/MouseUtility.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Editor/Helper/MouseUtility.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Editor/Helper/PathHandle.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Editor/Helper/PathHandle.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Editor/PathCreatorEditor.asmdef (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Editor/PathCreatorEditor.asmdef.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Editor/PathEditor.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Editor/PathEditor.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/BezierPath.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/BezierPath.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/EndOfPathInstruction.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/EndOfPathInstruction.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/GlobalDisplaySettings.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/GlobalDisplaySettings.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/MinMax3D.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/MinMax3D.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/PathCreator.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/PathCreator.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/PathCreatorData.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/PathCreatorData.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/PathSpace.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/PathSpace.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/VertexPath.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Objects/VertexPath.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/PathCreator.asmdef (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/PathCreator.asmdef.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Utility.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Utility/CubicBezierUtility.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Utility/CubicBezierUtility.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Utility/MathUtility.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Utility/MathUtility.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Utility/VertexPathUtility.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Runtime/Utility/VertexPathUtility.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Settings.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Settings/GlobalDisplaySettings.asset (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Core/Settings/GlobalDisplaySettings.asset.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Documentation.meta (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Documentation/Documentation.pdf (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Documentation/Documentation.pdf.meta (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Black.mat (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Black.mat.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Blue.mat (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Blue.mat.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Brown.mat (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Brown.mat.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Dark Grey.mat (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Dark Grey.mat.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Green.mat (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Green.mat.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Red.mat (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Red.mat.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Road Texture.png (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Road Texture.png.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Road Underside.mat (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Road Underside.mat.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Road.mat (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Materials/Road.mat.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Prefabs.meta (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Prefabs/Cube.prefab (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Prefabs/Cube.prefab.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Prefabs/Follower.prefab (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Prefabs/Follower.prefab.meta (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Prefabs/Path.prefab (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Prefabs/Path.prefab.meta (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Prefabs/Sphere.prefab (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Prefabs/Sphere.prefab.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scenes.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scenes/Follow Path.unity (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scenes/Follow Path.unity.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scenes/Generate Path.unity (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scenes/Generate Path.unity.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scenes/Mesh Examples.meta (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scenes/Mesh Examples/Road.unity (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scenes/Mesh Examples/Road.unity.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scenes/Object Placement.unity (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scenes/Object Placement.unity.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scenes/Path as Prefab.unity (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scenes/Path as Prefab.unity.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/CylinderMeshCreator.cs (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/CylinderMeshCreator.cs.meta (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/Editor.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/GeneratePathExample.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/GeneratePathExample.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/PathFollower.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/PathFollower.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/PathPlacer.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/PathPlacer.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/PathSceneTool.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/PathSceneTool.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/PathSpawner.cs (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/PathSpawner.cs.meta (100%) rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/RoadMeshCreator.cs (100%) mode change 100755 => 100644 rename {Path Creator Project/Assets => Assets}/PathCreator/Examples/Scripts/RoadMeshCreator.cs.meta (100%) mode change 100755 => 100644 rename {Path Creator Project/Packages => Packages}/manifest.json (74%) rename {Path Creator Project/Packages => Packages}/packages-lock.json (85%) delete mode 100644 Path Creator Project/.vscode/settings.json delete mode 100644 Path Creator Project/Logs/Packages-Update.log delete mode 100644 Path Creator Project/ProjectSettings/ProjectVersion.txt rename {Path Creator Project/ProjectSettings => ProjectSettings}/AudioManager.asset (100%) rename {Path Creator Project/ProjectSettings => ProjectSettings}/ClusterInputManager.asset (100%) rename {Path Creator Project/ProjectSettings => ProjectSettings}/DynamicsManager.asset (100%) rename {Path Creator Project/ProjectSettings => ProjectSettings}/EditorBuildSettings.asset (100%) rename {Path Creator Project/ProjectSettings => ProjectSettings}/EditorSettings.asset (100%) rename {Path Creator Project/ProjectSettings => ProjectSettings}/GraphicsSettings.asset (100%) rename {Path Creator Project/ProjectSettings => ProjectSettings}/InputManager.asset (100%) rename {Path Creator Project/ProjectSettings => ProjectSettings}/NavMeshAreas.asset (100%) rename {Path Creator Project/ProjectSettings => ProjectSettings}/NetworkManager.asset (100%) create mode 100644 ProjectSettings/PackageManagerSettings.asset rename {Path Creator Project/ProjectSettings => ProjectSettings}/Physics2DSettings.asset (100%) rename {Path Creator Project/ProjectSettings => ProjectSettings}/PresetManager.asset (100%) rename {Path Creator Project/ProjectSettings => ProjectSettings}/ProjectSettings.asset (100%) create mode 100644 ProjectSettings/ProjectVersion.txt rename {Path Creator Project/ProjectSettings => ProjectSettings}/QualitySettings.asset (100%) create mode 100644 ProjectSettings/SceneTemplateSettings.json rename {Path Creator Project/ProjectSettings => ProjectSettings}/TagManager.asset (100%) rename {Path Creator Project/ProjectSettings => ProjectSettings}/TimeManager.asset (100%) rename {Path Creator Project/ProjectSettings => ProjectSettings}/UnityConnectSettings.asset (100%) rename {Path Creator Project/ProjectSettings => ProjectSettings}/VFXManager.asset (100%) create mode 100644 ProjectSettings/VersionControlSettings.asset rename {Path Creator Project/ProjectSettings => ProjectSettings}/XRSettings.asset (100%) diff --git a/.gitignore b/.gitignore index 833e6d4..58cbc82 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,34 @@ -[Ll]ibrary/ -[Tt]emp/ -[Oo]bj/ -[Bb]uild/ -[Bb]uilds/ -Assets/AssetStoreTools* +# This .gitignore file should be placed at the root of your Unity project directory +# +# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore +# +/[Ll]ibrary/ +/[Tt]emp/ +/[Oo]bj/ +/[Bb]uild/ +/[Bb]uilds/ +/[Ll]ogs/ +/[Uu]ser[Ss]ettings/ + +# MemoryCaptures can get excessive in size. +# They also could contain extremely sensitive data +/[Mm]emoryCaptures/ + +# Recordings can get excessive in size +/[Rr]ecordings/ + +# Uncomment this line if you wish to ignore the asset store tools plugin +# /[Aa]ssets/AssetStoreTools* + +# Autogenerated Jetbrains Rider plugin +/[Aa]ssets/Plugins/Editor/JetBrains* # Visual Studio cache directory .vs/ +# Gradle cache directory +.gradle/ + # Autogenerated VS/MD/Consulo solution and project files ExportedObj/ .consulo/ @@ -22,16 +43,30 @@ ExportedObj/ *.booproj *.svd *.pdb +*.mdb *.opendb *.VC.db # Unity3D generated meta files *.pidb.meta *.pdb.meta +*.mdb.meta -# Unity3D Generated File On Crash Reports +# Unity3D generated file on crash reports sysinfo.txt # Builds *.apk +*.aab *.unitypackage +*.app + +# Crashlytics generated file +crashlytics-build.properties + +# Packed Addressables +/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* + +# Temporary auto-generated Android Assets +/[Aa]ssets/[Ss]treamingAssets/aa.meta +/[Aa]ssets/[Ss]treamingAssets/aa/* diff --git a/Path Creator Project/Assets/PathCreator.meta b/Assets/PathCreator.meta similarity index 100% rename from Path Creator Project/Assets/PathCreator.meta rename to Assets/PathCreator.meta diff --git a/Path Creator Project/Assets/PathCreator/Core.meta b/Assets/PathCreator/Core.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core.meta rename to Assets/PathCreator/Core.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor.meta b/Assets/PathCreator/Core/Editor.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Editor.meta rename to Assets/PathCreator/Core/Editor.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/Helper.meta b/Assets/PathCreator/Core/Editor/Helper.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Editor/Helper.meta rename to Assets/PathCreator/Core/Editor/Helper.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs b/Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs rename to Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs.meta b/Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs.meta rename to Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs b/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs rename to Assets/PathCreator/Core/Editor/Helper/PathHandle.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs.meta b/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs.meta rename to Assets/PathCreator/Core/Editor/Helper/PathHandle.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs b/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs rename to Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs.meta b/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs.meta rename to Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/PathCreatorEditor.asmdef b/Assets/PathCreator/Core/Editor/PathCreatorEditor.asmdef old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Editor/PathCreatorEditor.asmdef rename to Assets/PathCreator/Core/Editor/PathCreatorEditor.asmdef diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/PathCreatorEditor.asmdef.meta b/Assets/PathCreator/Core/Editor/PathCreatorEditor.asmdef.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Editor/PathCreatorEditor.asmdef.meta rename to Assets/PathCreator/Core/Editor/PathCreatorEditor.asmdef.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/PathEditor.cs b/Assets/PathCreator/Core/Editor/PathEditor.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Editor/PathEditor.cs rename to Assets/PathCreator/Core/Editor/PathEditor.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/PathEditor.cs.meta b/Assets/PathCreator/Core/Editor/PathEditor.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Editor/PathEditor.cs.meta rename to Assets/PathCreator/Core/Editor/PathEditor.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime.meta b/Assets/PathCreator/Core/Runtime.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime.meta rename to Assets/PathCreator/Core/Runtime.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects.meta b/Assets/PathCreator/Core/Runtime/Objects.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects.meta rename to Assets/PathCreator/Core/Runtime/Objects.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs b/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs rename to Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs.meta b/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs.meta rename to Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/EndOfPathInstruction.cs b/Assets/PathCreator/Core/Runtime/Objects/EndOfPathInstruction.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/EndOfPathInstruction.cs rename to Assets/PathCreator/Core/Runtime/Objects/EndOfPathInstruction.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/EndOfPathInstruction.cs.meta b/Assets/PathCreator/Core/Runtime/Objects/EndOfPathInstruction.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/EndOfPathInstruction.cs.meta rename to Assets/PathCreator/Core/Runtime/Objects/EndOfPathInstruction.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/GlobalDisplaySettings.cs b/Assets/PathCreator/Core/Runtime/Objects/GlobalDisplaySettings.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/GlobalDisplaySettings.cs rename to Assets/PathCreator/Core/Runtime/Objects/GlobalDisplaySettings.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/GlobalDisplaySettings.cs.meta b/Assets/PathCreator/Core/Runtime/Objects/GlobalDisplaySettings.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/GlobalDisplaySettings.cs.meta rename to Assets/PathCreator/Core/Runtime/Objects/GlobalDisplaySettings.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/MinMax3D.cs b/Assets/PathCreator/Core/Runtime/Objects/MinMax3D.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/MinMax3D.cs rename to Assets/PathCreator/Core/Runtime/Objects/MinMax3D.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/MinMax3D.cs.meta b/Assets/PathCreator/Core/Runtime/Objects/MinMax3D.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/MinMax3D.cs.meta rename to Assets/PathCreator/Core/Runtime/Objects/MinMax3D.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/PathCreator.cs b/Assets/PathCreator/Core/Runtime/Objects/PathCreator.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/PathCreator.cs rename to Assets/PathCreator/Core/Runtime/Objects/PathCreator.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/PathCreator.cs.meta b/Assets/PathCreator/Core/Runtime/Objects/PathCreator.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/PathCreator.cs.meta rename to Assets/PathCreator/Core/Runtime/Objects/PathCreator.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/PathCreatorData.cs b/Assets/PathCreator/Core/Runtime/Objects/PathCreatorData.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/PathCreatorData.cs rename to Assets/PathCreator/Core/Runtime/Objects/PathCreatorData.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/PathCreatorData.cs.meta b/Assets/PathCreator/Core/Runtime/Objects/PathCreatorData.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/PathCreatorData.cs.meta rename to Assets/PathCreator/Core/Runtime/Objects/PathCreatorData.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/PathSpace.cs b/Assets/PathCreator/Core/Runtime/Objects/PathSpace.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/PathSpace.cs rename to Assets/PathCreator/Core/Runtime/Objects/PathSpace.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/PathSpace.cs.meta b/Assets/PathCreator/Core/Runtime/Objects/PathSpace.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/PathSpace.cs.meta rename to Assets/PathCreator/Core/Runtime/Objects/PathSpace.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs b/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs rename to Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs.meta b/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs.meta rename to Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/PathCreator.asmdef b/Assets/PathCreator/Core/Runtime/PathCreator.asmdef old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/PathCreator.asmdef rename to Assets/PathCreator/Core/Runtime/PathCreator.asmdef diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/PathCreator.asmdef.meta b/Assets/PathCreator/Core/Runtime/PathCreator.asmdef.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/PathCreator.asmdef.meta rename to Assets/PathCreator/Core/Runtime/PathCreator.asmdef.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Utility.meta b/Assets/PathCreator/Core/Runtime/Utility.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Utility.meta rename to Assets/PathCreator/Core/Runtime/Utility.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/CubicBezierUtility.cs b/Assets/PathCreator/Core/Runtime/Utility/CubicBezierUtility.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/CubicBezierUtility.cs rename to Assets/PathCreator/Core/Runtime/Utility/CubicBezierUtility.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/CubicBezierUtility.cs.meta b/Assets/PathCreator/Core/Runtime/Utility/CubicBezierUtility.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/CubicBezierUtility.cs.meta rename to Assets/PathCreator/Core/Runtime/Utility/CubicBezierUtility.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs b/Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs rename to Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs.meta b/Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs.meta rename to Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/VertexPathUtility.cs b/Assets/PathCreator/Core/Runtime/Utility/VertexPathUtility.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/VertexPathUtility.cs rename to Assets/PathCreator/Core/Runtime/Utility/VertexPathUtility.cs diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/VertexPathUtility.cs.meta b/Assets/PathCreator/Core/Runtime/Utility/VertexPathUtility.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/VertexPathUtility.cs.meta rename to Assets/PathCreator/Core/Runtime/Utility/VertexPathUtility.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Settings.meta b/Assets/PathCreator/Core/Settings.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Settings.meta rename to Assets/PathCreator/Core/Settings.meta diff --git a/Path Creator Project/Assets/PathCreator/Core/Settings/GlobalDisplaySettings.asset b/Assets/PathCreator/Core/Settings/GlobalDisplaySettings.asset similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Settings/GlobalDisplaySettings.asset rename to Assets/PathCreator/Core/Settings/GlobalDisplaySettings.asset diff --git a/Path Creator Project/Assets/PathCreator/Core/Settings/GlobalDisplaySettings.asset.meta b/Assets/PathCreator/Core/Settings/GlobalDisplaySettings.asset.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Core/Settings/GlobalDisplaySettings.asset.meta rename to Assets/PathCreator/Core/Settings/GlobalDisplaySettings.asset.meta diff --git a/Path Creator Project/Assets/PathCreator/Documentation.meta b/Assets/PathCreator/Documentation.meta similarity index 100% rename from Path Creator Project/Assets/PathCreator/Documentation.meta rename to Assets/PathCreator/Documentation.meta diff --git a/Path Creator Project/Assets/PathCreator/Documentation/Documentation.pdf b/Assets/PathCreator/Documentation/Documentation.pdf similarity index 100% rename from Path Creator Project/Assets/PathCreator/Documentation/Documentation.pdf rename to Assets/PathCreator/Documentation/Documentation.pdf diff --git a/Path Creator Project/Assets/PathCreator/Documentation/Documentation.pdf.meta b/Assets/PathCreator/Documentation/Documentation.pdf.meta similarity index 100% rename from Path Creator Project/Assets/PathCreator/Documentation/Documentation.pdf.meta rename to Assets/PathCreator/Documentation/Documentation.pdf.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples.meta b/Assets/PathCreator/Examples.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples.meta rename to Assets/PathCreator/Examples.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials.meta b/Assets/PathCreator/Examples/Materials.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials.meta rename to Assets/PathCreator/Examples/Materials.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Black.mat b/Assets/PathCreator/Examples/Materials/Black.mat old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Black.mat rename to Assets/PathCreator/Examples/Materials/Black.mat diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Black.mat.meta b/Assets/PathCreator/Examples/Materials/Black.mat.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Black.mat.meta rename to Assets/PathCreator/Examples/Materials/Black.mat.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Blue.mat b/Assets/PathCreator/Examples/Materials/Blue.mat old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Blue.mat rename to Assets/PathCreator/Examples/Materials/Blue.mat diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Blue.mat.meta b/Assets/PathCreator/Examples/Materials/Blue.mat.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Blue.mat.meta rename to Assets/PathCreator/Examples/Materials/Blue.mat.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Brown.mat b/Assets/PathCreator/Examples/Materials/Brown.mat old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Brown.mat rename to Assets/PathCreator/Examples/Materials/Brown.mat diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Brown.mat.meta b/Assets/PathCreator/Examples/Materials/Brown.mat.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Brown.mat.meta rename to Assets/PathCreator/Examples/Materials/Brown.mat.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Dark Grey.mat b/Assets/PathCreator/Examples/Materials/Dark Grey.mat old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Dark Grey.mat rename to Assets/PathCreator/Examples/Materials/Dark Grey.mat diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Dark Grey.mat.meta b/Assets/PathCreator/Examples/Materials/Dark Grey.mat.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Dark Grey.mat.meta rename to Assets/PathCreator/Examples/Materials/Dark Grey.mat.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Green.mat b/Assets/PathCreator/Examples/Materials/Green.mat old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Green.mat rename to Assets/PathCreator/Examples/Materials/Green.mat diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Green.mat.meta b/Assets/PathCreator/Examples/Materials/Green.mat.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Green.mat.meta rename to Assets/PathCreator/Examples/Materials/Green.mat.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Red.mat b/Assets/PathCreator/Examples/Materials/Red.mat old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Red.mat rename to Assets/PathCreator/Examples/Materials/Red.mat diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Red.mat.meta b/Assets/PathCreator/Examples/Materials/Red.mat.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Red.mat.meta rename to Assets/PathCreator/Examples/Materials/Red.mat.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Road Texture.png b/Assets/PathCreator/Examples/Materials/Road Texture.png old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Road Texture.png rename to Assets/PathCreator/Examples/Materials/Road Texture.png diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Road Texture.png.meta b/Assets/PathCreator/Examples/Materials/Road Texture.png.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Road Texture.png.meta rename to Assets/PathCreator/Examples/Materials/Road Texture.png.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Road Underside.mat b/Assets/PathCreator/Examples/Materials/Road Underside.mat old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Road Underside.mat rename to Assets/PathCreator/Examples/Materials/Road Underside.mat diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Road Underside.mat.meta b/Assets/PathCreator/Examples/Materials/Road Underside.mat.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Road Underside.mat.meta rename to Assets/PathCreator/Examples/Materials/Road Underside.mat.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Road.mat b/Assets/PathCreator/Examples/Materials/Road.mat old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Road.mat rename to Assets/PathCreator/Examples/Materials/Road.mat diff --git a/Path Creator Project/Assets/PathCreator/Examples/Materials/Road.mat.meta b/Assets/PathCreator/Examples/Materials/Road.mat.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Materials/Road.mat.meta rename to Assets/PathCreator/Examples/Materials/Road.mat.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Prefabs.meta b/Assets/PathCreator/Examples/Prefabs.meta similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Prefabs.meta rename to Assets/PathCreator/Examples/Prefabs.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Prefabs/Cube.prefab b/Assets/PathCreator/Examples/Prefabs/Cube.prefab old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Prefabs/Cube.prefab rename to Assets/PathCreator/Examples/Prefabs/Cube.prefab diff --git a/Path Creator Project/Assets/PathCreator/Examples/Prefabs/Cube.prefab.meta b/Assets/PathCreator/Examples/Prefabs/Cube.prefab.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Prefabs/Cube.prefab.meta rename to Assets/PathCreator/Examples/Prefabs/Cube.prefab.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Prefabs/Follower.prefab b/Assets/PathCreator/Examples/Prefabs/Follower.prefab similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Prefabs/Follower.prefab rename to Assets/PathCreator/Examples/Prefabs/Follower.prefab diff --git a/Path Creator Project/Assets/PathCreator/Examples/Prefabs/Follower.prefab.meta b/Assets/PathCreator/Examples/Prefabs/Follower.prefab.meta similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Prefabs/Follower.prefab.meta rename to Assets/PathCreator/Examples/Prefabs/Follower.prefab.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Prefabs/Path.prefab b/Assets/PathCreator/Examples/Prefabs/Path.prefab similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Prefabs/Path.prefab rename to Assets/PathCreator/Examples/Prefabs/Path.prefab diff --git a/Path Creator Project/Assets/PathCreator/Examples/Prefabs/Path.prefab.meta b/Assets/PathCreator/Examples/Prefabs/Path.prefab.meta similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Prefabs/Path.prefab.meta rename to Assets/PathCreator/Examples/Prefabs/Path.prefab.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Prefabs/Sphere.prefab b/Assets/PathCreator/Examples/Prefabs/Sphere.prefab old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Prefabs/Sphere.prefab rename to Assets/PathCreator/Examples/Prefabs/Sphere.prefab diff --git a/Path Creator Project/Assets/PathCreator/Examples/Prefabs/Sphere.prefab.meta b/Assets/PathCreator/Examples/Prefabs/Sphere.prefab.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Prefabs/Sphere.prefab.meta rename to Assets/PathCreator/Examples/Prefabs/Sphere.prefab.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes.meta b/Assets/PathCreator/Examples/Scenes.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes.meta rename to Assets/PathCreator/Examples/Scenes.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Follow Path.unity b/Assets/PathCreator/Examples/Scenes/Follow Path.unity similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Follow Path.unity rename to Assets/PathCreator/Examples/Scenes/Follow Path.unity diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Follow Path.unity.meta b/Assets/PathCreator/Examples/Scenes/Follow Path.unity.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Follow Path.unity.meta rename to Assets/PathCreator/Examples/Scenes/Follow Path.unity.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Generate Path.unity b/Assets/PathCreator/Examples/Scenes/Generate Path.unity similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Generate Path.unity rename to Assets/PathCreator/Examples/Scenes/Generate Path.unity diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Generate Path.unity.meta b/Assets/PathCreator/Examples/Scenes/Generate Path.unity.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Generate Path.unity.meta rename to Assets/PathCreator/Examples/Scenes/Generate Path.unity.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples.meta b/Assets/PathCreator/Examples/Scenes/Mesh Examples.meta similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples.meta rename to Assets/PathCreator/Examples/Scenes/Mesh Examples.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity b/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity rename to Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity.meta b/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity.meta rename to Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Road.unity b/Assets/PathCreator/Examples/Scenes/Mesh Examples/Road.unity similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Road.unity rename to Assets/PathCreator/Examples/Scenes/Mesh Examples/Road.unity diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Road.unity.meta b/Assets/PathCreator/Examples/Scenes/Mesh Examples/Road.unity.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Mesh Examples/Road.unity.meta rename to Assets/PathCreator/Examples/Scenes/Mesh Examples/Road.unity.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Object Placement.unity b/Assets/PathCreator/Examples/Scenes/Object Placement.unity old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Object Placement.unity rename to Assets/PathCreator/Examples/Scenes/Object Placement.unity diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Object Placement.unity.meta b/Assets/PathCreator/Examples/Scenes/Object Placement.unity.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Object Placement.unity.meta rename to Assets/PathCreator/Examples/Scenes/Object Placement.unity.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Path as Prefab.unity b/Assets/PathCreator/Examples/Scenes/Path as Prefab.unity similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Path as Prefab.unity rename to Assets/PathCreator/Examples/Scenes/Path as Prefab.unity diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/Path as Prefab.unity.meta b/Assets/PathCreator/Examples/Scenes/Path as Prefab.unity.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Path as Prefab.unity.meta rename to Assets/PathCreator/Examples/Scenes/Path as Prefab.unity.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts.meta b/Assets/PathCreator/Examples/Scripts.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts.meta rename to Assets/PathCreator/Examples/Scripts.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs b/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs rename to Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs.meta b/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs.meta similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs.meta rename to Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/Editor.meta b/Assets/PathCreator/Examples/Scripts/Editor.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/Editor.meta rename to Assets/PathCreator/Examples/Scripts/Editor.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs b/Assets/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs rename to Assets/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs.meta b/Assets/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs.meta rename to Assets/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/GeneratePathExample.cs b/Assets/PathCreator/Examples/Scripts/GeneratePathExample.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/GeneratePathExample.cs rename to Assets/PathCreator/Examples/Scripts/GeneratePathExample.cs diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/GeneratePathExample.cs.meta b/Assets/PathCreator/Examples/Scripts/GeneratePathExample.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/GeneratePathExample.cs.meta rename to Assets/PathCreator/Examples/Scripts/GeneratePathExample.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/PathFollower.cs b/Assets/PathCreator/Examples/Scripts/PathFollower.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/PathFollower.cs rename to Assets/PathCreator/Examples/Scripts/PathFollower.cs diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/PathFollower.cs.meta b/Assets/PathCreator/Examples/Scripts/PathFollower.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/PathFollower.cs.meta rename to Assets/PathCreator/Examples/Scripts/PathFollower.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/PathPlacer.cs b/Assets/PathCreator/Examples/Scripts/PathPlacer.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/PathPlacer.cs rename to Assets/PathCreator/Examples/Scripts/PathPlacer.cs diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/PathPlacer.cs.meta b/Assets/PathCreator/Examples/Scripts/PathPlacer.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/PathPlacer.cs.meta rename to Assets/PathCreator/Examples/Scripts/PathPlacer.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/PathSceneTool.cs b/Assets/PathCreator/Examples/Scripts/PathSceneTool.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/PathSceneTool.cs rename to Assets/PathCreator/Examples/Scripts/PathSceneTool.cs diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/PathSceneTool.cs.meta b/Assets/PathCreator/Examples/Scripts/PathSceneTool.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/PathSceneTool.cs.meta rename to Assets/PathCreator/Examples/Scripts/PathSceneTool.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/PathSpawner.cs b/Assets/PathCreator/Examples/Scripts/PathSpawner.cs similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/PathSpawner.cs rename to Assets/PathCreator/Examples/Scripts/PathSpawner.cs diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/PathSpawner.cs.meta b/Assets/PathCreator/Examples/Scripts/PathSpawner.cs.meta similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/PathSpawner.cs.meta rename to Assets/PathCreator/Examples/Scripts/PathSpawner.cs.meta diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/RoadMeshCreator.cs b/Assets/PathCreator/Examples/Scripts/RoadMeshCreator.cs old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/RoadMeshCreator.cs rename to Assets/PathCreator/Examples/Scripts/RoadMeshCreator.cs diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scripts/RoadMeshCreator.cs.meta b/Assets/PathCreator/Examples/Scripts/RoadMeshCreator.cs.meta old mode 100755 new mode 100644 similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scripts/RoadMeshCreator.cs.meta rename to Assets/PathCreator/Examples/Scripts/RoadMeshCreator.cs.meta diff --git a/Path Creator Project/Packages/manifest.json b/Packages/manifest.json similarity index 74% rename from Path Creator Project/Packages/manifest.json rename to Packages/manifest.json index ab244fb..beef566 100644 --- a/Path Creator Project/Packages/manifest.json +++ b/Packages/manifest.json @@ -1,19 +1,13 @@ { "dependencies": { - "com.unity.2d.sprite": "1.0.0", - "com.unity.2d.tilemap": "1.0.0", - "com.unity.ads": "3.4.7", - "com.unity.analytics": "3.3.5", - "com.unity.collab-proxy": "1.2.16", - "com.unity.ide.rider": "1.1.4", - "com.unity.ide.vscode": "1.2.1", - "com.unity.multiplayer-hlapi": "1.0.6", - "com.unity.purchasing": "2.0.6", - "com.unity.test-framework": "1.1.16", - "com.unity.textmeshpro": "2.0.1", - "com.unity.timeline": "1.2.6", + "com.unity.collab-proxy": "1.5.7", + "com.unity.ide.rider": "2.0.7", + "com.unity.ide.visualstudio": "2.0.8", + "com.unity.ide.vscode": "1.2.3", + "com.unity.test-framework": "1.1.24", + "com.unity.textmeshpro": "3.0.6", + "com.unity.timeline": "1.4.8", "com.unity.ugui": "1.0.0", - "com.unity.xr.legacyinputhelpers": "2.1.4", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", diff --git a/Path Creator Project/Packages/packages-lock.json b/Packages/packages-lock.json similarity index 85% rename from Path Creator Project/Packages/packages-lock.json rename to Packages/packages-lock.json index f71a07a..ced5225 100644 --- a/Path Creator Project/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -1,51 +1,23 @@ { "dependencies": { - "com.unity.2d.sprite": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.2d.tilemap": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.ads": { - "version": "3.4.7", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.analytics": { - "version": "3.3.5", + "com.unity.collab-proxy": { + "version": "1.5.7", "depth": 0, "source": "registry", "dependencies": { - "com.unity.ugui": "1.0.0" + "com.unity.nuget.newtonsoft-json": "2.0.0" }, "url": "https://packages.unity.com" }, - "com.unity.collab-proxy": { - "version": "1.2.16", - "depth": 0, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, "com.unity.ext.nunit": { - "version": "1.0.0", + "version": "1.0.6", "depth": 1, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, "com.unity.ide.rider": { - "version": "1.1.4", + "version": "2.0.7", "depth": 0, "source": "registry", "dependencies": { @@ -53,42 +25,42 @@ }, "url": "https://packages.unity.com" }, - "com.unity.ide.vscode": { - "version": "1.2.1", + "com.unity.ide.visualstudio": { + "version": "2.0.8", "depth": 0, "source": "registry", - "dependencies": {}, + "dependencies": { + "com.unity.test-framework": "1.1.9" + }, "url": "https://packages.unity.com" }, - "com.unity.multiplayer-hlapi": { - "version": "1.0.6", + "com.unity.ide.vscode": { + "version": "1.2.3", "depth": 0, "source": "registry", - "dependencies": { - "nuget.mono-cecil": "0.1.6-preview" - }, + "dependencies": {}, "url": "https://packages.unity.com" }, - "com.unity.purchasing": { - "version": "2.0.6", - "depth": 0, + "com.unity.nuget.newtonsoft-json": { + "version": "2.0.0", + "depth": 1, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, "com.unity.test-framework": { - "version": "1.1.16", + "version": "1.1.24", "depth": 0, "source": "registry", "dependencies": { - "com.unity.ext.nunit": "1.0.0", + "com.unity.ext.nunit": "1.0.6", "com.unity.modules.imgui": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0" }, "url": "https://packages.unity.com" }, "com.unity.textmeshpro": { - "version": "2.0.1", + "version": "3.0.6", "depth": 0, "source": "registry", "dependencies": { @@ -97,10 +69,15 @@ "url": "https://packages.unity.com" }, "com.unity.timeline": { - "version": "1.2.6", + "version": "1.4.8", "depth": 0, "source": "registry", - "dependencies": {}, + "dependencies": { + "com.unity.modules.director": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0" + }, "url": "https://packages.unity.com" }, "com.unity.ugui": { @@ -108,23 +85,10 @@ "depth": 0, "source": "builtin", "dependencies": { - "com.unity.modules.ui": "1.0.0" + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0" } }, - "com.unity.xr.legacyinputhelpers": { - "version": "2.1.4", - "depth": 0, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "nuget.mono-cecil": { - "version": "0.1.6-preview", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, "com.unity.modules.ai": { "version": "1.0.0", "depth": 0, @@ -258,6 +222,18 @@ "depth": 0, "source": "builtin", "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.uielementsnative": "1.0.0" + } + }, + "com.unity.modules.uielementsnative": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", "com.unity.modules.imgui": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0" } diff --git a/Path Creator Project/.vscode/settings.json b/Path Creator Project/.vscode/settings.json deleted file mode 100644 index 4edd970..0000000 --- a/Path Creator Project/.vscode/settings.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "files.exclude": - { - "**/.DS_Store":true, - "**/.git":true, - "**/.gitignore":true, - "**/.gitmodules":true, - "**/*.booproj":true, - "**/*.pidb":true, - "**/*.suo":true, - "**/*.user":true, - "**/*.userprefs":true, - "**/*.unityproj":true, - "**/*.dll":true, - "**/*.exe":true, - "**/*.pdf":true, - "**/*.mid":true, - "**/*.midi":true, - "**/*.wav":true, - "**/*.gif":true, - "**/*.ico":true, - "**/*.jpg":true, - "**/*.jpeg":true, - "**/*.png":true, - "**/*.psd":true, - "**/*.tga":true, - "**/*.tif":true, - "**/*.tiff":true, - "**/*.3ds":true, - "**/*.3DS":true, - "**/*.fbx":true, - "**/*.FBX":true, - "**/*.lxo":true, - "**/*.LXO":true, - "**/*.ma":true, - "**/*.MA":true, - "**/*.obj":true, - "**/*.OBJ":true, - "**/*.asset":true, - "**/*.cubemap":true, - "**/*.flare":true, - "**/*.mat":true, - "**/*.meta":true, - "**/*.prefab":true, - "**/*.unity":true, - "build/":true, - "Build/":true, - "Library/":true, - "library/":true, - "obj/":true, - "Obj/":true, - "ProjectSettings/":true, - "temp/":true, - "Temp/":true - } -} \ No newline at end of file diff --git a/Path Creator Project/Logs/Packages-Update.log b/Path Creator Project/Logs/Packages-Update.log deleted file mode 100644 index 0e6b9db..0000000 --- a/Path Creator Project/Logs/Packages-Update.log +++ /dev/null @@ -1,142 +0,0 @@ - -=== Sun Jun 23 13:03:16 2019 - -Packages were changed. -Update Mode: resetToDefaultDependencies - -The following packages were added: - com.unity.analytics@3.3.2 - com.unity.purchasing@2.0.6 - com.unity.ads@2.0.8 - com.unity.textmeshpro@2.0.0 - com.unity.package-manager-ui@2.1.2 - com.unity.collab-proxy@1.2.16 - com.unity.timeline@1.0.0 - com.unity.modules.ai@1.0.0 - com.unity.modules.animation@1.0.0 - com.unity.modules.assetbundle@1.0.0 - com.unity.modules.audio@1.0.0 - com.unity.modules.cloth@1.0.0 - com.unity.modules.director@1.0.0 - com.unity.modules.imageconversion@1.0.0 - com.unity.modules.imgui@1.0.0 - com.unity.modules.jsonserialize@1.0.0 - com.unity.modules.particlesystem@1.0.0 - com.unity.modules.physics@1.0.0 - com.unity.modules.physics2d@1.0.0 - com.unity.modules.screencapture@1.0.0 - com.unity.modules.terrain@1.0.0 - com.unity.modules.terrainphysics@1.0.0 - com.unity.modules.tilemap@1.0.0 - com.unity.modules.ui@1.0.0 - com.unity.modules.uielements@1.0.0 - com.unity.modules.umbra@1.0.0 - com.unity.modules.unityanalytics@1.0.0 - com.unity.modules.unitywebrequest@1.0.0 - com.unity.modules.unitywebrequestassetbundle@1.0.0 - com.unity.modules.unitywebrequestaudio@1.0.0 - com.unity.modules.unitywebrequesttexture@1.0.0 - com.unity.modules.unitywebrequestwww@1.0.0 - com.unity.modules.vehicles@1.0.0 - com.unity.modules.video@1.0.0 - com.unity.modules.vr@1.0.0 - com.unity.modules.wind@1.0.0 - com.unity.modules.xr@1.0.0 - -=== Sun Jun 23 13:23:24 2019 - -Packages were changed. -Update Mode: updateDependencies - -The following packages were added: - com.unity.analytics@3.3.2 - com.unity.purchasing@2.0.6 - com.unity.ads@2.0.8 - com.unity.textmeshpro@2.0.0 - com.unity.package-manager-ui@2.1.2 - com.unity.collab-proxy@1.2.16 - com.unity.timeline@1.0.0 - com.unity.modules.ai@1.0.0 - com.unity.modules.animation@1.0.0 - com.unity.modules.assetbundle@1.0.0 - com.unity.modules.audio@1.0.0 - com.unity.modules.cloth@1.0.0 - com.unity.modules.director@1.0.0 - com.unity.modules.imageconversion@1.0.0 - com.unity.modules.imgui@1.0.0 - com.unity.modules.jsonserialize@1.0.0 - com.unity.modules.particlesystem@1.0.0 - com.unity.modules.physics@1.0.0 - com.unity.modules.physics2d@1.0.0 - com.unity.modules.screencapture@1.0.0 - com.unity.modules.terrain@1.0.0 - com.unity.modules.terrainphysics@1.0.0 - com.unity.modules.tilemap@1.0.0 - com.unity.modules.ui@1.0.0 - com.unity.modules.uielements@1.0.0 - com.unity.modules.umbra@1.0.0 - com.unity.modules.unityanalytics@1.0.0 - com.unity.modules.unitywebrequest@1.0.0 - com.unity.modules.unitywebrequestassetbundle@1.0.0 - com.unity.modules.unitywebrequestaudio@1.0.0 - com.unity.modules.unitywebrequesttexture@1.0.0 - com.unity.modules.unitywebrequestwww@1.0.0 - com.unity.modules.vehicles@1.0.0 - com.unity.modules.video@1.0.0 - com.unity.modules.vr@1.0.0 - com.unity.modules.wind@1.0.0 - com.unity.modules.xr@1.0.0 - com.unity.multiplayer-hlapi@1.0.2 - com.unity.xr.legacyinputhelpers@2.0.2 - -=== Fri Oct 2 00:17:46 2020 - -Packages were changed. -Update Mode: updateDependencies - -The following packages were added: - com.unity.2d.sprite@1.0.0 - com.unity.2d.tilemap@1.0.0 - com.unity.ads@3.4.7 - com.unity.analytics@3.3.5 - com.unity.collab-proxy@1.2.16 - com.unity.ide.rider@1.1.4 - com.unity.ide.vscode@1.2.1 - com.unity.modules.ai@1.0.0 - com.unity.modules.androidjni@1.0.0 - com.unity.modules.animation@1.0.0 - com.unity.modules.assetbundle@1.0.0 - com.unity.modules.audio@1.0.0 - com.unity.modules.cloth@1.0.0 - com.unity.modules.director@1.0.0 - com.unity.modules.imageconversion@1.0.0 - com.unity.modules.imgui@1.0.0 - com.unity.modules.jsonserialize@1.0.0 - com.unity.modules.particlesystem@1.0.0 - com.unity.modules.physics@1.0.0 - com.unity.modules.physics2d@1.0.0 - com.unity.modules.screencapture@1.0.0 - com.unity.modules.terrain@1.0.0 - com.unity.modules.terrainphysics@1.0.0 - com.unity.modules.tilemap@1.0.0 - com.unity.modules.ui@1.0.0 - com.unity.modules.uielements@1.0.0 - com.unity.modules.umbra@1.0.0 - com.unity.modules.unityanalytics@1.0.0 - com.unity.modules.unitywebrequest@1.0.0 - com.unity.modules.unitywebrequestassetbundle@1.0.0 - com.unity.modules.unitywebrequestaudio@1.0.0 - com.unity.modules.unitywebrequesttexture@1.0.0 - com.unity.modules.unitywebrequestwww@1.0.0 - com.unity.modules.vehicles@1.0.0 - com.unity.modules.video@1.0.0 - com.unity.modules.vr@1.0.0 - com.unity.modules.wind@1.0.0 - com.unity.modules.xr@1.0.0 - com.unity.multiplayer-hlapi@1.0.6 - com.unity.purchasing@2.0.6 - com.unity.test-framework@1.1.16 - com.unity.textmeshpro@2.0.1 - com.unity.timeline@1.2.6 - com.unity.ugui@1.0.0 - com.unity.xr.legacyinputhelpers@2.1.4 diff --git a/Path Creator Project/ProjectSettings/ProjectVersion.txt b/Path Creator Project/ProjectSettings/ProjectVersion.txt deleted file mode 100644 index cb55a75..0000000 --- a/Path Creator Project/ProjectSettings/ProjectVersion.txt +++ /dev/null @@ -1,2 +0,0 @@ -m_EditorVersion: 2019.4.8f1 -m_EditorVersionWithRevision: 2019.4.8f1 (60781d942082) diff --git a/Path Creator Project/ProjectSettings/AudioManager.asset b/ProjectSettings/AudioManager.asset similarity index 100% rename from Path Creator Project/ProjectSettings/AudioManager.asset rename to ProjectSettings/AudioManager.asset diff --git a/Path Creator Project/ProjectSettings/ClusterInputManager.asset b/ProjectSettings/ClusterInputManager.asset similarity index 100% rename from Path Creator Project/ProjectSettings/ClusterInputManager.asset rename to ProjectSettings/ClusterInputManager.asset diff --git a/Path Creator Project/ProjectSettings/DynamicsManager.asset b/ProjectSettings/DynamicsManager.asset similarity index 100% rename from Path Creator Project/ProjectSettings/DynamicsManager.asset rename to ProjectSettings/DynamicsManager.asset diff --git a/Path Creator Project/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset similarity index 100% rename from Path Creator Project/ProjectSettings/EditorBuildSettings.asset rename to ProjectSettings/EditorBuildSettings.asset diff --git a/Path Creator Project/ProjectSettings/EditorSettings.asset b/ProjectSettings/EditorSettings.asset similarity index 100% rename from Path Creator Project/ProjectSettings/EditorSettings.asset rename to ProjectSettings/EditorSettings.asset diff --git a/Path Creator Project/ProjectSettings/GraphicsSettings.asset b/ProjectSettings/GraphicsSettings.asset similarity index 100% rename from Path Creator Project/ProjectSettings/GraphicsSettings.asset rename to ProjectSettings/GraphicsSettings.asset diff --git a/Path Creator Project/ProjectSettings/InputManager.asset b/ProjectSettings/InputManager.asset similarity index 100% rename from Path Creator Project/ProjectSettings/InputManager.asset rename to ProjectSettings/InputManager.asset diff --git a/Path Creator Project/ProjectSettings/NavMeshAreas.asset b/ProjectSettings/NavMeshAreas.asset similarity index 100% rename from Path Creator Project/ProjectSettings/NavMeshAreas.asset rename to ProjectSettings/NavMeshAreas.asset diff --git a/Path Creator Project/ProjectSettings/NetworkManager.asset b/ProjectSettings/NetworkManager.asset similarity index 100% rename from Path Creator Project/ProjectSettings/NetworkManager.asset rename to ProjectSettings/NetworkManager.asset diff --git a/ProjectSettings/PackageManagerSettings.asset b/ProjectSettings/PackageManagerSettings.asset new file mode 100644 index 0000000..be4a797 --- /dev/null +++ b/ProjectSettings/PackageManagerSettings.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_EnablePreviewPackages: 0 + m_EnablePackageDependencies: 0 + m_AdvancedSettingsExpanded: 1 + m_ScopedRegistriesSettingsExpanded: 1 + oneTimeWarningShown: 0 + m_Registries: + - m_Id: main + m_Name: + m_Url: https://packages.unity.com + m_Scopes: [] + m_IsDefault: 1 + m_Capabilities: 7 + m_UserSelectedRegistryName: + m_UserAddingNewScopedRegistry: 0 + m_RegistryInfoDraft: + m_ErrorMessage: + m_Original: + m_Id: + m_Name: + m_Url: + m_Scopes: [] + m_IsDefault: 0 + m_Capabilities: 0 + m_Modified: 0 + m_Name: + m_Url: + m_Scopes: + - + m_SelectedScopeIndex: 0 diff --git a/Path Creator Project/ProjectSettings/Physics2DSettings.asset b/ProjectSettings/Physics2DSettings.asset similarity index 100% rename from Path Creator Project/ProjectSettings/Physics2DSettings.asset rename to ProjectSettings/Physics2DSettings.asset diff --git a/Path Creator Project/ProjectSettings/PresetManager.asset b/ProjectSettings/PresetManager.asset similarity index 100% rename from Path Creator Project/ProjectSettings/PresetManager.asset rename to ProjectSettings/PresetManager.asset diff --git a/Path Creator Project/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset similarity index 100% rename from Path Creator Project/ProjectSettings/ProjectSettings.asset rename to ProjectSettings/ProjectSettings.asset diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..26fc67f --- /dev/null +++ b/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 2020.3.10f1 +m_EditorVersionWithRevision: 2020.3.10f1 (297d780c91bc) diff --git a/Path Creator Project/ProjectSettings/QualitySettings.asset b/ProjectSettings/QualitySettings.asset similarity index 100% rename from Path Creator Project/ProjectSettings/QualitySettings.asset rename to ProjectSettings/QualitySettings.asset diff --git a/ProjectSettings/SceneTemplateSettings.json b/ProjectSettings/SceneTemplateSettings.json new file mode 100644 index 0000000..6f3e60f --- /dev/null +++ b/ProjectSettings/SceneTemplateSettings.json @@ -0,0 +1,167 @@ +{ + "templatePinStates": [], + "dependencyTypeInfos": [ + { + "userAdded": false, + "type": "UnityEngine.AnimationClip", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.Animations.AnimatorController", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.AnimatorOverrideController", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.Audio.AudioMixerController", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.ComputeShader", + "ignore": true, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Cubemap", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.GameObject", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.LightingDataAsset", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": false + }, + { + "userAdded": false, + "type": "UnityEngine.LightingSettings", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Material", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.MonoScript", + "ignore": true, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.PhysicMaterial", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.PhysicsMaterial2D", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.PostProcessing.PostProcessProfile", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.PostProcessing.PostProcessResources", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.VolumeProfile", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.SceneAsset", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": false + }, + { + "userAdded": false, + "type": "UnityEngine.Shader", + "ignore": true, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.ShaderVariantCollection", + "ignore": true, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Texture", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Texture2D", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Timeline.TimelineAsset", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + } + ], + "defaultDependencyTypeInfo": { + "userAdded": false, + "type": "", + "ignore": false, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + "newSceneOverride": 0 +} \ No newline at end of file diff --git a/Path Creator Project/ProjectSettings/TagManager.asset b/ProjectSettings/TagManager.asset similarity index 100% rename from Path Creator Project/ProjectSettings/TagManager.asset rename to ProjectSettings/TagManager.asset diff --git a/Path Creator Project/ProjectSettings/TimeManager.asset b/ProjectSettings/TimeManager.asset similarity index 100% rename from Path Creator Project/ProjectSettings/TimeManager.asset rename to ProjectSettings/TimeManager.asset diff --git a/Path Creator Project/ProjectSettings/UnityConnectSettings.asset b/ProjectSettings/UnityConnectSettings.asset similarity index 100% rename from Path Creator Project/ProjectSettings/UnityConnectSettings.asset rename to ProjectSettings/UnityConnectSettings.asset diff --git a/Path Creator Project/ProjectSettings/VFXManager.asset b/ProjectSettings/VFXManager.asset similarity index 100% rename from Path Creator Project/ProjectSettings/VFXManager.asset rename to ProjectSettings/VFXManager.asset diff --git a/ProjectSettings/VersionControlSettings.asset b/ProjectSettings/VersionControlSettings.asset new file mode 100644 index 0000000..dca2881 --- /dev/null +++ b/ProjectSettings/VersionControlSettings.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!890905787 &1 +VersionControlSettings: + m_ObjectHideFlags: 0 + m_Mode: Visible Meta Files + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/Path Creator Project/ProjectSettings/XRSettings.asset b/ProjectSettings/XRSettings.asset similarity index 100% rename from Path Creator Project/ProjectSettings/XRSettings.asset rename to ProjectSettings/XRSettings.asset From bf4ecaf7b06e610d4b89d7763e64fcbbe3539937 Mon Sep 17 00:00:00 2001 From: Sebastian Lague Date: Tue, 17 May 2022 10:49:08 +0200 Subject: [PATCH 10/12] Fix NaN related crash when trying to split at end of path --- .gitignore | 5 +- .../Core/Editor/Helper/PathHandle.cs | 464 ++--- .../Core/Editor/Helper/ScreenSpacePolyLine.cs | 416 ++--- Assets/PathCreator/Core/Editor/PathEditor.cs | 1605 +++++++++-------- .../Core/Runtime/Objects/BezierPath.cs | 1483 ++++++++------- Packages/manifest.json | 12 +- Packages/packages-lock.json | 31 +- ProjectSettings/MemorySettings.asset | 35 + ProjectSettings/ProjectVersion.txt | 4 +- ProjectSettings/boot.config | 0 10 files changed, 2197 insertions(+), 1858 deletions(-) create mode 100644 ProjectSettings/MemorySettings.asset create mode 100644 ProjectSettings/boot.config diff --git a/.gitignore b/.gitignore index 58cbc82..fb6707f 100644 --- a/.gitignore +++ b/.gitignore @@ -22,9 +22,8 @@ # Autogenerated Jetbrains Rider plugin /[Aa]ssets/Plugins/Editor/JetBrains* - -# Visual Studio cache directory -.vs/ +# VisualStudioCode +.vscode/* # Gradle cache directory .gradle/ diff --git a/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs b/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs index 613fdb8..0e5ff89 100644 --- a/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs +++ b/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs @@ -5,233 +5,239 @@ namespace PathCreationEditor { - public static class PathHandle - { - - public const float extraInputRadius = .005f; - - static Vector2 handleDragMouseStart; - static Vector2 handleDragMouseEnd; - static Vector3 handleDragWorldStart; - - static int selectedHandleID; - static bool mouseIsOverAHandle; - - public enum HandleInputType - { - None, - LMBPress, - LMBClick, - LMBDrag, - LMBRelease, - }; - - static float dstMouseToDragPointStart; - - static List ids; - static HashSet idHash; - - static PathHandle() - { - ids = new List(); - idHash = new HashSet(); - - dstMouseToDragPointStart = float.MaxValue; - } - - public static Vector3 DrawHandle(Vector3 position, PathSpace space, bool isInteractive, float handleDiameter, Handles.CapFunction capFunc, HandleColours colours, out HandleInputType inputType, int handleIndex) - { - int id = GetID(handleIndex); - Vector3 screenPosition = Handles.matrix.MultiplyPoint(position); - Matrix4x4 cachedMatrix = Handles.matrix; - - inputType = HandleInputType.None; - - EventType eventType = Event.current.GetTypeForControl(id); - float handleRadius = handleDiameter / 2f; - float dstToHandle = HandleUtility.DistanceToCircle(position, handleRadius + extraInputRadius); - float dstToMouse = HandleUtility.DistanceToCircle(position, 0); - - // Handle input events - if (isInteractive) - { - // Repaint if mouse is entering/exiting handle (for highlight colour) - if (dstToHandle == 0) - { - if (!mouseIsOverAHandle) - { - HandleUtility.Repaint(); - mouseIsOverAHandle = true; - } - } - else - { - if (mouseIsOverAHandle) - { - HandleUtility.Repaint(); - mouseIsOverAHandle = false; - } - } - switch (eventType) - { - case EventType.MouseDown: - if (Event.current.button == 0 && Event.current.modifiers != EventModifiers.Alt) - { - if (dstToHandle == 0 && dstToMouse < dstMouseToDragPointStart) - { - dstMouseToDragPointStart = dstToMouse; - GUIUtility.hotControl = id; - handleDragMouseEnd = handleDragMouseStart = Event.current.mousePosition; - handleDragWorldStart = position; - selectedHandleID = id; - inputType = HandleInputType.LMBPress; - } - } - break; - - case EventType.MouseUp: - dstMouseToDragPointStart = float.MaxValue; - if (GUIUtility.hotControl == id && Event.current.button == 0) - { - GUIUtility.hotControl = 0; - selectedHandleID = -1; - Event.current.Use(); - - inputType = HandleInputType.LMBRelease; - - - if (Event.current.mousePosition == handleDragMouseStart) - { - inputType = HandleInputType.LMBClick; - } - } - break; - - case EventType.MouseDrag: - if (GUIUtility.hotControl == id && Event.current.button == 0) - { - handleDragMouseEnd += new Vector2(Event.current.delta.x, -Event.current.delta.y); - Vector3 position2 = Camera.current.WorldToScreenPoint(Handles.matrix.MultiplyPoint(handleDragWorldStart)) - + (Vector3)(handleDragMouseEnd - handleDragMouseStart); - inputType = HandleInputType.LMBDrag; - // Handle can move freely in 3d space - if (space == PathSpace.xyz) - { - position = Handles.matrix.inverse.MultiplyPoint(Camera.current.ScreenToWorldPoint(position2)); - } - // Handle is clamped to xy or xz plane - else - { - position = MouseUtility.GetMouseWorldPosition(space); - } - - GUI.changed = true; - Event.current.Use(); - } - break; - } - } - - switch (eventType) - { - case EventType.Repaint: - Color originalColour = Handles.color; - Handles.color = (isInteractive) ? colours.defaultColour : colours.disabledColour; - - if (id == GUIUtility.hotControl) - { - Handles.color = colours.selectedColour; - } - else if (dstToHandle == 0 && selectedHandleID == -1 && isInteractive) - { - Handles.color = colours.highlightedColour; - } - - - Handles.matrix = Matrix4x4.identity; - Vector3 lookForward = Vector3.up; - Camera cam = Camera.current; - if (cam != null) - { - if (cam.orthographic) - { - lookForward= -cam.transform.forward; - } - else - { - lookForward = (cam.transform.position - position); - } - } - - capFunc(id, screenPosition, Quaternion.LookRotation(lookForward), handleDiameter, EventType.Repaint); - Handles.matrix = cachedMatrix; - - Handles.color = originalColour; - break; - - case EventType.Layout: - Handles.matrix = Matrix4x4.identity; - HandleUtility.AddControl(id, HandleUtility.DistanceToCircle(screenPosition, handleDiameter / 2f)); - Handles.matrix = cachedMatrix; - break; - } - - return position; - } - - public struct HandleColours - { - public Color defaultColour; - public Color highlightedColour; - public Color selectedColour; - public Color disabledColour; - - public HandleColours(Color defaultColour, Color highlightedColour, Color selectedColour, Color disabledColour) - { - this.defaultColour = defaultColour; - this.highlightedColour = highlightedColour; - this.selectedColour = selectedColour; - this.disabledColour = disabledColour; - } - } - - static void AddIDs(int upToIndex) - { - int numIDAtStart = ids.Count; - int numToAdd = (upToIndex - numIDAtStart) + 1; - for (int i = 0; i < numToAdd; i++) - { - string hashString = string.Format("pathhandle({0})", numIDAtStart + i); - int hash = hashString.GetHashCode(); - - int id = GUIUtility.GetControlID(hash, FocusType.Passive); - int numIts = 0; - - // This is a bit of a shot in the dark at fixing a reported bug that I've been unable to reproduce. - // The problem is that multiple handles are being selected when just one is clicked on. - // I assume this is because they're somehow being assigned the same id. - while (idHash.Contains(id)) { - numIts ++; - id += numIts * numIts; - if (numIts > 100) { - Debug.LogError("Failed to generate unique handle id."); - break; - } - } - - idHash.Add(id); - ids.Add(id); - } - } - - static int GetID(int handleIndex) - { - if (handleIndex >= ids.Count) - { - AddIDs(handleIndex); - } - - return ids[handleIndex]; - } - } + public static class PathHandle + { + + public const float extraInputRadius = .005f; + + static Vector2 handleDragMouseStart; + static Vector2 handleDragMouseEnd; + static Vector3 handleDragWorldStart; + + static int selectedHandleID; + static bool mouseIsOverAHandle; + + public enum HandleInputType + { + None, + LMBPress, + LMBClick, + LMBDrag, + LMBRelease, + }; + + static float dstMouseToDragPointStart; + + static List ids; + static HashSet idHash; + + static PathHandle() + { + ids = new List(); + idHash = new HashSet(); + + dstMouseToDragPointStart = float.MaxValue; + } + + public static Vector3 DrawHandle(Vector3 position, PathSpace space, bool isInteractive, float handleDiameter, Handles.CapFunction capFunc, HandleColours colours, out HandleInputType inputType, int handleIndex) + { + int id = GetID(handleIndex); + Vector3 screenPosition = Handles.matrix.MultiplyPoint(position); + Matrix4x4 cachedMatrix = Handles.matrix; + + inputType = HandleInputType.None; + + EventType eventType = Event.current.GetTypeForControl(id); + float handleRadius = handleDiameter / 2f; + float dstToHandle = HandleUtility.DistanceToCircle(position, handleRadius + extraInputRadius); + float dstToMouse = HandleUtility.DistanceToCircle(position, 0); + + // Handle input events + if (isInteractive) + { + // Repaint if mouse is entering/exiting handle (for highlight colour) + if (dstToHandle == 0) + { + if (!mouseIsOverAHandle) + { + HandleUtility.Repaint(); + mouseIsOverAHandle = true; + } + } + else + { + if (mouseIsOverAHandle) + { + HandleUtility.Repaint(); + mouseIsOverAHandle = false; + } + } + switch (eventType) + { + case EventType.MouseDown: + if (Event.current.button == 0 && Event.current.modifiers != EventModifiers.Alt) + { + if (dstToHandle == 0 && dstToMouse < dstMouseToDragPointStart) + { + dstMouseToDragPointStart = dstToMouse; + GUIUtility.hotControl = id; + handleDragMouseEnd = handleDragMouseStart = Event.current.mousePosition; + handleDragWorldStart = position; + selectedHandleID = id; + inputType = HandleInputType.LMBPress; + } + } + break; + + case EventType.MouseUp: + dstMouseToDragPointStart = float.MaxValue; + if (GUIUtility.hotControl == id && Event.current.button == 0) + { + GUIUtility.hotControl = 0; + selectedHandleID = -1; + Event.current.Use(); + + inputType = HandleInputType.LMBRelease; + + + if (Event.current.mousePosition == handleDragMouseStart) + { + inputType = HandleInputType.LMBClick; + } + } + break; + + case EventType.MouseDrag: + if (GUIUtility.hotControl == id && Event.current.button == 0) + { + handleDragMouseEnd += new Vector2(Event.current.delta.x, -Event.current.delta.y); + Vector3 position2 = Camera.current.WorldToScreenPoint(Handles.matrix.MultiplyPoint(handleDragWorldStart)) + + (Vector3)(handleDragMouseEnd - handleDragMouseStart); + inputType = HandleInputType.LMBDrag; + // Handle can move freely in 3d space + if (space == PathSpace.xyz) + { + position = Handles.matrix.inverse.MultiplyPoint(Camera.current.ScreenToWorldPoint(position2)); + } + // Handle is clamped to xy or xz plane + else + { + position = MouseUtility.GetMouseWorldPosition(space); + } + + GUI.changed = true; + Event.current.Use(); + } + break; + } + } + + switch (eventType) + { + case EventType.Repaint: + Color originalColour = Handles.color; + Handles.color = (isInteractive) ? colours.defaultColour : colours.disabledColour; + + if (id == GUIUtility.hotControl) + { + Handles.color = colours.selectedColour; + } + else if (dstToHandle == 0 && selectedHandleID == -1 && isInteractive) + { + Handles.color = colours.highlightedColour; + } + + + Handles.matrix = Matrix4x4.identity; + Vector3 lookForward = Vector3.zero; + Camera cam = Camera.current; + if (cam != null) + { + if (cam.orthographic) + { + lookForward = -cam.transform.forward; + } + else + { + lookForward = (cam.transform.position - position).normalized; + } + } + + if (lookForward == Vector3.zero) { + lookForward = Vector3.forward; + } + + capFunc(id, screenPosition, Quaternion.LookRotation(lookForward), handleDiameter, EventType.Repaint); + Handles.matrix = cachedMatrix; + + Handles.color = originalColour; + break; + + case EventType.Layout: + Handles.matrix = Matrix4x4.identity; + HandleUtility.AddControl(id, HandleUtility.DistanceToCircle(screenPosition, handleDiameter / 2f)); + Handles.matrix = cachedMatrix; + break; + } + + return position; + } + + public struct HandleColours + { + public Color defaultColour; + public Color highlightedColour; + public Color selectedColour; + public Color disabledColour; + + public HandleColours(Color defaultColour, Color highlightedColour, Color selectedColour, Color disabledColour) + { + this.defaultColour = defaultColour; + this.highlightedColour = highlightedColour; + this.selectedColour = selectedColour; + this.disabledColour = disabledColour; + } + } + + static void AddIDs(int upToIndex) + { + int numIDAtStart = ids.Count; + int numToAdd = (upToIndex - numIDAtStart) + 1; + for (int i = 0; i < numToAdd; i++) + { + string hashString = string.Format("pathhandle({0})", numIDAtStart + i); + int hash = hashString.GetHashCode(); + + int id = GUIUtility.GetControlID(hash, FocusType.Passive); + int numIts = 0; + + // This is a bit of a shot in the dark at fixing a reported bug that I've been unable to reproduce. + // The problem is that multiple handles are being selected when just one is clicked on. + // I assume this is because they're somehow being assigned the same id. + while (idHash.Contains(id)) + { + numIts++; + id += numIts * numIts; + if (numIts > 100) + { + Debug.LogError("Failed to generate unique handle id."); + break; + } + } + + idHash.Add(id); + ids.Add(id); + } + } + + static int GetID(int handleIndex) + { + if (handleIndex >= ids.Count) + { + AddIDs(handleIndex); + } + + return ids[handleIndex]; + } + } } \ No newline at end of file diff --git a/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs b/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs index 50b28a2..b16b792 100644 --- a/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs +++ b/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs @@ -6,210 +6,214 @@ namespace PathCreationEditor { - public class ScreenSpacePolyLine - { - const int accuracyMultiplier = 10; - // dont allow vertices to be spaced too far apart, as screenspace-worldspace conversion can then be noticeably off - const float intermediaryThreshold = .2f; - - public readonly List verticesWorld; - // For each point in the polyline, says which bezier segment it belongs to - readonly List vertexToPathSegmentMap; - // Stores the index in the vertices list where the start point of each segment is - readonly int[] segmentStartIndices; - - readonly float pathLengthWorld; - readonly float[] cumululativeLengthWorld; - - Vector2[] points; - - Vector3 prevCamPos; - Quaternion prevCamRot; - bool premCamIsOrtho; - - readonly Transform transform; - readonly Vector3 transformPosition; - readonly Quaternion transformRotation; - readonly Vector3 transformScale; - - public ScreenSpacePolyLine(BezierPath bezierPath, Transform transform, float maxAngleError, float minVertexDst, float accuracy = 1) - { - this.transform = transform; - transformPosition = transform.position; - transformRotation = transform.rotation; - transformScale = transform.localScale; - - // Split path in vertices based on angle error - verticesWorld = new List(); - vertexToPathSegmentMap = new List(); - segmentStartIndices = new int[bezierPath.NumSegments+1]; - - verticesWorld.Add(bezierPath[0]); - vertexToPathSegmentMap.Add(0); - Vector3 prevPointOnPath = bezierPath[0]; - float dstSinceLastVertex = 0; - Vector3 lastAddedPoint = prevPointOnPath; - float dstSinceLastIntermediary = 0; - - for (int segmentIndex = 0; segmentIndex < bezierPath.NumSegments; segmentIndex++) - { - Vector3[] segmentPoints = bezierPath.GetPointsInSegment(segmentIndex); - verticesWorld.Add(segmentPoints[0]); - vertexToPathSegmentMap.Add(segmentIndex); - segmentStartIndices[segmentIndex] = verticesWorld.Count-1; - - prevPointOnPath = segmentPoints[0]; - lastAddedPoint = prevPointOnPath; - dstSinceLastVertex = 0; - dstSinceLastIntermediary = 0; - - float estimatedSegmentLength = CubicBezierUtility.EstimateCurveLength(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3]); - int divisions = Mathf.CeilToInt(estimatedSegmentLength * accuracy * accuracyMultiplier); - float increment = 1f / divisions; - - for (float t = increment; t <= 1; t += increment) - { - Vector3 pointOnPath = CubicBezierUtility.EvaluateCurve(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3], t); - Vector3 nextPointOnPath = CubicBezierUtility.EvaluateCurve(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3], t + increment); - - // angle at current point on path - float localAngle = 180 - MathUtility.MinAngle(prevPointOnPath, pointOnPath, nextPointOnPath); - // angle between the last added vertex, the current point on the path, and the next point on the path - float angleFromPrevVertex = 180 - MathUtility.MinAngle(lastAddedPoint, pointOnPath, nextPointOnPath); - float angleError = Mathf.Max(localAngle, angleFromPrevVertex); - - - if (angleError > maxAngleError && dstSinceLastVertex >= minVertexDst) - { - dstSinceLastVertex = 0; - dstSinceLastIntermediary = 0; - verticesWorld.Add(pointOnPath); - vertexToPathSegmentMap.Add(segmentIndex); - lastAddedPoint = pointOnPath; - } - else - { - if (dstSinceLastIntermediary > intermediaryThreshold) - { - verticesWorld.Add(pointOnPath); - vertexToPathSegmentMap.Add(segmentIndex); - dstSinceLastIntermediary = 0; - } - else - { - dstSinceLastIntermediary += (pointOnPath - prevPointOnPath).magnitude; - } - dstSinceLastVertex += (pointOnPath - prevPointOnPath).magnitude; - } - prevPointOnPath = pointOnPath; - } - } - - segmentStartIndices[bezierPath.NumSegments] = verticesWorld.Count; - - // ensure final point gets added (unless path is closed loop) - if (!bezierPath.IsClosed) - { - verticesWorld.Add(bezierPath[bezierPath.NumPoints - 1]); - } - else - { - verticesWorld.Add(bezierPath[0]); - } - - // Calculate length - cumululativeLengthWorld = new float[verticesWorld.Count]; - for (int i = 0; i < verticesWorld.Count; i++) - { - verticesWorld[i] = MathUtility.TransformPoint(verticesWorld[i], transform, bezierPath.Space); - if (i > 0) { - pathLengthWorld += (verticesWorld[i - 1] - verticesWorld[i]).magnitude; - cumululativeLengthWorld[i] = pathLengthWorld; - } - } - - } - - void ComputeScreenSpace() - { - if (Camera.current.transform.position != prevCamPos || Camera.current.transform.rotation != prevCamRot || Camera.current.orthographic != premCamIsOrtho) - { - points = new Vector2[verticesWorld.Count]; - for (int i = 0; i < verticesWorld.Count; i++) - { - points[i] = HandleUtility.WorldToGUIPoint(verticesWorld[i]); - } - - prevCamPos = Camera.current.transform.position; - prevCamRot = Camera.current.transform.rotation; - premCamIsOrtho = Camera.current.orthographic; - } - } - - public MouseInfo CalculateMouseInfo() - { - ComputeScreenSpace(); - - Vector2 mousePos = Event.current.mousePosition; - float minDst = float.MaxValue; - int closestPolyLineSegmentIndex = 0; - int closestBezierSegmentIndex = 0; - - for (int i = 0; i < points.Length - 1; i++) - { - float dst = HandleUtility.DistancePointToLineSegment(mousePos, points[i], points[i + 1]); - - if (dst < minDst) - { - minDst = dst; - closestPolyLineSegmentIndex = i; - closestBezierSegmentIndex = vertexToPathSegmentMap[i]; - } - } - - Vector2 closestPointOnLine = MathUtility.ClosestPointOnLineSegment(mousePos, points[closestPolyLineSegmentIndex], points[closestPolyLineSegmentIndex + 1]); - float dstToPointOnLine = (points[closestPolyLineSegmentIndex] - closestPointOnLine).magnitude; - float percentBetweenVertices = dstToPointOnLine / (points[closestPolyLineSegmentIndex] - points[closestPolyLineSegmentIndex + 1]).magnitude; - Vector3 closestPoint3D = Vector3.Lerp(verticesWorld[closestPolyLineSegmentIndex], verticesWorld[closestPolyLineSegmentIndex + 1], percentBetweenVertices); - - float distanceAlongPathWorld = cumululativeLengthWorld[closestPolyLineSegmentIndex] + Vector3.Distance(verticesWorld[closestPolyLineSegmentIndex], closestPoint3D); - float timeAlongPath = distanceAlongPathWorld / pathLengthWorld; - - // Calculate how far between the current bezier segment the closest point on the line is - - int bezierSegmentStartIndex = segmentStartIndices[closestBezierSegmentIndex]; - int bezierSegmentEndIndex = segmentStartIndices[closestBezierSegmentIndex+1]; - float bezierSegmentLength = cumululativeLengthWorld[bezierSegmentEndIndex] - cumululativeLengthWorld[bezierSegmentStartIndex]; - float distanceAlongBezierSegment = distanceAlongPathWorld - cumululativeLengthWorld[bezierSegmentStartIndex]; - float timeAlongBezierSegment = distanceAlongBezierSegment/bezierSegmentLength; - - return new MouseInfo(minDst, closestPoint3D, distanceAlongPathWorld, timeAlongPath, timeAlongBezierSegment, closestBezierSegmentIndex); - } - - public bool TransformIsOutOfDate() { - return transform.position != transformPosition || transform.rotation != transformRotation || transform.localScale != transformScale; - } - - - public struct MouseInfo - { - public readonly float mouseDstToLine; - public readonly Vector3 closestWorldPointToMouse; - public readonly float distanceAlongPathWorld; - public readonly float timeOnPath; - public readonly float timeOnBezierSegment; - public readonly int closestSegmentIndex; - - - public MouseInfo(float mouseDstToLine, Vector3 closestWorldPointToMouse, float distanceAlongPathWorld, float timeOnPath, float timeOnBezierSegment, int closestSegmentIndex) - { - this.mouseDstToLine = mouseDstToLine; - this.closestWorldPointToMouse = closestWorldPointToMouse; - this.distanceAlongPathWorld = distanceAlongPathWorld; - this.timeOnPath = timeOnPath; - this.timeOnBezierSegment = timeOnBezierSegment; - this.closestSegmentIndex = closestSegmentIndex; - } - } - } + public class ScreenSpacePolyLine + { + const int accuracyMultiplier = 10; + // dont allow vertices to be spaced too far apart, as screenspace-worldspace conversion can then be noticeably off + const float intermediaryThreshold = .2f; + + public readonly List verticesWorld; + // For each point in the polyline, says which bezier segment it belongs to + readonly List vertexToPathSegmentMap; + // Stores the index in the vertices list where the start point of each segment is + readonly int[] segmentStartIndices; + + readonly float pathLengthWorld; + readonly float[] cumululativeLengthWorld; + + Vector2[] points; + + Vector3 prevCamPos; + Quaternion prevCamRot; + bool prevCamIsOrtho; + + readonly Transform transform; + readonly Vector3 transformPosition; + readonly Quaternion transformRotation; + readonly Vector3 transformScale; + + public ScreenSpacePolyLine(BezierPath bezierPath, Transform transform, float maxAngleError, float minVertexDst, float accuracy = 1) + { + this.transform = transform; + transformPosition = transform.position; + transformRotation = transform.rotation; + transformScale = transform.localScale; + + // Split path in vertices based on angle error + verticesWorld = new List(); + vertexToPathSegmentMap = new List(); + segmentStartIndices = new int[bezierPath.NumSegments + 1]; + + verticesWorld.Add(bezierPath[0]); + vertexToPathSegmentMap.Add(0); + Vector3 prevPointOnPath = bezierPath[0]; + float dstSinceLastVertex = 0; + Vector3 lastAddedPoint = prevPointOnPath; + float dstSinceLastIntermediary = 0; + + for (int segmentIndex = 0; segmentIndex < bezierPath.NumSegments; segmentIndex++) + { + Vector3[] segmentPoints = bezierPath.GetPointsInSegment(segmentIndex); + verticesWorld.Add(segmentPoints[0]); + vertexToPathSegmentMap.Add(segmentIndex); + segmentStartIndices[segmentIndex] = verticesWorld.Count - 1; + + prevPointOnPath = segmentPoints[0]; + lastAddedPoint = prevPointOnPath; + dstSinceLastVertex = 0; + dstSinceLastIntermediary = 0; + + float estimatedSegmentLength = CubicBezierUtility.EstimateCurveLength(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3]); + int divisions = Mathf.CeilToInt(estimatedSegmentLength * accuracy * accuracyMultiplier); + float increment = 1f / divisions; + + for (float t = increment; t <= 1; t += increment) + { + Vector3 pointOnPath = CubicBezierUtility.EvaluateCurve(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3], t); + Vector3 nextPointOnPath = CubicBezierUtility.EvaluateCurve(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3], t + increment); + + // angle at current point on path + float localAngle = 180 - MathUtility.MinAngle(prevPointOnPath, pointOnPath, nextPointOnPath); + // angle between the last added vertex, the current point on the path, and the next point on the path + float angleFromPrevVertex = 180 - MathUtility.MinAngle(lastAddedPoint, pointOnPath, nextPointOnPath); + float angleError = Mathf.Max(localAngle, angleFromPrevVertex); + + + if (angleError > maxAngleError && dstSinceLastVertex >= minVertexDst) + { + dstSinceLastVertex = 0; + dstSinceLastIntermediary = 0; + verticesWorld.Add(pointOnPath); + vertexToPathSegmentMap.Add(segmentIndex); + lastAddedPoint = pointOnPath; + } + else + { + if (dstSinceLastIntermediary > intermediaryThreshold) + { + verticesWorld.Add(pointOnPath); + vertexToPathSegmentMap.Add(segmentIndex); + dstSinceLastIntermediary = 0; + } + else + { + dstSinceLastIntermediary += (pointOnPath - prevPointOnPath).magnitude; + } + dstSinceLastVertex += (pointOnPath - prevPointOnPath).magnitude; + } + prevPointOnPath = pointOnPath; + } + } + + segmentStartIndices[bezierPath.NumSegments] = verticesWorld.Count; + + // ensure final point gets added (unless path is closed loop) + if (!bezierPath.IsClosed) + { + verticesWorld.Add(bezierPath[bezierPath.NumPoints - 1]); + } + else + { + verticesWorld.Add(bezierPath[0]); + } + + // Calculate length + cumululativeLengthWorld = new float[verticesWorld.Count]; + for (int i = 0; i < verticesWorld.Count; i++) + { + verticesWorld[i] = MathUtility.TransformPoint(verticesWorld[i], transform, bezierPath.Space); + if (i > 0) + { + pathLengthWorld += (verticesWorld[i - 1] - verticesWorld[i]).magnitude; + cumululativeLengthWorld[i] = pathLengthWorld; + } + } + + } + + void ComputeScreenSpace() + { + if (Camera.current.transform.position != prevCamPos || Camera.current.transform.rotation != prevCamRot || Camera.current.orthographic != prevCamIsOrtho) + { + points = new Vector2[verticesWorld.Count]; + for (int i = 0; i < verticesWorld.Count; i++) + { + points[i] = HandleUtility.WorldToGUIPoint(verticesWorld[i]); + } + + prevCamPos = Camera.current.transform.position; + prevCamRot = Camera.current.transform.rotation; + prevCamIsOrtho = Camera.current.orthographic; + } + } + + public MouseInfo CalculateMouseInfo() + { + ComputeScreenSpace(); + + Vector2 mousePos = Event.current.mousePosition; + float minDst = float.MaxValue; + int closestPolyLineSegmentIndex = 0; + int closestBezierSegmentIndex = 0; + + for (int i = 0; i < points.Length - 1; i++) + { + float dst = HandleUtility.DistancePointToLineSegment(mousePos, points[i], points[i + 1]); + + if (dst < minDst) + { + minDst = dst; + closestPolyLineSegmentIndex = i; + closestBezierSegmentIndex = vertexToPathSegmentMap[i]; + } + } + + Vector2 closestPointOnLine = MathUtility.ClosestPointOnLineSegment(mousePos, points[closestPolyLineSegmentIndex], points[closestPolyLineSegmentIndex + 1]); + float dstToPointOnLine = (points[closestPolyLineSegmentIndex] - closestPointOnLine).magnitude; + + float d = (points[closestPolyLineSegmentIndex] - points[closestPolyLineSegmentIndex + 1]).magnitude; + float percentBetweenVertices = (d == 0) ? 0 : dstToPointOnLine / d; + Vector3 closestPoint3D = Vector3.Lerp(verticesWorld[closestPolyLineSegmentIndex], verticesWorld[closestPolyLineSegmentIndex + 1], percentBetweenVertices); + + float distanceAlongPathWorld = cumululativeLengthWorld[closestPolyLineSegmentIndex] + Vector3.Distance(verticesWorld[closestPolyLineSegmentIndex], closestPoint3D); + float timeAlongPath = distanceAlongPathWorld / pathLengthWorld; + + // Calculate how far between the current bezier segment the closest point on the line is + + int bezierSegmentStartIndex = segmentStartIndices[closestBezierSegmentIndex]; + int bezierSegmentEndIndex = segmentStartIndices[closestBezierSegmentIndex + 1]; + float bezierSegmentLength = cumululativeLengthWorld[bezierSegmentEndIndex] - cumululativeLengthWorld[bezierSegmentStartIndex]; + float distanceAlongBezierSegment = distanceAlongPathWorld - cumululativeLengthWorld[bezierSegmentStartIndex]; + float timeAlongBezierSegment = distanceAlongBezierSegment / bezierSegmentLength; + + return new MouseInfo(minDst, closestPoint3D, distanceAlongPathWorld, timeAlongPath, timeAlongBezierSegment, closestBezierSegmentIndex); + } + + public bool TransformIsOutOfDate() + { + return transform.position != transformPosition || transform.rotation != transformRotation || transform.localScale != transformScale; + } + + + public struct MouseInfo + { + public readonly float mouseDstToLine; + public readonly Vector3 closestWorldPointToMouse; + public readonly float distanceAlongPathWorld; + public readonly float timeOnPath; + public readonly float timeOnBezierSegment; + public readonly int closestSegmentIndex; + + + public MouseInfo(float mouseDstToLine, Vector3 closestWorldPointToMouse, float distanceAlongPathWorld, float timeOnPath, float timeOnBezierSegment, int closestSegmentIndex) + { + this.mouseDstToLine = mouseDstToLine; + this.closestWorldPointToMouse = closestWorldPointToMouse; + this.distanceAlongPathWorld = distanceAlongPathWorld; + this.timeOnPath = timeOnPath; + this.timeOnBezierSegment = timeOnBezierSegment; + this.closestSegmentIndex = closestSegmentIndex; + } + } + } } \ No newline at end of file diff --git a/Assets/PathCreator/Core/Editor/PathEditor.cs b/Assets/PathCreator/Core/Editor/PathEditor.cs index e4dbd8b..2897759 100644 --- a/Assets/PathCreator/Core/Editor/PathEditor.cs +++ b/Assets/PathCreator/Core/Editor/PathEditor.cs @@ -5,741 +5,874 @@ using UnityEditor.IMGUI.Controls; using UnityEngine; -namespace PathCreationEditor { - /// Editor class for the creation of Bezier and Vertex paths - - [CustomEditor (typeof (PathCreator))] - public class PathEditor : Editor { - - #region Fields - - // Interaction: - const float segmentSelectDistanceThreshold = 10f; - const float screenPolylineMaxAngleError = .3f; - const float screenPolylineMinVertexDst = .01f; - - // Help messages: - const string helpInfo = "Shift-click to add or insert new points. Control-click to delete points. For more detailed infomation, please refer to the documentation."; - static readonly string[] spaceNames = { "3D (xyz)", "2D (xy)", "Top-down (xz)" }; - static readonly string[] tabNames = { "Bézier Path", "Vertex Path" }; - const string constantSizeTooltip = "If true, anchor and control points will keep a constant size when zooming in the editor."; - - // Display - const int inspectorSectionSpacing = 10; - const float constantHandleScale = .01f; - const float normalsSpacing = .2f; - GUIStyle boldFoldoutStyle; - - // References: - PathCreator creator; - Editor globalDisplaySettingsEditor; - ScreenSpacePolyLine screenSpaceLine; - ScreenSpacePolyLine.MouseInfo pathMouseInfo; - GlobalDisplaySettings globalDisplaySettings; - PathHandle.HandleColours splineAnchorColours; - PathHandle.HandleColours splineControlColours; - Dictionary capFunctions; - ArcHandle anchorAngleHandle = new ArcHandle (); - VertexPath normalsVertexPath; - - // State variables: - int selectedSegmentIndex; - int draggingHandleIndex; - int mouseOverHandleIndex; - int handleIndexToDisplayAsTransform; - - bool shiftLastFrame; - bool hasUpdatedScreenSpaceLine; - bool hasUpdatedNormalsVertexPath; - bool editingNormalsOld; - - Vector3 transformPos; - Vector3 transformScale; - Quaternion transformRot; - - Color handlesStartCol; - - // Constants - const int bezierPathTab = 0; - const int vertexPathTab = 1; - - #endregion - - #region Inspectors - - public override void OnInspectorGUI () { - // Initialize GUI styles - if (boldFoldoutStyle == null) { - boldFoldoutStyle = new GUIStyle (EditorStyles.foldout); - boldFoldoutStyle.fontStyle = FontStyle.Bold; - } - - Undo.RecordObject (creator, "Path settings changed"); - - // Draw Bezier and Vertex tabs - int tabIndex = GUILayout.Toolbar (data.tabIndex, tabNames); - if (tabIndex != data.tabIndex) { - data.tabIndex = tabIndex; - TabChanged (); - } - - // Draw inspector for active tab - switch (data.tabIndex) { - case bezierPathTab: - DrawBezierPathInspector (); - break; - case vertexPathTab: - DrawVertexPathInspector (); - break; - } - - // Notify of undo/redo that might modify the path - if (Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed") { - data.PathModifiedByUndo (); - } - } - - void DrawBezierPathInspector () { - using (var check = new EditorGUI.ChangeCheckScope ()) { - // Path options: - data.showPathOptions = EditorGUILayout.Foldout (data.showPathOptions, new GUIContent ("Bézier Path Options"), true, boldFoldoutStyle); - if (data.showPathOptions) { - bezierPath.Space = (PathSpace) EditorGUILayout.Popup ("Space", (int) bezierPath.Space, spaceNames); - bezierPath.ControlPointMode = (BezierPath.ControlMode) EditorGUILayout.EnumPopup (new GUIContent ("Control Mode"), bezierPath.ControlPointMode); - if (bezierPath.ControlPointMode == BezierPath.ControlMode.Automatic) { - bezierPath.AutoControlLength = EditorGUILayout.Slider (new GUIContent ("Control Spacing"), bezierPath.AutoControlLength, 0, 1); - } - - bezierPath.IsClosed = EditorGUILayout.Toggle ("Closed Path", bezierPath.IsClosed); - data.showTransformTool = EditorGUILayout.Toggle (new GUIContent ("Enable Transforms"), data.showTransformTool); - - Tools.hidden = !data.showTransformTool; - - // Check if out of bounds (can occur after undo operations) - if (handleIndexToDisplayAsTransform >= bezierPath.NumPoints) { - handleIndexToDisplayAsTransform = -1; - } - - // If a point has been selected - if (handleIndexToDisplayAsTransform != -1) { - EditorGUILayout.LabelField ("Selected Point:"); - - using (new EditorGUI.IndentLevelScope ()) { - var currentPosition = creator.bezierPath[handleIndexToDisplayAsTransform]; - var newPosition = EditorGUILayout.Vector3Field ("Position", currentPosition); - if (newPosition != currentPosition) { - Undo.RecordObject (creator, "Move point"); - creator.bezierPath.MovePoint (handleIndexToDisplayAsTransform, newPosition); - } - // Don't draw the angle field if we aren't selecting an anchor point/not in 3d space - if (handleIndexToDisplayAsTransform % 3 == 0 && creator.bezierPath.Space == PathSpace.xyz) { - var anchorIndex = handleIndexToDisplayAsTransform / 3; - var currentAngle = creator.bezierPath.GetAnchorNormalAngle (anchorIndex); - var newAngle = EditorGUILayout.FloatField ("Angle", currentAngle); - if (newAngle != currentAngle) { - Undo.RecordObject (creator, "Set Angle"); - creator.bezierPath.SetAnchorNormalAngle (anchorIndex, newAngle); - } - } - } - } - - if (data.showTransformTool & (handleIndexToDisplayAsTransform == -1)) { - if (GUILayout.Button ("Centre Transform")) { - - Vector3 worldCentre = bezierPath.CalculateBoundsWithTransform (creator.transform).center; - Vector3 transformPos = creator.transform.position; - if (bezierPath.Space == PathSpace.xy) { - transformPos = new Vector3 (transformPos.x, transformPos.y, 0); - } else if (bezierPath.Space == PathSpace.xz) { - transformPos = new Vector3 (transformPos.x, 0, transformPos.z); - } - Vector3 worldCentreToTransform = transformPos - worldCentre; - - if (worldCentre != creator.transform.position) { - //Undo.RecordObject (creator, "Centralize Transform"); - if (worldCentreToTransform != Vector3.zero) { - Vector3 localCentreToTransform = MathUtility.InverseTransformVector (worldCentreToTransform, creator.transform, bezierPath.Space); - for (int i = 0; i < bezierPath.NumPoints; i++) { - bezierPath.SetPoint (i, bezierPath.GetPoint (i) + localCentreToTransform, true); - } - } - - creator.transform.position = worldCentre; - bezierPath.NotifyPathModified (); - } - } - } - - if (GUILayout.Button ("Reset Path")) { - Undo.RecordObject (creator, "Reset Path"); - bool in2DEditorMode = EditorSettings.defaultBehaviorMode == EditorBehaviorMode.Mode2D; - data.ResetBezierPath (creator.transform.position, in2DEditorMode); - EditorApplication.QueuePlayerLoopUpdate (); - } - - GUILayout.Space (inspectorSectionSpacing); - } - - data.showNormals = EditorGUILayout.Foldout (data.showNormals, new GUIContent ("Normals Options"), true, boldFoldoutStyle); - if (data.showNormals) { - bezierPath.FlipNormals = EditorGUILayout.Toggle (new GUIContent ("Flip Normals"), bezierPath.FlipNormals); - if (bezierPath.Space == PathSpace.xyz) { - bezierPath.GlobalNormalsAngle = EditorGUILayout.Slider (new GUIContent ("Global Angle"), bezierPath.GlobalNormalsAngle, 0, 360); - - if (GUILayout.Button ("Reset Normals")) { - Undo.RecordObject (creator, "Reset Normals"); - bezierPath.FlipNormals = false; - bezierPath.ResetNormalAngles (); - } - } - GUILayout.Space (inspectorSectionSpacing); - } - - // Editor display options - data.showDisplayOptions = EditorGUILayout.Foldout (data.showDisplayOptions, new GUIContent ("Display Options"), true, boldFoldoutStyle); - if (data.showDisplayOptions) { - data.showPathBounds = GUILayout.Toggle (data.showPathBounds, new GUIContent ("Show Path Bounds")); - data.showPerSegmentBounds = GUILayout.Toggle (data.showPerSegmentBounds, new GUIContent ("Show Segment Bounds")); - data.displayAnchorPoints = GUILayout.Toggle (data.displayAnchorPoints, new GUIContent ("Show Anchor Points")); - if (!(bezierPath.ControlPointMode == BezierPath.ControlMode.Automatic && globalDisplaySettings.hideAutoControls)) { - data.displayControlPoints = GUILayout.Toggle (data.displayControlPoints, new GUIContent ("Show Control Points")); - } - data.keepConstantHandleSize = GUILayout.Toggle (data.keepConstantHandleSize, new GUIContent ("Constant Point Size", constantSizeTooltip)); - data.bezierHandleScale = Mathf.Max (0, EditorGUILayout.FloatField (new GUIContent ("Handle Scale"), data.bezierHandleScale)); - DrawGlobalDisplaySettingsInspector (); - } - - if (check.changed) { - SceneView.RepaintAll (); - EditorApplication.QueuePlayerLoopUpdate (); - } - } - } - - void DrawVertexPathInspector () { - - GUILayout.Space (inspectorSectionSpacing); - EditorGUILayout.LabelField ("Vertex count: " + creator.path.NumPoints); - GUILayout.Space (inspectorSectionSpacing); - - data.showVertexPathOptions = EditorGUILayout.Foldout (data.showVertexPathOptions, new GUIContent ("Vertex Path Options"), true, boldFoldoutStyle); - if (data.showVertexPathOptions) { - using (var check = new EditorGUI.ChangeCheckScope ()) { - data.vertexPathMaxAngleError = EditorGUILayout.Slider (new GUIContent ("Max Angle Error"), data.vertexPathMaxAngleError, 0, 45); - data.vertexPathMinVertexSpacing = EditorGUILayout.Slider (new GUIContent ("Min Vertex Dst"), data.vertexPathMinVertexSpacing, 0, 1); - - GUILayout.Space (inspectorSectionSpacing); - if (check.changed) { - data.VertexPathSettingsChanged (); - SceneView.RepaintAll (); - EditorApplication.QueuePlayerLoopUpdate (); - } - } - } - - data.showVertexPathDisplayOptions = EditorGUILayout.Foldout (data.showVertexPathDisplayOptions, new GUIContent ("Display Options"), true, boldFoldoutStyle); - if (data.showVertexPathDisplayOptions) { - using (var check = new EditorGUI.ChangeCheckScope ()) { - data.showNormalsInVertexMode = GUILayout.Toggle (data.showNormalsInVertexMode, new GUIContent ("Show Normals")); - data.showBezierPathInVertexMode = GUILayout.Toggle (data.showBezierPathInVertexMode, new GUIContent ("Show Bezier Path")); - - if (check.changed) { - SceneView.RepaintAll (); - EditorApplication.QueuePlayerLoopUpdate (); - } - } - DrawGlobalDisplaySettingsInspector (); - } - } - - void DrawGlobalDisplaySettingsInspector () { - using (var check = new EditorGUI.ChangeCheckScope ()) { - data.globalDisplaySettingsFoldout = EditorGUILayout.InspectorTitlebar (data.globalDisplaySettingsFoldout, globalDisplaySettings); - if (data.globalDisplaySettingsFoldout) { - CreateCachedEditor (globalDisplaySettings, null, ref globalDisplaySettingsEditor); - globalDisplaySettingsEditor.OnInspectorGUI (); - } - if (check.changed) { - UpdateGlobalDisplaySettings (); - SceneView.RepaintAll (); - } - } - } - - #endregion - - #region Scene GUI - - void OnSceneGUI () { - if (!globalDisplaySettings.visibleBehindObjects) { - Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual; - } - - EventType eventType = Event.current.type; - - using (var check = new EditorGUI.ChangeCheckScope ()) { - handlesStartCol = Handles.color; - switch (data.tabIndex) { - case bezierPathTab: - if (eventType != EventType.Repaint && eventType != EventType.Layout) { - ProcessBezierPathInput (Event.current); - } - - DrawBezierPathSceneEditor (); - break; - case vertexPathTab: - if (eventType == EventType.Repaint) { - DrawVertexPathSceneEditor (); - } - break; - } - - // Don't allow clicking over empty space to deselect the object - if (eventType == EventType.Layout) { - HandleUtility.AddDefaultControl (0); - } - - if (check.changed) { - EditorApplication.QueuePlayerLoopUpdate (); - } - } - - SetTransformState (); - } - - void DrawVertexPathSceneEditor () { - - Color bezierCol = globalDisplaySettings.bezierPath; - bezierCol.a *= .5f; - - if (data.showBezierPathInVertexMode) { - for (int i = 0; i < bezierPath.NumSegments; i++) { - Vector3[] points = bezierPath.GetPointsInSegment (i); - for (int j = 0; j < points.Length; j++) { - points[j] = MathUtility.TransformPoint (points[j], creator.transform, bezierPath.Space); - } - Handles.DrawBezier (points[0], points[3], points[1], points[2], bezierCol, null, 2); - } - } - - Handles.color = globalDisplaySettings.vertexPath; - - for (int i = 0; i < creator.path.NumPoints; i++) { - int nextIndex = (i + 1) % creator.path.NumPoints; - if (nextIndex != 0 || bezierPath.IsClosed) { - Handles.DrawLine (creator.path.GetPoint (i), creator.path.GetPoint (nextIndex)); - } - } - - if (data.showNormalsInVertexMode) { - Handles.color = globalDisplaySettings.normals; - Vector3[] normalLines = new Vector3[creator.path.NumPoints * 2]; - for (int i = 0; i < creator.path.NumPoints; i++) { - normalLines[i * 2] = creator.path.GetPoint (i); - normalLines[i * 2 + 1] = creator.path.GetPoint (i) + creator.path.localNormals[i] * globalDisplaySettings.normalsLength; - } - Handles.DrawLines (normalLines); - } - } - - void ProcessBezierPathInput (Event e) { - // Find which handle mouse is over. Start by looking at previous handle index first, as most likely to still be closest to mouse - int previousMouseOverHandleIndex = (mouseOverHandleIndex == -1) ? 0 : mouseOverHandleIndex; - mouseOverHandleIndex = -1; - for (int i = 0; i < bezierPath.NumPoints; i += 3) { - - int handleIndex = (previousMouseOverHandleIndex + i) % bezierPath.NumPoints; - float handleRadius = GetHandleDiameter (globalDisplaySettings.anchorSize * data.bezierHandleScale, bezierPath[handleIndex]) / 2f; - Vector3 pos = MathUtility.TransformPoint (bezierPath[handleIndex], creator.transform, bezierPath.Space); - float dst = HandleUtility.DistanceToCircle (pos, handleRadius); - if (dst == 0) { - mouseOverHandleIndex = handleIndex; - break; - } - } - - // Shift-left click (when mouse not over a handle) to split or add segment - if (mouseOverHandleIndex == -1) { - if (e.type == EventType.MouseDown && e.button == 0 && e.shift) { - UpdatePathMouseInfo (); - // Insert point along selected segment - if (selectedSegmentIndex != -1 && selectedSegmentIndex < bezierPath.NumSegments) { - Vector3 newPathPoint = pathMouseInfo.closestWorldPointToMouse; - newPathPoint = MathUtility.InverseTransformPoint (newPathPoint, creator.transform, bezierPath.Space); - Undo.RecordObject (creator, "Split segment"); - bezierPath.SplitSegment (newPathPoint, selectedSegmentIndex, pathMouseInfo.timeOnBezierSegment); - } - // If path is not a closed loop, add new point on to the end of the path - else if (!bezierPath.IsClosed) - { - // If control/command are held down, the point gets pre-pended, so we want to check distance - // to the endpoint we are adding to - var pointIdx = e.control || e.command ? 0 : bezierPath.NumPoints - 1; - // insert new point at same dst from scene camera as the point that comes before it (for a 3d path) - var endPointLocal = bezierPath[pointIdx]; - var endPointGlobal = - MathUtility.TransformPoint(endPointLocal, creator.transform, bezierPath.Space); - var distanceCameraToEndpoint = (Camera.current.transform.position - endPointGlobal).magnitude; - var newPointGlobal = - MouseUtility.GetMouseWorldPosition (bezierPath.Space, distanceCameraToEndpoint); - var newPointLocal = - MathUtility.InverseTransformPoint (newPointGlobal, creator.transform, bezierPath.Space); - - Undo.RecordObject (creator, "Add segment"); - if (e.control || e.command) { - bezierPath.AddSegmentToStart (newPointLocal); - } else { - bezierPath.AddSegmentToEnd (newPointLocal); - } - - } - - } - } - - // Control click or backspace/delete to remove point - if (e.keyCode == KeyCode.Backspace || e.keyCode == KeyCode.Delete || ((e.control || e.command) && e.type == EventType.MouseDown && e.button == 0)) { - - if (mouseOverHandleIndex != -1) { - Undo.RecordObject (creator, "Delete segment"); - bezierPath.DeleteSegment (mouseOverHandleIndex); - if (mouseOverHandleIndex == handleIndexToDisplayAsTransform) { - handleIndexToDisplayAsTransform = -1; - } - mouseOverHandleIndex = -1; - Repaint (); - } - } - - // Holding shift and moving mouse (but mouse not over a handle/dragging a handle) - if (draggingHandleIndex == -1 && mouseOverHandleIndex == -1) { - bool shiftDown = e.shift && !shiftLastFrame; - if (shiftDown || ((e.type == EventType.MouseMove || e.type == EventType.MouseDrag) && e.shift)) { - - UpdatePathMouseInfo (); - - if (pathMouseInfo.mouseDstToLine < segmentSelectDistanceThreshold) { - if (pathMouseInfo.closestSegmentIndex != selectedSegmentIndex) { - selectedSegmentIndex = pathMouseInfo.closestSegmentIndex; - HandleUtility.Repaint (); - } - } else { - selectedSegmentIndex = -1; - HandleUtility.Repaint (); - } - - } - } - - shiftLastFrame = e.shift; - - } - - void DrawBezierPathSceneEditor () { - - bool displayControlPoints = data.displayControlPoints && (bezierPath.ControlPointMode != BezierPath.ControlMode.Automatic || !globalDisplaySettings.hideAutoControls); - Bounds bounds = bezierPath.CalculateBoundsWithTransform (creator.transform); - - if (Event.current.type == EventType.Repaint) { - for (int i = 0; i < bezierPath.NumSegments; i++) { - Vector3[] points = bezierPath.GetPointsInSegment (i); - for (int j = 0; j < points.Length; j++) { - points[j] = MathUtility.TransformPoint (points[j], creator.transform, bezierPath.Space); - } - - if (data.showPerSegmentBounds) { - Bounds segmentBounds = CubicBezierUtility.CalculateSegmentBounds (points[0], points[1], points[2], points[3]); - Handles.color = globalDisplaySettings.segmentBounds; - Handles.DrawWireCube (segmentBounds.center, segmentBounds.size); - } - - // Draw lines between control points - if (displayControlPoints) { - Handles.color = (bezierPath.ControlPointMode == BezierPath.ControlMode.Automatic) ? globalDisplaySettings.handleDisabled : globalDisplaySettings.controlLine; - Handles.DrawLine (points[1], points[0]); - Handles.DrawLine (points[2], points[3]); - } - - // Draw path - bool highlightSegment = (i == selectedSegmentIndex && Event.current.shift && draggingHandleIndex == -1 && mouseOverHandleIndex == -1); - Color segmentCol = (highlightSegment) ? globalDisplaySettings.highlightedPath : globalDisplaySettings.bezierPath; - Handles.DrawBezier (points[0], points[3], points[1], points[2], segmentCol, null, 2); - } - - if (data.showPathBounds) { - Handles.color = globalDisplaySettings.bounds; - Handles.DrawWireCube (bounds.center, bounds.size); - } - - // Draw normals - if (data.showNormals) { - if (!hasUpdatedNormalsVertexPath) { - normalsVertexPath = new VertexPath (bezierPath, creator.transform, normalsSpacing); - hasUpdatedNormalsVertexPath = true; - } - - if (editingNormalsOld != data.showNormals) { - editingNormalsOld = data.showNormals; - Repaint (); - } - - Vector3[] normalLines = new Vector3[normalsVertexPath.NumPoints * 2]; - Handles.color = globalDisplaySettings.normals; - for (int i = 0; i < normalsVertexPath.NumPoints; i++) { - normalLines[i * 2] = normalsVertexPath.GetPoint (i); - normalLines[i * 2 + 1] = normalsVertexPath.GetPoint (i) + normalsVertexPath.GetNormal (i) * globalDisplaySettings.normalsLength; - } - Handles.DrawLines (normalLines); - } - } - - if (data.displayAnchorPoints) { - for (int i = 0; i < bezierPath.NumPoints; i += 3) { - DrawHandle (i); - } - } - if (displayControlPoints) { - for (int i = 1; i < bezierPath.NumPoints - 1; i += 3) { - DrawHandle (i); - DrawHandle (i + 1); - } - } - } - - void DrawHandle (int i) { - Vector3 handlePosition = MathUtility.TransformPoint (bezierPath[i], creator.transform, bezierPath.Space); - - float anchorHandleSize = GetHandleDiameter (globalDisplaySettings.anchorSize * data.bezierHandleScale, bezierPath[i]); - float controlHandleSize = GetHandleDiameter (globalDisplaySettings.controlSize * data.bezierHandleScale, bezierPath[i]); - - bool isAnchorPoint = i % 3 == 0; - bool isInteractive = isAnchorPoint || bezierPath.ControlPointMode != BezierPath.ControlMode.Automatic; - float handleSize = (isAnchorPoint) ? anchorHandleSize : controlHandleSize; - bool doTransformHandle = i == handleIndexToDisplayAsTransform; - - PathHandle.HandleColours handleColours = (isAnchorPoint) ? splineAnchorColours : splineControlColours; - if (i == handleIndexToDisplayAsTransform) { - handleColours.defaultColour = (isAnchorPoint) ? globalDisplaySettings.anchorSelected : globalDisplaySettings.controlSelected; - } - var cap = capFunctions[(isAnchorPoint) ? globalDisplaySettings.anchorShape : globalDisplaySettings.controlShape]; - PathHandle.HandleInputType handleInputType; - handlePosition = PathHandle.DrawHandle (handlePosition, bezierPath.Space, isInteractive, handleSize, cap, handleColours, out handleInputType, i); - - if (doTransformHandle) { - // Show normals rotate tool - if (data.showNormals && Tools.current == Tool.Rotate && isAnchorPoint && bezierPath.Space == PathSpace.xyz) { - Handles.color = handlesStartCol; - - int attachedControlIndex = (i == bezierPath.NumPoints - 1) ? i - 1 : i + 1; - Vector3 dir = (bezierPath[attachedControlIndex] - handlePosition).normalized; - float handleRotOffset = (360 + bezierPath.GlobalNormalsAngle) % 360; - anchorAngleHandle.radius = handleSize * 3; - anchorAngleHandle.angle = handleRotOffset + bezierPath.GetAnchorNormalAngle (i / 3); - Vector3 handleDirection = Vector3.Cross (dir, Vector3.up); - Matrix4x4 handleMatrix = Matrix4x4.TRS ( - handlePosition, - Quaternion.LookRotation (handleDirection, dir), - Vector3.one - ); - - using (new Handles.DrawingScope (handleMatrix)) { - // draw the handle - EditorGUI.BeginChangeCheck (); - anchorAngleHandle.DrawHandle (); - if (EditorGUI.EndChangeCheck ()) { - Undo.RecordObject (creator, "Set angle"); - bezierPath.SetAnchorNormalAngle (i / 3, anchorAngleHandle.angle - handleRotOffset); - } - } - - } else { - handlePosition = Handles.DoPositionHandle (handlePosition, Quaternion.identity); - } - - } - - switch (handleInputType) { - case PathHandle.HandleInputType.LMBDrag: - draggingHandleIndex = i; - handleIndexToDisplayAsTransform = -1; - Repaint (); - break; - case PathHandle.HandleInputType.LMBRelease: - draggingHandleIndex = -1; - handleIndexToDisplayAsTransform = -1; - Repaint (); - break; - case PathHandle.HandleInputType.LMBClick: - draggingHandleIndex = -1; - if (Event.current.shift) { - handleIndexToDisplayAsTransform = -1; // disable move tool if new point added - } else { - if (handleIndexToDisplayAsTransform == i) { - handleIndexToDisplayAsTransform = -1; // disable move tool if clicking on point under move tool - } else { - handleIndexToDisplayAsTransform = i; - } - } - Repaint (); - break; - case PathHandle.HandleInputType.LMBPress: - if (handleIndexToDisplayAsTransform != i) { - handleIndexToDisplayAsTransform = -1; - Repaint (); - } - break; - } - - Vector3 localHandlePosition = MathUtility.InverseTransformPoint (handlePosition, creator.transform, bezierPath.Space); - - if (bezierPath[i] != localHandlePosition) { - Undo.RecordObject (creator, "Move point"); - bezierPath.MovePoint (i, localHandlePosition); - - } - - } - - #endregion - - #region Internal methods - - void OnDisable () { - Tools.hidden = false; - } - - void OnEnable () { - creator = (PathCreator) target; - bool in2DEditorMode = EditorSettings.defaultBehaviorMode == EditorBehaviorMode.Mode2D; - creator.InitializeEditorData (in2DEditorMode); - - data.bezierCreated -= ResetState; - data.bezierCreated += ResetState; - Undo.undoRedoPerformed -= OnUndoRedo; - Undo.undoRedoPerformed += OnUndoRedo; - - LoadDisplaySettings (); - UpdateGlobalDisplaySettings (); - ResetState (); - SetTransformState (true); - } - - void SetTransformState (bool initialize = false) { - Transform t = creator.transform; - if (!initialize) { - if (transformPos != t.position || t.localScale != transformScale || t.rotation != transformRot) { - data.PathTransformed (); - } - } - transformPos = t.position; - transformScale = t.localScale; - transformRot = t.rotation; - } - - void OnUndoRedo () { - hasUpdatedScreenSpaceLine = false; - hasUpdatedNormalsVertexPath = false; - selectedSegmentIndex = -1; - - Repaint (); - } - - void TabChanged () { - SceneView.RepaintAll (); - RepaintUnfocusedSceneViews (); - } - - void LoadDisplaySettings () { - globalDisplaySettings = GlobalDisplaySettings.Load (); - - capFunctions = new Dictionary (); - capFunctions.Add (GlobalDisplaySettings.HandleType.Circle, Handles.CylinderHandleCap); - capFunctions.Add (GlobalDisplaySettings.HandleType.Sphere, Handles.SphereHandleCap); - capFunctions.Add (GlobalDisplaySettings.HandleType.Square, Handles.CubeHandleCap); - } - - void UpdateGlobalDisplaySettings () { - var gds = globalDisplaySettings; - splineAnchorColours = new PathHandle.HandleColours (gds.anchor, gds.anchorHighlighted, gds.anchorSelected, gds.handleDisabled); - splineControlColours = new PathHandle.HandleColours (gds.control, gds.controlHighlighted, gds.controlSelected, gds.handleDisabled); - - anchorAngleHandle.fillColor = new Color (1, 1, 1, .05f); - anchorAngleHandle.wireframeColor = Color.grey; - anchorAngleHandle.radiusHandleColor = Color.clear; - anchorAngleHandle.angleHandleColor = Color.white; - } - - void ResetState () { - selectedSegmentIndex = -1; - draggingHandleIndex = -1; - mouseOverHandleIndex = -1; - handleIndexToDisplayAsTransform = -1; - hasUpdatedScreenSpaceLine = false; - hasUpdatedNormalsVertexPath = false; - - bezierPath.OnModified -= OnPathModifed; - bezierPath.OnModified += OnPathModifed; - - SceneView.RepaintAll (); - EditorApplication.QueuePlayerLoopUpdate (); - } - - void OnPathModifed () { - hasUpdatedScreenSpaceLine = false; - hasUpdatedNormalsVertexPath = false; - - RepaintUnfocusedSceneViews (); - } - - void RepaintUnfocusedSceneViews () { - // If multiple scene views are open, repaint those which do not have focus. - if (SceneView.sceneViews.Count > 1) { - foreach (SceneView sv in SceneView.sceneViews) { - if (EditorWindow.focusedWindow != (EditorWindow) sv) { - sv.Repaint (); - } - } - } - } - - void UpdatePathMouseInfo () { - - if (!hasUpdatedScreenSpaceLine || (screenSpaceLine != null && screenSpaceLine.TransformIsOutOfDate ())) { - screenSpaceLine = new ScreenSpacePolyLine (bezierPath, creator.transform, screenPolylineMaxAngleError, screenPolylineMinVertexDst); - hasUpdatedScreenSpaceLine = true; - } - pathMouseInfo = screenSpaceLine.CalculateMouseInfo (); - } - - float GetHandleDiameter (float diameter, Vector3 handlePosition) { - float scaledDiameter = diameter * constantHandleScale; - if (data.keepConstantHandleSize) { - scaledDiameter *= HandleUtility.GetHandleSize (handlePosition) * 2.5f; - } - return scaledDiameter; - } - - BezierPath bezierPath { - get { - return data.bezierPath; - } - } - - PathCreatorData data { - get { - return creator.EditorData; - } - } - - bool editingNormals { - get { - return Tools.current == Tool.Rotate && handleIndexToDisplayAsTransform % 3 == 0 && bezierPath.Space == PathSpace.xyz; - } - } - - #endregion - - } +namespace PathCreationEditor +{ + /// Editor class for the creation of Bezier and Vertex paths + + [CustomEditor(typeof(PathCreator))] + public class PathEditor : Editor + { + + #region Fields + + // Interaction: + const float segmentSelectDistanceThreshold = 10f; + const float screenPolylineMaxAngleError = .3f; + const float screenPolylineMinVertexDst = .01f; + + // Help messages: + const string helpInfo = "Shift-click to add or insert new points. Control-click to delete points. For more detailed infomation, please refer to the documentation."; + static readonly string[] spaceNames = { "3D (xyz)", "2D (xy)", "Top-down (xz)" }; + static readonly string[] tabNames = { "Bézier Path", "Vertex Path" }; + const string constantSizeTooltip = "If true, anchor and control points will keep a constant size when zooming in the editor."; + + // Display + const int inspectorSectionSpacing = 10; + const float constantHandleScale = .01f; + const float normalsSpacing = .2f; + GUIStyle boldFoldoutStyle; + + // References: + PathCreator creator; + Editor globalDisplaySettingsEditor; + ScreenSpacePolyLine screenSpaceLine; + ScreenSpacePolyLine.MouseInfo pathMouseInfo; + GlobalDisplaySettings globalDisplaySettings; + PathHandle.HandleColours splineAnchorColours; + PathHandle.HandleColours splineControlColours; + Dictionary capFunctions; + ArcHandle anchorAngleHandle = new ArcHandle(); + VertexPath normalsVertexPath; + + // State variables: + int selectedSegmentIndex; + int draggingHandleIndex; + int mouseOverHandleIndex; + int handleIndexToDisplayAsTransform; + + bool shiftLastFrame; + bool hasUpdatedScreenSpaceLine; + bool hasUpdatedNormalsVertexPath; + bool editingNormalsOld; + + Vector3 transformPos; + Vector3 transformScale; + Quaternion transformRot; + + Color handlesStartCol; + + // Constants + const int bezierPathTab = 0; + const int vertexPathTab = 1; + + #endregion + + #region Inspectors + + public override void OnInspectorGUI() + { + // Initialize GUI styles + if (boldFoldoutStyle == null) + { + boldFoldoutStyle = new GUIStyle(EditorStyles.foldout); + boldFoldoutStyle.fontStyle = FontStyle.Bold; + } + + Undo.RecordObject(creator, "Path settings changed"); + + // Draw Bezier and Vertex tabs + int tabIndex = GUILayout.Toolbar(data.tabIndex, tabNames); + if (tabIndex != data.tabIndex) + { + data.tabIndex = tabIndex; + TabChanged(); + } + + // Draw inspector for active tab + switch (data.tabIndex) + { + case bezierPathTab: + DrawBezierPathInspector(); + break; + case vertexPathTab: + DrawVertexPathInspector(); + break; + } + + // Notify of undo/redo that might modify the path + if (Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed") + { + data.PathModifiedByUndo(); + } + } + + void DrawBezierPathInspector() + { + using (var check = new EditorGUI.ChangeCheckScope()) + { + // Path options: + data.showPathOptions = EditorGUILayout.Foldout(data.showPathOptions, new GUIContent("Bézier Path Options"), true, boldFoldoutStyle); + if (data.showPathOptions) + { + bezierPath.Space = (PathSpace)EditorGUILayout.Popup("Space", (int)bezierPath.Space, spaceNames); + bezierPath.ControlPointMode = (BezierPath.ControlMode)EditorGUILayout.EnumPopup(new GUIContent("Control Mode"), bezierPath.ControlPointMode); + if (bezierPath.ControlPointMode == BezierPath.ControlMode.Automatic) + { + bezierPath.AutoControlLength = EditorGUILayout.Slider(new GUIContent("Control Spacing"), bezierPath.AutoControlLength, 0, 1); + } + + bezierPath.IsClosed = EditorGUILayout.Toggle("Closed Path", bezierPath.IsClosed); + data.showTransformTool = EditorGUILayout.Toggle(new GUIContent("Enable Transforms"), data.showTransformTool); + + Tools.hidden = !data.showTransformTool; + + // Check if out of bounds (can occur after undo operations) + if (handleIndexToDisplayAsTransform >= bezierPath.NumPoints) + { + handleIndexToDisplayAsTransform = -1; + } + + // If a point has been selected + if (handleIndexToDisplayAsTransform != -1) + { + EditorGUILayout.LabelField("Selected Point:"); + + using (new EditorGUI.IndentLevelScope()) + { + var currentPosition = creator.bezierPath[handleIndexToDisplayAsTransform]; + var newPosition = EditorGUILayout.Vector3Field("Position", currentPosition); + if (newPosition != currentPosition) + { + Undo.RecordObject(creator, "Move point"); + creator.bezierPath.MovePoint(handleIndexToDisplayAsTransform, newPosition); + } + // Don't draw the angle field if we aren't selecting an anchor point/not in 3d space + if (handleIndexToDisplayAsTransform % 3 == 0 && creator.bezierPath.Space == PathSpace.xyz) + { + var anchorIndex = handleIndexToDisplayAsTransform / 3; + var currentAngle = creator.bezierPath.GetAnchorNormalAngle(anchorIndex); + var newAngle = EditorGUILayout.FloatField("Angle", currentAngle); + if (newAngle != currentAngle) + { + Undo.RecordObject(creator, "Set Angle"); + creator.bezierPath.SetAnchorNormalAngle(anchorIndex, newAngle); + } + } + } + } + + if (data.showTransformTool & (handleIndexToDisplayAsTransform == -1)) + { + if (GUILayout.Button("Centre Transform")) + { + + Vector3 worldCentre = bezierPath.CalculateBoundsWithTransform(creator.transform).center; + Vector3 transformPos = creator.transform.position; + if (bezierPath.Space == PathSpace.xy) + { + transformPos = new Vector3(transformPos.x, transformPos.y, 0); + } + else if (bezierPath.Space == PathSpace.xz) + { + transformPos = new Vector3(transformPos.x, 0, transformPos.z); + } + Vector3 worldCentreToTransform = transformPos - worldCentre; + + if (worldCentre != creator.transform.position) + { + //Undo.RecordObject (creator, "Centralize Transform"); + if (worldCentreToTransform != Vector3.zero) + { + Vector3 localCentreToTransform = MathUtility.InverseTransformVector(worldCentreToTransform, creator.transform, bezierPath.Space); + for (int i = 0; i < bezierPath.NumPoints; i++) + { + bezierPath.SetPoint(i, bezierPath.GetPoint(i) + localCentreToTransform, true); + } + } + + creator.transform.position = worldCentre; + bezierPath.NotifyPathModified(); + } + } + } + + if (GUILayout.Button("Reset Path")) + { + Undo.RecordObject(creator, "Reset Path"); + bool in2DEditorMode = EditorSettings.defaultBehaviorMode == EditorBehaviorMode.Mode2D; + data.ResetBezierPath(creator.transform.position, in2DEditorMode); + EditorApplication.QueuePlayerLoopUpdate(); + } + + GUILayout.Space(inspectorSectionSpacing); + } + + data.showNormals = EditorGUILayout.Foldout(data.showNormals, new GUIContent("Normals Options"), true, boldFoldoutStyle); + if (data.showNormals) + { + bezierPath.FlipNormals = EditorGUILayout.Toggle(new GUIContent("Flip Normals"), bezierPath.FlipNormals); + if (bezierPath.Space == PathSpace.xyz) + { + bezierPath.GlobalNormalsAngle = EditorGUILayout.Slider(new GUIContent("Global Angle"), bezierPath.GlobalNormalsAngle, 0, 360); + + if (GUILayout.Button("Reset Normals")) + { + Undo.RecordObject(creator, "Reset Normals"); + bezierPath.FlipNormals = false; + bezierPath.ResetNormalAngles(); + } + } + GUILayout.Space(inspectorSectionSpacing); + } + + // Editor display options + data.showDisplayOptions = EditorGUILayout.Foldout(data.showDisplayOptions, new GUIContent("Display Options"), true, boldFoldoutStyle); + if (data.showDisplayOptions) + { + data.showPathBounds = GUILayout.Toggle(data.showPathBounds, new GUIContent("Show Path Bounds")); + data.showPerSegmentBounds = GUILayout.Toggle(data.showPerSegmentBounds, new GUIContent("Show Segment Bounds")); + data.displayAnchorPoints = GUILayout.Toggle(data.displayAnchorPoints, new GUIContent("Show Anchor Points")); + if (!(bezierPath.ControlPointMode == BezierPath.ControlMode.Automatic && globalDisplaySettings.hideAutoControls)) + { + data.displayControlPoints = GUILayout.Toggle(data.displayControlPoints, new GUIContent("Show Control Points")); + } + data.keepConstantHandleSize = GUILayout.Toggle(data.keepConstantHandleSize, new GUIContent("Constant Point Size", constantSizeTooltip)); + data.bezierHandleScale = Mathf.Max(0, EditorGUILayout.FloatField(new GUIContent("Handle Scale"), data.bezierHandleScale)); + DrawGlobalDisplaySettingsInspector(); + } + + if (check.changed) + { + SceneView.RepaintAll(); + EditorApplication.QueuePlayerLoopUpdate(); + } + } + } + + void DrawVertexPathInspector() + { + + GUILayout.Space(inspectorSectionSpacing); + EditorGUILayout.LabelField("Vertex count: " + creator.path.NumPoints); + GUILayout.Space(inspectorSectionSpacing); + + data.showVertexPathOptions = EditorGUILayout.Foldout(data.showVertexPathOptions, new GUIContent("Vertex Path Options"), true, boldFoldoutStyle); + if (data.showVertexPathOptions) + { + using (var check = new EditorGUI.ChangeCheckScope()) + { + data.vertexPathMaxAngleError = EditorGUILayout.Slider(new GUIContent("Max Angle Error"), data.vertexPathMaxAngleError, 0, 45); + data.vertexPathMinVertexSpacing = EditorGUILayout.Slider(new GUIContent("Min Vertex Dst"), data.vertexPathMinVertexSpacing, 0, 1); + + GUILayout.Space(inspectorSectionSpacing); + if (check.changed) + { + data.VertexPathSettingsChanged(); + SceneView.RepaintAll(); + EditorApplication.QueuePlayerLoopUpdate(); + } + } + } + + data.showVertexPathDisplayOptions = EditorGUILayout.Foldout(data.showVertexPathDisplayOptions, new GUIContent("Display Options"), true, boldFoldoutStyle); + if (data.showVertexPathDisplayOptions) + { + using (var check = new EditorGUI.ChangeCheckScope()) + { + data.showNormalsInVertexMode = GUILayout.Toggle(data.showNormalsInVertexMode, new GUIContent("Show Normals")); + data.showBezierPathInVertexMode = GUILayout.Toggle(data.showBezierPathInVertexMode, new GUIContent("Show Bezier Path")); + + if (check.changed) + { + SceneView.RepaintAll(); + EditorApplication.QueuePlayerLoopUpdate(); + } + } + DrawGlobalDisplaySettingsInspector(); + } + } + + void DrawGlobalDisplaySettingsInspector() + { + using (var check = new EditorGUI.ChangeCheckScope()) + { + data.globalDisplaySettingsFoldout = EditorGUILayout.InspectorTitlebar(data.globalDisplaySettingsFoldout, globalDisplaySettings); + if (data.globalDisplaySettingsFoldout) + { + CreateCachedEditor(globalDisplaySettings, null, ref globalDisplaySettingsEditor); + globalDisplaySettingsEditor.OnInspectorGUI(); + } + if (check.changed) + { + UpdateGlobalDisplaySettings(); + SceneView.RepaintAll(); + } + } + } + + #endregion + + #region Scene GUI + + void OnSceneGUI() + { + if (!globalDisplaySettings.visibleBehindObjects) + { + Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual; + } + + EventType eventType = Event.current.type; + + using (var check = new EditorGUI.ChangeCheckScope()) + { + handlesStartCol = Handles.color; + switch (data.tabIndex) + { + case bezierPathTab: + if (eventType != EventType.Repaint && eventType != EventType.Layout) + { + ProcessBezierPathInput(Event.current); + } + + DrawBezierPathSceneEditor(); + break; + case vertexPathTab: + if (eventType == EventType.Repaint) + { + DrawVertexPathSceneEditor(); + } + break; + } + + // Don't allow clicking over empty space to deselect the object + if (eventType == EventType.Layout) + { + HandleUtility.AddDefaultControl(0); + } + + if (check.changed) + { + EditorApplication.QueuePlayerLoopUpdate(); + } + } + + SetTransformState(); + } + + void DrawVertexPathSceneEditor() + { + + Color bezierCol = globalDisplaySettings.bezierPath; + bezierCol.a *= .5f; + + if (data.showBezierPathInVertexMode) + { + for (int i = 0; i < bezierPath.NumSegments; i++) + { + Vector3[] points = bezierPath.GetPointsInSegment(i); + for (int j = 0; j < points.Length; j++) + { + points[j] = MathUtility.TransformPoint(points[j], creator.transform, bezierPath.Space); + } + Handles.DrawBezier(points[0], points[3], points[1], points[2], bezierCol, null, 2); + } + } + + Handles.color = globalDisplaySettings.vertexPath; + + for (int i = 0; i < creator.path.NumPoints; i++) + { + int nextIndex = (i + 1) % creator.path.NumPoints; + if (nextIndex != 0 || bezierPath.IsClosed) + { + Handles.DrawLine(creator.path.GetPoint(i), creator.path.GetPoint(nextIndex)); + } + } + + if (data.showNormalsInVertexMode) + { + Handles.color = globalDisplaySettings.normals; + Vector3[] normalLines = new Vector3[creator.path.NumPoints * 2]; + for (int i = 0; i < creator.path.NumPoints; i++) + { + normalLines[i * 2] = creator.path.GetPoint(i); + normalLines[i * 2 + 1] = creator.path.GetPoint(i) + creator.path.localNormals[i] * globalDisplaySettings.normalsLength; + } + Handles.DrawLines(normalLines); + } + } + + void ProcessBezierPathInput(Event e) + { + // Find which handle mouse is over. Start by looking at previous handle index first, as most likely to still be closest to mouse + int previousMouseOverHandleIndex = (mouseOverHandleIndex == -1) ? 0 : mouseOverHandleIndex; + mouseOverHandleIndex = -1; + for (int i = 0; i < bezierPath.NumPoints; i += 3) + { + + int handleIndex = (previousMouseOverHandleIndex + i) % bezierPath.NumPoints; + float handleRadius = GetHandleDiameter(globalDisplaySettings.anchorSize * data.bezierHandleScale, bezierPath[handleIndex]) / 2f; + Vector3 pos = MathUtility.TransformPoint(bezierPath[handleIndex], creator.transform, bezierPath.Space); + float dst = HandleUtility.DistanceToCircle(pos, handleRadius); + if (dst == 0) + { + mouseOverHandleIndex = handleIndex; + break; + } + } + + // Shift-left click (when mouse not over a handle) to split or add segment + if (mouseOverHandleIndex == -1) + { + if (e.type == EventType.MouseDown && e.button == 0 && e.shift) + { + UpdatePathMouseInfo(); + // Insert point along selected segment + if (selectedSegmentIndex != -1 && selectedSegmentIndex < bezierPath.NumSegments) + { + Vector3 newPathPoint = pathMouseInfo.closestWorldPointToMouse; + newPathPoint = MathUtility.InverseTransformPoint(newPathPoint, creator.transform, bezierPath.Space); + Undo.RecordObject(creator, "Split segment"); + bezierPath.SplitSegment(newPathPoint, selectedSegmentIndex, pathMouseInfo.timeOnBezierSegment); + } + // If path is not a closed loop, add new point on to the end of the path + else if (!bezierPath.IsClosed) + { + // If control/command are held down, the point gets pre-pended, so we want to check distance + // to the endpoint we are adding to + var pointIdx = e.control || e.command ? 0 : bezierPath.NumPoints - 1; + // insert new point at same dst from scene camera as the point that comes before it (for a 3d path) + var endPointLocal = bezierPath[pointIdx]; + var endPointGlobal = + MathUtility.TransformPoint(endPointLocal, creator.transform, bezierPath.Space); + var distanceCameraToEndpoint = (Camera.current.transform.position - endPointGlobal).magnitude; + var newPointGlobal = + MouseUtility.GetMouseWorldPosition(bezierPath.Space, distanceCameraToEndpoint); + var newPointLocal = + MathUtility.InverseTransformPoint(newPointGlobal, creator.transform, bezierPath.Space); + + Undo.RecordObject(creator, "Add segment"); + if (e.control || e.command) + { + bezierPath.AddSegmentToStart(newPointLocal); + } + else + { + bezierPath.AddSegmentToEnd(newPointLocal); + } + + } + + } + } + + // Control click or backspace/delete to remove point + if (e.keyCode == KeyCode.Backspace || e.keyCode == KeyCode.Delete || ((e.control || e.command) && e.type == EventType.MouseDown && e.button == 0)) + { + + if (mouseOverHandleIndex != -1) + { + Undo.RecordObject(creator, "Delete segment"); + bezierPath.DeleteSegment(mouseOverHandleIndex); + if (mouseOverHandleIndex == handleIndexToDisplayAsTransform) + { + handleIndexToDisplayAsTransform = -1; + } + mouseOverHandleIndex = -1; + Repaint(); + } + } + + // Holding shift and moving mouse (but mouse not over a handle/dragging a handle) + if (draggingHandleIndex == -1 && mouseOverHandleIndex == -1) + { + bool shiftDown = e.shift && !shiftLastFrame; + if (shiftDown || ((e.type == EventType.MouseMove || e.type == EventType.MouseDrag) && e.shift)) + { + UpdatePathMouseInfo(); + bool notSplittingAtControlPoint = pathMouseInfo.timeOnBezierSegment > 0 && pathMouseInfo.timeOnBezierSegment < 1; + if (pathMouseInfo.mouseDstToLine < segmentSelectDistanceThreshold && notSplittingAtControlPoint) + { + if (pathMouseInfo.closestSegmentIndex != selectedSegmentIndex) + { + selectedSegmentIndex = pathMouseInfo.closestSegmentIndex; + HandleUtility.Repaint(); + } + } + else + { + selectedSegmentIndex = -1; + HandleUtility.Repaint(); + } + + } + } + + shiftLastFrame = e.shift; + + } + + void DrawBezierPathSceneEditor() + { + + bool displayControlPoints = data.displayControlPoints && (bezierPath.ControlPointMode != BezierPath.ControlMode.Automatic || !globalDisplaySettings.hideAutoControls); + Bounds bounds = bezierPath.CalculateBoundsWithTransform(creator.transform); + + if (Event.current.type == EventType.Repaint) + { + for (int i = 0; i < bezierPath.NumSegments; i++) + { + Vector3[] points = bezierPath.GetPointsInSegment(i); + for (int j = 0; j < points.Length; j++) + { + points[j] = MathUtility.TransformPoint(points[j], creator.transform, bezierPath.Space); + } + + if (data.showPerSegmentBounds) + { + Bounds segmentBounds = CubicBezierUtility.CalculateSegmentBounds(points[0], points[1], points[2], points[3]); + Handles.color = globalDisplaySettings.segmentBounds; + Handles.DrawWireCube(segmentBounds.center, segmentBounds.size); + } + + // Draw lines between control points + if (displayControlPoints) + { + Handles.color = (bezierPath.ControlPointMode == BezierPath.ControlMode.Automatic) ? globalDisplaySettings.handleDisabled : globalDisplaySettings.controlLine; + Handles.DrawLine(points[1], points[0]); + Handles.DrawLine(points[2], points[3]); + } + + // Draw path + bool highlightSegment = (i == selectedSegmentIndex && Event.current.shift && draggingHandleIndex == -1 && mouseOverHandleIndex == -1); + Color segmentCol = (highlightSegment) ? globalDisplaySettings.highlightedPath : globalDisplaySettings.bezierPath; + Handles.DrawBezier(points[0], points[3], points[1], points[2], segmentCol, null, 2); + } + + if (data.showPathBounds) + { + Handles.color = globalDisplaySettings.bounds; + Handles.DrawWireCube(bounds.center, bounds.size); + } + + // Draw normals + if (data.showNormals) + { + if (!hasUpdatedNormalsVertexPath) + { + normalsVertexPath = new VertexPath(bezierPath, creator.transform, normalsSpacing); + hasUpdatedNormalsVertexPath = true; + } + + if (editingNormalsOld != data.showNormals) + { + editingNormalsOld = data.showNormals; + Repaint(); + } + + Vector3[] normalLines = new Vector3[normalsVertexPath.NumPoints * 2]; + Handles.color = globalDisplaySettings.normals; + for (int i = 0; i < normalsVertexPath.NumPoints; i++) + { + normalLines[i * 2] = normalsVertexPath.GetPoint(i); + normalLines[i * 2 + 1] = normalsVertexPath.GetPoint(i) + normalsVertexPath.GetNormal(i) * globalDisplaySettings.normalsLength; + } + Handles.DrawLines(normalLines); + } + } + + if (data.displayAnchorPoints) + { + for (int i = 0; i < bezierPath.NumPoints; i += 3) + { + DrawHandle(i); + } + } + if (displayControlPoints) + { + for (int i = 1; i < bezierPath.NumPoints - 1; i += 3) + { + DrawHandle(i); + DrawHandle(i + 1); + } + } + } + + void DrawHandle(int i) + { + Vector3 handlePosition = MathUtility.TransformPoint(bezierPath[i], creator.transform, bezierPath.Space); + + float anchorHandleSize = GetHandleDiameter(globalDisplaySettings.anchorSize * data.bezierHandleScale, bezierPath[i]); + float controlHandleSize = GetHandleDiameter(globalDisplaySettings.controlSize * data.bezierHandleScale, bezierPath[i]); + + bool isAnchorPoint = i % 3 == 0; + bool isInteractive = isAnchorPoint || bezierPath.ControlPointMode != BezierPath.ControlMode.Automatic; + float handleSize = (isAnchorPoint) ? anchorHandleSize : controlHandleSize; + bool doTransformHandle = i == handleIndexToDisplayAsTransform; + + PathHandle.HandleColours handleColours = (isAnchorPoint) ? splineAnchorColours : splineControlColours; + if (i == handleIndexToDisplayAsTransform) + { + handleColours.defaultColour = (isAnchorPoint) ? globalDisplaySettings.anchorSelected : globalDisplaySettings.controlSelected; + } + var cap = capFunctions[(isAnchorPoint) ? globalDisplaySettings.anchorShape : globalDisplaySettings.controlShape]; + PathHandle.HandleInputType handleInputType; + handlePosition = PathHandle.DrawHandle(handlePosition, bezierPath.Space, isInteractive, handleSize, cap, handleColours, out handleInputType, i); + + if (doTransformHandle) + { + // Show normals rotate tool + if (data.showNormals && Tools.current == Tool.Rotate && isAnchorPoint && bezierPath.Space == PathSpace.xyz) + { + Handles.color = handlesStartCol; + + int attachedControlIndex = (i == bezierPath.NumPoints - 1) ? i - 1 : i + 1; + Vector3 dir = (bezierPath[attachedControlIndex] - handlePosition).normalized; + float handleRotOffset = (360 + bezierPath.GlobalNormalsAngle) % 360; + anchorAngleHandle.radius = handleSize * 3; + anchorAngleHandle.angle = handleRotOffset + bezierPath.GetAnchorNormalAngle(i / 3); + Vector3 handleDirection = Vector3.Cross(dir, Vector3.up); + Matrix4x4 handleMatrix = Matrix4x4.TRS( + handlePosition, + Quaternion.LookRotation(handleDirection, dir), + Vector3.one + ); + + using (new Handles.DrawingScope(handleMatrix)) + { + // draw the handle + EditorGUI.BeginChangeCheck(); + anchorAngleHandle.DrawHandle(); + if (EditorGUI.EndChangeCheck()) + { + Undo.RecordObject(creator, "Set angle"); + bezierPath.SetAnchorNormalAngle(i / 3, anchorAngleHandle.angle - handleRotOffset); + } + } + + } + else + { + handlePosition = Handles.DoPositionHandle(handlePosition, Quaternion.identity); + } + + } + + switch (handleInputType) + { + case PathHandle.HandleInputType.LMBDrag: + draggingHandleIndex = i; + handleIndexToDisplayAsTransform = -1; + Repaint(); + break; + case PathHandle.HandleInputType.LMBRelease: + draggingHandleIndex = -1; + handleIndexToDisplayAsTransform = -1; + Repaint(); + break; + case PathHandle.HandleInputType.LMBClick: + draggingHandleIndex = -1; + if (Event.current.shift) + { + handleIndexToDisplayAsTransform = -1; // disable move tool if new point added + } + else + { + if (handleIndexToDisplayAsTransform == i) + { + handleIndexToDisplayAsTransform = -1; // disable move tool if clicking on point under move tool + } + else + { + handleIndexToDisplayAsTransform = i; + } + } + Repaint(); + break; + case PathHandle.HandleInputType.LMBPress: + if (handleIndexToDisplayAsTransform != i) + { + handleIndexToDisplayAsTransform = -1; + Repaint(); + } + break; + } + + Vector3 localHandlePosition = MathUtility.InverseTransformPoint(handlePosition, creator.transform, bezierPath.Space); + + if (bezierPath[i] != localHandlePosition) + { + Undo.RecordObject(creator, "Move point"); + bezierPath.MovePoint(i, localHandlePosition); + + } + + } + + #endregion + + #region Internal methods + + void OnDisable() + { + Tools.hidden = false; + } + + void OnEnable() + { + creator = (PathCreator)target; + bool in2DEditorMode = EditorSettings.defaultBehaviorMode == EditorBehaviorMode.Mode2D; + creator.InitializeEditorData(in2DEditorMode); + + data.bezierCreated -= ResetState; + data.bezierCreated += ResetState; + Undo.undoRedoPerformed -= OnUndoRedo; + Undo.undoRedoPerformed += OnUndoRedo; + + LoadDisplaySettings(); + UpdateGlobalDisplaySettings(); + ResetState(); + SetTransformState(true); + } + + void SetTransformState(bool initialize = false) + { + Transform t = creator.transform; + if (!initialize) + { + if (transformPos != t.position || t.localScale != transformScale || t.rotation != transformRot) + { + data.PathTransformed(); + } + } + transformPos = t.position; + transformScale = t.localScale; + transformRot = t.rotation; + } + + void OnUndoRedo() + { + hasUpdatedScreenSpaceLine = false; + hasUpdatedNormalsVertexPath = false; + selectedSegmentIndex = -1; + + Repaint(); + } + + void TabChanged() + { + SceneView.RepaintAll(); + RepaintUnfocusedSceneViews(); + } + + void LoadDisplaySettings() + { + globalDisplaySettings = GlobalDisplaySettings.Load(); + + capFunctions = new Dictionary(); + capFunctions.Add(GlobalDisplaySettings.HandleType.Circle, Handles.CylinderHandleCap); + capFunctions.Add(GlobalDisplaySettings.HandleType.Sphere, Handles.SphereHandleCap); + capFunctions.Add(GlobalDisplaySettings.HandleType.Square, Handles.CubeHandleCap); + } + + void UpdateGlobalDisplaySettings() + { + var gds = globalDisplaySettings; + splineAnchorColours = new PathHandle.HandleColours(gds.anchor, gds.anchorHighlighted, gds.anchorSelected, gds.handleDisabled); + splineControlColours = new PathHandle.HandleColours(gds.control, gds.controlHighlighted, gds.controlSelected, gds.handleDisabled); + + anchorAngleHandle.fillColor = new Color(1, 1, 1, .05f); + anchorAngleHandle.wireframeColor = Color.grey; + anchorAngleHandle.radiusHandleColor = Color.clear; + anchorAngleHandle.angleHandleColor = Color.white; + } + + void ResetState() + { + selectedSegmentIndex = -1; + draggingHandleIndex = -1; + mouseOverHandleIndex = -1; + handleIndexToDisplayAsTransform = -1; + hasUpdatedScreenSpaceLine = false; + hasUpdatedNormalsVertexPath = false; + + bezierPath.OnModified -= OnPathModifed; + bezierPath.OnModified += OnPathModifed; + + SceneView.RepaintAll(); + EditorApplication.QueuePlayerLoopUpdate(); + } + + void OnPathModifed() + { + hasUpdatedScreenSpaceLine = false; + hasUpdatedNormalsVertexPath = false; + + RepaintUnfocusedSceneViews(); + } + + void RepaintUnfocusedSceneViews() + { + // If multiple scene views are open, repaint those which do not have focus. + if (SceneView.sceneViews.Count > 1) + { + foreach (SceneView sv in SceneView.sceneViews) + { + if (EditorWindow.focusedWindow != (EditorWindow)sv) + { + sv.Repaint(); + } + } + } + } + + void UpdatePathMouseInfo() + { + + if (!hasUpdatedScreenSpaceLine || (screenSpaceLine != null && screenSpaceLine.TransformIsOutOfDate())) + { + screenSpaceLine = new ScreenSpacePolyLine(bezierPath, creator.transform, screenPolylineMaxAngleError, screenPolylineMinVertexDst); + hasUpdatedScreenSpaceLine = true; + } + pathMouseInfo = screenSpaceLine.CalculateMouseInfo(); + } + + float GetHandleDiameter(float diameter, Vector3 handlePosition) + { + float scaledDiameter = diameter * constantHandleScale; + if (data.keepConstantHandleSize) + { + scaledDiameter *= HandleUtility.GetHandleSize(handlePosition) * 2.5f; + } + return scaledDiameter; + } + + BezierPath bezierPath + { + get + { + return data.bezierPath; + } + } + + PathCreatorData data + { + get + { + return creator.EditorData; + } + } + + bool editingNormals + { + get + { + return Tools.current == Tool.Rotate && handleIndexToDisplayAsTransform % 3 == 0 && bezierPath.Space == PathSpace.xyz; + } + } + + #endregion + + } } \ No newline at end of file diff --git a/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs b/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs index a3912b2..dbff8b6 100644 --- a/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs +++ b/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs @@ -3,676 +3,827 @@ using PathCreation.Utility; using UnityEngine; -namespace PathCreation { - /// A bezier path is a path made by stitching together any number of (cubic) bezier curves. - /// A single cubic bezier curve is defined by 4 points: anchor1, control1, control2, anchor2 - /// The curve moves between the 2 anchors, and the shape of the curve is affected by the positions of the 2 control points - - /// When two curves are stitched together, they share an anchor point (end anchor of curve 1 = start anchor of curve 2). - /// So while one curve alone consists of 4 points, two curves are defined by 7 unique points. - - /// Apart from storing the points, this class also provides methods for working with the path. - /// For example, adding, inserting, and deleting points. - - [System.Serializable] - public class BezierPath { - public event System.Action OnModified; - public enum ControlMode { Aligned, Mirrored, Free, Automatic }; - - #region Fields - - [SerializeField, HideInInspector] - List points; - [SerializeField, HideInInspector] - bool isClosed; - [SerializeField, HideInInspector] - PathSpace space; - [SerializeField, HideInInspector] - ControlMode controlMode; - [SerializeField, HideInInspector] - float autoControlLength = .3f; - [SerializeField, HideInInspector] - bool boundsUpToDate; - [SerializeField, HideInInspector] - Bounds bounds; - - // Normals settings - [SerializeField, HideInInspector] - List perAnchorNormalsAngle; - [SerializeField, HideInInspector] - float globalNormalsAngle; - [SerializeField, HideInInspector] - bool flipNormals; - - #endregion - - #region Constructors - - /// Creates a two-anchor path centred around the given centre point - /// Should the end point connect back to the start point? - /// Determines if the path is in 3d space, or clamped to the xy/xz plane - public BezierPath (Vector3 centre, bool isClosed = false, PathSpace space = PathSpace.xyz) { - - Vector3 dir = (space == PathSpace.xz) ? Vector3.forward : Vector3.up; - float width = 2; - float controlHeight = .5f; - float controlWidth = 1f; - points = new List { +namespace PathCreation +{ + /// A bezier path is a path made by stitching together any number of (cubic) bezier curves. + /// A single cubic bezier curve is defined by 4 points: anchor1, control1, control2, anchor2 + /// The curve moves between the 2 anchors, and the shape of the curve is affected by the positions of the 2 control points + + /// When two curves are stitched together, they share an anchor point (end anchor of curve 1 = start anchor of curve 2). + /// So while one curve alone consists of 4 points, two curves are defined by 7 unique points. + + /// Apart from storing the points, this class also provides methods for working with the path. + /// For example, adding, inserting, and deleting points. + + [System.Serializable] + public class BezierPath + { + public event System.Action OnModified; + public enum ControlMode { Aligned, Mirrored, Free, Automatic }; + + #region Fields + + [SerializeField, HideInInspector] + List points; + [SerializeField, HideInInspector] + bool isClosed; + [SerializeField, HideInInspector] + PathSpace space; + [SerializeField, HideInInspector] + ControlMode controlMode; + [SerializeField, HideInInspector] + float autoControlLength = .3f; + [SerializeField, HideInInspector] + bool boundsUpToDate; + [SerializeField, HideInInspector] + Bounds bounds; + + // Normals settings + [SerializeField, HideInInspector] + List perAnchorNormalsAngle; + [SerializeField, HideInInspector] + float globalNormalsAngle; + [SerializeField, HideInInspector] + bool flipNormals; + + #endregion + + #region Constructors + + /// Creates a two-anchor path centred around the given centre point + /// Should the end point connect back to the start point? + /// Determines if the path is in 3d space, or clamped to the xy/xz plane + public BezierPath(Vector3 centre, bool isClosed = false, PathSpace space = PathSpace.xyz) + { + + Vector3 dir = (space == PathSpace.xz) ? Vector3.forward : Vector3.up; + float width = 2; + float controlHeight = .5f; + float controlWidth = 1f; + points = new List { centre + Vector3.left * width, centre + Vector3.left * controlWidth + dir * controlHeight, centre + Vector3.right * controlWidth - dir * controlHeight, centre + Vector3.right * width }; - perAnchorNormalsAngle = new List () { 0, 0 }; - - Space = space; - IsClosed = isClosed; - } - - /// Creates a path from the supplied 3D points - /// List or array of points to create the path from. - /// Should the end point connect back to the start point? - /// Determines if the path is in 3d space, or clamped to the xy/xz plane - public BezierPath (IEnumerable points, bool isClosed = false, PathSpace space = PathSpace.xyz) { - Vector3[] pointsArray = points.ToArray (); - - if (pointsArray.Length < 2) { - Debug.LogError ("Path requires at least 2 anchor points."); - } else { - controlMode = ControlMode.Automatic; - this.points = new List { pointsArray[0], Vector3.zero, Vector3.zero, pointsArray[1] }; - perAnchorNormalsAngle = new List (new float[] { 0, 0 }); - - for (int i = 2; i < pointsArray.Length; i++) { - AddSegmentToEnd (pointsArray[i]); - perAnchorNormalsAngle.Add (0); - } - } - - this.Space = space; - this.IsClosed = isClosed; - } - - /// Creates a path from the positions of the supplied 2D points - /// List or array of transforms to create the path from. - /// Should the end point connect back to the start point? - /// Determines if the path is in 3d space, or clamped to the xy/xz plane - public BezierPath (IEnumerable transforms, bool isClosed = false, PathSpace space = PathSpace.xy): - this (transforms.Select (p => new Vector3 (p.x, p.y)), isClosed, space) { } - - /// Creates a path from the positions of the supplied transforms - /// List or array of transforms to create the path from. - /// Should the end point connect back to the start point? - /// Determines if the path is in 3d space, or clamped to the xy/xz plane - public BezierPath (IEnumerable transforms, bool isClosed = false, PathSpace space = PathSpace.xy): - this (transforms.Select (t => t.position), isClosed, space) { } - - /// Creates a path from the supplied 2D points - /// List or array of 2d points to create the path from. - /// Should the end point connect back to the start point? - /// Determines if the path is in 3d space, or clamped to the xy/xz plane - public BezierPath (IEnumerable points, PathSpace space = PathSpace.xyz, bool isClosed = false): - this (points.Select (p => new Vector3 (p.x, p.y)), isClosed, space) { } - - #endregion - - #region Public methods and accessors - - /// Get world space position of point - public Vector3 this [int i] { - get { - return GetPoint (i); - } - } - - /// Get world space position of point - public Vector3 GetPoint (int i) { - return points[i]; - } - - /// Get world space position of point - public void SetPoint (int i, Vector3 localPosition, bool suppressPathModifiedEvent = false) { - points[i] = localPosition; - if (!suppressPathModifiedEvent) { - NotifyPathModified(); - } - } - - /// Total number of points in the path (anchors and controls) - public int NumPoints { - get { - return points.Count; - } - } - - /// Number of anchor points making up the path - public int NumAnchorPoints { - get { - return (IsClosed) ? points.Count / 3 : (points.Count + 2) / 3; - } - } - - /// Number of bezier curves making up this path - public int NumSegments { - get { - return points.Count / 3; - } - } - - /// Path can exist in 3D (xyz), 2D (xy), or Top-Down (xz) space - /// In xy or xz space, points will be clamped to that plane (so in a 2D path, for example, points will always be at 0 on z axis) - public PathSpace Space { - get { - return space; - } - set { - if (value != space) { - PathSpace previousSpace = space; - space = value; - UpdateToNewPathSpace (previousSpace); - } - } - } - - /// If closed, path will loop back from end point to start point - public bool IsClosed { - get { - return isClosed; - } - set { - if (isClosed != value) { - isClosed = value; - UpdateClosedState (); - } - } - } - - /// The control mode determines the behaviour of control points. - /// Possible modes are: - /// Aligned = controls stay in straight line around their anchor - /// Mirrored = controls stay in straight, equidistant line around their anchor - /// Free = no constraints (use this if sharp corners are needed) - /// Automatic = controls placed automatically to try make the path smooth - public ControlMode ControlPointMode { - get { - return controlMode; - } - set { - if (controlMode != value) { - controlMode = value; - if (controlMode == ControlMode.Automatic) { - AutoSetAllControlPoints (); - NotifyPathModified (); - } - } - } - } - - /// When using automatic control point placement, this value scales how far apart controls are placed - public float AutoControlLength { - get { - return autoControlLength; - } - set { - value = Mathf.Max (value, .01f); - if (autoControlLength != value) { - autoControlLength = value; - AutoSetAllControlPoints (); - NotifyPathModified (); - } - } - } - - /// Add new anchor point to end of the path - public void AddSegmentToEnd (Vector3 anchorPos) { - if (isClosed) { - return; - } - - int lastAnchorIndex = points.Count - 1; - // Set position for new control to be mirror of its counterpart - Vector3 secondControlForOldLastAnchorOffset = (points[lastAnchorIndex] - points[lastAnchorIndex - 1]); - if (controlMode != ControlMode.Mirrored && controlMode != ControlMode.Automatic) { - // Set position for new control to be aligned with its counterpart, but with a length of half the distance from prev to new anchor - float dstPrevToNewAnchor = (points[lastAnchorIndex] - anchorPos).magnitude; - secondControlForOldLastAnchorOffset = (points[lastAnchorIndex] - points[lastAnchorIndex - 1]).normalized * dstPrevToNewAnchor * .5f; - } - Vector3 secondControlForOldLastAnchor = points[lastAnchorIndex] + secondControlForOldLastAnchorOffset; - Vector3 controlForNewAnchor = (anchorPos + secondControlForOldLastAnchor) * .5f; - - points.Add (secondControlForOldLastAnchor); - points.Add (controlForNewAnchor); - points.Add (anchorPos); - perAnchorNormalsAngle.Add (perAnchorNormalsAngle[perAnchorNormalsAngle.Count - 1]); - - if (controlMode == ControlMode.Automatic) { - AutoSetAllAffectedControlPoints (points.Count - 1); - } - - NotifyPathModified (); - } - - /// Add new anchor point to start of the path - public void AddSegmentToStart (Vector3 anchorPos) { - if (isClosed) { - return; - } - - // Set position for new control to be mirror of its counterpart - Vector3 secondControlForOldFirstAnchorOffset = (points[0] - points[1]); - if (controlMode != ControlMode.Mirrored && controlMode != ControlMode.Automatic) { - // Set position for new control to be aligned with its counterpart, but with a length of half the distance from prev to new anchor - float dstPrevToNewAnchor = (points[0] - anchorPos).magnitude; - secondControlForOldFirstAnchorOffset = secondControlForOldFirstAnchorOffset.normalized * dstPrevToNewAnchor * .5f; - } - - Vector3 secondControlForOldFirstAnchor = points[0] + secondControlForOldFirstAnchorOffset; - Vector3 controlForNewAnchor = (anchorPos + secondControlForOldFirstAnchor) * .5f; - points.Insert (0, anchorPos); - points.Insert (1, controlForNewAnchor); - points.Insert (2, secondControlForOldFirstAnchor); - perAnchorNormalsAngle.Insert (0, perAnchorNormalsAngle[0]); - - if (controlMode == ControlMode.Automatic) { - AutoSetAllAffectedControlPoints (0); - } - NotifyPathModified (); - } - - /// Insert new anchor point at given position. Automatically place control points around it so as to keep shape of curve the same - public void SplitSegment (Vector3 anchorPos, int segmentIndex, float splitTime) { - splitTime = Mathf.Clamp01 (splitTime); - - if (controlMode == ControlMode.Automatic) { - points.InsertRange (segmentIndex * 3 + 2, new Vector3[] { Vector3.zero, anchorPos, Vector3.zero }); - AutoSetAllAffectedControlPoints (segmentIndex * 3 + 3); - } else { - // Split the curve to find where control points can be inserted to least affect shape of curve - // Curve will probably be deformed slightly since splitTime is only an estimate (for performance reasons, and so doesn't correspond exactly with anchorPos) - Vector3[][] splitSegment = CubicBezierUtility.SplitCurve (GetPointsInSegment (segmentIndex), splitTime); - points.InsertRange (segmentIndex * 3 + 2, new Vector3[] { splitSegment[0][2], splitSegment[1][0], splitSegment[1][1] }); - int newAnchorIndex = segmentIndex * 3 + 3; - MovePoint (newAnchorIndex - 2, splitSegment[0][1], true); - MovePoint (newAnchorIndex + 2, splitSegment[1][2], true); - MovePoint (newAnchorIndex, anchorPos, true); - - if (controlMode == ControlMode.Mirrored) { - float avgDst = ((splitSegment[0][2] - anchorPos).magnitude + (splitSegment[1][1] - anchorPos).magnitude) / 2; - MovePoint (newAnchorIndex + 1, anchorPos + (splitSegment[1][1] - anchorPos).normalized * avgDst, true); - } - } - - // Insert angle for new anchor (value should be set inbetween neighbour anchor angles) - int newAnchorAngleIndex = (segmentIndex + 1) % perAnchorNormalsAngle.Count; - int numAngles = perAnchorNormalsAngle.Count; - float anglePrev = perAnchorNormalsAngle[segmentIndex]; - float angleNext = perAnchorNormalsAngle[newAnchorAngleIndex]; - float splitAngle = Mathf.LerpAngle (anglePrev, angleNext, splitTime); - perAnchorNormalsAngle.Insert (newAnchorAngleIndex, splitAngle); - - NotifyPathModified (); - } - - /// Delete the anchor point at given index, as well as its associated control points - public void DeleteSegment (int anchorIndex) { - // Don't delete segment if its the last one remaining (or if only two segments in a closed path) - if (NumSegments > 2 || !isClosed && NumSegments > 1) { - if (anchorIndex == 0) { - if (isClosed) { - points[points.Count - 1] = points[2]; - } - points.RemoveRange (0, 3); - } else if (anchorIndex == points.Count - 1 && !isClosed) { - points.RemoveRange (anchorIndex - 2, 3); - } else { - points.RemoveRange (anchorIndex - 1, 3); - } - - perAnchorNormalsAngle.RemoveAt (anchorIndex / 3); - - if (controlMode == ControlMode.Automatic) { - AutoSetAllControlPoints (); - } - - NotifyPathModified (); - } - } - - /// Returns an array of the 4 points making up the segment (anchor1, control1, control2, anchor2) - public Vector3[] GetPointsInSegment (int segmentIndex) { - segmentIndex = Mathf.Clamp (segmentIndex, 0, NumSegments - 1); - return new Vector3[] { this [segmentIndex * 3], this [segmentIndex * 3 + 1], this [segmentIndex * 3 + 2], this [LoopIndex (segmentIndex * 3 + 3)] }; - } - - /// Move an existing point to a new position - public void MovePoint (int i, Vector3 pointPos, bool suppressPathModifiedEvent = false) { - - if (space == PathSpace.xy) { - pointPos.z = 0; - } else if (space == PathSpace.xz) { - pointPos.y = 0; - } - Vector3 deltaMove = pointPos - points[i]; - bool isAnchorPoint = i % 3 == 0; - - // Don't process control point if control mode is set to automatic - if (isAnchorPoint || controlMode != ControlMode.Automatic) { - points[i] = pointPos; - - if (controlMode == ControlMode.Automatic) { - AutoSetAllAffectedControlPoints (i); - } else { - // Move control points with anchor point - if (isAnchorPoint) { - if (i + 1 < points.Count || isClosed) { - points[LoopIndex (i + 1)] += deltaMove; - } - if (i - 1 >= 0 || isClosed) { - points[LoopIndex (i - 1)] += deltaMove; - } - } - // If not in free control mode, then move attached control point to be aligned/mirrored (depending on mode) - else if (controlMode != ControlMode.Free) { - bool nextPointIsAnchor = (i + 1) % 3 == 0; - int attachedControlIndex = (nextPointIsAnchor) ? i + 2 : i - 2; - int anchorIndex = (nextPointIsAnchor) ? i + 1 : i - 1; - - if (attachedControlIndex >= 0 && attachedControlIndex < points.Count || isClosed) { - float distanceFromAnchor = 0; - // If in aligned mode, then attached control's current distance from anchor point should be maintained - if (controlMode == ControlMode.Aligned) { - distanceFromAnchor = (points[LoopIndex (anchorIndex)] - points[LoopIndex (attachedControlIndex)]).magnitude; - } - // If in mirrored mode, then both control points should have the same distance from the anchor point - else if (controlMode == ControlMode.Mirrored) { - distanceFromAnchor = (points[LoopIndex (anchorIndex)] - points[i]).magnitude; - - } - Vector3 dir = (points[LoopIndex (anchorIndex)] - pointPos).normalized; - points[LoopIndex (attachedControlIndex)] = points[LoopIndex (anchorIndex)] + dir * distanceFromAnchor; - } - } - } - - if (!suppressPathModifiedEvent) { - NotifyPathModified (); - } - } - } - - /// Update the bounding box of the path - public Bounds CalculateBoundsWithTransform (Transform transform) { - // Loop through all segments and keep track of the minmax points of all their bounding boxes - MinMax3D minMax = new MinMax3D (); - - for (int i = 0; i < NumSegments; i++) { - Vector3[] p = GetPointsInSegment (i); - for (int j = 0; j < p.Length; j++) { - p[j] = MathUtility.TransformPoint (p[j], transform, space); - } - - minMax.AddValue (p[0]); - minMax.AddValue (p[3]); - - List extremePointTimes = CubicBezierUtility.ExtremePointTimes (p[0], p[1], p[2], p[3]); - foreach (float t in extremePointTimes) { - minMax.AddValue (CubicBezierUtility.EvaluateCurve (p, t)); - } - } - - return new Bounds ((minMax.Min + minMax.Max) / 2, minMax.Max - minMax.Min); - } - - /// Flip the normal vectors 180 degrees - public bool FlipNormals { - get { - return flipNormals; - } - set { - if (flipNormals != value) { - flipNormals = value; - NotifyPathModified (); - } - } - } - - /// Global angle that all normal vectors are rotated by (only relevant for paths in 3D space) - public float GlobalNormalsAngle { - get { - return globalNormalsAngle; - } - set { - if (value != globalNormalsAngle) { - globalNormalsAngle = value; - NotifyPathModified (); - } - } - } - - /// Get the desired angle of the normal vector at a particular anchor (only relevant for paths in 3D space) - public float GetAnchorNormalAngle (int anchorIndex) { - return perAnchorNormalsAngle[anchorIndex] % 360; - } - - /// Set the desired angle of the normal vector at a particular anchor (only relevant for paths in 3D space) - public void SetAnchorNormalAngle (int anchorIndex, float angle) { - angle = (angle + 360) % 360; - if (perAnchorNormalsAngle[anchorIndex] != angle) { - perAnchorNormalsAngle[anchorIndex] = angle; - NotifyPathModified (); - } - } - - /// Reset global and anchor normal angles to 0 - public void ResetNormalAngles () { - for (int i = 0; i < perAnchorNormalsAngle.Count; i++) { - perAnchorNormalsAngle[i] = 0; - } - globalNormalsAngle = 0; - NotifyPathModified (); - } - - /// Bounding box containing the path - public Bounds PathBounds { - get { - if (!boundsUpToDate) { - UpdateBounds (); - } - return bounds; - } - } - - #endregion - - #region Internal methods and accessors - - /// Update the bounding box of the path - void UpdateBounds () { - if (boundsUpToDate) { - return; - } - - // Loop through all segments and keep track of the minmax points of all their bounding boxes - MinMax3D minMax = new MinMax3D (); - - for (int i = 0; i < NumSegments; i++) { - Vector3[] p = GetPointsInSegment (i); - minMax.AddValue (p[0]); - minMax.AddValue (p[3]); - - List extremePointTimes = CubicBezierUtility.ExtremePointTimes (p[0], p[1], p[2], p[3]); - foreach (float t in extremePointTimes) { - minMax.AddValue (CubicBezierUtility.EvaluateCurve (p, t)); - } - } - - boundsUpToDate = true; - bounds = new Bounds ((minMax.Min + minMax.Max) / 2, minMax.Max - minMax.Min); - } - - /// Determines good positions (for a smooth path) for the control points affected by a moved/inserted anchor point - void AutoSetAllAffectedControlPoints (int updatedAnchorIndex) { - for (int i = updatedAnchorIndex - 3; i <= updatedAnchorIndex + 3; i += 3) { - if (i >= 0 && i < points.Count || isClosed) { - AutoSetAnchorControlPoints (LoopIndex (i)); - } - } - - AutoSetStartAndEndControls (); - } - - /// Determines good positions (for a smooth path) for all control points - void AutoSetAllControlPoints () { - if (NumAnchorPoints > 2) { - for (int i = 0; i < points.Count; i += 3) { - AutoSetAnchorControlPoints (i); - } - } - - AutoSetStartAndEndControls (); - } - - /// Calculates good positions (to result in smooth path) for the controls around specified anchor - void AutoSetAnchorControlPoints (int anchorIndex) { - // Calculate a vector that is perpendicular to the vector bisecting the angle between this anchor and its two immediate neighbours - // The control points will be placed along that vector - Vector3 anchorPos = points[anchorIndex]; - Vector3 dir = Vector3.zero; - float[] neighbourDistances = new float[2]; - - if (anchorIndex - 3 >= 0 || isClosed) { - Vector3 offset = points[LoopIndex (anchorIndex - 3)] - anchorPos; - dir += offset.normalized; - neighbourDistances[0] = offset.magnitude; - } - if (anchorIndex + 3 >= 0 || isClosed) { - Vector3 offset = points[LoopIndex (anchorIndex + 3)] - anchorPos; - dir -= offset.normalized; - neighbourDistances[1] = -offset.magnitude; - } - - dir.Normalize (); - - // Set the control points along the calculated direction, with a distance proportional to the distance to the neighbouring control point - for (int i = 0; i < 2; i++) { - int controlIndex = anchorIndex + i * 2 - 1; - if (controlIndex >= 0 && controlIndex < points.Count || isClosed) { - points[LoopIndex (controlIndex)] = anchorPos + dir * neighbourDistances[i] * autoControlLength; - } - } - } - - /// Determines good positions (for a smooth path) for the control points at the start and end of a path - void AutoSetStartAndEndControls () { - if (isClosed) { - // Handle case with only 2 anchor points separately, as will otherwise result in straight line () - if (NumAnchorPoints == 2) { - Vector3 dirAnchorAToB = (points[3] - points[0]).normalized; - float dstBetweenAnchors = (points[0] - points[3]).magnitude; - Vector3 perp = Vector3.Cross (dirAnchorAToB, (space == PathSpace.xy) ? Vector3.forward : Vector3.up); - points[1] = points[0] + perp * dstBetweenAnchors / 2f; - points[5] = points[0] - perp * dstBetweenAnchors / 2f; - points[2] = points[3] + perp * dstBetweenAnchors / 2f; - points[4] = points[3] - perp * dstBetweenAnchors / 2f; - - } else { - AutoSetAnchorControlPoints (0); - AutoSetAnchorControlPoints (points.Count - 3); - } - } else { - // Handle case with 2 anchor points separately, as otherwise minor adjustments cause path to constantly flip - if (NumAnchorPoints == 2) { - points[1] = points[0] + (points[3] - points[0]) * .25f; - points[2] = points[3] + (points[0] - points[3]) * .25f; - } else { - points[1] = (points[0] + points[2]) * .5f; - points[points.Count - 2] = (points[points.Count - 1] + points[points.Count - 3]) * .5f; - } - } - } - - /// Update point positions for new path space - /// (for example, if changing from xy to xz path, y and z axes will be swapped so the path keeps its shape in the new space) - void UpdateToNewPathSpace (PathSpace previousSpace) { - // If changing from 3d to 2d space, first find the bounds of the 3d path. - // The axis with the smallest bounds will be discarded. - if (previousSpace == PathSpace.xyz) { - Vector3 boundsSize = PathBounds.size; - float minBoundsSize = Mathf.Min (boundsSize.x, boundsSize.y, boundsSize.z); - - for (int i = 0; i < NumPoints; i++) { - if (space == PathSpace.xy) { - float x = (minBoundsSize == boundsSize.x) ? points[i].z : points[i].x; - float y = (minBoundsSize == boundsSize.y) ? points[i].z : points[i].y; - points[i] = new Vector3 (x, y, 0); - } else if (space == PathSpace.xz) { - float x = (minBoundsSize == boundsSize.x) ? points[i].y : points[i].x; - float z = (minBoundsSize == boundsSize.z) ? points[i].y : points[i].z; - points[i] = new Vector3 (x, 0, z); - } - } - } else { - // Nothing needs to change when going to 3d space - if (space != PathSpace.xyz) { - for (int i = 0; i < NumPoints; i++) { - // from xz to xy - if (space == PathSpace.xy) { - points[i] = new Vector3 (points[i].x, points[i].z, 0); - } - // from xy to xz - else if (space == PathSpace.xz) { - points[i] = new Vector3 (points[i].x, 0, points[i].y); - } - } - } - } - - NotifyPathModified (); - } - - /// Add/remove the extra 2 controls required for a closed path - void UpdateClosedState () { - if (isClosed) { - // Set positions for new controls to mirror their counterparts - Vector3 lastAnchorSecondControl = points[points.Count - 1] * 2 - points[points.Count - 2]; - Vector3 firstAnchorSecondControl = points[0] * 2 - points[1]; - if (controlMode != ControlMode.Mirrored && controlMode != ControlMode.Automatic) { - // Set positions for new controls to be aligned with their counterparts, but with a length of half the distance between start/end anchor - float dstBetweenStartAndEndAnchors = (points[points.Count - 1] - points[0]).magnitude; - lastAnchorSecondControl = points[points.Count - 1] + (points[points.Count - 1] - points[points.Count - 2]).normalized * dstBetweenStartAndEndAnchors * .5f; - firstAnchorSecondControl = points[0] + (points[0] - points[1]).normalized * dstBetweenStartAndEndAnchors * .5f; - } - points.Add (lastAnchorSecondControl); - points.Add (firstAnchorSecondControl); - } else { - points.RemoveRange (points.Count - 2, 2); - - } - - if (controlMode == ControlMode.Automatic) { - AutoSetStartAndEndControls (); - } - - if (OnModified != null) { - OnModified (); - } - } - - /// Loop index around to start/end of points array if out of bounds (useful when working with closed paths) - int LoopIndex (int i) { - return (i + points.Count) % points.Count; - } - - // Called when the path is modified - public void NotifyPathModified () { - boundsUpToDate = false; - if (OnModified != null) { - OnModified (); - } - } - - #endregion - - } + perAnchorNormalsAngle = new List() { 0, 0 }; + + Space = space; + IsClosed = isClosed; + } + + /// Creates a path from the supplied 3D points + /// List or array of points to create the path from. + /// Should the end point connect back to the start point? + /// Determines if the path is in 3d space, or clamped to the xy/xz plane + public BezierPath(IEnumerable points, bool isClosed = false, PathSpace space = PathSpace.xyz) + { + Vector3[] pointsArray = points.ToArray(); + + if (pointsArray.Length < 2) + { + Debug.LogError("Path requires at least 2 anchor points."); + } + else + { + controlMode = ControlMode.Automatic; + this.points = new List { pointsArray[0], Vector3.zero, Vector3.zero, pointsArray[1] }; + perAnchorNormalsAngle = new List(new float[] { 0, 0 }); + + for (int i = 2; i < pointsArray.Length; i++) + { + AddSegmentToEnd(pointsArray[i]); + perAnchorNormalsAngle.Add(0); + } + } + + this.Space = space; + this.IsClosed = isClosed; + } + + /// Creates a path from the positions of the supplied 2D points + /// List or array of transforms to create the path from. + /// Should the end point connect back to the start point? + /// Determines if the path is in 3d space, or clamped to the xy/xz plane + public BezierPath(IEnumerable transforms, bool isClosed = false, PathSpace space = PathSpace.xy) : + this(transforms.Select(p => new Vector3(p.x, p.y)), isClosed, space) + { } + + /// Creates a path from the positions of the supplied transforms + /// List or array of transforms to create the path from. + /// Should the end point connect back to the start point? + /// Determines if the path is in 3d space, or clamped to the xy/xz plane + public BezierPath(IEnumerable transforms, bool isClosed = false, PathSpace space = PathSpace.xy) : + this(transforms.Select(t => t.position), isClosed, space) + { } + + /// Creates a path from the supplied 2D points + /// List or array of 2d points to create the path from. + /// Should the end point connect back to the start point? + /// Determines if the path is in 3d space, or clamped to the xy/xz plane + public BezierPath(IEnumerable points, PathSpace space = PathSpace.xyz, bool isClosed = false) : + this(points.Select(p => new Vector3(p.x, p.y)), isClosed, space) + { } + + #endregion + + #region Public methods and accessors + + /// Get world space position of point + public Vector3 this[int i] + { + get + { + return GetPoint(i); + } + } + + /// Get world space position of point + public Vector3 GetPoint(int i) + { + return points[i]; + } + + /// Get world space position of point + public void SetPoint(int i, Vector3 localPosition, bool suppressPathModifiedEvent = false) + { + points[i] = localPosition; + if (!suppressPathModifiedEvent) + { + NotifyPathModified(); + } + } + + /// Total number of points in the path (anchors and controls) + public int NumPoints + { + get + { + return points.Count; + } + } + + /// Number of anchor points making up the path + public int NumAnchorPoints + { + get + { + return (IsClosed) ? points.Count / 3 : (points.Count + 2) / 3; + } + } + + /// Number of bezier curves making up this path + public int NumSegments + { + get + { + return points.Count / 3; + } + } + + /// Path can exist in 3D (xyz), 2D (xy), or Top-Down (xz) space + /// In xy or xz space, points will be clamped to that plane (so in a 2D path, for example, points will always be at 0 on z axis) + public PathSpace Space + { + get + { + return space; + } + set + { + if (value != space) + { + PathSpace previousSpace = space; + space = value; + UpdateToNewPathSpace(previousSpace); + } + } + } + + /// If closed, path will loop back from end point to start point + public bool IsClosed + { + get + { + return isClosed; + } + set + { + if (isClosed != value) + { + isClosed = value; + UpdateClosedState(); + } + } + } + + /// The control mode determines the behaviour of control points. + /// Possible modes are: + /// Aligned = controls stay in straight line around their anchor + /// Mirrored = controls stay in straight, equidistant line around their anchor + /// Free = no constraints (use this if sharp corners are needed) + /// Automatic = controls placed automatically to try make the path smooth + public ControlMode ControlPointMode + { + get + { + return controlMode; + } + set + { + if (controlMode != value) + { + controlMode = value; + if (controlMode == ControlMode.Automatic) + { + AutoSetAllControlPoints(); + NotifyPathModified(); + } + } + } + } + + /// When using automatic control point placement, this value scales how far apart controls are placed + public float AutoControlLength + { + get + { + return autoControlLength; + } + set + { + value = Mathf.Max(value, .01f); + if (autoControlLength != value) + { + autoControlLength = value; + AutoSetAllControlPoints(); + NotifyPathModified(); + } + } + } + + /// Add new anchor point to end of the path + public void AddSegmentToEnd(Vector3 anchorPos) + { + if (isClosed) + { + return; + } + + int lastAnchorIndex = points.Count - 1; + // Set position for new control to be mirror of its counterpart + Vector3 secondControlForOldLastAnchorOffset = (points[lastAnchorIndex] - points[lastAnchorIndex - 1]); + if (controlMode != ControlMode.Mirrored && controlMode != ControlMode.Automatic) + { + // Set position for new control to be aligned with its counterpart, but with a length of half the distance from prev to new anchor + float dstPrevToNewAnchor = (points[lastAnchorIndex] - anchorPos).magnitude; + secondControlForOldLastAnchorOffset = (points[lastAnchorIndex] - points[lastAnchorIndex - 1]).normalized * dstPrevToNewAnchor * .5f; + } + Vector3 secondControlForOldLastAnchor = points[lastAnchorIndex] + secondControlForOldLastAnchorOffset; + Vector3 controlForNewAnchor = (anchorPos + secondControlForOldLastAnchor) * .5f; + + points.Add(secondControlForOldLastAnchor); + points.Add(controlForNewAnchor); + points.Add(anchorPos); + perAnchorNormalsAngle.Add(perAnchorNormalsAngle[perAnchorNormalsAngle.Count - 1]); + + if (controlMode == ControlMode.Automatic) + { + AutoSetAllAffectedControlPoints(points.Count - 1); + } + + NotifyPathModified(); + } + + /// Add new anchor point to start of the path + public void AddSegmentToStart(Vector3 anchorPos) + { + if (isClosed) + { + return; + } + + // Set position for new control to be mirror of its counterpart + Vector3 secondControlForOldFirstAnchorOffset = (points[0] - points[1]); + if (controlMode != ControlMode.Mirrored && controlMode != ControlMode.Automatic) + { + // Set position for new control to be aligned with its counterpart, but with a length of half the distance from prev to new anchor + float dstPrevToNewAnchor = (points[0] - anchorPos).magnitude; + secondControlForOldFirstAnchorOffset = secondControlForOldFirstAnchorOffset.normalized * dstPrevToNewAnchor * .5f; + } + + Vector3 secondControlForOldFirstAnchor = points[0] + secondControlForOldFirstAnchorOffset; + Vector3 controlForNewAnchor = (anchorPos + secondControlForOldFirstAnchor) * .5f; + points.Insert(0, anchorPos); + points.Insert(1, controlForNewAnchor); + points.Insert(2, secondControlForOldFirstAnchor); + perAnchorNormalsAngle.Insert(0, perAnchorNormalsAngle[0]); + + if (controlMode == ControlMode.Automatic) + { + AutoSetAllAffectedControlPoints(0); + } + NotifyPathModified(); + } + + /// Insert new anchor point at given position. Automatically place control points around it so as to keep shape of curve the same + public void SplitSegment(Vector3 anchorPos, int segmentIndex, float splitTime) + { + if (float.IsNaN(splitTime)) + { + Debug.Log("Trying to split segment, but given value was invalid"); + return; + } + + splitTime = Mathf.Clamp01(splitTime); + + if (controlMode == ControlMode.Automatic) + { + points.InsertRange(segmentIndex * 3 + 2, new Vector3[] { Vector3.zero, anchorPos, Vector3.zero }); + AutoSetAllAffectedControlPoints(segmentIndex * 3 + 3); + } + else + { + // Split the curve to find where control points can be inserted to least affect shape of curve + // Curve will probably be deformed slightly since splitTime is only an estimate (for performance reasons, and so doesn't correspond exactly with anchorPos) + Vector3[][] splitSegment = CubicBezierUtility.SplitCurve(GetPointsInSegment(segmentIndex), splitTime); + points.InsertRange(segmentIndex * 3 + 2, new Vector3[] { splitSegment[0][2], splitSegment[1][0], splitSegment[1][1] }); + int newAnchorIndex = segmentIndex * 3 + 3; + MovePoint(newAnchorIndex - 2, splitSegment[0][1], true); + MovePoint(newAnchorIndex + 2, splitSegment[1][2], true); + MovePoint(newAnchorIndex, anchorPos, true); + + if (controlMode == ControlMode.Mirrored) + { + float avgDst = ((splitSegment[0][2] - anchorPos).magnitude + (splitSegment[1][1] - anchorPos).magnitude) / 2; + MovePoint(newAnchorIndex + 1, anchorPos + (splitSegment[1][1] - anchorPos).normalized * avgDst, true); + } + } + + // Insert angle for new anchor (value should be set inbetween neighbour anchor angles) + int newAnchorAngleIndex = (segmentIndex + 1) % perAnchorNormalsAngle.Count; + int numAngles = perAnchorNormalsAngle.Count; + float anglePrev = perAnchorNormalsAngle[segmentIndex]; + float angleNext = perAnchorNormalsAngle[newAnchorAngleIndex]; + float splitAngle = Mathf.LerpAngle(anglePrev, angleNext, splitTime); + perAnchorNormalsAngle.Insert(newAnchorAngleIndex, splitAngle); + + NotifyPathModified(); + } + + /// Delete the anchor point at given index, as well as its associated control points + public void DeleteSegment(int anchorIndex) + { + // Don't delete segment if its the last one remaining (or if only two segments in a closed path) + if (NumSegments > 2 || !isClosed && NumSegments > 1) + { + if (anchorIndex == 0) + { + if (isClosed) + { + points[points.Count - 1] = points[2]; + } + points.RemoveRange(0, 3); + } + else if (anchorIndex == points.Count - 1 && !isClosed) + { + points.RemoveRange(anchorIndex - 2, 3); + } + else + { + points.RemoveRange(anchorIndex - 1, 3); + } + + perAnchorNormalsAngle.RemoveAt(anchorIndex / 3); + + if (controlMode == ControlMode.Automatic) + { + AutoSetAllControlPoints(); + } + + NotifyPathModified(); + } + } + + /// Returns an array of the 4 points making up the segment (anchor1, control1, control2, anchor2) + public Vector3[] GetPointsInSegment(int segmentIndex) + { + segmentIndex = Mathf.Clamp(segmentIndex, 0, NumSegments - 1); + return new Vector3[] { this[segmentIndex * 3], this[segmentIndex * 3 + 1], this[segmentIndex * 3 + 2], this[LoopIndex(segmentIndex * 3 + 3)] }; + } + + /// Move an existing point to a new position + public void MovePoint(int i, Vector3 pointPos, bool suppressPathModifiedEvent = false) + { + + if (space == PathSpace.xy) + { + pointPos.z = 0; + } + else if (space == PathSpace.xz) + { + pointPos.y = 0; + } + Vector3 deltaMove = pointPos - points[i]; + bool isAnchorPoint = i % 3 == 0; + + // Don't process control point if control mode is set to automatic + if (isAnchorPoint || controlMode != ControlMode.Automatic) + { + points[i] = pointPos; + + if (controlMode == ControlMode.Automatic) + { + AutoSetAllAffectedControlPoints(i); + } + else + { + // Move control points with anchor point + if (isAnchorPoint) + { + if (i + 1 < points.Count || isClosed) + { + points[LoopIndex(i + 1)] += deltaMove; + } + if (i - 1 >= 0 || isClosed) + { + points[LoopIndex(i - 1)] += deltaMove; + } + } + // If not in free control mode, then move attached control point to be aligned/mirrored (depending on mode) + else if (controlMode != ControlMode.Free) + { + bool nextPointIsAnchor = (i + 1) % 3 == 0; + int attachedControlIndex = (nextPointIsAnchor) ? i + 2 : i - 2; + int anchorIndex = (nextPointIsAnchor) ? i + 1 : i - 1; + + if (attachedControlIndex >= 0 && attachedControlIndex < points.Count || isClosed) + { + float distanceFromAnchor = 0; + // If in aligned mode, then attached control's current distance from anchor point should be maintained + if (controlMode == ControlMode.Aligned) + { + distanceFromAnchor = (points[LoopIndex(anchorIndex)] - points[LoopIndex(attachedControlIndex)]).magnitude; + } + // If in mirrored mode, then both control points should have the same distance from the anchor point + else if (controlMode == ControlMode.Mirrored) + { + distanceFromAnchor = (points[LoopIndex(anchorIndex)] - points[i]).magnitude; + + } + Vector3 dir = (points[LoopIndex(anchorIndex)] - pointPos).normalized; + points[LoopIndex(attachedControlIndex)] = points[LoopIndex(anchorIndex)] + dir * distanceFromAnchor; + } + } + } + + if (!suppressPathModifiedEvent) + { + NotifyPathModified(); + } + } + } + + /// Update the bounding box of the path + public Bounds CalculateBoundsWithTransform(Transform transform) + { + // Loop through all segments and keep track of the minmax points of all their bounding boxes + MinMax3D minMax = new MinMax3D(); + + for (int i = 0; i < NumSegments; i++) + { + Vector3[] p = GetPointsInSegment(i); + for (int j = 0; j < p.Length; j++) + { + p[j] = MathUtility.TransformPoint(p[j], transform, space); + } + + minMax.AddValue(p[0]); + minMax.AddValue(p[3]); + + List extremePointTimes = CubicBezierUtility.ExtremePointTimes(p[0], p[1], p[2], p[3]); + foreach (float t in extremePointTimes) + { + minMax.AddValue(CubicBezierUtility.EvaluateCurve(p, t)); + } + } + + return new Bounds((minMax.Min + minMax.Max) / 2, minMax.Max - minMax.Min); + } + + /// Flip the normal vectors 180 degrees + public bool FlipNormals + { + get + { + return flipNormals; + } + set + { + if (flipNormals != value) + { + flipNormals = value; + NotifyPathModified(); + } + } + } + + /// Global angle that all normal vectors are rotated by (only relevant for paths in 3D space) + public float GlobalNormalsAngle + { + get + { + return globalNormalsAngle; + } + set + { + if (value != globalNormalsAngle) + { + globalNormalsAngle = value; + NotifyPathModified(); + } + } + } + + /// Get the desired angle of the normal vector at a particular anchor (only relevant for paths in 3D space) + public float GetAnchorNormalAngle(int anchorIndex) + { + return perAnchorNormalsAngle[anchorIndex] % 360; + } + + /// Set the desired angle of the normal vector at a particular anchor (only relevant for paths in 3D space) + public void SetAnchorNormalAngle(int anchorIndex, float angle) + { + angle = (angle + 360) % 360; + if (perAnchorNormalsAngle[anchorIndex] != angle) + { + perAnchorNormalsAngle[anchorIndex] = angle; + NotifyPathModified(); + } + } + + /// Reset global and anchor normal angles to 0 + public void ResetNormalAngles() + { + for (int i = 0; i < perAnchorNormalsAngle.Count; i++) + { + perAnchorNormalsAngle[i] = 0; + } + globalNormalsAngle = 0; + NotifyPathModified(); + } + + /// Bounding box containing the path + public Bounds PathBounds + { + get + { + if (!boundsUpToDate) + { + UpdateBounds(); + } + return bounds; + } + } + + #endregion + + #region Internal methods and accessors + + /// Update the bounding box of the path + void UpdateBounds() + { + if (boundsUpToDate) + { + return; + } + + // Loop through all segments and keep track of the minmax points of all their bounding boxes + MinMax3D minMax = new MinMax3D(); + + for (int i = 0; i < NumSegments; i++) + { + Vector3[] p = GetPointsInSegment(i); + minMax.AddValue(p[0]); + minMax.AddValue(p[3]); + + List extremePointTimes = CubicBezierUtility.ExtremePointTimes(p[0], p[1], p[2], p[3]); + foreach (float t in extremePointTimes) + { + minMax.AddValue(CubicBezierUtility.EvaluateCurve(p, t)); + } + } + + boundsUpToDate = true; + bounds = new Bounds((minMax.Min + minMax.Max) / 2, minMax.Max - minMax.Min); + } + + /// Determines good positions (for a smooth path) for the control points affected by a moved/inserted anchor point + void AutoSetAllAffectedControlPoints(int updatedAnchorIndex) + { + for (int i = updatedAnchorIndex - 3; i <= updatedAnchorIndex + 3; i += 3) + { + if (i >= 0 && i < points.Count || isClosed) + { + AutoSetAnchorControlPoints(LoopIndex(i)); + } + } + + AutoSetStartAndEndControls(); + } + + /// Determines good positions (for a smooth path) for all control points + void AutoSetAllControlPoints() + { + if (NumAnchorPoints > 2) + { + for (int i = 0; i < points.Count; i += 3) + { + AutoSetAnchorControlPoints(i); + } + } + + AutoSetStartAndEndControls(); + } + + /// Calculates good positions (to result in smooth path) for the controls around specified anchor + void AutoSetAnchorControlPoints(int anchorIndex) + { + // Calculate a vector that is perpendicular to the vector bisecting the angle between this anchor and its two immediate neighbours + // The control points will be placed along that vector + Vector3 anchorPos = points[anchorIndex]; + Vector3 dir = Vector3.zero; + float[] neighbourDistances = new float[2]; + + if (anchorIndex - 3 >= 0 || isClosed) + { + Vector3 offset = points[LoopIndex(anchorIndex - 3)] - anchorPos; + dir += offset.normalized; + neighbourDistances[0] = offset.magnitude; + } + if (anchorIndex + 3 >= 0 || isClosed) + { + Vector3 offset = points[LoopIndex(anchorIndex + 3)] - anchorPos; + dir -= offset.normalized; + neighbourDistances[1] = -offset.magnitude; + } + + dir.Normalize(); + + // Set the control points along the calculated direction, with a distance proportional to the distance to the neighbouring control point + for (int i = 0; i < 2; i++) + { + int controlIndex = anchorIndex + i * 2 - 1; + if (controlIndex >= 0 && controlIndex < points.Count || isClosed) + { + points[LoopIndex(controlIndex)] = anchorPos + dir * neighbourDistances[i] * autoControlLength; + } + } + } + + /// Determines good positions (for a smooth path) for the control points at the start and end of a path + void AutoSetStartAndEndControls() + { + if (isClosed) + { + // Handle case with only 2 anchor points separately, as will otherwise result in straight line () + if (NumAnchorPoints == 2) + { + Vector3 dirAnchorAToB = (points[3] - points[0]).normalized; + float dstBetweenAnchors = (points[0] - points[3]).magnitude; + Vector3 perp = Vector3.Cross(dirAnchorAToB, (space == PathSpace.xy) ? Vector3.forward : Vector3.up); + points[1] = points[0] + perp * dstBetweenAnchors / 2f; + points[5] = points[0] - perp * dstBetweenAnchors / 2f; + points[2] = points[3] + perp * dstBetweenAnchors / 2f; + points[4] = points[3] - perp * dstBetweenAnchors / 2f; + + } + else + { + AutoSetAnchorControlPoints(0); + AutoSetAnchorControlPoints(points.Count - 3); + } + } + else + { + // Handle case with 2 anchor points separately, as otherwise minor adjustments cause path to constantly flip + if (NumAnchorPoints == 2) + { + points[1] = points[0] + (points[3] - points[0]) * .25f; + points[2] = points[3] + (points[0] - points[3]) * .25f; + } + else + { + points[1] = (points[0] + points[2]) * .5f; + points[points.Count - 2] = (points[points.Count - 1] + points[points.Count - 3]) * .5f; + } + } + } + + /// Update point positions for new path space + /// (for example, if changing from xy to xz path, y and z axes will be swapped so the path keeps its shape in the new space) + void UpdateToNewPathSpace(PathSpace previousSpace) + { + // If changing from 3d to 2d space, first find the bounds of the 3d path. + // The axis with the smallest bounds will be discarded. + if (previousSpace == PathSpace.xyz) + { + Vector3 boundsSize = PathBounds.size; + float minBoundsSize = Mathf.Min(boundsSize.x, boundsSize.y, boundsSize.z); + + for (int i = 0; i < NumPoints; i++) + { + if (space == PathSpace.xy) + { + float x = (minBoundsSize == boundsSize.x) ? points[i].z : points[i].x; + float y = (minBoundsSize == boundsSize.y) ? points[i].z : points[i].y; + points[i] = new Vector3(x, y, 0); + } + else if (space == PathSpace.xz) + { + float x = (minBoundsSize == boundsSize.x) ? points[i].y : points[i].x; + float z = (minBoundsSize == boundsSize.z) ? points[i].y : points[i].z; + points[i] = new Vector3(x, 0, z); + } + } + } + else + { + // Nothing needs to change when going to 3d space + if (space != PathSpace.xyz) + { + for (int i = 0; i < NumPoints; i++) + { + // from xz to xy + if (space == PathSpace.xy) + { + points[i] = new Vector3(points[i].x, points[i].z, 0); + } + // from xy to xz + else if (space == PathSpace.xz) + { + points[i] = new Vector3(points[i].x, 0, points[i].y); + } + } + } + } + + NotifyPathModified(); + } + + /// Add/remove the extra 2 controls required for a closed path + void UpdateClosedState() + { + if (isClosed) + { + // Set positions for new controls to mirror their counterparts + Vector3 lastAnchorSecondControl = points[points.Count - 1] * 2 - points[points.Count - 2]; + Vector3 firstAnchorSecondControl = points[0] * 2 - points[1]; + if (controlMode != ControlMode.Mirrored && controlMode != ControlMode.Automatic) + { + // Set positions for new controls to be aligned with their counterparts, but with a length of half the distance between start/end anchor + float dstBetweenStartAndEndAnchors = (points[points.Count - 1] - points[0]).magnitude; + lastAnchorSecondControl = points[points.Count - 1] + (points[points.Count - 1] - points[points.Count - 2]).normalized * dstBetweenStartAndEndAnchors * .5f; + firstAnchorSecondControl = points[0] + (points[0] - points[1]).normalized * dstBetweenStartAndEndAnchors * .5f; + } + points.Add(lastAnchorSecondControl); + points.Add(firstAnchorSecondControl); + } + else + { + points.RemoveRange(points.Count - 2, 2); + + } + + if (controlMode == ControlMode.Automatic) + { + AutoSetStartAndEndControls(); + } + + if (OnModified != null) + { + OnModified(); + } + } + + /// Loop index around to start/end of points array if out of bounds (useful when working with closed paths) + int LoopIndex(int i) + { + return (i + points.Count) % points.Count; + } + + // Called when the path is modified + public void NotifyPathModified() + { + boundsUpToDate = false; + if (OnModified != null) + { + OnModified(); + } + } + + #endregion + + } } \ No newline at end of file diff --git a/Packages/manifest.json b/Packages/manifest.json index beef566..979d434 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -1,12 +1,12 @@ { "dependencies": { - "com.unity.collab-proxy": "1.5.7", - "com.unity.ide.rider": "2.0.7", - "com.unity.ide.visualstudio": "2.0.8", - "com.unity.ide.vscode": "1.2.3", - "com.unity.test-framework": "1.1.24", + "com.unity.collab-proxy": "1.15.16", + "com.unity.ide.rider": "3.0.13", + "com.unity.ide.visualstudio": "2.0.14", + "com.unity.ide.vscode": "1.2.5", + "com.unity.test-framework": "1.1.31", "com.unity.textmeshpro": "3.0.6", - "com.unity.timeline": "1.4.8", + "com.unity.timeline": "1.6.4", "com.unity.ugui": "1.0.0", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index ced5225..1424e5c 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -1,11 +1,11 @@ { "dependencies": { "com.unity.collab-proxy": { - "version": "1.5.7", + "version": "1.15.16", "depth": 0, "source": "registry", "dependencies": { - "com.unity.nuget.newtonsoft-json": "2.0.0" + "com.unity.services.core": "1.0.1" }, "url": "https://packages.unity.com" }, @@ -17,16 +17,16 @@ "url": "https://packages.unity.com" }, "com.unity.ide.rider": { - "version": "2.0.7", + "version": "3.0.13", "depth": 0, "source": "registry", "dependencies": { - "com.unity.test-framework": "1.1.1" + "com.unity.ext.nunit": "1.0.6" }, "url": "https://packages.unity.com" }, "com.unity.ide.visualstudio": { - "version": "2.0.8", + "version": "2.0.14", "depth": 0, "source": "registry", "dependencies": { @@ -35,21 +35,32 @@ "url": "https://packages.unity.com" }, "com.unity.ide.vscode": { - "version": "1.2.3", + "version": "1.2.5", "depth": 0, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, "com.unity.nuget.newtonsoft-json": { - "version": "2.0.0", - "depth": 1, + "version": "3.0.2", + "depth": 2, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, + "com.unity.services.core": { + "version": "1.3.1", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.nuget.newtonsoft-json": "3.0.2", + "com.unity.modules.androidjni": "1.0.0" + }, + "url": "https://packages.unity.com" + }, "com.unity.test-framework": { - "version": "1.1.24", + "version": "1.1.31", "depth": 0, "source": "registry", "dependencies": { @@ -69,7 +80,7 @@ "url": "https://packages.unity.com" }, "com.unity.timeline": { - "version": "1.4.8", + "version": "1.6.4", "depth": 0, "source": "registry", "dependencies": { diff --git a/ProjectSettings/MemorySettings.asset b/ProjectSettings/MemorySettings.asset new file mode 100644 index 0000000..5b5face --- /dev/null +++ b/ProjectSettings/MemorySettings.asset @@ -0,0 +1,35 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!387306366 &1 +MemorySettings: + m_ObjectHideFlags: 0 + m_EditorMemorySettings: + m_MainAllocatorBlockSize: -1 + m_ThreadAllocatorBlockSize: -1 + m_MainGfxBlockSize: -1 + m_ThreadGfxBlockSize: -1 + m_CacheBlockSize: -1 + m_TypetreeBlockSize: -1 + m_ProfilerBlockSize: -1 + m_ProfilerEditorBlockSize: -1 + m_BucketAllocatorGranularity: -1 + m_BucketAllocatorBucketsCount: -1 + m_BucketAllocatorBlockSize: -1 + m_BucketAllocatorBlockCount: -1 + m_ProfilerBucketAllocatorGranularity: -1 + m_ProfilerBucketAllocatorBucketsCount: -1 + m_ProfilerBucketAllocatorBlockSize: -1 + m_ProfilerBucketAllocatorBlockCount: -1 + m_TempAllocatorSizeMain: -1 + m_JobTempAllocatorBlockSize: -1 + m_BackgroundJobTempAllocatorBlockSize: -1 + m_JobTempAllocatorReducedBlockSize: -1 + m_TempAllocatorSizeGIBakingWorker: -1 + m_TempAllocatorSizeNavMeshWorker: -1 + m_TempAllocatorSizeAudioWorker: -1 + m_TempAllocatorSizeCloudWorker: -1 + m_TempAllocatorSizeGfx: -1 + m_TempAllocatorSizeJobWorker: -1 + m_TempAllocatorSizeBackgroundWorker: -1 + m_TempAllocatorSizePreloadManager: -1 + m_PlatformMemorySettings: {} diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt index 26fc67f..3dcb827 100644 --- a/ProjectSettings/ProjectVersion.txt +++ b/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2020.3.10f1 -m_EditorVersionWithRevision: 2020.3.10f1 (297d780c91bc) +m_EditorVersion: 2021.3.2f1 +m_EditorVersionWithRevision: 2021.3.2f1 (d6360bedb9a0) diff --git a/ProjectSettings/boot.config b/ProjectSettings/boot.config new file mode 100644 index 0000000..e69de29 From 424201a44ea029bb787cc7af2f264727c95ebea8 Mon Sep 17 00:00:00 2001 From: Sebastian Lague Date: Tue, 24 May 2022 22:32:22 +0200 Subject: [PATCH 11/12] Update .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 58cbc82..16bc830 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # # Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore # + /[Ll]ibrary/ /[Tt]emp/ /[Oo]bj/ @@ -23,6 +24,9 @@ # Autogenerated Jetbrains Rider plugin /[Aa]ssets/Plugins/Editor/JetBrains* +# Visual Studio Code +.vscode/ + # Visual Studio cache directory .vs/ From 5fc74d2f6a87280d1608c63536f2bc75fcd216a4 Mon Sep 17 00:00:00 2001 From: Riccardo Di Nuzzo Date: Thu, 4 May 2023 14:38:38 +0100 Subject: [PATCH 12/12] add default constructor to initialise bezierpath instance --- .../PathCreator/Core/Runtime/Objects/BezierPath.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs b/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs index dbff8b6..10147ac 100644 --- a/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs +++ b/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs @@ -46,14 +46,15 @@ public enum ControlMode { Aligned, Mirrored, Free, Automatic }; [SerializeField, HideInInspector] bool flipNormals; - #endregion + #endregion - #region Constructors + #region Constructors + public BezierPath() : this(Vector3.zero, false, PathSpace.xyz) { } - /// Creates a two-anchor path centred around the given centre point - /// Should the end point connect back to the start point? - /// Determines if the path is in 3d space, or clamped to the xy/xz plane - public BezierPath(Vector3 centre, bool isClosed = false, PathSpace space = PathSpace.xyz) + /// Creates a two-anchor path centred around the given centre point + /// Should the end point connect back to the start point? + /// Determines if the path is in 3d space, or clamped to the xy/xz plane + public BezierPath(Vector3 centre, bool isClosed = false, PathSpace space = PathSpace.xyz) { Vector3 dir = (space == PathSpace.xz) ? Vector3.forward : Vector3.up;