diff --git a/.idea/.idea.OpenMEP/.idea/.name b/.idea/.idea.OpenMEP/.idea/.name new file mode 100644 index 00000000..794249ec --- /dev/null +++ b/.idea/.idea.OpenMEP/.idea/.name @@ -0,0 +1 @@ +OpenMEP \ No newline at end of file diff --git a/OpenMEPSandbox/Algo/TravellingSaleman.cs b/OpenMEPSandbox/Algo/TravellingSaleman.cs new file mode 100644 index 00000000..9864cf26 --- /dev/null +++ b/OpenMEPSandbox/Algo/TravellingSaleman.cs @@ -0,0 +1,109 @@ +using Autodesk.DesignScript.Geometry; +using Autodesk.DesignScript.Runtime; + +namespace OpenMEPSandbox.Algo; + +[IsVisibleInDynamoLibrary(false)] +public static class TravellingSalesman +{ + + /// + /// takes a list of 3D points as input and returns the shortest route that visits each point exactly once + /// + /// + /// + public static List FindShortestRoute(List points) + { + List shortestRoute = null; + double shortestDistance = double.MaxValue; + + // Generate all possible permutations of the points + List> permutations = Permute(points); + + // For each permutation, calculate the total distance and keep track of the shortest route + foreach (List route in permutations) + { + double totalDistance = CalculateTotalDistance(route); + if (totalDistance < shortestDistance) + { + shortestDistance = totalDistance; + shortestRoute = route; + } + } + + return shortestRoute; + } + + /// + /// takes a list of 3D points as input and returns a list of all possible permutations of the input list + /// + /// + /// + static List> Permute(List points) + { + List> result = new List>(); + PermuteHelper(points, 0, result); + return result; + } + + /// + /// takes three arguments: the input list of points, the current index to swap, and the list to store the permutations + /// + /// + /// + /// + static void PermuteHelper(List points, int index, List> result) + { + if (index >= points.Count) + { + result.Add(new List(points)); + } + else + { + for (int i = index; i < points.Count; i++) + { + Swap(points, index, i); + PermuteHelper(points, index + 1, result); + Swap(points, index, i); + } + } + } + + /// + /// takes a list of points and two indices and swaps the elements at those indices in the list. + /// + /// + /// + /// + static void Swap(List points, int i, int j) + { + (points[i], points[j]) = (points[j], points[i]); + } + + static double CalculateTotalDistance(List route) + { + double totalDistance = 0; + for (int i = 0; i < route.Count - 1; i++) + { + totalDistance += CalculateDistance(route[i], route[i + 1]); + } + + totalDistance += CalculateDistance(route[route.Count - 1], route[0]); + return totalDistance; + } + + /// + /// calculates the distance between two points by using euclidean distance formula + /// + /// the first point + /// the second point + /// distance between two point + static double CalculateDistance(Autodesk.DesignScript.Geometry.Point point1, + Autodesk.DesignScript.Geometry.Point point2) + { + double dx = point2.X - point1.X; + double dy = point2.Y - point1.Y; + double dz = point2.Z - point1.Z; + return Math.Sqrt(dx * dx + dy * dy + dz * dz); + } +} \ No newline at end of file diff --git a/OpenMEPSandbox/Geometry/Point.cs b/OpenMEPSandbox/Geometry/Point.cs index f764323f..4f53bd2d 100644 --- a/OpenMEPSandbox/Geometry/Point.cs +++ b/OpenMEPSandbox/Geometry/Point.cs @@ -432,4 +432,27 @@ public static double CompareTo(Autodesk.DesignScript.Geometry.Point point1, int compareTo = point1.ToGSharkType().CompareTo(point2.ToGSharkType()); return compareTo; } + + /// + /// takes a list of 3D points as input and returns the shortest route that visits each point exactly once' + /// https://en.wikipedia.org/wiki/Travelling_salesman_problem + /// + /// the list 3d points + /// shortest route + /// + /// ![](../OpenMEPPage/geometry/dyn/pic/Point.FindShortestRoute.gif) + /// + public static List FindShortestRoute(List points) + { + List shortestRoute = TravellingSalesman.FindShortestRoute(points); + // connect line + List lines = new List(); + for (int i = 0; i < shortestRoute.Count - 1; i++) + { + Autodesk.DesignScript.Geometry.Line line = + Autodesk.DesignScript.Geometry.Line.ByStartPointEndPoint(shortestRoute[i], shortestRoute[i + 1]); + lines.Add(line); + } + return lines; + } } \ No newline at end of file diff --git a/docs/OpenMEPPage/geometry/dyn/Point.FindShortestRoute.dyn b/docs/OpenMEPPage/geometry/dyn/Point.FindShortestRoute.dyn new file mode 100644 index 00000000..15377059 --- /dev/null +++ b/docs/OpenMEPPage/geometry/dyn/Point.FindShortestRoute.dyn @@ -0,0 +1,324 @@ +{ + "Uuid": "ede9f09b-0e32-4cf4-9caa-71e08792c623", + "IsCustomNode": false, + "Description": "", + "Name": "Point.FindShortestRoute", + "ElementResolver": { + "ResolutionMap": { + "Autodesk.Point": { + "Key": "Autodesk.DesignScript.Geometry.Point", + "Value": "ProtoGeometry.dll" + }, + "Autodesk.DesignScript.Geometry.Point": { + "Key": "Autodesk.DesignScript.Geometry.Point", + "Value": "ProtoGeometry.dll" + } + } + }, + "Inputs": [], + "Outputs": [], + "Nodes": [ + { + "ConcreteType": "Dynamo.Graph.Nodes.ZeroTouch.DSFunction, DynamoCore", + "Id": "f0d8492c9c46470bbd11755d95060c51", + "NodeType": "FunctionNode", + "Inputs": [ + { + "Id": "0b800a9c796b4335a0c6658a02f9dc77", + "Name": "points", + "Description": "the list 3d points\n\nPoint[]", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "0c7b2d2372ca469d9003faf79156dd0b", + "Name": "lines", + "Description": "shortest route", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "FunctionSignature": "OpenMEPSandbox.Geometry.Point.FindShortestRoute@Autodesk.DesignScript.Geometry.Point[]", + "Replication": "Auto", + "Description": "takes a list of 3D points as input and returns the shortest route that visits each point exactly once' https://en.wikipedia.org/wiki/Travelling_salesman_problem\n\nPoint.FindShortestRoute (points: Point[]): Line[]" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CodeBlockNodeModel, DynamoCore", + "Id": "0d145102a0624e9b9360705884f4d9ca", + "NodeType": "CodeBlockNode", + "Inputs": [], + "Outputs": [ + { + "Id": "f6ba902e55ba4f1886924eb0b8731366", + "Name": "", + "Description": "Value of expression at line 1", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "82e1b2e58aca4678ae967b8531d91796", + "Name": "", + "Description": "Value of expression at line 2", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "6e0e33b9d1c7428a9c9e1338b690bbb0", + "Name": "", + "Description": "Value of expression at line 3", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "42a5367ef6074cd0937634d73f79de3f", + "Name": "", + "Description": "Value of expression at line 4", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "50d79e44d5f04cabbbaa1b61c25cdc3c", + "Name": "", + "Description": "Value of expression at line 5", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "806635c5731d4ea6b6969e2892008e23", + "Name": "", + "Description": "Value of expression at line 6", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Allows for DesignScript code to be authored directly", + "Code": "Autodesk.Point.ByCoordinates(0,0,0);\nAutodesk.Point.ByCoordinates(0,5,0);\nAutodesk.Point.ByCoordinates(5,5,0);\nAutodesk.Point.ByCoordinates(5,2,0);\nAutodesk.Point.ByCoordinates(2,0,0);\nAutodesk.Point.ByCoordinates(2,4,0);" + }, + { + "ConcreteType": "CoreNodeModels.CreateList, CoreNodeModels", + "VariableInputPorts": true, + "Id": "00a91c25b26b4e58b9d64e0325b4d6d3", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "1f0e54bd5b274730b1a8f8d73cbbce86", + "Name": "item0", + "Description": "Item Index #0", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "637feeeccd964fef93113db41c224118", + "Name": "item1", + "Description": "Item Index #1", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "14bd760ca1c843eb96c57f7014b93d38", + "Name": "item2", + "Description": "Item Index #2", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "d2e2388d856346a3b5c349b427a0b50d", + "Name": "item3", + "Description": "Item Index #3", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "cc1d927822a9498a9f9683017f5dd851", + "Name": "item4", + "Description": "Item Index #4", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "91402a60e71046578cb4d9509dfb5bac", + "Name": "item5", + "Description": "Item Index #5", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "d7143d61192f43a18ab648d5f4b98091", + "Name": "list", + "Description": "A list (type: var[]..[])", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Makes a new list from the given inputs" + } + ], + "Connectors": [ + { + "Start": "f6ba902e55ba4f1886924eb0b8731366", + "End": "1f0e54bd5b274730b1a8f8d73cbbce86", + "Id": "ed0d33587178492d959a2cb19c97c369", + "IsHidden": "False" + }, + { + "Start": "82e1b2e58aca4678ae967b8531d91796", + "End": "637feeeccd964fef93113db41c224118", + "Id": "717de5bec4c74e39b875f062fbd24092", + "IsHidden": "False" + }, + { + "Start": "6e0e33b9d1c7428a9c9e1338b690bbb0", + "End": "cc1d927822a9498a9f9683017f5dd851", + "Id": "363ca047293d4fcd87341eb5ed85c56c", + "IsHidden": "False" + }, + { + "Start": "42a5367ef6074cd0937634d73f79de3f", + "End": "d2e2388d856346a3b5c349b427a0b50d", + "Id": "c48b7a2dcbea4ae08917b8fccef1c631", + "IsHidden": "False" + }, + { + "Start": "50d79e44d5f04cabbbaa1b61c25cdc3c", + "End": "14bd760ca1c843eb96c57f7014b93d38", + "Id": "c59babffa8e348a89faf780ed6137c67", + "IsHidden": "False" + }, + { + "Start": "806635c5731d4ea6b6969e2892008e23", + "End": "91402a60e71046578cb4d9509dfb5bac", + "Id": "d6a1c3b02c494b64a5d8b889c5ac1b77", + "IsHidden": "False" + }, + { + "Start": "d7143d61192f43a18ab648d5f4b98091", + "End": "0b800a9c796b4335a0c6658a02f9dc77", + "Id": "00b51307133540b09d2c6817b921ae26", + "IsHidden": "False" + } + ], + "Dependencies": [], + "NodeLibraryDependencies": [ + { + "Name": "OpenMEP", + "Version": "1.0.0", + "ReferenceType": "Package", + "Nodes": [ + "f0d8492c9c46470bbd11755d95060c51" + ] + } + ], + "Thumbnail": "", + "GraphDocumentationURL": null, + "ExtensionWorkspaceData": [ + { + "ExtensionGuid": "28992e1d-abb9-417f-8b1b-05e053bee670", + "Name": "Properties", + "Version": "2.18", + "Data": {} + } + ], + "Author": "", + "Linting": { + "activeLinter": "None", + "activeLinterId": "7b75fb44-43fd-4631-a878-29f4d5d8399a", + "warningCount": 0, + "errorCount": 0 + }, + "Bindings": [], + "View": { + "Dynamo": { + "ScaleFactor": 1.0, + "HasRunWithoutCrash": true, + "IsVisibleInDynamoLibrary": true, + "Version": "2.18.0.2986", + "RunType": "Automatic", + "RunPeriod": "1000" + }, + "Camera": { + "Name": "_Background Preview", + "EyeX": 4.1163740158081055, + "EyeY": 11.711480140686035, + "EyeZ": -4.6511685848236084, + "LookX": -0.29294729232788086, + "LookY": -11.582551956176758, + "LookZ": -0.58217883110046387, + "UpX": -0.43292644619941711, + "UpY": 0.26892164349555969, + "UpZ": -0.86038118600845337 + }, + "ConnectorPins": [], + "NodeViews": [ + { + "Id": "f0d8492c9c46470bbd11755d95060c51", + "Name": "Point.FindShortestRoute", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 675.4272957172451, + "Y": -35.555634987164353 + }, + { + "Id": "0d145102a0624e9b9360705884f4d9ca", + "Name": "Code Block", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": -96.96352347258852, + "Y": -80.3031184014437 + }, + { + "Id": "00a91c25b26b4e58b9d64e0325b4d6d3", + "Name": "List Create", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 413.63838848029928, + "Y": -36.427648472860483 + } + ], + "Annotations": [], + "X": 197.38466150851013, + "Y": 126.77739430526466, + "Zoom": 1.0485066619380725 + } +} \ No newline at end of file diff --git a/docs/OpenMEPPage/geometry/dyn/pic/Point.FindShortestRoute.gif b/docs/OpenMEPPage/geometry/dyn/pic/Point.FindShortestRoute.gif new file mode 100644 index 00000000..6c490119 Binary files /dev/null and b/docs/OpenMEPPage/geometry/dyn/pic/Point.FindShortestRoute.gif differ