添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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 this previous article . 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.

Let’s see this in action :

Full source code with comments (designed to be used in pyRevit) :

from revitutils import doc, uidoc
from scriptutils.userinput import WPFWindow
# noinspection PyUnresolvedReferences
from Autodesk.Revit.DB import Transaction, ElementTransformUtils, Line, XYZ, Location, UnitType, UnitUtils
# noinspection PyUnresolvedReferences
from Autodesk.Revit.UI.Selection import ObjectType, ISelectionFilter
# noinspection PyUnresolvedReferences
from Autodesk.Revit.UI import IExternalEventHandler, IExternalApplication, Result, ExternalEvent, IExternalCommand
# noinspection PyUnresolvedReferences
from Autodesk.Revit.Exceptions import InvalidOperationException, OperationCanceledException
__doc__ = "Rotate object in any direction"
__title__ = "3D Rotate"
__author__ = "Cyril Waechter"
# Get current project units for angles
angle_unit = doc.GetUnits().GetFormatOptions(UnitType.UT_Angle).DisplayUnits
def xyz_axis(element_id):
    """Input : Element, Output : xyz axis of the element"""
    origin = doc.GetElement(element_id).Location.Point
    xyz_direction = [XYZ(origin.X + 1, origin.Y, origin.Z),
                     XYZ(origin.X, origin.Y + 1, origin.Z),
                     XYZ(origin.X, origin.Y, origin.Z + 1)]
    axis = []
    for direction in xyz_direction:
        axis.append(Line.CreateBound(origin, direction))
    return axis
class AxisISelectionFilter(ISelectionFilter):
    """ISelectionFilter that allow only which have an axis (Line)"""
    # noinspection PyMethodMayBeStatic, PyPep8Naming
    def AllowElement(self, element):
        if isinstance(element.Location.Curve, Line):
            return True
        else:
            return False
def axis_selection():
    """Ask user to select an element, return the axis of the element"""
        reference = uidoc.Selection.PickObject(ObjectType.Element, AxisISelectionFilter(), "Select an axis")
    except OperationCanceledException:
    else:
        axis = doc.GetElement(reference).Location.Curve
        return axis
class RotateElement(object):
    """class used to store rotation parameters. Methods then rotate elements."""
    def __init__(self):
        self.selection = uidoc.Selection.GetElementIds()
        self.angles = [0]
    def around_itself(self):
        """Method used to rotate elements on themselves"""
            t = Transaction(doc, "Rotate around itself")
            t.Start()
            for elid in self.selection:
                el_axis = xyz_axis(elid)
                for i in range(3):
                    if self.angles[i] == 0:
                    else:
                        ElementTransformUtils.RotateElement(doc, elid, el_axis[i], self.angles[i])
            t.Commit()
        except InvalidOperationException:
            import traceback
            traceback.print_exc()
        except:
            import traceback
            traceback.print_exc()
    def around_axis(self):
        """Method used to rotate elements around selected axis"""
            axis = axis_selection()
            t = Transaction(doc, "Rotate around axis")
            t.Start()
            ElementTransformUtils.RotateElements(doc, self.selection, axis, self.angles)
            t.Commit()
        except InvalidOperationException:
            import traceback
            traceback.print_exc()
        except:
            import traceback
            traceback.print_exc()
        finally:
            uidoc.Selection.SetElementIds(rotate_elements.selection)
rotate_elements = RotateElement()
# Create a subclass of IExternalEventHandler
class RotateElementHandler(IExternalEventHandler):
    """Input : function or method. Execute input in a IExternalEventHandler"""
    # __init__ is used to make function from outside of the class to be executed by the handler. \
    # Instructions could be simply written under Execute method only
    def __init__(self, do_this):
        self.do_this = do_this
    # Execute method run in Revit API environment.
    # noinspection PyPep8Naming, PyUnusedLocal
    def Execute(self, application):
            self.do_this()
        except InvalidOperationException:
            # If you don't catch this exeption Revit may crash.
            print "InvalidOperationException catched"
    # noinspection PyMethodMayBeStatic, PyPep8Naming
    def GetName(self):
        return "Execute an function or method in a IExternalHandler"
# Create handler instances. Same class (2 instance) is used to call 2 different method.
around_itself_handler = RotateElementHandler(rotate_elements.around_itself)
around_axis_handler = RotateElementHandler(rotate_elements.around_axis)
# Create ExternalEvent instance which pass these handlers
around_itself_event = ExternalEvent.Create(around_itself_handler)
around_axis_event = ExternalEvent.Create(around_axis_handler)
class RotateOptions(WPFWindow):
    Modeless WPF form used for rotation angle input
    def __init__(self, xaml_file_name):
        WPFWindow.__init__(self, xaml_file_name)
        self.set_image_source("xyz_img", "XYZ.png")
        self.set_image_source("plusminus_img", "PlusMinusRotation.png")
    # noinspection PyUnusedLocal
    def around_itself_click(self, sender, e):
            rotate_elements.selection = uidoc.Selection.GetElementIds()
            angles = [self.x_axis.Text, self.y_axis.Text, self.z_axis.Text]
            for i in range(3):
                angles[i] = UnitUtils.ConvertToInternalUnits(float(angles[i]), angle_unit)
            rotate_elements.angles = angles
        except ValueError:
            self.warning.Text = "Incorrect angles, input format required '0.0'"
        else:
            self.warning.Text = ""
            around_itself_event.Raise()
    # noinspection PyUnusedLocal
    def around_axis_click(self, sender, e):
            rotate_elements.angles = UnitUtils.ConvertToInternalUnits(float(self.rotation_angle.Text), angle_unit)
            rotate_elements.selection = uidoc.Selection.GetElementIds()
        except ValueError:
            self.warning.Text = "Incorrect angles, input format required '0.0'"
        else:
            around_axis_event.Raise()
RotateOptions('RotateOptions.xaml').Show()

Enjoy !

Posted in Python, Revit-en, Script and tagged , , , , on by . 9 Comments

Please help
I have downloaded api rotate but I still can’t rotate my item. I get an error saying ; can’t rotate element into this position

Some elements like lighting fixtures can’t be rotated in any direction. Please try with a non hosted air terminal for example, any pipe, duct, fitting etc…
It is a Revit limitation the only way to bypass it is to create rotation parameters inside this kind of families.

Hi, Thanks for sharing.

With your python code, I still cannot rote the model in any direction (can only rote in y-axis ).

Could you please show more details about how to break the limitation.

Thanks again.

As said above (my last reply). Some objects can rotate only on z-axis. What kind of object are you trying to rotate ? To break this limitation you need to build your family based on a reference line.

Following videos might help :
https://www.youtube.com/watch?v=i97k1zlUq5I
https://www.youtube.com/watch?v=1wdV2znoyG0

Hi Cyril

would you mind tell me which family type are you use in your video please?

most of objects in Revit cannot rotate in more than one axis (vertical with the work plane)

I am trying to build a family model that can rotate in all three axis without using reference line and angle parameters.

I have tried pipe elements but it’s not work when I use your plugin. maybe I use it in a wrong way?

Thank you
fuller

Hi Fuller,

I use this tool commonly for duct/pipe, duct/pipe fittings, duct/pipe accessories. In the video I also rotate an air terminal (it works as long as it is not an hosted family).

You get a message like «you cannot rotate this element in that direction» ? If yes. You need to build a family based on a reference line as mentionned in my previous answers.

Cheers,
Cyril

I would like to test your code, but I’m getting an exception.
Exception : IronPython.Runtime.Exceptions.ImportException: No module named revitutils
How do I install the missing module?
Sorry, but I’m really new to Revit.
Thanks Andreas

1. Check that you run pyRevit last version which is 4.7.4.
2. Search for a similar issues here : https://github.com/CyrilWaechter/pyRevitMEP/issues
3. If you don’t find a similar issue open a new one providing all necessary informations (Revit version, pyRevit version (+ engine + rocket mode etc… -> about screen), pyRevitMEP last update, error message, step to reproduce your error etc…)

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *