Dimension between walls corners using Revit's API

Dimension between walls corners using Revit's API

Anonymous
Not applicable
6,452 Views
27 Replies
Message 1 of 28

Dimension between walls corners using Revit's API

Anonymous
Not applicable

Hi,

 

When you're using Revit's Dimension Tool you can hit tab several times to switch between things to select. One of the options looks like it's the corner of the wall:

End.png

Corner.png

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

My question is: How do I get these apparent "Intersection of [..." via the API as References? I want to generate a dimension between them with code.

 

 

They don't seem to actually be intersections. The first one doesn't intersect with anything but itself.

 

Any information, including what these dots actually are, would be appreciated!

0 Likes
Accepted solutions (1)
6,453 Views
27 Replies
Replies (27)
Message 2 of 28

jeremytammik
Autodesk
Autodesk

Dear Thomas,

 

You can ask Revit to generate references for you that you can use for dimensioning purposes when you query an element for its geometry.

 

Check out these two examples of generating dimensioning between walls:

 

http://thebuildingcoder.typepad.com/blog/2011/02/dimension-walls-by-iterating-faces.html

 

http://thebuildingcoder.typepad.com/blog/2011/02/dimension-walls-using-findreferencesbydirection.htm...

 

They should provide all you need to solve the task you describe.

 

Also note that all the code presented there is included in and maintained (and thus up to date) with The Building Coder samples:

 

https://github.com/jeremytammik/the_building_coder_samples

 

Good luck and have fun!

 

Cheers

 

Jeremy



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

0 Likes
Message 3 of 28

Anonymous
Not applicable

Thanks Jeremy!

 

I've actually created a plugin using the instructions in those blog posts and it works fine for getting faces. I've implemented and tweaked both the "By Direction" and the HostObjectUtils.GetSideFaces(); workflows to suit my needs. I don't think that corners count as faces though and I don't think I can use a ReferenceIntersector to hit a point in space, especially if I don't know where it is.

 

I imagine the corner points are really the edge where the faces join, viewed from above. I can get the outside face of a wall with the HostObjectUtils class and then iterate over the edges of it but how do I robustly get the side edges?

 

I have no guarantee that the wall will have two edges that go straight up and two that are perfectly horizontal so while comparing endpoints on the curves of the edges could work 90% of the time, that 10% will be trouble. If the wall has openings, that'll create more edges presumably that'll get mixed in with everything else, making it even harder to figure out which ones are the edges I need.

 

Is there a shortcut, like with HostObjectUtils, to get the exterior corners of a wall?

0 Likes
Message 4 of 28

jeremytammik
Autodesk
Autodesk

I believe Revit can compute and maintain stable references to all aspects of the wall geometry, including each face, edge and vertex. Since your dimension lives in a 2D plane (I assume) and can be on the floor level (I assume) and your walls have corners on the floor level (I assume), I would assume that you can query the wall for its geometry, request references, find the vertices you want, and dimension from those. Please let us know how it goes. Thank you! Good luck, and happy weekend to you.



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

0 Likes
Message 5 of 28

Anonymous
Not applicable

Hope you had a good weekend!

 

The problem I really have is figuring out which of all the vertices extracted from the model are the ones I want.

 

I want either the vertices or the vertical edges of the external Face of the wall.

 

I can get the reference of the external face of the wall using HostObjectUtils but I can't interrogate that reference for geometric objects like edges and vertices. Is there a way of getting a geometric object from a Reference? I tried plugging it into GetElement() but that, unsurprisingly, returned the Element the geometric object belonged to. 

 

If I can't get the geometric objects out of the reference, how do I determine which face of the geometry of the wall is the external face? I could perform vector maths to find the faces perpendicular to the Orientation property of the wall and then find the furthest face in the direction of Orientation but is there a simpler way? How does HostObjectUtils do it?

 

 

Once I have the external face I could find the two vertices closest to the Base Constraint floor + Base Offset and dimension between them, using the Curve of the wall to get the Line of the dimension.

0 Likes
Message 6 of 28

jeremytammik
Autodesk
Autodesk
Accepted solution

Element.GetGeometryObjectFromReference



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

Message 7 of 28

Anonymous
Not applicable

Three days later and I've made it work!

 

I currently :

  1. Get the exterior face reference with HostObjectUtils,
  2. Get the actual face using GetGeometryObjectFromReference(),
  3. Extract the geometry from the wall using an Options object that gathers references,
  4. Compare all the faces on the Solid of the wall to the exterior face I got in step 2 to get the exterior face with references,
  5. Get all the edges of the face with 
  6. Evaluate(0.5) all the edges of that face to get the midpoint and therefore a general idea of where that edge is in the world,
  7. Ordering the edges in a list such that the edge with the midpoint with the lowest Z value is first and the highest Z value is last,
  8. Subtracting the lowest Z (First()) from the highest Z (Last()) to get the "height" of the wall,
  9. Add a third of the height to the lowest Z to get a "maximum height" in worldspace,
  10. Remove any edges from the list who's midpoint's (Evaluate(0.5)) Z is higher than the maximum height.
  11. Get the first end Reference of the First() edge in the list and the last end Reference of the Last() edge of the list.
  12. Dimension between those References.

 

 

 

0 Likes
Message 8 of 28

Anonymous
Not applicable

To be a stickler, the original question isn't answered but my problem is solved so I guess we're done. To anyone wondering what those dots are, they seem to be the edges of the face, seen from above.

They could also be the points on the ends of the lines but as there are 6 different points occupying that space in my pictures (the ends of all of the edges that join there) and as every other time you hit tab you select an edge, it seems most likely that it's the edge.

0 Likes
Message 9 of 28

jeremytammik
Autodesk
Autodesk

Dear Thomas,

 

Congratulations on the deep research and successful solution!

 

Thank you very much for sharing the description.

 

I am glad the task is solved, even if the question is not completely answered.

 

I there any possibility you could post an actual code snippet or two?

 

I would love to document this impressive solution of your more fully, e.g. in a blog post, for future reference.

 

By the way, what dots are you referring to?

 

Thank you!

 

Cheers,

 

Jeremy



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

0 Likes
Message 10 of 28

Anonymous
Not applicable

I'd be flattered to have my solution documented!

 

I'm not sure what my employer's thoughts are regarding sharing code and given that the code is legally speaking theirs I'll ask them first.

 

I've actually run into some small problems when applying my code to more complex walls. It works fine on a single, free standing walls but once you start joining walls together and putting doors in them you start getting some unintended effects. I'll try to clear it up and get back to you.

 

In my first post, the pictures show little squares on the corners of the walls which I've been referring to as dots. They come up when you select the Dimension tool and hit tab a few times while hovering your mouse over the wall ends.

0 Likes
Message 11 of 28

Anonymous
Not applicable
It's been several meetings and lots of discussion and the conclusion as to whether to share code is a yes, provided it is a joint writing effort between yourself and Ryder so that we can keep it consistent with Ryder's other public content. I've been asked to pull you into a conversation with our Communications Leader, Helen Whitfield to talk about documenting the solution if that's OK.
0 Likes
Message 12 of 28

jeremytammik
Autodesk
Autodesk

Thank you for your update!

 

Sorry to hear that this is causing so much discussion, and glad to hear that you reached a positive conclusion.

 

Yes, sure, with pleasure, absolutely OK.

 

How would you like us to get into touch?



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

0 Likes
Message 13 of 28

Anonymous
Not applicable

It's no bother! We're just getting our teeth into software development as a company so we've treated this as a precedent setting case. There was a lot of figuring things out on a more general scale which is what most of the meetings were about. Discussion is good!

 

If you throw me whichever email address you'd like to use through a private message, I'll set up an introductory email between all of us.

0 Likes
Message 14 of 28

jeremytammik
Autodesk
Autodesk

Dear Thomas,

 

Thank you for your update.

 

I escalated this to an official ADN case 12243183 [Dimension between walls corners using Revit's API] now.

 

You can use that to get in touch with me and anyone else in the AEC developer support team, in case I am unavailable for any reason.

 

We can use that as a container for the case notes and also attache sample material to it.

 

I hope this helps.

 

Best regards,

 

Jeremy



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

0 Likes
Message 15 of 28

dylanj
Contributor
Contributor

Jeremy,

 

Has this code been released anywhere? I would love to get my hands on it as i am running into the exact same issues listed above.

 

Kind Regards,

 

Dylan

0 Likes
Message 16 of 28

Anonymous
Not applicable
No code is around I'm afraid. I can walk you through what I did and take a look at any code that you've written to give you specific pointers but I no longer work for the company who own the code I wrote so I can't directly post my code any more.
0 Likes
Message 17 of 28

dylanj
Contributor
Contributor

So i actually go the got it to work using a Reference intersector. However it only works with openings created with window or door families. It return a reference for openings created "By face", but the reference return doesn't seem to be stable enough to dimension to. My code is nearly a thousand lines long so here's the important bits.

 

private List<Reference> GetWallOpenings(Wall wall, View3D view )
		{
			
		  Document doc = wall.Document;
		  Level level = doc.GetElement( wall.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 offsetOut = _offset * new UV( wallDirection.X, wallDirection.Y );
		 
		  XYZ rayStart = new XYZ( wallOrigin.X - offsetOut.U, wallOrigin.Y - offsetOut.V, elevation + _offset );
		 
		  ReferenceIntersector intersector = new ReferenceIntersector( wall.Id, FindReferenceTarget.Face, view );
		 
		  IList<ReferenceWithContext> refs = intersector.Find( rayStart, wallDirection );

List<Reference> faceReferenceList = new List<Reference>( refs
		    .Where<ReferenceWithContext>( r => IsSurface(
		      r.GetReference() ) )
		    .Where<ReferenceWithContext>( r => r.Proximity
		      < wallLength + _offset + _offset )
		    .Select<ReferenceWithContext, Reference>( r
		      => r.GetReference()));

 return faceReferenceList;
		}
public void test()
		  {
		  	UIDocument uidoc = this.ActiveUIDocument;
		  	Document doc = uidoc.Document;
		  	
		  	ReferenceArray refs = new ReferenceArray();
		  
		  	Reference myRef = uidoc.Selection.PickObject(ObjectType.Element, new MySelectionFilter("Walls"), "Select a wall");
		  	
		  	Wall wall = doc.GetElement(myRef) as Wall;
		  	
		  	 	
		  	//Creates an element e from the selected object reference -- this will be the wall element
            Element e = doc.GetElement(myRef);

            //Creates a selection filter to dump objects in for later selection
            ICollection<ElementId> selSet = new List<ElementId>();

            //Gets the bounding box of the selected wall element picked above
            BoundingBoxXYZ bb = e.get_BoundingBox(doc.ActiveView);

            //adds a buffer to the bounding box to ensure all elements are contained within the box
            XYZ buffer = new XYZ(0.1, 0.1, 0.1);

            //creates an ouline based on the boundingbox corners of the panel and adds the buffer
            Outline outline = new Outline(bb.Min - buffer, bb.Max + buffer);

            //filters the selection by the bounding box of the selected object
            //the "true" statement inverts the selection and selects all other objects
            BoundingBoxIsInsideFilter bbfilter = new BoundingBoxIsInsideFilter(outline, false);

            ICollection<BuiltInCategory> bcat = new List<BuiltInCategory>();
                
                
            //creates a new filtered element collector that filters by the active view settings
            FilteredElementCollector collector = new FilteredElementCollector(doc, doc.ActiveView.Id);

            //collects all objects that pass through the requirements of the bbfilter
            collector.WherePasses(bbfilter);

            //add all levels and grids to filter -- these are filtered out by the viewtemplate, but are nice to have
            bcat.Add(BuiltInCategory.OST_StructConnections);

            //create new multi category filter
            ElementMulticategoryFilter multiCatFilter = new ElementMulticategoryFilter(bcat);

            
            //create new filtered element collector, add the passing levels and grids, then remove them from the selection
            foreach (Element el in collector.WherePasses(multiCatFilter))
            {
            	if (el.Name.Equals("EMBEDS"))
            	{
            		selSet.Add(el.Id);
            	}
                
            }

		  	
               
                
		  	
		  	XYZ[] pts = new XYZ[99];
  			
		  	//View3D view = doc.ActiveView as View3D;
		  	View3D view = Get3dView(doc);
		  	

                        //THIS IS WHERE IT RETURNS THE WALL OPENING REFERENCES.  HOWEVER THEY ONLY ARE ABLE TO BE USED FOR DIMENSIONS IF THE OPENING IS CREATED USING A FAMILY SUCH AS A WINDOW OR DOOR. OPENING BY FACE/WALL DOES NOT WORK, EVEN THOUGH IT RETURNS PROPER REFERENCES

		  	List<Reference> openings = GetWallOpenings(e as Wall, view );
		  	
		  	foreach (Reference reference in openings) {
		  		refs.Append(reference);
		  	}
		  	
		  	TaskDialog.Show("REFERE",refs.Size.ToString());
		  	
			Curve wallLocation = (wall.Location as LocationCurve).Curve;

		  	int i = 0;
		  	
		  	foreach (ElementId ele in selSet) {
		  		FamilyInstance fi = doc.GetElement(ele) as FamilyInstance;
		  		Reference reference = ScottWilsonVoodooMagic.GetSpecialFamilyReference(fi,ScottWilsonVoodooMagic.SpecialReferenceType.CenterLR,doc);
		  		refs.Append(reference);
		  		
		  		pts[i] = ( fi.Location as LocationPoint ).Point;
		  		i++;
		  	}

		  	XYZ offset = new XYZ(0,0,4);
		  	
		  	Line line = Line.CreateBound( pts[0]+offset, pts[1]+offset );
		  	
            
            using( Transaction t = new Transaction( doc ) )
      		{
        		t.Start( "dimension embeds" );

        		Dimension dim = doc.Create.NewDimension(doc.ActiveView, line, refs );

        		t.Commit();
      		}
            
                
		  	
		  }

sorry for the messy code. i'm in the process of cleaning it up. it also uses Jeremy's Utils classes.

 

0 Likes
Message 18 of 28

Anonymous
Not applicable

Your code isn't messy! Well, I can read it at any rate which is good enough for me. It's got comments and everything!

 

So my first port of call would be to make sure that the faces that you're trying to dim between are all parallel. It's been a few months since I looked at my own dimensioning code but I seem to remember it ignoring non-parallel faces.

 

Can you make a dimension using only the references that are currently not working? Can you make one manually too? Can you make the dimension you want to make, manually? (AKA Can you blame the model for not working instead of your code? Always a relief when you can do that!)

 

I did my solution by picking apart the geometry of the wall and figuring out which edges were part of the face I wanted to dimension and parallel with each other and the end of the wall and then dimensioning between them. Which is a pain and not something I recommend if you have an alternative.

 

Let me know what happens when you try to dimension between just the dimensions that aren't working.

Message 19 of 28

dylanj
Contributor
Contributor
I will have a look, however I am able to dimension to the faces manually as the wall opening tool only allows vertical square openings.

I may try to dimension to an edge bounding the face if i can't get this to work. I will report back when I find time to test.

Thanks for the help!
0 Likes
Message 20 of 28

jeremytammik
Autodesk
Autodesk

Hi @dylanj and @Anonymous, I am thinking of publishing this partial result now, since it is interesting in itself with lots of great ideas listed. Do you have anything to add right now? Thank you!

 



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

0 Likes