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();
}
}
Element Creation
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();
}
}