Two Clicks to Create and Rotate Family Instances

public void createAndOrient()
{
    UIDocument uidoc = this.ActiveUIDocument;
    Document doc = uidoc.Document;
    Application app = doc.Application;

    // Get the family symbol named "North Arrow 2"
    FamilySymbol famSym = new FilteredElementCollector(doc).OfClass(typeof(FamilySymbol)).Where(q => q.Name == "North Arrow 2").First() as FamilySymbol;

    // use a transaction group so that all the individual transactions are merged into a single entry in the Undo menu
    // this is optional
    using (TransactionGroup tg = new TransactionGroup(doc,"Create and Orient Instances"))
    {
        tg.Start();
        // create an infinite loop so user can create multiple instances in a single command
        // ESC when prompted to select a point will thrown an exception which is how the loop is exited
        while (true)
        {
            try
            {                
                XYZ pickPoint = uidoc.Selection.PickPoint("Click to specify instance location. ESC to stop placing instances.");

                FamilyInstance familyInstance = null;    
                // Create the instance with the default orientation
                // This is done in its own transaction so that the user can see the new instance when they are prompted for the orientation
                using (Transaction t = new Transaction(doc,"Place Instance"))
                {
                    t.Start();
                    familyInstance = doc.Create.NewFamilyInstance(pickPoint, famSym, doc.ActiveView);
                    t.Commit();
                }

                XYZ orientPoint = uidoc.Selection.PickPoint("Click to specify orientation. ESC to stop placing instances.");

                // Create a line between the two points
                // A transaction is not needed because the line is a transient element created in the application, not in the document
                Line orientLine = app.Create.NewLineBound(pickPoint, orientPoint);

                // Compute the angle between the vertical direction (XZY.BasisY) and the orientLine
                double angle = XYZ.BasisY.AngleTo(orientLine.Direction);

                // For diagnostics in Task dialog below
                double angleDegrees = angle * 180 / Math.PI;

                // AngleTo always returns the smaller angle between the two lines (for example, it will always return 10 degrees, never 350)
                // so if the orient point is to the left of the pick point, then correct the angle by subtracting it from 2PI (Revit measures angles in degrees)
                if (orientPoint.X < pickPoint.X)
                    angle = 2 * Math.PI - angle;

                // To show the need for angle corrections
                double angleDegreesCorrected = angle * 180 / Math.PI;
                //TaskDialog.Show("info","Angle directly from AngleTo = " + angleDegrees + "\n Angle after X correction = " + angleDegreesCorrected);

                // Create an axis in the Z direction 
                Line axis = app.Create.NewLineBound(pickPoint, new XYZ(pickPoint.X, pickPoint.Y, pickPoint.Z + 10));

                using (Transaction t = new Transaction(doc,"Orient Instance"))
                {
                    t.Start();
                    ElementTransformUtils.RotateElement(doc, familyInstance.Id, axis, -angle);
                    t.Commit();
                }
            }
            catch
            {
                // Get here when the user hits ESC when prompted for selection
                // "break" exits from the while loop
                break;
            }
        }
    // Consolidate all the transactions for the individual creation / rotation transactions into a single Undo item	
    tg.Assimilate();
    }
}
Advertisements

How to create a door & tag

Continuing the theme of element creation from the last post, here is how to create a door and tag it.

public void CreateDoor()
{
    Document doc = this.ActiveUIDocument.Document;
    UIDocument uidoc = this.ActiveUIDocument;

    // Get the door type
    // FamilySymbol corresponds to the Type of a Family
    // In this case Door - Single-Flush - 34" x 80"
    FamilySymbol familySymbol = (from fs in new FilteredElementCollector(doc).
         OfClass(typeof(FamilySymbol)).
         Cast<FamilySymbol>()
         // need to put the \ before the " in the name of the door type so it isn't
         // treaded as the " that ends the string
         where (fs.Family.Name == "Single-Flush" && fs.Name == "34\" x 80\"")
         select fs).First();

    // Get the door tag
    FamilySymbol doorTagType = (from tag in new FilteredElementCollector(doc)
                                .OfClass(typeof(FamilySymbol)).OfCategory(BuiltInCategory.OST_DoorTags)
                                .Cast<FamilySymbol>() where tag.Name == "Door Tag" select tag).First();

    // Prompt the user to select a wall
    Reference r = uidoc.Selection.PickObject(ObjectType.Element, "Select the wall that will host the door");
    // Get the wall element from the selected reference
    Wall wall = doc.GetElement(r) as Wall;
    // Get the point on the wall where the selection was made
    XYZ globalPoint = r.GlobalPoint;
    // But we don't want to place the door at this "global point", because the Z of that point is at the
    // cut plane height of the plan view.

    // Instead, create a new XYZ using the X and Y from the GlobalPoint and the Z of the plan view's level
    ViewPlan viewPlan = doc.ActiveView as ViewPlan;
    Level level = viewPlan.GenLevel;
    XYZ levelPoint = new XYZ(globalPoint.X, globalPoint.Y, level.Elevation);

    using (Transaction t = new Transaction(doc,"Create door"))
    {
        t.Start();
        // Create door
        FamilyInstance door = doc.Create.NewFamilyInstance(levelPoint, familySymbol, e, Autodesk.Revit.DB.Structure.StructuralType.NonStructural);
        // Create tag.
        // Unlike room tags, which have a dedicated NewRoomTag method, the generic NewFamilyInstance is used to tag doors and other elements.
        FamilyInstance doorTag = doc.Create.NewFamilyInstance(levelPoint, doorTagType, doc.ActiveView);
        t.Commit();
    }
}

How to create a room and tag it

An AUGI user asked

“Is there a way to get the UV point from a user clicking the floor plan view during an active API session?

My goal is provide the user with a list of rooms to place and have them select a room from the list in form, click a point on the floor plan view and create the room via the API.”

Here is a macro to create a room with a room tag

public void CreateRoom()
{
    Document doc = this.ActiveUIDocument.Document;
    UIDocument uidoc = this.ActiveUIDocument;

    // Get the level of the plan view
    ViewPlan view = doc.ActiveView as ViewPlan;
    Level level = view.GenLevel; // use GenLevel, not Level

    // Create a UV from a point selected by the user (the Z value is not needed)
    XYZ xyz = uidoc.Selection.PickPoint("Select a point");
    UV uv = new UV(xyz.X, xyz.Y);

    using (Transaction t = new Transaction(doc, "Create Room"))
    {
        t.Start();
        Room room = doc.Create.NewRoom(level, uv);
        RoomTag tag = doc.Create.NewRoomTag(room,uv,view);
        t.Commit();
    }
}