Creating Solid with one PlanarFace using BRepBuilder based on slab sketch

Creating Solid with one PlanarFace using BRepBuilder based on slab sketch

andhxl
Contributor Contributor
1,928 Views
15 Replies
Message 1 of 16

Creating Solid with one PlanarFace using BRepBuilder based on slab sketch

andhxl
Contributor
Contributor

I need to create a Solid with a single PlanarFace using BRepBuilder.

A PlanarFace Edges based on a curves from sketch of a slab.

Below is the code of how I do it. I get the error "Autodesk.Revit.Exceptions.InvalidOperationException: 'BRep doesn't have enough faces.'" on brepBuilder.Finish();.

I do not understand how to create disconnected Solid with one PlanarFace (highlighted in red in the screenshot).

andhxl_0-1726748177954.png

 

var brepBuilder = new BRepBuilder(BRepType.Solid);
var surface = BRepBuilderSurfaceGeometry.Create(sketch.SketchPlane.GetPlane(), null);
BRepBuilderGeometryId faceId = brepBuilder.AddFace(surface, false);
foreach (CurveArray ca in sketch.Profile)
{
    BRepBuilderGeometryId loopId = brepBuilder.AddLoop(faceId);
    foreach (Curve c in ca)
    {
        var edge = BRepBuilderEdgeGeometry.Create(c);
        BRepBuilderGeometryId edgeId = brepBuilder.AddEdge(edge);
        brepBuilder.AddCoEdge(loopId, edgeId, false);
    }
    brepBuilder.FinishLoop(loopId);
}
brepBuilder.FinishFace(faceId);
brepBuilder.Finish();

using (var tr = new Transaction(doc, "Create a DirectShape"))
{
    tr.Start();
    DirectShape ds = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
    ds.SetShape(brepBuilder);
    tr.Commit();
}

 

0 Likes
Accepted solutions (2)
1,929 Views
15 Replies
Replies (15)
Message 2 of 16

Mohamed_Arshad
Advisor
Advisor

Hi @andhxl 

   Do you need to project curve over the slab top surface ? Can you please explain in detail 


Mohamed Arshad K
Software Developer (CAD & BIM)

0 Likes
Message 3 of 16

jeremy_tammik
Alumni
Alumni

Maybe the BRepBuilderExample Revit SDK sample will help?

   

  

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

andhxl
Contributor
Contributor

Hi, no, I need to get Solid with one PlanarFace. TessellatedShapeBuilder is not suitable for me, since I need not only Line, but also Arc and so on. Tessellation is not suitable

0 Likes
Message 5 of 16

Mohamed_Arshad
Advisor
Advisor

Hi @andhxl 

    you need to create solid using one planar face correct ?

 


Mohamed Arshad K
Software Developer (CAD & BIM)

0 Likes
Message 6 of 16

andhxl
Contributor
Contributor

Need Solid consisting of one PlanarFace, created by BRepBuilder.

This is how I get it using TessellatedShapeBuilder. I need the same thing, but without tessellation.

 

var tsb = new TessellatedShapeBuilder();
tsb.OpenConnectedFaceSet(true);
var facePoints = new List<IList<XYZ>>();
foreach (CurveArray ca in sketch.Profile)
{
    var points = new List<XYZ>();
    foreach (Curve c in ca)
    {
        if (c is Line)
        {
            points.Add(c.GetEndPoint(0));
        }
        else
        {
            IList<XYZ> curvePoints = c.Tessellate();
            for (int i = 0; i < curvePoints.Count - 1; i++)
            {
                points.Add(curvePoints[i]);
            }
        }
    }
    facePoints.Add(points);
}
tsb.AddFace(new TessellatedFace(facePoints, ElementId.InvalidElementId));
tsb.CloseConnectedFaceSet();
tsb.Build();

using (var tr = new Transaction(doc, "Create a DirectShape"))
{
    tr.Start();
    DirectShape ds = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
    ds.SetShape(tsb);
    tr.Commit();
}

 

0 Likes
Message 7 of 16

Mohamed_Arshad
Advisor
Advisor

HI @andhxl 

 

   I understand now, But you have created a TessellatedFace using single planar which a surface not solid. But we can't able to generate solid just using one planar face (Brep). You need thickness for that one. you can do one thing extract complete solid from the slab and and generate feature using DirectShape. Kindy check the below code.

 

 

foreach (GeometryObject geoObj in geoElement)
{
    if (geoObj is Solid)
    {
       solid = geoObj as Solid;

        if (solid.Volume > 0)
        {
            break;
        }
    }
}

List<GeometryObject> objects = new List<GeometryObject>();
objects.Add(solid);

using (Transaction createGometry = new Transaction(doc, "Create Disconnected Slab"))
{
    createGometry.Start();

    DirectShape directShape = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
    directShape.AppendShape(objects);

    createGometry.Commit();
}

 

 

This is what we can do, Generating Solid using Face it possible, but you have to define some thickness to it.

 

Hope this will Helps 🙂


Mohamed Arshad K
Software Developer (CAD & BIM)

0 Likes
Message 8 of 16

andhxl
Contributor
Contributor
No, that's not what I need. And we can create a Solid with one PlanarFace, I showed this code above with TessellatedShapeBuilder
0 Likes
Message 9 of 16

Mohamed_Arshad
Advisor
Advisor

@jeremy_tammik  @naveen.kumar.t @Moustafa_K 

 

Can anyone help in solving this 🙂


Mohamed Arshad K
Software Developer (CAD & BIM)

0 Likes
Message 10 of 16

Mohamed_Arshad
Advisor
Advisor

Hi @andhxl 

Check the below workaround code
Reference Code

 

            PlanarFace planarFace = null;

            foreach (GeometryObject geoObj in geoElement)
            {
                if (geoObj is Solid)
                {
                    Solid solid = geoObj as Solid;

                    if (solid.Volume > 0)
                    {
                        FaceArray faceArray = solid.Faces;

                        foreach (Face face in faceArray)
                        {
                            if (face is PlanarFace)
                            {
                                PlanarFace pFace = face as PlanarFace;

                                if (pFace.FaceNormal.Z == 1)
                                {
                                    planarFace = pFace;
                                    break;
                                }
                            }
                        }
                    }

                }
            }


List<GeometryObject> objects = new List<GeometryObject>();

using (Transaction createGometry = new Transaction(doc, "Create Surface Highlightes"))
{
    createGometry.Start();

    DirectShape directShape = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
    objects.Add(ConvertPlanarFaceToSolid(planarFace));
    directShape.AppendShape(objects);

    createGometry.Commit();
}

 

 

 

 public Solid ConvertPlanarFaceToSolid(PlanarFace planarFace)
 {
     // Get the boundary edges of the planar face
     IList<CurveLoop> curveLoops = planarFace.GetEdgesAsCurveLoops();

     // Get the normal direction of the face
     XYZ normal = planarFace.ComputeNormal(new UV(0.5, 0.5));

     // Set the extrusion height to a very small value, making it a thin BRep
     double extrusionHeight = 0.001;

     // Create a solid by extruding the face along its normal direction
     Solid faceSolid = GeometryCreationUtilities.CreateExtrusionGeometry(curveLoops, normal, extrusionHeight);

     return faceSolid;
 }

 

 

Reference Video
Revit_vxxEXVhNfj.gif


Mohamed Arshad K
Software Developer (CAD & BIM)

0 Likes
Message 11 of 16

andhxl
Contributor
Contributor
Need PlanarFace without any thickness(
0 Likes
Message 12 of 16

jeremy_tammik
Alumni
Alumni

Did you look at the BRepBuilderExample Revit SDK sample, as suggested above? You didn't say anything about that:

   

   

It includes a creation of a non-planar face, with no solid.

  

Please also take a look at the sample code in the documentation:

  

  

It demonstrates in detail the creation of faces, edges, coedges, including arcs.

  

Additional very helpful background information is provided by The Building Coder discussing BRepBuilder organisation:

  

   

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

ricaun
Advisor
Advisor
Accepted solution

BRepBuilder can be tricky to work with, in your case you should change the BRepType.Solid to BRepType.OpenShell, this basically allow the builder to return a solid without a volume.

 

Sometimes the orientation matter, specially in the BRepType.Solid and BRepType.Void that you need to make sure all edges is register in the correct orientation.

 

Pretty sure if you change to BRepType.OpenShell your code gonna work fine.

 

Here is a full sample to copy a Face to Solid.

FaceToSolid - 2024-09-19 12-37-33.gif

 

using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System;

namespace RevitAddin.Forum.Revit.Commands
{
    [Transaction(TransactionMode.Manual)]
    public class CommandFaceToSolid : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elementSet)
        {
            UIApplication uiapp = commandData.Application;
            Document document = uiapp.ActiveUIDocument.Document;

            try
            {
                var faceReference = uiapp.ActiveUIDocument.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Face);
                var element = document.GetElement(faceReference);
                var face = element.GetGeometryObjectFromReference(faceReference) as Face;

                var solid = CreateSolidFromFace(face);
                var normal = face.ComputeNormal(new UV(0.5, 0.5));

                using (Transaction transaction = new Transaction(document))
                {
                    transaction.Start("Create Solid");
                    var ds = DirectShape.CreateElement(document, new ElementId(BuiltInCategory.OST_GenericModel));
                    ds.SetName(ds.Category.Name);
                    ds.SetShape(new[] { solid });
                    ds.Location.Move(normal);
                    transaction.Commit();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }

            return Result.Succeeded;
        }

        private Solid CreateSolidFromFace(Face face)
        {
            var surface = face.GetSurface();
            var brepBuilder = new BRepBuilder(BRepType.OpenShell);
            var faceIsReversed = !face.OrientationMatchesSurfaceOrientation;
            BRepBuilderGeometryId faceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(surface, null), faceIsReversed);
            foreach (CurveLoop curveLoop in face.GetEdgesAsCurveLoops())
            {
                BRepBuilderGeometryId loopId = brepBuilder.AddLoop(faceId);
                foreach (Curve curve in curveLoop)
                {
                    var edge = BRepBuilderEdgeGeometry.Create(curve);
                    BRepBuilderGeometryId edgeId = brepBuilder.AddEdge(edge);
                    brepBuilder.AddCoEdge(loopId, edgeId, false);
                }
                brepBuilder.FinishLoop(loopId);
            }
            brepBuilder.SetFaceMaterialId(faceId, face.MaterialElementId);
            brepBuilder.FinishFace(faceId);
            brepBuilder.Finish();

            return brepBuilder.GetResult();
        }
    }
}

 

 

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

Message 14 of 16

andhxl
Contributor
Contributor

I looked, thanks. It became more clear. It especially helped to understand: https://thebuildingcoder.typepad.com/blog/2023/06/brepbuilder-and-toposurface-interior.html#2

0 Likes
Message 15 of 16

andhxl
Contributor
Contributor

Thanks, I've tried with BRepType.OpenShell and I got an internal error. If you take the curves from Face, then it really works without problems. But if you get the curves from the slab sketch, then you need to determine whether they are reversed or not. The sketch could have been constructed any way. Now it is clear that it is possible to do this and that is the whole problem. It remains to understand how to accurately determine this

0 Likes
Message 16 of 16

andhxl
Contributor
Contributor
Accepted solution

Thank you all! In the case of the sketch, I found this way out - combine several actions (creating Face and creating Solid based on it), to not care about the direction of the curves in the sketch:

 

        var sketch = (Sketch)doc.GetElement(floor.GetSketchId());

        Face face = CreateFaceFromSketch(sketch);
        Solid solid = CreateSolidFromFace(face);

        using (var tr = new Transaction(doc, "Creation DirectShape"))
        {
            tr.Start();
            DirectShape ds = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
            ds.SetShape([solid]);
            tr.Commit();
        }
    }

    private static PlanarFace CreateFaceFromSketch(Sketch sketch)
    {
        var curveLoops = new List<CurveLoop>();
        foreach (CurveArray ca in sketch.Profile)
        {
            var cl = new CurveLoop();
            foreach (Curve c in ca)
                cl.Append(c);
            curveLoops.Add(cl);
        }

        XYZ dir = sketch.SketchPlane.GetPlane().Normal;

        Solid tempSolid = GeometryCreationUtilities.CreateExtrusionGeometry(curveLoops, dir.Negate(), Application.MinimumThickness);

        return tempSolid.Faces
            .OfType<PlanarFace>()
            .First(f => f.FaceNormal.IsAlmostEqualTo(dir));
    }

    private static Solid CreateSolidFromFace(Face face)
    {
        var brepBuilder = new BRepBuilder(BRepType.OpenShell);

        var surface = BRepBuilderSurfaceGeometry.Create(face.GetSurface(), null);
        bool faceIsReversed = !face.OrientationMatchesSurfaceOrientation;
        BRepBuilderGeometryId faceId = brepBuilder.AddFace(surface, faceIsReversed);

        foreach (CurveLoop cl in face.GetEdgesAsCurveLoops())
        {
            BRepBuilderGeometryId loopId = brepBuilder.AddLoop(faceId);
            foreach (Curve c in cl)
            {
                BRepBuilderGeometryId edgeId = brepBuilder.AddEdge(BRepBuilderEdgeGeometry.Create(c));
                brepBuilder.AddCoEdge(loopId, edgeId, false);
            }
            brepBuilder.FinishLoop(loopId);
        }

        brepBuilder.SetFaceMaterialId(faceId, face.MaterialElementId);
        brepBuilder.FinishFace(faceId);
        brepBuilder.Finish();

        return brepBuilder.GetResult();
    }

 

Although, perhaps this can be done by creating a CurveLoop and determining the direction using IsCounterclockwise, without creating a temporary Solid

0 Likes