Announcements

Announcement: We’re aware of an issue affecting starting a new topic from category pages and creating new blog posts. New topics can still be started directly from the relevant board. Learn more here.

Get FaceWall Centerline Curve(s) as Cut by View

Get FaceWall Centerline Curve(s) as Cut by View

kudzuman
Advocate Advocate
3,183 Views
12 Replies
Message 1 of 13

Get FaceWall Centerline Curve(s) as Cut by View

kudzuman
Advocate
Advocate

I would like to draw a DetailCurve in the center of all Walls and FaceWalls visible in a view.

 

Using a FilteredElementCollector specifying a ViewID, I get all visible Walls and FaceWalls.

For the Walls I can get their Location and clone it.

For FaceWalls, Location does not return a curve so I cannot clone it.

 

So for FaceWalls I though maybe I could use GeometryElement with my view specified in the Options so that I get roughly what that FaceWall looks like at that view's cut section. But how do I use that to get a Curve or likely a collection of Curves to clone into a DetailCurve(s) that represent its centerline or edge along that cut plane?

 

One thought was to get an Interior and/or Exterior Face using HostObjectUtils.GetSideFaces(FaceWall, ShellLayerType.Interior) and then find all curves from GeometryElement that have more than 1 point intersecting that Face. I figure if a curve generated this way has its beginning and end on the face then its whole is likely on that face. But I don't know if that is possible or how to do that and would appreciate some help. I am still reading/struggling with/learning how to iterate through what GeometryElement returns for FaceWalls.

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

BenoitE&A
Collaborator
Collaborator

Hey Kudzuman,

I usually use only the LocationCurve of the walls and translate it to the faces of the walls to get the faces.

If you have a list of walls given by your FilteredElementCollector named myVisibleWalls :

List<Wall> myVisibleWalls;

 

for(int i =0; i< myVisibleWalls.Count ; i++)

{

Line myLine = (myVisibleWalls[i].Location as LocationCurve).Curve as Line;

// Creation of the normal vector to the location Line

XYZ normalVector = XYZ.BasisZ.CrossProduct(myLine.Direction);

// Set it to the right width (half of wall width)

normalVector = normalVector / normalVector.GetLength() * myVisibleWalls[i].WallType.Width /2;

// Translate myLine to get the 2 faces

Line l1= myLine.CreateTransformed(Transform.CreateTranslation(normalVector)) as Line;

Line l2= myLine.CreateTransformed(Transform.CreateTranslation(-normalVector)) as Line;

// You have your lines

}

 

Of course it does apply only to linear walls. 

If anybody has a better solution I am interested !

Benoit


Benoit FAVRE
CEO of etudes & automates
www.etudesetautomates.com/
0 Likes
Message 3 of 13

kudzuman
Advocate
Advocate

Like I said the Walls I can get what I want. it is FaceWalls that I am struggling with.

0 Likes
Message 4 of 13

BenoitE&A
Collaborator
Collaborator

Well the lines I create in my script are your FaceWalls cut by your view, right ? So they could be the detail lines you are searching for... Or you can use them as location for the lines you want to create.


Benoit FAVRE
CEO of etudes & automates
www.etudesetautomates.com/
0 Likes
Message 5 of 13

kudzuman
Advocate
Advocate

FaceWall objects are not Wall objects. Their Location properties are different. What you propose is dealing with Wall objects and not FaceWall objects.

0 Likes
Message 6 of 13

JimJia
Alumni
Alumni

I'm afraid this is no safe way to calculate the center line of facewall accurately because the facewall maybe sloped, or non-planar....

 

However, we do can calculate the centerline  approximately via Revit API, the code below demonstrates one workaround(note that the code is not safe, this is just code snippet):

 

The main points are: use  BooleanOperationsUtils.CutWithHalfSpace() to cut the solid of facewall by view plane, and then use the intersect curve of interior, exterior face to calculate the centerline.

 

I also attached the macro sample for your reference. 

		/// <summary>
		/// Retrieve center line of facewall cut by view approximately 
		/// </summary>
		public void FindFacewallCenterLine()
		{
			// find one facewall
			UIDocument uiDoc = new UIDocument(this.Document);
			Element wall = this.Document.GetElement(uiDoc.Selection.PickObject(ObjectType.Element, "Select one face wall"));
			//
			// get solid of face wall
			Options opt = new Options();
			opt.View = this.ActiveView;			
			Solid wallSolid = null;
			GeometryElement geomElem = wall.get_Geometry(opt);
			foreach(GeometryObject go in geomElem)
			{
				if(go is Solid)
				{
					wallSolid = go as Solid;
					break;				
				}			
			}
			//
			// create plane with current plane view, the normal is BasisZ
			Plane viewPlane = new Plane(XYZ.BasisZ, this.Document.ActiveView.Origin);
			//
			// cut the face wall solid with the view plane
			Solid cutSolid = BooleanOperationsUtils.CutWithHalfSpace(wallSolid, viewPlane);
			//
			// find the face which locates on the plane of the view 
			Face faceOnPlane = null;
			foreach(var face in cutSolid.Faces)
			{
				PlanarFace pface = face as PlanarFace;
				if(null == pface)
					continue;
				
				XYZ normal = pface.ComputeNormal(new UV());
				if(normal.IsAlmostEqualTo(XYZ.BasisZ) || normal.IsAlmostEqualTo(-XYZ.BasisZ))
				{
					faceOnPlane = pface;
					break;
				}
			}
			if(null == faceOnPlane)
				return;
			//
			// find the edges of the face
			List<Curve> edgeCrvs = new List<Curve>();
			foreach(EdgeArray ea in faceOnPlane.EdgeLoops)
			{
					foreach(Edge edge in ea)
					{
						edgeCrvs.Add(edge.AsCurve());
					}
			}
			if(edgeCrvs.Count < 2)
				return;
			//
			// sort curve by length
			var sortCrvs = edgeCrvs.OrderByDescending(x => x.Length).ToList();
			//
			// use the two longest to calcuate the centerline approximately
			// !!! NOTE: this is not safe way, you may use other safe way !!!
			XYZ end0 = (sortCrvs[0].GetEndPoint(0) + sortCrvs[1].GetEndPoint(1)) * 0.5;
			XYZ end1 = (sortCrvs[0].GetEndPoint(1) + sortCrvs[1].GetEndPoint(0)) * 0.5;
			Line centerLine = Line.CreateBound(end0, end1);
			//
			// draw the center line for debug
			using(Transaction tran = new Transaction(this.Document, "Curves"))
			{
				tran.Start();
				this.Document.Create.NewModelCurve(centerLine, this.ActiveView.SketchPlane);
				tran.Commit();
			}
		}

Jim Jia
Autodesk Forge Evangelist
https://forge.autodesk.com
Developer Technical Services
Autodesk Developer Network
Email: [email protected]
Message 7 of 13

kudzuman
Advocate
Advocate

Thanks Jim.

That helped me understand what I was reading about geometry in all the documentation. However, the unsafe aspects you mention are what I am really after. The trouble I run into with your solution is that the longest edgeloops are not always straight and are likely HermiteSplines in complex double curved FaceWalls. I could offset them maybe based on the wall family's thickness but what I am after is a DetailCurve for that centerline and now I am stuck trying to convert a HermiteSpline curve into a DetailCurve. Is that possible?

In the UI there is a Convert Line Tool but is there an API process that is similar?

0 Likes
Message 8 of 13

JimJia
Alumni
Alumni

Yes, it's possible to convert the geometry curve to curve element; for example, you can create  detailcurve element with API: Document.Create.NewDetailCurve(), see code snippet below:

			// sort intersect curves by length
			var sortCrvs = edgeCrvs.OrderByDescending(x => x.Length).ToList();
			//
			// create detail curve element with the geometry curve.
			using(Transaction tran = new Transaction(this.Document, "Curves"))
			{
				tran.Start();
				foreach(Curve crv in sortCrvs)
				{
					this.Document.Create.NewDetailCurve(this.Document.ActiveView, crv);
				}
				tran.Commit();
			}

Jim Jia
Autodesk Forge Evangelist
https://forge.autodesk.com
Developer Technical Services
Autodesk Developer Network
Email: [email protected]
0 Likes
Message 9 of 13

kudzuman
Advocate
Advocate

That is the code I have but it doesn't work when the Curve (crv) is a HermiteSpline.

 

If the FaceWall was placed on a double curved surface then the resulting edgeLoops from cutting the solid with the CutWithHalfSpace process are HermiteSplines. When you pass a HermiteSpline Curve to Create.NewDetailCurve, nothing gets created.

 

I could probably make a model line from a HermiteSpline but I need a Detail line. That is why I was asking if there is a Convert Line Types method/process in the API like there is in the UI.

0 Likes
Message 10 of 13

Revitalizer
Advisor
Advisor
Message 11 of 13

kudzuman
Advocate
Advocate

That link seems like it would help but getting the detail curve out of the DetailCurveArray returns Nothing even though the size is > 0.

 

Below is a piece of my code where I am having trouble.

cv is a curve I get earlier from a process using BooleanOperationsUtils.CutWithHalfSpace and finding EdgeLoops as posted by Jim.

vwf is the current view

 

Dim cve As DetailCurve = Nothing
 If TypeOf cv Is HermiteSpline Then
    Dim mca As ModelCurveArray = New ModelCurveArray()
    mca.Append(doc.Create.NewModelCurve(cv, vwf.SketchPlane))
    Dim dca As DetailCurveArray = doc.ConvertModelToDetailCurves(vwf, mca)
    Dim dcai As DetailCurveArrayIterator = dca.ForwardIterator
    While dcai.MoveNext()
         cve = TryCast(dcai.Current, DetailCurve) 'dcai.size = 1 but Current = Nothing
    End While
End If

What is the best way to get the DetailCurve element out of the DetailCurveArray?

Is there maybe a better way than above to turn that HermiteSpline Curve into a DetailCurve?

 

 

0 Likes
Message 12 of 13

Revitalizer
Advisor
Advisor
Accepted solution

Hi,

 

RevitAPI.cm says, regarding the NewModelCurve method and its return value:

 

"If successful a new model line element. Otherwise a null reference (Nothing in Visual Basic)."

 

Revit just cannot create a ModelCurve by the input curve.

 

Which options do you have?

 

  • You could try to convert the HermiteSpline to a NurbSpline and (try to) use this as the NewModeCurve input (NurbSpline.CreateCurve method).
  • You could get points from the input HermiteSpline, project them onto your view plane and create a new HermiteSpline by this points.

That's all some sort of approximation, I fear.

 

 

Revitalizer




Rudolf Honke
Software Developer
Mensch und Maschine





Message 13 of 13

kudzuman
Advocate
Advocate

Using NurbSpline.Create and passing the HermiteSpline object is close enough for what I need. Thanks.

 

The documentaion says you can't make certain curves depending on the view but doesn't elaborate on which kinds of curves and which kinds of views. I suppose HermiteSplines just can't be made into DetailCurves in plan views. So thanks for verifying and providing a good enough work around.

0 Likes