1

I am trying to rotate Revit elements about their center points. In order to do that, I need to select a Revit element and find its center point, then create a line with the coordinates at that elements center point.

My best idea to accomplish this is to wrap a Revit element in a bounding box and then find the center of that box. My problem is that I am unsure how to accomplish this.

I am using pyRevit (amazing tool) and I am stuck on how to either wrap the selected element with a bounding box or retrieve its existing bounding box.

Any help would be greatly appreciated! I am really trying to learn the Revit API and understand how everything works. I am making progress but there is a lot to unpack.

        def pickobject():
            from Autodesk.Revit.UI.Selection import ObjectType

            #define the active Revit application and document
            app = __revit__.Application
            doc = __revit__.ActiveUIDocument.Document
            uidoc = __revit__.ActiveUIDocument

            #define a transaction variable and describe the transaction
            t = Transaction(doc, 'This is my new transaction')

            # Begin new transaction
            t.Start()

            # Select an element in Revit
            picked = uidoc.Selection.PickObject(ObjectType.Element, "Select something.")


            ### ?????????? ###

            # Get bounding box of selected element.
            picked_bb = BoundingBoxXYZ(picked)  

            # Get max and min points of bounding box.
            picked_bb_max = picked_bb.Max
            picked_bb_min = picked_bb.Min

            # Get center point between max and min points of bounding box.
            picked_bb_center = (picked_bb_max + picked_bb_min) / 2

            ### ?????????? ###    

            # Close the transaction
            t.Commit()

            return picked, picked_bb_center  

Thanks in advance for taking a look at what I have so far. Please let me know if anything needs further clarification!

edit:

@CyrilWaechter

I think you are right. Using LocationPoint would probably make more sense. I looked through the script you linked (thank you btw!) and I tried implementing this section in my code.

transform = doc.GetElement(picked.ElementId).GetTransform()

I am passing the ElementId through this statement but I get the error, "Wall" object has no attribute 'GetTransform'. Could you please help me understand this?

edit 2: Thanks @JeremyTammik and @CyrilWaechter, your insights helped me understand where I was going wrong. While I still feel that certain properties are ambiguous in the Revit API, I was able to get my code to execute properly. I will post the code that I was able to get working below.

Christian Gentry
  • 811
  • 3
  • 10
  • 15
  • 1
    Is it not more interesting to use LocationPoint instead in your case ? In my case it is almost always better. You might be interested to take a look at [my tool](https://youtu.be/60Y_DJbIL5Y) and its [source code](https://github.com/CyrilWaechter/pyRevitMEP/tree/master/pyRevitMEP.tab/Tools.panel/Element3DRotation.pushbutton). – Cyril Waechter Dec 14 '18 at 09:33
  • @CyrilWaechter Thanks for this! Do you know why I am getting the error statement I listed in my edit? – Christian Gentry Dec 14 '18 at 16:45
  • 1
    Because as said in the error message. GetTransform method [do not exist for a Wall class](http://www.revitapidocs.com/2018.1/d0678575-843b-42ea-c91d-c94b13d7dd4f.htm). Have you tried to use ElementTransformUtils as at line 92 ? – Cyril Waechter Dec 15 '18 at 09:06
  • Thank you for the help @CyrilWaechter! I wasn't thinking about my selected elements as being part of the wall class. This got me on the right path! – Christian Gentry Dec 17 '18 at 01:15

3 Answers3

2

The centre of the bounding box is very easy to obtain. picked is a Reference. Get the ElementId from that, open it using doc.GetElement, and retrieve the bounding box using get_BoundingBox, cf. Conduits Intersecting a Junction Box :

Element e = Util.SelectSingleElement(
  uidoc, "a junction box" );

BoundingBoxXYZ bb = e.get_BoundingBox( null );

For certain elements and certain irregular shapes, you might want to use the centroid instead of the bounding box:

Jeremy Tammik
  • 7,333
  • 2
  • 12
  • 17
  • Hello Jeremy! Thank you for helping me to understand the difference between a reference and an ElementID. I was not aware of that distinction. I'm afraid that I am not understanding what to do once I have the ElementID. Would I write a line of code that says `e = doc.GetElement(picked.ElementId)` then `e_bb = e.BoundingBoxXYZ()`? Maybe it is the conversion from C# to Python I am not understanding. – Christian Gentry Dec 14 '18 at 16:56
  • the two lines of code you list look perfect to me... as you have probably verified by now... – Jeremy Tammik Dec 15 '18 at 17:06
  • Thank you for your help @JeremyTammik, Your hints got me on the right track! – Christian Gentry Dec 17 '18 at 01:14
2

Here is how I was able to solve my problem using pyRevit. This code allows you to rotate an element about its Z axis from the center of its bounding box.

To use this code, select a single Revit element and then open the Revit Python Shell. Copy and paste the code below into the Revit Python Shell notepad and click the run button. This will rotate the element by 45 degrees because the current rotateSelectedElement() argument is 45. You may change this number to any value before running.

# Import the math module to convert user input degrees to radians.
import math

# Get a list of all user selected objects in the Revit Document.
selection = [doc.GetElement(x) for x in uidoc.Selection.GetElementIds()]

# Definitions
def rotateSelectedElement(degrees_to_rotate):
    from Autodesk.Revit.UI.Selection import ObjectType

    #define the active Revit application and document
    app = __revit__.Application
    doc = __revit__.ActiveUIDocument.Document
    uidoc = __revit__.ActiveUIDocument

    #define a transaction variable and describe the transaction
    t = Transaction(doc, 'This is my new transaction')

    # Convert the user input from degrees to radians.
    converted_value = float(degrees_to_rotate) * (math.pi / 180.0)

    # Begin new transaction
    t.Start()

    # Get the first selected element from the current Revit doc.
    el = selection[0].Id

    # Get the element from the selected element reference
    el_ID = doc.GetElement(el)      

    # Get the Bounding Box of the selected element.
    el_bb = el_ID.get_BoundingBox(doc.ActiveView)

    # Get the min and max values of the elements bounding box.
    el_bb_max = el_bb.Max
    el_bb_min = el_bb.Min

    # Get the center of the selected elements bounding box.
    el_bb_center = (el_bb_max + el_bb_min) / 2

    #Create a line to use as a vector using the center location of the bounding box.
    p1 = XYZ(el_bb_center[0], el_bb_center[1], 0)
    p2 = XYZ(el_bb_center[0], el_bb_center[1], 1)
    myLine = Line.CreateBound(p1, p2)

    # Rotate the selected element.
    ElementTransformUtils.RotateElement(doc, el, myLine, converted_value)

    # Close the transaction
    t.Commit()


# Execute    
# Add the desired degrees to rotate by as an argument for rotateSelectedElement()
rotateSelectedElement(45)

edit: Made code clearer. Code now executes in Revit Python Shell without any further modifications. Refer to directions above if you have trouble!

Christian Gentry
  • 811
  • 3
  • 10
  • 15
  • 1
    Congratulations on the complete solution and thank you for sharing it. I'll happily add this discussion to [The Building Coder blog](https://thebuildingcoder.typepad.com/) for didactical purposes :-) – Jeremy Tammik Dec 18 '18 at 07:21
1

Edited and preserved for posterity by The Building Coder:

Many thanks to Christian for the interesting discussion and Cyril for the wealth of additional information he provides!

Jeremy Tammik
  • 7,333
  • 2
  • 12
  • 17
  • Thank you so much for this write up Jeremy. This is fantastic. I have updated my code solution to work directly in the Revit Python Shell without any further modifications. I have also added comments that better explain the code. Would you mind updating your blog post with this latest solution? I feel that the new solution is more beneficial to people with this same issue. Thanks! – Christian Gentry Jan 02 '19 at 17:35
  • sure thing, coming up... thank you for the appreciation and happy new year! – Jeremy Tammik Jan 03 '19 at 18:19
  • https://thebuildingcoder.typepad.com/blog/2018/12/rotate-picked-element-around-bounding-box-centre-in-python.html#5.1 – Jeremy Tammik Jan 03 '19 at 18:28