NewFamilyInstance, problem with host

NewFamilyInstance, problem with host

u20.htwr
Contributor Contributor
1,013 Views
8 Replies
Message 1 of 9

NewFamilyInstance, problem with host

u20.htwr
Contributor
Contributor

Hello everyone! 

I am creating an add-in that will place some new Family instances according to existing instances locations. 

The problem is that my families (to be placed) are wall-based, and I can't change that. And when I use NewFamilyInstance method with wall as Host element - families are placed on a random side of given wall. Well, it's probably not random, but I can't figure out how it works (flipping the wall giving same result).

To be more specific, here is what I'm doing: 

1. Get an element, and use ReferenceIntersector to find nearest wall via FacingOrientation of that element

2. Then I project a line to this wall to find nearest intersection point (on nearest face of the wall)

3. Now it's time to place wall-based instance to this point, but in 99% of times family is placed on the opposite face of the wall. 

 

When I try to place face-based family it is all working as expected, but I just can't use face-based families, only wall-based. Is there any way or workaround to place family on exact face of given wall? 

The only way I found is to create a Group of this family, mirror it across wall axis, and then ungroup and delete. But it seems to be overcomplicated and time-consuming, since there will be 50+ of this families placed. There must be another way

 

Any help will be highly appreciated

Accepted solutions (2)
1,014 Views
8 Replies
Replies (8)
Message 2 of 9

moturi.magati.george
Autodesk
Autodesk

Hi @u20.htwr,

 

You can share a minimum sample of the code if possible, the professionals on this forum might point out the issue faster that way.

That said, I would suggest you try the method below. It has a reference direction argument. Also read the remarks that have been added for the same.

NewFamilyInstance Method (XYZ, FamilySymbol, XYZ, Element, StructuralType) (revitapidocs.com)

 

public FamilyInstance NewFamilyInstance(
	XYZ location,
	FamilySymbol symbol,
	XYZ referenceDirection,
	Element host,
	StructuralType structuralType
)

 

  Moturi George,     Developer Advocacy and Support,  ADN Open
Message 3 of 9

u20.htwr
Contributor
Contributor

Hi! Thanks for your suggestion. Yes, this is the method I am currently using (because it's the only one that allows me to place host-based instance), but there is no control over placement side (of the host wall). Reference Direction do nothing, I tried everything

Message 4 of 9

moturi.magati.george
Autodesk
Autodesk

Hi @u20.htwr,

 

Please have a look at the thread below. There is a good example given by @jeremy_tammik on placing a family to the wall.

 

https://forums.autodesk.com/t5/revit-api-forum/newfamilyinstance-not-setting-level-of-family-instanc...

 

  Moturi George,     Developer Advocacy and Support,  ADN Open
Message 5 of 9

u20.htwr
Contributor
Contributor

Ok, I found solution that suits my needs by now. I just checking if BBox Center of created element is farther than Point projected on the wall (by ReferenceIntersector). If so, I just Mirroring created element across host wall axis and deleting original. 

LocationPoint of created host-based family is always in the center of host object (wall), so I used Bbox center instead. 

 

 

 

Message 6 of 9

jeremy_tammik
Alumni
Alumni
Accepted solution

Control placement over which side of the wall?

  

Search the forum!

  

https://forums.autodesk.com/t5/revit-api-forum/place-lighting-fixture-on-the-right-side-of-the-wall/...

  

Pretty recent discussion, fresh of the press...

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 7 of 9

u20.htwr
Contributor
Contributor

Thanks for the suggestion, but in linked thread author have face-based family to place, not host-based. 

Anyways, I found the solution

Message 8 of 9

jeremy_tammik
Alumni
Alumni

Oh dear, you were faster, our posts crossed...

  

Can you share your solution, please, for others to enjoy as well? 

  

Thank you!

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 9 of 9

u20.htwr
Contributor
Contributor
Accepted solution

Hi Jeremy, thanks for your suggestion! I was searching for two days straight, but never found this topic you linked. Thats a shame since it is exactly what I needed. 

As for my solution (which possibly will be thrown away  at this point) - I will share code and explanation. 

 

My initial idea is to place electric sockets according to equipment that is already present in project. 

So for example we have one piece of equipment, that will always be placed close to the wall (let's say some table). 

For start I use ReferenceIntersector to find closest wall behind that table. Then I use LinePlaneIntersection code (thanks again, Jeremy!) with Element.FacingOrientation to find projection point on the wall behind that table (projection from the center of Element). 

Now it's time to place socket. By now we should have input csv which contains data for Symbol name (socket), room, Element name (that table), height of socket and position of socket (distance from center of table). For each new socket there will be different input data. 

Cycling through project equipment we compare each Element name to be same as in the database, as well as it's room. In our company we have strongly typed projects, with exact same equipment pieces and positions, so simple cycling and compare will work. 

If match is found we use NewFamilyInstance to create socket. After that I get it's BoundingBox center (since LocationPoint of host-based family seems to always be in the center of host), and do simple comparison: 

if BBox center point is farther than projected point (from initial Element locationPoint, aka table) - it means that family is placed on the wrong side, so I just mirror it, and delete the original. Using MirrorElements method returns mirrored instance, that is then used to change parameters (height and position).

 

The whole idea might look sloppy, but since I create all families and maintain project template - I have full control over families origin points, etc., so its safe to use this strategy. 

 

public Result Execute(
          ExternalCommandData commandData,
        ref string message,
          ElementSet elements)
        {
            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Document doc = uidoc.Document;

            try
            {
                var view = doc.ActiveView;

                Selection selection = uidoc.Selection;

                // Pick equipment, temoprary for tests

                var pickedElementReference = selection.PickObject(
                  ObjectType.Element, new ElementPickFilter(),
                  "Please select");

                if (null == pickedElementReference)
                {
                    TaskDialog.Show("Failed", "Selection Failed");
                    return Result.Failed;
                }

                FamilyInstance ElementFamilyInstance
                  = doc.GetElement(pickedElementReference)
                    as FamilyInstance;

                if (ElementFamilyInstance == null)
                {
                    return Result.Failed;
                }

                // Getting location point and other data from instance

                LocationPoint location1 = ElementFamilyInstance.Location as LocationPoint; //name for tests
                XYZ ElementLocation = location1.Point;
                var view3d = Get3dView(doc);
                var orient = ElementFamilyInstance.FacingOrientation;


                // Finding nearest wall
                ReferenceIntersector refIntersector = new ReferenceIntersector(
                    new ElementCategoryFilter(BuiltInCategory.OST_Walls),
                    FindReferenceTarget.Element,
                    view3d
                    );

                var x = refIntersector.FindNearest(location1.Point, orient);
                var y = x.GetReference();
                var elementWall = doc.GetElement(y);
                Wall nearestWall = elementWall as Wall; 
                List<ElementId> lst = new List<ElementId>();
                lst.Add(elementWall.Id);

                //Getting wall faces and creating projection line
                IList<Reference> sideFacesInterior = HostObjectUtils.GetSideFaces(nearestWall, ShellLayerType.Interior);
                IList<Reference> sideFacesExterior = HostObjectUtils.GetSideFaces(nearestWall, ShellLayerType.Exterior);

                double lineParameter = 0;

                XYZ newPoint = ElementLocation + orient;
                newPoint = new XYZ(newPoint.X,newPoint.Y,newPoint.Z);
                var line1 = Line.CreateBound(ElementLocation, newPoint);

                Face faceInterior = uidoc.Document.GetElement(sideFacesInterior[0]).GetGeometryObjectFromReference(sideFacesInterior[0]) as Face;
                PlanarFace planeInterior = faceInterior as PlanarFace;
                var pointInterior = LinePlaneIntersection(line1, planeInterior, out lineParameter);

                Face faceExterior = uidoc.Document.GetElement(sideFacesExterior[0]).GetGeometryObjectFromReference(sideFacesExterior[0]) as Face;
                PlanarFace planeExterior = faceExterior as PlanarFace;
                var pointExterior = LinePlaneIntersection(line1, planeExterior, out lineParameter);

                XYZ minimalPoint = new XYZ();
                Reference placingRef = null;

                // Looking for closest projected point
                if (ElementLocation.DistanceTo(pointInterior) < ElementLocation.DistanceTo(pointExterior))
                {
                    minimalPoint = pointInterior;
                    placingRef = sideFacesInterior[0]; // No need 
                }
                else
                {
                    minimalPoint = pointExterior;
                    placingRef = sideFacesExterior[0]; // No need 
                }

                FamilySymbol famSym = GetSymbol(doc, "Double Socket 2.0", "Double Socket IP54");

                //Getting mirror plane from wall
                LocationCurve lc = nearestWall.Location as LocationCurve;
                XYZ EndP1 = lc.Curve.GetEndPoint(0);
                XYZ EndP2 = lc.Curve.GetEndPoint(1);
                double h1 = nearestWall.LookupParameter("Height").AsDouble();
                XYZ EndP3 = new XYZ(EndP1.X, EndP1.Y, EndP1.Z + h1);

                Plane plane = Plane.CreateByThreePoints(EndP1, EndP2, EndP3);

                FamilyInstance instance;

                using (var tr = new Transaction(doc, "Place instance"))
                {
                    tr.Start();
                    if (!famSym.IsActive)
                    { famSym.Activate(); doc.Regenerate(); }

                    instance = doc.Create.NewFamilyInstance(minimalPoint, famSym, new XYZ(0, 0, 0), nearestWall, Autodesk.Revit.DB.Structure.StructuralType.NonStructural);

                    tr.Commit();
                }
                // Probably there is no need to make separate transaction since I don't use LocationPoint of socket
                
                LocationPoint socketLocation = instance.Location as LocationPoint;
                XYZ socketPoint = socketLocation.Point; //can't use this since LocationPoint is always in the center of the host wall

                BoundingBoxXYZ elementBBox = instance.get_BoundingBox(view);

                XYZ globalMax = elementBBox.Max;
                XYZ globalMin = elementBBox.Min;

                Transform viewTransform = view.CropBox.Transform;
                XYZ max = viewTransform.Inverse.OfPoint(globalMax);
                XYZ min = viewTransform.Inverse.OfPoint(globalMin);

                XYZ UpRight = new XYZ(GetMax(min.X, max.X), GetMax(max.Y, min.Y), 0);
                XYZ DownLeft = new XYZ(GetMin(min.X, max.X), GetMin(max.Y, min.Y), 0);

                var Center = (UpRight + DownLeft) / 2;

                List<ElementId> mirrorElements = new List<ElementId>
                    {
                        instance.Id
                    };

                using (var tr1 = new Transaction(doc, "transaction"))
                {
                    tr1.Start();

                    if (ElementLocation.DistanceTo(minimalPoint) < ElementLocation.DistanceTo(Center)) //Can use Comparer here
                    {
                        var mirroredSocketIds = ElementTransformUtils.MirrorElements(doc, mirrorElements, plane, true);
                        mirrorElements.Clear();
                        doc.Delete(instance.Id);
                        ElementId newSocketId = mirroredSocketIds[0];
                        Element newSocket = doc.GetElement(newSocketId);
                    }

                    tr1.Commit();
                }

            }
            catch (OperationCanceledException)
            {
                return Result.Cancelled;
            }
            catch (Exception ex)
            {
                message = ex.Message;
                TaskDialog.Show("Failed", message);
                return Result.Failed;
            }
            return Result.Succeeded;
        }