C# Workshop 2018 - Day 2
C# Workshop 2018 - Day 2
LONG NGUYEN
Research Associate
Institute for Computational Design (ICD)
University of Stuttgart
26,27,28 / 04 / 2018
2
• Visual Studio
•
Day 2
Grasshopper API
• Develop Grasshopper plugins
• Objected-Oriented Programming
27/04/2018 • Meshes
• Major Exercise: Mesh-growth by adaptive
subdivsion
3
Visual Studio
• An IDE (Integrated Development Environment)
• A special kind of software program (and accompanying
toolset) that help us develop (other) software products
• Very powerful and popular
• Supports C++, C#, Visual Basic
• Officially supports Python since version 2015
• We will use VS to develop plugins for Grasshopper using C#
4
namespace Workshop
{
public class MyFirstGrasshopperComponent : GH_Component
{
public MyFirstGrasshopperComponent()
: base("MyComponent", "Nickname", "Description", "Category", "Subcategory")
{ }
namespace Workshop
{
public class MyFirstGrasshopperComponent : GH_Component
{
public MyFirstGrasshopperComponent()
: base(" Component",
"Nickname",
"Description",
"Category",
"Subcategory")
{ }
...
}
}
The constructor - 7
example
using ...
namespace Workshop
{
public class MyFirstGrasshopperComponent : GH_Component
{
public MyFirstGrasshopperComponent()
: base(" Construct Point",
"Nickname",
"Description",
"Category",
"Subcategory")
{ }
...
}
}
The constructor - 8
example
using ...
namespace Workshop
{
public class MyFirstGrasshopperComponent : GH_Component
{
public MyFirstGrasshopperComponent()
: base(" Construct Point",
" Pt",
"Description",
"Category",
"Subcategory")
{ }
...
}
}
The constructor - 9
example
using ...
namespace Workshop
{
public class MyFirstGrasshopperComponent : GH_Component
{
public MyFirstGrasshopperComponent()
: base(" Construct Point",
" Pt",
" Construct a point from (xzy) coordinates",
"Category",
"Subcategory")
{ }
...
}
}
The constructor - 10
example
using ...
namespace Workshop
{
public class MyFirstGrasshopperComponent : GH_Component
{
public MyFirstGrasshopperComponent()
: base(" Construct Point",
" Pt",
" Construct a point from (xzy) coordinates",
"Category",
"Subcategory")
{ }
...
}
}
The constructor - 11
example
using ...
namespace Workshop
{
public class MyFirstGrasshopperComponent : GH_Component
{
public MyFirstGrasshopperComponent()
: base(" Construct Point",
" Pt",
" Construct a point from (xzy) coordinates",
"Vector",
"Subcategory")
{ }
...
}
}
The constructor - 12
example
using ...
namespace Workshop
{
public class MyFirstGrasshopperComponent : GH_Component
{
public MyFirstGrasshopperComponent()
: base(" Construct Point",
" Pt",
" Construct a point from (xzy) coordinates",
"Vector",
"Subcategory")
{ }
...
}
}
The constructor - 13
example
using ...
namespace Workshop
{
public class MyFirstGrasshopperComponent : GH_Component
{
public MyFirstGrasshopperComponent()
: base(" Construct Point",
" Pt",
" Construct a point from (xzy) coordinates",
"Vector",
"Point")
{ }
...
}
}
14
a+
b
2
Live Example:
A component that computes
the average of two numbers
17
"Workshop",
"Utilities")
{ }
...
}
18
The “Average” component – Input and output
parameters
public class GhcAverage : GH_Component
{
...
...
}
19
The “Average” component – The
main part
public class GhcAverage : GH_Component
{
...
DA.SetData(0, average);
}
...
}
20
The “Average” component – Check for input
validity
protected override void SolveInstance(IGH_DataAccess DA)
{
double a = double.NaN;
double b = double.NaN;
Live Example:
A component that computes the centroid of a set of points,
and the distance from the centroid to each point
26
The “Centroid” component –– the constructor
...
}
27
The “Centroid” component – Input and output
parameters
public class GhcCentroid : GH_Component
{
...
...
}
28
The “Centroid” component – The main
part
protected override void SolveInstance(IGH_DataAccess DA)
{
List<Point3d> iPoints = new List<Point3d>();
DA.GetDataList("Points", iPoints);
DA.SetData("Centroid", centroid);
DA.SetDataList("Distances", distances);
}
29
Live Example
Moving Particle
31
The “Moving Particle” component – the
constructor
public class GhcMovingParticle : GH_Component
{
Point3d currentPosition = new Point3d(0.0, 0.0, 0.0);
public GhcMovingParticle ()
: base("Moving Particle",
"MvPrtc",
"Create a moving point in the direction specified by the
user",
"Workshop",
"Utilities")
{
}
...
}
The “Moving Particle” component – Input and output
32
parameters
public class GhcMovingParticle : GH_Component
{
...
...
}
The “Moving Particle” component – The 33
main part
public class GhcMovingParticle : GH_Component
{
Point3d currentPosition;
...
protected override void SolveInstance(IGH_DataAccess DA)
{
bool iReset = true;
DA.GetData(0, ref iReset);
if (iReset)
{
currentPosition = new Point3d(0.0, 0.0, 0.0);
return;
}
currentPosition += iVelocity;
DA.SetData(0, currentPosition);
}
}
34
35
OOP
Objected-Oriented Programming
36
myPyramid.BasePlane = Plane.WorldXY
myPyramid.Length = 1.2;
myPyramid.Width = 3.4;
myPyramid.Height = 5.6;
38
C# Script
Component
class ScriptInstance : GH_SriptInstance
{
private void RunScript()
{
Pyramid myPyramid = new Pyramid();
myPyramid.BasePlane = Plane.WorldXY;
myPyramid.Length = 1.2;
myPyramid.Width = 3.4;
myPyramid.Height = 5.6;
}
class Pyramid
{
public Plane BasePlane;
public double Length;
public double Width;
public double Height;
}
}
39
Class
definition
class Pyramid
{
public Plane BasePlane;
public double Length;
public double Width;
public double Height; Constructor
Class
definition
class Pyramid
{ CONVENTION:
... Names of public members start with uppercase
Client
code
Pyramid myPyramid = new Pyramid(Plane.WorldYZ, 1.2, 3.4, 5.6);
Pyramid yourPyramid = new Pyramid(3.0, 3.0, 2.0);
43
Class
definition
class Pyramid
{
public Plane BasePlane;
public double Length;
public double Width;
public double Height;
...
...
}
45
Class
definition
class Pyramid
{
public Plane BasePlane;
public double Length;
public double Width;
public double Height;
...
public Pyramid()
{
BasePlane = Plane.WorldXY;
Length = 1.0;
Width = 1.0;
Height = 1.0;
}
}
46
Client
code
Pyramid myPyramid = new Pyramid(myPlane, 1.2, 3.4, 5.6);
Pyramid yourPyramid = new Pyramid(3.0, 3.0, 2.0);
Pyramid herPyramid = new Pyramid();
Class
47
definition
class Pyramid
{
public Plane BasePlane;
public double Length;
public double Width; CONVENTION:
public double Height; Names of public members start with uppercase
…
public double ComputeVolume()
{
return 0.3333 * Length * Width * Height;
}
}
Client
code
Pyramid myPyramid = new Pyramid(myPlane 1.0, 1.0, 3.0)
double myPyramidVolume = myPyramid.ComputeVolume();
Class Client
definition Code
class Pyramid Pyramid myPyramid = new
{ Pyramid();
public double Length; myPyramid.Length = 2.0;
public double Width; myPyramid.topAngle = 15.0;
public double Height;
public Plane BasePlane; Private variables and functions
cannot be accessed from outside
private double topAngle; the class definition
}
They are to be used internally
within a class definition
50
Namespace
51
Namespace: Classes with identical names can coexist
RhinoComomon. RevitAPI.
dll dll
namespace namespace
Rhino.Geometry Autodesk.Revit
{ {
public class public class
Curve Curve
{ {
... ...
} }
} }
Rhino.Geometry Autodesk.Revit
52
Namespace
RhinoComomon. RevitAPI.
dll dll
namespace namespace
Rhino.Geometry Autodesk.Revit
{ {
public class public class
Curve Curve
{ {
... ...
} }
} }
Your GH
plugin
namespace Workshop
{
Rhino.Geometry.Curve myRhinoCurve = new
Rhino.Geometry.Curve();
Autodesk.Revit.Curve myRevitCurve = new
Autodesk.Revit.Curve();
}
53
Namespace
RhinoComomon. RevitAPI.
dll dll
namespace namespace
Rhino.Geometry Autodesk.Revit
{ {
public class public class
Curve Curve
{ {
... ...
} }
} }
Your GH
plugin
using Rhino.Geometry;
namespace Workshop
{
Curve myRhinoCurve = new Curve();
Autodesk.Revit.Curve myRevitCurve = new
Autodesk.Revit.Curve();
}
54
Namespace
RhinoComomon. RevitAPI.
dll dll
namespace namespace
Rhino.Geometry Autodesk.Revit
{ {
public class public class
Curve Curve
{ {
... ...
} }
} }
Your GH
plugin
using Rhino.Geometry;
using Autodesk.Revit;
namespace Workshop
{
Error:
Curve myRhinoCurve = new Curve(); Ambiguous !
Curve myRevitCurve = new Curve();
} !!
55
Namespace
RhinoComomon. RevitAPI.
dll dll
namespace namespace
Rhino.Geometry Autodesk.Revit
{ {
public class public class
Curve Curve
{ {
... ...
} }
} }
Your GH
plugin
using RhinoCurve = Rhino.Geometry.Curve
using RevitCurve = Autodesk.Revit.Curve
namespace Workshop
{
RhinoCurve myRhinoCurve = new RhinoCurve();
RevitCurve myRhinoCurve = new RevitCurve();
}
56
Namespace can be nested (just like folders)
namespace Rhino
{
namespace
namespace
Rhino.Geometry
Geometry
=
{
{
public class
public
Curve
class Curve
{
{
...
...
}
}
}
}
}
Geometry
Rhino.Geometry
Rhino
57
A namespace can contains many (sub)namespaces
RhinoComomon.
dll
namespace
Rhino.Geometry
namespace Rhino.Display
namespace Rhino.Input
58
namespace Workshop.Pyramid
{
public class GhcCreatePyramid : GH_Component
{
public GhcCreatePyramid()
: base("Create Pyramid", "Create Pyramid",
"Create Pyramid from position, length, width and height",
"Workshop", "Pyramid")
{
}
...
Plane.WorldXY);
pManager.AddNumberParameter("Height", "Height", "Height", GH_ParamAccess.item,
1.0);
pManager.AddNumberParameter("Width", "Width", "Width", GH_ParamAccess.item,
1.0);
pManager.AddNumberParameter("Length", "Length", "Length", GH_ParamAccess.item,
1.0);
}
...
DA.SetData("Pyramid", pyramid);
}
...
The “Display Pyramid” component 63
namespace Workshop.Pyramid
{
public class GhcDisplayPyramid : GH_Component
{
public GhcCreatePyramid()
: base(“Display Pyramid", " Display Pyramid ", "Display Pyramid ",
"Workshop", "Pyramid")
{
}
namespace Workshop.Pyramid
{
public class GhcDisplayPyramid : GH_Component
{
...
DA.SetDataList("Display", iPyramid.ComputeDisplayLines());
}
...
}
}
65
Live Example
Adding icons to our components
66
Adding an icon to the class definition
namespace Workshop.Pyramid
{
public class GhcCreatePyramid : GH_Component
{
...
...
}
}
67
Grasshopper icon design tips
• Resolution: 24 x 24
• DPI: 96 pixel/inch (very important, otherwise icon will appear blurry!)
• File formats: png, svg, … Avoid jpeg at all costs !
class Pyramid
{
public Point3d Position;
public double Length;
public double Width;
public double Height;
...
}
71
“private” can prevent funny problems
e.g. by disallowing user to access pyramid length variable
Error Message:
“length” is inaccessible due to its protection level
class Pyramid
{
private Point3d position;
private double length;
private double width;
private double height;
...
}
72
But …
now a “well-behaved” user can no longer query the length value of the pyramid
class Pyramid
{
private Point3d position;
private double length;
private double width;
private double height;
...
}
73
A possible solution: defining a “get” method
class Pyramid
{
private double length;
...
Now the user can query the length value (but cannot mess with it the length variable !!!)
...
myPyramid.SetLength(-1.0);
75
So, each private variable comes with a “set” and “get”
methods
class Pyramid
{
private double length;
...
Doing this…
myPyramid.SetLength(1988.10);
Print(myPyramid.GetLength());
YES !
Introducing …
78
Properties
(of a class)
79
Previously, …
class Pyramid
{
private double length;
...
class Pyramid
{
private double length;
Inheritance
87
LandAnim
al
class LandAnimal
{
public string Name;
public double Weight;
protected int legCount;
Inherit Inherit
Do Huma
g n
class Dog : LandAnimal class Human : LandAnimal
{ {
public string breed; public string Nationality;
public void bark() {...} public void ChangeNationality()
} {...}
}
All public and protected (but not private) members of LandAnimal will
automatically be added to Dog (and Human) class definition
88
Inheritance can be indirect and multi-level
LandAnim
al
Huma
n
class Human : LandAnimal
{
public string Nationality;
public void ChangeNationality()
{...}
}
Archite Engine
ct er
class Architect : Human class Engineer : Human
{ {
public string ArchitectLicenseID; public string EngineerLicenseID;
public void Design() {...} public void BuildRobot() {...}
} }
89
Some terminologies
GeometryBa
se
Struct
99
struct is similar to
class
For example:
Point3d (defined by RhinoCommon) is actually a struct,
not a class
public struct Point3d
{
public double X;
public double Y;
public double Z;
...
}
10
0
Point3d Pyramid
myPoint myPyramid
X, Y, Z
Enumeration Type
10
3
A data type that describes the day of the week
Defining an enumeration
type
enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri};
Previousl
y
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, “Check the inputs, you
idiot!!!”);
An enumeration type
Defined by Grasshopper
API
public enum GH_RuntimeMessageLevel { Blank, Remark, Warning,
Error };
10
5
Static classes
Static class members
10
Static variables of a 6
class
For example:
Point3d (defined by RhinoCommon) is actually a struct,
not a class
public struct Point3d
{
public double X;
public double Y;
public double Z;
...
}
10
Static functions of a 7
class
Called directly from the class, rather than from an instance of
the class
Inside the Math class defined by .NET
framework
public class Math
{
static public double Cos(double angle) { ... }
}
Client
Code
double result = Math.Cos(3.14);
Instead
of ...
Math myMathObject = new Math();
double result = myMathObject.Cos(3.14);
10
Static functions of a 8
class
Called directly from the class, rather than from an instance of
the class
NurbsCurve class definition (inside
RhinoCommon)
public class NurbsCurve
{
public Point3d PointAt(double t) { ... }
Client
Code
double circleArea = Math.PI * radius * radius
11
Static class 0
cannot be instantiated
Live example:
Defining a static Util class
11
A static class that contains some handy utility routines
2
public static class Util
{
static Random random = new Random();
double x = Math.Cos(angle);
double y = Math.Sin(angle);
Mesh Growth
by subdivision and avoiding self-collision
11
5
Mesh in RhinoCommon
11
6
4
1
0
3
5
11
7
Halfedge Mesh
An alternative way to represent a mesh
11
4
8
11
0
6 1
1 1 2
7 2 3
1
3 4
1 5
5
11
9
2 * a + 1 * b + 1
Average = * c
2 + 1 + 1
=0.5 * a + 0.25 * b +
0.25