Tuesday, October 23, 2012

DesignScript - Points

Autodesk Labs have just released DesignScript as a Technology Preview, so I’ve been looking at porting some of Nathan Miller’s Proving Ground RevitPythonScript examples, just to see how DesignScript works. This first post summarises the Point Creation examples:

Single Point

This is the simplest example: Create a point at specified coordinates.

import("ProtoGeometry.dll");

// create a point with x, y, and z coordinates

x_pos = 10;

y_pos = 10;

z_pos = 0;

pCoord = Point.ByCoordinates(x_pos, y_pos, z_pos);


This was adapted from the example on page 17 of the DesignScript Language Manual, version 09. It is very simple: Import the ProtoGeometry dll, define the coordinates and call Point.ByCoordinates(x,y,z).


Point by Coordinates


Row of Points


Create a row of points at specified xy values:

import("ProtoGeometry.dll");
// create a row of points
x_pos = 0..100..10;
y_pos = 0..100..10;
z_pos = 0;
pCoord = Point.ByCoordinates(x_pos, y_pos, z_pos);


This example is hardly more complex. Just by defining the x and y coordinates as ranges rather than single values, Point.ByCoordinates generates a row of points.


Row of Points


Grid of Points


Create a grid of points:

import("ProtoGeometry.dll");
// create a grid of points
x_pos = 0..100..10;
y_pos = 0..100..10;
z_pos = 0;
pCoord = Point.ByCoordinates(x_pos<1>, y_pos<2>, z_pos);


And just by making a tiny change to the code, adding the Replication Guides <1> and <2> to the Point.ByCoordinates call, Point.ByCoordinates generates a grid of points.


Grid of Points


Sine Wave Points


Create a grid of points, where the z-value of each point is a function of the x and y coordinates.

import("ProtoGeometry.dll");
import("Math.dll");


def RadiansToDegrees(n)
{
    return = n * 360/(2 * 3.142);
}


def CosXPlusSinY(x, y)
{
    return = Math.Cos(RadiansToDegrees(x)) + Math.Sin(RadiansToDegrees(y));
}


def PointZ(x, y)
{
    z = CosXPlusSinY(x, y);
    s = Print(z);
    return = Point.ByCoordinates(10 * x, 10 * y, 10 * z);
}


// create a grid of points where z = f(xy)
x_pos = 0..30;
y_pos = 0..30;


pCoord = PointZ(x_pos<1>, y_pos<2>);


Now this is suddenly much more complex. The simple range and replication guide tricks work for simple cases, but are harder to apply in more real-world examples.


I wanted to use the replication guides to generate the grid (I could have fallen back on nested loops, of course, but that would be a shame). There doesn’t seem to be a way to include the z function inline, like this:


pcoord = Point.ByCoordinate(x_pos<1>, y_pos<2>,CosXPlusSinY(x_pos,y_pos))   //fail


So I wrapped the z-calculation and the Point.ByCoordinates call in a custom function, PointZ(x,y). In this way, I could use the replication guides in the call to PointZ, and


The other slight complexity is that both Python and Dynamo use radian values for their trig functions, DesignScript expects degrees. Hence the RadiansToDegrees function (with a dodgy value for pi because, well, I haven’t been able to find the correct syntax for DesignScript’s PI() function).


Sine Wave Points


And here’s the source: DesignScript Points examples.

Friday, October 19, 2012

Dynamo - Indexed Family Instances

Once classic way to create forms in Revit is to place multiple family instances that are driven by an indexed instance parameter. Zach Kron gives a good explanation in his post Parametric Patterns VI: Increment. The only tedium is in placing and indexing all the instances.

I wondered how hard it would be to place and index the instances using Dynamo, and this is the result:

Indexed Family Instances

The Family Type Selector and the Instance Parameter Selector allow you to choose the family type to place and the driving parameter. The Build Sequence node and its inputs generate the sequence of values for the driving parameter.

The Map node calls my user node ‘Family Instance Creator – Indexed’ once for each number in the sequence, and each time, the node creates a family instance and sets the driving parameter:

Family Instance Creator - Indexed

This is fairly straightforward. All the instances are placed at 0,0,0. The Family Instance Creator node creates the instance, and the Set Instance Parameter node sets the driving parameter.

And here is the result, using a simple twisting box family:

Twisting Tower

Finally, here is the source: Indexed Family Instances.

Sunday, October 14, 2012

Dynamo - Plane using 3 Points

This is the second method of creating a Plane shown by Nathan Miller, ported into Revit Dynamo:

PG 2.2 Plane by 3 points

Again, it’s fairly simple. The only tricky bit is that Dynamo doesn’t expose the Revit API call NewPlane(CurveArray), so I’ve made a Python-based user node to wrap that functionality.

The three points in this example are made with my XYZ… user nodes, and fed into the pair of Line nodes to create two geometry lines. Note the order of the points: The end of one line has to connect to the start of the next. The List node puts the two lines into a list to feed into the Plane by Curves user node. Again, the Transaction node is there because Dynamo asked for it, and the Watch is there so you can see that something happens.

The Plane by Curves user node is simply a wrapper for a bit of Python:

Plane by Curves

Here is the Python script:

Plane By Curves Python

The input from Dynamo is a list of curves or lines. But you cannot use that list of curves as input to NewPlane. You have to unpack the list and put the curves into a Revit API CurveArray object, because that is the type of input that NewPlane(CurveArray) expects. So the “carray =…” line creates the empty CurveArray, and the “for i in range…” lines append each curve to the CurveArray.

This shows the power of the Python node for extending Dynamo. The NewPlane(CurveArray) call isn’t exposed in base Dynamo, but it is fairly easy to add with a Python-based user node. I’m no Python expert (and this node owes a lot to Nathan Miller’s original RevitPythonShell script), but it didn’t take too much trial-and-error to get it working.

And here’s the source: Link to Dynamo Definition PG 2.2 Sketch Plane 3 Points.

Tuesday, October 9, 2012

Dynamo - Plane using World XYZ

Nathan Miller has two examples of creating Planes in RevitPythonShell. This is the first, ported into Revit Dynamo:

PG 2.1

It is very simple. I figured that I’d be using XYZ.BasisZ and XYZ.Zero a fair bit, so it made sense to implement them as User Nodes. These are fed into the Plane node, which is in turn fed into the Sketch Plane node.

The Transaction node wraps the whole thing in a Revit Transaction. At the moment, I add a Transaction node when Dynamo complains that it needs one. Seems to be working so far.

The Watch node just allows you to see that something has happened when you run the code.

Of the two User Nodes, here is XYZ.BasisZ:

XYZ.BasisZ

And here is XYZ.Zero:

XYZ.Zero

As you can see, both User Nodes wrap a snippet of Python that calls the Revit API. I’ve found that you need to give the Python Script node an input to make it work, hence the otherwise superfluous Number nodes. Not sure if there’s a way to fix that just now.

And here’s the source: Link to Dynamo Definition PG 2.1 Sketch Plane World XYZ.dyn. Again, it’s a zip containing the dyn file and the two User Node dyf files. Open the dyfs first to load them into Dynamo’s library.

Monday, October 8, 2012

Dynamo - Sine Wave Points

Last Dynamo ‘Create Points’ example, porting ‘Sine Wave Points’ from Nathan Miller’s Proving Ground Wiki, Create Points page.

PG 1.4

This is the trickiest example so far. The two Build Sequence nodes at bottom left create the X and Y values. The Cartesian Product node applies the function ‘comb’ to each pair of XY values. I’ve created the function as a couple of User Nodes, and then Mapped the output into the XYZ Scale node to scale the grid up by 10x.

As an aside, note the difference between Cartesian Product and Combine nodes. Cartesian gives {X1Y1, X1Y2, X1Y3, … X2Y1, X2Y2, … XnYn} which makes a grid; while Combine gives {X1Y1, X2Y2, X3Y3, … XnYn} which would just make a line. More info: Cartesian Product on Wikipedia.

Regarding the User Nodes: I tend to use them to wrap custom code for easy testing. If you build a big plate of spaghetti, it’s very hard to tell what’s not working properly. If you break the code into ‘User Node’ chunks, you can test each piece in isolation to make sure it works. Also, with the Map-like nodes, I prefer to feed them a User Node function because it’s clear how the inputs are going to be mapped. I’m not even sure some of my Mapped functions could be made without User Nodes.

Anyhow, this example uses two User Nodes. First up is cos(x)+sin(y):

cos(x) sin(y)

Which very obviously does what it says on the tin.

Next is XYZ - z=f(xy), which I’m quietly pleased with:

XYZ - z=F(xy)

This uses the input function f to generate a z value from the input x and y values, and then outputs an XYZ object. I wasn’t sure whether you could pass a function into a User Node like this. It’s great that you can, since it makes the initial layout much more flexible: Just substitute a different function in place of cos(x)+sin(y) to generate a different set of z values for the grid.

I’ve used an Apply here rather than a Combine, since the node will only accept one pair of xy values at a time. If you wanted a node that would accept list inputs, you’d also need to Combine the XYZ. I’ll post an example when I find an application.

Here’s the source: Dynamo Definition PG 1.4c Sine Wave Points.zip. The zip file contains both the .dyn file and the two User Node .dyf files. Open each of the .dyf files in turn to load them into Dynamo’s library, and then open the .dyn.

Sunday, October 7, 2012

Dynamo - Grid of Points

Third in the series of basic Dynamo examples. This time following Nathan Miller’s RevitPythonShell ‘Grid of Points’ example, from the Proving Ground Wiki, Create Points page.

PG 1.3 Reference Point Grid

This example is really easy because Dynamo has a built-in XYZ Grid node. You just feed the relevant parameters into the node, and route the resulting list of XYZ objects to a Reference Point node to generate the grid of points.

Link to Dynamo Definition PG 1.3 Reference Point Grid.zip

Incidentally, to make any of these simple examples work in Dynamo, just use the Open… command from the File menu to open the .dyn file. It will open into the Home workspace, and you can Run it from there.

Thursday, October 4, 2012

Dynamo – Row of Points

This is a Dynamo re-working of Nathan Miller’s RevitPythonShell ‘Row of Points’ example, from the Proving Ground Wiki, Create Points page
PG 1.2 Reference Points.dyn
At the bottom left, the Build Sequence node is the equivalent of Nathan’s for-next loop. It generates a sequence (or list) of integers from 0 to 9. The two Map nodes create the X and Y values, and the Combine calls the XYZ node for each pair of X and Y values.
You can see that the program structure is quite different to a conventional, linear language like Python, C or Basic. The list-processing is like Lisp, of course, but the way the data flows between nodes takes a bit of getting used to.
For example, the Map node has two inputs: f(x) which is a function, and seq which is a sequence or list. The Map node applies the function to each item in the sequence, and outputs the results as a new sequence. In this case, I’ve used the function X (multiply). I’ve fed the value 10 into the multiply node’s x input, and left the y input for the Map node to use.
In the same way, my Combine node takes a function (comb) and two sequences (list1 and list2)as inputs . It applies the function to the two sequences, and outputs the results as a new sequence.
Link to Dynamo Definition PG 1.1 Reference Point.dyn