0% found this document useful (0 votes)
44 views122 pages

C# Workshop 2018 - Day 2

Uploaded by

artrustee
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
44 views122 pages

C# Workshop 2018 - Day 2

Uploaded by

artrustee
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 122

Workshop 2018

C# Scripting and Plugin Development


for Grasshopper
A Gentle Introduction to Hardcore Programming for Computational Design

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

Live Example: Our first Grasshopper plugin


C# class definition “template” for a custom Grasshopper 5
component
using ...

namespace Workshop
{
public class MyFirstGrasshopperComponent : GH_Component
{
public MyFirstGrasshopperComponent()
: base("MyComponent", "Nickname", "Description", "Category", "Subcategory")
{ }

protected override void RegisterInputParams(GH_Component.GH_InputParamManager


pManager)
{ }

protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager


pManager)
{ }

protected override void SolveInstance(IGH_DataAccess DA)


{ }

protected override System.Drawing.Bitmap Icon { }

public override Guid ComponentGuid { }


}
The constructor - 6
example
using ...

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

Build and install


Automatically copy the built .gha file to the Grasshopper 15
custom components folder

Copy "$(TargetPath)" "C:\Users\YourName\AppData\Roaming\Grasshopper\Libraries\


Workshop.gha"
16

a+
b
2
Live Example:
A component that computes
the average of two numbers
17

The “Average” component – The


constructor
public class GhcAverage : GH_Component
{
public GhcAverage()
: base("Average of 2 numbers",
"Avrg",
"Compute the average of two numbers",

"Workshop",
"Utilities")
{ }

...
}
18
The “Average” component – Input and output
parameters
public class GhcAverage : GH_Component
{
...

protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)


{
pManager.AddNumberParameter("First Number", "First", "The first number",
GH_ParamAccess.item, 0.0);
pManager.AddNumberParameter("Second Number", "Second", "The second number",
GH_ParamAccess.item, 0.0);
}

protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager


pManager)
{
pManager.AddNumberParameter("Average Value", "Average", "The average value",
GH_ParamAccess.item);
}

...
}
19
The “Average” component – The
main part
public class GhcAverage : GH_Component
{
...

protected override void SolveInstance(IGH_DataAccess DA)


{
double a = double.NaN;
double b = double.NaN;

DA.GetData(0, ref a);


DA.GetData(1, ref b);

double average = 0.5 * (a + b);

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;

DA.GetData(0, ref a);


DA.GetData(1, ref b);

double average = 0.5 * (a + b);


DA.SetData(0, average);
}
21
The “Average” component – Check for input
validity
protected override void SolveInstance(IGH_DataAccess DA)
{
double a = double.NaN;
double b = double.NaN;

bool success1 = DA.GetData(0, ref a);


bool success2 = DA.GetData(1, ref b);

double average = 0.5 * (a + b);


DA.SetData(0, average);
}
22
The “Average” component – Check for input
validity
protected override void SolveInstance(IGH_DataAccess DA)
{
double a = double.NaN;
double b = double.NaN;

bool success1 = DA.GetData(0, ref a);


bool success2 = DA.GetData(1, ref b);

if (success1 && success2)


{
double average = 0.5 * (a + b);
DA.SetData(0, average);
}
}
23
The “Average” component – Check for input
validity
protected override void SolveInstance(IGH_DataAccess DA)
{
double a = double.NaN;
double b = double.NaN;

bool success1 = DA.GetData(0, ref a);


bool success2 = DA.GetData(1, ref b);

if (success1 && success2)


{
double average = 0.5 * (a + b);
DA.SetData(0, average);
}
else
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Check the inputs, you
idiot!!!");
}
}
24

Inputs and outputs as lists


25

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

public class GhcCentroid : GH_Component


{
public GhcCentroid ()
: base("Centroid",
"Centroid",
"Find the centroid of a set of points, and compute the distance
from the
centroid to each point",
"Workshop",
"Utilities")
{ }

...
}
27
The “Centroid” component – Input and output
parameters
public class GhcCentroid : GH_Component
{
...

protected override void RegisterInputParams(GH_InputParamManager pManager)


{
pManager.AddPointParameter("Points", "Points", "Points", GH_ParamAccess.list);
}

protected override void RegisterOutputParams(GH_OutputParamManager pManager)


{
pManager.AddPointParameter("Centroid", "Centroid", "Centroid",
GH_ParamAccess.item);
pManager.AddNumberParameter("Distances", "Distances", "Distances",
GH_ParamAccess.list);
}

...

}
28
The “Centroid” component – The main
part
protected override void SolveInstance(IGH_DataAccess DA)
{
List<Point3d> iPoints = new List<Point3d>();
DA.GetDataList("Points", iPoints);

Point3d centroid = new Point3d(0.0, 0.0, 0.0);

foreach (Point3d point in iPoints)


centroid += point;

centroid = centroid / iPoints.Count;

DA.SetData("Centroid", centroid);

List<double> distances = new List<double>();

foreach (Point3d point in iPoints)


distances.Add(centroid.DistanceTo(point));

DA.SetDataList("Distances", distances);
}
29

Build and test


30

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
{
...

protected override void RegisterInputParams(GH_Component.GH_InputParamManager


pManager)
{
pManager.AddBooleanParameter("Reset", "Reset", "Reset",
GH_ParamAccess.item);
pManager.AddVectorParameter("Velocity", "Velocity", "Velocity",
GH_ParamAccess.item);
}

protected override void


RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
{
pManager.AddPointParameter("Particle", "Particle", "Particle",
GH_ParamAccess.item);
}

...
}
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);

Vector3d iVelocity = new Vector3d();


DA.GetData(1, ref iVelocity);

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

Class = user-defined data type


37
Defining a
class CONVENTION:
class Pyramid Names of public members start with uppercase
{
public Plane BasePlane;
public double Length;
public double Width;
public double Height;
}

Using the class (client


code)
Pyramid myPyramid = new Pyramid();

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

public Pyramid(Plane basePlane, double length, double width, double


height)
{
BasePlane = basePlane;
Length = length;
Width = width; Similar to the _init function in
Height = height; Python
}
}
40
Client
code
Pyramid myPyramid = new Pyramid(Plane.WorldYZ, 1.2, 3.4, 5.6);
Plane yourBasePlane = new Plane(new Point3d(1.0, 1.0, 2.0), Vector3d.ZAxis);
Pyramid yourPyramid = new Pyramid(yourBasePlane, 3.0, 3.0, 2.0);

Class
definition
class Pyramid
{ CONVENTION:
... Names of public members start with uppercase

public Pyramid(Plane basePlane, double length, double width, double


height)
{
BasePlane = basePlane;
Length = length;
Width = width;
Height = height;
}
}
41
Class
definition
class Pyramid
{
public Plane BasePlane;
public double Length;
public double Width;
public double Height;

... Another constructor

public Pyramid(double length, double width, double height)


{
BasePlane = Plane.WorldXY;
Length = length;
Width = width;
Height = height;
}
}
42

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;

...

public Pyramid() Default constructor


{
BasePlane = Plane.WorldXY;
Length = 1.0;
Width = 1.0;
Height = 1.0;
}
}
44
Class
definition
class Pyramid
{
public Plane BasePlane = Plane.WorldXY;
public double Length = 1.0;
public double Width = 1.0;
public double Height = 1.0;

...
}
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();

Pyramid yourPyramid = new Pyramid(2.0, 2.0, 3.0);


double yourPyramidVolume = yourPyramid.ComputeVolume();
Compute the display lines of the pyramid 48
Class Pyramid
{
...
public List<LineCurve> ComputeDisplayLines()
{
Point3d A = BasePlane.Origin + BasePlane.XAxis * Length * 0.5 + BasePlane.YAxis * Width * 0.5;
Point3d B = BasePlane.Origin - BasePlane.XAxis * Length * 0.5 + BasePlane.YAxis * Width * 0.5;
Point3d C = BasePlane.Origin - BasePlane.XAxis * Length * 0.5 - BasePlane.YAxis * Width * 0.5;
Point3d D = BasePlane.Origin + BasePlane.XAxis * Length * 0.5 - BasePlane.YAxis * Width * 0.5;
Point3d M = BasePlane.Origin + BasePlane.ZAxis * Height;
List<LineCurve> displayLines = new List<LineCurve>(); M
displayLines.Add(new LineCurve(A, B));
displayLines.Add(new LineCurve(B, C));
displayLines.Add(new LineCurve(C, D));
displayLines.Add(new LineCurve(D, A));
displayLines.Add(new LineCurve(A, M)); Z
displayLines.Add(new LineCurve(B, M));
displayLines.Add(new LineCurve(C, M));
displayLines.Add(new LineCurve(D, M));
C B
return displayLines;
} Y
...
D X basePlane
} A
49
“Public” and “Private”

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

Live Example: namespaces and classes


Implementing the Pyramid class in Visual Studio
59
Now, we need to define some custom GH
components,
so that the Pyramid class can be used in
Grasshopper
The “Create Pyramid” component 60

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")
{
}

protected override void RegisterInputParams(GH_Component.GH_InputParamManager


pManager) { ... }

protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager


pManager) { ... }

protected override void SolveInstance(IGH_DataAccess DA) { ... }

protected override System.Drawing.Bitmap Icon { ... }

public override Guid ComponentGuid { ... }


}
The “Create Pyramid” component 61

...

rotected override void RegisterInputParams(GH_Component.GH_InputParamManager


pManager)
{
pManager.AddPlaneParameter("Base Plane", "Base Plane", "Base Plane",
GH_ParamAccess.item,

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);
}

protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager


pManager)
{
pManager.AddGenericParameter("Pyramid", "Pyramid", "Pyramid",
The “Create Pyramid” component 62

...

protected override void SolveInstance(IGH_DataAccess DA)


{
Plane iBasePlane = Plane.Unset;
double iHeight = double.NaN;
double iLength = double.NaN;
double iWidth = double.NaN;

DA.GetData("Base Plane", ref iBasePlane);


DA.GetData("Height", ref iHeight);
DA.GetData("Width", ref iWidth);
DA.GetData("Length", ref iLength);

Pyramid pyramid = new Pyramid(iBasePlane, iLength, iWidth, iLength);

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")
{
}

rotected override void RegisterInputParams(GH_Component.GH_InputParamManager


pManager)
{
pManager.AddGenericParameter("Pyramid", "Pyramid", " Pyramid",
GH_ParamAccess.item);
}

protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager


pManager)
{
pManager.AddCurveParameter("Display", "Display", "Display",
GH_ParamAccess.list);
}
The “Display Pyramid” component 64

namespace Workshop.Pyramid
{
public class GhcDisplayPyramid : GH_Component
{
...

protected override void SolveInstance(IGH_DataAccess DA)


{
Pyramid iPyramid = null;
DA.GetData("Pyramid", ref iPyramid);

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
{
...

protected override void SolveInstance(IGH_DataAccess DA)


{ ... }

protected override System.Drawing.Bitmap Icon


{
get
{
return null;
return Workshop.Properties.Resources.Icon_PyramidCreate;
}
}

...
}
}
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 !

JPEG PNG, SVG

• Does not support transparency • Supports transparency


• Always contains artefacts • Accurate pixel colors, no
artefacts
68

Debugging with Visual Studio


69

Public and Private


70
“public” can cause funny problems
e.g. The user can set pyramid length to silly values

Pyramid myPyramid = new Pyramid();


myPyramid.Length = -1.2;
double myVolume = myPyramid.ComputeVolume(); // This will give negative volume
!!!

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

Pyramid myPyramid = new Pyramid();


myPyramid.length = -1.2;

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 

Pyramid myPyramid = new Pyramid();


Print(myPyramid.length.ToString());

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;

...

public double GetLength()


{
return length;
}
}

Now the user can query the length value (but cannot mess with it the length variable !!!)

Pyramid myPyramid = new Pyramid();


double myLength = myPyramid.GetLength();
74
Also: a “set” method
class Pyramid
{
private double length;

...

public double SetLength(double newLength)


{
if (newLength > 0.0)
length = newLength;
else
length = 0.1;
}
}

myPyramid.SetLength(-1.0);
75
So, each private variable comes with a “set” and “get”
methods
class Pyramid
{
private double length;

...

public double GetLength()


{
return length;
}

public void SetLength(double inputLength)


{
if (inputLength > 0.0)
length = inputLength;
else
length = 0.0;
}
}
76
The get and set functions works well
But a bit inconvenient from the user’s perspective

Doing this…
myPyramid.SetLength(1988.10);
Print(myPyramid.GetLength());

… is not as nice as this …


myPyramid.Length = 1988.10
Print(myPyramid.Length)
77

Can we have the best of both worlds?


i.e. A safe and also convenient way to
get and set field values of objects

YES !
Introducing …
78

Properties
(of a class)
79
Previously, …

class Pyramid
{
private double length;

...

public double GetLength()


{
return length;
}

public void SetLength(double inputLength)


{
if (inputLength > 0.0)
length = inputLength;
else
length = 0.0;
}
}
80
Let’s define a “Property” instead,
Class Client
definition code
class Pyramid Pyramid myPyramid = new Pyramid();
{
private double length; double myLength = myPyramid.Length;

public double Length myPyramid.Length = -2.0;


{
get { return length; }
From the user’s perspective (the client code),
set accessing the property Length feels like
{ accessing an ordinary field
if (value > 0.0)
length = And the designer of the class can still implement
value; “safeguarding” codes that handle silly input values
else
length =
0.0;
}
}
}
81
Use the “set” in the constructor

class Pyramid
{
private double length;

public double Length


{
...
}

public Pyramid(Point3d position, double length, double width, double


height)
{
...
Length = length
...
}
}
82
Public class vs. internal class

Public classes can be used from outside of the library


public class MyGrasshopperComponent : GH_Component
{
...
}

Internal classes can only be used within the library


class Pyramid
{
...
}
83

The “parametric” engine


of Grasshopper
84

Forward-Propagating Parametric Model


85

Behind-the-scene: How Grassshopper creates the custom component


we defined
GhcAverage ghcAverage1 = new GhcAverage(...)
ghcAverage1.RegisterInputParams(pManager)
ghcAverage1.RegisterOutputParams(pManager)
...
ghcAverage1.SolveInstance(DA)

GhcAverage ghcAverage2 = new GhcAverage()


ghcAverage2.RegisterInputParams(pManager)
ghcAverage2.RegisterOutputParams(pManager)
...
ghcAverage2.SolveInstance(DA)
86

Inheritance
87
LandAnim
al
class LandAnimal
{
public string Name;
public double Weight;
protected int legCount;

protected void eat() {...}


}

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

• Dog inherits LandAnimal


• Dog is derived from LandAnimal LandAnim
al
• LandAnimal is the parent class of Dog
base class
• Dog is a child class of LandAnimal Do
derived class g
90

Example: Class inheritance in RhinoCommon


91

Degree, Dimension, Domain


Curv ClosestPoint()
PointAt()
e

LineCurv ArcCurv NurbsCur PolyCurv


e
Line
e ve e
Points
Reparametrize()
92
Object

GeometryBa
se

Curv Brep Mesh Surfac


e e

LineCurv ArcCurv NurbsCur PolyCurv


e e ve e
93

Why is class inheritance useful?


94
Provide a template for class definition

Degree, Dimension, Domain


Curv ClosestPoint()
PointAt()
e

LineCurv ArcCurv NurbsCur PolyCurv


e e ve e
95
Provide a template for class definition
The class definitions for our GH plugin components are based on the GH_Component class “template”
This is how Grasshopper can automatically “recognizes” our components when the .gha file is installed

class MyGrasshopperComponent : GH_Component


{
protected override void RegisterInputParams(GH_Component.GH_OutputParamManager
pManager)
{ }

protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager


pManager)
{ }

protected override void SolveInstance(IGH_DataAccess DA)


{ }
}
96
Re-use codes from the base class

class MyFirstGrasshopperComponent : GH_Component


{
...
protected override void SolveInstance(IGH_DataAccess DA)
{
...
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, “Check the inputs, you
idiot!”);
...
}
...
}
This function was defined in the base class GH_Component and can be readily used by u
97
“Polymorphism”
Provides a common interface for related types
e.g. if a function asks for an input of type Curve,
we can also input any type derived from Curve

void MoveCurve(Curve curve)


ArcCurv {
e ...
}
LineCurv
Curv e
e NurbsCurv ArcCurve myArcCurve = new
e ArcCurve(...);
MoveCurve(myArcCurve);
PolyCurv
e LineCurve myLineCurve = new
LineCurve(...);
MoveCurve(myLineCurve);
98

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;

public Point3d() { ... }


public Point3d(double X, double Y, double Z) { ... }

public DistanceTo(Point3d other) { ... }

...
}
10
0

• Usually, struct are used to define simple


data types
• There is no inheritance
10
Important Difference 1

Struct is value type Class is reference type

Point3d Pyramid
myPoint myPyramid
X, Y, Z

actual pyramid data


(base plane, length,
width, height)
10
2

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};

Using the enumeration


types
Days myFavouriteDay = Days.Sunday;
Days yourFavouriteDay = Days.Friday;

BTW, enum types are value types


10
4
Example: an enum type from Grasshopper API

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;

public Point3d() { ... }


public Point3d(double X, double Y, double Z) { ... }

public DistanceTo(Point3d other) { ... }

...
}
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) { ... }

static public NurbsCurve Create(bool periodic, int degree,


IEnumerable<Point3d> points)
{ ... }
}

Client Code 1: The PointAt function must be called on an instance of


NurbsCurve class
Point3d pointOnCurve = myCurve.PointAt(0.5);

Client Code 2: calling the Create function directly from the


NurbsCurve class
NurbsCurve myCurve = NurbsCurve.Create(...);
10
Static variables of a 9
class
Inside the Math class defined by .NET
framework
public class Math
{
static public double PI = 3.1418...;
}

Client
Code
double circleArea = Math.PI * radius * radius
11
Static class 0
cannot be instantiated

e.g. The Math class is actually defined as


static
static public class Math
{
...
}
11
1

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();

public static Point3d GetRandomPoint(double minX, double maxX,


double minY,
double maxY, double minZ, double maxZ)
{
double x = minX + (maxX - minX) * random.NextDouble();
double y = minY + (maxY - minY) * random.NextDouble();
double z = minZ + (maxZ - minZ) * random.NextDouble();

return new Point3d(x, y, z);


}
}
11
A static class that contains some handy utility routines
3
static class Util
{
...

public static Vector3d GetRandomUnitVectorXY()


{
double angle = 2.0 * Math.PI * random.NextDouble();

double x = Math.Cos(angle);
double y = Math.Sin(angle);

return new Vector3d(x, y, 0.0);


}
}
11
4

Mesh Growth
by subdivision and avoiding self-collision
11
5

Mesh in RhinoCommon
11
6
4

1
0

Faces[0] refers to v2, v4,


2 v1
0 1 Faces[1] refers to v0, v3,
v2, v1
Faces[2] refers to v2, v3,
v5
2

3
5
11
7

Halfedge Mesh
An alternative way to represent a mesh
11
4
8
11

10 Let’s examine halfEdge2


1
0 9
8 halfEdge2.StartVertex: 2
5 3 halfEdge2.NextHalfEdge: 4
4 2 halfEdge2.PreviousHalfEdge: 0
halfEdge2.AdjacentFace: 1
2
0 1

0
6 1
1 1 2
7 2 3
1
3 4
1 5
5
11
9

Two essential mechanisms:


• Vertices push each other away (using
sphere-sphere collision)
• As an edge gets longer than a given
threshold, it will be split into two shorter
edges

Two extra mechanisms:


• Edge Length constraint: Prevent an edge
from getting too long
• Bending resistance: Each pair of adjacent
triangles will try to be flat
12
The Influence of Bending Resistance 0

No Low Moderate High


bending bending bending bending
resistance resistance resistance resistance
12
How to correctly combine different move vectors 1

Blend the vectors with equal percentage:


b Average = (a + b + c) *
0.33
Average = 0.33 * a + 0.33 * b +
c 0.33 * c
a What if we want to make “a” more influential:
Average = 0.9 * a + 0.05 * b +
0.05 * c
12
Blending vectors according to certain blending ratio 2

If we want to blend a, b, c according to the ratio 2:1:1

2 * a + 1 * b + 1
Average = * c
2 + 1 + 1

=0.5 * a + 0.25 * b +
0.25

You might also like