
Example 6.4 looked at adding random values to rotate the vectors within a range for each column and row, but with each column and row rotated independently of the rotation applied to its neighbours. This script takes the idea of adding random rotation–or noise–to a higher level, and will be important for a future script looking at agent based fields. While the results of the script in and of themselves may not be the most impressive, once we apply agents it will give some quite nice results…but that is for later, specifically Example 11.2 if you want to jump ahead!
The basic idea here will be that the rotation of each vector will be progressive through the field, with rotation being within a range plus or minus a variable parameter compared with the adjacent row and/or column.
To demonstrate, lets assume we have a very simple series of vectors in a linear sequence: vA – vB – vC – vD. In this demonstration, lets assume my “noise” value, or random rotation value, is +/-10.° Lets say vA has an angle of 0°. The next vector after this–vB–can rotate up to 10° from its neighbor (vA), so the angle of vB could be anywhere from -10° to +10°. The next vector, vC, can also be rotated up to 10° from its neighbor (vB), so if vB was rotated 8°, vC could be rotated anywhere from -2° to 18°. The theoretical range of vC is actually -20° to +20°, and the theoretical range of vD is -30° to 30° assuming vA is 0°. Got it? I thought so. Well, it should start to make sense (hopefully!) once we start trying it out in grasshopper!
This exercise will look a lot at data structures (again!) We will be creating our own structure, flipping it, and later matching one structure to another. Fun! So hopefully you have a fairly good grasp of this concept. If not, please at least read Exercise 1.1 again!
Step One – Setup Grid of Points

Here I am using a square polyline drawn in Rhino, referenced into Grasshopper to set up my field boundaries. I use a typical pattern of Divide Domain2 with “Isotrim” to find the centers of each cell. You will notice once I get the centers from this method using the Area component, the points are all in one list. I actually need these to be in columns and rows, similar to the data structure I would get from a grid. I can give these points a structure using the Partition List component. I feed the points in, and I define how many branches I want by using the “V” value from my Divide Domain2 process. Now my data is nicely structured to allow me to work with the rows and columns.
Notice I also want to extract four values from Step One to use in upcoming steps. These are:
A. The maximum cell dimension, which I get using the Dimensions component, followed by the Maximum component, followed by List Item listing item 0.
B. My vector start points, which I just structured and derived using the Area components to find the center of the cells.
C. The number of rows and
D. The number of columns.
I put these four ‘variables’ into purple containers, which are labeled in the image.
Step Two – Set an Initial Vector

I want to be able to change the overall average starting vector of my fields. This is not completely necessary but gives an added level of control over the output. Regardless, i need to set up my starting vectors, which I do by Moving my points in the Unit X Axis an amount equal to a percentage of the Maximum Cell Size (Variable A) determined in the previous step. This is so If I change the U and the V at the beginning, my geometry will scale along with it. I then use Rotate Vector component to get my starting condition.
Step Three – First Vector Rotation – Rows

This step and the next one are the core logic of this script, so pay attention! In the introduction, I explained we wanted each vector to rotate +/- our noise value in regards to its neighbor. Since we are working in 2 dimensions, we will need to do this twice. One for the rows (the first dimension), and again for the columns (the second dimension). This is also why it was essential to give our data a structure in the first step, so we could work with these rows and columns.
In this example, the amount of possible rotation in each step is +/- 18° from the neighboring row. This informs the R value for generating my list of Random numbers, with N value defined based on the number of Rows (Variable C). You can see these values spit out by the Random Number Generator (RNG).
To make these rotations progressive, however, we are going to use a Mass Addition component after the RNG output. Mass addition is typically used to sum a big series of numbers, and its first output will give you exactly this value. But the second output is equally handy. This gives you the “partial” result of the summation after each step.
To see what’s happening, I have attached a panel to both the RNG, as well as to the Mass Addition component, an enlargement of which is shown below.

The first number at Index 0 is -1.45. This is the rotation which will be applied to Every Item 0 – the first row of items. The next random number from the RNG is 16.39 (Index 1). But we don’t want to rotate the second row of items this amount. Instead, we use the partial result from mass addition (Index 1) which gives us (16.39 + -1.45 = 14.93). This process continues until by index 19, we have rotated a total of -37.24 from the original vector.
To see this graphically, we can compare the numeric values with vector preview for this step with Index 0 from Mass Addition applied to the bottom row, and values increasing to show up to Index 9 in the top row.

The image shows the amount each vector is rotated in relation to a dashed grey line, indicating its original position. You will notice in this particular series, the final vector at Index 19 is rotated -37.24 from the initial vector, although the image enlargement only goes to row 9 at -17.75. Theoretically, the vector at index 9 with a +/- 18° rotation at each step could have a total range of +/-180° assuming 10 rows. At index 19, i.e. 20 rows, we could have a theoretical potential range of +/- 360°, although in both cases, laws of probability make this extremely improbable.
Step Four – Second Vector Rotation – Columns

The logic to this step is nearly identical to the first step. We now want to do the same thing, but this time to the second dimension, the columns. Before I can do this though, I need to use a Flip Matrix component on the data structure of my vector list output from the previous step, which reorganizes the logic of the lists. Then, I do the exact same procedure as in Step 3 with a second RNG. I will need to make sure the N value on the RNG uses the Number of Columns variable (Variable D) instead of number of rows. In this case, they are the same, but later you may want these to be different. Also make sure the seed for the RNG is different otherwise the same rotation values as previously defined will be applied again.
Notice this time field is a bit more fluid, since we had already rotated everything once, but if you look carefully, you will notice that the amount of vector rotation in each column is equal. In the image below, the longer dashed line shows the results after the row rotation in Step 3, with a shorter faint line showing the initial vector direction.

The cumulative affect of the two rotations, however, means each vector will have a unique rotation, which is no more than its immediate neighbor either up and down, or left and right, in the matrix. Actually, it could be slightly more, if the vector was rotated -18° in both steps it would now have an angle of -36°. So the actual theoretical range for a value of 18 is actually twice that, but lets not get stuck on details 😉
One last thing, notice you need to use Flip Matrix again after this step to get the data structure back into its original form otherwise you will run into problems later. For example, if you tried to use the “Display Vectors” component using the flipped vector matrix, along with the original Center Point matrix, your results will not be correct (unless you flip the center point matrix as well).
Step Five – Create Rectangles

I am now going to use my vectors to create rectangles using the Line SDL component. This should be a pretty straightforward step by now.
Step Six- Assign color to rectangles

We’ve used the gradient tool quite a bit. Usually to assign a color based on an area, etc. Here I want the color to be based on the amount of rotation from the initial value. I can measure this using the Angle component to measure the angle between the list of initial vectors, and the list of Final vectors. Alternatively, I could also add the results of the two phases together. Whichever way you choose, the angle, converted to degrees, will serve as the “t” value for the gradient tool, with the L0 and L1 being set from 0 to 180 since all of my angle measurements will be positive and between 0 and 180°. I could alternately set L1 as the maximum value from a list of the angle measurements, but I am keeping it like this so you can see the effects of increasing rotation better across multiple examples later.
Two other things to note. Before I can apply the color to my rectangles, I need to make sure the data structure of my angle measurements matches the data structure of my list of rectangles. Currently they don’t since I created the rectangles from a “grafted” list after their creation. To get the structure matching, I can start by flattening my list of rectangles, and then I use the Unflatten Tree component. This asks you to provide a Guide according to which you will structure the data that is being unflattened. I use the structure of my vectors (either one will do) to serve as the guide.
Variations

Once everything is working, you can increase the field density in Step one. The variations shown above are all with a 20×20 starting grid. Notice with more columns and rows the total amount of theoretical rotation also increases. That’s about it! We will come back to this when we start looking at agents and the results will be a little more interesting!

Comments
2 responses
Is there a way to do this in 3d? Say, on a curved surface where each piece is placed on the normal of the surface?
There should be a way but rotating vectors in 3 dimensions gets pretty complicated.