Announcements

The Autodesk Community Forums has a new look. Read more about what's changed on the Community Announcements board.

DirectShape being deleted

grahamcook
Advocate

DirectShape being deleted

grahamcook
Advocate
Advocate

My goal is to import geometry from a 3d library containing tiled models of cities into Revit.  Those imported models need to respond to sectioned views.

The source data for these city models is from airborne photogrammetry.  I am not 100% sure what the process is that creates the geometry from the photogrammetry but it eventually finds its way to a dwg format.  These files contain nothing but Solids.  Example image below showing the dwg file:

 

grahamcook_0-1653604990419.png

 

The first attempt was to import the dwg as an ImportInstance and simply explode it back to its raw elements (creating section views through an ImportInstance does not section the objects hence the need to explode).  If we get lucky this approach works and we are left with a collection of native Revit solids.  However, more aften than not, exploding the ImportInstance will result in the whole instance being deleted because Revit is not able to deal with the complex solids.

 

Second idea was to import the dwg as an ImportInstance and create a DirectShape using the geometry:

 

IList<ImportInstance> Imports = (from elem in 
								new FilteredElementCollector(doc)
								.OfClass(typeof(ImportInstance))
								 let type = elem as ImportInstance
								 select type).ToList();

foreach (ImportInstance import in Imports)
{
	if (import == null)
		return;

	Options opt = new Options();
	//opt.DetailLevel = ViewDetailLevel.Fine;
	//opt.IncludeNonVisibleObjects = true;
	//opt.ComputeReferences = true;

	foreach (GeometryObject geometryObj in import.get_Geometry(opt))
	{
		DirectShape ds = DirectShape.CreateElement(doc, 
		       new ElementId(BuiltInCategory.OST_GenericModel));

		ds.SetShape(new List<GeometryObject>() { geometryObj });

                // only one geometry object in this loop so static name OK
		ds.Name = "MyShape";
	}
}

 

This works OK and results in a “solid” that can be sectioned.  However, there is another problem with this approach – If the original ImportInstance is deleted, the DirectShape is also deleted.  It’s like the geometry of the DirectShape is directly linked to the geometry of the ImportInstance.  A work around is to hide the ImportInstance in the views as required but that is not ideal.

 

I have not used DirectShape before and ask why the object it deleted when the original ImportInstance is removed from the model, and is there a way to decouple them?

 

I will add that there are tens of thousands of these files so automation is the key.

0 Likes
Reply
Accepted solutions (1)
644 Views
6 Replies
Replies (6)

naveen.kumar.t
Autodesk Support
Autodesk Support

Hi @grahamcook ,

 

Did you test this in UI?

Please be aware that the Revit API hardly ever supports any functionality that is not also available in the user interface

Therefore, if the UI does not support this, the API will probably not do so either.


So, it will always help to research the optimal manual approach to a solution first, before attacking the task programmatically.


Let's say you get the geometry objects(for example Modelcurve, model line) from the imported linked file and created a direct shape using the geometry objects, then, if you deleted the imported linked file, the direct shape also gets deleted along with the linked file.


This is because the created direct shape entirely depends on the geometry from the imported linked file.


I tested at my end and I am able to recreate the problem.


One workaround I could think of is to duplicate the geometry objects(for example Modelcurve, model line) and try to create a direct shape using the duplicated geometry objects. In this way, if you delete the original geometry objects, the direct shape won't be deleted(since the direct shape depends on the duplicated geometry objects).


I tested this solution in UI and it worked fine for me.


Since Revit UI supports this, I think API will also support this approach.


But I am not sure how efficient it will be.


Since your model is large, there could be some possibility of missing some of the geometry objects when you try to duplicate it or it could work well.


I hope this clarifies.


Naveen Kumar T
Developer Technical Services
Autodesk Developer Network

0 Likes

grahamcook
Advocate
Advocate

Thank you Naveen.
That clarifies the question on whether the geometry in the DirectShape is linked back to the original object it was based on.  On the question of did I try the workflow in the UI - yes, I was deleting the import instance via the UI after creating the DirectShape in code.  But my understanding is that a DirectShape can only be created via the API and is not exposed in the UI?

 

So, I need to recreate the Solid geometry somehow.  I have the code that drills into the parts of the imported solids (Face -> Mesh -> Triangulate Mesh -> points of triangles) but I'm not able to find how to put these back into new Faces / Solids.  I can only find the GeometryCreationUtilities  class that has methods for creating extrusions, lofts, sweeps, etc.

It would be great to be able to simply duplicate the geometry with something like geometryObject.Duplicate()!  I've looked at trying to copy the Solids using ElementTransformUtils.CopyElement() but a Solid does not have an ElementId, just an int id which isn't the same so it can't be passed into the CopyElement method.

Here's my code that gets me to the individual points of the solids.  If you have any pointers on how a Solid can be recreated using existing faces I would be very interested to know!

 

foreach (GeometryObject geometryObj in dwg.get_Geometry(new Options()))
{
	if (geometryObj is GeometryInstance)
	{
		GeometryInstance dwgInstance = geometryObj as GeometryInstance;

		if (null != dwgInstance)
		{
			Transform instTransform = dwgInstance.Transform;

			int count = 0;

			GeometryElement instanceGeometryElement = dwgInstance.GetSymbolGeometry();
			foreach (GeometryObject instObj in instanceGeometryElement)
			{
				// Expecting only Solids
				if (!(instObj is Solid))
					continue;
				try
				{
					Solid solid = instObj as Solid;
					if (null == solid || 0 == solid.Faces.Size || 0 == solid.Edges.Size)
					{
						continue;
					}

					// Get the faces and edges from solid, and transform the formed points
					foreach (Face face in solid.Faces)
					{
						Mesh mesh = face.Triangulate();

						for (int i = 0; i < mesh.NumTriangles; i++)
						{
							MeshTriangle mt = mesh.get_Triangle(i);


							XYZ fromPoint0 = mt.get_Vertex(0);
							XYZ fromPoint1 = mt.get_Vertex(1);
							XYZ fromPoint2 = mt.get_Vertex(2);

							if (instTransform != null)
							{
								fromPoint0 = instTransform.OfPoint(fromPoint0);
								fromPoint1 = instTransform.OfPoint(fromPoint1);
								fromPoint2 = instTransform.OfPoint(fromPoint2);
							}

							// Do something with these points.  Make a face for example?
							// Then create solid from faces?
																	

						}
					}
				}
				catch (Exception ex)
				{
					// handle error
				}
			}
		}
	}
}

 

0 Likes

naveen.kumar.t
Autodesk Support
Autodesk Support

Hi @grahamcook ,

 

Yes, you are right.DirectShape can only be created via the API.

I have asked the Revit Engineering team for you.

I will come back to you as soon as I receive a response from them.


Naveen Kumar T
Developer Technical Services
Autodesk Developer Network

0 Likes

naveen.kumar.t
Autodesk Support
Autodesk Support

Hi @grahamcook ,

 

I haven't received any response from the Engineering team yet.
My colleague Jeremy has shared the below link with me.
Please take a look at the below link

https://thebuildingcoder.typepad.com/blog/2015/11/flatten-all-elements-to-directshape.html 


Naveen Kumar T
Developer Technical Services
Autodesk Developer Network

0 Likes

grahamcook
Advocate
Advocate

Thanks Naveen

I can see from the sample in the link that a direct shape is created using the elements geometry as the input (the wall).  The original element is then deleted.  In this case the direct shape would appear to remain.

 

This behaviour differs from that that we have both encountered when extracting geometry from CAD linked or imported CAD elements, in which case deleting the original element that we extracted the geometry from, would also delete the direct shape object we had just created.

 

Its curious that even the most basic solids imported from AutoCAD (I tried a cube) is not able to be exploded without Revit popping a warning "Import contained some 3D data or points which can't be exploded" before deleting the import instead.  So perhaps the fact that Revit is not able to display the geometry of exploded CAD import solids is the reason the DirectShape element created from its geometry is also deleted.  Be interesting to see what the development team come back with.

0 Likes

grahamcook
Advocate
Advocate
Accepted solution

I have found a workable solution to the problem by copying the newly created DirectShape into a new file.  The DirectShape is then independent and maintained in the new file.  These can then be viewed and sectioned which is not otherwise possible if the solids are left buried in the original ImportInstance.

 

So, given that the DirectShape is quite happy sitting outside of the import file with no reference to the original ImportInstance, maybe the original problem described in the post is a bug within the API?

 

Below is the revised code:

IList<ImportInstance> Imports = (from elem in 
								new FilteredElementCollector(doc)
								.OfClass(typeof(ImportInstance))
								 let type = elem as ImportInstance
								 select type).ToList();

foreach (ImportInstance import in Imports)
{
	if (import == null)
		return;

	Options opt = new Options();
	//opt.DetailLevel = ViewDetailLevel.Fine;
	//opt.IncludeNonVisibleObjects = true;
	//opt.ComputeReferences = true;

	foreach (GeometryObject geometryObj in import.get_Geometry(opt))
	{
		DirectShape ds = DirectShape.CreateElement(doc, 
		       new ElementId(BuiltInCategory.OST_GenericModel));

		ds.SetShape(new List<GeometryObject>() { geometryObj });

        // only one geometry object in this loop so static name OK
		ds.Name = "MyShape";
		
		// copy DS to a new file.
		// Otherwise the shape will be deleted when the import instance is removed.
		Document targetDoc = UIApp.Application.OpenDocumentFile(@"P:\VCY\Revit\ImportSolid.rvt");

		using (Transaction trans = new Transaction(targetDoc))
		{
			if (trans.Start("Copy Solid") == TransactionStatus.Started)
			{
				ElementTransformUtils.CopyElements(doc, 
					new List<ElementId>() { ds.Id }, targetDoc, null, new CopyPasteOptions());

				trans.Commit();
			}
		}
	}
}