Grasshopper

algorithmic modeling for Rhino

I write something like this

Dim yy As rhino.Display.DisplayPipeline

yy.DrawLine(New line(New point3d(0, 0, 0), New point3d(100, 100, 100)), color.Red)

But there is a wrong message said"Object reference not set to an instance of an object".
Anywhere wrong?

Views: 3427

Replies to This Discussion

Hi Lotus,

There's two things wrong with this code, one trivial, one serious.

Dim yy as Rhino.Display.DisplayPipeline

This declares a DisplayPipeline, but it doesn't instantiate it. Meaning, the variable yy now exists, but it still has a value of null (Nothing in VB). When you then try to call a method on yy (i.e. DrawLine), you'll get a null reference exception ("Object reference not set to an instance of an object").

That was the trivial bit.

The serious bit is that this is the wrong approach. Every Viewport in Rhino has it's own DisplayPipeLine set up. You yourself cannot create a new pipeline, only Rhino can. In order to draw things in the viewport, you need to approach this differently.

You'll need to write two functions. The first must adjust the clipping region of the viewport to ensure that your line will be completely visible. The second must then draw this line. Viewport drawing in RhinoCommon is an event-based mechanism, so you'll also need to hook up the eventhandlers (and unhook them once you're done). First, the clipping function:

Private Sub MyDisplay_CalcBBox(ByVal sender As Object, ByVal e As Display.CalculateBoundingBoxEventArgs)
   e.IncludeBoundingBox(m_line.BoundingBox)
End Sub


where m_line is a class level or static Rhino.Geometry.Line

Now, the drawing function:

Private Sub MyDisplay_PostDraw(ByVal sender As Object, ByVal e As Display.DrawEventArgs)
   e.Display.DrawLine(m_line, Color.CornflowerBlue, 5)
End Sub


When you want to start drawing your geometry, do this:

AddHandler Rhino.Display.DisplayPipeline.CalculateBoundingBox, AddressOf MyDisplay_CalcBBox
AddHandler Rhino.Display.DisplayPipeline.PostDrawObjects, AddressOf MyDisplay_PostDraw


From this point onwards, whenever a viewport needs to redraw itself, these functions will get called. When you're done, repeat the above two lines but use RemoveHandler instead of AddHandler.

--
David Rutten
david@mcneel.com
Poprad, Slovakia
I just wrote a CustomDisplay class for RhinoCommon that will draw a bunch of geometric primitives (points, lines, curves, vectors etc.). It's easy to use (I think), but if you want anything complicated you'll have to revert to proper display event handlers.

This addition hasn't been approved yet by Steve and it will be a few days until the next release anyway.

Here's an example of how it would be used:

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.



--
David Rutten
david@mcneel.com
Poprad, Slovakia

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.

Attachments:

RSS

About

Translate

Search

Videos

  • Add Videos
  • View All

© 2024   Created by Scott Davidson.   Powered by

Badges  |  Report an Issue  |  Terms of Service