I’m experimenting with implementing DrawViewportWires in GHPython for drawing e.g. legends on top of 3D geometry generated in Grasshopper. Here’s a quick example:
The issue I’m trying to resolve is that the rectangles (drawn with Draw2dRectangle ) are drawn behind the 3D geometry that is drawn by Grasshopper (though it still appears to obscure the wires of the 3D mesh), while the text is drawn in the foreground:
I can see that this issue has been brought up previously by @andheum. But I’ve not been able to find any examples/code that demonstrate how one has resolved this (notably how to “add a callback to the DisplayPipeline.DrawForeground event”). I should also mention that I’ve been given great pointers by @dave_stasiuk and @tom_svilans on how they draw to the foreground from a display conduit in a compiled component. But before going down that path, I just wanted to make sure that I’m not missing something here.
So, is it possible to always draw to the screen space foreground from DrawViewportWires?
You really want to have HUD type display done in the DrawForeGround stage of drawing. DrawForeground occurs after DrawViewportWires and has depth testing turned off which is exactly what you want with a HUD.
Here is what I was able to cook up. It’s not as clean as just having a DrawForeground override in the component class, but it should work. Let me know if you have any questions.
I ended up using a combination of the two methods: Implementing both the hack to pipe into DrawViewportWires and the __enter__/__exit__ methods to add/remove the DrawForeground method. This means the component still behaves like a “normal” Grasshopper component (i.e. one can turn Preview off to stop drawing etc.).
I took a stab at a HUD display leveraging the DisplayPipeline.DrawForeground event handler in C#. Basic functionality works as expected, but I have a few questions on how to further customize it:
The event fires for each viewport separately. Is there a way to limit the HUD to only the active viewport?
Using the ViewCaptureToFile command with increased resolution doesn’t scale the HUD correctly and causes it to be drawn multiple times. Any ideas how to address this?
ScreenCaptureToFile:
Hi @stevebaer, @mrhe, I adapted a similar code based on the old forum, but I have some similar issues with geometries in Rhino and GH since I didn’t use DrawForeground, because of my limited C# knowledge. could you help me to fix it, please
Even in the Rendered mode is incomplete and sometimes disappeared.
Regards,
T.
to keep your dashboard consistently above the remaining 3d geometry you need to use the DrawForeground method. Here is what I did:
public override void BeforeRunScript()
{
_point.Clear();
_box = BoundingBox.Empty;
// Unregister & register new event
Rhino.Display.DisplayPipeline.DrawForeground -= DrawForeground;
Rhino.Display.DisplayPipeline.DrawForeground += DrawForeground;
}
public override BoundingBox ClippingBox
{get{return _box;}}
// Event function
private void DrawForeground(object sender, Rhino.Display.DrawEventArgs e)
{
for (int i = 0; i < _point.Count; i++)
DrawRec(e, _point[i]);
}
private void DrawRec(Rhino.Display.DrawEventArgs pipeline, Point3d point)
{
if (pipeline.Viewport.Id == pipeline.RhinoDoc.Views.ActiveView.ActiveViewportID)
{
Rectangle SQ = new Rectangle((int) point.X, (int) point.Y, 50, 50);
pipeline.Display.Draw2dRectangle(SQ, Color.Black, 5, Color.LimeGreen);
}
}
This gets you 80% there. You will notice, however, that the dashboard is now persistent even if you remove the script from your document or switch to a different one. Ideally, you would unregister from the DrawForeground event handler in the following two cases but I only know how to do it from a compiled component. Maybe someone else can help you with the script component counterparts:
public override void DocumentContextChanged(GH_Document document, GH_DocumentContext context) {}
public override void RemovedFromDocument(GH_Document document) {}
Alas, none that I ever found. Unfortunately I think it has to do with the display pipeline calling the DrawForeground override for each subdivided screen tile when the ViewCapture commands are run, and I was never able to figure out how to get around it.
I did start up a pipeline using SVG.NET to output vector-based representations of Conduit DrawObjects, but that work never made it into a released version.
There isn’t really any trick to deal with it; no way to get around it with the ViewportCaptureToFile command.
In the end, I had to write a custom command to produce a PDF output (in my case), starting with a new Rhino.Display.ViewCaptureSettings and the Rhino.FileIO.FilePDF classes. Then for all the custom 2d UI elements (like your floating “52”) that has to be drawn separately on the PDF (or image in your case), taking it out of your display conduit override. (All the 3d model elements in the viewport get rendered correctly.) You can do this by adding a check on the DrawEventArgs.Display.IsPrinting or IsInViewCapture field then skipping over those elements that are getting repeated.
FWIW the GHPython code doesn’t have this issue. It does have other ones though (for instance, having two components on the canvas that draws to screen space can become “entangled”). Anywho, I’ve suggested to Ehsan that perhaps they can add DrawForeground as a standard override in the new script editor (i.e. in addition to DrawViewportWires and DrawViewportMeshes). Which would hopefully give us a simple and unified method for drawing into screen space
Thank you for your answer, I guess I’ll play around with your script then for now. (just learning C#, my Python skills are even lower, but probably a good exercise)