{"id":255,"date":"2017-04-02T23:04:56","date_gmt":"2017-04-02T22:04:56","guid":{"rendered":"http:\/\/pythoncvc.net\/?p=255"},"modified":"2017-04-02T23:04:43","modified_gmt":"2017-04-02T22:04:43","slug":"revit-rotate-elements-in-any-direction-script","status":"publish","type":"post","link":"https:\/\/pythoncvc.net\/?p=255","title":{"rendered":"[Revit] Rotate elements in any direction script"},"content":{"rendered":"<p><a href=\"https:\/\/pythoncvc.net\/wp-content\/uploads\/2017\/04\/2017-04-02-17_58_20-Set-rotation-angle_.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-256\" src=\"https:\/\/pythoncvc.net\/wp-content\/uploads\/2017\/04\/2017-04-02-17_58_20-Set-rotation-angle_-300x300.png\" alt=\"\" width=\"300\" height=\"300\" srcset=\"https:\/\/pythoncvc.net\/wp-content\/uploads\/2017\/04\/2017-04-02-17_58_20-Set-rotation-angle_-300x300.png 300w, https:\/\/pythoncvc.net\/wp-content\/uploads\/2017\/04\/2017-04-02-17_58_20-Set-rotation-angle_-150x150.png 150w, https:\/\/pythoncvc.net\/wp-content\/uploads\/2017\/04\/2017-04-02-17_58_20-Set-rotation-angle_.png 406w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>More than 2 years ago I made <a href=\"https:\/\/pythoncvc.net\/?p=40\">an article<\/a> to show a way to rotate element using Revit API. Using external events in a modeless form as described in <a href=\"https:\/\/pythoncvc.net\/?p=247\">my previous article<\/a> you can for example make a GUI to get axis and angle from user inputs. It is also using ISelectionFilter as described <a href=\"https:\/\/pythoncvc.net\/?p=116\">in this previous article<\/a>. The new thing is that I use a standard class to store rotation parameters. This way parameters are dynamically feed to methods which are run in external events.<\/p>\n<p>Let&rsquo;s see this in action :<\/p>\n<p><iframe loading=\"lazy\" title=\"Revit API 3D Rotate script for pyRevit\" width=\"620\" height=\"349\" src=\"https:\/\/www.youtube.com\/embed\/60Y_DJbIL5Y?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>Full source code with comments (designed to be used in pyRevit) :<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from revitutils import doc, uidoc\r\nfrom scriptutils.userinput import WPFWindow\r\n\r\n# noinspection PyUnresolvedReferences\r\nfrom Autodesk.Revit.DB import Transaction, ElementTransformUtils, Line, XYZ, Location, UnitType, UnitUtils\r\n# noinspection PyUnresolvedReferences\r\nfrom Autodesk.Revit.UI.Selection import ObjectType, ISelectionFilter\r\n# noinspection PyUnresolvedReferences\r\nfrom Autodesk.Revit.UI import IExternalEventHandler, IExternalApplication, Result, ExternalEvent, IExternalCommand\r\n# noinspection PyUnresolvedReferences\r\nfrom Autodesk.Revit.Exceptions import InvalidOperationException, OperationCanceledException\r\n\r\n\r\n__doc__ = \"Rotate object in any direction\"\r\n__title__ = \"3D Rotate\"\r\n__author__ = \"Cyril Waechter\"\r\n\r\n# Get current project units for angles\r\nangle_unit = doc.GetUnits().GetFormatOptions(UnitType.UT_Angle).DisplayUnits\r\n\r\n\r\ndef xyz_axis(element_id):\r\n    \"\"\"Input : Element, Output : xyz axis of the element\"\"\"\r\n    origin = doc.GetElement(element_id).Location.Point\r\n    xyz_direction = [XYZ(origin.X + 1, origin.Y, origin.Z),\r\n                     XYZ(origin.X, origin.Y + 1, origin.Z),\r\n                     XYZ(origin.X, origin.Y, origin.Z + 1)]\r\n    axis = []\r\n    for direction in xyz_direction:\r\n        axis.append(Line.CreateBound(origin, direction))\r\n    return axis\r\n\r\n\r\nclass AxisISelectionFilter(ISelectionFilter):\r\n    \"\"\"ISelectionFilter that allow only which have an axis (Line)\"\"\"\r\n\r\n    # noinspection PyMethodMayBeStatic, PyPep8Naming\r\n    def AllowElement(self, element):\r\n        if isinstance(element.Location.Curve, Line):\r\n            return True\r\n        else:\r\n            return False\r\n\r\n\r\ndef axis_selection():\r\n    \"\"\"Ask user to select an element, return the axis of the element\"\"\"\r\n    try:\r\n        reference = uidoc.Selection.PickObject(ObjectType.Element, AxisISelectionFilter(), \"Select an axis\")\r\n    except OperationCanceledException:\r\n        pass\r\n    else:\r\n        axis = doc.GetElement(reference).Location.Curve\r\n        return axis\r\n\r\n\r\nclass RotateElement(object):\r\n    \"\"\"class used to store rotation parameters. Methods then rotate elements.\"\"\"\r\n    def __init__(self):\r\n        self.selection = uidoc.Selection.GetElementIds()\r\n        self.angles = [0]\r\n\r\n    def around_itself(self):\r\n        \"\"\"Method used to rotate elements on themselves\"\"\"\r\n        try:\r\n            t = Transaction(doc, \"Rotate around itself\")\r\n            t.Start()\r\n            for elid in self.selection:\r\n                el_axis = xyz_axis(elid)\r\n                for i in range(3):\r\n                    if self.angles[i] == 0:\r\n                        pass\r\n                    else:\r\n                        ElementTransformUtils.RotateElement(doc, elid, el_axis[i], self.angles[i])\r\n            t.Commit()\r\n        except InvalidOperationException:\r\n            import traceback\r\n            traceback.print_exc()\r\n        except:\r\n            import traceback\r\n            traceback.print_exc()\r\n\r\n    def around_axis(self):\r\n        \"\"\"Method used to rotate elements around selected axis\"\"\"\r\n        try:\r\n            axis = axis_selection()\r\n            t = Transaction(doc, \"Rotate around axis\")\r\n            t.Start()\r\n            ElementTransformUtils.RotateElements(doc, self.selection, axis, self.angles)\r\n            t.Commit()\r\n        except InvalidOperationException:\r\n            import traceback\r\n            traceback.print_exc()\r\n        except:\r\n            import traceback\r\n            traceback.print_exc()\r\n        finally:\r\n            uidoc.Selection.SetElementIds(rotate_elements.selection)\r\n\r\nrotate_elements = RotateElement()\r\n\r\n\r\n# Create a subclass of IExternalEventHandler\r\nclass RotateElementHandler(IExternalEventHandler):\r\n    \"\"\"Input :\u00a0function or method. Execute input in a IExternalEventHandler\"\"\"\r\n\r\n    # __init__ is used to make function from outside of the class to be executed by the handler. \\\r\n    # Instructions could be simply written under Execute method only\r\n    def __init__(self, do_this):\r\n        self.do_this = do_this\r\n\r\n    # Execute method run in Revit API environment.\r\n    # noinspection PyPep8Naming, PyUnusedLocal\r\n    def Execute(self, application):\r\n        try:\r\n            self.do_this()\r\n        except InvalidOperationException:\r\n            # If you don't catch this exeption Revit may crash.\r\n            print \"InvalidOperationException catched\"\r\n\r\n    # noinspection PyMethodMayBeStatic, PyPep8Naming\r\n    def GetName(self):\r\n        return \"Execute an function or method in a IExternalHandler\"\r\n\r\n# Create handler instances. Same class (2 instance) is used to call 2 different method.\r\naround_itself_handler = RotateElementHandler(rotate_elements.around_itself)\r\naround_axis_handler = RotateElementHandler(rotate_elements.around_axis)\r\n# Create ExternalEvent instance which pass these handlers\r\naround_itself_event = ExternalEvent.Create(around_itself_handler)\r\naround_axis_event = ExternalEvent.Create(around_axis_handler)\r\n\r\n\r\nclass RotateOptions(WPFWindow):\r\n    \"\"\"\r\n    Modeless WPF form used for rotation angle input\r\n    \"\"\"\r\n\r\n    def __init__(self, xaml_file_name):\r\n        WPFWindow.__init__(self, xaml_file_name)\r\n        self.set_image_source(\"xyz_img\", \"XYZ.png\")\r\n        self.set_image_source(\"plusminus_img\", \"PlusMinusRotation.png\")\r\n\r\n    # noinspection PyUnusedLocal\r\n    def around_itself_click(self, sender, e):\r\n        try:\r\n            rotate_elements.selection = uidoc.Selection.GetElementIds()\r\n            angles = [self.x_axis.Text, self.y_axis.Text, self.z_axis.Text]\r\n            for i in range(3):\r\n                angles[i] = UnitUtils.ConvertToInternalUnits(float(angles[i]), angle_unit)\r\n            rotate_elements.angles = angles\r\n        except ValueError:\r\n            self.warning.Text = \"Incorrect angles, input format required '0.0'\"\r\n        else:\r\n            self.warning.Text = \"\"\r\n            around_itself_event.Raise()\r\n\r\n    # noinspection PyUnusedLocal\r\n    def around_axis_click(self, sender, e):\r\n        try:\r\n            rotate_elements.angles = UnitUtils.ConvertToInternalUnits(float(self.rotation_angle.Text), angle_unit)\r\n            rotate_elements.selection = uidoc.Selection.GetElementIds()\r\n        except ValueError:\r\n            self.warning.Text = \"Incorrect angles, input format required '0.0'\"\r\n        else:\r\n            around_axis_event.Raise()\r\n\r\nRotateOptions('RotateOptions.xaml').Show()\r\n<\/pre>\n<p>Enjoy !<\/p>\n","protected":false},"excerpt":{"rendered":"<p>More than 2 years ago I made an article to show a way to rotate element using Revit API. Using external events in a modeless form as described in my previous article you can for example make a GUI to get axis and angle from user inputs. It is also using ISelectionFilter as described in [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":"","footnotes":""},"categories":[80,38,34],"tags":[142,144,146,131,148],"class_list":["post-255","post","type-post","status-publish","format-standard","hentry","category-python","category-revit-en","category-script-en","tag-externalevent-en","tag-modeless-form-en","tag-pyrevit-en","tag-revit-api-2017-en","tag-wpf-en"],"_links":{"self":[{"href":"https:\/\/pythoncvc.net\/index.php?rest_route=\/wp\/v2\/posts\/255","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pythoncvc.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pythoncvc.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pythoncvc.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pythoncvc.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=255"}],"version-history":[{"count":5,"href":"https:\/\/pythoncvc.net\/index.php?rest_route=\/wp\/v2\/posts\/255\/revisions"}],"predecessor-version":[{"id":262,"href":"https:\/\/pythoncvc.net\/index.php?rest_route=\/wp\/v2\/posts\/255\/revisions\/262"}],"wp:attachment":[{"href":"https:\/\/pythoncvc.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=255"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pythoncvc.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=255"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pythoncvc.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=255"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}