Access the sketch lines in a Stairs by Sketch

Access the sketch lines in a Stairs by Sketch

Anonymous
Not applicable
1,471 Views
2 Replies
Message 1 of 3

Access the sketch lines in a Stairs by Sketch

Anonymous
Not applicable

In the attached image, I've gone into Edit Mode for a Stairs by Sketch.  How can I access those lines in the API?

 

sketch.PNG

 

@RPTHOMAS108, you left a comment in a previous answer that makes me think this is possible.

0 Likes
Accepted solutions (1)
1,472 Views
2 Replies
Replies (2)
Message 2 of 3

RPTHOMAS108
Mentor
Mentor
Accepted solution

Lines for sketches are available directly as model lines through filtering:

BuiltInCategory.OST_StairsSketchBoundaryLines

BuiltInCategory.OST_StairsSketchRiserLines

 

Problem is always understanding which ones relate to the stair you are interested in. The model lines will have a sketch plane so they can be grouped by the Id of that. I believe there is a unique SketchPlane for each sketch but I'm not 100% on that.

 

There is a method Stairs.GetStairRuns leading to an array of StairsRun objects. From one of those you can call the method .GetFootprintBoundary which will return a CurveLoop. The geometry of this will match some of the geometry of the lines from BuiltInCategory.OST_StairsSketchBoundaryLines. So by using an approximate match of line geometry you know which model lines of category OST_StairsSketchBoundaryLines match the curves in StairsRun.GetFootprintBoundary. You've already grouped the model lines together by their SketchPlane Id so can establish the relationship of all lines related to that stair.

 

Sketches also exist in the API but I don't know how useful they are. They contain a profile property which returns a CurveArrArray (Array of curve arrays). One array will contain the outline, others will contain each riser line etc. The curves are not categorised or linked to the model lines noted above (apart from by similar geometry) so you'll have to compare geometry again to know which array relates to which part of the sketch. The usual method of returning sketches from objects is to delete the object record the ids of deleted items and then roll back the transaction. If you use this method on stairs it will return more than one sketch (if you have railings) and lots of lines. So you will still need to establish which sketch is the one you want and which lines are which. Below are some alternative extension methods for getting sketches and lines from stairs. Important to note that stairs can only contain lines and arcs so comparison of the curves is relatively straightforward.

 

VB.Net

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="Run"></param>
    ''' <returns>List of curves associated with stairs sketch, boundary lines, riser lines</returns>
    ''' <remarks></remarks>
    <Extension()> _
    Public Function GetSketchLines(ByVal Run As Architecture.StairsRun) As Tuple(Of List(Of ModelCurve), List(Of ModelCurve))
        Dim Out As New Tuple(Of List(Of ModelCurve), List(Of ModelCurve))(New List(Of ModelCurve), New List(Of ModelCurve))
        If Run.StairsRunStyle <> Architecture.StairsRunStyle.Sketched Then Return Out Else 

        Dim FPB As CurveLoop = Run.GetFootprintBoundary
        Dim D As Document = Run.Document
        Dim FEC As New FilteredElementCollector(D)
        Dim ECF_bl As New ElementCategoryFilter(BuiltInCategory.OST_StairsSketchBoundaryLines)
        Dim ECF_rl As New ElementCategoryFilter(BuiltInCategory.OST_StairsSketchRiserLines)
        Dim MCs_bl As List(Of ModelCurve) = FEC.WherePasses(ECF_bl).WhereElementIsNotElementType.ToElements.Cast(Of ModelCurve)()
        Dim MCs_rl As List(Of ModelCurve) = FEC.WherePasses(ECF_rl).WhereElementIsNotElementType.ToElements.Cast(Of ModelCurve)()
        'Compare each item in FPB to match one item in MCs by endpoints geometry (approx match) and curve type
        'only curves of arc and line are accepted in stairs sketches
        Dim RComp As New RT_RoughEqComparerOfBoundCurve(1)

        Dim ID As ElementId = ElementId.InvalidElementId
        For Each item As Curve In FPB
            Dim FC As ModelCurve = MCs_bl.Find(Function(j) RComp.Equals(j.GeometryCurve, item) = True)
            If FC Is Nothing = False Then
                ID = FC.SketchPlane.Id
                Exit For
            End If
        Next
        If ID = ElementId.InvalidElementId Then Return Out Else 
        'Get all curves in MCs that have this sketch plane
        Dim SK_bl As List(Of ModelCurve) = MCs_bl.FindAll(Function(jx) jx.SketchPlane.Id = ID)
        Dim SK_rl As List(Of ModelCurve) = MCs_rl.FindAll(Function(jx) jx.SketchPlane.Id = ID)
        Return New Tuple(Of List(Of ModelCurve), List(Of ModelCurve))(SK_bl, SK_rl)
    End Function

    <Extension()> _
    Public Function GetSketchesTransRollBackMethod(ByVal Stair As Autodesk.Revit.DB.Architecture.Stairs) As Sketch()
        Dim EID As List(Of ElementId) = Nothing
        'Are we in a transaction already, not sure...
        On Error GoTo TryST
        Using tx As New Transaction(Stair.Document, "Get Sketch")
            If tx.Start = TransactionStatus.Started Then
                EID = Stair.Document.Delete(Stair.Id)
                tx.RollBack()
            End If
        End Using
        GoTo SkipST
TryST:
        On Error GoTo SkipST
        Using tx As New SubTransaction(Stair.Document)
            If tx.Start = TransactionStatus.Started Then
                EID = Stair.Document.Delete(Stair.Id)
                tx.RollBack()
            End If
        End Using
SkipST:
        Dim Sk As New List(Of Sketch)
        For Each item As ElementId In EID
            Dim SKt As Sketch = TryCast(Stair.Document.GetElement(item), Sketch)
            If SKt Is Nothing = False Then
                Sk.Add(SKt)
            End If
        Next

        If Sk.Count > 0 Then
            Return Sk.ToArray
        Else
            Return Nothing
        End If
    End Function

    <Extension()> _
    Public Function GetSketch(ByVal Stair As Autodesk.Revit.DB.Architecture.Stairs) As Sketch

        Dim Runs As ICollection(Of ElementId) = Stair.GetStairsRuns
        If Runs.Count = 0 Then Return Nothing Else 
        Dim FR As Autodesk.Revit.DB.Architecture.StairsRun = Stair.Document.GetElement(Runs(0))
        If FR.StairsRunStyle <> Architecture.StairsRunStyle.Sketched Then Return Nothing Else 

        Dim CL As CurveLoop = FR.GetFootprintBoundary

        Dim D As Document = Stair.Document
        Dim FEC As New FilteredElementCollector(D)
        Dim ECF_bl As New ElementCategoryFilter(BuiltInCategory.OST_StairsSketchBoundaryLines)
        Dim IDs As List(Of ElementId) = FEC.WherePasses(ECF_bl).WhereElementIsNotElementType.ToElementIds

        Dim J As IEnumerable(Of ModelCurve)
        J = From ID As ElementId In IDs
            Let Cx As ModelCurve = TryCast(D.GetElement(ID), ModelCurve)
            Where Cx Is Nothing = False
            Where CL.Contains(Cx.GeometryCurve, New RT_RoughEqComparerOfBoundCurve(1))
            Select Cx

        Dim ML As List(Of ModelCurve) = J.ToList
        If ML.Count = 0 Then Return Nothing Else 

        Dim PL_ID As ElementId = ML(0).SketchPlane.Id

        Dim FEC1 As New FilteredElementCollector(D)
        Dim ECF_sk As New ElementClassFilter(GetType(Sketch))
        Dim IDs_SK As List(Of ElementId) = FEC1.WherePasses(ECF_sk).WhereElementIsNotElementType.ToElementIds

        Dim J1 As IEnumerable(Of Sketch)
        J1 = From x As ElementId In IDs_SK
             Let Sk As Sketch = TryCast(D.GetElement(x), Sketch)
             Where Sk Is Nothing = False
             Where Sk.SketchPlane.Id = PL_ID
             Select Sk

        Dim OneWholeArrayMatches = Function(Sk As Sketch) As Boolean
                                       For Each item As CurveArray In Sk.Profile
                                           Dim T As Integer = item.Size
                                           For Each C As Curve In item
                                               If CL.Contains(C, New RT_RoughEqComparerOfBoundCurve(1)) Then T -= 1 Else 
                                           Next
                                           If T = 0 Then
                                               Return True
                                           End If
                                           Return False
                                       Next
                                   End Function

        Dim J2 As IEnumerable(Of Sketch)
        J2 = From x As Sketch In J1
             Where OneWholeArrayMatches(x) = True
             Select x

        Dim out As List(Of Sketch) = J2.ToList
        If out.Count = 1 Then
            Return out(0)
        Else
            Return Nothing
        End If
    End Function

    Private Class RT_RoughEqComparerOfBoundCurve
        Implements IEqualityComparer(Of Curve)

        Private IntPrec_Ft As Double = 1 / 304.8
        Private IntIgnoreZ As Boolean = False

        Public Sub New(Optional Precision_mm As Double = 1, Optional IgnoreZ As Boolean = False)
            IntPrec_Ft = Precision_mm / 304.8
            IntIgnoreZ = IgnoreZ
        End Sub
        Private Sub CheckImplemented(C As Curve)
            If C.IsBound = False Then
                Throw New ArgumentException("Not a bound curve.", "C")
            End If
            If GetType(Arc) = C.GetType Then Exit Sub Else 
            If GetType(Line) = C.GetType Then Exit Sub Else 
            Throw New NotImplementedException("Not implemented for " & C.GetType.ToString)
        End Sub

        Public Overloads Function Equals(x As Curve, y As Curve) As Boolean Implements IEqualityComparer(Of Curve).Equals
            CheckImplemented(x)
            CheckImplemented(y)
            If x.GetType <> y.GetType Then Return False Else 
            Dim Eps As XYZ() = New XYZ(3) {x.GetEndPoint(0), x.GetEndPoint(1), y.GetEndPoint(0), y.GetEndPoint(1)}

            If GetDistTo(Eps(0), Eps(2)) <= IntPrec_Ft AndAlso GetDistTo(Eps(1), Eps(3)) <= IntPrec_Ft Then
                If x.GetType = GetType(Line) Then Return True Else 
            ElseIf GetDistTo(Eps(0), Eps(3)) <= IntPrec_Ft AndAlso GetDistTo(Eps(1), Eps(2)) <= IntPrec_Ft Then
                If x.GetType = GetType(Line) Then Return True Else 
            Else
                Return False
            End If

            Dim A1 As Arc = TryCast(x, Arc)
            Dim A2 As Arc = TryCast(y, Arc)
            If A1 Is Nothing OrElse A2 Is Nothing Then Return False Else 

            Return GetDistTo(A1.Evaluate(0.5, True), A2.Evaluate(0.5, True)) <= IntPrec_Ft
        End Function

        Private Function GetDistTo(XYZ1 As XYZ, XYZ2 As XYZ) As Double
            Return GetDistTo(New Double() {XYZ1.X, XYZ1.Y, XYZ1.Z}, New Double() {XYZ2.X, XYZ2.Y, XYZ2.Z})
        End Function
        Private Function GetDistTo(XYZ1 As Double(), XYZ2 As Double()) As Double
            If IntIgnoreZ Then
                XYZ1(2) = 0
                XYZ2(2) = 0
            End If
            Return ((XYZ1(0) - XYZ2(0)) ^ 2 + (XYZ1(1) - XYZ2(1)) ^ 2 + (XYZ1(2) - XYZ2(2)) ^ 2) ^ 0.5
        End Function

        Public Overloads Function GetHashCode(obj As Curve) As Integer Implements IEqualityComparer(Of Curve).GetHashCode
            Return obj.GetHashCode
        End Function
    End Class

 

 

 

Message 3 of 3

Anonymous
Not applicable

Thanks for all the different ideas!

0 Likes