Align Floor with Slab Edge via the API - not working
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
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