Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to generate x and y-axes of rooms in the Revit API (in Millimetre)

8 REPLIES 8
SOLVED
Reply
Message 1 of 9
muhammad2.bilal
1776 Views, 8 Replies

How to generate x and y-axes of rooms in the Revit API (in Millimetre)

Hi,

 

I'm trying to write code that gives the x and y-coordinates of the bottom-left point of each room in the design, based on the length in millimetre. If for somehow the code sets the bottom-left corner of the design at (0,0) and then from there if wall surrounds all the inner space such that wall width is 290mm, then the first room at the bottom-left corner starts at (291,291) coordinates. The code reads all the rooms and returns the list of coordinates in (mm) for each of them.

 

I tried to use the following code to achieve this, however, I found some of the X and Y axes are in minus and moreover they are not in Millimetre, which is meaningless for my purpose. 

 

 

 

Location loc = room.Location;
LocationPoint lp = loc as LocationPoint;
XYZ p = (null == lp) ? XYZ.Zero : lp.Point;
msg.Add(room.Name + " - " + p.X + ", "+ p.Y);

 

The attached design explains the idea better.

 

Can someone help me with Revit API methods or any relevant material that could be useful for me to achieve this functionality?

 

Your help will be greatly appreciated.

 

Thanks in Advance

 

Kind Regards

Bilal

8 REPLIES 8
Message 2 of 9

Dear Bilial,

 

Thank you for the interesting query.

 

This is easy to answer and solve.

 

Two steps are required:

 

  1. All Revit database length measurements are in imperial feet:

 

http://thebuildingcoder.typepad.com/blog/2011/03/internal-imperial-units.html

 

There is no way to change that. 

 

Therefore, when you retrieve coordinates via the Revit API, they will always be in feet, and you will have to convert them to millimeters yourself.

 

This is very easy, of course.

 

  1. If you are interested in the lower left-hand corner of the room, the room location point is of absolutely no use whatsoever.

 

Instead, you could, for instance, retrieve the room boundary, iterate over all the curves, and determine which endpoint is on the lower left.

 

If your rooms have straight edges, this is trivial.

 

If the edges are curved, it might get a bit more complicated.

 

  1. You might also be able to use the room bounding box. That would be simpler still, but maybe less precise.

 

I hope this helps.

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 3 of 9

Hi Jeremy,

 

I am grateful for the response and very nice explanation of various approaches to achieve the use case.

 

I'm trying to get my head around calculating the lower left corners by following your guidelines stated in option 2. I have written some code to at least traverse each segment, but I just confused about how to go further to compute the lower left corners of spaces. The code is as below: 

 

 

SpatialElementBoundaryOptions options = new SpatialElementBoundaryOptions();
            options.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Finish;
            List<string> msg = new List<string>();
            foreach(IList<BoundarySegment> bsList in r.GetBoundarySegments(options)) {

                foreach (BoundarySegment bs in bsList) {
                    
                    Wall w = r.Document.GetElement(bs.ElementId) as Wall;
                    Curve curve = (w.Location as LocationCurve).Curve;

                    
                    str.Append("Length: " + Math.Round(curve.Length * 304.8, 2) + 
                               " Starting point " + curve.GetEndPoint(0) + 
                               " Ending point " + curve..GetEndPoint(1) + "\n"); 

 

So, I am currently stuck with how to get ahead. 

 

Can you please explains further, particularly, the API functions as well as logic that can be useful for the purpose. Currently, GetEndPoint() method gives me start and end of the each boundary but don't know how to go further due to very limited documentation on the functions.

 

Any support will be highly appreciated and very useful at this point.

 

Many Thanks and 

 

Kind Regards,

Bilal

Message 4 of 9

I followed the option 2, as highlighted to implement the logic. Since I am getting the lowest coordinate, XYZ class is extended to customise the sorting logic. The code snippet below describes the contents of this class (_XYZ).

 

//Custom class that extended XYZ for sorting the coordinates
class _XYZ : XYZ, IComparable<_XYZ> {
   public _XYZ(double x, double y, double z) : base(x, y, z) { }
   public int CompareTo(_XYZ other) {
     if (this.X > other.X) return 1;
     else if (this.X < other.X) return -1;
     else {
        if (this.Y > other.Y) return 1;
        else if (this.Y < other.Y) return -1;
        else return 0;
     }
   }
}

 

So, the following set of activities is carried out step by step.

  1. Getting all the rooms from the document
  2. For each room, get the boundary segments, then walls and finally curves.
  3. For each curve, get the start and end point of wall and insert them into an array. Before inserting ensure the coordinate is unique to the array.
  4. Sort the array of points in ascending order and return the top most element of the array i.e., the lowest left corner of the room.

The following code snippet is achieving these steps:

 

//Procedure to get the lower left corner of the given room
public static _XYZ Get_Room_Coordinates(Room r) {
  //List to hold all the points of the walls of the given room
  List<_XYZ> l_points = new List<_XYZ>();
  SpatialElementBoundaryOptions options = new SpatialElementBoundaryOptions();
  options.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Finish;
  foreach (IList<BoundarySegment> bsList in r.GetBoundarySegments(options)) {
    foreach (BoundarySegment bs in bsList) {
      Wall w = r.Document.GetElement(bs.ElementId) as Wall;
      Curve curve = (w.Location as LocationCurve).Curve;
      if (isPointNotInList(l_points, new _XYZ(
                          Math.Round(curve.GetEndPoint(0).X),
                          Math.Round(curve.GetEndPoint(0).Y), 
                          Math.Round(curve.GetEndPoint(0).Z))))
                l_points.Add(new _XYZ(
                          Math.Round(curve.GetEndPoint(0).X),
                          Math.Round(curve.GetEndPoint(0).Y),
                          Math.Round(curve.GetEndPoint(0).Z)));
      if (isPointNotInList(l_points, new _XYZ(
                          Math.Round(curve.GetEndPoint(1).X), 
                          Math.Round(curve.GetEndPoint(1).Y), 
                          Math.Round(curve.GetEndPoint(1).Z))))
              l_points.Add(new _XYZ(
                          Math.Round(curve.GetEndPoint(1).X),
                          Math.Round(curve.GetEndPoint(1).Y),
                          Math.Round(curve.GetEndPoint(1).Z)));
       }
    }
  //Sorting the list of points to bring the lower left corner at the top
  l_points.Sort();
  return l_points[0];
}

To avoid duplicate insertion of points, the following function is called in the procedure given above.

 

//Function to avoid duplicate coordinates in the list
private static bool isPointNotInList(List<_XYZ> l_points, _XYZ l_new_point) {
  bool found = true;
  foreach (_XYZ pt in l_points)
    if (pt.X == l_new_point.X && pt.Y == l_new_point.Y) {
      found = false;
      break;
     }
  return found;
}

The following code calls the function to get the lower left corner of rooms and plotting the circles around them.

 

//Getting the document handle
    UIApplication uiapp = commandData.Application;
    UIDocument uidoc = uiapp.ActiveUIDocument;
    Document doc = uidoc.Document;
    //Retrieving rooms in the design
    IList<Element> rooms = Get_Rooms(doc);
    
    foreach (Room room in rooms) {
     _XYZ l_llc = RoomUtils.Get_Room_Coordinates(room);
                
     using (Transaction tx = new Transaction(doc)) {
       tx.Start("Draw Curves at Points");
       for (int ii = 0; ii <= 10; ii++) CreateCircle(doc, l_llc, 1); 
       tx.Commit();
     }

This code works for the design, where every room has unique wall objects, not a shared wall that flows through multiple rooms. So, if two rooms, for example, share a single wall object say towards the South (means the South wall of room 1 and 2 is a single wall object). The code will simply give the coordinates the lower-left corner of room 1 even for the room 2. And the reason is that the wall they are sharing has the starting point that is indeed the lower left corner of the room 1.

 

Can some guides me how to get the lower left corner for the rooms 2, i.e., the middle of the wall, in this situation. 

 

Any help will be highly appreciated.

 

Many Thanks and Kind Regards

Bilal

Message 5 of 9

The issue is resolved by replacing the following lines of code 

 

 

Wall w = r.Document.GetElement(bs.ElementId) as Wall;
      Curve curve = (w.Location as LocationCurve).Curve;

with the following code

 

 

Curve curve = bs.GetCurve();

in the Get_Room_Coordinates method.

 

The solution is working perfect now.

 

Many Thanks for the kind support.

Kind Regards

Bilal

 

Message 6 of 9

I implemented this for you in The Building Coder Samples:

 

https://github.com/jeremytammik/the_building_coder_samples

 

    /// <summary>
    /// Return bounding box calculated from the room 
    /// boundary segments. The lower left corner turns 
    /// out to be identical with the one returned by 
    /// the standard room bounding box.
    /// </summary>
    BoundingBoxXYZ GetBoundingBox(
      IList<IList<BoundarySegment>> boundary )
    {
      BoundingBoxXYZ bb = new BoundingBoxXYZ();
      double infinity = double.MaxValue;

      bb.Min = new XYZ( infinity, infinity, infinity );
      bb.Max = -bb.Min;

      foreach( IList<BoundarySegment> loop in boundary )
      {
        foreach( BoundarySegment seg in loop )
        {
          Curve c = seg.GetCurve();
          IList<XYZ> pts = c.Tessellate();
          foreach( XYZ p in pts )
          {
            bb.ExpandToContain( p );
          }
        }
      }
      return bb;
    }


Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 7 of 9

That generates the following result for as simple test with ten rooms, showing that the result from the room boundary edges is identical with the standard, simpler and more efficiently Revit element accessible bounding box for these simple rectangular samples: 

 

Room nr. '1' named 'Room 1' at (-31.85,19.49,0) with lower left corner (-39.74,13.41,0), bounding box ((-39.74,13.41,0),(-20.71,25.88,13.12)) and area 237.236585584282 sqf has 1 loop and 4 segments in first loop.
Room nr. '2' named 'Room 2' at (-31.85,5.67,0) with lower left corner (-39.74,0.29,0), bounding box ((-39.74,0.29,0),(-20.71,12.76,13.12)) and area 237.236585584282 sqf has 1 loop and 4 segments in first loop.
Room nr. '3' named 'Room 3' at (-16.54,19.49,0) with lower left corner (-20.05,13.41,0), bounding box ((-20.05,13.41,0),(-10.87,25.88,13.12)) and area 114.528006833791 sqf has 1 loop and 4 segments in first loop.
Room nr. '4' named 'Room 4' at (-16.54,5.67,0) with lower left corner (-20.05,0.29,0), bounding box ((-20.05,0.29,0),(-10.87,12.76,13.12)) and area 114.528006833791 sqf has 1 loop and 4 segments in first loop.
Room nr. '5' named 'Room 5' at (-4.97,19.49,0) with lower left corner (-10.21,13.41,0), bounding box ((-10.21,13.41,0),(-1.02,25.88,13.12)) and area 114.528006833791 sqf has 1 loop and 4 segments in first loop.
Room nr. '6' named 'Room 6' at (-4.97,5.67,0) with lower left corner (-10.21,0.29,0), bounding box ((-10.21,0.29,0),(-1.02,12.76,13.12)) and area 114.528006833791 sqf has 1 loop and 4 segments in first loop.
Room nr. '7' named 'Room 7' at (3.62,19.49,0) with lower left corner (-0.37,13.41,0), bounding box ((-0.37,13.41,0),(8.82,25.88,13.12)) and area 114.528006833791 sqf has 1 loop and 4 segments in first loop.
Room nr. '8' named 'Room 8' at (3.62,5.67,0) with lower left corner (-0.37,0.29,0), bounding box ((-0.37,0.29,0),(8.82,12.76,13.12)) and area 114.528006833791 sqf has 1 loop and 4 segments in first loop.
Room nr. '9' named 'Room 9' at (14.45,19.49,0) with lower left corner (9.47,13.41,0), bounding box ((9.47,13.41,0),(18.66,25.88,13.12)) and area 114.528006833791 sqf has 1 loop and 4 segments in first loop.
Room nr. '10' named 'Room 10' at (14.45,5.67,0) with lower left corner (9.47,0.29,0), bounding box ((9.47,0.29,0),(18.66,12.76,13.12)) and area 114.528006833789 sqf has 1 loop and 4 segments in first loop.


Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 8 of 9

Hi Jeremy Temmik,

 

I am very glad for the reply and the solution you posted.

 

A small piece of code but very efficient.

 

Many Thanks and Kind Regards

Bilal

Message 9 of 9

Dear Bilal,

 

Thank you for your appreciation.

 

I summarised, cleaned up and published our conversation on The Building Coder:

 

http://thebuildingcoder.typepad.com/blog/2016/08/vacation-end-forge-news-and-bounding-boxes.html#7

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Rail Community