family instance on reference plane with API

family instance on reference plane with API

mononull
Contributor Contributor
7,067 Views
25 Replies
Message 1 of 26

family instance on reference plane with API

mononull
Contributor
Contributor

Trying to place a family instance of electrical fixture onto a reference plane with Revit 2013 API.  I get the following ArgumentsInconsistentException "Reference direction is parallel to face normal at insertion point.", "Parameter name: multiple".  

using (Transaction trans = new Transaction(Doc, "TEST"))
{
    trans.Start();
XYZ bubbleEnd = new XYZ(5, 0, 0); XYZ freeEnd = new XYZ(-5, 0, 0);
XYZ cutVec = new XYZ(0, 0, 1);

ReferencePlane plane1 = Doc.Create.NewReferencePlane(bubbleEnd, freeEnd,
cutVec, Doc.activeView) //floor plan, top-down view

XYZ refDir = new XYZ(0, 0, -1);
FamilySymbol fs = new FilteredElementCollector(Doc)..yada..get duplex receptacle familysymbol

FamilyInstance fi = Doc.Create.NewFamilyInstance(plane1.Reference, XYZ.Zero, refDir, fs);

trans.Commit(); }

 

The reference plane drawn is a vertical reference plane stretching length-wise along the world x-axis and height-wise along the world z-axis.  If I were to add a receptacle using the revit interface it would connect on the -y face of the reference plane...the underside of the plane if looking down on the floor plan.  I've tried every possible reference direction for the newfamilyinstance in the code.  I tried refDir as positive and negative for each X, Y, and Z axis...and not one reference direction worked.  I also made an exact replica command as Jeremy's post here... http://thebuildingcoder.typepad.com/blog/2012/02/hosting-a-light-fitting-on-a-reference-plane.html and nothing worked here either.  Always the same above-mentioned exception.  

 

Any help is appreciated.

0 Likes
7,068 Views
25 Replies
Replies (25)
Message 2 of 26

mononull
Contributor
Contributor
" If I were to add a receptacle using the revit interface it would connect on the -y face of the reference plane...the underside of the plane if looking down on the floor plan."

*I meant to say positive y face...the receptacles are attached on the top side, the way they are drawn just appears the other way, but regardless, I still cannot get a reference direction to work.
Message 3 of 26

tf
Contributor
Contributor

I have the same exact issue.   We have an App that places electrical outlets in a space and this works great 75% of the time. The issue we have is on some interior rooms it uses the correct face of the wall but the hosted device is 180 degrees off. So the device face is inside the wall and the back of the device is in the room. Using the snoop command on the device and on the host wall we can't see how to calculate the correct reference direction? Also when I use anything other than (0,0,0) for the reference direction it throws an error that the says reference direction is parallel to the host. I tried (0,1,0) (1,0,0) (0,0,1) all with the same results.
Any suggestions would be greatly appreciated!

0 Likes
Message 4 of 26

BardiaJahan
Advocate
Advocate

@mononull 

 

Have you tried refDir = (0, 0, 0) ? Could you share the family you are trying to place?

0 Likes
Message 5 of 26

tf
Contributor
Contributor

Thanks for reply.  Yes I have used (0,0,0).  I am using the Revit "out of the box" US Imperial - Electrical - MEP - Electrical Power - Terminals - Duplex

As you can see from the Attached screen shot the results work fine for one of the devices and the other device is connected to the correct face but is flipped.  Using the snoop tool the direction, flipped, etc. look identical on both instances.

 

The code is very simple:

Dim RefDir As XYZ = New XYZ(0, 0, 0)
Dim ref As Reference = HostObjectUtils.GetSideFaces(SegWall, ShellLayerType.Interior).First()
CreateHostedElecDevice = M_document.Create.NewFamilyInstance(ref, DevicePoint, RefDir, DeviceFamilySymbol)

 

 

 Thanks again for any help!

AsReceptical.  Capture.JPG 

0 Likes
Message 6 of 26

BardiaJahan
Advocate
Advocate

Following is pretty much the same code as yours. I am trying it and it places the receptacle on the interior side of the walls at the middle. What are you using for DevicePoint?

 

Snippet

FilteredElementCollector collector = new FilteredElementCollector(document);
collector.OfClass(typeof(FamilySymbol)).OfCategory(BuiltInCategory.OST_ElectricalFixtures);
symbol = collector.FirstElement() as FamilySymbol; FilteredElementCollector collector2 = new FilteredElementCollector(document); collector2.OfClass(typeof(Wall)); foreach (Element element in collector2) { Wall wall = element as Wall; LocationCurve lc = wall.Location as LocationCurve; XYZ mid = 0.5 * (lc.Curve.GetEndPoint(0) + lc.Curve.GetEndPoint(1)); Reference reference = HostObjectUtils.GetSideFaces(wall, ShellLayerType.Interior).First(); Face face = document.GetElement(reference).GetGeometryObjectFromReference(reference) as Face; XYZ location = face.Project(mid).XYZPoint; Transaction t = new Transaction(document); t.Start("transaction"); XYZ refDir = new XYZ(0, 0, 0); FamilyInstance instance = document.Create.NewFamilyInstance(reference, location, refDir, symbol); t.Commit(); }

 

0 Likes
Message 7 of 26

matthew_taylor
Advisor
Advisor
A way I have gotten around newfamilyinstance placement method limitations is to use promptforfamilyinstanceplacement, but wrapped in a document changed event, and doing post-checking on the placed instances, and rehosting, rotating, or altering parameters in an additional transaction.

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?
0 Likes
Message 8 of 26

tf
Contributor
Contributor

Thanks for the reply!

The device point is the midpoint of the wall segment plus an offset above the floor to set the device height:

Dim pt As XYZ = Midpoint(seg.StartPoint, seg.EndPoint)
Dim DevicePoint as XYZ = NewPt(pt.X, pt.Y, SpacePoint.Z + (DeviceOffset / 12))

 

It doesn't work when the wall is interior on both sides.  It will work in one room but in the adjacent room the device on the same adjoining wall will be flipped.

No problem on exterior walls at all.  

 

Thanks

0 Likes
Message 9 of 26

tf
Contributor
Contributor
Thanks for the reply! The device point is the midpoint of the wall segment plus an offset above the floor to set the device height: Dim pt As XYZ = Midpoint(seg.StartPoint, seg.EndPoint) Dim DevicePoint as XYZ = NewPt(pt.X, pt.Y, SpacePoint.Z + (DeviceOffset / 12)) It doesn't work when the wall is interior on both sides. It will work in one room but in the adjacent room the device on the same adjoining wall will be flipped. No problem on exterior walls at all. Thanks
0 Likes
Message 10 of 26

tf
Contributor
Contributor

Thank you.  I tried that.  Unfortunately, the incorrectly flipped device can't be rotated or flipped.  Its as if it thinks it is inserted correctly.  The error is to the effect that the can't rotate to that position.  I could go to unhosted devices but that seems to defeat the purpose of revit.

0 Likes
Message 11 of 26

matthew_taylor
Advisor
Advisor
@tf did you use promptforfamilyinstanceplacement though? Once you have a family placed correctly, you can fairly easily swap it out for another one. Just get its info (you may need to regen first), delete it, and place a new one. Hoops to jump through, but it allows you to forget about most of the maths.@Anonymous did you use promptforfamilyinstanceplacement though? Once you have a family placed correctly, you can fairly easily swap it out for another one. Just get its info (you may need to regen first), delete it, and place a new one. Hoops to jump through, but it allows you to forget about most of the maths.
Otherwise, you need to get your transforms, normals, etc well thought out.

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?
0 Likes
Message 12 of 26

tf
Contributor
Contributor

@matthew_taylor  No we did not use  promptforfamilyinstanceplacement.   We are parametrically placing all the devices in the room.  By knowing the door location we know that the wall diagonally across from the door needs a quad receptical and a data jack.  All other walls need a duplex receptacle spaced 7' apart.  Also because we know the door location we can locate the lightswitch.  Ideally, all of this done without much human intervention. 

 

Do you think in the above scenario there is a way to use the promptforfamilyinstanceplacement  command programatically with minimal user input.

Really appreciate the help...

 

0 Likes
Message 13 of 26

matthew_taylor
Advisor
Advisor
@tf@Anonymous
Riiiiight. Gotcha. That actually hit me in the middle of the night! 😉
You need to get the Normal of the wall Face to use as the reference direction. It isn't a global 'use all' direction. I'm pretty sure that the building coder has a sample that computes the normal of a face. I think the code is something like face.ComputeNormal(UV.Zero)

Test with a series of walls that have been arrayed 360 degrees around a point. Any issues in your code will become transparent.

-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?
0 Likes
Message 14 of 26

tf
Contributor
Contributor
Thanks. I am struggling with that. I have the normal vector but I haven't been able to figure out how to translate it to a relevant Reference direction.
Looks like I am going in the right diection. Now I need to figure out how to make it work. Thank you...
0 Likes
Message 15 of 26

FAIR59
Advisor
Advisor

using HostObjectUtils.GetSideFaces() will give you an reference for some internal reference-plane, and not the "real" face of the wall. This may explain why the placed instances do not behave properly. On top of that, both the interior and the exterior plane have the same normal vector, explaining why some instances will disappear in the wall.

The solution will be to find the "real"faces of the wall.

 

Options opt = new Options();

opt.ComputeReferences =true;

opt.DetailLevel = ViewDetailLevel.Coarse;

GeometryElement geoElem = element.get_Geometry(opt);

foreach (Autodesk.Revit.DB.GeometryObject geomObj in geoElem)

{

  Autodesk.Revit.DB.Solid solid = geomObj as Autodesk.Revit.DB.Solid;

  if (null != solid)

  {

    foreach (Face f in solid.Faces)

    {

       //  test for correct normal vector succeeded, select f 

    }

  }

  Autodesk.Revit.DB.GeometryInstance geomInst = geomObj as Autodesk.Revit.DB.GeometryInstance;

  if (null != geomInst)

  {

     Autodesk.Revit.DB.GeometryElement transformedGeomElem = geomInst.GetSymbolGeometry();

     //  find faces in transformedGeoElem   

  }

}

Once you have the correct face, you can use either the face or face.Reference in the call

document.Create.NewFamilyInstance()

 

Message 16 of 26

tf
Contributor
Contributor
Thanks very much. We will give it a shot...
0 Likes
Message 17 of 26

tf
Contributor
Contributor

@FAIR59

Thanks very much.  Having trouble determining which face has the correct normal.  I know the space point and could compare to that but that may not work in all instances such as an L-shaped room.  Any ideas would be greatly appreciated.

Also,  Once the face is found why do we need to find the face in the TransformedGeoElem and how do we get the face corresponding to f.

 

Thanks again

 

0 Likes
Message 18 of 26

BardiaJahan
Advocate
Advocate

@tf 

I think the main reason behind what is happening is that your location point is not on the reference plane you are using (I think you can still use HostObjectUtils.GetSideFaces though ). For some interior walls, you need to place the receptacle on the exterior side in order to be inside the room!

You need to find which face is actually facing toward the room. (I think it was already suggested in the thread). One workflow is to use room.GetBoundarySegments to retrieve the boundary and iterate through the segments to find which side of your wall is overlapped with at least one of the segments. Then, that would be your reference plane to place the receptacle in that room.

Message 19 of 26

tf
Contributor
Contributor
@BardiaJahan,
Thanks for the reply. We are definetly getting the correct face as we are doing what you suggest, but it places the receptacle on tbe wrong side of the interior face so that it sticks into the wall.
I feel we are getting close with all the help on this forum!!
0 Likes
Message 20 of 26

FAIR59
Advisor
Advisor

@ tf

 

I was too hasty in dismissing the use of HostObjectUtils.GetSideFaces().

I can now place an Electrical Fixture (facebased) correctly using:

 

LocationCurve lc = wall.Location as LocationCurve;

XYZ mid = lc.Curve.Evaluate(0.5, true).Add(new XYZ(0,0,1));

 

Reference reference = HostObjectUtils.GetSideFaces(wall, ShellLayerType.Interior).First();

if (reference == null) continue;

Face _RefFace = element.GetGeometryObjectFromReference(reference) as Face;

 

location = _RefFace.Project(mid).XYZPoint;

instance = document.Create.NewFamilyInstance(reference , location, _RefFace.ComputeDerivatives(_RefFace.Project(location).UVPoint).BasisX.Normalize(), symbol);