JimYuan's Blog

Sharing the things I learned

0%

How Cherno explain about Thread

How we could do multi-thread(parallel computating)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <thread>

static bool s_Finished = false;

void DoWork(){
while(!s_Finished){
std::cout<<"Working...."<<std::endl();
}
}

int main(){
std::thread worker(DoWork); //pass as Function pointer

std::cin.get();
s_Finished = true;

worker.join();

std::cin.get(); // this line will wait until the threads(worker) joined.
}

Here we want program to do two things at the same time.

  1. keep printing Working....
  2. receive the cin.get() from user, once get something from the user s_Finished = true

thread::join

The behavior will be like this
idea

However we might not want to let one single thread occupy whole CPU resource, we can use std::this_thread::sleep_for(1s).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <thread>

static bool s_Finished = false;
void DoWork() {
using namespace std::literals::chrono_literals;
std::cout << "Started thread id=" << std::this_thread::get_id() << std::endl;
while (!s_Finished) {
std::cout << "Working...\n";
std::this_thread::sleep_for(1s); //sleep for a second
}
}

int main(){
std::thread worker(DoWork);
std::cin.get();
s_Finished = true;
worker.join(); //await in C#

std::cout << "Finished\n";
std::cout << "Started thread id=" << std::this_thread::get_id() << std::endl;

std::cin.get();
}

The demo of code above.
idea
Can notice that two std::this_thread::get_id() are different, cuz they are on the different threads.

How we could implement multi-thread in Grasshopper

Grasshopper Tutorial

Like this tutorial shown, we can use

1
2
3
System.Threading.Tasks.Parallel.For(0, loopCount, i =>{ {
//for loop body
}});

instead of using regular for-loop to achieve multi-thread.

unsafe problem and how to tackle it

This is the part I might not fully understand, and you are welcome to correct me. Thanks.
Seems simply use multi_thread with List, like the example in tutorial, is not safe.
Means multi-thread cannot merge to the List in the order correctly. As a result, this cause some data lost.

How we can tackle this problem

  1. We can use lock syntax to ensure the action of List
  2. We use regular array instead of List

Here are the codes.

Lock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void RunScript(List<Point3d> startPts, object y, ref object A)
{
List<Point3d> ptList = new List<Point3d>();
//for(int i = 0;i < startPts.Count; i++)
System.Threading.Tasks.Parallel.For(0, startPts.Count, i => {
{
//Declare out agent location
Point3d agentLoc = startPts[i];
//Add agents to ptLists
lock(ptList){
ptList.Add(agentLoc);
}
}
});
A = ptList;
}

array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void RunScript(List<Point3d> startPts, object y, ref object A)
{
//List<Point3d> ptList = new List<Point3d>();
Point3d[] ptList = new Point3d[startPts.Count];
//for(int i = 0;i < startPts.Count; i++)
System.Threading.Tasks.Parallel.For(0, startPts.Count, i => {
{
//Declare out agent location
Point3d agentLoc = startPts[i];
//Add agents to ptLists
ptList[i] = agentLoc;
}
});
A = ptList;
}

Rhino Official Documents_General Overview

multi-thread

Rhino Official Documents_Developer

Task Capable Component

Overview

Grasshopper for Rhino6 allows you to develop mult-threaded components by way of the new IGH_TaskCapableComponent interface. Benchmarks have shown that Grasshopper can run significantly faster when using multi-threaded components. Results may vary, as not all solutions can be compute in parallel

The Interface

When a component implement the IGH_TaskCapableComponent interface, Grasshopper will notice and potentially call a full enumeration of SolveInstance for the component twice in consecutive passes:

  1. The first pass is for collecting data and starting tasks to compute result
  2. The second pass is for using the results from the tasks to sets outputs.

https://discourse.mcneel.com/t/v6-feature-multi-threaded-gh-components/47049

I found that there are some Rhino official posts talk about mult-threaded in grasshopper.
From User interface, if you see the component has two grey dots on its upper-left corner, means that component support mult-threaded computation(parallel computing).

Still working on…. To be continue

(James_Ramsden_Multithreading)[http://james-ramsden.com/multithreading-a-foreach-loop-in-a-grasshopper-components-in-c/]

Recource Video
Thanks for the youtube channel, The Cherno. This post is basically a note I take to remind me what Function Pointer is about.

Function Pointer is a way to assign pointer to a variable.

Pass a function as a parameter.

The usual way about function

The implementation of a regular function should look like.

1
2
3
4
5
6
7
8
9
10
#include <iostream>

void HelloWorld(){
std::cout<< "Hello World!"<<std::endl;
}

int main(){
HelloWorld();
std::cin.get();
}

The result should be like
idea

What if we try to use auto key for this function?

idea
then you will see an error at the auto.
That is because what HelloWorld() return is a void.

But what if we do like this. auto function = HelloWorld
idea
then the error disapper.
This is because HelloWorld now return a Function Pointer instead of void.
so if we try to print the variable function in the next line, we will get the pointer of the HelloWorld function.
idea
idea
Due to the implicitly conversion,

1
2
3
auto function = HelloWorld;
//much the same as
auto function = &HelloWorld;

What Function Pointer is about

Functions are of course just CPU instructions, and they are stored in somewhere in our binary/executable file.

Find the Hello World function inside the CPU instruction and give it a pointer, to remember the location of this function code.

idea
idea

so here we can assign a function to a varible.

But what type is this auto actually?

Here the auto hold the type write like

1
void(*function)()

void(*)() is the type eventually, but since we have to get it a name so it became void(*function)().
It works with any other name.(Like I named it jim here)
idea

Becauce this syntax is kinda confusing, programmer tend to either

  • use auto, like what we did above
  • use typedef/using it

idea
Here we make HelloWorld function require one int parameter
idea

Back to programmer want to use function pointer in the first place

Programmers can pass one function to another function as a variable.

idea

Here this code also show how to use lambda function instead of writing out the full function body.

Reference: Cherno’s video

Intersection in 3D

According to this post

Most 3D lines do not intersect. A reliable method is to find the shortest line between two 3D lines. If the shortest line has a lenght of zero(or less than whatever tolerance you specify) then you know that the two original lines intersect.

Therefore, the question become

How can we find the shortest line between two 3D lines.

Check this article by Paul
Two examples I read

In short, this article introduced two thoughts/ implementation to get the minimal distance between two 3D lines.

  1. Write down the lenght of the line segment joining the two lines and then find the minimum
  2. dot product

If the minimal distance == 0, then we can say these two lines intersected

Intersection.LineLine in RhinoCommon

Intersection.LineLine

Example code I made:

1
2
3
4
5
6
7
8
9
10
11
using Rhino.Geometry.Intersect;
//
private void RunScript(Line lineA, Line lineB, double tolerance, bool finiteSegments, ref object A, ref object a, ref object b)
{
double lineA_a;
double lineB_b;
A = Intersection.LineLine(lineA, lineB, out lineA_a, out lineB_b, tolerance, finiteSegments);

a = lineA_a;
b = lineB_b;
}

IntersectinoLineLine_RhinoCommon

Also, there’s a componeny called Line|Line in grasshopper, we can directly use.
Line|Line

Intersection in 2D

How to check if two given line segments intersect? (2D)
GeeksforGeeks
One can determine if two given line segments(2D/laid on the same plane) intersect by the Orientation.

Orinentation of an oedered triplet of points in the plane can be:

  • counterclockwise
  • clockwise
  • colinear

Orinetation constructed by 3 points

check orinetation
In short, three points can form 2 lines. By checking these two lines($m_1$ & $m_2$) slope change, we can determine either

  1. counterclockerwise($m_2>m_1$)
  2. clockwise ($ m_2 > m_1$)
  3. colinear ($ m_1 == m_2$)
1
2
3
4
5
6
7
8
9
10
11
12
private void RunScript(Point3d pointA, Point3d pointB, Point3d pointC, ref object orientation)
{
double m1 = (pointB.Y - pointA.Y) / (pointB.X - pointA.X);
double m2 = (pointC.Y - pointB.Y) / (pointC.X - pointB.X);

Print(m1.ToString());
Print(m2.ToString());
if(m1 == m2) orientation = "colinear";
else if(m1 > m2) orientation = "clockwise";
else orientation = "counterclockwise";

}

Orientation

Let’s assume there are two line segments Line A and Line B.

  • The end points on Line A are Point p1 and Point q1
  • The end points on Line B are Point p2 and Point q2
    as the picture shown below.
    Orientation of 3-ordered points

Example of lines intersect to each other

2D line Intersection

starting from Line A(p1)

$ p1 \rightarrow q1 \rightarrow p2$ is clockwise, while
$ p1 \rightarrow q1 \rightarrow q2$ is counterclockwise.

starting from Line B(p2)

$ p2 \rightarrow q2 \rightarrow p1$ is counterclockwise, while
$ p2 \rightarrow q2 \rightarrow q1$ is clockwise.

If both cases have different orientations, then we can say that they are intersected to each other.

Example of lines are not intersected

2D line Intersection

starting from Line A(p1)

$ p1 \rightarrow q1 \rightarrow p2$ is clockwise, while
$ p1 \rightarrow q1 \rightarrow q2$ is counterclockwise.

starting from Line B(p2)

$ p2 \rightarrow q2 \rightarrow p1$ is counterclockwise, and
$ p2 \rightarrow q2 \rightarrow q1$ is also counterclockwise.

Special Case - colinear

Two lines colinear

Node

Introduction

I have implemented Convex hull algorithm in grasshopper by creating one C# scripting component. Here, I would like to note some codes I have learned.
There are many algorithms for finding convex hull, Javis' March, also known as Gift Wrapping Algorithm, should be the most intuitive one.
Though it is not the fastest one, this algorithm solve the problem with only an simple geometric algebra sense.

Some other algorithms about Convex hull can be found here

Dive into Algorithm

idea

While marching, by the result of the cross Product to determine the next point.

  • Prepare point cloud(many points) on one plane.
  • Decided the starting point
  • Using crossProduct property to march through the points.

Starting point

I set the buttom-most point as the starting point, and if the y is the same then compare x-value.

1
2
3
bool compare(Point3d a, Point3d b){ // to find the starting point
return (a.Y < b.Y) || (a.Y == b.Y && a.X < b.Y);
}

CrossProduct

1
2
3
4
5
6
7
Vector3d crossProduct(Vector3d u, Vector3d v){
double x = u.Y * v.Z - u.Z * v.Y;
double y = u.Z * v.X - u.X * v.Z;
double z = u.X * v.Y - u.Y * v.X;

return new Vector3d(x, y, z);
}

Length2

Though we should calculate distance, which should use square root, we here can do the same thing without using it because we want the relationship, instead of the precise distance.
By doing this way, we get what we want, and because not using square root calculation, it is faster.

1
2
3
double length2(Point3d a, Point3d b){
return (a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y);
}

All code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
private void RunScript(List<Point3d> pts, ref object A)
{
A = Javis_march(pts);
}

// <Custom additional code>
// find the buttom-most points(left most if the y values are the same)
bool compare(Point3d a, Point3d b){
return (a.Y < b.Y) || (a.Y == b.Y && a.X < b.Y);
}

Vector3d crossProduct(Vector3d u, Vector3d v){
double x = u.Y * v.Z - u.Z * v.Y;
double y = u.Z * v.X - u.X * v.Z;
double z = u.X * v.Y - u.Y * v.X;

return new Vector3d(x, y, z);
}

double length2(Point3d a, Point3d b){
return (a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y);
}

bool far(Point3d o, Point3d a, Point3d b){
return length2(o, a) > length2(o, b);
}

List<Point3d> Javis_march(List<Point3d> pts){

List<Point3d> vertexs = new List<Point3d>();

int start = 0;
for(int i = 0; i < pts.Count;i++){
if(compare(pts[i], pts[start]))
start = i;
}

vertexs.Add(pts[start]);
int current = start;

do{
int next = current;
for(int i = 0; i < pts.Count; i++){
double c = crossProduct(new Vector3d(pts[i] - vertexs[vertexs.Count - 1]), new Vector3d(pts[i] - pts[next])).Z;
if(c > 0 || c == 0 && far(vertexs[vertexs.Count - 1], pts[i], pts[next])){
next = i;
}
}
vertexs.Add(pts[next]);
current = next;
}
while(current != start);
return vertexs;
}

I used do-while first ,cause I want to at least run once, which make sure the list contains at least two points.

Note

Let’s assume if there are n points in the point cloud, and m points on the convex hull.
Then the total time complexity will be O(NM).

There are some faster algorithms doing sorting before running cross Product calculation, Andrew's Monotone Chain, for example. I might try to implement this algorithm the other day.

References

Node

Content

I just learned this algorithm to simplified polyline, with Professor. Jose’s amazing tutorial. People who want to learn a bit more, should definitely follow this channel.

The idea is relatively simple, but the implementation could be thousands and millions way.
The goal of this algorithm is to reduce the points in one polyline(points + line), but still want to remain to soul(properity, or shape to be more specifically). There are tons of benefits by doing this, obvious one is that this can clearly releave your memory while speed up the computation time.

Implement

The method here is to recursively find the farthest point to the two segment endpoints. That’s it.

Dive into the Csharp code

Something should be took care

  • epilson
  • left points
  • right points
  • two lists combinition
  • recursive function
  • Point-Line distance

Because this blog is for recording something I have learned, it is definitely not detail-clear to everyone. Please refer to the Professor Jose’s tutorial video, if you want to have a clear idea of this.

code

crossProduct

Though in RhinoCommon, there are one predefined crossProduct method that we could use directly, it is convenient that we made by ourselves in the cases of bring this method to another Csharp-based platform.

1
2
3
4
5
6
7
Vector3d crossProduct(Vector3d u, Vector3d v){
double x = u.Y * v.Z - u.Z * v.Y;
double y = u.Z * v.X - u.X * v.Z;
double z = u.X * v.Y - u.Y * v.X;

return new Vector3d(x, y, z);
}

Point-Line distance

$$ \frac{\vec{AB} \times \vec{AP}}{| \vec{AB}|}$$

1
2
3
4
5
6
7
8
9
10
11
double distToLine(Point3d P, Point3d A, Point3d B){
Vector3d AB = B - A;
Vector3d AP = P - A;

Vector3d cross = crossProduct(AB, AP);
double lengthCross = cross.Length;
double lengthAB = AB.Length;

double h = lengthCross / lengthAB;
return h;
}

simplifyPolyline

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
List<Point3d> simplifyPolyline(List<Point3d> points, double epsilon){

double maxDist = -1.0;
int maxIndex = -1;

for( int i = 1; i < points.Count - 1 ;i++){
double d = distToLine(points[i], points[0], points[points.Count - 1]);
if( d > maxDist){
maxDist = d;
maxIndex = i;
}
}

Print("max index: " + maxIndex + "\t maxdist:" + maxDist);

List<Point3d> cleanPoints;

if(maxDist > epsilon){
// Subdivide poly in two parts
Print("should do subdivision here");
List<Point3d> leftPoly = points.GetRange(0, maxIndex +1);
List<Point3d> rightPoly = points.GetRange(maxIndex, points.Count - maxIndex);

// Apply recursion to each one
List<Point3d> leftSimplified = simplifyPolyline(leftPoly, epsilon);
List<Point3d> rightSimplified = simplifyPolyline(rightPoly, epsilon);

// Avoid duplicating split points
rightSimplified.RemoveAt(0);
//rightSimplified = rightSimplified.GetRange(1,rightSimplified.Count-1);

cleanPoints = leftSimplified;
cleanPoints.AddRange(rightSimplified);

}
else{

// remove all points between endpoints and return
cleanPoints = new List<Point3d>(){points[0], points[points.Count - 1]};
}

return cleanPoints;
}

Note from reading Wiki

Application

The algorithm is used for the processing of vector graphics and cateographic generalization.

It does not always preserve the property of non-self intersection for curves which has led to the development of variant algorithms.

Like Jose mentioned in his video, this method is widely used in robotics to perform simplification and denoiseing of range data acuquired by a rotating range scanner.

Complexity

Reference

前言

因為在grasshopper中slider的上下界是在創建的時後就被確定的,像是直接在canvas中輸入0..10.0..20.0這樣就能產生slider滿足

  • 上界是20.0,下界是0.0
  • 精準度是小數點下一位
  • slider產生時的起始值就是10.0

不過這樣做不能runtime的時候更改slider的上下界,雖然必要性沒有很大,而且想要在runtime有新的值不一定要改到slider本身,但因為相對起來slider還是滿直觀的,所以找了一下在runtime更改slider的文章。
主要內容參考來自James-Ramsden這篇

內文

有時候會想要很明確掌握slider的範圍。像下面我自己嘗試的一小例子。

Node

Node

這個範例的邏輯很簡單,就是有個大的矩形然後我用大矩形的四個頂點做出四個空間,但因為我想要讓大矩形長寬是可以調整的,如果調整的話其他四個房間的slider如果能在runtie的時候就決定上界到多少就不會有超過跟重疊的狀況了。
所以用一下以下的C# code可以幫助決定上下界。

1
2
3
4
5
6
7
8
9
10
private void RunScript(object x, double y, ref object A)
{
var input = Component.Params.Input[0].Sources[0]; //get the first thing connected to the first input of this component
var slider = (Grasshopper.Kernel.Special.GH_NumberSlider) input; //try to cast that thing as a slider

if(slider != null) //if the component was successfully cast as a slider
{
slider.Slider.Value = (decimal) y;
}
}

這是讓y的值去寫進x裡面
Node
但你也可以用slider.Slider.Minimumslider.Slider.Maximum去決定他的上下界。
比如我x的上界會等於y值的一半。就可以這樣寫

1
2
3
4
5
6
7
8
9
10
11
12
13
private void RunScript(object x, double y, ref object A)
{
var input = Component.Params.Input[0].Sources[0]; //get the first thing connected to the first input of this component
var slider = (Grasshopper.Kernel.Special.GH_NumberSlider) input; //try to cast that thing as a slider

if(slider != null) //if the component was successfully cast as a slider
{
if(slider.Slider.Value > (decimal) y / 2){
slider.Slider.Value = (decimal) y / 2;
}
slider.Slider.Maximum = (decimal) y/2;
}
}

我有寫一個防護的判斷如果原本x的值就超過y/2的話我就讓值變成x = y/2 就不會有奇怪的UI出現了XD
Node

那前面房間長寬slider上下界可以這樣修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private void RunScript(object x, object y, double z, ref object A)
{
var input1 = Component.Params.Input[0].Sources[0];
var slider1 = (Grasshopper.Kernel.Special.GH_NumberSlider) input1;

var input2 = Component.Params.Input[1].Sources[0];
var slider2 = (Grasshopper.Kernel.Special.GH_NumberSlider) input2;

decimal val = slider2.Slider.Value;
if(slider1 != null && slider2 != null){

if(slider2.Slider.Value > (decimal) z - slider1.Slider.Value){
slider2.Slider.Value = (decimal) z - slider1.Slider.Value;
}
slider2.Slider.Minimum = 0;
slider2.Slider.Maximum = (decimal) z - slider1.Slider.Value;

if(slider1.Slider.Value > (decimal) z - slider2.Slider.Value){
slider1.Slider.Value = (decimal) z - slider2.Slider.Value;
}

slider1.Slider.Minimum = 0;
slider1.Slider.Maximum = (decimal) z - slider2.Slider.Value;

}
//A = val;
}

可以觀察一下Dining RoomLiving Room 的水平方向 slider 調整一個另一個的上下界也會改,主要的邏輯是 大矩形的寬 = DiningRoom_width + LivingRoom_Width
這是邊界條件啦
Node

碰到了XD 但沒有超過~

最後阿,有個可以換slider顏色的方法滿酷的,雖然不知道有甚麼用
Node

1
2
3
4
5
6
7
8
9
10
11
12
13
private void RunScript(object x, double y, System.Drawing.Color z, ref object A)
{
var input = Component.Params.Input[0].Sources[0]; //get the first thing connected to the first input of this component
var slider = (Grasshopper.Kernel.Special.GH_NumberSlider) input; //try to cast that thing as a slider

if(slider != null) //if the component was successfully cast as a slider
{
slider.Slider.DrawControlBackground = true;
slider.Slider.DrawControlBorder = true;
slider.Slider.ControlEdgeColour = Color.Blue;
slider.Slider.ControlBackColour = z;
}
}

要記得先using System.Drawing; 這行喔

參考

James-Ramsden這篇
James感覺也是grasshopper/Revit相關的部落客/工程師,有很多很好的資源,推薦。

前言

因為最近RIR(Rhino Inside Revit)的整合技術陸續Mcneel官方慢慢釋出相關的document跟陸續有點資料大家在討論,想整理一下目前的資料,以及剛聽完hiron對RhinoInside的簡介,想寫一下部分的筆記,也不確定可以寫多少,讀者加減把這篇看成入門的練習吧,如果有甚麼不詳盡的部分還請利用下方留言板跟我討論一下,謝謝~

What is Rhino.Inside

Rhino.Inside is a new technology developed by Robert McNeel & Associates that allows embedding Rhino WIP into other applications. Rhino.Inside is being embedded into many applications from a wide variety of disciplines.

Rhino.Inside.Revit

Rhino.Inside.Revit is an addon for Autodesk Revit that allows Rhino WIP to be loaded into the memory of Revit just like other Revit addons.

2020年5月的進度

如果大家點開RiR_github然後可以發現McNeel官方在.NET framework下實踐了把Rhino塞到各個平台上。
大概的操作狀況可以參考hironのblog, 我自己嘗試了在autocad軟體上成功執行TestRhinoInside了。

Rhino.Inside

  • Rhino.Inside illastrator
  • Rhino.Inside AutoCAD
  • Rhino.Inside Revit
  • Rhino.Inside BricsCAD
  • Rhino.Inside ConsoleApp
  • Rhino.Inside DotNet
  • Rhino.Inside JavaScript
  • Rhino.Inside UE
  • Rhino.Inside Excel
  • Rhino.Inside Unity

稍微嘗試了一下Rhino.Inside DotNet,拿了以前的模型丟進WinForms的小視窗。如果有用過WPF或是WinForms的話,大概可以知道做出一個pop up windows的感覺,Revit API也常常會遇到要設計給使用者輸入資訊的情況。
大概會像這樣
Node

不過也有發現現在功能相對不穩定,不一定一開就可以成功,可能開發團隊還在努力吧。

Sender&Receiver

這部份透過hiron的講解,大概讓我們知道Rhino.Inside的實作方式。因為還沒很深入去看底層的東西(一部分應該目前看也看不懂)就紀錄一下我的猜測,因為Rhino.Inside是把整個Rhino當作子程式掛件掛在其他程式上,如果用Rhino.Inside的時候開檔案管理員,應該會發現程式占用的記憶體會飆高。 但因為是在同一個主程式下的記憶體中運算,所以如果能做成一個通道把Rhino運算的東西傳給其他程式(傳回主程式),或讀取主程式的資料的話。Rhino.Inside 任何程式都可以看成是資料寫出跟寫入的過程。其實大方向很簡單的來說就是省去了存檔再讀取的步驟,但因為寫出往往是寫到硬碟,又到硬碟讀會讓流程整個變慢,更別說是想要Real Time修改東西了。

Implement in Grasshopper

hiron介紹了一段code如下:
Node

Sender

1
2
3
4
5
6
7
8
private void RunScript(string Send_Str)
{
using(var args = new Rhino.Runtime.NamedParametersEventArgs()){
args.Set("str", Send_Str);
Rhino.Runtime.HostUtils.ExecuteNamedCallback("ToRiR", args);
}
}

Receiver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private void RunScript(ref object Receive_str)
{
Receive(Component);
Receive_str = str;
}

// <Custom additional code>
string str;
bool registered = false;
IGH_Component comp = null;

void Receive(IGH_Component component){
if(!registered){
Rhino.Runtime.HostUtils.RegisterNamedCallback("ToRiR", RiRhino);
comp = component;
registered = true;
}
}
void RiRhino(object sender, Rhino.Runtime.NamedParametersEventArgs args){
string values;
if(args.TryGetString("str", out values)){
str = values;
}
comp.ExpireSolution(true);
}

因為其實C# Component中 Member縮排那邊有先定義過一個private readonly IGH_Component Component; ,所以在上面的RunScript可以直接用。

Runtime.HostUtils.ExecuteNamedCallback

Execute a named callback

NamedParametersEventArgs

Dictionary style class used for named callback from c++ -> .NET

Runtime.HostUtils.RegisterNamedCallback()

Register a named callback from C++ -> .NET

參考資料

前言

因為想好好寫一下分享文,也整理一下自己的筆記所以弄了這個簡單的 Github Page 如果有甚麼建議還請利用下面的gitalk留言回覆,謝謝~

這篇NodeInCode主要是想跟大家分享NodeInCode namespace。看論壇討論串也是2017年初出來的功能,我的理解是,如果在scripting(C#/python/VB)的時候,又想用其他component的功能就很方便。
例如,想要用weaverbirds的Loop_Subd就不用要把自己的C# code因為用不到第三方的component而切成兩個或以上,對資料的整理我覺得會有更好的自由度。

這是我找到的第一次大家討論這個namespace的反應。
Rhino.NodeInCode namespace?
很好笑的是樓主說:

Not sure if this is a dream…

哈哈哈我很能理解~

如何使用?

實際的操作我是跟著這篇,所以如果有不太懂的部分,可以仔細追一下這篇文。

1. 先確定要使用的component名稱

Component的全名我覺得不太直觀能找到,所以可以使用以下方是條列全部loaded components

1
A = Rhino.NodeInCode.Components.NodeInCodeFunctions.GetDynamicMemberNames();

2. 輸入參數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void RunScript(Mesh M, int L, int S, ref object A)
{
Rhino.NodeInCode.ComponentFunctionInfo mesh = Rhino.NodeInCode.Components.FindComponent("WeaverBird_WeaverbirdsLoopSubdivision");
if(mesh == null) return;

string[] warnings;
object[] result = mesh.Evaluate(new object[]{
M,
L,
S
},
false, out warnings
);

if(warnings != null) foreach(var w in warnings) Print(w);

A = result[0];
}

Node

當然有些list或DataTree的結構這邊也可以做。
透過這個方式可以重新包裝第三方的Component但因為Grasshopper基本上都是開源的,不要因為這樣就重新包裝別人的code說是自己的,要給設計方credit,這很重要。
之前有發生過pufferfish的component被包進另一個panda component的事件,很不好啦。

後記

之前哈佛的建築系教授garciadelcastillo 做了一個project GH#就是想做個documentation幫助從grasshopper介面轉到C#的過程。
很多時候不一定邏輯想不通,而是不確定可以用的工具有哪些。garciadelcastillo也提過說基本上RhinoCommon的document跟grasshopper的default component應該可以找到一對一對應的(我實際上覺得C#的自由度更大),
但有些輸入的parameters跟細微的定義會依照programmer的sense而有些微的不同。

另外最近我自己對RTree跟kangaroo的結合很感興趣,也發現也發現有這方面的研究
感覺是一個CITA的教授的projectRTree+Kangaroo
Petras Vestartas
雖然沒有實際看過他這個模擬的code但因為他是用kangaroo結合RTree所以應該也是用這樣NodeInCode的方式

因為之前做了Fuji Pavilion 1970 Expo用了kangaroo做這種模擬,但發現接觸點太多會讓計算變得很慢,所以有Rtree的幫忙應該會快很多。
其實目前覺得最快的應該是用FlexHopper(from Benjamin Felbrich) 因為FlexHopper用Nvidia Flex CLI作,用GPU會快很多,再加RTree那應該可以真的很快。

RTree