Know the size in legend components

Know the size in legend components

jjesusdelpino
Advocate Advocate
1,838 Views
12 Replies
Message 1 of 13

Know the size in legend components

jjesusdelpino
Advocate
Advocate

Hi forum,

 

Im trying to know the widht and height of legend componentes on legend views. For this, my approach has been this function with BoundingBoxes in a macro:

 

        public void WidhtAndHeight()
        {			
			UIDocument uidoc = this.ActiveUIDocument;
			Autodesk.Revit.DB.Document doc = this.ActiveUIDocument.Document;
		 	
			Reference myRef = uidoc.Selection.PickObject(ObjectType.Element);
	
			Element ele = doc.GetElement(myRef);
            
            BoundingBoxXYZ BB = ele.get_BoundingBox(uidoc.ActiveView);
            double width = BB.Max.X - BB.Min.X;
            double height = BB.Max.Y - BB.Min.Y;
            
            width = width *0.3048; //convert feet to meters
            height = height *0.3048; //convert feet to meters

            TaskDialog.Show("HELLO", "Width is: " + width.ToString());
            TaskDialog.Show("HELLO", "Height is: " + height.ToString());
        }

   It looks to me like it should work, and it fact it does, but not always. This are the result Im getting applying this function to some windows and doors:

Captura.JPG

The dimension mark the real size. While the number down every door/window its the result Im getting with this function. As you can see, Im getting the right dimensions for the 3 bigger doors. Then a little more in the 4 door, and much more with the smallest door. While in the windows im getting always 0.05m more than the real size.

 

I cant see the correlation between the results. The strangest thing is that in the door its giving me right results for the bigger ones but the two others even if they are not the real dimension, they are not even proportional so Im lost here. I have used the doors and windows that are by default in architectural template (attach file). Also Im using 0.01m in "host length" parameter and dont see any diference if I set it to more.

 

Any ideas about what is "seeing" the bounding box property? Any other consistent way to do this?

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

TripleM-Dev.net
Advisor
Advisor

Hi,

 

You're using ActiveView to retrieve the boundingbox, so it's possible a invisible line/refplane inside the family is taken into account.

 

I find the boundingbox usefull for a initial determination of size, but not to get exact dimensions for geometry.

2 alternatives:

- Iterate the geometry itself, Geometry Property , not sure if this works properly in a Legend?

- Use the Width/Height parameter in the family? (if it also actually is used as the Width/height of the family)

 

Also I would suggest using ConvertFromInternalUnits Method to convert value from internal units to another unit format

 

- Michel

0 Likes
Message 3 of 13

jjesusdelpino
Advocate
Advocate

Hi Michel thanks for your anwser,

 

I have examinated the families but I havent found between this dimensions and any hide plan or something. Maybe thats the case but I havent found anything. Considering option 2, that is discarded because not every family will have those parameters.

Considering option 1, I had discarded that possibility because what I can acces from that property are some solids and what I want its the width of the visual representation of the legend. But now Im thinking that maybe I can look for the MaxX ,MaxY and MaxZ coordinates for all that solids. Then depending of the orientation of the legend view (elevation, section, plant, etc) width would be some coordinate and height other. Im guessing that if orientation works the same way for every component this should work. Gonna try this. Thanks.

0 Likes
Message 4 of 13

jjesusdelpino
Advocate
Advocate

Hi again Michel,

 

Finally managed to solve this with geometry element iterating every solid item. Until now im getting the real values. Thanks also for the "ConvertFromInternalUnits" Tip.

Solution:

		public void WidhtAndHeight()
        {			
			UIDocument uidoc = this.ActiveUIDocument;
			Autodesk.Revit.DB.Document doc = this.ActiveUIDocument.Document;
		 	
			Reference myRef = uidoc.Selection.PickObject(ObjectType.Element);
	
			Element ele = doc.GetElement(myRef);
            
			Options options = new Options{ComputeReferences = true, IncludeNonVisibleObjects = false};
			
			GeometryElement Geometria = ele.get_Geometry(options);
			
			double MaxX = 0;
			double MaxY = 0;
			double MinX = 0;
			double MinY = 0;
			double MaxZ = 0;
			double MinZ = 0;
			
			foreach (var item in Geometria)
		    {
		        GeometryInstance geometryInstance = item as GeometryInstance;
		        if (geometryInstance != null)
		        {
		            GeometryElement geometryElement = geometryInstance.GetSymbolGeometry();
		            foreach (GeometryObject geoObject in geometryElement)
		            {
		                if (geoObject is Solid)
		                {
		                	Solid solido = geoObject as Solid;
		                	if (solido.Volume > 0.1)
		                	{
		                		BoundingBoxXYZ BB = solido.GetBoundingBox();
		                			
		                		if (BB.Max.X > MaxX)
		                		{
		                			MaxX = BB.Max.X;
		                		}
		                		if (BB.Min.X < MinX)
		                		{
		                			MinX = BB.Min.X;
		                		}
		                		
		                		if (BB.Max.Y > MaxY)
		                		{
		                			MaxY = BB.Max.Y;
		                		}
		                		if (BB.Min.Y < MinY)
		                		{
		                			MinY = BB.Min.Y;
		                		}
		                		
		                		if (BB.Max.Z > MaxZ)
		                		{
		                			MaxZ = BB.Max.Z;
		                		}
		                		if (BB.Min.Z < MinZ)
		                		{
		                			MinZ = BB.Min.Z;
		                		}
		                	}
		                }
		            }
		        }
			}
			
			TaskDialog.Show("HOLA", "El ancho es: " + UnitUtils.ConvertFromInternalUnits((MaxX - MinX), DisplayUnitType.DUT_METERS).ToString());
			TaskDialog.Show("HOLA", "El alto es: " + UnitUtils.ConvertFromInternalUnits((MaxY - MinY), DisplayUnitType.DUT_METERS).ToString());
			TaskDialog.Show("HOLA", "El profundo es: " + UnitUtils.ConvertFromInternalUnits((MaxZ - MinZ), DisplayUnitType.DUT_METERS).ToString());
			
        }

Regards.

 

0 Likes
Message 5 of 13

jjesusdelpino
Advocate
Advocate

Despite of this giving the width of any element. Its failing for a second purpose that I forgot. I was using the other method to align legend components in a legend view. This new method takes the dimensions of the GeometrySymbol, then the coordinates in the view are not considered. Then I cant compare CoordinateX for every item to know the distances like in the image:

capture 2.jpg

This lead me to think that if I cant use the BoundingBox of the legendcomponent in the view, this is not gonna be possible. Any idea?

 

Regards.

0 Likes
Message 6 of 13

jjesusdelpino
Advocate
Advocate

I temporary unmark the solution to see if someone can know something about this.

0 Likes
Message 7 of 13

jjesusdelpino
Advocate
Advocate

Im aware of:

https://thebuildingcoder.typepad.com/blog/2010/09/view-location-on-sheet.html

But this approach doesn´t work for legend componentes in legend views. Appearently, the only property that retrieves location coordinates in the view for a legend component is the BoundingBox of that legend component. So thinking out loud. I think that my efforts should be in the direction of trying to understand how BoundingBoxes for legend components in legend views work and how can I get consistent results from them.

0 Likes
Message 8 of 13

TripleM-Dev.net
Advisor
Advisor

Hi,

 

Inspect the transform.Origin of the GeometryElement(s).

This is where the Min/Max coordinates are related to, use this to convert the min/max to "legend" coordinates.

 

- Michel

0 Likes
Message 9 of 13

TripleM-Dev.net
Advisor
Advisor

Sample:

 

GeometryElement geometryElement = geometryInstance.GetSymbolGeometry();

 

A. Use Transform origin to recalculate Min/Max
=> geometryInstance.Transform.Origin

 
B. Transform the geometryElement directly (not sure if this works for Legend components)
=> GeometryElement geometryElement = geometryInstance.GetSymbolGeometry(geometryInstance.Transform);

 

0 Likes
Message 10 of 13

TripleM-Dev.net
Advisor
Advisor

Update, tested the 2 options with you're code

 


@TripleM-Dev.net wrote:

Sample:

 

 

GeometryElement geometryElement = geometryInstance.GetSymbolGeometry();

 

 

 

Works, with this the absolute min/max coordinates can be retrieved, save the origin as XYZ and use it to recalculte the resulting min/max coordinates to "Legend"-view coordinates.

A. Use Transform origin to recalculate Min/Max
=> geometryInstance.Transform.Origin

 

As I feared the option below doesn't work for Legend components.

 B. Transform the geometryElement directly (not sure if this works for Legend components)
=> GeometryElement geometryElement = geometryInstance.GetSymbolGeometry(geometryInstance.Transform);

 


Also use the Builtin parameter: LEGEND_COMPONENT_VIEW to determine if it's a front, back etc legend.

Because the coordinates directions are different per orientation.

 

0 Likes
Message 11 of 13

jjesusdelpino
Advocate
Advocate

Thanks for your implication Michel. Mmm Im trying this but Im missing something. Ok, Transform.Origin is a property from every geometry instance that retrieve absolute coordinates. Checked and understood. But, how do I know what its the "reference" point for every instance(door). I have to know this for the relatives between instances. Im using this function:

		public void GetMaxXAbsolute()
        {			
			UIDocument uidoc = this.ActiveUIDocument;
			Autodesk.Revit.DB.Document doc = this.ActiveUIDocument.Document;
		 	
			Reference myRef = uidoc.Selection.PickObject(ObjectType.Element);
	
			Element ele = doc.GetElement(myRef);
            
			Options options = new Options{ComputeReferences = true, IncludeNonVisibleObjects = false};
			
			GeometryElement Geometria = ele.get_Geometry(options);
			
			double MaxX = 0;
			double MaxY = 0;
			double MinX = 0;
			double MinY = 0;
			double MaxZ = 0;
			double MinZ = 0;
			
			double origin = 0;
			
			foreach (var item in Geometria)
		    {
		        GeometryInstance geometryInstance = item as GeometryInstance;

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

		            foreach (GeometryObject geoObject in geometryElement)
		            {
		                if (geoObject is Solid)
		                {
		                	Solid solido = geoObject as Solid;
		                	if (solido.Volume > 0.1)
		                	{
		                		BoundingBoxXYZ BB = solido.GetBoundingBox();
		                			
		                		if (BB.Max.X > MaxX)
		                		{
		                			MaxX = BB.Max.X;
		                			origin = geometryInstance.Transform.Origin.X;
		                		}
		                		if (BB.Min.X < MinX)
		                		{
		                			MinX = BB.Min.X;
		                		}
		                		
		                		if (BB.Max.Y > MaxY)
		                		{
		                			MaxY = BB.Max.Y;
		                		}
		                		if (BB.Min.Y < MinY)
		                		{
		                			MinY = BB.Min.Y;
		                		}
		                		
		                		if (BB.Max.Z > MaxZ)
		                		{
		                			MaxZ = BB.Max.Z;
		                		}
		                		if (BB.Min.Z < MinZ)
		                		{
		                			MinZ = BB.Min.Z;
		                		}
		                	}
		                }
		            }
		        }
			}	
			TaskDialog.Show("HOLA", (MaxX - origin).ToString());	
        }

To retrieve absolute MaxX for every instance. This is what you are suggesting right?

So, when I apply that to two doors, and substract one value to another. I should get the distance between the two doors right. From this operation I get 1.197m. But that distance doesn´t match any of the dimensions between the doors. e.g. or Im missing something basic, or this reference point its diferent for every type of door:

Captura.JPG

In the image I dimension start,center and finish between the two doors. Sorry about my english and sorry if this is too basic. Your help is really appreciated. 

 

Regards.

 

 

0 Likes
Message 12 of 13

TripleM-Dev.net
Advisor
Advisor
Accepted solution

Seems to be a combo of items needed.

Code below works, but only for the horizontal distance as the Z/Y coordinates are flipped between Plan and elevation view.

In Legend the Y value is the value "Up"-direction from view perspective, so for a front view Y = height, and Z=Thickness.

 

I split the code in parts, so it's easier to manage and re-use.

Main select code:

	public static void SelectEval(UIDocument uidoc)
		{
			Autodesk.Revit.DB.Document doc = uidoc.Document;
			Reference myRef = uidoc.Selection.PickObject(ObjectType.Element);

			BoundingBoxXYZ LegendBox = GetLegendBoundingbox(doc.GetElement(myRef));

			TaskDialog.Show("Info: ", LegendBox.Coordinates() + System.Environment.NewLine + "Width: " + LegendBox.WidthInUnits());
// Or display in meters
			//TaskDialog.Show("Info: ", LegendBox.Coordinates(DisplayUnitType.DUT_METERS) + System.Environment.NewLine + "Width: " + LegendBox.WidthInUnits(DisplayUnitType.DUT_METERS));
		}

 

Convertion from Element Solids min/max to a BoundingBox result.

public static BoundingBoxXYZ GetLegendBoundingbox(Element LegendObj)
		{

			Options options = new Options { ComputeReferences = true, IncludeNonVisibleObjects = false };
			GeometryElement Geometria = LegendObj.get_Geometry(options);

			Boolean firstpointset = false;
			double MaxX = 0;
			double MaxY = 0;
			double MinX = 0;
			double MinY = 0;
			double MaxZ = 0;
			double MinZ = 0;

			foreach (var item in Geometria)
			{
				GeometryInstance geometryInstance = item as GeometryInstance;
				if (geometryInstance != null)
				{
					//GeometryElement geometryElement = geometryInstance.GetSymbolGeometry();
					//We do seem to need to get the Instance Geometry
GeometryElement geometryElement = geometryInstance.GetInstanceGeometry();
// Taking the origin from the GeometryInstance results in strange results.
					//XYZ origin = geometryInstance.Transform.Origin;

					foreach (GeometryObject geoObject in geometryElement)
					{
						if (geoObject is Solid)
						{
							Solid solido = geoObject as Solid;
							if (solido.Volume > 0.001)
							{
								BoundingBoxXYZ BB = solido.GetBoundingBox();
//Take the orgin from Solid's BoundingBox and recalc min/max.
								XYZ origin = BB.Transform.Origin;

								XYZ BBoxMax = origin + BB.Max;
								XYZ BBoxMin = origin + BB.Min;

								if (firstpointset)
								{
									if (BBoxMax.X > MaxX)
									{
										MaxX = BBoxMax.X;
									}
									if (BBoxMin.X < MinX)
									{
										MinX = BBoxMin.X;
									}

									if (BBoxMax.Y > MaxY)
									{
										MaxY = BBoxMax.Y;
									}
									if (BBoxMin.Y < MinY)
									{
										MinY = BBoxMin.Y;
									}

									if (BBoxMax.Z > MaxZ)
									{
										MaxZ = BBoxMax.Z;
									}
									if (BBoxMin.Z < MinZ)
									{
										MinZ = BBoxMin.Z;
									}
								}
								{
									MaxX = BBoxMax.X;
									MaxY = BBoxMax.Y;
									MinX = BBoxMin.X;
									MinY = BBoxMin.Y;
									MaxZ = BBoxMax.Z;
									MinZ = BBoxMin.Z;
								}


							}
						}
					}
				}
			}

			//Parameter DirectionOfView =  LegendObj.get_Parameter(BuiltInParameter.LEGEND_COMPONENT_VIEW);

			//Z = length, distance towards viewer
			//LEGEND_COMPONENT_VIEW
			// = -6 => Back
			// = -7 => Front
			// = -8 => Floor plan

			BoundingBoxXYZ result = new BoundingBoxXYZ();
			result.Max = new XYZ(MaxX, MaxY, MaxZ);
			result.Min = new XYZ(MinX, MinY, MinZ);

			return result;
		}

 

And some helpers (Extensions)

		public static string Coordinate(this XYZ Sender, DisplayUnitType UnitSelect = DisplayUnitType.DUT_MILLIMETERS)
		{
			return UnitUtils.ConvertFromInternalUnits(Sender.X, UnitSelect).ToString() + ";" +
				UnitUtils.ConvertFromInternalUnits(Sender.Y, UnitSelect).ToString() + ";" +
				UnitUtils.ConvertFromInternalUnits(Sender.Z, UnitSelect).ToString();
		}

		public static string Coordinates(this BoundingBoxXYZ Sender, DisplayUnitType UnitSelect = DisplayUnitType.DUT_MILLIMETERS)
		{
			return "Min: " + Sender.Min.Coordinate(UnitSelect) + " Max: " + Sender.Max.Coordinate(UnitSelect);
		}

		public static string WidthInUnits(this BoundingBoxXYZ Sender, DisplayUnitType UnitSelect = DisplayUnitType.DUT_MILLIMETERS)
		{
			return UnitUtils.ConvertFromInternalUnits(Sender.Max.X - Sender.Min.X, UnitSelect).ToString();
		}

 

- Michel

0 Likes
Message 13 of 13

jjesusdelpino
Advocate
Advocate

Now I got it and understand what I was missing is changing the origin for every solid BB. Code is working like a charm setting properly the firspointset. Thank you so much, you have been really helpfull. 

 

Regards.

0 Likes