Use family parameters in extrusion

Use family parameters in extrusion

marius.fe
Contributor Contributor
5,384 Views
7 Replies
Message 1 of 8

Use family parameters in extrusion

marius.fe
Contributor
Contributor

Hi everybody, 

 

first, I am new to the forum and Revit API. So please be lenient if my code does not meet the standards. I work with Revit 2020 and I could not find a proper solution for my problem in the Revit SDK samples.

 

Current state:

Creating an extrusion or defining family parameters and assigning parameter values in a type I can solve using the API. These functions are implemented in the example attached. To run the example the generic family template has to be used.

In this example a simple extrusion of a rectangle profile is created. The parameters "edge length" and "extrusion height" are created. The parameters are not linked to the extrusion, so if you change a parameter value in Revit (after running the example), nothing changes in the extrusion geometry.

 

Desired state:

The parameters should be linked to the extrusion, so if you change one of the parameters. Does anyone have an idea how this can be implemented?

 

Thanks,

Marius

 

0 Likes
Accepted solutions (1)
5,385 Views
7 Replies
Replies (7)
Message 2 of 8

jeremy_tammik
Alumni
Alumni
0 Likes
Message 3 of 8

RPTHOMAS108
Mentor
Mentor

Most processes in the API run parallel with how they are done in the UI, for your case that means:

 

Document.FamilyCreate.NewDimension(View, Line, ReferenceArray) = >Dimension

Dimension.FamilyLabel

 

 

0 Likes
Message 4 of 8

marius.fe
Contributor
Contributor

Thanks Thomas for your answer! 
I tried the following:

Autodesk.Revit.DB.XYZ p0 = Autodesk.Revit.DB.XYZ.Zero;
Autodesk.Revit.DB.XYZ p1 = new Autodesk.Revit.DB.XYZ(edgeLength.Value, 0, 0);
Autodesk.Revit.DB.XYZ p2 = new Autodesk.Revit.DB.XYZ(edgeLength.Value, edgeLength.Value, 0);
Autodesk.Revit.DB.XYZ p3 = new Autodesk.Revit.DB.XYZ(0, edgeLength.Value, 0);
Line line1 = Line.CreateBound(p0, p1);
Line line2 = Line.CreateBound(p1, p2);
Line line3 = Line.CreateBound(p2, p3);
Line line4 = Line.CreateBound(p3, p0);
curveArray1.Append(line1);
curveArray1.Append(line2);
curveArray1.Append(line3);
curveArray1.Append(line4);

ReferenceArray references = new ReferenceArray();
references.Append(line1.GetEndPointReference(0));
references.Append(line1.GetEndPointReference(1));

m_familyDocument.FamilyCreate.NewDimension(m_familyDocument.ActiveView, line1, references);

curveArrArray.Append(curveArray1);
#endregion
// here create rectangular extrusion
Extrusion rectExtrusion = m_creationFamily.NewExtrusion(true, curveArrArray, sketchPlane, extrusionHeight.Value);

But the references are always null. So there is no entity for these lines, which can be referenced?

 

Best wishes, 

Marius

0 Likes
Message 5 of 8

RPTHOMAS108
Mentor
Mentor

Been investigating this and I believe that although you can dimension within the sketch for the UI this can't be done for the API. I've trialled both:

Line end point references

Line references

Both for the model line geometry curves that are part of the sketch (OST_SketchLines). Although the referece values for these match what is in the reference array for the dimension in Revit lookup I still get the 'invalid number of references' exception message. I think the issue stems from the fact the sketch lines are not visible in the view required by the .NewDimension method. Others may be able to advise otherwise on this.

 

If not you need to instead use the edges of the geometry of the extrusion, similar to below. This approach has certain disadvantages, the main one being that the shape of the extrusion can't be changed from outside the sketch with dimensions like it can when you place dimension within the sketch in the UI.

 

 

 Private Function SamePoints(Pt1 As XYZ, Pt2 As XYZ, Optional Tolerance As Double = 0.5 / 304.8) As Boolean
        Return Pt1.DistanceTo(Pt2) <= Tolerance
    End Function
    Private Function SameLines(LN1 As Line, LN2 As Line, Optional IgnoreEndOrder As Boolean = True,
                               Optional Tolerance As Double = 0.5 / 304.8) As Boolean

        If IgnoreEndOrder = False Then
            Return SamePoints(LN1.GetEndPoint(0), LN2.GetEndPoint(0), Tolerance) AndAlso
                SamePoints(LN1.GetEndPoint(1), LN2.GetEndPoint(1), Tolerance)
        Else
            If SamePoints(LN1.GetEndPoint(0), LN2.GetEndPoint(0), Tolerance) Then
                Return SamePoints(LN1.GetEndPoint(1), LN2.GetEndPoint(1), Tolerance)
            Else
                If SamePoints(LN1.GetEndPoint(0), LN2.GetEndPoint(1), Tolerance) Then
                    Return SamePoints(LN1.GetEndPoint(1), LN2.GetEndPoint(0), Tolerance)
                Else
                    Return False
                End If
            End If
        End If
    End Function

    Private Function EdgesForExtrusion(Extrusion As Extrusion, Lines As Line()) As Edge()

        Dim GeomObj As GeometryElement =
            Extrusion.Geometry(New Options With {.ComputeReferences = True, .DetailLevel = ViewDetailLevel.Fine})

        Dim S As Solid = GeomObj(0) 'Always a single top level solid for family extrusions? (probably).
        Dim EdgesOut As New List(Of Edge)

        For i = 0 To S.Edges.Size - 1
            If Lines.Length = EdgesOut.Count Then Exit For Else

            Dim E As Edge = S.Edges(i)
            Dim C As Line = TryCast(E.AsCurve, Line)
            If C Is Nothing Then Continue For Else

            'I index the input based on the count of items in the output:
            If SameLines(Lines(EdgesOut.Count), C, True) Then
                EdgesOut.Add(E)
            End If
        Next
        Return EdgesOut.ToArray
    End Function


    Private Function TObj120(ByVal commandData As Autodesk.Revit.UI.ExternalCommandData,
ByRef message As String, ByVal elements As Autodesk.Revit.DB.ElementSet) As Result

        If commandData.Application.ActiveUIDocument Is Nothing Then Return Result.Cancelled Else
        Dim UIDoc As UIDocument = commandData.Application.ActiveUIDocument
        Dim Doc As Document = UIDoc.Document
        If Doc.IsFamilyDocument = False Then Return Result.Cancelled Else
        Dim AcView As ViewPlan = TryCast(Doc.ActiveView, ViewPlan)
        If AcView Is Nothing Then Return Result.Cancelled Else

        Const EdgeLength As Double = 1

        Dim LN1 = Line.CreateBound(XYZ.Zero, New XYZ(EdgeLength, 0, 0))
        Dim LN2 = Line.CreateBound(New XYZ(EdgeLength, 0, 0), New XYZ(EdgeLength, EdgeLength, 0))
        Dim LN3 = Line.CreateBound(New XYZ(EdgeLength, EdgeLength, 0), New XYZ(0, EdgeLength, 0))
        Dim LN4 = Line.CreateBound(New XYZ(0, EdgeLength, 0), XYZ.Zero)

        Dim Ca As New CurveArray
        Ca.Append(LN1)
        Ca.Append(LN2)
        Ca.Append(LN3)
        Ca.Append(LN4)
        Dim Caa As New CurveArrArray
        Caa.Append(Ca)

        Using tx As New Transaction(Doc, "Extrude and Dimension")
            If tx.Start = TransactionStatus.Started Then

                Dim Ext As Extrusion = Doc.FamilyCreate.NewExtrusion(True, Caa, AcView.SketchPlane, 1)

                Const EndPointRefs As Boolean = False

                Dim ra As New ReferenceArray
                If EndPointRefs = False Then
                    'This works and is ok when you flex the family because the edges are parallel

                    Dim Edges As Edge() = EdgesForExtrusion(Ext, New Line() {LN4, LN2})
                    If Edges.Length <> 2 Then
                        tx.RollBack()
                    Else
                        ra.Append(Edges(0).Reference)
                        ra.Append(Edges(1).Reference)
                    End If
                Else
                    'This you can also do but when you flex the family it will fail (constraints not satisfied)
                    'Outside the sketch you can't change the shape of the extrusion

                    Dim Edges As Edge() = EdgesForExtrusion(Ext, New Line() {LN1})
                    If Edges.Length <> 1 Then
                        tx.RollBack()
                    Else
                        ra.Append(Edges(0).GetEndPointReference(0))
                        ra.Append(Edges(0).GetEndPointReference(1))
                    End If
                End If

                Dim DM As Dimension = Doc.FamilyCreate.NewDimension(AcView, LN1, ra)

                Dim PM As FamilyParameter =
                    Doc.FamilyManager.AddParameter("UniqueNamedParam", BuiltInParameterGroup.PG_GENERAL, ParameterType.Length, True)

                DM.FamilyLabel = PM

                tx.Commit()
            End If
        End Using

    End Function

 

0 Likes
Message 6 of 8

jeremy_tammik
Alumni
Alumni

Regardless of whether working through the UI or API, it is helpful in both environments to follow the key concepts of family creation -- Bone, Muscle and Skin:

 

https://thebuildingcoder.typepad.com/blog/2013/06/key-concepts-of-the-family-editor.html

  

 

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 7 of 8

jeremytammik
Autodesk
Autodesk

Dear Marius and Richard,

 

Many thanks to Richard for his great experience and helpful advice!

 

Marius, your colleague Diana pointed out this thread to me in her DAS ticket #17163477 [Use family parameters in extrusion]. I closed that ticket now, since I prefer to continue the discussion here in public and with you directly.

 

This is what I suggested to her there, before becoming aware of this thread addressing the same issue:

 

Thank you for your query.

 

A working example that exactly matches what you are asking for is provided by the Family API labs:

 

https://thebuildingcoder.typepad.com/blog/2009/08/the-revit-family-api.html

 

To most robust approach to control the extrusion and any other family definition geometry in the desired manner is to follow the 'Bone, Muscle and Skin' principle described by Steven Campbell in his Key Concepts of the Family Editor:

 

https://thebuildingcoder.typepad.com/blog/2013/06/key-concepts-of-the-family-editor.html

 

The Building Coder has collected these and other articles on using the Family API for Creating Family Definitions in a dedicated topic group:

 

https://thebuildingcoder.typepad.com/blog/about-the-author.html#5.25.1

 

An up-to-date version of the Family API labs is included in the AdnRevitApiLabsXtra GitHub repository:

 

https://github.com/jeremytammik/AdnRevitApiLabsXtra

 

Have you been able to resolve your issue yet?

 

Best regards,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 8 of 8

marius.fe
Contributor
Contributor
Accepted solution

Hi Jeremy, hi Rpthomas08,

 

first of all many thanks for your support and effort! I answer so late because I have just found a solution yesterday.

A warning in advance: I know that a family should ideally be built using the Bones, Muscles, Skin approach. The code in this post does not follow this approach completely. Those with similar problems must be aware of this if they want to use the code posted below.

 

The solution was to first create the extrusion and then get the references via the ModelLine.GeometryCurve:

 

Extrusion rectExtrusion = m_creationFamily.NewExtrusion(true, curveArrArray, sketchPlane, extrusionHeight.Value);

[...]

//  references
var eP0ElementId = line.GetEndPointReference(0);

ModelLine elem = m_familyDocument.GetElement(eP0ElementId.ElementId) as ModelLine;

ReferenceArray references = new ReferenceArray();
references.Append(elem.GeometryCurve.GetEndPointReference(0));
references.Append(elem.GeometryCurve.GetEndPointReference(1));

// create line
var endPoint0 = line.GetEndPoint(0);
var endPoint1 = line.GetEndPoint(1);
Line lineForDimension = Line.CreateBound(endPoint0, endPoint1);

// create dimension
Dimension pDimTw =
	m_familyDocument.FamilyCreate.NewLinearDimension(pViewPlan, lineForDimension, references);

// add label to the dimension
FamilyParameter paramTw = m_familyDocument.FamilyManager.get_Parameter("Edge length");
pDimTw.FamilyLabel = paramTw;

 

The full code example is attached to this post. Please note that the Generic Model template must be used and that the names of used reference planes etc. are in German language in the code.

 

Best regards,

Marius

0 Likes