Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Align Floor with Slab Edge via the API - not working

2 REPLIES 2
Reply
Message 1 of 3
alistairaMWWGJ
211 Views, 2 Replies

Align Floor with Slab Edge via the API - not working

Hi All,

I have engineering software that I have written that is sizing foundations for residential and commercial buildings, and I'm writing automation add-ins to help our draughting teams out.

 

The create a floor layout (using Structural Floor), and then we pop up a form showing the required sizes for the slab edges, internal thickenings etc, and depending on the ground conditions, some other options.

 

I have a number of processes, that perform the following

AddSlabEdge (adds the Slab Edge to the underside of the Slab along the perimeter)

AddInternalLoadBearingWalls (adds thickenings where required)

AddReEntrantBeams (when we're designing for low bearing capacity we have rules that add re-entrant beams)

AddInternalPodsAndSteel (finally we add pods, depending on the type of foundation and required steel).

 

My issues arise when an architect will come to the team and move a wall (say).  The problem is that while all the objects are joined (floor to slab edge, and internal beams etc), the slab edge isnt locked to the floor edge.  Our designers do it by selecting the floor edge, clicking the align tool and then selecting the edge, and locking.  When they move the edge of the floor, the slab edge moves with it.  I'm trying to do this programmatically, but I can't get the alignment to work.  

 

The code below shows the basic process.  User selects a floor. I enumerate through the floor edges, and add the Slab Edge.  I store the edge and the instance of the floor that I have added, as I can't gets its geometry until I have committed the transaction.  Then once completed, I enumerate again trying create the NewAlignment from the floor edge to the slab on the same curve.

 

I continually get "Failed to align Structural Framing instance. The two references are not geometrically aligned so the alignment cannot be created. Parameter Name: reference 2"

 

When I select my edges, I get the planarface of the outside edge of the floor.  I then create the slab edge along the same co-ordinates but 100mm lower (which is the thickness of the slab).  Should I be trying to align the face of the floor? Or the bottom outside edge?  Or the bottom of the floor with the top of the slab edge?  It appears through the front end the draughties just select the floor edge then the footing, and click the lock padlock, but not really sure what it is doing.

 

I've checked the forum and can't find anything that really descibes it.

 

Abbreviated code sample is here:

 

using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.DB.Structure;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Linq;

namespace KirkRoberts.Revit.AddIns.Foundations
{
    [Transaction(TransactionMode.Manual)]
    public class AddSlabEdge : IExternalCommand
    {
        public Result Execute(
            ExternalCommandData commandData,
            ref string message,
            ElementSet elements)
        {
            UIApplication uiApp = commandData.Application;
            UIDocument uiDoc = uiApp.ActiveUIDocument;
            Document doc = uiDoc.Document;
            View viewPlan = doc.ActiveView;

            try
            {

                if (CommandParameters.SelectedFloorId == null)
                {
                    // Select floor element
                    Reference pickedObj = uiDoc.Selection.PickObject(ObjectType.Element, "Select a floor element");
                    if (pickedObj == null)
                    {
                        TaskDialog.Show("Error", "No floor element selected.");
                        return Result.Failed;
                    }

                    CommandParameters.SelectedFloorId = pickedObj.ElementId;

                }

                ElementId selectedFloorId = CommandParameters.SelectedFloorId;

                // Retrieve the selected floor element
                Floor floor = doc.GetElement(selectedFloorId) as Floor;
                
                // Get the structural framing typefs
                FamilySymbol framingType = Utils.GetStructuralFramingType(doc, "Concrete-Rectangular", "SF1");

                // Get the level of the floor
                Level level = doc.GetElement(floor.LevelId) as Level;

                // Get the floor's boundary edge curves
                List<Edge> edgeCurves = GetFloorBoundaryEdges(floor);
                
                List<(FamilyInstance instance, Edge edge)> instances = new List<(FamilyInstance instance, Edge edge)>();

                using (Transaction tx = new Transaction(doc, "Add Beam Edge"))
                {
                    tx.Start();

                    // Other code removed for brevity

                    // Create structural framing instances along the floor edges
                    foreach (var curve in edgeCurves)
                    {
                        try
                        {
                            // Offset the curve downward by 100mm
                            Curve offsetCurve = OffsetCurveDownward(curve.AsCurve(), 100);

                            // Create structural framing instance along the edge curve
                            FamilyInstance instance = doc.Create.NewFamilyInstance(offsetCurve, framingType, level, StructuralType.Beam);

                            // Set the Y Justification to Right (integer value 3)
                            SetYJustification(instance, 3);

                            instances.Add((instance, curve));

                            // Join the beam to the floor
                            JoinGeometryUtils.JoinGeometry(doc, floor, instance);
                        }
                        catch (Exception ex)
                        {
                            TaskDialog.Show("Error", $"Failed to create structural framing instance: {ex.Message}");
                            throw;
                        }
                    }

                    tx.Commit();
                }


                using (Transaction tx2 = new Transaction(doc, "Align Slab Edge"))
                {
                    tx2.Start();

                    foreach (var instance in instances)
                    {
                        try
                        {
                            Reference instanceReference = GetInstanceEdgeReference(instance.instance);
                            if (instanceReference != null)
                            {
                                foreach (var curve in edgeCurves)
                                {
                                    if (curve == instance.edge)
                                    {
                                        Reference curveReference = curve.Reference;
                                        if (curveReference != null)
                                        {
                                            doc.Create.NewAlignment(uiDoc.ActiveView, instanceReference, curveReference);
                                            break;
                                        }

                                    }
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            TaskDialog.Show("Error", $"Failed to align structural framing instance: {ex.Message}");
                            throw;
                        }
                    }

                    tx2.Commit();
                }


                return Result.Succeeded;
            }
            catch (Exception ex)
            {
                message = ex.Message;
                TaskDialog.Show("Error", ex.Message);
                return Result.Failed;
            }
        }

        public static List<Curve> GetFloorBoundaryEdgeCurves(Floor floor)
        {
            List<Curve> edgeCurves = new List<Curve>();
            Options options = new Options { ComputeReferences = true, DetailLevel = ViewDetailLevel.Fine };

            GeometryElement geomElement = floor.get_Geometry(options);
            foreach (GeometryObject geomObj in geomElement)
            {
                if (geomObj is Solid solid)
                {
                    foreach (Face face in solid.Faces)
                    {
                        if (face is PlanarFace planarFace && planarFace.FaceNormal.IsAlmostEqualTo(XYZ.BasisZ))
                        {
                            EdgeArray edgeArray = planarFace.EdgeLoops.get_Item(0);
                            foreach (Edge edge in edgeArray)
                            {
                                edgeCurves.Add(edge.AsCurve());
                            }
                        }
                    }
                }
            }
            return edgeCurves;
        }

        private List<Edge> GetFloorBoundaryEdges(Floor floor)
        {
            List<Edge> edges = new List<Edge>();
            Options options = new Options();
            options.ComputeReferences = true;
            options.DetailLevel = ViewDetailLevel.Fine;

            GeometryElement geomElement = floor.get_Geometry(options);
            foreach (GeometryObject geomObj in geomElement)
            {
                if (geomObj is Solid solid)
                {
                    foreach (Face face in solid.Faces)
                    {
                        if (face is PlanarFace planarFace && planarFace.FaceNormal.IsAlmostEqualTo(XYZ.BasisZ))
                        {
                            EdgeArrayArray edgeArrays = planarFace.EdgeLoops;
                            foreach (EdgeArray edgeArray in edgeArrays)
                            {
                                foreach (Edge edge in edgeArray)
                                {
                                    edges.Add(edge);
                                }
                            }
                        }
                    }
                }
            }
            return edges;
        }


        private Curve OffsetCurveDownward(Curve curve, double offsetDistance)
        {
            offsetDistance = offsetDistance / 304.8;

            // Create a translation transform to move the curve downward
            XYZ translation = new XYZ(0, 0, -offsetDistance);
            Transform transform = Transform.CreateTranslation(translation);
            return curve.CreateTransformed(transform);
        }

        private void SetYJustification(FamilyInstance instance, int justification)
        {
            Parameter yJustificationParam = instance.get_Parameter(BuiltInParameter.Y_JUSTIFICATION);
            if (yJustificationParam != null && !yJustificationParam.IsReadOnly)
            {
                yJustificationParam.Set(justification);
            }
        }


        private Reference GetInstanceEdgeReference(FamilyInstance instance, Curve offsetCurve)
        {
            Options options = new Options { ComputeReferences = true, DetailLevel = ViewDetailLevel.Fine };
            GeometryElement geomElement = instance.get_Geometry(options);
            foreach (GeometryObject geomObj in geomElement)
            {
                if (geomObj is Solid solid)
                {
                    foreach (Edge edge in solid.Edges)
                    {
                        Curve edgeCurve = edge.AsCurve();
                        if (IsSameCurve(edgeCurve, offsetCurve))
                        {
                            return edge.Reference;
                        }
                    }
                }
            }
            return null;
        }

        private Reference GetInstanceFaceReference(FamilyInstance instance)
        {
            Options options = new Options { ComputeReferences = true, DetailLevel = ViewDetailLevel.Fine };
            GeometryElement geomElement = instance.get_Geometry(options);
            foreach (GeometryObject geomObj in geomElement)
            {
                if (geomObj is Solid solid)
                {
                    foreach (Face face in solid.Faces)
                    {
                        if (face.Reference != null)
                        {
                            return face.Reference;
                        }
                    }
                }
            }
            return null;
        }

        private Reference GetLocationCurveReference(FamilyInstance instance)
        {
            LocationCurve locationCurve = instance.Location as LocationCurve;
            return locationCurve?.Curve.Reference;
        }

        private Reference GetFloorFaceReference(Floor floor, Curve edgeCurve)
        {
            // Get the geometry options for fine detail level
            Options options = new Options { ComputeReferences = true, DetailLevel = ViewDetailLevel.Fine };

            GeometryElement geomElement = floor.get_Geometry(options);
            foreach (GeometryObject geomObj in geomElement)
            {
                if (geomObj is Solid solid)
                {
                    foreach (Face face in solid.Faces)
                    {
                        if (face is PlanarFace planarFace)
                        {
                            EdgeArray edgeArray = planarFace.EdgeLoops.Cast<EdgeArray>().FirstOrDefault();
                            if (edgeArray != null)
                            {
                                foreach (Edge edge in edgeArray)
                                {
                                    Curve edgeCurveFromFace = edge.AsCurve();
                                    if (IsSameCurve(edgeCurveFromFace, edgeCurve))
                                    {
                                        return edge.Reference;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return null;
        }

        private bool IsSameCurve(Curve curve1, Curve curve2)
        {
            XYZ start1 = curve1.GetEndPoint(0);
            XYZ end1 = curve1.GetEndPoint(1);
            XYZ start2 = curve2.GetEndPoint(0);
            XYZ end2 = curve2.GetEndPoint(1);

            return (start1.IsAlmostEqualTo(start2) && end1.IsAlmostEqualTo(end2)) ||
                   (start1.IsAlmostEqualTo(end2) && end1.IsAlmostEqualTo(start2));
        }


        private Reference GetInstanceEdgeReference(FamilyInstance instance)
        {
            Options options = new Options { ComputeReferences = true, DetailLevel = ViewDetailLevel.Fine };
            GeometryElement geomElement = instance.get_Geometry(options);
            if (geomElement == null)
            {
                return null;
            }

            foreach (GeometryObject geomObj in geomElement)
            {
                if (geomObj is Solid solid)
                {
                    foreach (Edge edge in solid.Edges)
                    {
                        return edge.Reference;
                    }
                }
            }
            return null;
        }
    }

}

Any ideas greatly appreciated.

 

Thanks heaps.  

 

Regards, Al

 

 

 

2 REPLIES 2
Message 2 of 3
Moustafa_K
in reply to: alistairaMWWGJ

I tried to simplify the issue to track down what is the root cause of alignment failure. I created two slabs and tried to align a face to face... definitely I made sure the geometry are face aligned first, before creating Alignment.

Moustafa_K_0-1719898015063.png

 

Then I picked Face A and Face B

 

var faceA = UiDoc.Selection.PickObject(ObjectType.Face);
var faceB = UiDoc.Selection.PickObject(ObjectType.Face);

 

Which returned these References:

 

FaceA: 2c8e0b1b-1ecd-48b5-b866-28403149164d-00185b2f:2:SURFACE
FaceB: 2c8e0b1b-1ecd-48b5-b866-28403149164d-001859d6:2:SURFACE

 

 

Then within Transaction I tried the NewAlignment Method which has failed!!!

 

Doc.Create.NewAlignment(UiDoc.ActiveGraphicalView, faceA, faceB);

 

 

Jumping to the UI to validate what went wrong, I aligned both faces again and locked the dimension, then I get a hold of this dimension to check what references are used to permit the Alignment via UI and not via API

and the returned references were exactly the faces I pick but in reversed order.

 

2c8e0b1b-1ecd-48b5-b866-28403149164d-001859d6:2:SURFACE
2c8e0b1b-1ecd-48b5-b866-28403149164d-00185b2f:2:SURFACE

 

 Well in contrary, it works on the Top faces just fine, with no errors. 

 

I tried over another Geometry such as Wall. it works fine on the Exterior, Interior faces and sides,  Top and bottom failed.

 

Sample Code used 

 

var faceA = UiDoc.Selection.PickObject(ObjectType.Face);
var faceB = UiDoc.Selection.PickObject(ObjectType.Face);
using (var trans = new Transaction(Doc, "AlignFace"))
{
    trans.Start();
    Doc.Create.NewAlignment(UiDoc.ActiveGraphicalView, faceB, faceA);
    trans.Commit();
}

 

 

@jeremy_tammik thoughts?

Moustafa Khalil
Cropped-Sharp-Bim-500x125-Autodesk-1
Message 3 of 3
alistairaMWWGJ
in reply to: Moustafa_K

Thanks Moustafa,

 

I think you're on the right track.  I'll play with the order of the elements.  How did you find the IDs from the front end?  Are you using Revit lookup and selecting them?

 

I've tried another track since I entered the problem, where I identifed both the faces sharing a joining line, and tried to align those.  I find the outside (not top or bottom) face of the floor, then the same face on the slab edge using this code - I pass in the slab edge (instance), and the bottom edge origin of the floor slab (I also found the edge of the floor with the same reference, but at the bottom).

 

private PlanarFace GetPlanarFaceForOrigin(FamilyInstance instance, XYZ origin)
{
    List<PlanarFace> planarFaces = new List<PlanarFace>();
    Options options = new Options { ComputeReferences = true, DetailLevel = ViewDetailLevel.Fine };
    GeometryElement geomElement = instance.get_Geometry(options);

    foreach (GeometryObject geomObj in geomElement)
    {
        if (geomObj is Solid solid)
        {
            foreach (Face face in solid.Faces)
            {
                if (face is PlanarFace planarFace)
                {
                    // dont want the top or bottom
                    if (!(planarFace.FaceNormal.IsAlmostEqualTo(XYZ.BasisZ) || planarFace.FaceNormal.IsAlmostEqualTo(-XYZ.BasisZ)))
                    {
                        foreach(EdgeArray edgeArray in planarFace.EdgeLoops)
                        {
                            foreach (Edge edge in edgeArray)
                            {
                                // I think 2 will be the top
                                // check
                                if (edge.AsCurve().GetEndPoint(0).IsAlmostEqualTo(origin)) {
                                    return planarFace;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    return null;
}

 

I then tried to align those 2 outside faces.

I'll go back to the draughting team and get them to show me and make sure I am selecting the same objects as through the front end.

From the front end, I click the Align Tool, select the floor edge, then shift.click the slab edge.  That brings up the padlock, and I click lock. 

 

alistairaMWWGJ_0-1719953321518.png

 

Then if I move the modify the floor boundary, the slab edge moves with the floor boundary.

 

Visually, I appear to grab the floor edge (blue above), then the "top" face of slab edge, but realistically, one is vertical and one is horizontal.  Hence why I tried to grab to 2 outside faces.

 

alistairaMWWGJ_1-1719953695534.png

 

I'll play with different faces and see how I get on.

 

Thanks heaps again for replying.

 

Cheers, Al

 

 

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Rail Community


Autodesk Design & Make Report