lisa-marie mueller

lisa-marie mueller

bullet pont

coordinate system utilities link

December 06, 2019

Laptop with code on screen, film camera, floppydisk, pink I hope everyone had a wonderful Thanksgiving if you celebrate or just had a great end to November. This week we continue on the path to automating interior elevations. As a reminder, I have been building a plug-in that automates various parts of creating interior elevations, and I will continue to develop it. Since this project will evolve as I continue, I have determined to set my primary goal for each stage. With this goal in mind, I will develop a functioning piece of software to meet the goal. Then I will discuss topics that cover key aspects for each step for building the plug-in. At the end of each stage, I will move the plug-in to a public repository on GitHub so you can use it too.

if you missed it:

Part 1: filtered element collector [c#]

Part 2: finding centroids and considering exceptions

Part 3: ViewFamilyTypeId

Part 4: ViewPlanId and Levels

Part 5: phases & goal #1 complete [includes GitHub link to the release]

Part 6: view tempaltes

Part 7: resizing CropBoxes

Part 8: creating FilledRegions & Goal #2 Complete [includes GitHub link to release]

primary goal #3

Rename the interior elevations based on the room name, add a suffix based on the cardinal directions to identify the elevations

overview

To rename the interior elevations, we first need to find the cardinal direction that the elevation marker is facing. To make this easier, we have a handful of methods that I added to a Utility class so we can reference them.

radians and degrees

First, we want to set up a couple of simple conversions and write a method to convert radians to degrees and vice versa.

GetDocNorthOffset

We also need to find the offset of the project north from true north. To do this, we first need to convert the ProjectLocationSet type to an IEnumerable of ProjectLocation so that it is a more standard type. Then we can filter for the project location with the name “Internal”. To get the offset, we find the angle between true north and project north and utilize our Radians2Degrees method to ensure it is returned in degrees. We will use this method in the Angle2Cardinal method described below.

GetVectorOffset

This method is very generic and simply gets the angle of a vector and returns this angle in degrees.

Angle2Cardinal

Since multiple views cannot have the same name, a great way to distinguish interior elevations is to incorporate a suffix indicating the direction of the interior elevation. To do this, we need to find the document’s north offset by using the GetDocNorthOffset method described above. It is possible for this angle to come back as a number greater than 360. Because the directions rotate around a circle, we can keep adding or subtracting 360 until the angle falls between 0 and 360 degrees. This makes assigning the suffix easier. After correcting the angle, we can assign suffixes based on true north. For example, if the angle is between 0 and 45 degrees or 315 degrees and 360 degrees, the suffix is East. We repeat this for each option. Then we can utilize this method next week to assist in renaming our views.

And that sets up a number of Utility methods that we can then use to rename our interior elevations. Next week, we will tackle renaming our views and complete the final step in automating our interior elevations.

resources

If you want to learn to code and don’t know where to start check out my posts about Steps to Learn to Code [for architects and designers] Part 1 and Part 2.

Revit API Docs

bullet pont

creating FilledRegions & Goal #2 Complete link

November 22, 2019

Laptop with code on screen, globe, game controller, green This week we continue on the path to automating interior elevations. As a reminder, I have been building a plug-in that automates various parts of creating interior elevations, and I will continue to develop it. Since this project will evolve as I continue, I have determined to set my primary goal for each stage. With this goal in mind, I will develop a functioning piece of software to meet the goal. Then I will discuss topics that cover key aspects for each step for building the plug-in. At the end of each stage, I will move the plug-in to a public repository on GitHub so you can use it too.

if you missed it:

Part 1: filtered element collector [c#]

Part 2: finding centroids and considering exceptions

Part 3: ViewFamilyTypeId

Part 4: ViewPlanId and Levels

Part 5: phases & goal #1 complete [includes GitHub link to the release]

Part 6: view tempaltes

Part 7: resizing CropBoxes

primary goal #2

Override the view’s parameters to set a view template, update the extents of the views, create a masking region, override the inside line weight of the masking region

overview

Since we resized the CropBox, we now want to place our masking region. The reason a masking region can be helpful is that when you want to have the border of the interior elevation follow along door openings and changes in ceilings and soffits, it can be easier to do it with a masking region. It also makes it easier to adjust the line weight around the elevation rather than having to worry about overriding the display graphics of the interior elevation crop every time.

FilledRegion boundaries

Because we are using the filled regions to mask the parts around our view that are beyond the room, we need to create a donut-shaped filled region. Anything in the middle is visible and anything beyond that will be masked. For this, we need inside and outside boundaries.

We create a new public method that returns a list of CurveLoops which we will use to create our FilledRegion. We call it FilledRegionBoundary and it needs a ViewSection parameter which will be our interior elevation view. If you remember from Part 7, when we set the interior elevation view CropBox, we set it so that it was 1’-0” beyond the room in width and height. So when we get the bounds of the interior elevation CropBox, they will be 1’-0” beyond the room. This is why, for the inside boundary, we need to add 1 to the minimum value and subtract 1 from the maximum value. This will put the boundary back to the edge of the room. Also similar to Part 7, we have to perform some more transformations. I have to give a shoutout to my husband who helped me with the inverse matrices. If you remember, when you get the coordinates of the CropBox of the elevations view it uses the coordinate system of the view. However, to create our filled region, we need to work with the project coordinates. To to do this, we actually have to use an inverse matrix to reverse the translations and rotations back to the project coordinate system. I added these methods to a utility file for matrices. You can get access to it through my GitHub account. After we find the correct coordinates, we simply draw lines between them and add those lines to a list.

Since we want the outside curve to be set back from the CropBox a bit, we can take the CropBox coordinates and set them back 6”. Then we transform the coordinates with the inverse matrix as we did for the interior boundary. After we find the correct coordinates, we simply draw lines between them and add those lines to a list just like above.

Finally, we create a CurveLoop for both the inside and outside curves. We add both to a list of CurveLoops which we return to use to create our FilledRegion.

create FilledRegions

Now that we have the curves for the boundaries of our filled region, we just have to create the filled region. We create a new public method that does not return anything. It takes in our document, the interior elevation ViewSection, and the room’s BoundingBoxXYZ as parameters. The first thing we do is call our filledRegionBoundaries method to get our boundaries. Then we need to get the element Id of the type of filled region we want to create. Since we are using it to mask our view, we want the fill pattern to be white (RGB 255, 255, 255). For Revit 2019 and above, filled regions that are opaque have the IsMasking property set to true. You can also change the filters below or make them more specific if you need to. This code block then returns the TypeId of the filled region we need. We can then use this along with our interior elevation view’s Id and our boundaries to create our filled region. Finally, we override the line thickness to provide us with a thicker outline. For the code below, I just filtered for a line that contained “05” and “solid” in the name. It is more complex to filter for line style properties like the thickness and pattern. You may need to adjust the filter for the name based on how you name your lines.

add to Execute

Our final step is to add the methods we’ve created in Goal #2 to the Execute method. We circle through each elevation, overwrite the CropBox, create the filled regions, and adjust the line weight of the filled regions. This means that we have completed Goal #2. You can access the updated code on my GitHub account. There you can also download the DLL so you can use the updated plug-in too. Have fun!

Please note that since we have now added even more filters and hard-coded selections, there are a couple of things you need to use this plug-in as written. Your Revit file needs:

  • to have all the rooms placed for which you want to create the interior elevations
  • an elevation ViewFamily type with a name that contains “interior”
  • a ViewPlan with a name that contains “interior elevations”
  • a ViewTemplate with a name that contains “interior” and “elevation”
  • a FilledRegionType with the BackgroundPatternColor set to white (RGB 255, 255, 255)
  • a LineStyle with a name that contains “05” and “solid”

I’m taking a break next week because of the holidays, but check back December 6 for the next post in the series. That week, we set our 3rd and final goal so that everyone can enjoy automated interior elevations by the end of the year. Have a great turkey day!

code summary



resources

If you want to learn to code and don’t know where to start check out my posts about Steps to Learn to Code [for architects and designers] Part 1 and Part 2.

Revit API Docs

bullet pont

resizing CropBoxes link

November 15, 2019

Laptop with code on screen, globe, game controller, blue This week we continue on the path to automating interior elevations. As a reminder, I have been building a plug-in that automates various parts of creating interior elevations, and I will continue to develop it. Since this project will evolve as I continue, I have determined to set my primary goal for each stage. With this goal in mind, I will develop a functioning piece of software to meet the goal. Then I will discuss topics that cover key aspects for each step for building the plug-in. At the end of each stage, I will move the plug-in to a public repository on GitHub so you can use it too.

if you missed it:

Part 1: filtered element collector [c#]

Part 2: finding centroids and considering exceptions

Part 3: ViewFamilyTypeId

Part 4: ViewPlanId and Levels

Part 5: phases & goal #1 complete [includes GitHub link to the release]

Part 6: view tempaltes

primary goal #2

Override the view’s parameters to set a view template, update the extents of the views, create a masking region, override the inside line weight of the masking region

CropBox

I have to admit I had a lot of fun with this one. I was able to recollect linear algebra which I have not used in many years. This week, we will create a method that adjusts the CropBox of the interior elevation views. You can use this for two things. First, to get rid of that pesky problem of having a small crop height on interior elevation views depending on your floors and levels (also discussed on the forums). Second, if you use masking regions over your interior elevation views to add a thick crop boundary and to make it easier to adjust the boundary around doors, other openings, and angled ceilings, we can set the crop of the interior elevation one foot beyond the room so the masking region shows. 

coordinate systems

coodinate sytem of elevations facing opposite directions While working on this part, I came across the fact that the coordinate system used for the view and the coordinate system used for the rooms is not the same. When you get the CropBox from a view, it uses the coordinate system of that view. However, when you overwrite the CropBox of the view, it uses the room’s (which is also usually the same as the project’s) coordinates. Additionally, in Revit the positive Z direction is always towards the user. So for example in plan view, Z is the direction that would be “up” in the real world, the height of your building. However, in elevations “up” is the Y coordinate. This means that the Y of the elevation maps to the Z of the room. But the X of the elevation varies and will be either the positive or negative X or the positive or negative Y of the room based on which elevation it is. This is because as you rotate around the elevation marker, Z stays towards the viewer, but the viewer is changing directions. This is why we need to use linear algebra. Thankfully, by using a linear transform, we can easily solve the problem.

transform

The other background information I wanted to touch on was that the Transform member in the Revit API was not working as expected for me. There is a Transform property for all BoundingBoxXYZs, in this case the CropBox of our view. When I attempted to use it to adjust the coordinates, it did not perform a linear transformation. So, I just set up my own matrices and calculated it out manually as you can see below.

We start by making our public method which will return a BoundingBoxXYZ (we will use this in Part 8, next week) and we will call it SetCropBox. It needs a ViewSection parameter which is our interior elevations views, and a Room parameter. We start by getting the bounds of the interior elevation CropBox and the bounds of the room’s CropBox. Then we set up our transform matrix with the basis coordinates. After we have that set up, we can do all the math and get out our transformed minimum and maximum values. Teaching everyone linear algebra would be a bit more than we have time for right now, so I recommend you check out some youtube videos if you do not remember transformations. Then we can create extended minimum and maximum bounds from our transformed coordinates. If you do not want to extend the bounds beyond the room boundary, simply keep the transformed coordinates.

box is empty

When I tried to assign the CropBox back to the interior elevation view, the assignment threw an InvalidArgumentException. The message was “Box is empty” in Revit. Looking into it, the exception happened when I was trying to set the larger coordinate value as the minimum and the smaller coordinate value as the maximum. To make sure this doesn’t happen, we added the following code to check if the minimum X and minimum Y values are the smaller values. If not, we switch the minimum and maximum.

Now we can just set our minimum and maximum values, set the bounds, and overwrite our elevation view’s CropBox. Next week, we’ll play around with filled regions.

code summary

resources

If you want to learn to code and don’t know where to start check out my posts about Steps to Learn to Code [for architects and designers] Part 1 and Part 2.

Revit API Docs

bullet pont

view templates link

November 8, 2019

Laptop with code on screen, This week we continue on the path to automating interior elevations. As a reminder, I have been building a plug-in that automates various parts of creating interior elevations, and I will continue to develop it. Since this project will evolve as I continue, I have determined to set my primary goal for each stage. With this goal in mind, I will develop a functioning piece of software to meet the goal. Then I will discuss topics that cover key aspects for each step for building the plug-in. At the end of each stage, I will move the plug-in to a public repository on GitHub so you can use it too.

if you missed it:

Part 1: filtered element collector [c#]

Part 2: finding centroids and considering exceptions

Part 3: ViewFamilyTypeId

Part 4: ViewPlanId and Levels

Part 5: phases & goal #1 complete [includes GitHub link to the release]

primary goal #2

Override the view’s parameters to set a view template, update the extents of the views, create a masking region, override the inside line weight of the masking region

view template

We now have a functioning plug-in that places our interior elevations, but it would be great to automate some additional tedious tasks like applying our masking region, double-checking the extents, and setting some standard Revit parameters. This week we will look at setting a view template for our interior elevation views.

Interior elevations have a ViewTemplateId that we can simply set after we find the view template we want to use. To find it we can use a FilteredElementCollector. One thing I discovered while putting this together is that although in Revit, the view templates are listed under categories (plan view templates are under a different category drop down then elevations) this has no relationship to anything that I could find in the Revit API, they all seem to be in one large bucket. This is why we filtered by name only. We set the filter to look for view templates that contain “interior” and “elevation” in the name. Depending on what you have named your view templates, you will need to adjust these filters to match the name you chose.

And that’s it. Pretty straight forward to set a view template. This same concept can also be applied to other Revit parameters of the view. If you categorize your views, for example, you can assign the correct category for interior elevations when they are created. Next week, we will update view extents to match the extents of the room and get rid of that pesky problem of having a small crop height on interior elevation views depending on your floors and levels (also discussed on the forums)

resources

If you want to learn to code and don’t know where to start check out my posts about Steps to Learn to Code [for architects and designers] Part 1 and Part 2.

Revit API Docs

bullet pont

phases and goal 1 complete link

November 1, 2019

Laptop with code on screen, toy bird, toy rocket orange First of all, I hope everyone had a fun and safe halloween! This week we continue on the path to automating interior elevations. As a reminder, I have been building a plug-in that automates various parts of creating interior elevations, and I will continue to develop it. Since this project will evolve as I continue, I have determined to set my primary goal for each stage. With this goal in mind, I will develop a functioning piece of software to meet the goal. Then I will discuss topics that cover key aspects for each step for building the plug-in. At the end of each stage, I will move the plug-in to a public repository on GitHub so you can use it too.

if you missed it:

Part 1: filtered element collector [c#]

Part 2: finding centroids and considering exceptions

Part 3: ViewFamilyTypeId

Part 4: ViewPlanId and Levels

primary goal #1

Create interior elevations for all placed rooms, place the tag at the center of the rooms, place the tag on the correct level when multiple levels exist, ensure the tag is visible on the interior elevation key plans (already in the project), and ensure the interior elevation tags have the proper phase settings.

phases

As I was working on this plug-in, I realized that the interior elevation maker and the interior elevation views are not necessarily created in the same phase as the plan that you use to create them (Part 4). Because of this, I added a few simple steps to find the phase of the view, and set the interior elevation marker and views to this same phase. We can make this adjustment in the PlaceElevations method.

execution

This completes all the elements of our Goal #1, it’s time to put it together. We need to write our Execute method. This will be a public method that outputs a Result. It takes ExternalCommandData, a ref string, and an ElementSet as input parameters.

We set up our initial variables for use in the entire program. Next, we use a try-catch block so that our plug-in does not crash Revit if it fails and so that we can output proper error messages. We use a FilteredElementCollector to filter all of our rooms (Part 1) and then call our method that collects the document’s PlanViews (Part 4). With this information gathered, we can circle through each room in the project, get the center of the room (Part 2), find the PlanView that the room is visible in (Part 4), and place our elevations (Part 3 & Part 4).

We have now accomplished Primary Goal #1 and have a program that places our interior elevations in all of our rooms. It considers projects with multiple levels, and ensures the elevations are in the correct phase. You can take a look at all of the code on my GitHub account. There you can also download the DLL from GitHub so you can use the plug-in too. Enjoy!

Next week we set Goal #2 and jump in to updating some of the parameter properties of the interior elevations.

code summary

resources

If you want to learn to code and don’t know where to start check out my posts about Steps to Learn to Code [for architects and designers] Part 1 and Part 2.

Revit API Docs

bullet pont

ViewPlanId and levels link

October 25, 2019

Laptop with code on screen, toy bird, toy rocket green This week we continue on the path to automating interior elevations. As a reminder, I have been building a plug-in that automates various parts of creating interior elevations, and I will continue to develop it. Since this project will evolve as I continue, I have determined to set my primary goal for each stage. With this goal in mind, I will develop a functioning piece of software to meet the goal. Then I will discuss topics that cover key aspects for each step for building the plug-in. At the end of each stage, I will move the plug-in to a public repository on GitHub so you can use it too.

if you missed it:

Part 1: filtered element collector [c#]

Part 2: finding centroids and considering exceptions

Part 3: ViewFamilyTypeId

primary goal #1

Create interior elevations for all placed rooms, place the tag at the center of the rooms, place the tag on the correct level when multiple levels exist, ensure the tag is visible on the interior elevation key plans (already in the project), and ensure the interior elevation tags have the proper phase settings

DocumentElevPlanViews

In order to place a ViewSection onto our ElevationMarker we need three parameters:

  • Document parameter - the document in which the ElevationMarker will be placed
  • ElementId parameter - ViewPlanId is the ViewPlan in which the ElevationMarker is visible, the ViewSection will inherit information from the ViewPlan
  • Integer parameter - to determine the index of the ElevationMarker at which the new view will be placed

Our document is just a variable that is passed through as an input. Our Integer parameter for the index will be a simple counter within a “for loop” so we don't need to use a method for its retrieval. This means we need to find the ViewPlanId. We start a new public method that returns a list of ViewPlans, and we call the list DocumentElevPlanViews. The only parameter it needs is the document input. This is where you need to evaluate how your project is set up. If you keep interior elevations visible in all plans, you can simplify the filtering of the FilteredElementCollector and just take the first plan it finds. However, some people have a plan just for interior elevation markers and keep them hidden in the rest of the plans. In this case, we filter down to plans that contain the name “interior elevations”. You need to make sure your plan names contain these words or adjust the string to accommodate your plan names.

So we have a new FilteredElementCollector and sort it down to the ViewPlans that contain the string “interior elevations”. Like always, if it returns null, we need to throw an exception.

levels

One thing you may realize is that what we have done so far works great if we only have a one story building, but if we have more than one story, our DocumentElevPlanViews list will contain multiple plans and the CreateElevation method requires just one ViewPlan. For multiple levels, we need to check which ViewPlan the Room is visible on. We create a new public method that returns a ViewPlan and call it GetViewPlanOfRooms. Since we are looping through rooms to place our elevations, we will do the same here. We will loop through each room and check that room against all the ViewPlan elements in our list. Once we find the ViewPlan that it is in, we will return that ViewPlan. If you have hundreds of levels with hundreds of plans in your model, this will take a while and you may want to consider optimizing this method, but for most buildings it will run fast enough.

Now we can add to the PlaceElevations method we started in Part3 and place the elevations. We have a simple “for loop” to start at index 0 and then increase to the remaining indices until we get to the MaximumViewCount for the ElevationMarker.

We almost have a functioning plug-in. Next week, we complete the final task of ensuring the interior elevation tags have the proper phase settings. Then we can write the method that will execute our program and we are finished with Goal #1.

code summary

resources

If you want to learn to code and don’t know where to start check out my posts about Steps to Learn to Code [for architects and designers] Part 1 and Part 2.

Revit API Docs

bullet pont

ViewFamilyTypeId link

October 18, 2019

Laptop with code on screen, toy bird, toy rocket This week we continue on the path to automating interior elevations. As a reminder, I have been building a plug-in that automates various parts of creating interior elevations, and I will continue to develop it. Since this project will evolve as I continue, I have determined to set my primary goal for each stage. With this goal in mind, I will develop a functioning piece of software to meet the goal. Then I will discuss topics that cover key aspects for each step for building the plug-in. At the end of each stage, I will move the plug-in to a public repository on GitHub so you can use it too.

if you missed it:

Part 1: filtered element collector [c#]

Part 2: finding centroids and considering exceptions

primary goal #1

Create interior elevations for all placed rooms, place the tag at the center of the rooms, place the tag on the correct level when multiple levels exist, ensure the tag is visible on the interior elevation key plans (already in the project), and ensure the interior elevation tags have the proper phase settings

FindFamilyTypeId

To place an ElevationMarker we need four parameters:

  • Document parameter - the document in which the ElevationMarker will be placed
  • ElementId parameter - ViewFamilyTypeId sets the type of view that is being created
  • XYZ parameter - coordinate to place the elevation
  • Integer parameter - the scale of the view

The document is just a variable that we will declare, we already found our XYZ parameter in Part 2, and for the initial build, I will hard-code the scale. That leaves us to find the ElementId parameter which needs to be the ViewFamilyTypeId used for the elevations that are hosted on the ElevationMarker.

You will need an interior elevation View Type within the Revit project you are working with. I recommend you set this up already in your template. Check the Autodesk Knowledge Network for instructions on how to create a new View Type. To gather our ViewFamilyTypeId, we can use our favorite, a FilteredElementCollector. We need to filter for ViewFamilyTypes and of those, we want the elevations where the name contains “interior”. If it returns null, we need to throw an exception so the program doesn’t crash and our user knows the error that occurred.

Now we can start putting together our method that will place our ElevationMarkers. We are not returning anything so we want a public void method. We will call it PlaceElevations and it will need a few parameters Document doc, XYZ center [which we found in Part 2], and a ViewPlan [which we will discuss next week]. We use the CreateElevationMarker method in the ElevationMarker class using the paramters we have now defined. This method returns a new object of type ElevationMarker.

resources

If you want to learn to code and don’t know where to start check out my posts about Steps to Learn to Code [for architects and designers] Part 1 and Part 2.

Revit API Docs

bullet pont

finding centroids & considering exceptions link

October 11, 2019

Laptop with code on screen, toy bird, toy rocket blue This week we continue on the path to automating interior elevations. As a reminder, I have been building a plug-in that automates various parts of creating interior elevations, and I will continue to develop it. Since this project will evolve as I continue, I have determined to set my primary goal for each stage. With this goal in mind, I will develop a functioning piece of software to meet the goal. Then I will discuss topics that cover key aspects for each step for building the plug-in. At the end of each stage, I will move the plug-in to a public repository on GitHub so you can use it too.

if you missed it:

Part 1: filtered element collector [c#]

primary goal #1

Create interior elevations for all placed rooms, place the tag at the center of the rooms, place the tag on the correct level when multiple levels exist, ensure the tag is visible on the interior elevation key plans (already in the project), and ensure the interior elevation tags have the proper phase settings

GetCenter

Today, we will write the method that finds the center of all of our rooms. We need to find the center because to place an ElevationMarker using the CreateElevationMarker method, we need an XYZ parameter (known to most of us as a coordinate). I have chosen the center of the room for this purpose.

The new public method needs to return an XYZ parameter, and we can call it GetCenter. The new method accepts one parameter, the list of rooms that we found in Part 1. Let’s find the center! Finding the center of the rooms would be perfectly fine if every room in all of our projects were rectangular. The truth is, they are probably not. If we think back to math class, what we are actually finding is the centroid. Thankfully, finding the centroid has a very simple formula, average the coordinates of the corners for a 2D shape.

From reviewing our math skills, we have determined that we need to get the corners of the room. First, we need to collect the BoundarySegments of each room and then we can get the start and end points of each BoundarySegment. When using the GetBoundarySegments method, the default value of the SpatialElementBoundaryLocation Enum is the finish surface boundary of the wall, for our case this is fine because we are just placing a tag. I specifically defined this parameter in the code so others looking at it would remember that the method is using the BoundarySegments at the finish surface location, especially if they aren’t as familiar with the Revit API.

Another item we need to remember from using Revit is that rooms can exist in your project even if they are not placed or are not bound. This is why we want to incorporate an “if statement” so that we only find the end points of the segments of the rooms that are bound, ei when the segments are not equal to null. If our program tries to find the endpoints of segments that are null, it will crash. Once we collect our endpoints, we can find the average for the X-, Y-, and Z-axis and create a new XYZ variable to store this coordinate. Thankfully, LINQ has a method for averages that we can use. You may have realized that we are double counting all the corners of the room since each segment overlaps endpoints with the adjacent segments. Because we are finding the average and each end point is counted twice, it does not affect our calculation.

exceptions

If you think about a few possible common shapes for rooms, one that comes to mind is an “L” shaped room. Depending on the proportions, it is possible for the centroid to end up on the edge of the room or very close to the edge. If the coordinates we use to place our ElevationMarker are outside of the room, our program will crash so we need to write in a few “if statements” to confirm the centroid is in the room, and if not we need to throw an exception. For our case, I choose to first check if the center we found is in the room using the IsPointInRoom method. If it is, perfect. If not, we move the coordinate one unit in the positive direction of the X-axis, and then check if it is now in the room. We repeat for the negative direction of the X-axis as well as the positive and negative directions of the Y-axis. This covers all our bases because a centroid will never fall outside of our room boundary, regardless of the shape. However, it is possible for the centroid to fall on or near the boundary line which Revit may not be happy with. If something went wrong and the centroid is still outside of the room, it will throw an exception so the user knows what happened.

When discussing “L” shaped rooms, we should also consider that they fall into a special category. We need to place two elevation markers to show all walls. For our Primary Goal #1, this aspect is too detailed. Software development is an iterative process and it is better to finish a functioning plug-in that covers 75-80% of your use cases, then to get caught up in every “what if” situation and never finish. For now, we will put this on the wishlist of features to add in the future.

And there we have it. We just found the centroid of all of our rooms. This is one method that can be used for many different applications, so I will likely pull it out into a utility class. [edited 10/16] Next week, we will take a look at the ViewFamilyTypeId which is the last parameter we need to place the ElevationMarker.

summary of code

resources

If you want to learn to code and don’t know where to start check out my posts about Steps to Learn to Code [for architects and designers] Part 1 and Part 2.

Revit API Docs

bullet pont

filtered element collector [c#] link

October 4, 2019

Laptop with code on screen, toy bird, toy rocket Whenever I start a new set of Construction Documents in Revit, there are certain tasks that I -and I’m sure many of my colleges and even many of you- despise. This is how my ‘Automation Wish List’ has developed. Every time I do something I dislike doing, I put it on the list. I have chosen one of these topics to cover first, interior elevations. Since I work primarily in the Civic sector we work on the interior design for our projects, all of our rooms have interior elevations, and the interior elevations often get quite detailed. I have been building a plug-in that automates various parts of creating interior elevations, and I will continue to develop it. Since this project will evolve as I continue, I have determined to set my primary goal for each stage. With this goal in mind, I will develop a functioning piece of software to meet the goal. Then I will discuss topics that cover key aspects for each step for building the plug-in. At the end of each stage, I will move the plug-in to a public repository on GitHub so you can use it too.

primary goal #1

Create interior elevations for all placed rooms, place the tag at the center of the rooms, place the tag on the correct level when multiple levels exist, ensure the tag is visible on the interior elevation key plans (already in the project), and ensure the interior elevation tags have the proper phase settings

getting started

To start, we need to collect all the rooms in the project. This is how we will find the center of the room and place the tags. To find the rooms, we will use what I have determined is, for myself, the most important and most used building block of the Revit API. I would describe the Filtered Element Collector class as a queryable collection of Revit objects. If you are new to programming, it takes all the elements in your Revit model and puts them in a bag. Then it allows you to filter out elements you do not want based on parameters you provide. After you filter, you have the elements you wanted left in the bag. In programming, you actually have to keep those elements somewhere and this will generally be a list or a single object, depending on what you need. For our case, we can very quickly collect all the rooms and store them in a list.

Next week, we will find the center of the rooms and take a look at some exceptions we need to consider so our program doesn't crash.

resources

If you want to learn to code and don’t know where to start check out my posts about Steps to Learn to Code [for architects and designers] Part 1 and Part 2.

Revit API Docs

bullet pont

back from break oct. 4 link

October 2, 2019

Poster with text back from break

Thank you for your patience. I had to take some time to finish up new content, and I am thrilled to announce that I will release it this week! Check back Friday to learn about my next series and to jump into the Revit API. See you then!