Grasshopper Scripting 106
Loops (II)
In the previous lesson we introduced some loops. Working with basic variables is interesting but I wanted to introduce a new variable type that will give more meaning to all this scripting effort. Point3d is a Rhino specific variable type. As a part of the RhinoCommon library, Point3d will allow us to create and represent points and use them as the base for much more interesting geometry.
Download here the GH file (0.9.014) containing the examples.
The Point3d Class
Point3d is part of the RhinoCommon library. Being a Reference-Type class, we should declare it as we did with lists.
Point3d myPoint = new Point3d();
Let’s create a point from scratch with components:
private void RunScript(ref object A)
{
Point3d myPoint = new Point3d();
myPoint.X = 1.0; //set the X property
myPoint.Y = 3.0; //set the Y property
myPoint.Z = 4.0; //set the Z property
A = myPoint;
}
We can do the whole thing if we pass the coordinates to the constructing sentence:
private void RunScript(ref object A)
{
Point3d myPoint = new Point3d(1.0, 3.0, 4.0);
A = myPoint;
}
A Spiral
Mixing point lists and loops we can start building familiar curves.
private void RunScript(ref object A)
{
//declare a point list
List<Point3d> myPtList = new List<Point3d>();
//declare variables for each coordiate
double Xcoord, Ycoord, Zcoord;
//set the radius, this could be an input variable
double radius = 10.0;
//build a buffer point
Point3d myPt;
//start the loop, the number of points could also be an input variable
for (int i = 0; i < 100; i++){
//calculate each coordinate
Xcoord = radius * Math.Cos(Math.PI * 2 * i / 100);
Ycoord = radius * Math.Sin(Math.PI * 2 * i / 100);
Zcoord = i;
//build a point with the coordinates
myPt = new Point3d(Xcoord, Ycoord, Zcoord);
//add the point to the list
myPtList.Add(myPt);
}
//output the list
A = myPtList;
}
Distance Meter
The following code takes a point cloud and returns the distance between each of them and a sample point together with the furthest and closest one
private void RunScript(List<Point3d> x, Point3d y, ref object A, ref object B, ref object C)
{
List<double> distList = new List<double>(); //declare the list for holding distances
double dist = 0.0; //declare a distance buffer
double minDist = y.DistanceTo(x[0]); //declare a variable for holding the minimum distance, initialize it with a posssible value
double maxDist = 0.0; //declare a variable for holding the maximum distance, initialize it at 0.0
B = x[0]; //set the B output to something
C = x[0]; //set the C output to something
foreach (Point3d samplePt in x){ //start the loop
dist = y.DistanceTo(samplePt); //calculate the distance between y and the sample point
distList.Add(dist); //add the distance to the list
if (dist < minDist){ //if the distance is smaller than the current minimum, we have a new minimum
B = samplePt; //output the corresponding point
minDist = dist; //set the distance as the new minimum
}
if (dist > maxDist){ //if the distance is larger than the current maximun, we have a new maximum
C = samplePt; //output the corresponing point
maxDist = dist; //set the distance as the new maximum
}
}
A = distList; //finally, output the list of distances
}
A key aspect here is to initialize the minDist and maxDist variables with the right values. If you pick 0.0 as the initial value for minDist there is no lower value possible, and if you pick a random number, you might be assuming there will be a distance smaller than that number. That is why we pick a real value as the first state.
Nested Loops
Nested loops are loops happening inside other loops. This gives access to complex operations where we need to cross-evaluate certain variables or work with multiple dimensions. It is also worth nothing that nested loops are dangerous if used without care, a loop within a loop within a loop, each one having 100 steps will result in a million calls being processed by the computer, and depending on the case, this could hang your computer without mercy.
Let’s see an example of nested loops to create a grid of points. Keep in mind that each loop needs its iterator.
private void RunScript(int x, int y, double s, ref object A)
{
List<Point3d> ptGrid = new List<Point3d>(); //create a list for the points
Point3d samplePt; //create a placeholder point
for (int i = 0; i < x; i++){ //start outer loop for first coordinate
for(int j = 0; j < y; j++){ //start inner loop for second coordinate
samplePt = new Point3d(i * s, j * s, 0); //create samplePt with x and y coordinates
ptGrid.Add(samplePt); //add the point to the list
}
}
A = ptGrid; //output the list
}
Challenge
1. Write a component that creates points arranged in the shape of a sphere with Radius R and centered at the origin.
2. Write a component that creates points arranged in the shape of a thorus with radius R1 and R2 and centered at the origin.
[EDIT] Challenge Solutions:
Challenge 1. Points Sphere:
private void RunScript(double R, int u, int v, ref object A)
{
List<Point3d> ptList = new List<Point3d>(); //declare a list to hold the points
for (int i = 0; i < u; i++){ //outer loop for u subdivisions of the alfa angle, between 0 and 2*PI
double alfa = Math.PI * 2 * i / u; //calculate alfa as a fraction of 2*PI
for (int j = 0; j <= v; j++){ //inner loop for v subdivisions of the beta angle (0 to PI)
double beta = Math.PI * j / v; //calculate beta as a fraction of PI
double ptX = R * Math.Cos(alfa) * Math.Sin(beta); //parametric equation of the sphere
double ptY = R * Math.Sin(alfa) * Math.Sin(beta);
double ptZ = R * Math.Cos(beta);
Point3d pt = new Point3d(ptX, ptY, ptZ); //create the point
ptList.Add(pt); //add it to the list
}
}
A = ptList; //output the list of points
}
Challenge 2. Points Torus:
private void RunScript(double R, double r, int u, int v, ref object A)
{
List<Point3d> ptList = new List<Point3d>(); //create a list to hold the points
for (int i = 0; i < u; i++){ //outer loop
double alpha = Math.PI * 2 * i / u; //calculate alpha as a fraction of 2*PI
for (int j = 0; j < v; j++){ //inner loop
double beta = Math.PI * 2 * j / v; //calculate beta as a fraction of PI
double ptX = (R + r * Math.Cos(beta)) * Math.Cos(alpha); //torus equation
double ptY = (R + r * Math.Cos(beta)) * Math.Sin(alpha);
double ptZ = r * Math.Sin(beta);
Point3d pt = new Point3d(ptX, ptY, ptZ); //create the point
ptList.Add(pt); //add it to the list
}
}
A = ptList; //output the list
}
A little note:
Please note that in this two examples, the inner and outer loops can be swapped together and that script would “build” the points then following Parallels > Meridians. See the example with the sphere:
private void RunScript(double R, int u, int v, ref object A)
{
List<Point3d> ptList = new List<Point3d>(); //declare a list to hold the points
for (int i = 0; i <= u; i++){ //outer loop for u subdivisions of the alfa angle, between 0 and PI
double alpha = Math.PI * i / u; //calculate alpha as a fraction of PI
for (int j = 0; j < v; j++){ //inner loop for v subdivisions of the beta angle (0 to 2*PI)
double beta = Math.PI * 2 * j / v; //calculate beta as a fraction of 2*PI
double ptX = R * Math.Cos(beta) * Math.Sin(alpha); //parametric equation of the sphere
double ptY = R * Math.Sin(beta) * Math.Sin(alpha);
double ptZ = R * Math.Cos(alpha);
Point3d pt = new Point3d(ptX, ptY, ptZ); //create the point
ptList.Add(pt); //add it to the list
}
}
A = ptList; //output the list of points
}
Download the solutions here (GH 0.9.0014)
Final Challenge
Write a component that calculates the first n prime numbers, with n being a reasonable number, not too big.