| About Boost Your BIM | Exciting and Award Winning Products |
Contact me for custom development or training |
December 1, 2012
Welcome!
June 14, 2013
Getting the area of all major faces of a host
In the last post where the user was prompted to select a single face to get its area. Building on that idea, this sample prompts the user to select a host element (wall, floor, roof, etc). Then it shows the areas of all major (side, top, and/or bottom faces).
For a wall, the interior and exterior faces are combined into a single list with the Concat method. For a floor or roof, Concat is used to create a single list with references to both the top and bottom faces.
public void areaHostMajorFaces()
{
Document doc = this.Document;
UIDocument uidoc = new UIDocument(doc);
Reference r = uidoc.Selection.PickObject(ObjectType.Element, "Select a face of a wall, roof, or floor");
Element e = doc.GetElement(r);
HostObject hostObj = e as HostObject;
if (hostObj == null)
{
TaskDialog.Show("Error", "Element selected is not a host object");
return;
}
IList<Reference> references = null;
if (hostObj is Wall)
{
IList<Reference> exterior = HostObjectUtils.GetSideFaces(hostObj, ShellLayerType.Exterior);
IList<Reference> interior = HostObjectUtils.GetSideFaces(hostObj, ShellLayerType.Interior);
references = new List<Reference>(exterior.Concat(interior));
}
else if (hostObj is RoofBase || hostObj is Floor)
{
IList<Reference> top = HostObjectUtils.GetTopFaces(hostObj);
IList<Reference> bottom = HostObjectUtils.GetBottomFaces(hostObj);
references = new List<Reference>(top.Concat(bottom));
}
string areas = "Areas of faces of element '" + e.Name + "' (" + e.Id + ")\n";
foreach (Reference myRef in references)
{
Face face = e.GetGeometryObjectFromReference(myRef) as Face;
areas += face.Area + "\n";
}
TaskDialog.Show("Area", areas);
}
June 12, 2013
Getting the area of a single face
At “Revit in Plain English“, Jay mentions that Revit reports a single area value for a wall, but that he wants that actual area of a specific face.
Here is a quick document macro (not an application macro like in my other samples) to get the area of any face.
public void areaOneFace()
{
Document doc = this.Document;
UIDocument uidoc = new UIDocument(doc);
Reference myRef = uidoc.Selection.PickObject(ObjectType.Face, "Select a face");
Element e = doc.GetElement(myRef);
Face face = e.GetGeometryObjectFromReference(myRef) as Face;
TaskDialog.Show("Area", "Area of face selected on element '" + e.Name + "' (" + e.Id + ")\n" + face.Area);
}
When the wall is attached to the roof so that the area of each side face is unique, the schedule reports the wall area as 619. With the macro we find that the area of each face is 618.52 and 568.08.
May 29, 2013
Want to learn the Revit API from Europe, Asia, or Australia?
I’m starting a new series of Revit API classes on Thursdays at 5am Eastern Time, ideal for those of you who are several time zones away. Email me at boostyourbim@gmail.com if you’d like to join us.
May 27, 2013
May 20, 2013
A few spots left for online API classes starting this week
Now’s the time to start learning how to write your own Revit API programs and create your own solutions to Make Revit Better! No programming experience needed. Small group classes make sure that you get personalized attention and learn at the pace that is just right for you.
The classes will be held every other week on:
- Monday 10:00-11:30am
- Tuesday 1:00-2:30pm
- Wednesday 3:30-5:00pm
- Friday 3:30-5:00pm
Interested? Contact Harry at BoostYourBIM@gmail.com
May 15, 2013
Using the Revit API to collect and count Curtain Panels (and Rooms and other “non-native” elements)
In the garden and natural world, “non-native” species can be invasives that lack predators and overwhelm the existing ecosystem.
For example, rooting and wallowing by non-native hogs causes extensive damage to the Great Smoky Mountains National Park.
Revit also has non-native classes like Curtain Panels and Rooms. They don’t root and wallow, but they can cause a minor complication as described in the video below.
May 9, 2013
Revit 2014 Macros in Ruby
Does it seem like all the cool kids are coding in Ruby these days?
If you want to be cool like them, here is a quick overview on how you can get started creating Revit macros in Ruby, a new feature in the 2014 API. I’m not sure there is anything you can do with Ruby that you can’t do in C#, but according to some people Ruby is more fun.
May 3, 2013
Customized API Training from the Comfort of Your Home
- Would you like to get started with the Revit API so you can make your own wonderful macros and commands?
- Tired of wasting hours searching the web for code samples or tutorials?
- Want to learn on your schedule with personalized instruction?
A couple experienced Revit users who are new to the API are interested in forming a small group to do a series of live, Revit API training sessions that I will teach over Google Hangout.
Class size limited to 5. Dates & times will be chosen to fit your schedule – we will probably meet 2-4 times per month. Cost is $45 per hour for each student and you can pay per class (no long-term commitment needed).
Interested? Email me at BoostYourBIM@gmail.com
May 1, 2013
Create a Simple Macro in Revit 2014
For those of you taking your fist steps with the Revit API, here is a short video showing all the basics of how to create the simplest macro
April 11, 2013
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();
}
}
April 4, 2013
How to Delete Line Styles
public void deleteLineStyle()
{
UIDocument uidoc = this.ActiveUIDocument;
Document doc = uidoc.Document;
Category linesCat = doc.Settings.Categories.get_Item("Lines");
IList<Category> categoryList = linesCat.SubCategories.Cast<Category>().Where(c => c.Name.StartsWith("A-") || c.Name.StartsWith("S-")).ToList();
IList<ElementId> idsToDelete = new List<ElementId>();
foreach (Category cat in categoryList)
{
idsToDelete.Add(cat.Id);
}
using (Transaction t = new Transaction(doc,"Delete LineStyle"))
{
t.Start();
doc.Delete(idsToDelete);
t.Commit();
}
}
April 2, 2013
Images for your Revit Elements
Here is some code and a video showing how you can associate images with elements in your Revit model and display them when the elements are selected.
public void showElementImage()
{
UIDocument uidoc = this.ActiveUIDocument;
Document doc = uidoc.Document;
// dictionary stores element id values and their corresponding image file URLs
Dictionary<int,string> dictionary = new Dictionary<int, string>();
dictionary.Add(181736,"http://3.bp.blogspot.com/_dpc8BBT2JGI/TP92yqwD0HI/AAAAAAAABVI/IKFc4lbNXZY/s1600/IMG_6487.JPG");
dictionary.Add(181750,"http://2.bp.blogspot.com/-8n-blC0CVeE/T_C-zHQCB3I/AAAAAAAATOg/gmPLrT71qwY/s1600/Yusupov+fancy+floor.jpg");
dictionary.Add(181784,"http://animatedpizzagifs.com/images/psycadelic-pizza-love.gif");
// prompt user to select elements
ElementId id = uidoc.Selection.PickObject(ObjectType.Element).ElementId;
// give message if no dictionary entry exists for this id value
if (!dictionary.ContainsKey(id.IntegerValue))
{
TaskDialog.Show("Error","No image exists for element id = " + id.IntegerValue);
return;
}
// create a Windows Form
using (System.Windows.Forms.Form myForm = new System.Windows.Forms.Form())
{
// Create a label to indicate the element id
System.Windows.Forms.Label label = new System.Windows.Forms.Label();
label.Text = "Image for element id " + id.IntegerValue;
label.AutoSize = true;
label.Location = new System.Drawing.Point(5, 5);
// create a picture box to show the image
System.Windows.Forms.PictureBox picture = new System.Windows.Forms.PictureBox();
// the location of the image is the dictionary entry for this element id value
picture.ImageLocation = dictionary[id.IntegerValue];
picture.Location = new System.Drawing.Point(5, 25);
picture.Width = 500;
picture.Height = 500;
// stretch or shrink to fit the PictureBox
picture.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
// add controls to the form
myForm.Controls.Add(label);
myForm.Controls.Add(picture);
// set the size & location of the form and display it
myForm.Height = picture.Top + picture.Height + 50;
myForm.Width = picture.Left + picture.Width + 50;
myForm.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
myForm.ShowDialog();
}
}
March 29, 2013
A Bit of Terminology
A reader was having difficulty compiling source code from a previous post, so I thought I’d post on some basics to explain a few terms and requirements about Revit macros:
A macro is an individual command that can be run in Revit. Here is the source and output from the simplest macro
A module is a collection of macros. To create a macro you must first create a module that will contain the macro. The macros in a module do not need to have any functional relationship. They can do completely different things and do not need to interact with each other in any way. You can put 1 macro in each module or 100 macros in each module. If you want to create utility functions (maybe to convert feet to meters or radians to degrees) then those functions need to be in the same module as the macros that use them. Here are a few restrictions:
- You cannot have two macros in the same module with the same name
- All macros in the same module must compile without errors for you to run any of the macros in that module
- Each module opens in its own session of SharpDevelop
Module_Startup and Module_Shutdown are special pieces of code that are automatically created by Revit when you create a new module. Their purpose is to give you a way to automatically run code when Revit starts and just before Revit exits. Module_Startup code is also executed every time you compile the module. By default they are empty and have no code that they run, and in most cases this is fine.
Each module must have one and only one instance of Module_Startup and Module_Shutdown. If you delete them you will get errors like:
The name 'Module_Startup' does not exist in the current context
If you have them more than once (or any other macro more than once) you will get errors like:
Type 'BoostYourBIM1.ThisApplication' already defines a member called 'Module_Startup' with the same parameter types
When a sample on this blog requires Module_Startup code (like http://boostyourbim.wordpress.com/2013/02/14/set-adaptive-component-parameter-values-with-placement-point-coordinates/where it is used to register Dynamic Model Update triggers) I will include the startup code in the blog post. You can copy that startup code into your existing startup method, or replace your existing startup method with this new one. But if you paste the new Module_Startup code and leave your existing Module_Startup code that will cause the error that there is already a Module_Startup in that module.
March 28, 2013
How to toggle the visibility of Automatic Sketch Dimensions
I was asked if the API could be used to make it easier to toggle the visibility of Temporary Sketch Dimensions. This macro will toggle the state of this subcategory for one or all views in the project.
In future post I will examine how this can be made easier to access by adding the command to the ribbon so it can be invoked with a keyboard shortcut instead of having to access it through the Macros dialog.
public void ToggleAutoSketchDimensionVisibility()
{
UIDocument uidoc = this.ActiveUIDocument;
Document doc = uidoc.Document;
// use the following line to set the visibility for only the active view
// use the FilteredElementCollector and foreach loop to set it for all views
// View view = doc.ActiveView;
Category autoSketchDimCat = doc.Settings.Categories.get_Item(BuiltInCategory.OST_Dimensions).SubCategories.get_Item("Automatic Sketch Dimensions");
using (Transaction t = new Transaction(doc,"Toggle 'Automatic Sketch Dimensions' Visibility"))
{
t.Start();
foreach (View view in new FilteredElementCollector(doc).OfClass(typeof(View)).Cast<View>())
{
// set the visibility of this category to the opposite of its current value (if it is True, set it to False, etc)
view.SetVisibility(autoSketchDimCat, !autoSketchDimCat.get_Visible(view));
}
t.Commit();
}
}
March 27, 2013
GetCellText – Contents of Schedule Cells in 2014
In 2013 we could read the contents of a schedule through a bit of a workaround – export the schedule to a text file with ViewSchedule.Export and read the file back in with StreamReader.
Now in 2014 we can read the cell values directly with ViewSchedule.GetCellText as shown below with a macro that reads the schedule and shows the data in a TaskDialog.
publicvoid Schedule2014GetCellText()
{
UIDocument uidoc = this.ActiveUIDocument;
Document doc = uidoc.Document;
ViewSchedule viewSchedule = doc.ActiveView as ViewSchedule;
SectionType sectionType = SectionType.Body;
string data = "";
for (int i = 0; i < getRowColumnCount(viewSchedule, sectionType, true); i++)
{
for (int j = 0; j < getRowColumnCount(viewSchedule, sectionType, false); j++)
{
data += viewSchedule.GetCellText(sectionType, i, j) + ",";
}
// remove the trailing "," after the last cell and add a newline for the end of this row
data = data.TrimEnd(',') + "\n";
}
TaskDialog.Show(viewSchedule.Name, data);
}
private int getRowColumnCount(ViewSchedule view, SectionType sectionType, bool countRows)
{
int ctr = 1;
// loop over the columns of the schedule
while (true)
{
try // GetCellText will throw an ArgumentException is the cell does not exist
{
if (countRows)
view.GetCellText(sectionType, ctr, 1);
else
view.GetCellText(sectionType, 1, ctr);
ctr++;
}
catch (Autodesk.Revit.Exceptions.ArgumentException)
{
return ctr;
}
}
}
March 26, 2013
What you can do with View Filters in the 2014 API
Here’s are the new Filter items for the View class in 2014:
- AddFilter - The filter will be added with default overrides, so there will be no change in the view’s display.
- GetFilterOverrides – Returns all graphic overrides of a specific filter in the view
- GetFilters – Returns the ids of the filters applied to the view
- GetFilterVisibility – Returns true if the elements associated with a specific filter are visible in the view
- IsFilterApplied – Returns true if a specific filter is applied to the view
- RemoveFilter
- SetFilterOverrides – Set the OverrideGraphicSettings for a filter in a view. Adds the filter to the view if it hadn’t been previously added.
- SetFilterVisibility – Set the visibility of a specific filter (true / false) for a view. Adds the filter to the view if it hadn’t been previously added.
public void Filter2014Sample()
{
UIDocument uidoc = this.ActiveUIDocument;
Document doc = uidoc.Document;
View view = doc.ActiveView;
TaskDialog.Show("Boost Your BIM","# of filters applied to this view = " + view.GetFilters().Count);
// create list of categories that will for the filter
IList<ElementId> categories = new List<ElementId>();
categories.Add(new ElementId(BuiltInCategory.OST_Walls));
// create a list of rules for the filter
IList<FilterRule> rules = new List<FilterRule>();
// This filter will have a single rule that the wall type width must be less than 1/2 foot
Parameter wallWidth = new FilteredElementCollector(doc).OfClass(typeof(WallType)).FirstElement().get_Parameter(BuiltInParameter.WALL_ATTR_WIDTH_PARAM);
rules.Add(ParameterFilterRuleFactory.CreateLessRule(wallWidth.Id, 0.5, 0.001));
ParameterFilterElement filter = null;
using (Transaction t = new Transaction(doc, "Create and Apply Filter"))
{
t.Start();
filter = ParameterFilterElement.Create(doc, "Thin Wall Filter", categories, rules);
view.AddFilter(filter.Id);
t.Commit();
}
string filterNames = "";
foreach (ElementId id in view.GetFilters())
{
filterNames += doc.GetElement(id).Name + "\n";
}
TaskDialog.Show("Boost Your BIM","Filters applied to this view: " + filterNames);
// Create a new OverrideGraphicSettings object and specify cut line color and cut line weight
OverrideGraphicSettings ogs = new OverrideGraphicSettings();
ogs.SetCutLineColor(new Color(255,0,0));
ogs.SetCutLineWeight(9);
using (Transaction t = new Transaction(doc, "Set Override Appearance"))
{
t.Start();
view.SetFilterOverrides(filter.Id,ogs);
t.Commit();
}
}
Revit 2014 API is going to Blow Your Faces Out!
public void explodeMe()
{
UIDocument uidoc = this.ActiveUIDocument;
Document doc = uidoc.Document;
// Create filter for all walls and roofs (RoofBase is the base class for the ExtrusionRoof & FootprintRoof classes
ElementClassFilter wallFilter = new ElementClassFilter(typeof(Wall),false);
ElementClassFilter roofFilter = new ElementClassFilter(typeof(RoofBase),false);
LogicalOrFilter orFilter = new LogicalOrFilter(wallFilter, roofFilter);
// explode each element 25 feet from its real location
double displacementDistance = 25;
using (Transaction t = new Transaction(doc, "Explode"))
{
t.Start();
foreach (Element e in new FilteredElementCollector(doc).WherePasses(orFilter))
{
IList<ElementId> idsToDisplace = new List<ElementId>();
idsToDisplace.Add(e.Id);
// explode will offset each element in the direction normal to one of its "major" faces (top face for roof, side face for wall)
Face face = null;
HostObject hostObj = e as HostObject;
if (hostObj is Wall)
{
Wall w = hostObj as Wall;
// only explode the exterior walls
if (!w.WallType.Name.Contains("Exterior"))
continue;
face = hostObj.GetGeometryObjectFromReference(HostObjectUtils.GetSideFaces(hostObj, ShellLayerType.Exterior).First()) as Face;
}
else if (hostObj is RoofBase)
face = hostObj.GetGeometryObjectFromReference(HostObjectUtils.GetTopFaces(hostObj).First()) as Face;
// Find the face normal at the center of the face
BoundingBoxUV bbox = face.GetBoundingBox();
// center of the face in the UV of the face
UV center = new UV((bbox.Max.U - bbox.Min.U)/2 + bbox.Min.U, (bbox.Max.V - bbox.Min.V)/2 + bbox.Min.V);
XYZ faceNormal = face.ComputeNormal(center);
// inserts are not automatically exploded with their hosts, so find them here and add them to the list
foreach (FamilyInstance fi in new FilteredElementCollector(doc).OfClass(typeof(FamilyInstance)).Cast<FamilyInstance>().Where(f => f.Host != null && f.Host.Id == hostObj.Id))
{
idsToDisplace.Add(fi.Id);
}
DisplacementElement displacementElement = DisplacementElement.Create(doc, idsToDisplace, faceNormal.Multiply(displacementDistance), doc.ActiveView, null);
// Create a Displacement Path annotation connecting the displaced element with its true location
Options options = new Options();
options.ComputeReferences = true;
Reference edgeRef = null;
// get a reference to any edge in the displaced element
foreach (GeometryObject gobj in hostObj.get_Geometry(options))
{
if (gobj is Solid)
{
Solid solid = gobj as Solid;
Edge edge = solid.Faces.get_Item(0).EdgeLoops.get_Item(0).get_Item(0);
edgeRef = edge.Reference;
break;
}
}
ElementId pathId = DisplacementPath.Create(doc, displacementElement, edgeRef, 0.5);
}
t.Commit();
}
}
What are the most interesting 2014 API topics?
Here’s an overview of the big new areas of the 2014 API. If you leave some comments about what topics you’d like to learn more about, I will do some posts on those topics.
- Worksharing – reload latest, synchronize with central, editability of elements and worksets, enabling worksharing
- RVT Links – selecting elements in links, creating rooms in links, loading, unloading, and more
- Views – category overrides, filters, non-rectangular crop regions, zoom level control,
- Views on sheets – rotation, better positioning, revisions
- Exploded views
- Macros – API to run, edit, and create macros
- Import – SAT, SketchUp, DWF files, export to Navis
- Site – API for toposurface, regions, and building pad
- Paint tool API
- Component repeater API
- Copy/Paste API
- Running a Revit UI command after an API command completes
- Project Browser interaction with the API
- Units – formatting, parsing, and converting
- Schedules – appearance and formatting
March 20, 2013
Using the API to resolve “identical instances in same place” warnings
The API can be used to parse a warnings HTML file exported from Revit and then delete the “less desirable” instance in each pair of instances in the same place.
The instance that gets deleted is the one
- without a tag (if one instance has a tag)
- with less instance parameter data
March 11, 2013
New App Availabe for download: In-N-Out Family Parameters
| Are you creating or modifying lots of Revit Family content?
Or are you quality checking content made by someone else? Or just looking for an easy way to review or modify type parameter data from many families at once? Then check out the “In-N-Out Family Parameters” tool which is now available for download! |




