From 33380f518efd2baf1132e887179661972b08a805 Mon Sep 17 00:00:00 2001 From: beverlylytle <57254617+beverlylytle@users.noreply.github.com> Date: Wed, 31 Mar 2021 09:13:40 +0200 Subject: [PATCH 1/2] Test new PR flow --- lecture_05/assignment_04/lytle/hello.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 lecture_05/assignment_04/lytle/hello.txt diff --git a/lecture_05/assignment_04/lytle/hello.txt b/lecture_05/assignment_04/lytle/hello.txt new file mode 100644 index 0000000..5dd01c1 --- /dev/null +++ b/lecture_05/assignment_04/lytle/hello.txt @@ -0,0 +1 @@ +Hello, world! \ No newline at end of file From 15e188366f9420588787e72193485c8e48c6a20d Mon Sep 17 00:00:00 2001 From: g-jami <72041472+g-jami@users.noreply.github.com> Date: Tue, 20 Apr 2021 20:27:29 +0200 Subject: [PATCH 2/2] Assignment_05_GJ --- .../Assignment_04_pickandplace_GJ.png | Bin 0 -> 82529 bytes .../assignment_05/Guillaume Jami/assembly.py | 81 + .../Guillaume Jami/assembly_GJ.json | 11859 ++++++++++++++++ .../Guillaume Jami/assignment_04.py | 7 + .../Guillaume Jami/assignment_05_GJ.ghx | 11653 +++++++++++++++ .../assignment_05/Guillaume Jami/model.py | 441 + .../Guillaume Jami/simple_comm.py | 136 + .../Guillaume Jami/simple_ur_script.py | 193 + .../assignment_05/Guillaume Jami/utils.py | 236 + .../Guillaume Jami/vacuum_gripper.stl | Bin 0 -> 9884 bytes 10 files changed, 24606 insertions(+) create mode 100644 lecture_06/assignment_05/Guillaume Jami/Assignment_04_pickandplace_GJ.png create mode 100644 lecture_06/assignment_05/Guillaume Jami/assembly.py create mode 100644 lecture_06/assignment_05/Guillaume Jami/assembly_GJ.json create mode 100644 lecture_06/assignment_05/Guillaume Jami/assignment_04.py create mode 100644 lecture_06/assignment_05/Guillaume Jami/assignment_05_GJ.ghx create mode 100644 lecture_06/assignment_05/Guillaume Jami/model.py create mode 100644 lecture_06/assignment_05/Guillaume Jami/simple_comm.py create mode 100644 lecture_06/assignment_05/Guillaume Jami/simple_ur_script.py create mode 100644 lecture_06/assignment_05/Guillaume Jami/utils.py create mode 100644 lecture_06/assignment_05/Guillaume Jami/vacuum_gripper.stl diff --git a/lecture_06/assignment_05/Guillaume Jami/Assignment_04_pickandplace_GJ.png b/lecture_06/assignment_05/Guillaume Jami/Assignment_04_pickandplace_GJ.png new file mode 100644 index 0000000000000000000000000000000000000000..8d8c7109f9eafd9a05960d0d3f15ebebc48d2ba3 GIT binary patch literal 82529 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 + + + + + + + 0 + 2 + 2 + + + + + + + 1 + 0 + 7 + + + + + + c4f913db-409d-41a9-88e1-f6f90989047d + Shaded + 1 + + 100;150;0;0 + + + 100;0;150;0 + + + + + + 637527254194888879 + + assignment_05_GJ.ghx + + + + + 0 + + + + + + -649 + -36 + + 0.869564235 + + + + + 0 + + + + + + + 0 + + + + + 1 + + + + + GhPython, Version=6.29.20238.11501, Culture=neutral, PublicKeyToken=null + 6.29.20238.11501 + + 00000000-0000-0000-0000-000000000000 + + + + + + + + + 105 + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + 5937d47f-10eb-43c2-b0f8-c788d579c8db + 76c149ba-07e7-470c-8952-7038c2cb9b2d + 2 + d2f1c6ac-f289-4bf1-8d1a-fe567b650ce6 + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + a003596c-fc8d-4ac5-a6f1-fd7555dd239b + 781902aa-fb33-4d03-bad8-fce4aafa0416 + 2c6392f9-9507-48f4-b75d-443af7101485 + 3 + c449f738-48e0-463f-84c2-3de642e2c783 + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + 19ea3000-8a91-4bca-8866-792502605bcd + d3765dd9-51b9-48b1-8583-25508339aed8 + 2 + f0abb7d1-5703-409d-aeeb-f17578617f22 + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + 7ab9dd8f-ef96-4fa7-83c6-73bbfa8256ad + e65f93ac-4a14-4fef-afc6-44d78530e45e + 38da7932-bace-4722-99e8-41530a8b7e79 + d87fb28a-1fe7-43f1-9815-d869f233b385 + 4 + 91dc31bc-730d-43e4-993d-d12c2e540cdb + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + 04b7fd46-609e-48b8-a151-5e9495abbd13 + af35f706-7af8-4260-b588-358a993d1352 + fbd9e873-ec49-47bc-8d5c-258d8036b745 + 3 + 561e86fd-5bc2-413d-b114-88ab13b1e128 + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + ebe99570-fddf-41df-aa4c-5d4202f08c7f + 475fa148-99eb-4bdd-ad50-28959a3b193d + b626a1f5-5bd9-42c3-96da-26f221b1048e + 3 + 2eab66a4-9eb5-4f2b-a1ca-56352e95fd8b + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + fd9b00a1-4fa2-4250-bf75-9a5d0a375bee + 32a47c5f-6e9f-4fd4-af69-5f33be3c44e4 + 2 + 70cc3701-891d-4c67-962c-3ad2a8b089da + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + 54ebcc2c-2159-48d1-88c8-9c12b4311847 + 9baf9b45-b983-48be-a3c4-ff13530c72c6 + a5f3f98d-e1af-45fc-a57c-8f01a829d2a3 + 314e45dc-6256-4dcc-90c3-aaa1e08b613a + 7e727ff6-4118-4fcc-8d61-a780f76ab28c + d3497764-72f4-4b96-9c43-e4b12c91e0f7 + 6 + 593256e2-b0be-41aa-a66a-aea94d736564 + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + d59210ff-fb0c-47ac-88e4-bfb3bdd17472 + 8c731948-abe8-4e50-a551-e6a07b85d9c4 + de944dfd-0524-4e32-847a-6d55172e6a96 + 3 + 7b3054d7-6661-482c-9932-e8494405a0cc + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + e4f9217d-c7c3-49af-9a83-834638eb0d4d + d42341b5-06d6-414d-8a72-d1a48effb96f + c6ffa12e-83a0-4af7-8681-e213e9c0ba9f + 87380de2-badc-4b2b-a438-aac8de443f24 + e37b5872-2136-4007-9701-3851c1cc4a87 + 5 + 5f5ad0a5-fe97-4e7a-8035-d77c5fc1da31 + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + 9bde434b-367e-491f-9f01-040b116c4562 + 6a7e4d1a-ceb2-4c60-bd73-b1fd7e57a88d + 453254b0-f451-4808-a6c8-f678281679fd + c789e886-ac48-44a7-8696-71249b62e335 + ffdf9a6f-f4a6-417e-96df-c38d59b6a4da + 4bbbdf99-354b-448e-8eb7-59249cc4e3d0 + b6ce1f01-cf45-408d-b8ba-45a08f564742 + 562c0dcf-a89c-4f0e-9470-6e8b91f11f7e + ca6f4492-c165-4b33-8d01-a55901efe6bd + 9 + 782b7b56-87b9-46aa-9921-048dc9501c7f + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + 726245b8-cd81-4793-93c4-5a74c6fadea6 + a17a3498-e042-4db0-b3e7-eca2f875e5b8 + fa81c62e-402f-4a98-b743-073a361974d6 + 3 + 1ab81cb5-14e4-4818-a0f6-6452a5a799cd + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + 49e35091-c6e9-4be6-af3b-1d3bc207a03a + ecee5b24-b1ed-418d-a4bd-6e0b750fcf1f + b7e20f86-efa7-4972-ae82-910348246785 + 97712fde-30f8-43e4-82bf-959e99216762 + ee91ac60-35dc-4d05-9b63-3e908d900c4e + 54e72725-6aae-4d4b-90ac-7db02740a14e + 60acdc37-fc82-478a-b966-1c2238e744fd + 285d8023-3187-4a8b-b033-5ca8221d7e7c + 0a375a78-f249-4c35-8278-385aecd42907 + a909b265-17c3-49bf-a1cd-31e1a3085e6f + ba4adbea-a1b3-4423-869a-5f5efc4db8fe + 11 + 3783e052-7b8c-43ea-9383-9d6be217070b + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + 5d32bed8-8ecb-420b-94ff-7e259c6e0271 + 34e985cd-859f-4d13-a639-0f4a1845fc21 + c6af1eaf-f143-4a51-80ae-4c170fc22b7e + 2662160f-5d25-46cc-b2fb-037617fa9fc1 + 4 + 13bd7bcd-1021-4f64-b7b2-2956533b3916 + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + 67e77c02-1764-4e11-bf1d-4203f550692e + d366a040-055c-47c4-93f0-6e444f072d66 + cec5af33-7de8-45a7-8a88-c746edfb38bd + 3 + a3e1344f-fdf4-4bf0-b837-3cd1331dc4b0 + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + bfe572e7-d997-4c3c-8488-4e03d01804ea + b278407c-79b3-49ef-a2c8-ee682b33d9ef + 6fe2e9d2-130c-4a02-b347-a1c6b831fc5a + 9a4be73c-3082-4c74-be27-41c7c1688c53 + 4 + bc1e7118-1698-4047-b398-98179fee3923 + Group + + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;148 + + A group of Grasshopper objects + 47c152bc-c823-4835-aad3-3bf5c281e892 + d19cf29a-0512-4632-b4e0-ecc00c5b8487 + 3ed3a5e2-d78a-43c5-99e3-a602e5221e79 + 1cb74eb0-a053-4919-a55e-3175350e261c + 052e844a-59bc-4346-a9d3-a1965fe01a5c + 5 + 809c1f85-d57b-457b-9d16-fe468d3bd119 + Group + + + + + + + + + + + 2e78987b-9dfb-42a2-8b76-3923ac8bd91a + Boolean Toggle + + + + + Boolean (true/false) toggle + 47c152bc-c823-4835-aad3-3bf5c281e892 + Boolean Toggle + Connect + false + 0 + true + + + + + + 44 + 195 + 110 + 22 + + + + + + + + + + 2e78987b-9dfb-42a2-8b76-3923ac8bd91a + Boolean Toggle + + + + + Boolean (true/false) toggle + d19cf29a-0512-4632-b4e0-ecc00c5b8487 + Boolean Toggle + Load robot + false + 0 + false + + + + + + 18 + 156 + 124 + 22 + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import Grasshopper +import System +from ghpythonlib.componentbase import executingcomponent as component +from scriptcontext import sticky as st + +from compas_fab.backends import RosClient +from compas_fab.ghpython.components import create_id + + +class ROSConnect(component): + def SetUpParam(self, p, name, nickname, description): + p.Name = name + p.NickName = nickname + p.Description = description + p.Optional = True + + def RegisterInputParams(self, pManager): + + p = Grasshopper.Kernel.Parameters.Param_String() + self.SetUpParam(p, "ip", "ip", "The ip address of ROS master. Defaults to 127.0.0.1") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Integer() + self.SetUpParam(p, "port", "port", "The port of ROS master. Defaults to 9090.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Boolean() + self.SetUpParam(p, "connect", "connect", "If `True`, connect to ROS. If `False`, disconnect from ROS. Defaults to False.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + def RegisterOutputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "ros_client", "ros_client", "The ROS client.") + self.Params.Output.Add(p) + + def SolveInstance(self, DA): + p0 = self.marshal.GetInput(DA, 0) + p1 = self.marshal.GetInput(DA, 1) + p2 = self.marshal.GetInput(DA, 2) + result = self.RunScript(p0, p1, p2) + + if result is not None: + self.marshal.SetOutput(result, DA, 0, True) + + def RunScript(self, ip, port, connect): + ros_client = None + + ip = ip or '127.0.0.1' + port = port or 9090 + + key = create_id(self, 'ros_client') + ros_client = st.get(key, None) + + if ros_client: + st[key].close() + if connect: + st[key] = RosClient(ip, port) + st[key].run(5) + + ros_client = st.get(key, None) + self.Message = 'Connected' if ros_client and ros_client.is_connected else 'Not connected' + return ros_client + + GhPython provides a Python script component + + 223 + 78 + + + 1764 + 967 + + true + true + true + 1 + 3ed3a5e2-d78a-43c5-99e3-a602e5221e79 + true + true + GhPython Script + ROS Connect + + + + + + 205 + 90 + 136 + 98 + + + 268 + 139 + + + + + + 3 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 1 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + 39f76aa4-b4b1-4eff-8540-c659df1e991b + ip + ip + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 207 + 92 + 46 + 31 + + + 231.5 + 107.6667 + + + + + + + + true + Script input port. + 745e0055-1456-4b6f-8e8f-d5f74af38e8d + port + port + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 207 + 123 + 46 + 31 + + + 231.5 + 139 + + + + + + + + true + Script input connect. + 140a303b-ef70-4b59-a913-724c0d0505cb + connect + connect + true + 0 + true + 47c152bc-c823-4835-aad3-3bf5c281e892 + 1 + 35915213-5534-4277-81b8-1bdc9e7383d2 + + + + + + 207 + 154 + 46 + 32 + + + 231.5 + 170.3333 + + + + + + + + Script output ros_client. + 54922ac8-ee9c-400b-ac86-feaf21cb04a2 + ros_client + ros_client + false + 0 + + + + + + 283 + 92 + 56 + 94 + + + 311 + 139 + + + + + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import Grasshopper +import System +from ghpythonlib.componentbase import executingcomponent as component +from scriptcontext import sticky as st + +import compas +from compas_fab.ghpython.components import create_id +from compas_fab.ghpython.components.icons import ros_robot_icon +from compas_ghpython.artists import RobotModelArtist + + +class ROSRobot(component): + def SetUpParam(self, p, name, nickname, description): + p.Name = name + p.NickName = nickname + p.Description = description + p.Optional = True + + def RegisterInputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "ros_client", "ros_client", "The ROS client.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Boolean() + self.SetUpParam(p, "load", "load", "If `True`, loads the robot from ROS. Defaults to False.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + def RegisterOutputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "robot", "robot", "The robot.") + self.Params.Output.Add(p) + + def SolveInstance(self, DA): + p0 = self.marshal.GetInput(DA, 0) + p1 = self.marshal.GetInput(DA, 1) + result = self.RunScript(p0, p1) + + if result is not None: + self.marshal.SetOutput(result, DA, 0, True) + + def get_Internal_Icon_24x24(self): + return ros_robot_icon + + def RunScript(self, ros_client, load): + self.Message = "Loading..." + compas.PRECISION = '12f' + + key = create_id(self, 'robot') + + if ros_client and ros_client.is_connected and load: + # Load URDF from ROS + robot = ros_client.load_robot(load_geometry=True) + robot.artist = RobotModelArtist(robot.model) + self.Message = "{} loaded".format(robot.name) + st[key] = robot + else: + if st.get(key): + self.Message = "{} loaded".format(st.get(key).name) + else: + self.Message = "No robot" + + robot = st.get(key, None) + if robot: # client sometimes need to be restarted, without needing to reload geometry + robot.client = ros_client + return robot + + GhPython provides a Python script component + + 669 + 112 + + + 788 + 927 + + true + true + true + 1 + 1cb74eb0-a053-4919-a55e-3175350e261c + true + true + GhPython Script + Load robot + + + + + + 376 + 117 + 124 + 85 + + + 446 + 160 + + + + + + 2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 1 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + fcf0c1b8-fa76-4032-92b0-ef2ada751e62 + ros_client + ros_client + true + 0 + true + 54922ac8-ee9c-400b-ac86-feaf21cb04a2 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + 1 + + + + + + 378 + 119 + 53 + 40 + + + 406 + 139.25 + + + + + + + + true + Script input load. + 4b059742-6fce-4e6a-b6b0-cd2e2ccfdb08 + load + load + true + 0 + true + d19cf29a-0512-4632-b4e0-ecc00c5b8487 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + 1 + + + + + + 378 + 159 + 53 + 41 + + + 406 + 179.75 + + + + + + + + Script output robot. + 9a9900f7-c5de-485a-9791-63a5e1930c86 + robot + robot + false + 0 + + + + + + 461 + 119 + 37 + 81 + + + 479.5 + 159.5 + + + + + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import Grasshopper +import System +from ghpythonlib.componentbase import executingcomponent as component +from scriptcontext import sticky as st + +from compas.geometry import Frame +from compas.geometry import Transformation +from compas_ghpython.artists import MeshArtist +from compas_ghpython import draw_frame +from compas_fab.ghpython.components import create_id +from compas_fab.ghpython.components.icons import robot_visualize_icon + +class RobotVisualize(component): + def SetUpParam(self, p, name, nickname, description): + p.Name = name + p.NickName = nickname + p.Description = description + p.Optional = True + + def RegisterInputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "robot", "robot", "The robot.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_String() + self.SetUpParam(p, "group", "group", "The planning group used for end-effector and base visualization.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "configuration", "configuration", "The robot's full configuration.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Boolean() + self.SetUpParam(p, "reload_scene", "reload_scene", "Whether or not to reload the scene information from the client.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + def RegisterOutputParams(self, pManager): + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "visual", "visual", "The robot's visual meshes.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "frames", "frames", "The robot's joint frames.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "attached_collision_meshes", "attached_collision_meshes", "Attached collision meshes in the scene.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "collision_meshes", "collision_meshes", "Collision meshes in the scene.") + self.Params.Output.Add(p) + + def SolveInstance(self, DA): + p0 = self.marshal.GetInput(DA, 0) + p1 = self.marshal.GetInput(DA, 1) + p2 = self.marshal.GetInput(DA, 2) + p3 = self.marshal.GetInput(DA, 3) + result = self.RunScript(p0, p1, p2, p3) + + if result is not None: + if not hasattr(result, '__getitem__'): + self.marshal.SetOutput(result, DA, 0, True) + else: + self.marshal.SetOutput(result[0], DA, 0, True) + self.marshal.SetOutput(result[1], DA, 1, True) + self.marshal.SetOutput(result[2], DA, 2, True) + self.marshal.SetOutput(result[3], DA, 3, True) + + def get_Internal_Icon_24x24(self): + return robot_visualize_icon + + def RunScript(self, robot, group, configuration, reload_scene): + + visual = None + frames = None + collision_meshes = [] + attached_collision_meshes = [] + + if robot: + configuration = configuration or robot.zero_configuration() + + robot.update(configuration, visual=True, collision=False) + frames = [draw_frame(f) for f in robot.transformed_frames(configuration, group)] + + visual = robot.artist.draw_visual() + + scene_key = create_id(self, 'scene') + + if robot and (reload_scene or scene_key not in st): + st[scene_key] = robot.client.get_planning_scene() + + scene = st.get(scene_key, None) + + if scene and robot and robot.client and robot.client.is_connected: + + scene_key_co = scene_key + '_co' + if reload_scene or scene_key_co not in st: + for co in scene.world.collision_objects: + # HACK: Little workaround to a bug in to_collision_meshes() + header = co.header + frame_id = header.frame_id + co.header = dict(frame_id=frame_id) + cms = co.to_collision_meshes() + # HACK: Restore proper header + co.header = header + + for cm in cms: + if frame_id != '/world': + raise NotImplementedException('Only world-referenced collision meshes are supported') + + if cm.frame != Frame.worldXY(): + t = Transformation.from_frame(cm.frame) + mesh = cm.mesh.transformed(t) + else: + mesh = cm.mesh + + rhino_mesh = MeshArtist(mesh).draw() + collision_meshes.append(rhino_mesh) + st[scene_key_co] = collision_meshes + + collision_meshes = st.get(scene_key_co, None) + + for aco in scene.robot_state.attached_collision_objects: + for acm in aco.to_attached_collision_meshes(): + frame_id = aco.object['header']['frame_id'] + frame = robot.forward_kinematics(configuration, options=dict(link=frame_id)) + t = Transformation.from_frame(frame) + + # Local CM frame + if acm.collision_mesh.frame and acm.collision_mesh.frame != Frame.worldXY(): + t = t * Transformation.from_frame(acm.collision_mesh.frame) + + mesh = acm.collision_mesh.mesh.transformed(t) + + rhino_mesh = MeshArtist(mesh).draw() + attached_collision_meshes.append(rhino_mesh) + + return (visual, frames, attached_collision_meshes, collision_meshes) + + GhPython provides a Python script component + + 776 + 53 + + + 1051 + 880 + + true + true + true + 1 + 49f90167-2646-4d22-86bb-012cb0fa4ce7 + true + true + GhPython Script + Visualize + + + + + + 2902 + 943 + 243 + 84 + + + 2991 + 985 + + + + + + 4 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 4 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + cf579257-0392-4efa-8abc-cd5056adcf57 + robot + robot + true + 0 + true + 9a9900f7-c5de-485a-9791-63a5e1930c86 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2904 + 945 + 72 + 20 + + + 2941.5 + 955 + + + + + + + + true + Script input group. + 1bfd0eaf-087b-48f8-94ab-ec1f66301ff5 + group + group + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2904 + 965 + 72 + 20 + + + 2941.5 + 975 + + + + + + + + true + Script input configuration. + 2b362d36-7eb3-4375-b95e-a6ac847f62ba + configuration + configuration + true + 0 + true + 9d1d7aec-b041-4a18-90aa-523201e25b01 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2904 + 985 + 72 + 20 + + + 2941.5 + 995 + + + + + + + + true + Script input reload_scene. + e0c5dc07-2e68-4c56-b256-f4d6f758b13b + reload_scene + reload_scene + true + 0 + true + 11f438a0-e803-417d-8e51-4bf9fdfb3f32 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2904 + 1005 + 72 + 20 + + + 2941.5 + 1015 + + + + + + + + Script output visual. + 7fb0382a-e34f-49af-8952-9248a6d53e87 + visual + visual + false + 0 + + + + + + 3006 + 945 + 137 + 20 + + + 3074.5 + 955 + + + + + + + + Script output frames. + a94910e2-c32f-4b57-b31b-4b2bb61f886c + frames + frames + false + 0 + + + + + + 3006 + 965 + 137 + 20 + + + 3074.5 + 975 + + + + + + + + Script output attached_collision_meshes. + ece8fe7d-7f3d-4b20-a022-3c3f798472a2 + attached_collision_meshes + attached_collision_meshes + false + 0 + + + + + + 3006 + 985 + 137 + 20 + + + 3074.5 + 995 + + + + + + + + Script output collision_meshes. + 5425d29f-9702-41b7-b855-110be3410f3f + collision_meshes + collision_meshes + false + 0 + + + + + + 3006 + 1005 + 137 + 20 + + + 3074.5 + 1015 + + + + + + + + + + + + + + 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe + Scribble + + + + + true + + -175.4048 + 163.6236 + + + -89.72121 + 163.6236 + + + -89.72121 + 193.9932 + + + -175.4048 + 193.9932 + + A quick note + Arial + 052e844a-59bc-4346-a9d3-a1965fe01a5c + false + Scribble + Scribble + 41 + ROS + + + + + + -180.4048 + 158.6236 + 95.68359 + 40.36963 + + + -175.4048 + 163.6236 + + + + + + + + + + 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe + Scribble + + + + + true + + -173.4048 + 518.1589 + + + -90.60402 + 518.1589 + + + -90.60402 + 547.988 + + + -173.4048 + 547.988 + + A quick note + Arial + 5d32bed8-8ecb-420b-94ff-7e259c6e0271 + false + Scribble + Scribble + 41 + Pick + + + + + + -178.4048 + 513.1589 + 92.80078 + 39.8291 + + + -173.4048 + 518.1589 + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import Grasshopper +import System +from ghpythonlib.componentbase import executingcomponent as component +from scriptcontext import sticky as st + +from compas_fab.ghpython.components import coerce_frame +from compas_fab.backends import RosValidationError + +class InverseKinematics(component): + def SetUpParam(self, p, name, nickname, description): + p.Name = name + p.NickName = nickname + p.Description = description + p.Optional = True + + def RegisterInputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "robot", "robot", "The robot.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "plane", "plane", "The plane or frame to calculate the inverse kinematic for.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "start_configuration", "start_configuration", + "If passed, the inverse will be calculated such that the calculated joint positions \ + differ the least from the start_configuration. Defaults to the zero configuration.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_String() + self.SetUpParam(p, "group", "group", "The planning group used for calculation. Defaults to the robot's main planning group.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + def RegisterOutputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "a", "configuration", "The planning group's configuration.") + self.Params.Output.Add(p) + + def SolveInstance(self, DA): + p0 = self.marshal.GetInput(DA, 0) + p1 = self.marshal.GetInput(DA, 1) + p2 = self.marshal.GetInput(DA, 2) + p3 = self.marshal.GetInput(DA, 3) + result = self.RunScript(p0, p1, p2, p3) + + if result is not None: + self.marshal.SetOutput(result, DA, 0, True) + + def get_Internal_Icon_24x24(self): + return inverse_kinematics_icon + + def RunScript(self, robot, plane, start_configuration, group): + configuration = None + self.Message = '' + if robot and robot.client and robot.client.is_connected and plane: + frame = coerce_frame(plane) + try: + configuration = robot.inverse_kinematics(frame, start_configuration, group) + except RosValidationError as e: + self.Message = 'No solution found' + raise + + if configuration: + self.Message = 'Solution found' + return configuration + + GhPython provides a Python script component + + 416 + 248 + + + 684 + 571 + + true + true + true + 1 + e4f9217d-c7c3-49af-9a83-834638eb0d4d + true + true + GhPython Script + IK + + + + + + 215 + 1349 + 207 + 84 + + + 330 + 1391 + + + + + + 4 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 1 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + 29fc4f93-58c2-4192-8f02-3ecdf4a1be23 + robot + robot + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 217 + 1351 + 98 + 20 + + + 267.5 + 1361 + + + + + + + + true + Script input plane. + c75f16a9-ac28-43bf-a9b6-95a50d2b00f1 + plane + plane + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 217 + 1371 + 98 + 20 + + + 267.5 + 1381 + + + + + + + + true + Script input start_configuration. + 79ab0fb8-9433-4fe1-a38b-0c119815d935 + start_configuration + start_configuration + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 217 + 1391 + 98 + 20 + + + 267.5 + 1401 + + + + + + + + true + Script input group. + 14fc99fc-0ac5-4bf2-9d18-9cfab5caf53b + group + group + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 217 + 1411 + 98 + 20 + + + 267.5 + 1421 + + + + + + + + Script output configuration. + 9f3010dd-f68e-4afc-ad16-9a448e090c7b + configuration + configuration + false + 0 + + + + + + 345 + 1351 + 75 + 80 + + + 382.5 + 1391 + + + + + + + + + + + + + + 2e78987b-9dfb-42a2-8b76-3923ac8bd91a + Boolean Toggle + + + + + Boolean (true/false) toggle + 11f438a0-e803-417d-8e51-4bf9fdfb3f32 + Boolean Toggle + reload + false + 0 + true + + + + + + 2770 + 1004 + 101 + 22 + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + from compas.geometry import Box +from compas.datastructures import Mesh +from compas_ghpython.artists import MeshArtist + +shape = Box.from_width_height_depth(w, h, d) +brick = Mesh.from_shape(shape) + +mesh = MeshArtist(brick).draw() + GhPython provides a Python script component + + 455 + 613 + + + 1134 + 1121 + + true + true + true + 7ab9dd8f-ef96-4fa7-83c6-73bbfa8256ad + false + true + GhPython Script + Python + + + + + + 100 + 841 + 88 + 64 + + + 132 + 873 + + + + + + 3 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 3 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + d5445420-7f73-489a-a402-f534142329eb + h + h + true + 0 + true + 38da7932-bace-4722-99e8-41530a8b7e79 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 102 + 843 + 15 + 20 + + + 111 + 853 + + + + + + + + true + Script input w. + 6282d3f3-b08b-4ead-81a5-16f764732ae7 + w + w + true + 0 + true + e65f93ac-4a14-4fef-afc6-44d78530e45e + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 102 + 863 + 15 + 20 + + + 111 + 873 + + + + + + + + true + Script input d. + a8705f46-83b1-4be5-83ca-a074b9e5c0a4 + d + d + true + 0 + true + d87fb28a-1fe7-43f1-9815-d869f233b385 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 102 + 883 + 15 + 20 + + + 111 + 893 + + + + + + + + Script output shape. + 252a3ad3-fe63-448c-9531-ba99a4a17577 + a + shape + false + 0 + + + + + + 147 + 843 + 39 + 20 + + + 166.5 + 853 + + + + + + + + Script output brick. + 5499898c-9def-41d8-a767-7d5681a3af05 + brick + brick + false + 0 + + + + + + 147 + 863 + 39 + 20 + + + 166.5 + 873 + + + + + + + + Script output mesh. + 6088143e-d145-4a47-8c41-51e32cd53990 + mesh + mesh + false + 0 + + + + + + 147 + 883 + 39 + 20 + + + 166.5 + 893 + + + + + + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + e65f93ac-4a14-4fef-afc6-44d78530e45e + Number Slider + + false + 0 + + + + + + -170 + 870 + 160 + 20 + + + -169.4536 + 870.9739 + + + + + + 3 + 1 + 0 + 0.05 + 0.01 + 0 + 0.016 + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + 38da7932-bace-4722-99e8-41530a8b7e79 + Number Slider + + false + 0 + + + + + + -170 + 850 + 192 + 20 + + + -169.4238 + 850.4012 + + + + + + 3 + 1 + 0 + 0.05 + 0.01 + 0 + 0.012 + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + d87fb28a-1fe7-43f1-9815-d869f233b385 + Number Slider + + false + 0 + + + + + + -169 + 891 + 192 + 20 + + + -168.9764 + 891.2905 + + + + + + 3 + 1 + 0 + 0.05 + 0.01 + 0 + 0.031 + + + + + + + + + 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe + Scribble + + + + + true + + -173.4048 + 664.7191 + + + -68.28224 + 664.7191 + + + -68.28224 + 694.5482 + + + -173.4048 + 694.5482 + + A quick note + Arial + 49e35091-c6e9-4be6-af3b-1d3bc207a03a + false + Scribble + Scribble + 41 + Place + + + + + + -178.4048 + 659.7191 + 115.1226 + 39.8291 + + + -173.4048 + 664.7191 + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + from compas_fab.robots import Configuration + +config = Configuration.from_revolute_values(j) + GhPython provides a Python script component + + 26 + 26 + + + 558 + 571 + + true + true + true + 1 + 9bde434b-367e-491f-9f01-040b116c4562 + false + true + GhPython Script + Home config + + + + + + 223 + 1103 + 84 + 98 + + + 250 + 1152 + + + + + + 1 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 1 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + 1 + true + Script variable Pickup config + 24655f45-8c71-4631-8530-a0bc0f33ad9f + j + j + true + 1 + true + 6a7e4d1a-ceb2-4c60-bd73-b1fd7e57a88d + 453254b0-f451-4808-a6c8-f678281679fd + c789e886-ac48-44a7-8696-71249b62e335 + ffdf9a6f-f4a6-417e-96df-c38d59b6a4da + 4bbbdf99-354b-448e-8eb7-59249cc4e3d0 + b6ce1f01-cf45-408d-b8ba-45a08f564742 + 6 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 225 + 1105 + 10 + 94 + + + 231.5 + 1152 + + + + + + + + Script output config. + f956b690-65c4-4b82-ac9a-488e4afed703 + config + config + false + 0 + + + + + + 265 + 1105 + 40 + 94 + + + 285 + 1152 + + + + + + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + 6a7e4d1a-ceb2-4c60-bd73-b1fd7e57a88d + Number Slider + + false + 0 + + + + + + 15 + 1093 + 160 + 20 + + + 15.35516 + 1093.502 + + + + + + 2 + 1 + 0 + 3.14 + -3.14 + 0 + 1.57 + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + 453254b0-f451-4808-a6c8-f678281679fd + Number Slider + + false + 0 + + + + + + 15 + 1115 + 160 + 20 + + + 15.3551 + 1115.102 + + + + + + 2 + 1 + 0 + 3.14 + -3.14 + 0 + -1.57 + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + c789e886-ac48-44a7-8696-71249b62e335 + Number Slider + + false + 0 + + + + + + 15 + 1136 + 160 + 20 + + + 15.3551 + 1136.702 + + + + + + 2 + 1 + 0 + 3.14 + -3.14 + 0 + 1.57 + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + ffdf9a6f-f4a6-417e-96df-c38d59b6a4da + Number Slider + + false + 0 + + + + + + 15 + 1158 + 160 + 20 + + + 15.3551 + 1158.302 + + + + + + 2 + 1 + 0 + 3.14 + -3.14 + 0 + -1.57 + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + 4bbbdf99-354b-448e-8eb7-59249cc4e3d0 + Number Slider + + false + 0 + + + + + + 15 + 1179 + 160 + 20 + + + 15.3551 + 1179.901 + + + + + + 2 + 1 + 0 + 3.14 + -3.14 + 0 + -1.57 + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + b6ce1f01-cf45-408d-b8ba-45a08f564742 + Number Slider + + false + 0 + + + + + + 14 + 1201 + 160 + 20 + + + 14.99004 + 1201.542 + + + + + + 2 + 1 + 0 + 3.14 + -3.14 + 0 + 3.14 + + + + + + + + + 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe + Scribble + + + + + true + + -173.4048 + 981.7598 + + + -54.10841 + 981.7598 + + + -54.10841 + 1012.109 + + + -173.4048 + 1012.109 + + A quick note + Arial + 726245b8-cd81-4793-93c4-5a74c6fadea6 + false + Scribble + Scribble + 41 + Offset + + + + + + -178.4048 + 976.7598 + 129.2964 + 40.34961 + + + -173.4048 + 981.7598 + + + + + + + + + + 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe + Scribble + + + + + true + + -173.4048 + 1362.047 + + + -14.24953 + 1362.047 + + + -14.24953 + 1391.876 + + + -173.4048 + 1391.876 + + A quick note + Arial + d42341b5-06d6-414d-8a72-d1a48effb96f + false + Scribble + Scribble + 41 + Toolbox + + + + + + -178.4048 + 1357.047 + 169.1553 + 39.8291 + + + -173.4048 + 1362.047 + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import Grasshopper +import System +from ghpythonlib.componentbase import executingcomponent as component +from scriptcontext import sticky as st + +from compas_fab.ghpython.components import create_id +from compas_fab.ghpython.components import coerce_frame +from compas_fab.ghpython.components.icons import plan_cartesian_motion_icon + +class PlanCartesianMotion(component): + def SetUpParam(self, p, name, nickname, description): + p.Name = name + p.NickName = nickname + p.Description = description + p.Optional = True + + def RegisterInputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "robot", "robot", "The robot.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "planes", "planes", "The planes or frames through which the path is defined.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.list + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "start_configuration", "start_configuration", + "The robot's full configuration, i.e. values for all configurable joints of the entire robot, at the starting position. Defaults to the all-zero configuration.") # noqa E501 + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_String() + self.SetUpParam(p, "group", "group", "The planning group used for calculation. Defaults to the robot's main planning group.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + +# p = Grasshopper.Kernel.Parameters.Param_GenericObject() +# self.SetUpParam(p, "attached_collision_meshes", "attached_collision_meshes", "A list of attached collision meshes to be included for planning.") +# p.Access = Grasshopper.Kernel.GH_ParamAccess.list +# self.Params.Input.Add(p) + +# p = Grasshopper.Kernel.Parameters.Param_GenericObject() +# self.SetUpParam(p, "path_constraints", "path_constraints", +# "Optional constraints that can be imposed along the solution path. Note that path calculation won't work if the start_configuration violates these constraints. Defaults to None.") # noqa E501 +# p.Access = Grasshopper.Kernel.GH_ParamAccess.list +# self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Number() + self.SetUpParam(p, "max_step", "max_step", "The approximate distance between the calculated points. (Defined in the robot's units.) Defaults to 0.01.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Boolean() + self.SetUpParam(p, "compute", "compute", "If `True`, calculates a trajectory.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + def RegisterOutputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "trajectory", "trajectory", "The calculated trajectory.") + self.Params.Output.Add(p) + + def SolveInstance(self, DA): + p0 = self.marshal.GetInput(DA, 0) + p1 = self.marshal.GetInput(DA, 1) + p2 = self.marshal.GetInput(DA, 2) + p3 = self.marshal.GetInput(DA, 3) + p4 = self.marshal.GetInput(DA, 4) + p5 = self.marshal.GetInput(DA, 5) +# p6 = self.marshal.GetInput(DA, 6) +# p7 = self.marshal.GetInput(DA, 7) +# result = self.RunScript(p0, p1, p2, p3, p4, p5, p6, p7) + result = self.RunScript(p0, p1, p2, p3, p4, p5) + + if result is not None: + self.marshal.SetOutput(result, DA, 0, True) + + def get_Internal_Icon_24x24(self): + return plan_cartesian_motion_icon + + def RunScript(self, robot, planes, start_configuration, group, max_step, compute): + + key = create_id(self, 'trajectory') + + max_step = float(max_step) if max_step else 0.01 + # NOTE: Read from scene + attached_collision_meshes = None + path_constraints = None + attached_collision_meshes = list(attached_collision_meshes) if attached_collision_meshes else None + + if robot and robot.client and robot.client.is_connected and start_configuration and planes and compute: + frames = [coerce_frame(plane) for plane in planes] + st[key] = robot.plan_cartesian_motion(frames, + start_configuration=start_configuration, + group=group, + options=dict( + max_step=max_step, + path_constraints=path_constraints, + attached_collision_meshes=attached_collision_meshes + )) + + trajectory = st.get(key, None) + + if trajectory and trajectory.fraction < 1.0: + raise Exception('Incomplete trajectory. Fraction={}'.format(trajectory.fraction)) + + return trajectory + GhPython provides a Python script component + + 416 + 248 + + + 684 + 571 + + true + true + true + 1 + c6ffa12e-83a0-4af7-8681-e213e9c0ba9f + true + true + GhPython Script + Plan Cartesian Motion + + + + + + 475 + 1312 + 188 + 157 + + + 590 + 1391 + + + + + + 6 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 1 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + d1e40e43-b307-4997-95f9-cc5ceb5b1ff8 + robot + robot + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 477 + 1314 + 98 + 25 + + + 527.5 + 1326.75 + + + + + + + + 1 + true + Script input planes. + 595db290-c1cd-47ae-a4a7-4d4b2c12919c + planes + planes + true + 1 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 477 + 1339 + 98 + 26 + + + 527.5 + 1352.25 + + + + + + + + true + Script input start_configuration. + e9a1a984-1028-4f75-90de-96a238f876cb + start_configuration + start_configuration + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 477 + 1365 + 98 + 25 + + + 527.5 + 1377.75 + + + + + + + + true + Script input group. + 0174acbc-9c27-4c7e-9508-fd4153cace00 + group + group + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 477 + 1390 + 98 + 26 + + + 527.5 + 1403.25 + + + + + + + + true + Script input max_step. + 0f1faba3-ea01-41f5-910a-d48118bef748 + max_step + max_step + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 477 + 1416 + 98 + 25 + + + 527.5 + 1428.75 + + + + + + + + true + Script input compute. + 0a27b8d5-ba01-4b82-8eab-e296cf91f0d1 + compute + compute + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 477 + 1441 + 98 + 26 + + + 527.5 + 1454.25 + + + + + + + + Script output trajectory. + 706ebe2f-4fa1-47c4-a7bb-388260bfe9f8 + trajectory + trajectory + false + 0 + + + + + + 605 + 1314 + 56 + 153 + + + 633 + 1390.5 + + + + + + + + + + + + + + f31d8d7a-7536-4ac8-9c96-fde6ecda4d0a + Cluster + + + + + + 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 + + Contains a cluster of Grasshopper components + true + 1 + 87380de2-badc-4b2b-a438-aac8de443f24 + Cluster + T0CF + false + + + + + 3 + 348f98ef-5f46-4a58-885f-0470a6d4f1f8 + 499c6afc-e204-4b10-aed8-ccc3ae4a7d31 + e6827551-bd5f-4777-9cc2-97e2a1cf285e + a734e7f6-2f8f-4929-916a-9608854f6392 + d0ccacbd-d43e-44d1-95fa-d65b95597d22 + 43cd6740-e900-4cb5-aac6-f6ee8442edb2 + + + + + + 4 + 1368 + 144 + 46 + + + 70 + 1391 + + + + + + 2 + 4f8984c4-7c7a-4d69-b0a2-183cbb330d20 + 3e8ca6be-fda8-4aaf-b5c0-3c54c8bb7312 + 1 + 4f8984c4-7c7a-4d69-b0a2-183cbb330d20 + + + + + Contains a collection of three-dimensional axis-systems + e6827551-bd5f-4777-9cc2-97e2a1cf285e + Plane + tcf_point + true + 0 + + + + + + 6 + 1370 + 49 + 21 + + + 32 + 1380.5 + + + + + + + + Contains a collection of floating point numbers + 499c6afc-e204-4b10-aed8-ccc3ae4a7d31 + Number + z_offset + true + 0 + 1 + + + + + + 6 + 1391 + 49 + 21 + + + 32 + 1401.5 + + + + + + 1 + + + + + 1 + {0} + + + + + 0.07 + + + + + + + + + + + Contains a collection of three-dimensional axis-systems + 348f98ef-5f46-4a58-885f-0470a6d4f1f8 + Plane + t0cf_frame + false + 0 + + + + + + 85 + 1370 + 61 + 42 + + + 115.5 + 1391 + + + + + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import os +from compas.geometry import Frame +from compas.datastructures import Mesh +from compas_fab.robots import PlanningScene +from compas_fab.robots import Tool +from compas_ghpython.artists import MeshArtist + +HERE = os.path.dirname(ghenv.Component.OnPingDocument().FilePath) + +# create tool from mesh and frame +mesh = Mesh.from_stl(os.path.join(HERE, 'vacuum_gripper.stl')) +frame = Frame([z_offset, 0, 0], [0, 0, 1], [0, 1, 0]) +tool = Tool(mesh, frame, name='vacuum_gripper') +tool_at_origin = MeshArtist(mesh).draw() + +if robot and robot.client and robot.client.is_connected: + scene = PlanningScene(robot) + if attached: + robot.attach_tool(tool) + scene.add_attached_tool() + else: + if robot.attached_tool: + scene.remove_attached_tool() + scene.remove_collision_mesh('attached_tool_link_collision_0') + robot.detach_tool() + + GhPython provides a Python script component + + 93 + 93 + + + 797 + 925 + + true + true + true + bfe572e7-d997-4c3c-8488-4e03d01804ea + false + true + GhPython Script + Attach tool + + + + + + 342 + 265 + 159 + 86 + + + 408 + 308 + + + + + + 3 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 1 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + 6e80e6d6-8757-4819-8351-1a7c5da7f996 + robot + robot + true + 0 + true + 9a9900f7-c5de-485a-9791-63a5e1930c86 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + 1 + + + + + + 344 + 267 + 49 + 27 + + + 370 + 280.6667 + + + + + + + + true + Script input attached. + 404a4436-fc31-4bbd-846f-0b582b5ffd9c + attached + attached + true + 0 + true + b278407c-79b3-49ef-a2c8-ee682b33d9ef + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 344 + 294 + 49 + 27 + + + 370 + 308 + + + + + + + + true + Script input z_offset. + b7e50fd9-4f43-49fe-8c0a-b5a025756216 + z_offset + z_offset + true + 0 + true + 9a4be73c-3082-4c74-be27-41c7c1688c53 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 344 + 321 + 49 + 28 + + + 370 + 335.3333 + + + + + + + + Script output tool_at_origin. + ec3d9765-86ad-4ef4-bb37-6e2408651259 + tool_at_origin + tool_at_origin + false + 0 + + + + + + 423 + 267 + 76 + 82 + + + 461 + 308 + + + + + + + + + + + + + + 2e78987b-9dfb-42a2-8b76-3923ac8bd91a + Boolean Toggle + + + + + Boolean (true/false) toggle + b278407c-79b3-49ef-a2c8-ee682b33d9ef + Boolean Toggle + Attach + false + 0 + true + + + + + + 15 + 296 + 101 + 22 + + + + + + + + + + 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe + Scribble + + + + + true + + -173.4048 + 301.41 + + + -91.44484 + 301.41 + + + -91.44484 + 331.2391 + + + -173.4048 + 331.2391 + + A quick note + Arial + 6fe2e9d2-130c-4a02-b347-a1c6b831fc5a + false + Scribble + Scribble + 41 + Tool + + + + + + -178.4048 + 296.41 + 91.95996 + 39.8291 + + + -173.4048 + 301.41 + + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + 9a4be73c-3082-4c74-be27-41c7c1688c53 + Number Slider + z_offset + false + 0 + + + + + + 15 + 324 + 170 + 20 + + + 15.73172 + 324.025 + + + + + + 3 + 1 + 0 + 0.2 + -0.1 + 0 + 0.07 + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import os +from compas.geometry import Frame +from compas.datastructures import Mesh +from compas_fab.robots import PlanningScene +from compas_fab.robots import CollisionMesh +from compas_ghpython.artists import MeshArtist +from compas_rhino.geometry import RhinoMesh + +if robot and robot.client and robot.client.is_connected: + static_cm_name = 'static_objects' + scene = PlanningScene(robot) + scene.remove_collision_mesh(static_cm_name) + if meshes: + for mesh in meshes: + mesh = RhinoMesh.from_geometry(mesh).to_compas() + cm = CollisionMesh(mesh, static_cm_name) + scene.append_collision_mesh(cm) + + GhPython provides a Python script component + + 156 + 156 + + + 924 + 745 + + true + true + true + 67e77c02-1764-4e11-bf1d-4203f550692e + false + true + GhPython Script + Python + + + + + + 424 + 390 + 75 + 60 + + + 485 + 420 + + + + + + 2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 0 + + + + + true + Script variable Python + d8842ce0-d92a-4fcf-8634-a387177ebe33 + robot + robot + true + 0 + true + 9a9900f7-c5de-485a-9791-63a5e1930c86 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + 1 + + + + + + 426 + 392 + 44 + 28 + + + 449.5 + 406 + + + + + + + + 1 + true + Script input meshes. + 90e52a89-cf0f-4c9d-ae82-b73d0248a98b + meshes + meshes + true + 1 + true + cec5af33-7de8-45a7-8a88-c746edfb38bd + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 426 + 420 + 44 + 28 + + + 449.5 + 434 + + + + + + + + + + + + + + 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe + Scribble + + + + + true + + -173.4048 + 410.6744 + + + -52.70705 + 410.6744 + + + -52.70705 + 441.044 + + + -173.4048 + 441.044 + + A quick note + Arial + d366a040-055c-47c4-93f0-6e444f072d66 + false + Scribble + Scribble + 41 + Scene + + + + + + -178.4048 + 405.6744 + 130.6978 + 40.36963 + + + -173.4048 + 410.6744 + + + + + + + + + + 1e936df3-0eea-4246-8549-514cb8862b7a + Mesh + + + + + Contains a collection of polygon meshes + cec5af33-7de8-45a7-8a88-c746edfb38bd + Mesh + Mesh + false + 0 + + + + + + 15 + 424 + 50 + 20 + + + 40.98947 + 434.8297 + + + + + + 1 + + + + + 1 + {0} + + + + + + Y2BkYGD4DwQgGgR4mIBEeFBGZl6+c35ubn6ejkJYalFxZn6erZmekaWekYGRsYWeoaGpgaGOgnNpTklpUaptXmppSVFijo5CQGlSTmayd2plSH52ap6tqamRkYVhqqV5srmpqamxASvIFmGw4Xruqfm5qSVFlXq+qcUZLEBx9jKIPVyJRckZmWWpxim5nPkFqXl5pUVJxSwpiSWJIEUcHBxMICcKqDMw2ADpzLKSHk5mIIMfRCixMjAw/apnYhBjhXjn938mBhGo155cue7n/vKy4P6nDALNjEofdHdKx/wByt9jgchbaAAJGQY08OW9PYwZvvCui1roO3upl5/ELbokDnjLR/Zn6/60f7GpZwHL8+/7+X8IcPjK3rFHN8KHzWN/dpTw/sxXD/blX/CyfyiaDVXTYI+E9yNhFDFQqDEyNDA4lEANhPmIgRHdKiDQdZZ5/chMCs0VEg5YlKKAdpfnXhLh28D6mBDCD+wZGO2C5LVh1jEysLAwMbOysrNxcXFwcnPz8ggI8PELCgoLiYmJiIqLy8hKSMhIysvLScnKysjLycnJS0urKKiqqqgrKqqpAGk1dXU1ZXV1RhZWViZWFhZ2Lm5uDm4uLl4BQUE+QQEBYTFxcRFxMTE5WRkZNVUlpQdAS7M/cOeDA6AZ6qiKW5/TPt+YL6q6Zz4jx56Fb3rC+b813uB32fw28ZGCmc+f0pn7ZH1kjhxbbve+abqtj8wxts7zL2+k8Aq3b/y4kn1j4ruWjYmPDL4fuPHjSvXjD/oneA0KJaRn759w1qVMfdNMg+8KvwVvzj1RtrBdd9P+C3/7ZP6dSOyQ4TtRx7iRMWDLvp/W13csBLmrd+qv02B36cPdlZyQceM8A/uUDZJSDSfmFZnF9DiZ3TjzV2BWkNmd2i0JwYpvCjcz7mqzuKn/JVTIAWjGxR9fc8FmzIeb4XtuL+8hBx7WhG49Xy+raTuYOo/5tR7h6pkofSBkZvM0XYnGaaxBQRMmMf3JKS649PLl5lp7F5elq3Mcv1+f6r3doIfn+rbEM2d/r1VYfjUwo2LzbaM1mbz/115e/L365P0Yi5jLV/++VSw/fOx9aXSb6dpEpZOVmtMDX6xJVMn5Wpn7f6fnavN0+/3qTz982cf9PHF9f8/PeNuef+z3HEMOHnXd+wNfEmoAppvsyfdlwP6ZB/fP5KqfnncWeT9iYBbxcPz5x57Foib1/Q31N5b/9+U0CcTIHWs/+Z636obMPW2u6e0fO6/yb+xnPlvHt+OPdPPk/eqxT5tPH/5x5NX3af1vzl/7u1Nejf/x4l39nGdrfVxet3OZGyxoYgv5q3/xy36X6+c/JAWfZ5r2/P73xTOZN5+XAyld3XzC/v57VkN5Z1ufe/xa/bH2f9+yM0yUOp/7aa1FF8ilS5ByE7YclVbN/A/C+oCRt7ED6qsDuctGqbiXEerAhJMML4GFA4ODb/YjEM0ALHhA9AHeqJdgQ/ckvYUbbvN2X9T/eqYGGJ+bOFtHwSgYBaNgFAwLAAA= + + 00000000-0000-0000-0000-000000000000 + + + + + + + + + + + + + 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe + Scribble + + + + + true + + -173.4048 + 1114.921 + + + -61.31544 + 1114.921 + + + -61.31544 + 1144.75 + + + -173.4048 + 1144.75 + + A quick note + Arial + 562c0dcf-a89c-4f0e-9470-6e8b91f11f7e + false + Scribble + Scribble + 41 + Home + + + + + + -178.4048 + 1109.921 + 122.0894 + 39.8291 + + + -173.4048 + 1114.921 + + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + a17a3498-e042-4db0-b3e7-eca2f875e5b8 + Number Slider + approach_offset + false + 0 + + + + + + 12 + 989 + 211 + 20 + + + 12.986 + 989.0408 + + + + + + 3 + 1 + 0 + 0.2 + 0 + 0 + 0.05 + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + from compas.geometry import Translation +from compas_fab.ghpython.components import coerce_frame + +# TODO: create a list of pickup frames with 3 frames: +# approach frame + pick up frame + approach frame + +f = coerce_frame(pick_t0cf_frame) + +approach_frame = f.copy() +approach_frame.transform(Translation.from_vector([0,0,approach_offset])) + +pickup_t0cf_frames = [approach_frame, f, approach_frame] + + GhPython provides a Python script component + + 1022 + 256 + + + 729 + 803 + + true + true + true + 1 + d59210ff-fb0c-47ac-88e4-bfb3bdd17472 + false + true + GhPython Script + Pickup frames + + + + + + 1501 + 650 + 221 + 105 + + + 1603 + 703 + + + + + + 2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 1 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + e8f5e947-99c5-441f-9aa2-ab76065faf07 + pick_t0cf_frame + pick_t0cf_frame + true + 0 + true + bf2d76a6-3501-45a4-893d-ae3e0eaa5ce4 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + 1 + + + + + + 1503 + 652 + 85 + 50 + + + 1547 + 677.25 + + + + + + + + true + Script input approach_offset. + 43dd6963-b012-4ebc-ba8f-ae8b82f5452f + approach_offset + approach_offset + true + 0 + true + fa81c62e-402f-4a98-b743-073a361974d6 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + 1 + + + + + + 1503 + 702 + 85 + 51 + + + 1547 + 727.75 + + + + + + + + Script output pickup_t0cf_frames. + b241bc34-d3b5-49b7-b546-a83f9d99d91f + pickup_t0cf_frames + pickup_t0cf_frames + false + 0 + + + + + + 1618 + 652 + 102 + 101 + + + 1669 + 702.5 + + + + + + + + + + + + + + 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe + Scribble + + + + + true + + 20.54071 + -47.42408 + + + 154.7497 + -47.42408 + + + 154.7497 + -14.66285 + + + 20.54071 + -14.66285 + + A quick note + Arial + b4428083-5df1-433c-b590-4df513e94c3b + false + Scribble + Scribble + 45 + INPUT + + + + + + 15.54071 + -52.42408 + 144.209 + 42.76123 + + + 20.54071 + -47.42408 + + + + + + + + + + 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe + Scribble + + + + + true + + 3089.445 + -44.5486 + + + 3210.317 + -44.5486 + + + 3210.317 + -11.21608 + + + 3089.445 + -11.21608 + + A quick note + Arial + 4c6e9558-0e0e-4605-a5de-a63e8b78f29f + false + Scribble + Scribble + 45 + JSON + + + + + + 3084.445 + -49.5486 + 130.8716 + 43.33252 + + + 3089.445 + -44.5486 + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import Grasshopper +import System +from ghpythonlib.componentbase import executingcomponent as component +from scriptcontext import sticky as st + +from compas_ghpython import draw_frame +from compas_fab.ghpython.components import create_id +from compas_fab.ghpython.components import coerce_frame +from compas_fab.ghpython.components.icons import trajectory_visualize_icon + +class TrajectoryVisualize(component): + def SetUpParam(self, p, name, nickname, description): + p.Name = name + p.NickName = nickname + p.Description = description + p.Optional = True + + def RegisterInputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "robot", "robot", "The robot.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_String() + self.SetUpParam(p, "group", "group", "The planning group for which this trajectory was planned.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "trajectory", "trajectory", "The calculated trajectory.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + def RegisterOutputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "start_configuration", "start_configuration", "The start configuration of the trajectory.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "configurations", "configurations", "The full configurations along the trajectory.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Number() + self.SetUpParam(p, "fraction", "fraction", "Indicates the percentage of requested trajectory that was calculated, e.g. 1 means the full trajectory was found.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Number() + self.SetUpParam(p, "time", "time", "The time which is needed to execute that trajectory at full speed.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Plane() + self.SetUpParam(p, "planes", "planes", "The planes of the robot's end-effector.") + self.Params.Output.Add(p) + + def SolveInstance(self, DA): + p0 = self.marshal.GetInput(DA, 0) + p1 = self.marshal.GetInput(DA, 1) + p2 = self.marshal.GetInput(DA, 2) + result = self.RunScript(p0, p1, p2) + + if result is not None: + if not hasattr(result, '__getitem__'): + self.marshal.SetOutput(result, DA, 0, True) + else: + self.marshal.SetOutput(result[0], DA, 0, True) + self.marshal.SetOutput(result[1], DA, 1, True) + self.marshal.SetOutput(result[2], DA, 2, True) + self.marshal.SetOutput(result[3], DA, 3, True) + self.marshal.SetOutput(result[4], DA, 4, True) + + def get_Internal_Icon_24x24(self): + return trajectory_visualize_icon + + def RunScript(self, robot, group, trajectory): + start_configuration = None + configurations = [] + fraction = 0. + time = 0. + + planes = [] + positions = [] + velocities = [] + accelerations = [] + + if robot and trajectory: + group = group or robot.main_group_name + + for c in trajectory.points: + configurations.append(robot.merge_group_with_full_configuration(c, trajectory.start_configuration, group)) + frame = robot.forward_kinematics(c, group, options=dict(solver='model')) + planes.append(draw_frame(frame)) + positions.append(c.positions) + velocities.append(c.velocities) + accelerations.append(c.accelerations) + + start_configuration = trajectory.start_configuration + fraction = trajectory.fraction + time = trajectory.time_from_start + + # return outputs if you have them; here I try it for you: + return (start_configuration, configurations, fraction, time, planes) + + GhPython provides a Python script component + + 416 + 248 + + + 684 + 571 + + true + true + true + 1 + e37b5872-2136-4007-9701-3851c1cc4a87 + true + true + GhPython Script + Visualize Trajectory + + + + + + 716 + 1321 + 188 + 140 + + + 786 + 1391 + + + + + + 3 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 5 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + 6aa301b4-6cee-4345-bd30-6f3863f96203 + robot + robot + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 718 + 1323 + 53 + 45 + + + 746 + 1345.667 + + + + + + + + true + Script input group. + fede8c15-1fd3-43a7-a90c-d3fe3da5daad + group + group + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 718 + 1368 + 53 + 45 + + + 746 + 1391 + + + + + + + + true + Script input trajectory. + 120bae85-d34a-4caf-8ea5-953d9524ed70 + trajectory + trajectory + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 718 + 1413 + 53 + 46 + + + 746 + 1436.333 + + + + + + + + Script output start_configuration. + 7dade1d5-4247-400d-9bd7-eb4b0b06927f + start_configuration + start_configuration + false + 0 + + + + + + 801 + 1323 + 101 + 27 + + + 851.5 + 1336.6 + + + + + + + + Script output configurations. + 4d980fad-4382-4c12-8ffa-80fb83ba449a + configurations + configurations + false + 0 + + + + + + 801 + 1350 + 101 + 27 + + + 851.5 + 1363.8 + + + + + + + + Script output fraction. + cc787af3-0b87-42c5-9e1a-1aff8eb7520e + fraction + fraction + false + 0 + + + + + + 801 + 1377 + 101 + 27 + + + 851.5 + 1391 + + + + + + + + Script output time. + 121fb4b2-8d79-43d2-bc51-0b7e5232d7a9 + time + time + false + 0 + + + + + + 801 + 1404 + 101 + 27 + + + 851.5 + 1418.2 + + + + + + + + Script output planes. + 11b58743-03db-4a58-80c9-8836f9ae9411 + planes + planes + false + 0 + + + + + + 801 + 1431 + 101 + 28 + + + 851.5 + 1445.4 + + + + + + + + + + + + + + 17b7152b-d30d-4d50-b9ef-c9fe25576fc2 + XY Plane + + + + + World XY plane. + true + c6af1eaf-f143-4a51-80ae-4c170fc22b7e + XY Plane + XY + + + + + + 175 + 491 + 65 + 31 + + + 207 + 507 + + + + + + Origin of plane + 3ba2ecfd-e216-4a78-a1fc-40c311b63561 + Origin + O + false + b2ac75b7-c534-4271-9a27-3649779c17d0 + 1 + + + + + + 177 + 493 + 15 + 27 + + + 186 + 506.5 + + + + + + 1 + + + + + 1 + {0} + + + + + + + 0 + 0 + 0 + + + + + + + + + + + + World XY plane + 947ea514-3a1f-4433-899e-2226ba952ee6 + Plane + P + false + 0 + + + + + + 222 + 493 + 16 + 27 + + + 230 + 506.5 + + + + + + + + + + + + f31d8d7a-7536-4ac8-9c96-fde6ecda4d0a + Cluster + + + + + + 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 + + Contains a cluster of Grasshopper components + 1 + 2662160f-5d25-46cc-b2fb-037617fa9fc1 + Cluster + T0CF + false + + + + + 3 + 58713318-131c-4f88-981b-e1d4d061c646 + 5edf06f3-69c3-4b43-a30f-29ad2bf28eb8 + bf2d76a6-3501-45a4-893d-ae3e0eaa5ce4 + d0ccacbd-d43e-44d1-95fa-d65b95597d22 + 43cd6740-e900-4cb5-aac6-f6ee8442edb2 + a734e7f6-2f8f-4929-916a-9608854f6392 + + + + + + 353 + 522 + 144 + 46 + + + 419 + 545 + + + + + + 2 + 4f8984c4-7c7a-4d69-b0a2-183cbb330d20 + 3e8ca6be-fda8-4aaf-b5c0-3c54c8bb7312 + 1 + 4f8984c4-7c7a-4d69-b0a2-183cbb330d20 + + + + + Contains a collection of three-dimensional axis-systems + 5edf06f3-69c3-4b43-a30f-29ad2bf28eb8 + Plane + tcf_point + true + b2ac75b7-c534-4271-9a27-3649779c17d0 + 1 + + + + + + 355 + 524 + 49 + 21 + + + 381 + 534.5 + + + + + + + + Contains a collection of floating point numbers + 58713318-131c-4f88-981b-e1d4d061c646 + Number + z_offset + true + 9a4be73c-3082-4c74-be27-41c7c1688c53 + 1 + 1 + + + + + + 355 + 545 + 49 + 21 + + + 381 + 555.5 + + + + + + 1 + + + + + 1 + {0} + + + + + 0.07 + + + + + + + + + + + Contains a collection of three-dimensional axis-systems + bf2d76a6-3501-45a4-893d-ae3e0eaa5ce4 + Plane + t0cf_frame + false + 0 + + + + + + 434 + 524 + 61 + 42 + + + 464.5 + 545 + + + + + + + + + + + + + + a8b97322-2d53-47cd-905e-b932c3ccd74e + Button + + + + + Button object with two values + False + True + 8c731948-abe8-4e50-a551-e6a07b85d9c4 + Button + Button + false + 0 + + + + + + 1738 + 568 + 103 + 22 + + + + + + + + + + 59daf374-bc21-4a5e-8282-5504fb7ae9ae + List Item + + + + + 0 + Retrieve a specific item from a list. + true + 5937d47f-10eb-43c2-b0f8-c788d579c8db + List Item + Item + + + + + + 2678 + 956 + 64 + 64 + + + 2712 + 988 + + + + + + 3 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 2e3ab970-8545-46bb-836c-1c11e5610bce + cb95db89-6165-43b6-9c41-5702bc5bf137 + 1 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + 1 + Base list + 7d1faee1-9526-4289-b122-75a309082be7 + List + L + false + ba0ed5fc-6a7f-48ff-9a13-2a2455c0e6a4 + 1 + + + + + + 2680 + 958 + 17 + 20 + + + 2690 + 968 + + + + + + + + Item index + 0314b618-3ebc-4edc-84c5-dfd0a0fb7a98 + Index + i + false + 76c149ba-07e7-470c-8952-7038c2cb9b2d + 1 + + + + + + 2680 + 978 + 17 + 20 + + + 2690 + 988 + + + + + + 1 + + + + + 1 + {0} + + + + + 0 + + + + + + + + + + + Wrap index to list bounds + 1f21af97-0724-439b-a832-12547b5356a0 + Wrap + W + false + 0 + + + + + + 2680 + 998 + 17 + 20 + + + 2690 + 1008 + + + + + + 1 + + + + + 1 + {0} + + + + + true + + + + + + + + + + + Item at {i'} + 9d1d7aec-b041-4a18-90aa-523201e25b01 + false + Item + i + false + 0 + + + + + + 2727 + 958 + 13 + 60 + + + 2733.5 + 988 + + + + + + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + 76c149ba-07e7-470c-8952-7038c2cb9b2d + Number Slider + + false + 0 + + + + + + 2420 + 978 + 159 + 20 + + + 2420.382 + 978.2473 + + + + + + 3 + 1 + 1 + 200 + 0 + 0 + 200 + + + + + + + + + 3e8ca6be-fda8-4aaf-b5c0-3c54c8bb7312 + Number + + + + + Contains a collection of floating point numbers + fa81c62e-402f-4a98-b743-073a361974d6 + Number + Num + false + a17a3498-e042-4db0-b3e7-eca2f875e5b8 + 1 + + + + + + 328 + 989 + 50 + 20 + + + 353.4652 + 999.7706 + + + + + + + + + + 8ec86459-bf01-4409-baee-174d0d2b13d0 + Data + + + + + Contains a collection of generic data + true + ca6f4492-c165-4b33-8d01-a55901efe6bd + Data + Data + false + f956b690-65c4-4b82-ac9a-488e4afed703 + 1 + + + + + + 328 + 1142 + 50 + 20 + + + 353.2021 + 1152.203 + + + + + + + + + + 1817fd29-20ae-4503-b542-f0fb651e67d7 + List Length + + + + + Measure the length of a list. + true + 54ebcc2c-2159-48d1-88c8-9c12b4311847 + List Length + Lng + + + + + + 1501 + 876 + 61 + 38 + + + 1530 + 895 + + + + + + 1 + Base list + 3dd3ba85-5aaf-47dc-80ca-cfa62b9c0851 + List + L + false + 0fc838b5-d3e6-4ce0-a346-c3a1d321b2f6 + 1 + + + + + + 1503 + 878 + 12 + 34 + + + 1510.5 + 895 + + + + + + + + Number of items in L + ba8fb317-fbd9-4274-8820-55e12a8553ff + Length + L + false + 0 + + + + + + 1545 + 878 + 15 + 34 + + + 1552.5 + 895 + + + + + + + + + + + + 4f8984c4-7c7a-4d69-b0a2-183cbb330d20 + Plane + + + + + Contains a collection of three-dimensional axis-systems + 0fc838b5-d3e6-4ce0-a346-c3a1d321b2f6 + Plane + Pln + false + e328113b-073c-46f1-b400-4227e548946d + 1 + + + + + + 778 + 678 + 50 + 20 + + + 803.885 + 688.4667 + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import math +from compas.geometry import Frame +from compas.geometry import Transformation +from compas.geometry import Translation +from compas_fab.robots import PlanningScene +from compas_fab.robots import AttachedCollisionMesh +from compas_fab.robots import CollisionMesh +from compas_fab.ghpython.components import coerce_frame + + +# TODO: Calculate placement and approach frames +# and prepare scene collision meshes + +place_tolerance = 0.002 +place_t0cf_frames = [coerce_frame(f).transformed(Translation.from_vector([0,0,0.006+place_tolerance])) for f in place_t0cf_frames] +approach_t0cf_frames = [coerce_frame(f).transformed(Translation.from_vector([0,0,approach_offset])) for f in place_t0cf_frames] + + +if robot: + scene = PlanningScene(robot) + scene.remove_attached_collision_mesh('brick') + scene.remove_collision_mesh('brick') + ee_link_name = robot.get_end_effector_link_name() + + if robot.attached_tool: + brick_frame = robot.attached_tool.frame.copy() + else: + brick_frame = Frame.worldXY() + + brick_frame.point.x += 0.006 + brick_acm = AttachedCollisionMesh(CollisionMesh(element, 'brick', brick_frame), ee_link_name) + scene.add_attached_collision_mesh(brick_acm) + + #prepare prev elements + built_elements = 'built_elements' + scene.remove_collision_mesh(built_elements) + + for f in robot.from_t0cf_to_tcf(place_t0cf_frames[0:i-1]): + t = Transformation.from_frame(f) + mesh = element.transformed(t) + cm = CollisionMesh(mesh, built_elements) + scene.append_collision_mesh(cm) + GhPython provides a Python script component + + 29 + 91 + + + 1587 + 1673 + + true + true + 1 + 9baf9b45-b983-48be-a3c4-ff13530c72c6 + false + true + GhPython Script + Prepare scene + + + + + + 1588 + 863 + 243 + 104 + + + 1698 + 915 + + + + + + 5 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 2 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Prepare scene + c9e4df6c-ac62-4400-9db9-b24b79162fbc + robot + robot + true + 0 + true + 9a9900f7-c5de-485a-9791-63a5e1930c86 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1590 + 865 + 93 + 20 + + + 1638 + 875 + + + + + + + + true + Script input i. + 79a39e07-fd19-41ae-9428-089da3b1bd8b + i + i + true + 0 + true + ba8fb317-fbd9-4274-8820-55e12a8553ff + 1 + 48d01794-d3d8-4aef-990e-127168822244 + + + + + + 1590 + 885 + 93 + 20 + + + 1638 + 895 + + + + + + + + 1 + true + Script input place_t0cf_frames. + e61432af-6e45-43dc-b26e-bea2ff009454 + place_t0cf_frames + place_t0cf_frames + true + 1 + true + 0fc838b5-d3e6-4ce0-a346-c3a1d321b2f6 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1590 + 905 + 93 + 20 + + + 1638 + 915 + + + + + + + + true + Script input element. + 311b430c-5e93-4eab-9179-0ef6c103abd1 + element + element + true + 0 + true + 5499898c-9def-41d8-a767-7d5681a3af05 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1590 + 925 + 93 + 20 + + + 1638 + 935 + + + + + + + + true + Script input approach_offset. + 750362de-d9c9-4077-8c89-2f6fe9444338 + approach_offset + approach_offset + true + 0 + true + fa81c62e-402f-4a98-b743-073a361974d6 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1590 + 945 + 93 + 20 + + + 1638 + 955 + + + + + + + + Script output approach_t0cf_frames. + 9a395231-fda6-44ab-b851-46f21049e011 + approach_t0cf_frames + approach_t0cf_frames + false + 0 + + + + + + 1713 + 865 + 116 + 50 + + + 1771 + 890 + + + + + + + + Script output place_t0cf_frames. + 05804095-9196-4944-b8ba-233555dc1a50 + place_t0cf_frames + place_t0cf_frames + false + 0 + + + + + + 1713 + 915 + 116 + 50 + + + 1771 + 940 + + + + + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import os +import math +import compas +from compas.geometry import Frame +from compas.geometry import Transformation +from compas.geometry import Translation +from compas_ghpython.artists import MeshArtist +from compas_fab.robots import Configuration +from compas_fab.robots import PlanningScene +from compas_fab.robots import AttachedCollisionMesh +from compas_fab.robots import CollisionMesh +from compas_fab.ghpython.components import coerce_frame +from scriptcontext import sticky as st + +# TODO: Calculate two trajectories: +# 1) kinematic plan from last point of pickup +# 2) cartesian plan for placing + +def generate_trajectory(f, pif, pickup_trajectory): + + #initialize start_configuration + start_configuration = robot.zero_configuration() + #place trafectory + if pickup_trajectory: + start_configuration.values = pickup_trajectory.points[-1].values + place_trajectory = robot.plan_cartesian_motion(f, + start_configuration, + group=group, + options=dict(max_step=0.01, + avoid_collisions=True, + planner_id='RRTstarkConfigDefault')) + #pickup trajectory + start_configuration.values = place_trajectory.points[-1].values + pickup_trajectory = robot.plan_cartesian_motion(pif, + start_configuration, + group=group, + options=dict(max_step=0.01, + avoid_collisions=True, + planner_id='RRTstarkConfigDefault')) + + st[place_key] = place_trajectory + st[traj_key] = pickup_trajectory + place_trajectory = st.get(place_key) + pickup_trajectory = st.get(traj_key) + + return place_trajectory, pickup_trajectory + + +traj_key = '{}_{}'.format('pickup_trajectory', ghenv.Component.InstanceGuid) +place_key = '{}_{}'.format('place_trajectory', ghenv.Component.InstanceGuid) + +pif = [coerce_frame(pf) for pf in pickup_t0cf_frames] +frames = [[af, plf, af] for plf, af in zip(place_t0cf_frames, approach_t0cf_frames)] + +pickup_trajectories = [] +place_trajectories = [] + +if robot and compute: + for f in frames: + place_trajectory, pickup_trajectory = generate_trajectory(f, pif, pickup_trajectory) + pickup_trajectories.append(pickup_trajectory) + place_trajectories.append(place_trajectory) + + GhPython provides a Python script component + + 5 + 52 + + + 2248 + 1893 + + true + true + 1 + a5f3f98d-e1af-45fc-a57c-8f01a829d2a3 + false + true + GhPython Script + Plan Motion + + + + + + 1910 + 863 + 248 + 164 + + + 2040 + 945 + + + + + + 8 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 2 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Exercise + ca9f115d-3d3d-4588-959a-d284fa2c2819 + robot + robot + true + 0 + true + 9a9900f7-c5de-485a-9791-63a5e1930c86 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + 1 + + + + + + 1912 + 865 + 113 + 20 + + + 1970 + 875 + + + + + + + + true + Script input group. + ed9ae14a-2d2b-4e52-b139-b2856c9035ba + group + group + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1912 + 885 + 113 + 20 + + + 1970 + 895 + + + + + + + + true + Script input home_config. + 019898a9-2d8c-4cef-bb68-22c1f5e5e7dc + home_config + home_config + true + 0 + true + ca6f4492-c165-4b33-8d01-a55901efe6bd + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + 1 + + + + + + 1912 + 905 + 113 + 20 + + + 1970 + 915 + + + + + + + + true + Script input pickup_trajectory. + 217d4a4a-4eb2-46fa-852c-720a1f916b36 + pickup_trajectory + pickup_trajectory + true + 0 + true + dcfe8806-86f9-4cfc-af81-819cd25482f0 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1912 + 925 + 113 + 20 + + + 1970 + 935 + + + + + + + + 1 + true + Script input pickup_t0cf_frames. + 18057ac5-0cdb-4748-8bbe-d09ad71943b7 + pickup_t0cf_frames + pickup_t0cf_frames + true + 1 + true + b241bc34-d3b5-49b7-b546-a83f9d99d91f + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1912 + 945 + 113 + 20 + + + 1970 + 955 + + + + + + + + 1 + true + Script input approach_t0cf_frames. + 38ca1d8b-ce5f-41b1-a31c-12ab715428af + approach_t0cf_frames + approach_t0cf_frames + true + 1 + true + 9a395231-fda6-44ab-b851-46f21049e011 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1912 + 965 + 113 + 20 + + + 1970 + 975 + + + + + + + + 1 + true + Script input place_t0cf_frames. + d2330b8f-dadb-47a0-a5ce-51653a422c3a + place_t0cf_frames + place_t0cf_frames + true + 1 + true + 05804095-9196-4944-b8ba-233555dc1a50 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1912 + 985 + 113 + 20 + + + 1970 + 995 + + + + + + + + true + Script input compute. + 93e2f7ca-9611-43a2-afe0-bdb6c0498b83 + compute + compute + true + 0 + true + 314e45dc-6256-4dcc-90c3-aaa1e08b613a + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1912 + 1005 + 113 + 20 + + + 1970 + 1015 + + + + + + + + Script output pickup_trajectories. + 0ac0402c-aca7-4167-9000-cfddc8a90179 + pickup_trajectories + pickup_trajectories + false + 0 + + + + + + 2055 + 865 + 101 + 80 + + + 2105.5 + 905 + + + + + + + + Script output place_trajectories. + a46ae31f-ef20-45a8-89b4-578092ccba5e + place_trajectories + place_trajectories + false + 0 + + + + + + 2055 + 945 + 101 + 80 + + + 2105.5 + 985 + + + + + + + + + + + + + + 2e78987b-9dfb-42a2-8b76-3923ac8bd91a + Boolean Toggle + + + + + Boolean (true/false) toggle + 314e45dc-6256-4dcc-90c3-aaa1e08b613a + Boolean Toggle + Toggle + false + 0 + true + + + + + + 1906 + 835 + 104 + 22 + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + # TODO: Merge all trajectories +configs = [] +for i, p in enumerate(pick): + if i == 0: # first pickup & place + configurations = pick1.points + place[i].points + p.points + else: # from 2nd to last pickup & place + configurations = place[i].points + p.points + configs.extend(configurations) + +#configs = pick1.points +#for place_trajectory, pick_trajectory in zip(place, pick): +# configs.extend(place_trajectory.points) +# configs.extend(pick_trajectory.points) + + GhPython provides a Python script component + + 243 + 519 + + + 1607 + 1408 + + true + true + 1 + 7e727ff6-4118-4fcc-8d61-a780f76ab28c + false + true + GhPython Script + Merge trajectories + + + + + + 2234 + 860 + 147 + 132 + + + 2284 + 926 + + + + + + 3 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 2 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + 40f57814-6fbb-4633-b6e1-7e774843d97b + pick1 + pick1 + true + 0 + true + dcfe8806-86f9-4cfc-af81-819cd25482f0 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2236 + 862 + 33 + 42 + + + 2254 + 883.3333 + + + + + + + + 1 + true + Script input pick. + d53c24e6-bee9-445a-91ad-3500c38bad25 + pick + pick + true + 1 + true + 0ac0402c-aca7-4167-9000-cfddc8a90179 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2236 + 904 + 33 + 43 + + + 2254 + 926 + + + + + + + + 1 + true + Script input place. + ff2f0e70-e3c1-43d8-9768-ad16fff70f86 + place + place + true + 1 + true + a46ae31f-ef20-45a8-89b4-578092ccba5e + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2236 + 947 + 33 + 43 + + + 2254 + 968.6666 + + + + + + + + Script output configurations. + 88ff1b67-787f-4ad4-a11e-3ae6f1c31b08 + configurations + configurations + false + 0 + + + + + + 2299 + 862 + 80 + 64 + + + 2339 + 894 + + + + + + + + Script output configs. + ba0ed5fc-6a7f-48ff-9a13-2a2455c0e6a4 + configs + configs + false + 0 + + + + + + 2299 + 926 + 80 + 64 + + + 2339 + 958 + + + + + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import Grasshopper +import System +from ghpythonlib.componentbase import executingcomponent as component +from scriptcontext import sticky as st + +from compas_ghpython import draw_frame +from compas_fab.ghpython.components import create_id +from compas_fab.ghpython.components import coerce_frame +from compas_fab.ghpython.components.icons import trajectory_visualize_icon + +class TrajectoryVisualize(component): + def SetUpParam(self, p, name, nickname, description): + p.Name = name + p.NickName = nickname + p.Description = description + p.Optional = True + + def RegisterInputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "robot", "robot", "The robot.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_String() + self.SetUpParam(p, "group", "group", "The planning group for which this trajectory was planned.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "trajectory", "trajectory", "The calculated trajectory.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + def RegisterOutputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "start_configuration", "start_configuration", "The start configuration of the trajectory.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "configurations", "configurations", "The full configurations along the trajectory.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Number() + self.SetUpParam(p, "fraction", "fraction", "Indicates the percentage of requested trajectory that was calculated, e.g. 1 means the full trajectory was found.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Number() + self.SetUpParam(p, "time", "time", "The time which is needed to execute that trajectory at full speed.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Plane() + self.SetUpParam(p, "planes", "planes", "The planes of the robot's end-effector.") + self.Params.Output.Add(p) + + def SolveInstance(self, DA): + p0 = self.marshal.GetInput(DA, 0) + p1 = self.marshal.GetInput(DA, 1) + p2 = self.marshal.GetInput(DA, 2) + result = self.RunScript(p0, p1, p2) + + if result is not None: + if not hasattr(result, '__getitem__'): + self.marshal.SetOutput(result, DA, 0, True) + else: + self.marshal.SetOutput(result[0], DA, 0, True) + self.marshal.SetOutput(result[1], DA, 1, True) + self.marshal.SetOutput(result[2], DA, 2, True) + self.marshal.SetOutput(result[3], DA, 3, True) + self.marshal.SetOutput(result[4], DA, 4, True) + + def get_Internal_Icon_24x24(self): + return trajectory_visualize_icon + + def RunScript(self, robot, group, trajectory): + start_configuration = None + configurations = [] + fraction = 0. + time = 0. + + planes = [] + positions = [] + velocities = [] + accelerations = [] + + if robot and trajectory: + group = group or robot.main_group_name + + for c in trajectory.points: + configurations.append(robot.merge_group_with_full_configuration(c, trajectory.start_configuration, group)) + frame = robot.forward_kinematics(c, group, options=dict(solver='model')) + planes.append(draw_frame(frame)) + positions.append(c.positions) + velocities.append(c.velocities) + accelerations.append(c.accelerations) + + start_configuration = trajectory.start_configuration + fraction = trajectory.fraction + time = trajectory.time_from_start + + # return outputs if you have them; here I try it for you: + return (start_configuration, configurations, fraction, time, planes) + + GhPython provides a Python script component + + 416 + 248 + + + 684 + 571 + + true + true + 1 + a003596c-fc8d-4ac5-a6f1-fd7555dd239b + true + true + GhPython Script + Visualize Trajectory + + + + + + 2560 + 443 + 188 + 140 + + + 2630 + 513 + + + + + + 3 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 5 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + c2ecc370-89a1-4036-98cb-7c6f65d1a833 + robot + robot + true + 0 + true + 9a9900f7-c5de-485a-9791-63a5e1930c86 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2562 + 445 + 53 + 45 + + + 2590 + 467.6667 + + + + + + + + true + Script input group. + 8ef2dfa3-7a51-4468-87a7-e909f16a93a9 + group + group + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2562 + 490 + 53 + 45 + + + 2590 + 513 + + + + + + + + true + Script input trajectory. + 532027a2-c91c-42f4-96ca-4df4cab1a38f + trajectory + trajectory + true + 0 + true + dcfe8806-86f9-4cfc-af81-819cd25482f0 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2562 + 535 + 53 + 46 + + + 2590 + 558.3334 + + + + + + + + Script output start_configuration. + 4ca48c0c-9209-4c41-bb93-a7a6e9870d3e + start_configuration + start_configuration + false + 0 + + + + + + 2645 + 445 + 101 + 27 + + + 2695.5 + 458.6 + + + + + + + + Script output configurations. + f8b39b52-7c01-410c-8138-f8d2ef2fa87b + configurations + configurations + false + 0 + + + + + + 2645 + 472 + 101 + 27 + + + 2695.5 + 485.8 + + + + + + + + Script output fraction. + 9065a65a-77c0-4ef1-87e7-afe00bdb0ecf + fraction + fraction + false + 0 + + + + + + 2645 + 499 + 101 + 27 + + + 2695.5 + 513 + + + + + + + + Script output time. + 2fdce139-a2fa-41a7-8020-1ea165e94d9c + time + time + false + 0 + + + + + + 2645 + 526 + 101 + 27 + + + 2695.5 + 540.2 + + + + + + + + Script output planes. + c86f8bc2-ded5-4c66-9c5a-48f54206c708 + planes + planes + false + 0 + + + + + + 2645 + 553 + 101 + 28 + + + 2695.5 + 567.4 + + + + + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import Grasshopper +import System +from ghpythonlib.componentbase import executingcomponent as component +from scriptcontext import sticky as st + +from compas_ghpython import draw_frame +from compas_fab.ghpython.components import create_id +from compas_fab.ghpython.components import coerce_frame +from compas_fab.ghpython.components.icons import trajectory_visualize_icon + +class TrajectoryVisualize(component): + def SetUpParam(self, p, name, nickname, description): + p.Name = name + p.NickName = nickname + p.Description = description + p.Optional = True + + def RegisterInputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "robot", "robot", "The robot.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_String() + self.SetUpParam(p, "group", "group", "The planning group for which this trajectory was planned.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "trajectory", "trajectory", "The calculated trajectory.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + def RegisterOutputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "start_configuration", "start_configuration", "The start configuration of the trajectory.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "configurations", "configurations", "The full configurations along the trajectory.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Number() + self.SetUpParam(p, "fraction", "fraction", "Indicates the percentage of requested trajectory that was calculated, e.g. 1 means the full trajectory was found.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Number() + self.SetUpParam(p, "time", "time", "The time which is needed to execute that trajectory at full speed.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Plane() + self.SetUpParam(p, "planes", "planes", "The planes of the robot's end-effector.") + self.Params.Output.Add(p) + + def SolveInstance(self, DA): + p0 = self.marshal.GetInput(DA, 0) + p1 = self.marshal.GetInput(DA, 1) + p2 = self.marshal.GetInput(DA, 2) + result = self.RunScript(p0, p1, p2) + + if result is not None: + if not hasattr(result, '__getitem__'): + self.marshal.SetOutput(result, DA, 0, True) + else: + self.marshal.SetOutput(result[0], DA, 0, True) + self.marshal.SetOutput(result[1], DA, 1, True) + self.marshal.SetOutput(result[2], DA, 2, True) + self.marshal.SetOutput(result[3], DA, 3, True) + self.marshal.SetOutput(result[4], DA, 4, True) + + def get_Internal_Icon_24x24(self): + return trajectory_visualize_icon + + def RunScript(self, robot, group, trajectory): + start_configuration = None + configurations = [] + fraction = 0. + time = 0. + + planes = [] + positions = [] + velocities = [] + accelerations = [] + + if robot and trajectory: + group = group or robot.main_group_name + + for c in trajectory.points: + configurations.append(robot.merge_group_with_full_configuration(c, trajectory.start_configuration, group)) + frame = robot.forward_kinematics(c, group, options=dict(solver='model')) + planes.append(draw_frame(frame)) + positions.append(c.positions) + velocities.append(c.velocities) + accelerations.append(c.accelerations) + + start_configuration = trajectory.start_configuration + fraction = trajectory.fraction + time = trajectory.time_from_start + + # return outputs if you have them; here I try it for you: + return (start_configuration, configurations, fraction, time, planes) + + GhPython provides a Python script component + + 55 + 110 + + + 790 + 655 + + true + true + 1 + 781902aa-fb33-4d03-bad8-fce4aafa0416 + true + true + GhPython Script + Visualize Trajectory + + + + + + 2558 + 604 + 188 + 140 + + + 2628 + 674 + + + + + + 3 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 5 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + 7dc3e565-84df-4953-bf24-5c77f7c7a423 + robot + robot + true + 0 + true + 9a9900f7-c5de-485a-9791-63a5e1930c86 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2560 + 606 + 53 + 45 + + + 2588 + 628.6667 + + + + + + + + true + Script input group. + 73e6a41a-9f77-4b43-afc1-6e73a6533efa + group + group + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2560 + 651 + 53 + 45 + + + 2588 + 674 + + + + + + + + true + Script input trajectory. + 5f0d4956-14e9-427b-ab67-35240ba513f7 + trajectory + trajectory + true + 0 + true + bb002e1d-5173-4ee7-a7c5-4967ac376aee + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2560 + 696 + 53 + 46 + + + 2588 + 719.3334 + + + + + + + + Script output start_configuration. + d3235a1d-b214-4000-8569-d80b021b81cc + start_configuration + start_configuration + false + 0 + + + + + + 2643 + 606 + 101 + 27 + + + 2693.5 + 619.6 + + + + + + + + Script output configurations. + 5ec3cd18-022a-47e7-a77c-f60c949ef49c + configurations + configurations + false + 0 + + + + + + 2643 + 633 + 101 + 27 + + + 2693.5 + 646.8 + + + + + + + + Script output fraction. + ce13f3f5-9eb5-450c-a548-9749a81a9b11 + fraction + fraction + false + 0 + + + + + + 2643 + 660 + 101 + 27 + + + 2693.5 + 674 + + + + + + + + Script output time. + 10a7dcba-3a56-4867-9762-82c0d0c4c5e7 + time + time + false + 0 + + + + + + 2643 + 687 + 101 + 27 + + + 2693.5 + 701.2 + + + + + + + + Script output planes. + c252b34a-1aef-4345-9144-82d68324950d + planes + planes + false + 0 + + + + + + 2643 + 714 + 101 + 28 + + + 2693.5 + 728.4 + + + + + + + + + + + + + + 59daf374-bc21-4a5e-8282-5504fb7ae9ae + List Item + + + + + 2 + Retrieve a specific item from a list. + true + 2c6392f9-9507-48f4-b75d-443af7101485 + List Item + Item + + + + + + 2454 + 682 + 71 + 64 + + + 2488 + 714 + + + + + + 3 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 2e3ab970-8545-46bb-836c-1c11e5610bce + cb95db89-6165-43b6-9c41-5702bc5bf137 + 3 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + 1 + Base list + 2536c882-3bd9-4327-9585-3abdcf223b2f + List + L + false + 0ac0402c-aca7-4167-9000-cfddc8a90179 + 1 + + + + + + 2456 + 684 + 17 + 20 + + + 2466 + 694 + + + + + + + + Item index + 405dc0e0-6f36-4b18-bf46-ff30825051d3 + Index + i + false + 0 + + + + + + 2456 + 704 + 17 + 20 + + + 2466 + 714 + + + + + + 1 + + + + + 1 + {0} + + + + + 0 + + + + + + + + + + + Wrap index to list bounds + c11ab3ef-4c6d-44a8-88d0-800d69d7585a + Wrap + W + false + 0 + + + + + + 2456 + 724 + 17 + 20 + + + 2466 + 734 + + + + + + 1 + + + + + 1 + {0} + + + + + true + + + + + + + + + + + Item at {-2'} + bb002e1d-5173-4ee7-a7c5-4967ac376aee + false + Item -2 + -2 + false + 0 + + + + + + 2503 + 684 + 20 + 20 + + + 2513 + 694 + + + + + + + + Item at {-1'} + 35eaba3d-01ac-44a0-a089-6eb45a516918 + false + Item -1 + -1 + false + 0 + + + + + + 2503 + 704 + 20 + 20 + + + 2513 + 714 + + + + + + + + Item at {i'} + e136193d-2f3b-41a5-b6e7-731f0eaa65e5 + false + Item + i + false + 0 + + + + + + 2503 + 724 + 20 + 20 + + + 2513 + 734 + + + + + + + + + + + + + + 537b0419-bbc2-4ff4-bf08-afe526367b2c + Custom Preview + + + + + Allows for customized geometry previews + true + ed0f70d1-142a-4a13-8cf6-ae134783d257 + Custom Preview + Preview + + + + + + + 3415 + 1019 + 48 + 65 + + + 3449 + 1052 + + + + + + Geometry to preview + true + adb03630-9339-4e68-a1c3-7e119f78d90d + Geometry + G + false + a3d2f4c0-32db-43a1-8295-40c99a145ca9 + 1 + + + + + + 3417 + 1021 + 17 + 30 + + + 3427 + 1036.25 + + + + + + + + The material override + fab79a21-11bc-40b0-913f-9efbf3ddf6de + Material + M + false + 1a804a97-9b85-4230-a3b1-f0eff8f793e7 + 1 + + + + + + 3417 + 1051 + 17 + 31 + + + 3427 + 1066.75 + + + + + + 1 + + + + + 1 + {0} + + + + + + 255;221;160;221 + + + 255;66;48;66 + + 0.5 + + 255;255;255;255 + + 0 + + + + + + + + + + + + + + + 59daf374-bc21-4a5e-8282-5504fb7ae9ae + List Item + + + + + 1 + Retrieve a specific item from a list. + true + c91d7211-9cd1-4e72-b248-c7b8194d29da + List Item + Item + + + + + + 3294 + 1003 + 71 + 64 + + + 3328 + 1035 + + + + + + 3 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 2e3ab970-8545-46bb-836c-1c11e5610bce + cb95db89-6165-43b6-9c41-5702bc5bf137 + 2 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + 1 + Base list + af8c693e-ac3f-420e-8c52-a607cc2449c4 + List + L + false + 5425d29f-9702-41b7-b855-110be3410f3f + 1 + + + + + + 3296 + 1005 + 17 + 20 + + + 3306 + 1015 + + + + + + + + Item index + 597cc5db-f181-4e8f-b541-8fb5b4c771b9 + Index + i + false + 0 + + + + + + 3296 + 1025 + 17 + 20 + + + 3306 + 1035 + + + + + + 1 + + + + + 1 + {0} + + + + + 0 + + + + + + + + + + + Wrap index to list bounds + a3c834d9-ab99-47d5-8464-e267ee386a35 + Wrap + W + false + 0 + + + + + + 3296 + 1045 + 17 + 20 + + + 3306 + 1055 + + + + + + 1 + + + + + 1 + {0} + + + + + true + + + + + + + + + + + Item at {-1'} + a3d2f4c0-32db-43a1-8295-40c99a145ca9 + false + Item -1 + -1 + false + 0 + + + + + + 3343 + 1005 + 20 + 30 + + + 3353 + 1020 + + + + + + + + Item at {i'} + 62af9f1b-d4da-4985-b080-2f88c858c938 + false + Item + i + false + 0 + + + + + + 3343 + 1035 + 20 + 30 + + + 3353 + 1050 + + + + + + + + + + + + + + 4fdfe351-6c07-47ce-9fb9-be027fb62186 + Shift List + + + + + Offset all items in a list. + true + c5ddd8d0-09e6-4df1-adf6-11395b48fd76 + Shift List + Shift + + + + + + 3299 + 1107 + 66 + 64 + + + 3333 + 1139 + + + + + + 1 + List to shift + 30b7268c-4bfb-4bc8-b589-e338ec57e0c2 + List + L + false + 5425d29f-9702-41b7-b855-110be3410f3f + 1 + + + + + + 3301 + 1109 + 17 + 20 + + + 3311 + 1119 + + + + + + + + Shift offset + 62b39994-727e-4d69-82fb-6cf0662c7220 + Shift + S + false + 0 + + + + + + 3301 + 1129 + 17 + 20 + + + 3311 + 1139 + + + + + + 1 + + + + + 1 + {0} + + + + + -1 + + + + + + + + + + + Wrap values + 930334a1-e544-4d53-b828-395d9386bfea + Wrap + W + false + 0 + + + + + + 3301 + 1149 + 17 + 20 + + + 3311 + 1159 + + + + + + 1 + + + + + 1 + {0} + + + + + false + + + + + + + + + + + 1 + Shifted list + a2311c9f-7aed-4d4d-965e-64553de08172 + List + L + false + 0 + + + + + + 3348 + 1109 + 15 + 60 + + + 3355.5 + 1139 + + + + + + + + + + + + 537b0419-bbc2-4ff4-bf08-afe526367b2c + Custom Preview + + + + + Allows for customized geometry previews + true + 066da693-d960-4d27-bb66-e473cc3ca3d4 + Custom Preview + Preview + + + + + + + 3413 + 1122 + 48 + 65 + + + 3447 + 1155 + + + + + + Geometry to preview + true + a8c7a71a-10c5-4e2f-bd62-7acf9b3d4bee + Geometry + G + false + a2311c9f-7aed-4d4d-965e-64553de08172 + 1 + + + + + + 3415 + 1124 + 17 + 30 + + + 3425 + 1139.25 + + + + + + + + The material override + 3afe242d-8bd4-4dd1-b859-67f462b06e4b + Material + M + false + 1f9ff1ad-b792-4248-b3c1-943ac5786365 + 1 + + + + + + 3415 + 1154 + 17 + 31 + + + 3425 + 1169.75 + + + + + + 1 + + + + + 1 + {0} + + + + + + 255;221;160;221 + + + 255;66;48;66 + + 0.5 + + 255;255;255;255 + + 0 + + + + + + + + + + + + + + + 1e936df3-0eea-4246-8549-514cb8862b7a + Mesh + + + + + Contains a collection of polygon meshes + true + 98a6e12d-8448-4993-9edd-71c7658e5f38 + Mesh + Mesh + false + 7fb0382a-e34f-49af-8952-9248a6d53e87 + 1 + + + + + + 3318 + 947 + 50 + 20 + + + 3343.404 + 957.9166 + + + + + + + + + + 537b0419-bbc2-4ff4-bf08-afe526367b2c + Custom Preview + + + + + Allows for customized geometry previews + true + 0deadd24-8f12-4829-aaa9-82bf0d5415f5 + Custom Preview + Preview + + + + + + + 3413 + 938 + 48 + 65 + + + 3447 + 971 + + + + + + Geometry to preview + true + 0825e73d-9630-4e0b-8abb-ec912bdfbb34 + Geometry + G + false + 98a6e12d-8448-4993-9edd-71c7658e5f38 + 1 + + + + + + 3415 + 940 + 17 + 30 + + + 3425 + 955.25 + + + + + + + + The material override + 6429eb66-ae28-47d9-9763-442445701133 + Material + M + false + 70cc5425-d702-4584-b46d-0509ab8a651d + 1 + + + + + + 3415 + 970 + 17 + 31 + + + 3425 + 985.75 + + + + + + 1 + + + + + 1 + {0} + + + + + + 255;221;160;221 + + + 255;66;48;66 + + 0.5 + + 255;255;255;255 + + 0 + + + + + + + + + + + + + + + 9c53bac0-ba66-40bd-8154-ce9829b9db1a + Colour Swatch + + + + + Colour (palette) swatch + 70cc5425-d702-4584-b46d-0509ab8a651d + Colour Swatch + Swatch + false + 0 + + 0;255;255;255 + + + + + + + 3280 + 978 + 88 + 20 + + + 3280.535 + 978.3527 + + + + + + + + + + 9c53bac0-ba66-40bd-8154-ce9829b9db1a + Colour Swatch + + + + + Colour (palette) swatch + 1f9ff1ad-b792-4248-b3c1-943ac5786365 + Colour Swatch + Swatch + false + 0 + + 91;45;189;131 + + + + + + + 3284 + 1185 + 88 + 20 + + + 3284.563 + 1185.333 + + + + + + + + + + 9c53bac0-ba66-40bd-8154-ce9829b9db1a + Colour Swatch + + + + + Colour (palette) swatch + 1a804a97-9b85-4230-a3b1-f0eff8f793e7 + Colour Swatch + Swatch + false + 0 + + 197;255;255;255 + + + + + + + 3280 + 1079 + 88 + 20 + + + 3280.604 + 1079.713 + + + + + + + + + + c552a431-af5b-46a9-a8a4-0fcbc27ef596 + Group + + + + + 1 + + 150;255;255;255 + + A group of Grasshopper objects + ed0f70d1-142a-4a13-8cf6-ae134783d257 + c91d7211-9cd1-4e72-b248-c7b8194d29da + c5ddd8d0-09e6-4df1-adf6-11395b48fd76 + 066da693-d960-4d27-bb66-e473cc3ca3d4 + 98a6e12d-8448-4993-9edd-71c7658e5f38 + 0deadd24-8f12-4829-aaa9-82bf0d5415f5 + 70cc5425-d702-4584-b46d-0509ab8a651d + 1f9ff1ad-b792-4248-b3c1-943ac5786365 + 1a804a97-9b85-4230-a3b1-f0eff8f793e7 + 9 + b652b4a5-2a9a-405b-8b30-b42a702ce466 + Group + color mesh + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + from compas_rhino import unload_modules +unload_modules('model') + +import Rhino.Geometry as rg +import math as m + +from model import Brick + +planes = [] +meshes = [] + +for i in range (z_number):#Z + for j in range (x_number): #x + for k in range (y_number): #y + if i % 2 == 1: + + x_value = (Brick.REFERENCE_LENGTH * j)+(3*j)+(Brick.REFERENCE_LENGTH/2) + y_value = Brick.REFERENCE_WIDTH*k + (4.5*k) * (j/6 +0.03) + z_value = Brick.REFERENCE_HEIGHT * i + i*1 + plane_origin = rg.Point3d(x_value, y_value, z_value ) + plane = rg.Plane(plane_origin, rg.Vector3d.XAxis, rg.Vector3d.YAxis) + rg.Plane.Rotate(plane, m.radians(j*4 +10), plane.ZAxis, plane.Origin) + #rg.Plane.Translate(plane, rg.Vector3d(0,0,0)) + + else: + + x_value = (Brick.REFERENCE_LENGTH * j)+(3*j) + y_value = Brick.REFERENCE_WIDTH*k + (4.5*k) * (j/6 +0.03) + z_value = Brick.REFERENCE_HEIGHT * i + i*1 + plane_origin = rg.Point3d(x_value, y_value, z_value ) + plane = rg.Plane(plane_origin, rg.Vector3d.XAxis, rg.Vector3d.YAxis) + rg.Plane.Rotate(plane, m.radians(j*4+10), plane.ZAxis, plane.Origin) + #rg.Plane.Translate(plane, rg.Vector3d(0,0,0)) + + + myBrick = Brick(plane) + planes.append(myBrick.base_plane()) + meshes.append(myBrick.mesh()) + + + + + + + + + GhPython provides a Python script component + + 905 + 278 + + + 2240 + 2019 + + true + true + false + ecee5b24-b1ed-418d-a4bd-6e0b750fcf1f + false + true + GhPython Script + New pattern + + + + + + 208 + 664 + 136 + 95 + + + 280 + 712 + + + + + + 3 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 3 + 3ede854e-c753-40eb-84cb-b48008f14fd4 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Bricks + 3ae1f858-6f97-40e7-894e-7e79d9980bfc + x_number + x_number + true + 0 + true + b7e20f86-efa7-4972-ae82-910348246785 + 1 + 48d01794-d3d8-4aef-990e-127168822244 + + + + + + 210 + 666 + 55 + 30 + + + 239 + 681.1667 + + + + + + + + true + Script input y_number. + 266f99a8-d92a-483a-8486-fcfff78a2045 + y_number + y_number + true + 0 + true + 97712fde-30f8-43e4-82bf-959e99216762 + 1 + 48d01794-d3d8-4aef-990e-127168822244 + + + + + + 210 + 696 + 55 + 30 + + + 239 + 711.5 + + + + + + + + true + Script input z_number. + b32f1f4c-9e01-46a3-85bb-95719646b77d + z_number + z_number + true + 0 + true + ee91ac60-35dc-4d05-9b63-3e908d900c4e + 1 + 48d01794-d3d8-4aef-990e-127168822244 + + + + + + 210 + 726 + 55 + 31 + + + 239 + 741.8334 + + + + + + + + The execution information, as output and error streams + 94076860-4624-4147-a577-0e7f980a0a14 + out + out + false + 0 + + + + + + 295 + 666 + 47 + 30 + + + 318.5 + 681.1667 + + + + + + + + Script output meshes. + 59e0a68d-c1f0-47ac-a611-d218da7d1ea0 + meshes + meshes + false + 0 + + + + + + 295 + 696 + 47 + 30 + + + 318.5 + 711.5 + + + + + + + + Script output planes. + 7fc4b0c1-c68f-421f-83d3-ce723a03bfaa + planes + planes + false + 0 + + + + + + 295 + 726 + 47 + 31 + + + 318.5 + 741.8334 + + + + + + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + b7e20f86-efa7-4972-ae82-910348246785 + Number Slider + + false + 0 + + + + + + 10 + 674 + 181 + 20 + + + 10.6203 + 674.014 + + + + + + 3 + 1 + 1 + 10 + 0 + 0 + 4 + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + 97712fde-30f8-43e4-82bf-959e99216762 + Number Slider + + false + 0 + + + + + + 10 + 697 + 181 + 20 + + + 10.31134 + 697.803 + + + + + + 3 + 1 + 1 + 10 + 0 + 0 + 3 + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + ee91ac60-35dc-4d05-9b63-3e908d900c4e + Number Slider + + false + 0 + + + + + + 9 + 720 + 181 + 20 + + + 9.754333 + 720.5229 + + + + + + 3 + 1 + 1 + 10 + 0 + 0 + 5 + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + 54e72725-6aae-4d4b-90ac-7db02740a14e + Number Slider + + false + 0 + + + + + + 26 + 644 + 165 + 20 + + + 26.14142 + 644.3318 + + + + + + 3 + 1 + 0 + 0.001 + 0 + 0 + 0.001 + + + + + + + + + 4d2a06bd-4b0f-4c65-9ee0-4220e4c01703 + Scale + + + + + Scale an object uniformly in all directions. + true + 60acdc37-fc82-478a-b966-1c2238e744fd + Scale + Scale + + + + + + 370 + 700 + 67 + 64 + + + 402 + 732 + + + + + + Base geometry + d54ecf4b-f7d5-4fe7-9cb5-7b3b72071a32 + Geometry + G + true + 7fc4b0c1-c68f-421f-83d3-ce723a03bfaa + 1 + + + + + + 372 + 702 + 15 + 20 + + + 381 + 712 + + + + + + + + Center of scaling + c5b4834e-1fa8-4cbf-b03d-954548a0b9dd + Center + C + false + 0 + + + + + + 372 + 722 + 15 + 20 + + + 381 + 732 + + + + + + 1 + + + + + 1 + {0} + + + + + + + 0 + 0 + 0 + + + + + + + + + + + + Scaling factor + bd187879-3f5e-4c2e-a184-3da8220806b4 + Factor + F + false + 54e72725-6aae-4d4b-90ac-7db02740a14e + 1 + + + + + + 372 + 742 + 15 + 20 + + + 381 + 752 + + + + + + 1 + + + + + 1 + {0} + + + + + 0.5 + + + + + + + + + + + Scaled geometry + be157387-2fdb-4ca4-948d-ec4fe87d5d98 + Geometry + G + false + 0 + + + + + + 417 + 702 + 18 + 30 + + + 426 + 717 + + + + + + + + Transformation data + a1e7a09d-d39a-4514-b833-6e712e2955c2 + Transform + X + false + 0 + + + + + + 417 + 732 + 18 + 30 + + + 426 + 747 + + + + + + + + + + + + e9eb1dcf-92f6-4d4d-84ae-96222d60f56b + Move + + + + + Translate (move) an object along a vector. + true + 285d8023-3187-4a8b-b033-5ca8221d7e7c + Move + Move + + + + + + 455 + 696 + 67 + 50 + + + 487 + 721 + + + + + + Base geometry + 7e693878-ac4f-4227-8f63-1db12b40cc8f + Geometry + G + true + be157387-2fdb-4ca4-948d-ec4fe87d5d98 + 1 + + + + + + 457 + 698 + 15 + 23 + + + 466 + 709.5 + + + + + + + + Translation vector + 5a835661-d960-43c9-8c55-c47c37be27dd + Motion + T + false + 1ce54dea-cb37-4f20-83c6-0edaa26ec43a + 1 + + + + + + 457 + 721 + 15 + 23 + + + 466 + 732.5 + + + + + + 1 + + + + + 1 + {0} + + + + + + 0 + 0 + 10 + + + + + + + + + + + + Translated geometry + dae489a9-07c9-46f6-8e62-bf21c52b9a31 + Geometry + G + false + 0 + + + + + + 502 + 698 + 18 + 23 + + + 511 + 709.5 + + + + + + + + Transformation data + 0b7fa6fd-cd9b-4582-b9e3-d18729869210 + Transform + X + false + 0 + + + + + + 502 + 721 + 18 + 23 + + + 511 + 732.5 + + + + + + + + + + + + d3d195ea-2d59-4ffa-90b1-8b7ff3369f69 + Unit Y + + + + + Unit vector parallel to the world {y} axis. + true + 0a375a78-f249-4c35-8278-385aecd42907 + Unit Y + Y + + + + + + 210 + 614 + 63 + 28 + + + 239 + 628 + + + + + + Unit multiplication + ba06b012-5594-45c5-90a0-3819ca81ff20 + Factor + F + false + a909b265-17c3-49bf-a1cd-31e1a3085e6f + 1 + + + + + + 212 + 616 + 12 + 24 + + + 219.5 + 628 + + + + + + 1 + + + + + 1 + {0} + + + + + 1 + + + + + + + + + + + World {y} vector + 1ce54dea-cb37-4f20-83c6-0edaa26ec43a + Unit vector + V + false + 0 + + + + + + 254 + 616 + 17 + 24 + + + 262.5 + 628 + + + + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + a909b265-17c3-49bf-a1cd-31e1a3085e6f + Number Slider + + false + 0 + + + + + + 28 + 621 + 163 + 20 + + + 28.42804 + 621.228 + + + + + + 1 + 1 + 0 + 1 + 0 + 0 + 0.6 + + + + + + + + + f31d8d7a-7536-4ac8-9c96-fde6ecda4d0a + Cluster + + + + + + 1VsJdBPVGp62aZt0oWV5gMoyynIAaVlEReRJkhlbAt0gBdoKyjSZpiNJJkympS2iFVlEC1KW+lhkUSilKODCc2GrT/AI6qmo74lP5KGiaKu8olIBRd69s6SdLQuE5eWcnNPcZeb+3/dv97+3epy2lbhIN3sJfCIQBIkG3w4eZ4mDcj9USjJeinbDrhzQDLvhJxYOEeeNJQk7ycAh0UJ3nNhlwWGzATQNm3bCWt/RPH7VBtcTT+WWNcbmMGQpRc6C/XGgP8ZaDJ5iTxKaM0lvcW65h4TdUcKLE4W+LJpxEU7Y05d7W41vlpV0kjaWtPv6ahB7F5wsotwUC6TIYWgPybAU6RUfC786nGC59+jBjx0bmlrqfjmqj8dJr42hPKwgPFwiossiXKT4CyBElDsYusRtT3UUlxkmghVAqLwijPCTKLZiYBwrvpWDCCzmYbBY4fmRQnNMLsE4SG5kL/DT/MelSwfA37oCmnaJCCd/c5MxejIQV/IqA2xRvMYw0ebJIMrpErb92Lh0sG6PYnBC+lhTBlXIEIwAkTg+WjG0Yxuq2YVQDt/4ZPCN59sks2B7DN8OHxApkKpLn9SmJI09m39oXZaZOW97zW0p08/OkACuy6RLSQNGu1mCcrfpG/yqsTUolyHcXifgFh3gAjMHooQbpbn3o4STdjtQAi0FP2gmNWYsZbeTblGCiASL28sSbhuZXkLZxbV9YdCv7bHl/fEb+z+xJuqNoi7KtemzKNsMRWuciWUZqrCE5SH1EW2GmsM19YG/G5w4gryDIcgCM4KMNUfnUKU0h11vrpfFkcpGLN5DMITrIcrtKWFFM4zQkD/RTHhJ1EHSLpJlylVFeq2PvV/X+JT0/Q+Wbm9d2/8WiUj6dGGqQqyIdH029yreCDnIYqx0CWMjOZUD321fzjzT+esIy5Ylfzl9MGJCYjzfLVEIOC9IcIwuAM4BAI7HhCBvmGTgVNI40nBIAk6EAE6kBjidROUAbYIWqCLUbcsWIvPzBdir5Y98trX6XJQEoZhMGs5X4pOrwAeR4bP72Brv4WZi3Ot97st5QN/PFgZ8gHr4wcf4T6xDDvTlXha4ZeDxCB91CvuO4N4AALIVt7dU5SBdDsEWi1JHzR46R2dhSReCtMWJGB5bOOYOROvT15jAUwe8lKDYMX4Uu7PPsO3+1fvQBIRl+t1sqjL+fITui3e+fPVG1PhBguenskzQ36Vq/DRUcPorASEiKBCKYCTkdNgOGFUF4empzT+fX/j02I0Xrbmdt3VploBg8D1FiULeVUGB01JNFICWCiEiAtEIEdtS35x865HfsBWfOI+2fNl0VCJP3GRO3dC8/AJloIjRgLI/xpAwSojhAC1iaBc6u6y8Yg5qo10e2g0sxhtckDg+bVfCqJMrxy9bwuwum920QGt1CrijQF+wkeJeoEwnAIxzQaSolEeKSgz0tigiRQyi7Qy7COuaXdZOYFXxZnZ25b9T2yt73pL12IXnfzkiES8+r232tVAn42gg6VcAh+lAnTbI1Wn6faD3u+vj9NwlrkJe72LauTl5fAqKkvJAlAzZZFy+HXVaqgfNevnc43POSinJ90dJ/tWhpNkvJS03KiXw8f5SBh8lFYEo2fMYvTTNtNm47G3XKufJA9OklBT4o6QgYOYw92LzMX1V56zaQ/uPvHRsUemVZg6QsbN+GbtwIzEWUqaQLFBmA7sylimxqdMlz8WkOR7/CCVTk8NvPOmCP3sSUNGioMICguf3oSUJiYL8TtLtYItVhZ/FjOhiGTM9bWPZYGZndNZeqfAZ3ESl8BlXR/gWv8L/JuYGkYhGbnBg3qLsikeozJ3Pdx+9+45EVCJMdI6TcMv2j3o/4N0tjPSCxMBGO538Xh2li1C2mCHJFDvlIt1eDgSUKKO8Kd5yYCEub1B5wvbivcNmp9aP24aN+by4qOILlZUqU4QcpzugezA6PsSQH3Zl1L/benqe+adXrtQ9GADwDRjckSrdw8a3kvDkpoMiLVH//7Q47nqz36E1q9KWLVi+b4P+FBI2WuRbkSukZfoTgJaDGrScXroAL/7hsEiLTouWfbVV99Vd3DFuV8Po3Pd2eztJTT+Lc79SXvyVG4Zo8lLkpMEexe1APTTlZlHer3tV8ZcHN7UlKQkA7QEJ2PviBx/d9+kRfOWFfz2wMn/ObVdqF3ohbKoRMOilDviKrhdEAqK1COj69WK806hGbOOd3+zf8d4sPKBdGISndMRKvCztUitRxvFd7Z+EdBDaZLgZWBsgB0QTMpx2psqr3CsFY1dty7vsYBOhwV7lxxhSuR+wtxqw12ASeIrR4unP+a9s6pyqM/99avfVtZ7MzQF5igsrT3Hs0GtGVOupEVueGTLQUv1wjX5+tm1ZMES1W19AM5Q72CDNUIvI6UuAH4R1ldr2RMZqEenX4FQ9XngtTl8BaCnykuxVc6ByNxeUA/Ut66oYGvIr+D7Vnh+9Fj81G9+p2tDaZFn3ecKr2Avb7dKCXV4BqmJr0LtGacSkpCk047SjYKIHTgyuhNOtrmNL+R0zM6ufJG/7j87wu/oiFCBG5hUEW7/pA5R2j1C/Oacog93OqbRapV9rZ5qUzVAOilMVTk5VsWqOLsUK5hyyzM9L/X76pHH/lWoG/wRlop8d0KLlmeyVBta+QPy9QiX7qKKS3R9HjAeuy340DqNpxk65hbNC7Wp2iHvUDlIdVeXu/LT646d71FmqCxdOinrBmheMV47ICX/9dojATaUqN8M4bgQbN2jZuGH8mdZn7fb750UZTjYXzJgrkSVhIs3CUqyGnes0MBwE9AEWsXkIUQY+BPpOgjudFVorUmD0C8oFXKhe8XHkF2uy671TRh1LPfI37TUqj/xyQHeweI4HeL4F8QSOYIS8kGu0ghTz3ZCO/JK4NaEszUOgrkzPJEe+7Vj7KfbEynuXx1/CRl+uMskT7Od+tQz6sdq0ftEHz5JVJ/ZdoR9AMgA2u7XqUmgWjmxoCOnE796JolYMsME1kQxqc9K2GbMoLzwaVmoKCjwqQ9gpwq0eaql7h80/eNZqWf3kus2nZ9z6nBRHk9vhVMHRFIJRTvKSOOkAKVxbiA0FvIMCeHVq4J04fCMU9XrPb57wRq/z+0NymIm8/dn9+Ev56fN18pfIFEGHob/sK7dvJB9HGnzFkzgtfzls08SDD+zYmla3Ken9fzx6tkYqC3ePQ33zAVbDCNdyfCxitBNIBJvg7ZhLC+tr1BDuZULhvRYPTCjSGcLrLaY9HmAv/OUJb6QFF41eniuBrgihS+5DQVek0PXIthfn9TljNW9adfjFiyM7GfQW/CEJxvCrSuwreR37LX5wcvrcDpbhxa1zl6iAoSBWzhYissUDH68F/CTn1MfSzunHrV3deGzA1Hf/lOaBVgBaYaFTFqQSxQeZaafdZ1GRGCGyPnRwb/yUdScWiRWKTQOOTRSabO2bFqa+BZrs7SfCJjW+EkzozBIgNeqmWVKXRvNA8m6IoQinKpRyEmIsLOGkbD7dVxdXuXcQe3RWqoJr5e+A1Zl0uWSZbyGdTfaHwZYIhqZJE+/kN4vBWdHQH3vipz55CTuxPw9D6n+SOTMRT4HMBC0yF3373qizd89JryeKVnkGTxkgPZIRIroJeH0pnzpE+9gYFWa1u1XEBxHQAONHqirsnww5vuHmp85k73updesLDXl3ay5EgXQ06DSVBZtajAQ2WiZsjJVnxDjorQr3baJOTNI5s2tI2ubknFd36jdFhO82UZj3GMg9QPwKDG6+VHIL4xjQO1eRW4ibVjVwbvLlFgQM+zBzsPORWxWoGYUPbSo4iJtfNr/b8pNHb7zczEGGkqH3mjHDd87EX/vW2DPlr4OHa6KkTCwiQkFuoV/kqm6ExKJ7Cp77+63nx4RyvpvYRiIwP/WUr3bd1hLH4szH8QVruha8vUd6BVDVatXuPch4O/Xn/V+ZhnW1zN9qbJg+/OjucGh3tV+Oaq4LR7oM4FbhiDQk0KevUd4S2rmvmCL69VfyHcv1ux5mHCs4pKVqh5+V40AaOe8qXQ8buOjDR8rXTrA89+XbPSzHRzx/Ha+HcShU+UVhuRjoExGNQL839t9Ttv04zfTcgh13fRY7SmqiiXwBFLU6Kbu80uvvNg4K5pEMZUO93EQUIIJ6Kc7VlxLOEg03L/fHfpaiTFrDX7z5GuNd952Y0i1se/1rLD5tIRbDLwc2x4rPwSkHxUouzMenA3xwygsvwLd3AXoL3FaX8msWG6MyiTIRX/6Tb4zKpNzSNoCW1U0ob6RHT4b4SsZWTjYKStBBSwkOrSEyLt7TLX1uba/Ypj6zZZYN3RBqxTOCv0pu9t0QdMK5XtIB/7cBxPgi8NOOFpajgH2G5evzg1GgCQ7YT4BkkL96kTonqLrT+cTyrMb4FebXFvZ4mDkxv6f6upU1J9gTrJElcf6frzkpEkNjN9BbG1JimMwtq538qpINfWdH75HvnRm/ZyVy0x8n3/9ImvJY4Wyli7EGDJ0rTsa7H/im0vzso8muXZ81brzC0NkAwXlWKD4rQifSCfSuDano1J0DR9SHAXaK4U91BqqCdOx4bPLv3+4wPs7cfqHX4BHSU1IDLk5WAoUHBKp7E/7n2ELv+F13TRo3dWTshXAAtdEvULU37n3zFkVe6M/5x3Mc+rlCdevis+//nP9LWtUc85QBsd9JC6rX8AoVR8o2v6TsvBGSc5GEkFK6hIx2nleVBnkarVO4yGtBAnJLOxfikfvXEz04yxDiVxKiEb/WrPh4dF3W79lbEqaOTFqf1iQRpSNO+u4zah2UaNUsUtrPJYRyN/DZNApCfNu9VBQwE+yN9y3NusMZPx1In78n+vD9URPnBlirgoNYnOQ6gk1jGjGkcoVweFqnKGwcB71bQzo87eI7M7GT/u+J7hsxqu+hxvtNrz3FDO0+wdsapoOTMBc3Kj8CEKwEAOVAL6AA6FMOoOvhBaI5bYNDxiKBPi1jgu0PzYkIB+XaeYo8objsU/IrdSPfCSwuVfPllU2gd3WId4HzUmCBwt8/u8mTBKnw/PxrsRHkhF/vV/hNUuEjAwmfH1B4a5b5k6lD47JWrs493fPA8BelwudrCB/+f6HghN/qV/jtUuGjAglfEFD4nh/URxze3y1zXXX6KnbdQen/tsYUaAiv8s8K4RD+Vb/Cv4H9Dw== + + Contains a cluster of Grasshopper components + 1 + ba4adbea-a1b3-4423-869a-5f5efc4db8fe + Cluster + T0CF + false + + + + + 3 + 20606d38-1b1b-4d29-9066-7314a397b2ea + af7d3a43-bf28-4d3a-a968-187b89544dcd + e328113b-073c-46f1-b400-4227e548946d + 43cd6740-e900-4cb5-aac6-f6ee8442edb2 + d0ccacbd-d43e-44d1-95fa-d65b95597d22 + a734e7f6-2f8f-4929-916a-9608854f6392 + + + + + + 592 + 665 + 144 + 46 + + + 658 + 688 + + + + + + 2 + 4f8984c4-7c7a-4d69-b0a2-183cbb330d20 + 3e8ca6be-fda8-4aaf-b5c0-3c54c8bb7312 + 1 + 4f8984c4-7c7a-4d69-b0a2-183cbb330d20 + + + + + Contains a collection of three-dimensional axis-systems + 20606d38-1b1b-4d29-9066-7314a397b2ea + Plane + tcf_point + true + dae489a9-07c9-46f6-8e62-bf21c52b9a31 + 1 + + + + + + 594 + 667 + 49 + 21 + + + 620 + 677.5 + + + + + + + + Contains a collection of floating point numbers + af7d3a43-bf28-4d3a-a968-187b89544dcd + Number + z_offset + true + 9a4be73c-3082-4c74-be27-41c7c1688c53 + 1 + 1 + + + + + + 594 + 688 + 49 + 21 + + + 620 + 698.5 + + + + + + 1 + + + + + 1 + {0} + + + + + 0.07 + + + + + + + + + + + Contains a collection of three-dimensional axis-systems + e328113b-073c-46f1-b400-4227e548946d + Plane + t0cf_frame + false + 0 + + + + + + 673 + 667 + 61 + 42 + + + 703.5 + 688 + + + + + + + + + + + + + + 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe + Scribble + + + + + true + + 1619.181 + -46.97431 + + + 1818.934 + -46.97431 + + + 1818.934 + -13.64179 + + + 1619.181 + -13.64179 + + A quick note + Arial + 1ca3c3b8-18a0-485b-8393-a4aa121cf8d4 + false + Scribble + Scribble + 45 + SOLVING + + + + + + 1614.181 + -51.97431 + 209.7534 + 43.33252 + + + 1619.181 + -46.97431 + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + # Mac will not automatically do this so, here we go +import os +import sys +HERE = os.path.dirname(ghenv.Component.OnPingDocument().FilePath) +if HERE not in sys.path: sys.path.append(HERE) + +## Start assembly definition +from assembly import Assembly +from assembly import Element + +from compas.datastructures import Mesh +from compas.geometry import Translation +from compas_fab.ghpython.components import coerce_frame + + +assembly = Assembly() + +assembly.attributes['home_config'] = home_config +assembly.attributes['element'] = element +assembly.attributes['place_tolerance'] = place_tolerance +assembly.approach_offset = approach_offset + +source_element = assembly.attributes['element'] +assembly.pick_t0cf_frame = coerce_frame(pick_t0cf_frame) +place_t0cf_frames = [coerce_frame(f) for f in place_t0cf_frames] + +for key, frame in enumerate(robot.from_t0cf_to_tcf(place_t0cf_frames)): + x, y, z = 0, 0, (source_element.height / 2) + + frame = frame.transformed(Translation.from_vector([x, y, z])) + approach_t0cf_frame = frame.transformed(Translation.from_vector([0, 0, approach_offset])) + element = Element(frame=frame, + approach_frame=approach_t0cf_frame, + geometry_at_origin=Mesh.from_shape(source_element)) + assembly.add_element(element, key) + +pickup_t0cf_frames = assembly.pick_t0cf_frames() + GhPython provides a Python script component + + 457 + 356 + + + 1502 + 1535 + + true + true + 1 + fd9b00a1-4fa2-4250-bf75-9a5d0a375bee + false + true + GhPython Script + Create assembly + + + + + + 960 + 581 + 229 + 144 + + + 1070 + 653 + + + + + + 7 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 2 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Create assembly + aec2f3dd-0e8c-4302-9056-6d8e2ef8126a + robot + robot + true + 0 + true + 9a9900f7-c5de-485a-9791-63a5e1930c86 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 962 + 583 + 93 + 20 + + + 1010 + 593 + + + + + + + + true + Script input pick_t0cf_frame. + 67454834-dabb-4df3-8f8f-8c4b144be0d5 + pick_t0cf_frame + pick_t0cf_frame + true + 0 + true + bf2d76a6-3501-45a4-893d-ae3e0eaa5ce4 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 962 + 603 + 93 + 20 + + + 1010 + 613 + + + + + + + + 1 + true + Script input place_t0cf_frames. + cba0b97a-eb2f-40ba-b7eb-902c76d3b817 + place_t0cf_frames + place_t0cf_frames + true + 1 + true + e328113b-073c-46f1-b400-4227e548946d + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 962 + 623 + 93 + 20 + + + 1010 + 633 + + + + + + + + true + Script input approach_offset. + 356fc3f0-79bc-4969-8bd2-8f3724556a7d + approach_offset + approach_offset + true + 0 + true + a17a3498-e042-4db0-b3e7-eca2f875e5b8 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 962 + 643 + 93 + 20 + + + 1010 + 653 + + + + + + + + true + Script input element. + 78aea61d-45ca-4843-b8f8-d3d3f5fbdde8 + element + element + true + 0 + true + 252a3ad3-fe63-448c-9531-ba99a4a17577 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 962 + 663 + 93 + 20 + + + 1010 + 673 + + + + + + + + true + Script input home_config. + 74c6f8c9-9b0d-44e8-a6cc-e4a80ff0e2b0 + home_config + home_config + true + 0 + true + f956b690-65c4-4b82-ac9a-488e4afed703 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 962 + 683 + 93 + 20 + + + 1010 + 693 + + + + + + + + true + Script input place_tolerance. + 7bf4ca6c-8e62-4f70-961e-0d4543c8ef2e + place_tolerance + place_tolerance + true + 0 + true + 0 + 39fbc626-7a01-46ab-a18e-ec1c0c41685b + + + + + + 962 + 703 + 93 + 20 + + + 1010 + 713 + + + + + + 1 + + + + + 1 + {0} + + + + + Grasshopper.Kernel.Types.GH_Number + 0.008 + + + + + + + + + + + Script output assembly. + c3c6a6b8-81c2-457b-9ca0-dc150f8a77e8 + assembly + assembly + false + 0 + + + + + + 1085 + 583 + 102 + 70 + + + 1136 + 618 + + + + + + + + Script output pickup_t0cf_frames. + 31e84a49-19da-4674-8b74-5ae598560f11 + pickup_t0cf_frames + pickup_t0cf_frames + false + 0 + + + + + + 1085 + 653 + 102 + 70 + + + 1136 + 688 + + + + + + + + + + + + + + 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe + Scribble + + + + + true + + 979.2699 + 538.9601 + + + 1177.443 + 538.9601 + + + 1177.443 + 576.9372 + + + 979.2699 + 576.9372 + + A quick note + Arial + 32a47c5f-6e9f-4fd4-af69-5f33be3c44e4 + false + Scribble + Scribble + 41 + Assembly + + + + + + 974.2699 + 533.9601 + 208.1733 + 47.97705 + + + 979.2699 + 538.9601 + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + from compas.datastructures import Mesh +from compas_fab.robots import AttachedCollisionMesh +from compas_fab.robots import CollisionMesh +from compas_fab.robots import PlanningScene + + +if robot: + scene = PlanningScene(robot) + scene.remove_collision_mesh('built_elements') + + a = [] + for key in assembly.nodes(): + # Linear order assumed here! + if key >= i: break + element = assembly.element(key) + cm = CollisionMesh(element.geometry_at_placement, 'built_elements') + scene.append_collision_mesh(cm) + + # Setup attached element + scene.remove_attached_collision_mesh('brick') + scene.remove_collision_mesh('brick') + + ee_link_name = robot.get_end_effector_link_name() + if robot.attached_tool: + brick_frame = robot.attached_tool.frame.copy() + else: + brick_frame = Frame.worldXY() + + # Centered origin, get half + element = assembly.attributes['element'] + brick_frame.point.x += element.height / 2 + element = Mesh.from_shape(element) + brick_acm = AttachedCollisionMesh(CollisionMesh(element, 'brick', brick_frame), ee_link_name) + scene.add_attached_collision_mesh(brick_acm) + + GhPython provides a Python script component + + 668 + 417 + + + 558 + 571 + + true + true + 1 + 19ea3000-8a91-4bca-8866-792502605bcd + false + true + GhPython Script + ROS scene + + + + + + 1694 + 1073 + 83 + 81 + + + 1763 + 1114 + + + + + + 3 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 0 + + + + + true + Script variable Python + 80aa6b6d-833e-45c7-bd00-414d70dbd152 + robot + robot + true + 0 + true + 9a9900f7-c5de-485a-9791-63a5e1930c86 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1696 + 1075 + 52 + 25 + + + 1723.5 + 1087.833 + + + + + + + + true + Script input assembly. + 8b372ba5-db8d-4c20-bea0-df45cf256aa4 + assembly + assembly + true + 0 + true + c3c6a6b8-81c2-457b-9ca0-dc150f8a77e8 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1696 + 1100 + 52 + 26 + + + 1723.5 + 1113.5 + + + + + + + + true + Script input i. + 7213734f-9532-4ad7-a5e8-0ab215743b3a + i + i + true + 0 + true + d3765dd9-51b9-48b1-8583-25508339aed8 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1696 + 1126 + 52 + 26 + + + 1723.5 + 1139.167 + + + + + + + + + + + + + + 57da07bd-ecab-415d-9d86-af36d7073abc + Number Slider + + + + + Numeric slider for single values + d3765dd9-51b9-48b1-8583-25508339aed8 + Number Slider + + false + 0 + + + + + + 1505 + 1130 + 160 + 20 + + + 1505.196 + 1130.106 + + + + + + 3 + 1 + 1 + 100 + 0 + 0 + 0 + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + if assembly: + assembly.pick_trajectory = pick_trajectory + GhPython provides a Python script component + + 130 + 130 + + + 566 + 683 + + true + true + 1 + d3497764-72f4-4b96-9c43-e4b12c91e0f7 + false + true + GhPython Script + Store + + + + + + 2271 + 804 + 108 + 48 + + + 2365 + 828 + + + + + + 2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 0 + + + + + true + Script variable Python + e4a7cd6c-eb62-465f-98e1-81d030fe775e + assembly + assembly + true + 0 + true + c3c6a6b8-81c2-457b-9ca0-dc150f8a77e8 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + 1 + + + + + + 2273 + 806 + 77 + 22 + + + 2313 + 817 + + + + + + + + true + Script input pick_trajectory. + d2b17130-8fac-4015-a044-301cd7ef312d + pick_trajectory + pick_trajectory + true + 0 + true + 0ac0402c-aca7-4167-9000-cfddc8a90179 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2273 + 828 + 77 + 22 + + + 2313 + 839 + + + + + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import Grasshopper +import System +from ghpythonlib.componentbase import executingcomponent as component +from scriptcontext import sticky as st + +from compas_fab.ghpython.components import create_id +from compas_fab.ghpython.components import coerce_frame +from compas_fab.ghpython.components.icons import plan_cartesian_motion_icon + +class PlanCartesianMotion(component): + def SetUpParam(self, p, name, nickname, description): + p.Name = name + p.NickName = nickname + p.Description = description + p.Optional = True + + def RegisterInputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "robot", "robot", "The robot.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "planes", "planes", "The planes or frames through which the path is defined.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.list + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "start_configuration", "start_configuration", + "The robot's full configuration, i.e. values for all configurable joints of the entire robot, at the starting position. Defaults to the all-zero configuration.") # noqa E501 + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_String() + self.SetUpParam(p, "group", "group", "The planning group used for calculation. Defaults to the robot's main planning group.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + +# p = Grasshopper.Kernel.Parameters.Param_GenericObject() +# self.SetUpParam(p, "attached_collision_meshes", "attached_collision_meshes", "A list of attached collision meshes to be included for planning.") +# p.Access = Grasshopper.Kernel.GH_ParamAccess.list +# self.Params.Input.Add(p) + +# p = Grasshopper.Kernel.Parameters.Param_GenericObject() +# self.SetUpParam(p, "path_constraints", "path_constraints", +# "Optional constraints that can be imposed along the solution path. Note that path calculation won't work if the start_configuration violates these constraints. Defaults to None.") # noqa E501 +# p.Access = Grasshopper.Kernel.GH_ParamAccess.list +# self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Number() + self.SetUpParam(p, "max_step", "max_step", "The approximate distance between the calculated points. (Defined in the robot's units.) Defaults to 0.01.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Boolean() + self.SetUpParam(p, "compute", "compute", "If `True`, calculates a trajectory.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + def RegisterOutputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "trajectory", "trajectory", "The calculated trajectory.") + self.Params.Output.Add(p) + + def SolveInstance(self, DA): + p0 = self.marshal.GetInput(DA, 0) + p1 = self.marshal.GetInput(DA, 1) + p2 = self.marshal.GetInput(DA, 2) + p3 = self.marshal.GetInput(DA, 3) + p4 = self.marshal.GetInput(DA, 4) + p5 = self.marshal.GetInput(DA, 5) +# p6 = self.marshal.GetInput(DA, 6) +# p7 = self.marshal.GetInput(DA, 7) +# result = self.RunScript(p0, p1, p2, p3, p4, p5, p6, p7) + result = self.RunScript(p0, p1, p2, p3, p4, p5) + + if result is not None: + self.marshal.SetOutput(result, DA, 0, True) + + def get_Internal_Icon_24x24(self): + return plan_cartesian_motion_icon + + def RunScript(self, robot, planes, start_configuration, group, max_step, compute): + + key = create_id(self, 'trajectory') + + max_step = float(max_step) if max_step else 0.01 + # NOTE: Read from scene + attached_collision_meshes = None + path_constraints = None + attached_collision_meshes = list(attached_collision_meshes) if attached_collision_meshes else None + + if robot and robot.client and robot.client.is_connected and start_configuration and planes and compute: + frames = [coerce_frame(plane) for plane in planes] + st[key] = robot.plan_cartesian_motion(frames, + start_configuration=start_configuration, + group=group, + options=dict( + max_step=max_step, + path_constraints=path_constraints, + attached_collision_meshes=attached_collision_meshes + )) + + trajectory = st.get(key, None) + + if trajectory and trajectory.fraction < 1.0: + raise Exception('Incomplete trajectory. Fraction={}'.format(trajectory.fraction)) + + return trajectory + GhPython provides a Python script component + + 380 + 257 + + + 1840 + 1391 + + true + true + 1 + de944dfd-0524-4e32-847a-6d55172e6a96 + true + true + GhPython Script + Plan Cartesian Motion + + + + + + 1745 + 595 + 188 + 157 + + + 1860 + 674 + + + + + + 6 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 1 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + 34656f8c-a260-4302-a0c3-3549e25875bf + robot + robot + true + 0 + true + 9a9900f7-c5de-485a-9791-63a5e1930c86 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1747 + 597 + 98 + 25 + + + 1797.5 + 609.75 + + + + + + + + 1 + true + Script input planes. + e1b06454-8651-4966-b63c-b2f48e117af8 + planes + planes + true + 1 + true + 31e84a49-19da-4674-8b74-5ae598560f11 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1747 + 622 + 98 + 26 + + + 1797.5 + 635.25 + + + + + + + + true + Script input start_configuration. + 2e99ead4-6674-4c78-a25e-02db600f1f62 + start_configuration + start_configuration + true + 0 + true + f956b690-65c4-4b82-ac9a-488e4afed703 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1747 + 648 + 98 + 25 + + + 1797.5 + 660.75 + + + + + + + + true + Script input group. + 8c43e63e-6356-42ed-a19f-cf754c556a98 + group + group + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1747 + 673 + 98 + 26 + + + 1797.5 + 686.25 + + + + + + + + true + Script input max_step. + 0d3db76f-ef50-4a94-b1cb-6182a9acf64e + max_step + max_step + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1747 + 699 + 98 + 25 + + + 1797.5 + 711.75 + + + + + + + + true + Script input compute. + 6a261cfe-c2da-4610-9b10-c0e39ee31eb7 + compute + compute + true + 0 + true + 8c731948-abe8-4e50-a551-e6a07b85d9c4 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 1747 + 724 + 98 + 26 + + + 1797.5 + 737.25 + + + + + + + + Script output trajectory. + dcfe8806-86f9-4cfc-af81-819cd25482f0 + trajectory + trajectory + false + 0 + + + + + + 1875 + 597 + 56 + 153 + + + 1903 + 673.5 + + + + + + + + + + + + + + fbac3e32-f100-4292-8692-77240a42fd1a + Point + + + + + Contains a collection of three-dimensional points + b2ac75b7-c534-4271-9a27-3649779c17d0 + Point + Pt + false + 0 + + + + + + 11 + 551 + 50 + 20 + + + 36.95251 + 561.5808 + + + + + + 1 + + + + + 1 + {0} + + + + + + + -0.0643269056259189 + 0.668512451261046 + 0.0817357251225426 + + + + + + + + + + + + + + 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe + Scribble + + + + + true + + 2560.79 + 267.9866 + + + 2832.074 + 267.9866 + + + 2832.074 + 298.3563 + + + 2560.79 + 298.3563 + + A quick note + Arial + 04b7fd46-609e-48b8-a151-5e9495abbd13 + false + Scribble + Scribble + 41 + Save to JSON + + + + + + 2555.79 + 262.9866 + 281.2849 + 40.36966 + + + 2560.79 + 267.9866 + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import os +import json +import compas + +HERE = os.path.dirname(ghenv.Component.OnPingDocument().FilePath) +rfile = os.path.join(HERE, 'assembly.json') + +# We are storing a full assembly representation +if save and assembly: + compas.json_dump(assembly, rfile) + + # Pretty formatting because OCD + with open(rfile, 'r+') as rf: + data = json.load(rf) + rf.seek(0) + json.dump(data, rf, indent=4, sort_keys=True) + + GhPython provides a Python script component + + 1405 + 231 + + + 1579 + 1305 + + true + false + 1 + af35f706-7af8-4260-b588-358a993d1352 + false + true + GhPython Script + Save + + + + + + 2710 + 320 + 112 + 44 + + + 2779 + 342 + + + + + + 2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 2 + 3ede854e-c753-40eb-84cb-b48008f14fd4 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Save + 693870d1-7975-4bc2-9352-ffba36c01fcb + assembly + assembly + true + 0 + true + c3c6a6b8-81c2-457b-9ca0-dc150f8a77e8 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + 2 + + + + + + 2712 + 322 + 52 + 20 + + + 2739.5 + 332 + + + + + + + + true + Script input save. + d857b401-066f-4625-9f96-df6662a5dc8c + save + save + true + 0 + true + fbd9e873-ec49-47bc-8d5c-258d8036b745 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2712 + 342 + 52 + 20 + + + 2739.5 + 352 + + + + + + + + The execution information, as output and error streams + 5f98ae84-f5dc-414b-ad0a-341943c35da4 + out + out + false + 0 + + + + + + 2794 + 322 + 26 + 20 + + + 2807 + 332 + + + + + + + + Script output a. + d37b140a-2c87-4117-9ffa-a5e5a78894eb + a + a + false + 0 + + + + + + 2794 + 342 + 26 + 20 + + + 2807 + 352 + + + + + + + + + + + + + + a8b97322-2d53-47cd-905e-b932c3ccd74e + Button + + + + + Button object with two values + False + True + fbd9e873-ec49-47bc-8d5c-258d8036b745 + Button + Button + false + 0 + + + + + + 2429 + 341 + 103 + 22 + + + + + + + + + + 410755b1-224a-4c1e-a407-bf32fb45ea7e + 00000000-0000-0000-0000-000000000000 + GhPython Script + + + + + import Grasshopper +import System +from ghpythonlib.componentbase import executingcomponent as component +from scriptcontext import sticky as st + +from compas.geometry import Frame +from compas.geometry import Transformation +from compas_ghpython.artists import MeshArtist +from compas_ghpython import draw_frame +from compas_fab.ghpython.components import create_id +from compas_fab.ghpython.components.icons import robot_visualize_icon + +class RobotVisualize(component): + def SetUpParam(self, p, name, nickname, description): + p.Name = name + p.NickName = nickname + p.Description = description + p.Optional = True + + def RegisterInputParams(self, pManager): + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "robot", "robot", "The robot.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_String() + self.SetUpParam(p, "group", "group", "The planning group used for end-effector and base visualization.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "configuration", "configuration", "The robot's full configuration.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_Boolean() + self.SetUpParam(p, "reload_scene", "reload_scene", "Whether or not to reload the scene information from the client.") + p.Access = Grasshopper.Kernel.GH_ParamAccess.item + self.Params.Input.Add(p) + + def RegisterOutputParams(self, pManager): + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "visual", "visual", "The robot's visual meshes.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "frames", "frames", "The robot's joint frames.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "attached_collision_meshes", "attached_collision_meshes", "Attached collision meshes in the scene.") + self.Params.Output.Add(p) + + p = Grasshopper.Kernel.Parameters.Param_GenericObject() + self.SetUpParam(p, "collision_meshes", "collision_meshes", "Collision meshes in the scene.") + self.Params.Output.Add(p) + + def SolveInstance(self, DA): + p0 = self.marshal.GetInput(DA, 0) + p1 = self.marshal.GetInput(DA, 1) + p2 = self.marshal.GetInput(DA, 2) + p3 = self.marshal.GetInput(DA, 3) + result = self.RunScript(p0, p1, p2, p3) + + if result is not None: + if not hasattr(result, '__getitem__'): + self.marshal.SetOutput(result, DA, 0, True) + else: + self.marshal.SetOutput(result[0], DA, 0, True) + self.marshal.SetOutput(result[1], DA, 1, True) + self.marshal.SetOutput(result[2], DA, 2, True) + self.marshal.SetOutput(result[3], DA, 3, True) + + def get_Internal_Icon_24x24(self): + return robot_visualize_icon + + def RunScript(self, robot, group, configuration, reload_scene): + + visual = None + frames = None + collision_meshes = [] + attached_collision_meshes = [] + + if robot: + configuration = configuration or robot.zero_configuration() + + robot.update(configuration, visual=True, collision=False) + frames = [draw_frame(f) for f in robot.transformed_frames(configuration, group)] + + visual = robot.artist.draw_visual() + + scene_key = create_id(self, 'scene') + + if robot and (reload_scene or scene_key not in st): + st[scene_key] = robot.client.get_planning_scene() + + scene = st.get(scene_key, None) + + if scene: + scene_key_co = scene_key + '_co' + if reload_scene or scene_key_co not in st: + for co in scene.world.collision_objects: + # HACK: Little workaround to a bug in to_collision_meshes() + header = co.header + frame_id = header.frame_id + co.header = dict(frame_id=frame_id) + cms = co.to_collision_meshes() + # HACK: Restore proper header + co.header = header + + for cm in cms: + if frame_id != '/world': + raise NotImplementedException('Only world-referenced collision meshes are supported') + + if cm.frame != Frame.worldXY(): + t = Transformation.from_frame(cm.frame) + mesh = cm.mesh.transformed(t) + else: + mesh = cm.mesh + + rhino_mesh = MeshArtist(mesh).draw() + collision_meshes.append(rhino_mesh) + st[scene_key_co] = collision_meshes + + collision_meshes = st.get(scene_key_co, None) + + for aco in scene.robot_state.attached_collision_objects: + for acm in aco.to_attached_collision_meshes(): + frame_id = aco.object['header']['frame_id'] + frame = robot.forward_kinematics(configuration, options=dict(link=frame_id)) + t = Transformation.from_frame(frame) + + # Local CM frame + if acm.collision_mesh.frame and acm.collision_mesh.frame != Frame.worldXY(): + t = t * Transformation.from_frame(acm.collision_mesh.frame) + + mesh = acm.collision_mesh.mesh.transformed(t) + + rhino_mesh = MeshArtist(mesh).draw() + attached_collision_meshes.append(rhino_mesh) + + return (visual, frames, attached_collision_meshes, collision_meshes) + + GhPython provides a Python script component + + 776 + 53 + + + 821 + 880 + + true + true + true + 1 + ebe99570-fddf-41df-aa4c-5d4202f08c7f + true + true + GhPython Script + Visualize + + + + + + 2589 + 130 + 243 + 84 + + + 2678 + 172 + + + + + + 4 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 84fa917c-1ed8-4db3-8be1-7bdc4a6495a2 + 4 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + 8ec86459-bf01-4409-baee-174d0d2b13d0 + + + + + true + Script variable Python + 217464a9-767d-43bb-ba34-7c1c7164471d + robot + robot + true + 0 + true + 9a9900f7-c5de-485a-9791-63a5e1930c86 + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2591 + 132 + 72 + 20 + + + 2628.5 + 142 + + + + + + + + true + Script input group. + f0885311-9e17-4d64-a69c-94739238cb77 + group + group + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2591 + 152 + 72 + 20 + + + 2628.5 + 162 + + + + + + + + true + Script input configuration. + ad098fef-2525-4213-933b-639056d48baa + configuration + configuration + true + 0 + true + 0 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2591 + 172 + 72 + 20 + + + 2628.5 + 182 + + + + + + + + true + Script input reload_scene. + 2600c4d3-355c-4dd4-a088-1ab7e6316901 + reload_scene + reload_scene + true + 0 + true + 475fa148-99eb-4bdd-ad50-28959a3b193d + 1 + 87f87f55-5b71-41f4-8aea-21d494016f81 + + + + + + 2591 + 192 + 72 + 20 + + + 2628.5 + 202 + + + + + + + + Script output visual. + 8556425a-ca3b-4287-b2a3-e311c52b6cd8 + visual + visual + false + 0 + + + + + + 2693 + 132 + 137 + 20 + + + 2761.5 + 142 + + + + + + + + Script output frames. + 500e7fb0-17fb-4e84-842c-b00468c3ac32 + frames + frames + false + 0 + + + + + + 2693 + 152 + 137 + 20 + + + 2761.5 + 162 + + + + + + + + Script output attached_collision_meshes. + 5522dc69-489f-4f95-8657-1166564b80dd + attached_collision_meshes + attached_collision_meshes + false + 0 + + + + + + 2693 + 172 + 137 + 20 + + + 2761.5 + 182 + + + + + + + + Script output collision_meshes. + 79dc54ae-3aad-4bbe-af91-2fe6663ef6eb + collision_meshes + collision_meshes + false + 0 + + + + + + 2693 + 192 + 137 + 20 + + + 2761.5 + 202 + + + + + + + + + + + + + + 2e78987b-9dfb-42a2-8b76-3923ac8bd91a + Boolean Toggle + + + + + Boolean (true/false) toggle + 475fa148-99eb-4bdd-ad50-28959a3b193d + Boolean Toggle + reload + false + 0 + true + + + + + + 2425 + 196 + 101 + 22 + + + + + + + + + + 7f5c6c55-f846-4a08-9c9a-cfdc285cc6fe + Scribble + + + + + true + + 2583.522 + 80.05577 + + + 2837.39 + 80.05577 + + + 2837.39 + 109.8849 + + + 2583.522 + 109.8849 + + A quick note + Arial + b626a1f5-5bd9-42c3-96da-26f221b1048e + false + Scribble + Scribble + 41 + Visualization + + + + + + 2578.522 + 75.05577 + 263.8677 + 39.8291 + + + 2583.522 + 80.05577 + + + + + + + + + + + + + + + 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 + + + + + \ No newline at end of file diff --git a/lecture_06/assignment_05/Guillaume Jami/model.py b/lecture_06/assignment_05/Guillaume Jami/model.py new file mode 100644 index 0000000..cbbe844 --- /dev/null +++ b/lecture_06/assignment_05/Guillaume Jami/model.py @@ -0,0 +1,441 @@ +import Rhino.Geometry as rg +import rhinoscriptsyntax as rs +import math as m +import simple_comm as c +import simple_ur_script as ur + + +class Fabrication(): + ROBOT_IP = "192.168.10.10" + + def __init__(self, fabricate=False, brick_planes=None, robot_ip=ROBOT_IP): + self.brick_planes = brick_planes + self.robot_ip = robot_ip + self.accel = 0.1 + self.vel = 0.1 + self.script = "" + self.way_planes = [] + + def tcp(self): + self.script += ur.set_tcp_by_angles( + 0.0, 0.0, 74.0, m.radians(0.0), m.radians(180.0), m.radians(0)) + + def set_robot_base_plane(self): + + rs.MessageBox( + "move robot to base plane origin, press OK when there", 0) + data = c.listen_to_robot(ROBOT_IP) + pose = data['pose'] + pt_1 = rg.Point3d(pose[0]*1000, pose[1]*1000, pose[2]*1000) + print(pt_1) + rs.MessageBox( + "move robot to base plane positive x direction, press OK when there", 0) + data = c.listen_to_robot(ROBOT_IP) + pose = data['pose'] + pt_2 = rg.Point3d(pose[0]*1000, pose[1]*1000, pose[2]*1000) + print(pt_2) + rs.MessageBox( + "move robot to base plane positive y direction, press OK when there", 0) + data = c.listen_to_robot(robot_ip) + pose = data['pose'] + pt_3 = rg.Point3d(pose[0]*1000, pose[1]*1000, pose[2]*1000) + print(pt_3) + + robot_base = rg.Plane(pt_1, pt_2-pt_1, pt_3-pt_1) + text_file = open("robot_base.txt", "w") + text_file.write(str(robot_base.Origin)+"," + + str(robot_base.XAxis)+","+str(robot_base.YAxis)) + text_file.close() + + # def load_robot_base_plane(self): + text_file = open("robot_base.txt", "r") + string = text_file.read() + values = string.split(",") + values = [float(value) for value in values] + + base_origin = rg.Point3d(values[0], values[1], values[2]) + base_x_axis = rg.Vector3d(values[3], values[4], values[5]) + base_y_axis = rg.Vector3d(values[6], values[7], values[8]) + + base_plane = rg.Plane(base_origin, base_x_axis, base_y_axis) + return base_plane + + def set_robot_base_plane_from_pts(self): + + pt_0 = rg.Point3d(327, 230, 0) # base plane origin + # point on base plane positive x direction + pt_1 = rg.Point3d(499, 230, 0) + pt_2 = rg.Point3d(499, 499, 0) # point on base plane positive xy + + robot_base = rg.Plane(pt_0, pt_1-pt_0, pt_2-pt_0) + return robot_base + + def rhino_to_robot_space(self, in_plane): + plane = in_plane.Clone() + _r_matrix = rg.Transform.PlaneToPlane( + rg.Plane.WorldXY, self.set_robot_base_plane_from_pts()) + plane.Transform(_r_matrix) + return plane + + def robot_transformation(self): # LOAD robot model + _robot_matrix = rg.Transform.PlaneToPlane( + self.set_robot_base_plane_from_pts(), rg.Plane.WorldXY) + return _robot_matrix + + def pickup_brick(self, pick_up_plane): + """Example of a method's documentation. + + Parameters + ---------- + point_x : float + Description of the first param + point_y : float + Description of the second param + """ + + safe_distance = 50 + + safe_plane = pick_up_plane.Clone() + safe_plane.Translate(rg.Vector3d(0, 0, safe_distance)) + + self.script += ur.move_l(safe_plane, self.accel, self.vel) + self.way_planes.append(safe_plane) + + self.script += ur.move_l(pick_up_plane, self.accel, self.vel) + self.way_planes.append(pick_up_plane) + + self.script += ur.set_digital_out(4, True) + self.script += ur.sleep(1) + + self.script += ur.move_l(safe_plane, self.accel, self.vel) + self.way_planes.append(safe_plane) + + return None + + def place_brick(self, plane): + """ + this function gereates the robotic sequience for placing a brick + Requires a plane wich describes the possition of the brick""" + + safe_distance = 50 + + safe_plane = plane.Clone() + safe_plane.Translate(rg.Vector3d(0, 0, safe_distance)) + + self.script += ur.move_l(safe_plane, self.accel, self.vel) + self.way_planes.append(safe_plane) + + self.script += ur.move_l(plane, self.accel, self.vel) + self.way_planes.append(plane) + + self.script += ur.set_digital_out(4, False) + self.script += ur.sleep(1) + + self.script += ur.move_l(safe_plane, self.accel, self.vel) + self.way_planes.append(safe_plane) + + return None + + def procedure(self, transform=True): + + self.tcp() + robot_planes = [] + + if transform: + pick_up_plane = rg.Plane(rg.Point3d( + 0, 450, 0), rg.Vector3d.XAxis, rg.Vector3d.YAxis) + for plane in (self.brick_planes): + robot_planes.append(self.rhino_to_robot_space(plane)) + + else: + pick_up_plane = self.rhino_to_robot_space( + rg.Plane(rg.Point3d(0, 450, 0), rg.Vector3d.XAxis, rg.Vector3d.YAxis)) + robot_planes = self.brick_planes + + for plane in (robot_planes): + self.pickup_brick(pick_up_plane) + self.place_brick(plane) + + def visualize(self): + """this funktion visualizes the planes wich are sent to the robot""" + + crv = [] + + for plane in self.way_planes: + #print (plane.Origin) + pt = plane.Origin + crv.append(pt) + + curve = rg.NurbsCurve.Create(False, 1, crv) + print(curve) + return self.way_planes, curve + + def send(self): + """ + this funktion sends the script to the robot.""" + + self.script = c.concatenate_script(self.script) + #c.send_script(self.script, self.robot_ip) + return self.script + + +class Brick(object): + REFERENCE_LENGTH = 31 + REFERENCE_WIDTH = 25 + REFERENCE_HEIGHT = 8 + + def __init__(self, plane, length=REFERENCE_LENGTH, width=REFERENCE_WIDTH, height=REFERENCE_HEIGHT): + """Brick containes picking plane, placing plane and geometry + + Parameters + ---------- + plane : Rhino Geometry plane + this plane describes the possition and orientation of the Brick + + """ + self.plane = plane + self.length = length + self.width = width + self.height = height + + + def dimensions(self): + """returns the dimenesnions of the brick: + + Returns + ---------- + [length : float, width : float, height: float] + + """ + + return self.length, self.width, self.height + + def pts(self): + """returns 8 points describing the brick: + + Returns + ---------- + [pt0 : bottom point at origin, + pt1 : bottom point at possitive Y, + pt2 : bottom point at possitive X, + pt3 : bottom point at possitive X and poossitive Y, + pt4 : top point at origin, + pt1 : top point at possitive Y, + pt2 : top point at possitive X, + pt3 : top point at possitive X and poossitive Y] + + """ + + pt_0 = rg.Point3d(0, 0, 0) + pt_1 = rg.Point3d(self.length, 0, 0) + pt_2 = rg.Point3d(self.length, self.width, 0) + pt_3 = rg.Point3d(0, self.width, 0) + + pt_4 = rg.Point3d(0, 0, self.height) + pt_5 = rg.Point3d(self.length, 0, self.height) + pt_6 = rg.Point3d(self.length, self.width, self.height) + pt_7 = rg.Point3d(0, self.width, self.height) + + b_pts = [pt_0, pt_1, pt_2, pt_3, pt_4, pt_5, pt_6, pt_7] + + return b_pts + + def origin(self): + """returns the origin plane in the centre of the base of the brick: + + Returns + ---------- + [Rhino Geometry Plane] + + """ + vec = (self.pts()[2]-self.pts()[0])/2 + origin = self.pts()[0] + vec + plane = rg.Plane(origin, rg.Vector3d.XAxis, rg.Vector3d.YAxis) + return plane + + def transformation(self): + """Transoformation matrix fot transformating the brick to the brick possiotn: + + Returns + ---------- + [Rhino Geometry Transformation] + + """ + return rg.Transform.PlaneToPlane(self.origin(), self.plane) + + def base_plane(self): + """Base plane of the transformed brick: + + Returns + ---------- + [Rhino Geometry Plane] + + """ + + p_plane = self.origin() + p_plane.Transform(self.transformation()) + return p_plane + + def picking_plane(self): + """Robot's picking plane on the transformed brick: + + Returns + ---------- + [Rhino Geometry Plane] + + """ + p_plane = self.origin() + p_pt = p_plane.Origin + p_plane = rg.Plane(p_pt+rg.Vector3d(0, 0, self.height), + p_plane.XAxis, p_plane.YAxis) + + p_plane.Transform(self.transformation()) + return p_plane + + def surface(self): + """NURB surfaces depicting the brick: + + Returns + ---------- + [srf0 : base surface, + srf1 : long edge, + srf2 : top surface + srf3 : long edge, + srf4 : short edge + srf5 : short edge] + + """ + tran_brick_pts = [] + for pt in self.pts(): + transformation_pt = pt.Clone() + transformation_pt.Transform(self.transformation()) + tran_brick_pts.append(transformation_pt) + + pt_0, pt_1, pt_2, pt_3, pt_4, pt_5, pt_6, pt_7 = tran_brick_pts + + srf_0 = rg.NurbsSurface.CreateFromPoints( + [pt_0, pt_1, pt_3, pt_2], 2, 2, 1, 1) + srf_1 = rg.NurbsSurface.CreateFromPoints( + [pt_0, pt_1, pt_4, pt_5], 2, 2, 1, 1) + srf_2 = rg.NurbsSurface.CreateFromPoints( + [pt_4, pt_5, pt_7, pt_6], 2, 2, 1, 1) + srf_3 = rg.NurbsSurface.CreateFromPoints( + [pt_6, pt_7, pt_2, pt_3], 2, 2, 1, 1) + srf_4 = rg.NurbsSurface.CreateFromPoints( + [pt_0, pt_3, pt_4, pt_7], 2, 2, 1, 1) + srf_5 = rg.NurbsSurface.CreateFromPoints( + [pt_1, pt_2, pt_5, pt_6], 2, 2, 1, 1) + + return (srf_0, srf_1, srf_2, srf_3, srf_4, srf_5) + + def mesh(self): + """Mesh depicting the brick: + + Returns + ---------- + mesh_brick : Mesh + """ + + mesh_brick = rg.Mesh.CreateFromBox(self.pts(), 1, 1, 1) + mesh_brick.Transform(self.transformation()) + + return mesh_brick + + +class Wall(): + def __init__(self, x_cnt, z_cnt): + """Wall generates and contains the bricks + + Parameters + ---------- + x_cnt : int + this number corresponds to the length of the wall (amount of bricks) + + x_cnt : int + this number corresponds to the hight of the wall + (amount of brick layers) + + """ + + self.x_cnt = x_cnt + self.z_cnt = z_cnt + + + self.b_length = Brick.REFERENCE_LENGTH + self.b_width = Brick.REFERENCE_WIDTH + self.b_height = Brick.REFERENCE_HEIGHT + + def brick_possitions(self): + """Genrates a Rhino Geometry plane for each brick + + This is where the design of the wall is created. + + Returns + ---------- + brick_planes : [Rhino Geometry Plane, Rhino Geometry Plane, ...] + + """ + + brick_planes = [] + for i in range(self.z_cnt): # layer count + for j in range(self.x_cnt): # layer length + + if i % 2 == 0: + x_pos = j * (self.b_length+1) + rotation = m.radians(10) + else: + x_pos = j * (self.b_length+1) + (self.b_length/2)-1 + rotation = m.radians(-10) + + z_pos = i * (self.b_height) + + origin = rg.Point3d(x_pos, 0, z_pos) + plane = rg.Plane(origin, rg.Vector3d.XAxis, rg.Vector3d.YAxis) + plane.Rotate(rotation, rg.Vector3d.ZAxis) + brick_planes.append(plane) + + return brick_planes + + def geometric_model(self): + """Generates a 3D model of the wall + + Returns + ---------- + geo : [Rhino Geometry Surface, Rhino Geometry Surface, ...] + + """ + + planes = [] + geo = [] + + for plane in self.brick_possitions(): + myBrick = Brick(plane) + planes.append(myBrick.base_plane()) + # geo.extend(myBrick.surface()) + + geo.append(myBrick.mesh()) + + visualizeFabrication = Fabrication( + brick_planes=self.brick_possitions()) + visualizeFabrication.procedure(transform=False) + robot_matrix = visualizeFabrication.robot_transformation() + + print(robot_matrix) + + return geo, visualizeFabrication.visualize(), robot_matrix + + def fabrication_model(self): + """Generates all the data necessary for the robotic fabrication + process and sends the comands to the robot + + Returns script + ---------- + script : "UR script" + """ + + myFabrication = Fabrication(brick_planes=self.brick_possitions()) + + myFabrication.procedure() + script = myFabrication.send() + + return script, myFabrication.visualize() diff --git a/lecture_06/assignment_05/Guillaume Jami/simple_comm.py b/lecture_06/assignment_05/Guillaume Jami/simple_comm.py new file mode 100644 index 0000000..0b66f4d --- /dev/null +++ b/lecture_06/assignment_05/Guillaume Jami/simple_comm.py @@ -0,0 +1,136 @@ +import socket +from struct import * + +def concatenate_script(list_ur_commands): + """ + Internal function that concatenates generated UR script into one large script file. Usually used to combine + scripts generated by the GrasshopperPython components + + Args: + list_ur_commands: A list of formatted UR Script strings + + Returns: + ur_script: The concatenated script + """ + + ur_script = "\ndef my_script():\n" + #ur_script += '\tpopup("running my_script")\n' + + combined_script = "" + for ur_cmd in list_ur_commands: + combined_script += ur_cmd + + #format combined script + lines = combined_script.split("\n") + for l in lines: + ur_script += "\t" + l + "\n" + + ur_script += 'end\n' + ur_script += '\nmy_script()\n' + return ur_script + +def send_script(script_to_send,robot_ip): + """ + Opens a socket to the Robot and sends a script + + Args: + script_to_send: Script to send to socket + robot_id: Integer. ID of robot + + """ + + '''Function that opens a socket connection to the robot''' + PORT = 30002 + HOST = robot_ip + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(2) + try: + s.connect((HOST, PORT)) + except: + print ("Cannot connect to ",HOST,PORT) + + s.settimeout(None) + max_size= 2<<18 + n=len(script_to_send) + if n>max_size: + raise Exception("Program too long") + + try: + s.send(script_to_send) + except: + print("failed to send") + s.close() + +def listen_to_robot(robot_ip): + PORT = 30003 + HOST = robot_ip + # Create dictionary to store data + chunks={} + chunks["target_joints"] = [] + chunks["actual_joints"]= [] + chunks["forces"] = [] + chunks["pose"] = [] + chunks["time"] = [0] + + data = read(HOST, PORT) + get_messages(data, chunks) + return chunks + +def read(HOST, PORT): + """ + Method that opens a TCP socket to the robot, receives data from the robot server and then closes socket + + Returns: + data: Data broadcast by the robot. In bytes + """ + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(1) + try: + s.connect((HOST, PORT)) + print "connected" + except: + traceback.print_exc() + print "Cannot connect to ",HOST,PORT + s.settimeout(None) + data = s.recv(1024) + s.close() + return data + +def get_messages(bytes, chunks_info): + """ + Function parses data stream and selects the following information: + 1) q_target + 2) q_actual + 3) TCP force + 4) Tool Vector + 5) Time + + This data is formatted and the chunks dictionary is updated + for more info see: http://wiki03.lynero.net/Technical/RealTimeClientInterface + """ + + + # get messages + q_target = bytes[12:60] + q_actual = bytes[252:300] + tcp_force = bytes[540:588] + tool_vector = bytes[588:636] + controller_time = bytes[740:748] + + # format type: int, + fmt_double6 = "!dddddd" + fmt_double1 = "!d" + + #Unpack selected data + target_joints = unpack(fmt_double6,q_target) + chunks_info["target_joints"]= (math.degrees(j) for j in target_joints) + actual_joints = unpack(fmt_double6,q_actual) + chunks_info["actual_joints"]= (math.degrees(j) for j in actual_joints) + forces = unpack(fmt_double6,tcp_force) + chunks_info["forces"]= forces + pose = unpack(fmt_double6,tool_vector) + chunks_info["pose"]= pose + time = unpack(fmt_double1,controller_time) + chunks_info["time"]= time diff --git a/lecture_06/assignment_05/Guillaume Jami/simple_ur_script.py b/lecture_06/assignment_05/Guillaume Jami/simple_ur_script.py new file mode 100644 index 0000000..8cb4612 --- /dev/null +++ b/lecture_06/assignment_05/Guillaume Jami/simple_ur_script.py @@ -0,0 +1,193 @@ +""" +This module wraps standard UR Script functions. +Main change is that plane infromation substitute for pose data +""" + +import utils +import Rhino.Geometry as rg + +# Some Constants +MAX_ACCEL = 1.5 +MAX_VELOCITY = 2 + + +def move_l(plane_to, accel, vel): + """ + Function that returns UR script for linear movement in tool-space. + + Args: + plane_to: Rhino.Geometry Plane. A target plane for calculating pose (in UR base coordinate system) + accel: tool accel in m/s^2 + vel: tool speed in m/s + + Returns: + script: UR script + """ + + # Check acceleration and velocity are non-negative and below a set limit + accel = MAX_ACCEL if (abs(accel) >MAX_ACCEL) else abs(accel) + vel = MAX_VELOCITY if (abs(vel) > MAX_VELOCITY) else abs(vel) + + _matrix = rg.Transform.PlaneToPlane(rg.Plane.WorldXY,plane_to) + _axis_angle= utils.matrix_to_axis_angle(_matrix) + # Create pose data + _pose = [plane_to.OriginX/1000, plane_to.OriginY/1000, plane_to.OriginZ/1000,_axis_angle[0], _axis_angle[1], _axis_angle[2]] + _pose_fmt = "p[" + ("%.4f,"*6)[:-1]+"]" + _pose_fmt = _pose_fmt%tuple(_pose) + # Format UR script + script = "movel(%s, a = %.2f, v = %.2f)\n"%(_pose_fmt,accel,vel) + return script + + +def move_l_blend(plane_to, accel, vel, blend_radius = 0): + """ + Function that returns UR script for linear movement in tool-space. + + Args: + plane_to: Rhino.Geometry Plane. A target plane for calculating pose (in UR base coordinate system) + accel: tool accel in m/s^2 + vel: tool speed in m/s + + Returns: + script: UR script + """ + + # Check acceleration and velocity are non-negative and below a set limit + accel = MAX_ACCEL if (abs(accel) >MAX_ACCEL) else abs(accel) + vel = MAX_VELOCITY if (abs(vel) > MAX_VELOCITY) else abs(vel) + # Check blend radius is positive + blend_radius = max(0, blend_radius) + + _matrix = rg.Transform.PlaneToPlane(rg.Plane.WorldXY,plane_to) + _axis_angle= utils.matrix_to_axis_angle(_matrix) + # Create pose data + _pose = [plane_to.OriginX/1000, plane_to.OriginY/1000, plane_to.OriginZ/1000,_axis_angle[0], _axis_angle[1], _axis_angle[2]] + _pose_fmt = "p[" + ("%.4f,"*6)[:-1]+"]" + _pose_fmt = _pose_fmt%tuple(_pose) + # Format UR script + script = "movel(%s, a = %.2f, v = %.2f, r = %.4f)\n"%(_pose_fmt, accel, vel, blend_radius) + return script + +def move_j(joints, accel, vel): + """ + Function that returns UR script for linear movement in joint space. + + Args: + joints: A list of 6 joint angles (double). + accel: tool accel in m/s^2 + accel: tool accel in m/s^2 + vel: tool speed in m/s + + Returns: + script: UR script + """ + # Check acceleration and velocity are non-negative and below a set limit + + _j_fmt = "[" + ("%.2f,"*6)[:-1]+"]" + _j_fmt = _j_fmt%tuple(joints) + script = "movej(%s, a = %.2f, v = %.2f)\n"%(_j_fmt,accel,vel) + return script + +def set_tcp_by_plane(x_offset, y_offset, z_offset, ref_plane=rg.Plane.WorldXY): + """ + TODO: Need to test if this gives the correct result + Function that returns UR script for setting tool center point + + Args: + x_offset: float. tooltip offset in mm + y_offset: float. tooltip offset in mm + z_offset: float. tooltip offset in mm + ref_plane: Plane that defines orientation of the tip. If none specified, world XY plane used as default. (in UR base coordinate system) + + Returns: + script: UR script + """ + + if (ref_plane != rg.Plane.WorldXY): + _matrix = rg.Transform.PlaneToPlane(rg.Plane.WorldXY,ref_plane) + _axis_angle= utils.matrix_to_axis_angle(_matrix) + else: + _axis_angle = rg.Vector3d(0,0,0) + # Create pose data + _pose = [x_offset/1000, y_offset/1000, z_offset/1000,_axis_angle[0], _axis_angle[1], _axis_angle[2]] + _pose_fmt = "p[" + ("%.4f,"*6)[:-1]+"]" + _pose_fmt = _pose_fmt%tuple(_pose) + + # Format UR script + script = "set_tcp(%s)\n"%(_pose_fmt) + return script + +def set_tcp_by_angles(x_offset, y_offset, z_offset, x_rotate, y_rotate, z_rotate): + """ + Function that returns UR script for setting tool center point + + Args: + x_offset: float. tooltip offset in mm + y_offset: float. tooltip offset in mm + z_offset: float. tooltip offset in mm + x_rotation: float. rotation around world x axis in radians + y_rotation: float. rotation around world y axis in radians + z_rotation: float. rotation around world z axis in radians + + Returns: + script: UR script + """ + + #Create rotation matrix + _rX = rg.Transform.Rotation(x_rotate, rg.Vector3d(1,0,0), rg.Point3d(0,0,0)) + _rY = rg.Transform.Rotation(y_rotate, rg.Vector3d(0,1,0), rg.Point3d(0,0,0)) + _rZ = rg.Transform.Rotation(z_rotate, rg.Vector3d(0,0,1), rg.Point3d(0,0,0)) + _r = _rX * _rY * _rZ + _axis_angle= utils.matrix_to_axis_angle(_r) + + # Create pose data + _pose = [x_offset/1000, y_offset/1000, z_offset/1000,_axis_angle[0], _axis_angle[1], _axis_angle[2]] + _pose_fmt = "p[" + ("%.4f,"*6)[:-1]+"]" + _pose_fmt = _pose_fmt%tuple(_pose) + + # Format UR script + script = "set_tcp(%s)\n"%(_pose_fmt) + return script + +def popup(message, title): + """ + Function that returns UR script for popup + + Args: + message: float. tooltip offset in mm + title: float. tooltip offset in mm + + Returns: + script: UR script + """ + script = 'popup("%s","%s") \n' %(message,title) + return script + +def sleep(time): + """ + Function that returns UR script for sleep() + + Args: + time: float.in s + + Returns: + script: UR script + """ + script = "sleep(%s) \n" %(time) + return script + +def set_digital_out(id, signal): + """ + Function that returns UR script for setting digital out + + Args: + id: int. Input id number + signal: boolean. signal level - on or off + + Returns: + script: UR script + """ + + # Format UR script + script = "set_digital_out(%s,%s)\n"%(id,signal) + return script diff --git a/lecture_06/assignment_05/Guillaume Jami/utils.py b/lecture_06/assignment_05/Guillaume Jami/utils.py new file mode 100644 index 0000000..1156739 --- /dev/null +++ b/lecture_06/assignment_05/Guillaume Jami/utils.py @@ -0,0 +1,236 @@ +""" +This module contains utility functions: + 1) Transformation functions + 2) Useful geometry functions e.g. Intersections +""" + +import Rhino.Geometry as rg +import math + +# ----- Coordinate System conversions ----- + +def rhino_to_robotbase(ref_plane, model_base): + """ + Function that transforms a reference plane from Rhino coordinate system to the robot's base coordinate system + TODO (Jason): maybe change this whole method? maybe not model but robot base + + Args: + ref_plane: Rhino.Geometry plane object. The reference plane to transform + model_base: Rhino.Geometry plane object. The base plane for building on. Given in robot's base coordinate system. + + Returns: + ref_plane: Reference plane transformed to robot space + """ + + # Transform the orientation plane based on model_base coordinate system + _matrix = rg.Transform.PlaneToPlane(rg.Plane.WorldXY,model_base) + #_matrix = rg.Transform.PlaneToPlane(model_base,rg.Plane.WorldXY,) + ref_plane.Transform(_matrix) + return ref_plane + +def matrix_to_axis_angle(m): + """ + Function that transforms a 4x4 matrix to axis-angle format + referenced from Martin Baker's www.euclideanspace.com + + Args: + m: Rhino.Geometry Transform structure - 4x4 matrix + + Returns: + axis: Rhino.Geometry Vector3d object - axis-angle notation + """ + + epsilon = 0.01 + epsilon2 = 0.01 + + if (math.fabs(m.M01 - m.M10) < epsilon) & (math.fabs(m.M02 - m.M20) < epsilon) & (math.fabs(m.M12 - m.M21) < epsilon): + #singularity found + #first check for identity matrix which must have +1 for all terms + #in leading diagonal and zero in other terms + if (math.fabs(m.M01 + m.M10) < epsilon2) & (math.fabs(m.M02 + m.M20) < epsilon2) & (math.fabs(m.M12 + m.M21) < epsilon2) & (math.fabs(m.M00 + m.M11 + m.M22 - 3) < epsilon2): + #this singularity is identity matrix so angle = 0 make zero angle, arbitrary axis + angle = 0 + x = 1 + y = z = 0 + else: + # otherwise this singularity is angle = 180 + angle = math.pi; + xx = (m.M00 + 1) / 2 + yy = (m.M11 + 1) / 2 + zz = (m.M22 + 1) / 2 + xy = (m.M01 + m.M10) / 4 + xz = (m.M02 + m.M20) / 4 + yz = (m.M12 + m.M21) / 4 + if ((xx > yy) & (xx > zz)): + # m.M00 is the largest diagonal term + if (xx < epsilon): + x = 0 + y = z = 0.7071 + else: + x = math.sqrt(xx) + y = xy / x + z = xz / x + elif (yy > zz): + # m.M11 is the largest diagonal term + if (yy < epsilon): + x = z = 0.7071 + y = 0 + else: + y = math.sqrt(yy) + x = xy / y + z = yz / y + else: + # m.M22 is the largest diagonal term so base result on this + if (zz < epsilon): + x = y = 0.7071 + z = 0 + else: + z = math.sqrt(zz) + x = xz / z + y = yz / z + else: + s = math.sqrt((m.M21 - m.M12) * (m.M21 - m.M12)+ (m.M02 - m.M20) * (m.M02 - m.M20)+ (m.M10 - m.M01) * (m.M10 - m.M01)); # used to normalise + if (math.fabs(s) < 0.001): + #prevent divide by zero, should not happen if matrix is orthogonal and should be + s = 1 + angle = math.acos((m.M00 + m.M11 + m.M22 - 1) / 2) + x = (m.M21 - m.M12) / s + y = (m.M02 - m.M20) / s + z = (m.M10 - m.M01) / s + angleRad = angle + axis = rg.Vector3d(x,y,z) + axis = axis*angleRad + + return axis + +def matrix_to_euler(m): + """ + Gets the Euler rotation angles from a transformation matrix + from http://forums.codeguru.com/archive/index.php/t-329530.html + + Args: + m = Transform object + Returns: + tuple of euler angles in radians + """ + + rotz = math.atan2(m.M10, m.M00) + roty = -math.asin(m.M20) + rotx = math.atan2(m.M21, m.M22) + return (rotx, roty, rotz) + +# ----- Matrix related helper functions + +def dh_matrix((d, theta, a, alpha)): + """ + This function creates the Denavit Hartenberg transformation matrix between adjacent frames + + Arguments: + d: Joint distance. in mm + theta: joint angle. in radians + a: link length. in mm + alpha: twist angle. in radians + + Returns: + m: Denavit Hartenberg transformation matrix + """ + + _matrix = [ + (math.cos(theta), -math.sin(theta) * math.cos(alpha),math.sin(theta) * math.sin(alpha),a * math.cos(theta)), + (math.sin(theta), math.cos(theta) * math.cos(alpha), -math.cos(theta) * math.sin(alpha), a * math.sin(theta)), + (0, math.sin(alpha),math.cos(alpha),d), + (0,0,0,1) + ] + + m = rg.Transform() + for i in range(4): + for j in range(4): + m[i,j] = _matrix[i][j] + + return m + +def concatenate_matrices(matrices): + """ + This function creates a concatenated matrix from a list of matrices + + Arguments: + matrices: A list of tranformation matrices + + Returns: + _transform: Concatenated matrix + """ + _transform = matrices[0] + for i in range(1,len(matrices)): + _transform *= matrices[i] + return _transform + + +# ----- Miscellaneous geometry helper functions +def signed_angle(v1,v2,v_normal): + """ + This function gets the angle between 2 vectors -pi < theta< pi + + Arguments: + v1: Vector3d. First unitized vector + v2: Vector3d. Second unitized vector + v_normal: Vector3d. Normal to 2 vectors that determines what is positive/negative + + Returns: + theta: float. signed angle between -pi and pi + """ + # from 0 to pi + c = rg.Vector3d.Multiply(v1,v2) + n = rg.Vector3d.CrossProduct(v1,v2) + s= n.Length + + theta = math.atan2(s,c) + + if (rg.Vector3d.Multiply(n, v_normal) < 0): + theta *= -1 + return theta + +def cir_cir_intersection(cir1,cir2): + """ + Funtion that returns the intersection points between two circles + + Arguments: + 1) cir1: First circle + 2) cir2: Second Circle + + Returns: + xpts: list of 2 Point3d objectts + + Note that there is no error checking + """ + r1 = cir1.Radius + r2 = cir2.Radius + d = cir1.Center.DistanceTo(cir2.Center) + + a = (r1 **2 - r2**2 + d**2)/(2*d) + h = math.sqrt(r1 **2 - a **2 ) + + v_c1 = rg.Vector3d(cir1.Center) + v_c2 = rg.Vector3d(cir2.Center) + + v_c1c2 = v_c2 - v_c1 + v_c1c2.Unitize() + v_c1c2 *= a + + v_pt0 = v_c1 + v_c1c2 + + v_pt0ptX = rg.Vector3d.CrossProduct(cir1.Normal,v_c1c2) + v_pt0ptX.Unitize() + v_pt0ptX *= h + + xpt1 = rg.Point3d(v_pt0 + v_pt0ptX) + v_pt0ptX.Reverse() + xpt2 = rg.Point3d(v_pt0 + v_pt0ptX) + + return [xpt1,xpt2] + +def check_arguments(function): + def decorated(*args): + if None in args: + raise TypeError("Invalid Argument") + return function(*args) + return decorated diff --git a/lecture_06/assignment_05/Guillaume Jami/vacuum_gripper.stl b/lecture_06/assignment_05/Guillaume Jami/vacuum_gripper.stl new file mode 100644 index 0000000000000000000000000000000000000000..4fbbb2e731785db0ee5d2f99b3f77678209388ca GIT binary patch literal 9884 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 VuKUeLH#2k>b(vtS$k5ED^j~ijBhCN- literal 0 HcmV?d00001