lisa-marie mueller

lisa-marie mueller

bullet pont

resizing CropBoxes link

November 15, 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]

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!

bullet pont

gone to the beach link

August 30, 2019

Watercolor paintings in frames with 'Gone to the Begach' sign

I'm enjoying a holiday at the moment but will be ready to jump into more content upon my return. See you September 14!

bullet pont

steps to learn to code [for architects & designers] part 2 link

August 23, 2019

Coding books, clock, and telephone Once you have identified which programming language to start with, it’s time to evaluate how you want to learn. For help with choosing a language, take a look at Steps to Learn to Code [for architects & designers] Part 1. Evaluate your learning style and figure out how you learn best. Some flexible options include online with set deadlines, online at your own pace, and in a classroom. Also, evaluate your budget for learning to code for both time and money.

community college

Community colleges offer both classroom and online classes and tend to have more flexible schedules including evening courses. If your local community college does not offer a class that fits your needs, you can look at any community college in your state to see if they offer an online class that works for you. Community college classes have the benefit of a verifiable curriculum, a transcript as proof of completing the course, a low cost, and a dedicated teacher with a smaller number of students. Even for the online classes, you will most likely be able to interact with your teacher and your classmates. Community college classes do have more regular homework assignments and tasks to complete and they are on a schedule so there are deadlines and final exam dates set for you. This works well if you appreciate the accountability.

My first coding class was through a community college, and I decided to take an online class that worked better for my schedule at the time. I appreciated the deadlines because it allowed me to make completing the class a priority for myself.

massive open online course (mooc)

There are many online options offered by both top universities around the world and by independent content developers. Some are free, some are paid, but generally, even those that are paid are a low price. I do want to advise that some online courses offer that you can pay for a certificate of completion. I would always be wary of this as you can easily prove your coding skill level and it is likely that the certificate of completion will not be necessary. MOOCs have the benefit that they are online, many of them are free or very low cost, they have reviews from others so you know what you are getting, and they can be completed on your own time with no deadlines. This can be great if you like to set your own goals and schedule.

I have participated in a few different MOOCs that ranged in hours of content and included both free and paid courses. I was able to find a handful of free classes that I enjoyed. I have, however, also found it useful to pursue some of the paid courses. These are usually offered for a very low fee. I have not had to pay more than $20 per class, and many times the courses contain 30+ hours of content. I would recommend you make sure to buy these courses on sale, as the sites have frequent sales.

bootcamp

Bootcamps are fast-paced, intensive coding camps. There is a much higher cost of attending a bootcamp and most are in-person classes. The benefits of a bootcamp are that you can learn a lot in a short time, you have professional teachers, and you learn a wide variety of skills and best practices. It is important to note that there are mixed reviews about bootcamps. I have not completed a coding bootcamp, but know individuals (even at tech companies) that do agree that the skills gained at a bootcamp can get you to a level that would even allow you to change careers if you are interested in pursuing development full-time.

From what I have researched when I was considering joining a bootcamp, you get out of it what you put in. I choose not to attend a bootcamp because I did not want to switch careers and I felt bootcamps were too high of a cost for what I wanted to learn. I started with online and community classes instead.

forge your path

There are countless routes you can take to becoming a developer. For every path, you have to put in the time, but it is rewarding to learn a new skill. Regardless of which path you take, I feel that it is most important to make it fun! For example, if you like computer games, take a C# class that teaches you to code while making computer games. Keep learning to code fun for you and you will be much more successful. It is important to remember, that coding is a new skill and you will not learn it overnight, but putting in the time will open many new possibilities.