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: 

Get wall openings

23 REPLIES 23
SOLVED
Reply
Message 1 of 24
Anonymous
6582 Views, 23 Replies

Get wall openings

Hi!

 

I'm struggeling to get wall openings using the API. What i'm interested in is the coordinates of a rectangular wall opening (see pic).

I've tried FindInserts(), but it's not given that anything is inserted in the opening. 

 

Does anyone have a solution for this?

 

 

Thanks,

Eirik Aasved Holst

 

wallOpenings.jpg

23 REPLIES 23
Message 2 of 24
jeremytammik
in reply to: Anonymous

Dear Erik,

 

This is an interesting questyion, and therefore has many answers.

 

You can determine the elements inserted in the wall (incl. openings) by temporarily deleting the wall. That gives you a list of all the dependent object s that also got deleted. Then you have the elements you are after.

 

You could look at the wall geometry before and after deleting the inseerted elements and determine the differences.

 

You could query the wall for its geometry and determine all the holes in the elevation view.

 

My personal favourite candidate right now, off the hip, would be to shoot a ray along the wall center line and determine all the faces that it hits.

 

That only works if you know one specific height or elevation (or several) at which all the openings really are open, so that they really have side faces that the ray intersection will hit.

 

This technique is demonstrated to find columns intersecting a wall by the FindReferencesByDirection / FindColumns SDK sample.

 

Have fun exploring, and please let us know what you end up with.

 

Cheers,

 

Jeremy



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

Message 3 of 24
Anonymous
in reply to: jeremytammik

Wonderfull! The ray shooting solution works perfectly.

 

I use the function for a planar view and set the rays elevation equal to view's elevation.

 

For anyone else intrested in using the same technique to locate wall openings, remember to set the rays origin a step outside the wall to determine if the wall starts with an opening or not (see pic).

 

pseducode for my solution:

 

//WallOpening2D - a simple class with two coordinates and some other basic info.

 

list<WallOpening2D> GetWallOpenings(Wall wall, ViewPlan view){

  var rayStart = new XYZ(wallOrigin.X - wallDirection.X, wallOrigin.Y - wallDirection.Y, GetElevation(view));

  pointList = (from reference in new ReferenceIntersector(view).Find(rayStart, wallDirection)

                   .where(IsSurface)

                   .where(ref => ref.Proximity < wallLength + "step outside"))

                   select reference.GetReference.GlobalPoint)

  if(!pointList.First().IsAlmostEqualTo(wallOrigin) //CHECK IF FIRST POINT IS NOT AT WALL START

    pointList.Insert(0, wallOrigin);

  else

    pointList.remove(pointList(First));

  if(!IsEven(pointList.Count)  //IF NOT EVEN POINTCOUNT -> WALL ENDS WITH OPENING

    pointList.Add(wallEndPoint);

  var wallOpenings = new List<WallOpening2D>();

  for(i = 0; i<pointList.Count -1; i += 2)

      wallOpenings.Add(new WallOpening2D(pointList[i], pointList[i+1]);

  return wallOpenings;

}

 

 

wallOpenings2.jpg

Message 4 of 24
jeremytammik
in reply to: Anonymous

Dear Erik,

 

I am very glad it helped.

 

Thank you for sharing the pseudo code.

 

I started implementing the real thing based on that and ran into one single little snag:

 

error CS1502: The best overloaded method match for 'Autodesk.Revit.DB.ReferenceIntersector.ReferenceIntersector(Autodesk.Revit.DB.View3D)' has some invalid arguments 
error CS1503: Argument 1: cannot convert from 'Autodesk.Revit.DB.ViewPlan' to 'Autodesk.Revit.DB.View3D'

Here is my implementation so far:

 

List<WallOpening2D> GetWallOpenings(
  Wall wall,
  ViewPlan view )
{
  Document doc = wall.Document;
  Level level = doc.GetElement( view.LevelId ) as Level;
  double elevation = level.Elevation;
  Curve c = (wall.Location as LocationCurve).Curve;
  XYZ wallOrigin = c.GetEndPoint(0);
  XYZ wallEndPoint = c.GetEndPoint(1);
  XYZ wallDirection =wallEndPoint - wallOrigin;
  double wallLength = wallDirection.GetLength();
  wallDirection = wallDirection.Normalize();
  UV offset = new UV( wallDirection.X, wallDirection.Y );
  double step_outside = offset.GetLength();
      
  XYZ rayStart = new XYZ( wallOrigin.X - offset.U, 
    wallOrigin.Y - offset.V, elevation );

  ReferenceIntersector intersector = new ReferenceIntersector( view ); // *** error here ***

  IList<ReferenceWithContext> refs = intersector.Find( rayStart, wallDirection );
  List<XYZ> pointList = new List<XYZ>( refs
    .Where<ReferenceWithContext>( r => IsSurface(r.GetReference()))
    .Where<ReferenceWithContext>( r => r.Proximity < wallLength + step_outside)
    .Select<ReferenceWithContext,XYZ>( r => r.GetReference().GlobalPoint) );

  // Check if first point is not at wall start.
  // If so, the wall begins with an opening, so 
  // add its start point.

  if(!pointList.First().IsAlmostEqualTo(wallOrigin))
  {
    pointList.Insert(0, wallOrigin);
  }
  else
  {
    pointList.Remove(pointList.First());
  }

  // If the point count in not even, the wall 
  // ends  with an opening, so add its end as 
  // a new last point.

  if(!IsEven(pointList.Count))
  {
    pointList.Add(wallEndPoint);
  }

  int n = pointList.Count;
  var wallOpenings = new List<WallOpening2D>( n / 2 );
  for( int i = 0; i < n; i += 2 )
  {
    wallOpenings.Add( new WallOpening2D { 
      Start = pointList[i], 
      End = pointList[i + 1] } );
  }
  return wallOpenings;
}

How did you work around that, please?

 

Did you create a 3D view to match the 2D one?

 

If so, do you have the code for doing that handy?

 

Thank you!

 

Cheers,

 

Jeremy



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

Message 5 of 24
jeremytammik
in reply to: jeremytammik

I get a pretty weird point list from my sample wall with four openings, none of them at either end of the wall, all in the middle.

 

The wall start point appears twice, the wall end point does not appear at all, and the points are not sorted by proximity:

 

Screen Shot 2015-12-16 at 15.52.17 .png

 

How do you handle that?

 

I have to add lots more cleanup and  foolproofing to get a reliable result.

 

Don't you?



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

Message 6 of 24
jeremytammik
in reply to: Anonymous

I implemented a working command in The Building Coder samples release 2016.0.124.0:

 

https://github.com/jeremytammik/the_building_coder_samples

 

https://github.com/jeremytammik/the_building_coder_samples/releases/tag/2016.0.124.0

 

The external command name is CmdWallOpenings:

 

https://github.com/jeremytammik/the_building_coder_samples/blob/master/BuildingCoder/BuildingCoder/C...

 

Cheers,

 

Jeremy



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

Message 7 of 24
Anonymous
in reply to: jeremytammik

Jeremy,

 

Some corrections to my pseudocode:

 

- You are right about the view3D. I'm using the 2D view to get the elevation of the view range cut plane. For the ReferenceIntersector, i'm using the standard 3DView (just make sure it does noe have an active section box and that all relevant elements are visible).

 

var default3DView = new FilteredElementCollector(doc).OfClass(typeof (View3D)).ToElements().Cast<View3D>()
.FirstOrDefault(v => v != null && !v.IsTemplate && v.Name.Equals("{3D}"));

Of course one may create a new 3D view if that works better.

 

 

 

-I forgot to specify that the referenceIntersector should only return Faces beloning to the relevant wall. 

var referenceIntersector = new ReferenceIntersector(wall.Id, FindReferenceTarget.Face, default3DView);

 

- "The wall start point appears twice, the wall end point does not appear at all, and the points are not sorted by proximity:"

The point will get sortet by proximiti when the wall.id is specified to the ReferenceIntersector. Your points are sorted by element, then by proximity.

To get the wall endPoint, it is important to set the "step_outside" in 

.Where<ReferenceWithContext>( r => r.Proximity < wallLength + step_outside)

 

sufficiently large. The first point appears twice probably because "!pointList.First().IsAlmostEqualTo(wallOrigin)" in

if(!pointList.First().IsAlmostEqualTo(wallOrigin))
  {
    pointList.Insert(0, wallOrigin);
  }

will always return true (or !false) and therefore the  start point gets inserted. This is because the wallOrigins Z-coordinate is not neccesarily the same as the rayStart z-coordinate. I've changed the code to 

Curve c = (wall.Location as LocationCurve).Curve;
var wallStartPoint = new XYZ(c.GetEndPoint(0).X, c.GetEndPoint(0).Y, elevation);
var rayStart = new XYZ(wallStartPoint.X - wallLine.Direction.X, wallStartPoint.Y - wallLine.Direction.Y, wallStartPoint.Z);

[...]

if (!pointList.First().IsAlmostEqualTo(wallStartPoint))
                pointList.Insert(0, wallStartPoint);

 

My code woks quite well now, thank you for the suggestion to use a shooting ray 🙂

Please let me know if anything is unclear.

 

-Eirik

 

Message 8 of 24
Revitalizer
in reply to: Anonymous

Hi,

 

one addition to the default 3D view.

If there is a workshared environment, there is no "{3D}".

Instead of this, if a user creates a new default 3D view, this will be named like "{3d - username}".

Note the username suffix and the lowercase "d".

 

 

Revitalizer




Rudolf Honke
Software Developer
Mensch und Maschine





Message 9 of 24
jeremytammik
in reply to: Anonymous

Dear Erik,

 

Thank you for picking up my questions.

 

As you can see from my later post and GitHub submission, I also found and solved all the issues you mention my own way.

 

1. I just use the currently active user selected 3D view.

 

2. Yes, I noticed and added that as well.

 

3. Nope. The points I list above are the exact results of the call to Find, with nothing added or removed. I think my problems were caused by using elevation zero, so the ray was passing through the bottom edges of the wall faces. That way, it is hit or miss whether an intersection is found or not. I raised it off the floor and extend it beyond the wall at each end by 0.1 feet, and then I get reliable results. I am surprised that is not causing problems for you.

 

4. My code works well too. Check it out on GitHub. If you see any further possible improvements, please let me know.

 

Thank you!

 

Cheers,

 

Jeremy



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

Message 10 of 24
Revitalizer
in reply to: jeremytammik

Hi Jeremy,

 

instead of just elevating the one and single ray, what about using multiple rays, like a comb ?

 

 

Rudi

 




Rudolf Honke
Software Developer
Mensch und Maschine





Message 11 of 24
jeremytammik
in reply to: Revitalizer

Dear Rudi,

 

I totally agree.

 

Define a parameter specifying the minimum opening size, and then fan a comb up the entire wall side.

 

Retrieve all the resulting intersection points, sort them by proximity, eliminate duplicates, and Bob's your uncle.

 

Cheers, 

 

Jeremy



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

Message 12 of 24
jeremytammik
in reply to: Anonymous

The Building Coder summarised this discussion and published a full working solution:

 

http://thebuildingcoder.typepad.com/blog/2015/12/retrieving-wall-openings-and-sorting-points.html

 

Enjoy!

 

Cheers,

 

Jeremy



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

Message 13 of 24
Anonymous
in reply to: Revitalizer

What about just using  Wall.FindInserts(true, false, false, false)

then check the category of each returned elements for BuiltInCategory.OST_Doors, BuiltInCategory.OST_Windows and for rectangular openings, check for element Type: Opening.

 

 

 

 

Message 14 of 24
jeremytammik
in reply to: Anonymous

Dear Scott,

 

Read the second sentence of the original question  🙂

 

Cheers,

 

Jeremy



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

Message 15 of 24
Anonymous
in reply to: jeremytammik

To get the opening faces using the method I suggested you then loop through all perpendicular faces of the wall using Wall.GetGeneratingElementIds to match against your list of openings.

 

FindInserts used with the parameters i stated will find the windows whether or not anything is 'inserted' in the openings whatever that means.

I use this method quite frequently.

 

you could also just skip the FindInserts Step and just check each face of the wall fo those created by a door / window instance or opening element.

Message 16 of 24
jeremytammik
in reply to: Anonymous

Dear Scott,

 

Wow, that sounds very interesting and promising.

 

I guess I took Eirik's initial statement "I've tried FindInserts" a bit too much at face value without checking deeper.

 

Can you illustrate your suggestion in a little bit more detail, for instance with a snippet of pseudo or real source code?

 

Maybe I could add that to The Building Coder sample that I implemented to compare the two approaches.

 

Thank you!

 

Cheers,

 

Jeremy



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

Message 17 of 24
Anonymous
in reply to: jeremytammik

No Problem.

 

I just whipped up a Revit 2015 command as a demo: https://dl.dropboxusercontent.com/u/74965361/GetOpeningProfiles.zip

Message 18 of 24
jeremytammik
in reply to: Anonymous

Dear Scott,

 

Thank you very much for pointing out that FindInserts can be used after all, and for 'whipping up' this fine little sample.

 

I promoted this to a blog post and added it to The Building Coder samples:

 

http://thebuildingcoder.typepad.com/blog/2015/12/wall-opening-profiles-and-happy-holidays.html

 

I hope you like that.

 

Merry Xmas and a Happy New Year to you!

 

Best regards,

 

Jeremy



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

Message 19 of 24
Anonymous
in reply to: Anonymous

No problem, I'm a big fan of indirect geometric analysis and raycasting but performing concrete data matching using the database just takes the uncertainty out of it and gives me more confidence.
Message 20 of 24
jeremytammik
in reply to: Anonymous

Optimally, you have both at your disposal, a whole arsenal of tools.

 

Also, a test suite that exercises severaldifferent approaches and adds assertions to ensure that the results really match your expectations, e.g., using one tool to verify the results of another.

 

And benchmarking to make sure you really choose the right one for the right task.

 

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 Design & Make Report