algorithmic modeling for Rhino
Tags:
Dim yy as Rhino.Display.DisplayPipeline
Private Sub MyDisplay_CalcBBox(ByVal sender As Object, ByVal e As Display.CalculateBoundingBoxEventArgs)
e.IncludeBoundingBox(m_line.BoundingBox)
End Sub
Private Sub MyDisplay_PostDraw(ByVal sender As Object, ByVal e As Display.DrawEventArgs)
e.Display.DrawLine(m_line, Color.CornflowerBlue, 5)
End Sub
AddHandler Rhino.Display.DisplayPipeline.CalculateBoundingBox, AddressOf MyDisplay_CalcBBox
AddHandler Rhino.Display.DisplayPipeline.PostDrawObjects, AddressOf MyDisplay_PostDraw
Dim cd As New Rhino.Display.CustomDisplay(True)
cd.AddPoint(New Point3d(0,0,0))
cd.AddPoint(New Point3d(1,0,0), Color.DarkRed, PointStyle.Simple, 5)
cd.AddLine(New Line(New Point(0,0,0), New Point(1,0,0)), Color.DarkGreen, 4)
cd.AddVector(New Point3d(5,5,0), New Vector3d(0,0,10), Color.OrangeRed, True)
'from this point onwards the viewports will show the above geometry.
cd.Dispose()
'Once Dispose has been called the preview will be cleared. You can no longer use this instance of CustomDisplay from now on.
Hello David,
I tried your code and I managed to draw geometry as you mention above but I did not understand how to refresh the viewport and in order to clear the drawing, so when I try a component like this:
private void RunScript(double x, ref object A)
{
Rhino.Display.CustomDisplay cd = new Rhino.Display.CustomDisplay(true);
cd.AddPoint(new Point3d (x, 1, 0));
}
I get the viewport full of points and not only one.
Could you please give a hand to solve this?
Thanks!
Hi Miguel,
a CustomDisplay class maintains a bunch of object for all eternity*. You can clear the entire CustomDisplay class, but you cannot remove individual objects. It is therefore most suited for showing static geometry, though if you're willing to repopulate the CustomDisplay class from scratch every frame you can also use it to show animated geometry.
CustomDisplay will draw its geometry inside all viewports, but only when the viewports redraw themselves. CustomDisplay does not cause viewports to redraw.
If you want a specific CustomDisplay instance to stop drawing its geometry, you must dispose of it. Unless you call the Dispose() function the CustomDisplay will remain alive and active forever. This is what's happening in your case, you're creating new CustomDisplays every time RunScript is called without getting rid of old ones.
What you need to do is define your CustomDisplay class either as a Static variable (not possible in C#) inside RunScript or as a class-level member, i.e. outside any function blocks. For example, do this:
private boid RunScript(double x, ref object A)
{
if (m_display == null)
m_display = new Rhino.Display.CustomDisplay(true);
m_display.Clear();
m_display.AddPoint(new Point3d(x, 1, 0));
}
//<additional code>
private Rhino.Display.CustomDisplay m_display;
//</additional code>
By declaring m_display outside of the RunScript function you will not lose track of it once RunScript returns. And now every time RunScript runs you will clear the old point data and add a new point. And the first time RunScript runs you will construct the m_display instance.
* Or until Rhino shuts down rather.
--
David Rutten
david@mcneel.com
Poprad, Slovakia
Ok, David, very explanatory, as always.
Just to make sure I understood what is going on:
by declaring a private class, we are making the component to always declare this class first (erasing the old one and going through the conditional statement) everytime it runs the script.
Is that right (although not with the proper terms)? :-)
Whenever you define a variable inside a function (this is known as "method level declaration" I think) it will disappear over the horizon when the function ends. That doesn't necessarily mean it will be destroyed though, there's all kinds of fringe cases where an instance can be kept alive long after the function that created it has terminated. CustomDisplay is such a fringe case. It is kept alive because it handles events from Rhino viewports, and it's not until those viewports are destroyed that the CustomDisplay instance can also be destroyed.
When you call Dispose() on a CustomDisplay, it will sever those event handlers and thus distance itself from the existence of the Rhino viewports. It is now fair game for the Garbage Collector*.
When you declare a variable inside a class, but not inside a function (this is known as "class level declaration" I believe), its 'lifetime' is now tied to the existence of the class. As long as the class remains active, the variable will not be destroyed. But the same fringe cases apply here as well, it's possible for a class-level member to be kept alive beyond the lifetime of the class that declared it.
A third option you have is to declare the variable as a static class level member. It is now shared amongst all instances of your class and will never, ever be destroyed unless you do so manually.
These three options basically translate to:
public void MyMethod()
{
// Method-level declaration
MyClass someLocalInstance = new MyClass();
}
---------------------------------------
public void MyMethod()
{
if (someLocalInstance == null)
someLocalInstance = new MyClass();
}
// Class-level declaration
private MyClass someLocalInstance;
----------------------------------------
public void MyMethod()
{
if (someLocalInstance == null)
someLocalInstance = new MyClass();
}
// Static class-level declaration
private static MyClass someLocalInstance;
In VB.NET you have a fourth option, as it's possible to declare a method-level variable as static as well, thus making sure it is shared amongst all instances of that method, but C# doesn't have this possibility. However, it is very similar to the class-level static option, so you're not missing out on much.
"by declaring a private class, we are making the component to always declare this class first (erasing the old one and going through the conditional statement) everytime it runs the script."
Not quite. By declaring it as a class-level variable (i.e. private Rhino.Display.CustomDisplay m_display; ) you ensure that there's ever only one of these around. This then allows you to clear the data from the last time the RunScript function was called, which is what you need to do to avoid more and more geometry clogging up the viewports over time.
--
David Rutten
david@mcneel.com
Poprad, Slovakia
Great! Perfectly understood now.
Many thanks for your explanation again!
Dear Grasshopper Development Team,
Could you please provide an example for adding geometry to the bounding box in c#? Also does all geometry need to be added or just sufficient geometry to establish the extent of the new bounding box?
The bounding box you provide must encompass all the geometry you wish to draw. (The only exception to this is geometry you draw on frustum planes, which are always camera oriented.)
The best thing to do is maintain a BoundingBox variable which is set to Empty on every new solution. Then you grow this boundingbox with every piece of geometry you are going to draw.
private BoundingBox _bbox;
// to erase your boundingbox:
_bbox = BoundingBox.Empty;
// to add geometry to your boundingbox:
_bbox.Union(geometry.BoundingBox());
David,
I appreciate the answer but I am still having some difficulty. Would it be possible for you to provide some sample code? I ask partly because I am trying to fill a bounding box with brep geometry per your code but am only receiving errors that state that breps do not contain a definition for 'BoundingBox', etc.
It's probably called GetBoundingBox() or something.
If you upload a minimal file I can add some preview logic to it.
David,
Thank you. Here is a simple example.
Welcome to
Grasshopper
Added by Parametric House 0 Comments 0 Likes
Added by Parametric House 0 Comments 0 Likes
Added by Parametric House 0 Comments 0 Likes
Added by Parametric House 0 Comments 0 Likes
© 2024 Created by Scott Davidson. Powered by