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 get a Solids location in the project

20 REPLIES 20
SOLVED
Reply
Message 1 of 21
jan
Advocate
3313 Views, 20 Replies

How to get a Solids location in the project

I am struggling to get the true location of a Solid in a Casework Instance.

 

I can get the boundingbox of the solid, but the location is always far away from the Casework Instance.

 

I guess that there must be som kind of transformation method to solve this, but all methods that I have tried returns the same result.

 

Any ideas?

 

Jan Grenov

20 REPLIES 20
Message 2 of 21
matthew_taylor
in reply to: jan

Hi @jan,

I would imagine .GetTotalTransform should give you the transform of an instance. From there you should be able to transform your bounding box minimum and maximum points.

 

If that doesn't solve your issue, you may want to:

  1. Explain the reason why you need to get the solid.
  2. Enclose some code so your process and approach are clear.

Cheers,

 

-Matt


Cheers,

-Matt
_______________________________________________________________________________
Marking a post as a 'solution' helps the community. Giving a post 'Kudos' is as good as saying thanks. Why not do both?
Message 3 of 21
jan
Advocate
in reply to: matthew_taylor

Hi Matt

 

Thanks for your reply.

 

 

The task, that I am working on, is actually to obtain distances from some Casework to surrounding objects.

In order to do that I tried this:

 

1. Get the FamilyInstance BoundingBox and calculate center point as XYZ rayBasePoint for later use

2. Get the FamilySymbol BoundingBox and calculate width, depth and height

3. Get the rotation angle rotAng of the FamilyInstance

4. Use rayBasePoint and rotAngA for casting rays from rayBasePoint to left, right, front, back, up, down (relative to the elements rotation angle rotAng)

5. Get the Proximity property of  referenceIntersector.FindNearest(rayBasePoint, rayDirection);

6. Do some relative simple calculation and get the distances.

 

But I found that the BoundingBox represents all elements - not just the 3D model elements. That is why I tried to access the solids inside the Familyinstance in order to construct my own sort of Solids-Only-BoundingBox.

 

I will reply again when i have tried your proposed solution.

 

Regards

Jan

Message 4 of 21
jeremytammik
in reply to: jan

Dear Jan,

 

Very similar family instances may have very different transformation sequences, depending on whether they can reuse the original symbol geometry unmodified or not. In the former case, they have a transform that needs to be taken into account. In the latter, they may possibly define their own modified version of the symbol geometry in world coordinates and not require a transform. I believe I handle this correctly in the OBJ viewer:

 

http://thebuildingcoder.typepad.com/blog/2012/07/obj-model-exporter-with-multiple-solid-support.html

 

I also believe the SDK ElementViewer sample handles this correctly:

 

/a/lib/revit/2017.1/SDK/Samples/Viewers/ElementViewer/

 

Check using those two with your specific problematic family instances, debug what they do, and you can solve your problem.

 

Cheers,

 

Jeremy



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

Message 5 of 21
jan
Advocate
in reply to: jeremytammik

Hi Jeremy

 

Thanks for replying 🙂

 

I will do some investigation using your example.

I'll post a reply when I have found out how to do the trick ... or I am stuck once again.

 

 

 

 

...by the way. How come both yours and Matthews pictures are showing, but not mine? (Just a thought)

 

Regards

Jan

Message 6 of 21
jeremytammik
in reply to: jan

Dear Jan,

 

Good luck with your research.

 

Regarding the pictures, you need to note the difference between inserting an image into the body text (using the 'Photos' icon) and attaching a file (using 'Attachments' below the body text editor).

 

Cheers,

 

Jeremy



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

Message 7 of 21
BobbyC.Jones
in reply to: jan

Hi Jan,

 

To get an accurate bounding box of just the solid objects in a family instance I use this in the context of a 3D view.

 

        private static BoundingBoxXYZ GetBoundingBoxFromVisibleSolids(FamilyInstance familyInstance, View sourceView)
        {
            var visibleEdgePoints = familyInstance.
                GetVisibleSolids(sourceView).
                Where(solid => solid.Volume > 0).
                SelectMany(solid => solid.Edges.Cast<Edge>()).
                SelectMany(edge => edge.Tessellate()).
                ToList();

            if (!visibleEdgePoints.Any()) {
                throw new ArgumentException($"This family instance has no visible solid edges: {familyInstance.Id}");
            }

            var minX = visibleEdgePoints.Min(p => p.X);
            var minY = visibleEdgePoints.Min(p => p.Y);
            var minZ = visibleEdgePoints.Min(p => p.Z);

            var maxX = visibleEdgePoints.Max(p => p.X);
            var maxY = visibleEdgePoints.Max(p => p.Y);
            var maxZ = visibleEdgePoints.Max(p => p.Z);

            var min = new XYZ(minX, minY, minZ);
            var max = new XYZ(maxX, maxY, maxZ);

            var shrinkWrapBoundingBox = new BoundingBoxXYZ
            {
                Min = min,
                Max = max,
                Transform = familyInstance.GetTotalTransform()
            };

            return shrinkWrapBoundingBox;
        }

        public static IList<Solid> GetVisibleSolids(this FamilyInstance instance, View revitView)
        {
            var visibleInView = new Options
            {
                View = revitView,
                ComputeReferences = false,
                IncludeNonVisibleObjects = false
            };

            var visibleSolids = instance.get_Geometry(visibleInView).
                OfType<GeometryInstance>().
                SelectMany(geoInstance => geoInstance.SymbolGeometry.OfType<Solid>()).
                Where(solid => solid.Visibility == Visibility.Visible).
                ToList();

            return visibleSolids;
        }
--
Bobby C. Jones
Message 8 of 21
jan
Advocate
in reply to: BobbyC.Jones

Hi Bobby

 

This solution looks interesting indeed but it seems like there is something that I am missing.

I get this error message:

'FamilyInstance' does not contain a definition for 'GetVisibleSolids' and  no extension method 'GetVisibleSolids' accepting a first argument og type 'Familyinstance' cound be found (are you missing a using directive or an assembly reference?)

 

screenDump.png

 

Normally I prefer vb.net but I am in the process of learning to program in c# as well. Maybe my lack of understanding has something to do with that.

Can you help me out?

 

Regards

Jan

Message 9 of 21

Hi @jeremytammik,

I think Jan is referring to his Autodesk Profile picture as shown on his profile:

http://forums.autodesk.com/t5/user/viewprofilepage/user-id/1115210

 

Cheers,

 

-Matt


Cheers,

-Matt
_______________________________________________________________________________
Marking a post as a 'solution' helps the community. Giving a post 'Kudos' is as good as saying thanks. Why not do both?
Message 10 of 21

@jan_holon, when i right click your profile picture and say 'open in new tab, the following message is displayed:

 

This XML file does not appear to have any style information associated with it. The document tree is shown below.
 
 
<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>2340E15078844A1B</RequestId>
<HostId>
ScTydwX//TPRioMaMIk1FvL1Vrdc7G5SEKub0mViUtuSWUnBk48SRKp35U28mF3OMvIVHe+STRE=
</HostId>
</Error>
The link displayed in the address bar is:
 
https://s3.amazonaws.com/com.autodesk.storage.public.production/oxygen/BFYBNCNR43JZ/profilepictures/x120.jpg?r=0
 
Cheers,
 
Jeremy


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

Message 11 of 21
BobbyC.Jones
in reply to: jan

Hi Jan,

You can either place the GetVisibleSolids() method in a static class that's visible to the GetBoundingBoxFromVisibleSolids() method, or you can call it as a normal method, and not as an extension.

 

            var visibleEdgePoints = GetVisibleSolids(familyInstance, sourceView).
                Where(solid => solid.Volume > 0).
                SelectMany(solid => solid.Edges.Cast<Edge>()).
                SelectMany(edge => edge.Tessellate()).
                ToList();

 

As a side note, I need to add support for those instances that Jeremy mentions, and handles in his code, when a family instance does not use its symbol geometry.  I may have a few minutes later this morning to add that, and if so, I'll post it.

--
Bobby C. Jones
Message 12 of 21
BobbyC.Jones
in reply to: BobbyC.Jones

As for vb.net help, I'm not overly familiar with it, but I did a quick google search and found this article on how to create extension methods, https://msdn.microsoft.com/en-us/library/bb514025.aspx

 

--
Bobby C. Jones
Message 13 of 21
jan
Advocate
in reply to: BobbyC.Jones

Thanks to you guys I now have a solution Smiley Happy

 

It might certainly not be the most elegant solution, but for now it will definitly do.

I did not (yet) solve the problem that I described in my initial question, but I solved my task ...

 

  1. Part one:
            String returnValue = string.Empty;

            //Get the objects angle
            double angle = GetFamInstAngle(famInst);

            // Find a 3D view to use for the ReferenceIntersector constructor
            FilteredElementCollector collector = new FilteredElementCollector(doc);
            Func<View3D, bool> isNotTemplate = v3 => !(v3.IsTemplate);
            View3D view3D = collector.OfClass(typeof(View3D)).Cast<View3D>().First<View3D>(isNotTemplate);

  2. Path two: Get the true center, width, depth and height of the casework
            BoundingBoxXYZ bBoxFaminst = famInst.get_BoundingBox(view3D);

            //This is where I am calling Bobbys code that I put in a separate public static class SolidsBoundaryBox in a namespace AAA
            BoundingBoxXYZ bBoxSolidsOnly = AAA.SolidsBoundaryBox.GetBoundingBoxFromVisibleSolids(famInst, view3D);

            //Find familieinstansens boundingboks-center
            //= det punkt skal vi bruge som udgangspunkt for beregning af de to punkter, vi vil skyde tilbage fra

            XYZ instanceBBoxCenter = bBoxFaminst.Min.Add(bBoxFaminst.Max.Subtract(bBoxFaminst.Min) / 2);
            XYZ solidsCenter = bBoxSolidsOnly.Min.Add(bBoxSolidsOnly.Max.Subtract(bBoxSolidsOnly.Min) / 2);
            double trueWidth = bBoxSolidsOnly.Max.X - bBoxSolidsOnly.Min.X;
            double trueDepth = bBoxSolidsOnly.Max.Y - bBoxSolidsOnly.Min.Y;
            double trueHeight = bBoxSolidsOnly.Max.Z - bBoxSolidsOnly.Min.Z;
            double trueHalfWidth = trueWidth / 2;
            double trueHalfDepth = trueDepth / 2;
            double trueHalfHeight = trueHeight / 2;

            XYZ basePoint = new XYZ(instanceBBoxCenter.X, instanceBBoxCenter.Y, solidsCenter.Z);

     

            XYZ pointOnLeftSide = GetPointOnCaseworkSide(doc, basePoint, trueWidth, trueDepth,trueHeight, angle, directionTowards.Left, famInst.Id, view3D);
            XYZ pointOnBackSide = GetPointOnCaseworkSide(doc, basePoint, trueWidth, trueDepth, trueHeight, angle, directionTowards.Back, famInst.Id, view3D);
            XYZ pointOnBottom = GetPointOnCaseworkSide(doc, basePoint, trueWidth, trueDepth, trueHeight, 0, directionTowards.Bottom, famInst.Id, view3D);

     


            XYZ directionVectorWidth = new XYZ(Math.Cos(angle), Math.Sin(angle), 0);
            XYZ directionVectorDepth = new XYZ(Math.Cos(angle - (Math.PI / 2)), Math.Sin(angle - (Math.PI / 2)), 0);

            XYZ point1OnMiddleOfWidth = pointOnLeftSide.Add(directionVectorWidth.Multiply(trueHalfWidth));
            XYZ point2OnMiddleOfWidth = point1OnMiddleOfWidth.Add(directionVectorDepth.Multiply(trueHalfDepth));

            XYZ point1OnMiddleOfDepth = pointOnBackSide.Add(directionVectorDepth.Multiply(trueHalfDepth));
            XYZ point2OnMiddleOfDepth = point1OnMiddleOfDepth.Add(directionVectorWidth.Multiply(trueHalfWidth));

            //True boundingbox Z-value:
            double trueZValue = bBoxSolidsOnly.Max.Z - ((bBoxSolidsOnly.Max.Z - bBoxSolidsOnly.Min.Z) / 2);

            //Now get the true center point
            XYZ trueCenterPoint = GetIntersection(point1OnMiddleOfWidth, point2OnMiddleOfWidth, point1OnMiddleOfDepth, point2OnMiddleOfDepth);
            trueCenterPoint = new XYZ(trueCenterPoint.X, trueCenterPoint.Y, trueZValue);

  3. Part three: At this point we have the true center of the casework, and we can use this center as a ray base point for calculating distances
            Dictionary<string, XYZ> lstViewDirections = new Dictionary<string, XYZ>();
            XYZ vDir = new XYZ(Math.Cos(angle), Math.Sin(angle), 0);
            lstViewDirections.Add("Right", vDir);

            vDir = new XYZ(Math.Cos(angle + Math.PI), Math.Sin(angle + Math.PI), 0);
            lstViewDirections.Add("Left", vDir);

            vDir = new XYZ(Math.Cos(angle + (Math.PI / 2)), Math.Sin(angle + (Math.PI / 2)), 0);
            lstViewDirections.Add("Back", vDir);

            vDir = new XYZ(Math.Cos(angle + Math.PI + (Math.PI / 2)), Math.Sin(angle + Math.PI + (Math.PI / 2)), 0);
            lstViewDirections.Add("Front", vDir);

            vDir = new XYZ(0, 0, 1);
            lstViewDirections.Add("Up", new XYZ(0, 0, 1));

            vDir = new XYZ(0, 0, -1);
            lstViewDirections.Add("Down", new XYZ(0, 0, -1));

            ReferenceWithContext rwc = null;
            foreach (KeyValuePair<string, XYZ> kvp in lstViewDirections)
            {
                XYZ rayDirection = kvp.Value;

                List<ElementId> elementIds = new List<ElementId>
                {
                    famInst.Id
                };
                ExclusionFilter flt = new ExclusionFilter(elementIds);
                ReferenceIntersector refIntersector = new ReferenceIntersector(flt, FindReferenceTarget.Element, view3D);

                rwc = refIntersector.FindNearest(trueCenterPoint, rayDirection);
                Element elem = null;
                if (rwc != null)
                {
                    Reference reference = rwc.GetReference();
                    elem = doc.GetElement(reference);
                }

                string category;
                string name;
                double distance;
                bool distanceUndefined = false;
                string categoryString;
                string nameString;
                string distanceString;


                if (rwc == null | elem == null)
                {
                    category = "Nothing";
                    distance = 0;
                    name = "Nothing";
                    distanceUndefined = true;
                }
                else
                {
                    category = elem.Category.Name;
                    name = elem.Name;
                    distance = rwc.Proximity;

                    switch (kvp.Key)
                    {
                        case "Right":
                            distance = Math.Abs(distance - trueHalfWidth);
                            break;
                        case "Left":
                            distance = Math.Abs(distance - trueHalfWidth);
                            break;
                        case "Back":
                            distance = Math.Abs(distance - trueHalfDepth);
                            break;
                        case "Front":
                            distance = Math.Abs(distance - trueHalfDepth);
                            break;
                        case "Up":
                            distance = Math.Abs(distance - trueHalfHeight);
                            break;
                        case "Down":
                            distance = Math.Abs(distance - trueHalfHeight);
                            break;
                        default:
                            distance = 0;
                            distanceUndefined = true;
                            break;
                    }
                }

                if (distanceUndefined)
                {
                    distanceString = string.Concat("Distance = ", "Undefined");
                }
                else
                {
                    if (distance < 0.000001)
                    {
                        distance = 0;
                    }

                    //Convert to mm
                    distance = Math.Round(distance = 304.8 * distance, 0);

                    distanceString = string.Concat("Distance = ", distance, " mm");
                }

                switch (kvp.Key)
                {
                    case "Right":
                        categoryString = string.Concat("RightCat = ", category);
                        nameString = string.Concat("RightName = ", name);
                        break;
                    case "Left":
                        categoryString = string.Concat("LeftCat = ", category);
                        nameString = string.Concat("LeftName = ", name);
                        break;
                    case "Back":
                        categoryString = string.Concat("BackCat = ", category);
                        nameString = string.Concat("BackName = ", name);
                        break;
                    case "Front":
                        categoryString = string.Concat("FrontCat = ", category);
                        nameString = string.Concat("FrontName = ", name);
                        break;
                    case "Up":
                        categoryString = string.Concat("TopCat = ", category);
                        nameString = string.Concat("TopName = ", name);
                        break;
                    case "Down":
                        categoryString = string.Concat("BottomCat = ", category);
                        nameString = string.Concat("BottomName = ", name);
                        break;
                    default:
                        categoryString = "UnknownCat";
                        nameString = "UnknownName";
                        break;
                }

                returnValue = string.Concat(returnValue, "\n", categoryString, " ; ", nameString, " ; ", distanceString);

            }

            return returnValue;


        }


        public double GetFamInstAngle(FamilyInstance famInst)
        {
            LocationPoint locationPoint = famInst.Location as LocationPoint;
            double angle;
            if (locationPoint == null)
            {
                LocationCurve lc = famInst.Location as LocationCurve;
                if (lc == null)
                {
                    throw new Exception("Failed to get family instance location. Neither Location Point nor Location Curve is defined.");
                }
                else
                {
                    XYZ insertionPoint = lc.Curve.GetEndPoint(0);
                    XYZ curveEndPoint = lc.Curve.GetEndPoint(1);
                    Line lin = lc.Curve as Line;
                    if (lin == null)
                    {
                        throw new Exception("Failed to retrieve the objects orientation. The objects location curve was expected to be a Line.");
                    }
                    angle = lin.Direction.AngleTo(new XYZ(1, 0, 0));
                }
            }
            else
            {
                angle = locationPoint.Rotation;
            }

            return angle;


        }

 

 

Message 14 of 21
BobbyC.Jones
in reply to: jan

Hi Jan,

I'm glad you got it resolved, great work!  I haven't reviewed all your code, there's a lot of it 🙂  But I did notice you're calculating the family instance angle by digging into the Location property.  Family instances have a FacingOrientation property that you may find handy.

 

And don't forget about the bug in my code, it will fail if the family instance contains the geometry at the top level instead of referencing a GeometryInstance.  My guess is that you won't hit it if your casework families are anything like ours.  But that just makes it more insidious as it will jump out and bite you when you least expect it.  The fix is easy, a simple test after calling get geometry, and is demonstrated in Jeremy's code.

 

Bobby

--
Bobby C. Jones
Message 15 of 21
jeremytammik
in reply to: jan

Dear Jan,

 

Congratulations on solving it and thank you for sharing the code.

 

I would volunteer to clean it up if you put it into a nice, clean, simple, minimal reproducible case.

 

@kinjal provided a prime example of what a reproducible case can look like at its very best:

 

http://thebuildingcoder.typepad.com/blog/2017/02/setting-active-view-during-idling.html

 

I am pretty sure that your code can be significantly shortened and simplified.

 

However, as it stands right now, my brain is unable to take it in.

 

I would have to put it into an IDE like Visual Studio or SharpDevelop and start cleaning it up to start understanding what the individual steps really do.

 

In your case, a minimal reproducible case would have to include some sample instances to select and run it on, of course, together with some kind of visualisation of the expected results.

 

Thank you!

 

Cheers,

 

Jeremy



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

Message 16 of 21
jan
Advocate
in reply to: jeremytammik

Hi Jeremy

 

That would really be great! I expect that I may learn a lot from your solution.

I attach the c# project plus two Revit test project files.

 

Regards Jan

Message 17 of 21
jan
Advocate
in reply to: BobbyC.Jones

Hi Bobby

 

Thanks for the information about the FacingOrientation property. I wonder why I havn't seen that property before now. That comes in handy in many situations.

 

+ I am aware of the bug as you described. Thanks for reminding me.

 

Regards Jan

Message 18 of 21
jeremytammik
in reply to: jan

Dear Jan,

 

Thank you for sharing your sample material.

 

Come to think of it, maybe my age-old setout points add-in already satisfies all your needs?

 

https://github.com/jeremytammik/SetoutPoints

 

Can you have a look?

 

If you need something that is not already included there, maybe that would be the best place for me to add it?

 

Cheers,

 

Jeremy



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

Message 19 of 21
jan
Advocate
in reply to: jeremytammik

Hi Jeremy

 

I'll take a look at your code.

 

/Jan

Message 20 of 21
jan
Advocate
in reply to: jeremytammik

Hi Jeremy

 

That turned out to be the final solution Smiley Happy

By making som minor adjustments to your code I managed to optimize my solution.

 

I ended up creating a class GeomVertices containing

 

  • public static string RealString(double a) ... (as defined in your code)
  • public static string PointString(XYZ p) ... (as defined in your code) 
  • class XyzEqualityComparer ... (as defined in your code) 
  • public static Dictionary<XYZ, int> GetCorners(List<Solid> solids) ... (a slightly modified version of your the GetCorners in your code)
  • public static List<Solid> GetSolids(Element e, Options opt, out Transform t) ... (a slightly modified version of GetSolid in yupr code)
  • + a new function and a new class of my own.
  • public static BboxInfo GetSolidsBoundingBoxInfo(Dictionary<XYZ, int> dict, ref Transform t) ...
  • public  class BboxInfo ...

By using the information returned by GetSolidsBoundingBoxInfo I can finally solve the task of getting distances to other elements by casting rays in various directions.

 

I attach the class GeomVertices

 

Thanks again for your help.

 

Regards

Jan

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