Creating a mass between two Toposurfaces

Creating a mass between two Toposurfaces

MustaeenUrRehmann.Qazi
Contributor Contributor
2,078 Views
9 Replies
Message 1 of 10

Creating a mass between two Toposurfaces

MustaeenUrRehmann.Qazi
Contributor
Contributor

Hi, 

I have cylindrical layers in my project which represent layers of soil in a borehole. Since i have a number of boreholes,  I have defined Toposurfaces which bind together the layer boundaries of same layer types in different boreholes to form a 3D structure. Now the task at hand is to create a mass between two consecutive toposurfaces to form a whole 3D Body. The created mass would have the same material and Colour as the layers it binds together and will lie between two toposurfaces.  I have tried building a tesselated shape builder and also tried using a loft,  But somehow I am unable to define the loft and the Tesselated shape between the toposurfaces. Any help in this regard would be greatly appreciated.

My revit project that contains the structure is attached hereby for reference.

 

The code that creates loft is here: (The code works by selecting the first topography and building the loft but Iam unable to confine the loft between two toposurfaces )

Selection sel = uiapp.ActiveUIDocument.Selection;
                TopographySurface topo = doc.GetElement(sel.PickObject(ObjectType.Element)) as TopographySurface;
                Mesh mesh = topo.get_Geometry(new Options()).First(m => m is Mesh) as Mesh;

                double offsetDist = 10;

                List<Solid> solids = new List<Solid>();
                using (Transaction tx = new Transaction(doc))
                {
                    tx.Start("Generate Topography");
                    for (int i = 0; i < mesh.NumTriangles; i++)
                    {
                        XYZ[] triangleCorners = new XYZ[3];

                        MeshTriangle triangle = mesh.get_Triangle(i);
                        triangleCorners[0] = triangle.get_Vertex(0);
                        triangleCorners[1] = triangle.get_Vertex(1);
                        triangleCorners[2] = triangle.get_Vertex(2);

                        List<Curve> curves = new List<Curve>();
                        List<Curve> newCurves = new List<Curve>();


                        curves.Add(Line.CreateBound(triangleCorners[0], triangleCorners[1]));
                        curves.Add(Line.CreateBound(triangleCorners[1], triangleCorners[2]));
                        curves.Add(Line.CreateBound(triangleCorners[2], triangleCorners[0]));
                        newCurves.Add(Line.CreateBound(new XYZ(triangleCorners[0].X, triangleCorners[0].Y, triangleCorners[0].Z - offsetDist), new XYZ(triangleCorners[1].X, triangleCorners[1].Y, triangleCorners[1].Z - offsetDist)));
                        newCurves.Add(Line.CreateBound(new XYZ(triangleCorners[1].X, triangleCorners[1].Y, triangleCorners[1].Z - offsetDist), new XYZ(triangleCorners[2].X, triangleCorners[2].Y, triangleCorners[2].Z - offsetDist)));
                        newCurves.Add(Line.CreateBound(new XYZ(triangleCorners[2].X, triangleCorners[2].Y, triangleCorners[2].Z - offsetDist), new XYZ(triangleCorners[0].X, triangleCorners[0].Y, triangleCorners[0].Z - offsetDist)));


                        List<CurveLoop> profiles = new List<CurveLoop>();

                        CurveLoop curveLoop = CurveLoop.Create(curves);
                        profiles.Add(curveLoop);
                        CurveLoop newCurveLoop = CurveLoop.Create(newCurves);
                        profiles.Add(newCurveLoop);

                        //create direct shape from curveloops
                        SolidOptions options = new SolidOptions(ElementId.InvalidElementId, ElementId.InvalidElementId);

                        //create loft
                        Solid loft = GeometryCreationUtilities.CreateLoftGeometry(profiles, options);
                        solids.Add(loft);

                        DirectShape ds1 = null;
                        ds1 = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
                        ds1.ApplicationId = "Application id";
                        ds1.ApplicationDataId = "Geometry object id";
                        ds1.Name = "TOPO";
                        ds1.SetShape(new GeometryObject[] { loft });

                    }

 

Cheers,

Mustaeen Qazi

 

 

0 Likes
Accepted solutions (1)
2,079 Views
9 Replies
Replies (9)
Message 2 of 10

jeremy_tammik
Alumni
Alumni

Ignoring all the details you describe, I wonder about the point of all this.

 

I would have thought that the one and only purpose of a borehole is to determine the Z extents of the different layers of soils and other materials. The cylindrical shape is an artifact of how the Z extents are obtained and can be ignored.

 

If that is correct, I would completely ignore the cylindrical aspects of the borehole data and just work with the Z coordinates of the top and bottom extents of each of the different layers it penetrates.

 

These Z extents plus the X and Y location of the borehole is all you need to generate separate meshes or solids representing each layer.

  

That is, assuming that the boreholes are vertical.

  

Is my thinking completely off target here?

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 3 of 10

MustaeenUrRehmann.Qazi
Contributor
Contributor

Hi Jeremy,

Thanks. Yes you are thinking in right direction and I have proceeded in a similar way in my project, So i took the Z points for each layer and added a toposurface at layer boundaries ignoring any cylindrical aspects of borehole. The only point where i got stuck at is creation of a mass between these toposurfaces. i tried with loft and tesselated shape builder and also read your posts  e.g this one "The Building Coder: Creating and Inserting an Extrusion Family (typepad.com)" and some older Issues with mass placement e.g "How to create an in-place mass - Autodesk Community - Revit Products".  But somehow iam not able to confine the mass between the toposurfaces i.e the bottom surface of the loft/mass deosnt end at the next Toposurface/Layer Horizon, but rather continues till end of borehole. So any Idea how i could acheive this confinement would be helpful.

0 Likes
Message 4 of 10

jeremy_tammik
Alumni
Alumni

Can you add a couple of images showing the original borehole information, the simplified abstract information displaying only the 2D borehole X, Y locations, the Z elevations of the different layer boundaries, maybe connected by vertical lines indicating the borehole abstractions, and an image of the final result that you are aiming for?

 

I don't quite see how a loft is going to help you achieve the kind of shapes you need.

 

I think the easiest way to create such a geometry might be via a direct shape.

 

The most flexible and powerful way to create a direct shape is via the BRepBuilder class:

 

https://www.revitapidocs.com/2022/94c1fef4-2933-ce67-9c2d-361cbf8a42b4.htm

 

However, it is a bit unwieldy to use. Look at these numerous other samples for easier ways to create a direct shape:

 

https://thebuildingcoder.typepad.com/blog/about-the-author.html#5.50

 

I am sure some of them will help you achieve the desired result.

 

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

MustaeenUrRehmann.Qazi
Contributor
Contributor

Hi Jeremy,

Thanks for the info iam into the direct shape currently and looking out for possible solutions. In the mean time I made a screenshot of my boreholes and linked the layers together in 2D and created my desired structure out of it (also in 2D) just for a visualization of my intended purpose. There are two images below the first one  shows only the boreholes in 2D and a second image  shows the final structure i want to achieve.  Just for info, iam able to connect the layers with eachother to form a skeleton using toposurfaces, now I have to fill up the skeleton with a mass.

 

Here the image with the boreholes, different colours represent different layers in each borehole. 

boreholes.PNG 2nd Image with the intended structure: 

I have connected the layer horizons (as shown by black lines) using toposurfaces, now i want to fill the space between the toposurfaces with a mass that has same material as my layers

intended_structure.PNG

0 Likes
Message 6 of 10

jeremy_tammik
Alumni
Alumni

Thank you very much for the nice snapshots. It is much clearer to me now. 

  

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

RPTHOMAS108
Mentor
Mentor

Massing options with the API are very limited you could use FreeFormElements in the family environment that takes a solid.

 

The tessellated shape builder is probably still your best option as it is quite tolerant of face orientation and order of faces added to the connected face sets.

 

It may be that you were creating self intersecting geometry i.e. is lower face always below upper? The other issue is that you have sometimes less or more points on the lower surface than the upper, so how do you form the edges between upper and lower surfaces (to complete the solid)?

 

I still believe you don't have enough bore holes and they are not in a regular enough pattern to infer what is going on between them. Also wrong tool for the job isn't C3D a better option as a starting point i.e. bring the mesh surfaces in via CAD link and generate the solids from those?

 

Message 8 of 10

MustaeenUrRehmann.Qazi
Contributor
Contributor
Hi Thomas thanks for detailed info. Yes you are right the tesselated shape suites best but the end purpose of the model doesnt allow this.
Same is the case with other software, i have modelled geology using various softwares and python libraries but the current requirements allow only revit to be used.
The horizons are not selfintersecting, it depends on the perspective, some isolated findings can be considered as geological lenses since the requirement is only limited to horizons, the property variation between boreholes is also not very much important
0 Likes
Message 9 of 10

MustaeenUrRehmann.Qazi
Contributor
Contributor
Accepted solution

Finally its solved with some deformities which are under improvement:

I used lofts in the end !!

So, I created curveloops out of the boundary points of toposurfaces and then created a loft out of it

(See image at the end. Project file is also attached)

here is the code for those who might need it in future:

 using (Transaction tx = new Transaction(doc))
                {
                    tx.Start("Generate 3D bodies");
                    Selection sel = uiapp.ActiveUIDocument.Selection;
                    TopographySurface topo1 = doc.GetElement(sel.PickObject(ObjectType.Element)) as TopographySurface;
                    //Mesh mesh = topo1.get_Geometry(new Options()).First(m => m is Mesh) as Mesh;
                    List<Solid> solids = new List<Solid>();

                    IList<XYZ> bounding_pts1 = topo1.GetBoundaryPoints();
                    //double offsetDist = 10;
                    List<Curve> curves1 = new List<Curve>();

                    for (int i = 0; i < bounding_pts1.Count - 1 ; ++i)
                    {
                        
                            // Create lines and add to curves list
                            Line line = Line.CreateBound(bounding_pts1[i], bounding_pts1[i+1]);
                           //Append points to CurveLoop
                            curves1.Add(line);
                                                                          
                    }
                    //Create line connecting start and end point of topography bound points and adding it to curves collec
                    /*int lastpointidx = bounding_pts1.Count - 1;
                    Line last_line = Line.CreateBound(bounding_pts1[lastpointidx], bounding_pts1[0]);
                    curves1.Add(last_line);*/

                    // create curve loop from list of curves i.e a polygon
                    CurveLoop curveLoop1 = CurveLoop.Create(curves1);

                    // now get second topography

                    Selection sel2 = uiapp.ActiveUIDocument.Selection;
                    TopographySurface topo2 = doc.GetElement(sel2.PickObject(ObjectType.Element)) as TopographySurface;
                    IList<XYZ> bounding_pts2 = topo2.GetBoundaryPoints();

                    List<Curve> curves2 = new List<Curve>();

                    for (int i = 0; i < bounding_pts2.Count - 1 ; ++i)
                    {
                       
                            // Create lines and add to curves list
                            Line line = Line.CreateBound(bounding_pts2[i], bounding_pts2[i+1]);
                            //Append points to CurveLoop
                            curves2.Add(line);

                    }

                    //Create line connecting start and end point of topography bound points and adding it to curves collec
                    /*int lastpointidx2 = bounding_pts2.Count - 1;
                    Line last_line2 = Line.CreateBound(bounding_pts2[lastpointidx2], bounding_pts2[0]);
                    curves2.Add(last_line2);*/

                    // create curve loop from list of curves i.e a polygon
                    CurveLoop curveLoop2 = CurveLoop.Create(curves2);

                    List<CurveLoop> curve_profiles = new List<CurveLoop>();
                    curve_profiles.Add(curveLoop1);
                    curve_profiles.Add(curveLoop2);

                    //create direct shape from curveloops
                    SolidOptions options = new SolidOptions(topo1.MaterialId, ElementId.InvalidElementId);

                    //create loft
                    Solid loft = GeometryCreationUtilities.CreateLoftGeometry(curve_profiles, options);
                    solids.Add(loft);

                    DirectShape ds1 = null;
                    ds1 = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
                    ds1.ApplicationId = "Application id";
                    ds1.ApplicationDataId = "Geometry object id";
                    ds1.Name = "3D_soil_body";
                    ds1.SetShape(new GeometryObject[] { loft });

                    tx.Commit();
                }

 3D_modelle.PNG

Message 10 of 10

jeremy_tammik
Alumni
Alumni

Cool. Congratulations on solving and thank you for sharing a nice example of using the CreateLoftGeometry method.

  

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