Professional Documents
Culture Documents
Reference in a Nutshell
1st edition
author: Krzysztof Nazar
contact: krzysztof.a.nazar@gmail.com
parametric
support
C# Basic C# for GH3d
Reference in a Nutshell
1st edition
author: Krzysztof Nazar
contact: krzysztof.a.nazar@gmail.com
1. C# scripting component
C# scripting component can be found in Math tab in Script section. You can also place it on the canvas by
double-clicking and typing C#.
By default the component is created with x and y inputs and out and A outputs. out is a special output
printing text messages about errors, warning and script behaviour.
Names of the inputs and outputs (excluding out) become names of the variables inside a script. This
means you have to follow C# naming rules when you rename them. After zooming in pluses and minuses
appear which allow to add or remove inputs and outputs.
toggle script
editor shrinking
run script
variable names
Before you start scripting you have to specify input types by
right clicking on the component. In addition, you have three
ways of providing data (item, list, data tree).
Visit http://4.rhino3d.com/5/rhinocommon/ for full reference of
RhinoCommon types.
Note: outputs types are always set to generic object type.
RhinoCommon structures
RhinoCommon classes
2. Declaring variables
In C# you declare a variable by providing type followed by variables name. The rules are:
-it cant be any of the reserved keywords,
-it has to start with a letter or an underscore,
-it can contain letters, underscores and numbers,
-no whitespaces!
A declared variable is a container for a certain type of data. Assigning a value for the rst time is called
initialization.
data_type variable_name;
data_type variable_name = value;
data_type variable1, variable2, variable3;
data_type variable1 = value, variable2 = value;
3. Data types
C# has some built in data types. Numeric types are divided into:
1. Signed integrals (positive and negative values): sbyte, short, int, long
2. Unsigned integrals (only positive values): byte, ushort, uint, ulong
3. Real numbers: float, double, decimal
These types dier with an amount of memory occupied and the method of number representation.
There are also:
char type which represents a single Unicode character (eg. C)
string type which represents an immutable chain of char (eg C# course)
3b. Rhino and Grasshopper data types
In Rhino there are some custom data types designed to represent geometry. Some of the examples are
Point3d, Line, Poyline, Brep. Check out Rhino 5 SDK for full reference.
Every RhinoCommon type has its wrapper in Grasshopper. Wrapping RhinoCommon objects in GH objects
inside a script before outputting is a good practice which speeds up the program a lot.
Point3d GH_Point
Vector3d GH_Vector
Curve GH_Curve
Surface GH_Surface
double GH_Number
bool GH_Boolean
An operator transforms and combines expressions. Following is the list of operators commonly used:
5. Boolean type
C# bool type is a logical value that can be assigned either true or false. It is used to check conditions of
script or program execution.
bool myCondition;
declaration and initialization of a bool type variable myCondition
myCondition = true;
If you want to compare numbers you have to use one of the following operators. All of them return bool type
data. Notice the dierence between an assignment(single =) and comparison(double =) operators!
if (some_condition)
{
//doSomething; if() checks if the condition (bool type) is true or
} false and executes one of the statement blocks
else
{
//doSomethingElse;
}
switch(some_variable)
{ if (some_variable == value1)
case value1: doSomething(); doSomething();
break; else if (some_variable == value2)
case value2: doSomethingElse(); doSomethingElse();
Is equivalent to: else if (some_variable == value3)
break;
case value3: doSomethingElse2(); doSomethingElse2();
break; else
default: doSomethingElse3(); doSomethingElse3();
break;
}
9. for() loop
Sometimes you want to do things in a loop. The most popular is a for() loop. This is the syntax:
The most common approach is to use initial statement for declaring and initializing an indexing variable
(usually i, j, k, l, m, n) with 0, set the condition to indexing variable smaller than number of passes and
end every pass with incrementation of the indexing variable (eg. i++;)
It is possible to prematurely end a loop with break; instruction or to jump to the next pass with continue;
Print(i.ToString());
} This loop would print only even values from zero
to eight (at ten loop exits). If count is lower than 10,
then the loop will nish earlier.
for() loop is not starting if the condition is not met at the beginning.
This is simpler version of a loop. It executes until the conditions are met and is not starting if they are not.
You can use break; and continue; like in a for loop.
int number=1;
The only dierence between while() and do ... while() loops is the latter executes at least once no matter if
the condition is met or not. Notice the semicolon after while().
13a. Arrays
type[] arrayName = new type[numberOfElements];
An array represents a xed number of variables (elements) of a particular type. They are always stored in a
contiguous block of memory what provides very ecient access. You need to declare how many elements
will an array have. You access a specic element in an array by providing its index in square brackets after
the arrays name. Indexing starts from 0!
13b. Lists
List<type> listName = new List<type>();
Lists are similar to arrays but can have varying number of elements. You can add and remove data from a
list. And once again: indexing starts from 0!
14a. Methods
A method performs some action in a series of instructions. It can receive some data from the caller by
parameters and output data back by specifying a return type. A method can specify void return type,
which indicates that its not returning any value to the caller. It is possible to output more than one variable
(list, array, data tree) via ref/out parameters.
public void PrintHello()
{
A non-returning method
Print(Hello);
}
//...
//inside a Runscript() method:
int number1 = 4;
int number2 = 0; Results:
Hello
PrintHello(); 4
PrintNumber(number1); 8
number2 = TwoTimes(number1);
PrintNumber(number2);
//...
PrintNumber(number1);
PrintNumber(number2);
PrintNumber(number2, label);
//...
14c. Out and ref modiers
It is possible to return more than one value to a caller. By default arguments are passed by value, what
means that a copy of object is created inside a method and destroyed after returning. You can however pass
by reference which means that an alias of passed object is created. This makes all changes persistent.
//...
//inside a Runscript() method:
int myNumber = 2;
Iterate() iterated only a local copy of myNumber.
Print(myNumber.ToString());
IterateRef(ref myNumber);
Print(myNumber.ToString());
//...
An out argument is like ref, except it need not be assigned before being passed into a method and has to
be assigned before it comes out of the method.
Notice you have to write ref or out both when you dene and call a method.
In case of value types, assignment operator creates an actual independent copy of an object. point1
wasnt aected by changes made on point2.
MyPoint2d_Struct instance:
point1: point2:
}
X 10 15
values
Y 20 20
memory
independent instances
//...
//inside a Runscript() method: Declaration and
MyPoint2d_Class point1 = new MyPoint2d_Class(); initialization of
point1.X = 10; a custom-type object
point1.Y = 20;
Accessing a member
MyPoint2d_Class point2 = point1; with . operator
point2.X = 15;
Copying a reference
Print(point1.X.ToString()); // 15
to an object!
Print(point1.Y.ToString()); // 20
Print(point2.X.ToString()); // 15
Print(point2.Y.ToString()); // 20
//...
In case of reference types, assignment operator creates a copy of reference to data. Reference is in fact a
variable storing a memory adress. Data referenced by point1 was aected by changes made on point2,
because they are pointing to the same place in memory!.
MyPoint2d_Class instance:
point1
}
15
}
Reference X
data data
Y 20
point2
//...
//inside a Runscript() method:
BoxSide mySide = BoxSide.Top;
Note: you can cast enum to its underlying type.
if(mySide == BoxSide.Top)
Print(Were on top!);
Print(((int)mySide).ToString());
//...
Notice that now both x and y are private (can be accessed only from the inside of class). To make it
possible to set and read coordinates we dened three public methods. This approach is reasonable if you
want to prevent the user from inputting strange values (then a check inside the setting method is needed).
//...
//inside a Runscript() method:
Point myPoint = new Point(); Results:
myPoint.SetCoordinates(10, 15.3); 10
Print(myPoint.GetX().ToString()); 15.3
Print(myPoint.GetY().ToString());
//...
//...
//inside a Runscript() method:
Point myPoint = new Point();
myPoint.SetX(10); Same member is accessed with dierent methods.
if(myPoint.GetX() > 20)
Print(Were so far!);
//...
There is a way to combine restricted only-method access with convenience of public elds. It is about using
properties.
Print(myPoint.X.ToString()); //10
Print(myPoint.Y.ToString()); //0
//...
If you dont want to specify any special behaviour you can just restrict setting or getting a value by the user.
This type of a member is called an automatic property and the most common use is to make setting
private (controlled by the class) and getting being public.
When an object is created in memory, a special method called a constructor is executed. It usually initializes
all elds and properties of an object and thus allows to set initial state.
Constructors are non-returning, public (in general) and have the same name as the class. If you dont dene
any constructor, then a default parameterless, empty constructor is implicitly created by the compiler.
Print(myPoint.X.ToString()); //15
Print(myPoint.Y.ToString()); //16.2
//...
What if a constructor accepts arguments that are named exactly as the objects elds? No problem, just use
this keyword which denotes the object itself.
Inheritance is one of the most important aspects of every object-oriented programming (OOP) language. It
is used for code reuse and extensibility.
//...
//inside a Runscript() method:
ColouredPoint myColouredPoint = new myColouredPoint();
myColouredPoint.X = 3.14;
myColouredPoint.Y = 10.5;
myColouredPoint.ColourRGB = Color.FromArgb(0,0,0);
Print(myPoint.X.ToString()); //3.14
Print(myPoint.Y.ToString()); //10.5
Print(myPoint.ColourRGB .ToString()); //Color [A=255, R=0, G=0, B=0]
//...
ColouredPoint inherits all members of its base class Point. When a ColouredPoint object is created, a
default base class constructor is called rst. It is possible to reuse some base code like this:
//...
//inside a Runscript() method:
Point myPoint = new Point(15, 16.2);
//...
//inside a Runscript() method: Accessing a static member by
Point myPoint1 = new Point(15, 16.2); typing class name, a dot
Point myPoint2 = new Point(-3, 12); operator and a members name
Point myPoint3 = new Point(5, -7.8);
Print(Point.PointCount.ToString()); //3
//...
16. DataTree<> class
DataTree<> class is designed for handling branched data you know from Grasshopper. Consider the
following example:
(C)
(D)
(A)
(B) (E)
1. A list of two items is created 2. A list of two items is branched 3. A data tree is branched
In fact, the resulting data structure is a list of lists which are marked with branching info. We have the
following:
List<int> {2, 1}; with branching info {0;0;0;0} (made from the rst item(C) made from the rst item(A))
List<int> {2}; with branching info {0;0;0;1} (made from the second item(D) made from the rst item(A))
List<int> {2, 1}; with branching info {0;0;1;0} (made from the rst item(E) made from the second item(B))
Its that simple! When components try to combine data trees, they match lists by GH_Paths. Heres how you
iterate through a DataTree:
Property returning a
for(int i=0; i<myTree.BranchCount; i++)
{ number of branches
for(int j=0; j<myTree.Branch(i).Count; j++)
{ Method returning a
Print(myTree.Branch(i)[j].ToString()); branch as a list
}
} Accessing elements
of returned list