SPLINE: working on script

We are trying to understand the potential of VB scripting within Revit, together with prof. Gian Marco Todesco
(thank you SO much!).

 

This post shows our attempt to generate a script that works on top of a given spline, drawn in Revit 2010.

Given a spline in R2010 the script: 

1-attaches N reference points to it; 

2-attaches a family to each refPoint;

 

This procedure seems pretty easy, 
but we found it wasn't!

Here are the steps and the problems we faced:

 

 

0_DOCUMENT vs APPLICATION  scripts

At first, i would like to underline that R2010 makes a difference in Syntax depending if the script is working within the current Document only, or in the whole Application.

What happens is that when we are in the Application ambient, we need to "call the document".

For instance, a command in Document mode should suond like 

DoThis.ThisWay

while a command in Application is like

ActiveDocument.DoThis.ThisWay

It was important to clarify this

since that part appears several times in the scripts
i'm going to publish:
all of them are written in Application mode. 

 

1_first bits of Script

When I've described the problem to prof Todesco, he took some days to study R2010 and after that he gave me this following bit of script: it puts 20 reference Points on the spline.

At first i need to select the spline, after That I launch the macro. The reason will be clear enough once you'll read the code.

The first, grey part of the script is mandatory for each VB script: I'll write it only once, but you need it for each of the scripts I am going to show.

 

'this part is mandatory- i will not replicate in following scripts but it is always needed

Imports Autodesk.Revit

Imports System.Collections

Imports Autodesk.Revit.Elements


<System.AddIn.AddIn("AppAddIn", Version:="1.0", Publisher:="", Description:="")> _

Partial Class ThisApplication


    Private Sub Module_Startup(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Startup


    End Sub


    Private Sub Module_Shutdown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shutdown


    End Sub

 

End of the mandatory part, beginning of the script!

 

 

    Public Sub spline()

 

'checking i've taken the spline only
        Dim selection As ElementSet = ActiveDocument.Selection.Elements

        If (selection.Size <> 1) Then

            MsgBox("A single component should be selected")

            Return

        End If



        'checking i've taken a spline
        Dim iter As IEnumerator = selection.ForwardIterator

        iter.MoveNext()

        Dim element As Autodesk.Revit.Element = iter.Current

        If Not TypeOf element Is Autodesk.Revit.Elements.CurveElement Then

            MsgBox("A curve should be selected")

            Return

        End If

        Dim curve As Elements.CurveElement = element

        Dim curveRef As Geometry.Reference = curve.GeometryCurve.Reference


'Me.ActiveDocument is the way to call the current instance

        'Create n reference points along the spline

        Dim n As Integer = 20

        Me.ActiveDocument.BeginTransaction()

        For i = 0 To n - 1

            Dim t As Double = i / (n - 1)

            Dim pointRef As PointOnEdge = Me.Create.NewPointOnEdge(curveRef, t)

            Dim referencePoint As ReferencePoint = Me.ActiveDocument.FamilyCreate.NewReferencePoint(pointRef)

        Next

        Me.ActiveDocument.EndTransaction()
    End Sub

 

I've underlined those lines because, if macro is running in Application, we need to specify that each cycle is a Transaction, otherwise it won't be read properly.
 

Few notes: we're dividing the spline into 20 pieces, but we are not controlling how.This control will be object of a future post. Moreover, first and last poing given by the macro are already existing in the spline, since these are driving geometry, so Revit says that there are superimposed refPoints somewhere.

 

1_inserting family instances.

After Receiving this bit of script, i've written a small procedure which loads a family and creates a number of instances, and maps a parameter (IMPORTANT! we are making conceptual mass).

 

        'check if the family is loaded

        If (Not ActiveDocument.LoadFamily("cubo.rfa")) Then

            MsgBox("Loaded a family nono")

            Return

        End If

        Dim fs As Symbols.FamilySymbol = Nothing

        If (Not ActiveDocument.LoadFamilySymbol("cubo.rfa", "cubo", fs)) Then

            MsgBox("Non riesco a caricare il family symbol. ciao ciao")

            Return

       End If

 

'declarations

        Dim x As Double

        Dim i As Integer


        'creating a number of instances

        Me.ActiveDocument.BeginTransaction()

 

        For i = 1 To 20

            x = i

            Dim fi_pos As Geometry.XYZ = Application.Create.NewXYZ(0, 20 * x, 0)

            Dim fi As FamilyInstance

            fi = ActiveDocument.FamilyCreate.NewFamilyInstance(fi_pos, fs, Structural.Enums.StructuralType.NonStructural)


            'setting the parameter

            fi.ParametersMap("l").Set(Math.PI * 0.5 * x)


        Next


        Me.ActiveDocument.EndTransaction()

 

 

I've written this one departing from the one already existing on this website.

After that i've just copyied and pasted these two bits.

What we have is a macro that makes the two operations at the same time, but without any relation.

If i try and write them in the same transaction, something extremely important happens.

Revit cant create the first instace.

Since our cycle is defined   

For i = 0 To n - 1

while the paramether expression is

fi.ParametersMap("l").Set(Math.PI * 0.5 * i)

the very first paramether value is 0. In the definition of revit's family THIS CONDITION IS NOT ACCEPTABLE since Volume parameter is defined as

V= K / l

When interfacing Revit to VB Script or other scripting sources, it is vital to build up families whose paramether are free to vary according to the values given by the macro, otherwise the interaction will be distructive only, like in this case.

 

3_Building Up Relations

We need now to build up some relations among these two parts.

First thing, i want each family instance to be attached to a refPoint. In order to do so, we shall just rewrite the script:

 

        'declarations

        Dim n As Integer = 20

        Dim t As Double

        Dim coord As Geometry.XYZ

        Dim pointRef As PointOnEdge

        Dim MagicPoint As ReferencePoint

        Dim fi As FamilyInstance


        Me.ActiveDocument.BeginTransaction()


        'cycle
        For i = 0 To n - 1

            'dividing the spline

            t = i / (n - 1)

 


            'getting magicPoint's position

            pointRef = Me.Create.NewPointOnEdge(curveRef, t)

            MagicPoint = Me.ActiveDocument.FamilyCreate.NewReferencePoint(pointRef)

            coord = MagicPoint.Position

 

            'creating an instance

            fi = ActiveDocument.FamilyCreate.NewFamilyInstance(coord, fs, Structural.Enums.StructuralType.NonStructural)

            fi.ParametersMap("l").Set(Math.PI * 0.5 * i + 1)


        Next

        Me.ActiveDocument.EndTransaction()

 
The trick here is to get coordinates from a reference point and use it to position the instance.
It is possible to import a family in several ways. This time we are creating a NewFamilyInstance( -POSITION- , -SYMBOL- , -STRUCTURAL TYPE- ).
 
I had to modify the script in order to obtain a result
 
 
This is the result.
As usual, few notes: 1-geometry is not associative, since i've just given coordinates instead of hosting families on refPoints 2-instances do not follow spline geometry, spline direction.
I've therefore explored other ways of creating instances: basically it is possbile either to attach it on a face, or on a location. I've tried this second chance which, in its most complete formula, allows to create a NewFamilyInstance( -POSITION- , -SYMBOL- , -HOST-, -DIRECTION-, -STRUCTURAL TYPE- ). Since we know Revit-tongue, we are extremely interested on the "host". Therefore i've tried and assign reference point as host
 
fi = ActiveDocument.FamilyCreate.NewFamilyInstance(coord, fs, MagicPoint, Structural.Enums.StructuralType.NonStructural)
 

What we see is that refPoint geometry is associative, while instances are not...seems like they are not hosted on ref points really. This situation is annoying.

On the other hand it is possible to control direction of the instances.

referenceDirection is the direction towards which instances are "looking at".

I can orientate them 45°.

Dim real_direction as Geometry.XYZ

real_direction =  Application.Create.NewXYZ(1, 1, 0)

 fi = ActiveDocument.FamilyCreate.NewFamilyInstance(coord, fs,real_direction ,MagicPoint, Structural.Enums.StructuralType.NonStructural)

...or along the spline...

 

real_direction =  puntoMagico.GetCoordinateSystem.BasisX

...or i can orientate them from n-th to n-1-th point. I am publishing the full script for this version.

 

    Public Sub insert_and_spline_HOST_discretize()

        'load family

        If (Not ActiveDocument.LoadFamily("cubo.rfa")) Then

            MsgBox("Loaded a family nono")

            Return

        End If


        Dim fs As Symbols.FamilySymbol = Nothing

        If (Not ActiveDocument.LoadFamilySymbol("cubo.rfa", "cubo", fs)) Then

            MsgBox("Non riesco a caricare il family symbol. ciao ciao")

            Return

        End If


     'checking i've taken the spline only

        Dim selection As ElementSet = ActiveDocument.Selection.Elements

        If (selection.Size <> 1) Then

            MsgBox("A single component should be selected")

            Return

        End If

        'Get the selected element and check that it is a curve


        Dim iter As IEnumerator = selection.ForwardIterator

        iter.MoveNext()

        Dim element As Autodesk.Revit.Element = iter.Current

        If Not TypeOf element Is Autodesk.Revit.Elements.CurveElement Then

            MsgBox("A curve should be selected")

            Return

        End If

        Dim curve As Elements.CurveElement = element

        Dim curveRef As Geometry.Reference = curve.GeometryCurve.Reference

        'dichiarazioni

        Dim n As Integer = 20

        Dim t As Double

        Dim coord_prev As Geometry.XYZ

        Dim coord As Geometry.XYZ

        Dim pointRef As PointOnEdge

        Dim MagicPoint As ReferencePoint

        Dim real_direction As Geometry.XYZ

        Dim fi As FamilyInstance

        Me.ActiveDocument.BeginTransaction()

        'instance i=0

        pointRef = Me.Create.NewPointOnEdge(curveRef, 0)

        MagicPoint = Me.ActiveDocument.FamilyCreate.NewReferencePoint(pointRef)

        coord = MagicPoint.Position

        'cycle

        For i = 0 To n - 1

            'collecting magicPoint's position for the next iteration

            coord_prev = coord

            'dividing the spline

            t = i / (n - 1)

            'getting magicPoint's position

            pointRef = Me.Create.NewPointOnEdge(curveRef, t)

            MagicPoint = Me.ActiveDocument.FamilyCreate.NewReferencePoint(pointRef)

            coord = MagicPoint.Position

            'getting reference direction

            real_direction = coord.Subtract(coord_prev)

            'creating an instance

            fi = ActiveDocument.FamilyCreate.NewFamilyInstance(coord, fs, real_direction, MagicPoint, Structural.Enums.StructuralType.NonStructural)

            fi.ParametersMap("l").Set(Math.PI * 0.5 * i + 1)

        Next

        Me.ActiveDocument.EndTransaction()


    End Sub

Another way of solving the problem of mismatch between Family Paramethers and Script Based - Paramether Map.

 

It will be vital now on to work on how to Host families to Revit geometrical entities.

 

AllegatoDimensione
Binary Data inserisci su spline.rfa364 KB
Binary Data cubo.rfa224 KB

commenti

In this "easy" script, Professor Todesco succeded in drawing a simple line into a project.

 

    Public Sub line()

        Dim p0 As Geometry.XYZ = Application.Create.NewXYZ(1.0, 0.0, 0.0)

        Dim p1 As Geometry.XYZ = Application.Create.NewXYZ(0.0, 1.0, 0.0)


'creating a line by two points

        Dim line As Geometry.Line = Application.Create.NewLine(p0, p1, True)

'creating a plane by normal + a point

        Dim plane As Geometry.Plane = Application.Create.NewPlane(p0.Cross(p1), p1)

 

'sketchplanes are extremely important entities in revit: they allow to draw on them.    

   Dim skplane As SketchPlane = Document.Create.NewSketchPlane(plane)


'drawing this line on the sketchplane

        Dim mCurve As ModelCurve = Document.Create.NewModelCurve(line, skplane)


    End Sub

 

 

marco.mondello@gmail.com

I've found a problem when changing parameter of a via-script built-in family.

Changing a paramether is something we do every day, but this seems to be a problem when re-executing a macro.

For instance i've changeda paramether in my family: it's about changing an inverse law into a quadratic law for height variation of this box. I have not touched the paramether mapped by the macro.

Indeed, it is possble to make the cannge effective into the .rfa by loading the family (as usual....)

On the other hand, if i try and re-launch the macro, this execution fails. It says that i am trying to modify the macro ouside the transaction (i'm making a move outside the transaction- which move, according to me, corresponds to the reloading of the family).

It will be necessary to make the routine able to allow the riparametrization of a buildt in instance, otherwise most of revit feautures won't be able to be effective when using macros.

marco.mondello@gmail.com