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