Creating dimension for legend component - Dimension not showing

Creating dimension for legend component - Dimension not showing

jjesusdelpino
Advocate Advocate
3,841 Views
12 Replies
Message 1 of 13

Creating dimension for legend component - Dimension not showing

jjesusdelpino
Advocate
Advocate

Hi forum. I have been a few days trying to create dimensions for legends components with no succes.

The objetive is, create dimensions for legend components of width and height if its a reference available in the legend component. I satisfy that condition by intersecting BoundingBox of Legend Component and Geometry of the Symbol.  In this code, the legends its created. I get his id and can see it with Revit LookUp (where indicates that the dimensions belongs to the proper legend view). But still the dimension is not being showed.

 

I have also consulted this posts: https://forums.autodesk.com/t5/revit-api-forum/created-dimension-not-showing-in-document/td-p/738043... https://forums.autodesk.com/t5/revit-api-forum/dimension-a-legend-component/td-p/8673305 which make me understand that I had to use GetSymbolGeometry instead that GetInstanceGeometry (and aply the translation transform) that but the legend component still now shows. Also consulted and tried what this post https://forums.autodesk.com/t5/revit-api-forum/copy-dimensions-from-a-view-to-another/m-p/7226217 expose but not getting any result either.

 

The code is:

 

		public void CreateDimension()
		{
			UIDocument uidoc = this.ActiveUIDocument;
			Autodesk.Revit.DB.Document doc = this.ActiveUIDocument.Document;
			
			ElementId Id = new ElementId(532709); //This is de Id of the legend component. Just put yourself if you want to try.
			Element element = doc.GetElement(Id);
			
			ReferenceArray RA = new ReferenceArray();
					
			BoundingBoxXYZ BB = element.get_BoundingBox(doc.ActiveView); 
			XYZ TopL = new XYZ(BB.Min.X, BB.Max.Y, 0);
			XYZ BotL = new XYZ(BB.Min.X, BB.Min.Y, 0);
			XYZ TopR = new XYZ(BB.Max.X, BB.Max.Y, 0);
			XYZ BotR = new XYZ(BB.Max.X, BB.Min.Y, 0);
					
			Curve VerticalLeft= Line.CreateBound(TopL, BotL) as Curve;
			Curve VerticalRight = Line.CreateBound(TopR, BotR) as Curve;
			Curve HorizontalSupe = Line.CreateBound(TopL, TopR) as Curve;
			Curve HorizontalInfe = Line.CreateBound(BotL, BotR) as Curve;
			
			List<Curve> Curvas = new List<Curve>{VerticalIzq, VerticalDer, HorizontalSupe, HorizontalInfe}; //List of curves to intersect with symbol geometry later
			
			List<XYZ> Puntos = new List<XYZ>(); //To get XYZs for line for dimension

		    List<Edge> edges = new List<Edge>();
		    Options options = new Options{ComputeReferences = true, IncludeNonVisibleObjects = false};
		    var geoEle = element.get_Geometry(options);
		    
		    foreach (Curve curve in Curvas)
		   	{
			    foreach (var item in geoEle)
			    {
			        GeometryInstance geometryInstance = item as GeometryInstance;

			        if (geometryInstance != null)
			       	{
			            GeometryElement geometryElement = geometryInstance.GetSymbolGeometry();
			            BoundingBoxXYZ BBS = geometryElement.GetBoundingBox();
			            XYZ MinBB = new XYZ(BBS.Min.X, BBS.Min.Y, 0);
			            XYZ Move = new XYZ(AbajoIzq.X - MinBB.X, AbajoIzq.Y - MinBB.Y, 0);
			            
			            Transform Transformacion = Transform.CreateTranslation(Move);
			            
			            GeometryElement TgeometryElement = geometryElement.GetTransformed(Transformacion);

			            foreach (GeometryObject geoObject in TgeometryElement)
			            {
			                if (geoObject is Solid)
			                {
			                    Solid solid = geoObject as Solid;
			                    if (!solid.Edges.IsEmpty)
			                    {
	
			                    	foreach (Edge edge in solid.Edges)
			                    	{
			                    		try //Put this try/catch to avoid "curve is to short" error.
			                    		{
				                    		Line line1 = edge.AsCurve() as Line;
				                    		XYZ Inicio = new XYZ(line1.GetEndPoint(0).X, line1.GetEndPoint(0).Y, 0);
				                    		XYZ Fin = new XYZ(line1.GetEndPoint(1).X, line1.GetEndPoint(1).Y, 0);
				                    		Line line = Line.CreateBound(Inicio,Fin);
				                    		SetComparisonResult src=curve.Intersect(line);
				                    		string SRCs = SRC.ToString();  //Check what type of intersection there it is  
					                    	
		                    				if (SRCs == "Equal" || SRCs == "Subset" || SRCs == "Superset")
				                    		{
				                    			RA.Append(edge.Reference);
				                    			XYZ Punto = new XYZ((edge.AsCurve().GetEndPoint(0).X + edge.AsCurve().GetEndPoint(1).X)/2,(edge.AsCurve().GetEndPoint(0).Y + 
				                    			edge.AsCurve().GetEndPoint(1).Y)/2,0); //Get the mid point of the edge reference to use it as line for the dimension.
				                    			Puntos.Add(Punto);
				                    			goto Fin1;
				                    		}	    	
			                    		}
			                    		catch{}  
			                    	} 	
		                    	}	
		                    }
		                }
		                
		        	}	
		    	}
			    TaskDialog.Show("Hello", "No references found for " + curve.ToString());
				goto Fin2; 
			    Fin1:;
		    }
		   	
		   	Line LineaDimension = Line.CreateBound(Puntos[0],Puntos[1]);

		   	using (Transaction t = new Transaction(doc, "family test"))
			{
				t.Start();

				Dimension dim = doc.Create.NewDimension(doc.ActiveView, LineaDimension, RA);
				
				TaskDialog.Show("HOLA", dim.Id.ToString());
				
				doc.Regenerate();

				t.Commit();		
			}	
		   Fin2:;  		
		}

Maybe I have some typo erros cause I have translated some of the code to be more readable by you. But the rest of the code (the intersection part and that stuff) is working ok as the dimensions is being created but not showed. I cant figure out any more whats happening so if you have any idea about this... Thanks!

0 Likes
Accepted solutions (1)
3,842 Views
12 Replies
Replies (12)
Message 2 of 13

TripleM-Dev.net
Advisor
Advisor

Hi,

 

I can't see what type of legend component it is. First try it in the UI, can you dimension there?

A Detail item can be dimensioned, a Door can't etc...

 

- Michel

0 Likes
Message 3 of 13

jjesusdelpino
Advocate
Advocate

Hi Michel, thanks for your anwser. 

 

I can put dimensions normally with UI. Im doing this with family Instances (doors, window, bath, furniture, etc). 

 

Regards

0 Likes
Message 4 of 13

TripleM-Dev.net
Advisor
Advisor

Ok, I see what you mean, Correct.

 

I've been trying something;

Dimension the element in the UI and look at the References of the dimension.

Seems the Dimension with Code takes the wrong element for the reference (Stable reference is missing the instance GUID).

 

If I input 2 Full StableReferences strings as references the dimension works by Code (so is possible).

For the Faces; adding the GUID of the Element to the StableReference of the Face.Reference.ConvertToStableReference I manage to dimension elements. Edges not tested.

 

I will see if I can up with a full sample, can you post a screenshot of what you trying to dimension?

 

Dimension Reference By CodeDimension Reference By Code

 

Dimension Reference By UIDimension Reference By UI

 

 

 

 

- Michel

0 Likes
Message 5 of 13

jjesusdelpino
Advocate
Advocate

Thanks Michel, thats an approach that hadn´t come to my mind as Im not acquainted with the concept of "StableReference". Im going to study that concept, test your suggestion with edges and come with the results.

 

The objetive is make dimensions for every item that allows having a full widht/height dimension. Whatever category.

Capture.JPG

Regards

0 Likes
Message 6 of 13

jjesusdelpino
Advocate
Advocate

Update with discovers and questions. Checked https://thebuildingcoder.typepad.com/blog/2016/04/stable-reference-string-magic-voodoo.html that has led to me to start understanding StableReferences. Anyway I still have to make deep understanding about this theme. Anyway, at this time, in terms of my post this is what I have tried and discovered. Next example is with a Bathtub family.

 

StableReference of dimension through UI

b5317376-1ade-404d-8253-43d0a382ee6e-000820e5:0:INSTANCE:b5317376-1ade-404d-8253-43d0a382ee6e-000820e6:0:1:117:SURFACE

 

Appearently it has more divisions (":") than it should according to Jeremy´s post. Anyway from this I can assume:

- b5317376-1ade-404d-8253-43d0a382ee6e-000820e5     --> BathTub GUID. Checked in the model.

- 0  --> Always 0. Dont know why but seems ok.

- INSTANCE --> Shows that references its a family instance. Ok.

- b5317376-1ade-404d-8253-43d0a382ee6e-000820e6   --> According to Jeremy´s post this is Family Symbol GUID. But in this case its not. Its the same GUID than BathTubGUID plus one in last digit. Also (so strange) this number is the same that the missing dimension has on his first place (see next).

- 0:1:117 one of this numbers must be the index of the referenced geometry object within the FamilySymbol Instance's geometry table, probably 117, but not sure about other two.

- SURFACE. the type of geometry referenced.

 

 

 

 

StableReference missing dimension

b5317376-1ade-404d-8253-43d0a382ee6e-000820e6:0:1:30:LINEAR

-b5317376-1ade-404d-8253-43d0a382ee6e      --> This first item should be BathTub GUID. Its the same that appears in 4th index previously. BathThub GUID plus one in last digit.

- 0   --> Same that previously

- 1:30 ---> One of this should be the index of the referenced geometry object within the FamilySymbol Instance's geometry table, probably 30, but not sure anyway.

- LINEAR --> type of reference.

 

Other checks.

BathTub GUID

b5317376-1ade-404d-8253-43d0a382ee6e-000820e5

 

BathTub Family Symbol GUID

ac0917a2-994d-4015-84aa-e0feb2ff5340-00066bf9

 

Dimension GUID through UI

337f9c8b-4482-4abd-85e8-59ca8d0f743c-00083908

 

 

All this is kind of messy. The only conclusion I can reach is that with this procedure all the dimension´s references gets corrupt. Maybe in best case scenario I can figure out the index of the referenced geometry and compose the StableReference manually. Will keep updating.

Regards. 

0 Likes
Message 7 of 13

TripleM-Dev.net
Advisor
Advisor

Hi,

 

A = Take the UniqueId of the instance element (in this case the Legend component)

B = Take the string from Reference.ConvertToStableRepresentation from the geometry nested element (like Face, line etc)

 

Full Stable reference = A + ":0:INSTANCE:" + B

0 = as far as we know always 0, maybe for future support?

 

Reference ritem = Reference.ParseFromStableRepresentation(doc, stableReferencestring);

 

- Michel 

Message 8 of 13

jjesusdelpino
Advocate
Advocate

I think I have found something maybe useful. Just checked that plus one thing in the StableReference with other elements. It turns out that thats the natural behaviour with Legend Components. Checked with one door.

 

Family Symbol door

79456ea1-303f-4cb1-8e18-ea9522527c8b-0007afb1

 

Door Legend Component GUID

b5317376-1ade-404d-8253-43d0a382ee6e-000820e5

 

StableReference Dimension through UI

b5317376-1ade-404d-8253-43d0a382ee6e-000820e5:0:INSTANCE:b5317376-1ade-404d-8253-43d0a382ee6e-000820e6:0:1:117:SURFACE

 

So, it looks like legends components dont follow the rules expose in Jeremy´s post. In this case. Token3 instead of being the family symbol GUID, its Legend Component GUID again, plus one in the last digit. If this proves to be the natural behaviour we are getting closer to compose the StableReference manually.

 

What its disconcerting me now its that, the end of the reference (0:1:117:SURFACE) its the same in the door and in the bathtub example, what led me to think that the information that usualy provides token4, (the index of the referenced geometry object within the FamilySymbol Instance's geometry table) is not given at any moment in this new structure. Will keep investigatin and updating. Regards.

0 Likes
Message 9 of 13

TripleM-Dev.net
Advisor
Advisor
Accepted solution

Try this, it finds all Planarfaces and orders them together based on orientation by FaceNormal (simple example).

After that, a dimension is created for all collection with more than 1 face.

It also creates dimension you can't see!, normally I would compare it to the view's orientation but a legend is 2D.

So the direction should be retieved from the legend component (parameter LEGEND_COMPONENT_VIEW)

 

Below the result of the code, the vertical dimension is horizontal at first, move it a little and it jumps vertical (placement line correction needed in code). Interesting concept.

 

ResultResult

 

 

 

 

 

 

 

 

 

 

 

And the Code used, removed as much as possible

Will need optimalizations and I would place foreach loops in seperate methods.

[Transaction(TransactionMode.Manual)]
    public class TestCommand_C04_CreateDimension : IExternalCommand
    { 
		public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            UIDocument uidoc = commandData.Application.ActiveUIDocument;
            Autodesk.Revit.DB.Document doc = uidoc.Document;

			ElementId Id = new ElementId(342011); //This is de Id of the legend component. Just put yourself if you want to try.
			Element element = doc.GetElement(Id);
			 
			String elementGuid = element.UniqueId + ":0:INSTANCE:";
		   
			Options options = new Options { ComputeReferences = true, IncludeNonVisibleObjects = false };
			options.View = doc.ActiveView;
			var geoEle = element.get_Geometry(options);
		 
			Dictionary<PlanarFace, List<PlanarFace>> FacesCollection = new Dictionary<PlanarFace, List<PlanarFace>>(); 

			foreach (var item in geoEle)
			{
				GeometryInstance geometryInstance = item as GeometryInstance;

				if (geometryInstance != null)
				{
					GeometryElement geometryElement = geometryInstance.GetSymbolGeometry(geometryInstance.Transform); 

					foreach (GeometryObject geoObject in geometryElement)
					{
						if (geoObject is Solid)
						{
							Solid solid = geoObject as Solid;

							if (!solid.Faces.IsEmpty)
							{
								 
								foreach (Face face in solid.Faces)
								{

									if (face is PlanarFace)
									{
										PlanarFace pface = (PlanarFace)face;
										  
										bool added = false;
										foreach (KeyValuePair<PlanarFace, List<PlanarFace>> faceitem in FacesCollection)
										{ 
											double Result = Math.Abs( faceitem.Key.FaceNormal.DotProduct(pface.FaceNormal)); // Simple compare of Planarface normal.
											if (Result == 1)
											{
												faceitem.Value.Add(pface);
												added = true; 
											} 
										}
										if (!added)
										{
											FacesCollection.Add(pface, new List<PlanarFace> { pface });
										}
										 
									}

								}
								 
							}

						}
					}

				}
			}

			 
			Line LineaDimension = Line.CreateBound(new XYZ(0,0,0), new XYZ(10,0,0)); // vector should match the face orientation.

			using (Transaction t = new Transaction(doc, "family test"))
			{
				t.Start();
				 
				foreach (KeyValuePair<PlanarFace, List<PlanarFace>> faceitems in FacesCollection)
				{
					if (faceitems.Value.Count > 1)
					{ 
						ReferenceArray RAS = new ReferenceArray();
						foreach (PlanarFace faceitem in faceitems.Value)
						{  
							RAS.Append(Reference.ParseFromStableRepresentation(doc, elementGuid + faceitem.Reference.ConvertToStableRepresentation(doc))); 
						} 
						Dimension dim = doc.Create.NewDimension(doc.ActiveView, LineaDimension, RAS); 
					}
				}

				t.Commit();
			} 

			return Result.Succeeded;
        }
    }

 

- Michel

0 Likes
Message 10 of 13

jjesusdelpino
Advocate
Advocate

Manage to create the StableReference as you suggest. The string is ok but after creating the dimension applying the StableReferences, once dimension is created, the StableReference go back to same again. As result of this I believe that what Im failing in its the edge/reference selection. Regards

0 Likes
Message 11 of 13

jjesusdelpino
Advocate
Advocate

Wow, thank you so much. Gonna check this code carefully. Will be back. Regards.

0 Likes
Message 12 of 13

jjesusdelpino
Advocate
Advocate

Hi again, Im still working on this. I dont have many time as this is not my professional ocupation right now. While I do this I mark your post as the accepted solution cause I think is the better approach to this. Anyway, in case that its of interest of someone reading this, I have faced a couple of problems with this code:

 

-First one. Geometryelement doesn´t show "real" geometry faces for complex geometry. If I run this on a prismatic pure volume, the output is:

*2 Solids, one of them without edges or faces, and the other one containing the real geometry (6 faces and 12 edges)

Captura de pantalla 2020-05-04 18.52.49.png

While if done with an standar door:

*Some lines and 2 solids, in this case both of them with 0 faces or edges.

Captura de pantalla 2020-05-04 18.55.21.png 

This behaviour lead me to think that this should be done with other entity.

 

 

-Second one is figuring out how to manage that faces to use must be the total widht and height of the component (those intersecting BoundingBox). I thought to use Face.Intersect Method (Face), but then I struggle trying to create a face from bounding box.

 

Thank you TripleM-Dev.net. Regards.

0 Likes
Message 13 of 13

zefreestijl
Advocate
Advocate
Hi, thanks for your method,

did a lot of search and found your ways,

it works and the answer is very clear and simple.

Thank you :]
0 Likes