lisa-marie mueller

lisa-marie mueller

bullet pont

input validation for WPF user interfaces link

August 7, 2020

typewriter, Cuckoo Clock, marbles in blue User interfaces allow you to customize your plug-ins and provide a better experience. Your UI can be quite simple or more complex to allow users to control a variety of inputs. I’ve posted a few times about the use of Windows Presentation Form (WPF), which is one common way to create user interfaces. There are a lot of options that allow you to customize the interface. You can check out Microsoft’s documentation if you are looking for a place to get started.

goals and considerations

  • create the grids by entering width and height information
  • randomly delete curtain wall grids but allow for user configurations
  • randomize curtain wall panel materials
  • option to remove mullions

WPF form to allow for wall selection

Today, I am going to discuss data validation, specifically for user selected elements. If you are allowing a user to select a Revit element in the project, it is important to confirm the element is of the correct type. This way your application will not throw an error. We can confirm the element type by first assigning our user selected element to a variable or putting multiple elements in a list. For our Curtain Wall Randomizer, the user only selects one element so we can assign it to the variable userObjectSelection. Then we can use the GetElement method to select it and cast it to a Wall because we are expecting a Wall. Since we are specifically looking for a curtain wall, we also need to check that the wall has a CurtainGrid which is a defining factor of a curtain wall. If this check returns null, we know the user did not select a wall and they need to try again. If they did select a wall, then we can move forward and run the rest of our application.

summary

The steps to verify this information will take place in your main method. Similarly, you can check for other element types or iterate over a list to review multiple elements. Next week we will look at one more UI feature, sliders.

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

delete mullions link

July 31, 2020

typewriter, Cuckoo Clock, marbles in green This week, we complete the final step before reviewing a few things in the user interface. In addition to modeling curtain walls with randomized panel sizes and panel types, users may want to model panelized rain screen systems. Our ‘randomizer’ makes it easier to model both systems. One key difference is that the curtain wall system will have mullions but a panelized rain screen system, will not need mullions. Therefore, we will want to allow for the option to delete the mullions.

goals and considerations

  • create the grids by entering width and height information
  • randomly delete curtain wall grids but allow for user configurations
  • randomize curtain wall panel materials
  • option to remove mullions

In order to delete the mullions on the curtain wall, we will create a new method that does not return anything. The parameters we need are our Document, UIDocument, and the curtain wall we are operating on. First we get all the mullions’ Ids and add this to a list. Then we iterate on each mullion. Before we can make any changes, we need to unpin the mullion and then we can simply delete it.

summary

And those few steps allow us to delete the mullions. Next week, we’ll take a look at input validation for data entered through a WPF user interface.

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

randomize curtain wall panel types link

July 24, 2020

typewriter, Cuckoo Clock, marbles in red Now that we have all the panel types that we gathered last week, we’ll want to randomize the panel types. For this plug-in, I limited the input to a maximum of five different panel types which will each be a different material. The five panel types are a user input in our UI and the user will also be able determine the mix of each material.

goals and considerations

  • create the grids by entering width and height information
  • randomly delete curtain wall grids but allow for user configurations
  • randomize curtain wall panel materials
  • option to remove mullions

gather and unpin panels

We are going to create a new method called RandomizePanelMaterial that does not return anything. The parameters we need for our method are the Document, UIDocument, and the Curtainwall we are randomizing. We also need the percentage of each material (mtl#Max) and the curtain wall panel type (mtl#) both of which the user selected. To start, we need to get all the ElementIds of the panels on our curtain wall and add them to a list. Then we can use the GetElement method with the ElementIds as a parameter to return the element object. This way, we can iterate on each panel. Before we can change the type, we will need to unpin each panel.

randomize types

In addition to getting our curtain wall panel at the beginning of our foreach loop, we will also need to get a random number. Based on the user selected material mix, each panel has a percentage represented by an integer between 1 and 100. We have a maximum of five panel types but if the user has fewer than five panel types, the values for the unused types will simply be zero. In our execute method, we calculate our mtlMax variable. All we do is add the previous materials’ percentages to the current material. For example if we have material A, B, and C with the mix 10, 30, 60, mtlMax1 would be 10, mtlMax2 would be 40, and mtlMax3 would be 100. Our random number will be between 0 and 99. In order to assign the material, we will see in what range the random number if. If it is between 0 and 10, we will assign material A. If it is between 11 and 30, we will assign material B. If it is between 31 and 100, we will assign material C.

summary

Now that we have randomized the panel materials, we have one last step before looking at a few things in the UI. Next week, we’ll incorporate the option to delete the mullions.

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

get valid curtain wall panel types link

July 17, 2020

typewriter, Cuckoo Clock, marbles in orange A few weeks ago, I took a pause from discussing our plug-in on randomizing curtain walls because Andreas and I entered a hackathon and we wanted to share some information on our project. This week, we’re back to curtain walls and switching gears from the curtain wall grid lines to the curtain wall panels. In addition to randomizing curtain wall panel sizes, we also want to be able to randomize the material.

goals and considerations

  • create the grids by entering width and height information
  • randomly delete curtain wall grids but allow for user configurations
  • randomize curtain wall panel materials
  • option to remove mullions

GetValidTypes

The first step to be able to change the material of the panels is to get a list of all the options. This is where data type validation comes into play. Generally when you want to collect all the elements of a certain type, you use a filtered element collector. For curtain walls, this can return panel types that, if used, throw an error that they are invalid. In order to avoid this, we want to first get the ElementId of the first panel of our selected curtain wall since we know this is a valid type. Then we can use the GetElement method to retrieve the panel Element and assign it to a variable.

To put together our list of valid curtain wall panels, we use the GetValidTypes method and add these valid types to a list. In our valid panel types, it is possible to have some that aren’t panels for curtain walls. To filter out those, we need to check if the name of the Category is “Curtain Panels.” Once we’ve checked for this, we have a list of curtain wall panel types that can be used to switch out the panels of our curtain wall system.

conclusion

Now that we have all the types of panels we can use, our next step will be randomizing the panels. Next week, we will walk through how to randomly assign a panel type based on a user entered ratio.

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

Streaming information between the RevitAPI and a VR application (StreamVR) link

With guest writer: Andreas Brake

July 10, 2020

computer with StreamVR logo, oculus quest and controllers, clock, falling abstract objects blue Happy Friday! This week I am excited to introduce Andreas Brake as the guest writer for this post. Andreas was my partner in crime for our project StreamVR which won the Best Overall Project Award at the ENGworks Hackathon. He is a software engineer at Topgolf Media doing server development. In his free time, he enjoys hiking, gaming, and gardening. This week, he will dive into the technical details of our VR application and how we set up communication between the RevitAPI and StreamVR.

communication

To start with, I will discuss how we communicated information between the Revit plug-in and the VR Application. There were many options to weight when making this decision. We first looked at the option of running an HTTP server from the Revit plug-in that would act primarily as a proxy for the RevitAPI. This setup could allow us to perhaps even go so far as to expose the RevitAPI via GraphQL or other information retrieval frameworks popular in the web world. We ran quickly into a few problems with this. Firstly, due to corporate computer policies, it was not guaranteed (in fact very unlikely) that the Revit application would have sufficient permissions to expose HTTP ports on the machine and allow incoming traffic. Furthermore, we wanted to maintain the possible use case of having an architect or designer drive a meeting with clients which would require multiple applications connected to the model and staying in sync if any changes occurred. This scenario became dramatically more complex using HTTP calls.

Therefore, we decided to communicate our data over a message bus. Simply put, this is a program that allows for applications to push bits of information and have other connected applications listen to and process these messages. There are many options for messaging buses from MSMQ to RabbitMQ but we ended up using NATS as, among other reasons, it is a very light-weight solution that is deployable on most operating systems.

Using a messaging bus allows us to communicate between Revit and our Application as follows. We first stand up a nats-server on a machine that allows it. This means that a Revit user who has some admin privileges can run this locally. Alternatively, in a corporate setting, the organization’s IT team can deploy this to the local network and ensure a fixed IP or DNS to this server. Next, both the Revit plug-in and the VR Application connect to the server’s address. The plug-in subscribes to the TO_SERVER channel and the Application pushes requests and commands to this channel which the plug-in is now able to process and reply to.

revit plug-in

VR is a powerful tool to convey design intent. Architects, clients, designers, contractors, and engineers can utilize VR to simulate use cases and to make decisions while experiencing the space. Although there are tools to view BIM (building information models) in VR, so far, the process has been linear, exporting a model from Revit into a rendering software. There is a disconnect between VR and BIM. By allowing users to make changes in VR and synchronize their changes back to Revit, design teams and owners can have more fluid conversations allowing design and documentation to exist in the same space.

The reason for the application hanging is because the plug-in is constantly looping and listening for new additions to the message queue. Every loop, it will attempt to pop a command from the queue and process it based on the message contents. These requests will generally return one or more RevitAPI objects that have been converted to a data transfer object (DTO) which can then be serialized and understood by our VR Application. These DTO schemas will be further discussed shortly. There are six currently supported request types and seven transfer objects.

data transfer objects

Data Transfer Objects (DTOs) are the intermediate link between Revit and the VR application. Our Revit plug-in takes the features of a Revit object and saves these as parameters of a DTO and then can translate those parameters to the VR application and vice versa. Most of our DTOs inherit a simple Element class similar to the RevitAPI. Our base Element however only has 2 fields: Id and Name. Some of our objects, such as Family and Material, do not currently have additional data beyond these fields. For these cases, the Name field is being used to resolve an existing locally defined Prefab or Material in the VR application. The Id is then used to link these Unity GameObjects to FamilyInstances and material-defining Faces.

More complex objects include Wall, Floor, and Ceiling which are similar to each other as, beyond their inheritedId and Name fields, they primarily define a list of Faces that represent their geometry. This was the simplest way of telling our VR Application how to render these objects. The Application reconstructs the meshes based on the provided vertices and indexes.

The final DTO we define is the FamilyInstance. This object has the additional fields of a HostId, FamilyId, and a Transform object, defining the object’s position and rotation. Our focus for this hackathon was to provide the option to move, rotate, and place this DTO through the VR application.

revit plug-in supported requests

GET_ALL

Param: Type
Return: All objects of given type
Description: This request first takes the passed "Type" and uses Reflection to resolve the c# Type from the RevitAPI assembly. If the Type correctly resolves, we then use a FilteredElementCollector to get all objects of this Type from the Revit model which we then attempt to convert to the corresponding DTO.

Request

Response

GET

Param: ElementId
Return: Object with corresponding ElementId
Description: This request differs from the GET_ALL request as it only takes the ElementId for the RevitAPI object. This ElementId is used with the Document.GetElement method to retrieve the corresponding object which is then converted to its DTO and returned.

Request

Response

SET

Param: DTO
Return: Updated DTO
Description: The set request takes in the entire DTO as a parameter. First, this process takes the "Id" field and resolves the corresponding RevitAPI object. Then, it creates a transaction and passes both of these objects through the defined reverse mapper for the object’s Type. The fields that are able to be updated depend on what we support through the reverse mapper. Some fields are strictly read-only (e.g. Id) and therefore will not be updated even if the corresponding DTO field has a new value. In order to ensure that the VR Application has the correct values after this operation, the updated RevitAPI object is converted to its DTO and returned.

Request

Response

PAINT

Param: DTO
Return: Updated DTO
Description: This request is similar to SET as it also takes in a DTO to be updated. The primary difference comes in that the only DTO type supported is a "Face" which corresponds to the paintable Autodesk.Revit.DB.Face object. The process here is very straight-forward. The MaterialId and ElementId fields are extracted from the DTO and used to resolve the RevitAPI objects corresponding to the new material and parent object for the face. Then, the exact face of the parent is identified using the provided FaceIndex. The new material is then applied using the existing Document.Paint method. As usual, the updated face DTO is returned.

Request

Response

CREATE

Param: Base object details
Return: DTO for created object
Description: Create operates differently than our previous requests as it needs to create a new RevitAPI object based on base fields. Which fields are required for input depends on the Type to be created. For this hackathon we focused on support for creating new FamilyInstances. The fields required for FamilyInstances are the FamilyId and the Transform.Origin location where the object should be placed. The object is then placed using the Document.Create.NewFamilyInstance method.

Request

Response

EXIT

Param: none
Return: none
Description: This final parameter is very simple. It tells the plug-in to stop processing requests and return control to the Revit Application.

conclusion

And that sums up the work we did for the hackathon. Another huge thank you to Andreas Brake for guest writing this post. I hope you learned a few things to consider for your future projects. Check out our demo video and github to view the work we did! Next week, I will dive back in to the Revit plug-in for randomizing curtain walls.

bullet pont

lessons learned developing a VR app in Unity in 7 days (StreamVR) link

July 3, 2020

computer with StreamVR logo, oculus quest and controllers, clock, falling abstract objects blue Happy Friday! I ended up taking a break last week because Andreas and I decided last-minute to join the ENGworks hackathon. I’m so glad we did because it was so much fun! It was my first hackathon and I’ve been wanting to participate in one for a while now. ENGworks offered a great opportunity with their online event. If you missed it but you want to participate in a hackathon, AEC Hackathon is hosting an online event coming up and they have some excellent presentations scheduled. I will continue the series on randomizing curtain walls later this month, right now I wanted to take some time to provide more information on our hackathon project.

Although I did not post last week, I still kept my pledge to donate to a different organization each week for the month of June. Staying informed, sharing stories and perspectives, and even just taking some time to laugh is so important right now. This is why last week we donated to our local NPR station.

the hackathon

It was June 18th that I found out about the ENGworks hackathon and the registration deadline was June 19th. I spoke with Andreas about the hackathon and asked if he wanted to participate with me. He was on board and we immediately started brainstorming. I feel like I have new thoughts weekly about how I want to change the AEC industry with technology. For the hackathon, we had to find a problem we could solve in less than 4 days with 3 additional days of brainstorming and prep. Ultimately, we landed on our idea: StreamVR. We are excited to say we received the award for Best Overall Project for our application.

the problem

VR is a powerful tool to convey design intent. Architects, clients, designers, contractors, and engineers can utilize VR to simulate use cases and to make decisions while experiencing the space. Although there are tools to view BIM (building information models) in VR, so far, the process has been linear, exporting a model from Revit into a rendering software. There is a disconnect between VR and BIM. By allowing users to make changes in VR and synchronize their changes back to Revit, design teams and owners can have more fluid conversations allowing design and documentation to exist in the same space.

the process

Our plan for setting up the project was two fold. One piece involved setting up the VR application that runs on the headset, the other was the Revit plug-in. For the Revit plug-in we set up a data bus to stream information from the Revit API to our VR application. Next week, Andreas will be joining as a guest writer and we will share more in depth information on the Revit plug-in. This week, I will discuss a few of the lessons we learned going from downloading Unity to developing a VR application for the Oculus Quest in 7 days. You can watch our demo video on YouTube.

lessons learned

VR development is changing fast

We learned this the hard way on day 3. After going through a number of tutorials for VR in Unity, I kept hitting road blocks. We found that most of the information was already out of date. We were originally referencing tutorials from late 2019 and even those released in November were already missing critical information. Thankfully we stumbled upon a youtube channel by Valem, the channel is also called Valem, that recently released a series of videos that introduces the Unity XR tool kit. If you are interested in developing for VR with Unity, I recommend you check out his Introduction to VR in Unity - UNITY XR TOOLKIT playlist. And we were on track again after going through his tutorials and able to move forward with our VR application.

understanding SDKs

Software Development Kits are a collection of development tools which include compilers, debuggers, and frameworks. For VR, there are SDKs that target specific headset devices. Unity has also developed an SDK that allows for functionality across different XR headsets. We started by using Oculus specific SDKs but our attempts were not working. We ultimately did not have time to fix all the bugs but thankfully found excellent resources for using the general Unity XR libraries which work across headsets. Ultimately, it’s a better path because, in theory, it allows for interoperability. If you are planning to develop in Unity, I would recommend reviewing and understanding the SDK options and how this fits in with your application.

prioritize and organize

When you have an idea for a project, it’s easy to want to complete everything and even easier to want to get the features you are working on to be perfect. I always try to remind myself: KISS - keep it simple stupid. And sometimes my husband has to remind me when I go down a rabbit hole. As with anything it’s important to prioritize. Identify your minimum viable product and work towards that goal. As new ideas come to mind, keep a list and add these features later. For the hackathon, we determined early on that our goal was to extract the model information from Revit and render it in the VR application. We focused on structural surfaces and FF&E families to limit the scope to something we could accomplish in 4 days. We also wanted to allow users to move families, create families, and paint surfaces in VR and have those changes synchronize back to Revit in real time. For the families, we started with only non-hosted families and we manually exported the FBX model files for testing. This helped to limit our scope and allow us to have a presentable project in just a few days.

conclusion

These are a few items that I hope will be helpful as you jump into your next project or hackathon. Next week, Andreas will guest write a post that dives into how we extracted data from the Revt model and communicated with the VR application.

Check out our demo video and github to view the work we did!

bullet pont

randomly delete curtain wall grid line segments link

June 19, 2020

typewriter, Cuckoo Clock, marbles in green My weekly donations through the month of June continue and this week, my love of theatre has influenced my selection. Musicals are my favorite but I love it all! Plays, operas, ballets, and concerts inspire me. I love when performances challenge your view of the world or allow you to see things from someone else’s point of view. Because of this, I have chosen to donate to the Fund for Black Theatre in the U.S. this week. The fund is locally organized on behalf of Sew Productions Inc and proceeds will be distributed to support Black artists and Black Theatre. If you remember, last week, we created our curtain wall grid lines. This week we will want to randomly delete grid line segments to get the effect of varying panel sizes.

goals and considerations

  • create the grids by entering width and height information
  • randomly delete curtain wall grids but allow for user configurations
  • randomize curtain wall panel materials
  • option to remove mullions

randomly delete horizontal grids

We create a new method and call it RandDeleteHorizGrids. The parameters we need are the document, the user selected curtain wall, a user entered integer that we will use for the probability of deleting the grid, and the user entered information on the maximum number of grids that should be deleted in a row. It’s also important to note that in the execute method, we need to create a random number generator. The seed will be the date and time. It is important to only have one random number generator for the entire application to ensure sequences don’t repeat. If you use the same seed in a random number generator, you will get the same series of random numbers.

In our RandDeleteHorizGrids, we first need to declare our variables. Then we can take each horizontal grid and divide it into the individual curves. Each curve is a curtain grid line segment which we will delete or keep.

We are also using a counter to track how many gird line segment have been deleted in a “column” of the curtain wall. This allows the user to set a maximum size to each panel. For example, if you want a maximum 8 foot panel and your grid is 2 feet on center then you can remove a maximum of 3 consecutive gird line segments in any column. Our counter keeps track of this so if we reach the maximum, the grid line segment is not removed and the counter is reset to 0. Before we can start deleting curves, we need to confirm our removal counter list is initialized. If our removal counter list is null, we create a new list with zero at each index. The number of indexes is equal to the number of columns that our curtain wall has.

We are also tracking the index (i) of the curve in the list of curves. “i” starts at zero and while it is less than the total number of curves, we add 1 for each time we loop through. In our for loop, we take the curve at that index to perform our actions. We use our random number generator to get a random number between 0 and 99. Then we check if this random number is less than the user entered probability. For example, if the user chose the probability of grids being removed to be 30 then the grid is only deleted if the random number is less than 30. We also need to check if your counter is less than the maximum number of horizontal grid line segments that should be removed in a column. If both are true, we remove the grid line segment. If both are false, we reset the counter to 0.

randomly delete vertical grids

In this case, we do not need to make any adjustments to accommodate the vertical grid lines. We do just rename our variables for clarity. The reason I have the horizontal and vertical grids in different methods is that you may want to delete only horizontal grid line segments or only vertical grid line segments. Randomly deleting both means you will have non-rectangular shapes for your panels. Separate methods allow us to easily call each method.

conclusion

And that sums up how to randomly delete curtain wall grid line segments to have random panel sizes. These methods still allow for user control by allowing for input on the maximum panel size and on the probability of a gird line segment being deleted. Next week we will start to look at the curtain wall panels.

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

create curtain wall grid lines link

June 12, 2020

typewriter, Cuckoo Clock, marbles in pink As mentioned last week, for June I have pledged to donate to a different organization each week. This week, I have donated to my local food bank: the Alameda County Community Food Bank. If you are able, this is an excellent way to support others. Food banks are doing even more for our communities right now when other support may not currently be available like school lunch programs.

Last week, we found the minimum and maximum coordinate of the panel in our curtain wall so this week we can use it to create the grid lines. We covered briefly that to create a grid there are three parameters we need. These are a boolean to determine if it is a U (true) or a V (false) gridline, a position for placing the grid line, and a boolean to determine if it is adding the entire line or just one segment of it. We also know that the position will change because we need to step along the curtain wall to create multiple grid lines until we reach the end.

goals and considerations

  • create the grids by entering width and height information
  • randomly delete curtain wall grids but allow for user configurations
  • randomize curtain wall panel materials
  • option to remove mullions

position - horizontal grid lines

We can create a new method to create the horizontal grid lines and call it MakeHorizGrids. We are not returning anything but we do have a number of parameters: the document, the curtain wall that the user had selected, the minimum height of the panels, and the minimum and maximum coordinates of the panel that we found last week. The minimum height of the panels will be one of the user inputs we need in order to create the grid lines.

We can use a while loop to check if the new position we are proposing is still located on the curtain wall by comparing the Z coordinate of the position to the Z coordinate of the curtain wall’s maximum. If it is less, then we are still locating the grid on the wall. For horizontal grid lines, we are locating the next grid up the curtain wall in the Z direction which means we can just take the minimum height and add it to the Z coordinate.

position - vertical grid lines

For the vertical grid lines we start with the same method but instead of moving up the curtain wall, we are moving along the curtain wall. In addition to the curtain wall min (the bottom left corner of the curtain wall), we also need the bottom right corner of the curtain wall - the other endpoint. We can create that from the X and Y coordinates of the maximum and the Z coordinate of the minimum. Since we are starting in the bottom left corner, to create vertical grid lines we are moving the position by some factor in the X and Y direction.

Starting with our curtain wall minimum, we know the length of the curtain wall is √((X2-X1)2 + (Y2-Y1)2). The vector for the direction we need to move is the bottom right corner (btmCorner) minus the bottom left corner (cwMin) divided by the length of the curtain wall (distance).

We can again use a while loop to check if the distance we have moved is less than the total distance minus the minimum width of a panel to ensure the next grid line is still located on the curtain wall. If it is less, then we can add our minimum width multiplied by our direction vector to the X and Y of the coordinate to move along the curtain wall to create the next grid line.

conclusion

Now that we have the correct grid lines created for the curtain wall, we can tackle randomly deleting grid line segments next week.

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

curtain wall panel min and max link

June 5, 2020

typewriter, Cuckoo Clock, marbles in blue This past week, I have taken a lot of time to think. I thought about the impacts we have on the lives of others. What can we all do to better the future? I encourage everyone to take a minute and do the same. I went in circles about posting a topic this week. In such challenging times, when so many people are hurting, it’s easy to feel like there is nothing we can do to help. I believe that what I can do is to better myself and educate myself and help others grow as well. This week, I have donated to the NAACP Legal Defense and Educational Fund and I pledge to donate every week through the end of June to a different organization that helps bring about the change we need. USA Today wrote about 100 Ways You Can Take Action Against Racism Right Now. Not all of the actions require a monetary commitment, so I encourage you to review the resources they have collected.

For this week’s content, I would like to continue looking at the curtain wall grid. Last week, we deleted all the grid lines, so this week we can create new ones.

goals and considerations

  • create the grids by entering width and height information
  • randomly delete curtain wall grids but allow for user configurations
  • randomize curtain wall panel materials
  • option to remove mullions

First we will want to take a look at the method we are using: AddGridLine. There are three parameters we need. These are a boolean to determine if the grid line is a U (true) or a V (false) grid line, a position for placing the grid line, and lastly a boolean to determine if it is adding the entire line (false) or just one segment of it (true). Determining the boolean values are straight forward, but the position can be a bit tricky.

position

To determine a position for grid lines, I need a coordinate that is in a corner of the curtain wall. This way, I can then add the width or height of the panels to get to the next curtain grid line until I reach the edge of the wall. My first thought was using the bounding box of the curtain wall. When I printed out the minimum coordinate of the bounding box and the coordinates of some of the curtain wall panels, they were not in the same plane. I started thinking that since all the grids are removed, we have one large curtain wall panel. If I can locate the minimum coordinate of the curtain wall panel, I can use this as the starting point for creating the grids. A curtain wall panel does not have any properties that are minimum and maximum values. However, we know that it is made of CurveLoops, which are just lines, which all have endpoints. So finding the minimum coordinate had two steps: collect all the endpoints and find which endpoint is the minimum coordinate. I should also mention that I am targeting to create the CurtainGrid on a flat curtain walls so there are some assumptions we can make.

curtain wall panel coordinates

I start my method and call it GetCWMinMax. The easiest way to store these coordinates is in a tuple so this is what we will return. The parameter taken in by the method is the curtain wall that the user selects. First we declare the variables we will need: the tuple (which we will return with the min and max coordinates) and a list of all the coordinates.

To get the list of coordinates, we first need to retrieve the CurveLoops that construct the curtain wall panel. Since we know there is only one panel, we can safely use the first CurtainCell and get the first set of CurveLoops that defines it. A CurveLoop contains a list of curves, in this case we know that they are all lines because we are working with a single panel, which in Revit cannot be curved. If we add it to our list as a Line we can get the origin of the line so that we only add each endpoint once. Now we have a list of endpoints and we need to find the minimum endpoint. The method we use to find the min and max coordinates from the list can be used for many different situations. I added it to my Utility file so it is easier to use in the future and I called it GetMinMaxFromList.

min and max coordinates from list

The next task is finding the minimum and maximum values in a list of coordinates. The challenge here is that these points can be anywhere within the coordinate system. It’s much easier if all the coordinates are in the positive side of the coordinate system. Therefore, we want to find the smallest X and independently the smallest Y coordinates and shift all coordinates by these amounts. We can put this into a new list called xyzListAdjusted.

Now that all the coordinates are in the positive quadrant, we can use Euclidian distance, √(a² + b²), to find the coordinates closest and furthest from (0,0). Then we check which index these coordinates are located at so we can go back to our original coordinate list and find the corresponding coordinates. We then return these original coordinates as our minimum and maximum.

conclusion

Now we have the starting coordinate at which we can place our new CurtainGridLines. Next week, we will go over placing those lines along the entire curtain wall.

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

delete curtain wall grid lines link

May 29, 2020

typewriter, Cuckoo Clock, marbles in purple For my next series, I wanted to explore the Revit API a bit further and dive into Curtain Walls. I’ve found that a common Dynamo script is a script that randomizes curtain wall mullions. Its mentioned across many forum posts and resources. I wanted to make sure to say thanks to my colleague Payam Rad (who also posts helpful Revit tips and tricks you can check out) who requested this script and gave us the idea to put it together for our firm a few years ago. Panelized rain screens are still a trendy design feature and having a script to randomize these panels saves time. Since it is still in use, I wanted to explore the topic in more detail and also look at customization options that can more easily be incorporated through the Revit API. I think the biggest benefit to using a plug-in is the additional control a user has when presented with a well planned user interface.

goals and considerations

  • create the grids by entering width and height information
  • randomly delete curtain wall grids but allow for user configurations
  • randomize curtain wall panel materials
  • option to remove mullions

overview

I would say that this plug-in is all about options. The goal is to create the most flexibility. A lot of this flexibility is accomplished through the user interface but it is also accomplished in how we write our methods. One of the options I wanted to incorporate was the option to create the CurtainGrid. This way you could even use the tool just to create a CurtainGrid. I incorporated a selection in the dialog box (more on this later) that allows the user to select if they would like to use the grid they already created or if they would like the plug-in to create this grid based on width and height information. I allowed this to be a user selected option because I understand you may also just want to randomly delete CurtainGrid Lines and use your own grid.

So the first overarching goal is to create a CurtainGrid for the CurtainWall using width and height information. There are a few steps we need to take to get there. One item to consider is what the input will be. In this case, the user will select a curtain wall in Revit. We need to assume they forgot to delete the grid. So if we are creating a grid, the first thing we actually need to do is delete the existing grid lines. This is what we will walk through today.

overview

We can start our first public method and call it DeleteAllGrids. We will not return anything. In Revit, CurtainWalls have CurtainGrids and these grids are separated into the U (horizontal) and V (vertical) grids lines. We can use the GetUGridLineIds and GetVGridLineIds methods to collect these elements. Revit also has a handy Delete method we can use. It looks like we just solved the problem in four lines of code, however this is not the case. It’s not that simple when it comes to CurtainWalls.

type association

In Revit, you can create CurtainWall Types that automatically create CurtainGrids when a user places that Type of CurtainWall. If a CurtainGrid is created through this way, it has a Revit parameter called Type Association which is set to “dependent”. If a CurtainGrid is dependent, you cannot delete it. When you manually set this Revit parameter to be “independent” through Revit, the parameter is hidden.

I was playing around a bit with Revit LookUp and logging some additional information and found out that the Type Association is stored as an integer. When the CurtainGrid is “dependent”, the Type Association is 1 and if it is “independent” it is 0. So we want get the Type Association parameter using the GetParameters method. If the Type Association is equal to 1 (dependent) then we want to make sure we set it to 0 (independent).

locked grids

Then we also have to keep in mind that a user can lock elements in Revit. This includes CurtainGrids, and you cannot delete locked elements. This is a very simple check. For each grid, we just want to check if the Lock property is true and if it is, set it to false.

delete grids

Finally, once we’ve done all the checks to make sure it is possible to delete the grid, then we can delete it. We can repeat the same for the vertical grids.

conclusion

And that wraps up the considerations for deleting curtain grids. Next week, we will start to process of creating a new CurtainGrid and discuss some of the challenges with finding the coordinates to use.

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