diff --git a/.gitignore b/.gitignore index 833e6d4..16bc830 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,38 @@ -[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 Code +.vscode/ # Visual Studio cache directory .vs/ +# Gradle cache directory +.gradle/ + # Autogenerated VS/MD/Consulo solution and project files ExportedObj/ .consulo/ @@ -22,16 +47,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 51% rename from Path Creator Project/Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs rename to Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs index 0ae6b33..1f7aa53 --- a/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/MouseUtility.cs +++ b/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 +} 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/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs b/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs new file mode 100644 index 0000000..0e5ff89 --- /dev/null +++ b/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs @@ -0,0 +1,243 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using PathCreation; + +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.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/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/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs b/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs new file mode 100644 index 0000000..b16b792 --- /dev/null +++ b/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs @@ -0,0 +1,219 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using PathCreation; +using PathCreation.Utility; + +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 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/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/Assets/PathCreator/Core/Editor/PathEditor.cs b/Assets/PathCreator/Core/Editor/PathEditor.cs new file mode 100644 index 0000000..2897759 --- /dev/null +++ b/Assets/PathCreator/Core/Editor/PathEditor.cs @@ -0,0 +1,878 @@ +using System.Collections.Generic; +using PathCreation; +using PathCreation.Utility; +using UnityEditor; +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(); + 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/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/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs b/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs new file mode 100644 index 0000000..10147ac --- /dev/null +++ b/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs @@ -0,0 +1,830 @@ +using System.Collections.Generic; +using System.Linq; +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 + 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) + { + + 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) + { + 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/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 92% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs rename to Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs index 5fd6e6b..fb63fb8 --- a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/VertexPath.cs +++ b/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; @@ -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); @@ -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/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 53% rename from Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs rename to Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs index af862ba..a476689 --- a/Path Creator Project/Assets/PathCreator/Core/Runtime/Utility/MathUtility.cs +++ b/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); + } } } } 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/Assets/PathCreator/Examples/Scenes/Mesh Examples.meta b/Assets/PathCreator/Examples/Scenes/Mesh Examples.meta new file mode 100644 index 0000000..192a200 --- /dev/null +++ b/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/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity b/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity new file mode 100644 index 0000000..e51a845 --- /dev/null +++ b/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/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity.meta b/Assets/PathCreator/Examples/Scenes/Mesh Examples/Cylinder.unity.meta new file mode 100644 index 0000000..db9155f --- /dev/null +++ b/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/Assets/PathCreator/Examples/Scenes/Mesh Examples/Road.unity similarity index 100% rename from Path Creator Project/Assets/PathCreator/Examples/Scenes/Road.unity rename to Assets/PathCreator/Examples/Scenes/Mesh Examples/Road.unity diff --git a/Path Creator Project/Assets/PathCreator/Examples/Scenes/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/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/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs b/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs new file mode 100644 index 0000000..1d4e484 --- /dev/null +++ b/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/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs.meta b/Assets/PathCreator/Examples/Scripts/CylinderMeshCreator.cs.meta new file mode 100644 index 0000000..a62a487 --- /dev/null +++ b/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: 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/Packages/manifest.json b/Packages/manifest.json new file mode 100644 index 0000000..979d434 --- /dev/null +++ b/Packages/manifest.json @@ -0,0 +1,43 @@ +{ + "dependencies": { + "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.6.4", + "com.unity.ugui": "1.0.0", + "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/Packages/packages-lock.json b/Packages/packages-lock.json new file mode 100644 index 0000000..1424e5c --- /dev/null +++ b/Packages/packages-lock.json @@ -0,0 +1,358 @@ +{ + "dependencies": { + "com.unity.collab-proxy": { + "version": "1.15.16", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.services.core": "1.0.1" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ext.nunit": { + "version": "1.0.6", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.ide.rider": { + "version": "3.0.13", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.6" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ide.visualstudio": { + "version": "2.0.14", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.1.9" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ide.vscode": { + "version": "1.2.5", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.nuget.newtonsoft-json": { + "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.31", + "depth": 0, + "source": "registry", + "dependencies": { + "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": "3.0.6", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.timeline": { + "version": "1.6.4", + "depth": 0, + "source": "registry", + "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": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0" + } + }, + "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.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" + } + }, + "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/.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/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs b/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs deleted file mode 100755 index 613fdb8..0000000 --- a/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/PathHandle.cs +++ /dev/null @@ -1,237 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; -using UnityEditor; -using PathCreation; - -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]; - } - } -} \ No newline at end of file diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs b/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs deleted file mode 100755 index 50b28a2..0000000 --- a/Path Creator Project/Assets/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; -using UnityEditor; -using PathCreation; -using PathCreation.Utility; - -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; - } - } - } -} \ No newline at end of file diff --git a/Path Creator Project/Assets/PathCreator/Core/Editor/PathEditor.cs b/Path Creator Project/Assets/PathCreator/Core/Editor/PathEditor.cs deleted file mode 100755 index 7c52cc6..0000000 --- a/Path Creator Project/Assets/PathCreator/Core/Editor/PathEditor.cs +++ /dev/null @@ -1,736 +0,0 @@ -using System.Collections.Generic; -using PathCreation; -using PathCreation.Utility; -using UnityEditor; -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) { - // 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); - - Undo.RecordObject (creator, "Add segment"); - if (e.control || e.command) { - bezierPath.AddSegmentToStart (newPathPoint); - } else { - bezierPath.AddSegmentToEnd (newPathPoint); - } - - } - - } - } - - // 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 - - } - -} \ No newline at end of file diff --git a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs b/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs deleted file mode 100755 index a3912b2..0000000 --- a/Path Creator Project/Assets/PathCreator/Core/Runtime/Objects/BezierPath.cs +++ /dev/null @@ -1,678 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -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 { - 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 - - } -} \ 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 6c678fb..0000000 --- a/Path Creator Project/Logs/Packages-Update.log +++ /dev/null @@ -1,90 +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 diff --git a/Path Creator Project/Packages/manifest.json b/Path Creator Project/Packages/manifest.json deleted file mode 100644 index 526aca6..0000000 --- a/Path Creator Project/Packages/manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "dependencies": { - } -} diff --git a/Path Creator Project/ProjectSettings/ProjectVersion.txt b/Path Creator Project/ProjectSettings/ProjectVersion.txt deleted file mode 100644 index 8f3d813..0000000 --- a/Path Creator Project/ProjectSettings/ProjectVersion.txt +++ /dev/null @@ -1 +0,0 @@ -m_EditorVersion: 2018.1.9f2 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 68% rename from Path Creator Project/ProjectSettings/EditorSettings.asset rename to ProjectSettings/EditorSettings.asset index a01cbc8..ab517c4 100644 --- a/Path Creator Project/ProjectSettings/EditorSettings.asset +++ b/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/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/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/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..3dcb827 --- /dev/null +++ b/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 2021.3.2f1 +m_EditorVersionWithRevision: 2021.3.2f1 (d6360bedb9a0) 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 diff --git a/ProjectSettings/boot.config b/ProjectSettings/boot.config new file mode 100644 index 0000000..e69de29