Structural framings visible start\end point coordinates when using Reference Plane

dchemanovSCVQB
Participant

Structural framings visible start\end point coordinates when using Reference Plane

dchemanovSCVQB
Participant
Participant

Hello. I have the following steps:

1. Create a structural framing

2. Add Reference Plane

dchemanovSCVQB_0-1678964793515.png

3. Cut Geometry using this Reference Plane

dchemanovSCVQB_1-1678964991194.png

4. Open 3D view

dchemanovSCVQB_2-1678965132888.png

Curve curve = (element.Location as LocationCurve).Curve;
XYZ startPoint = curve.GetEndPoint(0);
XYZ endPoint = curve.GetEndPoint(1);

This code will return start and end coordinates of actual frame, but how can I get start and end coordinates of visible part of the frame?

 

Thanks in advance.

0 Likes
Reply
Accepted solutions (2)
817 Views
13 Replies
Replies (13)

RPTHOMAS108
Mentor
Mentor

You need to interrogate the actual geometry the location line will be unaffected.

 

Element.Geometry

 

If you look at the instance transform you can translate the cut geometry to the symbol location and then find the end faces by normal and see where they sit along the X-Axis. One end face normal will be aligned with -ve XYZ.basisX and the other XYZ.BasisX. If however the end faces have been cut non-square then this will not be the case. For that case you can do a dot product comparison to identify the faces with normals that are mostly pointing east and west or look at other aspects such as faces pointing to the contrary along with areas etc. If the face is cut at 45 degrees then dot product will not be of much use but you know there are uncut faces (beam side faces) that are pointing normal to the east and west directions. So you find the end faces be elimination of side faces perhaps. Then you have beam end notching which creates perhaps many faces pointing east and west, so you have to then start considering areas or faces most remote from transform origin in east/west direction.

 

For a cut FamilyInstance the GeometryInstance inside the GeometryElement will be empty of solids but the method GeometryInstance.Transform will still be available so you use the inverse of that transform to transform the top level solids (aligning them longitudinally with XYZ.BasisX).

 

You should find that the transform origin is located at the mid point of the location line. So you can equate the local offsets for end faces (along symbol space basis x) with offsets from the centre of the actual location line in instance coordinates (along the vector direction of the location line in instance space).

 

All of this assumes that the family was based upon a standard template which has certain established conventions.

dchemanovSCVQB
Participant
Participant

It looks like really hard way 🙂

I can rephrase my question then, maybe someone would offer simplier solution.

I'm creating tag with leader for my beam using https://www.revitapidocs.com/2018/1f622654-786a-b8fd-1f81-278698bacd5b.htm.

I need to calculate XYZ pnt somehow, so tag leader would point to the beam. For now I'm using 

locationCurve.Curve.Evaluate(0.5, true)

to get the point, but when beam is cut, sometimes I can receive  a leader pointing to nowhere.

So my question is how to make leader always point visible part of the beam.

dchemanovSCVQB_0-1679297181123.png

 

RPTHOMAS108
Mentor
Mentor

If you want the centre of the beam you could perhaps use the centre of the BoundingBox from the Solid in the geometry (not Element.BoundingBox which will ignore the cut). If it doesn't have solid geometry (coarse view) then you can find the overall bounding box via extents of other geometry such as model lines.

 

Additionally since the BoundingBox circumscribes a straight beam the min will roughly represent one end and the max the other. Obviously you can make more accurate adjustments to that in other ways e.g. the bounding box to a solid in local symbol space is used to draw a line between ends across the top surface of the bounding box cube with that line then transformed to instance space.

 

 

dchemanovSCVQB
Participant
Participant

I'm using following code to get solid:

Options options = new Options
{
    DetailLevel = ViewDetailLevel.Fine
};

GeometryElement geometryElement = element.get_Geometry(options);

Solid solid = GetSolid(geometryElement);

if (solid != null)
{
    BoundingBoxXYZ boundixBox = solid.GetBoundingBox();

    return (boundixBox.Min + boundixBox.Max) / 2;
}

private static Solid GetSolid(GeometryElement geometryElement)
{
    foreach (GeometryObject geometryObject in geometryElement)
    {
        Solid solid = geometryObject as Solid;
        if (solid != null && solid.Id != ElementId.InvalidElementId.IntegerValue)
        {
            return solid;
        }

        GeometryInstance geometryInstance = geometryObject as GeometryInstance;
        if (geometryInstance != null)
        {
            GeometryElement transformedGeometryElement = geometryInstance.GetInstanceGeometry(geometryInstance.Transform);
            return GetSolid(transformedGeometryElement);
        }
    }

    return null;
}

For any beam I always get (0, 0, 0) point:

dchemanovSCVQB_0-1679383813529.png

RPTHOMAS108
Mentor
Mentor

BoundingBox has a transform the Min/Max points are often centred on 0 so they would result in zero if averaged.  Since in this case the transform has no rotation component you can get the origin from the bounding box transform and use that but strictly speaking you should be using:

 

Solid.GetBoundingBox.Transform

and Transform.OfPoint with the Min/Max points.

 

RPTHOMAS108_0-1679405063238.png

I have come across bounding boxes in other scenarios where the min/max are not mirror opposites so I would not just use the origin instead of OfPoint since OfPoint will always give the right answer also allowing for the rotation aspect where such exists.

 

Generally for geometry the bounding box transform has no rotation component.

 

dchemanovSCVQB
Participant
Participant

Thank you very much. It works prefect in almost all cases, but I have a strange one. I have a beam that received some stage tag position using your approach. I tried to visualize its bounding box (draw a line between transformed min and max points) and received some strange result.

dchemanovSCVQB_1-1679418098462.png

But in my other cases your approach works great.

 

dchemanovSCVQB
Participant
Participant

I've attached my rvt file, just in case)

 

dchemanovSCVQB
Participant
Participant

I also tried to use public XYZ ComputeCentroid() method of solid (https://www.revitapidocs.com/2015/42d79808-231b-f802-f574-6b799c95b871.htm) and received the same behavior for this beam.

RPTHOMAS108
Mentor
Mentor
Accepted solution

You've not included any code so hard to say what you are getting wrong but I suspect it is you treating uncut and cut geometry the same way, you need to do that from a lower level i.e. avoid using GetInstanceGeometry. My approach would be as below:

 

Private Function Obj_230320a(ByVal commandData As Autodesk.Revit.UI.ExternalCommandData,
ByRef message As String, ByVal elements As Autodesk.Revit.DB.ElementSet) As Result
        Dim UIApp As UIApplication = commandData.Application
        Dim UIDoc As UIDocument = commandData.Application.ActiveUIDocument
        If UIDoc Is Nothing Then Return Result.Cancelled Else
        Dim IntDoc As Document = UIDoc.Document

        Dim R As Reference
        Try
            R = UIDoc.Selection.PickObject(Selection.ObjectType.Element)
        Catch ex As Exception
            Return Result.Cancelled
        End Try

        Dim El As Element = IntDoc.GetElement(R)
        Dim GeomEl As GeometryElement = El.Geometry(New Options With {.DetailLevel = ViewDetailLevel.Fine})
        Dim geomInst As GeometryInstance = GeomEl.OfType(Of GeometryInstance).FirstOrDefault

        'This is the old way can use GetSymbolGeometryId.SymbolId now
        'FS below is not the Revit type but a element for the graphical symbol.
        Dim FS As FamilySymbol = IntDoc.GetElement(geomInst.Symbol.Id)
        Dim GeomElSymb As GeometryElement = FS.Geometry(New Options With {.DetailLevel = ViewDetailLevel.Fine})
        Dim S As Solid = GeomElSymb.OfType(Of Solid).Where(Function(k) k.Volume > 0).FirstOrDefault
        Dim BB As BoundingBoxXYZ = S.GetBoundingBox

        Dim Max As XYZ = BB.Transform.OfPoint(BB.Max)
        Dim Min As XYZ = BB.Transform.OfPoint(BB.Min)
        Dim EP0 As New XYZ(Min.X, (Min.Y + Max.Y) / 2, Max.Z)
        Dim EP1 As New XYZ(Max.X, (Min.Y + Max.Y) / 2, Max.Z)

        Dim LN As Line = Line.CreateBound(EP0, EP1)
        LN = LN.CreateTransformed(geomInst.Transform)

        Using tx As New Transaction(IntDoc, "LN")
            If tx.Start = TransactionStatus.Started Then
                'Extension method (not part of RevitAPI)
                LN.Draw(IntDoc)
                tx.Commit()
            End If
        End Using


        Return Result.Succeeded
    End Function

 

Test cut and uncut when dealing with geometryTest cut and uncut when dealing with geometry

dchemanov
Participant
Participant
Thanks a lot! My bad was in understanding transforms. Your code did the trick.
0 Likes

RPTHOMAS108
Mentor
Mentor
Accepted solution

Actually going back to the original problem, you want to find the ends of the cut solid and get the bounding box of that. So this below is more appropriate for that task. The geometry in the symbol will be uncut.

 

Private Function Obj_230323a(ByVal commandData As Autodesk.Revit.UI.ExternalCommandData,
ByRef message As String, ByVal elements As Autodesk.Revit.DB.ElementSet) As Result
        Dim UIApp As UIApplication = commandData.Application
        Dim UIDoc As UIDocument = commandData.Application.ActiveUIDocument
        If UIDoc Is Nothing Then Return Result.Cancelled Else
        Dim IntDoc As Document = UIDoc.Document

        Dim R As Reference
        Try
            R = UIDoc.Selection.PickObject(Selection.ObjectType.Element)
        Catch ex As Exception
            Return Result.Cancelled
        End Try

        Dim El As Element = IntDoc.GetElement(R)
        Dim GeomEl As GeometryElement = El.Geometry(New Options With {.DetailLevel = ViewDetailLevel.Fine})
        'check for top level solid first
        Dim S As Solid = GeomEl.OfType(Of Solid).Where(Function(k) k.Volume > 0).FirstOrDefault
        Dim geomInst As GeometryInstance = GeomEl.OfType(Of GeometryInstance).FirstOrDefault

        If S Is Nothing Then
            'This is the old way
            'FS below is not the Revit type but a element for the graphical symbol.
            Dim FS As FamilySymbol = IntDoc.GetElement(geomInst.Symbol.Id)
            Dim GeomElSymb As GeometryElement = FS.Geometry(New Options With {.DetailLevel = ViewDetailLevel.Fine})
            S = GeomElSymb.OfType(Of Solid).Where(Function(k) k.Volume > 0).FirstOrDefault
            If S Is Nothing Then
                Return Result.Cancelled
            End If
        Else
            S = SolidUtils.CreateTransformed(S, geomInst.Transform.Inverse) ' local symbol space
        End If
        Dim BB As BoundingBoxXYZ = S.GetBoundingBox

        Dim Max As XYZ = BB.Transform.OfPoint(BB.Max)
        Dim Min As XYZ = BB.Transform.OfPoint(BB.Min)
        Dim EP0 As New XYZ(Min.X, (Min.Y + Max.Y) / 2, Max.Z)
        Dim EP1 As New XYZ(Max.X, (Min.Y + Max.Y) / 2, Max.Z)

        Dim LN As Line = Line.CreateBound(EP0, EP1)
        LN = LN.CreateTransformed(geomInst.Transform)

        Return Result.Succeeded

    End Function

 

dchemanovSCVQB
Participant
Participant

Yeah, based on my samples I got almost this solution. Thanks a lot, this thread was very helpful for my understanding!

0 Likes

dchemanovSCVQB
Participant
Participant

I case someone will need to check C# solution

Options options = new Options
{
    DetailLevel = ViewDetailLevel.Fine
};

GeometryElement geometryElement = element.get_Geometry(options);
Solid solid = geometryElement.OfType<Solid>().FirstOrDefault(x => x.Volume > 0);
GeometryInstance geometryInstance = null;

if (solid == null)
{
    geometryInstance = geometryElement.OfType<GeometryInstance>().FirstOrDefault();

    FamilySymbol familySymbol = element.Document.GetElement(geometryInstance.Symbol.Id) as FamilySymbol;
    GeometryElement geometryElementSymbol = familySymbol.get_Geometry(options);
    solid = geometryElementSymbol.OfType<Solid>().FirstOrDefault(x => x.Volume > 0);
}

if (solid != null)
{
    BoundingBoxXYZ boundingBoxXYZ = solid.GetBoundingBox();

    XYZ min = boundingBoxXYZ.Transform.OfPoint(boundingBoxXYZ.Min);
    XYZ max = boundingBoxXYZ.Transform.OfPoint(boundingBoxXYZ.Max);

    if (geometryInstance != null)
    {
        min = geometryInstance.Transform.OfPoint(min);
        max = geometryInstance.Transform.OfPoint(max);
    }

    return (min + max) / 2;
}
0 Likes